# 变更审计记录:禁用用户改为移除用户 + 小程序鉴权两层模型修复 | 字段 | 值 | |------|-----| | 日期 | 2026-03-23 21:42:26 | | Prompt-ID | P20260323-190012 | | Session-ID | 01d3f04b | | Session 路径 | docs/audit/session_logs/2026-03/23/32_834c940b_190011 | ## 操作摘要 用户指出小程序鉴权模型混淆了两层:第一层微信身份(JWT)不应因业务状态 disabled 而拒绝;租户不应有全局禁用用户的权限,只能从店铺关系中移除。本次改动移除了 login/refresh 接口对 disabled 用户的 403 拦截,使 disabled 用户可正常获取受限令牌,由前端状态路由处理业务层逻辑。 ## 本次对话文件变更 ### 新增文件 - `docs/audit/prompt_logs/prompt_log_20260323_190012.md` - `docs/audit/session_logs/2026-03/23/31_d178bd1a_185322/main_01_6e26cf47.md` ### 删除文件 - `docs/audit/session_logs/2026-03/23/31_d178bd1a_185322/main_01_b213fe92.md`(session log 替换) ## 改动注解 ### `apps/backend/app/routers/xcx_auth.py` - 变更类型:修改 - 原始原因:用户被禁用后跳转登录页报 401/403 错误。根因是鉴权模型混淆了两层——第一层微信身份(JWT)不应因业务状态 disabled 而拒绝登录/刷新;租户管理后台的"禁用"操作直接改 auth.users.status='disabled' 是全局操作,不符合多租户隔离。 - 思路分析: 1. **login 接口**:移除 `if user_status == "disabled": raise HTTPException(403)` 拦截。disabled/new/pending/rejected 统一签发受限令牌,由前端状态路由处理。 2. **refresh 接口**:同样移除 disabled 用户的 403 拦截。第一层(微信身份)始终有效,disabled 只影响第二层(业务状态路由)。 3. **附带改动**:login 时补全 unionid 幂等更新逻辑(CHANGE 2026-03-22);login/dev-login 返回 role 字段;/me 接口查询当前门店角色并返回;新增 cancel-application 接口;dev-switch-role 角色列表更新为小程序端 4 角色(coach/staff/head_coach/manager);site_code_mapping 引用改为 biz.sites。 4. **trace 装饰器**:所有路由函数添加 `@trace_service` 装饰器用于调用链追踪。 - 修改结果:disabled 用户可正常登录并获取受限令牌,前端根据 user_status 路由到对应页面(申请页/审核中/被拒绝/被禁用提示)。租户管理后台后续将"禁用"改为"移除"(删 user_site_roles + 条件重置 status=new)。 ## 合规检查 - ✅ OpenAPI spec 已重新导出(137 paths, 194 schemas) - ⚠️ `apps/backend/docs/API-REFERENCE.md` 待同步更新 - 💡 请重连 OpenAPI Power 的 MCP server 以加载新 spec - ✅ 无新增迁移 SQL - ⚠️ DDL 基线待确认(`has_ddl_baseline: false`) ## 风险提示 - 已有 disabled 状态的用户需要手动处理(当前测试库 id=8778 已恢复为 approved) - 第三次拒绝自动禁用逻辑保持不变(系统级行为) - dev-switch-role 角色列表从 `(coach, staff, site_admin, tenant_admin)` 改为 `(coach, staff, head_coach, manager)`,需确认开发调试场景覆盖