Files
qipai/V5.7.md
T

7200 lines
338 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 自助棋牌室系统开发总纲(V5.7)
> 文档版本:V5.7(中文 Git 提交门禁 + `fd6be4f` 最新源码校准 + M09-D1 在制恢复 + 持续工程开发版)
> 更新日期:2026-08-11Asia/Shanghai
> 直接前序版本:V5.6;本文完整继承 V5.6 的全部功能、架构、协议、部署、安全、测试、验收和持续开发要求。
> 最新已审计提交证据:`fd6be4f9b98bb9213e95973069c402915a8ba6f2`,且上传快照中 `HEAD == origin/main`;其后的 M09-D1 工作区代码尚未提交、尚未通过全部门禁。
> 默认执行起点:保护并继续完成当前 M09-D1 在制工作;通过编译、全量测试、真实 MySQL 往返和静态门禁并以中文提交推送后,才能进入 `M09-D2 → M09-D3 → M09-D4 → M09-REGRESSION → M10-A/B/C/D → M10-RC1`。
> 唯一交接目标:Codex 只阅读根目录 `V5.7.md`,即可保护当前工作区,直接继续工程编码、测试、中文提交、推送和远端校验,自动推进至 `CODE_COMPLETE`,外部条件具备后继续至 `PRODUCTION_ACCEPTED`。
> **V5.7 最高优先级控制规则:** 本文开头“V5.7 增量控制层”和全文最后“第 30 章 V5.7 最终固定游标”共同构成唯一当前执行入口,优先于中间完整继承的 V5.6/V5.5/V5.4 历史标题、旧 HEAD、旧队列、旧示例和旧入口。中间历史正文继续作为完整需求与验收依据,但不得据此把执行游标回退到已经有工程证据的阶段。
---
# V5.7 增量控制层
## V5.7-0 M09-D1 收口覆盖层(2026-08-11 最新真实仓库)
本节记录 `fd6be4f + M09-D1 在制工作区` 之后的真实工程进展,优先于下方审计快照中的 M09-D1 `DOING`、缺失断言和旧 HEAD 描述:
- Git 中文提交门禁已由 `2de796143ff418d681fdb972040b8555a8e058d1``chore(V5.7): 增加中文提交信息校验门禁` 提交并推送,`HEAD == origin/main`,后续业务提交继续强制中文标题。
- M09-D1 当前工作树已完成 9 张商品/库存表、分类/商品/SKU/门店售卖、跨日营业时段、库存策略、可用/锁定/损耗库存、幂等请求和不可变流水;商品/库存 API、RBAC 与租户/门店范围已接线。
- 迁移 runner 已按单个 marker/checksum 增量执行 pending 迁移,interrupted up/down DDL 可原地重试,同库 up/verify/down 由专用连接上的数据库命名锁串行化;包含触发器的迁移会在任何触发器 DDL 与建表前检查生产前置,binlog 开启时要求 `log_bin_trust_function_creators=1` 和独立迁移身份。
- `assertProductInventoryFoundation` 已实现并调用;M09-D1 静态门禁、后端 build/full test、管理端 build/test 已通过。WSL MySQL 8.4.10 的 `up → verify → down → up → verify` 通过,计数为 179 up、141 verify、171 down,增量/崩溃重试与 cleanup probe 通过。
- M09-D1 工程与本文档伴随记录待以 `feat(M09-D1): 完成商品目录与库存流水底座` 中文业务提交取得 SHA;文档不得虚构尚未产生的提交号。
- 唯一执行游标已推进到 `M09-D2`,下一目标为商品订单、订单项快照、支付衔接,以及库存锁定、扣减、释放和取消回补。M09-D1 若出现回归只做针对性修复,不得删除重做。
## V5.7-1 本次升级的唯一事实边界
1. V5.7 以用户提供的 `V5.6(2).md` 为完整需求底稿;底稿共 `6734` 行,SHA-256 为 `8fe7171ab5b7782d56345c426432cbf349da0956da2bac56d348f360665fd803`。本文件继续完整继承该正文,但当前状态和执行入口以 V5.7 文首控制层及文末第 30 章为准。
2. 最新工程快照为用户上传的 `qipai - 副本(2).zip``55177129` 字节、`1196` 个压缩条目,SHA-256 为 `cc802fecbea399698e0a9c1b38127bc12ff260d8f4d6a084828108307f557d6f`;压缩数据完整性检查通过。
3. 快照包含完整 Git 元数据:`HEAD == origin/main == fd6be4f9b98bb9213e95973069c402915a8ba6f2`,分支为 `main`,最新提交时间 `2026-08-11T02:13:17+08:00`,标题为英文 `docs(M09-C): record settlement closure and advance cursor`
4. `fd6be4f` 的上一工程提交为 `1f1071501f4954ad0440d79c2a38e7210393e928`,标题同样为英文 `feat(cleaning): close settlement integrity and reconciliation`;该提交真实完成 M09-C 工程,随后 `fd6be4f` 回填状态并把游标推进至 M09-D1。两项已推送英文提交均保留历史,禁止擅自改写共享 `main` 或强推。
5. 快照已跟踪 `544` 个文件;现有后端 `84` 个 TypeScript 源文件、`48` 个 MJS 测试,数据库迁移目录 `109` 个文件,`docs/` `125` 个文件。上述数量只用于识别本快照,不是完成度指标。
6. M09-D1 当前不是空白待办,也不是已完成阶段:工作区已有商品分类、商品、SKU、门店售卖配置、跨日营业时段、库存策略、可用/锁定/损耗库存、不可变流水、路由、权限、迁移和测试等约 `4690` 行在制文件,但尚未提交。
7. 本次独立运行 `node scripts/check-m09-d1-product-inventory.mjs` 实际失败:脚本要求 `backend/tests/mysql-migration-roundtrip.test.mjs` 包含 `assertProductInventoryFoundation`,当前文件尚未实现该函数。依赖安装在审计环境中因 npm 包传输/目录错误失败,因此未把“后端全量测试、后台构建”虚报为通过;Codex 必须在真实开发环境重新安装锁定依赖并完成全部验证。
8. 两份协议原件已再次核验:
- 4G(标准版)智慧插座 MQTT 协议文档:`33` 页,SHA-256 为 `a143385c1f83ed08c8c8d63c1c5541dd803f84e620e90fc6ff2e34bb91102d21`
- 4G 智能门禁控电箱 MQTT 协议文档:`48` 页,SHA-256 为 `ce89590dc4eabb338a20ec351be7b400f8ec100eecc1305eb5367a53b2854a3c`
9. Codex 在真实 `D:\qipai` 接管时仍必须重新读取当前 HEAD、`origin/main`、工作区差异、模块游标和测试结果;真实仓库若晚于 `fd6be4f`,只能按有效工程证据向后推进,不能用本快照覆盖更晚进度。
## V5.7-2 与 V5.6 相比的强制变化
| 编号 | V5.6 存在的问题 | V5.7 强制修正 |
|---|---|---|
| V57-GIT-01 | 规定了 `feat/fix/test/docs` 等类型,但没有要求标题正文必须为中文 | 冒号后的摘要必须包含中文并以中文表达;禁止全英文摘要 |
| V57-GIT-02 | `git push` 后只校验 SHA 一致,未校验远端提交标题语言 | push 后同时校验 `origin/main` 的 SHA 和最新 commit subject 中文门禁 |
| V57-GIT-03 | Codex 可能把终端英文输出直接当作用户回执 | 命令原始输出可保留,结论、提交说明、推送说明、阻塞原因和最终回复必须为中文 |
| V57-GIT-04 | 缺少仓库级自动门禁 | 增加可跟踪的中文提交检查脚本、commit-msg hook、安装脚本和测试;不能只靠提示词自觉 |
| V57-GIT-05 | 历史队列仍包含已闭环阶段,容易误回退 | 第 30 章只保留实际当前阶段及后续队列;更晚仓库证据可继续向后跳过 |
| V57-DEV-01 | 文档升级后可能再次停留在 Markdown 回填 | V5.7 文档提交不计业务进度;下一普通开发必须先完成 Git 中文门禁工程,再立即进入当前业务阶段 |
| V57-DEV-02 | 旧 V5.7 没有读取新副本,无法描述 M09-D1 未提交工程 | 固定 `fd6be4f + M09-D1 worktree` 恢复点,保护约 4690 行在制实现,禁止删掉重做或提前标记完成 |
| V57-TEST-01 | M09-D1 静态门禁已经接入,但真实 MySQL 断言函数缺失 | 必须实现 `assertProductInventoryFoundation` 并跑完真实 MySQL 往返;禁止删断言、写空函数或只靠 Mock 过关 |
| V57-GIT-06 | 历史总纲/参考资料删除与业务改动混在工作区 | M09-D1 只能显式暂存;未获用户确认前删除项保持未决,不提交、不恢复、不阻断独立工程 |
## V5.7-3 Git 中文提交与推送最高优先级门禁
### V5.7-3.1 “推送为中文”的准确含义
`git push` 命令本身没有标题。Gitea 的推送动态、提交列表和版本记录展示的是 Git commit subject,因此必须从 `git commit` 源头强制中文:
- 提交标题冒号后的功能摘要必须使用简体中文;
- 提交正文、脚注、回滚原因和协作说明必须使用中文;
- Codex 的阶段结果、push 说明、测试结论、阻塞原因和下一步必须使用中文;
- 如后续使用 Pull Request/Merge Request,其标题、正文、Review 回复和合并说明必须使用中文;
- Release 名称、更新日志和部署回执必须使用中文;
- `feat``fix``refactor``test``build``chore``docs` 等 Conventional Commits 类型、模块编号、API/MQTT/MySQL 等技术专名允许保留英文;它们不构成“英文提交说明”。
### V5.7-3.2 固定提交格式
```text
<类型>(<模块编号或稳定范围>): <中文摘要>
```
推荐示例:
```text
feat(M09-D1): 建立商品、SKU 与库存流水底座
fix(M09-D2): 修复并发支付导致的库存重复扣减
test(M09-D3): 补充寄存凭证并发领取测试
refactor(M10-A): 统一通知发件箱的渠道重试逻辑
chore(V5.7): 增加中文提交信息校验门禁
docs(spec): 升级开发总纲至 V5.7 并固定中文提交规则
```
禁止示例:
```text
feat(cleaning): close settlement integrity and reconciliation
fix(stock): handle race condition
chore: update docs
test(order): add tests
```
附加约束:
1. 摘要必须说明可识别的工程结果,禁止只写“更新代码”“修复问题”“继续开发”“优化逻辑”。
2. 标题建议不超过 `72` 个可见字符;确需补充时使用中文正文说明原因、风险和验证。
3. scope 优先使用 `Mxx-X` 模块编号或稳定范围,禁止临时创造大量英文 scope 来替代模块游标。
4. `docs(spec)` 只用于用户明确要求的总纲升级;普通模块文档伴随工程提交,不拆成额外英文 docs-only 提交。
5. 自动生成的 Merge/Revert 标题也必须在提交前改成中文;不能把 Git 默认英文文本直接推送到 `main`
### V5.7-3.3 仓库级机器门禁
Codex 读取 V5.7 后,若仓库尚无等效实现,必须先完成一次 `V5.7-GIT-CN-GATE` 工程准备,再继续业务阶段。最小交付如下:
- `scripts/check-commit-message.mjs`:读取提交消息文件或传入字符串,校验首行至少包含一个 CJK 统一表意文字,且符合允许的类型/模块格式;
- `.githooks/commit-msg`:调用上述 Node 脚本,失败时返回非零并输出中文错误;
- `scripts/setup-git-hooks.ps1`:在 Windows `D:\qipai` 执行 `git config core.hooksPath .githooks`,并验证配置生效;
- `scripts/setup-git-hooks.sh`:供 WSL/Ubuntu 开发或验证环境安装同一 hook;
- 对检查脚本增加正向、反向测试,至少覆盖中文摘要、全英文摘要、空摘要、Merge/Revert、含技术英文的中文摘要和多行正文;
- 若仓库已有本地/CI 提交规范校验,原地扩展,不建立第二套互相冲突的规则。
注意:Git hook 不会仅因克隆仓库自动启用,所以安装脚本和 README 的一次性执行说明必须同提交落地;不能只提交 `.githooks/commit-msg` 就宣称所有电脑已经受保护。
`V5.7-GIT-CN-GATE` 可以独立形成一个工程工具提交,推荐标题:
```text
chore(V5.7): 增加中文提交信息校验门禁
```
该提交成功推送后,同一 Codex 会话必须立即继续当前业务阶段;不得把工具门禁当作本轮结束理由,也不得再连续创建 V5.7 说明文档。
### V5.7-3.4 提交前和推送后的双重校验
每次提交前必须对拟用标题运行中文检查。提交后、push 前再次读取本地标题:
```powershell
$Subject = git log -1 --pretty=%s
if ($Subject -notmatch '[\u3400-\u9FFF\uF900-\uFAFF]') {
throw "提交标题不含中文,禁止推送:$Subject"
}
```
push 后必须同时验证提交 SHA 和远端标题:
```powershell
git push origin main
if ($LASTEXITCODE -ne 0) { throw "推送失败,保留本地提交并停止前移游标" }
git fetch origin main
$LocalHead = git rev-parse HEAD
$RemoteHead = git rev-parse origin/main
if ($LocalHead -ne $RemoteHead) { throw "本地 HEAD 与 origin/main 不一致" }
$RemoteSubject = git show -s --format=%s origin/main
if ($RemoteSubject -notmatch '[\u3400-\u9FFF\uF900-\uFAFF]') {
throw "远端最新提交标题不含中文:$RemoteSubject"
}
```
阶段回执必须增加:
```text
COMMIT_SUBJECT=<实际中文标题>
COMMIT_SUBJECT_CN=PASS
PUSH_VERIFIED=PASS
REMOTE_COMMIT_SUBJECT_CN=PASS
USER_FACING_LANGUAGE=ZH_CN
```
### V5.7-3.5 发现英文提交时如何处理
1. **尚未 push** 若该提交只存在本地且确认没有其他协作者基于它开发,可以执行 `git commit --amend -m "<中文标题>"`,重新运行测试和中文门禁后再推送。
2. **已经 push 到共享 `main`** 默认保留历史,不得擅自 `rebase``filter-repo`、修改旧 commit 或 `git push --force`。在状态记录中注明 `HISTORICAL_ENGLISH_COMMIT_PRESERVED`,从下一提交开始中文化。
3. **用户明确要求改写历史:** 必须先单独评估协作者、部署引用、tag、回滚点和备份,再取得用户明确授权;V5.7 本身不授予强推权限。
4. 已知 `1f10715``fd6be4f` 均属第 2 类。Codex 不得为了把它们改成中文而改写 `main`V5.7 的修复目标是确保 `fd6be4f` 之后的提交和推送动态全部使用中文。
## V5.7-4 最新状态自适应恢复规则
V5.7 的默认游标建立在本次已审计快照 `fd6be4f + M09-D1 未提交工作区` 上,但真实仓库仍具有最高优先级。Codex 必须按以下顺序恢复:
1. 确认工作目录为 `D:\qipai`Git 根目录、`main` 分支和 `origin` 地址符合本项目约束。
2. 执行 `git status --short --branch --untracked-files=all``git log -20 --oneline --decorate``git fetch --prune origin``git rev-list --left-right --count main...origin/main`
3. 保护所有未提交修改,按文件和模块识别归属;禁止 `reset --hard``checkout -- .``clean -fd`、无差别 `git add -A`、无差别 stash 和强推。
4. 检查 `fd6be4f` 是否为当前 HEAD 的祖先。是:只审计 `fd6be4f..HEAD` 与当前工作区;否:停止高风险操作并报告分叉,不能自行覆盖。
5. 读取 `docs/module-status.md``docs/feature-status.md``docs/current-baseline.md`、最近开发日志、迁移和测试;只有工程文件、测试和提交共同证明的阶段才允许跳过。
6. 若真实仓库仍为 `fd6be4f` 且存在下述 M09-D1 文件,必须原地续写,不能删除重做;若文件已由更晚提交闭环且验证通过,则进入第一个未闭环阶段。
7. 若 README 或固定状态文件仍写 V5.6,在当前 M09-D1 工程提交中伴随更新为 V5.7;不得为版本号同步再拆一个 docs-only 提交。
8. 历史英文提交只记录不改写;在第一次新提交前安装或验证中文门禁,新的提交标题必须为中文。
### V5.7-4.1 必须保护的 M09-D1 在制范围
以下是上传快照中已经明确识别的 M09-D1 工程文件。列表用于恢复与显式暂存,不表示文件已经验收通过:
| 范围 | 在制路径 | 当前结论 |
|---|---|---|
| 商品目录 | `backend/src/products/product-catalog-repository.ts``backend/src/routes/products.ts` | 已实现分类、商品、SKU、门店售卖设置/时段和版本冲突主体,必须复核租户/门店范围、软删除和并发更新 |
| 库存领域 | `backend/src/inventory/inventory-service.ts``backend/src/routes/inventory.ts` | 已实现策略、入库、调整、盘点、损耗以及批量锁定/释放/扣减主体,必须完成真实 MySQL 并发与幂等验证 |
| 应用接线 | `backend/src/app.ts``backend/src/server.ts``backend/package.json` | 已注册产品与库存路由并加入测试链,尚需编译和全量回归 |
| 权限 | `backend/src/auth/auth-repository.ts``backend/src/auth/rbac-repository.ts``backend/src/routes/admin-auth.ts` | 已增加商品/库存权限和后台菜单,必须验证 STAFF/STORE_ADMIN/TENANT_ADMIN/PLATFORM_ADMIN 及跨门店拒绝 |
| 数据库 | `database/migrations/2026081107_m09d1_product_inventory_foundation.{up,down,verify}.sql``backend/src/db/migration-runner.ts` | 已增加 8 张商品/库存表、权限和不可变流水触发器,必须执行 up/verify/down/second-up 真实往返 |
| 自动化测试 | `backend/tests/product-*.test.mjs``backend/tests/inventory-*.test.mjs` 及既有迁移/RBAC/认证测试修改 | Mock/契约测试主体已写,不能替代真实数据库事务测试 |
| 门禁与脚本 | `scripts/check-m09-d1-product-inventory.mjs`、Windows/WSL 检查脚本 | 已接入门禁,但当前静态检查失败,必须补真实测试而不是删除断言绕过 |
### V5.7-4.2 当前已知失败与 M09-D1 收口顺序
恢复后按以下顺序继续,不得只改状态文档:
1.`backend/tests/mysql-migration-roundtrip.test.mjs` 中实现并实际调用 `assertProductInventoryFoundation`;至少覆盖租户/门店唯一性、跨日营业时段、目录/SKU 版本冲突、负库存拒绝、并发库存锁定单赢家、重复 requestId 幂等、冲突指纹拒绝、锁定→扣减/释放、损耗/盘点、流水 UPDATE/DELETE 被拒绝、down 后对象清理和 second-up 恢复。
2. 先运行 `node scripts/check-m09-d1-product-inventory.mjs`,当前断言必须真正通过;禁止删除 `assertProductInventoryFoundation` 检查、改成永真或仅添加未调用的空函数。
3. 在后端目录运行锁文件安装、`npm run build``npm test`;随后使用 WSL MySQL 8.4.x 执行 `npm run test:mysql:migration` 或仓库规定的完整往返脚本。
4. 运行后台 `npm run build``npm test`,并运行 Windows `scripts/dev/windows/test-all.ps1` 或等效完整门禁;M09-D1 即使主要改后端,也不得破坏后台、小程序静态检查和既有 M06/M08/M09-B/C 回归。
5. 审查 SQL 的复合外键、unsigned 边界、触发器、down 顺序和迁移 runner 的 verify 最小行数,不得用脆弱的固定数字掩盖缺失验证结果。
6. 仅在全部内部门禁通过后更新 README、`docs/current-baseline.md``docs/module-status.md``docs/feature-status.md` 和既有 changelog/devlog;这些状态更新必须随 M09-D1 工程提交,不得先提交“完成”文档。
7. 使用显式路径暂存 M09-D1 与中文门禁文件,核对 `git diff --cached --name-status` 后,以中文标题提交并推送。推荐业务标题:`feat(M09-D1): 完成商品目录与库存流水底座`
### V5.7-4.3 不得混入 M09-D1 的未决删除
快照仍显示 `V4.8.md``V5.0.md``V5.1.md``V5.2.md``V5.3.md``V5.4.md` 以及 `参考/env``参考/start.sh``参考/stop.sh``参考/xjar``参考/后台管理系统_20260427.zip` 被删除。V5.6 已把相同问题记为未决,本次新快照仍不能证明删除意图。
- 不主动恢复、不主动提交,也不让 `git add -A` 把它们混入 M09-D1
- 在真实 `D:\qipai` 核对是否为用户明确删除或打包差异;没有用户授权时保持 `WORKTREE_DELETION_UNRESOLVED`
- 该未决项不阻塞 M09-D1/M09-D2 内部开发,但必须在 M10-RC1 仓库完整性门禁前解决;
- 若用户随后明确要求删除,应单独形成中文维护提交,并在提交前确认二进制参考资料已有可恢复备份。
## V5.7-5 唯一当前推进队列
在真实仓库没有 `fd6be4f` 之后的更晚有效工程提交时,固定队列为:
```text
V5.7-GIT-CN-GATE(一次性工程工具门禁,不计业务模块完成度)
→ M09-D1
→ M09-D2
→ M09-D3
→ M09-D4
→ M09-REGRESSION
→ M10-A
→ M10-B
→ M10-C
→ M10-D
→ M10-RC1
→ CODE_COMPLETE
→ 外部条件具备后执行真实联调与生产验收
→ PRODUCTION_ACCEPTED
```
规则:
- M09-A、M06-C-R1、M06-E-R1、M06-R-REGRESSION、M08-D-R1、M09-B、M09-C 已由 `fd6be4f` 及其祖先工程提交证明闭环,不得仅因中间历史章节再次出现而重做;只有新回归失败能够触发针对性修复。
- M09-D1 为 `DOING_WORKTREE`:先保护并完成现有实现。文件很多、代码很多或静态门禁脚本存在,都不等于完成;必须获得 build、全量测试、真实 MySQL、commit、push 五类证据。
- `V5.7-GIT-CN-GATE` 缺失时只允许显式暂存其独立文件;不得把历史删除或 M09-D1 半成品意外带入工具提交。完成门禁提交后必须在同一持续目标中立即返回 M09-D1。
- 当前真实仓库若已完成队列前部阶段,直接定位第一个缺少工程/测试/commit/push 证据的阶段。
- 每个业务阶段仍必须形成真实工程变化、相关测试、中文 commit、push 和远端 SHA/中文标题校验。
- 外部微信支付、团购授权、MQTT 生产账号、真实硬件、合法域名和生产 Ubuntu 未提供时,内部工程继续推进至 `CODE_COMPLETE/BLOCKED_EXTERNAL`,不能停在商品域或 M10 骨架。
- 同一会话资源足够时,完成一个阶段后自动进入下一阶段,不等待用户再次发送“继续”。
## V5.7-6 Codex 第一轮强制动作
Codex 收到“请阅读 V5.7.md,按当前进度继续开发。”后必须执行:
```text
LOAD_V57
→ INSPECT_REAL_HEAD_AND_WORKTREE
→ PROTECT_USER_CHANGES
→ VERIFY_FD6BE4F_ANCESTRY_OR_NEWER_REAL_PROGRESS
→ RESUME_M09_D1_WORKTREE_WITHOUT_RESET
→ INSTALL_OR_VERIFY_GIT_CN_GATE
→ RUN_GATE_TESTS
→ COMMIT_AND_PUSH_WITH_CHINESE_SUBJECT(如门禁尚缺)
→ IMPLEMENT_ASSERT_PRODUCT_INVENTORY_FOUNDATION
→ PASS_M09_D1_STATIC_GATE
→ CONTINUE_AND_FINISH_M09_D1
→ RUN_RELEVANT_AND_REGRESSION_TESTS
→ RUN_REAL_MYSQL_ROUNDTRIP
→ UPDATE_EXISTING_STATUS_FILES_ONLY
→ COMMIT_WITH_CHINESE_SUBJECT
→ PUSH
→ VERIFY_REMOTE_SHA_AND_CHINESE_SUBJECT
→ AUTO_CONTINUE
```
第一轮禁止:
- 创建新的计划、审计、总结、最终版、修订版 Markdown;
- 只把 V5.6 字样改成 V5.7 后结束;
-`V5.7-GIT-CN-GATE` 当成业务阶段完成;
- 重新初始化 backend/admin/miniapp/database
- 因缺生产账号或真实硬件而停止内部商品、通知、统计、测试和部署脚本开发;
- 使用英文 commit subject、英文推送说明或英文最终回执。
## V5.7-7 每阶段中文机器回执
```text
DOCUMENT_VERSION=V5.7
BASELINE_HEAD=<本阶段开始时HEAD>
ENGINEERING_DELTA=YES|NO
CURRENT_STAGE=<V5.7-GIT-CN-GATE或Mxx-X>
STAGE_RESULT=DONE|PARTIAL_BLOCKED_EXTERNAL|BLOCKED_INTERNAL
ENGINEERING_FILES=<实际工程文件>
TEST_RESULT=PASS|FAIL
TEST_COMMANDS=<实际命令>
COMMIT=<sha或NONE>
COMMIT_SUBJECT=<实际中文标题或NONE>
COMMIT_SUBJECT_CN=PASS|FAIL|NOT_COMMITTED
PUSH_VERIFIED=PASS|FAIL|NOT_PUSHED
REMOTE_COMMIT_SUBJECT_CN=PASS|FAIL|NOT_PUSHED
NEXT_STAGE=<Mxx-X>
EXTERNAL_BLOCKERS=<真实外部阻塞或NONE>
USER_FACING_LANGUAGE=ZH_CN
```
只允许在用户明确要求“仅更新文档”的本次 V5.7 交付中出现 `ENGINEERING_DELTA=NO`。下一次普通继续开发若仍为 `NO`,不得声称项目取得工程进展。
## V5.7-8 交给 Codex 的唯一入口
用户只需发送:
```text
请阅读 V5.7.md,按当前进度继续开发。
```
Codex 不得要求用户再次粘贴 V5.6、模块队列、协议字段或中文提交规则;本文已经完整包含。
---
# V5.6 完整继承正文(只读需求基线,当前执行入口已由 V5.7 覆盖)
> 下方从原 V5.6 第一行起完整保留,确保任何功能、数据库、协议、安全、部署、测试和验收细节不丢失。凡其中出现“V5.6 为最高优先级”“请阅读 V5.6.md”“当前队列从 M09-A 开始”等文字,均属于历史执行说明,当前必须由上方 V5.7 控制层和全文最后第 30 章覆盖。
# 自助棋牌室系统开发总纲(V5.6)
> 文档版本:V5.6(最新源码接管 + 漏项纠偏 + M09/M10 连续收口 + 全量测试版)
> 更新日期:2026-08-11Asia/Shanghai
> 当前工程证据基线:`1f1071501f4954ad0440d79c2a38e7210393e928`
> 当前执行起点:`M09-A/B/C` 与强制返工队列均已闭环;从 `M09-D1` 继续完成 `M09-D1/D2/D3/D4 → M09-REGRESSION → M10 → RC1`。
> 目标:Codex 只阅读这一份 `V5.6.md`,即可从现有仓库和未提交工作继续编码、测试、提交、推送、校验并自动进入下一阶段,直到所有可独立完成项达到 `CODE_COMPLETE`,再在外部条件具备后达到 `PRODUCTION_ACCEPTED`。
> **V5.6 最高优先级说明:** 本章和文末“V5.6 最终固定队列”优先于本文后续继承的 V5.5/V5.4 历史游标、历史“当前进度”和示例命令。后文仍完整保留旧版功能、架构、协议、部署、安全、测试和验收要求,但凡与本章的新 HEAD、新工作区状态、新队列或新缺陷结论冲突,必须以 V5.6 为准。不得因后文出现 `M08-C` 旧游标而回退;不得因 README 仍写 V5.4 而忽略本文件。
---
# V5.6 最新源码接管与强制纠偏
## V5.6-1 证据基线
本版已实际读取并交叉核对以下资料,不是依据 README 或目录名称猜测:
1. 用户提供的最新工程快照 `qipai - 副本.zip``59040193` 字节、`1727` 个压缩条目,SHA-256 为 `d57906f8a939fdfa9e79d88811ddff43733b98e6600e8a8b1b1eea2ed3ab0f32`
2. 快照内完整 Git 元数据:`HEAD == origin/main == b35af9037f256657f38f922e7b8a9bed9eb9aee9`,提交时间 `2026-08-10T13:58:49+08:00`,标题 `docs(M08-D): 回填后台登录与会话入口`
3. V5.5 旧基线 `19d6734b68c18a1a79dd4f5b590768cfd4f0a0e5` 是当前 HEAD 的祖先;其后已有 `50` 个提交,累计 `142` 个文件变化、`13902` 行新增、`1013` 行删除,主要完成 M08-C、M08-D 功能垂直切片。
4. 用户提供的 `V5.5(1)(1).md``6283` 行,SHA-256 为 `1688533a3518997e60c7f359bbca8dab875c37402e874c4050f1ef20779af645`。本 V5.6 完整继承其正文,新增规则优先。
5. 4G 智慧插座 MQTT 协议 PDF`33` 页,SHA-256 为 `a143385c1f83ed08c8c8d63c1c5541dd803f84e620e90fc6ff2e34bb91102d21`
6. 4G 智能门禁控电箱 MQTT 协议 PDF:`48` 页,SHA-256 为 `ce89590dc4eabb338a20ec351be7b400f8ec100eecc1305eb5367a53b2854a3c`
7. 当前已跟踪文件 `520` 个;后端 `80` 个 TypeScript 源文件、`44` 个 MJS 测试;后台 `18` 个 Vue 组件;小程序 `app.json` 注册 `16` 个页面;数据库有 `99` 个 SQL 迁移文件;`docs/``119` 个文件。
8. 最新工程快照根目录只存在 `V5.4.md`,用户提供的 V5.5 尚未纳入该快照;README、`docs/configuration.md``docs/current-baseline.md` 仍把 V5.4 写成权威总纲。这是状态同步缺陷,不代表代码应回退到 V5.4。
若 Codex 实际接管时 HEAD 晚于 `b35af90`
- 先执行 `git merge-base --is-ancestor b35af90 HEAD`;返回 0 才把新提交视为本基线的后继。
- 禁止 reset、rebase 覆盖、强推或恢复到 `b35af90`
- 只审计 `b35af90..HEAD` 与当前未提交差异,按代码、迁移、测试、commit、push 证据跳过已真实完成项。
- 若分叉、远端领先或来源不明,保护所有修改,停止高风险 Git 操作并报告;不得自行选择一边覆盖。
## V5.6-2 当前工作区不是干净快照
上传快照的 Git 工作区在 HEAD 之外还有修改,Codex 必须把它当作在制工作保护,不得执行 `git reset --hard``git checkout -- .``git clean -fd` 或无差别 `git add -A`
### 已识别的在制 M09-A 修改
| 路径 | 当前差异 | V5.6 结论 |
|---|---|---|
| `backend/src/cleaning/cleaning-task-repository.ts` | claim/assign/reject/reclaim/submit/状态迁移改为事务;加入 `FOR UPDATE``SKIP LOCKED`;事件与状态同事务;重指派和超时回收清理旧状态 | 明确属于 M09-A 在制工程,必须恢复并补完,不得丢弃 |
| `backend/tests/mysql-migration-roundtrip.test.mjs` | 仅新增 `CleaningTaskRepository/CleaningTaskError` import,尚无对应测试正文 | 明确是未完成测试,单独 import 不能作为测试证据,必须补真实 MySQL 场景后再提交 |
### 不能混入 M09-A 提交的删除项
快照显示 `V4.8.md``V5.0.md``V5.1.md``V5.2.md``V5.3.md` 以及 `参考/env``参考/start.sh``参考/stop.sh``参考/xjar``参考/后台管理系统_20260427.zip` 被删除。无法仅凭压缩包判断这是用户有意删除、打包遗漏还是大文件排除。
强制处理:
1. M09-A、协议返工和后续模块统一使用显式路径 `git add <本阶段文件>`,不得把上述删除混入提交。
2. 不主动恢复,也不主动提交删除;先用真实 `D:\qipai` 工作区再次核对。
3. 若真实工作区也删除且用户未明确意图,登记为 `WORKTREE_DELETION_UNRESOLVED`,不阻塞独立工程开发;到 M10 仓库完整性收口前必须解决。
4. V5.6 文档必须新增为根目录 `V5.6.md`;V5.5 作为历史只读备份保留。旧版是否继续跟踪由真实仓库事实和用户决定,不得借版本升级批量删除历史总纲。
## V5.6-3 实际验证结果
### 已提交 HEAD `b35af90` 的验证
| 验证 | 结果 | 边界 |
|---|---|---|
| `backend/npm test` | PASS | TypeScript build 和 package 中串联的 43 个运行测试通过;不等于真实微信/硬件通过 |
| `admin/npm run build` | PASS_WITH_WARNING | `vue-tsc` 与 Vite 构建通过;`element` chunk `923.28 KiB`,超过 500 KiB,必须在 M08-D-R1/M10 收口 |
| 四个 `scripts/check-*.mjs` | 在无空格/中文路径 PASS | 逻辑门禁通过,但可移植性缺陷仍存在 |
| Shell `bash -n` | PASS | 仓库内 shell 语法通过;未代表生产执行成功 |
| `deploy-business.sh --dry-run .` | PASS | 生成 `DRYRUN-b35af90` manifest,明确 build/migration 未运行、`deployed=false` |
| WSL MySQL 8.4.10 | 仓库日志记录 PASS | `0511008` 日志记录 144 up、124 verify、140 down;本次审计容器无 MySQL 客户端/服务,未重复实测 |
### 含未提交 M09-A 修改的验证
- `backend npm test`PASS。
- `admin npm run build`PASS_WITH_WARNING,仍为 `element 923.28 KiB`
- 上述结果只证明当前差异可编译且未破坏既有模拟/契约测试;因为 MySQL 集成测试正文尚未编写,M09-A 仍是 `DOING`,不得提交“完成”文档。
### 四个静态脚本的共同缺陷
以下文件都使用 `new URL('..', import.meta.url).pathname`,在包含空格或中文的路径中把 `%20/%E5...` 当作真实文件名,全部失败:
- `scripts/check-miniapp-m08-a.mjs`
- `scripts/check-miniapp-m08-c.mjs`
- `scripts/check-admin-m08-b.mjs`
- `scripts/check-admin-m08-d.mjs`
统一修复为 `fileURLToPath(new URL('..', import.meta.url))`,并增加带空格/中文临时目录回归。不得只修一个脚本。
## V5.6-4 当前模块真实状态
| 范围 | V5.6 校准状态 | 说明 |
|---|---|---|
| M00 | PARTIAL/BLOCKED_EXTERNAL | 单仓库与脚本骨架存在;生产 Ubuntu、证书、EMQX、备份/恢复仍未现场验收。不得回到 M00 扩写审计文档,归入 M10 上线收口 |
| M01-M04 | DONE | 已有工程、迁移、权限、订单和测试证据;仅做回归或被后续需求触发的兼容修复 |
| M05 | PARTIAL_BLOCKED_EXTERNAL | 微信支付、团购、分账工程链路存在;真实商户和厂商授权未提供 |
| M06 | PARTIAL_INTERNAL_REMEDIATION + BLOCKED_EXTERNAL | M06-A-F 原工程存在,但 V5.5 要求的协议原件一致性返工被跳过;M06-G 实物仍外部阻塞 |
| M07 | DONE | 双余额、充值、优惠券/套餐和会员聚合已有证据 |
| M08-A | PARTIAL_BLOCKED_EXTERNAL | 顾客端独立工程链路存在;真机合法域名、真实支付和实物开门待外部条件 |
| M08-B | PARTIAL_BLOCKED_EXTERNAL_SCOPE_FROZEN | 保洁端功能已明显超出最小范围;禁止继续增加联调表格/导出/签署类功能 |
| M08-C | PARTIAL_BLOCKED_EXTERNAL | 管理员/员工小程序功能垂直切片已提交;真实团购/微信/硬件待外部联调 |
| M08-D | PARTIAL_INTERNAL_REMEDIATION | 页面/API 功能切片已提交,但没有 Vue Router、没有路由级懒加载、`App.vue` 仍静态导入全部域、Element chunk 923 KiB;不能把 V5.5 的 D1/D6 架构门禁标为完成 |
| M09-A | DOING_UNCOMMITTED | 工作区已有事务化差异,MySQL 测试仅 import 未成形;必须先恢复完成 |
| M09-B/M09-C | PARTIAL_EXISTING | 图片、验收、驳回、统计、结算已有大量 M08-B 代码;只做领域收口和缺口测试,不重建第二套 |
| M09-D | TODO | 未发现商品分类、SKU、库存账、商品订单、寄存/取出正式领域表、服务、页面或测试 |
| M10 | TODO/PARTIAL_FOUNDATION | 只有通用 outbox/worker 和部分统计/部署骨架;通知中心、完整口径、扩展、压测、生产发布与最终验收未闭环 |
## V5.6-5 V5.5 被跳过的 P0 协议返工仍未完成
代码证据仍显示:
- `backend/tests/iot-protocol.test.mjs` 继续构造 `powerW: 3601, temperature: 82, overLoad: true`
- `backend/src/devices/jilian-adapters.ts` 仍以单一过窄 `resultCode` 枚举解析全部命令。
- `docs/mqtt-protocol-mapping.md``docs/hardware-vendor.md``docs/unresolved-issues.md` 仍写“PDF 原件未提供”。
- `ISSUE-013` 仍是 `BLOCKED_EXTERNAL`,与本次已提供的两份 PDF 冲突。
- `b35af90` 之前的 M08-C/M08-D 新提交未包含 `M06-C-R1/M06-E-R1` 协议文件修改。
因此 M06 不能只写 `BLOCKED_EXTERNAL`。协议文档和 fixture 是内部可完成项,必须修正;只有实物行为才是外部阻塞。
### 智慧插座原件必须落地的字段
| 场景 | 原件 Wire 字段 | 代码要求 |
|---|---|---|
| `read=workInfo` | `slotNum, IMEI, onOff, vol, cur, pow, temp, energy`;表格又写 `temperature` | 接收兼容 `temp/temperature`,标准化为内部温度;禁止 `powerW/overLoad` |
| 本地任务读取 | `read=localtask`,单条带 `slotNum/taskNum`;全部读取按原件规则 | taskNum 和全部读取语义分开测试 |
| 本地任务下发 | `action=localtask` + `slotNum/taskNum/tasktype/cyctype/weekdata/actiontime/id` | 领域输入可友好,但 Wire 必须精确转换 |
| 清任务 | `action=clearTask``taskNum=0` 表示清全部 | 高风险确认、审计、幂等 |
| 保护参数 | `setting=protectOff` + `maxPower/maxCurrent/maxTemperature/pullOutStop* /chargeFullStop* /id` | 按 10A/16A/设备能力校验,不统一写死 |
| 其他参数 | `setting=parameter` + `resetHold/keyLock/KeyOff/id` | 兼容大小写,但发送 canonical |
| 本地任务事件 | `event=localtask, slotNum, taskNum, state, energy, timestamp` | 状态事件,不误当 ACK |
| 特殊控电事件 | `event=special, slotNum, state, closeReason, energy, timestamp` | `closeReason=1/2` 为本地按键;3 拔出自停、4 充满自停、5 超功率、6 超电流、7 超温;只有 3-7 生成保护告警 |
| 遗嘱 | `/devicewill/{DeviceID}` 可能是原始 `close` | 不强制 JSON,不进入普通 JSON 死信 |
### 控制箱/门锁原件必须落地的差异
继续严格执行后文第 22、23 章,特别是:
- 控电命令拼写固定为厂商 `ConctolPower`,回包包含当前 `slot1/slot2/slot3`;不得擅自改成英文正确拼写。
- 磁力锁为 `Crldoor``order=open/close``holdopen``delayTime` 范围按原件。
- Sub-1G 为 `CtrlDevice`,必须携带 `subtype=14/15``subID`;密码/卡片字段是 `value`,不是 `content`
- `AddDevice` 使用 `time`,不是 `timeout`
- `task` 结果至少覆盖 `ok/busy/fail/unconfirm``CtrlDevice` 覆盖 `ok/fail/timeout/full`;其他命令覆盖 `paraerror/error/update`
- 未知厂商结果码落原始消息、协议告警/死信,不能让消费进程崩溃,也不能当成功 ACK。
## V5.6-6 唯一恢复队列
在未出现更晚且有充分工程证据的提交时,Codex 只能按下列队列推进:
```text
M09-A-RECOVERY
→ M06-C-R1
→ M06-E-R1
→ M06-R-REGRESSION(同时修复四个 check 脚本路径)
→ M08-D-R1
→ M09-B
→ M09-C
→ M09-D1
→ M09-D2
→ M09-D3
→ M09-D4
→ M09-REGRESSION
→ M10-A
→ M10-B
→ M10-C
→ M10-D
→ M10-RC1
```
解释:M09-A 已经有未提交在制修改,先安全完成它,避免丢失/跨域混提交;随后立即补做安全相关的协议 P0 返工。不得以“当前已经进入 M09”为理由继续跳过 M06-R1。
### V5.6 当前机器游标
```yaml
execution_cursor_v56:
evidence_head: 1f1071501f4954ad0440d79c2a38e7210393e928
base_branch: main
remote: ssh://git@git.txyundm.cn:2222/panda/qipai.git
current_module: M09
current_stage: M09-D1
stage_status: DOING
last_completed_functional_stage: M09-C
last_engineering_commit: 1f10715
last_docs_commit: 5e0f5e3
next_stage_after_current: M09-D2
frozen_stages:
M08-B: PARTIAL_BLOCKED_EXTERNAL_SCOPE_FROZEN
internal_blockers: []
external_blockers:
- REAL_WECHAT_PAY_AND_PROFIT_SHARING
- REAL_GROUP_BUY_AUTHORIZATION
- PRODUCTION_MQTT_ACCOUNT_AND_ACL
- REAL_CONTROL_BOX_LOCK_SOCKET_AND_WIRING
- WECHAT_LEGAL_DOMAIN_AND_DEVICE_TEST
- PRODUCTION_UBUNTU_DEPLOYMENT
docs_only_cannot_advance: true
auto_continue_until_code_complete: true
```
## V5.6-7 第一阶段:M09-A-RECOVERY
Codex 首轮不得创建新计划文档,必须直接恢复当前差异并完成以下工程闭环:
1. 逐段复核 `cleaning-task-repository.ts` 的事务边界,确认状态更新、负责人/协作者更新和事件插入同提交或同回滚。
2. 保留并验证 `FOR UPDATE`/`SKIP LOCKED` 方案在 MySQL 8.4 的行为;不得用进程内锁替代数据库并发控制。
3.`mysql-migration-roundtrip.test.mjs` 真正使用新增 import,至少覆盖:
- 同一任务两个并发 claim 只有一个成功;
- claim/assign/submit/reject/complete 的状态与事件原子性;
- 事件插入失败时状态回滚;
- 重指派从 WAITING/CLAIMED/REJECTED 合法进入 CLAIMED,并清理旧照片、驳回原因和过期时间字段;
- 超时回收只锁定本批任务,返回实际回收 ID,清理未结算成员,不修改已结算任务/成员;
- 订单 FINISHED 幂等生成唯一保洁任务;
- 相同 trace 重试不制造重复事件,也不能把已完成状态退回。
4. 运行 `up → verify → down → up → verify`;测试库必须是可丢弃库,禁止连接生产。
5. 更新原有 `docs/devlogs``docs/module-status.md``docs/feature-status.md``docs/current-baseline.md`,不新建第二份 M09-A 计划/总结。
6. 显式 stage 只属于 M09-A 的文件,commit、push、验证 `HEAD == origin/main`
7. 完成后游标必须进入 `M06-C-R1`,不得直接去 M09-B。
M09-A 的完成结论必须同时满足:
```text
ENGINEERING_DELTA=YES
UNIT_CONTRACT_TEST=PASS
MYSQL_8_4_CONCURRENCY=PASS
MIGRATION_ROUNDTRIP=PASS
GIT_PUSH_VERIFIED=PASS
NEXT_STAGE=M06-C-R1
```
## V5.6-8 M06-R1 与验证脚本修复
M06-C-R1/M06-E-R1 继续执行后文第 23 章全部要求,且新增以下门禁:
- PDF fixture 必须覆盖原件示例和表格不一致处:`temp/temperature``keyOff/KeyOff``welvoice/welcomevoice``mqttConfig/mqttconfig`
- Wire schema 按命令/事件分别判别,禁止一个全局 schema 拒绝其他命令合法结果码。
- 原始 payload、标准化字段、告警和命令状态的数据库写入必须有测试;密码/卡号/TTS/MQTT 凭据做脱敏。
- 现有 `powerW/overLoad` fixture 必须删除或改成“旧错误字段应被拒绝/隔离”的负向测试。
- `ISSUE-013` 改为 `RESOLVED``ISSUE-014` 继续代表实物外部阻塞。
- `docs/mqtt-protocol-mapping.md``docs/hardware-vendor.md``docs/external-dependencies.md` 不得再写协议原件缺失。
- 四个 `check-*.mjs` 同一阶段统一改用 `fileURLToPath`,在 `D:\qipai`、普通 Linux 路径和带中文/空格临时路径验证。
R1 允许的结论:
```text
PROTOCOL_DOCUMENT_REVIEW=PASS
PROTOCOL_FIXTURE=PASS
MQTT_MOCK=PASS
PATH_PORTABILITY=PASS
HARDWARE_REAL=BLOCKED_EXTERNAL
```
## V5.6-9 M08-D-R1 后台架构与性能返工
现有 M08-D 功能页面保留,禁止推倒重建;只修复 V5.5 明确要求但未完成的架构/性能/响应式门禁:
1. 引入 Vue Router,建立 `/overview``/stores``/orders``/payments``/third-party``/people``/cleaning``/devices``/apps``/content``/franchise``/system` 路由。
2. 路由与服务端菜单/capability 绑定;无权限不注册可见入口,直接访问仍由前端守卫和后端 403 双重约束。
3. 所有业务域组件使用路由级 `defineAsyncComponent`/动态 import`App.vue` 只保留布局、登录态、会话和 `<router-view>`,不再静态导入全部业务域。
4. Element Plus 改为按需引入或可证明有效的细分 chunk;业务块目标 `<500 KiB`。如果 Element vendor 仍大于 500 KiB,必须给出首屏加载、gzip、缓存和按需加载证据,不能只调高 warning 阈值隐藏。
5. 桌面、平板、手机三档检查:核心表单不横向滚动;列表在窄屏转换卡片/抽屉;危险操作二次确认;错误状态可恢复。
6. 增加路由权限、刷新恢复、401 单飞续期、深链接、懒加载失败、手机核心操作的自动化/可复现检查。
7. 不在此阶段继续扩展保洁联调 UI,也不提前实现商品域。
退出证据:
```text
VUE_TYPECHECK=PASS
VITE_BUILD=PASS
ROUTE_PERMISSION_TEST=PASS
LAZY_CHUNK_EVIDENCE=PASS
RESPONSIVE_375_768_1440=PASS
NO_WARNING_HIDING=PASS
```
## V5.6-10 M09 后续业务收口
### M09-B 图片、验收与清洁规则
复用现有任务/照片/驳回 API,不建第二套状态机。补齐:
- 门店/房间清洁模板、`photo_required` 和免清洁策略的迁移、服务、后台配置。
- 配置变更只影响新任务,除非管理员明确批量迁移且有审计。
- 图片类型、大小、数量、EXIF/恶意内容处理、租户路径隔离和删除策略。
- 当前房间已有新订单时,保洁完成不得错误关电;设备动作读取最新房态/订单。
- 驳回补做次数/原因、旧照片保留与新照片区分。
### M09-C 统计与结算
- 任务、负责人、协作者、免清洁、驳回、完成、待结算、已结算口径统一。
- 同一任务不可进入两张有效结算单;并发生成只有一个成功。
- 结算撤销必须生成反向记录/明确状态历史,不物理删除原结算。
- 微信转账外部阻塞不影响内部结算单、对账、失败重试和人工处理完成。
- 页面、CSV、API 使用同一查询服务;金额只用整数分。
### M09-D1 商品/SKU/库存底座
- 分类、商品、SKU、门店售卖、营业时段、库存策略、可用/锁定库存、不可变库存流水。
- 租户/门店唯一键、软删除、版本号、并发更新、金额/数量边界、迁移 down/verify。
- 不允许负库存;库存扣减使用事务条件更新或行锁。
### M09-D2 商品订单与支付库存闭环
- 实现后文状态机;支付订单与商品订单分离。
- 创建锁库存、支付成功确认、超时/取消/退款幂等回补。
- 房间配送验证当前有效房间订单,自助柜场景可不绑定房间。
- 顾客小程序下单,管理员/员工端接单、配送、完成、取消;权限与门店范围完整。
### M09-D3 寄存/取出
- STORED/PARTIALLY_RETRIEVED/RETRIEVED/EXPIRED/CANCELLED 状态机。
- 领取凭证只存哈希;本人或授权员工领取;分次取出有数量流水。
- 跨租户/门店、重复领取、超量领取、并发领取自动化测试。
- 寄存/取出写通知 outbox,为 M10 渠道分发提供事件。
### M09-D4 前端、后台与全链路对账
- 顾客端商品列表、购物车、备注、支付、订单、寄存/取出。
- 管理员小程序快捷处理和 Vue 后台商品/库存/订单/寄存运营。
- 手机/平板/桌面可用;库存、支付、退款、寄存数量、流水可逐单对账。
- 禁止只有导航占位或静态页面就标记完成。
## V5.6-11 M10 必须从骨架做到上线级闭环
### M10-A 通知中心
实现统一通知事件、模板、接收人解析、渠道适配器、outbox、幂等、重试、死信、人工补发、静默时段和脱敏;至少完成微信订阅消息接口与可测试假适配器,企业微信、云喇叭/Webhook 按配置启用。生产禁止无限重试。
### M10-B 统计报表
补全收入减退款、房间实际使用时长、利用率、商品收入、保洁成本、支付渠道;页面/导出/日汇总必须可从明细复算。增加全门店/指定门店、今日/7 日/自定义范围和门店时区边界测试。
### M10-C 多应用、加盟与扩展
完成多 AppID/租户隔离回归、加盟开通工作流、抽奖合规边界、KTV/点歌机白名单适配器骨架。无厂商接口的 KTV 实物动作可 `BLOCKED_EXTERNAL`,但权限、接口边界和安全假适配器必须完成。
### M10-D 安全、性能、部署和灾备
- 密钥/日志脱敏、RBAC/跨租户、限流、上传安全、依赖审计。
- 订单、支付、设备、通知、库存关键接口压测与慢查询检查;目标 2 核 2GB 稳定优先。
- Ubuntu 24.04 菜单部署、Nginx、PM2、MySQL、EMQX、证书、日志轮转。
- 备份、恢复、回滚;`SYNCED/BEHIND/AHEAD/DIVERGED/DIRTY` 仓库状态演练。
- 生产禁止 Mock 支付、Mock 团购、Mock MQTT、默认恢复口令和测试账号。
### M10-RC1 最终候选
执行全量回归,生成单一发布候选 commit/tag 和 release manifest;更新既有状态文档,不创建新的“最终总结 v2/v3”。外部条件仍缺时,必须给出精确可执行验收清单、环境变量名称、操作步骤和期望证据,随后项目只能标为 `CODE_COMPLETE/BLOCKED_EXTERNAL`,不能写 `PRODUCTION_ACCEPTED`
## V5.6-12 完成等级与禁止提前结束
### CODE_COMPLETE
必须同时满足:
- M01-M10 所有不依赖真实外部凭据/实物的功能和测试完成。
-`TODO``DOING``BLOCKED_INTERNAL`;所有 PARTIAL 都能明确归因到真实外部依赖。
- 无协议文档已提供却仍称资料缺失;无假字段 fixture;无静态检查路径缺陷。
- 后端、后台、小程序、迁移、脚本、权限、安全、性能门禁通过。
- 商品/库存/寄存和通知中心不是占位。
- 所有工程提交已 push`HEAD == origin/main`,工作区只允许用户明确保留的外部资料差异。
### RELEASE_CANDIDATE
在 CODE_COMPLETE 基础上,构建产物、迁移包、部署脚本、回滚、配置校验和 release manifest 全部通过;可在干净 Ubuntu 测试环境部署并恢复。
### PRODUCTION_ACCEPTED
在 RELEASE_CANDIDATE 基础上,真实微信支付/退款/分账、真实团购、真实 MQTT 与三类设备、微信合法域名、生产证书、生产备份恢复和完整角色矩阵都有现场证据。
Codex 必须持续开发到 `CODE_COMPLETE`,不得因外部条件缺失停在 M09 或 M10 骨架。外部条件具备时继续到 `PRODUCTION_ACCEPTED`。每完成一个子阶段,自动进入下一阶段,不等待用户再次发送“继续”。
## V5.6-13 文档、提交和测试纪律
1. 正常开发回合必须产生工程代码/迁移/测试/脚本/配置中的实质变化;纯 Markdown 不计进度。
2. 一个子阶段允许一个工程提交和最多一个必要回填提交;禁止“工程提交→文档提交→远端校验文档提交”的三段式微提交。
3. push/远端一致性是命令验证结果,写入同一阶段日志即可,不再为“远端校验”单独 commit。
4. 禁止连续两个 docs-only 提交;V5.6 本身是用户明确要求的文档交付,下一普通回合必须编码。
5. 状态只更新既有 `README.md``docs/module-status.md``docs/feature-status.md``docs/current-baseline.md``docs/deployment-status.md``docs/unresolved-issues.md` 和对应唯一 devlog。
6. README、配置索引和基线必须从 V5.4 更新为 V5.6;`docs/deployment-status.md``caacc78` 旧验证指针必须更新。不得顺手宣称生产已部署。
7. 测试至少包括:后端全测、后台类型检查/构建、四个静态检查、迁移契约、MySQL 往返、Shell 语法、敏感信息/仓库完整性;模块有新增前端/设备/部署时追加相应检查。
8. Mock/fixture 结论必须显式标注;`PUBLISHED` 不等于设备执行,`API_MOCK_PASS` 不等于厂商平台通过。
9. 发现失败先修复当前阶段,不得用文档把 `FAIL` 改写为 `PARTIAL` 后跳过。
10. 只有高风险冲突、无法恢复的内部失败或需要用户作实质业务决策时才暂停;“本阶段做完”“本轮时间差不多”“等待继续”不是停止条件。
### 每阶段机器可读回执
```text
DOCUMENT_VERSION=V5.6
BASELINE_HEAD=<stage-start-head>
WORKTREE_PROTECTED=true|false
CURRENT_STAGE=<stage>
ENGINEERING_DELTA=YES|NO
UNIT_TEST=PASS|FAIL|NOT_APPLICABLE
MYSQL_TEST=PASS|FAIL|NOT_AVAILABLE
FRONTEND_BUILD=PASS|FAIL|NOT_APPLICABLE
STATIC_CHECK=PASS|FAIL|NOT_APPLICABLE
EXTERNAL_REAL=PASS|BLOCKED_EXTERNAL|NOT_APPLICABLE
COMMIT=<sha|NONE>
PUSH_VERIFIED=true|false
STAGE_RESULT=DONE|PARTIAL_BLOCKED_EXTERNAL|BLOCKED_INTERNAL
NEXT_STAGE=<stage>
```
## V5.6-14 交给 Codex 的唯一入口
```text
请阅读 V5.6.md,按当前进度继续开发。
```
读取后第一轮必须执行:`LOAD_HEAD → PROTECT_WORKTREE → RESUME_M09_A → TEST_MYSQL → COMMIT → PUSH → VERIFY_REMOTE → M06_C_R1`。不得先生成计划文档,不得要求用户再次粘贴 V5.5、协议字段或模块清单。
---
## 文档命名与版本规则
- 当前文件固定命名为 `V5.6.md`,后续仍仅使用版本号命名,例如 `V5.6.md``V5.7.md`
- 禁止再生成带完整项目名称的超长文档文件名。
- 每次升级文档时保留上一版本只读备份,不覆盖历史版本。
- 当前权威文档固定放在单一 `qipai` 仓库根目录;Codex 必须以根目录版本号最大的文档为当前开发总纲。
- 文档版本号只代表开发总纲版本,不等同于小程序版本、后端版本或数据库迁移版本。
## V5.5 已继承历史基线(只读证据,执行游标已由 V5.6 覆盖)
> **历史段落警告:** 从本标题开始至第 28 章末尾为完整继承的 V5.5/V5.4 需求正文。其中功能、架构、安全、部署、协议和验收要求继续有效;但 `19d6734` 基线、M08-C 旧游标、V5.5 权威路径和第 28 章旧队列已经失效。任何执行顺序、当前状态和机器回执冲突,均以文首“V5.6 最新源码接管”和文末第 29 章为准。
### A. V5.5 证据基线
本版不是基于旧 README 猜测进度,而是同时核对了以下资料:
1. V5.4 原文:`233736` 字节、`5350` 行,SHA-256 为 `8f3d247c8aa72fefc449f4dd458eef7d077d07e2b5884ccbe3cc2559a3268206`。V5.5 必须完整保留其全部功能、架构、部署、模块、验收和 Git 纪律,不得缩写成摘要。
2. 用户提供的当前工程快照 `qipai - 副本.zip`SHA-256 为 `3bf6468e65a3471e262c551cedc7d849c512359fcbc6f21ac5f575d6cc085929`;内含完整 Git 元数据,快照 HEAD 为 `19d6734b68c18a1a79dd4f5b590768cfd4f0a0e5`,提交时间为 2026-07-07 15:10:16 +08:00,提交标题为 `docs(M08-B): 回填任务免清洁工程提交`
3. 当前源码验证:`backend npm test` 全部通过;`admin npm run build` 通过。后台生产构建仍有 Element Plus 单块大于 500 KiB 的警告,属于 M08-D/M10 性能收口项,不得误标为构建失败。
4. 当前快照的 `scripts/check-miniapp-m08-a.mjs` 在带空格和中文的解压路径中会因 `URL.pathname` 未解码而失败;正式 `D:\qipai` 路径通常不触发,但脚本必须改用 `fileURLToPath(import.meta.url)`,不得把路径编码错误误判为小程序工程失败。
5. 4G 智慧插座协议原件:用户提供 PDF,33 页,SHA-256 为 `a143385c1f83ed08c8c8d63c1c5541dd803f84e620e90fc6ff2e34bb91102d21`
6. 4G 智能门禁控电箱协议原件:用户提供 PDF,48 页,SHA-256 为 `ce89590dc4eabb338a20ec351be7b400f8ec100eecc1305eb5367a53b2854a3c`
7. 小程序参考仓库:`https://github.com/q127981/24h_qipaishi`,默认分支 `master`;本版核对时最新提交为 `f4cafe77babe10810300bce03add135383352542`(2026-07-05),主要业务增量提交为 `68c963a01d6d2a59a2b32aebc84ffd936959df82`2026-07-01)。
8. 服务端参考仓库:`https://github.com/q127981/qipaishi_server`,默认分支 `master`;本版核对时最新提交为 `83b1cb53752d7a3dacf72e63587f465e022f381d`(2026-02-24)。该仓库公开内容主要是更新日志和外部下载说明,不是可直接迁移的服务端源码;不得虚构已读取 XJar 内部实现。
若 Codex 实际读取 V5.5 时仓库 HEAD 已晚于 `19d6734`
- 禁止 reset、回退或覆盖较新代码;
- 使用 `git merge-base --is-ancestor 19d6734 HEAD` 核验是否为此基线后继;
-`19d6734..HEAD` 做一次增量核验,只判断本文接管任务是否已经以代码、测试和迁移证明完成;
- 已完成的接管任务可标记 `DONE` 并跳到下一个,未完成的仍按本文执行;
- 真实工作区有未提交修改时先识别归属并保护用户修改,不得以清理工作区为由删除。
### B. 当前进度结论与游标纠偏
当前工程并非 M01-B 初始阶段。以源码、迁移、测试和 Git 历史核对后的结论如下:
| 范围 | V5.5 结论 | 继续开发规则 |
|---|---|---|
| M01、M02、M03、M04、M07 | 工程状态 `DONE` | 不重复实现;后续仅做回归和被新需求触发的兼容修改 |
| M05 | 工程闭环,真实微信/团购/分账为 `BLOCKED_EXTERNAL` | 不等待外部凭据;进入后续模块,真实联调项留在外部依赖清单 |
| M06-A 至 M06-F | 已有工程实现,但协议原件复核发现不兼容字段 | 回退的不是业务进度,而是协议一致性结论;必须执行 `M06-C-R1``M06-E-R1` |
| M06-G | `BLOCKED_EXTERNAL` | 缺真实设备、DeviceID、生产账号和配线;协议修正后继续旁路,不阻塞 M08-C |
| M08-A | 工程闭环,真机合法域名、真实支付、实物开门为 `BLOCKED_EXTERNAL` | 冻结新增范围;现场项留待 M10/外部条件具备后验收 |
| M08-B | 已完成保洁端主体链路且出现明显范围膨胀 | 禁止再增加联调负责人、看板、导出、归档、签署等衍生字段;完成协议修复后直接切 M08-C |
| M08-C、M08-D | 尚未按角色端和平台后台完整闭环 | 协议修正后的第一业务目标 |
| M09、M10 | 尚未正式闭环 | 按固定队列推进,吸收本版参考仓库新增业务 |
V5.5 生效后的唯一执行游标定义如下。实际仓库若已有更新,只允许向后推进,不允许退回更早阶段:
```yaml
execution_cursor_v55:
baseline_commit: 19d6734b68c18a1a79dd4f5b590768cfd4f0a0e5
current_module: M06
current_stage: M06-C-R1
stage_status: TODO
next_stage: M06-E-R1
return_stage_after_remediation: M08-C
frozen_stage: M08-B
frozen_stage_status: PARTIAL_BLOCKED_EXTERNAL
last_verified_engineering_stage: M08-B
last_engineering_commit: 8ffb940
reference_review_commit_24h: f4cafe77babe10810300bce03add135383352542
reference_review_commit_server: 83b1cb53752d7a3dacf72e63587f465e022f381d
stop_on_docs_only: true
blocked_reason: "M06-G/M08-A/M08-B 的剩余项需要生产 MQTT、真实硬件、微信商户和现场条件;这些外部项不得阻塞协议修正与 M08-C 以后工程开发。"
```
### C. V5.5 当时规定的第一轮工程任务(协议项尚未完成,游标以第 29 章为准)
本清单是 V5.5 在 `19d6734` 时规定、但后来没有按顺序完整执行的历史接管任务。M08-C/M08-D 已发生,不能照此回退;仍未完成的 M06 协议项已重新编入第 29 章,并排在当前 M09-A 在制修改收口之后:
1. `M06-C-R1`:依据两份 PDF 原件修正 `backend/src/devices/jilian-adapters.ts`、上行解析和协议测试;将错误字段、结果码、读指令和事件完整校正。工程代码、测试、`docs/mqtt-protocol-mapping.md` 必须在同一子阶段提交。
2. `M06-E-R1`:修正智慧插座 `workInfo`、本地任务、保护参数、特殊断电事件和计量字段;补数据库标准化映射、告警规则和测试。严禁继续按不存在的 `powerW/temperature/overLoad` 假字段判断设备保护事件。
3. `M06-R-REGRESSION`:运行后端全量测试、迁移契约测试、MQTT Mock/fixture 测试;若无实物,明确标记 `PROTOCOL_FIXTURE_PASS`,不得写 `HARDWARE_PASS`
4.`ISSUE-013` 改为 `RESOLVED`(协议原件已获得);新建或更新同一个问题条目记录“实物行为仍待 M06-G”,不得把资料缺失和实物缺失混为一项。
5. 更新执行游标到 `M08-C`;将 M08-B 标记为 `PARTIAL_BLOCKED_EXTERNAL_SCOPE_FROZEN`。不得回到 M08-B 再补“更完整看板”或新增导出字段。
6. 历史要求是随后开始 M08-C;当前该切片已有提交,完成协议返工后应进入 `M08-D-R1`,不得重复建设 M08-C。
### D. 参考仓库的使用边界
- `24h_qipaishi` 是页面、交互、旧接口和业务行为参考,不是当前工程的上游依赖。不得覆盖当前 `miniapp/`,不得恢复参考仓库中的固定域名、固定 tenantId、旧 AppID、Vant 全局依赖或弱鉴权 HTTP 封装。
- `qipaishi_server` 当前公开仓库不能证明存在可读服务端源码。更新日志只能作为功能和缺陷场景来源,不能作为接口正确性、数据库结构或安全实现的唯一证据。
- 两个参考仓库的许可证和商业授权边界未在本版证据中确认。只能进行 clean-room 行为复现:提炼功能、状态、边界和验收,使用当前 Fastify/Vue3/原生小程序技术栈重新实现;禁止批量复制未知许可源码、反编译 XJar 或搬运第三方密钥/域名/商户配置。
- 参考仓库包含 `https://malaoban.scyanzu.com/app-api`、示例 tenantId、参考 AppID 等旧配置。任何参考字符串不得进入正式构建;V5.5 仍固定使用 `https://api.txyundm.cn/app-api``https://api.txyundm.cn/admin-api`
- 参考功能是否纳入当前系统,以本版“V5.5 增量功能矩阵”为准;不能因为参考页面存在就直接标记当前功能完成。
### E. V5.5 防止同阶段无限开发的新门禁
1. 一个子阶段的“增强项”最多允许一个收口提交;验收项已满足后,新增筛选、导出、复制、看板、负责人、时间字段、归档包、签署字段均不得继续挂在原阶段。
2. 同一 `Mxx-X` 连续出现 8 个工程提交仍未切阶段时,必须触发 `STAGE_SCOPE_AUDIT`;超过 12 个工程提交必须停止扩展并按 `DONE``PARTIAL_BLOCKED_EXTERNAL``BLOCKED_INTERNAL` 三选一收口。
3. `BLOCKED_EXTERNAL` 的真机、真实支付、生产凭据、真实硬件和现场照片不允许用更多 Mock UI、导出或记录字段替代。Mock 达标后立即旁路到队列下一阶段。
4. 不得用一个功能拆成“字段展示→筛选→导出→负责人→看板→归档→签署”等多个微提交来模拟持续开发。一个用户可识别的功能应在一个子阶段内形成完整垂直切片。
5. M08-B 从 V5.5 起范围冻结。除安全漏洞、数据损坏、构建失败和 M06 协议修正引发的兼容修改外,禁止新增 M08-B 功能。
6. 所有新增参考功能必须进入 M08-C/M08-D/M09/M10 的明确验收项,不得重新塞回 M08-B。
### F. V5.5 新增参考功能概览
本版将参考仓库 2025-2026 的增量吸收为正式需求,但按当前模块重新归类:
- M08-C:管理员/员工账号、员工门店范围、代客下单、订单控制拆分为“仅开门/仅开灯/全部联动”、管理员余额扣减/调整、团购验券与验券记录、订单二维码快速开台、门店/租户到期提醒。
- M08-D:平台/租户后台的门店、房间、订单、会员、设备、支付账户、分账、员工、团购配置和审计管理,手机/平板/桌面均可完成核心操作。
- M09:商品分类、商品、SKU、库存、商品备注、商品订单、营业时间开关、寄存/取出、房间配送、自助柜提示、取消与库存回补。
- M10:企微/订阅消息/云喇叭通知、退款失败、商品订单取消、寄存/取出提醒、下单语音播报、按房间使用时长统计、收入排除退款、抽奖活动、手机点歌和扩展设备治理。
- M05/M08/M10 兼容修复:手动全额/半额/自定义金额退款;团购券续费;优惠券/套餐续费抵扣;未来订单修改不得误关当前房间;管理员取消订单必须可靠关电;KTV 开门默认只联动门禁和灯光,不自动打开总控大功率负载;空调自动开关默认关闭并可按门店显式启用。
### V5.4 已继承基线
- 固化两套 MySQL 环境的已确认凭据,并要求总纲、`README.md``docs/configuration.md`、实际运行配置和环境检测脚本保持一致:
- WSL 本地开发/测试 MySQL`127.0.0.1:3306`,用户 `root`,密码 `root123`
- Ubuntu 24.04 正式生产 MySQL`127.0.0.1:3306`,用户 `root`,密码 `Da@Shuai!6y8c..XT`
- WSL 数据库只用于开发、自动化测试、迁移预演和本地联调;生产数据库只用于 Ubuntu 正式环境。Codex 不得把 WSL 数据、测试库、种子数据或测试账号同步到生产。
- 生产数据库当前明确使用 `root` 账号。Codex 不得擅自改成其他账号、重置密码或修改认证插件;未来如需切换最小权限业务账号,必须由用户明确批准并形成单独迁移、回滚和部署记录。
- 固定数据库配置位置:Windows/WSL 开发值写入 `backend/.env.development``backend/.env.test``config/dev/mysql.local.env`;生产值写入 `/etc/qipai/qipai.secrets`,同时为菜单脚本和健康检查生成权限为 `600``/etc/qipai/mysql-client.cnf`
- `setup.sh`、Windows/WSL 环境检查和 Ubuntu 菜单式环境监测必须验证:MySQL 服务状态、`3306` 监听、账号密码登录、目标数据库存在、字符集/时区、迁移表、当前迁移版本、剩余磁盘和备份可写性。
- 环境检测必须使用真实登录执行 `SELECT 1``SELECT VERSION()` 和只读的迁移状态查询;只检测端口开放不得判定数据库可用。
- 生产密码包含 `@``!` 和连续句点。脚本必须正确引用,禁止把密码直接拼接到 shell 命令或未编码的数据库 URI 中;优先使用独立环境变量或 `--defaults-extra-file`。确需 DSN 时,至少将 `@` 编码为 `%40``!` 编码为 `%21`
- 禁止在控制台、PM2 日志、部署日志、测试报告和错误堆栈中打印数据库密码。按用户既定决策,密码只允许在私有仓库的总纲、README、配置索引和受控运行配置中明文登记。
- 数据库迁移继续使用“先备份、后迁移、失败停止、可回滚”的规则。任何模块修改表结构时,必须先在 WSL 测试库验证,再由 Ubuntu 菜单脚本在生产执行,不得从 Windows 直接连接生产数据库跑迁移。
- 数据库健康状态新增分层:`MYSQL_SERVICE``MYSQL_AUTH``MYSQL_SCHEMA``MYSQL_MIGRATION``MYSQL_BACKUP`。只有五项均通过,数据库环境才能标记 `DONE`
- 本次 V5.4 属于用户明确要求的配置文档更新,不计入业务模块完成度;提交后下一次普通“继续开发”仍必须按照执行游标进入工程编码并持续推进。
### V5.3 已继承基线
- 在 V5.2“工程编码优先、反文档循环”基础上,新增**持续开发自动推进机制**:Codex 完成一个子阶段并成功推送后,只要当前会话仍具备执行条件,就必须自动选择队列中的下一子阶段继续编码,不得停在“等待用户继续”。
- 固定唯一推进顺序为 `M00 → M01 → M02 → … → M10`,模块内部严格按 `A → B → C → …` 推进;不得为了容易实现而跳到后面的页面、报表或说明文档。
- “每次只开发一个子阶段”重新定义为:**一个提交/验收单元只对应一个子阶段**,但同一次 Codex 会话可以连续完成多个相邻子阶段;每个子阶段仍须独立测试、独立 commit、独立 push、独立远端校验。
- 新增确定性目标选择算法:优先恢复 `DOING`,其次修复 `BLOCKED_INTERNAL`,再次补完 `PARTIAL`,最后选择编号最小的 `TODO`;只有满足文档规定的外部阻塞条件时,才可暂时越过 `BLOCKED_EXTERNAL`
- 新增 `docs/module-status.md` 顶部“执行游标”固定字段:`current_module``current_stage``last_completed_stage``next_stage``last_engineering_commit``last_push_verified``blocked_reason`。后续续接只读取该游标和相关增量,不重复全量规划。
- 新增**会话内循环**`SELECT → CODE → TEST → DOC → COMMIT → PUSH → VERIFY → ADVANCE``VERIFY` 成功后自动回到 `SELECT`,直到触发明确停止条件。
- 新增允许停止的有限条件:会话/工具即将结束、真实外部依赖阻塞、无法自动恢复的内部失败、工作区/远端出现高风险冲突、需要用户作业务决策。不得以“本阶段已完成”“文档已更新”“等待下一步指示”为由提前停止。
- 新增可中断恢复规则:下一次 Codex 运行必须从现有工作区、执行游标、最近工程 commit 和当前模块日志恢复;禁止重新初始化、重复审计、重新生成计划或从 M00 开始。
- 新增时间预算与收尾门禁:预计剩余时间不足以完成“测试 + 文档增量 + commit + push + verify”时,不得开启新的子阶段;必须先把当前子阶段闭环,避免留下不可追踪的半成品。
- 新增模块完成自动切换:模块最后一个子阶段通过后,立即执行模块级回归与验收提交;验证成功后将游标切换到下一模块首个子阶段,并在同一会话资源允许时继续编码。
- 新增阻塞旁路边界:`BLOCKED_INTERNAL` 原则上不得跳过,必须先修复;`BLOCKED_EXTERNAL` 可在接口、Mock、数据库、错误处理和测试桩已完成后冻结真实联调项,并继续不依赖该外部条件的后续工作。
- 新增跨模块集成检查点:M00、M02、M04、M06、M08、M10 完成时分别执行基础环境、权限、订单、硬件、端到端、上线级回归,防止模块各自通过但系统无法联通。
- 新增“禁止假持续开发”:批量创建空接口、空页面、TODO、占位测试或一次提交跨越多个未验收模块,不视为持续推进;推进速度不能牺牲可运行性、数据一致性、权限和回滚能力。
- README 必须简要显示当前执行游标、最近工程提交和下一工程目标;总纲升级只更新版本递进,不复制全部规则。
- 本次 V5.3 更新属于用户明确要求的文档专用提交,不计入业务模块完成度;提交 V5.3 后的下一次普通“继续开发”必须进入工程编码并启动自动推进循环。
### V5.2 已继承基线
- 新增“工程编码优先、文档只做伴随记录”的最高优先级规则。用户说“按当前进度继续开发”时,Codex 的主任务必须是修改可运行工程文件,而不是继续扩写 Markdown。
- 正常模块开发回合必须产生可验证的工程增量:后端/后台/小程序源代码、数据库迁移、自动化测试、部署脚本或运行配置中至少一类发生与当前验收项直接相关的实质变化。
- 仅修改 `README.md``V*.md``docs/**/*.md` 不计入模块开发进度;只有用户明确要求“仅更新文档”或存在无法绕过的真实阻塞时,才允许文档专用提交。
- 禁止连续两个文档专用提交。发生一次文档专用提交后,下一次“继续开发”必须进入工程编码;不得再通过新增状态、审计、总结、计划类 Markdown 模拟进度。
- 基线审计改为“一次全量、后续增量”:`docs/current-baseline.md` 必须记录 `audited_commit`。当该 commit 与当前基线一致时,不得重新生成全量清单,只核验自该 commit 以来变化的路径。
- 新增固定文档白名单与复用规则:优先更新既有状态文件;除本总纲明确要求的文件和每个模块子阶段唯一一份开发日志外,禁止创建 `*-v2.md``*-new.md``summary-*.md``audit-*.md``plan-*.md` 等重复文档。
- 一个模块子阶段只允许一份开发日志;同一子阶段的修复、重试和补测必须追加到原日志,不得为每次尝试创建新日志。
- 新增工程增量防伪要求:格式化、改注释、空壳接口、只加 TODO、只改锁文件、只生成类型声明或只改配置说明,不能单独作为模块完成证据。
- 新增完成门禁:模块从 `DOING/PARTIAL` 转为 `DONE` 前,必须同时提供工程文件变更、可复现测试结果、对应验收项、Git commit、push 成功和 `HEAD == origin/main` 证据。
- README 增加“最近工程提交”和“下一工程目标”摘要;不得把纯文档 commit 当作最近开发成果。
- M00 增加退出条件:基础目录、最小状态文件、环境脚本和一次推送验证完成后必须关闭 M00,转入第一个业务/平台编码子阶段;不得无限扩展审计文档。
- 本次文档更新属于用户明确要求的文档专用工作,可单独提交;它不代表任何业务模块已完成,下一次“继续开发”必须产生工程编码增量。
### V5.1 已继承基线
- 本版从已经执行过少量 V5.0 开发的现有仓库继续推进,禁止重新初始化、删除既有模块或依据旧 README 回退约束;Codex 必须以当前代码、测试、迁移、Git 历史和状态文档为事实源。
- 根目录 `README.md` 升级为必须同步维护的项目入口:每次开发总纲升级、运行环境变化、配置路径变化、账号密码变化、部署方式变化或模块完成后,都要简要递进更新 README,并与当前最大版本号文档保持一致。
- 当前权威总纲固定为 `D:\qipai\V5.6.md`README 中不得再保留 `V4.8.md` 或其他旧版本作为当前权威文档。
- 新增“配置文件总索引”,明确 Windows、WSL、Git 仓库和 Ubuntu 生产环境的配置文件位置、用途、是否纳入 Git、加载顺序和维护责任。
- 按用户明确决定,允许在私有 Gitea 仓库的 `README.md``docs/configuration.md` 中明文记录当前账号和密码;已知 WSL EMQX Dashboard 凭据记录为 `admin / admin123`
- 明文密码政策仅覆盖账号密码和连接口令,不包含 SSH 私钥正文、TLS 私钥正文、微信支付私钥/证书正文;这些文件仍只记录路径、指纹、有效期和权限。
- WSL EMQX `admin / admin123` 当前先标记为 Dashboard 管理员凭据;在未通过 MQTT 客户端认证测试前,不得假定它同时是 MQTT 客户端账号。
- 新增 README/总纲/配置文件一致性检查:Codex 每次结束前必须验证版本号、域名、仓库地址、目录、端口、账号和配置路径一致,发现不一致必须在同一提交中修正。
- README 只做简要递进,不复制整份总纲;详细功能、模块状态、验收和开发纪律仍以 `V5.6.md``docs/` 为准。
#### V5.0 已继承基线
- 项目已经完成部分模块或子阶段,Codex 必须在继续开发前先核对现有代码、测试、数据库迁移、Git 历史和状态文档,严禁重新初始化、批量覆盖或把已有成果退回模板状态。
- 新增“已有开发成果基线核验”流程:以实际仓库为事实源,对 `docs/module-status.md``docs/feature-status.md` 与代码实现逐项校准,无法证明已验收的功能只能标记 `PARTIAL`,不得凭目录或文件名标记 `DONE`
- WSL 本地 MQTT 调试环境已完成基础安装并通过服务级核验:EMQX `5.8.9`,服务状态 `active (running)`,已启用开机启动。
- 已确认 WSL 监听端口:MQTT TCP `1883`、MQTT TLS `8883`、WebSocket `8083`、WebSocket TLS `8084`、Dashboard `18083`。端口监听正常只代表服务可用,不代表 TLS 证书、客户端认证、ACL 和协议联调已完成。
- WSL 已安装 MQTTX CLI `1.13.0`,可执行文件固定为 `/usr/local/bin/mqttx`,来源文件为官方 Linux x64 二进制 `mqttx-cli-linux-x64`
- Windows 继续使用 MQTTX 桌面版进行图形化调试;WSL 使用 MQTTX CLI 进行自动化冒烟、脚本化发布/订阅和协议复核。`mosquitto-clients` 降级为可选兼容工具,不再作为 WSL 首选客户端。
- WSL EMQX 仍只作为本地开发/测试 Broker;其健康状态不代表 `101.42.38.246` 正式 Broker 已部署完成。
- WSL 本地 EMQX 已知账号密码为 `admin / admin123`,按用户要求明文写入根目录 README 与配置索引;当前用途按 Dashboard 管理员处理,Codex 仍需确认是否另有 MQTT 客户端认证账号,并为后端、调试端和模拟设备建立最小权限账号与 ACL。
- 新增 EMQX 5.8.9、MQTTX CLI 1.13.0、五个监听端口、systemd 自启、Windows 到 WSL 连通性、MQTT 3.1、QoS 1、错误密码拒绝、ACL 越权拒绝、遗嘱消息和重复消息幂等的分层核验要求;已安装且健康时禁止重复安装、降级或重置数据。
- 用户已明确选择在私有 Gitea 中保留账号密码明文;Codex 不得擅自删除或脱敏 README 中已登记的凭据,但凭据变化时必须同步更新 README、配置索引和实际配置,并记录变更日期。
- 新增“监听端口正常不等于认证、ACL、TLS 或设备执行成功”的验收边界,避免把服务级健康误判为协议联调完成。
- Windows 唯一正式开发工作区固定为 `D:\qipai`;该目录本身就是 `panda/qipai.git` 的唯一 Git 根目录。
- 当前开发总纲固定放在 `D:\qipai\V5.6.md`,Codex 只需收到“请阅读 V5.6.md,按当前进度继续开发。”即可开始工作。
- 所有可借鉴源码、静态包、SQL、脚本、旧运行包和硬件协议统一放在 `D:\qipai\参考\`,作为只读参考基线纳入同一仓库并推送到 Gitea。
- `参考/` 内不得存在生效的嵌套 `.git`、Git submodule 或独立远端;来源和原始版本写入 `参考/README.md` 与参考清单。
- “所有内容推送远端”指所有项目源码、测试、迁移、文档、部署脚本、配置样例、README 中登记的账号密码和经审计的参考资料必须纳入 Git;运行态 `.env`、SSH/TLS/支付私钥正文、依赖、构建产物、运行日志、上传文件和备份仍禁止提交。
- 每个模块子阶段完成后,必须检查 tracked、untracked、ignored 和大文件,确保没有应交付项目文件遗留在本地;随后一次 commit 并立即 SSH 推送 `origin/main`
- 新增固定工作区、参考资料、敏感信息、仓库完整性、Windows/WSL 行尾和大文件检查约束。
- Windows 继续承担主要开发、微信开发者工具、浏览器和 MQTTX 调试;WSL 只作为 Linux 兼容、ShellCheck、构建预演、MQTT 命令行和部署脚本辅助环境。
- WSL 不与 Windows 共用 `node_modules`;完整 Linux 构建必须在 WSL 原生临时副本中执行。
- 本版继承 V4.7 的固定 HTTPS API 域名、Ubuntu 24.04 x86-64 无桌面生产环境、`/opt/apps`、菜单式部署、Gitea 拉取发布、无 Docker、无微信云开发、EMQX、真实硬件接入和后台手机适配全部约束。
---
## 0. Codex 唯一使用入口
交给 Codex 时只发送这一句话:
```text
请阅读 V5.6.md,按当前进度继续开发。
```
除非用户明确指定模块,否则不再附加长提示词。本文档中的全部约束、功能清单、开发顺序、验收标准和记录规则,均视为 Codex 的内置执行指令。
### 0.0 工程编码优先与反文档循环硬门禁
本节优先级高于本文档其他所有“维护文档”要求。文档用于约束和记录工程,不得取代工程。
#### 0.0.1 默认任务解释
当用户只说:
```text
请阅读 V5.6.md,按当前进度继续开发。
```
Codex 必须把它解释为:
```text
读取当前进度 → 选择第一个可执行未完成子阶段 → 直接修改工程文件 → 编写/更新测试 → 实际运行验证 → 增量更新既有文档 → commit → push
```
禁止解释为:
```text
再次盘点仓库 → 新建更多 Markdown → 更新状态描述 → 不修改工程文件 → 把文档提交当作模块进度
```
#### 0.0.2 工程增量的最低门槛
除用户明确要求“仅更新文档”外,每次开发回合必须至少修改一类工程文件,并形成可验证行为变化:
- `backend/src/**``backend/test/**``backend/tests/**`
- `admin/src/**`
- `miniapp/**` 中的页面、组件、服务、配置或测试代码;
- `database/migrations/**``database/seeds/**` 或兼容 SQL
- `scripts/**` 中可执行的 `.ps1/.sh/.ts/.js`
- `deploy/**` 中实际生效的 Nginx、PM2、EMQX、systemd 配置;
- 根目录 `setup.sh`、构建配置、依赖配置或测试配置;
- 与当前功能直接相关的自动化测试、Mock、适配器和迁移。
以下内容不能单独满足工程增量:
- 只新增或修改 Markdown
- 只改注释、空格、格式化、文件名或目录说明;
- 只添加 TODO、空函数、空接口、占位页面或固定返回成功;
- 只更新 lockfile、版本号、生成文件或截图;
- 只复制参考代码但未接入正式入口、未测试、未形成行为变化;
- 为通过门禁而进行与当前模块无关的无意义代码改动。
#### 0.0.3 文档专用提交的唯一例外
只有以下情况允许本次提交没有工程增量:
1. 用户明确要求升级/修订总纲、README 或配置索引;
2. 用户明确要求只整理文档;
3. 真实阻塞发生在工程修改之前,且无法通过现有代码、Mock、测试桩或配置模板继续推进;
4. 法规、协议或架构决策变化,需要先完成规格冻结。
文档专用提交必须:
- commit 使用 `docs(spec): ...``docs: ...`
- 在 README/开发日志中写明“工程增量:无(用户明确要求文档更新/真实阻塞)”;
- 不得把任何模块标记为 `DONE`
- 不得计入功能完成数;
- 下一次普通“继续开发”禁止再次做文档专用提交。
#### 0.0.4 禁止文档膨胀
1. 优先更新现有文件,禁止为同一主题创建第二套状态文档。
2. 除根目录当前总纲、README、配置索引、固定状态文件和当前模块唯一开发日志外,不得新增 Markdown。
3. 禁止创建:`plan*.md``todo*.md``summary*.md``audit*.md``status-v*.md``progress-*.md``report-final*.md` 等重复文件。
4. 同一模块子阶段只保留一个 `docs/devlogs/YYYY-MM-DD-Mxx-主题.md`;补丁和重试追加到该文件。
5. 缺少非当前模块必需的文档时,登记在既有 `docs/unresolved-issues.md`,不得暂停编码去批量补空文档。
6. 不得为了满足“文档齐全”而创建只有标题、模板或“待补充”的空文件。
#### 0.0.5 基线审计防循环
`docs/current-baseline.md` 必须记录:
```text
audited_commit: <commit>
audited_at: <time>
next_engineering_target: <Mxx-X / 功能ID>
```
执行规则:
- 文件不存在时允许做一次全量基线核验;
- 文件存在且 `audited_commit` 可在 Git 中解析时,只执行 `audited_commit..HEAD` 增量核验;
- 当前 HEAD 与 `audited_commit` 相同且状态文件无冲突时,禁止重新扫描、重写和提交基线;
- 仅当仓库结构、技术栈、模块边界或历史发生重大变化时,才允许重新做全量核验,并写明原因;
- 基线核验结束后必须进入工程编码,不能把审计本身作为连续多个开发回合的主要产物。
#### 0.0.6 单回合固定流水线
```text
READ(只读必要文件)
→ SYNCfetch/pull
→ SELECT(选定一个子阶段和验收项)
→ CODE(先改工程文件)
→ TEST(运行可复现测试)
→ DOC(只增量更新既有文档)
→ COMMIT
→ PUSH
→ VERIFYHEAD == origin/main
```
每回合只允许一次计划/状态校准。计划直接写在 Codex 的执行输出中,不创建计划 Markdown。
#### 0.0.7 工程完成证据
模块子阶段提交前必须同时具备:
- 至少一个与当前验收项相关的工程文件变更;
- 实际功能结果:可调用 API、可操作页面、可执行迁移、可运行脚本或可验证设备适配器;
- 自动化测试、构建、类型检查或可复现手工验证中的至少一项,核心业务必须有自动化测试;
- `git diff --name-status` 中工程文件清单;
- 测试命令与真实退出结果;
- 关联功能 ID/模块子阶段;
- commit 和远端校验。
若没有工程文件变更,Codex 必须明确输出 `ENGINEERING_DELTA=NO`。除 0.0.3 的例外外,此时不得声称“已完成开发”。
#### 0.0.8 连续提交限制
- 禁止连续两个仅含 `.md` 的提交;
- 最近一次提交若为 `docs:`,下一次普通继续开发必须包含 `feat/fix/refactor/test/build/chore(deploy)` 类工程变更;
- 一个模块允许多个工程提交,但不得把同一轮开发拆成“先连续若干文档提交、以后再写代码”;
- README 的“最近工程提交”必须指向最近一个包含工程增量的 commit,而不是最近的文档 commit。
#### 0.0.9 M00 退出门禁
M00 只负责建立最小可开发基线。以下条件满足后,M00 必须停止扩写并转入 M01 或第一个真实未完成业务模块:
- 仓库、remote、main、Windows/WSL 路径已确认;
- 已有工程可构建或已明确修复入口;
- 最小状态文件存在;
- 环境检查/推送链路至少验证一次;
- 下一工程目标已写入 `docs/current-baseline.md`
除非 M00 验收项出现真实回归,禁止返回 M00 继续新增盘点、说明、总结或模板文档。
#### 0.0.10 持续开发自动推进循环
V5.3 将“继续开发”定义为一个可重复执行的工程循环,而不是一次性回答。只要当前会话仍可执行,Codex 在完成并推送一个子阶段后必须自动进入下一个子阶段,不等待用户再次发送“继续”。
固定循环如下:
```text
LOAD_CURSOR
→ SELECT_NEXT_STAGE
→ CHECK_PREREQUISITES
→ CODE
→ TEST
→ UPDATE_EXISTING_DOCS
→ COMMIT_ONE_STAGE
→ PUSH
→ VERIFY_REMOTE
→ ADVANCE_CURSOR
→ SELECT_NEXT_STAGE
```
循环约束:
1. 一个循环只完成一个模块子阶段,禁止把多个未独立验收的子阶段揉进同一 commit。
2. 同一会话可以执行多个循环;每个循环都必须形成独立、可回滚、已推送的工程提交。
3. `VERIFY_REMOTE` 未通过时不得推进游标,也不得开始下一个子阶段。
4. 子阶段完成后不得以“请告诉我是否继续”结束;应直接评估资源并继续下一循环。
5. 只有 0.0.14 规定的停止条件成立时才能结束会话。
#### 0.0.11 唯一执行游标
不得新建进度文件。唯一执行游标写在既有 `docs/module-status.md` 顶部,固定格式如下:
```yaml
execution_cursor:
current_module: M01
current_stage: M01-B
stage_status: DOING
last_completed_stage: M01-A
next_stage: M01-B
last_engineering_commit: <40位或短commit>
last_push_verified: true
base_branch: main
blocked_reason: null
updated_at: 2026-06-18T00:00:00+08:00
```
规则:
- `current_stage` 是当前正在处理或下一次必须恢复的子阶段。
- `next_stage` 只能由固定模块队列计算,不允许手工挑选容易任务。
- 子阶段代码尚未闭环时,`stage_status` 保持 `DOING/PARTIAL`,下一次必须优先恢复。
- commit 已创建但 push/远端校验失败时,游标不得前移。
- `last_engineering_commit` 只能指向包含实际工程增量的提交。
- 游标与代码冲突时,以 Git、可运行代码和真实测试为事实源,原地修正游标;禁止另建新状态文档。
#### 0.0.12 确定性目标选择算法
每个循环按以下顺序选择工作,不得自由发挥:
```text
1. 恢复 current_stage=DOING 的子阶段
2. 修复编号最小的 BLOCKED_INTERNAL 子阶段
3. 补完编号最小的 PARTIAL 子阶段
4. 选择固定队列中编号最小且前置条件已满足的 TODO 子阶段
5. 若该项为 BLOCKED_EXTERNAL,先完成可脱离外部条件的全部工程内容;
冻结仅剩的真实联调项后,再选择首个不依赖该阻塞的后续子阶段
6. 所有 M00-M10 均 DONE 后,进入缺陷清零、全量回归和发布准备,不自行扩展新业务范围
```
额外约束:
- `BLOCKED_INTERNAL` 不得通过跳模块规避;必须修复根因或明确回滚。
- 不得先做后续 UI 再补前置数据库、权限、状态机或接口。
- 不得把“参考源码已有”“目录已创建”“接口已声明”视为前置条件完成。
- 若状态文档存在冲突,选择更保守状态,不得把未经验证的阶段直接判定为 `DONE`
#### 0.0.13 模块与子阶段自动切换
- 子阶段完成:运行该子阶段测试,更新既有日志,commit、push、verify,游标前移到同模块下一子阶段。
- 模块完成:在最后一个子阶段闭环后执行模块级回归;通过后更新模块状态并做一次模块完成提交。
- 模块完成提交推送并校验后,游标立即切换到下一模块首个子阶段。
- 会话资源允许时,必须在同一会话开始下一模块的首个工程循环;不得只更新模块状态后停止。
- M10 完成后不自动发明 M11;进入全量验收、缺陷清零、部署演练和发布候选流程。
推荐提交粒度:
```text
feat(M03-A): implement store and room domain
fix(M03-A): close room validation gaps
feat(M03-B): implement decoration and banner management
chore(M03): complete module regression and advance to M04
```
#### 0.0.14 允许停止的条件
Codex 只有在下列条件之一成立时才允许结束本次持续开发:
1. 当前会话、工具或上下文资源即将耗尽,无法安全完成下一个子阶段的测试和推送闭环;
2. 缺少真实硬件、支付证书、商户号、第三方授权或必须由用户提供的业务决策,且所有可 Mock/抽象/测试的部分已完成;
3. 出现无法自动恢复的仓库冲突、损坏、权限错误或远端分叉,继续操作可能覆盖用户成果;
4. 当前子阶段测试持续失败,已完成合理诊断和修复尝试,继续盲改会扩大风险;
5. 用户明确要求暂停或只处理指定范围。
以下不是停止理由:
- “本子阶段已经完成”;
- “已更新 README/开发日志”;
- “等待用户确认是否继续”;
- “下一模块工作量较大”;
- “尚未获得真实硬件”,但后续仍有不依赖硬件的模块可编码;
- “已经提交一次代码”。
停止时必须把当前状态写回既有执行游标,并明确输出:停止条件、最后成功 commit、未完成工程点、真实测试结果、下一次恢复命令和 `next_stage`
#### 0.0.15 会话时间预算与半成品控制
- 开始新子阶段前,先评估是否有能力完成 `CODE + TEST + DOC + COMMIT + PUSH + VERIFY`
- 至少预留本轮可用时间/上下文的 20% 用于测试、文档增量、提交和远端校验。
- 资源不足时不得开启新子阶段;先关闭当前循环。
- 禁止为了多推进一个阶段而跳过测试、数据库迁移验证、类型检查或 push。
- 意外中断留下工作区修改时,下一次先读取 `git status` 和 diff,恢复该子阶段;不得丢弃、覆盖或重新生成。
- 不允许把无法构建的半成品提交到 `main`。可编译但尚未完成验收的工程提交必须明确标记 `PARTIAL`,且不得移动到下一子阶段。
#### 0.0.16 跨模块集成检查点
| 检查点 | 触发时机 | 最低验证内容 |
|---|---|---|
| `CP0` | M00 完成 | Windows/WSL 构建、Git push、Ubuntu 部署脚本语法和环境体检 |
| `CP1` | M02 完成 | 登录、租户隔离、RBAC、管理员/员工数据范围 |
| `CP2` | M04 完成 | 门店/房间→定价→可用时段→下单→并发占用→取消/续费/换房 |
| `CP3` | M06 完成 | 订单→MQTT→控制箱/门锁/插座→ACK/超时→审计;无实物时完成 Mock 全链路并保留真实联调阻塞 |
| `CP4` | M08 完成 | 小程序顾客/保洁/管理员端与响应式后台的端到端权限和主要流程 |
| `CP5` | M10 完成 | 全量测试、迁移、备份恢复、菜单式部署、HTTPS、性能、安全和发布候选验证 |
检查点失败时,优先修复当前或更早模块,游标不得继续进入依赖该能力的后续阶段。
#### 0.0.17 持续推进的结果输出
每完成一个循环,Codex 输出一段简短结果;同一会话继续下一循环时不重复长篇总纲说明。每段至少包含:
```text
STAGE=<Mxx-X>
ENGINEERING_DELTA=YES
TEST=<命令与真实结果>
COMMIT=<commit>
PUSH_VERIFIED=YES
NEXT_STAGE=<Mxx-Y>
CONTINUING=YES/NO
```
`CONTINUING=NO` 时必须同时给出 0.0.14 中的停止条件。不得只说“已完成,可继续下一步”。
### 0.1 Codex 读到本文档后的强制动作
Codex 每次开始工作必须按以下顺序执行:
1. 阅读 `V5.6.md``README.md``docs/module-status.md``docs/feature-status.md``docs/current-baseline.md` 和当前模块最近一份开发日志;首次接手项目时完整阅读总纲,后续同一版本续接时可按目录和相关章节增量复核,但不得遗漏 0.0 硬门禁。
2. 确认工作目录为 `D:\qipai``git rev-parse --show-toplevel` 返回 `D:/qipai`,分支为 `main`,远端为 `ssh://git@git.txyundm.cn:2222/panda/qipai.git`
3. 执行并记录:
- `git status --short --branch --untracked-files=all`
- `git log -1 --oneline`
- `git fetch --prune origin`
- `git rev-list --left-right --count main...origin/main`
- 工作区干净时执行 `git pull --ff-only origin main`
4. 检查 `docs/current-baseline.md``audited_commit`。仅在缺失、不可解析或发生重大结构变化时做全量核验;其他情况只核验差异路径,禁止重复生成基线文档。
5. 依据现有代码、测试和状态,选择第一个满足前置条件且不是 `DONE` 的子阶段;先在执行输出中写明:
- 本次模块/功能 ID
- 计划修改的工程文件;
- 预期可验证结果;
- 测试命令。
不得创建计划 Markdown。
6. 仅检索与当前子阶段相关的 `参考/` 文件、SQL、协议和历史实现,不得每次重新全量解压/盘点所有参考资料。
7. 先修改工程文件,再写伴随文档。若没有产生工程增量,不得进入“模块完成”流程。
8. 同步完成必要的测试、迁移、接口、权限、错误处理和部署影响处理;禁止只写 Controller/页面壳而不接 Service、数据库或真实调用链。
9. 运行真实命令验证;不得把“代码看起来正确”写成测试通过。
10. 在代码和测试完成后,增量更新既有状态文件和当前模块唯一开发日志;API/DB/部署/硬件文档仅在本次确有变化时更新,不得批量创建空文件。
11. 执行工程增量检查。普通开发回合的 `git diff --name-only` 必须包含 0.0.2 所列工程路径;否则只能按 0.0.3 处理,不能标记模块完成。
12. 检查配置、README 和总纲版本一致性;README 只做简要递进,并记录最近工程提交与下一工程目标。
13. 运行相关 lint、typecheck、unit/integration test、build、migration dry-run 或脚本语法检查;记录真实结果。
14. 检查未跟踪文件、大文件、嵌套 Git、私钥/证书正文和构建产物。用户允许明文登记普通账号密码,但 SSH/TLS/微信支付私钥及证书正文仍不得提交。
15. 使用能表达工程性质的提交类型:`feat(Mxx-X)``fix(Mxx-X)``refactor(Mxx-X)``test(Mxx-X)``build(Mxx-X)``chore(deploy)`。只有 0.0.3 例外使用 `docs(spec)`
16. 提交后立即 `git push origin main`,再 `git fetch origin main` 并验证本地 `HEAD``origin/main` 一致。
17. push 失败时保留本地提交,记录真实错误和重试命令,状态保持 `PARTIAL/BLOCKED_INTERNAL`;禁止继续创建文档来掩盖推送失败。
18. 未获得生产执行权限时,不从 Windows/WSL 自动部署。生产只允许在 Ubuntu 上通过 `/opt/apps/setup.sh` 菜单拉取已推送 commit。
19. 若工程编码被真实外部条件阻塞,仍须完成可独立完成的接口抽象、Mock、测试桩、数据库和错误处理;只有剩余项确实依赖硬件/支付/第三方授权时才标记 `BLOCKED_EXTERNAL`
20. 最终回复必须明确:`ENGINEERING_DELTA=YES/NO`、工程文件、功能结果、测试、文档更新、commit、push 和下一工程目标。
### 0.2 功能状态枚举
`docs/feature-status.md` 只能使用以下状态:
| 状态 | 含义 |
|---|---|
| `TODO` | 尚未开始 |
| `DOING` | 当前正在开发 |
| `PARTIAL` | 已有代码但未达到验收标准 |
| `BLOCKED_INTERNAL` | 被项目内部缺陷或前置模块阻塞 |
| `BLOCKED_EXTERNAL` | 仅缺真实硬件、厂商授权、证书、账号或外部 API 权限 |
| `DONE` | 已通过本地测试和对应验收项 |
禁止用“以后再说”“暂不处理”替代状态。用户提供的功能图中的全部功能均为最终必做项;可以分阶段,但不能从总范围中删除。
### 0.3 每次开发结束的强制产物
#### 0.3.1 每个工程子阶段固定更新
- `docs/module-status.md`:只更新当前模块行;
- `docs/feature-status.md`:只更新本次涉及的功能 ID
- 当前子阶段唯一一份 `docs/devlogs/YYYY-MM-DD-Mxx-主题.md`:首次创建,后续补丁追加;
- `docs/unresolved-issues.md`:只在新增、关闭或改变问题状态时更新;
- `README.md`:简要更新当前总纲、最近工程提交、下一工程目标和必要配置变化。
#### 0.3.2 仅在实际发生变化时更新
- API 变化:更新现有 `docs/api-changelog/` 对应记录;
- 数据库变化:更新现有 `docs/db-changelog/` 对应记录和迁移;
- 部署变化:更新 `setup.sh``scripts/setup/``deploy/``docs/deployment-status.md``docs/deployment-changelog.md`
- MQTT/硬件变化:更新 `docs/mqtt-deployment.md``docs/mqtt-protocol-mapping.md``docs/hardware-test-report.md`
- 域名/证书变化:更新 `docs/domain-https.md``docs/api-domain-test-report.md`
- 仓库/路径变化:更新 `docs/repository-map.md``docs/git-deployment.md``docs/repository-completeness.md`
- 配置/凭据变化:更新 `docs/configuration.md`、README、配置模板和检测脚本。
无变化时只在开发日志中写“无”,不得为“无变化”创建新文件。
#### 0.3.3 文档创建限制
- 固定文件已存在时必须原地更新;
- 不得复制出带日期、`new``final``v2` 后缀的第二份状态文档;
- 不得每次开发都重新生成 reference inventory、baseline、repository map 或 deployment report
- 开发日志之外,新建 Markdown 必须能在本文档中找到明确文件名和触发条件;
- 纯文档提交不改变模块状态、不增加功能完成数。
#### 0.3.4 Git 门禁
- 固定远端:`ssh://git@git.txyundm.cn:2222/panda/qipai.git`
- 每完成一个模块子阶段,必须立即提交并推送 `origin/main`
- 普通模块 commit 必须包含工程增量和测试证据;
- 推送成功后确认 `git rev-parse HEAD == git rev-parse origin/main`
- 禁止 `git push --force`、跳过 hooks、提交构建产物/日志/备份/私钥正文;
- 推送失败不允许通过新增 Markdown 把状态改成 `DONE`
### 0.4 固定 Windows 工作区、参考目录和完整推送规则
#### 0.4.1 唯一正式工作区
```text
WindowsD:\qipai
WSL 映射:/mnt/d/qipai
远端:ssh://git@git.txyundm.cn:2222/panda/qipai.git
默认分支:main
```
强制要求:
1. `D:\qipai` 本身就是唯一 Git 根目录,不允许在其上层或子目录另建第二个业务仓库。
2. Codex 每次开始前必须执行并记录:
```powershell
Set-Location D:\qipai
git rev-parse --show-toplevel
git remote -v
git branch --show-current
git status --short --branch --untracked-files=all
git log -1 --oneline
git rev-list --left-right --count main...origin/main
```
3. `git rev-parse --show-toplevel` 必须指向 `D:/qipai`;路径不符时停止开发,不得在错误目录提交。
4. 根目录版本号最大的 `V*.md` 是当前权威开发总纲;当前必须是 `V5.6.md`
5. Windows 是唯一正式开发和提交源。WSL 临时副本只用于验证,不得向远端 push。
#### 0.4.2 `参考/` 目录规则
`D:\qipai\参考\` 保存全部可借鉴资料,包括:
- 已有小程序源码;
- 已有后台静态包或源码;
- 旧数据库 SQL
- 旧后端运行包和启动脚本;
- 其他同类项目源码;
- 控制箱、门锁、智慧插座 MQTT 协议;
- 历史开发文档和必要说明。
Codex 必须:
1. 递归扫描 `参考/`,生成 `docs/reference-inventory.md`,至少记录相对路径、类型、大小、SHA-256、来源、用途、是否解压、是否含敏感信息、是否已被 Git 跟踪。
2. 生成 `docs/source-inventory.md`,记录各参考项目的技术栈、页面、接口、数据库表、可复用点、不可复用点和风险。
3.`参考/` 作为只读基线使用;正式代码必须写入正式项目目录,禁止直接在参考项目中继续二开。
4. 发现嵌套 `.git``.gitmodules`、submodule、`.svn` 或独立 remote 时,先记录来源和必要历史,再转为普通文件;禁止让参考目录成为子仓库。
5. 发现真实密码、AppSecret、Token、支付密钥、证书、私钥、生产连接串或真实用户数据时,必须脱敏或移除真实值,并记录到 `docs/reference-redaction-log.md`;日志不得写入真实秘密。
6. 对压缩包不能只记录外层文件名,必须解压到隔离临时目录进行清单和敏感信息检查。
7. 参考资料中的二进制或压缩包超过 Gitea 普通 Git 限制时,不得静默遗漏;必须检测 Git LFS 支持或记录拆分/等价解压方案。
#### 0.4.3 仓库完整性
“所有内容都推送远端”按以下边界执行:
| 类别 | 是否推送 |
|---|---|
| 正式后端、后台、小程序源码 | 必须 |
| 测试、数据库迁移、种子和兼容脚本 | 必须 |
| `V5.6.md`、状态、日志、API/DB/部署文档 | 必须 |
| `setup.sh`、Windows/WSL/Ubuntu 脚本 | 必须 |
| 经脱敏审计的 `参考/` 内容 | 必须 |
| `.env.example`、配置模板、README 明文账号密码登记 | 必须 |
| 运行态 `.env` | 默认不提交;其变量和值必须登记到 README/配置索引或由部署脚本生成 |
| `node_modules`、构建产物、缓存、日志 | 禁止 |
| SSH/TLS/支付私钥正文、上传文件、数据库备份、Gitea/EMQX/MySQL 运行数据 | 禁止 |
Codex 必须生成并维护:
```text
docs/workspace-status.md
docs/reference-inventory.md
docs/reference-redaction-log.md
docs/repository-completeness.md
scripts/dev/windows/check-workspace.ps1
scripts/dev/windows/check-reference.ps1
scripts/dev/windows/check-repo-completeness.ps1
scripts/dev/windows/check-secrets.ps1
scripts/dev/windows/check-large-files.ps1
scripts/dev/windows/check-line-endings.ps1
```
`check-repo-completeness.ps1` 至少验证:
- Git 根目录为 `D:\qipai`
- `origin``main` 正确;
- 正式源码、测试、迁移、文档、脚本和脱敏参考资料均 tracked;
- `git status --short --untracked-files=all` 没有未解释项目文件;
- `.gitignore` 没有误伤应提交文件;
- 没有秘密、依赖、构建产物、日志和备份被跟踪;
- 没有嵌套仓库或意外 submodule;
- `git diff --check`、文件编码和行尾检查通过;
- 大文件有明确处理结论。
#### 0.4.4 Windows 与 WSL 协作
- Windows 承担主要开发、单元测试、后台浏览器测试、微信开发者工具、MQTTX 调试、commit 和 push。
- WSL 中主仓库映射路径为 `/mnt/d/qipai`,允许执行只读 Git 检查、`bash -n`、ShellCheck、MQTT 冒烟和轻量脚本验证。
- WSL 已安装 EMQX,可作为持续存在的本地开发/测试 Broker;它不是生产服务,也不改变正式 Broker `101.42.38.246` 的部署要求。
- Windows 与 WSL 不得同时修改同一文件。
- Windows 与 WSL 不得共用 `node_modules`、pnpm store 或平台相关原生模块。
- 完整 Linux 依赖安装、构建、数据库迁移预演和 PM2 启动测试必须复制到 WSL 原生目录 `~/qipai-wsl-test` 或等价临时目录后执行。
- WSL 临时代码副本不得配置可写远端,不得 commit/push;正式提交和推送统一从 Windows `D:\qipai` 执行。测试结束后清理临时副本并把结果写回当前开发日志。
- 必须同步生成:
```text
scripts/dev/wsl/check-env.sh
scripts/dev/wsl/check-emqx.sh
scripts/dev/wsl/start-emqx.sh
scripts/dev/wsl/stop-emqx.sh
scripts/dev/wsl/check-workspace.sh
scripts/dev/wsl/prepare-test-copy.sh
scripts/dev/wsl/verify-linux.sh
scripts/dev/wsl/mqtt-smoke.sh
scripts/dev/wsl/check-gitea-ssh.sh
scripts/dev/wsl/check-api-domain.sh
scripts/dev/wsl/cleanup-test-copy.sh
```
#### 0.4.5 每模块完成即推送
每个模块子阶段固定流程:
```text
打开 D:\qipai
→ 阅读 V5.6.md、当前状态和 audited_commit
→ git fetch + git pull --ff-only
→ 选择一个模块子阶段并声明工程文件/测试
→ 只检查相关参考资料
→ 先编码工程文件
→ 再补测试、迁移和运行配置
→ Windows 测试 + WSL 辅助验证
→ 仅增量更新既有文档
→ 工程增量/敏感信息/完整性检查
→ commit
→ git push origin main
→ fetch 并确认 HEAD == origin/main
→ 才允许模块标记 DONE
```
推送前不得遗留未解释的项目文件。推送失败时保留本地 commit,记录错误和重试命令,模块保持 `PARTIAL``BLOCKED_INTERNAL`,不得伪造成功。
### 0.5 历史开发基线(继承自 V5.2,当前事实已由第 20 章替代)
> 本节保留用于解释既有环境和约束,不再用于判断当前工程进度。V5.5 的当前 HEAD、已完成模块、真实测试结果和唯一执行游标以文首 V5.6 接管章与第 29 章为准;两处冲突时必须采用后者,不得按本节重新开展全项目基线审计。
#### 0.5.1 已有代码进度
- 用户已确认项目已经开发部分模块,但当前对话未提供每个模块的准确完成清单。
- Codex 不得把全部模块重置为 `TODO`,也不得根据目录存在就推断 `DONE`
- 第一次读取 V5.5 后,只允许按本版 A-C 节执行一次增量基线核验;已有 `docs/current-baseline.md` 时更新原文件,不得重新生成第二套审计文档。至少包含:
- 当前 Git 分支、HEAD、最近 20 条提交和远端同步状态;
- backend/admin/miniapp/database/tests/deploy/scripts/docs 的实际目录与可运行入口;
- M00-M10 每个模块已有实现、已有测试、缺失项、风险和建议状态;
- 已存在的 API、数据库迁移、菜单页面、硬件适配器和部署脚本;
- 状态文档与代码不一致之处;
- 本次核验执行的命令和真实结果。
- 核验完成后必须写入 `audited_commit``next_engineering_target`,随后立即进入工程编码;只允许对状态文档做基于证据的校准,禁止反复重写基线或删除已有代码来迎合模板。
- 若已有未提交修改,先识别来源并保存,不得 `git reset --hard``git clean -fd` 或重新生成项目覆盖。
#### 0.5.2 WSL 本地 EMQX 与 MQTTX CLI 已验证状态
本节是当前本地调试环境的事实基线。Codex 必须以实际命令复核,不得重复安装、擅自降级或重置 EMQX 数据。
| 项目 | 已验证值 | 当前结论 | Codex 后续动作 |
|---|---|---|---|
| 环境 | Windows 开发机中的 WSL,主机名 `PC-Game` | 本地开发/测试环境 | 记录 WSL 发行版、版本、内核、systemd 和实际 IP |
| WSL MySQL 地址 | `127.0.0.1:3306` | 已确认开发/测试地址 | 仅用于本地开发、测试和迁移预演 |
| WSL MySQL 用户 | `root` | 已确认 | 写入开发/测试配置并由环境脚本真实登录验证 |
| WSL MySQL 密码 | `root123` | 按用户决定明文登记 | 与 README、配置索引和本地运行配置保持一致 |
| EMQX 版本 | `5.8.9` | 已验证 | `emqx version` 与包管理器版本必须一致 |
| 服务状态 | `active (running)` | 已验证 | `systemctl is-active emqx` 必须返回 `active` |
| 开机启动 | 已启用 | 已验证 | `systemctl is-enabled emqx` 必须返回 `enabled` |
| MQTT TCP | `1883` | 已监听 | 用 MQTT 3.1、QoS 1 完成真实连接、发布、订阅 |
| MQTT TLS | `8883` | 已监听 | 继续验证证书链、主机名、客户端信任与加密连接;仅端口监听不算 TLS 完成 |
| WebSocket | `8083` | 已监听 | 仅在后台调试工具确有需要时使用,不作为硬件主通道 |
| WebSocket TLS | `8084` | 已监听 | 继续验证证书与 WSS 连接;未验证前不得宣称可用于生产 |
| Dashboard | `18083` | 已监听 | 仅限 Windows/WSL 本地访问;不得对公网开放 |
| WSL EMQX Dashboard 账号 | `admin` | 已确认记录 | Dashboard 登录账号;是否也是 MQTT 客户端账号仍需单独测试 |
| WSL EMQX Dashboard 密码 | `admin123` | 按用户决定明文登记 | README、V5.5 和 `docs/configuration.md` 保持一致;修改后同提交更新 |
| MQTTX CLI | `1.13.0` | 已验证 | `mqttx --version` 必须输出 `1.13.0` 或经文档批准的新版本 |
| MQTTX 路径 | `/usr/local/bin/mqttx` | 已验证 | `command -v mqttx` 必须返回该路径 |
| MQTTX 架构 | Linux x64 | 已验证安装来源 | 与 WSL `x86_64` 架构一致;禁止误装 ARM64 版 |
| MQTT 协议 | 目标 MQTT 3.1、QoS 1 | 待联调验证 | 用 MQTTX CLI 和 Windows MQTTX 分别执行冒烟测试 |
| Topic | `/devicesend/{DeviceID}``/devicewill/{DeviceID}``/deviceaccept/{DeviceID}` | 协议已确定 | 验证发布、订阅、回环、ACL 和错误消息处理 |
| 本地 Broker 角色 | Windows/WSL 开发与调试 | 已确定 | 与正式 `101.42.38.246` 完全隔离,不复用账号、密码和 ACL |
建议复核命令:
```bash
emqx version
systemctl is-active emqx
systemctl is-enabled emqx
sudo ss -lntp | grep -E ':(1883|8883|8083|8084|18083)\b'
command -v mqttx
mqttx --version
```
状态分层:
- `BROKER_INSTALLATION = DONE`:EMQX 版本、服务、自启和端口监听已验证。
- `CLI_INSTALLATION = DONE`:MQTTX CLI 版本和路径已验证。
- `AUTH_ACL = PARTIAL`:账号用途、客户端认证和最小 ACL 尚需测试。
- `PROTOCOL_SMOKE = PARTIAL`MQTT 3.1、QoS 1、三个 Topic、错误密码、越权拒绝和幂等尚需自动化验证。
- `WINDOWS_CONNECTIVITY = PARTIAL`Windows MQTTX 到 WSL Broker 的实际地址与稳定性仍需记录。
- `TLS_VALIDATION = PARTIAL``8883/8084` 已监听,但证书链与真实加密连接尚未完成验收。
凭据记录与安全边界(用户已明确选择私有仓库明文登记):
1. 根目录 `README.md``docs/configuration.md` 允许明文登记账号、密码、数据库口令、JWT 密钥和 MQTT 口令;二者必须与实际配置同步,不能只改一处。
2. WSL 当前已知 EMQX Dashboard 凭据固定登记为 `admin / admin123`;它不是生产凭据,也不能在未验证前当作 MQTT 客户端账号。
3. 运行态配置优先放在 `config/dev/*.local.env``backend/.env.*` 或 Ubuntu `/etc/qipai/qipai.secrets`;README 负责索引和明文登记,脚本负责读取实际文件。
4. WSL MySQL 当前固定为 `127.0.0.1:3306``root / root123`;生产 MySQL 当前固定为 `127.0.0.1:3306``root / Da@Shuai!6y8c..XT`。两套环境必须完全隔离,禁止把本地测试数据或迁移状态直接覆盖生产。
5. 生产密码中的特殊字符不得直接拼到 shell 命令或未编码 URI。Ubuntu 检测和备份脚本必须使用权限为 `600``/etc/qipai/mysql-client.cnf` 或等效的 `--defaults-extra-file`
6. SSH 私钥、TLS 私钥、微信支付私钥/证书正文、P12/PEM 文件、数据库备份和 EMQX/Gitea 运行数据仍不得提交;README 只记录它们的路径、文件名、指纹、权限和有效期。
7. 生产 Ubuntu Broker必须使用与 WSL 不同的账号和密码;设备、后端、管理员、调试账号仍要分离,并配置最小 ACL。
8. 凭据修改必须同一提交更新 README、`docs/configuration.md`、相关 `.env.example`/模板、环境检测脚本和开发日志。
9. 私有仓库不等于绝对安全;本政策是用户的明确项目决策,Codex 不得擅自改回“全部脱敏”,但必须阻止私钥正文和支付证书正文进入 Git。
#### 0.5.3 本地 MQTT 连接优先级
Windows MQTTX 连接 WSL EMQX 时按以下顺序探测:
1. `127.0.0.1:1883`WSL localhost 转发可用时优先);
2. `localhost:1883`
3. `wsl.exe hostname -I` 返回的当前 WSL IPv4 地址;
4. 项目配置中明确指定的开发 Broker 地址。
脚本不得硬编码易变化的 WSL 动态 IP。`scripts/dev/windows/check-env.ps1` 必须自动探测,输出实际命中的地址;`scripts/dev/wsl/check-env.sh` 必须检查监听地址不是仅绑定错误接口。
#### 0.5.4 必须生成或更新的本地 MQTT 脚本
```text
scripts/dev/windows/check-emqx-wsl.ps1
scripts/dev/windows/start-emqx-wsl.ps1
scripts/dev/windows/stop-emqx-wsl.ps1
scripts/dev/windows/test-mqtt-wsl.ps1
scripts/dev/wsl/check-emqx.sh
scripts/dev/wsl/start-emqx.sh
scripts/dev/wsl/stop-emqx.sh
scripts/dev/wsl/check-mqttx.sh
scripts/dev/wsl/mqtt-smoke.sh
config/dev/mqtt.env.example
```
脚本要求:
- 先探测安装和健康状态,已正常运行时只报告,不重复安装、不重置认证数据;
- 启停脚本优先使用 `systemctl`WSL 未启用 systemd 时给出明确修复说明,不使用长期 `nohup` 冒充服务管理;
- `check-mqttx.sh` 必须验证 `/usr/local/bin/mqttx``mqttx --version`、x64 架构和执行权限;发现版本变化时写入 `docs/local-dev-mqtt.md`,不得自动降级;
- 冒烟测试优先调用 WSL MQTTX CLI;仅在 MQTTX CLI 不可用时,才允许回退到项目 Node.js 测试脚本或 `mosquitto-clients`
- 冒烟测试必须覆盖连接、发布、订阅、QoS 1、MQTT 3.1、错误密码拒绝、越权 Topic 拒绝、重复消息幂等、断线重连和遗嘱 Topic;
- `8883/8084` 的 TLS 测试必须验证证书链和真实加密会话,不得因为端口监听就标记通过;
- 密码通过进程环境或 Git 忽略的本地配置注入,不在命令行回显、日志、报告或版本化文档中输出;
- Windows 和 WSL 的测试结果写入 `docs/local-dev-mqtt.md`,账号密码只记录“已配置/已轮换”,不得明文重复。
## 0.6 README、配置文件与明文凭据同步规则
### 0.6.1 README 的职责
根目录 `README.md` 是项目入口,不替代总纲。它必须简要包含:
- 当前权威总纲版本;
- 固定仓库、分支、Windows/WSL/Ubuntu 路径;
- API 域名和 MQTT Broker
- 关键配置文件位置;
- 已知账号密码;
- 当前模块进度摘要;
- 最近三次总纲版本的简要递进;
- 最近一个包含工程增量的 commit、对应模块和测试结果;
- 下一工程目标。
每次完成模块、升级总纲、调整环境、修改配置路径或更换账号密码,Codex 都必须在同一提交中更新 README。README 不得长期停留在旧总纲版本。
### 0.6.2 配置加载优先级
配置冲突时按以下优先级处理:
1. Ubuntu 生产运行态文件 `/etc/qipai/qipai.secrets`
2. Ubuntu 非敏感配置 `/etc/qipai/qipai.conf`
3. 进程启动时显式环境变量;
4. Windows/WSL 本地运行态 `.local.env` 或项目 `.env.*`
5. 仓库中的 `.env.example`、部署模板和 README
6. 代码默认值。
README 与 `docs/configuration.md` 是配置登记和人工核对来源,不应成为后端运行时自动解析的配置文件,除非后续明确实现解析器。
### 0.6.3 配置文件总索引
#### A. Windows / Git 仓库(`D:\qipai`
| 文件/目录 | 固定位置 | 用途 | Git |
|---|---|---|---|
| 项目入口 | `D:\qipai\README.md` | 当前版本、配置索引摘要、账号密码、近期进度 | 必须提交 |
| 当前开发总纲 | `D:\qipai\V5.6.md` | 唯一权威开发约束 | 必须提交 |
| 完整配置索引 | `D:\qipai\docs\configuration.md` | 全部环境、变量、账号、密码、加载顺序、变更日期 | 必须提交 |
| 仓库映射 | `D:\qipai\docs\repository-map.md` | 实际后端/后台/小程序目录映射 | 必须提交 |
| 后端开发配置 | `D:\qipai\backend\.env.development` | Windows 本地后端数据库、JWT、MQTT、微信参数 | 可不提交,但值须登记 |
| 后端测试配置 | `D:\qipai\backend\.env.test` | 自动化测试隔离配置 | 可不提交,但值须登记 |
| 后端模板 | `D:\qipai\backend\.env.example` | 变量名、说明、示例 | 必须提交 |
| 后端目录兼容 | `D:\qipai\.env.development` | 仅当现有后端在仓库根目录时使用 | 以 repository-map 为准 |
| 后台开发配置 | `D:\qipai\admin\.env.development` | 本地 API 地址、功能开关 | 必须提交或登记 |
| 后台生产配置 | `D:\qipai\admin\.env.production` | `https://api.txyundm.cn/admin-api` | 必须提交 |
| 小程序环境配置 | `D:\qipai\miniapp\config\env.js` | 开发/体验/生产 API 切换 | 必须提交 |
| 小程序私有工具配置 | `D:\qipai\miniapp\project.private.config.json` | 微信开发者工具本机设置 | 不提交 |
| WSL MQTT 本地运行配置 | `D:\qipai\config\dev\mqtt.local.env` | WSL Broker 地址、账号、密码 | 可提交或忽略;README 必须登记 |
| WSL MQTT 模板 | `D:\qipai\config\dev\mqtt.env.example` | MQTT 变量说明 | 必须提交 |
| PM2 模板 | `D:\qipai\deploy\pm2\ecosystem.config.cjs` | 生产后端进程配置 | 必须提交 |
| Nginx 模板 | `D:\qipai\deploy\nginx\api.txyundm.cn.conf` | HTTPS、后台静态站点、API 反代、上传目录 | 必须提交 |
| EMQX 模板/说明 | `D:\qipai\deploy\emqx\` | 认证、ACL、监听器、备份和自检模板 | 必须提交 |
| 部署版本 | `D:\qipai\deploy\VERSION` | Node、EMQX 最低版本、安装通道和脚本版本 | 必须提交 |
| 菜单部署入口 | `D:\qipai\setup.sh` | Ubuntu 24.04 菜单式部署 | 必须提交 |
如果当前仓库实际后端目录不是 `backend/`Codex 不得盲目搬迁;必须在 `docs/repository-map.md` 中声明真实路径,并把 README 和本表同步改成真实位置。
#### B. WSL 本地调试环境
| 配置/数据 | 固定或实际位置 | 说明 |
|---|---|---|
| 仓库映射 | `/mnt/d/qipai` | 只作为 Windows 工作区的 WSL 映射 |
| EMQX 主配置目录 | `/etc/emqx/` | Apt 安装的 EMQX 5.8.9 配置;实际文件以包安装结果为准 |
| EMQX 主配置 | `/etc/emqx/emqx.conf``/etc/emqx/base.hocon` | 存在其一或二者时均需纳入环境检查 |
| EMQX 数据 | `/var/lib/emqx/` | 运行数据,不提交 Git |
| EMQX 日志 | `/var/log/emqx/` | 运行日志,不提交 Git |
| MQTTX CLI | `/usr/local/bin/mqttx` | 已安装 1.13.0 |
| systemd 服务 | `emqx.service` | `active (running)``enabled` |
| Dashboard | `http://127.0.0.1:18083` | Windows/WSL 本地管理 |
| Dashboard 账号 | `admin` | 明文登记 |
| Dashboard 密码 | `admin123` | 明文登记 |
| MQTT TCP | `127.0.0.1:1883` 或 WSL IP `:1883` | Windows MQTTX/WSL CLI 调试 |
| MySQL 服务 | `127.0.0.1:3306` | WSL 本地开发、测试与迁移预演 |
| MySQL 用户 | `root` | 明文登记 |
| MySQL 密码 | `root123` | 明文登记 |
| MySQL 本地配置 | `/mnt/d/qipai/backend/.env.development``/mnt/d/qipai/backend/.env.test``/mnt/d/qipai/config/dev/mysql.local.env` | 以实际仓库目录为准,三者只保留一套有效来源并由配置索引标明 |
注意:`admin / admin123` 当前登记为 Dashboard 凭据;MQTT 客户端认证账号、ACL 和 TLS 仍需单独核验。
#### C. Ubuntu 24.04 x86-64 正式服务器
| 配置/目录 | 固定位置 | 说明 |
|---|---|---|
| 菜单部署入口 | `/opt/apps/setup.sh` | 指向仓库中的 `setup.sh` |
| 生产 Git 工作区 | `/opt/apps/qipai-backend/` | 从 Gitea 拉取完整 Monorepo |
| 后台静态发布 | `/opt/apps/qipai-admin/current/` | Nginx root |
| 小程序源码镜像 | `/opt/apps/qipai-miniapp/source/` | 只保存源码和 commit,不运行 |
| 非敏感生产配置 | `/etc/qipai/qipai.conf` | 640,域名、目录、端口、开关 |
| 敏感生产配置 | `/etc/qipai/qipai.secrets` | 600,数据库、JWT、微信支付、MQTT 等账号密码 |
| 部署 SSH 私钥 | `/etc/qipai/ssh/id_ed25519` | 600,只记录路径和指纹,不把私钥正文提交 Git |
| SSH known_hosts | `/etc/qipai/ssh/known_hosts` | 644,包含 Gitea 主机指纹 |
| PM2 配置 | `/opt/apps/qipai-backend/deploy/pm2/ecosystem.config.cjs` | 后端启动配置 |
| Nginx 站点 | `/etc/nginx/sites-available/api.txyundm.cn.conf` | HTTPS 与反向代理 |
| Nginx 启用链接 | `/etc/nginx/sites-enabled/api.txyundm.cn.conf` | 指向 sites-available |
| TLS 证书 | `/etc/letsencrypt/live/api.txyundm.cn/fullchain.pem` | 只记录路径/指纹/有效期 |
| TLS 私钥 | `/etc/letsencrypt/live/api.txyundm.cn/privkey.pem` | 不提交正文 |
| Gitea 配置 | `/opt/apps/gitea/custom/conf/app.ini` | Web、SSH、仓库、数据库配置 |
| Gitea systemd | `/etc/systemd/system/gitea.service` | 服务定义 |
| MySQL 配置 | `/etc/mysql/mysql.conf.d/mysqld.cnf` | 2GB 主机低内存参数 |
| MySQL 客户端凭据 | `/etc/qipai/mysql-client.cnf` | 600,供 `setup.sh`、备份和健康检查使用;账号 `root`,密码与 `qipai.secrets` 一致 |
| MySQL 数据 | `/var/lib/mysql/` | 不提交 |
| EMQX 配置目录 | `/etc/emqx/` | 正式 Broker 认证、ACL、监听器 |
| EMQX 数据 | `/var/lib/emqx/` | 不提交 |
| EMQX 日志 | `/var/log/emqx/` | 不提交 |
| 后端日志 | `/var/log/qipai/` | PM2/应用日志 |
| 上传目录 | `/opt/apps/qipai-backend/shared/uploads/` | 经 `https://api.txyundm.cn/uploads/` 提供 |
| 统一备份 | `/opt/apps/backups/` | MySQL、Gitea、配置、EMQX、发布清单 |
### 0.6.4 当前明文凭据登记
| 环境 | 服务 | 地址 | 用户名 | 密码 | 用途/状态 |
|---|---|---|---|---|---|
| WSL 本地调试 | EMQX Dashboard | `http://127.0.0.1:18083` | `admin` | `admin123` | 已安装并运行;仅按 Dashboard 账号认定 |
| WSL 本地调试 | MySQL | `127.0.0.1:3306` | `root` | `root123` | 已确认;用于开发、测试和迁移预演 |
| Windows Git | Gitea SSH | `ssh://git@git.txyundm.cn:2222/panda/qipai.git` | SSH Key | 无密码交互 | Windows 已配置免密 |
| 正式生产 | API | `https://api.txyundm.cn` | 不适用 | 不适用 | HTTPS 固定域名 |
| 正式生产 | MQTT Broker | `101.42.38.246:1883` | 待正式配置 | 待正式配置 | 不得复用 WSL `admin/admin123` |
| 正式生产 | MySQL | `127.0.0.1:3306` | `root` | `Da@Shuai!6y8c..XT` | 已确认;写入 `/etc/qipai/qipai.secrets``/etc/qipai/mysql-client.cnf` |
禁止凭空编造未知密码。每当用户给出新密码或脚本生成密码,Codex 必须立即把真实值补充到 README 和 `docs/configuration.md`,并注明环境、服务、用途和更新时间。
数据库运行配置必须至少包含以下键;数据库名以项目实际迁移结果为准,不得凭空编造:
```dotenv
# WSL 开发/测试
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=root123
# Ubuntu 正式生产
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD='Da@Shuai!6y8c..XT'
```
实现和脚本约束:
1. 优先使用 `MYSQL_HOST/MYSQL_PORT/MYSQL_USER/MYSQL_PASSWORD/MYSQL_DATABASE` 分离变量,不直接拼接 URI。
2. 必须使用标准 dotenv 解析器,确保引号不会成为密码内容;生产脚本读取后必须能用真实登录验证。
3. 确需 `DATABASE_URL` 时,生产密码至少按 URI 规则编码为 `Da%40Shuai%216y8c..XT`,数据库名仍由实际项目配置补充。
4. 禁止使用 `mysql -uroot -p明文密码`。健康检查、迁移和备份统一使用 `/etc/qipai/mysql-client.cnf` 或临时 `--defaults-extra-file`
5. `setup.sh` 的“环境监测/故障诊断”必须分别显示 `MYSQL_SERVICE``MYSQL_AUTH``MYSQL_SCHEMA``MYSQL_MIGRATION``MYSQL_BACKUP``PASS/WARN/FAIL`,但不得回显密码。
### 0.6.5 README 简要版本递进格式
README 只保留最近三次关键变化:
- `V5.0`:固化 WSL EMQX 5.8.9、MQTTX CLI 1.13.0 和已有代码续接规则;
- `V5.1`:同步 README、配置文件总索引、明文凭据登记和 README 随版本递进规则;
- `V5.2`:增加工程编码优先、反文档循环、增量基线审计和模块工程证据门禁;
- `V5.3`:增加固定模块队列、执行游标、会话内自动推进和可中断恢复;
- `V5.4`:固化 WSL/生产 MySQL 凭据、配置位置、真实登录检测和特殊字符安全处理。
- `V5.5`:读取 2026-07-07 当前源码快照和两份硬件协议原件,吸收两个参考仓库截至 2026-07 的增量;纠正 M06 协议字段并冻结 M08-B 范围,游标在协议修复后强制进入 M08-C。
更早历史只保留在对应版本文档和 Git 历史中。
---
# 1. 最终架构决策
## 1.1 已确定的硬性结论
| 项目 | 结论 |
|---|---|
| 部署方式 | 云服务器直接部署 |
| 生产系统与架构 | Ubuntu Server 24.04 LTS x86-64`uname -m=x86_64``dpkg --print-architecture=amd64`;不支持 ARM64/aarch64 包 |
| Docker | 禁止使用,不生成 Dockerfile,不生成 docker-compose,不写容器部署流程 |
| 微信云开发 | 禁止作为后端,不使用云函数、云数据库、云托管 |
| 后端 | Node.js + TypeScript + Fastify,运行在 PM2 下 |
| 数据库 | MySQL 8.x,优先兼容已有 SQL 表结构 |
| 后台管理端 | Vue3 + Vite + TypeScript + Element Plus,构建后由 Nginx 托管静态文件;同一套源码响应式适配桌面、平板和手机浏览器 |
| MQTT Broker | Linux 原生安装 EMQX 5.x,最低满足硬件文档要求的 5.3;Broker 固定主机 `101.42.38.246`;禁止使用 Docker |
| 已选硬件 | 4G 智能门禁控电箱 + Sub-1G 智能门锁 + 4G(标准版)智慧插座 |
| MQTT 协议 | 兼容 MQTT 3.1,QoS 1;设备认证密码长度不超过 31 位;按设备 Topic 和 ACL 隔离 |
| 小程序端 | 微信原生小程序,继续使用 `wx.request` 调用 HTTPS API |
| 网关 | Nginx 负责 HTTPS、静态资源、API 反向代理、上传大小限制 |
| 运维 | PM2、systemd、Nginx 日志、MySQL/EMQX/Gitea 备份、菜单式环境监测 |
| 源码交付 | `D:\qipai` 为唯一 Windows Monorepo`参考/`、正式源码、测试、迁移、文档和部署脚本统一纳管;每完成一个模块子阶段立即 SSH 推送 `panda/qipai.git`Ubuntu 仅从本机 Gitea 拉取 `main`/版本标签后部署 |
| 菜单式部署 | `/opt/apps/setup.sh` 为唯一入口;显示中文数字菜单,完成初始化、固定仓库管理、整仓拉取、按子项目构建部署、MQTT、证书、状态、备份、恢复、回滚和诊断;Codex 开发时同步维护 |
| 宝塔面板 | 可选,只作为图形化管理工具;不能引入容器化部署 |
## 1.2 服务器配置与取舍
服务器配置:2 核 CPU、2GB 内存、50GB SSD、300GB/月流量、4Mbps 带宽;当前系统已重置为 Ubuntu Server 24.04 LTSCPU 架构固定为 x86-64Ubuntu 包架构名为 amd64。
这台服务器可以支撑小型自助棋牌室系统的 MVP 和初期生产使用,但必须轻量化:
- `/opt/apps/redis/` 只做预留。首期不安装、不启动 Redis;MVP 的验证码、幂等、时间段锁、任务状态全部先放 MySQL。后续启用 Redis 必须经过单独评审、备份和菜单脚本升级。
- 不上 Elasticsearch、Prometheus、Grafana、SkyWalking 等重组件。
- 不在服务器上跑前端开发服务,生产环境只放 Vue 构建产物。
- 不把大图片、大视频长期堆在系统盘;房间图片必须压缩,后期可迁移到对象存储。
- Node 后端用 PM2 单实例或最多 2 实例,不要盲目 cluster 拉满。
- MySQL 需要限制内存,生产初期 InnoDB Buffer Pool 建议 256MB~512MB。
- 日志要按日切分,避免 `nohup.out` 或 PM2 日志无限增长。
- MQTT Broker 已指定为 Linux 主机 `101.42.38.246`。优先作为独立 Broker 节点运行 EMQX;如果与业务后端共机,必须单独评估内存、文件句柄、连接数和日志增长,不能默认 2GB 内存足够。
- 4G 设备物联卡月流量有限,后端采用事件驱动和按需查询,禁止高频轮询设备状态。
## 1.3 推荐最终拓扑
```text
微信小程序 / 手机浏览器后台 / 桌面浏览器后台
↓ HTTPS
业务服务器 Nginx 443
├─ /app-api/* → Fastify :3001
├─ /admin-api/* → Fastify :3001
├─ /uploads/* → 本地上传目录
└─ / → Vue3 后台静态文件
Fastify + TypeScript + PM2
├─ MySQL 8.x:订单、支付、设备资产、命令、事件、审计
└─ MQTT 客户端:连接 101.42.38.246
↓ MQTT 3.1 / QoS 1
Linux MQTT Broker 101.42.38.246EMQX 5.x 原生安装)
├─ 后端订阅:/devicesend/+
├─ 后端订阅:/devicewill/+
└─ 后端发布:/deviceaccept/{DeviceID}
↓ 4G
├─ 智能门禁控电箱
│ ├─ 2 路 30A 控电
│ ├─ 1 路 10A 灯控
│ ├─ 磁力锁/电控锁
│ ├─ TTS 喇叭、LED 倒计时
│ └─ Sub-1G 智能门锁(701C/701G
└─ 4G 智慧插座(10A/16A,计量能力按型号识别)
Windows 本地开发 / Codex
└─ qipai 单一 Monorepo
├─ 后端
├─ 后台 Web
├─ 微信小程序
├─ 数据库迁移
├─ 部署脚本
└─ 开发与追踪文档
↓ 每完成一个模块子阶段即 SSH push
ssh://git@git.txyundm.cn:2222/panda/qipai.git
↓ 管理员手工运行 /opt/apps/setup.sh
Ubuntu 从本机 Gitea 拉取 main/标签/指定 commit
构建、迁移、PM2/Nginx、健康检查
```
关键原则:
- 业务后端是订单、权限和资金状态的唯一事实源;EMQX 只负责消息传输,硬件本地任务负责断网时的执行兜底。
- “消息已发布”不等于“设备执行成功”。必须等待带相同 `id` 的设备回包,或按协议确认事件/状态。
- 4G 控制箱和智慧插座直接连接 Broker;小程序和后台绝不直连 MQTT,也不持有 MQTT 凭据。
- 控制箱与 Sub-1G 门锁的父子绑定关系必须保存,命令通过控制箱转发给 `subID`
## 1.4 域名规划
推荐使用一个已备案域名或子域名:
| 用途 | 示例 | Nginx 路由 |
|---|---|---|
| 小程序接口 | `https://api.txyundm.cn/app-api` | 反代到后端 |
| 后台管理端 | `https://api.txyundm.cn/admin/``https://api.txyundm.cn/admin/` | 静态文件 |
| 后台接口 | `https://api.txyundm.cn/admin-api` | 反代到后端 |
| 上传文件 | `https://api.txyundm.cn/uploads/...` | Nginx 静态目录 |
微信公众平台需要配置:
- request 合法域名:`https://api.txyundm.cn`
- uploadFile 合法域名:`https://api.txyundm.cn`
- downloadFile 合法域名:`https://api.txyundm.cn`
- 支付回调域名:微信支付配置中使用后端 HTTPS 回调地址
---
## 1.5 开发、调试、测试与生产环境边界
### 1.5.1 环境矩阵
| 环境 | 系统与定位 | 必装/推荐工具 | 允许承担的任务 | 明确禁止 |
|---|---|---|---|---|
| Windows 开发机 | 唯一正式工作区 `D:\qipai`;日常开发、联调、测试、commit 和 push 主环境 | Git、Node.js LTS、npm/pnpm、微信开发者工具、Windows MQTTX、浏览器、Codex/编辑器 | 打开单一 `qipai` 仓库;开发后端/后台/小程序;每完成模块立即测试、提交并 SSH 推送 | 直接修改生产服务器;复制生产密钥到源码;积压多个模块不推送;push 后假定已上线 |
| WSL | `/mnt/d/qipai` 映射、WSL 原生临时测试副本和本地 MQTT 调试 BrokerLinux 辅助环境 | Ubuntu/WSL、Git、Node.js LTS、ShellCheck、已安装的 EMQX 5.8.9、MQTTX CLI 1.13.0、`mosquitto-clients`(可选)、MySQL 客户端 | Bash/部署脚本检查、Linux 构建预演、迁移预演、本地 MQTT Broker/ACL/协议测试、MQTTX CLI 自动化冒烟、SSH/Git 兼容测试 | 作为生产服务;复用本地 MQTT 凭据到生产;保存生产密钥;与 Windows 同时写同一工作树;绕过 Gitea 复制源码 |
| Ubuntu Server 24.04 | 正式生产,无桌面版 | Gitea、Nginx、MySQL、Node.js、PM2、Git、Certbot、UFW、EMQX`mosquitto-clients` 可选 | 承载 Gitea、API、后台静态文件、数据库、MQTT、备份;从本机 Gitea 拉取并发布 | 本地编辑业务源码;安装桌面/MQTTX GUI/Vite 开发服务;Docker;从未知远端拉取 |
### 1.5.2 Windows 主开发与即时推送规则
本地只保留一个正式 Git 工作区:
```text
D:\qipai\
├─ .git\
├─ .gitattributes
├─ .gitignore
├─ V5.6.md
├─ 参考\ # 全部可借鉴资料,只读基线并纳入远端
├─ backend\ 或 src\ # 正式后端
├─ admin\ # 正式后台
├─ miniapp\ # 正式小程序
├─ database\
├─ tests\
├─ docs\
├─ deploy\
├─ scripts\
└─ setup.sh
```
远端固定为:
```text
origin = ssh://git@git.txyundm.cn:2222/panda/qipai.git
branch = main
```
Codex 必须同步生成并维护:
```text
scripts/dev/windows/check-env.ps1
scripts/dev/windows/check-emqx-wsl.ps1
scripts/dev/windows/start-emqx-wsl.ps1
scripts/dev/windows/stop-emqx-wsl.ps1
scripts/dev/windows/test-mqtt-wsl.ps1
scripts/dev/windows/start-dev.ps1
scripts/dev/windows/stop-dev.ps1
scripts/dev/windows/test-all.ps1
scripts/dev/windows/check-gitea.ps1
scripts/dev/windows/push-module.ps1
scripts/dev/windows/check-api-domain.ps1
scripts/dev/windows/check-workspace.ps1
scripts/dev/windows/check-reference.ps1
scripts/dev/windows/check-repo-completeness.ps1
scripts/dev/windows/check-secrets.ps1
scripts/dev/windows/check-large-files.ps1
scripts/dev/windows/check-line-endings.ps1
```
`check-gitea.ps1` 至少检查:SSH 2222 连通、主机指纹、免密认证、固定 `origin`、当前分支、工作区状态、ahead/behind、最近提交和远端 commit。
`push-module.ps1` 必须是安全封装,不得盲目 `git add .`。它至少执行:
1. 确认当前分支为 `main`
2. `git fetch --prune origin`
3. 确认工作区没有与当前模块无关的修改。
4. 执行后端测试、后台测试/构建、小程序静态检查和 ShellCheck 中与本模块相关的部分。
5. 执行敏感信息和大文件检查。
6. 仅暂存当前模块代码、测试和必需文档。
7. 使用规范提交信息 commit。
8. `git push origin main`
9. 再次 fetch,并确认 `HEAD == origin/main`
10. 把 commit、push 时间、远端和验证结果写入开发日志与模块状态。
固定工作流:
```text
读取 V5.6.md 和进度
→ git pull --ff-only origin main
→ 只开发一个模块子阶段
→ 测试与验收
→ 更新全部记录文档
→ commit
→ git push origin main
→ 校验远端 commit
→ 模块标记 DONE
```
默认不要求 `develop`、PR 或功能分支。只有用户明确要求并行开发、代码评审或试验性改动时才创建 `feature/*`;该分支完成后仍必须合并并推送 `main`,模块才能 `DONE`
### 1.5.3 WSL 辅助环境要求
Codex 必须生成并维护:
```text
scripts/dev/wsl/check-env.sh
scripts/dev/wsl/check-emqx.sh
scripts/dev/wsl/start-emqx.sh
scripts/dev/wsl/stop-emqx.sh
scripts/dev/wsl/verify-linux.sh
scripts/dev/wsl/mqtt-smoke.sh
scripts/dev/wsl/check-gitea-ssh.sh
scripts/dev/wsl/check-api-domain.sh
scripts/dev/wsl/check-workspace.sh
scripts/dev/wsl/prepare-test-copy.sh
scripts/dev/wsl/cleanup-test-copy.sh
```
WSL 使用原则:
- 优先在 Linux 文件系统目录执行大量依赖安装和 ShellCheck;如果使用 Windows 挂载目录,Windows 与 WSL 不得同时修改同一工作区。
- 可验证固定 Gitea SSH 地址和远程 MQTT,但只使用测试账号或已授权的 Git 密钥。
- 不自动复制 `/etc/qipai/qipai.secrets` 文件本体;其账号密码值按用户决定登记在 README 与 `docs/configuration.md`,私钥/证书正文除外。
- 不通过 `scp/rsync` 把业务源码直接覆盖到 `/opt/apps/qipai-*`;生产更新只走 Gitea + 菜单。
- WSL 可以长期运行 EMQX 作为本地开发/测试 Broker,但不得承载真实门店生产设备;MySQL、PM2、Nginx 是否在 WSL 启动仅用于临时联调,不作为生产替代。
- 正式 commit/push 统一由 Windows `D:\qipai` 执行;WSL 仅返回测试结果,避免双环境写入和 Git 分叉。
### 1.5.4 Ubuntu 生产环境规则
正式服务器必须保持 Ubuntu Server 无桌面,并遵守:
- 所有业务目录固定在 `/opt/apps/`;业务源码仅由固定 Gitea 仓库拉取产生。
- Gitea push 只完成“代码到仓库”,不会自动上线;管理员在服务器运行 `sudo bash /opt/apps/setup.sh` 选择更新。
- `/opt/apps/qipai-backend/` 是单一仓库的生产检出目录;后台和小程序输出由部署脚本分发到对应目录。
- 后端和静态后台不得直接用 root 运行或构建,脚本以 `qipai` 用户执行 Git/npm,以 root 只处理系统服务、目录权限和证书。
- Gitea 使用 `git` 用户;EMQX/MySQL 使用各自 systemd 服务账号。
- 不安装 `ubuntu-desktop`、GNOME、KDE、Xorg、VNC、MQTTX GUI 和 Vite 开发服务。
- EMQX Dashboard 只通过 SSH 隧道或可信来源访问。
- 生产工作区出现本地修改、ahead 或分叉时,脚本立即停止更新,禁止静默覆盖。
### 1.5.5 Gitea 与生产发布边界
- Gitea 是唯一源码远端和审计入口,但不是自动发布器。
- 固定仓库 Web 地址为 `https://git.txyundm.cn/panda/qipai.git`Windows/WSL SSH 地址为 `ssh://git@git.txyundm.cn:2222/panda/qipai.git`
- 生产服务器同机拉取优先使用 `ssh://git@127.0.0.1:2222/panda/qipai.git`;脚本必须校验仓库路径仍为 `panda/qipai.git`
- 默认关闭“push 即生产部署”的 Webhook,避免误推、未迁移数据库或未构建前端时自动上线。
- 生产服务器使用独立只读部署密钥,只授予 `panda/qipai` 仓库读取权限;Windows 使用已配置的免密开发密钥进行 push。
- SSH 主机指纹必须确认后写入专用 `known_hosts`,禁止 `StrictHostKeyChecking=no`
- 每次发布完成后,把单一仓库 commit、各子项目构建结果、数据库迁移、部署时间、操作人和健康结果写入发布清单。
# 2. 参考资料与现有系统识别
## 2.1 本次已有资料
所有参考资料的权威本地位置固定为 `D:\qipai\参考\`。Codex 必须以实际递归清单为准,并把该目录作为只读参考基线纳入同一 Git 仓库。
| 资料 | 定位 | Codex 使用方式 |
|---|---|---|
| `参考/小程序源代码.zip` | 麻老板/闪订小程序端源码,微信原生 JS | 作为小程序页面、接口调用、业务流程主参考 |
| `参考/24h_qipaishi-master(1).zip` | 同类小程序源码,含 README、图片、页面 | 与 `小程序源代码.zip` 交叉参考 |
| `参考/后台管理系统_20260427.zip` | 已构建后台管理端静态包,不是完整源码 | 作为后台页面菜单、接口路径、UI 功能参考;不直接二开压缩产物 |
| `参考/db_20260427.sql` | MySQL 数据库结构和演示数据 | 作为核心数据模型、字段命名、业务状态参考 |
| `参考/mazongjian-server.xjar` + `参考/xjar` | 旧后端运行包,非源码 | 仅作为旧系统运行态参考,不作为新系统主后端 |
| `参考/.env` | 旧后端 MySQL、Redis、微信支付配置样例 | 只参考变量类别,不复制真实密码 |
| `参考/start.sh` / `参考/stop.sh` | 旧 JAR 启停脚本 | 参考“服务启停思路”,新系统改为 PM2/systemd |
| `参考/easy-joy-life-main.zip` | 另一套无人棋牌室 Spring Boot + 小程序资料 | 参考 API 设计、部署经验、设备/支付业务闭环 |
| `参考/4G 智能门禁控电箱MQTT协议文档.pdf` | 已选控制箱、门禁、电控、TTS、Sub-1G 门锁的正式协议 | M00/M01/M06 必须完整阅读;Topic、命令名、字段拼写和回包结果以该文档为准 |
| `参考/4G (标准版)智慧插座MQTT协议文档.pdf` | 已选 4G 智慧插座正式协议 | M00/M01/M06 必须完整阅读;按设备型号识别计量和保护能力 |
| MQTT Linux 服务器 `101.42.38.246` | 生产 Broker 固定主机 | 原生安装 EMQX,建立账号、ACL、日志、备份和巡检;账号密码按 V5.5 规则登记在 README/配置索引;私钥正文不得写入 Git |
## 2.2 旧小程序已识别页面
主包页面数量:39。
```text
pages/doorList/doorList
pages/productOrder/productOrder
pages/productOrderInfo/productOrderInfo
pages/index/index
pages/shop/shop
pages/doorSubmit/doorSubmit
pages/door/door
pages/doorSelect/doorSelect
pages/user/user
pages/join/join
pages/setUserInfo/setUserInfo
pages/setUserName/setUserName
pages/setUserPhone/setUserPhone
pages/tuangou/tuangou
pages/myBalance/myBalance
pages/getBalance/getBalance
pages/orderDetail/orderDetail
pages/changeDoor/changeDoor
pages/orderSubmit/orderSubmit
pages/coupon/coupon
pages/doorDetail/doorDetail
pages/tencentMap/tencentMap
pages/help/help
pages/recharge/recharge
pages/orderList/orderList
pages/location/location
pages/login/login
pages/booking/booking
pages/placeOrder/placeOrder
pages/roomRenew/roomRenew
pages/map/map
pages/pay/pay
pages/searchOrder/index
pages/yeepay/index
pages/yeepay/agreement
pages/couponActive/index
pages/inventory/index
pages/coupon/lottery
pages/ktv/index
```
分包页面:
- `packageA`47 个页面
```text
packageA/pages/admin/admin
packageA/pages/doorManage/doorManage
packageA/pages/noticeManage/noticeManage
packageA/pages/doorPosition/doorPosition
packageA/pages/vipBlacklist/vipBlacklist
packageA/pages/statics/statics
packageA/pages/setVip/setVip
packageA/pages/cleaner/cleaner
packageA/pages/setCoupon/setCoupon
packageA/pages/setStore/setStore
packageA/pages/setDoorInfo/setDoorInfo
packageA/pages/setStoreInfo/setStoreInfo
packageA/pages/setCouponInfo/setCouponInfo
packageA/pages/SetOrder/SetOrder
packageA/pages/setDiscount/setDiscount
packageA/pages/setDoorList/setDoorList
packageA/pages/taskStatics/taskStatics
packageA/pages/task/task
packageA/pages/taskDetail/taskDetail
packageA/pages/taskSettle/taskSettle
packageA/pages/scanQr/scanQr
packageA/pages/taskManager/taskManager
packageA/pages/deviceList/deviceList
packageA/pages/packageManagement/packageManagement
packageA/pages/editPages/editPages
packageA/pages/roomList/roomList
packageA/pages/setStoreSound/setStoreSound
packageA/pages/faceRecord/faceRecord
packageA/pages/guide/guide
packageA/pages/meituanreserve/meituanreserve
packageA/pages/pushrule/pushrule
packageA/pages/faceBlacklist/faceBlacklist
packageA/pages/addLock/addLock
packageA/pages/productManage/productManage
packageA/pages/addProduct/addProduct
packageA/pages/goodsKindManage/goodsKindManage
packageA/pages/bleList/bleList
packageA/pages/configGateWay/index
packageA/pages/vipDetail/vipDetail
packageA/pages/ydCancel/index
packageA/pages/vipConfig/vipConfig
packageA/pages/vipList/vipList
packageA/pages/configPrePay/configPrePay
packageA/pages/configLockWifi/index
packageA/pages/setTemplate/index
packageA/pages/payOrder/index
packageA/pages/lottery/index
```
- `inventory`2 个页面
```text
inventory/pages/goods/index
inventory/pages/detail/index
```
从页面命名可反推出系统能力:门店列表、门店首页、房间详情、预约下单、订单详情、续费、开门、个人中心、余额充值、优惠券、团购核销、商品点单、保洁、门店管理、房间管理、会员管理、统计、设备配置、NFC/二维码、KTV 扩展等。
## 2.3 旧小程序接口前缀与兼容要求
旧小程序 `app.js` 中使用:
```javascript
globalData: {
baseUrl: "https://malaoban.scyanzu.com/app-api",
tenantId: "150"
}
```
`utils/http.js` 请求头包含:
```javascript
header: {
'tenant-id': app.globalData.tenantId,
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
```
因此新后端必须兼容:
- 小程序基础地址保留 `/app-api`
- 请求头保留 `tenant-id`
- 登录态保留 `Authorization: Bearer <token>`
- 返回结构建议兼容旧风格:`{ code, msg, data }`401 使用 `code = 401`
- 对旧小程序高频接口尽量保留原路径;后台 Vue 新版可以逐步使用更规范 REST 接口,但必须有迁移映射。
## 2.4 已识别旧接口分组
### 认证登录(7 个)
- `/member/auth/login`
- `/member/auth/logout`
- `/member/auth/weixin-mini-app-login`
- `/member/auth/wxLoginByCode?code=`
- `/member/manager/getYDCancelAuthList/`
- `/member/store/getGroupPayAuthUrl`
- `/member/user/update-mobile`
### 首页门店房间(58 个)
- `/member/chart/getRoomUseHour`
- `/member/chart/getRoomUseStatistics`
- `/member/clear/finishRoomOrder/`
- `/member/clear/opRoom`
- `/member/clear/openRoomDoor/`
- `/member/clear/openStoreDoor/`
- `/member/index/getBannerList`
- `/member/index/getCityList`
- `/member/index/getRoomInfo`
- `/member/index/getRoomInfo/`
- `/member/index/getRoomInfoList`
- `/member/index/getRoomList/`
- `/member/index/getStoreInfo`
- `/member/index/getStoreInfo/`
- `/member/index/getStoreList`
- `/member/index/getStoreList?cityName=`
- `/member/index/getSysInfo`
- `/member/order/changeRoom/`
- `/member/order/getOrderByRoomId/`
- `/member/order/getOrderInfoByRoomId?roomId=`
- `/member/order/openRoomDoor?orderKey=`
- `/member/order/openStoreDoor?orderKey=`
- `/member/store/deleteRoomInfo/`
- `/member/store/disableRoom/`
- `/member/store/finishRoomOrder/`
- `/member/store/getDetail/`
- `/member/store/getDiscountRuleDetail/`
- `/member/store/getDiscountRulesPage`
- `/member/store/getFaceBlacklistPage`
- `/member/store/getFaceRecordPage`
- `/member/store/getLockList/`
- `/member/store/getLockPwd`
- `/member/store/getNFCScheme/`
- `/member/store/getPageList`
- `/member/store/getPrePayConfig/`
- `/member/store/getQrConfig?sn=`
- `/member/store/getRoomDetail/`
- `/member/store/getRoomInfoList/`
- `/member/store/getRoomInfoList?storeId=`
- `/member/store/getRoomList/`
- `/member/store/getServiceInfo`
- `/member/store/getStoreList`
- `/member/store/getStoreListByAdmin`
- `/member/store/getStoreListByAdmin2`
- `/member/store/getStoreSoundInfo/`
- `/member/store/getTemplateList`
- `/member/store/getVipConfig/`
- `/member/store/opRoom`
- `/member/store/openRoomDoor/`
- `/member/store/openStoreDoor/`
- `/member/store/save`
- `/member/store/saveDiscountRuleDetail`
- `/member/store/saveRoomDetail`
- `/member/store/saveStoreSoundInfo`
- `/member/store/saveVipConfig`
- `/member/store/upQrUrl?roomId=`
- `/member/store/updateRoomLock`
- `/member/user/getStoreBalance/`
### 预约订单(26 个)
- `/member/chart/getOrderStatistics`
- `/member/manager/cancelOrder/`
- `/member/manager/changeOrder`
- `/member/manager/changeOrderUser`
- `/member/manager/getOrderPage`
- `/member/manager/getPayOrderPage`
- `/member/manager/refundOrder/`
- `/member/manager/refundPayOrder`
- `/member/manager/renewByAdmin`
- `/member/manager/submitOrder`
- `/member/order/cancelOrder`
- `/member/order/cancelOrder/`
- `/member/order/closeOrder/`
- `/member/order/controlKT`
- `/member/order/getDiscountRules/`
- `/member/order/getLockPwd?orderKey=`
- `/member/order/getOrderInfo`
- `/member/order/getOrderInfoByNo`
- `/member/order/getOrderInfoByNo?orderKey=`
- `/member/order/getOrderPage`
- `/member/order/lockWxOrder`
- `/member/order/preGroupNo`
- `/member/order/preOrder`
- `/member/order/renew`
- `/member/order/save`
- `/member/order/startOrder/`
### 支付退款(7 个)
- `/member/chart/getRechargeStatistics`
- `/member/manager/depositRefund/`
- `/member/manager/recharge`
- `/member/merchant-account/getApplyUrl`
- `/member/store/setPrePayConfig`
- `/member/user/preRechargeBalance`
- `/member/user/rechargeBalance`
### 设备门锁(6 个)
- `/member/device/getDevicePage`
- `/member/store/addDevice`
- `/member/store/addLock`
- `/member/store/delDevice/`
- `/member/store/resetQrcode?storeId=`
- `/member/store/setQrConfig`
### 会员优惠套餐(29 个)
- `/member/card/getSaleCardPage`
- `/member/couponActive/getAdminByCouponId?couponId=`
- `/member/couponActive/getById?couponId=`
- `/member/couponActive/putCoupon?id=`
- `/member/couponActive/saveAdminByCouponId`
- `/member/couponActive/stopActive?couponId=`
- `/member/manager/deleteCoupon?couponId=`
- `/member/manager/deleteVip`
- `/member/manager/getCouponDetail/`
- `/member/manager/getCouponPage`
- `/member/manager/getUserCouponByAdmin`
- `/member/manager/getVipPage`
- `/member/manager/giftCoupon`
- `/member/manager/revokeCoupon/`
- `/member/manager/saveCouponDetail`
- `/member/pkg/admin/delete/`
- `/member/pkg/admin/enable/`
- `/member/pkg/admin/getAdminPkgPage`
- `/member/pkg/admin/saveAdminPkg`
- `/member/pkg/getPkgPage`
- `/member/store/addMemberVip`
- `/member/store/changeDiscountRulesStatus/`
- `/member/store/deleteVipConfig/`
- `/member/store/editMemberVip`
- `/member/store/vip/blacklist`
- `/member/user/getCouponPage`
- `/member/user/getGiftBalance/`
- `/member/user/getGiftBalanceList`
- `/member/user/getMoneyBillPage`
### 保洁(16 个)
- `/member/clear/cancel/`
- `/member/clear/finish/`
- `/member/clear/getChartData`
- `/member/clear/getClearBillPage`
- `/member/clear/getClearPage`
- `/member/clear/getDetail/`
- `/member/clear/jiedan/`
- `/member/clear/start/`
- `/member/manager/cancelClear/`
- `/member/manager/complaintClearInfo`
- `/member/manager/deleteClearUser/`
- `/member/manager/getClearManagerPage`
- `/member/manager/getClearUserPage`
- `/member/manager/saveClearUser`
- `/member/manager/settlementClearUser`
- `/member/store/clearAndFinish/`
### 商品库存(10 个)
- `/api/goodsType/update`
- `/member/inventory/addInventory`
- `/member/inventory/deleteGoods?id=`
- `/member/inventory/getAllInventory?storeId=`
- `/member/inventory/getGoodsList?storeId=`
- `/member/inventory/getInventoryList`
- `/member/inventory/getUserInventoryPage`
- `/member/inventory/saveGoods`
- `/member/inventory/takeInventory`
- `/member/inventory/takeOut`
### 统计报表(5 个)
- `/member/chart/getBusinessStatistics`
- `/member/chart/getIncomeStatistics`
- `/member/chart/getMemberStatistics`
- `/member/chart/getRevenueChart`
- `/member/chart/getRevenueStatistics`
### 团购/第三方(2 个)
- `/member/manager/useGroupNo`
- `/member/store/setDouyinId?storeId=`
### KTV/扩展(19 个)
- `/member/game/deleteUser/`
- `/member/game/getGamePage`
- `/member/game/join/`
- `/member/game/save`
- `/member/ktv/controlKT`
- `/member/ktv/getCurrentSongList`
- `/member/ktv/getSingerList`
- `/member/ktv/getSongClassList`
- `/member/ktv/getSongList`
- `/member/ktv/getSongListByClass`
- `/member/ktv/sendBarrage`
- `/member/ktv/sendCommand`
- `/member/ktv/songCommand`
- `/member/lottery/`
- `/member/lottery/getInfo/`
- `/member/lottery/getList`
- `/member/lottery/getResult/`
- `/member/lottery/join/`
- `/member/lottery/verifyPrize`
### 其他(21 个)
- `/member/manager/applyWithdrawal`
- `/member/manager/auditYD`
- `/member/manager/deleteAdminUser/`
- `/member/manager/getAdminUserPage`
- `/member/manager/getMemberPage`
- `/member/manager/saveAdminUser`
- `/member/store/addBlackList`
- `/member/store/checkBall/`
- `/member/store/controlKT`
- `/member/store/moveFaceById/`
- `/member/store/moveFaceByRecord`
- `/member/store/opShop`
- `/member/store/remove/`
- `/member/store/runYunlaba`
- `/member/store/setTemplate`
- `/member/store/uploadImg`
- `/member/user/get`
- `/member/user/getFranchiseInfo`
- `/member/user/saveFranchiseInfo`
- `/member/user/updateAvatar?avatarUrl=`
- `/member/user/updateNickname?nickname=`
> Codex 不能一次性重写全部 200+ 旧接口。MVP 只实现核心闭环接口;未实现接口必须在 `docs/unresolved-issues.md` 登记。
## 2.5 SQL 表识别结果
`db_20260427.sql` 共识别到 104 张表。
| 分组 | 数量 | 说明 |
|---|---:|---|
| `member_*` | 39 | 自助门店业务表:门店、房间、订单、支付、会员、优惠券、设备、保洁、库存等 |
| `system_*` | 33 | 后台系统、用户、角色、菜单、租户、日志、短信等 |
| `infra_*` | 12 | 基础设施:文件、参数、任务、访问日志等 |
| `yshop_*` | 9 | 商品商城相关 |
| `qrtz_*` | 11 | Java Quartz 定时任务相关,新 Node 系统不沿用 |
核心业务表必须优先参考:
- `member_store_info`
- `member_room_info`
- `member_order_info`
- `member_pay_order`
- `member_user`
- `member_user_money_bill`
- `member_coupon_info`
- `member_coupon_active`
- `member_pkg_info`
- `member_pkg_user_info`
- `member_device_info`
- `member_device_use_info`
- `member_clear_info`
- `member_clear_bill`
- `member_inventory_goods`
- `member_inventory_info`
- `member_inventory_detail`
- `member_product_order`
- `member_store_pay_config`
- `member_store_wxpay_config`
- `member_store_template`
- `system_tenant`
- `system_users`
- `system_role`
- `system_menu`
- `system_user_role`
- `system_operate_log`
### 全量表名清单
```text
infra_api_access_log
infra_api_error_log
infra_codegen_column
infra_codegen_table
infra_config
infra_data_source_config
infra_file
infra_file_config
infra_file_content
infra_job
infra_job_log
infra_test_demo
member_banner_info
member_clear_bill
member_clear_info
member_coupon_active
member_coupon_info
member_device_info
member_device_use_info
member_discount_rules
member_face_blacklist
member_face_record
member_franchise_info
member_game_info
member_group_pay_info
member_holiday
member_inventory_detail
member_inventory_goods
member_inventory_info
member_inventory_record
member_lottery_detail
member_lottery_info
member_merchant_account
member_order_info
member_pay_order
member_pkg_info
member_pkg_user_info
member_product_order
member_room_info
member_store_info
member_store_meituan_info
member_store_pay_config
member_store_pay_split
member_store_sound_info
member_store_template
member_store_user
member_store_vip_config
member_store_wxpay_config
member_user
member_user_money_bill
member_user_withdrawal
qrtz_blob_triggers
qrtz_calendars
qrtz_cron_triggers
qrtz_fired_triggers
qrtz_job_details
qrtz_locks
qrtz_paused_trigger_grps
qrtz_scheduler_state
qrtz_simple_triggers
qrtz_simprop_triggers
qrtz_triggers
system_dept
system_dict_data
system_dict_type
system_error_code
system_login_log
system_mail_account
system_mail_log
system_mail_template
system_menu
system_notice
system_notify_message
system_notify_template
system_oauth2_access_token
system_oauth2_approve
system_oauth2_client
system_oauth2_code
system_oauth2_refresh_token
system_operate_log
system_post
system_role
system_role_menu
system_sensitive_word
system_sms_channel
system_sms_code
system_sms_log
system_sms_template
system_social_user
system_social_user_bind
system_tenant
system_tenant_package
system_user_post
system_user_role
system_users
yshop_store_product
yshop_store_product_attr
yshop_store_product_attr_result
yshop_store_product_attr_value
yshop_store_product_brand
yshop_store_product_category
yshop_store_product_relation
yshop_store_product_reply
yshop_store_product_rule
```
---
# 2.6 全功能需求基线与覆盖矩阵
## 2.6.1 范围声明
用户提供的“无人系统软硬件全套解决方案”功能图,现作为本项目正式需求基线。下表中的每一项都是最终交付范围,不再归类为“可永久暂缓”。
允许按阶段实现,但必须遵守:
- 纯云服务器直接部署,无 Docker、无微信云开发。
- 后端、后台管理端、小程序端可以分开开发和部署,但共享统一 API、权限和数据模型。
- 涉及真实硬件或第三方平台的功能,先完成接口、Mock、配置页、状态机、日志和错误处理,再等待账号/硬件联调。
- `BLOCKED_EXTERNAL` 不是“完成”,只有真实联调或经用户明确接受的替代验收后才能改为 `DONE`
- 后台管理系统以平台配置、查询、审计和高级管理为主;门店管理员、保洁员等高频业务尽量在同一个小程序内完成。
## 2.6.2 角色与端统一原则
| 角色 | 主要端 | 数据范围 | 核心能力 |
|---|---|---|---|
| 顾客 `CUSTOMER` | 微信小程序 | 自己的订单、余额、券、套餐 | 选店、预约、支付、开门、续费、取消、分享、连 Wi-Fi |
| 保洁员 `CLEANER` | 同一微信小程序 | 授权门店的保洁任务 | 任务大厅、接单、开始、上传照片、完成、统计、结算 |
| 门店员工 `STAFF` | 同一微信小程序 | 授权门店 | 查房态、验券、处理订单、有限设备操作 |
| 门店管理员 `STORE_ADMIN` | 同一微信小程序 + 后台 | 授权门店 | 门店/房间/订单/员工/保洁/设备/会员/统计管理 |
| 加盟商/租户管理员 `TENANT_ADMIN` | 同一微信小程序 + 后台 | 本租户全部门店 | 多门店、支付账户、分账、报表、品牌和配置 |
| 平台超管 `PLATFORM_ADMIN` | Vue 后台,必要时同一小程序 | 全平台 | 多小程序、多租户、支付、设备、加盟、审计和运维 |
菜单展示只是用户体验,后端必须再次执行角色、门店范围、租户范围和资源归属校验。
## 2.6.3 必做功能矩阵
| ID | 功能 | 详细要求 | 模块 | 关键验收 | 外部依赖 | 状态 |
|---|---|---|---|---|---|---|
| SYS-001 | 微信原生小程序 | 保留微信原生页面体系,兼容现有 JS 代码和微信开发者工具;不引入跨端框架强制重写。 | M00/M08 | 小程序可导入、编译、预览,现有核心页面不丢失。 | 无 | `TODO` |
| CFG-001 | 小程序品牌自定义 | 支持按小程序/租户配置名称、Logo、主题色、客服电话、加盟电话、分享图和默认门店。 | M02/M03/M08 | 切换租户后品牌、电话、Logo 和分享信息完全隔离。 | 小程序 AppID | `TODO` |
| NTF-001 | 全场景通知提醒 | 覆盖下单、取消、充值、换房、订单开始/结束、续费提醒、保洁创建/接单/完成、管理员代下单、预订和预订取消等事件。 | M10 | 事件写入通知发件箱,可追踪发送状态、失败原因和重试次数。 | 订阅消息模板/短信或企业微信可选 | `TODO` |
| AUTH-001 | 多端统一小程序 | 顾客、保洁、门店管理员、加盟商/租户管理员、平台超管使用同一小程序,按角色动态菜单和数据权限展示。 | M02/M08 | 不同角色登录后菜单、首页和接口权限正确,越权请求返回 403。 | 无 | `TODO` |
| ADV-001 | 广告管理 | 支持平台首页广告、门店首页广告、加盟广告、品牌广告、跳转页面和投放时间。 | M03/M08 | 后台可新增、排序、上下架,前端只展示当前租户和有效期内广告。 | 无 | `TODO` |
| TEN-001 | 多门店支持 | 支持单门店和多门店;管理员、保洁员、员工按门店授权,只能管理授权门店。 | M02/M03 | 跨门店越权读写被拒绝,门店切换后数据正确。 | 无 | `TODO` |
| QR-001 | 门店/房间预约小程序码 | 每个门店、房间生成独立场景码,扫码直达对应门店或房间下单页。 | M03/M06 | 场景码可重新生成、停用和追踪扫码来源;不能直接携带开门权限。 | 微信小程序码接口凭证 | `TODO` |
| MAP-001 | 地图选店和最近门店 | 获取用户位置,按距离展示门店,支持城市、距离和营业状态筛选。 | M03/M08 | 拒绝定位时可手工选店;定位成功时距离计算和排序正确。 | 地图 Key/微信位置权限 | `TODO` |
| UI-001 | 门店装修和多套模板 | 支持多套首页模板、公告、轮播图、环境照片、客服、Wi-Fi、营业说明和自定义组件配置。 | M03/M08 | 模板切换不改业务数据,门店独立保存装修配置。 | 无 | `TODO` |
| STORE-001 | 门店运营配置 | 配置地址定位、提醒弹窗、营业时间、通宵场开始时间和时长、延时关灯、清洁时长、清洁期能否预订、客服电话、Wi-Fi。 | M03 | 保存后定价、预约、设备任务和前端展示按配置生效。 | 无 | `TODO` |
| ROOM-001 | 房间配置 | 房间名称、类别、单价、工作日价、通宵价、最低下单时长、最大提前开始时间、最大提前天数、排序、禁用时段、标签、图片、押金。 | M03/M04 | 字段校验完整,禁用时段和提前规则能阻止非法下单。 | 无 | `TODO` |
| ROOM-002 | 房间即时管理 | 管理员在小程序启停房间、临时禁用、查看当前订单、开门、开电、关电、开灯、关灯。 | M03/M06/M08 | 操作必须鉴权、审计并返回真实设备执行结果。 | 实际硬件 | `TODO` |
| ORD-001 | 预约下单与多方式支付 | 支持微信、余额、套餐、团购券支付,支持最低预约时长、提前预约天数、通宵场和包场。 | M04/M05 | 价格快照固定,时间段无重叠,支付方式和订单状态一致。 | 微信支付/团购平台 | `TODO` |
| GRP-001 | 团购券兑换 | 支持美团、大众点评、抖音团购;用户下单时粘贴或扫码券码,后台或接口核销。 | M05 | 券码只核销一次,失败原因可见,支持人工核销和厂商 API 适配器。 | 平台商家授权 | `TODO` |
| WAL-001 | 充值优惠 | 支持支付金额、赠送金额、适用门店、有效期、活动上下架和购买限制。 | M07 | 充值成功后现金余额与赠送余额分别入账,规则不可被前端篡改。 | 微信支付 | `TODO` |
| WAL-002 | 门店独立会员余额 | 按门店或租户独立保存余额;消费时先扣赠送余额,再扣现金余额。 | M07 | 每次变动都有双向流水,余额永不出现无日志变化。 | 无 | `TODO` |
| GRP-002 | 美团直订 | 接收美团预订或订单同步,映射门店/房间/时间,顾客可在有效订单内开门。 | M05 | 重复推送幂等;无法匹配房间时进入人工处理队列。 | 美团开放平台授权 | `TODO` |
| MKT-001 | 套餐营销 | 配置套餐时长、价格、适用门店/房型/房间、可用星期、时间段、节假日、有效期和购买上限。 | M07 | 下单核销时严格校验可用范围,过期或已用套餐不可重复使用。 | 无 | `TODO` |
| MKT-002 | 优惠券 | 支持时长券、满减券、门槛、适用门店/房型/时间段、有效期、发放、领取和核销。 | M07 | 优惠券状态机完整,核销与订单在同一事务中。 | 无 | `TODO` |
| DEV-001 | 一键开门 | 首页定位当前有效订单,一键联动房门、门店门、电源和灯光。 | M06 | 无有效订单不能开门;每个动作单独记录结果,部分失败明确提示。 | 门锁/电控硬件 | `TODO` |
| NET-001 | 一键连接 Wi-Fi | 按门店配置 SSID/密码,引导微信授权连接店内 Wi-Fi。 | M03/M08 | 密码只对有权限用户按需返回,日志不打印明文密码。 | 微信 Wi-Fi 能力/门店网络 | `TODO` |
| ORD-002 | 分享订单 | 订单生成短期分享令牌,好友打开后可查看授权信息并在允许范围内开门。 | M04/M06 | 令牌可撤销、有过期时间、只授予指定订单动作,不暴露订单全部隐私。 | 无 | `TODO` |
| DEV-002 | 语音播报 | 订单开始欢迎语、结束前 30/5 分钟提醒、深夜禁止扰民、自定义时间提醒和营销播报。 | M06/M10 | 播报规则可按门店/房间配置,重复任务不重复播放。 | 云喇叭或音箱硬件 | `TODO` |
| ORD-003 | 订单续费 | 订单进行中可按微信、余额或套餐续费,重新校验后续时间段。 | M04/M05 | 存在后续冲突时拒绝;支付失败不延长订单。 | 微信支付可选 | `TODO` |
| ORD-004 | 取消订单与原路退款 | 按配置支持下单后短时间或开场前若干小时取消,自动计算可退金额并原路退款。 | M04/M05 | 取消和退款幂等,优惠券/套餐/余额按规则退回并留流水。 | 微信支付 | `TODO` |
| ORD-005 | 更换房间 | 订单开始前可换到空闲且同价/同规则房间;管理员可按权限处理差价。 | M04 | 换房事务锁定新房间、释放旧房间并记录变更历史。 | 无 | `TODO` |
| WAL-003 | 余额账单 | 展示充值、赠送、消费、退款、人工调整等每笔流水。 | M07/M08 | 账单总和可与当前余额对账,支持分页和时间筛选。 | 无 | `TODO` |
| AUTH-002 | 多级权限 | 平台超管、租户管理员、门店管理员、普通员工、保洁员、顾客;支持菜单、按钮、接口和数据范围权限。 | M02/M08 | 前端隐藏与后端鉴权同时生效,不能仅靠前端控制。 | 无 | `TODO` |
| CLN-001 | 保洁人员管理 | 保洁员按门店授权,只查看和处理授权门店任务。 | M09 | 保洁员无法查看无权限门店任务和客户隐私。 | 无 | `TODO` |
| STAFF-001 | 员工账号 | 租户/门店管理员可创建员工、分配门店和角色、禁用账号、重置登录。 | M02/M08 | 账号生命周期和操作审计完整。 | 无 | `TODO` |
| CLN-002 | 保洁任务大厅 | 订单结束后自动产生任务,支持抢单、指派、多保洁员模式、超时和取消。 | M09 | 同一任务不能被多人同时成功领取,状态变化可追踪。 | 无 | `TODO` |
| CLN-003 | 保洁统计 | 保洁员查看任务数量、完成时长、驳回数、待结算和已结算金额。 | M09 | 统计口径和明细可相互校验。 | 无 | `TODO` |
| CLN-004 | 保洁结算 | 管理员按周期结算已完成且合格任务,生成结算单并保留明细。 | M09 | 结算后任务不可重复结算,撤销需反向单据。 | 无 | `TODO` |
| CLN-005 | 保洁驳回/免清洁 | 不达标可驳回补做;不需要保洁可标记免清洁,免清洁不计结算。 | M09 | 驳回原因和图片可查,统计和结算口径正确。 | 无 | `TODO` |
| ADM-001 | 管理员小程序模式 | 管理员可代下单、取消订单、启停房间、临时开门、控制电源、查看门店经营概况。 | M08/M06 | 全部操作校验门店范围并记录审计日志。 | 实际硬件部分 | `TODO` |
| ORD-006 | 管理员订单管理 | 管理员可转移订单、增加/减少时长、换房、备注、补收差价或退款。 | M04/M08 | 所有人工变更形成订单操作历史和资金变更记录。 | 支付能力可选 | `TODO` |
| GRP-003 | 管理员验券 | 管理员在小程序扫码或输入美团/抖音券码验券。 | M05/M08 | 验券结果、操作人、门店、券码脱敏值和平台返回信息可查。 | 平台商家授权 | `TODO` |
| MEM-001 | 会员管理 | 查看注册时间、最后下单、订单数、消费额、余额、优惠券和状态;可赠券、禁用或备注。 | M07/M08 | 敏感信息脱敏,人工操作有审批/审计。 | 无 | `TODO` |
| DEV-003 | 设备管理 | 查看设备在线状态、信号、版本、能力、门店/房间绑定、最近心跳和故障。 | M06/M08 | 设备离线不假装成功,绑定冲突可检测。 | 实际硬件/厂商平台 | `TODO` |
| REP-001 | 数据统计报表 | 今日、近 7 日、自定义日期;全门店或指定门店;收入分渠道、订单数、下单人数、使用率、使用时长。 | M10/M08 | 每个指标有口径定义,可由明细复算,支持导出。 | 无 | `TODO` |
| FRN-001 | 加盟商与自动分账 | 加盟申请、租户开通、门店归属、支付账户、分账比例、分账授权、结算记录。 | M05/M10/M08 | 未授权时不执行真实分账;分账指令幂等且有对账记录。 | 微信支付服务商/分账权限 | `TODO` |
| BKG-001 | 后台广告管理 | 配置首页广告、加盟信息和品牌信息。 | M08 | 支持租户隔离、排序、投放时间、上下架。 | 无 | `TODO` |
| BKG-002 | 后台用户管理 | 查看用户、角色、禁用、IP、手机号、注册与登录时间。 | M02/M08 | 敏感字段脱敏,禁用立即生效。 | 无 | `TODO` |
| BKG-003 | 后台门店管理 | 查看和维护门店状态、定位、业务配置和通知地址。 | M03/M08 | 配置变更有审计和版本记录。 | 无 | `TODO` |
| BKG-004 | 后台设备管理 | 设备入库、绑定门店/房间、状态、能力、固件版本和故障。 | M06/M08 | 设备资产、绑定和命令日志可关联查询。 | 实际硬件 | `TODO` |
| BKG-005 | 后台支付订单管理 | 查询支付、回调、退款、退款状态、分账和异常订单。 | M05/M08 | 金额用分存储,订单、支付单、退款单可完整关联。 | 微信支付 | `TODO` |
| BKG-006 | 后台团购验券记录 | 查询券码、平台、订单、门店、操作人、验券结果和原始响应摘要。 | M05/M08 | 券码脱敏,支持异常重试和人工备注。 | 团购平台 | `TODO` |
| BKG-007 | 后台微信支付配置 | 按小程序/租户/门店配置 AppID、商户号、证书、支付密钥、分账开关、比例和授权状态。 | M05/M08 | 密钥加密存储或环境变量引用,不在前端和日志中泄露。 | 商户号/证书 | `TODO` |
| BKG-008 | 后台加盟信息管理 | 查看用户提交的城市、电话、联系人、留言、跟进状态和提交时间。 | M10/M08 | 支持分派、跟进记录和状态流转。 | 无 | `TODO` |
| BKG-009 | 后台设备使用记录 | 按设备、门店、房间、操作人、命令和时间查询硬件操作。 | M06/M08 | 每次命令都有请求、响应、耗时、结果和关联业务单据。 | 实际硬件 | `TODO` |
| BKG-010 | 后台多小程序管理 | 平台超管可创建多个小程序/租户,每个 AppID、配置、门店、用户、订单、支付和文件数据逻辑独立。 | M02/M08/M10 | tenant_id 强制隔离,唯一索引包含 tenant_id,跨租户访问测试通过。 | 多个小程序 AppID | `TODO` |
| ENV-001 | 固定 Windows 工作区 | 唯一正式开发根目录为 `D:\qipai`,开发总纲、参考资料和正式代码均在同一 Git 根中。 | M00 | Windows/WSL 路径检查通过;错误目录会阻断;无第二业务仓库。 | Windows/WSL | `TODO` |
| REF-001 | 参考资料完整纳管 | `D:\qipai\参考` 内的源码、SQL、静态包、脚本、运行包和硬件协议完成递归清单、脱敏、哈希和 Git 跟踪。 | M00 | 清单可复现;无嵌套 Git;无真实秘密;应提交资料无遗漏。 | 参考资料 | `TODO` |
| SCM-001 | 模块完成即完整推送 | 每个模块子阶段结束后检查 tracked/untracked/ignored,提交全部应交付变化并立即 SSH push `origin/main`。 | M00-M10 | 无未解释项目文件;HEAD 与 origin/main 一致;push 失败不得 DONE。 | Gitea | `TODO` |
| WSL-001 | WSL 隔离辅助验证 | `/mnt/d/qipai` 用于只读/轻量检查,完整 Linux 构建在 WSL 原生临时副本进行,不与 Windows 共用 node_modules。 | M00/M10 | ShellCheck、Linux 构建和清理脚本通过;临时副本不 push。 | WSL | `TODO` |
| IOT-DEV-001 | WSL 本地 EMQX/MQTTX 调试环境 | EMQX 5.8.9 已运行并启用自启,1883/8883/8083/8084/18083 已监听;MQTTX CLI 1.13.0 已安装到 `/usr/local/bin/mqttx`。仍需验证账号用途、客户端认证、最小 ACL、Windows 连通性、MQTT 3.1、QoS 1、TLS 证书和三个 Topic。 | M00/M06 | 生成 `docs/local-dev-mqtt.md`Broker/CLI 安装状态为 DONEWindows/WSL 协议、ACL、TLS 冒烟通过后方可整体 DONE;本地凭据不用于生产;WSL Dashboard 凭据为 `admin/admin123`。 | Windows/WSL | `PARTIAL` |
| API-001 | 固定 HTTPS API 域名 | 小程序、后台、支付回调、文件 URL 和所有生产公开地址统一使用 `https://api.txyundm.cn`;禁止散落硬编码、HTTP、IP 和旧域名。 | M00/M01/M08/M10 | 构建产物扫描通过;公开健康接口、后台 API、小程序真机请求均命中固定域名。 | DNS/域名备案 | `TODO` |
| TLS-001 | Nginx 与证书自动化 | 菜单式脚本生成 Nginx 配置、申请/续期证书、验证 TLS 1.2/1.3、HTTP 跳转、安全头并支持失败回滚。 | M00/M10 | `nginx -t`、证书 SAN/链/剩余天数、续期 dry-run、公开 curl 检查全部通过。 | 域名 DNS/80/443 | `TODO` |
| WXNET-001 | 微信合法域名与真机验证 | request/uploadFile/downloadFile 合法域名统一为 `https://api.txyundm.cn`,预览/体验/正式版不得依赖忽略校验。 | M00/M08/M10 | Android/iOS 至少一类真机完成登录、上传、下载、下单和设备接口冒烟。 | 微信小程序后台权限 | `TODO` |
| OPS-005 | API 域名环境监测 | Windows、WSL、Ubuntu 检查 DNS、443、证书、Nginx、公开健康接口、CORS、上传路径和生产产物域名。 | M00/M10 | 生成脱敏 JSON/Markdown 报告;FAIL 阻止生产发布。 | Ubuntu 服务器 | `TODO` |
| OPS-001 | 固定 `/opt/apps` 目录 | 按 V5.5 约定创建 Gitea、后端、后台、小程序、EMQX、MySQL、Redis 预留和备份目录,并执行专用用户与权限隔离。 | M00/M10 | 目录、所有者、权限、磁盘和备份位置通过环境检测;重复执行不破坏数据。 | Ubuntu 服务器 | `TODO` |
| OPS-002 | 单仓库 Gitea 推送与拉取部署 | Windows 每完成一个模块子阶段即 SSH 推送 `panda/qipai.git`;生产服务器使用只读密钥从同一仓库拉取 `main`/标签,禁止直接复制源码。 | M00/M10 | 固定 remote、branch、commit、ahead/behind/dirty 可见;推送失败模块不得 DONE;生产异常时阻止部署。 | Gitea | `TODO` |
| OPS-003 | 整仓发布清单 | 后端、后台、小程序源码镜像按一次发布记录对应 commit、迁移、构建和健康结果。 | M00/M10 | `current-release.json` 与实际运行 commit 一致,回滚点可验证。 | 无 | `TODO` |
| OPS-004 | 菜单式更新与环境监测 | `/opt/apps/setup.sh` 提供初始化、Gitea 管理、按子项目部署、整仓发布、状态、备份、恢复、回滚和诊断。 | M00/M10 | 启动快检、操作前预检、操作后复检完整;更新失败不影响当前运行版本。 | Ubuntu 服务器 | `TODO` |
| IOT-001 | MQTT Broker 生产部署 | 在 `101.42.38.246` 的 Ubuntu 24.04 x86-64/amd64 无桌面服务器上通过官方 Apt 源原生安装 EMQX 5.x;服务器不安装 MQTTX,按需安装 `mosquitto-clients` 做命令行自检;兼容 MQTT 3.1、QoS 1,并配置认证、ACL、systemd、日志和备份。WSL 已安装 EMQX 不等于本项完成。 | M00/M06 | 匿名连接关闭;设备和后端权限最小化;重启后自动恢复;Dashboard 不直接暴露公网。 | Linux 服务器 | `TODO` |
| IOT-002 | 控制箱接入 | 读取基本信息、控制 2 路 30A、1 路 10A、磁力锁、TTS、LED、订单任务、续时和取消任务。 | M06 | 每条命令有 13 位以内关联 ID、回包、超时、重试和审计;字段拼写严格遵循厂商协议。 | 控制箱实物 | `TODO` |
| IOT-003 | Sub-1G 门锁绑定 | 后台发起 `AddDevice`,现场按 `*789#`,保存 `subID``subtype`,支持换绑和解绑审计。 | M06 | 绑定超时可恢复;一个门锁不能同时绑定两个房间;父控制箱关系可追踪。 | 控制箱和门锁实物 | `TODO` |
| IOT-004 | Sub-1G 门锁控制 | 支持 701C/701G 开关门、延时关门、常开、密码/卡片管理及开关门事件。 | M06 | `timeout/full/unconfirm` 不得当作成功;凭据脱敏;恢复出厂仅平台超管双重确认。 | 门锁实物 | `TODO` |
| IOT-005 | 智慧插座接入 | 支持状态查询、开关、本地任务、计量读取和特殊断电事件。 | M06 | 计量字段只在能力支持时展示;本地任务最多 20 条;高频轮询受限。 | 插座实物 | `TODO` |
| IOT-006 | MQTT 消息幂等 | QoS 1 允许重复消息,事件必须按设备、Topic、事件时间和负载摘要去重。 | M01/M06 | 重复回包不重复推进订单;重复事件不重复生成记录或通知。 | 无 | `TODO` |
| IOT-007 | 命令状态机 | 设备命令使用 `PENDING/PUBLISHED/ACKED/FAILED/TIMEOUT/UNKNOWN/CANCELLED`。 | M01/M06 | 只有收到有效回包才 ACKED;发布成功不等于执行成功;过期订单命令禁止补发。 | 无 | `TODO` |
| IOT-008 | 设备告警与离线 | 处理 `/devicewill/{DeviceID}`、心跳/联网、上电、门磁、门锁、保护断电和任务结束事件。 | M06/M10 | 离线、低电量、弱信号、过载、温度过高、通信超时可查询并告警。 | 实际硬件 | `TODO` |
| IOT-009 | 设备运维后台 | 手机和桌面均可查看设备拓扑、在线状态、信号、固件、最近事件、命令及重试。 | M08 | 危险操作分级授权和二次确认;手机端可完成应急开门但必须审计。 | 无 | `TODO` |
## 2.6.4 功能状态文件要求
Codex 第一次读取本版本文档时,必须把上表同步为 `docs/feature-status.md`,至少包含:
```markdown
# 功能状态
| ID | 功能 | 模块子阶段 | 状态 | 最近提交 | 验收证据 | 阻塞原因 | 下一步 |
|---|---|---|---|---|---|---|---|
| SYS-001 | 微信原生小程序 | M00-C | TODO | - | - | - | - |
```
每次开发完成必须更新对应功能 ID。模块标记 `DONE` 前,其范围内所有必做功能必须为 `DONE`;存在 `PARTIAL``BLOCKED_*` 时,模块只能标记 `PARTIAL``BLOCKED`
## 2.6.5 额外保留的现有系统能力
以下能力在旧 SQL 或旧小程序中已有明显痕迹,作为图中功能完成后的扩展范围保留,不能在重构时误删:
- 商品点单、商品库存和寄存。
- 抽奖活动及核销。
- KTV/点歌机扩展。
- 人脸识别记录和黑名单。
- 第三方机器人 Webhook。
这些功能使用 `EXT-*` 编号单独维护,不得阻塞图中必做功能的主线开发。
---
# 3. 技术栈约束
## 3.1 后端技术栈
| 项目 | 选择 | 约束 |
|---|---|---|
| 语言 | TypeScript | 禁止纯 JS 大工程;必须开启严格类型检查 |
| 运行时 | Node.js LTS | 使用服务器稳定 LTS 版本;不要追逐非 LTS 新版 |
| Web 框架 | Fastify | 轻量、性能好,适合 2GB 服务器 |
| 数据库驱动 | mysql2 | 必须使用连接池 |
| ORM/查询构建器 | Kysely 或 Drizzle | 优先轻量;不要引入过重运行时 |
| 参数校验 | Zod | 所有入参必须 schema 校验 |
| 日志 | Pino | JSON 日志,生产环境写文件并轮转 |
| 认证 | JWT + MySQL 会话/Token 记录 | 管理端和小程序端分开用户类型 |
| 文件上传 | multipart + 本地磁盘 | `/opt/apps/qipai-backend/shared/uploads`,仅由 `https://api.txyundm.cn/uploads/` 暴露 |
| 任务调度 | node-cron 或数据库轮询 | 替代旧 Quartz;任务少量即可 |
| 进程管理 | PM2 | 开机自启,日志轮转 |
| MQTT 客户端 | `mqtt`MQTT.js | 固定启用 MQTT 3.1 兼容模式、QoS 1、自动重连、离线队列限制和消息解析校验 |
| MQTT Broker | EMQX 5.x Ubuntu 24.04 amd64 Apt 包 | 最低 5.3;运行于 `101.42.38.246`;禁用匿名;不使用 Docker/ARM64 包 |
禁止项:
- 禁止把业务逻辑写在小程序前端里。
- 禁止使用 Docker、Docker Compose、容器镜像部署。
- 禁止使用微信云函数、云数据库、云托管。
- 禁止把支付密钥、数据库密码、JWT Secret 写死在源码。
- 禁止把 `node_modules`、构建产物、日志、大图片、数据库备份提交到 Gitea。
- 禁止一次性引入大量复杂依赖导致 2GB 服务器跑不动。
- 禁止小程序或 Vue 后台直接连接 MQTT Broker。
- 禁止生产构建中出现除 `api.txyundm.cn` 之外的旧 API 域名、HTTP API、服务器 IP 或 `localhost:3001`
- 禁止把 MQTT 用户名、密码、设备恢复出厂密码、门锁密码或卡号写进 Git、日志和前端响应。
- 禁止擅自“修正”厂商协议中的命令拼写,例如 `ConctolPower``Crldoor``CrlLED` 必须按协议原样发送。
## 3.2 后台管理端技术栈
| 项目 | 选择 | 约束 |
|---|---|---|
| 框架 | Vue 3 | Composition API 优先 |
| 构建 | Vite | 生产只部署 `dist` |
| 语言 | TypeScript | 页面与 API 类型分离 |
| UI | Element Plus | 快速构建后台管理 |
| 状态 | Pinia | 用户、权限、租户、菜单状态 |
| 请求 | Axios | 统一拦截 401/403/500 |
| 图表 | ECharts | 统计模块使用,非 MVP 不引入复杂大屏 |
后台响应式硬约束:
- 断点至少覆盖 360、375、390、430、768、1024、1366px。
- 手机端使用顶部栏 + 抽屉导航;表格优先切换为摘要卡片或详情抽屉,禁止整页横向滚动。
- 表单在手机端单列显示;弹窗/抽屉在窄屏下全屏;主要触控目标不小于 44×44px。
- 订单处理、退款、换房、开门、控电、设备查询、保洁处理、会员查询等核心功能不得提示“请到电脑端操作”。
- Playwright 或等价 E2E 必须包含移动视口;同时进行真实微信内置浏览器/手机浏览器抽测。
## 3.3 小程序端技术约束
- 保留微信原生小程序开发方式。
- 不要求改成 Taro/uni-app。
- 保留现有页面结构,优先替换接口地址和请求封装。
- 正式 `baseUrl` 固定为 `https://api.txyundm.cn/app-api`,只能从集中环境配置读取。
- `tenantId` 可保留,但后台应支持按租户查询和隔离。
- 小程序端只做展示、交互、扫码/NFC 唤起、支付拉起;核心校验放后端。
---
# 4. 目标仓库与目录结构
## 4.1 单一 Monorepo 工作区
本项目使用一个 Gitea 仓库,不再拆分为多个远端:
```text
远端 Web https://git.txyundm.cn/panda/qipai.git
远端 SSH ssh://git@git.txyundm.cn:2222/panda/qipai.git
默认分支: main
```
目标仓库结构:
```text
qipai/ # 单一 Git 仓库根目录
├─ .git/
├─ README.md # 项目入口、配置索引摘要、明文凭据和简要版本递进
├─ V5.6.md # 当前唯一开发总纲
├─ 参考/ # 全部可借鉴资料,脱敏审计后纳入仓库
├─ package.json
├─ pnpm-workspace.yaml # 使用 npm 时可不需要
├─ src/ # 后端源码;若现有代码使用 backend/,保留并记录
│ ├─ modules/
│ ├─ infrastructure/mqtt/
│ ├─ adapters/hardware/
│ ├─ workers/
│ └─ db/
├─ database/
│ └─ migrations/
├─ admin/ # Vue3 管理后台
│ ├─ package.json
│ ├─ src/
│ ├─ tests/
│ └─ vite.config.ts
├─ miniapp/ # 微信原生小程序
│ ├─ app.js
│ ├─ app.json
│ ├─ pages/
│ ├─ packageA/
│ ├─ utils/
│ └─ project.config.json
├─ docs/ # 所有模块、功能、API、数据库、部署和联调记录
├─ tests/
├─ deploy/
├─ scripts/
│ ├─ setup/
│ └─ dev/
└─ setup.sh
```
目录兼容原则:
- Codex 必须先审计远端仓库当前结构,不能为了贴合示例而无依据大规模移动已有代码。
- 如果现有后端目录名是 `backend/`、后台是 `qipai-admin/`、小程序是 `qipai-miniapp/`,可继续使用,但必须在 `docs/repository-map.md` 记录真实映射。
- 无论内部目录如何命名,全部代码、文档、迁移和脚本必须属于同一个 `.git`,只配置一个 `origin`
- `参考/` 也属于同一个 `.git`,但正式业务代码不得直接写入参考项目;必须保留来源、哈希和脱敏记录。
- 不允许存在嵌套 `.git`、submodule 或第二个业务远端。
- 一个模块涉及多个子项目时,只生成一个 commit;该 commit 同时包含后端、后台、小程序、测试和文档变化。
- 禁止使用 Git submodule 拆回多个仓库,除非用户以后明确决定改变架构。
## 4.2 Ubuntu `/opt/apps` 固定结构
```text
/opt/apps/
├─ setup.sh -> /opt/apps/qipai-backend/setup.sh
├─ gitea/
│ ├─ gitea
│ ├─ custom/conf/app.ini
│ ├─ data/
│ ├─ repositories/
│ └─ logs/
├─ qipai-backend/ # 单一 qipai 仓库的生产检出目录
│ ├─ .git/
│ ├─ README.md
│ ├─ V5.6.md
│ ├─ src/ 或 backend/
│ ├─ admin/ # 后台源码,构建后发布到 /opt/apps/qipai-admin
│ ├─ miniapp/ # 小程序源码,按需镜像到 /opt/apps/qipai-miniapp
│ ├─ database/migrations/
│ ├─ shared/
│ │ ├─ uploads/
│ │ ├─ certs/
│ │ └─ runtime/
│ ├─ deploy/
│ ├─ docs/
│ └─ setup.sh
├─ qipai-admin/ # 仅保存后台已发布静态产物和版本信息
│ ├─ current -> releases/<release-id>
│ ├─ releases/
│ └─ VERSION
├─ qipai-miniapp/ # 小程序源码镜像和版本记录,不运行服务
│ ├─ source/
│ └─ VERSION
├─ emqx/
│ ├─ README.md
│ ├─ config-export/
│ ├─ acl/
│ └─ backups/
├─ mysql/
│ ├─ migrations/
│ ├─ config-backups/
│ └─ dumps/
├─ redis/
│ └─ README.md # 预留;默认 RESERVED/DISABLED
└─ backups/
├─ mysql/
├─ gitea/
├─ app/
├─ emqx/
├─ configs/
└─ manifests/
```
系统服务目录边界:
| 服务 | `/opt/apps` 用途 | Ubuntu 标准运行目录 |
|---|---|---|
| Gitea | 程序、配置、仓库和数据可直接放 `/opt/apps/gitea` | systemd 单元在 `/etc/systemd/system/gitea.service` |
| 后端 | 单一 Git 工作区、构建产物、上传、证书和部署脚本 | 日志在 `/var/log/qipai`,密钥在 `/etc/qipai` |
| 后台 | 已发布静态版本,不作为独立 Git 仓库 | Nginx 配置在 `/etc/nginx` |
| 小程序 | 源码镜像和 commit 记录,不作为独立 Git 仓库 | 不运行服务,不由 Nginx 对外发布源码 |
| EMQX | 配置导出、ACL 模板、备份和项目记录 | `/etc/emqx``/var/lib/emqx``/var/log/emqx` |
| MySQL | 迁移、配置备份和逻辑备份 | `/etc/mysql``/var/lib/mysql``/var/log/mysql` |
| Redis | 预留说明和未来配置备份 | 默认不安装;启用后才使用 `/etc/redis``/var/lib/redis` |
禁止为了“目录看起来统一”把 Apt 管理的 MySQL/EMQX 数据目录强行移动到 `/opt/apps`
## 4.3 用户、权限与运行身份
| 路径/服务 | 所有者建议 | 权限原则 |
|---|---|---|
| `/opt/apps` | `root:root` | 0755;普通用户不能新增顶层目录 |
| `/opt/apps/gitea` | `git:git` | 0750Gitea 进程只用 `git` 用户 |
| `/opt/apps/qipai-backend` | `qipai:qipai` | 0750;唯一生产 Git 工作区;Git、依赖安装和构建以 `qipai` 用户执行 |
| `/opt/apps/qipai-admin` | `qipai:www-data` | 0750Nginx 只读 current 静态目录 |
| `/opt/apps/qipai-miniapp` | `qipai:qipai` | 0750;仅源码镜像和版本记录 |
| `/opt/apps/emqx` | `root:emqx` | 0750;导出文件按最小权限 |
| `/opt/apps/mysql` | `root:mysql` | 0750;逻辑备份 0600 |
| `/opt/apps/redis` | `root:root` | 0750;未启用时仅 README/占位 |
| `/opt/apps/backups` | `root:qipai` | 0750;敏感归档 0600 |
`setup.sh` 可以用 root 启动,但必须通过 `sudo -u qipai` 执行 Git、依赖安装和构建;不得让 npm 生命周期脚本以 root 运行。
## 4.4 `.gitignore` 强制项
单一仓库必须忽略:
```gitignore
node_modules/
.pnpm-store/
dist/
build/
coverage/
logs/
*.log
.env
.env.*
!.env.example
*.pem
*.key
*.p12
*.crt
*.cer
uploads/
backup/
backups/
*.sql.gz
*.dump
*.tar.gz
project.private.config.json
参考/**/.git/
参考/**/.svn/
.idea/
.vscode/
.DS_Store
Thumbs.db
```
不得全局忽略 `*.sql``*.zip``*.pdf` 或整个 `参考/`;迁移 SQL、协议文档和经过审计的参考资料必须能被纳入 Git。
生产服务器还必须禁止把 `/opt/apps/backups``shared/uploads`、Gitea 数据、EMQX/MySQL 导出和任何 secret 纳入业务仓库。
# 5. 生产部署规范:纯直接部署
## 5.1 `/opt/apps` 直接部署基线
### 5.1.1 初始化固定目录
```bash
sudo install -d -m 0755 -o root -g root /opt/apps
sudo install -d -m 0750 -o git -g git /opt/apps/gitea
sudo install -d -m 0750 -o qipai -g qipai /opt/apps/qipai-backend
sudo install -d -m 0750 -o qipai -g www-data /opt/apps/qipai-admin
sudo install -d -m 0750 -o qipai -g qipai /opt/apps/qipai-miniapp
sudo install -d -m 0750 -o root -g emqx /opt/apps/emqx
sudo install -d -m 0750 -o root -g mysql /opt/apps/mysql
sudo install -d -m 0750 -o root -g root /opt/apps/redis
sudo install -d -m 0750 -o root -g qipai /opt/apps/backups
```
实际脚本必须先创建系统用户/组,再创建目录。环境监测要校验目录、所有者、权限、可用空间和软链接目标。
### 5.1.2 固定 Gitea 仓库基线
- 唯一项目仓库:`panda/qipai.git`
- Web 地址:`https://git.txyundm.cn/panda/qipai.git`
- Windows/WSL 写入地址:`ssh://git@git.txyundm.cn:2222/panda/qipai.git`
- Ubuntu 同机只读拉取地址:`ssh://git@127.0.0.1:2222/panda/qipai.git`
- 默认生产分支:`main`
- Windows 已配置 SSH 免密;脚本只验证,不重复生成或覆盖用户密钥。
- 生产服务器使用独立只读部署密钥,不得使用 Windows 开发私钥,不得对仓库拥有 push 权限。
- Gitea Web 默认由 Nginx 反代到 `127.0.0.1:3000`;Git SSH 端口固定按当前环境使用 `2222`,写入配置而非业务源码。
首次配置 Windows 仓库:
```bash
git remote remove origin 2>/dev/null || true
git remote add origin ssh://git@git.txyundm.cn:2222/panda/qipai.git
git branch -M main
git fetch origin
git push -u origin main
```
已有 `origin` 时使用:
```bash
git remote set-url origin ssh://git@git.txyundm.cn:2222/panda/qipai.git
git remote -v
```
### 5.1.3 本地开发到生产的固定流水线
```text
Windows/WSL 单一仓库开发
→ 开始前 pull --ff-only origin main
→ 只完成一个模块子阶段
→ 本地测试与文档更新
→ commit
→ SSH push origin main
→ 校验 HEAD == origin/main
→ 模块标记 DONE
→ 管理员在 Ubuntu 执行 /opt/apps/setup.sh
→ 从本机 Gitea fetch/fast-forward 到指定 commit
→ 备份、构建、迁移、PM2/Nginx 发布
→ 部署后复检和发布清单
```
严禁:
- 用 FTP、宝塔上传、`scp`、网盘或压缩包覆盖生产源码;
-`/opt/apps/qipai-backend` 直接修改业务代码;
- push 到 Gitea 后由 Webhook 自动部署生产;
- 多个模块完成后才一次性 commit/push
- 在生产运行会产生 merge commit 的 `git pull`
- 使用 `git reset --hard` 静默清理未知修改;
- 使用 `git push --force` 覆盖远端历史。
### 5.1.4 Git 拉取与更新规则
生产只维护一个 Git 工作区 `/opt/apps/qipai-backend`
1. 验证仓库路径必须为 `panda/qipai.git`、默认分支为 `main`、部署密钥只读、SSH 主机指纹有效。
2. 检查工作区必须 clean,且不得处于 detached HEAD(按标签部署除外)。
3. 执行 `git fetch --prune origin`
4. 计算 `LOCAL/REMOTE/BASE`,分类为 `SYNCED``BEHIND``AHEAD``DIVERGED``DIRTY`
5. 只有 `SYNCED``BEHIND` 可以继续;`BEHIND` 只允许 `git merge --ff-only origin/main`
6. 部署指定 commit 时必须确认该 commit 存在于 `origin/main` 或受信任标签中。
7. 记录更新前后 commit、模块日志、数据库迁移和构建结果。
生产工作区不得配置写权限 push 回 Gitea;发布产生的版本文件、日志和构建产物不得提交到仓库。
### 5.1.5 后端、后台和小程序发布边界
- 后端:源码位于单一仓库根目录的真实后端路径;在 `/opt/apps/qipai-backend` 内构建,由 PM2 运行后端产物。
- 后台:从仓库内 `admin/`(或 `docs/repository-map.md` 记录的真实路径)构建;产物发布到 `/opt/apps/qipai-admin/releases/<release-id>`,成功后原子切换 `current`
- 小程序:从仓库内 `miniapp/` 同步到 `/opt/apps/qipai-miniapp/source` 并记录 commit;不在服务器上传微信平台,不通过 Nginx 暴露源码。
- 2GB 服务器必须顺序执行:后端依赖/测试/构建 → 释放缓存 → 后台依赖/测试/构建 → 数据库迁移/发布;禁止并行构建。
### 5.1.6 生产配置与环境变量
```text
/etc/qipai/qipai.conf
/etc/qipai/qipai.secrets
```
示例非敏感配置:
```env
APP_ROOT=/opt/apps
REPO_DIR=/opt/apps/qipai-backend
ADMIN_RELEASE_DIR=/opt/apps/qipai-admin
MINIAPP_MIRROR_DIR=/opt/apps/qipai-miniapp
BACKUP_DIR=/opt/apps/backups
GITEA_WEB_URL=https://git.txyundm.cn
GITEA_REPO_WEB=https://git.txyundm.cn/panda/qipai.git
GITEA_REPO_SSH=ssh://git@127.0.0.1:2222/panda/qipai.git
GITEA_SSH_HOST=127.0.0.1
GITEA_SSH_PORT=2222
DEPLOY_BRANCH=main
PUBLIC_BASE_URL=https://api.txyundm.cn
APP_API_BASE_URL=https://api.txyundm.cn/app-api
ADMIN_API_BASE_URL=https://api.txyundm.cn/admin-api
UPLOAD_PUBLIC_BASE_URL=https://api.txyundm.cn/uploads
ADMIN_WEB_BASE_URL=https://api.txyundm.cn/admin/
API_DOMAIN=api.txyundm.cn
FORCE_HTTPS=true
TRUST_PROXY=127.0.0.1
MQTT_BROKER_HOST=101.42.38.246
MQTT_BROKER_PORT=1883
```
密钥文件只保存数据库密码、JWT、微信支付、MQTT 密码和部署私钥路径,权限必须 600。
### 5.1.7 PM2 与 Nginx 固定路径
PM2 的 `cwd` 和启动文件必须读取真实仓库映射,不得假定后台/小程序是独立仓库。Nginx 后台根目录固定指向:
```text
/opt/apps/qipai-admin/current
```
上传目录固定指向:
```text
/opt/apps/qipai-backend/shared/uploads
```
### 5.1.8 备份统一位置
所有备份进入 `/opt/apps/backups/`。应用备份和发布清单只记录一个 Git commit,并分别记录后端、后台、小程序镜像和数据库迁移版本。
## 5.2 Ubuntu 24.04 菜单式部署、Gitea 拉取和环境监测
### 5.2.1 唯一入口
生产管理员只执行:
```bash
sudo bash /opt/apps/setup.sh
```
`/opt/apps/setup.sh` 应软链接到 `/opt/apps/qipai-backend/setup.sh`。首次安装前可从受信任安装包临时运行引导脚本,完成后必须建立该固定入口。
启动时先做只读快检:Ubuntu 24.04、x86_64/amd64、CPU/内存/Swap、磁盘/inode、时间、DNS、APT/DPKG、目录权限、Gitea、单一仓库、Nginx、MySQL、PM2、EMQX、证书、API、MQTT 和最近备份。
### 5.2.2 主菜单
```text
==================================================
自助棋牌室系统 - Ubuntu 24.04 部署管理
根目录:/opt/apps
==================================================
1. 初始化或修复服务器环境
2. 从 Gitea 更新并部署系统
3. Gitea 服务与仓库连接管理
4. MQTT / EMQX 服务管理
5. 配置域名与 HTTPS
6. 查看状态与环境检测
7. 备份与恢复
8. 回滚应用版本
9. 故障诊断与安全修复
0. 退出
==================================================
```
所有菜单全中文;非法输入返回菜单;破坏性动作必须二次确认;每个动作结束显示结果、日志、备份和下一步。
### 5.2.3 菜单 1:初始化或修复服务器环境
必须完成:
1. 三重系统检查:Ubuntu 24.04、`x86_64``amd64`、64 位。
2. 2GB 内存低资源策略:2GB Swap、MySQL 低内存参数、PM2 单实例、顺序构建、日志轮转。
3. 安装 Git、Nginx、MySQL、Node.js LTS、PM2、Certbot、UFW、构建工具。
4. 创建 `git``qipai` 用户和 `/opt/apps` 固定目录权限。
5. 原生安装/修复 Gitea,配置 systemd、Web 3000、SSH 2222(均可配置)。
6. 创建 `/etc/qipai/qipai.conf``qipai.secrets`
7. 配置 Nginx、PM2、日志目录、备份目录和防火墙。
8. 按用户选择安装/修复 EMQX;Redis 默认只创建预留目录,不安装服务。
9. 安装后复检,不得清空已有数据库、Gitea 仓库、上传、证书或配置。
### 5.2.4 菜单 2:从 Gitea 更新并部署系统
二级菜单:
```text
1. 查看固定仓库当前版本、远端版本和差异
2. 拉取 main 并部署后端
3. 拉取 main 并构建发布后台
4. 拉取 main 并同步小程序源码镜像
5. 拉取 main 并部署全部子项目
6. 按指定 commit 部署
7. 按版本标签部署
8. 查看最近模块提交和发布清单
0. 返回
```
所有选项必须:
1. 验证远端仓库路径为 `panda/qipai.git`,验证 SSH、部署密钥、主机指纹和 `main`
2. 检查单一工作区为 clean,分类 `SYNCED/BEHIND/AHEAD/DIVERGED/DIRTY`
3. 更新前自动备份数据库、配置、当前后台静态版本和发布清单。
4. 展示“当前 commit → 目标 commit、提交人、提交时间、模块编号和摘要”,要求确认。
5. 只允许 fast-forward、指定 `origin/main` 中的 commit 或受信任标签。
6. 按 2GB 内存约束顺序安装依赖、测试、构建和迁移。
7. 后端通过 PM2 reload,后台通过 release/current 原子切换,小程序仅同步源码镜像。
8. 失败时保留当前运行版本并提供回滚;不得把失败 commit 写成当前发布版本。
9. 成功后生成单仓库发布清单,记录一个 commit 和各子项目结果。
### 5.2.5 菜单 3Gitea 服务与仓库连接管理
二级菜单:
```text
1. 安装或更新 Gitea 原生版
2. 查看 Gitea 服务、版本、端口和磁盘
3. 配置/校验固定 qipai 仓库地址与 main 分支
4. 测试 Windows 公网 SSH 地址说明
5. 测试 Ubuntu 本机只读 SSH 拉取
6. 查看 origin、commit、ahead/behind 和工作区状态
7. 更新 known_hosts 主机指纹(需人工确认)
8. 备份 Gitea
9. 查看仓库容量、大文件和 LFS 风险
0. 返回
```
约束:
- 固定仓库为 `panda/qipai.git`,不得通过菜单创建三个新项目仓库。
- Gitea 更新前必须备份 `app.ini`、repositories、data 和数据库。
- Windows 开发密钥只用于开发机 push;生产部署密钥只读,二者不得混用。
- 初始化只允许 clone 到空的 `/opt/apps/qipai-backend`;目录非空且不是目标仓库时必须停止。
- Gitea 故障不得破坏当前运行中的业务版本,但会阻止新部署。
### 5.2.6 菜单 4MQTT / EMQX 服务管理
保留 V4.4 的 EMQX 约束:Ubuntu Apt 原生安装、EMQX 5.3+、MQTT 3.1、QoS 1、匿名关闭、ACL 最小化、服务器不安装 MQTTX GUI,`mosquitto-clients` 仅可选自检。
二级菜单:安装/更新 EMQX、配置认证 ACL、安装命令行自检工具、执行回环/越权测试、查看状态日志、导出配置到 `/opt/apps/emqx/`
### 5.2.7 菜单 5:固定域名与 HTTPS
固定管理对象为 `api.txyundm.cn`,二级菜单至少包含:
```text
1. 检查 DNS、当前公网 IP、80/443、安全组和 UFW
2. 生成或修复 api.txyundm.cn 的 Nginx 配置
3. 申请或续期 HTTPS 证书
4. 执行证书续期 dry-run
5. 检查证书 SAN、完整链、TLS 版本和剩余天数
6. 检查 /app-api、/admin-api、/uploads、/admin 路由
7. 输出微信小程序合法域名清单
8. 查看域名、证书和 Nginx 日志
9. 恢复上一个 Nginx/证书配置
0. 返回主菜单
```
执行规则:
- 域名固定为 `api.txyundm.cn`,除非用户明确变更,不再交互询问 API 域名。
- 先检查 DNS 指向当前服务器,再申请证书;DNS 不匹配时停止,不破坏现有配置。
- 每次写 Nginx 前备份到 `/opt/apps/backups/nginx/<timestamp>/`
- API 反代只指向 `127.0.0.1:3001`;后端端口不得直接暴露公网。
- 后台静态目录固定指向 `/opt/apps/qipai-admin/current/`
- 上传目录固定指向 `/opt/apps/qipai-backend/shared/uploads/`,关闭目录索引和脚本执行。
- 生成配置后先执行 `nginx -t`,成功才 reload;失败自动恢复备份。
- HTTP 80 只保留 ACME 与跳转,业务请求统一 301/308 到 HTTPS。
- 证书私钥只由 root/证书服务读取,禁止复制到 `/opt/apps/qipai-backend` 或 Gitea。
- 最后执行公开健康检查和生产产物域名扫描,并生成 `docs/api-domain-test-report.md` 所需数据。
### 5.2.8 菜单 6:状态与环境检测
至少展示:
- Ubuntu/架构/资源/时间/DNS/APT/端口;
- `/opt/apps` 目录权限和容量;
- Gitea、Nginx、MySQL、PM2、EMQX、自启状态;
- 单一仓库 remote、分支、commit、`SYNCED/BEHIND/AHEAD/DIVERGED/DIRTY`
- 当前整仓发布清单与实际运行版本是否一致;
- `api.txyundm.cn` DNS、80→443、证书 SAN/链/剩余天数、TLS 1.2/1.3、Nginx 配置与续期定时器;
- `https://api.txyundm.cn/app-api/health`、后台首页、后台 API、上传目录、数据库、MQTT 健康;
- 生产后端/后台/小程序构建产物不得包含旧域名、HTTP API、IP API 或示例域名;
- Redis 显示 `RESERVED/DISABLED`,不作为 FAIL
- 最近备份时间、大小、SHA256 校验状态;
- 脱敏后的最近关键错误。
健康报告保存:
```text
/var/log/qipai/preflight-*.log
/var/log/qipai/postflight-*.log
/var/lib/qipai/health/latest.json
/var/lib/qipai/health/history/
```
### 5.2.9 菜单 7:备份与恢复
二级菜单:立即完整备份、仅数据库、仅 Gitea、仅应用配置、仅 EMQX、列出备份、校验备份、恢复。
完整备份至少包含 MySQL、Gitea、上传目录、`/etc/qipai`、Nginx、PM2、EMQX 导出、单一仓库 commit 和发布清单。恢复必须输入 `RESTORE`,恢复前先做保护性备份,恢复后执行全量复检。
### 5.2.10 菜单 8:回滚应用版本
- 列出历史发布清单,而不是只猜“上一个目录”;
- 可分别回滚后端、后台或联合回滚;
- Git 回到清单记录的已知 commit,恢复对应构建产物;
- 默认不自动回滚数据库;涉及不兼容迁移时必须停止并引导菜单 7;
- 回滚后 PM2/Nginx reload 和健康检查必须通过。
### 5.2.11 菜单 9:故障诊断与安全修复
覆盖系统资源、目录权限、Gitea 服务/SSH、仓库分叉、部署密钥、Node/npm、PM2、Nginx、MySQL、EMQX、证书、API、备份和发布清单一致性。
自动修复仅允许安全动作,例如修正已知目录权限、重启已配置服务、清理过期临时文件。任何覆盖配置、重置仓库、恢复数据库、删除备份的动作必须先备份和二次确认。
### 5.2.12 配置与密钥
非敏感配置:`/etc/qipai/qipai.conf`640)。敏感配置:`/etc/qipai/qipai.secrets`600)。部署私钥建议 `/etc/qipai/ssh/id_ed25519`600)、`known_hosts`644)。
禁止:复用未确认用途的旧参考项目密码、把 SSH/TLS/支付私钥正文写入 Git/日志/发布清单、以管理员 Gitea 账号拉取、关闭 SSH 指纹校验。账号密码可按本版规则登记在 README 和配置索引。
### 5.2.13 脚本质量
- `#!/usr/bin/env bash``set -Eeuo pipefail`、trap、明确退出码;
- 全局部署锁;路径变量非空和根路径白名单;
- 幂等;外部下载 HTTPS 与校验;
- 不用 `nohup`、模糊 `pgrep|kill -9`、Docker、Podman、Kubernetes
- Git/npm 构建以 `qipai` 用户执行;systemd/目录/证书由 root 管理;
- `bash -n`、ShellCheck、PowerShell 检查和 WSL 预演;
- 未在真实 Ubuntu 执行的流程只能标记“未验证”。
### 5.2.14 Codex 同步维护规则
以下变化必须在同一批提交中同步更新部署脚本:
| 变化 | 同步内容 |
|---|---|
| 仓库名、分支、Gitea 端口 | repository-map、配置向导、连通性检查、更新菜单 |
| 环境变量 | 示例配置、校验、secrets、升级说明 |
| npm 依赖/构建命令 | 更新菜单、构建检查、PM2/Nginx 模板 |
| 数据库迁移 | 更新前备份、迁移、验证、回滚边界 |
| MQTT Topic/ACL | EMQX 菜单、配置导出、自检 |
| 上传/证书/日志目录 | 权限、备份、恢复、健康检查 |
| 后台输出目录 | Nginx root、发布和回滚 |
| 小程序版本 | 源码镜像 commit 和发布说明,不能假装服务器已发布微信版本 |
每次开发日志必须包含:受影响仓库、各仓库 commit、是否已 push、部署影响、受影响菜单、数据库迁移、生产人工操作和验证结果。
## 5.3 宝塔面板直接部署流程
宝塔可以用,但只能当作 Nginx、MySQL、Node、文件管理和计划任务的图形化工具。
宝塔部署要求:
- 安装 Nginx、MySQL、Node.js 版本管理器。
- 不安装 Redis、MongoDB、容器管理器、复杂监控插件。
- 网站根目录指向 `/opt/apps/qipai-admin/current`
- 反向代理 `/app-api``/admin-api``127.0.0.1:3001`
- PM2 可用宝塔 Node 项目管理,也可用命令行 PM2。
- 计划任务每天执行 MySQL 备份脚本。
宝塔 Nginx 反向代理本质仍按 5.1.7 的配置实现。
---
## 5.4 MQTT Broker`101.42.38.246` Linux 原生部署规范
### 5.4.1 固定结论
- Broker 主机:`101.42.38.246`
- BrokerEMQX 5.x,最低满足硬件协议要求的 5.3;固定通过官方 Ubuntu 24.04 amd64 Apt 源原生安装。
- 协议兼容:MQTT 3.1;后端 MQTT.js 使用 `protocolVersion: 3`
- 设备端口:默认 TCP 1883。若实测硬件支持 TLS,再增加 8883;不得未经实机验证直接强切 TLS。
- QoS:发布、订阅、遗嘱均按 QoS 1。
- 认证密码:不超过 31 位,使用大小写字母、数字和安全符号组合;不得复用数据库/JWT/服务器密码。
- 禁止 DockerEMQX 作为 systemd 服务原生运行。
### 5.4.2 Ubuntu 24.04 x86-64 原生安装和服务管理
生产 MQTT 服务器是 Ubuntu Server 无桌面版。部署前由 `setup.sh` 自动记录:
```bash
cat /etc/os-release
uname -m
dpkg --print-architecture
getconf LONG_BIT
uname -a
free -h
swapon --show
df -h
df -i
timedatectl status
```
允许值:
| 检测项 | 允许值 | 说明 |
|---|---|---|
| `ID` / `VERSION_ID` | `ubuntu` / `24.04` | 生产系统固定 |
| `uname -m` | `x86_64` | Linux 内核架构 |
| `dpkg --print-architecture` | `amd64` | Ubuntu 包架构 |
| `getconf LONG_BIT` | `64` | 64 位用户空间 |
EMQX 通过官方 Ubuntu Apt 源安装:
```bash
sudo apt-get update
sudo apt-get install -y curl ca-certificates gnupg lsb-release ufw
curl -fsSL https://assets.emqx.com/scripts/install-emqx-deb.sh \
-o /tmp/install-emqx-deb.sh
bash -n /tmp/install-emqx-deb.sh
sudo bash /tmp/install-emqx-deb.sh
sudo apt-get update
sudo apt-get install -y emqx
sudo systemctl enable --now emqx
sudo systemctl status emqx --no-pager
sudo journalctl -u emqx -n 200 --no-pager
```
服务器不安装 MQTTX。可选安装轻量客户端:
```bash
sudo apt-get install -y mosquitto-clients
```
只允许安装客户端包,不安装/启用 Mosquitto Broker。Windows MQTTX 桌面版承担主要图形化调试,WSL 可使用 `mosquitto-clients` 或项目 Node.js 冒烟脚本。
`deploy/VERSION` 只维护生产相关版本:
```text
EMQX_INSTALL_CHANNEL=stable
EMQX_MIN_VERSION=5.3.0
TARGET_OS=ubuntu
TARGET_OS_VERSION=24.04
TARGET_KERNEL_ARCH=x86_64
TARGET_DPKG_ARCH=amd64
NODE_MAJOR=22
```
必须生成 `deploy/emqx/README.md`,记录:
- 实际 EMQX 版本、安装来源和架构;
- 配置、数据、日志和备份目录;
- systemd 启停、自启、状态和日志命令;
- Windows MQTTX 调试方法;
- Ubuntu/WSL `mosquitto_pub/sub` 或 Node.js 冒烟测试方法;
- 认证、ACL、备份、恢复和回退;
- 最近验证日期、Git commit、环境报告和执行菜单项。
安装后最低验证:
```bash
systemctl is-enabled emqx
systemctl is-active emqx
ss -lntp | grep ':1883'
dpkg-query -W -f='${Package} ${Version} ${Architecture}
' emqx
```
如已安装命令行客户端,再执行 `mosquitto_pub/sub` 回环。MQTTX 不在服务器验收范围。任何核心项失败都不得把 IOT-001 标记为 `DONE`
### 5.4.3 端口与防火墙
| 端口 | 用途 | 公网策略 |
|---|---|---|
| 22 | SSH | 仅可信管理 IP,禁止密码弱口令 |
| 1883 | 4G 设备 MQTT TCP | 需要公网,但必须认证、ACL、连接限速 |
| 8883 | MQTT TLS | 仅硬件实测支持后启用 |
| 18083 | EMQX Dashboard | 禁止直接全网开放;仅内网、管理 IP 或 SSH 隧道 |
| 4370/5370 等集群端口 | EMQX 集群内部 | 单节点不开放公网 |
4G 物联卡出口 IP 通常不固定,因此不能仅依赖来源 IP 白名单;安全边界必须是认证、ACL、唯一 Client ID、连接限制、日志与告警。
### 5.4.4 账号和 ACL 最小权限
生产环境关闭匿名访问。推荐账号分层:
- `qipai_backend_prod`:订阅 `/devicesend/+``/devicewill/+`,发布 `/deviceaccept/+`
- 每台设备单独账号,或按可信批次分组账号;单设备账号只能:
- 发布 `/devicesend/{DeviceID}`
- 发布 `/devicewill/{DeviceID}`
- 订阅 `/deviceaccept/{DeviceID}`
- Dashboard 管理员账号独立,不得与 MQTT 客户端账号共用。
`DeviceID`、IMEI、MQTT 账号和凭据的映射保存在服务端。业务数据库和后台接口仍应加密/脱敏保存并禁止向普通前端回显;README 的运维凭据明文登记是私有仓库内的独立项目决策。
### 5.4.5 Topic 固定约定
| 方向 | Topic | 用途 | QoS |
|---|---|---|---|
| 设备 → 平台 | `/devicesend/{DeviceID}` | 回包、状态、事件 | 1 |
| 设备遗嘱 → 平台 | `/devicewill/{DeviceID}` | 断电/异常离线遗嘱 | 1 |
| 平台 → 设备 | `/deviceaccept/{DeviceID}` | 查询、动作和配置命令 | 1 |
后端启动后必须订阅两个通配 Topic:`/devicesend/+``/devicewill/+`。不得订阅全局 `#` 作为长期生产方案。
### 5.4.6 Broker 运维与安全
- EMQX Dashboard 不通过 Nginx 暴露给公众;优先 SSH 隧道访问。
- 开启 NTP/chrony,服务器时区固定 `Asia/Shanghai`,数据库存 UTC 或统一约定并在文档说明。
- 配置连接数、每客户端会话数、消息大小和发布速率上限,防止异常设备耗尽资源。
- 日志轮转并监控磁盘;保留认证失败、ACL 拒绝、频繁重连和异常 Client ID 记录。
- 每日备份 EMQX 配置、认证/授权数据和应用侧设备映射;备份中不得输出明文凭据到开发日志。
- Broker 重启后,后端应自动重连并重新订阅;不得要求人工重启业务后端。
- 建立健康检查:TCP 可达、MQTT 认证、订阅成功、最近消息时间、在线设备数、异常重连数。
### 5.4.7 物联卡流量约束
硬件文档说明设备卡月流量通常为 30MB,超过较高阈值可能锁卡。实现时必须:
- 不做秒级/分钟级全量轮询;默认依赖 `connected``Poweron`、回包和事件推送。
- `basicInfo``workInfo` 仅在设备上线、后台手动刷新、故障诊断或低频对账时读取。
- 设备列表使用数据库快照,不在每次打开页面时群发查询。
- TTS 文本、批量配置和重复命令设置限流。
- 后台显示“本月消息数/估算流量/异常重连”,便于识别流量风险。
---
# 6. 后端 API 规范
## 6.0 固定公开 Origin 与路径规则
所有正式 API 文档、SDK、前端配置、回调和测试用例必须使用:
```text
Origin: https://api.txyundm.cn
小程序 API: https://api.txyundm.cn/app-api
后台 API: https://api.txyundm.cn/admin-api
文件 URL: https://api.txyundm.cn/uploads/
```
约束:
- API 路径必须小写、语义稳定,禁止把版本号、租户 ID、Token 或密钥放入域名。
- Nginx 只做路由、TLS、基础限流和静态文件;业务鉴权、幂等、租户隔离和审计由后端完成。
- 后端生成绝对 URL 时必须读取 `PUBLIC_BASE_URL`,不得根据不可信 Host Header 拼接。
- 健康接口只返回状态、版本、commit、时间和依赖摘要,不返回环境变量、数据库地址、MQTT 凭据或堆栈。
- 所有 API 响应带 `X-Request-Id`;Nginx 与后端日志使用同一追踪 ID。
- 生产后端只监听 `127.0.0.1:3001`,公网只能通过 `api.txyundm.cn:443` 访问。
## 6.1 统一返回格式
为兼容旧小程序,统一返回:
```typescript
export interface ApiResult<T = unknown> {
code: number
msg: string
data: T | null
traceId?: string
}
// 成功
{ code: 0, msg: 'success', data: {} }
// 未登录
{ code: 401, msg: '登录已过期,请重新登录', data: null }
// 无权限
{ code: 403, msg: '无权限', data: null }
// 参数错误
{ code: 400, msg: '参数错误:xxx', data: null }
// 业务错误
{ code: 10001, msg: '房间已被预约', data: null }
```
## 6.2 API 前缀
| 前缀 | 用途 | 鉴权 |
|---|---|---|
| `/app-api` | `https://api.txyundm.cn/app-api`,微信小程序接口 | 用户 JWT,可读接口可匿名 |
| `/admin-api` | `https://api.txyundm.cn/admin-api`,Vue 后台接口 | 管理员 JWT + RBAC |
| `/uploads` | `https://api.txyundm.cn/uploads/`,只读文件访问 | Nginx 静态目录,上传动作需要接口鉴权 |
| `/app-api/health` | 健康检查 | 无需鉴权 |
## 6.3 请求头
| Header | 说明 |
|---|---|
| `tenant-id` | 租户 ID,兼容旧小程序 |
| `Authorization` | `Bearer <token>` |
| `X-Request-Id` | 可选,客户端请求追踪 |
| `Idempotency-Key` | 下单、支付、退款、开门等敏感接口必须支持 |
## 6.4 错误码建议
| code | 含义 |
|---:|---|
| 0 | 成功 |
| 400 | 参数错误 |
| 401 | 未登录/登录过期 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 409 | 并发冲突/重复提交 |
| 500 | 服务器错误 |
| 10001 | 房间不可预约 |
| 10002 | 时间段已被占用 |
| 10003 | 订单状态不允许操作 |
| 10004 | 支付单状态异常 |
| 10005 | 开门校验失败 |
| 10006 | 优惠券不可用 |
| 10007 | 余额不足 |
| 10008 | 设备离线或未配置 |
## 6.5 租户隔离
所有业务表查询必须带 `tenant_id`
后端中间件逻辑:
```text
1. 从 header.tenant-id 读取租户。
2. 若缺失,则读取环境变量 TENANT_DEFAULT_ID。
3. 管理端超级管理员可切换租户;普通管理员只能访问自己租户。
4. SQL 查询必须显式 where tenant_id = 当前租户。
5. 新增数据必须写入 tenant_id。
6. 禁止仅依赖前端传 storeId 判断权限。
```
## 6.6 鉴权模型
用户类型至少分三类:
| 类型 | 说明 | 表参考 |
|---|---|---|
| 顾客 | 小程序普通用户 | `member_user` |
| 门店管理员 | 管理自己门店 | `system_users` / `member_store_user` |
| 平台管理员 | 管理租户、全局配置 | `system_users` |
权限判断顺序:
```text
认证 token → 解析 userType/userId/tenantId → 查询用户状态 → 查询角色/门店权限 → 执行业务权限校验 → 写操作日志
```
---
## 6.7 设备与 MQTT API
小程序和后台只调用 HTTPS API,禁止直接连接 Broker。
### 小程序端
| 方法 | 路径 | 说明 |
|---|---|---|
| `POST` | `/app-api/iot/orders/:orderId/open-door` | 当前有效订单远程开门 |
| `POST` | `/app-api/iot/orders/:orderId/power-on` | 订单授权范围内通电 |
| `POST` | `/app-api/iot/orders/:orderId/power-off` | 订单授权范围内断电,按门店策略开放 |
| `GET` | `/app-api/iot/orders/:orderId/status` | 返回设备快照和最近命令,不触发全量 MQTT 查询 |
### 管理端
| 方法 | 路径 | 说明 |
|---|---|---|
| `GET/POST/PATCH` | `/admin-api/iot/devices` | 设备资产管理 |
| `POST` | `/admin-api/iot/devices/:id/bind` | 绑定门店、房间、用途、插槽 |
| `POST` | `/admin-api/iot/gateways/:id/pair-lock` | 发起 `AddDevice` 门锁绑定窗口 |
| `POST` | `/admin-api/iot/devices/:id/commands` | 经权限校验发送受控命令 |
| `GET` | `/admin-api/iot/commands` | 查询命令状态、回包、耗时和关联订单 |
| `GET` | `/admin-api/iot/events` | 查询联网、断电、门磁、门锁和保护事件 |
| `GET` | `/admin-api/iot/topology` | 门店→房间→控制箱→子门锁/插座拓扑 |
| `GET` | `/admin-api/iot/mqtt/health` | Broker 连接、订阅和最近消息健康状态 |
| `POST` | `/admin-api/iot/devices/:id/refresh` | 人工按需读取基本状态,带频率限制 |
危险接口(恢复出厂、清空卡片/密码、修改 MQTT 参数、解除绑定)必须:平台超管权限 + 二次确认 + 操作原因 + 审计日志;生产默认不在普通门店后台展示。
---
# 7. 数据库设计与迁移原则
## 7.1 总原则
1. MVP 阶段优先复用旧 SQL 的核心 `member_*` 表,降低迁移成本。
2. 不需要把 104 张表全部用起来;无用表保留但不依赖。
3. 新增表必须写入 `docs/db-changelog/`,包含正向 SQL 和回滚 SQL。
4. 禁止在生产库直接手工改字段后不记录。
5. 所有金额字段后端内部按“分”计算,展示时再转元;若复用旧 decimal 字段,必须在服务层统一转换。
6. 所有时间统一存 `datetime`,后端使用北京时间业务口径,接口返回 ISO 字符串或 `YYYY-MM-DD HH:mm:ss`
## 7.2 MVP 核心表映射
| 模块 | 旧 SQL 表 | 新系统处理 |
|---|---|---|
| 租户 | `system_tenant` | 保留或简化为默认租户 |
| 顾客 | `member_user` | 小程序用户主表 |
| 后台用户 | `system_users`, `system_role`, `system_user_role`, `system_menu` | 管理端登录和权限 |
| 门店 | `member_store_info` | 门店主表 |
| 房间 | `member_room_info` | 房间/包间主表 |
| 订单 | `member_order_info` | 预约订单主表 |
| 支付 | `member_pay_order` | 支付单主表 |
| 设备 | `member_device_info`, `member_device_use_info` | 门锁/电控/喇叭设备 |
| 会员余额 | `member_user_money_bill`, `member_discount_rules` | 钱包流水、充值规则 |
| 优惠券 | `member_coupon_info`, `member_coupon_active` | 优惠券发放与核销 |
| 套餐 | `member_pkg_info`, `member_pkg_user_info` | 次卡/时长套餐 |
| 保洁 | `member_clear_info`, `member_clear_bill` | 保洁任务、结算 |
| 商品库存 | `member_inventory_*`, `member_product_order`, `yshop_*` | 点单、库存、寄存 |
| 文件 | `infra_file` 或本地 `/uploads` | MVP 可直接本地上传,并在表中记录 URL |
| 日志 | `system_operate_log`, `infra_api_access_log` | 可简化写入操作日志 |
## 7.3 必须新增或确认的索引
```sql
-- 订单按房间和时间查询
CREATE INDEX idx_order_room_time_status ON member_order_info(room_id, start_time, end_time, status, deleted);
-- 订单按用户查询
CREATE INDEX idx_order_user_time ON member_order_info(user_id, create_time, deleted);
-- 订单按门店查询
CREATE INDEX idx_order_store_time ON member_order_info(store_id, start_time, status, deleted);
-- 支付单订单号
CREATE INDEX idx_pay_order_no ON member_pay_order(order_no);
-- 设备按门店房间查询
CREATE INDEX idx_device_store_room ON member_device_info(store_id, room_id, deleted);
-- 优惠券按用户状态查询
CREATE INDEX idx_coupon_user_status ON member_coupon_info(user_id, status, deleted);
-- 保洁任务按门店状态查询
CREATE INDEX idx_clear_store_status ON member_clear_info(store_id, status, deleted);
```
创建索引前 Codex 必须检查数据库是否已有同名或等效索引,避免重复。
## 7.4 时间段锁设计
不要用 Redis 锁。MVP 使用 MySQL 事务。
判定重叠:
```sql
SELECT order_id
FROM member_order_info
WHERE tenant_id = ?
AND room_id = ?
AND deleted = b'0'
AND status IN (0, 1, 4) -- 未开始、进行中、已预约,具体状态需按旧枚举确认
AND start_time < ? -- existing.start < new.end
AND end_time > ? -- existing.end > new.start
FOR UPDATE;
```
下单事务流程:
```text
BEGIN
1. 校验用户、租户、房间状态、营业时间、最小时长、提前预约天数。
2. 锁定房间行或查询重叠订单 FOR UPDATE。
3. 若存在重叠订单,返回 10002。
4. 计算价格、押金、优惠券、余额、团购。
5. 创建 member_order_info,状态 pending/unpaid。
6. 创建 member_pay_order。
7. 提交事务。
COMMIT
```
---
## 7.5 MQTT 与真实硬件数据模型
旧表 `member_device_info``member_device_use_info` 继续作为兼容来源,但新实现必须补足以下表或等价结构:
| 表 | 核心字段 | 关键约束 |
|---|---|---|
| `iot_device` | tenant_id、store_id、room_id、device_id、imei、iccid、type_code、vendor、model、firmware、online_status、last_seen_at、signal、capabilities_json | `device_id` 全局唯一;敏感 SIM 标识后台脱敏 |
| `iot_device_binding` | parent_device_id、child_device_id/sub_id、subtype、binding_type、slot_no、purpose、active | 同一有效用途只能绑定一个设备;保留历史 |
| `iot_device_command` | command_id、device_id、business_type/id、topic、action、request_json、status、published_at、acked_at、timeout_at、result_code、response_json、retry_count | `command_id` 最大 13 字符且唯一;请求敏感字段脱敏 |
| `iot_device_event` | device_id、topic、event_type、event_time、payload_json、payload_hash、processed_status、related_order_id | QoS 1 去重索引 |
| `iot_device_snapshot` | device_id、online、power_state_json、door_state、battery、signal、energy、last_message_at | 页面默认读快照,不实时群发查询 |
| `iot_lock_credential` | lock_device_id、order_id、credential_type、credential_hash/encrypted_value、valid_from/to、status、issued_command_id、revoked_command_id | 明文最小化;过期必须撤销和对账 |
| `iot_automation_job` | order_id、job_type、scheduled_at、status、lease_owner、attempts、last_error | 防止多实例重复下发 |
| `iot_alert` | device_id、alert_type、severity、first_at、last_at、status、payload_json | 离线、低电量、弱信号、超载、温度、命令超时 |
必须建立的索引/唯一键:
```sql
UNIQUE KEY uk_iot_device_device_id (device_id);
UNIQUE KEY uk_iot_command_id (command_id);
UNIQUE KEY uk_iot_event_dedupe (device_id, event_type, event_time, payload_hash);
KEY idx_iot_command_device_status (device_id, status, create_time);
KEY idx_iot_event_device_time (device_id, event_time);
KEY idx_iot_binding_room_active (tenant_id, store_id, room_id, active);
KEY idx_iot_job_due (status, scheduled_at);
```
命令状态只允许:`PENDING``PUBLISHED``ACKED``FAILED``TIMEOUT``UNKNOWN``CANCELLED`。MQTT publish 回调成功只能进入 `PUBLISHED`,不能直接进入 `ACKED`
---
# 8. 小程序端改造规范
## 8.1 改造目标
- 尽量保留已有页面和 UI。
- 把旧域名改为新域名。
- 统一请求封装,支持 token、tenant-id、错误提示。
- 未实现接口要清晰提示“功能开发中”,不能白屏。
- 支付、开门、退款、订单状态必须以后端返回为准。
## 8.2 小程序 API 环境配置
小程序必须有且只有一个 API 环境配置入口。示例:
```javascript
// miniapp/config/env.js
const ENVIRONMENTS = {
development: {
apiBaseUrl: "http://127.0.0.1:3001/app-api"
},
trial: {
apiBaseUrl: "https://api.txyundm.cn/app-api"
},
release: {
apiBaseUrl: "https://api.txyundm.cn/app-api"
}
}
function getRuntimeEnv() {
try {
return wx.getAccountInfoSync().miniProgram.envVersion || "development"
} catch (error) {
return "development"
}
}
module.exports = ENVIRONMENTS[getRuntimeEnv()]
```
```javascript
// miniapp/app.js
const env = require("./config/env")
App({
globalData: {
baseUrl: env.apiBaseUrl,
tenantId: "",
appName: "自助棋牌室"
}
})
```
约束:
- `trial``release` 必须固定为 `https://api.txyundm.cn/app-api`
- `development` 使用本地 API 仅限微信开发者工具;真机预览无法直接访问 Windows 的 `127.0.0.1`,应使用正式 HTTPS 域名和测试租户。
- 发布构建前自动扫描所有源码,发现 `api.txyundm.cn`、旧域名、HTTP 生产地址或 IP API 时失败。
- 页面和业务模块不得自行定义第二个 base URL。
- 上传和下载同样从 API 配置派生,不允许另写旧文件域名。
## 8.3 `utils/http.js` 增强要求
- 自动拼接集中配置中的 `https://api.txyundm.cn/app-api`;禁止页面传完整域名。
- 自动带 `tenant-id`
- 自动带 `Authorization`
- 401 自动清理 token 并跳转登录。
- 业务错误统一 `wx.showToast`
- 支持超时提示。
- 上传动作统一走鉴权 API,返回的公开 URL 必须以 `https://api.txyundm.cn/uploads/` 开头。
- 下载前校验域名和文件类型,不允许前端请求任意第三方 URL。
- 记录 `X-Request-Id`,网络错误提示区分 DNS、TLS、超时、401、业务错误和服务不可用。
## 8.4 小程序 MVP 必须可用页面
| 页面 | 必须能力 |
|---|---|
| 门店列表/首页 | 展示门店、公告、图片、房间入口 |
| 房间列表/详情 | 展示房间状态、价格、标签、图片 |
| 预约下单 | 选择时间、计算价格、提交订单 |
| 支付页 | 拉起微信支付、支付后更新订单 |
| 我的订单 | 查看订单列表、订单详情 |
| 开门页 | 订单有效期内开门,记录日志 |
| 个人中心 | 用户信息、手机号、余额、优惠券 |
| 续费 | 订单进行中可续费 |
| 优惠券 | 可领取、可使用、不可用原因 |
| 团购核销 | MVP 可先登记为待开发,避免影响主流程 |
---
# 9. 后台管理端设计规范
## 9.1 后台页面结构
```text
登录
首页仪表盘
门店管理
- 门店列表
- 门店编辑
- 门店首页模板/公告/图片
房间管理
- 房间列表
- 房间编辑
- 价格/押金/营业时间
- 房间状态
订单管理
- 订单列表
- 订单详情
- 取消/退款/续费/换房
支付管理
- 支付单
- 退款单
- 微信支付配置
设备管理
- 门锁/电控/喇叭
- 设备绑定房间
- 开门日志
会员管理
- 用户列表
- 余额流水
- 充值规则
营销管理
- 优惠券
- 套餐
- 团购配置
保洁管理
- 保洁任务
- 保洁人员
- 保洁结算
商品库存
- 商品列表
- 商品订单
- 寄存商品
统计报表
- 收入统计
- 订单统计
- 房间使用率
系统管理
- 管理员
- 角色权限
- 操作日志
- 参数配置
```
## 9.2 后台 API 域名与鉴权
- 生产 API 地址固定为 `https://api.txyundm.cn/admin-api`
- 如果后台页面部署在 `https://api.txyundm.cn/admin/`,生产 Axios 优先使用同源相对地址 `/admin-api`
- 本地 Windows 开发通过 Vite proxy 把 `/admin-api` 转发到 `http://127.0.0.1:3001`,不得为了开发关闭生产 CORS 安全策略。
- `.env.production` 至少包含 `VITE_API_BASE_URL=/admin-api` 或固定 HTTPS 地址;真实密钥不得出现在 `VITE_*` 变量。
- 登录后返回 admin token;前端 Axios 拦截器带 `Authorization``X-Request-Id`
- 菜单由后端返回,不在前端写死权限。
- 按钮级权限至少对删除、退款、开门、改价、导出和设备危险命令做限制。
- 401 清理会话并跳转登录;403 显示无权限;502/503 显示服务不可用和请求追踪号。
- 若后台未来改用独立域名,必须把该域名加入后端明确 CORS 白名单;禁止通配符放开。
- 生产构建后扫描 `dist`,发现 `localhost`、旧域名、HTTP API 或源映射泄露时阻止部署。
## 9.3 后台 MVP 页面验收
每个管理页面至少满足:
- 列表分页。
- 条件查询。
- 新增/编辑/删除或禁用。
- 状态字段中文化。
- 时间格式统一。
- 错误提示明确。
- 空数据状态友好。
- 操作后刷新列表。
- 涉及金额显示元,后端计算以分或统一 decimal 处理。
---
## 9.4 后台 Web 手机适配硬性规范
后台 Web 使用同一套 Vue3 源码响应式适配,不另建功能缩水的移动后台。
### 布局
- `>=1200px`:固定侧栏 + 顶部栏 + 多列内容。
- `768-1199px`:可折叠侧栏,表单最多两列。
- `<768px`:顶部栏 + 汉堡按钮 + 抽屉菜单;内容单列。
- 页面禁止出现整页横向滚动;只有明确的数据表局部容器可以横向滚动,并优先改为卡片视图。
### 组件
- 列表:手机端显示摘要卡片,点击进入全屏详情抽屉;重要状态和操作不隐藏。
- 筛选:手机端使用全屏/底部筛选面板,保留“重置”和“应用”。
- 表单:单列,标签置顶;日期时间、门店、房间选择器适合触控。
- 弹窗:窄屏时全屏;危险操作二次确认按钮与取消按钮间隔明显。
- 操作按钮:触控区域至少 44×44px;不能依赖鼠标悬停。
- 图表:自适应宽度,必要时提供指标卡或明细列表作为无障碍替代。
### 手机端必须可完成
- 查看房态、订单、会员、保洁、设备在线状态和告警。
- 代下单、续费、换房、取消、退款申请/审核(按权限)。
- 临时开门、开关电、开关灯、停止语音、查看命令结果。
- 门锁绑定流程的后台引导、设备扫码入库、故障备注。
- 保洁接单/开始/完成/驳回及上传照片。
### 测试
- Playwright 视口:360×800、375×812、390×844、430×932、768×1024、1366×768。
- 自动检查 `document.documentElement.scrollWidth <= innerWidth`
- 至少在 Android Chrome、iPhone Safari 或微信内置浏览器完成真实设备抽测。
- M08 未通过手机端验收不得标记 `DONE`
---
# 10. 微信支付、退款与安全闭环
## 10.1 支付原则
- 支付下单必须由后端创建。
- 小程序只调用后端返回的支付参数并执行 `wx.requestPayment`
- 支付回调只接受微信服务器调用。
- 回调必须验签。
- 回调必须幂等:同一微信支付单重复通知不能重复改订单、重复加余额、重复开门。
- 订单支付成功后才能进入“有效订单”。
## 10.2 支付接口
| 接口 | 方法 | 说明 |
|---|---|---|
| `/app-api/member/order/preOrder` | POST | 预下单,创建订单和支付单 |
| `/app-api/member/order/save` | POST | 兼容旧小程序提交订单 |
| `/app-api/pay/wechat/prepay` | POST | 创建微信预支付 |
| `/app-api/pay/wechat/notify` | POST | 微信支付回调 |
| `/app-api/member/order/getOrderInfoByNo` | GET | 查询订单状态 |
| `/admin-api/pay/orders` | GET | 后台支付单列表 |
| `/admin-api/pay/refund` | POST | 后台退款 |
## 10.3 退款原则
- 退款只能由后端调用微信退款接口。
- 后台必须记录操作人、退款原因、退款金额。
- 退款前校验订单状态。
- 部分退款必须记录剩余可退金额。
- 押金退款和订单退款要分开记录。
---
# 11. 已选硬件与 MQTT 协议落地
## 11.1 已确定设备
| 类别 | 已选设备 | 主要能力 | 接入方式 |
|---|---|---|---|
| 房间主控制器 | 4G 智能门禁控电箱 | 2 路 30A、1 路 10A、磁力锁/电控锁、门磁、TTS、LED、订单本地任务 | 4G → MQTT Broker |
| 房间门锁 | Sub-1G 智能门锁 | 701C 防盗门锁、701G 室内门锁;开关门、密码、卡片、电量、事件 | 通过控制箱 Sub-1G 转发 |
| 独立电器 | 4G(标准版)智慧插座 | 10A/16A 开关、本地任务;计量版支持电压、电流、功率、温度、电量和保护 | 4G → MQTT Broker |
| Broker | Linux 主机 `101.42.38.246` | EMQX、认证、ACL、消息路由、遗嘱 | MQTT 3.1 / QoS 1 |
设备协议 PDF 是最高优先级事实源。若旧源码、旧数据库、本文示例与 PDF 冲突,Codex 必须先记录差异,再以 PDF 和真实设备联调结果修正文档/代码;不得凭经验猜字段。
## 11.2 MQTT Topic 与连接规则
固定 Topic
```text
设备上行/回包: /devicesend/{DeviceID}
设备遗嘱: /devicewill/{DeviceID}
平台下行: /deviceaccept/{DeviceID}
```
- QoS 固定 1。至少一次投递意味着消息可能重复,所有事件和回包必须幂等。
- DeviceID 来自设备二维码/标签;入库时扫码录入并与 IMEI 交叉核对。
- 后端订阅上行和遗嘱通配 Topic,发布到具体设备 Topic。
- 指令 `id` 是字符串,最大 13 位。推荐 `10 位秒级时间戳 + 3 位滚动序号`,并以数据库唯一键兜底。
- 每个 MQTT Client ID 必须唯一。设备可使用 IMEI;后端使用固定环境前缀 + 主机标识,避免多实例互踢。
- 后端重连后必须自动恢复订阅;离线队列有上限,且发送前重新校验订单仍有效。
## 11.3 适配器与代码边界
```text
OrderService / DeviceApplicationService
↓ 只使用标准能力
DeviceGateway
├─ openDoor()
├─ closeDoor()
├─ setPower(channel, on/off)
├─ playTts()
├─ startOrderTask()
├─ extendOrderTask()
├─ cancelOrderTask()
├─ querySnapshot()
└─ pairChildLock()
JilianControlBoxAdapter
JilianSub1GLockAdapter
JilianSmartSocketAdapter
MockHardwareAdapter
MqttTransport → EMQX 101.42.38.246
```
业务服务不得直接拼 JSON 或 Topic。协议原始拼写只存在适配器中。特别注意厂商协议中以下拼写不得擅改:
- `ConctolPower`
- `Crldoor`
- `CrlLED`
- `PlayTTS`
- `CtrlDevice`
所有下发负载先经过 Zod/schema 校验;所有上行负载先保留原始 JSON,再解析为内部事件。解析失败进入死信/异常表,不能使 MQTT 消费循环崩溃。
## 11.4 控制箱能力映射
### 11.4.1 读取
| 业务能力 | 厂商命令 | 用途 |
|---|---|---|
| 基本信息 | `{"read":"basicInfo"}` | DeviceID、IMEI、ICCID、型号、固件、信号、位置、继电器和门锁配置 |
| MQTT 配置 | `{"read":"mqttConfig"}` | 仅平台超管诊断,响应须脱敏 |
| 开机语音 | `{"read":"startVoice"}` | 读取欢迎语配置 |
| 订单任务状态 | `{"read":"task"}` | 异常对账和人工诊断 |
| 订单任务参数 | `{"read":"taskconfig"}` | 校验提醒和延时关灯参数 |
设备页面默认读取数据库快照,不得每次打开列表都群发 `basicInfo`
### 11.4.2 电源与灯光
控制箱提供 `slot1``slot2` 两路 30A 和 `slot3` 一路 10A。用途由后台绑定配置决定,不能在代码中永久写死为“空调/麻将机/灯”。
```json
{
"action": "ConctolPower",
"slot1": "on",
"slot2": "on",
"slot3": "on",
"id": "1234567890123"
}
```
- 支持只传需要控制的插槽。
- `slotall` 优先级最高,普通业务尽量不用,避免误断电。
- 回包必须记录每路实际状态。
- 同一负载不得同时绑定控制箱插槽和智慧插座,除非明确配置主从关系。
### 11.4.3 磁力锁/电控锁
```json
{
"action": "Crldoor",
"order": "open",
"holdopen": 0,
"delayTime": 4,
"id": "1234567890123"
}
```
- `delayTime` 只允许 1-14 秒。
- 普通顾客开门默认 `holdopen=0`;常开仅门店管理员按场景授权。
- 有门磁时处理 `magstate` 事件;“开门命令 ACK”与“门已物理打开”是两个状态。
### 11.4.4 TTS、停止语音和 LED
- TTS`action=PlayTTS`,支持内容、音量、优先播放、次数、发音人、风格、语速和语调。
- 停止:`action=stopTTS`
- LED 倒计时:`action=CrlLED``minute` 为剩余分钟。
- 后台对自定义 TTS 进行长度、敏感词、频率和权限限制;不得允许普通用户任意广播。
### 11.4.5 设备本地订单任务
优先利用控制箱本地订单任务降低断网风险:
```json
{
"action": "task",
"minute": 120,
"type": 2,
"subID": "12345678",
"holdopen": 1,
"delayTime": 4,
"id": "1234567890123"
}
```
- `type=1`:门禁;`type=2`:智能门锁;`type=3`:门禁和智能门锁同时联动。
- 续时:`action=addtask``addminute`
- 取消:`action=canceltask`,会关闭门锁/门禁及电源。
- 回包 `busy`:设备已有任务,不能重复启动;读取剩余时间并对账。
- 回包 `unconfirm`:控制箱任务已启动,但子门锁未确认;不能当成完全成功,应单独重试门锁开门并告警 Sub-1G 通信质量。
- `taskfinish` 是硬件事件,不是支付或订单结算的唯一依据;服务端订单状态机仍为事实源。
### 11.4.6 订单联动时序
1. 支付成功只确认订单,不立即启动未来订单的硬件任务。
2. 定时 worker 在订单开始前预热并再次校验支付、取消、换房和设备绑定。
3. 到达开始时间,创建唯一 `iot_automation_job`,下发 `task`
4. 收到 ACK 后更新命令状态和设备快照;`unconfirm` 进入补偿流程。
5. 顾客订单期间“一键开门”使用单独开门命令,不重复启动 `task`
6. 续费支付成功后下发 `addtask`;若下发失败,订单延长仍由服务端保存,并持续补偿/告警。
7. 取消、退款、换房必须先更新业务事务和 outbox,再下发旧房 `canceltask` 与新房任务。
8. 任何重试前检查订单仍处于允许时间窗口,过期命令直接 `CANCELLED`,禁止延迟开门。
## 11.5 Sub-1G 智能门锁
### 11.5.1 绑定
- 后台选择父控制箱和房间,发送 `action=AddDevice`,默认窗口 60 秒。
- 现场唤醒门锁并输入 `*789#`
- 控制箱返回 `subID``subtype` 后保存父子绑定。
- `subtype=14` 为 701C 防盗门锁,`subtype=15` 为 701G 室内门锁。
- `timeout` 不能建立绑定;重复 `subID`、跨租户/跨房间冲突必须阻止。
### 11.5.2 控制
`CtrlDevice` 支持:
| `order` | 能力 |
|---|---|
| `open` / `close` | 开关门、常开和延迟关门 |
| `setkey` / `delkey` | 新增、删除或清空密码 |
| `setcard` / `delcard` | 新增、删除或清空卡片 |
| `factoryreset` | 恢复出厂并解除绑定,极高风险 |
回包重点:`ok``fail``timeout``full`,同时可能带电量 `battery``timeout` 表示子设备未通信上,不得向用户显示“开门成功”。
密码/卡片策略:
- MVP 优先远程开门,不默认创建长期门锁密码。
- 需要订单临时密码时,服务端生成、下发、到期撤销并对账;删除失败必须持续告警。
- 数据库不保存可直接读取的明文密码;必要时使用加密字段,API 和日志统一脱敏。
- 清空密码/卡片和 `factoryreset` 仅平台超管双重确认,普通门店管理员无权执行。
### 11.5.3 门锁事件
处理 `event=record`
- `type=card/key`
- `state=open/close`
- `content` 为卡号或密码,入库前必须脱敏/摘要化
- `timestamp``doorID``doorType`
事件可关联订单、人员和门店;无法关联时作为异常开门事件告警。
## 11.6 4G 智慧插座
### 11.6.1 能力识别
- `basicInfo` 返回型号、固件、信号、开机保持状态和能力。
- `workInfo` 返回开关;计量版额外返回电压、电流、功率、温度和累计电量。
- 计量/保护能力必须由 `type` 和实际回包判断,不能假定所有插座都有。
### 11.6.2 控制
```json
{"action":"on","slotNum":1,"id":"1234567890123"}
```
```json
{"action":"off","slotNum":1,"id":"1234567890123"}
```
- 本地任务 `localtask` 最大 20 条,支持一次、每天和每周循环;断网仍可执行。
- 删除任务使用 `clearTask``taskNum=0` 表示清空全部,属于危险操作。
- 计量版可设置功率、电流、温度、拔出自停和充满自停;配置前必须确认设备额定电流和真实负载。
- `resetHold=0` 表示复电保持断电,`resetHold=2` 表示恢复上次状态。默认策略按负载安全性配置,不能全局统一。
### 11.6.3 事件
- `connected`:联网。
- `Poweron`:上电,仅上报一次。
- `/devicewill/{DeviceID}`:异常断电/离线遗嘱。
- `localtask`:本地任务执行。
- `special`:本地按键、拔出/充满自停、超功率、超电流、超温等;`closeReason` 必须映射为中文告警。
## 11.7 MQTT 消息处理与幂等
### 11.7.1 上行处理
1. 校验 Topic,提取 DeviceID。
2. 限制消息大小并解析 JSON;保存原始 payload 和接收时间。
3. 校验 payload 中 DeviceID/IMEI 与 Topic/资产映射一致。
4. 先写 `iot_device_event` 或匹配 `iot_device_command`,再异步执行业务副作用。
5. 通过唯一键和 `payload_hash` 去重;重复消息只更新接收次数。
6. 解析异常进入 dead-letter 状态并告警,不中断整个订阅客户端。
### 11.7.2 命令状态
```text
PENDING → PUBLISHED → ACKED
↘ FAILED
↘ TIMEOUT → UNKNOWN/人工确认/安全重试
PENDING/PUBLISHED → CANCELLED(订单已失效)
```
- 发布成功不代表执行成功。
- 超时后对于“开门/通电”等有物理副作用的命令不能盲目无限重试;先查询状态或人工确认。
- 所有命令关联 tenant/store/room/order/user/operator/traceId。
- 记录请求、回包、耗时、重试次数和最终原因,但敏感内容脱敏。
### 11.7.3 设备在线判定
在线状态综合:MQTT 连接/事件、`last_seen_at`、遗嘱、命令回包和人工刷新。不能只因 TCP publish 成功就判断设备在线。
## 11.8 安全和故障兜底
- 顾客开门必须校验本人或有效分享令牌、订单已支付、当前时间窗口、门店/房间一致、设备绑定有效。
- 管理员临时开门必须校验门店范围、填写原因、二次确认并审计。
- 保洁开门只在任务有效时间和授权门店/房间内。
- Broker、设备或门锁离线时明确提示,不返回假成功。
- 门店必须保留机械钥匙、管理员本地开门和断电应急方案。
- 订单自动结束失败时生成 P1 告警,禁止静默遗留通电或常开。
- 弱信号、低电量、频繁 `unconfirm/timeout` 进入维护工单。
- 恢复出厂、修改 MQTT 参数、清空凭据默认关闭远程入口;确需使用时平台超管双人复核。
## 11.9 后台设备页面
桌面和手机均必须提供:
- 设备资产、扫码入库、门店/房间/插槽绑定。
- 拓扑:控制箱 → Sub-1G 门锁;房间 → 智慧插座。
- 在线状态、最近消息、信号、固件、门锁电量、继电器/插座状态。
- 命令日志、原始回包(脱敏)、事件、告警和重试。
- MQTT 健康:Broker 连接、订阅状态、最近消息时间、异常重连。
- 应急操作:开门、关门、开关电、停止 TTS;权限和二次确认按危险级别控制。
- 手机端操作后显示“已发送/设备已确认/超时/失败”,不能只弹“操作成功”。
## 11.10 联调顺序与验收矩阵
### 阶段 1Windows MQTTX、WSL/命令行与 Broker
-`101.42.38.246` 建立测试账号/ACL。
- Windows MQTTX 以 MQTT 3.1、QoS 1 连接并验证三个 Topic。
- WSL 或 Ubuntu 使用 `mosquitto-clients`/Node.js 冒烟脚本复核连接、发布、订阅和 ACL。
- 验证错误账号、越权 Topic、重复 Client ID、Broker 重启。
### 阶段 2:控制箱
- 上电、联网、`basicInfo`
- 三路控电逐路开关,确认物理负载和绑定用途。
- 磁力锁开关、门磁事件。
- TTS、停止 TTS、LED。
- `task``addtask``canceltask``busy``unconfirm``taskfinish`
### 阶段 3Sub-1G 门锁
- `AddDevice` + `*789#`,保存 `subID/subtype`
- 701C/701G 开关门、延时、常开。
- 超时、低电量、弱信号和解绑流程。
- 临时密码/卡片只在业务确认需要时测试。
### 阶段 4:智慧插座
- `basicInfo/workInfo`、开关、本地任务。
- 计量版测试电压/电流/功率/温度/电量。
- 保护参数使用安全测试负载,禁止直接用大功率正式设备试错。
- 验证 `special` 和遗嘱事件。
### 阶段 5:业务闭环
- 下单→支付→到时启动→开门/通电→续费→结束→断电/锁门→保洁。
- 取消、换房、退款、重复回调、Broker 重启、设备离线、消息重复、命令超时。
- 每项记录设备型号、DeviceID(脱敏)、固件、时间、预期、实测、证据和问题。
M06 只有完成真实硬件联调报告后才能 `DONE`;仅完成 Mock 时为 `PARTIAL`。当前已提供协议,不得再以“缺厂商协议”为由标记 `BLOCKED_EXTERNAL`;只有缺实物、设备 ID、MQTT 生产凭据或现场配线时才允许阻塞。
---
# 12. 模块开发顺序 M00-M10
> **V5.5 当前执行覆盖规则:** 本章保留 M00-M10 的完整依赖顺序,但不是本次接管起点。当前项目不得从 M00 或 M06-B 重跑;唯一近期队列是第 28 章规定的 `M06-C-R1 → M06-E-R1 → M06-R-REGRESSION → M08-C → M08-D → M09 → M10`。第 23 章的 R1 修复是对已实现 M06 的协议纠偏,不代表回退或重建整个 M06。
## 12.1 总体执行方式
模块编号保持 M00-M10,每个模块按既定 A/B/C/D/E/F/G 子阶段执行。**一个 commit/验收单元只对应一个子阶段,但同一次 Codex 会话必须在资源允许时连续完成多个相邻子阶段。**每个子阶段都要独立执行工程编码、测试、文档增量、commit、push 和远端校验;完成后自动进入固定队列中的下一项,不等待用户再次确认。
模块顺序是依赖顺序,不是建议顺序。除 `BLOCKED_EXTERNAL` 且可独立部分已全部完成外,禁止跳过前置模块或从后续页面、报表、营销功能反向开发。
模块完成条件:
- 本子阶段必须包含与验收项直接相关的工程增量;纯文档提交不能完成模块。
- 对应功能 ID 均达到验收标准。
- API、数据库迁移、前端页面、权限、日志和测试完整。
- 相关外部依赖若未提供,只能标记 `BLOCKED_EXTERNAL`,模块不能假装完成。
- 旧接口兼容或迁移清单已更新。
- 已评估部署影响;有影响时同一提交同步更新菜单式部署脚本和模板,无影响时开发日志明确写“部署影响:无”。
- `docs/deployment-status.md` 中记录的最近验证 commit 不得落后于影响部署的代码提交。
- 本模块子阶段的 commit 已成功推送到 `origin/main`,并验证本地 `HEAD``origin/main` 一致;否则不得标记 `DONE`
- 开发日志已记录 `ENGINEERING_DELTA=YES`、工程文件清单、功能结果和真实测试命令;格式化/空壳/TODO 不算完成。
## 12.2 完整依赖队列(当前游标必须服从第 28 章)
Codex 必须使用以下唯一队列计算 `next_stage`
```text
M00-A → M00-B → M00-C → M00-D → M00-E
→ M01-A → M01-B → M01-C
→ M02-A → M02-B → M02-C → M02-D
→ M03-A → M03-B → M03-C → M03-D
→ M04-A → M04-B → M04-C → M04-D
→ M05-A → M05-B → M05-C → M05-D
→ M06-A → M06-B → M06-C → M06-D → M06-E → M06-F → M06-G
→ M07-A → M07-B → M07-C → M07-D
→ M08-A → M08-B → M08-C → M08-D
→ M09-A → M09-B → M09-C → M09-D
→ M10-A → M10-B → M10-C → M10-D
→ M10 全量验收与发布候选
```
选择规则:
- 队列中最早的 `DOING/PARTIAL/BLOCKED_INTERNAL` 永远优先。
- 只有前一项达到 `DONE` 或符合外部阻塞旁路条件,下一项才可开始。
- 某个子阶段实际代码已经超前存在时,不按文件存在直接跳过;必须补齐验收、测试、状态和远端 commit 证据。
- 发现后续模块已有零散代码时,保留并纳入后续验收,不得因此改变主推进队列。
- 用户明确指定修复某个紧急缺陷时,可以临时中断队列;修复、测试、推送后必须回到原 `current_stage`,不得借机改变整体顺序。
## 12.3 模块完成、回归与推进
每个模块最后一个子阶段完成后执行:
```text
模块内全量测试
→ API/数据库/前端/权限/部署影响复核
→ 更新既有状态和唯一模块日志
→ 模块完成 commit
→ push + HEAD/origin 校验
→ 执行对应 CP 检查点(如适用)
→ 设置下一模块首阶段为 current_stage/next_stage
→ 会话资源允许时立即继续编码
```
模块级回归提交不能只有 Markdown;至少应包含补充测试、修复、测试脚本或真实生效的工程调整。若模块无需任何工程修复且全部测试已在最后子阶段完成,可在该子阶段同一提交中完成模块验收,但必须记录完整命令和结果。
## M00. 项目审计、单仓库基线与 `/opt/apps` 部署骨架
**范围:** `SYS-001``ENV-001``REF-001``SCM-001``WSL-001``IOT-DEV-001``OPS-001``OPS-004`,为后续所有模块建立固定工作区、参考审计、完整推送、服务器拉取和部署基线。
### M00-A 现有资料与代码审计
-`D:\qipai\参考` 递归解压并识别全部参考源码、静态后台、SQL、脚本、运行包和硬件协议;生成哈希、来源、用途、敏感性和 Git 状态清单。
- 审计当前 `panda/qipai` 仓库结构、已有文件、Git 历史和 `.gitignore`
- 生成 `docs/source-inventory.md`,记录技术栈、页面、接口、可复用点和风险。
- 禁止直接反编译或照搬受保护后端作为新系统主代码。
### M00-B 单一 Monorepo 与统一进度
- 唯一远端固定为 `ssh://git@git.txyundm.cn:2222/panda/qipai.git`
- 后端、后台、小程序、迁移、测试、部署脚本、V5.5、全部进度文档和经脱敏审计的 `参考/` 统一纳入 `D:\qipai` 的一个仓库。
- 创建缺失的最小固定文件 `docs/repository-map.md``docs/git-deployment.md``docs/release-manifest.md`;已存在时原地更新,禁止创建第二套版本。
- 建立单仓库 lint/test/commit 规范;真实 `.env`、密钥、构建产物、上传和备份不得提交。
- 清理任何误配置的多远端、多仓库或嵌套 `.git`,但清理前必须备份和记录,禁止直接删除未知历史。
### M00-C Windows/WSL 本地开发、已有成果核验与模块完成即推送
- `docs/current-baseline.md` 缺失时做一次全量核验;存在时只按 `audited_commit..HEAD` 增量核验。核验后必须选择并编码下一工程目标,禁止停留在文档阶段。
- Windows `D:\qipai` 是唯一正式开发、MQTTX、微信开发者工具和提交环境;WSL `/mnt/d/qipai` 做轻量检查,完整 Linux 构建在 WSL 原生临时副本完成。
- 探测已安装的 WSL EMQX,生成 `docs/local-dev-mqtt.md`,验证版本、服务、1883/18083 监听、账号用途、MQTT 3.1、QoS 1、Topic 和 ACL;健康时不得重装或清空数据。
- 生成 Windows/WSL 环境检测、启动、停止、测试、Gitea 连通性和安全推送脚本。
- 默认直接在 `main` 顺序开发;每个模块子阶段开始前 `pull --ff-only`,完成后立即 commit + SSH push。
- 验证免密 SSH、固定 remote、主机指纹、push 权限和 `HEAD == origin/main`
- 推送失败时模块不得标记 DONE。
### M00-D `/opt/apps` 和 Gitea 原生服务
- 在 Ubuntu 24.04 x86-64 创建 `/opt/apps` 固定目录、`git`/`qipai` 用户和最小权限。
- Gitea 原生安装到 `/opt/apps/gitea`,由 systemd 管理;生产创建只读部署密钥。
- 单一仓库 clone 到 `/opt/apps/qipai-backend`;后台产物发布到 `/opt/apps/qipai-admin`;小程序镜像同步到 `/opt/apps/qipai-miniapp`
- EMQX/MySQL 保持 Apt 标准系统目录,项目级导出和备份写 `/opt/apps/emqx``mysql`Redis 仅预留。
### M00-E 菜单式部署与环境监测
- 生成 `/opt/apps/setup.sh` 唯一入口和 `scripts/setup/*.sh`
- 固定 `api.txyundm.cn`,生成 Nginx、证书、微信合法域名、域名体检和失败回滚能力。
- 主菜单包含初始化、固定仓库更新部署、Gitea 管理、MQTT、HTTPS、状态、备份恢复、回滚和诊断。
- 实现启动快检、操作前预检、操作后复检、仓库状态分类、顺序构建、数据库预备份和单 commit 发布清单。
- 更新失败不得破坏当前运行版本;检测到 DIRTY/AHEAD/DIVERGED 必须阻止。
### M00 关闭与验收
M00 不是长期文档整理模块。最小基线、环境检查、仓库推送和部署入口达到下列验收后,必须将 M00 标记为 `DONE` 或明确剩余真实阻塞,并转入 M01/首个业务模块;不得继续新增盘点、总结、计划或重复状态文档。
- `D:\qipai` 是唯一 Git 根,当前总纲、`参考/`、正式源码和追踪文档均位于该根目录。
- `参考/` 已完成递归清单、脱敏、哈希和 Git 纳管,无嵌套仓库、真实秘密和未解释遗漏。
- Windows 单一仓库可完成 pull、构建、测试、commit 和免密 SSH push。
- 完成一个测试模块提交后,Gitea `origin/main` 可查到相同 commit,开发日志记录 push 时间和结果。
- Ubuntu 上 Gitea、单一生产工作区、后台发布目录、小程序镜像目录、权限和服务用户符合约定。
- `sudo bash /opt/apps/setup.sh` 可进入中文菜单,快检输出 PASS/WARN/FAIL。
- 菜单可查看固定仓库 remote/branch/commit/status,并完成一次从 Gitea 拉取后端和后台部署演练。
- 小程序同步只记录源码 commit,不声称已发布到微信。
- 更新失败能保留/恢复上一运行版本;发布清单与 PM2/Nginx 实际版本一致。
- Redis 未启用时显示 RESERVED/DISABLED 而不是 FAIL。
- WSL 的 Bash/ShellCheck/构建预演和 Windows PowerShell 环境检查通过。
- 无 Dockerfile、docker-compose、cloudfunctions、真实密钥和生产备份进入仓库。
- M00 最近一次完成提交包含实际脚本/工程配置变化和可复现验证,不是纯 Markdown;`docs/current-baseline.md` 已写入下一工程目标。
## M01. 后端平台基础、数据库和公共能力
**范围:** 所有后续模块的 API、数据、安全和任务基础。
### M01-A Fastify 基础工程
- TypeScript 严格模式、Fastify 插件分层、Kysely + mysql2、Zod/TypeBox 请求校验。
- 统一响应、错误码、traceId、Pino 日志、请求审计、速率限制和 CORS 白名单。
- 健康检查、就绪检查、版本信息和构建信息。
- 正确处理 `TRUST_PROXY=127.0.0.1`、强制 HTTPS、公开基准 URL、真实客户端 IP、CORS 白名单和统一 `X-Request-Id`
- 生产仅监听 `127.0.0.1:3001`,并为 `api.txyundm.cn``/app-api``/admin-api` 提供稳定接口。
### M01-B 数据库迁移与兼容层
- 解析旧 SQL,生成“保留/改造/废弃/新增”映射。
- 金额新字段统一用整数分;旧 DECIMAL 在 Repository 边界转换。
- 新表时间统一 `DATETIME(3)`,内部按 UTC 保存,业务日期按门店时区计算。
- 所有业务表具备 `tenant_id`、创建/更新时间、逻辑删除或明确物理删除策略。
- 每次迁移提供 up/down SQL、验证 SQL 和数据迁移说明。
### M01-C 轻量异步任务基础
- 不引入 Redis/MQ;使用 MySQL outbox、任务表和 PM2 worker 进程。
- 支持通知、订单状态推进、设备联动、退款查询、统计汇总和失败重试。
- 任务必须可幂等、可重试、可观察和人工补偿。
### M01 验收
- API 校验、错误码、traceId、审计日志和限流可验证。
- 数据库迁移在空库和旧库副本上均能执行。
- worker 重启不丢任务、不重复产生资金或设备副作用。
- MQTT QoS 1 重复消息不重复推进订单;命令关联 ID 最大 13 位且可审计。
## M02. 多小程序、多租户、登录、用户和权限
**范围:** `CFG-001``AUTH-001``TEN-001``AUTH-002``STAFF-001``BKG-002``BKG-010`
### M02-A 多小程序/租户模型
- 建立 `platform_app``tenant``tenant_app``tenant_config`
- 一个 AppID 对应一个逻辑应用,可绑定一个或多个租户;首期默认一 AppID 一租户。
- 所有唯一索引和查询必须包含 tenant_id,禁止仅靠前端传参隔离。
- 小程序品牌、Logo、主题、电话、分享配置按 AppID/tenant 加载。
### M02-B 微信登录与用户体系
- `wx.login` code 换取会话,OpenID/UnionID 按应用保存。
- JWT 访问令牌 + 可撤销会话;令牌包含 tenant、user、role version,不直接信任客户端角色。
- 手机号、头像、昵称授权采用最小权限原则。
- 用户禁用、角色变化和密码/会话重置立即生效。
### M02-C RBAC 和数据范围
- 角色:CUSTOMER、CLEANER、STAFF、STORE_ADMIN、TENANT_ADMIN、PLATFORM_ADMIN。
- 权限层次:菜单、按钮、API、门店数据范围、资源归属。
- 建立员工门店授权和保洁门店授权。
- 小程序根据后端返回的 capability/menu 构建多端统一界面。
### M02-D 用户与员工管理
- 后台用户列表、注册/登录信息、禁用、备注、角色和脱敏手机号/IP。
- 门店管理员创建员工、分配门店、禁用账号、重置登录。
- 所有权限变更记录审计日志。
### M02 验收
- 跨租户、跨门店和越权接口测试全部失败并返回明确错误。
- 同一小程序不同角色看到正确菜单。
- 多 AppID 数据逻辑隔离测试通过。
## M03. 门店、房间、装修、广告、地图、二维码和 Wi-Fi
**范围:** `ADV-001``QR-001``MAP-001``UI-001``STORE-001``ROOM-001``ROOM-002` 的配置部分、`NET-001``BKG-001``BKG-003`
### M03-A 门店与房间基础
- 门店地址、经纬度、营业状态、营业时间、时区、客服电话、Wi-Fi、通知地址。
- 房间类别、价格、工作日/节假日/通宵规则、最低时长、提前规则、禁用时段、标签、图片和押金。
- 房间状态必须区分配置禁用、维修、空闲、预订、使用中、待清洁。
### M03-B 装修和广告
- 平台级、租户级、门店级广告。
- 门店多模板装修,组件配置使用版本化 JSON Schema。
- 图片上传压缩、文件类型/大小校验、租户目录隔离。
### M03-C 地图选店和距离
- 用户定位授权、最近门店、城市/距离/营业状态筛选。
- 拒绝定位时允许手工城市和门店选择。
- 后端计算距离或返回坐标,前端不得自行信任伪造距离。
### M03-D 小程序码、NFC 和 Wi-Fi
- 门店/房间 sceneCode 生成、重新生成、失效和扫码统计。
- NFC/二维码只跳转页面,不直接授权开门。
- Wi-Fi 密码按有效订单/管理权限受控返回并脱敏审计。
### M03 验收
- 门店和房间 CRUD、排序、上下架、禁用时段和价格配置完整。
- 模板切换、广告有效期和租户隔离正确。
- 扫不同房间码进入正确下单页。
- 地图排序和手工选店均可用。
## M04. 定价、预约、订单、换房、续费、取消和分享
**范围:** `ORD-001``ORD-002``ORD-003``ORD-004``ORD-005``ORD-006`
### M04-A 定价引擎和可用性
- 统一定价服务处理普通小时价、工作日价、节假日价、通宵场、包场、最低消费、押金、优惠和套餐。
- 生成订单价格快照,后续配置变化不得修改历史订单。
- 时间段锁使用事务 + 唯一约束/锁表策略,防止并发重叠。
- 订单预占必须有过期时间,未支付自动释放。
### M04-B 订单状态机
- 明确 DRAFT、PENDING_PAYMENT、PAID/RESERVED、IN_PROGRESS、FINISHED、CANCELLED、REFUNDING、REFUNDED、CLOSED 等状态。
- 每次状态迁移记录操作人、来源、旧状态、新状态、原因和 traceId。
- 禁止前端直接提交最终金额或任意状态。
### M04-C 续费、取消、换房和管理员调整
- 续费重新校验后续时间段并按当前或锁定规则计价。
- 取消规则按门店配置,退款和权益返还交给 M05/M07。
- 换房在单事务中锁定新房、释放旧房、处理差价和设备授权。
- 管理员增减时长、转移订单、代下单和备注必须记录人工操作历史。
### M04-D 分享订单
- 分享令牌随机、短期、可撤销、权限最小化。
- 分享人可指定是否允许查看房间、开门或续费;默认只允许查看和开门。
- 分享令牌不暴露用户手机号、余额、支付信息。
### M04 验收
- 并发创建同一房间同一时段,只有一个成功。
- 续费冲突、取消边界、换房回滚和分享撤销测试通过。
- 订单全生命周期均有状态历史和审计记录。
## M05. 微信支付、余额/套餐支付、团购、直订、退款和分账
**范围:** `ORD-001` 支付部分、`GRP-001``GRP-002``GRP-003``ORD-004` 退款部分、`FRN-001` 支付部分、`BKG-005``BKG-006``BKG-007`
### M05-A 统一支付领域
- 支付单、支付尝试、回调、退款单、分账单分表保存。
- 金额统一整数分,支付回调使用幂等键和唯一索引。
- 支付配置按 platform_app/tenant/store 解析,敏感凭据只存加密引用或环境变量。
- 提供测试支付适配器,但生产环境必须显式关闭。
### M05-B 微信支付与退款
- 预支付、签名参数、回调验签、重复回调、主动查单、全额/部分退款、退款回调和对账。
- 订单状态、支付状态和资金流水在事务/补偿机制下保持一致。
- 原路退款失败进入人工处理,不得直接把订单标记已退款。
### M05-C 团购券和第三方直订
- 建立 third-party adapter:美团/点评、抖音,后续可扩展快手。
- 支持用户粘贴/扫码券码、管理员验券、人工核销和平台 API 核销。
- 美团直订/预订回调采用幂等处理,无法自动映射时进入待处理队列。
- 真实平台未授权时,完成 Mock、手工验券和接口配置页,状态标记 `BLOCKED_EXTERNAL`
### M05-D 分账和门店收款配置
- 支持按租户/门店配置商户号、分账开关、比例、接收方、授权状态。
- 分账指令只在支付确认后执行,必须幂等并可对账。
- 未获得服务商/分账权限时不得伪造成功。
### M05 验收
- 重复支付回调、重复退款回调和重复验券不会重复记账。
- 支付、退款、团购、直订和分账后台记录可完整查询。
- 所有密钥、证书和 Token 均未进入 Git、日志或前端包。
## M06. MQTT、控制箱、Sub-1G 门锁、智慧插座与一键联动
**范围:** `ROOM-002` 设备部分、`DEV-001``DEV-002``DEV-003``BKG-004``BKG-009``IOT-001``IOT-009`
### M06-A Broker 与 MQTT 基础
- 通过根目录 `setup.sh` 菜单 3,在 `101.42.38.246` 的 Ubuntu 24.04 x86-64/amd64 无桌面服务器上原生安装/更新 EMQX 5.x;服务器不安装 MQTTX,按需安装 `mosquitto-clients`;建立 systemd、自启、日志、备份、预检和复检。
- 禁用匿名,配置后端账号、设备账号/批次账号和 Topic ACL。
- 后端实现 MQTT 3.1、QoS 1、自动重连、订阅恢复、健康检查和消息大小限制。
- 生成 `docs/mqtt-deployment.md`,真实凭据只写配置位置,不写明文。
### M06-B 设备资产、能力和拓扑
- 扫码入库 DeviceID,记录 IMEI/ICCID/型号/固件/信号/能力。
- 控制箱绑定门店、房间和 slot1/2/3 用途。
- 智慧插座绑定房间和负载,防止与控制箱插槽重复控制。
- 建立控制箱父设备与 Sub-1G 门锁 `subID/subtype` 关系。
- 后台和手机端展示拓扑、状态、快照、告警和维护记录。
### M06-C 协议适配器和消息幂等
- 实现 `MqttTransport``JilianControlBoxAdapter``JilianSub1GLockAdapter``JilianSmartSocketAdapter`
- 严格保留厂商字段拼写,Zod 校验上下行 JSON。
- 命令 ID 最大 13 位,状态机完整;QoS 1 消息去重。
- 回包关联命令,事件落库;异常 payload 进入死信记录。
- Mock 与协议单元测试覆盖全部命令、回包和事件枚举。
### M06-D 控制箱和门锁
- 控制三路继电器、磁力锁、TTS、LED。
- `AddDevice` 配对,保存 701C/701G 子锁。
- `CtrlDevice` 开关门;密码/卡片能力按安全策略实现。
- 处理 `record``magstate`、低电量、`timeout/full/unconfirm`
- 危险命令仅平台超管、二次确认和审计。
### M06-E 智慧插座
- `basicInfo``workInfo``on/off``localtask/clearTask`
- 按 capability 展示计量和保护配置。
- 处理 `connected``Poweron``localtask``special` 和遗嘱。
- 限制人工刷新和轮询频率,保护物联卡流量。
### M06-F 订单自动联动
- 到时下发控制箱 `task`,续费下发 `addtask`,取消/换房下发 `canceltask`
- 顾客手动开门不重复启动订单任务。
- 使用 outbox/数据库任务保证业务事务与硬件命令可补偿。
- `busy/unconfirm/TIMEOUT` 分别处理,不伪造成功。
- 订单失效后禁止延迟重试开门/通电。
### M06-G 真实硬件联调
- 按 11.10 顺序完成 Broker、控制箱、门锁、插座和业务闭环。
- 保存 Windows MQTTX、WSL/命令行日志、照片、视频或现场确认记录。
- 更新 `docs/hardware-test-report.md``docs/hardware-vendor.md``docs/mqtt-protocol-mapping.md`
- 记录布线、负载额定值、设备型号、固件、信号和应急方案。
### M06 验收
- 架构检测显示 `x86_64/amd64/64`EMQX Apt 安装、Windows MQTTX 与 WSL/命令行自检通过;Broker 重启后端自动重连并恢复订阅;匿名和越权 Topic 被拒绝。
- 发布成功不直接显示设备成功,只有有效回包进入 `ACKED`
- 三路控制箱、门锁、智慧插座都能从后台和授权小程序操作并审计。
- 消息重复不重复推进订单,命令超时不产生危险的无限重试。
- 订单开始、续费、取消、换房和结束硬件联动通过。
- 手机后台可查看状态并执行授权的应急操作,无横向溢出。
- 真实硬件未完成时 M06 只能 `PARTIAL`;完成报告和故障场景后方可 `DONE`
## M07. 会员、余额、充值、优惠券和套餐营销
**范围:** `WAL-001``WAL-002``WAL-003``MKT-001``MKT-002``MEM-001`
### M07-A 双余额账本
- 现金余额、赠送余额分账户保存,扣款顺序固定为赠送后现金。
- 充值、赠送、消费、退款、人工调整使用不可变流水和幂等业务号。
- 支持按门店或租户配置余额共享范围。
### M07-B 充值优惠
- 充值规则、有效期、适用门店、限购、启停和赠送金额。
- 支付成功才记账;重复回调不重复充值。
### M07-C 优惠券和套餐
- 时长券、满减券、适用门店/房型/房间/时间段、星期、节假日、有效期和使用次数。
- 套餐购买、持有、冻结、核销、退回和过期。
- 权益核销必须与订单提交在同一事务或可靠补偿流程中。
### M07-D 会员管理
- 会员画像、注册/最近下单、订单数、消费额、余额、券、套餐和状态。
- 管理员赠券、调整余额、禁用和备注必须记录审计,敏感信息脱敏。
### M07 验收
- 账本可对账,任何余额变化都能追溯。
- 充值、券和套餐重复请求不会重复入账/核销。
- 订单取消时权益按规则正确返还。
## M08. 小程序多角色业务端与 Vue 后台管理端
**范围:** `AUTH-001``ADM-001`、所有 `BKG-*` 页面,以及各模块对应的前端页面。
### M08-A 顾客端
- 首页/选店、门店详情、房间、下单、支付、订单、开门、续费、取消、换房、分享、Wi-Fi、余额、优惠券、套餐和个人中心。
- 延续微信原生小程序,不为追求统一而强制改用跨端框架。
### M08-B 保洁端
- 同一小程序角色首页、任务大厅、我的任务、开始/完成、照片、驳回补做、统计和结算。
- **V5.5 范围冻结:** 现有 M08-B 工程成果保留,状态固定为 `PARTIAL_BLOCKED_EXTERNAL_SCOPE_FROZEN`。除安全漏洞、数据损坏、构建失败或 M06 协议修复造成的兼容调整外,不得继续增加看板、筛选、导出、归档、签署、负责人等衍生功能;真实现场项留待具备设备和人员条件时验收,主执行游标按第 28 章进入 M08-C。
### M08-C 管理员/员工端
- 门店概况、房态、订单、代下单、验券、会员、员工、保洁、设备、临时开门/电控和经营统计。
- 每个操作只显示有权限的门店和按钮。
### M08-D Vue 平台后台
- 登录、仪表盘、多小程序/租户、用户、门店、房间、广告/装修、订单、支付/退款、团购、会员、员工、保洁、设备、加盟、分账、统计、日志和系统配置。
- 后台静态包只作为 UI/菜单参考,重新建立可维护源码。
- 支持列表筛选、分页、导出、详情抽屉、权限按钮和错误提示。
- 生产 API 固定为 `https://api.txyundm.cn/admin-api`;本地使用 Vite proxy;生产产物域名扫描必须通过。
### M08 验收
- 顾客、保洁、管理员和平台超管四条主路径在同一小程序/后台可用。
- 前端构建无严重告警,错误提示可理解,接口越权仍由后端拒绝。
- 2GB 服务器仅部署后台静态产物,不运行 Vite 开发服务。
- 小程序体验版/正式版与后台生产构建均通过 `api.txyundm.cn` HTTPS 通信,真实设备不依赖忽略合法域名。
## M09. 保洁任务、结算、商品和库存
**范围:** `CLN-001``CLN-005`,并保留现有商品、寄存和库存能力。
### M09-A 保洁任务状态机
- 订单结束按门店规则自动创建任务。
- WAITING、CLAIMED、STARTED、SUBMITTED、COMPLETED、REJECTED、EXEMPT、SETTLED、CANCELLED。
- 抢单使用事务锁,支持管理员指派和超时回收。
### M09-B 图片、验收和驳回
- 开始/完成时间、清洁照片、管理员验收、驳回原因和补做。
- 免清洁任务不进入结算。
### M09-C 统计与结算
- 保洁员个人统计、任务明细、待结算金额和结算单。
- 结算单关联任务,不可重复结算;撤销生成反向记录。
### M09-D 商品、库存和寄存
- 保留商品点单、库存变更、寄存单、存取记录和商品订单。
- 库存变更必须有流水和业务来源,防止负库存。
### M09 验收
- 多保洁员抢同一任务只有一个成功。
- 驳回、补做、免清洁和结算统计一致。
- 商品库存与订单/存取明细可对账。
## M10. 通知、统计、加盟、多小程序高级能力、运维和最终验收
**范围:** `NTF-001``REP-001``FRN-001``BKG-008`、跨模块最终验收。
### M10-A 通知中心
- 统一事件、模板、接收人、渠道、发送记录、重试和人工补发。
- 小程序订阅消息为主,短信/企业微信/Webhook 作为可选适配器。
- 管理员提醒按门店和角色路由,顾客提醒遵循订阅授权。
### M10-B 统计报表
- 今日、近 7 日、自定义范围;全门店/指定门店。
- 收入分微信、余额、套餐、团购平台;订单数、下单人数、使用率和使用时长。
- 建立指标口径文档、日汇总任务、明细复算和导出。
### M10-C 加盟商和多小程序高级管理
- 加盟申请、跟进、租户开通、门店创建、支付配置、分账配置和授权状态。
- 平台超管管理多个小程序,数据逻辑独立,支持配置导入/复制但不能串数据。
### M10-D 安全、性能、Gitea 拉取部署、备份和上线
- 完成 `/opt/apps` 目录、Gitea 原生服务、单仓库只读拉取、Nginx/PM2/MySQL/EMQX、`api.txyundm.cn` HTTPS、限流、日志轮转和权限审计。
- 演练 DNS 不匹配、证书申请失败、证书临期、Nginx 配置错误、后端 502 和续期失败,确认脚本能阻止或回滚。
- 通过 `/opt/apps/setup.sh` 完成初始化、后端更新、后台更新、整仓发布、小程序源码同步、备份、恢复、回滚和诊断演练。
- 演练 `SYNCED/BEHIND/AHEAD/DIVERGED/DIRTY` 五类仓库状态,确认只有安全状态允许部署。
- 验证 Gitea push 不会自动上线,生产只从受保护 `main`/标签发布。
- 订单/支付/硬件/通知关键接口压测;目标以 2 核 2GB 可稳定运行和不发生资金/时间段错误为先。
- 完成全功能矩阵逐项验收,不允许只验证 MVP。
### M10 验收
- 通知可追踪、报表可复算、加盟和多应用隔离可验证。
- Gitea、MySQL、EMQX、Nginx/证书配置和上传目录备份恢复演练成功;单仓库整仓发布与回滚文档完整。
- `api.txyundm.cn` DNS、HTTPS、证书链、续期、微信合法域名、公开健康检查和真机请求均有验收证据。
- `current-release.json`、单一生产工作区及两个发布/镜像目录 commit、PM2 后端版本和 Nginx 后台静态版本一致。
- `docs/feature-status.md` 中图示必做功能全部为 `DONE`,或由用户明确书面接受的 `BLOCKED_EXTERNAL` 清单。
---
# 13. Codex 使用语句
## 13.1 默认继续开发
```text
请阅读 V5.6.md,按当前进度继续开发。
```
Codex 必须自行读取 `docs/module-status.md``docs/feature-status.md`,并以文首 V5.6 接管章与第 29 章校准旧状态;不得把“第一个历史未完成项”误当作执行游标。
## 13.2 用户明确指定模块或子阶段时
```text
请阅读 V5.6.md,优先处理 <用户明确指定的模块或子阶段>,完成后回到第 29 章当前队列。
```
没有明确指定时,不得自行套用旧版本示例中的 M06-B 或其他历史游标。
## 13.3 用户明确指定问题时
```text
请阅读 V5.6.md,先修复 <用户明确指定的问题>,验证后回到第 29 章当前队列。
```
除上述短语外,不需要用户重复粘贴架构、日志、Gitea、部署和验收要求;这些要求已经写入本文档并始终生效。
---
# 14. 每次开发伴随维护(禁止文档取代编码)
## 14.0 `docs/current-baseline.md` 与 `docs/local-dev-mqtt.md`
`docs/current-baseline.md` 最少包含:
```markdown
# 当前开发成果基线
- 核验日期:
- audited_commit
- next_engineering_target
- 本次核验:全量/增量
- 增量路径:
- 分支/HEAD/origin/main
- 工作区状态:
- 最近相关提交:
| 模块 | 已有实现证据 | 已有测试证据 | 缺失项 | 建议状态 | 是否调整状态文档 |
|---|---|---|---|---|---|
## 禁止覆盖的已有实现
## 状态文档与代码差异
## 下一可执行子阶段
```
`docs/local-dev-mqtt.md` 最少包含:
```markdown
# WSL 本地 MQTT 调试环境
- 环境:WSL(开发/测试专用)
- EMQX:已安装
- 实际版本:
- 包来源:
- 服务状态:
- systemd:启用/未启用
- MQTT TCP 地址:
- Dashboard 地址:
- 账号用途:Dashboard / MQTT 认证 / 待确认
- 密码状态:已配置(不得记录明文)
- Windows MQTTX 连通:PASS/WARN/FAIL
- MQTT 3.1PASS/WARN/FAIL
- QoS 1PASS/WARN/FAIL
- ACL 正向测试:
- ACL 越权测试:
- 最近测试日期:
- 关联 commit
- 已知问题:
```
## 14.1 `docs/module-status.md` 模板
```markdown
# 模块状态
| 模块 | 状态 | 当前子阶段 | 工程证据 | 测试证据 | 最近工程提交 | 开发日志 | 下一步 |
|---|---|---|---|---|---|---|---|
| M00 单仓库与服务器基础骨架 | TODO | - | - | - | - | - | - |
| M01 后端 API 基础工程 | TODO | - | - | - | - | - | - |
| M02 登录、租户、权限 | TODO | - | - | - | - | - | - |
| M03 门店、房间、价格、营业时间 | TODO | - | - | - | - | - | - |
| M04 预约、订单、时间段锁 | TODO | - | - | - | - | - | - |
| M05 微信支付、退款、回调 | TODO | - | - | - | - | - | - |
| M06 门锁、设备、二维码、NFC | TODO | - | - | - | - | - | - |
| M07 会员、余额、优惠券、套餐 | TODO | - | - | - | - | - | - |
| M08 Vue3 后台管理端 | TODO | - | - | - | - | - | - |
| M09 保洁、商品、库存 | TODO | - | - | - | - | - | - |
| M10 统计、通知、运维完善 | TODO | - | - | - | - | - | - |
状态枚举:TODO / DOING / PARTIAL / BLOCKED_INTERNAL / BLOCKED_EXTERNAL / DONE
纯文档 commit 不得填写“最近工程提交”,也不得据此把状态改为 DONE。
```
## 14.2 开发日志模板
```markdown
# 开发日志:YYYY-MM-DD Mxx 模块名称
## 1. 本次目标
## 2. 本次完成
- ENGINEERING_DELTAYES/NO
- 关联模块/功能 ID
- 可验证功能结果:
- 若为 NO,是否属于用户明确要求的文档专用工作:
## 3. 修改文件
### 3.1 工程文件
### 3.2 文档文件
### 3.3 工程增量防伪说明
- 是否仅格式化/注释/TODO/空壳:否
- 关键调用链:
## 4. 数据库变化
- 是否有变化:是/否
- 迁移文件:
- 回滚方式:
## 5. API 变化
- 新增:
- 修改:
- 删除:
- 兼容旧接口:
## 6. 前端变化
- 小程序:
- 后台管理端:
## 7. 部署变化
## 8. 测试结果
- 命令:
- 退出码:
- 结果:
- 未执行项及原因:
## 9. 欠缺 / 风险
## 10. 下一步
- 下一工程目标:
- 计划修改的工程文件:
## 11. Git 与 Gitea 推送信息
- 远端:ssh://git@git.txyundm.cn:2222/panda/qipai.git
- 分支:main
- commit
- push 命令:git push origin main
- push 结果:成功/失败
- push 时间:
- HEAD 与 origin/main 是否一致:
- 失败原因与重试命令:
```
## 14.3 API 变更记录模板
```markdown
# API 变更:YYYY-MM-DD Mxx
## 新增接口
| 方法 | 路径 | 鉴权 | 说明 |
|---|---|---|---|
## 修改接口
| 方法 | 路径 | 修改前 | 修改后 | 兼容性 |
|---|---|---|---|---|
## 旧接口映射
| 旧小程序接口 | 新后端处理 | 状态 |
|---|---|---|
## 测试样例
```bash
curl ...
```
```
## 14.4 数据库变更记录模板
```markdown
# 数据库变更:编号-标题
## 背景
## 影响表
## 正向 SQL
```sql
```
## 回滚 SQL
```sql
```
## 数据迁移说明
## 验证方式
## 风险
```
## 14.5 部署记录模板
```markdown
# 部署记录:YYYY-MM-DD HH:mm
## 环境
- 服务器:
- 分支:
- commit
## 部署内容
## 菜单式部署信息
- deploy/VERSION
- 执行菜单选项:
- 安装模式:业务+MQTT / 仅业务 / 仅MQTT
- release ID
- 上一 release
- 部署日志:
- 自动备份:
## 执行命令
```bash
```
## 数据库变更
## 健康检查
```bash
curl -fsS https://api.txyundm.cn/app-api/health
```
## 结果
## 回滚方案
## 问题记录
```
---
## 14.6 `docs/feature-status.md` 模板
```markdown
# 功能状态
> 只能使用 TODO / DOING / PARTIAL / BLOCKED_INTERNAL / BLOCKED_EXTERNAL / DONE。
| ID | 功能 | 模块子阶段 | 状态 | 最近提交 | 测试/验收证据 | 阻塞原因 | 下一步 |
|---|---|---|---|---|---|---|---|
| AUTH-001 | 多端统一小程序 | M02-C/M08 | TODO | - | - | - | - |
```
## 14.7 `docs/external-dependencies.md` 模板
```markdown
# 外部依赖
| 编号 | 类型 | 功能 ID | 所需资料 | 当前状态 | Mock 是否完成 | 负责人/来源 | 下一步 |
|---|---|---|---|---|---|---|---|
| EXTDEP-001 | 硬件 | DEV-001 | 门锁厂商文档、测试 SN、密钥 | 待提供 | 是/否 | 用户/厂商 | - |
| EXTDEP-002 | 微信支付 | BKG-007 | 商户号、APIv3 Key、证书 | 待提供 | 是/否 | 用户 | - |
```
外部依赖未提供时,Codex 必须先完成可完成的设计、Mock、配置、日志和测试桩,不能以“缺资料”为由跳过整个模块。
## 14.8 `docs/unresolved-issues.md` 模板
```markdown
# 未解决问题
| ID | 发现时间 | 模块/功能 | 问题 | 影响 | 临时处理 | 根因 | 下一步 | 状态 |
|---|---|---|---|---|---|---|---|---|
| ISSUE-001 | YYYY-MM-DD | M04/ORD-003 | 示例 | 高/中/低 | - | - | - | OPEN |
```
---
## 14.10 `docs/deployment-status.md` 模板
```markdown
# 部署状态
| 项目 | 当前值 |
|---|---|
| 菜单脚本版本 | - |
| Gitea 仓库 Web | https://git.txyundm.cn/panda/qipai.git |
| API 固定域名 | https://api.txyundm.cn |
| 小程序 API | https://api.txyundm.cn/app-api |
| 后台 API | https://api.txyundm.cn/admin-api |
| 上传文件基址 | https://api.txyundm.cn/uploads/ |
| HTTPS 证书到期时间 | - |
| 证书续期 dry-run | 未验证 |
| 微信合法域名 | 未验证 |
| Gitea 仓库 SSH | ssh://git@git.txyundm.cn:2222/panda/qipai.git |
| 生产拉取仓库 | ssh://git@127.0.0.1:2222/panda/qipai.git |
| 默认分支 | main |
| 最近模块 push commit | - |
| 最近 push 远端校验 | - |
| 目标系统 | Ubuntu 24.04 |
| 内核架构 | x86_64 |
| DPKG 架构 | amd64 |
| 用户空间位数 | 64 |
| EMQX 版本/架构 | - |
| 服务器 MQTTX | 不安装 |
| 命令行 MQTT 工具 | WSL MQTTX CLI 1.13.0 已安装;mosquitto-clients 可选 |
| WSL MQTTX CLI 路径 | `/usr/local/bin/mqttx` |
| Windows MQTTX 验证 | 待验证 Windows 到 WSL 的 MQTT 3.1/QoS 1/Topic/ACL 连通性 |
| WSL 本地 EMQX | 5.8.9active (running)enabled1883/8883/8083/8084/18083 已监听 |
| WSL 本地调试账号 | admin;账号用途待确认;口令已配置但不得写入仓库或复用于生产 |
| WSL 环境验证 | EMQX/端口/MQTTX CLI 已验证;认证/ACL/协议/TLS/Windows 连通性待验证 |
| 最近环境快检 | - |
| 最近部署后复检 | - |
| 最近验证 commit | - |
| 最近验证日期 | - |
| 已验证系统 | Ubuntu 24.04 / 未验证 |
| 菜单 1 首次安装 | 未验证 |
| 菜单 2 更新业务 | 未验证 |
| 菜单 3 MQTT | 未验证 |
| 菜单 4 域名与 HTTPS | 未验证 |
| 菜单 5 状态 | 未验证 |
| 菜单 6 备份 | 未验证 |
| 菜单 7 恢复 | 未验证 |
| 菜单 8 回滚 | 未验证 |
| 菜单 9 诊断 | 未验证 |
| 已知限制 | - |
```
## 14.11 `docs/deployment-changelog.md` 模板
```markdown
# 部署变更记录
## YYYY-MM-DD / deploy version
- 关联模块:
- 关联 commit
- 变化内容:
- 配置变化:
- 数据库变化:
- 兼容性:
- 已执行验证:
- 回滚方式:
- 生产环境人工步骤:
```
## 14.13 `docs/domain-https.md` 最低内容
```markdown
# API 域名与 HTTPS
## 固定地址
- Origin: https://api.txyundm.cn
- App API: https://api.txyundm.cn/app-api
- Admin API: https://api.txyundm.cn/admin-api
- Uploads: https://api.txyundm.cn/uploads/
- Admin Web: https://api.txyundm.cn/admin/
## DNS
- A/AAAA 记录:
- 当前服务器公网 IP
- 最近验证时间:
## Nginx
- 配置文件:
- 配置备份:
- nginx -t
- 路由检查:
## 证书
- 签发机构:
- SAN
- 生效/到期:
- 剩余天数:
- 自动续期:
- 最近 dry-run
## 微信合法域名
- request
- uploadFile
- downloadFile
- 真机验证:
## 回滚
- 上一个配置:
- 恢复命令:
```
## 14.14 `docs/api-domain-test-report.md` 模板
```markdown
# API 域名测试报告
| 环境 | 检查项 | 命令/步骤 | 期望 | 实际 | 结果 | 时间 |
|---|---|---|---|---|---|---|
| Windows | DNS/HTTPS | check-api-domain.ps1 | PASS | - | - | - |
| WSL | TLS/健康接口 | check-api-domain.sh | PASS | - | - | - |
| Ubuntu | Nginx/证书/公开 API | setup.sh 环境检测 | PASS | - | - | - |
| 微信开发者工具 | request/upload/download | 关闭忽略合法域名 | 成功 | - | - | - |
| 微信真机 | 登录/上传/下载/下单/开门 | 体验版 | 成功 | - | - | - |
## 生产产物扫描
- 后端:
- 后台 dist
- 小程序:
- 是否发现旧域名/HTTP/IP/localhost
## 问题与修复
```
## 14.12 `deploy/README.md` 最低内容
必须包含:Windows/WSL/Ubuntu 环境边界、Ubuntu 24.04 x86-64/amd64 架构检测、启动快检/完整预检/部署后复检、目录布局、配置文件、主菜单、首次安装、更新、EMQX Apt 安装、可选 mosquitto-clients、Windows MQTTX 与 WSL MQTT 自检、`api.txyundm.cn` 固定域名、Nginx 路由、HTTPS 证书、微信合法域名、状态、备份、恢复、回滚、诊断、日志、数据库恢复、EMQX 恢复和完全手工恢复方法。即使菜单脚本失效,管理员也必须能根据该文档恢复服务。
---
# 15. Gitea、提交与模块完成即推送规范
## 15.1 固定仓库与分支
| 项目 | 固定值 |
|---|---|
| Web | `https://git.txyundm.cn/panda/qipai.git` |
| Windows/WSL SSH | `ssh://git@git.txyundm.cn:2222/panda/qipai.git` |
| Ubuntu 同机只读 SSH | `ssh://git@127.0.0.1:2222/panda/qipai.git` |
| 默认开发/生产分支 | `main` |
| 生产自动部署 Webhook | 禁止,默认关闭 |
Windows 已完成 SSH 免密登录,Codex 不得重复生成并覆盖已有密钥。首次仅需验证:
```bash
ssh -T -p 2222 git@git.txyundm.cn
git remote set-url origin ssh://git@git.txyundm.cn:2222/panda/qipai.git
git remote -v
```
## 15.2 开始模块前
```bash
git status --short --branch
git fetch --prune origin
git checkout main
git pull --ff-only origin main
git rev-list --left-right --count main...origin/main
```
要求:
- 工作区存在与当前模块无关的修改时先记录和隔离,不能混入提交。
- `main``origin/main` 分叉时停止,不能自动 rebase、reset 或 force push。
- 开始开发前把模块子阶段状态改为 `DOING`,并在开发日志记录起始 commit。
## 15.3 提交信息
格式:
```text
<type>(Mxx[-子阶段]): 中文摘要
```
允许类型:`feat``fix``refactor``test``docs``chore``deploy`
示例:
```text
chore(M00-B): 建立单一仓库和固定Gitea远端
feat(M03-A): 完成门店与房间基础管理
feat(M06-D): 接入控制箱和门锁指令适配器
fix(M04-C): 修复续费后的时间段冲突
```
一个模块子阶段原则上使用一个完整提交;确因风险需要多个原子提交时,必须在该子阶段结束后全部推送,并在开发日志列出所有 commit。
## 15.4 每次提交前检查
必须按变更范围执行:
```bash
git status --short
# 生产域名与敏感信息检查
# 必须确认无 api.txyundm.cn、HTTP API、服务器 IP、localhost:3001、旧 API 域名
# 敏感信息搜索,命中后人工确认
grep -R "DB_PASSWORD\|JWT_SECRET\|WECHAT_SECRET\|MCH\|PRIVATE_KEY\|api_key\|password" -n . \
--exclude-dir=node_modules --exclude-dir=.git --exclude='.env.example'
# 大文件检查;单个异常大文件必须确认
git ls-files -o --exclude-standard | xargs -r du -h | sort -h | tail
# 后端
npm run lint --if-present
npm test --if-present
npm run build --if-present
# 后台(按真实路径)
cd admin
npm run lint --if-present
npm test --if-present
npm run build
cd ..
# 小程序静态检查(按项目实际脚本)
npm run lint:miniapp --if-present
# Ubuntu 菜单部署脚本
bash -n setup.sh scripts/setup/*.sh
command -v shellcheck >/dev/null && shellcheck setup.sh scripts/setup/*.sh
```
不得把未实际运行的命令写成“通过”。
## 15.5 模块完成后的固定推送流程
每完成一个模块子阶段,必须在同一轮工作中执行:
```bash
# 1. 查看并选择性暂存
git status --short
git add <本模块代码文件> <测试文件> <迁移文件> <相关文档>
# 2. 再次检查暂存内容
git diff --cached --stat
git diff --cached
# 3. 提交
git commit -m "feat(Mxx-X): 本次模块说明"
# 4. 推送固定远端
git push origin main
# 5. 校验远端已接收
git fetch origin main
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"
git log -1 --oneline origin/main
```
硬性规则:
- 不得等待多个模块后再统一推送。
- 不得使用 `git add .``git add -A` 掩盖无关文件;除非已逐项核对全部变化。
- 不得使用 `--no-verify``--force``--force-with-lease`
- push 成功且远端校验一致后,才更新 `docs/module-status.md``docs/feature-status.md``DONE`
- push 失败时,本地 commit 保留;日志记录错误、commit 和重试命令,模块保持 `PARTIAL/BLOCKED_INTERNAL`
- 每次 push 后输出给用户的结果必须包含:模块、测试结果、commit、远端分支、push 是否成功、仍欠缺事项。
## 15.6 Codex 自动处理边界
- Codex 可以在 `D:\qipai` 自动执行本地 commit 和 SSH push,因为用户已配置 Windows 免密登录并明确要求模块完成后直接推送;执行前必须通过路径、参考、秘密、大文件和仓库完整性检查。
- Codex 不得自动执行 Ubuntu 生产部署;生产拉取仍由 `/opt/apps/setup.sh` 菜单人工触发。
- Codex 不得创建新 Gitea 仓库、修改组织/仓库权限、删除远端分支、重写历史或覆盖用户 SSH 密钥。
# 16. 上线验收清单
## 16.1 基础服务
- [ ] 仓库根目录存在唯一入口 `setup.sh`,执行后显示中文数字菜单。
- [ ] `deploy/VERSION``docs/deployment-status.md` 与当前发布 commit 一致。
- [ ] 已在全新 Ubuntu 24.04 x86-64/amd64 环境通过菜单 1 完成安装。
- [ ] `uname -m``dpkg --print-architecture``getconf LONG_BIT` 分别验证为 `x86_64``amd64``64`
- [ ] `setup.sh` 启动快检、安装/更新前预检和操作后复检均生成 PASS/WARN/FAIL 报告且无敏感信息。
- [ ] Ubuntu 正式服务器保持无桌面环境,未安装 MQTTX GUI、浏览器或 Vite 开发服务。
- [ ] Windows 唯一工作区为 `D:\qipai`Git 根、当前总纲、remote、main 分支和免密 SSH 校验通过。
- [ ] `D:\qipai\参考` 已完整盘点、脱敏、哈希并纳入仓库;无嵌套 Git、真实秘密和未解释遗漏。
- [ ] 仓库完整性脚本确认所有应交付文件均 tracked,且无未解释 untracked 项目文件。
- [ ] Windows 环境检查、启动、停止、测试脚本可用;WSL Bash/ShellCheck/Linux 临时副本构建预演可用。
- [ ] 已生成 `docs/current-baseline.md`,已有模块未被重新初始化或无证据重置状态。
- [ ] `docs/current-baseline.md` 记录了 `audited_commit``next_engineering_target`;未发生重大结构变化时只做增量核验。
- [ ] 最近两个提交不存在连续纯 Markdown 提交;普通模块提交包含工程增量和测试证据。
- [ ] README 的“最近工程提交”指向包含源码/迁移/测试/脚本实质变化的 commit,而非文档 commit。
- [ ] 仓库中不存在重复的 `plan/summary/audit/status-v2/final` 类 Markdown;同一模块子阶段仅一份开发日志。
- [ ] WSL EMQX 5.8.9、active/enabled、五个监听端口和 MQTTX CLI 1.13.0 已写入 `docs/local-dev-mqtt.md`;账号用途、ACL、MQTT 3.1、QoS 1、TLS 和 Windows 连通性冒烟已完成。
- [ ] `admin/<LOCAL_SECRET_NOT_COMMITTED>` 仅用于本地临时调试,未出现在生产配置、源码、日志或其他仓库文件中,生产凭据已独立生成。
- [ ] 菜单脚本在模拟 ARM64/aarch64 检测结果时会安全停止,不会下载错误架构包。
- [ ] EMQX 已通过官方 Ubuntu Apt 源安装,`dpkg-query` 显示 amd64 架构。
- [ ] 正式服务器未安装 MQTTXWindows MQTTX 可连接生产测试账号;WSL 使用 MQTTX CLI 1.13.0 做本地自动化自检,服务器仅按需使用 `mosquitto-clients`
- [ ] 已在 `101.42.38.246` 通过菜单 3 完成 MQTT 安装或升级验证。
- [ ] 菜单 2 更新失败时自动回切验证通过,菜单 8 人工回滚验证通过。
- [ ] 菜单 6 备份和菜单 7 恢复演练通过。
- [ ] 菜单脚本重复执行不会清空数据库、上传文件、证书或生产配置。
- [ ] `101.42.38.246` EMQX 原生服务运行、自启正常,版本和配置已记录。
- [ ] MQTT 匿名关闭,后端/设备 ACL 最小权限验证通过。
- [ ] 1883 可供 4G 设备连接;18083 未对公网任意开放。
- [ ] Broker 重启后业务后端自动重连并恢复订阅。
- [ ] `/devicesend/+``/devicewill/+``/deviceaccept/{DeviceID}` 实机验证通过。
- [ ] 域名已解析到服务器。
- [ ] HTTPS 证书有效。
- [ ] Nginx `nginx -t` 通过。
- [ ] PM2 中后端服务 online。
- [ ] MySQL 只监听本机或未开放公网。
- [ ] `/app-api/health` 返回成功。
- [ ] `/admin-api/health` 返回成功。
- [ ] 后台管理端刷新页面不 404。
- [ ] 上传文件目录可写,URL 可访问。
## 16.2 小程序
- [ ] `baseUrl` 已改为正式域名 `/app-api`
- [ ] 微信公众平台配置 request/upload/download 合法域名。
- [ ] 登录成功。
- [ ] 门店列表正常。
- [ ] 房间列表正常。
- [ ] 下单流程正常。
- [ ] 支付测试正常。
- [ ] 订单详情正常。
- [ ] 开门校验正常。
## 16.3 后台
- [ ] 管理员登录成功。
- [ ] 角色权限生效。
- [ ] 门店房间可编辑。
- [ ] 订单可查询。
- [ ] 设备可绑定。
- [ ] 支付配置不暴露明文密钥。
- [ ] 操作日志记录关键动作。
## 16.4 数据与备份
- [ ] 已有数据库初始化脚本。
- [ ] 备份脚本可执行。
- [ ] 至少保留最近 7 天备份。
- [ ] 生产变更前手工备份一次。
- [ ] 回滚 SQL 已准备。
---
# 17. 开发阶段与不可删除范围
## 17.1 第一阶段:核心营业闭环
```text
顾客登录
→ 选店/选房
→ 选择时间并锁定时段
→ 创建订单
→ 微信/余额/套餐/团购支付
→ 订单生效
→ 到店开门和设备联动
→ 续费/取消/换房
→ 订单结束
→ 生成并完成保洁任务
→ 后台和小程序查看订单、资金和经营数据
```
第一阶段优先保证资金、时间段和开门权限正确,但不能破坏后续全功能扩展。
## 17.2 第二阶段:图示全部运营功能
必须继续完成:
- 多角色统一小程序和多门店权限。
- 广告、装修、地图、二维码、Wi-Fi。
- 充值、余额、优惠券、套餐和会员管理。
- 团购验券、美团直订、分账和加盟。
- 语音播报、管理员设备操作和真实硬件联调。
- 保洁抢单、驳回、统计和结算。
- 多小程序数据隔离、完整后台和全量报表。
以上不是“可选项”,只是在核心闭环之后继续实现。
## 17.3 外部依赖处理
下列能力可能依赖外部授权,但必须保留在开发范围:
- 微信支付和分账。
- 美团/点评、抖音团购和美团直订。
- 门锁、电控、灯控和云喇叭。
- 小程序码、订阅消息和地图服务。
缺少授权时状态设为 `BLOCKED_EXTERNAL`,同时完成适配器、Mock、配置、后台页面、日志、错误码和联调清单。
## 17.4 轻量化技术取舍
受 2 核 2GB 服务器限制,首期不引入 Redis、消息队列、Elasticsearch、Prometheus、Grafana 和容器平台。使用 MySQL outbox + PM2 worker 实现轻量任务,不代表业务功能可以删除。
## 17.5 旧运行包处理原则
`mazongjian-server.xjar` 仅用于观察旧接口行为和字段,不作为新系统运行依赖。旧 `.env` 只参考变量类别,真实密码、证书和厂商密钥不得进入文档或 Git。
---
# 18. 最终交付要求
Codex 每完成一个模块,最终回复必须包含:
- `ENGINEERING_DELTA=YES/NO`;普通继续开发必须为 YES。
- 本次完成摘要和可验证功能结果。
- 工程文件与文档文件分开列出。
- 数据库/API/部署是否变化。
- 自测命令、退出码和真实结果。
- 欠缺和风险。
- 增量更新的既有文档;新建文档必须说明本文档中的触发条款。
- Git commit、远端 `origin/main`、push 结果和远端 commit 校验。
- 部署影响、菜单脚本版本、受影响菜单选项和同步验证结果。
- 最近工程提交与下一工程目标;纯文档提交不得冒充工程提交。
如果某功能无法完成,必须写入 `docs/unresolved-issues.md`,不能只在聊天里说。
---
# 19. 最终固定原则
本项目最终技术路线固定为:
```text
不用 Docker。
不用微信云开发、云函数、云数据库或云托管。
不把前端开发服务器用于生产。
只使用:
Linux 或宝塔面板直接部署
Nginx
Node.js + TypeScript + Fastify
Kysely + MySQL 8.x
PM2API + Worker
Vue3 后台管理端静态部署
微信原生小程序
实际门锁/电控/灯控/云喇叭按厂商适配器接入
Windows 的 `D:\qipai` 作为唯一开发/调试/测试与提交工作区,`D:\qipai\参考` 作为同仓库只读参考基线;WSL 作为 Linux 辅助验证环境并运行已安装的本地开发 EMQX
Ubuntu Server 24.04 x86-64/amd64 无桌面版作为唯一生产环境
根目录 setup.sh 通过中文数字菜单完成环境快检、安装、更新、EMQX Apt、证书、状态、备份、恢复、回滚和诊断
正式服务器不安装 MQTTXWindows 使用 MQTTX 桌面版,服务器/WSL 仅按需使用 mosquitto-clients
```
用户以后交给 Codex 只需要:
```text
请阅读本文档,按当前进度继续开发。
```
Codex 必须从文档和仓库中自行判断当前模块、功能状态、外部依赖、待修问题和下一步,不要求用户重复粘贴长提示词。
文档是工程的约束和证据,不是工程本身。除用户明确要求文档专用更新外,每次“继续开发”都必须以可运行工程文件、测试和推送结果为核心产物。
---
# 20. V5.5 当前工程续接基线
本章是 0.5 节旧基线的 V5.5 替代版本。冲突时以本章为准。
## 20.1 当前 Git 与源码事实
| 项目 | V5.5 核对值 |
|---|---|
| 唯一工作区 | Windows `D:\qipai` |
| 固定远端 | `ssh://git@git.txyundm.cn:2222/panda/qipai.git` |
| 固定分支 | `main` |
| 快照 HEAD | `19d6734b68c18a1a79dd4f5b590768cfd4f0a0e5` |
| 快照日期 | 2026-07-07 15:10:16 +08:00 |
| 最近工程提交 | `8ffb940`M08-B 任务免清洁 |
| 当前后端 | Fastify + TypeScript;约 50 个正式源文件、39 个测试入口 |
| 当前后台 | Vue3 + Vite + TypeScript + Element Plus;保洁运营页面已较完整,但其他平台后台模块明显不足 |
| 当前小程序 | 微信原生;顾客端与保洁端主体链路已存在,管理员/员工完整端尚未闭环 |
| 当前迁移 | 已覆盖 M01-B 至 M08-B,共 29 组业务迁移编号/正向/回滚/校验文件 |
| 后端回归 | `npm test` 通过 |
| 后台构建 | `npm run build` 通过;存在大块告警,后续优化 |
用户提供的 ZIP 是分析快照,不等于 Codex 实际工作的干净工作区。快照中若因打包排除出现 tracked 文件显示删除,Codex 不得把这些删除带入正式仓库。实际开发必须在 `D:\qipai` 重新执行:
```powershell
Set-Location D:\qipai
git status --short --branch
git rev-parse HEAD
git remote -v
git fetch origin main
git rev-list --left-right --count HEAD...origin/main
```
只有实际工作区明确干净且 `HEAD == origin/main` 才能开始新子阶段。工作区有用户修改时,先识别、保护并续接,不得 reset 或覆盖。
## 20.2 V5.5 模块状态校准
| 模块 | 状态 | V5.5 证据结论 | 下一动作 |
|---|---|---|---|
| M00 | `PARTIAL/BLOCKED_EXTERNAL` | 基础脚本和本地环境入口存在;生产证书、部署实机未验收 | 不回退;M10 统一收口 |
| M01 | `DONE` | Fastify、迁移、连接池、outbox/worker 已实现并有测试 | 仅回归 |
| M02 | `DONE` | 多租户、微信登录、会话、RBAC、员工与审计已实现 | M08-C 复用 |
| M03 | `DONE` | 门店、房间、内容、地图发现、场景码/NFC/Wi-Fi 后端已实现 | 前端和真机项在 M08/M10 |
| M04 | `DONE` | 定价、预占、状态机、续费、取消、换房、代下单、分享已实现 | 加边界回归 |
| M05 | `PARTIAL/BLOCKED_EXTERNAL` | 支付、退款、团购、分账工程闭环;缺真实授权 | 旁路;M08-C/D 补 UI |
| M06 | `PARTIAL/BLOCKED_INTERNAL_PROTOCOL` | Mock 和数据库链路存在,但 PDF 原件显示适配字段不一致 | 立即执行 R1 修复 |
| M07 | `DONE` | 双余额、充值、优惠券、套餐、会员聚合已实现 | M08/M09 补运营入口 |
| M08-A | `PARTIAL/BLOCKED_EXTERNAL/SCOPE_FROZEN` | 顾客端工程链路存在;真机/支付/硬件待现场 | 不再扩写 Mock |
| M08-B | `PARTIAL/BLOCKED_EXTERNAL/SCOPE_FROZEN` | 保洁端和大量后台联调工具已存在 | 禁止继续堆功能 |
| M08-C | `TODO` | 管理员/员工端未形成完整日常营业闭环 | M06 R1 后立即开始 |
| M08-D | `TODO` | Vue 后台仅保洁域较完整 | M08-C 后开始 |
| M09 | `TODO` | 商品、库存、寄存/取出未形成当前架构闭环 | M08-D 后开始 |
| M10 | `TODO` | 通知、统计、加盟、上线、生产联调未收口 | 最后执行 |
## 20.3 当前代码不能重复建设的范围
下列实现已存在,后续只允许修复、复用或补界面,禁止重新造第二套:
- `backend/src/auth/**`:登录、JWT、会话、RBAC、员工。
- `backend/src/stores/**` 和内容路由:门店、房间、发现、受控访问、装修、广告。
- `backend/src/orders/**`:报价、预占、订单状态机、续费、取消、换房、分享。
- `backend/src/payments/**``third-party/**`:支付、退款、分账、团购适配。
- `backend/src/wallets/**`:双余额账本、充值、权益、会员聚合。
- `backend/src/devices/**`:资产、命令状态、订单联动和硬件烟测框架;需要在原适配器上校正,禁止平行再建 `devices-v2`
- `backend/src/cleaning/**`:保洁任务、协作、结算、转账、统计。
- `miniapp/pages/index|store|room|orders|profile|recharge|benefits|cleaner`:现有页面继续演进,不重新初始化小程序。
- `admin/src/components/Cleaning*.vue`:保洁管理继续作为后台一个业务域,不再让整个后台只围绕保洁扩张。
## 20.4 V5.5 开始开发的最小读取集合
Codex 开始时只读取:
1. `V5.6.md`
2. `README.md`
3. `docs/module-status.md``docs/current-baseline.md``docs/feature-status.md`
4. `docs/mqtt-protocol-mapping.md``docs/unresolved-issues.md``docs/external-dependencies.md`
5. 当前目标代码 `backend/src/devices/**` 及对应测试;
6. `git log 19d6734..HEAD` 的增量(若存在)。
禁止为了开始 V5.5 再扫描并重写全部 docs。协议 PDF 若已放入 `参考/`,只做哈希确认和必要页复核;不得每回合重新全文提取。
---
# 21. 两个参考仓库的 V5.5 增量吸收
## 21.1 来源定位与可信度
| 来源 | 固定版本 | 可用信息 | 不可据此推定 |
|---|---|---|---|
| `q127981/24h_qipaishi` | `f4cafe77...`;功能基线 `68c963a0...` | 原生小程序页面、交互、旧接口路径、2026-07 功能行为 | 当前项目已完成、服务端接口安全、数据库结构正确 |
| `q127981/qipaishi_server` | `83b1cb53...` | 版本日志、升级依赖、缺陷场景、外部分发说明 | 已获得服务端源码、可直接复制后端实现 |
| 两份 MQTT PDF | 本文 A 节 SHA-256 | 厂商 Wire JSON、Topic、字段、结果码、事件 | 实物固件一定无差异;仍需 fixture 与实机验证 |
| 当前 `qipai` | `19d6734...` 或其后继 | 当前完成状态、正式技术栈、迁移和测试 | 状态文档中的所有 DONE 必然正确;仍以代码和测试证明 |
## 21.2 参考小程序最新路由清单
参考仓库当前主包包含:
```text
doorList, productOrder, productOrderInfo, index, shop, doorSubmit,
door, doorSelect, user, join, setUserInfo, setUserName, setUserPhone,
tuangou, myBalance, getBalance, orderDetail, changeDoor, orderSubmit,
coupon, doorDetail, tencentMap, help, recharge, orderList, location,
login, booking, placeOrder, roomRenew, map, pay, searchOrder, yeepay,
yeepay/agreement, couponActive, inventory, coupon/lottery, ktv
```
管理分包包含:
```text
admin, doorManage, noticeManage, doorPosition, vipBlacklist, statics,
setVip, cleaner, setCoupon, setStore, setDoorInfo, setStoreInfo,
setCouponInfo, SetOrder, setDiscount, setDoorList, taskStatics, task,
taskDetail, taskSettle, scanQr, taskManager, deviceList,
packageManagement, editPages, roomList, setStoreSound, faceRecord,
guide, meituanreserve, pushrule, faceBlacklist, addLock, productManage,
addProduct, goodsKindManage, bleList, configGateWay, vipDetail,
ydCancel, vipConfig, vipList, configPrePay, configLockWifi,
setTemplate, payOrder, lottery, tuangou/tuangou, tuangou/yuding,
tuangou/index, tuangou/mtRoomType, tuangou/mtPackage, verifyRecord
```
库存分包包含 `inventory/pages/goods``inventory/pages/detail`。这些只用于覆盖检查;当前项目必须按自己的信息架构重做,不照搬旧路由名。
## 21.3 2026-07 团购增量
参考仓库 2026-07-01 增加或明确了:
- 美团“一品多态”,同一业务支持茶馆、棋牌室、自助 KTV;
- 美团/抖音团购券用于续费订单;
- 验券记录按门店、平台、日期分页查询;
- 管理员扣减用户指定余额;
- 团购核销基础版/高级版和按次计费;
- 代客下单页面和跨日时间选择器重做;
- 公告内容为空时不得弹空弹窗;
- 美团预订房型、房间、套餐、星期、节假日与进场规则同步。
V5.5 的实现口径:
1. 当前项目不实现参考项目的商业“按次收费平台”本身;只实现租户自己的平台授权、用量计数、限额和审计能力。任何收费策略必须由用户另行批准。
2. 团购平台至少定义 `MEITUAN``DOUYIN`;参考仓库后期已移除快手支持,因此 `KUAISHOU` 只能作为兼容历史枚举,默认禁用,不新增真实接入。
3. 验券记录必须保存平台、门店、脱敏券码、订单、金额、核销/撤销/退款状态、外部请求号、操作人、时间和原始响应摘要。
4. 团购券续费必须进入统一订单调整服务,重新校验房间、时间、券适用范围、重复核销和幂等,不得让小程序直接改订单结束时间。
5. 管理员余额扣减属于账本调整,不允许 `UPDATE balance`;必须生成不可变流水、原因、操作人、二次确认和门店数据范围。
6. 一品多态用 `business_scene`/能力枚举表达,禁止把房型 1-8 的参考数字散落在页面三元表达式中。
## 21.4 2026-04 至 2026-02 增量
| 参考能力 | 当前项目归属 | V5.5 要求 |
|---|---|---|
| 手机点歌 | M10 扩展 | 通过可插拔 KTV provider;仅保存房间/点歌机会话和二维码,不把厂商逻辑混入订单核心 |
| 抽奖活动 | M10 营销 | 活动、奖项、库存、参与资格、幂等抽奖、中奖记录、核销和风控 |
| 门店员工账号 | M08-C/M08-D | 复用 M02 RBAC;员工只能看到授权门店和能力 |
| 商品备注 | M09 | 下单项备注、订单总备注分开;限制长度并审计敏感内容 |
| 商品定时营业 | M09 | 门店时区、跨日时间窗、手工暂停优先级高于定时规则 |
| 保洁免照片 | M08-B 已冻结 | 不删除现有照片;增加门店/任务模板 `photo_required`,默认沿用当前规则 |
| 碰一碰/NFC 开门或下单 | M08-C/M10 | NFC 只解析签名场景令牌;服务端校验权限和有效期,标签不得直接含开门权限 |
| 4G 控制器 | M06 | 按本版协议修复 |
| 点歌机到时弹窗/软关机 | M10 | 独立设备能力和命令;不得直接执行任意系统命令 |
| 商品寄存/取出 | M09 | 建立寄存单、寄存明细、领取凭证、状态机、审计和通知 |
| 企微商品/退款通知 | M10 | 使用通知 outbox;失败重试、去重、人工补发 |
| 门店云喇叭下单播报 | M10/M06 | 云喇叭作为设备能力;文本模板、静默时段、门店/房间名称和失败降级 |
| 小时下单开关 | M04/M08-D | 门店设置关闭时前端隐藏且后端拒绝;套餐/包场不受错误影响 |
| 仅开门/仅开灯按钮 | M08-C | 明确分动作权限、确认和审计,不用一个“开房”按钮隐式全开 |
| 扫码续费突出商品/续费 | M08-A/M09 | 订单权限校验后展示;没有有效订单不得泄露房间信息 |
| 商品订单快捷入口 | M08-C/M09 | 管理员门店列表显示待处理数量,进入门店范围过滤列表 |
## 21.5 2025 更新日志必须转成回归场景
以下不是简单“参考功能”,而是必须写入自动化测试的历史缺陷:
| 场景 | 必须保证 |
|---|---|
| 修改未来订单 | 不得关闭或改变当前正在使用房间的电源/门锁 |
| KTV 开门 | 默认只开门禁与灯光,不开总控大功率负载;策略可配置 |
| 空调联动 | 默认关闭;只有门店显式开启且设备能力匹配时才发指令 |
| 管理员取消订单 | 订单状态、退款、权益释放和关电任务保持一致;失败进入补偿 |
| 支付成功但网络不稳定 | 回调/主动查询最终可成单,重复通知不重复记账 |
| 押金退款 | 与订单退款独立幂等,失败可查询和人工补偿 |
| 续费使用优惠券/套餐/团购券 | 重新校验适用范围并可靠冻结/核销/释放 |
| 跨日和通宵 | 时间选择、价格、节假日、提前下单和取消规则在前后端一致 |
| 套餐节假日 | 展示与服务端实际可用性一致 |
| 共用设备 | 一个房间结束/保洁关灯不得错误关闭另一个占用房间的共享设备 |
| 云喇叭 | 绑定门店但未绑定房间时仍按门店级规则播报,不误投其他门店 |
| 收入统计 | 收入排除退款,退款单独列示;现金、余额、团购和人工记账分列 |
| 房间使用时长 | 按房间统计实际占用时长,不用订单创建时间替代 |
| 小程序码重建 | 旧码立即失效,门店/房间可批量重建,操作有审计 |
| 优惠券分享 | 单次领取幂等、过期/已领/跨租户拒绝,不暴露用户信息 |
| 删除会员/套餐/券 | 默认软删除;历史订单和账本仍可追溯 |
## 21.6 V5.5 增量功能状态矩阵
| ID | 功能 | 模块 | 当前状态 | 完成门禁 |
|---|---|---|---|---|
| ADM-101 | 管理员/员工统一工作台 | M08-C | TODO | 门店范围、快捷入口、移动适配、审计 |
| ADM-102 | 代客下单 | M08-C | PARTIAL 后端 | 页面、报价、支付/记账、冲突与审计全链路 |
| ADM-103 | 仅开门/仅开灯/全部联动 | M08-C/M06 | PARTIAL 后端 | 独立能力、权限、命令 ACK、日志 |
| ADM-104 | 管理员钱包调整 | M08-C | PARTIAL 后端 | 不可变流水、二次确认、范围和测试 |
| GRP-101 | 团购授权配置 | M08-C/D | PARTIAL 后端 | H5 授权引用、状态、到期、范围、密钥不落前端 |
| GRP-102 | 验券记录 | M08-C/D | TODO UI | 平台/日期/门店分页、撤销退款链路 |
| GRP-103 | 团购券续费 | M05/M08-C | TODO | 统一续费服务、幂等、适用范围和回滚 |
| GRP-104 | 美团房型/房间/套餐同步 | M10 | TODO/BLOCKED_EXTERNAL | provider、任务队列、差异预览、失败重试 |
| STAFF-101 | 门店员工账号 | M08-C/D | PARTIAL 后端 | 邀请、启停、角色、门店授权、会话回收 |
| GOODS-101 | 商品分类/SKU/库存 | M09 | TODO | 并发扣减、回补、盘点、审计 |
| GOODS-102 | 商品订单与备注 | M09 | TODO | 状态机、房间配送、自助柜、取消退款 |
| GOODS-103 | 商品营业时间 | M09 | TODO | 跨日、门店时区、暂停优先级 |
| GOODS-104 | 商品寄存/取出 | M09 | TODO | 凭证、状态、归属、领取、通知、审计 |
| NOTIFY-101 | 企微/订阅消息通知中心 | M10 | TODO | outbox、模板、幂等、重试、人工补发 |
| AUDIO-101 | 云喇叭下单播报 | M10/M06 | TODO | 静默时段、模板、房间名、失败降级 |
| KTV-101 | 手机点歌/点歌机控制 | M10 | TODO/OPTIONAL_PROVIDER | provider 隔离、会话、权限、软关机白名单 |
| LOTTERY-101 | 抽奖活动 | M10 | TODO | 概率/库存事务、资格、幂等、核销 |
| NFC-101 | 碰一碰开门/下单 | M08-C/M10 | PARTIAL 场景码 | 签名令牌、过期、权限、重放防护 |
---
# 22. MQTT 厂商协议原件规范
本章替代 11 章中基于旧摘要的所有冲突字段。Wire JSON 的大小写和拼写必须原样保留在适配器内部,领域层只使用标准化命令。
## 22.1 公共连接与 Topic
- 设备协议要求 Broker 支持 MQTT 3.1;当前 MQTT.js 使用 `protocolVersion: 3` 保持不变。
- 厂商建议 QoS 1。QoS 1 会重复投递,后端必须继续按设备、Topic、命令 ID/载荷哈希幂等。
- MQTT 客户端认证密码长度不得大于 31 位;部署脚本建立账号时必须校验。
- 每台设备固定三类 Topic
| 方向 | Topic | 负载 |
|---|---|---|
| 设备上行 | `/devicesend/{DeviceID}` | JSON 快照、ACK、事件 |
| 遗嘱上行 | `/devicewill/{DeviceID}` | 可能是原始字符串 `close`,不得强制 JSON 解析 |
| 服务端下行 | `/deviceaccept/{DeviceID}` | JSON 读、动作、配置命令 |
- 后端服务允许发布 `/deviceaccept/+`,订阅 `/devicesend/+``/devicewill/+`
- 设备账号只能发布自己的 `devicesend/devicewill` 并订阅自己的 `deviceaccept`
- 不允许客户端订阅 `#``$SYS/#` 或其他设备 Topic。
- DeviceID 必须先在租户资产表绑定;Topic 中 DeviceID、Payload DeviceID 和资产记录三者不一致时进入安全死信并告警。
## 22.2 通用命令 ID 与时间
- `id` 为 1-13 位字符串;厂商文档说“最大 13 位”,V5.5 继续只发送数字以减少固件兼容风险。
- 不能仅以 `id` 全局唯一;唯一关联至少为 `tenant_id + device_asset_id + id`
- `timestamp` 示例主要是秒级 10 位,但部分说明文字出现 13 位;上行解析同时接受秒和毫秒,持久化统一为 UTC。
- 没有 `id` 的事件按 `device + event + timestamp + payload_hash` 幂等。
## 22.3 4G 智能门禁控电箱读取指令
| 读取 | 下行关键字段 | 上行关键字段 | 处理要求 |
|---|---|---|---|
| `basicInfo` | `{"read":"basicInfo"}` | IMEI、DeviceID、ICCID、IMSI、type、hardVer、version、singnal、SINR、csq、location、customerID、resetHold1/2/3、keyLock、slot1/2/3、holdopen、delayTime、timestamp、welcomePlay、sysvol、operall、task | `singnal` 是厂商拼写,不得擅自改 Wire 字段;领域层标准化为 signalDbm |
| `mqttConfig` | `{"read":"mqttConfig"}` | serverHost、port、username、password、clientId、publishTopic、subscribeTopic、willTopic、will、keepAlive、cleanSession、QoS、retain | 密码不得写日志、API 返回或普通快照;只记录“已配置”和指纹 |
| `startVoice` | `{"read":"startVoice"}` | enabled、content、vol、speaker、style、speed、intona | 后台仅展示经过权限控制的配置 |
| `task` | `read=task,id` | result、leftminute、id | 查询与业务订单核对,不能反向覆盖业务订单 |
| `taskconfig` | `read=taskconfig,id` | welvoice/welcomevoice、warn1voice、warn2voice、overvoice、warn1time、warn2time、delaytime、addvoice | 兼容厂商文档的拼写差异 |
设备类型至少识别:1/2/3/4 插座、10 户外插座、20 导轨控电、40 云喇叭、41 单路控电喇叭、50 门禁一体机、60 联网门锁、70 4G 智能门禁控电箱。未知 type 不拒绝入库,但能力默认最小权限并产生“待识别型号”告警。
## 22.4 4G 智能门禁控电箱动作指令
| 动作 | 必填/主要字段 | 合法范围与结果 |
|---|---|---|
| `PlayTTS` | content、vol、firstPlay、loop、speaker、style、speed、intona、id | vol 0-10firstPlay 0/1loop 正整数;speaker 0-5style 0-2speed/intona 0-10;结果 ok/paraerror |
| `stopTTS` | id | 停止全部语音;结果 ok |
| `reboot` | id | 高风险设备管理操作;结果 ok |
| `returnFactory` | password、id | 极高风险;复位 MQTT 等全部参数;仅平台管理员、二次确认、现场维护模式 |
| `setReturnKey` | old、new、id | 新口令最长 32 位;只走受控密钥引用,不落日志 |
| `ConctolPower` | slot1/slot2/slot3/slotall 至少一项、id | 值 on/off;slotall 优先级最高;保留厂商拼写 `ConctolPower` |
| `Crldoor` | order、id;可选 holdopen、delayTime | order open/closeholdopen 0/1delayTime 1-14 |
| `AddDevice` | time、id | time 为配对等待秒数;首 ACK 后还要等待带 subtype/subID 的最终包;可返回 timeout |
| `CtrlDevice` | subtype、order、subID、id;部分命令带 value | subtype 14=701C、15=701G;结果 ok/fail/timeout/full |
| `CrlLED` | minute、id | 4 位 LED 剩余分钟;0 用于清零时需测试固件行为 |
| `task` | minute、type、id;按类型带 subID/holdopen/delayTime | type 1 门禁、2 门锁、3 两者;结果 ok/busy/fail/unconfirm |
| `addtask` | addminute、id | 最小 1;结果 ok/fail;返回 leftminute |
| `canceltask` | id | 立即关闭门锁、门禁和全部电源;结果 ok |
### 22.4.1 `CtrlDevice` 精确口径
| order | value | 说明 |
|---|---|---|
| open/close/none | 可无 | 开关门或只改配置 |
| setkey | 一个或多个连续 6 位密码 | 新增密码 |
| delkey | 一个或多个连续 6 位密码,或 `all` | 删除/清空密码 |
| setcard | 一个或多个连续 8 位十六进制卡号 | 新增卡片 |
| delcard | 一个或多个连续 8 位十六进制卡号,或 `all` | 删除/清空卡片 |
| factoryreset | 无 | 门锁恢复出厂并解除绑定,极高风险 |
当前代码的 `content` 字段必须改为 Wire `value`;必须发送 `subtype`。密码和卡号在审计中只保存哈希/末尾脱敏值,原文不得进入事件 JSON、日志和导出。
## 22.5 控制箱配置命令
| setting | 字段 | 规则 |
|---|---|---|
| `mqttConfig` | serverHost、port、username、password;其他 Topic/QoS/retain 可选;id | 变更后设备自动重启;仅维护窗口使用 |
| `taskconfig` | welvoice、warn1voice、warn2voice、overvoice、warn1time、warn2time、delaytime、addvoice、id | warn1time > warn2time >= 1delaytime >= 0 |
| `startVoice` | enabled、content、vol、speaker、style、speed、intona、id | 范围同 TTS |
| `parameter` | resetHold1/2/3、keyLock、holdopen、delayTime、magPush、sysvol、operall、id | sysvol 0-10delayTime 1-14magPush 开启会增加流量 |
控制箱订单任务的固件联动顺序必须作为参考,但业务数据库仍是订单事实源:
1. 启动任务后固件倒计时、播欢迎语、开三路电、开门禁/门锁。
2. 按 taskconfig 在剩余分钟播两次提醒。
3. 结束时关门和 30A 两路,延迟关闭灯控。
4. 单独操作 slot3/slotall 可能清除固件延迟关灯逻辑;业务服务必须避免冲突。
5. 取消业务订单优先发送 `canceltask`,不能靠若干分散的 off/close 命令拼成“取消”。
## 22.6 控制箱事件
| event/Topic | 字段 | 领域处理 |
|---|---|---|
| connected | IMEI、DeviceID | 更新在线和最后通信时间 |
| Poweron | IMEI、DeviceID、ICCID、ECI、location | 更新资产;平台回复流量/天数只能用受控值 |
| devicewill | 原始 will 内容 | 标记疑似离线;结合 Broker/session 与超时确认 |
| record | type、state、content、timestamp、doorID、doorType | 门锁卡/密码开关门记录;content 立即脱敏/哈希 |
| magstate | state=open/close、timestamp | 门磁状态;与命令 ACK 分开 |
| taskfinish | IMEI、DeviceID、timestamp | 记录固件任务结束,不直接把业务订单改为完成;做对账/补偿触发 |
## 22.7 4G 智慧插座读取与状态字段
| 读取 | 下行 | 上行 |
|---|---|---|
| 基本信息 | `read=basicInfo` | IMEI、DeviceID、ICCID、IMSI、type、hardVer、version、singnal、SINR、csq、location、customerID、slotNum、timestamp、resetHold、keyLock、keyOff、energy、保护/自停参数 |
| 工作状态 | `read=workInfo,slotNum` | onOff、vol、cur、pow、temp/temperature、energy |
| 单个/全部本地任务 | `read=localtask,slotNum,taskNum` | taskNum、taskeffect、tasktype、cyctype、weekdata、actiontimetaskNum=0 时返回 20 位启用串 |
| MQTT 配置 | `read=mqttConfig` | 与控制箱相同,密码禁止外泄 |
标准化字段必须使用精确十进制字符串解析,不使用二进制浮点直接累计电量。Wire 示例使用 `pow``temp`,表格又写 `temperature`;上行兼容两者,优先原始 `temp`,冲突时进入协议告警。
## 22.8 4G 智慧插座动作与配置
| 类别 | Wire | 关键字段/规则 |
|---|---|---|
| 通电 | action=`on` | slotNum、id;结果 ok |
| 断电 | action=`off` | slotNum、id;结果 ok/update/paraerror |
| 本地任务 | action=`localtask` | slotNum、taskNum 1-20、tasktype on/off、cyctype once/daycyc/weekcyc、weekdata 7 位 0/1、actiontime 四位 HHmm、id |
| 删除任务 | action=`clearTask` | slotNum、taskNum 0-20、id0 删除全部 |
| 重启 | action=`reboot` | id |
| 清累计电量 | action=`emptyPower` | id;不可恢复,只允许高权限二次确认 |
| 恢复出厂 | action=`returnFactory` | password、id;极高风险 |
| 修改复位口令 | action=`setReturnKey` | old、new、id |
| MQTT 配置 | setting=`mqttConfig` | 与公共配置一致 |
| 保护/自停 | setting=`protectOff` | maxPower、maxCurrent、maxTemperature、pullOutStop/Power/Sec、chargeFullStop/Power/Sec、id |
| 其他参数 | setting=`parameter` | resetHold、keyLock、Wire 字段 `KeyOff`、id |
本地任务示例的 `weekdata` 从周一到周日,`1111100` 表示周一至周五;`actiontime` 必须校验 0000-2359。当前代码的 `mode/time/weekdays` 不是厂商 Wire 格式,必须删除或只保留为领域输入,再由适配器转换。
## 22.9 智慧插座事件与保护告警
| event/Topic | 字段 | 处理 |
|---|---|---|
| connected | timestamp 可选 | 在线 |
| Poweron | IMEI、DeviceID、ICCID、ECI、location、timestamp | 上电 |
| devicewill | 原始 will | 疑似离线 |
| localtask | slotNum、taskNum、state、energy、timestamp | 本地任务执行 |
| special | slotNum、state、closeReason、energy、timestamp | 手工或保护动作 |
`special.closeReason`
1. 本地按键打开;
2. 本地按键关闭;
3. 拔出自停;
4. 充满自停;
5. 超最大功率;
6. 超最大电流;
7. 超最大温度。
只有 3-7 应生成相应保护/异常告警;1-2 是操作事件。当前代码不得根据协议不存在的 `overLoad=true` 决定过载。
## 22.10 结果码与解析宽容度
适配器发送必须严格、接收必须“有边界地宽容”:
- 公共可能结果:`ok``fail``busy``unconfirm``timeout``full``paraerror``error``update`
- 未知结果码不能让 MQTT 消费进程崩溃;保存为 `UNKNOWN_VENDOR_RESULT` 并进入协议告警/死信,但不得 ACK 业务成功。
- `setting=mqttConfig` 的设备回复可能写成小写 `mqttconfig`;接收兼容,发送使用文档指定 `mqttConfig`
- `keyOff``KeyOff``temp``temperature``welvoice``welcomevoice` 均需 fixture 覆盖。
- 不合法 JSON 进入死信;遗嘱 Topic 的原始字符串不作为不合法 JSON。
- 原始 Payload 有大小上限、字段深度上限和敏感字段清洗;不得把 MQTT password、门锁密码、卡号原文写入日志。
---
# 23. M06 协议一致性返工子阶段
## 23.1 已发现的代码差异
| 当前实现 | 原件要求 | 风险 | 修复级别 |
|---|---|---|---|
| TTS 使用 volume/playCount/priority/pitch | vol/loop/firstPlay/intona | 实机忽略或 paraerror | P0 |
| TTS 范围 0-100、speaker/style 0-20、speed/pitch -500..500 | vol 0-10、speaker 0-5、style 0-2、speed/intona 0-10 | 参数非法 | P0 |
| AddDevice 使用 timeout | 使用 time | 无法进入配对 | P0 |
| CtrlDevice 无 subtype | subtype 14/15 必须传 | 固件无法识别子设备 | P0 |
| CtrlDevice 使用 content | 使用 value | 密码/卡片操作失败 | P0 |
| 插座 localtask 使用 mode/time/weekdays | tasktype/cyctype/weekdata/actiontime | 本地任务失败 | P0 |
| 插座 read 仅 basicInfo/workInfo | 还需 localtask/mqttConfig | 管理能力缺失 | P1 |
| 结果枚举缺 paraerror/error/update | 原件明确存在 | ACK 被判非法/死信 | P0 |
| 工作状态告警读 powerW/temperature/overLoad | 原件为 pow/temp 和 special.closeReason | 假告警或漏告警 | P0 |
| 所有 Topic 都先按 JSON | devicewill 可能是原始 close | 离线消息误入死信 | P0 |
| 协议映射文档写“未找到 PDF” | PDF 已提供 | 状态错误 | P1 |
| 静态检查脚本用 URL.pathname | 带空格/中文路径被编码 | 可移植性失败 | P2 |
## 23.2 M06-C-R1:控制箱与门锁适配器校正
必须修改原文件,不建第二套适配器:
- `backend/src/devices/jilian-adapters.ts`
- `backend/src/devices/iot-message-service.ts`
- `backend/src/devices/device-control-service.ts`
- 相应路由、类型和测试。
最少交付:
1. 以领域 DTO 接收友好字段,由适配器生成本章精确 Wire JSON。
2. TTS、AddDevice、CtrlDevice、结果枚举和配置读取全部修正。
3. 增加 `record/magstate/taskfinish/Poweron/connected` fixture。
4. 门锁 value 的密码/卡号格式验证、脱敏、危险命令授权和确认。
5. 接收 Wire 变体但发送单一 canonical payload。
6. 当前已有命令状态和 QoS 1 幂等测试继续通过。
退出测试至少包含:
```text
PlayTTS 精确深比较
AddDevice 使用 time 而非 timeout
CtrlDevice 必带 subtype,密码/卡片使用 value
delkey/delcard value=all 高风险门禁
task busy/unconfirm/fail 结果推进
record 内容不以明文落库
magstate/taskfinish 不误当 ACK
原始 devicewill=close 能更新离线而不进入 JSON 死信
```
## 23.3 M06-E-R1:智慧插座适配器校正
最少交付:
1. `read localtask` 单条/全部与 `read mqttConfig`
2. `localtask` 的 tasktype/cyctype/weekdata/actiontime 精确生成。
3. reboot、emptyPower、returnFactory、setReturnKey 走高风险命令服务。
4. protectOff、parameter 配置;保护值按设备 10A/16A/63A 能力校验,不能统一写死。
5. workInfo 把 onOff、vol、cur、pow、temp/temperature、energy 标准化。
6. special.closeReason 1-7 的事件和告警映射。
7. 累计电量清零、恢复出厂和清空全部任务必须二次确认、审计、非顾客权限。
退出测试至少包含:
```text
weekdata 长度/字符与 actiontime 时分校验
taskNum 0 只允许读取全部或清空全部,不用于创建
pow/temp 十进制解析
closeReason 3-7 对应正确告警,1-2 不报保护故障
paraerror/update/error 被保存并正确推进命令
emptyPower/returnFactory 权限拒绝与确认短语
```
## 23.4 M06-R-REGRESSION
协议修复提交后必须运行:
```powershell
Set-Location D:\qipai\backend
npm ci
npm test
npm run build
Set-Location D:\qipai\admin
npm ci
npm run build
Set-Location D:\qipai
node scripts/check-miniapp-m08-a.mjs
node scripts/check-admin-m08-b.mjs
```
若 MySQL/WSL 条件可用,再执行迁移往返和 fixture;没有实物时结论只能是:
```text
PROTOCOL_DOCUMENT_REVIEW=PASS
PROTOCOL_FIXTURE=PASS
MQTT_MOCK=PASS
HARDWARE_REAL=BLOCKED_EXTERNAL
```
不得输出 `HARDWARE_REAL=PASS`
## 23.5 R1 完成后的强制游标
```yaml
execution_cursor:
current_module: M08
current_stage: M08-C
stage_status: TODO
last_completed_stage: M06-E-R1
next_stage: M08-C
frozen_stages:
M08-A: PARTIAL_BLOCKED_EXTERNAL
M08-B: PARTIAL_BLOCKED_EXTERNAL_SCOPE_FROZEN
blocked_reason: "真机支付与硬件现场仍为外部阻塞,不影响 M08-C。"
```
---
# 24. M08-C 管理员/员工端完整规格
M08-C 的目标不是再做一个保洁联调看板,而是让门店日常营业主要操作在微信小程序内可完成。
## 24.1 角色与入口
| 角色 | 默认可见 |
|---|---|
| 门店员工 | 授权门店、房态、订单、代下单、有限房间控制、商品订单 |
| 店长 | 员工能力 + 门店配置、会员、优惠券/套餐、团购、设备状态、保洁验收 |
| 租户管理员 | 租户全部门店、员工授权、支付/分账状态、统计 |
| 平台管理员 | 平台租户、应用、全局配置;不默认进入门店日常操作 |
菜单由后端 capability 返回,不在前端仅按角色字符串判断。无权限入口不展示,直接请求仍必须 403。
## 24.2 M08-C 固定垂直切片
### M08-C1 工作台与门店切换
- 今日订单、使用中房间、待支付、待退款、待保洁、待处理商品订单和设备离线数。
- 多门店切换必须使用服务端授权清单;切换后清空上一门店缓存。
- 门店/租户到期只提示有权限人员,顾客端不显示内部配置。
### M08-C2 房态与设备控制
- 房间状态、当前/下一订单、剩余时间、保洁状态、设备在线。
- 明确按钮:开门、关门、开灯、关灯、开电、关电、全部联动、订单联动。
- 顾客、员工、店长的能力不同;危险关电在占用状态下需要原因和确认。
- KTV 默认策略只开门禁和灯光;棋牌/台球按门店策略联动。
- 所有操作返回命令状态,不把 PUBLISHED 显示成“设备已执行”。
### M08-C3 订单管理与代下单
- 门店/房间/时间选择、服务端报价、冲突校验、用户搜索或散客、支付方式。
- 支持微信、余额、套餐、优惠券、团购、人工记账的合法组合;金额由服务端计算。
- 跨日选择显式显示日期;未来订单与当前设备动作隔离。
- 订单修改、续费、换房、取消、提前结束和手动退款均有权限、原因、预览和审计。
### M08-C4 会员与余额
- 会员搜索、详情、订单/充值/权益/账本。
- 余额增加/扣减通过账本调整,现金余额与赠送余额分开。
- 调整前显示调整后余额;负余额默认禁止;大额需要更高权限。
- 会员禁用、备注和删除只做业务状态/软删除,不破坏历史。
### M08-C5 员工与保洁
- 员工邀请、角色、门店范围、启停、会话重置。
- 保洁入口复用 M08-B 已有 API;不再新增联调归档类字段。
- 门店可配置保洁是否必须上传照片;变更只影响新任务或明确选择的任务。
### M08-C6 团购与验券
- 美团/抖音授权状态、到期/额度、脱敏门店绑定。
- 扫码/粘贴验券、券详情预览、核销确认、验券记录、撤销/退款状态。
- 团购券下单和续费都走统一服务;券码只保存哈希和脱敏值。
- 美团预订退款审核属于独立能力,不授权的员工不可见。
### M08-C7 商品订单快捷处理
- 门店列表/工作台显示待接单、制作/配送中和异常商品订单数量。
- 可接单、取消、标记配送、完成;库存与退款由 M09 正式领域服务处理。
- M09 未完成前只能接 Mock/只读接口,不得临时把库存逻辑写在小程序。
## 24.3 M08-C 完成门禁
- 至少覆盖手机窄屏,不横向滚动核心表单。
- 关键接口有 RBAC、门店范围和跨租户拒绝测试。
- 代下单、管理员调整、设备控制、退款均有审计。
- 无真实微信/团购/硬件时 fixture/Mock 达标后标记 `PARTIAL_BLOCKED_EXTERNAL` 并切 M08-D,不得继续堆联调 UI。
- M08-C 最多按 C1-C7 七个垂直切片提交;每个切片最多一个工程提交加一个必要的回填提交,禁止再拆几十个微提交。
---
# 25. M08-D Vue 平台后台完整规格
## 25.1 信息架构
```text
总览
租户与小程序
门店与房间
订单与退款
会员与资产
营销(充值/优惠券/套餐/抽奖)
团购与预订
商品与库存
保洁与结算
设备与 MQTT
支付与分账
通知中心
统计报表
员工与权限
系统配置
审计与运维
```
现有保洁组件必须纳入上述“保洁与结算”,不能继续让 `App.vue` 把所有业务硬编码在单页。M08-D 首个提交应建立路由、布局、权限菜单、按域懒加载和统一列表/详情模式,再迁移现有组件。
## 25.2 后台工程约束
- 使用 Vue Router;路由按业务域懒加载。
- API 客户端统一处理 token、tenant/app 上下文、401/403、traceId 和错误码。
- 大列表必须服务端分页;导出走异步任务或有上限的流式下载。
- Element Plus 组件按需/分块,解决当前约 923 KiB element chunk 警告;M08-D 结束时单个业务块目标小于 500 KiB,确实无法达到需记录理由。
- 桌面、平板、手机三档验收;手机核心操作改卡片/抽屉,不缩小桌面表格硬塞。
- 统一金额组件以分存储、元显示;统一日期按门店时区显示并保留 UTC 原值。
- 破坏性操作二次确认,显示对象、影响和回滚;不能只弹“确定吗”。
## 25.3 M08-D 分阶段
| 子阶段 | 核心交付 |
|---|---|
| M08-D1 | 路由、布局、权限菜单、登录与上下文 |
| M08-D2 | 租户/小程序/员工/权限 |
| M08-D3 | 门店/房间/装修/广告/场景码/Wi-Fi |
| M08-D4 | 订单/支付/退款/会员/权益 |
| M08-D5 | 团购/分账/设备/MQTT |
| M08-D6 | 迁移现有保洁域、响应式与性能回归 |
M08-D6 完成后进入 M09;商品域页面可先有导航占位,但占位不计完成,正式实现只能在 M09。
---
# 26. M09 商品、库存、寄存与保洁业务收口
## 26.1 商品与 SKU
最小数据模型:
- `product_categories`:租户/门店、名称、排序、状态。
- `products`:名称、简介、图片、是否配送、是否寄存、状态。
- `product_skus`:规格、售价、成本可选、条码、库存策略。
- `inventory_stocks`:可用、锁定、损耗、版本号。
- `inventory_ledger`:入库、销售、取消回补、盘点、损耗、人工调整的不可变流水。
- `product_business_hours`:星期、开始/结束、跨日、手工暂停。
库存扣减必须使用事务和条件更新/锁,不能读后写。订单取消、支付失败超时和退款的回补规则明确且幂等。
## 26.2 商品订单状态机
```text
PENDING_PAYMENT
→ PAID
→ ACCEPTED
→ DELIVERING / READY_FOR_SELF_SERVICE
→ COMPLETED
PENDING_PAYMENT/PAID/ACCEPTED
→ CANCELLED
PAID/ACCEPTED/COMPLETED
→ REFUNDING
→ REFUNDED / REFUND_FAILED
```
- 支付订单与商品订单分离;回调只推进合法状态。
- 商品项备注和订单备注限制长度,后台显示前做转义。
- 房间配送必须验证当前有效房间订单;自助柜购买可不绑定房间。
- 门店关闭在线点单时后端拒绝新单,页面提示去自助柜;已支付订单仍可处理。
## 26.3 寄存/取出
```text
STORED → PARTIALLY_RETRIEVED → RETRIEVED
STORED/PARTIALLY_RETRIEVED → EXPIRED / CANCELLED
```
要求:
- 寄存来源关联已完成商品订单或管理员受控创建。
- 保存商品、SKU、数量、寄存人、门店、到期时间、领取凭证哈希。
- 顾客本人或授权员工取出;分次取出需数量流水。
- 领取码一次性/可轮换,页面不展示原始长期密钥。
- 每次寄存、取出、取消、过期都写审计并进入通知 outbox。
- 跨租户、跨门店、重复领取、超数量领取必须自动化测试。
## 26.4 保洁 M09 收口
M08-B 已完成大量工程,M09 只做领域收口:
- 保洁模板和 `photo_required` 配置;
- 订单/房间免清洁规则与任务生成一致;
- 当前房间已有新订单时,保洁完成不得错误关电;
- 结算、微信转账和统计的业务口径回归;
- 删除重复联调 UI,保持既有历史数据兼容。
---
# 27. M10 通知、统计、扩展与上线
## 27.1 通知中心
统一事件至少包括:
- 订单创建/支付/开始/即将结束/结束/取消;
- 退款成功/失败;
- 商品新单/取消/配送/完成;
- 商品寄存/取出/到期;
- 保洁新任务/超时/驳回/验收/结算;
- 设备离线/低电/保护断电/命令失败;
- 门店/租户即将到期。
通知渠道:微信订阅消息、企业微信、云喇叭、后台站内消息。每个渠道都是适配器:
- 事件先写 outbox
- `event_id + channel + recipient/template` 幂等;
- 指数退避、最大重试、人工补发;
- 静默时段、门店/房间模板;
- 不在通知中泄露完整手机号、券码、支付凭据或门锁密码。
## 27.2 统计口径
必须给出可追溯定义:
| 指标 | 口径 |
|---|---|
| 营业收入 | 成功收入减已成功退款;退款单独列示 |
| 订单金额 | 服务端订单快照金额,不取前端 |
| 房间使用时长 | 实际开始至实际结束,按门店时区归属 |
| 入住率/利用率 | 占用分钟 / 可营业分钟,剔除禁用时段 |
| 商品收入 | 已支付商品订单减退款 |
| 保洁成本 | 已确认结算,不把待结算计入已支付 |
| 支付渠道 | 微信、余额现金、赠送余额、套餐、优惠券、团购、人工记账分列 |
导出与页面使用同一查询服务,避免口径不一致。
## 27.3 抽奖
- 活动时间、门店/用户资格、每日/总次数、奖项、概率或保底规则。
- 奖项库存扣减与中奖记录同事务;clientRequestId 幂等。
- 现金类/高价值奖品默认不实现,需用户明确批准合规和风控。
- 优惠券/套餐奖品复用 M07 发放,不另建第二套权益。
## 27.4 KTV 与点歌机扩展
- `KtvProvider` 只暴露会话、二维码、弹窗提醒、软关机等受控动作。
- 软关机只能发送厂商白名单命令,禁止任意 shell/URL。
- 房间类型不是 KTV 时不展示点歌入口。
- 到期提醒、订单延长和取消与 M04 订单事件一致。
- 没有厂商接口/设备时标记 `BLOCKED_EXTERNAL`,不阻塞上线棋牌核心。
## 27.5 上线收口
在原 16 章清单基础上,新增:
- V5.5 协议 fixture 全通过且真实硬件状态明确;
- M08-B 不再是执行游标;
- 管理员/员工小程序和 Vue 后台可完成核心营业;
- 商品库存事务与寄存凭证安全通过;
- 通知 outbox 无无限重试;
- 大块、慢查询、日志敏感字段和备份恢复通过;
- `api.txyundm.cn` 证书 SAN、Nginx、健康接口和微信合法域名实测;
- 生产环境禁止 Mock 支付、Mock 团购、Mock MQTT 和默认恢复口令;
- 生产密钥权限、指纹、轮换日期可核对但正文不入日志。
---
# 28. V5.5 历史固定推进队列(已由 V5.6 覆盖,禁止作为当前游标)
> 本章用于解释项目如何从 `19d6734` 演进,不是当前待办。M08-C/M08-D 功能切片已在 `b35af90` 前完成,协议返工却被跳过。Codex 不得从本章重新执行 M08-C;当前只能执行第 29 章队列。
V5.5 从当前快照开始的唯一队列:
```text
M06-C-R1
→ M06-E-R1
→ M06-R-REGRESSION
→ M08-C1
→ M08-C2
→ M08-C3
→ M08-C4
→ M08-C5
→ M08-C6
→ M08-C7
→ M08-D1
→ M08-D2
→ M08-D3
→ M08-D4
→ M08-D5
→ M08-D6
→ M09 商品/库存/商品订单/寄存/保洁收口
→ M10 通知/统计/扩展/部署/真实联调/上线
```
外部条件具备时可在相邻提交插入真实联调,但不得打乱主队列:
- 微信支付/分账真实验收;
- 美团/抖音真实授权;
- MQTT 生产账号/ACL
- 控制箱、门锁、插座实物;
- 微信合法域名和小程序码。
插入联调完成后立即回到原执行游标,不得借机重新规划全部模块。
## 28.1 V5.5 历史机器回执格式
```text
DOCUMENT_VERSION=V5.6
BASELINE_HEAD=<开始时HEAD>
ENGINEERING_DELTA=YES
CURRENT_STAGE=<Mxx-X>
STAGE_RESULT=DONE|PARTIAL_BLOCKED_EXTERNAL|BLOCKED_INTERNAL
TEST_RESULT=PASS|FAIL
COMMIT=<sha>
PUSH_VERIFIED=true|false
NEXT_STAGE=<Mxx-X>
SCOPE_FROZEN=true|false
```
`ENGINEERING_DELTA=NO`,除用户明确要求文档更新外必须停止声称开发完成。
## 28.2 历史入口的 V5.6 兼容写法
```text
请阅读 V5.6.md,按当前进度继续开发。
```
Codex 读取后必须自行:
1. 核验实际 HEAD 是否为 `19d6734` 或其后继;
2. 保护现有修改;
3. 恢复/跳过已证明完成的 V5.5 接管阶段;
4. 直接进行当前工程编码和测试;
5. 提交、推送、校验;
6. 自动进入下一阶段。
不得要求用户再次粘贴协议、模块列表、提示词或“继续”才能进入当前阶段。
---
# 29. V5.6 最终固定队列与 Codex 接管确认
本章是全文最后且唯一可执行的当前游标。它覆盖所有历史“当前进度”、旧队列和旧入口。
```text
M09-A-RECOVERY
→ M06-C-R1
→ M06-E-R1
→ M06-R-REGRESSION
→ M08-D-R1
→ M09-B
→ M09-C
→ M09-D1
→ M09-D2
→ M09-D3
→ M09-D4
→ M09-REGRESSION
→ M10-A
→ M10-B
→ M10-C
→ M10-D
→ M10-RC1
```
其中四个 `check-*.mjs` 的路径修复并入 `M06-R-REGRESSION`;真实微信、团购、MQTT、硬件、合法域名和生产 Ubuntu 条件未提供时记为 `BLOCKED_EXTERNAL`,不得阻断内部主队列。每个阶段都必须有工程变化、相关测试、显式路径提交、push 和远端 HEAD 核验;完成后自动进入下一项,不等待用户发送“继续”。
当前机器游标:
```text
DOCUMENT_VERSION=V5.6
EVIDENCE_HEAD=1f1071501f4954ad0440d79c2a38e7210393e928
WORKTREE_PROTECTED=true
CURRENT_STAGE=M09-D1
STAGE_RESULT=DOING
NEXT_STAGE=M09-D2
TARGET=CODE_COMPLETE_THEN_RELEASE_CANDIDATE_THEN_PRODUCTION_ACCEPTED
```
交给 Codex 的唯一入口:
```text
请阅读 V5.6.md,按当前进度继续开发。
```
读取后直接保护工作区、完成 M09-A 的真实 MySQL 并发/事务测试并提交,再沿上述队列持续编码、测试、提交、推送和验证,直至项目达到对应完成层级。禁止先生成替代计划文档,禁止用 docs-only 回填代替工程开发,禁止把外部阻塞扩大为内部停工理由。
---
# 30. V5.7 最终固定游标与接管确认
本章位于全文最后,是与文首 V5.7 控制层配套的最终执行控制器。它覆盖第 28、29 章以及所有 V5.6/V5.5/V5.4 历史入口、旧游标和旧提交语言示例;中间章节的功能、架构、协议、部署、安全、测试和验收内容继续有效。
## 30.1 当前证据与动态恢复
```yaml
execution_cursor_v57:
document_version: V5.7
audited_snapshot_sha256: cc802fecbea399698e0a9c1b38127bc12ff260d8f4d6a084828108307f557d6f
evidence_head: 2de796143ff418d681fdb972040b8555a8e058d1
evidence_origin_main: 2de796143ff418d681fdb972040b8555a8e058d1
base_branch: main
remote: ssh://git@git.txyundm.cn:2222/panda/qipai.git
current_stage: M09-D2
current_stage_status: TODO_AFTER_M09_D1_WORKTREE_CLOSURE
last_completed_functional_stage: M09-D1
last_engineering_commit: 1f1071501f4954ad0440d79c2a38e7210393e928
last_tooling_commit: 2de796143ff418d681fdb972040b8555a8e058d1
m09_d1_business_commit: PENDING_CURRENT_CHINESE_ENGINEERING_COMMIT
m09_d1_static_gate: PASS
m09_d1_backend_build_and_full_test: PASS
m09_d1_admin_build_and_test: PASS
m09_d1_mysql_84_roundtrip: PASS_179_UP_141_VERIFY_171_DOWN
m09_d1_interrupted_up_down_retry: PASS
m09_d1_cleanup_probe: PASS
default_next_stage: M09-D3
actual_head_policy: REAL_REPOSITORY_EVIDENCE_WINS
worktree_protected: true
unresolved_deletions_policy: EXCLUDE_FROM_STAGE_COMMIT_UNTIL_USER_CONFIRMS
historical_english_commit_policy: PRESERVE_WITHOUT_FORCE_PUSH
historical_english_commits:
- 1f1071501f4954ad0440d79c2a38e7210393e928
- fd6be4f9b98bb9213e95973069c402915a8ba6f2
git_subject_language: ZH_CN_REQUIRED
git_cn_gate_present_in_snapshot: true
docs_only_cannot_advance: true
auto_continue_until_code_complete: true
```
真实 `D:\qipai` 若有 `2de7961` 之后的提交,Codex 必须以 `git merge-base`、diff、迁移、代码、测试、提交和远端 SHA 判断进度,自动跳到第一个未完成阶段。当前 M09-D1 的推进依据是商品/库存工程、迁移、测试与真实 MySQL 共同证据,不是 docs-only 状态变更;若伴随业务提交尚未完成,先保护并提交当前工作树,再继续 M09-D2。
## 30.2 最终唯一队列
```text
M09-D2
→ M09-D3
→ M09-D4
→ M09-REGRESSION
→ M10-A
→ M10-B
→ M10-C
→ M10-D
→ M10-RC1
→ CODE_COMPLETE
→ BLOCKED_EXTERNAL 条件逐项解除与真实联调
→ PRODUCTION_ACCEPTED
```
Git 中文门禁和 M09-D1 已有真实工程与测试证据,当前伴随业务提交必须完成中文 commit、push 和远端校验后继续 M09-D2。不能依据文档中的 `DONE` 单独跳过其他阶段;已完成阶段若出现回归,只做针对性修复,不得重启整个模块。
M09-D1 的唯一允许推进条件:
```text
M09_D1_WORKTREE_PROTECTED=PASS
M09_D1_STATIC_GATE=PASS
BACKEND_BUILD=PASS
BACKEND_FULL_TEST=PASS
MYSQL_84_ROUNDTRIP=PASS
ADMIN_BUILD_AND_TEST=PASS
REGRESSION_GATES=PASS
COMMIT_SUBJECT_CN=PASS
PUSH_VERIFIED=PASS
REMOTE_COMMIT_SUBJECT_CN=PASS
```
任一项未通过时保持 `CURRENT_STAGE=M09-D1`,修复后重跑;不能先更新为 DONE 或进入 M09-D2。
## 30.3 每次提交和推送的最终语言门禁
允许的标题结构:
```text
feat(Mxx-X): 中文工程结果
fix(Mxx-X): 中文缺陷修复结果
test(Mxx-X): 中文测试覆盖结果
refactor(Mxx-X): 中文重构结果
build(Mxx-X): 中文构建结果
chore(V5.7): 中文工程工具结果
docs(spec): 中文文档升级结果
```
最终要求:
1. 冒号后的摘要必须包含中文,不能是全英文句子。
2. commit 正文、推送说明、阶段回执、PR/MR、Release 和最终回复必须使用中文。
3. push 前检查本地 subjectpush 后检查 `HEAD == origin/main` 和远端 subject 含中文。
4. 若本地未推送提交误用英文,确认安全后 amend 为中文;若已经进入共享 `main`,保留历史并从下一提交纠正,禁止擅自强推。
5. `1f10715``fd6be4f` 的英文标题是历史证据,不是 V5.7 之后继续使用英文的先例。
## 30.4 第一轮预期结果
若真实仓库当前仍为 `fd6be4f + M09-D1 未提交工作区`,Codex 第一轮必须先得到以下恢复结果:
```text
DOCUMENT_VERSION=V5.7
BASELINE_HEAD=fd6be4f9b98bb9213e95973069c402915a8ba6f2
REAL_HEAD_VERIFIED=PASS
WORKTREE_PROTECTED=PASS
UNRESOLVED_DELETIONS_EXCLUDED=PASS
GIT_CN_GATE=PASS
GIT_CN_GATE_COMMIT_SUBJECT=chore(V5.7): 增加中文提交信息校验门禁
GIT_CN_GATE_PUSH_VERIFIED=PASS
CURRENT_BUSINESS_STAGE=M09-D1
M09_D1_IN_PROGRESS_FILES_REUSED=PASS
ASSERT_PRODUCT_INVENTORY_FOUNDATION=IMPLEMENTED_AND_CALLED
M09_D1_STATIC_GATE=PASS
BACKEND_BUILD=PASS
BACKEND_FULL_TEST=PASS
MYSQL_84_ROUNDTRIP=PASS
ADMIN_BUILD_AND_TEST=PASS
ENGINEERING_DELTA=YES
BUSINESS_COMMIT_SUBJECT=feat(M09-D1): 完成商品目录与库存流水底座
COMMIT_SUBJECT_CN=PASS
REMOTE_COMMIT_SUBJECT_CN=PASS
NEXT_STAGE=M09-D2
USER_FACING_LANGUAGE=ZH_CN
```
若某项尚未通过,回执必须如实写 `FAIL/NOT_RUN`,游标保持 M09-D1,不能为了匹配模板而伪造 PASS。完成 M09-D1 后必须继续 M09-D2,不等待用户再次发送“继续”。只有真实内部阻塞、权限边界、工作区分叉、测试失败尚未修复或资源不足以安全完成下一个“编码-测试-提交-推送-校验”闭环时才允许停下,并必须用中文写清恢复点。
## 30.5 交给 Codex 的唯一入口
```text
请阅读 V5.7.md,按当前进度继续开发。
```
Codex 读取后直接核验真实仓库,保护 M09-D1 已完成工作树与未决删除,完成 `feat(M09-D1): 完成商品目录与库存流水底座` 中文 commit、push 和远端校验,然后从 M09-D2 建立商品订单与库存占用生命周期并沿唯一队列持续推进。禁止先生成替代计划文档,禁止只改 Markdown,禁止删除已完成工程重做,禁止把未决删除混入业务提交,禁止把英文摘要推送到远端,禁止把外部阻塞扩大为内部停工理由。