# 角色硬编码与接口影响专项补充分析

> 适用范围：`leaudit-platform` 当前角色权限与地区隔离体系  
> 文档定位：从主方案中拆出的专项补充稿，专门分析“硬编码角色如何改”和“哪些接口会被联动影响”。

---

## 1. 结论先行

当前项目里的角色硬编码不是零散问题，而是已经渗透到：

- 后端服务层能力派生
- 后端服务层动作放行
- 前端 UI 可见性和可编辑性
- 前端 guard、fallback、role mapping

因此，这类改造不能只改某一个 service，也不能只改权限表配置。

如果只改一部分，会出现 3 类典型问题：

1. 后端 permission 已放行，但服务层仍按角色名拒绝
2. 后端边界已收敛，但前端仍按旧角色逻辑展示按钮或入口
3. 菜单、页面、接口、详情、下载这些边界继续不一致

正确做法是：

1. 先识别所有硬编码角色位置
2. 再区分它们的职责类型
3. 最后按“能力抽象 -> 服务层替换 -> 前端去角色化”的顺序渐进改造

---

## 2. 当前硬编码角色的 4 种形态

## 2.1 服务层上下文派生型

这类代码并不直接判断某个 permission，而是把角色名先转成派生能力：

- `is_global`
- `can_manage`
- `is_super_admin`
- `is_area_admin`
- `bypass_area`

典型位置：

- `documentServiceImpl.py`
- `govdocServiceImpl.py`
- `usageStatsServiceImpl.py`
- `contractTemplateServiceImpl.py`
- `rbacAdminServiceImpl.py`
- `homeServiceImpl.py`

典型模式：

- `role_key IN ('super_admin', 'provincial_admin') => is_global`
- `role_key IN ('super_admin', 'provincial_admin', 'admin') => can_manage`
- `role_key = 'super_admin' => is_super_admin / bypass_area`

本质问题：

- 角色名被直接当成能力模型
- 一旦出现新领域管理员，所有上下文派生逻辑都要重复改

## 2.2 服务层动作白名单型

这类代码直接按角色名决定某个业务动作是否允许。

典型位置：

- `ragDatasetServiceImpl.py`
- `ragChatServiceImpl.py`

典型模式：

- `UserRole not in ("provincial_admin", "admin", "super_admin")`
- `user_role == "provincial_admin"`
- `UserRole == "admin"`

本质问题：

- 控制器层已经做 permission 校验
- 服务层又加了角色白名单
- 结果会出现“有权限但角色名不对仍被拒绝”

这是当前最需要优先清理的硬编码类型。

## 2.3 前端 UI 能力硬编码型

这类代码不是后端鉴权，但会直接影响用户感知边界。

典型位置：

- `components/dify-dataset-manager/index.tsx`
- `components/dify-dataset-manager/area-dataset-config.tsx`
- `hooks/use-area-dataset-config.ts`

典型模式：

- `provincial_admin` 可编辑全部
- `super_admin` 可编辑全部
- `admin` 仅可编辑本地区

本质问题：

- 前端自己在解释“谁能管理”
- 即使后端以后完成去角色化，前端仍可能展示旧权限状态

## 2.4 前端兼容层硬编码型

这类代码主要存在于路由、guard 和迁移兼容层。

典型位置：

- `legal-platform-frontend/lib/auth/user-routes.ts`
- `legal-platform-frontend/lib/api/legacy/auth/user-routes.ts`
- `legal-platform-frontend/lib/auth/guard.ts`
- `legal-platform-frontend/lib/auth/cross-checking-access.ts`

典型模式：

- `provincial_admin -> admin`
- `super_admin -> admin`
- `developer -> admin`
- `provincial_admin` 自动获得“省局 area”候选

本质问题：

- 前端把真实角色体系压缩成少数桶位
- 继续保留会让权限模型越来越难以统一

---

## 3. 为什么不能直接删掉所有角色硬编码

不是所有角色硬编码都应该同一天删除，因为它们承担职责不同。

## 3.1 可优先替换的

优先替换：

- 服务层动作白名单
- RAG 管理动作角色白名单

原因：

- 这些逻辑已经和 permission 决策重复
- 删除后收益最大，副作用相对可控

