Files

4627 lines
178 KiB
Markdown
Raw Permalink 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.
# 自助棋牌室系统开发总纲(V4.8)
> 文档版本:V4.8(固定 Windows 工作区 + 参考目录纳管 + 单仓库完整推送 + WSL 辅助验证增强版)
> 更新日期:2026-06-15
> 目标:用这一份 Markdown 作为项目唯一开发总纲,让 Codex 自动识别当前进度,按功能编号和模块子阶段连续开发;最终覆盖顾客端、管理员端、保洁端、平台后台、支付、团购、通知、统计、加盟、多小程序隔离,以及已经选定的 4G 智能门禁控电箱、Sub-1G 智能门锁、4G 智慧插座和 Linux MQTT 服务器的真实联动。后台 Web 必须同时适配桌面、平板和手机浏览器;项目必须同步维护 Windows/WSL 开发测试脚本,以及适用于 Ubuntu Server 24.04 x86-64Ubuntu 包架构名为 amd64、无桌面版)的菜单式部署、环境监测与运维脚本;小程序和后台统一通过 `https://api.txyundm.cn` 与后端通信。
---
## 文档命名与版本规则
- 当前文件固定命名为 `V4.8.md`,后续仅使用版本号命名,例如 `V4.8.md``V5.0.md`
- 禁止再生成带完整项目名称的超长文档文件名。
- 每次升级文档时保留上一版本只读备份,不覆盖历史版本。
- 当前权威文档固定放在单一 `qipai` 仓库根目录;Codex 必须以根目录版本号最大的文档为当前开发总纲。
- 文档版本号只代表开发总纲版本,不等同于小程序版本、后端版本或数据库迁移版本。
## V4.8 本次变更
- Windows 唯一正式开发工作区固定为 `D:\qipai`;该目录本身就是 `panda/qipai.git` 的唯一 Git 根目录。
- 当前开发总纲固定放在 `D:\qipai\V4.8.md`,Codex 只需收到“请阅读 V4.8.md,按当前进度继续开发。”即可开始工作。
- 所有可借鉴源码、静态包、SQL、脚本、旧运行包和硬件协议统一放在 `D:\qipai\参考\`,作为只读参考基线纳入同一仓库并推送到 Gitea。
- `参考/` 内不得存在生效的嵌套 `.git`、Git submodule 或独立远端;来源和原始版本写入 `参考/README.md` 与参考清单。
- “所有内容推送远端”指所有项目源码、测试、迁移、文档、部署脚本、配置样例和经脱敏审计的参考资料必须纳入 Git;真实密钥、真实 `.env`、证书私钥、依赖、构建产物、运行日志、上传文件和备份仍禁止提交。
- 每个模块子阶段完成后,必须检查 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
请阅读 V4.8.md,按当前进度继续开发。
```
除非用户明确指定模块,否则不再附加长提示词。本文档中的全部约束、功能清单、开发顺序、验收标准和记录规则,均视为 Codex 的内置执行指令。
### 0.1 Codex 读到本文档后的强制动作
Codex 每次开始工作,必须按以下顺序执行,不能直接凭聊天上下文大范围改代码:
1. 完整阅读本文档,不能只读取当前模块标题。
2. 确认当前目录是 `D:\qipai`,且 `git rev-parse --show-toplevel` 返回同一路径;该目录是单一 `qipai` Git 仓库,包含 `参考/`、后端、后台、小程序、文档、数据库迁移和部署脚本;不得在其他位置或子目录再建业务仓库。
3. 检查并记录:
- `git remote -v`
- `git branch --show-current`
- `git status --short --branch`
- `git log -1 --oneline`
- `git rev-list --left-right --count main...origin/main`
4. `origin` 必须指向 `ssh://git@git.txyundm.cn:2222/panda/qipai.git`。若未配置则设置;若指向其他仓库,先记录并纠正,禁止推错远端。
5. 开始开发前必须执行 `git fetch --prune origin``git pull --ff-only origin main`。存在未提交修改、分叉或拉取失败时,先处理或记录阻塞,不得静默覆盖。
6. 读取以下进度文件;不存在时先按本文档模板创建:
- `docs/module-status.md`
- `docs/feature-status.md`
- `docs/unresolved-issues.md`
- `docs/external-dependencies.md`
- `docs/hardware-vendor.md`
- `docs/mqtt-deployment.md`
- `docs/mqtt-protocol-mapping.md`
- `docs/hardware-test-report.md`
- `docs/deployment-status.md`
- `docs/deployment-changelog.md`
- `docs/repository-map.md`
- `docs/git-deployment.md`
- `docs/release-manifest.md`
- `docs/domain-https.md`
- `docs/api-domain-test-report.md`
- `deploy/README.md`
- `deploy/VERSION`
- `setup.sh`
7. 找到第一个满足前置条件且状态不是 `DONE` 的模块子阶段或功能编号。
8. 每次只开发一个模块子阶段,或一组无法拆分的强关联功能;不得同时横跨多个大模块。
9. 开发前必须优先核对 `D:\qipai\参考`、现有正式代码、旧小程序接口、SQL、硬件协议和历史文档,记录“复用/改写/弃用”判断;不得照搬密钥、密码、真实数据和厂商私有信息。
10. 完成代码、迁移、测试和文档后,必须在本次会话内提交并通过 SSH 推送到远端;不得只写代码或只本地 commit。
11. 每次开发都必须判断是否影响安装、环境变量、构建、数据库迁移、Nginx、PM2、EMQX、目录权限、备份或健康检查;有影响时必须在同一提交中同步更新 `setup.sh`、内部菜单脚本、部署模板和部署文档。
12. 即使本次没有部署影响,也必须在开发日志中写明“部署影响:无”。
13. 菜单式部署脚本不存在、菜单选项不可用或与当前代码不匹配时,优先修复部署脚本,不能继续堆叠无法上线的业务代码。
14. 能自动判断的问题自行处理,不要反复让用户确认技术细节。
15. 只有缺少真实硬件、商户号、支付证书、第三方平台授权或厂商协议时,才允许标记 `BLOCKED_EXTERNAL`;此时仍须完成接口抽象、Mock、数据库、后台配置、错误处理和测试桩。
16. 不得声称未实际执行的测试通过,不得声称未实际完成的 Gitea push 或生产拉取成功。
17. 推送成功后记录远端 commit 校验结果;只有 `git rev-parse HEAD``git rev-parse origin/main` 一致,模块才允许标记为 `DONE`
18. 除非用户明确要求并提供生产执行权限,Codex 不得从 Windows/WSL 自动触发生产部署。
19. 生产部署必须由 Ubuntu 上的 `/opt/apps/setup.sh` 菜单执行;Gitea 收到 push 不等于已经上线,禁止默认启用 push 即自动发布的 Webhook。
### 0.2 功能状态枚举
`docs/feature-status.md` 只能使用以下状态:
| 状态 | 含义 |
|---|---|
| `TODO` | 尚未开始 |
| `DOING` | 当前正在开发 |
| `PARTIAL` | 已有代码但未达到验收标准 |
| `BLOCKED_INTERNAL` | 被项目内部缺陷或前置模块阻塞 |
| `BLOCKED_EXTERNAL` | 仅缺真实硬件、厂商授权、证书、账号或外部 API 权限 |
| `DONE` | 已通过本地测试和对应验收项 |
禁止用“以后再说”“暂不处理”替代状态。用户提供的功能图中的全部功能均为最终必做项;可以分阶段,但不能从总范围中删除。
### 0.3 每次开发结束的强制产物
每次开发完成后,必须同时更新:
- `docs/module-status.md`
- `docs/feature-status.md`
- `docs/devlogs/YYYY-MM-DD-Mxx-主题.md`
- `docs/api-changelog/`API 有变化时)
- `docs/db-changelog/`(数据库有变化时)
- `docs/deployments/`(部署方式或生产环境有变化时)
- `docs/unresolved-issues.md`
- `docs/external-dependencies.md`(涉及硬件、支付或第三方平台时)
- `docs/mqtt-deployment.md`(MQTT 安装、账号、ACL、端口、备份、恢复和巡检变化时)
- `docs/mqtt-protocol-mapping.md`(协议命令、字段、Topic、解析器或适配器变化时)
- `docs/hardware-test-report.md`(每次真实控制箱、门锁、插座联调后)
- `setup.sh``scripts/setup/``deploy/`(只要本次变更影响部署)
- `docs/deployment-status.md`(记录脚本版本、最近验证 commit、支持系统、验证环境和已知限制)
- `docs/deployment-changelog.md`(记录菜单选项、部署脚本、配置模板、迁移和回滚能力变化)
- `docs/repository-map.md`(记录单一仓库、固定 Gitea 地址、Monorepo 子目录、生产目录、默认分支和权限边界)
- `docs/git-deployment.md`(记录本地推送、服务器拉取、整仓发布、回滚和异常状态处理)
- `docs/release-manifest.md`(记录最近一次发布的单一仓库 commit、各子项目构建结果、数据库迁移和部署结果)
- `docs/domain-https.md`(记录固定域名、DNS、Nginx、证书、微信合法域名、续期和回滚方式)
- `docs/api-domain-test-report.md`(记录本地、WSL、服务器和微信真机对 `api.txyundm.cn` 的验证结果)
- `docs/workspace-status.md`(记录固定 Windows/WSL 路径、工具版本和最近检查结果)
- `docs/reference-inventory.md`(记录 `参考/` 全量文件、哈希、用途、敏感性和 Git 状态)
- `docs/reference-redaction-log.md`(记录脱敏文件和字段类别,不保存真实值)
- `docs/repository-completeness.md`(记录 tracked/untracked/ignored/大文件/嵌套仓库和远端一致性)
Git 处理规则:
- 固定远端:`ssh://git@git.txyundm.cn:2222/panda/qipai.git`
- 每完成一个模块子阶段,必须立即提交并推送到 `origin/main`
- 推送前必须通过相关测试、敏感信息检查和文档完整性检查。
- 推送成功后必须执行 `git fetch origin main` 并确认本地 `HEAD``origin/main` 一致。
- 推送失败时保留本地提交,记录失败原因、commit 和重试命令;不得标记 `DONE`,不得伪造推送成功。
- 禁止 `git push --force`、禁止跳过 hooks、禁止把密钥、构建产物、日志、备份和数据库导出推入仓库。
### 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` 是当前权威开发总纲;当前必须是 `V4.8.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 仓库完整性
“所有内容都推送远端”按以下边界执行:
| 类别 | 是否推送 |
|---|---|
| 正式后端、后台、小程序源码 | 必须 |
| 测试、数据库迁移、种子和兼容脚本 | 必须 |
| `V4.8.md`、状态、日志、API/DB/部署文档 | 必须 |
| `setup.sh`、Windows/WSL/Ubuntu 脚本 | 必须 |
| 经脱敏审计的 `参考/` 内容 | 必须 |
| `.env.example`、配置模板 | 必须 |
| 真实 `.env`、密钥、支付证书、私钥 | 禁止 |
| `node_modules`、构建产物、缓存、日志 | 禁止 |
| 上传文件、数据库备份、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 调试。
- WSL 中主仓库路径为 `/mnt/d/qipai`,允许执行只读 Git 检查、`bash -n`、ShellCheck、MQTT 冒烟和轻量脚本验证。
- Windows 与 WSL 不得同时修改同一文件。
- Windows 与 WSL 不得共用 `node_modules`、pnpm store 或平台相关原生模块。
- 完整 Linux 依赖安装、构建、数据库迁移预演和 PM2 启动测试必须复制到 WSL 原生目录 `~/qipai-wsl-test` 或等价临时目录后执行。
- WSL 临时副本不得配置可写远端,不得 commit/push,测试结束后清理并把结果写回当前开发日志。
- 必须同步生成:
```text
scripts/dev/wsl/check-env.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
→ 阅读 V4.8.md 和当前状态文档
→ 检查 参考/ 中可复用实现
→ git fetch + git pull --ff-only
→ 只开发一个模块子阶段
→ Windows 测试
→ WSL 辅助验证
→ 更新代码、测试、迁移、文档和部署脚本
→ 敏感信息、大文件、行尾和仓库完整性检查
→ commit
→ git push origin main
→ fetch 并确认 HEAD == origin/main
→ 才允许模块标记 DONE
```
推送前不得遗留未解释的项目文件。推送失败时保留本地 commit,记录错误和重试命令,模块保持 `PARTIAL``BLOCKED_INTERNAL`,不得伪造成功。
---
# 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.example.com/app-api` | 反代到后端 |
| 后台管理端 | `https://admin.example.com/``https://api.example.com/admin/` | 静态文件 |
| 后台接口 | `https://api.example.com/admin-api` | 反代到后端 |
| 上传文件 | `https://api.example.com/uploads/...` | Nginx 静态目录 |
微信公众平台需要配置:
- request 合法域名:`https://api.example.com`
- uploadFile 合法域名:`https://api.example.com`
- downloadFile 合法域名:`https://api.example.com`
- 支付回调域名:微信支付配置中使用后端 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 原生临时测试副本;Linux 辅助环境 | Ubuntu/WSL、Git、Node.js LTS、ShellCheck、`mosquitto-clients`、MySQL 客户端 | Bash/部署脚本检查、Linux 构建预演、迁移预演、MQTT 冒烟、SSH/Git 兼容测试 | 作为生产服务;保存生产密钥;与 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
├─ V4.8.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/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
读取 V4.8.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/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` 的生产内容。
- 不通过 `scp/rsync` 把业务源码直接覆盖到 `/opt/apps/qipai-*`;生产更新只走 Gitea + 菜单。
- WSL 不长期运行 EMQX、MySQL、PM2 或 Nginx 作为生产替代。
- WSL 中的 commit/push 与 Windows 共用同一 Git 历史;任何一个环境推送前都必须先 pull --ff-only,避免分叉。
### 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、日志、备份和巡检;不得把密码写入本文档或 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` |
| 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` 目录 | 按 V4.8 约定创建 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、日志和备份。 | 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.example.com`、HTTP API、服务器 IP、`localhost:3001` 或旧 API 域名。
- 禁止把 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/
├─ V4.8.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/
│ ├─ V4.8.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` 与 `.gitattributes` 强制项
单一仓库必须忽略:
```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。
`.gitattributes` 必须统一 Windows 与 WSL 行尾,至少包含:
```gitattributes
* text=auto
*.sh text eol=lf
*.bash text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.json text eol=lf
*.js text eol=lf
*.ts text eol=lf
*.vue text eol=lf
*.sql text eol=lf
*.md text eol=lf
*.ps1 text eol=crlf
*.png binary
*.jpg binary
*.jpeg binary
*.pdf binary
*.zip binary
*.xjar binary
```
如果使用 Git LFS,必须先验证 Gitea LFS 可用,并只对经过评审的大文件路径配置;禁止未经验证就把全部二进制迁入 LFS。
生产服务器还必须禁止把 `/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)。
禁止:复用旧参考项目明文密码、把密钥写入 Git/日志/发布清单、以管理员 Gitea 账号拉取、关闭 SSH 指纹校验。
### 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 账号和凭据的映射保存在服务端。密码只存密文/加密值,后台接口只返回是否已配置,永不回显明文。
### 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.example.com`、旧域名、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 子阶段。Codex 每次只完成一个子阶段,并在 `docs/module-status.md``docs/feature-status.md` 中记录。
模块完成条件:
- 对应功能 ID 均达到验收标准。
- API、数据库迁移、前端页面、权限、日志和测试完整。
- 相关外部依赖若未提供,只能标记 `BLOCKED_EXTERNAL`,模块不能假装完成。
- 旧接口兼容或迁移清单已更新。
- 已评估部署影响;有影响时同一提交同步更新菜单式部署脚本和模板,无影响时开发日志明确写“部署影响:无”。
- `docs/deployment-status.md` 中记录的最近验证 commit 不得落后于影响部署的代码提交。
- 本模块子阶段的 commit 已成功推送到 `origin/main`,并验证本地 `HEAD``origin/main` 一致;否则不得标记 `DONE`
## M00. 项目审计、单仓库基线与 `/opt/apps` 部署骨架
**范围:** `SYS-001``ENV-001``REF-001``SCM-001``WSL-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`
- 后端、后台、小程序、迁移、测试、部署脚本、V4.8、全部进度文档和经脱敏审计的 `参考/` 统一纳入 `D:\qipai` 的一个仓库。
- 创建 `docs/repository-map.md``docs/git-deployment.md``docs/release-manifest.md`
- 建立单仓库 lint/test/commit 规范;真实 `.env`、密钥、构建产物、上传和备份不得提交。
- 清理任何误配置的多远端、多仓库或嵌套 `.git`,但清理前必须备份和记录,禁止直接删除未知历史。
### M00-C Windows/WSL 本地开发与模块完成即推送
- Windows `D:\qipai` 是唯一正式开发、MQTTX、微信开发者工具和提交环境;WSL `/mnt/d/qipai` 做轻量检查,完整 Linux 构建在 WSL 原生临时副本完成。
- 生成 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 验收
- `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、真实密钥和生产备份进入仓库。
## 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
请阅读 V4.8.md,从 M06-B 继续开发。
```
## 13.3 指定问题
```text
请阅读 V4.8.md,先修复 ISSUE-012,再继续当前模块。
```
除上述短语外,不需要用户重复粘贴架构、日志、Gitea、部署和验收要求;这些要求已经写入本文档并始终生效。
---
# 14. 每次开发必须维护的文档
## 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 / DONE / BLOCKED / PARTIAL
```
## 14.2 开发日志模板
```markdown
# 开发日志:YYYY-MM-DD Mxx 模块名称
## 1. 本次目标
## 2. 本次完成
## 3. 修改文件
## 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 工具 | mosquitto-clients:未安装/已安装 |
| Windows MQTTX 验证 | 未验证 |
| WSL 环境验证 | 未验证 |
| 最近环境快检 | - |
| 最近部署后复检 | - |
| 最近验证 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 开始模块前
Windows PowerShell
```powershell
Set-Location D:\qipai
git rev-parse --show-toplevel
git status --short --branch --untracked-files=all
git fetch --prune origin
git checkout main
git pull --ff-only origin main
git rev-list --left-right --count main...origin/main
```
WSL 辅助检查:
```bash
git -C /mnt/d/qipai status --short --branch --untracked-files=all
git -C /mnt/d/qipai diff --check
bash -n /mnt/d/qipai/setup.sh
```
要求:
- 工作区存在与当前模块无关的修改时先记录和隔离,不能混入提交。
- `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 每次提交前检查
必须按变更范围执行:
Windows 优先执行 `scripts/dev/windows/test-all.ps1``check-secrets.ps1``check-large-files.ps1``check-line-endings.ps1``check-repo-completeness.ps1`;下列命令是脚本必须覆盖的等价检查,不要求用户手工逐条输入。
```bash
git status --short
# 生产域名与敏感信息检查
# 必须确认无 api.example.com、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 密钥。
## 15.7 `D:\qipai` 完整推送完成条件
每个模块子阶段准备提交时,Codex 必须把工作区文件分为四类:
| 类别 | 处理 |
|---|---|
| 应交付 | 正式源码、测试、迁移、文档、部署脚本、配置样例、脱敏参考资料;必须纳入当前或已有 Git 历史 |
| 本地生成 | `node_modules`、dist、coverage、日志、缓存;必须忽略或删除 |
| 敏感阻断 | `.env`、私钥、支付证书、Token、生产备份;不得提交,必须脱敏或迁移到安全配置 |
| 外部/大文件阻断 | 超过限制或许可不明的参考文件;不得静默遗漏,必须记录哈希、原因和处理方案 |
模块允许标记 `DONE` 的必要条件:
1. `git rev-parse --show-toplevel``D:/qipai`
2. 当前总纲是根目录版本号最大的 `V*.md`
3. `git status --short --untracked-files=all` 没有未解释的应交付文件。
4. `参考/` 本次新增或变化已更新清单、脱敏日志和 Git 状态。
5. 所有相关代码、测试、迁移、文档和部署改动在同一模块提交中。
6. Windows 测试和 WSL 辅助验证结果已记录;未执行项明确标记“未执行”。
7. push 成功且 `HEAD == origin/main`
建议由 `scripts/dev/windows/push-module.ps1` 串联完成:路径检查、pull、测试、敏感信息扫描、大文件扫描、行尾检查、仓库完整性、commit、push 和远端校验。
# 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 临时副本构建预演可用。
- [ ] 菜单脚本在模拟 ARM64/aarch64 检测结果时会安全停止,不会下载错误架构包。
- [ ] EMQX 已通过官方 Ubuntu Apt 源安装,`dpkg-query` 显示 amd64 架构。
- [ ] 正式服务器未安装 MQTTXWindows MQTTX 可连接生产测试账号;服务器/WSL 可选 `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 每完成一个模块,最终回复必须包含:
- 本次完成摘要。
- 修改文件列表。
- 数据库/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 辅助验证环境
Ubuntu Server 24.04 x86-64/amd64 无桌面版作为唯一生产环境
根目录 setup.sh 通过中文数字菜单完成环境快检、安装、更新、EMQX Apt、证书、状态、备份、恢复、回滚和诊断
正式服务器不安装 MQTTXWindows 使用 MQTTX 桌面版,服务器/WSL 仅按需使用 mosquitto-clients
```
用户以后交给 Codex 只需要:
```text
请阅读本文档,按当前进度继续开发。
```
Codex 必须从文档和仓库中自行判断当前模块、功能状态、外部依赖、待修问题和下一步,不要求用户重复粘贴长提示词。