# 变更审计记录：小程序权限体系统一改造（W1-W5）

| 字段 | 值 |
|------|-----|
| 日期 | 2026-03-27 09:55:17 |
| Prompt-ID | P20260327-083929 |
| Session-ID | 0ecf5499 |
| Session 路径 | docs/audit/session_logs/2026-03/27/11_476da926_075522 |

## 操作摘要

小程序权限体系统一改造——后端为权限唯一真相源，前端根据权限码动态控制页面/tab/按钮可见性。涵盖 5 个工作项（W1-W5）：refresh_token 有效期延长、`/api/xcx/me` 返回 permissions 列表、角色-权限码映射修正、后端端点从 `require_approved` 迁移到 `require_permission("xxx")`、前端权限守卫重写为权限码驱动。

## 变更范围

- **W1 — Token 有效期**：`apps/backend/app/config.py` — refresh_token 有效期从 7 天延长至 30 天
- **W2 — 权限下发**：`apps/backend/app/schemas/xcx_auth.py` UserStatusResponse 新增 `permissions: list[str]` 字段；`apps/backend/app/routers/xcx_auth.py` `/api/xcx/me` 返回当前用户的 permissions 列表
- **W3 — 角色权限映射**：`db/zqyy_app/migrations/2026-03-27__fix_role_permissions.sql` 修正角色-权限码映射关系
- **W4 — 后端端点权限化**：
  - `xcx_tasks.py`：7 个端点从 `require_approved` → `require_permission("view_tasks")`
  - `xcx_performance.py`：2 个端点 → `require_permission("view_tasks")`
  - `xcx_customers.py`：2 个端点 → `require_permission("view_board_customer")`
  - `xcx_coaches.py`：1 个端点 → `require_permission("view_board_coach")`
- **W5 — 前端权限守卫重写**：
  - `auth-guard.ts`：重写为权限码驱动（不再基于角色硬编码）
  - `app.ts`：改用 `syncPermissions` 同步权限
  - `typings/index.d.ts`：globalData 新增 `permissions` 字段
  - 看板页面（board-finance/board-customer/board-coach）：`.ts` + `.wxml` 改为动态二级 tab，根据权限码控制可见性

## 风险与回滚

- 风险点：
  - 权限码不匹配：若数据库 `auth.role_permissions` 映射遗漏某权限码，对应角色用户将被 403 拒绝
  - 前后端权限一致性：前端 tab 可见性依赖 `permissions` 数组，后端 API 依赖 `require_permission`，两者必须使用相同的权限码字符串
  - refresh_token 延长至 30 天：已签发的旧 token（7 天有效期）不受影响，新 token 才生效
  - 迁移脚本依赖 `auth.roles` 和 `auth.permissions` 表已存在且包含正确的角色/权限记录
- 回滚要点：
  - 数据库：回滚 `2026-03-27__fix_role_permissions.sql` 中的映射变更
  - 后端：将 12 个端点的 `require_permission(...)` 改回 `require_approved`；移除 UserStatusResponse 的 `permissions` 字段；恢复 config.py 的 7 天有效期
  - 前端：恢复 `auth-guard.ts` 为角色驱动版本；恢复 `app.ts` 的 `syncVisibleTabs`；恢复看板页面的静态 tab 逻辑

## 改动注解

### `apps/backend/app/config.py`
- 变更类型：修改
- 原始原因：W1 要求延长 refresh_token 有效期，减少用户频繁重新登录
- 思路分析：将 `REFRESH_TOKEN_EXPIRE_DAYS` 从 7 改为 30，属于配置级变更，不影响 token 签发/验证逻辑
- 修改结果：新签发的 refresh_token 有效期 30 天，已签发的旧 token 不受影响

### `apps/backend/app/schemas/xcx_auth.py`
- 变更类型：修改
- 原始原因：W2 需要在 `/api/xcx/me` 响应中携带权限码列表，供前端做动态可见性控制
- 思路分析：在 `UserStatusResponse` Pydantic model 中新增 `permissions: list[str]` 字段，默认空列表
- 修改结果：前端可通过 `me.permissions` 获取当前用户的全部权限码