## 3.2 需要先抽象再替换的

需要先抽象：

- `is_global`
- `can_manage`
- `is_super_admin`
- `is_area_admin`

原因：

- 它们已经被多个模块用于拼接 SQL 过滤条件
- 直接删会导致大量数据边界逻辑断裂

正确做法：

- 先统一沉淀为能力派生层
- 再逐步把“角色名判断”替换为“能力决策”

## 3.3 可以保留为兼容层但必须收缩边界的

允许短期保留：

- route fallback
- role mapping
- 某些旧前端 guard

但必须满足：

1. 只存在于适配层
2. 不再扩散到新业务代码
3. 后续有明确移除计划

---

## 4. 推荐的替换目标

当前很多角色语义，其实应该改写成能力语义。

建议的替换关系如下：

- `super_admin` 全局绕过
  替换为：`is_super_admin`
- `provincial_admin => 全省可见`
  替换为：`effective_scope == ALL`
- `admin => 本地区管理`
  替换为：`effective_scope == DEPT + domain manage permission`
- `common => 自己的数据`
  替换为：`effective_scope == SELF`
- `provincial_admin/admin/super_admin` 共用白名单
  替换为：显式 `create/update/delete/manage` permission

这一步的本质，是把“角色名”换成“能力决策”。

---

## 5. 建议新增的统一能力层

建议新增统一能力派生对象，例如：

- `is_super_admin`
- `has_global_scope`
- `has_area_scope`
- `can_manage_rbac`
- `can_manage_rag_dataset`
- `can_manage_contract_templates`
- `can_view_usage_stats`
- `can_bypass_home_area`

这些能力不应直接写死在角色名上，而应由以下信息共同决策：

- `roles`
- `permissions`
- `data_scope`
- 可选 `condition_filter`

建议新增两层统一能力：

1. `ScopeContextProvider`
2. `AdminCapabilityResolver`

前者负责“当前用户的通用数据边界”，后者负责“某个业务域是否具备管理能力”。

---

## 6. 当前高风险冲突区

## 6.1 RAG 是最典型的双轨冲突区

当前链路是：

1. 控制器层按 permission 校验
2. 服务层又按 `UserRole` 白名单校验
3. 最终管理能力同时受 permission 和角色名双重控制

风险：

- 未来新增 `rag_manager` 这类角色时，即使给了 `rag:dataset:manage/create/update/delete`，服务层仍会拒绝

优先级：

- 最高

## 6.2 RBAC 管理域存在“角色管理能力”和“权限管理能力”双重耦合

当前链路是：

1. `_assertManagePermission` 依赖 `can_manage`
2. `_assertPermission` 再校验具体权限点

风险：

- 如果未来某用户拥有 `rbac:*` 权限，但主角色不是 `admin/provincial_admin/super_admin`，仍可能被挡在第一层

优先级：

- 高

## 6.3 文档 / 公文 / 统计存在共性上下文派生复制

这些模块并没有直接角色白名单放行，但都重复实现了：

- `is_global`
- `can_manage`
- `is_super_admin`

风险：

- 改一个模块不改另一个，边界会漂移

优先级：

- 高

## 6.4 前端知识库管理仍在自行解释角色

风险：

- 后端已经 permission 化后，前端仍会显示不该显示的按钮
- 或相反，后端已允许，前端仍不展示入口

优先级：

- 高

---

## 7. 受影响接口分析

下面重点回答“其他接口会不会影响到”。

答案是：**会，而且影响面不小。**

## 7.1 文档模块

受影响接口包括：

- `POST /api/upload`
- `GET /api/documents/list`
- `GET /api/documents/status`
- `GET /api/documents/{DocumentId}`
- `GET /api/v3/review-points/{DocumentId}`
- `PATCH /api/v3/review-points/{ReviewPointResultId}/audit`
- `PATCH /api/v3/documents/{DocumentId}/confirm`
- `POST /api/documents/{DocumentId}/attachments`
- `PUT /api/documents/{DocumentId}`
- `DELETE /api/documents/{DocumentId}`

影响原因：

