酒店模块需求缺口清单.md 64 KB

酒店模块需求缺口清单

需求基线需求说明书.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 管理端(兼餐厅前台与宾馆前台职能)」

判断 结论
主链路 ✅ 扫码 → 房号确认 → 点餐 → 购物车 → 下单 → 支付 → 接单/备餐/出餐/配送/完成 → 退单退款,已跑通
厨房端 完全缺失(0 页面、0 打印、0 菜品语音播报)
微信支付 ⏸️ 暂缓(2026-09-07):微信小程序不上架,首发只做支付宝小程序。后端 resolvePayChannelWECHAT/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 排除出小程序构建
品牌风格设置 ⚠️ 暂缓(待甲方确认)
订单金额正确性 🔴 存在资金缺陷已修复(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.vueviews/hotel/businessHours.vue
👨‍🍳 厨房端 大屏 + 打印机,2 页 完全未实现(仅「出餐」动作可在 PC 订单页点击)
🏨 宾馆前台 PC / 移动,2 页 无独立端,PC 订单页覆盖全订单查看/筛选/任意阶段退单/手动退款;缺移动端 views/hotel/order.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_itemspec_option_ids / addition_idsvarchar(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 累加进 unitPricesubtotal = 重算后单价 × 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_configdel_flag,固定配置行使用确定性小整数主键 已豁免 该表是键值型运行时配置,只更新不删行,属 AGENTS.md 5.11 允许的例外场景;BaseEntity 未声明 delFlag、项目也无全局逻辑删除配置,不会触发 SQL 报错。主键约定:1=order_pause2=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 即可把订单标记为已支付 新增 HotelPayConfighotel/pay/config/)读 hotel.pay.mock-enabledapplication.yml 默认 false(可被 FORGE_HOTEL_PAY_MOCK_ENABLED 覆盖)、application-dev.yml 覆盖 truemockSuccess 方法首行校验开关,关闭时 WARN 日志 + 抛 BusinessException("模拟支付已禁用")@PostConstruct 启动即打印开关状态,开启时用 WARN 级资金安全告警
paySource 默认 MOCK createPay() @RequestParam(required = false, defaultValue = "MOCK"),不传即走 Mock 去掉 required = falsedefaultValue 改为必传;缺参由 Spring 直接拒绝,不再静默走 Mock
✅ 前端 Mock 兜底 pay.vue:非 ALIPAY 分支直接调 hotelPayMockSuccess显示「支付成功」但没有真实扣款 拆分而非删除(保 H5 dev 调试链路):改判后端返回的 payChannelelse 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 归一为 ALIPAYALIPAY_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.ymlhotel.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.ymlhotel.alipay 块 → 生产启动崩溃(P0)。
更正application.yml L42-43 spring.profiles.active: dev,且 docker-compose.yml / .env.example 无任何 profile 覆盖,项目也没有 application-prod.yml。因此当前启动(包括 docker 部署)都会加载 application-dev.ymlhotel.alipay.* 全部有值,不会崩
我错在哪:把 application.yml 当成「生产专用配置」,忽略了它 L43 默认激活 dev profile,application-dev.yml 会被合并加载
文件直读 + Select-String + docker 配置核实 原判断错误,已撤销

⚠️ hotel-alipay-secret-externalize 变更已关闭(用户已决策接受,后期生产密钥不提交)。原 P0 判断错误,已撤销。


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_ORDERREFUND_REQUEST 两类事件
  • 需求 3.5 流程图要求:接单 → 通知厨房(语音播报 + 打印小票);出餐 → 通知餐厅前台安排送餐
  • 现状:orderAccept / orderReady 均无任何通知发布

修复方向SseEmitterManager 增加 ORDER_ACCEPTEDORDER_READY 事件;orderAccept() / orderReady() 发布;PC useOrderNotification.js 增加对应提示;厨房端订阅。


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.jsonmp-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-alipaygit 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-ALIPAYmy.tradePay 分支、room-confirm.vue L376-396 授权成功/取消/失败处理
  • 支付宝沙箱配置已齐备:application-dev.yml L98-111(app-id 9021000166699753gateway-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:3001application-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(⏸️ ⚠️ ↩️ )+ 原生 <image>
需改组件 HotelTabBar.vue 已改完(2026-09-07):#ifdef H5 保留原 WebkitMask 实现与 iconStyle() 函数(函数也一并包进条件编译,小程序包里不留死代码),#ifndef H5 改用 <image mode="aspectFit">。SVG 内 stroke="currentColor"image 中解析为黑色、无法随激活态换色,故激活态改用 opacity: 1 / 0.45 区分,文字颜色照旧跟随激活态。H5 产物已验证仍含 WebkitMask
需改基础设施 ⚠️ utils/httputils/cryptostore 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)、menuItemsprefix 逻辑(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<Teleport to="body"> 直接报 [vite:vue] not supported: Teleport在 2.4s 内终止整个 mp 构建。原因:unplugin-vue-components 会为任何被引用到的组件生成注册代码,与「顾客 8 页是否引用」无关,页面裁剪挡不住它。→ 列为第 1 批首位新增阻塞项,需 #ifdef H5 包裹 Teleport 并为 mp 提供普通 <view> 分支
员工接口鉴权 ✅ 已核实安全:`/hotel/qrcode/bind

📌 顾客 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.createtradeNo),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.getAuthCodeAlipayAuthController#getUserInfouserId → 落库绑定 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.vuemain.jsstore/index.jsutils/httputils/crypto 仍是小程序启动必经之路。先修基础设施,再动业务代码

⚠️ pages.json 当前启动页是 pages/login/index;小程序侧首页须换成 pages/hotel/customer/room-confirm,同样用条件编译处理,不改 H5 启动页。tabBar 整块用 #ifdef H5 排除(顾客端用的是自绘 HotelTabBar,不依赖原生 tabBar)。

⚠️ key-exchange.jsgenerateSessionKey 依赖 globalThis.crypto.getRandomValues + btoacrypto-config.jsincludePaths 为空时全量加密,所有请求都会触发崩溃。可参照 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:进度条 + 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/statusPOST /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/rankingdishSalesRanking)与前端 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/statuspause/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 实时达标)
兼容 微信/支付宝小程序 第 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<Teleport>,且第 1 批基础设施(axios / crypto / persist / global:window)一项未动,故小程序当前仍不可构建
兼容 Chrome 90+ / Safari 14+ / 移动端浏览器 ✅ PC 管理端 + H5 满足
兼容 厨房大屏 1920×1080 + 热敏打印机 ❌ 无厨房端、无打印
语音 前台新订单语音提醒(可暂停/调音量) ⚠️ 有提醒,无音量/暂停控制
语音 厨房播报菜品名称和房号 ❌ 未实现
视觉 简洁商务风、留白多、扁平化 ✅ H5 亚朵墨绿主题符合
视觉 三套候选配色(商务深蓝/暖金雅致/墨绿清新)待客户确认 ❌ 仅墨绿一套且硬编码
视觉 品牌风格设置后五端同步生效 ❌ 未实现
视觉 移动端底部 Tab 导航 / PC 左导航右内容 HotelTabBar 3-tab + Forge 后台布局
安全 支付环境自动判断,防支付方式注入 ⚠️ 部分达标(2026-09-07):环境判断仍在前端(UA + 条件编译)、paySource 仍由前端传,非服务端环境判断;但注入面已收口 —— paySource 改必传、resolvePayChannelmockOrReject 单一收口点对所有非白名单渠道显式抛异常、mockSuccesshotel.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 分钟跨租户扫描 selectExpiredUnpaidOrderIdscancelTimeoutOrder(状态 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
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
9 域名与主体资质:备案域名、非个人主体认证、小程序备案 3.6.3
10 /r/ 路径下放不放网页 3.6.2

九、建议补齐顺序

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 开关(新增 HotelPayConfigapplication.yml 默认 false,6 份 yml 同步)+ mockSuccess 首行拦截 + createPay 渠道解析前置
    • createPaypaySource 去掉 defaultValue="MOCK" 改必传
    • ✅ WECHAT / WECHAT_MP 渠道显式抛 BusinessExceptionresolvePayChannelmockOrReject 单一收口点,消除原有两处静默降级(原计划只记录了一处)
    • ⚠️ pay.vue else MOCK 分支 拆分而非删除(原计划为「删除」):删除会破坏 dev H5 微信内置浏览器调试,改为 else if (payChannel === 'MOCK') 保留原逻辑 + 新 else 显式 showModal 报错
    • AlipayAuthController 手机号日志脱敏(新增 maskPhone())+ AlipayAuthServiceImpl 密文报文改只记 responseLength
    • ➕ 超出原计划新修复:refundOrder 退款渠道显式判定(原「非 ALIPAY 即 MOCK」兜底会在接入微信后造成真实资损)+ 两份生产 application.ymlhotel 块(原缺块且 @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<Teleport to="body"> 使 pnpm build:mp-alipay 在 2.4s 内直接失败([vite:vue] not supported: Teleport)。需 #ifdef H5 包裹 Teleport + 为 mp 提供普通 <view> 分支,否则后续任何 mp 验证都无法进行。详见 3.6.4「重 DOM 组件」行。

  1. uni storage 适配器 → app.js / auth.js / hotel-order.js 三个 persist
  2. axios → uni.request(保留 interceptors.js 加解密 + token 逻辑)
  3. key-exchange.js L96 随机数与 Base64(参照 crypto-config.js L92-98 已有的 uni.request 范式)
  4. vite.config.js define:{global:'window'} 按平台条件化
  5. utils/file.js 图片直连(getFileDownloadUrl 返回绝对 https)
  6. directives/loading.jsv-loading 注册加 #ifdef H5

第 2 批(二维码 URL 格式 + 构建裁剪,2 天)—— ✅ 已完成(2026-09-07),全部 6 项均在位(git diff 核实)

  1. QR_BASE_PATH 配置化 → hotel.qr.path(dev: /#/pages/hotel/scan-bind;prod: /r/)。QrCodeUtils.buildQrCodeUrl 新增带 basePath 的重载、旧签名委托保留(纯增量,零调用方改动);HotelQrConstants.QR_BASE_PATH 降级为「配置缺省时的回退值」;HotelQrCodeServiceImpl 新增 qrBasePath 字段并改调新重载
  2. room-confirm.vue 新增 resolveScanParams():优先级显式 c/s > 微信 q > 支付宝 qrCodedecodeURIComponent 带 try/catch 容错,正则 [?&]c=([^&]+)scan-bind.vueparseQrUrl 同口径,对 hash 与非 hash URL 均成立
  3. pages.json 条件编译裁剪:scan-bind + qr-scanner 两页整块包进 // #ifdef H5(实测 H5 产物仍含两个 chunk)。⏳ tabBar 整块排除与小程序侧启动页切换未做(属第 1 批地基范畴,见 3.6.7)
  4. 实现方式已修正 + 已落地:不需要在 scan-bind.vue / qr-scanner.vue 内部#ifdef H5 —— 页面被 pages.json 裁掉后根本不参与编译。真正需要同步保护的是 pages/index/index.vue 的 5 处入口引用(快捷菜单项 / SCAN_BIND_PERM / menuItemsprefix / isRegisteredH5Route / handleShortcut),均已包进 #ifdef H5git diff 核实在位)。H5 dev 零变化,小程序构建时首页不渲染该入口。见 3.6.4「首页入口已同步」行
  5. HotelTabBar.vue 图标 CSS mask → <image>#ifdef H5 保留原 WebkitMask 实现与 iconStyle()#ifndef H5<image mode="aspectFit"> + opacity 区分激活态
  6. 已完成.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 内可完整验证)

  1. 后端 ALIPAY_MP 渠道:AlipayTradeCreateRequestalipay.trade.create)→ tradeNo
  2. buyer_id 串联:my.getAuthCodegetUserInfouserIdcreatePay
  3. userId ↔ stayId 落库绑定 + 后端归属校验(顺带收口 R2
  4. pay.vue L205 分支拆清:MP-ALIPAY 只走 tradeNo,不碰 payForm
  5. return-url / notify-url 调整(natapp 免费域名回调不稳;两份 yml 同步)

第 1~3 批建议变更名:hotel-alipay-mp-adaptation涉及资金与身份,必须经人工审查

第 4 批(上线前,依赖外部资质,可延后)

  1. 备案域名 + HTTPS 证书 + 支付宝后台普通链接二维码规则 + 校验文件上传
  2. /r/ 403 拦截(仅放行 .txt 校验文件)+ 顾客端接口平台身份校验(3.6.2 两层拦截)
  3. 正式 appid + 商户号 → sandbox: false + gateway-url 切线上
  4. 真实桌牌物料生成(资质到位前一张都别印) 25.(微信恢复时)微信后台规则 + JSAPI 支付 + openid 绑定 + 3.3 WECHAT_MP 渠道

第 5 批(厨房闭环)

  1. 3.4 厨房端待备餐/出餐页 → 27. 3.5 SSE 接单/出餐事件 → 28. 小票打印(先浏览器打印模板)

第 6 批(运营与管理完善)

  1. 5.12 状态推送满足 < 3s → 30. 5.2 数据看板补齐 → 31. 5.3 hotelconfig + 品牌配色 → 5.4 临时暂停接单 ✅ 已完成 → 5.10 加料 PC 入口 ✅ 已澄清 + 5.11 操作日志埋点 → 32. 5.7 房间启停点餐 + 二维码补打 → 33. 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 节更新为小程序化前置条件 + 安全方案
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 + signqr_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 + 原生 <image> → 页面级零改造。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.108V1.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 资金红线收口):新增 HotelPayConfighotel.pay.mock-enabled(6 份 yml 同步:生产 false / dev 与 example true)、mockSuccess 首行拦截、createPaypaySourcedefaultValue + 渠道解析前置、resolvePayChannelmockOrReject 单一收口点、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 H5manifest.jsonmp-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 H5manifest.jsonmp-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 / handleShortcutpackage.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<Teleport to="body">[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.mdtemplates/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.mdstatus: propose只出方案未动一行代码),登记 3.3.1 两条新红线:① 支付宝沙箱私钥明文入库且已随 commit 65508d9 推到 origin/master;② 🔴 P0 新发现 —— AlipayConfig 5 个 @Value 无默认值 + 两份生产 application.ymlhotel.alipay 块 + forge-hotel 是两个服务的直接依赖且无 @Conditional* → 生产 profile 启动即崩,与第 2 批已修的 hotel.qr.base-url 同类,属上批遗漏。(三)修正自己两处错误说法.gitignore L83 早就有 **/application-dev.yml(不需追加规则,需 git rm --cached);AGENTS.md 2.4 的意图表述是对的(不符的是仓库状态,仅路径前缀 forge/ 该改)。本轮未改任何代码文件