# 酒店模块需求缺口清单 > **需求基线**:`需求说明书.html` v1.0(2026-07-26,状态:**需求确认中**) > **代码基线**:master @ 2026-09-03 实地核对(非文档推断) > **本文档职责**:需求视角的验收对照与缺口台账(回答「需求要什么、还差什么」) > **条目归宿**:缺口关闭后不在本文档堆积实现细节,转入 `code-copilot/changes/<变更名>/`,本文档只留状态与链接 > > **配套文档**(四份均在 `forge-server/forge-business/forge-hotel/` **同一目录**): > - `酒店模块迁移跟踪.md` — 代码地图与实现进度(回答「代码里有什么」) > - `酒店二维码模块开发文档.md` — **二维码与开放接口实现权威**(4 张表全字段 / 17 个开放接口全清单 / URL 格式与签名 / 双端识别与两层拦截 / 上线硬门槛) > - `酒店订单支付系统开发文档.md` — **支付实现权威**(支付环境支持矩阵 / `wap.pay` vs `trade.create` 协议差异 / SQL 与时序图 / `ALIPAY_MP` 方案代码 / Mock 资金红线修复代码)—— 2026-09-07 已从仓库根目录移入本目录 > > 本文档引用上述三份时一律用**指针**(章节号),不复制实现正文。 --- ## 一、结论速览 需求定义**五端协同**(顾客端 / 餐厅前台 / 厨房端 / 宾馆前台 / 后台管理),当前实际落地为**「1 个顾客端 H5 + 1 个 PC 管理端 + 1 个餐厅前台页面 + 餐厅 PAD 移动端 + 宾馆前台移动端」**。 | 判断 | 结论 | |------|------| | 主链路 | ✅ 扫码 → 房号确认 → 点餐 → 购物车 → 下单 → 支付 → 接单/备餐/出餐/配送/完成 → 退单退款,**已跑通** | | 厨房端 | ⚠️ **基础已有**(厨房订单页:日期筛选+分组展示+统计+详情+出餐;缺语音播报+小票打印) | | 微信支付 | ⏸️ **暂缓**(2026-09-07):微信小程序不上架,首发只做支付宝小程序。后端 `resolvePayChannel` 对 `WECHAT`/`WECHAT_MP` **显式抛 `BusinessException` 阻断**(不再静默降级为 MOCK),资质到位后再实现 | | ~~Mock 支付免登录暴露~~ | ~~🔴 资金红线~~ ✅ **已收口**(2026-09-07):`hotel.pay.mock-enabled` 开关(生产默认 `false`、dev 覆盖 `true`)+ `mockSuccess` 首行拦截 + `paySource` 改必传 + `mockOrReject` 单一收口点消除两处静默降级 + 退款渠道显式判定 + 手机号日志脱敏。⚠️ **变更 Spec 待补、人工审查待执行**,见 3.3 | | 小程序载体 | 📋 **已确认目标**:正式载体为**微信小程序 + 支付宝小程序**(双端),H5 仅用于开发阶段测试;**首发支付宝单端**。二维码方案已定为**自研普通链接码 + 双平台各自配规则**(不调平台二维码生成 API),依赖面已核实收敛,见 3.6 | | 员工端载体 | ✅ **已明确不属于小程序**:员工登录 H5 后扫码分配房间号,功能已完成。`scan-bind.vue` / `qr-scanner.vue` 用 `#ifdef H5` 排除出小程序构建 | | 品牌风格设置 | ⚠️ **暂缓**(待甲方确认) | 当前采用固定亚朵墨绿风格(`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 页 | ✅ **已完成**:`restaurantOrder.vue`(三 Tab:全部订单/订单看板/营业时间,看板含已拒单列,支持全日期筛选);✅ **PAD 移动端** `staff-order.vue`(三 Tab 同 PC,无日期筛选固定当天,10 秒轮询+看板自适应列宽+首页快捷入口) | `views/hotel/restaurantOrder.vue`、`views/hotel/order.vue`、`views/hotel/businessHours.vue`、`forge-h5-ui/src/pages/hotel/staff-order.vue` | | 👨‍🍳 厨房端 | 大屏 + 打印机,2 页 | ⚠️ **部分完成**:新增 `kitchenOrder.vue`(日期筛选+分组展示+统计+详情弹窗+出餐操作);缺语音播报、自动打印 | `views/hotel/kitchenOrder.vue` | | 🏨 宾馆前台 | PC / 移动,2 页 | **无独立端**,PC 订单页覆盖全订单查看/筛选/任意阶段退单/手动退款;✅ **移动端** `hotel-order.vue`(订单总览+搜索筛选)+ `hotel-order-refund.vue`(退单管理/订单详情+时间线)(2026-09-09) | `views/hotel/order.vue`、`forge-h5-ui/src/pages/hotel/hotel-order.vue`、`forge-h5-ui/src/pages/hotel/hotel-order-refund.vue` | | ⚙️ 后台管理 | PC,5 页 | 菜品/订单/房间三页齐备且更强;**数据看板指标不全**、**系统设置缺失** | `views/hotel/` | --- ## 三、🔴 P0 缺口(阻塞上线,含资金与安全) ### 3.1 ~~订单金额未计入规格/加料加价 —— 资金缺陷~~ ✅ 已修复 > **AGENTS.md 5.9 安全红线:涉及资金,必须在 Spec 中标注并经人工审查。** **验收结论**:规格加价与加料加价已由**服务端重算**,支付页金额 = 下单页金额 = 购物车金额。前端只传 ID,价格一律以数据库为准,不信任前端传值;`specDesc` / `additionsDesc` 保留为冗余展示文本。 **已核实的实现位置(2026-09-07 实地复核)** | 环节 | 位置 | 行为 | |------|------|------| | 表结构 | `db/migration/V1.0.109__add_hotel_order_item_spec_addition_ids.sql` | `hotel_order_item` 增 `spec_option_ids` / `addition_ids`(`varchar(500)`,逗号分隔,带 `information_schema` 防重复保护) | | 实体 | `HotelOrderItem.java` L45 / L48 | `specOptionIds` / `additionIds` | | 前端提交 | `forge-h5-ui/src/pages/hotel/customer/order-confirm.vue` L248-249 | items 携带 `specOptionIds` / `additionIds`(由 `specs[].optionId` / `additions[].id` join 而来) | | 后端重算 | `HotelOrderServiceImpl.orderCreate()` L198-216 | 按 ID 查 `hotel_dish_spec_option.price_extra` + `hotel_dish_addition.extra_price` 累加进 `unitPrice`,`subtotal = 重算后单价 × quantity` | | 前端算价(展示用) | `dish-detail.vue` L317-319 | `unitPrice = 菜品基础价 + Σ priceExtra + Σ extraPrice`,仅用于页面展示,不作为落库依据 | > ⚠️ **文档纠错(2026-09-07)**:本节原记录的迁移版本(`V1.0.104` / `V1.0.108`)与代码行号(`order-confirm.vue` L141-148、`orderCreate()` L158-175)均与实际不符,引用的 `code-copilot/changes/hotel-order-amount-fix/` 目录**根本不存在**;原「后果(当前即可复现)」「修复方向」两段描述的是修复前状态,与本节标题的 ✅ 已修复直接矛盾。上述内容已按实地复核结果更正或删除。 --- ### 3.2 ~~开放接口租户与订单越权风险 —— 数据隔离缺陷~~ ✅ 已修复 > 需求 7.安全要求:「订单数据隔离,各端仅可见本端权限范围内数据」。 **验收结论(三级加固,2026-09-04 完成于 `HotelCustomerController.java`)** | 层 | 措施 | |---|---| | SQL 层 | 所有查询在 Mapper XML 中按 `tenant_id` 过滤(根本保障) | | 应用层 | 新增参数校验 + 防御性校验 + 审计日志(L181-311);`tenantId` 来源由二维码签名机制保证可信 | | 监控层 | DEBUG/INFO/WARN 三级日志记录所有访问和操作 | > 📌 **接口级风险的证据链唯一权威**:`/hotel/open/**` 免登录白名单范围(`SaTokenConfig` L79/L104)、`mockSuccess` 免付款、`paySource` 默认 MOCK、`tenantId` 可伪造、手机号脱敏 —— 见 `酒店二维码模块开发文档.md` **3.2 / 3.3**(17 个开放接口全清单 + 安全红线);资金类修复代码见 `酒店订单支付系统开发文档.md` **10.1**。本节按文首职责约定只留验收状态与残留风险台账,不复制证据链。 **残留风险登记(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,取值区间不重叠 | > **2026-09-07 决策**:顾客端正式载体确认为微信小程序 + 支付宝小程序(双端),H5 仅用于开发阶段测试。R2 在小程序化时随平台 openid/user_id 身份体系自然消解;**原自建「短时签名 token」方案已废弃**(无需再建 token 体系),H5 阶段也不单独执行方案 A(stayId 前端传参 + 后端校验)。落地方式见 3.6.6。 --- ### 3.3 ~~微信支付渠道缺失 + Mock 支付免登录暴露(资金红线)~~ ✅ Mock 红线已收口(微信渠道仍阻断) > **2026-09-07 决策**:微信小程序**暂缓上架**(微信支付资质未开通),顾客端首发只做**支付宝小程序**。 > 下表中已修复项与微信是否上架**无关**,是免登录接口本身的漏洞,已于第 0 批全部落地。 | 项 | 修复前现状 | 落地处置(2026-09-07 已完成) | |---|---|---| | ✅ Mock 接口免登录 | `HotelPayController#mockSuccess` 位于 `/hotel/open/**` 白名单内(`SaTokenConfig` L79/L104 整体放行),**无任何开关保护**。任何人枚举 `orderId` + `tenantId` 发一个 POST 即可把订单标记为已支付 | 新增 `HotelPayConfig`(`hotel/pay/config/`)读 `hotel.pay.mock-enabled`:`application.yml` 默认 `false`(可被 `FORGE_HOTEL_PAY_MOCK_ENABLED` 覆盖)、`application-dev.yml` 覆盖 `true`;`mockSuccess` **方法首行**校验开关,关闭时 WARN 日志 + 抛 `BusinessException("模拟支付已禁用")`;`@PostConstruct` 启动即打印开关状态,开启时用 WARN 级资金安全告警 | | ✅ `paySource` 默认 MOCK | `createPay()` `@RequestParam(required = false, defaultValue = "MOCK")`,不传即走 Mock | 去掉 `required = false` 与 `defaultValue` 改为必传;缺参由 Spring 直接拒绝,不再静默走 Mock | | ✅ 前端 Mock 兜底 | `pay.vue`:非 ALIPAY 分支直接调 `hotelPayMockSuccess` → **显示「支付成功」但没有真实扣款** | **拆分而非删除**(保 H5 dev 调试链路):改判后端返回的 `payChannel`,`else if (payChannel === 'MOCK')` 保留原调试逻辑,新增 `else` 分支 `uni.showModal` 显式报错、**禁止再兜底调 mockSuccess**。生产环境后端永不返回 `payChannel='MOCK'`,故新 `else` 只在生产命中 | | ✅ 渠道静默降级 | `resolvePayChannel()` 有**两处**静默降级到 MOCK(`paySource` 为空 / 未知渠道),任何非法值都能免付款 | 抽出 `mockOrReject(reason)` **单一收口点**:`WECHAT`/`WECHAT_MP` → 抛「微信支付暂未开通,请使用支付宝支付或到前台付款」;空值 → 抛「支付渠道不能为空」;未知值 → 抛「不支持的支付渠道: X」;`MOCK`/`H5` → 抛「模拟支付已禁用」。仅 `mock-enabled=true` 时才 `return "MOCK"`,全仓不再有裸 `return "MOCK"` 兜底 | | ✅ `createPay` 渠道误判
(改造中新发现) | `createPay()` 用**原始 `paySource`** 判 `"ALIPAY".equals(paySource)`,而 `resolvePayChannel` 已把 `ALIPAY_MP` 归一为 `ALIPAY` → `ALIPAY_MP` 漏进 else 被静默置为 MOCK | 渠道解析**前置**到方法开头,后续分支一律用解析后的 `payChannel` 判断;`payParams` 显式回填 `payChannel` 供前端分支使用 | | ✅ 退款渠道误判
(改造中新发现,潜在资损) | `refundOrder()` 用 `!"ALIPAY".equals(payChannel)` 兜底判 MOCK → **未来接入微信后,微信订单退款会被误判为「无资金流」直接标记成功,造成真实资损** | 改为显式判定 `"MOCK".equals(paidChannel)` / `PAY_SOURCE_MOCK.equals(order.getPaySource())` / `payLog == null` 三种无资金流情形;其余非 ALIPAY 渠道 `log.error` + 抛「该支付渠道暂不支持在线退款,请联系前台人工处理」 | | ✅ 手机号日志脱敏 | `AlipayAuthController#getUserInfo` 直接打印 `vo.getPhone()`;`AlipayAuthServiceImpl#getPhoneNumber` 把手机号密文报文整包 `log.warn` | 前者改调新增私有方法 `maskPhone()`(保留前 3 后 4、中间 `****`,长度不足 7 位统一返回 `****`);后者改为只记 `responseLength`(AGENTS.md 5.9) | **微信渠道修复方向(资质到位后再做)**:补 `WECHAT_MP` 分支(小程序 JSAPI 支付,需 openid)+ 微信回调验签 + 微信退款接口。`resolvePayChannel` 已为该渠道预留显式阻断路径,接入时把对应的 `mockOrReject` 调用换成真实实现即可,**禁止再引入任何形式的兜底降级**。 **前置依赖**:微信商户号 + **非个人主体**已认证小程序(个人主体不支持扫普通链接二维码打开小程序,见 3.6.3 #1)。 **验证结果(2026-09-07)**:后端全 reactor `mvn compile` BUILD SUCCESS;`forge-hotel` 单模块 97 个源文件编译通过;H5 `pnpm build:h5` Build complete;6 份 yml `hotel` 块核对一致(生产 `mock-enabled:false` / dev 与 example `true`);**dev 环境行为零变化**(二维码 URL 拼接结果不变、Mock 支付链路照旧可用)。 > ⚠️ **AGENTS.md 5.9 合规状态:Spec 已补写,人工审查待执行**(2026-09-07 更新)。本节属资金类变更,**代码先于 Spec 落地**,属流程倒置。已补写 `code-copilot/changes/hotel-pay-mock-hardening/spec.md`(按 `templates/spec.md` 13 节结构,第 8.1 节标注三个资金风险点、第 10.1 节登记与原方案的三处差异、第 11 节回填**实际**改动文件)+ `tasks.md`(8 个 Task 全标已执行)。第 13 节 HARD-GATE 的「确认时间」「确认人」**留空,只能由需求方本人手填**,签署前状态停在 `review`,不得归档。未补 `test-spec.md`(本次是安全收口而非新功能,验证口径见 `酒店订单支付系统开发文档.md` 10.1;`forge-hotel` 无测试基础设施,本次未建单元测试)。 #### 3.3.1 改造中的两个观察(已更正) | # | 发现 | 证据 | 归属 | |---|---|---|---| | ① | **【已决策接受】支付宝沙箱密钥明文入库**。`application-dev.yml` 的 `hotel.alipay.private-key` / `alipay-public-key` 为明文全文,该文件**被 git 跟踪**,且已随 commit `65508d9` 推送到 `origin/master`。
**用户决策**:git 是**内网私有仓库**(192.168.8.4),不在外网;密钥是沙箱密钥;**后期如有必要,会直接不提交生产的密钥**。
**处置**:本变更关闭(`hotel-alipay-secret-externalize` 不再推进)。后续生产密钥不入库由用户把控 | `git ls-files` / `git log -S` / `git branch -r --contains` | 用户已决策接受,变更关闭 | | ② | **【已撤销】生产 profile 启动崩溃 — 判断错误,当前不会崩**。
**原判断**:`AlipayConfig` 5 个 `@Value` 无默认值 + 生产 `application.yml` 无 `hotel.alipay` 块 → 生产启动崩溃(P0)。
**更正**:`application.yml` L42-43 `spring.profiles.active: dev`,且 docker-compose.yml / .env.example **无任何 profile 覆盖**,项目也**没有** `application-prod.yml`。因此当前启动(包括 docker 部署)都会加载 `application-dev.yml`,`hotel.alipay.*` 全部有值,**不会崩**。
**我错在哪**:把 `application.yml` 当成「生产专用配置」,忽略了它 L43 默认激活 dev profile,`application-dev.yml` 会被合并加载 | 文件直读 + `Select-String` + docker 配置核实 | 原判断错误,已撤销 | > ⚠️ `hotel-alipay-secret-externalize` 变更已关闭(用户已决策接受,后期生产密钥不提交)。原 P0 判断错误,已撤销。 --- ### 3.4 厨房端(需求 4.3 四项中一项未做) | 需求项 | 优先级 | 状态 | |---|---|---| | 待做订单列表(按接单时间排序) | P0 | ⚠️ **基础已有**:`kitchenOrder.vue` 日期筛选(默认当天)+ 按状态分组展示(待备餐/制作中/已完成)+ 订单详情弹窗 | | 语音播报菜品内容 | P0 | ✅ **已完成**:TTS 语音播报(`SpeechSynthesis`)播报菜品名 + 房号,厨房端 `kitchenOrder.vue` 集成 SSE 自动刷新 + 新订单语音提醒 | | 自动打印小票(菜品/房号/备注) | P0 | ❌ 全仓无任何打印相关代码 | | 出餐操作 | P0 | ✅ 借道 PC `order.vue` → `/hotel/order/ready` | **修复方向**:小票打印(先浏览器 `window.print()` 模板),需确认热敏打印机型号与对接方式(见八章 #3)。 --- ### 3.5 ~~出餐/接单通知事件缺失(需求 4.2「出餐通知接收」)~~ ✅ 已完成 - `SseEmitterManager` 现支持 `NEW_ORDER`、`ORDER_READY`、`REFUND_REQUEST`、`CUSTOMER_ORDER_STATUS` 四类事件 - `orderAccept()` / `orderReady()` 等方法均调用 `notifyCustomerStatus()` 发布顾客端状态变更通知 - PC 端 `useOrderNotification.js` 监听 `NEW_ORDER` / `ORDER_READY` / `REFUND_REQUEST`(刷新 + 提示音 + 桌面通知) - 厨房端 `kitchenOrder.vue` 集成 SSE,新订单自动刷新 + TTS 语音播报 - 顾客端 `order-status.vue` 监听 `CUSTOMER_ORDER_STATUS`(SSE 实时推送 + 30 秒兜底轮询) - `HotelCustomerController` 新增免登录 SSE 端点 `GET /hotel/open/customer/sse/subscribe` --- ### 3.6 顾客端小程序载体(需求 4.1「扫码进入」、7.兼容性) > **2026-09-07 决策汇总** > - 顾客端正式载体:**微信小程序 + 支付宝小程序**(双端),当前 H5 仅用于开发阶段测试 > - **首发只做支付宝小程序**,微信小程序暂缓(支付资质未开通,见 3.3) > - **员工端不属于小程序**:员工登录 H5 后扫码分配房间号,功能已完成,不需改造 > - 二维码**自研生成**,不调用 `wxacode.getUnlimited` / `alipay.open.app.qrcode.create` > - 所有改造**不得影响 H5 开发测试**(手法约束见 3.6.7) **当前状态** - `forge-h5-ui/src/manifest.json`:`mp-weixin.appid = ""`;`mp-alipay` 节点 L61-64 **已有 `appid: ""` 占位 + 说明注释**(`git diff` 核实在位),值待真实 appid 到位后一次性填入。⚠️ 空串下只能在支付宝 IDE 以「测试号」本地预览,无法上传体验版/发布正式版,也无法配「普通链接二维码」规则 - `forge-h5-ui/.env.mp-alipay`:已创建(绝对 HTTPS 域名占位 + 白名单登记说明),✅ **当前已生效** —— `package.json` L9/L25 的 `dev:mp-alipay` / `build:mp-alipay` **均已带 `--mode mp-alipay`**(`git diff` 核实在位)。⚠️ 该 `--mode` **不可省**:uni-app / Vite 只按 mode 加载 `.env.[mode]`、**不会按平台加载 `.env.[platform]`**(实测 `@dcloudio` 包内无任何 `loadEnv` 调用),去掉后该文件静默失效、构建会退回 `.env.production` 的相对路径导致请求全挂。另注:mode 文件需**自包含全部变量**,不会与 `.env.production` 合并 - 页面代码已全部就绪(8 个顾客端页面 + TabBar + 主题样式),uni-app 架构天然支持 H5 → 小程序编译 - 已有预留:`AlipayAuthController#getUserInfo`(支付宝授权取姓名 + 手机号)、`pay.vue` 中 `#ifdef MP-ALIPAY` 的 `my.tradePay` 分支、`room-confirm.vue` L376-396 授权成功/取消/失败处理 - 支付宝沙箱配置已齐备:`application-dev.yml` L98-111(app-id `9021000166699753`、`gateway-url` 沙箱网关、`sandbox: true`) #### 3.6.1 二维码双端自动识别方案(已确认可行) **验收结论**:自研普通链接二维码,**不调用** `wxacode.getUnlimited` / `alipay.open.app.qrcode.create`。同一个 https URL 在微信与支付宝后台**各配一条前缀规则**,两平台规则表互相独立,因此「微信扫 → 微信小程序、支付宝扫 → 支付宝小程序」由平台机制天然实现,无需任何判断逻辑。满足需求 7.「只允许微信/支付宝扫码入口」的载体要求。 > 📌 **实现细节唯一权威**:两套 URL 格式(员工 `/h5/` vs 顾客 `/r/`)、hash 路由劫持陷阱、`c`/`t`/`s` 参数与签名不得更改、URL 不落库的零迁移成本、前端 `resolveScanParams()` 三端取参 —— 见 `酒店二维码模块开发文档.md` **4.2 ~ 4.5**。本节按文档职责约定(见文首)只留验收状态,不复制实现细节。 #### 3.6.2 排斥非微信/支付宝扫码(必须两层,只做第一层是假的) | 层 | 机制 | 效果 | |---|---|---| | **入口层** | 微信/支付宝扫一扫**命中已配规则时不会向服务器发起 HTTP 请求**,由平台客户端直接拉起小程序;其它扫码器(系统相机、QQ、浏览器)会真实请求 `/r/` | `/r/` 对所有真实 HTTP 请求**返回 403**,只放行平台校验用 `.txt` 文件;不含任何业务数据与跳转 | | **接口层** | 顾客端开放接口要求携带小程序平台身份(支付宝 `user_id` / 微信 `openid`),由平台 `getAuthCode` → 后端换取,前端无法伪造 | 非小程序环境拿不到身份,`/hotel/open/customer/**` 一律 401 | > ⚠️ 入口层**不构成强制**:截获 URL 参数者仍可直接用 curl 调免登录后端接口。两层必须同时落地,只有接口层才是真正的安全边界(且同时解决 R2)。 > > 📌 **口径已统一(2026-09-07)**:本节原写「`/r/` 返回静态提示页」,与 `酒店二维码模块开发文档.md` **4.6 / 4.5** 的「返回 403」矛盾。以二维码文档为准,统一为 **403**(放行 `.txt` 校验文件)。 #### 3.6.3 小程序化前置条件(7 项,资质由公司统一申请中) | # | 前置项 | 说明 | 当前 | |---|---|---|---| | 1 | **非个人主体**小程序 | 微信「扫普通链接二维码打开小程序」**仅开放给已认证的非个人主体**,个人主体完全不支持,无技术手段绕过;支付宝侧同样要求企业主体 | ⏳ 申请中 | | 2 | 小程序认证 | 微信/支付宝各自完成企业认证 | ⏳ | | 3 | 小程序备案 | 工信部 2023.9 起强制,未备案不能发布线上版本 | ⏳ | | 4 | 域名 ICP 备案 | 二维码 URL 域名必须已备案 | ⏳ | | 5 | HTTPS 且不能带端口 | 当前 dev 为 `http://192.168.10.4:3001`(`application-dev.yml` L97),三项均不满足 | ❌ | | 6 | 校验文件可上传 | 平台要求把校验文件放到域名对应目录,需服务器可写 | ⏳ | | 7 | 已发布线上版本 | 规则生效前小程序必须有线上版本 | ⏳ | > 资质链条:营业执照 → 小程序认证 → 小程序备案 → 域名备案 → HTTPS → 发布线上版 → 配规则。**真机扫码跳转在链条走完前无法验证**,但代码侧可全部提前准备好。 > ⚠️ **沙箱限制(影响验收方式)**:支付宝沙箱 App 的扫一扫**不是通用扫码器**,普通 URL 二维码不解析,因此沙箱期无法真机验证扫码跳转,只能用 IDE「模拟扫码」验证参数解析链路。 > > 📌 模拟扫码的具体操作步骤、微信侧对应方式、`s` 签名的 dev 取值与调试接口建议,见 `酒店二维码模块开发文档.md` **4.7「开发期替代验证」**。 #### 3.6.4 已核实的依赖面(比预想小得多) **验收结论**:改造范围已收敛,**顾客 8 页页面级零改造**,风险集中在全局基础设施而非业务页面。 | 判断 | 结论 | |---|---| | 顾客 8 页 | ✅ **零改造**:不用 `AiIcon`、不用 CSS mask、不用 `v-loading`,图标全是 emoji(`⏸️` `⚠️` `↩️` `✓`)+ 原生 `` | | 需改组件 | ✅ `HotelTabBar.vue` **已改完**(2026-09-07):`#ifdef H5` 保留原 `WebkitMask` 实现与 `iconStyle()` 函数(函数也一并包进条件编译,小程序包里不留死代码),`#ifndef H5` 改用 ``。SVG 内 `stroke="currentColor"` 在 `image` 中解析为黑色、无法随激活态换色,故激活态改用 `opacity: 1 / 0.45` 区分,文字颜色照旧跟随激活态。H5 产物已验证仍含 `WebkitMask` | | 需改基础设施 | ⚠️ `utils/http`、`utils/crypto`、`store` persist、`utils/file`(**不可裁剪**,见 3.6.7) | | 员工专属页 | ✅ **已在 `pages.json` 用 `// #ifdef H5` 整块排除**(2026-09-07;`pages.json` 支持 `//` 注释,实测 H5 产物仍含 `pages-hotel-scan-bind` / `pages-hotel-qr-scanner` 两个 chunk)。页面被裁掉后不参与编译,因此**两个 `.vue` 内部无需再加 `#ifdef`**,其独占的 `AiIcon`/`AiButton`/`AiResult`/`html5-qrcode` 自动不进小程序包 | | ✅ 首页入口已同步 | `pages/index/index.vue` 的「扫码绑定」快捷入口(L245-254)、`SCAN_BIND_PERM` 常量(L274-276)、`menuItems` 的 `prefix` 逻辑(L282-288)、`isRegisteredH5Route` 路由白名单(L412-427)、`handleShortcut` 跳转共 **5 处引用均已包进 `#ifdef H5`**(2026-09-07,`git diff` + 文件直读核实在位)。效果:**H5 dev 零变化**(两页在 H5 构建中仍注册),小程序构建时首页不渲染该入口、不会 `navigateTo` 到未注册页面 | | 重 DOM 组件 | 🔴 **原判「一行代码不用改」已被实测推翻**(2026-09-07 `pnpm build:mp-alipay`):`AiAvatarCropper.vue` 的 `` 直接报 `[vite:vue] not supported: Teleport` 并**在 2.4s 内终止整个 mp 构建**。原因:`unplugin-vue-components` 会为任何被引用到的组件生成注册代码,与「顾客 8 页是否引用」无关,页面裁剪挡不住它。→ **列为第 1 批首位新增阻塞项**,需 `#ifdef H5` 包裹 `Teleport` 并为 mp 提供普通 `` 分支 | | 员工接口鉴权 | ✅ 已核实安全:`/hotel/qrcode/bind|unbind`、`/hotel/room/list-all` 均**不在** `/hotel/open/**` 白名单,需登录 | > 📌 顾客 8 页的逐页依赖清单(哪个页面用了哪个组件/工具/store)见 `酒店二维码模块开发文档.md` **5.2**。 #### 3.6.5 支付宝小程序支付(🔴 当前 100% 不可用) **验收结论**:需求 4.1「在线支付」在支付宝小程序载体下**不通过**。后端 `ALIPAY` 渠道只返 `payForm`、**不返 `tradeNo`**,前端 `my.tradePay({ tradeNO: undefined })` 必然 fail。 > ⚠️ **口径修正(勿误判为免付款)**:支付宝小程序**不是免付款下单,而是支付 100% 不可用**。落到 `pay.vue` Mock 兜底分支的是 `paySource='WECHAT'`(微信已暂缓,真实用户不会触发);但 `mockSuccess` 接口本身的免登录暴露与此**无关**,属独立资金红线,见 3.3。 **处置**:新增 `ALIPAY_MP` 渠道(`alipay.trade.create` → `tradeNo`),`buyer_id` 由已有的 `AlipayAuthController#getUserInfo` 提供,链路完整可串。列入第 3 批(见第九节)。 > 📌 协议差异原理(`pageExecute()` 本地签名不调网关)、后端代码、沙箱验证限制、`return-url`/`notify-url` 问题与两份 yml 同步要求,见 `酒店订单支付系统开发文档.md` **7.1 / 7.3**。 #### 3.6.6 小程序化安全方案(同步解决 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 会以新形式重现。 > 支付宝单端即可闭环:`my.getAuthCode` → `AlipayAuthController#getUserInfo` → `userId` → 落库绑定 `stayId`,无需等微信资质。 #### 3.6.7 改造手法约束:**不得影响 H5 开发测试** 所有改动必须归入以下三类之一,**禁止直接替换现有实现**: | 手法 | 适用 | 原理 | |---|---|---| | ① 条件编译 | 前端 | `#ifdef H5` 保留原实现,`#ifndef H5` 放小程序实现,两套代码物理隔离 | | ② 配置开关 | 后端 | `application.yml` 默认严格,`application-dev.yml` 覆盖为宽松,dev 行为完全不变 | | ③ 纯增量 | 双端 | 新增接口/分支/文件,不动任何现有路径与签名 | **小程序运行时无下列 H5 API**,命中即启动崩溃:`window` / `document` / `navigator` / `localStorage` / `sessionStorage` / `XMLHttpRequest` / `fetch` / `URL.createObjectURL` / `globalThis.crypto.getRandomValues`。WXSS 不支持 `-webkit-mask` + 本地 SVG 组合、不支持 `html`/`body` 选择器(用 `page`)、tabBar `iconPath` 不支持 SVG。 > ⚠️ **全局启动链路不可裁剪**:即使只发布 hotel 模块,`App.vue`、`main.js`、`store/index.js`、`utils/http`、`utils/crypto` 仍是小程序启动必经之路。**先修基础设施,再动业务代码**。 > > ⚠️ `pages.json` 当前启动页是 `pages/login/index`;小程序侧首页须换成 `pages/hotel/customer/room-confirm`,同样用条件编译处理,**不改 H5 启动页**。tabBar 整块用 `#ifdef H5` 排除(顾客端用的是自绘 `HotelTabBar`,不依赖原生 tabBar)。 > > ⚠️ `key-exchange.js` 的 `generateSessionKey` 依赖 `globalThis.crypto.getRandomValues` + `btoa`;`crypto-config.js` 的 `includePaths` 为空时全量加密,所有请求都会触发崩溃。可参照 `crypto-config.js` L92-98 已有的 `uni.request` 范式改造。 > > ⚠️ `pinia-plugin-persistedstate` v3.2.0 默认 `storage = localStorage`,必须接 uni storage 适配器(适配器内部判平台,H5 路径不变)。 --- ## 四、功能项逐条对照(需求 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`:进度条 + **SSE 实时推送** + 30 秒兜底轮询;状态 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 项 → 全部完成) | 需求项 | 优先级 | 状态 | 说明 | |---|---|---|---| | 新订单语音提醒 | P0 | ✅ | SSE + MP3(`女生-订单提醒.mp3`)+ 桌面通知 + 3 秒防抖合并 + Chrome 自动播放策略绕过 | | 接单/拒单 | P0 | ✅ | `/hotel/order/accept`、`/reject`(拒单必填原因,且自动退款) | | 订单处理流转 | P0 | ✅ | accept → prepare → ready → deliver → complete | | 营业时间设置 | P0 | ✅ | `businessHours.vue`:多时段、按星期、启禁用 | | 出餐通知接收 | P0 | ✅ | `SseEmitterManager.notifyOrderReady()` 发布 `ORDER_READY` 事件;PC `useOrderNotification.js` 监听并提示 | | 当日订单看板 | P1 | ✅ | `restaurantOrder.vue` 三 Tab 布局:全部订单(统计卡片+分组列表+拒单记录)+ 订单看板(6 列卡片含已拒单列,参考 board.vue 样式)+ 营业时间(时段管理+暂停接单);支持全日期筛选,V1.0.117 建菜单;✅ **订单详情弹窗**(小票样式,全部订单和看板卡片均可点击打开,底部按状态显示操作按钮:接单/拒单/配送/完成);✅ **PAD 移动端** `staff-order.vue`(2026-09-09,三 Tab 同 PC 端,无日期筛选固定当天,10 秒轮询+看板自适应列宽+首页快捷入口) | | **临时暂停接单** | 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 项 → 2 完成 / 1 基础已有 / 1 未做) 见 3.4。 ### 4.4 宾馆前台(4 项 → 全部完成) | 需求项 | 优先级 | 状态 | 说明 | |---|---|---|---| | 全订单查看 | P0 | ✅ | `/hotel/order/page`,不限状态 | | 订单状态总览 | P0 | ✅ | 房间号/状态/联系人/支付方式筛选 | | 退单(任何阶段) | P0 | ✅ **更强** | `hotelRefund`(任意阶段强制退)+ `refundApprove`/`refundReject`(审核)+ 自动全额退款 + **手动退款重试**(`refund_status=2`) | | 订单详情查看 | P1 | ✅ | PC 端详情弹窗有基本信息/金额/时间记录/菜品明细;✅ **移动端** `hotel-order-refund.vue` 详情模式含完整订单信息卡片+菜品明细+状态时间线(2026-09-09) | ### 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 | ~~数据看板补齐(热门菜品/平均送达时长/趋势图)~~ | ~~已关闭~~ | ✅ **已完成**(V1.0.116)。新增独立看板页 `dashboard.vue`(KPI 卡片 + ECharts 趋势图 + 热门菜品 Top5 + 餐段分布);后端 `HotelDashboardController` + `DashboardVO`(TodayKpi/DailyTrend/HotDish/MealDistribution)+ Mapper XML 4 条 SQL(环比/趋势/热门菜/餐段统计);Flyway 脚本 `V1.0.116__add_hotel_dashboard_menu.sql` 建菜单 + 按钮权限。平均送达时长 = `AVG(TIMESTAMPDIFF(MINUTE, accept_time, complete_time))`,仅统计已完成订单;热门菜品按 ISO 周(周一起始)统计销量 Top5;餐段分布从 `hotel_business_hours` 表匹配启用时段。 | 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 | ~~语音提醒可控(音量/暂停)~~ | ~~已关闭~~ | ✅ **需求取消**。用户确认现有 MP3 固定音量提醒已够用,无需增加音量调节滑块和暂停按钮控制 UI。 | 5.6 | 当日看板显示预计送达时间 | `order.vue` 看板区 | 数据已有 `customDeliveryTime` | | 5.7 | ~~房间「启停点餐」开关 + 二维码重生成/补打~~ | ~~已关闭~~ | ✅ **需求取消**。用户确认现有批量生成/绑定/下载 ZIP 功能已够用,无需单条重新生成;房间管理的一键状态设置(空闲/入住/打扫/维护)已满足运营需求,无需额外「启停点餐」字段。 | 5.8 | ~~订单状态时间线可视化~~ | ~~已关闭~~ | ✅ **已完成**。`order.vue` / `restaurantOrder.vue` / `kitchenOrder.vue` 详情弹窗均使用 Naive UI `NTimeline` 组件展示订单流转时间轴(下单→接单→备餐→出餐→配送→完成),每节点显示时间、操作人、状态图标。 | 5.9 | ~~餐厅前台 / 宾馆前台移动端(PAD)~~ | ~~已关闭~~ | ✅ **已完成**(2026-09-09)。餐厅 PAD:`staff-order.vue`(三 Tab:全部订单/订单看板/营业时间,10 秒轮询+看板自适应列宽+首页快捷入口+返回键 switchTab 回首页)。宾馆前台移动端:`hotel-order.vue`(订单总览+统计卡片+搜索筛选)+ `hotel-order-refund.vue`(退单管理/订单详情+状态时间线+同意/驳回退单)。后端零改动,全部复用现有 `HotelOrderController` 接口。H5 API 层新增 13 个餐厅员工接口 + 4 个酒店前台接口 | | ~~5.10~~ | ~~PC 端菜品加料编辑入口~~ | ~~已澄清~~ | ✅ **功能已实现**。通过「搭配」规格组实现加料:PC 菜品规格页 → 创建可选规格组「搭配」→ 管理选项(鸡蛋 +¥0.02/芝士 +¥0.03);H5 顾客端显示为加料项。数据复用 `hotel_dish_spec_group` + `hotel_dish_spec_option`,无需独立模块。见截图证据(PC 管理端 + H5 顾客端)。如需更直观 UI,可在菜品编辑页增加快捷入口,但非必须。 | | 5.11 | ~~菜品操作日志~~ | ~~已关闭~~ | ✅ **已完成**(2026-09-09)。后端 `HotelDishLog` 实体新增 `operatorId/operatorName` 字段 + ServiceImpl 各操作方法调用 `saveLog()` 记录日志(CREATE/EDIT/DELETE/ON_SALE/OFF_SHELF/SOLD_OUT/RESTORE)+ Controller 新增 `GET /hotel/dish/logs?dishId=xxx&pageNum=1&pageSize=20` 接口 + Flyway 脚本 `V1.0.119__add_hotel_dish_log_operator_fields.sql`;前端 `dish.vue` 操作列增加“操作日志”按钮 + NModal 弹窗展示日志列表(操作类型标签 + 操作人 + 时间 + 备注),API 层新增 `getDishLogPage()`。 | ~~5.12~~ | ~~顾客端状态更新延迟~~ | ~~已关闭~~ | ✅ **已完成**(2026-09-10)。后端 `SseEmitterManager.notifyCustomerOrderStatus()` 广播 `CUSTOMER_ORDER_STATUS` 事件;`HotelOrderServiceImpl` 10 处状态变更方法调用 `notifyCustomerStatus()`;`HotelCustomerController` 新增免登录 SSE 端点 `GET /hotel/open/customer/sse/subscribe`;H5 `order-status.vue` 用 `EventSource` 监听实时推送 + 30 秒兜底轮询(SSE 断开时生效)。满足 < 3s 需求 | --- ## 六、非功能性需求对照(需求第 7 章) | 类别 | 需求 | 现状 | |---|---|---| | 性能 | 扫码进入 < 2s、菜单加载 < 1.5s、状态更新 < 3s | ⚠️ 未做专项压测;状态更新 ✅ **顾客端 SSE 实时推送达标**(PC 端 SSE + 顾客端 SSE + 30s 兜底轮询) | | 兼容 | 微信/支付宝小程序 | ⏳ **第 2 批已落地、第 1 批未开始**:首发支付宝小程序(微信暂缓);二维码用自研普通链接码 + 双平台各自配规则;7 项资质门槛申请中(见 3.6.3)。已完成:`hotel.qr.path` 配置化、`resolveScanParams()` 三端取参、`pages.json` 员工两页条件编译裁剪、`HotelTabBar` 图标 `#ifdef H5` 双分支(见 3.6.4)。🔴 实测 `pnpm build:mp-alipay` **仍在 2.4s 内失败**于 `AiAvatarCropper.vue` 的 ``,且第 1 批基础设施(axios / crypto / persist / `global:window`)一项未动,故小程序**当前仍不可构建** | | 兼容 | Chrome 90+ / Safari 14+ / 移动端浏览器 | ✅ PC 管理端 + H5 满足 | | 兼容 | 厨房大屏 1920×1080 + 热敏打印机 | ❌ 无厨房端、无打印 | | 语音 | 前台新订单语音提醒(可暂停/调音量) | ⚠️ 有提醒,**无音量/暂停控制** | | 语音 | 厨房播报菜品名称和房号 | ✅ **已完成**:`kitchenOrder.vue` 集成 `SpeechSynthesis` TTS 播报菜品名 + 房号 | | 视觉 | 简洁商务风、留白多、扁平化 | ✅ H5 亚朵墨绿主题符合 | | 视觉 | 三套候选配色(商务深蓝/暖金雅致/墨绿清新)待客户确认 | ❌ 仅墨绿一套且硬编码 | | 视觉 | 品牌风格设置后五端同步生效 | ❌ 未实现 | | 视觉 | 移动端底部 Tab 导航 / PC 左导航右内容 | ✅ `HotelTabBar` 3-tab + Forge 后台布局 | | 安全 | 支付环境自动判断,防支付方式注入 | ⚠️ **部分达标**(2026-09-07):环境判断仍在**前端**(UA + 条件编译)、`paySource` 仍由前端传,非服务端环境判断;但**注入面已收口** —— `paySource` 改必传、`resolvePayChannel` 用 `mockOrReject` 单一收口点对所有非白名单渠道显式抛异常、`mockSuccess` 受 `hotel.pay.mock-enabled` 开关保护(生产默认 `false`)。伪造 `paySource` 已无法免付款,最多触发一次业务异常。见 3.3 | | 安全 | 只允许微信/支付宝扫码入口 | ❌ **未实现**:需入口层(`/r/` 对真实 HTTP 请求返回 **403**,只放行 `.txt` 校验文件)+ 接口层(平台身份校验,**真正的安全边界**)两层同时落地,见 3.6.2 | | 安全 | 订单金额不可由前端篡改 | ✅ 菜品单价 / 规格加价 / 加料加价 / 配送费全部服务端重算,`OrderCreateDTO` 已不含任何金额字段 | | 安全 | 退单权限控制(接单前顾客自退、接单后仅宾馆前台) | ⚠️ 后端状态机规则正确,但**开放接口无身份凭证**,权限边界可被绕过(见 3.2 R2);支付宝小程序化时随 `user_id → stayId` 绑定一并收口(见 3.6.6) | | 安全 | 订单数据隔离 | ⚠️ 租户隔离已落地(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~~ | ✅ **已确认**(2026-09-07):微信支付未开通,**微信小程序暂缓上架**,首发只做支付宝小程序(沙箱测试);后端保留 `#ifdef MP-WEIXIN` 占位代码 + WECHAT 渠道显式阻断 | | 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 | | 9 | ~~**域名与主体资质**:备案域名、非个人主体认证、小程序备案~~ | ~~3.6.3~~ | ⏳ **公司已开始统一申请**(2026-09-07)。资质链条未走完前真机扫码跳转无法验证,开发期用支付宝 IDE「模拟扫码」验证参数解析链路 | | ~~10~~ | ~~**`/r/` 路径下放不放网页**~~ | ~~3.6.2~~ | ✅ **已定**(2026-09-07):**不放提示页**,`/r/` 对所有真实 HTTP 请求返回 **403**,只放行平台校验用 `.txt` 文件。理由:微信/支付宝命中规则时根本不发 HTTP 请求,403 不影响正常用户,而提示页反而可能被当成可用入口。口径与 `酒店二维码模块开发文档.md` **4.6** 一致 | --- ## 九、建议补齐顺序 > **2026-09-07 重排依据**:微信小程序暂缓上架 + 域名/主体资质申请中 + 员工端继续走 H5 + 改造不得影响 H5 开发测试。 **第 0 批(🔴 资金与安全红线,最高优先,不等资质、不等需求确认)—— ✅ 代码已全部落地(2026-09-07),⚠️ Spec 与人工审查未闭环** 1. ~~`3.1` 订单金额计入规格/加料加价~~ ✅ **已修复**(2026-09-04) 2. ~~`3.2` `deliveryFee` 服务端化 + 开放接口入参校验统一~~ ✅ **已关闭**(2026-09-04) 3. ~~`3.3` **Mock 支付收口**~~ ✅ **已完成**(2026-09-07,实现形态与原计划有 2 处出入,详见 3.3): - ✅ `hotel.pay.mock-enabled` 开关(新增 `HotelPayConfig`;`application.yml` 默认 `false`,6 份 yml 同步)+ `mockSuccess` 首行拦截 + `createPay` 渠道解析前置 - ✅ `createPay` 的 `paySource` 去掉 `defaultValue="MOCK"` 改必传 - ✅ WECHAT / WECHAT_MP 渠道显式抛 `BusinessException`;`resolvePayChannel` 抽 `mockOrReject` 单一收口点,**消除原有两处静默降级**(原计划只记录了一处) - ⚠️ `pay.vue` else MOCK 分支 **拆分而非删除**(原计划为「删除」):删除会破坏 dev H5 微信内置浏览器调试,改为 `else if (payChannel === 'MOCK')` 保留原逻辑 + 新 `else` 显式 `showModal` 报错 - ✅ `AlipayAuthController` 手机号日志脱敏(新增 `maskPhone()`)+ `AlipayAuthServiceImpl` 密文报文改只记 `responseLength` - ➕ 超出原计划新修复:`refundOrder` 退款渠道显式判定(原「非 ALIPAY 即 MOCK」兜底会在接入微信后造成**真实资损**)+ 两份生产 `application.yml` 补 `hotel` 块(原缺块且 `@Value` 无默认值 → 生产 profile 启动即崩) > 变更名:`hotel-pay-mock-hardening`。⚠️ **涉及资金,必须经人工审查**(AGENTS.md 5.9)—— 当前**代码先行、Spec 未补**,属合规缺口,需补写 `spec.md`(第 8 节资金风险标注 + 第 13 节 HARD-GATE 确认记录)后才算闭环。 > ⏳ **`3.2 R2` 订单归属校验**:已确认随小程序化自然消解(平台 openid/user_id 不可伪造 → 后端按映射校验),H5 测试阶段不单独执行方案 A。支付宝单端即可闭环,无需等微信资质(见第 3 批 #18)。 **第 1 批(支付宝小程序全局地基,不可裁剪,3~5 天)—— ❌ 未开始** > 🔴 **2026-09-07 实测新增首位阻塞项(必须最先做)**:`AiAvatarCropper.vue` 的 `` 使 `pnpm build:mp-alipay` 在 2.4s 内直接失败(`[vite:vue] not supported: Teleport`)。需 `#ifdef H5` 包裹 `Teleport` + 为 mp 提供普通 `` 分支,否则后续任何 mp 验证都无法进行。详见 3.6.4「重 DOM 组件」行。 4. uni storage 适配器 → `app.js` / `auth.js` / `hotel-order.js` 三个 persist 5. axios → `uni.request`(保留 `interceptors.js` 加解密 + token 逻辑) 6. `key-exchange.js` L96 随机数与 Base64(参照 `crypto-config.js` L92-98 已有的 `uni.request` 范式) 7. `vite.config.js` `define:{global:'window'}` 按平台条件化 8. `utils/file.js` 图片直连(`getFileDownloadUrl` 返回绝对 https) 9. `directives/loading.js` 的 `v-loading` 注册加 `#ifdef H5` **第 2 批(二维码 URL 格式 + 构建裁剪,2 天)—— ✅ 已完成(2026-09-07),全部 6 项均在位(`git diff` 核实)** 10. ✅ `QR_BASE_PATH` 配置化 → `hotel.qr.path`(dev: `/#/pages/hotel/scan-bind`;prod: `/r/`)。`QrCodeUtils.buildQrCodeUrl` **新增带 `basePath` 的重载**、旧签名委托保留(纯增量,零调用方改动);`HotelQrConstants.QR_BASE_PATH` 降级为「配置缺省时的回退值」;`HotelQrCodeServiceImpl` 新增 `qrBasePath` 字段并改调新重载 11. ✅ `room-confirm.vue` 新增 `resolveScanParams()`:优先级显式 `c`/`s` > 微信 `q` > 支付宝 `qrCode`,`decodeURIComponent` 带 try/catch 容错,正则 `[?&]c=([^&]+)` 与 `scan-bind.vue` 的 `parseQrUrl` 同口径,对 hash 与非 hash URL 均成立 12. ✅ `pages.json` 条件编译裁剪:`scan-bind` + `qr-scanner` 两页整块包进 `// #ifdef H5`(实测 H5 产物仍含两个 chunk)。⏳ **tabBar 整块排除与小程序侧启动页切换未做**(属第 1 批地基范畴,见 3.6.7) 13. ✅ **实现方式已修正 + 已落地**:不需要在 `scan-bind.vue` / `qr-scanner.vue` **内部**加 `#ifdef H5` —— 页面被 `pages.json` 裁掉后根本不参与编译。真正需要同步保护的是 `pages/index/index.vue` 的 5 处入口引用(快捷菜单项 / `SCAN_BIND_PERM` / `menuItems` 的 `prefix` / `isRegisteredH5Route` / `handleShortcut`),**均已包进 `#ifdef H5`**(`git diff` 核实在位)。H5 dev 零变化,小程序构建时首页不渲染该入口。见 3.6.4「首页入口已同步」行 14. ✅ `HotelTabBar.vue` 图标 CSS mask → ``:`#ifdef H5` 保留原 `WebkitMask` 实现与 `iconStyle()`,`#ifndef H5` 走 `` + `opacity` 区分激活态 15. ✅ **已完成**:`.env.mp-alipay` 已创建**且已生效**(`package.json` L9/L25 的 `--mode mp-alipay` 在位;uni-app 不按平台加载 env,该 `--mode` 不可省,见 3.6「当前状态」);`manifest.json` L61-64 的 `mp-alipay.appid` **占位在位、值为空串**,待资质填入。另注:原计划写的沙箱 appid `9021000166699753` 是**支付宝开放平台沙箱应用 ID**,与小程序 appid 不属同一体系,**不能直接填入** **第 3 批(支付宝小程序支付,2~3 天,沙箱 IDE 内可完整验证)** 16. 后端 `ALIPAY_MP` 渠道:`AlipayTradeCreateRequest`(`alipay.trade.create`)→ `tradeNo` 17. `buyer_id` 串联:`my.getAuthCode` → `getUserInfo` → `userId` → `createPay` 18. `userId ↔ stayId` 落库绑定 + 后端归属校验(**顺带收口 R2**) 19. `pay.vue` L205 分支拆清:MP-ALIPAY 只走 `tradeNo`,不碰 `payForm` 20. `return-url` / `notify-url` 调整(natapp 免费域名回调不稳;两份 yml 同步) > 第 1~3 批建议变更名:`hotel-alipay-mp-adaptation`。**涉及资金与身份,必须经人工审查**。 **第 4 批(上线前,依赖外部资质,可延后)** 21. 备案域名 + HTTPS 证书 + 支付宝后台普通链接二维码规则 + 校验文件上传 22. `/r/` **403 拦截**(仅放行 `.txt` 校验文件)+ 顾客端接口平台身份校验(3.6.2 两层拦截) 23. 正式 appid + 商户号 → `sandbox: false` + `gateway-url` 切线上 24. 真实桌牌物料生成(**资质到位前一张都别印**) 25.(微信恢复时)微信后台规则 + JSAPI 支付 + openid 绑定 + `3.3` WECHAT_MP 渠道 **第 5 批(厨房闭环)** 26. ~~`3.4` 厨房端待备餐/出餐页~~ ✅ 已完成(`kitchenOrder.vue` + TTS 语音播报) → 27. ~~`3.5` SSE 接单/出餐事件~~ ✅ 已完成(`ORDER_READY` + `CUSTOMER_ORDER_STATUS`) → 28. 小票打印(先浏览器打印模板,待确认打印机型号) **第 6 批(运营与管理完善)** 29. ~~`5.12` 状态推送满足 < 3s~~ ✅ 已完成 → ~~`5.2` 数据看板补齐~~ ✅ 已完成 → 31. `5.3` `hotelconfig` + 品牌配色 → ~~`5.4` 临时暂停接单~~ ✅ 已完成 → ~~`5.10` 加料 PC 入口~~ ✅ 已澄清 + ~~`5.11` 操作日志埋点~~ ✅ 已完成 → ~~`5.7` 房间启停点餐 + 二维码补打~~ ✅ 需求取消 → ~~`5.9` 前台移动端~~ ✅ 已完成(2026-09-09)→ ~~`5.8` 订单时间线~~ ✅ 已完成 → ~~`5.5` 语音提醒可控~~ ✅ 需求取消 > 每一项落地时按 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 节更新为小程序化前置条件 + 安全方案 | | 2026-09-07 | 微信暂缓 + 二维码方案定稿 | 用户确认:微信支付未开通期间**微信小程序暂缓上架**,首发只做支付宝小程序;二维码采用**自研普通链接码**(不调 `wxacode.getUnlimited` / `alipay.open.app.qrcode.create`),微信/支付宝各自配前缀规则实现双端自动识别;**员工端扫码绑定房间不属于小程序**,继续走 H5(功能已完成);资质由公司统一申请中。3.3 升级为「微信支付缺失 + Mock 免登录资金红线」;3.6 拆为 3.6.1~3.6.7(二维码方案 / 两层拦截 / 7 项前置 / 依赖面 / 支付协议 / 安全方案 / H5 零影响手法) | | 2026-09-07 | 核实二维码改造零迁移成本 | `hotel_qr_code` 表只存 `short_code` + `sign`,**无 `qr_url` / `qr_content` 字段**,URL 由 `QrCodeUtils.buildQrCodeUrl()` 运行时拼接 → 改格式不涉及数据迁移,也不影响已生成的码;但桌牌印制后改动 = 全部重印,故二维码 URL 改造列为第 2 批(资质到位前完成) | | 2026-09-07 | 修正支付宝小程序支付结论 | 原判「支付宝小程序走 Mock 免付款」**错误**。实读 `pay.vue` L190-389:`AlipayTradeWapPayRequest.pageExecute()` 本地签名即成功、`payForm` 有值 → L205 条件成立 → 执行 `my.tradePay({tradeNO: undefined})` → 必然 fail。正确结论:**支付宝小程序支付 100% 不可用**;落到 Mock 分支的是 `paySource='WECHAT'` | | 2026-09-07 | 依赖面核实收敛 | 顾客 8 页只依赖 `HotelTabBar` + `@/api` + `getFileDownloadUrl` + `hotel-order` store;**不用 `AiIcon`、不用 CSS mask、不用 `v-loading`**,图标全为 emoji + 原生 `` → 页面级零改造。`AiIcon`/`AiButton`/`AiResult`/`html5-qrcode` 仅在员工 H5 页,`#ifdef H5` 排除即自动不进小程序包。员工接口鉴权边界已核实安全(不在 `/hotel/open/**` 白名单) | | 2026-09-07 | 废弃文档删除 | 用户确认 `酒店AGENTS.md` 为废弃版本,已删除。其中本轮决策内容(二维码双端识别、7 项门槛、两层拦截、Mock 资金红线、17 个开放接口清单)已转移至本文档 3.3/3.6 与 `酒店二维码模块开发文档.md` | | 2026-09-07 | 批次重排 | 第九节按「微信暂缓 + 资质申请中 + 不影响 H5 测试」重排为第 0~6 批:第 0 批 Mock 资金红线(不等资质)→ 第 1 批小程序全局地基 → 第 2 批二维码 URL + 构建裁剪 → 第 3 批支付宝支付(沙箱 IDE 可验证)→ 第 4 批资质到位后上线项 → 第 5 批厨房闭环 → 第 6 批运营管理。建议拆为 `hotel-pay-mock-hardening`(第 0 批)+ `hotel-alipay-mp-adaptation`(第 1~3 批)两个变更提案 | | 2026-09-07 | **四份文档去重归位** | 按各文档文首已声明的职责边界(迁移跟踪 = 代码地图与实现进度 / 本文档 = 需求验收对照与缺口台账 / 二维码文档 = 二维码实现 / 支付文档 = 支付实现)消除重复:本文档 3.6.1/3.6.3/3.6.4/3.6.5 的小程序实现细节、以及 3.1/3.2 的证据链与已废弃签名 token 方案,均改为「验收结论 + 📌 指针」,权威副本归位到对应开发文档(二维码文档 3.2/3.3/4.2~4.7/5.2、支付文档 7.1/7.3/10.1);R1/R2/R3/R6 活台账与九章排期保留在本文档 | | 2026-09-07 | **修正 5 处与代码不符 + 3 处文档互相矛盾** | 实地复核后更正:① 迁移版本 `V1.0.104`/`V1.0.108` → **`V1.0.109`**;② `order-confirm.vue` L141-148 → **L248-249**;③ `HotelOrderServiceImpl.orderCreate()` L158-175 → **L198-216**;④ 删除不存在的 `code-copilot/changes/hotel-order-amount-fix/` 假链接;⑤ 删除 3.1「后果(当前即可复现)」「修复方向」两段(描述修复前状态,与标题 ✅ 已修复矛盾)。矛盾口径统一:`/r/` 静态提示页 → **403**(3.6.2 / 六章 / 八章 #10 / 九章 #22 四处同步);支付文档「见 7.2」→ **「见 7.1」**;迁移跟踪注入方式 `@RequiredArgsConstructor` → **`@Autowired`(57 处)** | | 2026-09-07 | **第 0 批 + 第 2 批代码落地** | 第 0 批(Mock 资金红线收口):新增 `HotelPayConfig` 读 `hotel.pay.mock-enabled`(6 份 yml 同步:生产 `false` / dev 与 example `true`)、`mockSuccess` 首行拦截、`createPay` 的 `paySource` 去 `defaultValue` + 渠道解析前置、`resolvePayChannel` 抽 `mockOrReject` 单一收口点、`pay.vue` else 分支**拆分而非删除**、`AlipayAuthController.maskPhone()` + `AlipayAuthServiceImpl` 只记 `responseLength`。改造中**新发现并修复 4 项文档未记录的隐患**:① 两份生产 `application.yml` 完全没有 `hotel` 块而 `@Value("${hotel.qr.base-url}")` 无默认值 → 生产 profile 启动即崩;② `createPay` 用原始 `paySource` 判断致 `ALIPAY_MP` 漏进 MOCK;③ `resolvePayChannel` 有**两处**静默降级(原只记录一处);④ `refundOrder` 用「非 ALIPAY 即 MOCK」兜底 → 未来接微信会造成**真实资损**。第 2 批:`hotel.qr.path` 配置化(`buildQrCodeUrl` 重载)+ `resolveScanParams()` + `pages.json` 员工两页 `#ifdef H5` + `HotelTabBar` 图标双分支 + `.env.mp-alipay`。验证:后端全 reactor `mvn compile` BUILD SUCCESS、H5 `pnpm build:h5` Build complete、`GetProblems` 12 个文件 No errors、6 份 yml 核对一致、**dev 行为零变化**。✅ `index.vue` 入口 5 处 `#ifdef H5`、`manifest.json` 的 `mp-alipay.appid` 占位、`package.json` 的 `--mode mp-alipay` **三项均在位**(`git diff` 核实),已在 3.6「当前状态」与 3.6.4 登记 | | 2026-09-07 | 🔴 **纠错:三处「用户已回退」记载全部错误** | 本文档 3.6「当前状态」、3.6.4「首页入口」行与第九节第 13/15 项,以及 `酒店二维码模块开发文档.md` 1.2 / 5.2 / 10.2 / 10.3、`酒店模块迁移跟踪.md` H5 基础设施段,原均记载「`pages/index/index.vue` 的 5 处 `#ifdef H5`、`manifest.json` 的 `mp-alipay.appid` 占位、`package.json` 的 `--mode mp-alipay` 已被用户回退」与「`.env.mp-alipay` 为死配置」。经 `git status --porcelain` + `git diff` + 文件直读三重复核,**三处改动全部在位**(`index.vue` L245-254 / L274-276 / L282-288 / L412-427 / `handleShortcut`;`package.json` L9 / L25;`manifest.json` L61-64),`.env.mp-alipay` 因 `--mode mp-alipay` 在位而**会被正常加载**。误记根因:仅凭 IDE 的 attached_files「用户修改了该文件」提示就推断为「回退」,未验证修改方向。**纠正后的工作约定:判断工作区状态一律以 `git diff` 为准,不得从 IDE 提示推断内容方向**。本项不影响任何已完成的功能结论,仅修正状态记载 | | 2026-09-07 | **实测推翻两项原有判断 + 核实两处环境事实** | ① 3.6.4「重 DOM 组件一行代码不用改」**错误**:`pnpm build:mp-alipay` 在 2.4s 内失败于 `AiAvatarCropper.vue` 的 ``(`[vite:vue] not supported: Teleport`),因 `unplugin-vue-components` 会为任何被引用组件生成注册代码,页面裁剪挡不住 → 列为第 1 批首位阻塞项;② `.env.[platform]` **不会被自动加载**:实测 `@dcloudio` 包内无任何 `loadEnv` 调用,uni-app/Vite 只按 mode 加载 `.env.[mode]`,故 `.env.mp-alipay` 必须配 `--mode mp-alipay` 才生效(且该 mode 文件需自包含全部变量,不会与 `.env.production` 合并)。环境事实:`mvn` 不在 PATH(实际位于 `D:\java_install\apache-maven-3.9.16`);仓库根 `forge/` 是**空目录**、真实 Maven 根为 `forge-server/`,而 AGENTS.md 2.2 仍写 `cd forge && mvn clean install` | | 2026-09-07 | **补写资金变更 Spec + 新发现两条独立红线** | (一)按 AGENTS.md 5.9 补写 `code-copilot/changes/hotel-pay-mock-hardening/` 的 `spec.md`(`templates/spec.md` 13 节结构,`status: review`)+ `tasks.md`(8 Task 全标已执行):第 8.1 节摊开三个需人工审查的资金风险点(`mockOrReject` 覆盖面 / `refundOrder` 无流水也标退款成功 / `pay.vue` 保留 MOCK 分支)、第 10.1 节登记与原方案三处差异、第 11 节回填 `git status` 核实的**实际**改动文件。HARD-GATE 确认时间/确认人**留空**,只能由需求方本人手填,AI 不得代签;3.3 合规状态由「未闭环」更新为「Spec 已补写、审查待执行」。(二)新建 `code-copilot/changes/hotel-alipay-secret-externalize/spec.md`(`status: propose`,**只出方案未动一行代码**),登记 3.3.1 两条新红线:① 支付宝沙箱私钥明文入库且已随 commit `65508d9` 推到 `origin/master`;② 🔴 **P0 新发现** —— `AlipayConfig` 5 个 `@Value` 无默认值 + 两份生产 `application.yml` 无 `hotel.alipay` 块 + `forge-hotel` 是两个服务的直接依赖且无 `@Conditional*` → 生产 profile 启动即崩,与第 2 批已修的 `hotel.qr.base-url` 同类,属上批遗漏。(三)**修正自己两处错误说法**:`.gitignore` L83 早就有 `**/application-dev.yml`(不需追加规则,需 `git rm --cached`);AGENTS.md 2.4 的意图表述是对的(不符的是仓库状态,仅路径前缀 `forge/` 该改)。本轮**未改任何代码文件** | | 2026-09-08 | **餐厅前台订单管理页完成 + 后端修复** | ① 新增 `restaurantOrder.vue`(三 Tab:全部订单/订单看板/营业时间),看板 6 列含已拒单列,全部订单含拒单记录+拒单原因,支持全日期筛选;V1.0.117 建菜单(一级「餐厅管理」+子「餐厅订单」,权限 `restaurant:order:*`)。② 后端 `queryDate` 类型 `LocalDate` → `Date`(5 个文件)+ 双重 `@DateTimeFormat`;`selectDashboard` SQL 按日期过滤 pendingCount/preparingCount/deliveringCount。③ 五端矩阵餐厅前台由「无独立端」更新为 ✅ 已完成;4.2 当日订单看板由 ⚠️ 更新为 ✅;5.2 数据看板更新为餐厅前台看板已完成 | | 2026-09-09 | **厨房订单页面增强** | ① 新增 `kitchenOrder.vue`(日期筛选默认当天 + 统计卡片:待备餐/制作中/今日完成 + 分组展示:待备餐/制作中/已完成 + 订单详情弹窗含操作按钮)。② 后端新增 `HotelKitchenController`(`/hotel/kitchen/list` + `/stats`,均支持 `date` 参数)+ Service 接口/实现 + Mapper 接口 + XML(`selectKitchenOrderList` + `selectKitchenStats`)。③ 前端 API 新增 `getKitchenOrderList(date)` + `getKitchenStats(date)`。④ SQL 修复:`todayCompletedCount` 从 `status = 6` 改为 `status >= 4`(厨房视角出餐即完成),`selectKitchenOrderList` 改为展示当天所有活跃订单(`status NOT IN (7, 9, 10, 11)`)按创建时间升序。⑤ 五端矩阵厨房端由「完全未实现」更新为 ⚠️ 部分完成;3.4 待做订单列表由 ❌ 更新为 ⚠️ 基础已有;4.3 状态同步更新 | | 2026-09-09 | **餐厅 PAD 移动端 + 宾馆前台移动端完成** | ① 新增 `staff-order.vue`(餐厅 PAD 订单管理,三 Tab:全部订单/订单看板/营业时间,10 秒轮询+看板自适应列宽+首页快捷入口+返回键 switchTab 回首页)。② 新增 `hotel-order.vue`(宾馆前台订单总览,3 张统计卡片+房间号搜索+状态下拉筛选+卡片式列表+底部 TabBar)+ `hotel-order-refund.vue`(退单管理/订单详情,列表模式+详情模式含状态时间线+同意/驳回退单)。③ H5 API 层新增 13 个餐厅员工接口 + 4 个酒店前台接口。④ pages.json 注册 3 条新路由。⑤ 首页新增「餐厅订单」和「酒店订单」快捷入口。⑥ 后端零改动,全部复用现有 `HotelOrderController` 接口。 修复 `hotelOrderDetail` API 命名冲突(改为 `hotelStaffOrderDetail`,避免覆盖顾客端同名免登录接口)。⑧ 五端矩阵餐厅前台补加 PAD 移动端;宾馆前台补加移动端;5.9 已关闭;4.2 标题更新为「全部完成」;4.4 标题更新为「全部完成」;4.4 订单详情查看由 ⚠️ 更新为 ✅ | | 2026-09-09 | **数据看板 + 订单时间线状态更正** | 确认 `dashboard.vue` 已完成(V1.0.116):KPI 卡片 + ECharts 趋势图 + 热门菜品 Top5 + 餐段分布;后端 `HotelDashboardController` + `DashboardVO` + Mapper XML 4 条 SQL;Flyway 脚本建菜单。 确认 `order.vue` / `restaurantOrder.vue` / `kitchenOrder.vue` 详情弹窗均使用 Naive UI `NTimeline` 组件展示订单流转时间轴。③ 文档状态同步更新:缺口清单 5.2 / 5.8 标记为 ✅ 已关闭;迁移跟踪表新增 dashboard 行、前端页面汇总表补充 NTimeline 说明。 | | 2026-09-09 | **P1 需求裁剪决策** | 用户确认三项 P1 功能无需实现: ~~语音提醒音量/暂停控制~~(5.5):现有 MP3 固定音量提醒已够用; ~~二维码重新生成/补打~~(5.7):批量生成/绑定/下载 ZIP 已满足需求;③ ~~房间启停点餐开关~~(5.7):房间一键状态设置(空闲/入住/打扫/维护)已满足运营需求。文档同步更新:缺口清单 5.5 / 5.7 标记为 ✅ 需求取消,第 6 批排期同步调整。 | | 2026-09-09 | **菜品操作日志埋点完成** | ① 后端 `HotelDishLog` 实体新增 `operatorId/operatorName` 字段;② ServiceImpl 各操作方法调用 `saveLog()` 记录日志(CREATE/EDIT/DELETE/ON_SALE/OFF_SHELF/SOLD_OUT/RESTORE),并记录操作人信息(`SessionHelper.getUserId()/getUserName()`); Controller 新增 `GET /hotel/dish/logs?dishId=xxx&pageNum=1&pageSize=20` 接口;④ Flyway 脚本 `V1.0.119__add_hotel_dish_log_operator_fields.sql`;⑤ 前端 `dish.vue` 操作列增加"操作日志"按钮 + NModal 弹窗展示日志列表(操作类型标签 + 操作人 + 时间 + 备注);⑥ API 层新增 `getDishLogPage()`。5.11 已关闭。 | | 2026-09-10 | **SSE 通知 + TTS 播报 + 顾客端实时推送完成** | ① 核实 `SseEmitterManager` 已支持 `NEW_ORDER`/`ORDER_READY`/`REFUND_REQUEST`/`CUSTOMER_ORDER_STATUS` 四类事件;② `HotelOrderServiceImpl` 10 处状态变更调用 `notifyCustomerStatus()` 广播顾客端通知;③ `HotelCustomerController` 新增免登录 SSE 端点 `GET /hotel/open/customer/sse/subscribe`;④ H5 `order-status.vue` 用 `EventSource` 替换 10 秒轮询(SSE 实时推送 + 30 秒兜底轮询);⑤ 厨房端 `kitchenOrder.vue` 已集成 TTS 语音播报(`SpeechSynthesis`)菜品名+房号 + SSE 自动刷新;⑥ 3.4 标题更新为「一项未做」(仅缺小票打印);3.5 标记 ✅ 已完成;4.2 出餐通知接收 ✅;4.3 更新为「2 完成 / 1 基础已有 / 1 未做」;5.12 已关闭;六章性能/语音状态同步更新;九章第 5 批 #26-27 标记已完成、第 6 批 #29 标记已完成 |