酒店模块小程序化改造汇总.md 15 KB

酒店模块小程序化改造汇总

文档定位:本轮改造的进展汇总 + 理解导读,回答「这一轮到底改了什么、为什么改、解决了哪些问题、还有什么待办」。

本文档不是实现权威。具体实现细节请查同目录四份权威文档(用指针引用,不复制正文):

  • 代码地图 / 实现进度 → 酒店模块迁移跟踪.md
  • 需求缺口 / 补齐排期(第 0~6 批,排期唯一权威)→ 酒店模块需求缺口清单.md
  • 二维码与开放接口实现 → 酒店二维码模块开发文档.md
  • 支付实现(协议 / 回调 / 幂等 / Mock 红线代码)→ 酒店订单支付系统开发文档.md

核实基线:本文所有文件清单、改动性质均以工作区未提交改动的 git diff 逐一核实为准,非凭印象。


0. 元信息

内容
汇总时间 2026-09-07
覆盖范围 工作区当前全部未提交改动(26 个文件,+1366 / -702)+ 2 个新增变更目录
关联变更目录 code-copilot/changes/hotel-pay-mock-hardening/code-copilot/changes/hotel-alipay-secret-externalize/
改造主线 酒店点餐系统由「H5 开发态」走向「支付宝小程序上线态」
硬约束 全程不破坏现有 H5 开发测试(详见第 2.2 节)

1. 一句话总纲

本轮所有改动,本质上只服务一个目标:让酒店点餐系统能从「H5 开发态」平滑走向「支付宝小程序上线态」,且全程不破坏现在的 H5 开发测试

改动之所以看起来横跨很多层面,是因为它同时触及了后端 / 前端 / 构建 / 配置 / 文档五层,但内在逻辑是统一的:

一条主线(小程序载体适配)
  + 一条安全支线(Mock 支付生产加固)
  + 两个核实过程中挖出的待决策问题(P0 启动崩溃、密钥明文入库)
  + 文档同步

2. 改造背景与约束

2.1 业务方向

  • 首发只做支付宝小程序;微信小程序因商户号未申请,已决策暂缓上架,但后端三端共用,WECHAT 渠道仍须显式阻断。
  • 非支付宝 / 微信的扫码器一律不可访问(两层拦截:入口层 + 接口层)。
  • 资质问题后期补充,本轮只判定并落地技术可行性

2.2 硬约束:不影响 H5 开发测试

所有改动只能归入以下三类手法,禁止直接替换现有实现:

手法 说明 本轮用例
条件编译 #ifdef H5 保留原实现,#ifndef H5 走小程序分支 前端页面、TabBar、首页入口
配置开关 生产 application.yml 默认严格,开发 application-dev.yml 覆盖宽松 hotel.qr.pathhotel.pay.mock-enabled
纯增量 新增类 / 方法 / 重载 / 分支,旧签名委托保留 QrCodeUtils 重载、HotelPayConfigresolveScanParams

2.3 载体差异(为什么必须适配)

能力 H5 载体 小程序载体
扫码落地 hash 路由 /#/pages/hotel/scan-bind 专属前缀 /r/(平台规则按 path 前缀匹配拉起)
员工扫码绑定 支持(html5-qrcode + 相机) 不支持(无 navigator.mediaDevices
图标渲染 CSS mask 引用本地 SVG 可换色 WXSS/ACSS 不支持 mask,需 <image>
扫码入参 页面直接拿 c/t/s 平台把整条 URL 塞进 q(微信)/qrCode(支付宝)

3. 主线:H5 → 支付宝小程序「载体适配」

3.1 后端二维码:扫码落地路径可配置

  • 解决的问题:扫码落地路径原来硬编码成 H5 的 hash 路由常量,但小程序拉起规则要求专属前缀 /r/,两者冲突。
  • 手法:抽成配置项 hotel.qr.pathQrCodeUtils.buildQrCodeUrl 新增带 basePath 的重载,旧签名委托保留;空值回退默认常量。
  • 关键设计:二维码 URL 不落库hotel_qr_code 只存 short_code + sign,URL 运行时拼接),因此改格式零数据迁移成本
  • 对 H5 影响。dev 环境 hotel.qr.path 锁定旧值 /#/pages/hotel/scan-bind,既有二维码与扫码流程完全不变。
文件 改动
QrCodeUtils.java 新增带 basePath 重载(+35),旧签名委托
HotelQrCodeServiceImpl.java 注入 qrBasePath,2 处调用改用重载(+14)
HotelQrConstants.java QR_BASE_PATH 补注释说明其为回退值(+8,值不变)

3.2 前端:双载体条件编译

  • 解决的问题:小程序不支持 H5 的相机扫码页、CSS mask SVG 换色、员工扫码绑定页。
  • 手法:用 #ifdef H5 / #ifndef H5 把仅 H5 的能力裁掉,H5 分支原样保留。
  • 对 H5 影响。H5 构建走原分支,行为不变;小程序构建裁掉不适用部分。
