# 酒店模块小程序化改造汇总 > **文档定位**:本轮改造的**进展汇总 + 理解导读**,回答「这一轮到底改了什么、为什么改、解决了哪些问题、还有什么待办」。 > > 本文档**不是**实现权威。具体实现细节请查同目录四份权威文档(用指针引用,不复制正文): > - 代码地图 / 实现进度 → `酒店模块迁移跟踪.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.path`、`hotel.pay.mock-enabled` | | 纯增量 | 新增类 / 方法 / 重载 / 分支,旧签名委托保留 | `QrCodeUtils` 重载、`HotelPayConfig`、`resolveScanParams` | ### 2.3 载体差异(为什么必须适配) | 能力 | H5 载体 | 小程序载体 | |---|---|---| | 扫码落地 | hash 路由 `/#/pages/hotel/scan-bind` | 专属前缀 `/r/`(平台规则按 path 前缀匹配拉起) | | 员工扫码绑定 | 支持(html5-qrcode + 相机) | 不支持(无 `navigator.mediaDevices`) | | 图标渲染 | CSS mask 引用本地 SVG 可换色 | WXSS/ACSS 不支持 mask,需 `` | | 扫码入参 | 页面直接拿 `c`/`t`/`s` | 平台把整条 URL 塞进 `q`(微信)/`qrCode`(支付宝) | --- ## 3. 主线:H5 → 支付宝小程序「载体适配」 ### 3.1 后端二维码:扫码落地路径可配置 - **解决的问题**:扫码落地路径原来**硬编码**成 H5 的 hash 路由常量,但小程序拉起规则要求专属前缀 `/r/`,两者冲突。 - **手法**:抽成配置项 `hotel.qr.path`;`QrCodeUtils.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` 改用 `` + 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` 命令缺 `--mode`,`manifest.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/**` 内**完全免登录**,且 `createPay` 的 `paySource` 默认值就是 `"MOCK"`,加上 `resolvePayChannel` 两处静默降级——构造请求即可免付款。 | 加固点 | 手法 | |---|---| | 单一收口 | 抽 `mockOrReject()`:`mock-enabled=true` 返回 `MOCK`,否则抛 `BusinessException` | | 开关控制 | 新增 `HotelPayConfig` 读 `hotel.pay.mock-enabled`(生产 `false` 严格、dev `true` 宽松) | | 接口层拦截 | `mockSuccess` 首行拦截;`createPay` 的 `paySource` 去掉 `defaultValue` | | 渠道显式判定 | WECHAT / 未知渠道显式抛异常,不再降级为 MOCK | | 退款加固 | `refundOrder` 改显式判 `"MOCK".equals(payChannel)`,非 ALIPAY 非 MOCK 抛异常要求人工介入 | | 前端兜底禁止 | `pay.vue` 的 else MOCK 分支「拆分」而非「删除」:`else if (payChannel==='MOCK')` 保留 dev 调试逻辑,新增 else 显式 `showModal` 报错,**禁止兜底调 mockSuccess** | | 日志脱敏 | `AlipayAuthController.maskPhone`、`AlipayAuthServiceImpl` 只记 `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-bind`(**H5 旧值不变**) | | `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.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` 会被合并加载。 - **连带更正**:我上一批往 `application.yml` 加 `hotel:` 块时写的注释「缺此项会导致生产 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.md`,`status: 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.yml` 的 `mock-enabled: true` 会覆盖 `application.yml` 的 `false`。若直接拿 dev 配置上生产,mock 支付是开着的。请确认上生产前是否会建独立 prod profile / 改 dev 值 / 设环境变量覆盖。 > ⚠ `hotel-alipay-secret-externalize` 变更已关闭(用户已决策接受,后期生产密钥不提交)。 --- ## 附:本文档维护说明 - 本文档是**小程序化改造这一持续任务**的阶段性进展汇总,后续批次(第 3~6 批:MP 身份体系、支付宝 `trade.create`、图片绝对路径、上线资质切换)落地后可继续更新。 - 具体实现以四份权威文档为准;本文只负责「整体视图 + 问题-解决对照 + 待办跟踪」。 - 排期与缺口的**唯一权威**是 `酒店模块需求缺口清单.md` 第 0~6 批,本文不重复维护排期。