# 用户权限开发 TaskList

> 目标：在 `leaudit-platform` 里，基于当前真实业务落地一套 **单地区隔离 + 角色权限 + 数据范围控制** 的用户体系，并能把老系统用户数据平滑迁移过来。

这份文档不是泛泛设计稿，而是后续开发的执行清单。

---

## 1. 当前结论先说清楚

当前这条线，应该按下面原则做，不要再发散：

- 用户地区只认 `sso_users.area`
- 数据隔离只做单地区，不做多地区授权
- 当前业务角色只保留：
  - `provincial_admin`
  - `admin`
  - `common`
- `super_admin` 只作为可选系统维护角色
- 数据范围只保留：
  - `ALL`
  - `DEPT`
  - `SELF`
- 新系统要兼容老系统已有数据结构，不要为了“理论优雅”把老数据迁移难度抬高

---

## 2. 当前新系统现状盘点

## 2.0 本次实际检查结果（2026-04-29）

我已经实际检查了当前 `leaudit_platform` 新库和老库 `docauditai`，结论如下：

- 新库 `leaudit_platform` 原先 **完全没有** 这 7 张核心 RBAC 表：
  - `sso_users`
  - `roles`
  - `user_role`
  - `permissions`
  - `role_permissions`
  - `sys_routes`
  - `role_route`
- 新库现阶段只有 `leaudit_*` 业务表，所以原有认证/权限代码其实处于“代码已写、库表未落地”的状态
- 我已经补了可执行 SQL：
  - `scripts/user_rbac_schema_patch.sql`
  - `scripts/user_rbac_seed.sql`
  - `scripts/user_rbac_migration_audit.sql`
- 我已经把 `schema_patch + seed` 执行进当前新库
- 当前新库 RBAC 初始化结果：
  - `roles = 4`
  - `permissions = 30`
  - `role_permissions = 90`
  - `sys_routes = 9`
  - `role_route = 30`
  - `sso_users = 0`
  - `user_role = 0`

老库 `docauditai` 的迁移审计关键结果：

- `sso_users = 4106`
- `roles = 3`
- `user_role = 11`
- `permissions = 133`
- `role_permissions = 222`
- `sys_routes = 34`
- `role_route = 95`
- 老库 **没有空地区、没有重复 sub、没有重复 username**
- 老库 **4106 个用户里有 4098 个没有角色**
- 老库真实角色分布非常集中：
  - `common = 6`
  - `admin = 4`
  - `provincial_admin = 1`

这说明：

- 老系统用户登录主数据是可迁的
- 但角色并不是“每个用户都显式分配了 user_role”
- 新系统迁移时必须补一轮“默认角色落地策略”，不能只机械复制 `user_role`

## 2.1 已有能力

当前新系统已经有这些基础：

- 已有 `sso_users` 登录查询逻辑
- 已有 JWT 签发与鉴权
- 已有 `user_role / roles / role_permissions / permissions` 的查询代码雏形
- 已有受保护路由统一挂载 JWT 依赖：`fastapi_modules/fastapi_leaudit/controllers/__init__.py`
- 已有认证入口：`/auth/login`、`/auth/password_login`

## 2.2 当前实际代码路径

当前这条线的核心代码在：

- `fastapi_modules/fastapi_leaudit/services/impl/authServiceImpl.py`
- `fastapi_modules/fastapi_leaudit/services/impl/permissionServiceImpl.py`
- `fastapi_common/fastapi_common_security/jwtService.py`
- `fastapi_common/fastapi_common_security/security.py`
- `fastapi_modules/fastapi_leaudit/controllers/auth/authController.py`

## 2.3 当前最关键的问题

### 问题 1：权限设计文档和实际代码字段名不一致

当前代码查的是：

- `roles.role_key`
- `permissions.permission_key`
- `role_permissions.grant_type`

但我前面新写的初始化 SQL 用的是：

- `roles.role_name`
- `permissions.permission_code`

这说明：

