Files
qipai/V5.4.md
T
2026-06-18 15:08:30 +08:00

5351 lines
228 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.4)
> 文档版本:V5.4(持续开发自动推进 + 固定模块队列 + MySQL 双环境凭据固化 + 配置检测增强版)
> 更新日期:2026-06-18
> 目标:用这一份 Markdown 作为项目唯一开发总纲,让 Codex 自动识别当前进度,按功能编号和模块子阶段连续开发;最终覆盖顾客端、管理员端、保洁端、平台后台、支付、团购、通知、统计、加盟、多小程序隔离,以及已经选定的 4G 智能门禁控电箱、Sub-1G 智能门锁、4G 智慧插座和 Linux MQTT 服务器的真实联动。后台 Web 必须同时适配桌面、平板和手机浏览器;项目必须同步维护 Windows/WSL 开发测试脚本,以及适用于 Ubuntu Server 24.04 x86-64Ubuntu 包架构名为 amd64、无桌面版)的菜单式部署、环境监测与运维脚本;小程序和后台统一通过 `https://api.txyundm.cn` 与后端通信。
---
## 文档命名与版本规则
- 当前文件固定命名为 `V5.4.md`,后续仅使用版本号命名,例如 `V5.4.md``V5.5.md`
- 禁止再生成带完整项目名称的超长文档文件名。
- 每次升级文档时保留上一版本只读备份,不覆盖历史版本。
- 当前权威文档固定放在单一 `qipai` 仓库根目录;Codex 必须以根目录版本号最大的文档为当前开发总纲。
- 文档版本号只代表开发总纲版本,不等同于小程序版本、后端版本或数据库迁移版本。
## 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.4.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.4.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.4.md`,Codex 只需收到“请阅读 V5.4.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.4.md,按当前进度继续开发。
```
除非用户明确指定模块,否则不再附加长提示词。本文档中的全部约束、功能清单、开发顺序、验收标准和记录规则,均视为 Codex 的内置执行指令。
### 0.0 工程编码优先与反文档循环硬门禁
本节优先级高于本文档其他所有“维护文档”要求。文档用于约束和记录工程,不得取代工程。
#### 0.0.1 默认任务解释
当用户只说:
```text
请阅读 V5.4.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.4.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.4.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.4.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.4.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)
#### 0.5.1 已有代码进度
- 用户已确认项目已经开发部分模块,但当前对话未提供每个模块的准确完成清单。
- Codex 不得把全部模块重置为 `TODO`,也不得根据目录存在就推断 `DONE`
- 第一次读取 V5.4 后,必须执行一次只读基线核验并生成 `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.4 和 `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)'
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.4.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 凭据、配置位置、真实登录检测和特殊字符安全处理。
更早历史只保留在对应版本文档和 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.4.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.4.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.4 规则登记在 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.4 约定创建 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.4.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.4.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
## 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 固定推进队列
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.4、全部进度文档和经脱敏审计的 `参考/` 统一纳入 `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 保洁端
- 同一小程序角色首页、任务大厅、我的任务、开始/完成、照片、驳回补做、统计和结算。
### 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
请阅读本文档,按当前进度继续开发。
```
Codex 必须自行读取 `docs/module-status.md``docs/feature-status.md`,选择第一个可执行的未完成子阶段。
## 13.2 指定模块或子阶段
```text
请阅读 V5.4.md,从 M06-B 继续开发。
```
## 13.3 指定问题
```text
请阅读 V5.4.md,先修复 ISSUE-012,再继续当前模块。
```
除上述短语外,不需要用户重复粘贴架构、日志、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 必须从文档和仓库中自行判断当前模块、功能状态、外部依赖、待修问题和下一步,不要求用户重复粘贴长提示词。
文档是工程的约束和证据,不是工程本身。除用户明确要求文档专用更新外,每次“继续开发”都必须以可运行工程文件、测试和推送结果为核心产物。