# 角色管理与权限矩阵方案 > 需求来源:prompts.md(用户管理模块增加角色管理) > 状态:已确认(2026-08-20) ## 需求重申 在用户管理模块中新增**角色管理**: 1. **角色 CRUD**:新建/编辑/删除角色;可设置(或取消)**一个默认角色**——未选择角色的用户自动套用默认角色权限 2. **角色关联用户**:角色可关联多个用户;用户编辑时可单选角色 3. **角色可访问部门列表**:每个角色可配置"可访问部门"(组织树多选),用于矩阵中"已配置部门"判定 4. **表格式权限矩阵**: - **横向表头(操作列,7 项)**:查看 / 下载 / 编辑 / 删除 / 授权 / 挂载附件 / 解除挂载(与 `AllowedAction` 对齐) - **纵向表头(两级)**:第一级 = 五个密级(公开/内部/秘密/机密/绝密),第二级 = 本部门文档 / 已配置部门文档 / 其他部门文档 → 共 5×3 = 15 行 - **表头复选框**:每个行头与每个列头均有复选框,勾选/取消即批量切换整行或整列 5. **权限叠加**:矩阵与现有单文档 ACL 为 **OR 叠加**(矩阵为主权限源;现有 ACL 保留,后续将演进为限时跨部门借阅,本次不实现限时) ## 关键设计决策(已确认) | 决策点 | 结论 | |---|---| | ADMIN 与自定义角色关系 | **保留内置超管**:role_code(ADMIN/USER)不变,ADMIN 恒为超管(绕过矩阵,密级限制仍适用);自定义角色仅挂 USER 体系 | | 矩阵与文档 ACL 关系 | **OR 叠加**;后续 ACL 迁移为限时借阅权限(本次不实现) | | 部门三分类判定 | 本部门 = 文档归属机构是用户机构或其下级(org_path 前缀);已配置部门 = 文档归属机构 ∈ 角色可访问部门列表;其余 = 其他部门 | ## 数据模型(迁移 0012_role_management) | 表 | 结构 | |---|---| | `sys_role` | id、role_name、sort_no、status(ENABLED/DISABLED)、remark、is_default、`default_marker`(计算列:is_default=1→1 否则 NULL)+ 唯一约束保证全局至多一个默认角色(复用 active_marker 模式)+ BusinessTableMixin | | `sys_role_dept` | role_id → sys_role、organization_id → sys_organization,唯一约束 (role_id, organization_id, active_marker) + BusinessTableMixin | | `sys_role_permission` | role_id、security_level、dept_scope(OWN/ASSIGNED/OTHER)、can_view / can_download / can_edit / can_delete / can_config_permission / can_bind_attachment / can_unbind_attachment(布尔列,每角色固定 15 行)+ 唯一约束 (role_id, security_level, dept_scope, active_marker) + BusinessTableMixin | | `sys_user` | 新增 `role_id` FK → sys_role 可空(NULL → 默认角色) | ## 权限判定融合(authorization_service.py) `evaluate_plan_access` / `plan_allowed_actions` 新增矩阵判定: 1. 密级前置检查不变(密级不足一律拒绝,ADMIN 也不绕过) 2. 部门归属分类:文档 org_path 以用户机构 org_path 为前缀 → **OWN**;∈ 角色可访问部门列表 → **ASSIGNED**;否则 **OTHER**;文档无归属 → OTHER 3. 取用户角色(无 → 默认角色;无默认 → 矩阵贡献为空)→ 查 `(security_level, dept_scope)` 行 → 与 ACL 结果 OR 叠加 4. 角色矩阵变更不动 auth_version(权限实时查库);用户换角色时 auth_version+1 ## API 设计 | 接口 | 说明 | |---|---| | GET /roles(分页+keyword+status) | 角色列表(含成员数、isDefault) | | POST /roles | 新建(roleName/sortNo/status/remark/isDefault) | | PUT /roles/ | 编辑(rowVersion 乐观锁) | | DELETE /roles/?rowVersion= | 删除(有关联用户时拒绝,ROLE_IN_USE) | | GET /roles//departments | 可访问部门 id 列表 | | PUT /roles//departments | 整存部门列表(rowVersion) | | GET /roles//permissions | 15×7 矩阵 | | PUT /roles//permissions | 矩阵整存整取(rowVersion) | | PUT /roles//users | 关联用户集合(rowVersion) | | GET /users 扩展 | User DTO 增加 roleId/roleName | - 全部 admin_required + business_audit 审计 - 新 AuditAction:CREATE_ROLE / EDIT_ROLE / DELETE_ROLE / CHANGE_ROLE_PERMISSION / CHANGE_ROLE_USER;AuditTarget.ROLE - 新错误:RoleInUseError(ROLE_IN_USE)、RoleNotFound、默认角色/矩阵参数校验错误 ## 前端(DirectoryPage) 1. 第三个 Tab **"角色管理"**:角色列表(名称/默认/成员数/状态/操作) 2. 角色编辑弹窗:名称、排序、状态、是否默认 3. 角色详情:基本信息 + 可访问部门(组织树复选)+ 权限矩阵表格(行头两级嵌套,行头/列头复选框全选切换) 4. UserEditorModal 增加"角色"下拉(仅 USER 显示) ## 实现阶段 | 阶段 | 内容 | 验证 | |---|---|---| | 1 | 数据模型 + 迁移 0012 | test_migration.py | | 2 | 角色 CRUD/部门/矩阵/用户关联 API + 审计 + OpenAPI | 新增 pytest | | 3 | authorization_service 矩阵判定融合 | 判定单测 + 既有 90 测试守底 | | 4 | 前端角色 Tab + 矩阵 + 用户角色下拉 | tsc + vitest | | 5 | 全量回归 | 前后端全量测试 | ## 风险 - **高**:evaluate_plan_access 为核心访问路径,融合必须零回归(既有测试守底) - **中**:默认角色唯一性并发(default_marker 计算列 + 唯一约束 DB 层兜底) - **中**:矩阵整存整取与乐观锁交互 - **低**:文档 organization_id 为空的历史数据 → 归入 OTHER