- **现阶段不能直接执行那份初始化 SQL 到现有库**
- 必须先按当前实际数据库字段重新收一版正式建表/初始化方案

### 问题 2：登录后只取“一个角色”

当前 `authServiceImpl.py` 里：

```python
SELECT r.role_key ... LIMIT 1
```

这意味着：

- 多角色用户会被截断成单角色
- JWT 中的 `roles` 没真正使用起来
- 后续 `ALL > DEPT > SELF` 的数据范围收敛逻辑无法成立

### 问题 3：JWT 里没有真正放入完整权限集

当前 JWT 虽然支持 `roles` 字段，但登录时只传了：

- `userRole`
- `area`

没有把：

- 全量角色列表
- 权限点列表
- 最终数据范围摘要

稳定打进去。

这会导致：

- `/me` 接口还不完整
- 前端拿不到统一用户权限视图
- 后端每次都要重新查库，且现有逻辑并未闭环

### 问题 4：当前没有统一的数据范围注入层

现在只是“能查角色 / 权限”，但还没有真正统一实现：

- 文档列表自动按 `area` 过滤
- 用户列表自动按 `area` 过滤
- 评查 run / result 自动按文档归属校验

也就是说：

- **功能权限雏形有了，数据权限还没有真正落地**

### 问题 5：OAuth 登录仍然可能把 area 当外部输入覆盖

当前 OAuth 登录里：

- `Area=requestData.get("area")`
- 然后直接 `UPDATE sso_users SET ... area = :area`

这是很危险的。

因为这意味着：

- 前端或调用方传什么 area，就可能把用户地区改掉
- 数据隔离基础字段可能被外部请求污染

后续必须改成：

- area 只能来自后台可信源
- 登录请求不能直接覆盖 area

### 问题 6：新用户自动创建后没有默认角色闭环

当前 OAuth 首登自动插入 `sso_users`，但代码里没有看到紧接着分配默认 `common` 角色的闭环。

风险：

- 新用户建出来了，但没有角色
- 没角色则权限查询为空
- 前端表现为“登录成功但啥也看不到”

### 问题 7：密码仍然是明文比对

当前 `PasswordLogin()` 里：

- 直接 `user.get("password") != Password`

这是明显安全风险。

即使当前先沿用旧系统，也至少要在 tasklist 里明确：

- 第一阶段兼容旧明文
- 第二阶段升级为哈希存储与校验

### 问题 8：缺少统一的 `/api/auth/me`

当前文档里已经定义了 `/api/auth/me`，但现有 controller 里还没有真正落地。

这会影响：

- 前端初始化用户态
- 角色/权限/地区统一回显
- 登录后刷新恢复

---

## 3. 老系统可迁移数据结论

基于老项目 `docauditai` 的数据库快照和代码分析，当前可直接继承的核心表是：

- `sso_users`
- `roles`
- `user_role`
- `permissions`
- `role_permissions`
- `sys_routes`
- `role_route`

## 3.1 老系统 `sso_users` 关键字段

老系统 `sso_users` 目前至少包含：

- `id`
- `sub`
- `username`
- `nick_name`
- `phone_number`
- `email`
- `ou_id`
- `ou_name`
- `status`
- `is_leader`
- `password`
- `try_count`
- `try_login_time`
- `area`
- `tenant_name`
- `dep_short_name`
- `dep_name`
- `mq_person_uuid`
- `mq_account_uuid`
- `mq_synced_at`
- `created_at`
- `updated_at`
- `deleted_at`

### 迁移判断

这说明：

- 新系统**完全没必要重新发明用户主表**
- 最优方案是：
  - 继续沿用 `sso_users`
  - 补必要索引/约束/注释
  - 修正其业务语义

## 3.2 老系统 `roles` 关键字段

老系统 `roles` 目前至少包含：

- `id`
- `role_key`
- `role_name`
- `data_scope`
- `description`
- `parent_role_id`
- `priority`
- `is_system_role`
- `permissions_cache`
- `metadata`
- `created_at`
- `updated_at`

### 迁移判断