文件 改动
pages.json scan-bind / qr-scanner 两页包进 #ifdef H5(+7),小程序不注册
index/index.vue 扫码绑定入口、权限常量、路由白名单、快捷跳转全部 #ifdef H5(+25)
HotelTabBar.vue 图标渲染 #ifndef H5 改用 <image> + opacity 区分激活态(+17)

3.3 前端:小程序扫码入参解析

  • 解决的问题:小程序扫「普通链接二维码」时,平台不拆 query,而是把整条 URL 塞进 q(微信,已 encodeURIComponent)/qrCode(支付宝) 字段,原解析逻辑取不到 c/s
  • 手法room-confirm.vue 新增 resolveScanParams(),优先级「显式 c/s > 微信 q > 支付宝 qrCode」,用与 scan-bind.vue 一致的正则口径解析。
  • 对 H5 影响。H5 仍走「显式 c/s 透传」分支,resolveScanParams 对 H5 是恒等返回。
文件 改动
room-confirm.vue 新增 resolveScanParams()onMounted 改用它解析(+51)

3.4 构建:支付宝小程序可打包

  • 解决的问题:支付宝小程序无法打包——dev/build:mp-alipay 命令缺 --modemanifest.json 缺 appid 占位。
  • 手法:补构建参数 + appid 空占位(待企业资质下来再填)+ 新增专属环境变量文件。
  • 对 H5 影响。仅新增 mp-alipay 相关脚本与配置,H5 构建链不受影响。
文件 改动
package.json dev:mp-alipay / build:mp-alipay--mode mp-alipay(+4)
manifest.json mp-alipay.appid 空占位 + 说明注释(+4)
.env.mp-alipay 新增,支付宝小程序构建环境变量