- 这些接口都依赖文档服务里的用户上下文派生和数据范围过滤
- 一旦 `is_global/can_manage` 的计算逻辑变化，全部都会联动

## 7.2 公文模块

受影响接口包括：

- `POST /api/govdoc/documents`
- `GET /api/govdoc/documents`
- `GET /api/govdoc/documents/{documentId}`
- `PATCH /api/govdoc/documents/{documentId}`
- `DELETE /api/govdoc/documents/{documentId}`
- `POST /api/govdoc/runs`
- `GET /api/govdoc/runs/{runId}`
- `GET /api/govdoc/runs/{runId}/result`
- `GET /api/govdoc/runs/{runId}/findings`
- `GET /api/govdoc/runs/{runId}/entities`
- `GET /api/govdoc/runs/{runId}/structure`
- `GET /api/govdoc/runs/{runId}/outline`
- `GET /api/govdoc/runs/{runId}/paragraphs`
- `GET /api/govdoc/runs/{runId}/report/html`
- `GET /api/govdoc/runs/{runId}/report/docx`
- `GET /api/govdoc/documents/{documentId}/original`

影响原因：

- 公文模块也使用了同构的上下文派生
- 尤其结果、报告、下载类接口最容易出现“资源详情边界未同步”的问题

## 7.3 统计模块

受影响接口包括：

- `GET /api/v3/usage-stats/overview`
- `GET /api/v3/usage-stats/trends`
- `GET /api/v3/usage-stats/by-users`
- `GET /api/v3/usage-stats/by-departments`
- `GET /api/v3/usage-stats/by-areas`
- `GET /api/v3/usage-stats/details`

影响原因：

- 当前统计接口对管理员可见性有明显上下文派生依赖
- 去角色化后，这些接口需要统一切到“统计域 permission + scope”模型

## 7.4 RAG 模块

受影响接口包括：

- `GET /api/v3/rag/apps`
- `GET /api/v3/rag/apps/default`
- `GET /api/v3/rag/datasets/my`
- `GET /api/v3/rag/datasets/admin`
- `POST /api/v3/rag/datasets/admin`
- `PUT /api/v3/rag/datasets/admin/{DatasetId}`
- `DELETE /api/v3/rag/datasets/admin/{DatasetId}`
- `GET /api/v3/rag/datasets/{DatasetId}`
- `PATCH /api/v3/rag/datasets/{DatasetId}`
- 各类 `/datasets/{DatasetId}/documents`
- 各类 `/datasets/{DatasetId}/segments`
- 各类检索测试接口
- `POST /api/v3/rag/chat/messages`
- 会话、消息反馈、会话重命名、删除等接口

影响原因：

- 控制器层和服务层当前存在双轨权限逻辑
- 这是最典型的“permission 改了，接口仍会被角色名卡住”的模块

## 7.5 RBAC 管理模块

受影响接口包括：

- `GET /api/v3/rbac/roles`
- `POST /api/v3/rbac/roles`
- `PUT /api/v3/rbac/roles/{RoleId}`
- `DELETE /api/v3/rbac/roles/{RoleId}`
- `GET /api/v3/rbac/users`
- `GET /api/admin/users/organizations/tree`
- `GET /api/v3/rbac/roles/{RoleId}/users`
- `POST /api/v3/rbac/users/{UserId}/roles`
- `DELETE /api/v3/rbac/users/{UserId}/roles/{RoleId}`
- `GET /api/v3/rbac/users/{UserId}/roles`
- `GET /api/v3/routes`
- `GET/PUT /api/rbac/roles/{RoleId}/routes`
- `GET/POST /api/v3/rbac/role-permissions`
- `POST /api/v3/rbac/roles/{RoleId}/access`
- `GET /api/v3/routes/{RouteId}/permissions`

影响原因：

- 当前同时依赖 `_assertManagePermission` 和 `_assertPermission`
- 第一层还是角色派生管理能力

## 7.6 首页入口模块

受影响接口：

- `GET /api/home/entry-modules`

影响原因：

- 当前首页入口存在 `super_admin` 的 area bypass 语义
- 这类入口可见性也属于权限边界的一部分

## 7.7 合同模板模块

受影响接口包括：