建议：

- 保留 `role_key` 作为机器标识
- 保留 `role_name` 作为中文/展示名
- 继续使用 `data_scope`
- `parent_role_id / permissions_cache / metadata` 可以先保留但不作为第一阶段核心依赖

## 3.3 老系统 `permissions` 关键字段

老系统 `permissions` 目前至少包含：

- `id`
- `permission_key`
- `module`
- `resource`
- `action`
- `description`
- `display_name`
- `permission_type`
- `is_system`
- `metadata`
- `parent_id`
- `sort_order`
- `route_id`
- `api_path`
- `api_method`
- `created_at`
- `updated_at`

### 迁移判断

建议：

- 不要再新造 `permission_code`
- 直接沿用 `permission_key`
- 后续新系统全部按 `permission_key` 统一

## 3.4 老系统 `role_permissions` 关键字段

老系统 `role_permissions` 目前至少包含：

- `role_id`
- `permission_id`
- `grant_type`
- `data_scope`
- `condition_filter`
- `metadata`
- `created_at`
- `updated_at`

### 迁移判断

建议：

- 继续保留 `grant_type`
- 第一阶段只用 `GRANT`
- `DENY` 能力先兼容不扩展
- `condition_filter` 暂不作为第一阶段核心能力

---

## 4. 最终推荐表结构策略

这里不是从零建库，而是：

- **以老系统实际表结构为准**
- 对新系统补齐缺失字段、索引、语义约束
- 避免再造第二套平行用户权限模型

## 4.1 `sso_users` 目标策略

### 保留字段

- 保留老系统现有字段不动
- 核心业务字段继续认：`area`

### 必须加的约束 / 索引

建议检查并补：

- `UNIQUE(sub)`
- `INDEX(area)`
- `INDEX(status)`
- `INDEX(deleted_at)`
- `INDEX(username)`

### 字段语义正式化

- `sub`：统一身份唯一标识
- `username`：登录名 / 工号 / 展示账号
- `nick_name`：真实姓名
- `area`：用户主地区，也是默认数据隔离依据
- `password`：短期兼容旧值，后续迁移为哈希

## 4.2 `roles` 目标策略

继续沿用：

- `role_key`：机器标识，例如 `provincial_admin`
- `role_name`：中文展示名，例如“省级管理员”
- `data_scope`：默认数据范围

建议正式保留的角色：

- `provincial_admin`
- `admin`
- `common`
- `super_admin`（可选）

## 4.3 `permissions` 目标策略

继续沿用：

- `permission_key`
- `api_path`
- `api_method`
- `route_id`

建议后续所有新权限都按下面格式命名：

- `auth:me:read`
- `documents:list:read`
- `documents:upload:write`
- `audit:run:execute`
- `rules:binding:update`

不要混用：

- 一部分 `documents.list`
- 一部分 `document:read:all`

必须统一成一种风格。

### 我建议

直接收口成老系统已有风格：

- `module:resource:action`

这样能和老数据完全对齐。

## 4.4 `role_permissions` 目标策略

继续沿用：

- `grant_type`
- `data_scope`

建议唯一约束明确为：

- `(role_id, permission_id, grant_type, data_scope)`

但在第一阶段实际配置时：

- 尽量只放 `GRANT`
- `data_scope` 按权限点是否涉及数据决定

## 4.5 `user_role` 目标策略

继续沿用现有结构即可。

关键点不是表结构，而是后端逻辑必须支持：

- 一个用户可以挂多个角色
- 登录时不能 `LIMIT 1`
- 权限汇总和数据范围要支持多角色收敛

## 4.6 `sys_routes / role_route` 目标策略

继续沿用旧系统结构即可。

重点是：

- 前端菜单权限可以继续从这里出
- 但 **接口权限不能只依赖菜单权限**
- API 权限必须还是走 `permissions / role_permissions`

---

## 5. 当前逻辑漏洞清单（必须按优先级修）

## P0：必须先修

### 5.1 角色/权限字段命名统一