4. 安全支线:MOCK 模拟支付「生产加固」

  • 背景:AGENTS.md 5.9 资金红线。开发调试靠 MOCK 模拟支付(点一下就「支付成功」),若该通道在生产也能走,等于任何人不用真付钱就能把订单置为已支付
  • 原隐患/hotel/open/pay/mockSuccess 落在 Sa-Token 白名单 /hotel/open/**完全免登录,且 createPaypaySource 默认值就是 "MOCK",加上 resolvePayChannel 两处静默降级——构造请求即可免付款。
加固点 手法
单一收口 mockOrReject()mock-enabled=true 返回 MOCK,否则抛 BusinessException
开关控制 新增 HotelPayConfighotel.pay.mock-enabled(生产 false 严格、dev true 宽松)
接口层拦截 mockSuccess 首行拦截;createPaypaySource 去掉 defaultValue
渠道显式判定 WECHAT / 未知渠道显式抛异常,不再降级为 MOCK
退款加固 refundOrder 改显式判 "MOCK".equals(payChannel),非 ALIPAY 非 MOCK 抛异常要求人工介入
前端兜底禁止 pay.vue 的 else MOCK 分支「拆分」而非「删除」:else if (payChannel==='MOCK') 保留 dev 调试逻辑,新增 else 显式 showModal 报错,禁止兜底调 mockSuccess
日志脱敏 AlipayAuthController.maskPhoneAlipayAuthServiceImpl 只记 responseLength
  • 对 H5 影响。dev 环境 mock-enabled: true,现有模拟支付调试行为完全不变。
文件 改动
HotelPayConfig.java 新增,读 hotel.pay.mock-enabled
HotelPayServiceImpl.java mockOrReject 收口 + resolvePayChannel + refundOrder 渠道判定(+80)
HotelPayController.java paySource 去默认值 + mockSuccess 首行拦截(+19)
AlipayAuthController.java 手机号脱敏 maskPhone(+16)
AlipayAuthServiceImpl.java 日志只记 responseLength(+3)
pay.vue else MOCK 分支拆分(+20)

5. 配置层设计(「不影响 H5」的关键)

同一段 hotel: 配置,在生产开发两份文件里故意不同

配置项 生产 application.yml 开发 application-dev.yml
qr.path ${FORGE_HOTEL_QR_PATH:/r/}(小程序前缀) /#/pages/hotel/scan-bindH5 旧值不变
qr.base-url ${FORGE_HOTEL_QR_BASE_URL:}(空串,靠环境变量注入) http://192.168.10.4:3001局域网 IP 不变
pay.mock-enabled ${FORGE_HOTEL_PAY_MOCK_ENABLED:false}(严格禁止) true模拟支付调试不变
  • 结论:生产收紧、开发保持原样。现在 H5 怎么测,改完之后还怎么测,一模一样。
  • 涉及 6 份 yml:admin-server 与 app-server 各 3 份(application.yml / application-dev.yml / application-dev.example.yml)。
  • 环境变量名易错点:是 FORGE_HOTEL_PAY_MOCK_ENABLED不是 FORGE_HOTEL_PAY_MOCK(写错不报错,只静默退回 false)。

6. 核实过程中的两个观察(已更正)

6.1 【已撤销】生产 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 会被合并加载。
  • 连带更正:我上一批往 application.ymlhotel: 块时写的注释「缺此项会导致生产 profile 启动失败」(L227)也是基于同样的错误前提。实际效果:因为 dev.yml 覆盖,application.yml 里的 hotel 块当前不生效(无害,但也无「修复崩溃」的作用)。
  • 真正的配置架构观察:本项目没有独立的生产 profile 配置,所有环境(含 docker 部署)都用 dev profile,application-dev.yml 承载了全部实际连接配置(数据库、Redis、支付宝沙箱)。若未来要建立独立生产 profile,需要同步建立 application-prod.yml 并提供所有必要配置。

6.2 【已决策接受】支付宝沙箱密钥明文入库

  • 现象application-dev.yml 里的支付宝私钥 / 公钥是明文,且已随 commit 65508d9 推送到远端 origin/master
  • 用户决策:git 是内网私有仓库(192.168.8.4),不在外网;密钥是沙箱密钥;后期如有必要,会直接不提交生产的密钥
  • 处置:本变更关闭(hotel-alipay-secret-externalize 不再推进)。后续生产密钥不入库由用户把控。
  • 注意:本汇总文档不含任何真实密钥字符,避免二次扩散。

7. 文档同步与变更目录

动作 对象 说明
删除 forge-hotel/酒店AGENTS.md(-420)、forge-h5-ui/AGENTS.md(-19) 废弃版本,用户确认删除
移动 酒店订单支付系统开发文档.md 由仓库根 git mv 移入 forge-hotel/ 目录
同步 迁移跟踪 / 二维码开发文档 / 需求缺口清单 / 支付系统文档 回填本轮改动、行号纠错、新发现登记
新建变更 code-copilot/changes/hotel-pay-mock-hardening/ spec.md + tasks.md,HARD-GATE 留空待签
新建变更 code-copilot/changes/hotel-alipay-secret-externalize/ spec.mdstatus: propose,只写方案

8. 全量文件改动清单

后端代码(8)

文件 主题 性质
QrCodeUtils.java 二维码路径可配置 增量重载
HotelQrCodeServiceImpl.java 二维码路径可配置 注入 + 调用改重载
HotelQrConstants.java 二维码路径可配置 注释补充(值不变)
HotelPayConfig.java Mock 支付加固 新增类
HotelPayServiceImpl.java Mock 支付加固 收口 + 渠道判定
HotelPayController.java Mock 支付加固 接口层拦截
AlipayAuthController.java 日志脱敏 手机号掩码
AlipayAuthServiceImpl.java 日志脱敏 只记长度

配置(6)

文件 主题 性质
admin/app application.yml 主配置 hotel 块 新增(qr + pay),当前被 dev.yml 覆盖不生效
admin/app application-dev.yml 开发宽松值 新增 qr.path 旧值 + mock-enabled=true
admin/app application-dev.example.yml 模板同步 补齐 path + pay 块

前端 H5(8)

文件 主题 性质
package.json 小程序构建 --mode mp-alipay
manifest.json 小程序构建 appid 空占位
.env.mp-alipay 小程序构建 新增
pages.json 载体裁剪 #ifdef H5
index/index.vue 载体裁剪 #ifdef H5 入口
HotelTabBar.vue 图标适配 #ifndef H5 用 image
room-confirm.vue 扫码入参 新增 resolveScanParams
pay.vue Mock 加固 else 分支拆分

文档(5 + 2 目录)

见第 7 节。


9. 对 H5 开发测试的影响评估

总结论:零影响。 逐项核对:

你现在的 H5 行为 改造后 保证手法
扫二维码进员工绑定页 不变 dev qr.path 锁旧值
局域网 IP + 端口访问 不变 dev qr.base-url 保持 http://192.168.10.4:3001
点「模拟支付」调试下单 不变 dev mock-enabled: true
首页扫码绑定入口 不变 #ifdef H5 保留
TabBar 图标换色 不变 #ifdef H5 走 mask 原实现
员工相机扫码 不变 两页仅 H5 注册

10. 待办事项(需要用户决策)

  1. 签 HARD-GATE:读 hotel-pay-mock-hardening/spec.md 第 8.1 节(3 个资金审查点)+ 第 9 节 Q1~Q3,手填第 13 节确认时间 / 确认人(AI 不代签)。
  2. 生产部署方式确认:当前项目所有环境(含 docker)都用 dev profile,application-dev.ymlmock-enabled: true 会覆盖 application.ymlfalse。若直接拿 dev 配置上生产,mock 支付是开着的。请确认上生产前是否会建独立 prod profile / 改 dev 值 / 设环境变量覆盖。

hotel-alipay-secret-externalize 变更已关闭(用户已决策接受,后期生产密钥不提交)。


附:本文档维护说明

  • 本文档是小程序化改造这一持续任务的阶段性进展汇总,后续批次(第 3~6 批:MP 身份体系、支付宝 trade.create、图片绝对路径、上线资质切换)落地后可继续更新。
  • 具体实现以四份权威文档为准;本文只负责「整体视图 + 问题-解决对照 + 待办跟踪」。
  • 排期与缺口的唯一权威酒店模块需求缺口清单.md 第 0~6 批,本文不重复维护排期。