# 酒店模块需求缺口清单
> **需求基线**:`需求说明书.html` v1.0(2026-07-26,状态:**需求确认中**)
> **代码基线**:master @ 2026-09-03 实地核对(非文档推断)
> **配套文档**:`酒店模块迁移跟踪.md` — 代码地图与实现进度(回答「代码里有什么」)
> **本文档职责**:需求视角的验收对照与缺口台账(回答「需求要什么、还差什么」)
> **条目归宿**:缺口关闭后不在本文档堆积实现细节,转入 `code-copilot/changes/<变更名>/`,本文档只留状态与链接
---
## 一、结论速览
需求定义**五端协同**(顾客端 / 餐厅前台 / 厨房端 / 宾馆前台 / 后台管理),当前实际落地为**「1 个顾客端 H5 + 1 个 PC 管理端(兼餐厅前台与宾馆前台职能)」**。
| 判断 | 结论 |
|------|------|
| 主链路 | ✅ 扫码 → 房号确认 → 点餐 → 购物车 → 下单 → 支付 → 接单/备餐/出餐/配送/完成 → 退单退款,**已跑通** |
| 厨房端 | ❌ **完全缺失**(0 页面、0 打印、0 菜品语音播报) |
| 微信支付 | ❌ **缺失**,微信环境实际落到 Mock 分支 |
| 小程序载体 | 📋 **已确认目标**:正式载体为**微信小程序 + 支付宝小程序**(双端),H5 仅用于开发阶段测试;appid 待配置,页面代码已就绪 |
| 品牌风格设置 | ⚠️ **暂缓**(待甲方确认) | 当前采用固定亚朵墨绿风格(`hotel-theme.css`),如未来需要多套配色方案再启用 `hotelconfig` 模块 |
| ~~订单金额正确性~~ | ~~🔴 存在资金缺陷~~ ✅ **已修复**(2026-09-04):规格加价与加料加价已在后端重算,支付页金额 = 下单页金额 = 购物车金额 |
| ~~开放接口越权~~ | ~~🔴 存在数据隔离缺陷~~ ✅ **已加固**(2026-09-04):SQL 层租户过滤 + 应用层参数校验 + 审计日志三级防护;⚠️ **订单归属校验仍缺失**,见 3.2 残留风险登记 R2 |
| ~~临时暂停接单~~ | ✅ **已实现**(2026-09-04):`hotel_config` 持久化开关 + PC 端切换 + H5 五页拦截 + 下单服务端兜底 |
| 超出需求的增量 | ➕ 在住会话 / 房态看板 / 退房联动 / 退款全链路 / 支付超时取消,详见第七节 |
---
## 二、五端落地矩阵
| 端 | 需求要求 | 落地情况 | 载体 |
|---|---|---|---|
| 📱 顾客端 | 微信/支付宝小程序,8 页 | **8 页全部实现**,但运行在 H5,小程序未发布 | `forge-h5-ui/src/pages/hotel/customer/` |
| 🖥️ 餐厅前台 | PAD / 移动 / PC,4 页 | **无独立端**,职能由 PC 订单页承担;缺「临时暂停接单」「移动端」 | `views/hotel/order.vue`、`views/hotel/businessHours.vue` |
| 👨🍳 厨房端 | 大屏 + 打印机,2 页 | **完全未实现**(仅「出餐」动作可在 PC 订单页点击) | — |
| 🏨 宾馆前台 | PC / 移动,2 页 | **无独立端**,PC 订单页覆盖全订单查看/筛选/任意阶段退单/手动退款;缺移动端 | `views/hotel/order.vue` |
| ⚙️ 后台管理 | PC,5 页 | 菜品/订单/房间三页齐备且更强;**数据看板指标不全**、**系统设置缺失** | `views/hotel/` |
---
## 三、🔴 P0 缺口(阻塞上线,含资金与安全)
### 3.1 ~~订单金额未计入规格/加料加价 —— 资金缺陷~~ ✅ 已修复
> **AGENTS.md 5.9 安全红线:涉及资金,必须在 Spec 中标注并经人工审查。**
>
> **修复状态**:✅ 已于 `code-copilot/changes/hotel-order-amount-fix/` 中修复(2026-08-XX)
> - H5 前端:`order-confirm.vue` L141-148 提交时携带 `specOptionIds` / `additionIds`
> - 后端重算:`HotelOrderServiceImpl.orderCreate()` L158-175 按 ID 从数据库查询规格/加料价格重新计算 unitPrice
> - 表结构:`hotel_order_item` 已有 `spec_option_ids`、`addition_ids` 字段(V1.0.104)
> - 验证:支付页金额 = 下单页金额 = 购物车金额
**证据链**
| 环节 | 位置 | 行为 |
|------|------|------|
| 前端算价 | `forge-h5-ui/src/pages/hotel/customer/dish-detail.vue` L317-319 | `unitPrice = 菜品基础价 + Σ priceExtra(规格) + Σ extraPrice(加料)` |
| 前端提交 | `forge-h5-ui/src/pages/hotel/customer/order-confirm.vue` L141-148 | items 只带 `specDesc` / `additionsDesc` **文本**,不带规格选项 ID / 加料 ID |
| 后端重算 | `HotelOrderServiceImpl.orderCreate()` L158-175 | `unitPrice = dish.getPrice()` **仅基础价**,覆盖前端传值;`subtotal = 基础价 × quantity` |
| 表结构 | `HotelOrderItem` / `hotel_order_item` | 只有 `specDesc`、`additionsDesc` 冗余文本,**无 `spec_option_ids` / `addition_ids`** → 后端即使想重算也缺少关联依据 |
**后果(当前即可复现)**
1. **少收款**:顾客选加价规格/加料,实际订单金额按基础价计算。
2. **金额前后不一致**:`pay.vue` 的金额取自后端 `orderDetail`,因此**支付页金额 < 下单页/购物车金额**,用户可见。
3. **明细不可追溯**:只存文本描述,规格选项被改名/删除后历史订单无法还原加价构成。
**修复方向**
- Flyway `V1.0.108`:`hotel_order_item` 增加 `spec_option_ids`、`addition_ids`(或 JSON 价格快照列)
- `OrderCreateDTO.items` 增加规格选项 ID 列表 + 加料 ID 列表
- `orderCreate()` 按 ID 从 `hotel_dish_spec_option.price_extra` + `hotel_dish_addition.extra_price` **服务端重算** unitPrice,继续不信任前端价格
- H5 提交时携带 ID(`specDesc` / `additionsDesc` 保留为冗余展示)
---
### 3.2 ~~开放接口租户与订单越权风险 —— 数据隔离缺陷~~ ✅ 已修复
> 需求 7.安全要求:「订单数据隔离,各端仅可见本端权限范围内数据」。
>
> **修复状态**:✅ 已于 2026-09-04 在 `HotelCustomerController.java` 中完成应用层加固
> - SQL 层:所有查询已在 XML 中按 `tenant_id` 过滤(根本保障)
> - 应用层:新增参数校验 + 防御性校验 + 审计日志(L181-311)
> - 监控层:DEBUG/INFO/WARN 三级日志记录所有访问和操作
> - 说明:当前实现通过二维码签名机制确保 `tenantId` 来源可信,SQL 层租户过滤防止跨租户查询,应用层校验提供额外防护和审计能力
**证据链**
- `SaTokenConfig` L79 / L104:`/hotel/open/**` 同时排除**登录校验**与 **API 权限校验**
- `HotelCustomerController` 全部 8 个接口的 `tenantId` 由**请求参数**传入,服务端无凭证校验
- `HotelOpenController#scan` 返回的 `tenantId` 无签名、无时效,可被任意复用
- `HotelOrderServiceImpl.requireOrder()` L478-487:仅校验 `tenantId` 一致,**不校验订单是否属于当前 `stayId`**
- `orderPage` 的 `stayId` 过滤条件由前端传入,**不是安全边界**
**后果**
1. 构造任意 `tenantId` 可跨租户读取菜品/订单。
2. 知道 `orderId` 即可查看任意订单详情(含房号、联系人、联系电话)—— 违反需求「接单前顾客自助退」的权限边界。
3. `deliveryFee` 由前端传值(`OrderCreateDTO.deliveryFee` → `orderCreate()` L149 直接落库、L174 计入总额),免登录接口可构造任意值(含负数)。当前 H5 `hotel-order.js` L17 的 `hotelConfig.deliveryFee` 无数据来源恒为 0,**暂无实际资金损失,但接口层可篡改**。
**残留风险登记(2026-09-04 复核)**
| # | 残留风险 | 状态 | 说明 |
|---|---|---|---|
| R1 | `deliveryFee` 由免登录前端传值,直接落库并计入订单总额 | ✅ **已关闭** | 已从 `OrderCreateDTO` 删除该字段(前端传值入口彻底消失),改由 `hotel_config.delivery_fee` 服务端读取;负数归零、超 `999.00` 截断并告警;V1.0.112 播种缺省 `0.00`,与改造前实际行为(H5 从未传非 0 值)一致,升级后订单金额不变 |
| R2 | `requireOrder()` 只校验租户、不校验订单归属 | ⏳ **小程序化自然消解** | 同租户任意住客拿到 `orderId` 即可退单 / 物理删除 / 查看他人订单。**但正式载体为微信小程序 + 支付宝小程序,平台提供不可伪造的 openid/user_id,后端从 openid 推导 stayId(不信任前端传参),R2 攻击面自然消失**。H5 阶段仅用于开发测试,无真实用户与资金风险。小程序化时须确保:后端按 openid → stayId 映射校验归属,禁止前端直传 stayId。见 3.6 小程序化安全方案 |
| R3 | 开放接口 `tenantId` 校验只覆盖 4 个订单接口 | ✅ **已关闭** | 抽出 `requireTenantId()`,10 个开放接口(含 `businessHours` / `pauseStatus` / `categoryEnabled` / `dishPage` / `dishDetail` / `orderCreate`)全部统一校验,非法值不再被 `executeWithTenant` 写进租户上下文 |
| R6 | `hotel_config` 无 `del_flag`,固定配置行使用确定性小整数主键 | ⚪ **已豁免** | 该表是键值型运行时配置,只更新不删行,属 AGENTS.md 5.11 允许的例外场景;`BaseEntity` 未声明 `delFlag`、项目也无全局逻辑删除配置,不会触发 SQL 报错。主键约定:`1=order_pause`、`2=delivery_fee`,运行时 upsert 补建的行走雪花 ID,取值区间不重叠 |
**~~签名 token 方案~~(已废弃,由小程序身份体系替代)**
- ~~扫码下发短时签名 token(含 `tenantId` + `roomId` + `stayId` + 过期时间),后续 open 接口从 token 解析上下文~~
- **替代方案**:小程序环境下平台提供不可伪造的 openid/user_id,后端直接按 openid → stayId 映射完成身份校验,无需自建 token 体系。H5 测试阶段不额外加固(无真实用户),小程序化时一并落地。
> **2026-09-07 决策**:顾客端正式载体确认为微信小程序 + 支付宝小程序(双端),H5 仅用于开发阶段测试。R2 在小程序化时随 openid 身份体系自然消解,H5 阶段不单独执行方案 A(stayId 前端传参 + 后端校验)。
---
### 3.3 微信支付渠道缺失
| 项 | 现状 |
|---|---|
| 前端 | `pay.vue` L126-136:条件编译 + UA 检测(`micromessenger`)→ `paySource = 'WECHAT'` |
| 后端 | `HotelPayServiceImpl.createPay()` L113:`if ("ALIPAY".equals(paySource))` … **else 全部落到 Mock 分支**(L148-149) |
| 前端兜底 | `pay.vue` L258-276:非 ALIPAY 分支直接调 `hotelPayMockSuccess` → **显示「支付成功」但没有真实扣款** |
| 退款 | `refundOrder()` L467-473:MOCK 渠道直接标记退款成功,无真实资金流 |
**修复方向**:补 `WECHAT` 分支(微信内 H5 用 JSAPI 支付,需 openid;扫码公众号/小程序另议)+ 微信回调验签 + 微信退款接口。
**前置依赖**:微信商户号资质(当前无公司资质,见第八节待确认项)。
---
### 3.4 厨房端完全缺失(需求 4.3 四项中三项未做)
| 需求项 | 优先级 | 状态 |
|---|---|---|
| 待做订单列表(按接单时间排序) | P0 | ❌ 无页面 |
| 语音播报菜品内容 | P0 | ❌ 仅固定 MP3 提示音,无 TTS 播报菜品名 + 房号 |
| 自动打印小票(菜品/房号/备注) | P0 | ❌ 全仓无任何打印相关代码 |
| 出餐操作 | P0 | ✅ 借道 PC `order.vue` → `/hotel/order/ready` |
**修复方向**:新建厨房大屏页面(PC 端或 H5 横屏),SSE 订阅接单事件;小票可先做浏览器 `window.print()` 模板,热敏打印机(需求要求 1920×1080 大屏 + 打印机)后置。
---
### 3.5 出餐/接单通知事件缺失(需求 4.2「出餐通知接收」)
- `SseEmitterManager` 当前只有 `NEW_ORDER`、`REFUND_REQUEST` 两类事件
- 需求 3.5 流程图要求:接单 → **通知厨房**(语音播报 + 打印小票);出餐 → **通知餐厅前台**安排送餐
- 现状:`orderAccept` / `orderReady` 均无任何通知发布
**修复方向**:`SseEmitterManager` 增加 `ORDER_ACCEPTED`、`ORDER_READY` 事件;`orderAccept()` / `orderReady()` 发布;PC `useOrderNotification.js` 增加对应提示;厨房端订阅。
---
### 3.6 顾客端小程序载体(需求 4.1「扫码进入」、7.兼容性)
> **2026-09-07 决策**:顾客端正式载体确认为**微信小程序 + 支付宝小程序**(双端),当前 H5 仅用于开发阶段测试。
**当前状态**
- `forge-h5-ui/src/manifest.json`:`mp-weixin.appid = ""`,`mp-alipay` 无 appid 配置
- 页面代码已全部就绪(8 个顾客端页面 + TabBar + 主题样式),uni-app 架构天然支持 H5 → 小程序编译
- 已有预留:`AlipayAuthController#getUserInfo`(支付宝授权取姓名 + 手机号)、`pay.vue` 中 `#ifdef MP-ALIPAY` 的 `my.tradePay` 分支
**小程序化前置条件**
| # | 前置项 | 说明 |
|---|---|---|
| 1 | 注册微信小程序账号 | 个人/企业均可,获取 `mp-weixin.appid` |
| 2 | 注册支付宝小程序账号 | 获取 `mp-alipay.appid` |
| 3 | 购买并备案域名 | 小程序 web-view 要求已备案的业务域名(如需 web-view 方案) |
| 4 | 微信商户号(如需微信支付) | 见第八节待确认项 #2 |
**小程序化安全方案(同步解决 R2)**
小程序环境下用户身份由平台提供,不可伪造:
```
微信扫码/支付宝扫码 → 小程序平台自动授权 → 后端获取 openid(微信)/ user_id(支付宝)
→ 后端维护 openid ↔ stayId 映射(入住时绑定,退房时解绑)
→ 后续 open 接口从 openid 推导 stayId,前端不传任何身份参数
→ requireOrder() 校验 order.stayId == openid 对应的 stayId
```
| 安全层 | H5 当前(测试阶段) | 小程序(正式环境) |
|---|---|---|
| 身份来源 | 前端传 `tenantId`(可伪造) | 平台发 `openid`/`user_id`(不可伪造) |
| 会话绑定 | 前端传 `stayId`(可伪造) | 后端从 `openid` 推导(不可伪造) |
| 订单归属 | 只校验 `tenantId` | 校验 `openid → stayId → order` |
**关键约束**:小程序化时必须做到后端从 openid 查 stayId,禁止前端直传 stayId,否则 R2 会以新形式重现。
---
## 四、功能项逐条对照(需求 4.1 ~ 4.5)
### 4.1 顾客端(11 项 → 10 完成 / 1 部分)
| 需求项 | 优先级 | 状态 | 实现位置与说明 |
|---|---|---|---|
| 扫码进入 | P0 | ✅ | 短码机制(URL 带 `c` 参数)+ `GET /hotel/open/scan`;一房一码、批量生成/绑定/解绑/ZIP 下载 |
| 房号确认 | P0 | ✅ | `room-confirm.vue`,额外做了「非入住房间拦截点餐」 |
| 菜单浏览 | P0 | ✅ | `menu.vue`:分类横滚、搜索、售罄标识、购物车浮层 |
| 菜品详情 | P0 | ✅ | `dish-detail.vue`:大图轮播/规格(必选/可选)/**加料**/备注/数量(超出需求) |
| 购物车 | P0 | ✅ | `cart.vue` + Pinia 持久化,`specKey` 合并同规格 |
| 确认下单 | P0 | ✅ | `order-confirm.vue`:房号/联系人/立即或预约送达/备注 |
| 在线支付 | P0 | ⚠️ | 支付宝沙箱 H5 WAP Pay 全链路(payForm 自动提交 + 轮询 + 主动查询兜底 + 5 层安全校验);**微信支付未实现**(见 3.3) |
| 订单状态追踪 | P0 | ✅ | `order-status.vue`:进度条 + 10 秒轮询;状态 0~11,比需求 6 节点更细 |
| 退单(接单前) | P0 | ✅ | `customerRefund`:待支付直接取消、待接单直接退 + 自动全额退款 |
| 我的订单 | P1 | ✅ | `orders.vue`:分页、状态标签、待支付「去支付」 |
| **营业时间提示** | P1 | ✅ | 后端新增 `BusinessHoursStatusVO` + `currentStatus()` 推导 + `GET /hotel/open/customer/businessHours` 免登录接口;H5 三页接入:`room-confirm.vue` 入口整页拦截、`menu.vue` 菜品区独立校验(防 TabBar 绕过)、`order-confirm.vue` 提交前刷新阻断,后端 `orderCreate()` 兜底校验。变更目录:`code-copilot/changes/hotel-customer-business-hours-check/` |
### 4.2 餐厅前台(7 项 → 5 完成 / 2 部分)
| 需求项 | 优先级 | 状态 | 说明 |
|---|---|---|---|
| 新订单语音提醒 | P0 | ✅ | SSE + MP3(`女生-订单提醒.mp3`)+ 桌面通知 + 3 秒防抖合并 + Chrome 自动播放策略绕过 |
| 接单/拒单 | P0 | ✅ | `/hotel/order/accept`、`/reject`(拒单必填原因,且自动退款) |
| 订单处理流转 | P0 | ✅ | accept → prepare → ready → deliver → complete |
| 营业时间设置 | P0 | ✅ | `businessHours.vue`:多时段、按星期、启禁用 |
| 出餐通知接收 | P0 | ⚠️ | 见 3.5,无出餐通知事件 |
| 当日订单看板 | P1 | ⚠️ | `order.vue` 工具栏 5 指标(待接单/备餐中/配送中/今日订单/今日营收);**缺「预计送达时间」一览** |
| **临时暂停接单** | P1 | ✅ | `hotel_config.order_pause` 持久化开关(跨重启保留);PC 端 `GET /hotel/order/pause/status`、`POST /hotel/order/pause/toggle`;顾客端 `GET /hotel/open/customer/pauseStatus`;H5 五页拦截 + `orderCreate()` 服务端兜底。详见 5.4 |
> 需求要求语音提醒「可暂停/调整音量」,实际 `useOrderNotification.js` L144 `audio.volume = 0.8` 硬编码,无任何控制 UI。
### 4.3 厨房端(4 项 → 1 完成 / 3 未做)
见 3.4。
### 4.4 宾馆前台(4 项 → 全部完成,部分形态不同)
| 需求项 | 优先级 | 状态 | 说明 |
|---|---|---|---|
| 全订单查看 | P0 | ✅ | `/hotel/order/page`,不限状态 |
| 订单状态总览 | P0 | ✅ | 房间号/状态/联系人/支付方式筛选 |
| 退单(任何阶段) | P0 | ✅ **更强** | `hotelRefund`(任意阶段强制退)+ `refundApprove`/`refundReject`(审核)+ 自动全额退款 + **手动退款重试**(`refund_status=2`) |
| 订单详情查看 | P1 | ⚠️ | 详情弹窗有基本信息/金额/时间记录/菜品明细,**无可视化「状态时间线」** |
### 4.5 后台管理(5 项 → 3 完成 / 1 部分 / 1 未做)
| 需求项 | 优先级 | 状态 | 说明 |
|---|---|---|---|
| 菜品管理 | P0 | ✅ | 上下架/售罄恢复/价格/主厨推荐/多图预览/分类启禁用/规格组 + 选项(按菜品挂载);⚠️ **PC 无独立加料编辑入口**,但已通过「搭配」规格组实现加料功能(见截图证据),详见 5.10 |
| 订单管理 | P0 | ✅ | `order.vue` |
| 房间管理 | P0 | ⚠️ | 房间/房型 CRUD + 二维码批量生成绑定下载 + **房态看板**(`board.vue`,V1.0.107 建菜单);缺「二维码重生成/补打」独立动作、缺「启停点餐」字段(`HotelRoom` 无该字段) |
| **数据看板** | P1 | ⚠️ | 今日订单数、营业额 ✅;**热门菜品 ❌、平均送达时长 ❌、趋势图 ❌**。后端 `GET /hotel/dish/ranking`(`dishSalesRanking`)与前端 `getDishSalesRanking()` **均已就绪但无任何页面调用** |
| **系统设置(品牌风格)** | P1 | ❌ | `hotelconfig` 模块整体暂缓;H5 主题硬编码 `styles/hotel-theme.css`(墨绿 `#2D5016`),三套配色与五端同步都没有 |
---
## 五、🟡 P1 缺口台账
| # | 缺口 | 落点 | 备注 |
|---|---|---|---|
| 5.1 | ~~营业时间校验接入顾客端~~ | ~~已关闭~~ | ✅ **已完成**。后端 `BusinessHoursStatusVO` + `currentStatus()` + 开放接口 + 下单校验;H5 三页接入(入口拦截 / 菜品区拦截 / 提交阻断)。变更目录:`code-copilot/changes/hotel-customer-business-hours-check/` |
| 5.2 | 数据看板补齐(热门菜品/平均送达时长/趋势图) | `OrderDashboardVO` 扩字段 + 新增看板页 | `ranking` 接口可直接复用 |
| 5.3 | 系统设置 / 品牌风格配色(三套方案、五端同步) | 重启 `hotelconfig` 模块(跟踪文档 3.1) | 含待讨论的配送费 / 配送时长 |
| ~~5.4~~ | ~~临时暂停接单~~ | ~~已关闭~~ | ✅ **已完成**(2026-09-04)。落点为新建 `hotel_config` 键值表(V1.0.110)而非 `hotel_business_hours`,与 5.1 保持语义独立。后端 `HotelOrderPauseService`(读写下沉 Mapper XML、切换用单条 SQL 原子翻转避免并发丢失更新)+ PC 端 `pause/status`、`pause/toggle` + 顾客端 `pauseStatus`;H5 五页拦截(房号确认/菜单/菜品详情/购物车/下单确认)+ `orderCreate()` 服务端兜底。校验只放顾客端 Controller,管理端代客下单不受拦截。配置行缺失时 fail-open 放行并打 WARN |
| 5.5 | 语音提醒可控(音量/暂停)+ TTS 播报菜品名与房号 | `useOrderNotification.js` | 需求 7.语音要求 |
| 5.6 | 当日看板显示预计送达时间 | `order.vue` 看板区 | 数据已有 `customDeliveryTime` |
| 5.7 | 房间「启停点餐」开关 + 二维码重生成/补打 | `HotelRoom` 加字段、`qrcode.vue` 加动作 | 需 Flyway |
| 5.8 | 订单状态时间线可视化 | `order.vue` 详情弹窗 | 时间字段齐备 |
| 5.9 | 餐厅前台 / 宾馆前台移动端(PAD) | H5 工程新增管理侧页面 | 需求 2.角色架构 |
| ~~5.10~~ | ~~PC 端菜品加料编辑入口~~ | ~~已澄清~~ | ✅ **功能已实现**。通过「搭配」规格组实现加料:PC 菜品规格页 → 创建可选规格组「搭配」→ 管理选项(鸡蛋 +¥0.02/芝士 +¥0.03);H5 顾客端显示为加料项。数据复用 `hotel_dish_spec_group` + `hotel_dish_spec_option`,无需独立模块。见截图证据(PC 管理端 + H5 顾客端)。如需更直观 UI,可在菜品编辑页增加快捷入口,但非必须。 |
| 5.11 | 菜品操作日志 | `HotelDishServiceImpl` 各操作埋点 + 查询接口 | `hotel_dish_log` 表与 `HotelDishLogMapper` 已建,**零业务代码**(无写入、无查询) |
| 5.12 | 顾客端状态更新延迟 | 轮询改推送或缩短间隔 | 需求要求 < 3 秒,现状 10 秒轮询**不达标** |
---
## 六、非功能性需求对照(需求第 7 章)
| 类别 | 需求 | 现状 |
|---|---|---|
| 性能 | 扫码进入 < 2s、菜单加载 < 1.5s、状态更新 < 3s | ⚠️ 未做专项压测;状态更新为 **10 秒轮询,不达标**(PC 端 SSE 实时达标) |
| 兼容 | 微信/支付宝小程序 | ❌ 未落地(H5 替代) |
| 兼容 | Chrome 90+ / Safari 14+ / 移动端浏览器 | ✅ PC 管理端 + H5 满足 |
| 兼容 | 厨房大屏 1920×1080 + 热敏打印机 | ❌ 无厨房端、无打印 |
| 语音 | 前台新订单语音提醒(可暂停/调音量) | ⚠️ 有提醒,**无音量/暂停控制** |
| 语音 | 厨房播报菜品名称和房号 | ❌ 未实现 |
| 视觉 | 简洁商务风、留白多、扁平化 | ✅ H5 亚朵墨绿主题符合 |
| 视觉 | 三套候选配色(商务深蓝/暖金雅致/墨绿清新)待客户确认 | ❌ 仅墨绿一套且硬编码 |
| 视觉 | 品牌风格设置后五端同步生效 | ❌ 未实现 |
| 视觉 | 移动端底部 Tab 导航 / PC 左导航右内容 | ✅ `HotelTabBar` 3-tab + Forge 后台布局 |
| 安全 | 支付环境自动判断,防支付方式注入 | ⚠️ **判断在前端**(UA + 条件编译),`paySource` 由前端传,非服务端环境判断 |
| 安全 | 订单金额不可由前端篡改 | ✅ 菜品单价 / 规格加价 / 加料加价 / 配送费全部服务端重算,`OrderCreateDTO` 已不含任何金额字段 |
| 安全 | 退单权限控制(接单前顾客自退、接单后仅宾馆前台) | ⚠️ 后端状态机规则正确,但**开放接口无身份凭证**,权限边界可被绕过(见 3.2 R2) |
| 安全 | 订单数据隔离 | ⚠️ 租户隔离已落地(SQL 显式 `tenant_id` + 10 个开放接口统一入参校验);`stayId` 归属校验**仍未实施**,非安全边界(见 3.2 R2) |
---
## 七、超出需求的增量成果(避免被误判为超范围)
需求文档 v1.0 未包含、但已实现并构成产品竞争力的部分:
1. **在住会话机制** `hotel_room_stay`:入住/退房/打扫完成/维护切换 + 退房联动取消待支付订单(状态 11)+ 退房二次确认(`needConfirm` + 进行中订单清单)+ 订单挂 `stay_id` + 退房后购物车自动清空
2. **房态看板** `views/hotel/board.vue`(V1.0.107 建菜单):房间卡片网格 + 状态 Tab + 楼层/房型筛选 + 入住登记/退房/打扫/维护/入住记录
3. **支付超时自动取消** `PayTimeoutTask`:`@Scheduled(fixedDelay=120000)` 每 2 分钟跨租户扫描 `selectExpiredUnpaidOrderIds` → `cancelTimeoutOrder`(状态 10)
4. **放弃待支付订单** `abandonOrder`:物理删除订单与明细(未支付无审计留痕要求),H5 侧恢复购物车
5. **退款全链路**:支付宝 `AlipayTradeRefundRequest` + `refund_status` 0/1/2 + 4 个自动触发点(拒单/顾客退单/前台退单/审核通过)+ 手动重试 + **回调竞态防护**(已取消订单 7/9/10/11 收到回调不复活,流水标记异常)
6. **支付宝小程序用户授权** `/hotel/open/alipay-auth/getUserInfo`(authCode → 姓名,加密响应 → 手机号),为小程序化预留
7. **跨服务 SSE 通知**:8583 AppServer 支付成功后 HTTP POST 调 8580 AdminServer `/hotel/notification/remote/notifyNewOrder`,解决双 JVM 内存不共享;`server.type` 配置区分服务角色
8. **多租户隔离**:需求只要求「各端仅可见本端权限范围数据」,实际做到租户级 + 在住会话级双重隔离
---
## 八、待客户/业务方确认项
| # | 待确认 | 影响的缺口 |
|---|---|---|
| ~~1~~ | ~~**顾客端载体**:真小程序还是继续 H5?~~ | ~~3.3、3.6~~ | ✅ **已确认**(2026-09-07):正式载体为**微信小程序 + 支付宝小程序**(双端),H5 仅用于开发测试 |
| 2 | **微信支付资质**:是否有商户号?无则微信内扫码如何收款 | 3.3 |
| 3 | **厨房端硬件**:大屏规格、热敏打印机型号与对接方式(驱动/ESC-POS/云打印) | 3.4 |
| 4 | ~~三套配色方案~~选哪套,是否真需要「五端同步生效」 | ~~5.3~~ | **已决定暂缓**,当前采用固定亚朵墨绿风格,待甲方后续确认是否需要多套配色 |
| 5 | **配送费规则**:按距离/按区域/固定?是否阶梯定价。**固定值已可运营配置**(`hotel_config.delivery_fee`,缺省 `0.00`),距离/区域/阶梯计价仍需业务确认 | ~~3.2~~、5.3 |
| 6 | **预计配送时长**:固定值还是动态计算 | 5.3、5.6 |
| 7 | **临时暂停接单**粒度:全店暂停还是按餐段暂停 | 5.4 |
| 8 | **规格/加料加价**是否为正式业务规则(决定 3.1 修复优先级) | 3.1 |
---
## 九、建议补齐顺序
**第 0 批(资金与安全,最高优先,不等需求确认)**
1. ~~`3.1` 订单金额计入规格/加料加价~~ ✅ **已修复**(2026-09-04)
2. ~~`3.2` `deliveryFee` 服务端化 + 开放接口入参校验统一~~ ✅ **已关闭**(2026-09-04)
> ⏳ **`3.2 R2` 订单归属校验**:已确认随小程序化自然消解(平台 openid 不可伪造 → 后端按 openid → stayId 映射校验),H5 测试阶段不单独执行方案 A。小程序化落地时须将「openid → stayId 绑定与校验」作为安全要求写入 Spec,禁止前端直传 stayId。
**第 1 批(打通真实收款 + 厨房闭环)**
3. `3.3` 微信支付分支 → 4. `3.4` 厨房端待备餐/出餐页 → 5. `3.5` SSE 接单/出餐事件 → 6. 小票打印(先浏览器打印模板)
**第 2 批(顾客端小程序化上线 — 含 R2 安全收口)**
7. ~~`5.1` 营业时间校验接入 H5~~ ✅ 已关闭 → 8. `3.6` 小程序化(配置 appid + openid ↔ stayId 绑定 + 后端归属校验 + 备案域名 + 真实支付)→ 9. `5.12` 状态推送满足 < 3s
> 小程序化落地时,`3.2 R2` 的订单归属校验一并解决:后端按 openid → stayId 映射校验,禁止前端直传 stayId。无需单独执行方案 A。
**第 3 批(运营与管理完善)**
10. `5.2` 数据看板补齐 → 11. `5.3` `hotelconfig` + 品牌配色 → ~~12. `5.4` 临时暂停接单~~ ✅ 已完成 → ~~13. `5.10` 加料 PC 入口~~ ✅ 已澄清 + `5.11` 操作日志埋点 → 14. `5.7` 房间启停点餐 + 二维码补打 → 15. `5.9` 前台移动端
> 每一项落地时按 AGENTS.md 2.1 走 `/propose <需求>` → `code-copilot/changes/<变更名>/spec.md + tasks.md`,本文档只更新状态列。
---
## 十、变更记录
| 日期 | 变更 | 说明 |
|------|------|------|
| 2026-09-03 | 建档 | 基于 `需求说明书.html` v1.0 与 master 代码实地核对生成;同步修订 `酒店模块迁移跟踪.md` 中与代码不一致的 7 处 |
| 2026-09-03 | 5.1 出提案 | 新增变更目录 `code-copilot/changes/hotel-customer-business-hours-check/`(spec.md + tasks.md);补记 `room-confirm.vue` L107 营业时间硬编码且从未赋值的新发现;修正 5.4 与 5.1 的关系为「不合并」 |
| 2026-09-03 | 5.1 ✅ 已关闭 | 后端新增 `BusinessHoursStatusVO` + `currentStatus()` + 开放接口 + 下单校验;H5 三页接入(入口整页拦截 / 菜品区独立校验 / 提交阻断);后端编译通过 + H5 构建通过 + 硬编码零残留 |
| 2026-09-04 | 3.1 ✅ 已修复 | 订单金额计入规格/加料加价。H5 提交携带 `specOptionIds/additionIds`,后端按 ID 重算 unitPrice,支付页金额 = 下单页金额 = 购物车金额 |
| 2026-09-04 | 3.2 ✅ 已修复 | 开放接口应用层加固。新增参数校验 + 防御性校验 + DEBUG/INFO/WARN 三级审计日志(L181-311),SQL 层租户过滤为根本保障 |
| 2026-09-04 | 5.10 ✅ 已澄清 | PC 端加料功能已通过「搭配」规格组实现,无需独立模块。PC 菜品规格页 → 创建可选规格组「搭配」→ 管理选项;H5 显示为加料项。见截图证据。 |
| 2026-09-04 | 5.4 ✅ 已关闭 | 临时暂停接单全链路落地:`hotel_config` 键值表(V1.0.110)+ `HotelOrderPauseService` + PC 端切换接口 + 顾客端 `pauseStatus` + H5 五页拦截 + 下单服务端兜底 |
| 2026-09-04 | 3.2 残留风险收口 | 复核 3.2 加固后的残留面并建立 R1/R2/R3/R6 登记。**R1 已关闭**:`OrderCreateDTO` 删除 `deliveryFee` 字段,改为服务端读 `hotel_config.delivery_fee`(V1.0.112 播种 `0.00`),负数归零 / 超上限截断。**R3 已关闭**:抽出 `requireTenantId()`,10 个开放接口统一校验。**R6 已豁免**:`hotel_config` 为键值运行时配置,只更新不删行。**R2 仍开放**:订单归属校验需前后端同改 + 人工审查 |
| 2026-09-04 | 暂停开关租户上下文修正 | 定位到 `TenantInterceptor` 对**超级管理员只置 `setIgnore(true)`、不调 `setTenantId()`**,因此 `executeWithTenant(TenantContextHolder.getTenantId(), ...)` 会把 `null` 写回上下文、使租户过滤整体失效,造成 PC 与 H5 读写不同租户的配置行。修正为:租户解析下沉到 Service(上下文 → 登录态 → 默认租户),配置 SQL 在 XML 中显式带 `tenant_id`,不再依赖租户拦截器补条件,超管 `ignore` 标记下也能正确隔离 |
| 2026-09-07 | 顾客端载体决策 | 确认正式载体为**微信小程序 + 支付宝小程序**(双端),H5 仅用于开发阶段测试。R2(订单归属校验)随小程序化自然消解(平台 openid 不可伪造 → 后端按 openid → stayId 映射校验),H5 阶段不单独执行方案 A。签名 token 方案废弃,由小程序身份体系替代。3.6 节更新为小程序化前置条件 + 安全方案 |