# AGENTS.md - 酒店二维码模块开发规范
> **重要**: 此文件会被 IDEA QoderWork 插件自动读取,为 AI 助手提供项目上下文和开发规范
**项目**: Forge Admin - 酒店二维码模块 (Hotel QR Code Module)
**技术栈**: Spring Boot 3.2 + Vue 3.5 + Naive UI 2.42 + MyBatis-Plus 3.5
**最后更新**: 2026-08-10
---
## 一、核心开发规范(必须遵守)
### 1.1 后端规范
**启动类**: `ForgeAdminServerApplication`(主应用入口,聚合所有插件)
**依赖注入**:
```java
@RequiredArgsConstructor // ✅ Lombok 构造器注入
private final HotelQrCodeMapper qrCodeMapper;
// ❌ 禁止使用 @Autowired
```
**响应体**:
```java
// ✅ 必须使用 RespInfo
return RespInfo.success(data);
return RespInfo.error("错误信息");
// ❌ 禁止使用 DataResult / ResponseUtil
```
**ID生成**:
```java
// ✅ 使用雪花算法(MyBatis-Plus ASSIGN_ID)
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
// ❌ 禁止使用 UUID / 自增ID
```
**实体继承**:
```java
// ✅ 所有实体必须继承 TenantEntity
public class HotelQrCode extends TenantEntity { ... }
// TenantEntity 包含: tenantId, createBy, createTime, createDept, updateBy, updateTime
```
**逻辑删除**:
```java
// ✅ 必须显式声明 @TableLogic
@TableLogic(value = "0", delval = "id")
private Long delFlag;
// Mapper XML 查询必须显式过滤: AND del_flag = 0
```
**Controller路由**:
```java
@RestController
@RequestMapping("/hotel")
public class HotelQrCodeController { ... }
// 实际访问: /hotel/qrcode/page, /hotel/room/page 等
```
**SQL 规范**:
- 查询类 SQL **禁止**在 Service 层用 `LambdaQueryWrapper` 构建
- 必须写在 Mapper XML 中(DataScopeInterceptor 按 mapperMethod 精确匹配)
- 例外:仅单表 `selectById`、`insert`、`updateById`、`deleteById` 等内置方法允许
**租户 ID 规则**:
- 业务数据的 `tenant_id` **必须设为 `1`**(默认租户),**禁止设 `0`**
- `TenantLineInnerInterceptor` 自动追加 `WHERE tenant_id = 当前租户ID`
### 1.2 前端规范
**组件语法**:
```vue
```
**列表页面**:
```vue
```
**字典使用**:
```vue
```
**按钮样式约定**:
| 类名 | 颜色 | 场景 |
|------|------|------|
| `text-primary` | 蓝 | 编辑、查看、绑定、下载 |
| `text-warning` | 黄 | 解绑、禁用、重置 |
| `text-error` | 红 | 删除 |
| `text-success` | 绿 | 启用 |
| `text-info` | 灰蓝 | 详情、日志 |
---
## 二、项目架构
### 2.1 模块位置
```
forge-server/forge-business/forge-hotel/ # 酒店业务模块
├── controller/ # REST 控制器
├── service/ # 服务接口
│ └── impl/ # 服务实现
├── mapper/ # MyBatis Mapper 接口 + XML
├── domain/ # 数据库实体
├── dto/ # 请求 DTO
── vo/ # 响应 VO
├── constant/ # 常量定义
└── utils/ # 工具类(二维码生成、签名校验)
forge-admin-ui/src/views/hotel/ # 前端页面
├── qrcode.vue # 二维码管理(AiCrudPage)
├── room.vue # 房间管理(AiCrudPage)
└── roomType.vue # 房型管理(AiCrudPage)
forge-h5-ui/src/pages/hotel/ # H5 移动端
└── scan-bind.vue # 扫码绑定房间页面
```
### 2.2 三端架构
| 端 | 技术 | 部署 | 核心功能 |
|---|------|------|---------|
| PC 管理后台 | Vue3 + Naive UI + AiCrudPage | PC浏览器 | 批量生成、批量删除、批量下载ZIP、绑定/重绑/解绑、状态管理 |
| H5 移动端 | UniApp + html5-qrcode | 手机浏览器(HTTPS) | 员工扫码绑定房间、查看绑定状态 |
| 开放接口 | REST API | 免登录 | 顾客扫码查询二维码信息 |
---
## 三、技术栈清单
### 后端
- Java 17 + Spring Boot 3.2
- MyBatis-Plus 3.5 + 动态数据源
- Sa-Token 1.38(认证授权)
- 多租户隔离(TenantLineInnerInterceptor)
- ZXing 3.5.3(二维码生成)
- MySQL 8.0+ / Redis 6.0+
### 前端
- Vue 3.5 + Naive UI 2.42
- Vite 7 + Pinia 3 + UnoCSS 66
- AiCrudPage / AiSearch / AiTable(框架标准组件)
- html5-qrcode(H5 扫码)
---
## 四、数据库设计
### 4.1 表结构
**hotel_qr_code(二维码表)**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键(雪花算法) |
| tenant_id | BIGINT | 租户ID,默认1 |
| short_code | VARCHAR(32) | 二维码唯一短码(8位大写字母+数字) |
| sign | VARCHAR(64) | MD5签名值 |
| room_id | BIGINT | 绑定房间ID(NULL=未绑定) |
| room_no | VARCHAR(32) | 冗余房间号 |
| status | VARCHAR(16) | 状态:0=启用, 1=禁用(对应字典 sys_normal_disable) |
| batch_no | VARCHAR(32) | 批次号(格式:QR+yyyyMMddHHmmss) |
| bind_user_id | BIGINT | 最近绑定人ID |
| bind_user_name | VARCHAR(64) | 最近绑定人姓名 |
| bind_time | DATETIME | 最近绑定时间 |
| unbind_user_id | BIGINT | 最近解绑人ID |
| unbind_user_name | VARCHAR(64) | 最近解绑人姓名 |
| unbind_time | DATETIME | 最近解绑时间 |
| del_flag | BIGINT | 逻辑删除标记(0=未删除,删除时写入主键ID) |
**唯一索引**: `UNIQUE (tenant_id, short_code, del_flag)`
**hotel_room(房间表)**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| tenant_id | BIGINT | 租户ID |
| room_no | VARCHAR(32) | 房间号(租户内唯一) |
| room_type_id | BIGINT | 房型ID |
| floor_no | VARCHAR(16) | 楼层 |
| status | VARCHAR(16) | 状态:VACANT/OCCUPIED/CLEANING/MAINTENANCE |
| sort_order | INT | 排序号 |
| remark | VARCHAR(256) | 备注 |
| del_flag | BIGINT | 逻辑删除标记 |
**唯一索引**: `UNIQUE (tenant_id, room_no, del_flag)`
**hotel_room_type(房型表)**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| tenant_id | BIGINT | 租户ID |
| type_name | VARCHAR(64) | 房型名称 |
| description | VARCHAR(256) | 房型描述 |
| sort_order | INT | 排序号 |
| del_flag | BIGINT | 逻辑删除标记 |
**hotel_qr_bind_log(绑定日志表)**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| tenant_id | BIGINT | 租户ID |
| qr_code_id | BIGINT | 二维码ID |
| short_code | VARCHAR(32) | 冗余短码 |
| room_id | BIGINT | 房间ID |
| room_no | VARCHAR(32) | 冗余房间号 |
| action_type | VARCHAR(16) | 操作类型:BIND/REBIND/UNBIND |
| operator_id | BIGINT | 操作人ID |
| operator_name | VARCHAR(64) | 操作人姓名 |
| operate_time | DATETIME | 操作时间 |
| remark | VARCHAR(256) | 备注 |
### 4.2 字典数据
| 字典类型 | 字典值 | 标签 | 标签样式 |
|----------|--------|------|----------|
| sys_normal_disable | 0 | 正常 | success |
| sys_normal_disable | 1 | 停用 | danger |
| hotel_room_status | VACANT | 空闲 | success |
| hotel_room_status | OCCUPIED | 入住 | primary |
| hotel_room_status | CLEANING | 打扫中 | warning |
| hotel_room_status | MAINTENANCE | 维护中 | danger |
| hotel_qr_bind_action | BIND | 绑定 | success |
| hotel_qr_bind_action | REBIND | 重绑 | warning |
| hotel_qr_bind_action | UNBIND | 解绑 | danger |
---
## 五、API 接口清单
### 5.1 管理端接口(需 Sa-Token 登录)
**二维码管理**:
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/hotel/qrcode/page` | 分页查询二维码 |
| GET | `/hotel/qrcode/:id` | 查询二维码详情 |
| POST | `/hotel/qrcode/batch-generate` | 批量生成二维码 |
| POST | `/hotel/qrcode/:id/bind` | 绑定房间 |
| POST | `/hotel/qrcode/:id/rebind` | 重新绑定房间 |
| POST | `/hotel/qrcode/:id/unbind` | 解绑房间 |
| PUT | `/hotel/qrcode/:id/status` | 更新二维码状态 |
| POST | `/hotel/qrcode/batch-remove` | 批量删除二维码(逻辑删除) |
| POST | `/hotel/qrcode/batch-download` | 批量下载二维码ZIP |
| GET | `/hotel/qrcode/:id/bind-logs` | 查询绑定日志 |
**房间管理**:
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/hotel/room/page` | 分页查询房间 |
| GET | `/hotel/room/:id` | 查询房间详情 |
| POST | `/hotel/room` | 新增房间 |
| PUT | `/hotel/room` | 修改房间 |
| POST | `/hotel/room/remove/:id` | 删除房间 |
| GET | `/hotel/room/list-all` | 查询全部房间(下拉用) |
**房型管理**:
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/hotel/room-type/list` | 查询房型列表 |
| GET | `/hotel/room-type/:id` | 查询房型详情 |
| POST | `/hotel/room-type` | 新增房型 |
| PUT | `/hotel/room-type` | 修改房型 |
| POST | `/hotel/room-type/remove/:id` | 删除房型 |
### 5.2 开放接口(免登录)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/hotel/open/scan?shortCode=xxx&sign=xxx` | 扫码查询二维码信息 |
---
## 六、二维码 URL 格式
```
{baseUrl}/#/pages/hotel/scan-bind?c={shortCode}&t={tenantId}&s={sign}
```
| 参数 | 说明 |
|------|------|
| c | 二维码短码(8位) |
| t | 租户ID |
| s | MD5签名值 = MD5(shortCode + tenantId + secret) |
**签名密钥配置**:
- 默认值: `hotel_qr_default_secret`
- 生产环境: 通过环境变量 `HOTEL_QR_SECRET` 覆盖
---
## 七、关键业务逻辑
### 7.1 批量生成
- 生成数量:10/50/100/200 个一批,上限 500
- 短码:8位大写字母+数字,租户内唯一
- 批次号:QR + yyyyMMddHHmmss
- 状态:默认启用(status=0)
### 7.2 绑定/重绑/解绑
- **绑定**:未绑定的二维码可绑定房间,记录绑定人/绑定时间
- **重绑**:已绑定的二维码可重新绑定,覆盖原绑定关系,更新绑定人/绑定时间
- **解绑**:清除绑定关系,记录解绑人/解绑时间(不覆盖绑定信息)
- 所有操作均记录到 `hotel_qr_bind_log` 表
### 7.3 批量删除
- 仅允许删除未绑定房间的二维码
- 已绑定的二维码拒绝删除,提示先解绑
- 逻辑删除(del_flag 写入主键ID)
### 7.4 批量下载ZIP
- 勾选多个二维码,后端生成 PNG 图片打包为 ZIP
- 文件名以短码命名:`{shortCode}.png`
- 单条下载:操作列"下载"按钮
### 7.5 扫码查询(开放接口)
- 免登录,根据 shortCode 查询二维码(忽略租户过滤)
- 签名校验:MD5(shortCode + tenantId + secret)
- 使用二维码所属租户 ID 设置租户上下文,执行后续查询
---
## 八、环境变量
| 变量名 | 必填 | 默认值 | 说明 |
|--------|------|--------|------|
| HOTEL_QR_SECRET | 否 | hotel_qr_default_secret | 二维码签名密钥(生产环境必须修改) |
---
## 九、后续扩展预留
| 扩展点 | 说明 |
|--------|------|
| 钉钉 H5 集成 | 通过 DingTalk SSO 获取员工身份,调用绑定/重绑接口 |
| 小程序 openid 绑定 | 在 `/hotel/open/` 下新增小程序专用接口 |
| 顾客点餐流程 | 扫码后进入点餐 H5/小程序页面 |
| 房间状态实时推送 | WebSocket 推送房间状态变更 |
---
## 十、给 AI 助手的指令
当协助开发酒店模块时,请:
1. **严格遵循规范**: 使用 `@RequiredArgsConstructor`、`RespInfo`、雪花算法ID、Composition API
2. **使用 AiCrudPage**: 列表页面必须使用 `AiCrudPage` 组件,禁止手动拼接搜索栏
3. **使用字典组件**: 状态/类型等字段必须使用 `DictTag` / `useDict()`,禁止硬编码
4. **SQL 写在 XML**: 查询类 SQL 必须写在 Mapper XML 中,禁止 Service 层用 LambdaQueryWrapper
5. **逻辑删除**: 所有业务表默认逻辑删除,`@TableLogic` 必须显式声明
6. **租户隔离**: 业务数据 `tenant_id=1`,查询自动追加租户过滤
7. **参考现有代码**: 查看 `forge-hotel` 模块中已有实现
**禁止**:
- 不使用 `@Autowired`(使用 `@RequiredArgsConstructor`)
- ❌ 不使用 `DataResult` / `ResponseUtil`(使用 `RespInfo`)
- ❌ 不使用 UUID / 自增ID(使用雪花算法)
- ❌ 不在 Service 层构建查询 SQL(写在 Mapper XML)
- 不手动拼接搜索栏(使用 AiCrudPage)
- ❌ 不硬编码字典值(使用 DictTag / useDict)
- ❌ 不设 `tenant_id=0`(必须为 1)
---
## 十一、文件路径
- 后端模块: `forge-server/forge-business/forge-hotel/`
- PC前端页面: `forge-admin-ui/src/views/hotel/`
- H5前端页面: `forge-h5-ui/src/pages/hotel/`
- API文件: `forge-admin-ui/src/api/hotel.js`
- 数据库迁移: `forge-server/db/migration/`
- 开发文档: `forge-server/forge-business/forge-hotel/酒店二维码模块开发文档.md`
---
**配置版本**: v2.0
**维护者**: QoderWork AI Assistant