### `apps/backend/app/routers/xcx_auth.py`
- 变更类型：修改
- 原始原因：W2 配合 schema 变更，在 `/api/xcx/me` 路由中查询并填充 permissions
- 思路分析：从 `auth.role_permissions` + `auth.permissions` 表 JOIN 查询当前用户角色对应的权限码列表
- 修改结果：approved 用户调用 `/api/xcx/me` 时，响应中包含完整的 permissions 数组

### `apps/backend/app/routers/xcx_tasks.py`
- 变更类型：修改
- 原始原因：W4 将端点级鉴权从"已审核即可访问"升级为"需持有特定权限码"
- 思路分析：7 个端点的依赖注入从 `require_approved` 替换为 `require_permission("view_tasks")`，实现细粒度权限控制
- 修改结果：仅持有 `view_tasks` 权限码的角色可访问任务相关端点

### `apps/backend/app/routers/xcx_performance.py`
- 变更类型：修改
- 原始原因：W4 绩效端点同样需要权限码控制
- 思路分析：2 个端点改为 `require_permission("view_tasks")`，与任务模块共享同一权限码（绩效是任务的子功能）
- 修改结果：绩效端点与任务端点权限一致

### `apps/backend/app/routers/xcx_customers.py`
- 变更类型：修改
- 原始原因：W4 客户看板端点需要独立权限码
- 思路分析：2 个端点改为 `require_permission("view_board_customer")`
- 修改结果：仅持有 `view_board_customer` 权限码的角色可访问客户看板

### `apps/backend/app/routers/xcx_coaches.py`
- 变更类型：修改
- 原始原因：W4 助教看板端点需要独立权限码
- 思路分析：1 个端点改为 `require_permission("view_board_coach")`
- 修改结果：仅持有 `view_board_coach` 权限码的角色可访问助教看板

### `db/zqyy_app/migrations/2026-03-27__fix_role_permissions.sql`
- 变更类型：新增
- 原始原因：W3 需要修正角色-权限码映射，确保各角色拥有正确的权限集合
- 思路分析：迁移脚本操作 `auth.role_permissions` 表，建立角色与权限码的多对多映射
- 修改结果：数据库中角色权限映射与 W4/W5 的权限码使用保持一致

### `apps/miniprogram/miniprogram/utils/auth-guard.ts`
- 变更类型：修改
- 原始原因：W5 将前端权限守卫从角色硬编码改为权限码驱动
- 思路分析：重写核心逻辑——不再维护 `PAGE_ROLES` 角色→页面映射，改为从 `globalData.permissions` 读取权限码，页面/tab 可见性由权限码决定
- 修改结果：前端权限判断与后端权限码完全对齐，新增权限码时只需更新数据库映射，无需改前端代码

### `apps/miniprogram/miniprogram/app.ts`
- 变更类型：修改
- 原始原因：W5 全局入口需要同步权限码到 globalData
- 思路分析：将 `syncVisibleTabs` 替换为 `syncPermissions`，在 `/api/xcx/me` 返回后将 permissions 存入 globalData
- 修改结果：小程序启动时自动同步权限码，后续页面/组件可直接读取

### `apps/miniprogram/typings/index.d.ts`
- 变更类型：修改
- 原始原因：W5 TypeScript 类型定义需要同步新增 permissions 字段
- 思路分析：在 `IAppOption.globalData` 接口中新增 `permissions: string[]`
- 修改结果：全局类型安全，IDE 自动补全支持

### `apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts`
- 变更类型：修改
- 原始原因：W5 财务看板二级 tab 需要根据权限码动态显示
- 思路分析：从 globalData.permissions 过滤出当前用户可见的二级 tab，动态渲染
- 修改结果：不同角色看到不同的二级 tab 组合

### `apps/miniprogram/miniprogram/pages/board-finance/board-finance.wxml`
- 变更类型：修改
- 原始原因：W5 配合 .ts 的动态 tab 逻辑，模板需要支持动态渲染
- 思路分析：tab 列表改为 `wx:for` 遍历动态数组
- 修改结果：模板与逻辑层联动，tab 可见性由权限码控制