要统一到底使用：

- `role_key` 还是 `role_name`
- `permission_key` 还是 `permission_code`

当前结论：

- 统一用 `role_key`
- 统一用 `permission_key`

### 5.2 登录角色查询改成多角色

当前：

- `LIMIT 1`

必须改成：

- 查询全部角色
- JWT 带 `roles`
- 兼容保留一个 `user_role` 主角色字段给旧前端

### 5.3 OAuth 登录禁止直接覆盖 `area`

必须改成：

- 登录入参中的 `area` 不再直接写库
- area 只能来自：
  - 老库已存在值
  - 后台管理维护
  - 后续可信组织同步

### 5.4 新用户自动分配默认角色

OAuth 首登新用户创建后，必须补：

- 自动赋 `common`

### 5.5 `/api/auth/me` 落地

要把当前用户的：

- 基本信息
- `area`
- `roles`
- `permissions`

统一返回。

## P1：第二阶段紧跟着修

### 5.6 数据范围注入

至少要先覆盖：

- 文档列表
- 文档详情
- 用户列表
- 评查状态 / 结果

### 5.7 密码校验升级

短期兼容旧明文，长期迁移到哈希：

- 登录时支持“旧明文兼容 + 新哈希优先”
- 用户下次修改密码时写入哈希

### 5.8 权限缓存 / 权限聚合

后续可以考虑：

- JWT 轻载
- 服务端缓存权限聚合结果

但这不是 P0。

---

## 6. 老系统用户数据迁移策略

## 6.1 迁移原则

不是“把老用户复制一份到新表”，而是：

- **尽可能复用原表结构**
- 用增量清洗 + 角色校正 + 地区校正的方式迁移

## 6.2 迁移对象

第一阶段只迁这几类数据：

1. `sso_users`
2. `roles`
3. `user_role`
4. `permissions`
5. `role_permissions`
6. `sys_routes`
7. `role_route`

## 6.3 `sso_users` 迁移规则

### 必迁字段

- `id`
- `sub`
- `username`
- `nick_name`
- `phone_number`
- `email`
- `ou_id`
- `ou_name`
- `status`
- `is_leader`
- `password`
- `area`
- `tenant_name`
- `dep_name`
- `dep_short_name`
- `created_at`
- `updated_at`
- `deleted_at`

### 可选迁字段

- `mq_person_uuid`
- `mq_account_uuid`
- `mq_synced_at`
- `try_count`
- `try_login_time`

### 迁移前校验

要先出统计：

- `sub` 是否有重复
- `username` 是否有重复
- `area` 是否为空
- `status != 0` 的禁用用户数量
- `deleted_at is not null` 的软删除用户数量

## 6.4 角色映射规则

要先梳理老库中真实 `role_key` 分布。

预期映射：

- `provincial_admin` → 保留
- `admin` → 保留
- `common` → 保留
- 其他历史角色：
  - 先做映射表
  - 无法归类的先落到 `common`
  - 再人工复核

### 明确不再延续的角色

- `city_admin`
- `review_manager`
- `review_user`
- `rule_admin`

如果老库真的还残留这些历史角色，不建议直接带入新系统业务角色定义，应该做映射清洗。

## 6.5 area 清洗规则

迁移前要先把 `sso_users.area` 跑一遍质量检查：

- 是否为空
- 是否存在历史别名
- 是否存在前后空格
- 是否存在同义不同写法

最后要统一成一套稳定值，例如：

- `省局`
- `梅州`
- `云浮`
- `揭阳`
- `潮州`

---

## 7. 开发分阶段 TaskList

下面是正式开发顺序。

## 阶段 A：数据库基线收口

### A1. 先核对当前库真实表结构

目标：

- 以当前数据库真实字段为准
- 不再拿“理想 SQL”直接执行

动作：

- 检查 `sso_users`
- 检查 `roles`
- 检查 `user_role`
- 检查 `permissions`
- 检查 `role_permissions`
- 检查 `sys_routes`
- 检查 `role_route`

产出：

