commit 28613b2093c353020c19b9b69fc311c5ef3c1efe Author: Codex Date: Mon Jun 15 15:34:50 2026 +0800 chore(M00-A): 建立单仓库基线和参考清单 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1d6ad4c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,16 @@ +* text=auto +*.sh text eol=lf +setup.sh text eol=lf +*.ps1 text eol=crlf +*.md text eol=lf +*.json text eol=lf +*.ts text eol=lf +*.js text eol=lf +*.vue text eol=lf +*.wxml text eol=lf +*.wxss text eol=lf +*.sql text eol=lf +*.zip binary +*.xjar binary +xjar binary + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bd5e85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +.env +.env.* +!.env.example +*.pem +*.key +*.p12 +*.crt +*.csr +node_modules/ +dist/ +build/ +coverage/ +.cache/ +.vite/ +logs/ +*.log +uploads/ +backup/ +backups/ +*.bak +*.dump +*.sql.gz +*.zip.tmp +*.tmp +.DS_Store +Thumbs.db + +# Recorded in docs/reference-inventory.md as a large-file blocked reference. +参考/mazongjian-server.xjar + +# Raw reference archives or SQL with secrets, dependencies, or production data. +# Keep local copies only; commit sanitized extracts or metadata instead. +参考/easy-joy-life-main.zip +参考/24h_qipaishi-master(1).zip +参考/小程序源代码.zip +参考/db_20260427.sql diff --git a/README.md b/README.md new file mode 100644 index 0000000..d90a30e --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# 自助棋牌室系统 + +本仓库是 `panda/qipai.git` 的单一 Monorepo 工作区,固定 Windows 开发路径为 `D:\qipai`。 + +## 固定约束 + +- 当前权威总纲:`V4.8.md` +- 固定远端:`ssh://git@git.txyundm.cn:2222/panda/qipai.git` +- 默认分支:`main` +- 生产域名:`https://api.txyundm.cn` +- 生产系统:Ubuntu Server 24.04 x86-64/amd64 +- 部署方式:无 Docker,使用根目录 `setup.sh` 菜单式部署 + +## 目录 + +- `backend/`:Fastify + TypeScript 后端 API +- `admin/`:Vue3 后台管理端 +- `miniapp/`:微信原生小程序 +- `database/`:迁移、种子和兼容 SQL +- `deploy/`:部署说明、版本和生产配置模板 +- `scripts/`:Windows、WSL 和 Ubuntu 辅助脚本 +- `docs/`:模块状态、开发日志、变更记录和验收文档 +- `参考/`:只读参考资料,正式开发不得直接在其中二开 + diff --git a/V4.8.md b/V4.8.md new file mode 100644 index 0000000..f73edaf --- /dev/null +++ b/V4.8.md @@ -0,0 +1,4626 @@ +# 自助棋牌室系统开发总纲(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 必须从文档和仓库中自行判断当前模块、功能状态、外部依赖、待修问题和下一步,不要求用户重复粘贴长提示词。 diff --git a/admin/.gitkeep b/admin/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/admin/.gitkeep @@ -0,0 +1 @@ + diff --git a/backend/.gitkeep b/backend/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/backend/.gitkeep @@ -0,0 +1 @@ + diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/database/migrations/.gitkeep @@ -0,0 +1 @@ + diff --git a/database/seeds/.gitkeep b/database/seeds/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/database/seeds/.gitkeep @@ -0,0 +1 @@ + diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..2cd5c49 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,33 @@ +# 部署说明 + +本项目生产环境固定为 Ubuntu Server 24.04 x86-64/amd64,无桌面环境,禁止 Docker。生产入口固定为 `/opt/apps/setup.sh`,本仓库根目录 `setup.sh` 是同源菜单脚本。 + +## 环境边界 + +- Windows:`D:\qipai`,唯一正式开发、测试、提交和推送工作区。 +- WSL:`/mnt/d/qipai`,仅做轻量检查;完整 Linux 构建必须复制到 WSL 原生临时目录。 +- Ubuntu:`/opt/apps`,由管理员通过菜单执行安装、更新、备份、恢复、回滚和诊断。 + +## 固定地址 + +- Gitea SSH:`ssh://git@git.txyundm.cn:2222/panda/qipai.git` +- 生产只读 SSH:`ssh://git@127.0.0.1:2222/panda/qipai.git` +- API:`https://api.txyundm.cn` +- App API:`https://api.txyundm.cn/app-api` +- Admin API:`https://api.txyundm.cn/admin-api` + +## 菜单 + +1. 首次安装 +2. 更新业务服务 +3. 安装或检查 EMQX +4. 配置域名与 HTTPS +5. 查看状态 +6. 备份 +7. 恢复 +8. 回滚 +9. 诊断 +0. 退出 + +当前版本仅提供 M00 菜单骨架和安全提示,生产安装动作将在后续 M00-D/M00-E 完善。 + diff --git a/deploy/VERSION b/deploy/VERSION new file mode 100644 index 0000000..9f4466a --- /dev/null +++ b/deploy/VERSION @@ -0,0 +1,2 @@ +0.0.1-m00-baseline + diff --git a/docs/api-changelog/.gitkeep b/docs/api-changelog/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/api-changelog/.gitkeep @@ -0,0 +1 @@ + diff --git a/docs/api-domain-test-report.md b/docs/api-domain-test-report.md new file mode 100644 index 0000000..2930f45 --- /dev/null +++ b/docs/api-domain-test-report.md @@ -0,0 +1,21 @@ +# 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:待扫描 + +## 问题与修复 + +- 当前仅建立报告模板。 + diff --git a/docs/db-changelog/.gitkeep b/docs/db-changelog/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/db-changelog/.gitkeep @@ -0,0 +1 @@ + diff --git a/docs/db-schema-inventory.md b/docs/db-schema-inventory.md new file mode 100644 index 0000000..1d6470c --- /dev/null +++ b/docs/db-schema-inventory.md @@ -0,0 +1,37 @@ +# 旧数据库结构清单 + +> 来源:`参考/db_20260427.sql` 与 `easy-joy-life-main.zip` 中 SQL 的隔离初审。原 SQL 含 `INSERT` 数据、文件 URL、日志和业务配置,已从 Git 跟踪中移出。本清单仅记录表结构线索和后续迁移参考方向。 + +## `db_20260427.sql` 表分组 + +| 分组 | 表名 | 可参考点 | +|---|---|---| +| 平台基础/系统 | `system_tenant`, `system_tenant_package`, `system_users`, `system_role`, `system_menu`, `system_role_menu`, `system_dept`, `system_post`, `system_user_role`, `system_user_post` | 多租户、角色、菜单、部门和平台后台权限模型。 | +| 字典/通知/日志 | `system_dict_type`, `system_dict_data`, `system_notice`, `system_notify_template`, `system_notify_message`, `system_operate_log`, `system_login_log`, `system_error_code`, `system_sensitive_word` | 字典、通知模板、站内通知、操作日志和错误码。 | +| 短信/邮件/OAuth | `system_sms_channel`, `system_sms_template`, `system_sms_code`, `system_sms_log`, `system_mail_account`, `system_mail_template`, `system_mail_log`, `system_oauth2_access_token`, `system_oauth2_refresh_token`, `system_oauth2_client`, `system_oauth2_code`, `system_oauth2_approve`, `system_social_user`, `system_social_user_bind` | 第三方登录、短信、邮件和 OAuth 体系,仅作抽象参考。 | +| 基础设施 | `infra_config`, `infra_file`, `infra_file_config`, `infra_file_content`, `infra_job`, `infra_job_log`, `infra_api_access_log`, `infra_api_error_log`, `infra_codegen_table`, `infra_codegen_column`, `infra_data_source_config`, `infra_test_demo` | 配置、文件、定时任务、API 日志和代码生成。 | +| 门店/房间 | `member_store_info`, `member_room_info`, `member_store_user`, `member_store_template`, `member_store_sound_info` | 门店、房间、门店员工、装修模板和语音配置。 | +| 设备/门禁 | `member_device_info`, `member_device_use_info`, `member_face_record`, `member_face_blacklist` | 设备绑定、设备使用记录、人脸记录和黑名单。 | +| 订单/支付 | `member_order_info`, `member_pay_order`, `member_product_order`, `member_group_pay_info`, `member_store_pay_config`, `member_store_wxpay_config`, `member_store_pay_split`, `member_merchant_account` | 订单、支付订单、商品订单、团购支付、门店支付配置、微信支付配置和分账。 | +| 会员/资金 | `member_user`, `member_user_money_bill`, `member_user_withdrawal`, `member_store_vip_config`, `member_pkg_info`, `member_pkg_user_info` | 会员、余额流水、提现、会员配置、套餐和用户套餐。 | +| 优惠/营销 | `member_coupon_info`, `member_coupon_active`, `member_discount_rules`, `member_lottery_info`, `member_lottery_detail`, `member_banner_info`, `member_holiday` | 优惠券、活动、折扣、抽奖、广告和节假日。 | +| 保洁/加盟/美团 | `member_clear_info`, `member_clear_bill`, `member_franchise_info`, `member_store_meituan_info` | 保洁任务/账单、加盟信息和美团门店配置。 | +| 库存/商品 | `member_inventory_info`, `member_inventory_detail`, `member_inventory_goods`, `member_inventory_record`, `member_game_info`, `yshop_store_product`, `yshop_store_product_attr`, `yshop_store_product_attr_value`, `yshop_store_product_attr_result`, `yshop_store_product_brand`, `yshop_store_product_category`, `yshop_store_product_relation`, `yshop_store_product_reply`, `yshop_store_product_rule` | 库存、商品、游戏/娱乐项目和商品规格。 | +| 调度 | `qrtz_job_details`, `qrtz_triggers`, `qrtz_cron_triggers`, `qrtz_simple_triggers`, `qrtz_blob_triggers`, `qrtz_simprop_triggers`, `qrtz_fired_triggers`, `qrtz_calendars`, `qrtz_locks`, `qrtz_paused_trigger_grps`, `qrtz_scheduler_state` | Quartz 调度表;新系统首期按 V4.8 使用 MySQL outbox + PM2 worker,不照搬 Quartz。 | + +## `easy-joy-life-main.zip` SQL 表线索 + +| 文件 | 表名 | 可参考点 | +|---|---|---| +| `schema.sql` | `users`, `stores`, `rooms`, `orders`, `order_cancel_records`, `payments`, `devices`, `admins`, `system_config`, `admin_users`, `admin_roles`, `admin_permissions`, `admin_user_roles`, `admin_role_permissions`, `admin_operation_logs`, `openapi_call_logs` | 轻量 MVP 表结构,适合参考第一阶段核心闭环。 | +| `mysql-init.sql` | `stores`, `rooms`, `users`, `payment_orders`, `order_cancel_records`, `user_wallets`, `wallet_transactions`, `admin_users`, `admin_roles`, `admin_permissions`, `admin_user_roles`, `admin_role_permissions`, `admin_operation_logs`, `openapi_call_logs`, `system_config` | 门店、房间、用户、支付订单、钱包和后台权限初始化。 | +| `db-init-bank-card-paypassword.sql` | `user_bank_cards`, `user_pay_passwords` | 银行卡和支付密码能力,后续资金功能参考。 | +| `data.sql` | `stores`, `rooms` | 示例种子数据;不导入真实项目。 | + +## 迁移原则 + +- 只从旧 SQL 提取表意、字段类别、状态枚举和业务关系。 +- 不提交原始 `INSERT` 数据,不导入旧文件 URL、日志、用户数据或生产配置。 +- 正式迁移文件必须重新设计 `tenant_id` 隔离、索引、审计字段和回滚 SQL。 +- 首期后端按 V4.8 固定为 Fastify + Kysely + MySQL,不复用 Java/Spring 表生成方式。 + diff --git a/docs/deployment-changelog.md b/docs/deployment-changelog.md new file mode 100644 index 0000000..31b4a6e --- /dev/null +++ b/docs/deployment-changelog.md @@ -0,0 +1,13 @@ +# 部署变更记录 + +## 2026-06-15 / 0.0.1-m00-baseline + +- 关联模块:M00 +- 关联 commit:本地 HEAD(push 待完成) +- 变化内容:建立根目录 `setup.sh`、部署目录和状态文档。 +- 配置变化:固定 `api.txyundm.cn`、`/opt/apps`、Ubuntu 24.04 amd64。 +- 数据库变化:无。 +- 兼容性:当前仅菜单骨架,不执行生产改动。 +- 已执行验证:待运行脚本。 +- 回滚方式:尚未生产部署,无生产回滚。 +- 生产环境人工步骤:后续由管理员在 Ubuntu `/opt/apps/setup.sh` 执行。 diff --git a/docs/deployment-status.md b/docs/deployment-status.md new file mode 100644 index 0000000..702f065 --- /dev/null +++ b/docs/deployment-status.md @@ -0,0 +1,42 @@ +# 部署状态 + +| 项目 | 当前值 | +|---|---| +| 菜单脚本版本 | 0.0.1-m00-baseline | +| 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 | 本地 HEAD(push 待完成) | +| 最近 push 远端校验 | 未通过:`git.txyundm.cn:2222` 主机指纹未确认/SSH 连接不稳定 | +| 目标系统 | Ubuntu 24.04 | +| 内核架构 | x86_64 | +| DPKG 架构 | amd64 | +| 用户空间位数 | 64 | +| EMQX 版本/架构 | 未安装/未验证 | +| 服务器 MQTTX | 不安装 | +| 命令行 MQTT 工具 | mosquitto-clients:未安装/未验证 | +| Windows MQTTX 验证 | 未验证 | +| WSL 环境验证 | 未验证 | +| 最近环境快检 | 2026-06-15 本地骨架检查 | +| 最近部署后复检 | 未执行 | +| 最近验证 commit | 本地 HEAD(本地);M00-C 推送脚本语法检查通过 | +| 最近验证日期 | - | +| 已验证系统 | Ubuntu 24.04 / 未验证 | +| 菜单 1 首次安装 | 未验证 | +| 菜单 2 更新业务 | 未验证 | +| 菜单 3 MQTT | 未验证 | +| 菜单 4 域名与 HTTPS | 未验证 | +| 菜单 5 状态 | 未验证 | +| 菜单 6 备份 | 未验证 | +| 菜单 7 恢复 | 未验证 | +| 菜单 8 回滚 | 未验证 | +| 菜单 9 诊断 | 未验证 | +| 已知限制 | 当前仅为 M00 基线骨架,未执行生产部署。 | diff --git a/docs/deployments/.gitkeep b/docs/deployments/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/deployments/.gitkeep @@ -0,0 +1 @@ + diff --git a/docs/devlogs/2026-06-15-M00-A-单仓库基线.md b/docs/devlogs/2026-06-15-M00-A-单仓库基线.md new file mode 100644 index 0000000..d6c3ddd --- /dev/null +++ b/docs/devlogs/2026-06-15-M00-A-单仓库基线.md @@ -0,0 +1,60 @@ +# 开发日志:2026-06-15 M00-A/M00-B 单仓库基线 + +## 范围 + +- 建立 `D:\qipai` 单一 Git 根和固定 origin。 +- 创建进度文档、仓库地图、部署状态、参考清单和脱敏日志。 +- 对 `参考/` 首批文件生成外层 SHA-256。 +- 脱敏旧 `参考/env` 中的密码类字段。 +- 创建根目录菜单脚本和 Windows/WSL 检查脚本骨架。 +- 对 4 个参考压缩包执行隔离解包初审,识别技术栈、依赖目录和敏感配置风险。 +- 将含密钥/依赖/真实数据风险的原始参考包和 SQL 从 Git 跟踪中移出,仅保留哈希和审计结论。 +- 生成 `docs/reference-page-map.md`、`docs/db-schema-inventory.md` 和 `docs/reference-api-inventory.md` 作为可提交脱敏摘要。 +- 生成 `scripts/dev/windows/push-module.ps1` 和 `check-gitea-ssh.ps1`,串联模块检查、提交、推送和远端校验。 + +## 起始状态 + +- `D:\qipai` 初始不是 Git 仓库。 +- 根目录仅有 `V4.8.md` 和 `参考/`。 +- `参考/` 包含多个压缩包、SQL、旧运行包和旧启动脚本。 + +## 复用/改写/弃用判断 + +- 旧小程序、后台和同类项目压缩包:仅作参考,待隔离解包审计后再迁入正式目录。 +- 旧 SQL:仅参考表结构,不直接导入真实数据。 +- 旧 `.env`:只保留变量类别,真实值已脱敏。 +- `mazongjian-server.xjar`:仅观察旧接口行为,不反编译、不作为新系统运行依赖。 +- `easy-joy-life-main.zip`:含 Spring Boot 后端、小程序、SQL、PEM 和支付配置;只抽象参考订单/设备/钱包/权限设计,原包不提交。 +- `24h_qipaishi-master(1).zip` 与 `小程序源代码.zip`:含微信原生小程序页面、依赖目录、旧 AppID 和旧 API 域名;只记录页面和业务流程,原包不提交。 +- `db_20260427.sql`:含旧业务数据、文件 URL 和任务日志;原 SQL 不提交,后续生成脱敏 schema-only。 + +## 部署影响 + +有。新增 `setup.sh` 菜单骨架和部署状态文档,但未执行生产部署。 + +## 测试记录 + +- Windows 检查:已执行 `scripts/dev/windows/test-all.ps1`,工作区、参考目录、行尾检查通过;发现 `参考/mazongjian-server.xjar` 超过 100MB,已按大文件阻断项记录并忽略。 +- 敏感信息检查:已执行 `scripts/dev/windows/check-secrets.ps1`,未发现明文敏感信息模式。 +- 参考解包审计:`check-reference.ps1` 已输出 ZIP-AUDIT 摘要;`easy-joy-life-main.zip` 发现 5 个敏感配置/证书类文件;两个小程序包各发现依赖目录。 +- 脱敏摘要:已生成页面地图、数据库结构清单和接口线索文档。 +- Windows 推送脚本:已新增,遇到 SSH 主机指纹问题时停止,不自动覆盖 `known_hosts`。 +- PowerShell 语法检查:`push-module.ps1`、`check-gitea-ssh.ps1` 均已通过 Parser 检查。 +- WSL 检查:未执行,本轮仅完成 Windows 基线脚本。 +- Git fetch/push:`git fetch --prune origin` 连接 `git.txyundm.cn:2222` 曾超时;最新 `git push origin main` 返回 `Host key verification failed`,随后 `ssh -vvv -T -p 2222 git@git.txyundm.cn` 超时。当前无法安全确认远端主机指纹。 + +## 风险 + +- 参考压缩包尚未完成逐文件解包审计。 +- `mazongjian-server.xjar` 体积约 194MB,暂不纳入普通 Git 提交;待确认 Git LFS 或等价归档策略。 +- 原始小程序包、`easy-joy-life-main.zip` 和旧 SQL 已确认不适合直接提交;后续必须生成脱敏摘录或结构化迁移草案。 +- 当前无法完成 Gitea SSH 主机指纹确认和 push,模块不能标记 DONE。 + +## Git 与 Gitea + +- 远端:`ssh://git@git.txyundm.cn:2222/panda/qipai.git` +- 分支:`main` +- commit:本地 HEAD(最终哈希见 `git log -1 --oneline`) +- push 结果:失败,最新为 `Host key verification failed`;详细 SSH 连接测试超时 +- HEAD 与 origin/main:待校验 +- 重试命令:先人工确认 `git.txyundm.cn:2222` 主机指纹,再执行 `git fetch --prune origin && git push origin main && git fetch origin main` diff --git a/docs/domain-https.md b/docs/domain-https.md new file mode 100644 index 0000000..8e4995e --- /dev/null +++ b/docs/domain-https.md @@ -0,0 +1,44 @@ +# 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:未验证 +- 真机验证:未执行 + +## 回滚 + +- 上一个配置:无 +- 恢复命令:待生成 + diff --git a/docs/external-dependencies.md b/docs/external-dependencies.md new file mode 100644 index 0000000..143677e --- /dev/null +++ b/docs/external-dependencies.md @@ -0,0 +1,9 @@ +# 外部依赖 + +| 编号 | 类型 | 功能 ID | 所需资料 | 当前状态 | Mock 是否完成 | 负责人/来源 | 下一步 | +|---|---|---|---|---|---|---|---| +| EXTDEP-001 | Gitea | SCM-001/OPS-002 | `ssh://git@git.txyundm.cn:2222/panda/qipai.git` 访问权限 | 待首次 push 验证 | 不适用 | 用户/Gitea | 首次提交后执行 push 与 `HEAD == origin/main` 校验。 | +| EXTDEP-002 | Ubuntu 服务器 | OPS-001/OPS-004/TLS-001 | Ubuntu 24.04 x86-64、域名、Nginx、证书权限 | 待生产人工执行 | 否 | 用户/服务器 | Codex 仅维护菜单脚本,不自动生产部署。 | +| EXTDEP-003 | 硬件 | IOT-001 | EMQX 主机、控制箱、门锁、插座和厂商协议 | 待实机联调 | 否 | 用户/厂商 | 后续 M06 完成适配器和 Mock。 | +| EXTDEP-004 | 微信平台 | WXNET-001 | 小程序 AppID、合法域名、真机体验权限 | 待提供/验证 | 否 | 用户/微信公众平台 | M08/M10 验证真机链路。 | + diff --git a/docs/feature-status.md b/docs/feature-status.md new file mode 100644 index 0000000..57fcda0 --- /dev/null +++ b/docs/feature-status.md @@ -0,0 +1,19 @@ +# 功能状态 + +> 只能使用 TODO / DOING / PARTIAL / BLOCKED_INTERNAL / BLOCKED_EXTERNAL / DONE。 + +| ID | 功能 | 模块子阶段 | 状态 | 最近提交 | 测试/验收证据 | 阻塞原因 | 下一步 | +|---|---|---|---|---|---|---|---| +| ENV-001 | 固定 Windows 工作区 | M00-A/M00-B | PARTIAL | 本地 HEAD | 已确认 `D:\qipai`,已初始化 Git 根并完成本地提交;待 push 校验。 | Gitea SSH 超时。 | 恢复网络后推送并校验 `HEAD == origin/main`。 | +| REF-001 | 参考资料完整纳管 | M00-A | PARTIAL | 本地 HEAD | 已生成哈希清单、脱敏日志、页面地图、接口线索和旧数据库结构清单;含秘密/依赖/真实数据风险的原始包和 SQL 已移出 Git 跟踪。 | 后续仍需按模块生成正式源码/迁移,旧 xjar 需大文件策略。 | 进入 M00-B/M00-C 前继续保持原包忽略和摘要可追溯。 | +| SCM-001 | 模块完成即完整推送 | M00-B/M00-C | BLOCKED_INTERNAL | 本地 HEAD | 已配置固定 origin 并完成本地提交;push 未完成。 | `git.txyundm.cn:2222` 主机指纹未确认/SSH 连接不稳定。 | 人工确认主机指纹后重试 fetch/push 并校验远端。 | +| WSL-001 | WSL 隔离辅助验证 | M00-C | PARTIAL | 本地 HEAD | 已生成 WSL 轻量检查、临时副本、Linux 语法验证、MQTT 冒烟和清理脚本;尚未在 WSL 环境执行。 | WSL 实际执行未验证。 | 在可用 WSL 中运行 `scripts/dev/wsl/verify-linux.sh`。 | +| API-001 | 固定 HTTPS API 域名 | M00-E/M01/M08/M10 | TODO | - | - | DNS/HTTPS 生产验证未执行。 | 在部署脚本和代码中统一 `api.txyundm.cn`。 | +| TLS-001 | Nginx 与证书自动化 | M00-E/M10 | TODO | - | - | DNS/80/443 生产验证未执行。 | 补菜单脚本和证书检查。 | +| WXNET-001 | 微信合法域名与真机验证 | M00-E/M08/M10 | TODO | - | - | 微信后台/真机未验证。 | 先完成检查报告模板。 | +| OPS-001 | 固定 `/opt/apps` 目录 | M00-D/M10 | TODO | - | - | Ubuntu 生产操作未执行。 | 生成部署菜单和目录检查。 | +| OPS-002 | 单仓库 Gitea 推送与拉取部署 | M00-B/M10 | BLOCKED_INTERNAL | 本地 HEAD | origin 已设置,本地提交完成;远端 push 未完成。 | `git.txyundm.cn:2222` 主机指纹未确认/SSH 连接不稳定。 | 人工确认主机指纹后重试首次提交推送。 | +| OPS-003 | 整仓发布清单 | M00-E/M10 | TODO | - | - | - | 生成 release manifest 模板。 | +| OPS-004 | 菜单式更新与环境监测 | M00-E/M10 | TODO | - | - | - | 生成 `setup.sh` 和 `scripts/setup`。 | +| IOT-001 | MQTT Broker 生产部署 | M00/M06 | TODO | - | - | 缺生产 EMQX 实机验证。 | 先完成部署文档和 MQTT 状态模板。 | +| SYS-001 | 微信原生小程序 | M00-C/M08 | TODO | - | - | 参考小程序压缩包尚未深度整理。 | 后续导入正式 `miniapp/`。 | diff --git a/docs/git-deployment.md b/docs/git-deployment.md new file mode 100644 index 0000000..b296771 --- /dev/null +++ b/docs/git-deployment.md @@ -0,0 +1,43 @@ +# Git 与部署流 + +## 固定仓库 + +- Windows/WSL SSH:`ssh://git@git.txyundm.cn:2222/panda/qipai.git` +- 生产同机只读 SSH:`ssh://git@127.0.0.1:2222/panda/qipai.git` +- 默认分支:`main` + +## 本地推送 + +1. 在 `D:\qipai` 完成一个模块子阶段。 +2. 运行 Windows 检查脚本。 +3. 提交本模块相关文件。 +4. 执行 `git push origin main`。 +5. 执行 `git fetch origin main` 并校验 `HEAD == origin/main`。 + +推荐使用: + +```powershell +powershell -ExecutionPolicy Bypass -File scripts/dev/windows/push-module.ps1 ` + -Message "chore(M00-C): 完善模块推送脚本" ` + -Paths @("scripts/dev/windows/push-module.ps1", "docs/git-deployment.md") +``` + +如出现 `Host key verification failed`,先通过可信渠道确认 `git.txyundm.cn:2222` 的 SSH 主机指纹,再更新 `known_hosts`。Codex 不自动覆盖主机信任记录。 + +## SSH 检查 + +```powershell +powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-gitea-ssh.ps1 +``` + +该脚本只检查已有 known_hosts 与 SSH 握手,不自动写入新主机指纹。 + +## 当前已知阻塞 + +- 本地 `known_hosts` 只有 `[localhost]:2222` 记录,未安全确认 `[git.txyundm.cn]:2222`。 +- 最新 push 返回 `Host key verification failed`。 +- 在可信渠道确认主机指纹前,不应自动追加或覆盖 known_hosts。 + +## 生产发布 + +生产服务器不得因 Gitea push 自动发布。管理员必须在 Ubuntu 上通过 `/opt/apps/setup.sh` 菜单拉取、构建、迁移和复检。 diff --git a/docs/hardware-test-report.md b/docs/hardware-test-report.md new file mode 100644 index 0000000..9b72e34 --- /dev/null +++ b/docs/hardware-test-report.md @@ -0,0 +1,6 @@ +# 硬件联调报告 + +| 时间 | 设备 | 场景 | 环境 | 结果 | 证据 | 问题 | +|---|---|---|---|---|---|---| +| 2026-06-15 | - | 尚未联调 | 本地文档骨架 | 未执行 | - | 缺实机联调。 | + diff --git a/docs/hardware-vendor.md b/docs/hardware-vendor.md new file mode 100644 index 0000000..710a2cf --- /dev/null +++ b/docs/hardware-vendor.md @@ -0,0 +1,9 @@ +# 硬件厂商与设备 + +| 设备 | 当前资料 | 状态 | 下一步 | +|---|---|---|---| +| 4G 智能门禁控电箱 | 待在参考资料中定位正式协议 | TODO | M06 前完成协议录入。 | +| Sub-1G 智能门锁 | 待在参考资料中定位正式协议 | TODO | M06 前完成绑定和控制模型。 | +| 4G 智慧插座 | 待在参考资料中定位正式协议 | TODO | M06 前完成型号能力识别。 | +| EMQX Broker | 固定主机 `101.42.38.246` | TODO | M00/M10 生成部署与巡检。 | + diff --git a/docs/module-status.md b/docs/module-status.md new file mode 100644 index 0000000..be7c8ad --- /dev/null +++ b/docs/module-status.md @@ -0,0 +1,15 @@ +# 模块状态 + +| 模块 | 状态 | 最近提交 | 最近开发日志 | 备注 | +|---|---|---|---|---| +| M00 单仓库与服务器基础骨架 | PARTIAL | 本地 HEAD | docs/devlogs/2026-06-15-M00-A-单仓库基线.md | 本地提交完成;Gitea SSH 超时,push 成功前不得标记 DONE。 | +| M01 后端 API 基础工程 | TODO | - | - | 等 M00 基线完成后开始。 | +| M02 登录、租户、权限 | TODO | - | - | - | +| M03 门店、房间、价格、营业时间 | TODO | - | - | - | +| M04 订单、时段锁定、支付闭环 | TODO | - | - | - | +| M05 会员、余额、套餐、优惠券 | TODO | - | - | - | +| M06 设备、MQTT 与真实硬件联动 | TODO | - | - | - | +| M07 保洁、通知、任务和语音播报 | TODO | - | - | - | +| M08 微信原生小程序完整业务 | TODO | - | - | - | +| M09 后台管理端完整业务 | TODO | - | - | - | +| M10 部署、域名、验收和运维闭环 | TODO | - | - | - | diff --git a/docs/mqtt-deployment.md b/docs/mqtt-deployment.md new file mode 100644 index 0000000..2bf21a0 --- /dev/null +++ b/docs/mqtt-deployment.md @@ -0,0 +1,16 @@ +# MQTT 部署记录 + +| 项目 | 当前值 | +|---|---| +| Broker 主机 | 101.42.38.246 | +| Broker 软件 | EMQX 5.x,待安装/验证 | +| 安装方式 | Ubuntu Apt 原生安装,禁止 Docker | +| MQTTX | 服务器不安装,Windows 可使用 | +| 命令行工具 | mosquitto-clients 可选 | +| 匿名连接 | 待验证关闭 | +| ACL | 待配置 | +| 1883 | 待验证 | +| 18083 | 待验证不对公网任意开放 | +| 备份 | 待生成 | +| 恢复 | 待生成 | + diff --git a/docs/mqtt-protocol-mapping.md b/docs/mqtt-protocol-mapping.md new file mode 100644 index 0000000..f356315 --- /dev/null +++ b/docs/mqtt-protocol-mapping.md @@ -0,0 +1,10 @@ +# MQTT 协议映射 + +当前尚未完成厂商协议深度录入。后续 M06 必须以正式协议文档为准,记录 Topic、命令、字段、回包、超时、重试和审计要求。 + +| 设备 | Topic/命令 | 方向 | 字段 | 状态 | +|---|---|---|---|---| +| 4G 智能门禁控电箱 | 待补充 | 双向 | 待补充 | TODO | +| Sub-1G 智能门锁 | 待补充 | 双向 | 待补充 | TODO | +| 4G 智慧插座 | 待补充 | 双向 | 待补充 | TODO | + diff --git a/docs/reference-api-inventory.md b/docs/reference-api-inventory.md new file mode 100644 index 0000000..3543a12 --- /dev/null +++ b/docs/reference-api-inventory.md @@ -0,0 +1,41 @@ +# 参考接口线索 + +> 本文档只记录接口路径类别和迁移方向,不记录旧域名、AppID、Token、密钥或真实请求参数。 + +## 小程序旧接口模式 + +旧小程序通过 `app.globalData.baseUrl + path` 拼接接口。旧 `baseUrl` 已确认不是正式域名,正式迁移必须统一为: + +```text +https://api.txyundm.cn/app-api +``` + +| 类别 | 旧路径线索 | 新系统迁移方向 | +|---|---|---| +| 用户/登录 | 登录、手机号、用户资料、token 存储 | M02/M08 统一微信登录、手机号授权、角色识别和 token 刷新。 | +| 门店/房间 | 门店列表、房间列表、房间详情、地图定位 | M03/M08 提供租户隔离的门店、房间、营业时间和价格查询。 | +| 订单 | 创建订单、订单列表、订单详情、取消、换房、续费 | M04/M08 实现时间段锁定、订单状态机、续费/取消/换房。 | +| 支付/资金 | 支付、余额、充值、套餐、支付订单 | M04/M05 接入微信支付、余额、套餐和资金流水;缺商户资料时做 Mock。 | +| 团购/优惠 | 团购、优惠券、抽奖、活动 | M05/M10 建模优惠券、团购验券和活动配置。 | +| 保洁 | 保洁任务、任务详情、任务统计、任务结算 | M07/M08 实现保洁角色、抢单/完成/驳回和结算。 | +| 设备/开门 | 门锁、网关、设备列表、开门提交、Wi-Fi 配置 | M06/M08 接入 MQTT 设备适配器,开门权限必须绑定有效订单。 | +| 后台管理小程序入口 | 管理员、门店管理、房间管理、模板、推送规则 | M08/M09 明确小程序管理端与 Web 后台的权限边界。 | +| 库存/商品 | 库存商品、出入库、商品分类、商品订单 | 后续运营功能,不进入第一阶段核心闭环。 | +| 外部平台 | 美团预订、易达取消、第三方支付桥 | 外部授权缺失时标记 `BLOCKED_EXTERNAL`,保留适配器和 Mock。 | + +## 后台静态包接口线索 + +`参考/后台管理系统_20260427.zip` 为构建产物,静态 JS 中可见后台 API、字典和权限命名痕迹,但不得作为正式源码。后续 M09 只参考: + +- 菜单结构和页面覆盖范围。 +- 字典枚举名称。 +- 管理端列表/表单字段。 +- API 前缀迁移到 `https://api.txyundm.cn/admin-api`。 + +## 禁止事项 + +- 禁止把旧 `baseUrl`、localhost、IP、旧域名写入正式代码。 +- 禁止复用旧 AppID、AppSecret、支付密钥、证书路径和真实商户号。 +- 禁止直接复制构建产物作为 Vue3 后台源码。 +- 禁止让小程序绕过微信合法域名验证。 + diff --git a/docs/reference-inventory.md b/docs/reference-inventory.md new file mode 100644 index 0000000..80e2f57 --- /dev/null +++ b/docs/reference-inventory.md @@ -0,0 +1,22 @@ +# 参考资料清单 + +| 路径 | 类型 | 大小 | SHA-256 | 来源 | 用途 | 是否解压审计 | 敏感性 | Git 状态 | +|---|---|---:|---|---|---|---|---|---| +| `参考/24h_qipaishi-master(1).zip` | zip | 11649307 | `013ABA400BA44002DED6FF63D4E2480A080A9F1CB40E350916030DEBD7CE3D9A` | 用户提供 | 参考同类棋牌室源码结构 | 已隔离解包初审 | 含 `node_modules`/`miniprogram_npm` 和旧 AppID/旧域名,需清理后纳管 | 阻断,原包暂不提交 | +| `参考/db_20260427.sql` | SQL | 5221326 | `B6D67B37FDA6B2C277A80A5D5FA7A7B54B3DE1049F0E3C16EBB1331CD6D8B8C7` | 用户提供 | 参考旧数据库表和字段 | 已初扫 | 含 INSERT 数据、文件 URL、日志和业务配置,需生成脱敏结构版 | 阻断,原 SQL 暂不提交 | +| `参考/easy-joy-life-main.zip` | zip | 13660811 | `FC2B53CC0301B636B5BEC1E8A13223ED00B0D2DF5F101DAE67AC64722B5052FD` | 用户提供 | 参考无人棋牌室业务闭环 | 已隔离解包初审 | 含 PEM、支付密钥、AppSecret、数据库密码和生产/测试域名 | 阻断,原包暂不提交 | +| `参考/env` | env 样例 | 1195 | `D8557B6314E39BF0E010087D39D253461CDD6185215488B400B13389C4476391` | 用户提供 | 参考旧运行包变量类别 | 不适用 | 已发现并脱敏 | 待提交 | +| `参考/mazongjian-server.xjar` | 旧后端运行包 | 194259651 | `E2403E24670CA9366BBB4CC2844E1979F3060C9ABEB0D76506BA0893677C17F0` | 用户提供 | 仅观察旧接口行为,不作为新系统依赖 | 不反编译 | 大文件,许可/体积风险 | 大文件阻断,暂不提交;已记录哈希 | +| `参考/start.sh` | shell | 1265 | `31C39BD7AE3994BB9B6F63AAB2CFF77ED9D7800ABF37D276556A28B8D9E2FCD8` | 用户提供 | 参考旧服务启动方式 | 不适用 | 需确认无秘密 | 待提交 | +| `参考/stop.sh` | shell | 771 | `5E218E887C43B5439C9BFCC216F34E15C03F3A0F6A16596BDEDD4D477D55BF94` | 用户提供 | 参考旧服务停止方式 | 不适用 | 需确认无秘密 | 待提交 | +| `参考/xjar` | 二进制工具 | 2150870 | `AC02279C343A008D6CC9FC4CC7983DEC1984775B4182F48AC628A20124784F66` | 用户提供 | 旧运行包相关工具,正式系统不依赖 | 不适用 | 二进制许可风险 | 待提交 | +| `参考/后台管理系统_20260427.zip` | zip | 9297096 | `9E5F09AFC38ADEB4FFAD724406F95D7F4E6494A8C3572486ED81C5C1E1FAF0F2` | 用户提供 | 参考旧后台静态/源码 | 待完成 | 未知,需解包扫描 | 待提交 | +| `参考/小程序源代码.zip` | zip | 11579802 | `6EC18BE0C5E0602C75E114A12772F13E96BBAFCE6817ECE3FE0BF84D93B33A25` | 用户提供 | 参考旧微信小程序页面和接口 | 已隔离解包初审 | 含 `node_modules`/`miniprogram_npm`、旧 AppID、旧域名和 localhost 示例 | 阻断,原包暂不提交 | + +## 当前结论 + +- 未发现生效的嵌套 `.git` 目录。 +- `参考/env` 已脱敏,真实值不进入仓库,当前哈希为脱敏后的文件哈希。 +- `24h_qipaishi-master(1).zip`、`小程序源代码.zip`、`easy-joy-life-main.zip` 和 `db_20260427.sql` 已完成隔离初审;原始文件暂不提交,后续只提交脱敏后的结构、源码或摘要。 +- 压缩包和 SQL 已完成首轮隔离审计并发现阻断项;已生成 `reference-page-map`、`db-schema-inventory` 和 `reference-api-inventory` 三份脱敏摘要。M00-A 仍需后续生成可迁移源码/SQL 后才能完全 DONE。 +- 大文件 `参考/mazongjian-server.xjar` 已记录哈希并加入忽略,待确认 Git LFS 或等价归档策略后再处理。 diff --git a/docs/reference-page-map.md b/docs/reference-page-map.md new file mode 100644 index 0000000..5002a52 --- /dev/null +++ b/docs/reference-page-map.md @@ -0,0 +1,53 @@ +# 参考小程序页面地图 + +> 来源为隔离临时目录解包初审结果。本文档只记录页面路径和业务用途,不记录旧 AppID、旧域名、密钥或真实数据。 + +## `小程序源代码.zip` / `24h_qipaishi-master(1).zip` + +两包页面结构高度一致,均为微信原生小程序,使用 Vant Weapp,并包含主包、`packageA` 管理分包和 `inventory` 库存分包。原包含 `node_modules` / `miniprogram_npm`、旧 AppID、旧 API 域名和 localhost 示例,已作为本地阻断项,不直接提交。 + +### 主包页面 + +| 分类 | 页面路径 | 可参考点 | +|---|---|---| +| 首页/门店 | `pages/index/index`, `pages/shop/shop`, `pages/location/location`, `pages/map/map`, `pages/tencentMap/tencentMap` | 门店展示、定位、地图选点和门店列表入口。 | +| 房间/门店选择 | `pages/doorList/doorList`, `pages/door/door`, `pages/doorSelect/doorSelect`, `pages/doorDetail/doorDetail` | 房间列表、房间详情、选择房间和门店房间联动。 | +| 下单/订单 | `pages/placeOrder/placeOrder`, `pages/orderSubmit/orderSubmit`, `pages/orderList/orderList`, `pages/orderDetail/orderDetail`, `pages/searchOrder/index` | 创建订单、订单列表、订单详情、订单搜索。 | +| 支付/续费/余额 | `pages/pay/pay`, `pages/roomRenew/roomRenew`, `pages/recharge/recharge`, `pages/myBalance/myBalance`, `pages/getBalance/getBalance`, `pages/yeepay/index`, `pages/yeepay/agreement` | 支付、续费、充值、余额和第三方支付入口。 | +| 团购/优惠 | `pages/tuangou/tuangou`, `pages/coupon/coupon`, `pages/couponActive/index`, `pages/coupon/lottery` | 团购券、优惠券、活动和抽奖入口。 | +| 用户/登录 | `pages/login/login`, `pages/user/user`, `pages/setUserInfo/setUserInfo`, `pages/setUserName/setUserName`, `pages/setUserPhone/setUserPhone`, `pages/join/join` | 登录、用户资料、手机号和加盟入口。 | +| 其他业务 | `pages/productOrder/productOrder`, `pages/productOrderInfo/productOrderInfo`, `pages/inventory/index`, `pages/ktv/index`, `pages/help/help`, `pages/booking/booking`, `pages/changeDoor/changeDoor`, `pages/doorSubmit/doorSubmit` | 商品订单、库存入口、KTV、帮助、预约、换房和开门提交。 | + +### `packageA` 管理分包 + +| 分类 | 页面路径 | 可参考点 | +|---|---|---| +| 管理首页 | `pages/admin/admin`, `pages/statics/statics`, `pages/guide/guide` | 管理员首页、统计和引导页。 | +| 门店/房间 | `pages/doorManage/doorManage`, `pages/doorPosition/doorPosition`, `pages/setStore/setStore`, `pages/setStoreInfo/setStoreInfo`, `pages/setDoorInfo/setDoorInfo`, `pages/setDoorList/setDoorList`, `pages/roomList/roomList` | 门店、房间、定位和房间列表管理。 | +| 订单/支付 | `pages/SetOrder/SetOrder`, `pages/payOrder/index`, `pages/configPrePay/configPrePay`, `pages/ydCancel/index` | 管理端订单、支付订单、预付配置和易达取消。 | +| 设备/门锁 | `pages/deviceList/deviceList`, `pages/addLock/addLock`, `pages/bleList/bleList`, `pages/configGateWay/index`, `pages/configLockWifi/index` | 设备列表、门锁绑定、蓝牙/网关/Wi-Fi 配置。 | +| 保洁任务 | `pages/cleaner/cleaner`, `pages/task/task`, `pages/taskDetail/taskDetail`, `pages/taskManager/taskManager`, `pages/taskSettle/taskSettle`, `pages/taskStatics/taskStatics` | 保洁角色、任务详情、任务管理、结算和统计。 | +| 会员/营销 | `pages/setVip/setVip`, `pages/vipDetail/vipDetail`, `pages/vipConfig/vipConfig`, `pages/vipList/vipList`, `pages/vipBlacklist/vipBlacklist`, `pages/setCoupon/setCoupon`, `pages/setCouponInfo/setCouponInfo`, `pages/setDiscount/setDiscount`, `pages/packageManagement/packageManagement`, `pages/lottery/index` | 会员、黑名单、优惠券、折扣、套餐和抽奖。 | +| 商品/库存 | `pages/productManage/productManage`, `pages/addProduct/addProduct`, `pages/goodsKindManage/goodsKindManage` | 商品和分类管理。 | +| 外部平台/消息 | `pages/meituanreserve/meituanreserve`, `pages/pushrule/pushrule`, `pages/noticeManage/noticeManage`, `pages/setTemplate/index`, `pages/setStoreSound/setStoreSound` | 美团预订、推送规则、通知、模板和门店声音配置。 | +| 其他 | `pages/editPages/editPages`, `pages/faceRecord/faceRecord`, `pages/faceBlacklist/faceBlacklist`, `pages/scanQr/scanQr` | 装修、门禁人脸、扫码和黑名单。 | + +### `inventory` 库存分包 + +| 页面路径 | 可参考点 | +|---|---| +| `pages/goods/index` | 库存商品类型维护。 | +| `pages/detail/index` | 门店库存详情、入库、出库和盘点。 | + +## `easy-joy-life-main.zip` 小程序 + +微信原生小程序,页面更偏 MVP 预约闭环,可参考但不直接复用配置。 + +| 分类 | 页面路径 | 可参考点 | +|---|---|---| +| 门店/房间 | `pages/index/index`, `pages/nearby-stores/nearby-stores`, `pages/store-detail/store-detail`, `pages/select-room/select-room`, `pages/room-schedule/room-schedule`, `pages/room-detail/room-detail` | 附近门店、门店详情、房间选择、房态时间表。 | +| 预约/订单/开门 | `pages/booking/booking`, `pages/orders/orders`, `pages/order-detail/order-detail`, `pages/unlock/unlock` | 预约下单、订单列表、订单详情和开门入口。 | +| 用户/钱包 | `pages/profile/profile`, `pages/login/login`, `pages/user-profile/user-profile`, `pages/wallet/wallet`, `pages/recharge/recharge`, `pages/transaction-records/transaction-records` | 用户资料、钱包、充值和交易明细。 | +| 支付扩展 | `pages/bank-card/bank-card`, `pages/add-bank-card/add-bank-card`, `pages/pay-password/pay-password`, `pages/payment-webview/payment-webview`, `pages/cashier-pay-bridge/index` | 银行卡、支付密码、WebView 支付桥。 | +| 支持/消息 | `pages/messages/messages`, `pages/message-detail/message-detail`, `pages/help-center/help-center`, `pages/help-detail/help-detail`, `pages/customer-service/customer-service`, `pages/about/about`, `pages/settings/settings`, `pages/favorites/favorites` | 消息、帮助、客服、关于和收藏。 | + diff --git a/docs/reference-redaction-log.md b/docs/reference-redaction-log.md new file mode 100644 index 0000000..f677b85 --- /dev/null +++ b/docs/reference-redaction-log.md @@ -0,0 +1,8 @@ +# 参考资料脱敏记录 + +| 时间 | 文件 | 字段类别 | 处理方式 | 备注 | +|---|---|---|---|---| +| 2026-06-15 | `参考/env` | MySQL 密码、Redis 密码、微信支付商户密钥/证书路径类别 | 替换为占位符;不保留真实值 | 旧运行包环境样例仅保留变量名和用途。 | +| 2026-06-15 | `参考/easy-joy-life-main.zip` | PEM 证书、微信支付密钥、AppSecret、数据库密码、支付宝私钥、第三方平台密钥 | 原包从 Git 跟踪中移出并加入忽略;仅保留哈希和字段类别 | 不在文档记录真实值;后续如需复用,必须重新生成脱敏摘录。 | +| 2026-06-15 | `参考/db_20260427.sql` | 旧业务数据、文件 URL、任务日志、配置数据 | 原 SQL 从 Git 跟踪中移出并加入忽略;仅保留哈希和用途 | 后续生成 schema-only 或脱敏迁移草案。 | +| 2026-06-15 | `参考/24h_qipaishi-master(1).zip`、`参考/小程序源代码.zip` | 旧 AppID、旧 API 域名、依赖目录 | 原包从 Git 跟踪中移出并加入忽略;仅保留哈希和页面/接口摘要 | 后续迁入正式小程序时重建依赖和配置。 | diff --git a/docs/release-manifest.md b/docs/release-manifest.md new file mode 100644 index 0000000..040a726 --- /dev/null +++ b/docs/release-manifest.md @@ -0,0 +1,12 @@ +# 发布清单 + +| 项目 | 当前值 | +|---|---| +| 最近发布 commit | 未发布;本地 M00 基线提交为本地 HEAD | +| 发布分支 | main | +| 后端构建 | 未执行 | +| 后台构建 | 未执行 | +| 小程序检查 | 未执行 | +| 数据库迁移 | 未执行 | +| 部署结果 | 未部署 | +| 回滚点 | - | diff --git a/docs/repository-completeness.md b/docs/repository-completeness.md new file mode 100644 index 0000000..cc265ba --- /dev/null +++ b/docs/repository-completeness.md @@ -0,0 +1,13 @@ +# 仓库完整性 + +| 检查项 | 当前结果 | 证据/备注 | +|---|---|---| +| Git 根目录 | PASS | 已在 `D:\qipai` 初始化。 | +| 固定远端 | PASS | `origin=ssh://git@git.txyundm.cn:2222/panda/qipai.git`。 | +| 当前分支 | PASS | `main`。 | +| 总纲文件 | PASS | 根目录存在 `V4.8.md`。 | +| 参考资料清单 | PARTIAL | 已记录外层文件、哈希和风险;4 个压缩包已隔离解包初审,仍需生成脱敏可提交摘录。 | +| 敏感信息 | PASS/PARTIAL | 明文敏感模式扫描已通过;`参考/env` 已脱敏;含秘密/真实数据风险的原始包和 SQL 已移出 Git 跟踪。 | +| 大文件 | WARN | `参考/mazongjian-server.xjar` 约 194MB,已记录哈希并作为大文件阻断项忽略。 | +| 行尾/空白 | PASS | `scripts/dev/windows/test-all.ps1` 已运行,`git diff --check` 通过。 | +| 远端一致性 | BLOCKED_INTERNAL | `git.txyundm.cn:2222` 主机指纹未确认且连接不稳定;待人工确认主机指纹并成功 push 后校验。 | diff --git a/docs/repository-map.md b/docs/repository-map.md new file mode 100644 index 0000000..46679e7 --- /dev/null +++ b/docs/repository-map.md @@ -0,0 +1,23 @@ +# 仓库地图 + +| 路径 | 用途 | 提交策略 | +|---|---|---| +| `V4.8.md` | 当前开发总纲 | 必须提交 | +| `backend/` | 后端 API | 必须提交源码、测试和配置样例 | +| `admin/` | 后台管理端 | 必须提交源码,不提交构建产物 | +| `miniapp/` | 微信原生小程序 | 必须提交源码,不提交本地缓存 | +| `database/` | 数据库迁移和种子 | 必须提交脱敏 SQL | +| `deploy/` | 部署版本和说明 | 必须提交 | +| `scripts/` | 检查、部署和运维脚本 | 必须提交 | +| `docs/` | 状态、日志、变更和验收记录 | 必须提交 | +| `docs/reference-page-map.md` | 旧小程序页面脱敏地图 | 必须提交 | +| `docs/db-schema-inventory.md` | 旧 SQL 表结构脱敏清单 | 必须提交 | +| `docs/reference-api-inventory.md` | 旧接口路径类别和迁移方向 | 必须提交 | +| `参考/` | 只读参考资料 | 经脱敏审计后提交;大文件按清单处理 | + +## 边界 + +- 单一 Git 根:`D:\qipai` +- 固定远端:`ssh://git@git.txyundm.cn:2222/panda/qipai.git` +- 生产目录:`/opt/apps` +- 默认分支:`main` diff --git a/docs/source-inventory.md b/docs/source-inventory.md new file mode 100644 index 0000000..0724bd2 --- /dev/null +++ b/docs/source-inventory.md @@ -0,0 +1,19 @@ +# 源码参考盘点 + +| 参考项 | 技术栈/形态 | 页面/接口/表线索 | 可复用点 | 不可复用点 | 风险 | 当前处理 | +|---|---|---|---|---|---|---| +| `参考/小程序源代码.zip` | 微信原生小程序压缩包,Vant Weapp + 分包 | 约 40 个主包页面、2 个分包;覆盖首页、门店、房间、下单、支付、续费、团购、保洁、设备、库存、后台管理入口 | 页面路径、请求封装、订单/续费/保洁/设备页面流程 | 原包含 `node_modules`、旧 AppID、旧域名、localhost 示例,不直接提交/照搬 | 依赖包和旧配置风险 | 原包阻断;后续迁入需重建依赖并改为 `https://api.txyundm.cn/app-api` | +| `参考/后台管理系统_20260427.zip` | Vue/Element 类后台静态构建产物 | `index.html`、`static/js`、`static/css`、字体和 gzip 资源 | 菜单、字典、接口命名、后台字段参考 | 构建产物不能作为正式后台源码;旧 API 前缀需重建 | 旧域名/静态配置风险 | 可作 UI/接口字段参考,正式后台仍用 Vue3 源码重建 | +| `参考/db_20260427.sql` | MySQL SQL 导出 | 含配置、文件、任务、会员、门店、折扣、订单等表和大量 `INSERT` | 表结构、枚举、业务字段和历史状态机参考 | 原始数据、URL、日志、业务记录不得直接提交或导入 | 真实数据/旧域名/日志风险 | 原 SQL 阻断;后续生成脱敏 schema-only 或迁移草案 | +| `参考/24h_qipaishi-master(1).zip` | 微信原生小程序压缩包,Vant Weapp + 分包 | 与 `小程序源代码.zip` 高度相似,含门店、房间、订单、充值、团购、KTV、保洁、设备等页面 | 小程序业务页面和交互流程 | 原包含依赖目录、旧 AppID、旧域名,不能直接纳管 | 依赖包和旧配置风险 | 原包阻断;只作为页面清单参考 | +| `参考/easy-joy-life-main.zip` | Spring Boot 2.7 Java 后端 + 微信小程序 + SQL + 部署文档 | 后端约 140 个 Java 文件;含 stores、rooms、orders、payments、devices、admin、wallet 等 SQL 表 | 订单、钱包、设备、后台权限、支付和短信模块设计参考 | 技术路线不符合 V4.8;含 PEM、支付密钥、AppSecret、DB 密码等,不能提交原包 | 真实秘密和证书风险 | 原包阻断;只记录抽象设计,不复用密钥/证书/配置 | +| `参考/mazongjian-server.xjar` | 旧 Java 运行包 | 不反编译 | 仅用于观察旧接口行为 | 不作为运行依赖,不作为新系统源码 | 大文件和受保护代码风险 | 记录哈希,必要时只保留外部归档说明 | +| `参考/start.sh` / `参考/stop.sh` | 旧 shell 脚本 | 启停旧服务 | 了解旧进程和端口 | 不用于生产部署 | 旧路径/旧密钥风险 | 后续抽取安全变量类别 | + +## 已生成的脱敏摘要 + +| 文档 | 内容 | 后续使用 | +|---|---|---| +| `docs/reference-page-map.md` | 旧小程序页面、分包和业务分类 | M08 小程序迁移和页面验收清单。 | +| `docs/db-schema-inventory.md` | 旧 SQL 表分组、MVP 表线索和迁移原则 | M01/M03/M04/M05 数据库建模。 | +| `docs/reference-api-inventory.md` | 旧接口路径类别、正式 API 迁移方向和禁止事项 | M01 API 设计、M08 小程序请求迁移、M09 后台 API 对齐。 | diff --git a/docs/unresolved-issues.md b/docs/unresolved-issues.md new file mode 100644 index 0000000..3bb0ace --- /dev/null +++ b/docs/unresolved-issues.md @@ -0,0 +1,8 @@ +# 未解决问题 + +| ID | 发现时间 | 模块/功能 | 问题 | 影响 | 临时处理 | 根因 | 下一步 | 状态 | +|---|---|---|---|---|---|---|---|---| +| ISSUE-001 | 2026-06-15 | M00/REF-001 | `参考/mazongjian-server.xjar` 约 194MB,可能超过普通 Git/Gitea 单文件建议限制。 | 中 | 记录哈希与用途;首次提交前确认远端是否接受,必要时改用等价脱敏说明或 LFS 策略。 | 旧运行包体积较大。 | 推送失败时保留提交并记录处理方案。 | OPEN | +| ISSUE-002 | 2026-06-15 | M00/REF-001 | 参考压缩包已完成隔离初审,但尚未生成可迁移源码摘录。 | 中 | 已生成页面、接口和表结构脱敏摘要。 | 原包含依赖目录、旧域名或敏感配置。 | 后续按模块生成正式小程序/后端/后台代码,不直接复用原包。 | OPEN | +| ISSUE-003 | 2026-06-15 | M00/SCM-001 | `git.txyundm.cn:2222` SSH 无法完成主机指纹确认和 push。 | 高 | 保留本地提交并记录重试命令;不自动覆盖 known_hosts。 | 当前远端主机指纹未能安全确认,连接测试仍不稳定。 | 人工确认 Gitea SSH 主机指纹后,重试 `git fetch --prune origin && git push origin main && git fetch origin main`。 | OPEN | +| ISSUE-004 | 2026-06-15 | M00/REF-001 | 多个原始参考包或 SQL 含秘密、依赖目录或旧业务数据,不能直接提交。 | 高 | 原包/原 SQL 移出 Git 跟踪并加入忽略;已生成脱敏页面、接口和表结构摘要。 | 参考资料未预先脱敏。 | 后续仅提交重新实现的正式源码、schema-only 迁移或脱敏摘录。 | OPEN | diff --git a/docs/workspace-status.md b/docs/workspace-status.md new file mode 100644 index 0000000..e5baf51 --- /dev/null +++ b/docs/workspace-status.md @@ -0,0 +1,12 @@ +# 工作区状态 + +| 项目 | 当前值 | +|---|---| +| Windows 工作区 | `D:\qipai` | +| WSL 映射 | `/mnt/d/qipai` | +| 当前总纲 | `V4.8.md` | +| Git 根 | `D:\qipai`(本轮初始化) | +| 固定远端 | `ssh://git@git.txyundm.cn:2222/panda/qipai.git` | +| 默认分支 | `main` | +| 最近检查时间 | 2026-06-15 | +| 当前阻塞 | `git.txyundm.cn:2222` 主机指纹未确认/SSH 连接不稳定;首次 push 待完成 | diff --git a/miniapp/.gitkeep b/miniapp/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/miniapp/.gitkeep @@ -0,0 +1 @@ + diff --git a/scripts/dev/windows/check-gitea-ssh.ps1 b/scripts/dev/windows/check-gitea-ssh.ps1 new file mode 100644 index 0000000..ed184bd --- /dev/null +++ b/scripts/dev/windows/check-gitea-ssh.ps1 @@ -0,0 +1,18 @@ +$ErrorActionPreference = "Stop" + +$hostName = "git.txyundm.cn" +$port = "2222" +$knownHosts = Join-Path $env:USERPROFILE ".ssh\known_hosts" + +Write-Host "Checking known_hosts entries for $hostName port $port" +& ssh-keygen -F "[$hostName]:$port" +if ($LASTEXITCODE -ne 0) { + Write-Host "WARN: no known_hosts entry for [$hostName]:$port" +} + +Write-Host "Attempting read-only SSH handshake." +& ssh -T -p $port "git@$hostName" +if ($LASTEXITCODE -ne 0) { + throw "Gitea SSH handshake failed. Confirm host fingerprint before editing $knownHosts." +} + diff --git a/scripts/dev/windows/check-large-files.ps1 b/scripts/dev/windows/check-large-files.ps1 new file mode 100644 index 0000000..0d2ac42 --- /dev/null +++ b/scripts/dev/windows/check-large-files.ps1 @@ -0,0 +1,16 @@ +$ErrorActionPreference = "Stop" + +$limitBytes = 100MB +$large = Get-ChildItem -Recurse -File | Where-Object { + $_.FullName -notmatch "\\.git\\" -and $_.Length -gt $limitBytes +} + +if ($large) { + $large | ForEach-Object { + Write-Host ("LARGE-FILE: {0} {1:N0} bytes" -f $_.FullName, $_.Length) + } + Write-Host "WARN: files over 100MB found; document the handling strategy." +} +else { + Write-Host "PASS: no files over 100MB found." +} diff --git a/scripts/dev/windows/check-line-endings.ps1 b/scripts/dev/windows/check-line-endings.ps1 new file mode 100644 index 0000000..6a7439a --- /dev/null +++ b/scripts/dev/windows/check-line-endings.ps1 @@ -0,0 +1,8 @@ +$ErrorActionPreference = "Stop" + +& git diff --check +if ($LASTEXITCODE -ne 0) { + throw "git diff --check failed." +} + +Write-Host "PASS: whitespace and line ending check passed." diff --git a/scripts/dev/windows/check-reference.ps1 b/scripts/dev/windows/check-reference.ps1 new file mode 100644 index 0000000..a3d85d8 --- /dev/null +++ b/scripts/dev/windows/check-reference.ps1 @@ -0,0 +1,58 @@ +$ErrorActionPreference = "Stop" + +$referenceDirName = ([char]0x53C2).ToString() + ([char]0x8003).ToString() +$referenceRoot = Join-Path (Resolve-Path ".").Path $referenceDirName +if (-not (Test-Path $referenceRoot)) { + throw "Missing reference directory: $referenceRoot" +} + +$nestedGit = Get-ChildItem $referenceRoot -Recurse -Force -Directory -Filter ".git" -ErrorAction SilentlyContinue +if ($nestedGit) { + $nestedGit | ForEach-Object { Write-Host "NESTED-GIT: $($_.FullName)" } + throw "Nested .git found under reference directory." +} + +$blockedRawReferences = @( + "$referenceDirName/mazongjian-server.xjar", + "$referenceDirName/easy-joy-life-main.zip", + "$referenceDirName/24h_qipaishi-master(1).zip", + "$referenceDirName/$(([char]0x5C0F).ToString() + ([char]0x7A0B).ToString() + ([char]0x5E8F).ToString() + ([char]0x6E90).ToString() + ([char]0x4EE3).ToString() + ([char]0x7801).ToString()).zip", + "$referenceDirName/db_20260427.sql" +) + +$tracked = @(& git -c core.quotePath=false ls-files) +foreach ($blocked in $blockedRawReferences) { + if ($tracked -contains $blocked) { + throw "Blocked raw reference is tracked: $blocked" + } +} + +Get-ChildItem $referenceRoot -Recurse -File | ForEach-Object { + $hash = Get-FileHash -Algorithm SHA256 $_.FullName + "{0} {1} {2}" -f $hash.Hash, $_.Length, $_.FullName +} + +$zipFiles = Get-ChildItem $referenceRoot -File -Filter "*.zip" -ErrorAction SilentlyContinue +foreach ($zip in $zipFiles) { + $safeName = [IO.Path]::GetFileNameWithoutExtension($zip.Name) + $extractRoot = Join-Path $env:TEMP "qipai-reference-check" + $extractDir = Join-Path $extractRoot $safeName + if (Test-Path $extractDir) { + Remove-Item -LiteralPath $extractDir -Recurse -Force + } + New-Item -ItemType Directory -Force $extractDir | Out-Null + try { + Expand-Archive -LiteralPath $zip.FullName -DestinationPath $extractDir -Force + $fileCount = (Get-ChildItem $extractDir -Recurse -File | Measure-Object).Count + $dependencyDirs = Get-ChildItem $extractDir -Recurse -Force -Directory -Include "node_modules","miniprogram_npm" -ErrorAction SilentlyContinue + $secretFiles = Get-ChildItem $extractDir -Recurse -File -Include "*.pem","*.key","*.p12","*.crt","application*.yml","application*.properties" -ErrorAction SilentlyContinue + Write-Host "ZIP-AUDIT: $($zip.Name) files=$fileCount dependencyDirs=$($dependencyDirs.Count) sensitiveConfigFiles=$($secretFiles.Count)" + } + finally { + if (Test-Path $extractDir) { + Remove-Item -LiteralPath $extractDir -Recurse -Force + } + } +} + +Write-Host "PASS: reference directory basic scan completed." diff --git a/scripts/dev/windows/check-repo-completeness.ps1 b/scripts/dev/windows/check-repo-completeness.ps1 new file mode 100644 index 0000000..930f5a3 --- /dev/null +++ b/scripts/dev/windows/check-repo-completeness.ps1 @@ -0,0 +1,16 @@ +$ErrorActionPreference = "Stop" + +& powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-workspace.ps1 +& powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-line-endings.ps1 +& powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-large-files.ps1 + +$status = & git status --short --untracked-files=all +if ($status) { + Write-Host "Current git status:" + $status | ForEach-Object { Write-Host $_ } +} +else { + Write-Host "PASS: worktree has no pending changes." +} + +Write-Host "PASS: repository completeness baseline check finished; review pending changes before commit." diff --git a/scripts/dev/windows/check-secrets.ps1 b/scripts/dev/windows/check-secrets.ps1 new file mode 100644 index 0000000..d286b5b --- /dev/null +++ b/scripts/dev/windows/check-secrets.ps1 @@ -0,0 +1,48 @@ +$ErrorActionPreference = "Stop" + +$patterns = @( + "DB_PASSWORD\s*=\s*(?!<)", + "JWT_SECRET\s*=\s*(?!<)", + "WECHAT_SECRET\s*=\s*(?!<)", + "PRIVATE_KEY", + "api_key\s*[:=]\s*(?!<)", + "password\s*[:=]\s*(?!<)" +) + +$excluded = @("\.git\", "node_modules", "dist", "build") +$excludedFiles = @( + "V4.8.md", + "scripts\dev\windows\check-secrets.ps1" +) +$hits = @() + +$candidateFiles = @() +(& git -c core.quotePath=false ls-files --cached --others --exclude-standard) | ForEach-Object { + if ($_ -and (Test-Path $_ -PathType Leaf)) { + $candidateFiles += (Resolve-Path $_).Path + } +} + +$candidateFiles | ForEach-Object { + $path = $_ + foreach ($skip in $excluded) { + if ($path -match [regex]::Escape($skip)) { return } + } + $relativePath = Resolve-Path -Relative $path + $relativePath = $relativePath.TrimStart('.', '\', '/') + if ($excludedFiles -contains $relativePath) { return } + $text = Get-Content -Raw -ErrorAction SilentlyContinue $path + foreach ($pattern in $patterns) { + if ($text -match $pattern) { + $hits += $path + break + } + } +} + +if ($hits.Count -gt 0) { + $hits | Sort-Object -Unique | ForEach-Object { Write-Host "SECRET-CHECK-HIT: $_" } + throw "Potential secrets found. Please review and redact." +} + +Write-Host "PASS: no plaintext secret pattern found." diff --git a/scripts/dev/windows/check-workspace.ps1 b/scripts/dev/windows/check-workspace.ps1 new file mode 100644 index 0000000..2abac7f --- /dev/null +++ b/scripts/dev/windows/check-workspace.ps1 @@ -0,0 +1,24 @@ +$ErrorActionPreference = "Stop" + +$expectedRoot = "D:\qipai" +$current = (Resolve-Path ".").Path +if ($current -ne $expectedRoot) { + throw "Workspace must be $expectedRoot, actual: $current" +} + +$gitRoot = (& git rev-parse --show-toplevel) -replace '/', '\' +if ($gitRoot -ne $expectedRoot) { + throw "Git root must be $expectedRoot, actual: $gitRoot" +} + +$branch = & git branch --show-current +if ($branch -ne "main") { + throw "Branch must be main, actual: $branch" +} + +$origin = & git remote get-url origin +if ($origin -ne "ssh://git@git.txyundm.cn:2222/panda/qipai.git") { + throw "Invalid origin: $origin" +} + +Write-Host "PASS: workspace, git root, branch and origin are valid." diff --git a/scripts/dev/windows/push-module.ps1 b/scripts/dev/windows/push-module.ps1 new file mode 100644 index 0000000..cfcc37e --- /dev/null +++ b/scripts/dev/windows/push-module.ps1 @@ -0,0 +1,85 @@ +param( + [Parameter(Mandatory = $true)] + [string]$Message, + + [string[]]$Paths = @(), + + [switch]$Amend +) + +$ErrorActionPreference = "Stop" + +function Run-Step { + param( + [string]$Name, + [scriptblock]$Block + ) + Write-Host "== $Name ==" + & $Block +} + +Run-Step "workspace check" { + powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-workspace.ps1 +} + +Run-Step "local tests" { + powershell -ExecutionPolicy Bypass -File scripts/dev/windows/test-all.ps1 + powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-secrets.ps1 +} + +Run-Step "git preflight" { + $branch = & git branch --show-current + if ($branch -ne "main") { + throw "Branch must be main, actual: $branch" + } + + $origin = & git remote get-url origin + if ($origin -ne "ssh://git@git.txyundm.cn:2222/panda/qipai.git") { + throw "Invalid origin: $origin" + } + + & git status --short --branch --untracked-files=all +} + +Run-Step "stage files" { + if ($Paths.Count -gt 0) { + foreach ($path in $Paths) { + & git add -- $path + } + } + else { + Write-Host "No paths supplied. Review status and stage files manually, then rerun with -Paths." + throw "No paths supplied." + } + + & git diff --cached --stat + if (-not (& git diff --cached --name-only)) { + throw "No staged changes." + } +} + +Run-Step "commit" { + if ($Amend) { + & git commit --amend --no-edit + } + else { + & git commit -m $Message + } +} + +Run-Step "push" { + & git push origin main +} + +Run-Step "remote verify" { + & git fetch origin main + $head = & git rev-parse HEAD + $remote = & git rev-parse origin/main + if ($head -ne $remote) { + throw "Remote verification failed: HEAD=$head origin/main=$remote" + } + & git log -1 --oneline origin/main +} + +Write-Host "PASS: module pushed and verified." + diff --git a/scripts/dev/windows/test-all.ps1 b/scripts/dev/windows/test-all.ps1 new file mode 100644 index 0000000..fbb75d7 --- /dev/null +++ b/scripts/dev/windows/test-all.ps1 @@ -0,0 +1,14 @@ +$ErrorActionPreference = "Stop" + +& powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-workspace.ps1 +& powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-reference.ps1 +& powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-line-endings.ps1 +& powershell -ExecutionPolicy Bypass -File scripts/dev/windows/check-large-files.ps1 + +if (Test-Path "package.json") { + npm run lint --if-present + npm test --if-present + npm run build --if-present +} + +Write-Host "PASS: M00 local checks completed." diff --git a/scripts/dev/wsl/check-api-domain.sh b/scripts/dev/wsl/check-api-domain.sh new file mode 100644 index 0000000..78bb764 --- /dev/null +++ b/scripts/dev/wsl/check-api-domain.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +curl -fsS --max-time 10 https://api.txyundm.cn/app-api/health + diff --git a/scripts/dev/wsl/check-env.sh b/scripts/dev/wsl/check-env.sh new file mode 100644 index 0000000..3b5c389 --- /dev/null +++ b/scripts/dev/wsl/check-env.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "WSL env:" +uname -a +command -v bash >/dev/null +command -v git >/dev/null + diff --git a/scripts/dev/wsl/check-gitea-ssh.sh b/scripts/dev/wsl/check-gitea-ssh.sh new file mode 100644 index 0000000..082df70 --- /dev/null +++ b/scripts/dev/wsl/check-gitea-ssh.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +ssh -T -p 2222 git@git.txyundm.cn + diff --git a/scripts/dev/wsl/check-workspace.sh b/scripts/dev/wsl/check-workspace.sh new file mode 100644 index 0000000..ff617f2 --- /dev/null +++ b/scripts/dev/wsl/check-workspace.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd /mnt/d/qipai +test "$(git rev-parse --show-toplevel)" = "/mnt/d/qipai" +test "$(git branch --show-current)" = "main" +git status --short --branch --untracked-files=all + diff --git a/scripts/dev/wsl/cleanup-test-copy.sh b/scripts/dev/wsl/cleanup-test-copy.sh new file mode 100644 index 0000000..61cf589 --- /dev/null +++ b/scripts/dev/wsl/cleanup-test-copy.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +rm -rf "${HOME}/qipai-wsl-test" +echo "Cleaned ${HOME}/qipai-wsl-test" + diff --git a/scripts/dev/wsl/mqtt-smoke.sh b/scripts/dev/wsl/mqtt-smoke.sh new file mode 100644 index 0000000..596334e --- /dev/null +++ b/scripts/dev/wsl/mqtt-smoke.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "MQTT smoke test placeholder. Configure host/user/password outside Git before use." + diff --git a/scripts/dev/wsl/prepare-test-copy.sh b/scripts/dev/wsl/prepare-test-copy.sh new file mode 100644 index 0000000..b031592 --- /dev/null +++ b/scripts/dev/wsl/prepare-test-copy.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +src="/mnt/d/qipai" +dst="${HOME}/qipai-wsl-test" +rm -rf "${dst}" +mkdir -p "${dst}" +rsync -a --exclude .git --exclude node_modules --exclude dist --exclude build "${src}/" "${dst}/" +echo "Prepared ${dst}" + diff --git a/scripts/dev/wsl/verify-linux.sh b/scripts/dev/wsl/verify-linux.sh new file mode 100644 index 0000000..27e9528 --- /dev/null +++ b/scripts/dev/wsl/verify-linux.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd /mnt/d/qipai +bash -n setup.sh +for file in scripts/dev/wsl/*.sh; do + bash -n "$file" +done +echo "PASS: Linux shell syntax checks passed." + diff --git a/scripts/setup/README.md b/scripts/setup/README.md new file mode 100644 index 0000000..c5254f5 --- /dev/null +++ b/scripts/setup/README.md @@ -0,0 +1,4 @@ +# Ubuntu 菜单脚本目录 + +后续 M00-D/M00-E 将在此目录补充 `/opt/apps` 初始化、Gitea 拉取、业务部署、EMQX、Nginx、证书、备份、恢复、回滚和诊断脚本。 + diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..3badf72 --- /dev/null +++ b/setup.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +set -euo pipefail + +VERSION="0.0.1-m00-baseline" +APP_ROOT="/opt/apps" +REPO_SSH="ssh://git@127.0.0.1:2222/panda/qipai.git" +PUBLIC_API="https://api.txyundm.cn" + +print_header() { + echo "自助棋牌室系统部署菜单 ${VERSION}" + echo "生产目录: ${APP_ROOT}" + echo "固定 API: ${PUBLIC_API}" + echo +} + +check_arch() { + local kernel_arch dpkg_arch bits + kernel_arch="$(uname -m 2>/dev/null || echo unknown)" + dpkg_arch="$(dpkg --print-architecture 2>/dev/null || echo unknown)" + bits="$(getconf LONG_BIT 2>/dev/null || echo unknown)" + echo "内核架构: ${kernel_arch}" + echo "DPKG 架构: ${dpkg_arch}" + echo "用户空间位数: ${bits}" + if [ "${kernel_arch}" != "x86_64" ] || [ "${dpkg_arch}" != "amd64" ] || [ "${bits}" != "64" ]; then + echo "FAIL: 仅支持 Ubuntu 24.04 x86-64/amd64。" + exit 1 + fi +} + +quick_check() { + echo "== 启动快检 ==" + check_arch + command -v git >/dev/null 2>&1 && echo "Git: PASS" || echo "Git: WARN 未安装" + command -v nginx >/dev/null 2>&1 && echo "Nginx: PASS" || echo "Nginx: WARN 未安装" + command -v node >/dev/null 2>&1 && echo "Node.js: PASS" || echo "Node.js: WARN 未安装" + echo "仓库: ${REPO_SSH}" +} + +not_implemented() { + echo "当前 M00 基线仅提供菜单骨架;该选项将在后续子阶段完善。" +} + +main_menu() { + while true; do + print_header + echo "1. 首次安装" + echo "2. 更新业务服务" + echo "3. 安装或检查 EMQX" + echo "4. 配置域名与 HTTPS" + echo "5. 查看状态" + echo "6. 备份" + echo "7. 恢复" + echo "8. 回滚" + echo "9. 诊断" + echo "0. 退出" + printf "请选择: " + read -r choice + case "${choice}" in + 1|2|3|4|6|7|8) not_implemented ;; + 5|9) quick_check ;; + 0) exit 0 ;; + *) echo "无效选项" ;; + esac + echo + done +} + +main_menu "$@" + diff --git a/参考/env b/参考/env new file mode 100644 index 0000000..081dd18 --- /dev/null +++ b/参考/env @@ -0,0 +1,40 @@ +######### Mysql数据库的配置 ######### +# 数据库的IP地址 +M_MYSQL_HOST=127.0.0.1 +# 数据库的端口 Mysql默认是3306 +M_MYSQL_PORT=3306 +# 数据库的名称 +M_MYSQL_DATABASE=malaoban +# 数据库的用户名 +M_MYSQL_USERNAME=malaoban +# 数据库的密码 +M_MYSQL_PASSWORD= + +######### Redis数据库的配置 ######### +# 数据库的IP地址 +M_REDIS_HOST=127.0.0.1 +# 数据库的端口 Redis默认是6379 +M_REDIS_PORT=6379 +# 数据库的索引 写0~16范围内的数字 默认是0 +M_REDIS_INDEX=0 +# 数据库的密码 +M_REDIS_PASSWORD= + +#### 是否开启接口文档 #### 开启true 关闭flase +M_OPEN_API_DOC=false + +######### 微信支付服务商的配置 ######### +######### 注意:通常情况无需修改 ######### +#微信支付服务商的小程序APPID +M_WX_PAY_APPID= +#微信支付服务商的商户号 +M_WX_PAY_MCHID= +#微信支付服务商的v2密钥 +M_WX_PAY_MCHKEY= +#支付证书p12文件的路径 +M_WX_PAY_KEYPATH= +#接收分账的商户号 +M_WX_PAY_SPLITMCHID= +#接收分账的商户营业执照全称 +M_WX_PAY_SPLITMCHNAME= +####################################### diff --git a/参考/start.sh b/参考/start.sh new file mode 100644 index 0000000..9258780 --- /dev/null +++ b/参考/start.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# 第一步:检查进程是否存在 +check_process() { + local process_names=("xjar" "mazongjian-server.xjar" "mazongjian-server.jar") + for name in "${process_names[@]}"; do + if pgrep -f "$name" > /dev/null; then + echo "系统已在运行中,无法启动。请先执行 sh stop.sh" + exit 1 + fi + done +} + +# 第二步:加载 .env 文件(如果存在) +load_env() { + if [ -f ".env" ]; then + echo "加载环境变量文件: .env" + export $(grep -v '^#' .env | xargs) + else + echo "未找到 .env 文件,跳过环境变量加载" + fi +} + +# 第三步:启动服务 +start_service() { + if [ -f "mazongjian-server.xjar" ] && [ -f "xjar" ]; then + echo "检测到 xjar 和 mazongjian-server.xjar,使用 xjar 启动..." + nohup ./xjar java -jar mazongjian-server.xjar & + elif [ -f "mazongjian-server.jar" ]; then + echo "检测到 mazongjian-server.jar,直接启动..." + nohup java -jar mazongjian-server.jar & + else + echo "系统无法启动,文件不存在" + exit 1 + fi +} + +# 主流程 +check_process +load_env +start_service + +# 第四步:提示查看日志 +echo "请使用命令查看启动日志:tail -1000f nohup.out" diff --git a/参考/stop.sh b/参考/stop.sh new file mode 100644 index 0000000..ef0894e --- /dev/null +++ b/参考/stop.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# 安全停止函数(精确匹配进程名) +safe_kill() { + local process_names=("xjar" "mazongjian-server.xjar" "mazongjian-server.jar") + local found=0 + + for name in "${process_names[@]}"; do + # 使用数组来存储所有匹配的PID + pids=($(pgrep -f "$name")) + if [ ${#pids[@]} -gt 0 ]; then + found=1 + echo "停止进程: $name (PIDs: ${pids[@]})" + # 逐个杀死进程 + for pid in "${pids[@]}"; do + kill -9 "$pid" + done + fi + done + + if [ "$found" -eq 0 ]; then + echo "未找到运行中的服务进程,无需停止" + else + echo "所有服务进程已停止" + fi +} + +# 主流程 +safe_kill diff --git a/参考/xjar b/参考/xjar new file mode 100644 index 0000000..05927c8 Binary files /dev/null and b/参考/xjar differ diff --git a/参考/后台管理系统_20260427.zip b/参考/后台管理系统_20260427.zip new file mode 100644 index 0000000..cbf5e73 Binary files /dev/null and b/参考/后台管理系统_20260427.zip differ