# 角色硬编码与接口影响专项补充分析 > 适用范围:`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 决定能力” 这才是后续权限平台可持续演进的正确方向。