# 角色去硬编码迁移清单

> 适用范围：当前项目中所有直接依赖 `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 条，才算真正完成迁移。
