Files
leaudit-platform-backend/docs/权限与地区隔离/角色去硬编码迁移清单.md
T

255 lines
11 KiB
Markdown

# 角色去硬编码迁移清单
> 适用范围:当前项目中所有直接依赖 `super_admin/provincial_admin/admin/common/developer` 的权限判断
> 文档定位:从“问题分析”进入“逐文件迁移清单”,指导研发按优先级拆改。
---
## 1. 迁移目标
本清单的目标不是把所有“角色”这个概念删掉,而是把“用角色名直接判断能力”的写法替换成下面三类正规来源:
1. `permission_key`
2. `effective_scope`
3. `module policy / capability snapshot`
保留角色本身作为“授权载体”,但不再让角色名直接主导业务逻辑。
---
## 2. 问题类型定义
为方便排期,先统一问题类型。
| 类型 | 含义 |
| --- | --- |
| `CTX` | 服务层上下文派生硬编码 |
| `ACT` | 服务层动作放行硬编码 |
| `UI` | 前端按钮/编辑态硬编码 |
| `GUARD` | 前端路由、guard、fallback、role mapping 硬编码 |
| `COMPAT` | 兼容层短期保留但必须收缩的旧逻辑 |
---
## 3. 后端迁移清单
## 3.1 P0:必须最先处理
| 文件 | 当前写法 | 问题类型 | 替换目标 | 优先级 | 风险 | 影响范围 |
| --- | --- | --- | --- | --- | --- | --- |
| `fastapi_modules/fastapi_leaudit/services/impl/ragDatasetServiceImpl.py` | 多处 `UserRole in ('provincial_admin','admin','super_admin')` | `ACT` | 改为 `rag:* permission + RagPolicy + effective_scope` | `P0` | 高 | RAG 管理接口、数据集文档接口 |
| `fastapi_modules/fastapi_leaudit/services/impl/ragChatServiceImpl.py` | `user_role == 'provincial_admin'``_app_visible` 角色分支 | `ACT` | 改为 `PUBLIC_MIXED` 策略 | `P0` | 高 | 应用列表、默认应用、聊天入口 |
| `fastapi_modules/fastapi_leaudit/controllers/ragChatController.py` | 把 `UserRole` 透传给 service | `ACT` | 改为透传 `ScopeContext/CapabilitySnapshot` | `P0` | 中 | 所有 RAG 接口联动 |
## 3.2 P1:统一上下文能力层
| 文件 | 当前写法 | 问题类型 | 替换目标 | 优先级 | 风险 | 影响范围 |
| --- | --- | --- | --- | --- | --- | --- |
| `fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py` | `_getCurrentUserContext()` 中按 `role_key IN (...)` 生成 `is_global/can_manage/is_super_admin` | `CTX` | 改为 `PermissionDecisionService` | `P1` | 高 | 文档列表、详情、附件、确认、删除 |
| `fastapi_modules/fastapi_leaudit/services/impl/govdocServiceImpl.py` | 通过 `is_global/can_manage` 控制 `region/created_by` | `CTX` | 改为 `GovdocPolicy` | `P1` | 高 | 公文详情、run、报告、下载 |
| `fastapi_modules/fastapi_leaudit/services/impl/usageStatsServiceImpl.py` | `_getCurrentUserContext``role_key IN (...)` 派生范围 | `CTX` | 改为 `UsageStatsPolicy` | `P1` | 中高 | 概览、趋势、明细 |
| `fastapi_modules/fastapi_leaudit/services/impl/contractTemplateServiceImpl.py` | `is_global/can_manage/is_area_admin` 依赖角色名 | `CTX` | 改为 `ContractTemplatePolicy` | `P1` | 中 | 模板列表、搜索、创建、删除 |
| `fastapi_modules/fastapi_leaudit/services/impl/rbacAdminServiceImpl.py` | `bool_or(r.role_key IN (...))` 派生管理能力 | `CTX` | 改为 `RbacAdminPolicy + rbac permission` | `P1` | 高 | 用户列表、组织树、角色分配 |
| `fastapi_modules/fastapi_leaudit/services/impl/homeServiceImpl.py` | `role_key = 'super_admin' => bypass_area` | `CTX` | 改为首页能力快照 | `P1` | 中 | 首页入口可见性 |
## 3.3 P2:默认角色与兼容角色清理
| 文件 | 当前写法 | 问题类型 | 替换目标 | 优先级 | 风险 | 影响范围 |
| --- | --- | --- | --- | --- | --- | --- |
| `fastapi_modules/fastapi_leaudit/services/impl/authServiceImpl.py` | 用户无角色时自动补 `common`;回退主角色 `common` | `COMPAT` | 保留默认角色机制,但从业务能力判断中剥离“common 特权假设” | `P2` | 中 | 登录、初始化用户 |
| `fastapi_modules/fastapi_leaudit/services/impl/rbacServiceImpl.py` | `roleBucket = "admin" if any(role in {...}) else "common"` | `COMPAT` | 仅短期保留在菜单兼容层,逐步删除角色桶映射 | `P2` | 高 | 路由树、菜单 fallback |
| `fastapi_modules/fastapi_leaudit/services/impl/rbacServiceImpl.py` | 静态蓝图 `_COMPAT_ROUTE_BLUEPRINTS``admin/common` 分桶 | `COMPAT` | 改为数据库路由兜底,不认角色桶 | `P2` | 中高 | 前端菜单 |
---
## 4. 前端迁移清单
## 4.1 P0:直接影响权限感知
| 文件 | 当前写法 | 问题类型 | 替换目标 | 优先级 | 风险 | 影响范围 |
| --- | --- | --- | --- | --- | --- | --- |
| `legal-platform-frontend/components/dify-dataset-manager/index.tsx` | `provincial_admin/super_admin/admin` 决定 `canEditDataset` | `UI` | 改为读 `rag` 能力或 permission_map | `P0` | 高 | RAG 编辑按钮、详情编辑态 |
| `legal-platform-frontend/hooks/use-area-dataset-config.ts` | `roleCanManageDataset = provincial_admin/super_admin/admin` | `UI` | 改为纯 permission 决策 | `P0` | 高 | 知识库配置页全部操作 |
| `legal-platform-frontend/components/dify-dataset-manager/area-dataset-config.tsx` | `isProvincialAdmin`、非省级自动填地区 | `UI` | 改为根据 `effective_scope``allowed_areas` 决定 | `P0` | 中高 | 地区选择器、创建编辑表单 |
## 4.2 P1:菜单与路由层
| 文件 | 当前写法 | 问题类型 | 替换目标 | 优先级 | 风险 | 影响范围 |
| --- | --- | --- | --- | --- | --- | --- |
| `legal-platform-frontend/lib/auth/user-routes.ts` | 静态 `FALLBACK_MENU_DATA.admin/common` | `GUARD` | 收敛为数据库路由兜底,不做角色桶映射 | `P1` | 高 | 主菜单 |
| `legal-platform-frontend/lib/api/legacy/auth/user-routes.ts` | `mapUserRoleToRoleKey()``provincial_admin -> admin``developer -> admin` | `GUARD` | 删除角色映射,直接使用后端返回路由与权限 | `P1` | 高 | 菜单缓存、路由授权 |
| `legal-platform-frontend/components/layout/Sidebar.tsx` | 按 `userRole` 拉路由、写 `localStorage.user_role` | `GUARD` | 改为按登录态和后端返回能力渲染 | `P1` | 中高 | 侧边栏菜单、权限 map |
| `legal-platform-frontend/lib/auth/cross-checking-access.ts` | `provincial_admin` 自动补 `省局` area 候选 | `GUARD` | 改为服务端返回 `allowed_areas` | `P1` | 中 | 交叉评查入口展示 |
| `legal-platform-frontend/lib/auth/guard.ts` | `developer/provincial_admin` 白名单进入设置页 | `GUARD` | 改为检查后台管理权限 | `P1` | 高 | 设置页访问 |
## 4.3 P2:页面级残留
| 文件 | 当前写法 | 问题类型 | 替换目标 | 优先级 | 风险 | 影响范围 |
| --- | --- | --- | --- | --- | --- | --- |
| `legal-platform-frontend/app/(audit)/contract-template/list/ContractTemplateListClient.tsx` | `currentUser.role === "admin"` 才能上传 | `UI` | 改为 `contract_template:create:write` | `P2` | 中高 | 合同模板上传入口 |
| `legal-platform-frontend/app/(audit)/role-permissions/RolePermissionsClient.tsx` | `provincial_admin` 禁删、`coreRoles = ['provincial_admin','admin','common']``setIsCityAdmin(userRole==='admin')` | `UI/COMPAT` | 保留系统角色保护逻辑,但与角色名硬编码解耦 | `P2` | 中高 | 角色权限管理页 |
| `legal-platform-frontend/hooks/usePermission.tsx` | 默认 `userRole = common`,大量消费 `localStorage.user_role` | `COMPAT` | 保留兼容字段,但新增能力快照来源,逐步弱化角色中心地位 | `P2` | 中 | 多页面基础权限钩子 |
| `legal-platform-frontend/components/layout/Layout.tsx` | 默认 `userRole = developer`,回退读取本地角色 | `COMPAT` | 改为显式会话能力初始化 | `P2` | 中 | 全局布局 |
---
## 5. 接口影响面清单
角色去硬编码不会只影响代码写法,还会影响接口行为。
## 5.1 后端接口影响
| 模块 | 受影响接口 |
| --- | --- |
| RAG | `/v3/rag/apps``/v3/rag/apps/default``/v3/rag/datasets/admin*``/v3/rag/datasets/{id}*` |
| 文档 | `/documents/list``/documents/{id}``/documents/status``/v3/review-points/*` |
| 公文 | `/govdoc/documents*``/govdoc/runs/*``/govdoc/documents/{id}/original` |
| 统计 | `/v3/usage-stats/*` |
| RBAC | `/v3/rbac/users*``/v3/rbac/roles/{id}/users``/v3/rbac/users/{id}/roles*` |
| 合同模板 | `/v3/contract-templates*` |
| 首页 | 首页入口模块加载逻辑 |
## 5.2 前端受影响页面
| 页面/模块 | 受影响点 |
| --- | --- |
| 侧边栏菜单 | 路由来源、权限 map 来源 |
| 知识库管理页 | 可编辑、可创建、地区选择范围 |
| 合同模板页 | 上传按钮、删除按钮 |
| 设置页 | 进入 guard |
| 交叉评查入口 | 是否显示入口模块 |
| 角色权限管理页 | 系统角色保护、核心角色展示逻辑 |
---
## 6. 推荐替换模式
## 6.1 后端替换模式
不推荐:
```python
if user_role in ("provincial_admin", "admin", "super_admin"):
...
```
推荐:
```python
decision = await permissionDecisionService.decide(
ScopeContext(
user_id=current_user_id,
permission_key="rag:dataset:update",
module="rag",
resource_id=dataset_id,
)
)
if not decision.allowed:
raise Forbidden()
```
## 6.2 前端替换模式
不推荐:
```ts
const canUpload = currentUser.role === "admin";
```
推荐:
```ts
const canUpload = hasPermission("contract_template:create:write");
```
或者:
```ts
const canManageDataset = capability.rag?.manage === true;
```
---
## 7. 兼容策略
并不是所有角色相关代码都要同一天删除。
建议分 3 类处理:
1. `立即删除`
- 服务层动作白名单
- 前端页面按钮直接按角色判断
2. `先替换后删除`
- `is_global/can_manage/bypass_area`
3. `短期保留但冻结`
- 路由 fallback
- 本地 `user_role` 兼容字段
“冻结”的意思是:
- 不再新增依赖
- 只允许向统一能力来源迁移
---
## 8. 风险说明
## 8.1 最大风险
如果只改后端,不改前端,会出现:
- 后端已经可用,但前端按钮还不显示
- 菜单还按旧角色展示
- 用户误以为权限失效
如果只改前端,不改后端,会出现:
- 前端展示放开
- 后端仍按旧角色拒绝
- 联调体验极差
## 8.2 第二风险
去硬编码后,如果没有统一能力快照,前端容易又发明一套新的本地推断规则。
所以必须同步提供:
- `/auth/me` 扩展能力
- 或专门的当前用户能力接口
---
## 9. 推荐执行顺序
建议按下面顺序推进:
1. `ragDatasetServiceImpl.py`
2. `ragChatServiceImpl.py`
3. RAG 前端 3 个文件
4. `documentServiceImpl.py`
5. `govdocServiceImpl.py`
6. `usageStatsServiceImpl.py`
7. `rbacAdminServiceImpl.py`
8. `contractTemplateServiceImpl.py`
9. `homeServiceImpl.py`
10. 菜单/guard/fallback 前端文件
原因:
- 先打掉最明显的双轨冲突
- 再统一高风险数据域
- 最后收口前端兼容层
---
## 10. 完成判定
角色去硬编码完成,不是指仓库里再也没有 `admin` 字符串,而是指:
1. 角色名不再直接决定业务动作是否允许
2. 角色名不再直接决定数据边界
3. 前端不再用角色名推断核心按钮与菜单
4. 新增自定义角色时,不需要全仓库搜角色名改逻辑
只有达到这 4 条,才算真正完成迁移。