### `apps/miniprogram/miniprogram/pages/board-customer/board-customer.ts`
- 变更类型：修改
- 原始原因：W5 客户看板同上
- 思路分析：同 board-finance 的动态 tab 方案
- 修改结果：客户看板二级 tab 权限码驱动

### `apps/miniprogram/miniprogram/pages/board-customer/board-customer.wxml`
- 变更类型：修改
- 原始原因：W5 配合动态 tab
- 思路分析：同 board-finance.wxml
- 修改结果：模板支持动态 tab 渲染

### `apps/miniprogram/miniprogram/pages/board-coach/board-coach.ts`
- 变更类型：修改
- 原始原因：W5 助教看板同上
- 思路分析：同 board-finance 的动态 tab 方案
- 修改结果：助教看板二级 tab 权限码驱动

### `apps/miniprogram/miniprogram/pages/board-coach/board-coach.wxml`
- 变更类型：修改
- 原始原因：W5 配合动态 tab
- 思路分析：同 board-finance.wxml
- 修改结果：模板支持动态 tab 渲染

## 本次对话文件变更

### 新增文件
- `docs/audit/prompt_logs/prompt_log_20260327_083929.md`
- `docs/audit/session_logs/2026-03/27/11_476da926_075522/main_01_db3ab14f.md`
- `docs/audit/session_logs/2026-03/27/11_476da926_075522/sub_01_db3ab14f.md`

### 删除文件
- `docs/audit/session_logs/2026-03/27/11_476da926_075522/main_01_77123f5a.md`

## 验证

1. 数据库验证：执行迁移后 `SELECT r.code, p.code FROM auth.role_permissions rp JOIN auth.roles r ON r.id=rp.role_id JOIN auth.permissions p ON p.id=rp.permission_id ORDER BY r.code, p.code;` 确认映射正确
2. 后端验证：`GET /api/xcx/me` 返回的 `permissions` 数组包含当前角色对应的全部权限码
3. 前端验证：
   - coach 登录 → 看到任务 tab，看不到客户/助教看板 tab
   - staff 登录 → 看到看板 tab，看不到任务 tab
   - head_coach/manager 登录 → 看到全部 tab
   - 无权限用户访问受保护端点 → 返回 403
4. 迁移状态：⚠️ `2026-03-27__fix_role_permissions.sql` 需确认已在测试库执行

## 文件清单

| 文件 | 操作 | 工作项 |
|------|------|--------|
| `apps/backend/app/config.py` | 修改 | W1 |
| `apps/backend/app/schemas/xcx_auth.py` | 修改 | W2 |
| `apps/backend/app/routers/xcx_auth.py` | 修改 | W2 |
| `apps/backend/app/routers/xcx_tasks.py` | 修改 | W4 |
| `apps/backend/app/routers/xcx_performance.py` | 修改 | W4 |
| `apps/backend/app/routers/xcx_customers.py` | 修改 | W4 |
| `apps/backend/app/routers/xcx_coaches.py` | 修改 | W4 |
| `db/zqyy_app/migrations/2026-03-27__fix_role_permissions.sql` | 新增 | W3 |
| `apps/miniprogram/miniprogram/utils/auth-guard.ts` | 修改 | W5 |
| `apps/miniprogram/miniprogram/app.ts` | 修改 | W5 |
| `apps/miniprogram/typings/index.d.ts` | 修改 | W5 |
| `apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts` | 修改 | W5 |
| `apps/miniprogram/miniprogram/pages/board-finance/board-finance.wxml` | 修改 | W5 |
| `apps/miniprogram/miniprogram/pages/board-customer/board-customer.ts` | 修改 | W5 |
| `apps/miniprogram/miniprogram/pages/board-customer/board-customer.wxml` | 修改 | W5 |
| `apps/miniprogram/miniprogram/pages/board-coach/board-coach.ts` | 修改 | W5 |
| `apps/miniprogram/miniprogram/pages/board-coach/board-coach.wxml` | 修改 | W5 |
