# 架构决策:门店开通、一次性员工邀请与操作审计 > 日期:2026-08-02 > 状态:Accepted / Code Complete,待生产迁移与微信真机验收 > Owner:Backend Core + Store Miniapp FE + Store Admin FE > Review:Data Model / Architecture / Access & Privacy = Yes ## 1. 决策背景 旧流程把 `Store.invite_code` 当作永久共享凭证,公开员工注册只凭 8 位码和手填手机号即可创建 staff;老板注册也接受未核验手机号和明文密码。Admin/小程序的“直接创建员工”还生成无法安全交付的初始密码。该模型不满足真实试点的身份核验、撤销、有效期、审计和离职失效要求。 本批目标不是扩展组织/权限系统,而是让 3~5 家独立单店能自行完成开通,并让受邀员工本人安全加入。 ## 2. 已冻结决策 ### 2.1 门店开通 - 新老板只走 `POST /api/onboarding/register-boss`,服务端用微信 `phoneCode` 换取并核验手机号;不接受手填手机号或明文密码。 - 新门店状态为 `in_progress`;历史门店无法可靠判断是否已完成开通,迁移统一写 `unknown`。 - 开通清单四步:门店资料、预约容量、服务项目为必填;邀请员工为选填,支持单人门店。 - 只有 boss 可显式完成;完成回执保存 `completed_at/by` 并写 AuditLog。清单不阻断已有业务接口,避免历史门店升级即停摆。 ### 2.2 员工邀请 - legacy `invite_code` 仅保留数据库兼容,不返回 Admin/小程序,不再参与查询或注册。 - 老板创建 `StaffInvitation`:姓名、本人微信手机号、1~30 天有效期。 - token 使用 32 字节 `SecureRandom`(256-bit),原文只在创建响应返回一次;数据库仅保存 SHA-256。 - 邀请状态:`pending → accepted`,或 `pending → revoked/expired`;只能首次接受,已接受不能撤销。 - 预览只返回门店名、受邀姓名、脱敏手机号、状态和有效期。 - 接受时必须由微信换得的手机号与邀请绑定手机号精确匹配;成功后才创建 staff、消费邀请并签发 session。 - 已存在 User/openid/unionid 冲突均拒绝。本期 User 仍是单角色模型,不把 customer 静默升级为 staff;试点遇到冲突时使用独立工作手机号,后续如有真实频次再引入 Account + StoreMembership。 ### 2.3 会话与离职 HMAC 签名和过期校验之后,每次受保护请求继续读取活跃 User,校验账号未删除且 `role/storeId` 与 token 一致;boss/staff 还必须归属仍有效的 Store。删除员工、停用门店或权限范围变化后,旧 token 立即 401,无需等待 7 天到期。 ### 2.4 AuditLog 与 BusinessEvent - `BusinessEvent` 记录可统计经营事实;本批补齐 `store_registered`。 - `AuditLog` 记录门店开通/设置与员工权限操作,只追加、不更新/软删。 - Audit metadata 只允许服务端白名单标量,禁止手机号、token/URL、openid/unionid、密码/密钥、IP、地址/坐标、备注/内容和原始请求体。 ## 3. API 契约 | 方法 | 路径 | 权限 | 关键语义 | |------|------|------|----------| | POST | `/api/onboarding/register-boss` | public + 微信手机号核验 | 原子创建门店/老板,返回 session | | GET | `/api/admin/onboarding` | boss/staff + 本店 | 计算开通清单 | | POST | `/api/admin/onboarding/complete` | boss + 本店 | 必填项齐全后显式完成 | | POST | `/api/admin/staff-invitations` | boss + 本店 | 原始 token 仅本响应一次 | | GET | `/api/admin/staff-invitations` | boss + 本店 | 脱敏列表,无 token/hash | | DELETE | `/api/admin/staff-invitations` | boss + 本店 | 撤销 pending 邀请 | | GET | `/api/staff-invitations/preview` | public | 脱敏预览 | | POST | `/api/staff-invitations/accept` | public + 微信手机号核验 | 手机号匹配才创建 staff | 旧 `/api/user/register-boss`、`/api/user/register-staff`、`/api/user/create-staff`、`/api/store/register`、`/api/store/invite-code` 保留兼容入口但只返回明确 410 业务码,不产生写入。 ## 4. 数据迁移与不变量 迁移:`backend/db/migrations/20260802_create_store_onboarding.sql`,在前五个迁移之后第六个执行。 新增 6 项生产只读不变量,总数由 18 增至 24: 1. 门店开通状态枚举合法; 2. completed 必须有完成回执; 3. unknown/in_progress 不得带完成回执; 4. 邀请状态枚举合法; 5. accepted 与接受回执一致; 6. revoked 与撤销回执一致。 ## 5. 前端行为 - Admin 设置页顶部显示开通清单,员工页改为邀请中心;创建后必须立即复制,之后只能撤销并重建。 - 小程序老板可直接用 `open-type=share` 转发带受控 path 的邀请卡片。 - 员工从卡片进入可先匿名预览,再点击微信手机号授权接受。 - H5 允许预览,但不会提供绕过微信手机号核验的接受路径。 ## 6. 明确不做 - 不引入多门店组织、复杂 RBAC、StoreMembership、审批流或通用 IAM。 - 不接入短信生产登录,不恢复明文密码交付。 - 不调用微信 URL Link/小程序码后台 API;试点先使用小程序原生分享卡片,Admin 提供复制消息作为补充。 - 不自动执行生产迁移、不改生产数据。 ## 7. 回滚 应用回滚只切回前一 backend/Admin/H5/小程序版本;数据库新增列/表/索引保留,不执行 down migration,不覆盖已经产生的邀请、审计或员工账号。旧应用会忽略新增结构,但 legacy 注册仍应保持关闭,避免重新暴露永久凭证路径。