- 一份“现网真实表结构快照”
- 一份“目标结构差异清单”

### A2. 出正式迁移 SQL

目标：

- 不是重建表
- 是补字段 / 补索引 / 补约束 / 补初始化数据

动作：

- 修正角色初始化 SQL
- 修正权限初始化 SQL
- 修正菜单初始化 SQL
- 统一全部字段名到现有真实库

产出：

- `用户权限_schema_patch.sql`
- `用户权限_seed.sql`

## 阶段 B：登录与 JWT 收口

### B1. 修 `AuthServiceImpl`

必须改：

- 多角色查询
- 新用户默认角色分配
- OAuth 登录禁止外部覆盖 `area`
- 登录响应统一返回 `roles`
- 兼容返回单个 `user_role`

### B2. 修 `JwtService`

必须改：

- token 中写入 `roles`
- 保留 `user_role`
- 写入 `area`
- 视情况写入精简 `permissions`

### B3. 新增 `/api/auth/me`

返回：

- 用户基本信息
- area
- roles
- permissions

## 阶段 C：权限聚合与数据范围引擎

### C1. 做统一权限聚合服务

目标：

- 给定 `user_id`
- 返回：
  - 角色列表
  - 权限列表
  - 每个权限点最终 data_scope

### C2. 做统一数据范围判定器

目标：

- 输入：用户 + 权限点 + 业务资源
- 输出：
  - `ALL`
  - `DEPT`
  - `SELF`

### C3. 先接到核心接口

优先改：

- `/api/documents/list`
- `/api/audit/run/{RunId}`
- `/api/audit/result/{RunId}`
- `/api/users/list`

## 阶段 D：老用户数据迁移

### D1. 出迁移前检查 SQL

统计：

- 用户数
- 角色分布
- area 分布
- 无角色用户
- 空 area 用户
- 重复 sub / username

### D2. 出迁移脚本

做法：

- 先备份
- 再清洗 area
- 再导入 / 映射角色
- 再补默认角色
- 再跑校验

### D3. 迁移后验收

验收项：

- 登录正常
- `/api/auth/me` 正常
- 文档列表按地区隔离正常
- admin 不能跨区
- provincial_admin 能看全局
- common 只能看自己

## 阶段 E：安全与运维收口

### E1. 密码升级方案

- 兼容旧明文
- 新密码写哈希
- 后续批量升级

### E2. 审计日志

至少记录：

- 登录成功/失败
- 角色变更
- 用户地区变更
- 权限分配变更

### E3. 文档留档

要把以下文档保持最新：

- `docs/用户与地区权限完整设计方案.md`
- `docs/接口/用户权限与权限点清单.md`
- `docs/用户权限开发TaskList.md`

---

## 8. 推荐的实际开发顺序

如果按“马上开始干”的节奏，建议严格按下面顺序：

1. 先检查当前库真实 RBAC 表结构
2. 再把初始化 SQL 改成真实可执行版
3. 再改登录与 JWT
4. 再补 `/api/auth/me`
5. 再做权限聚合与数据范围注入
6. 再做老用户迁移脚本
7. 最后再做密码升级和审计收口

这个顺序的原因很简单：

- 不先确认表结构，后面代码全会写偏
- 不先收口登录态，前端无法稳定接入
- 不先做数据范围，地区隔离只是纸面方案
- 不最后做迁移，容易把脏数据带着跑

---

## 9. 我建议下一步立刻做什么

下一步最应该做的不是直接改 controller，而是：

### 第一步

先把**当前数据库里 7 张核心表的真实结构**完整导出来：

- `sso_users`
- `roles`
- `user_role`
- `permissions`
- `role_permissions`
- `sys_routes`
- `role_route`

### 第二步

基于真实结构，产出两份正式 SQL：

- schema patch SQL
- seed SQL

### 第三步

再开始改：

- `authServiceImpl.py`
- `jwtService.py`
- `/api/auth/me`

也就是说，**下一阶段开发入口就是“数据库真相收口”**。

