# 自助棋牌室系统开发总纲(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-64(Ubuntu 包架构名为 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 Windows:D:\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 LTS,CPU 架构固定为 x86-64,Ubuntu 包架构名为 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.246(EMQX 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 `。 - 返回结构建议兼容旧风格:`{ 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/ │ ├─ 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` | 0750;Gitea 进程只用 `git` 用户 | | `/opt/apps/qipai-backend` | `qipai:qipai` | 0750;唯一生产 Git 工作区;Git、依赖安装和构建以 `qipai` 用户执行 | | `/opt/apps/qipai-admin` | `qipai:www-data` | 0750;Nginx 只读 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/`,成功后原子切换 `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 菜单 3:Gitea 服务与仓库连接管理 二级菜单: ```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 菜单 4:MQTT / 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//`。 - 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`。 - Broker:EMQX 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/服务器密码。 - 禁止 Docker;EMQX 作为 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 { 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 ` | | `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 联调顺序与验收矩阵 ### 阶段 1:Windows 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`。 ### 阶段 3:Sub-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 (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 架构。 - [ ] 正式服务器未安装 MQTTX;Windows 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 PM2(API + Worker) Vue3 后台管理端静态部署 微信原生小程序 实际门锁/电控/灯控/云喇叭按厂商适配器接入 Windows 的 `D:\qipai` 作为唯一开发/调试/测试与提交工作区,`D:\qipai\参考` 作为同仓库只读参考基线,WSL 作为 Linux 辅助验证环境 Ubuntu Server 24.04 x86-64/amd64 无桌面版作为唯一生产环境 根目录 setup.sh 通过中文数字菜单完成环境快检、安装、更新、EMQX Apt、证书、状态、备份、恢复、回滚和诊断 正式服务器不安装 MQTTX;Windows 使用 MQTTX 桌面版,服务器/WSL 仅按需使用 mosquitto-clients ``` 用户以后交给 Codex 只需要: ```text 请阅读本文档,按当前进度继续开发。 ``` Codex 必须从文档和仓库中自行判断当前模块、功能状态、外部依赖、待修问题和下一步,不要求用户重复粘贴长提示词。