- `GET /api/v3/contract-templates/categories`
- `GET /api/v3/contract-templates`
- `POST /api/v3/contract-templates`
- `GET /api/v3/contract-templates/search`
- `GET /api/v3/contract-templates/{TemplateId}`
- `DELETE /api/v3/contract-templates/{TemplateId}`

影响原因：

- 当前业务语义明确写着“地区管理员才能上传”
- 如果只去角色化、不补合同模板域显式权限，这类能力会失焦

## 7.8 中影响模块

中影响但必须纳入联调范围的还有：

- 交叉评查模块
- 评查点模块
- 评查点分组模块
- 规则配置模块

原因：

- 它们虽然不一定都直接依赖角色名白名单
- 但仍依赖 permission、route、入口和关系访问逻辑
- 一旦整体权限能力模型调整，也必须回归验证

---

## 8. 前端联动影响

不能只看后端接口，前端也会同步受影响。

## 8.1 菜单与路由

受影响位置：

- `Sidebar.tsx`
- `user-routes.ts`
- `check-route-permission.ts`
- fallback route mapping

风险：

- 菜单可见性与真实接口权限继续分叉
- 某些角色被映射压扁后，真实权限无法完整反映

## 8.2 RAG 管理页面

受影响位置：

- `components/dify-dataset-manager/*`
- `hooks/use-area-dataset-config.ts`

风险：

- 后端能力已收敛，前端仍按旧角色名展示编辑入口

## 8.3 页面 guard

受影响位置：

- `lib/auth/guard.ts`
- `lib/auth/cross-checking-access.ts`
- `lib/auth/session-user.ts`
- `lib/auth/jwt.ts`

风险：

- `user_role` 被继续当成完整权限模型使用

---

## 9. 推荐改造顺序

建议按下面顺序推进，避免同时炸开所有联动面。

## 9.1 第一阶段

先做平台能力层：

- `PermissionDecisionService`
- `ScopeContextProvider`
- `AdminCapabilityResolver`

目标：

- 不先改业务接口行为，只先统一决策能力

## 9.2 第二阶段

优先改 RAG：

- 清理服务层角色白名单
- 改为 permission + scope/policy 决策

原因：

- 这是最典型、收益也最大的双轨冲突区

## 9.3 第三阶段

接入共用上下文模块：

- 文档
- 公文
- 统计

原因：

- 它们共享大量上下文派生逻辑
- 最适合沉淀统一 `QueryScopeBuilder`

## 9.4 第四阶段

处理管理域：

- RBAC 管理
- 首页入口
- 合同模板

目标：

- 把“管理能力”从角色名迁移到领域 permission

## 9.5 第五阶段

清理前端角色解释层：

- role mapping
- fallback route
- guard
- UI 编辑能力判断

目标：

- 前端不再自行解释“谁是管理员”

---

## 10. 必做回归清单

建议单独维护一份“角色去硬编码回归清单”，至少覆盖：

1. 用户拥有 permission，但主角色不是 `admin/provincial_admin` 时，接口是否仍能正确访问
2. 用户获得某领域管理权限后，是否无需改代码即可生效
3. 菜单、按钮、接口、详情、下载、导出边界是否一致
4. 前端是否仍存在基于 `user_role` 的旧判断放大或缩小能力
5. RAG 管理接口是否已完全摆脱角色白名单
6. RBAC 管理接口是否已从“角色管理能力”切换到“权限管理能力”
7. 文档、公文、统计是否仍存在模块间边界不一致

---

## 11. 最终建议

这次专项分析的核心结论只有一句话：

**角色硬编码改造，本质上不是“替换几个 if”，而是把整套权限系统从“角色名驱动”升级为“能力决策驱动”。**

如果只做局部替换，问题会更隐蔽。

如果按下面顺序推进，风险最低：

1. 先抽象能力层
2. 先处理 RAG 双轨冲突
3. 再统一文档/公文/统计上下文派生
4. 再处理 RBAC 管理域和首页入口
5. 最后清理前端角色解释和 fallback

这样改完之后，项目才能真正从：

- “角色名决定能力”

走向：

- “权限点 + 数据范围 + 模块 policy 决定能力”

这才是后续权限平台可持续演进的正确方向。
