RBAC与部门管理员权限管理方案.md 24 KB

RBAC 角色体系 + 部门管理员 + 权限申请审批 实现计划

状态:待用户确认(未开始编码) 创建日期:2026-08-14 关联:基于 v2.0 技术方案,叠加多角色与权限工作流


一、需求重述与澄清

1.1 用户原始需求

  1. 两类管理员角色:
    • 超级管理员(SUPER_ADMIN):系统级,所有文档所有操作
    • 部门管理员(DEPT_ADMIN):管理"被授权部门"的文档与人员,对其他部门不可见
  2. 超级管理员职责:
    • 把"部门 X 的管理权"授予某些用户(即设置某人为部门 X 的管理员)
    • 拥有所有文档的所有操作权限(已是现状)
  3. 部门管理员默认权限:
    • 对所管理部门的所有文档:所有操作权限
    • 对其他部门文档:无权限
    • 可对所辖部门人员做"细粒度权限划分"
  4. 普通用户默认权限:
    • 仅能查看本部门管理的所有文档
    • 不能查看其他部门文档
    • 部门管理员与超级管理员都可对他做细粒度授权

1.2 待澄清的歧义点(确认计划时一并说明,便于修正)

# 歧义点 我的默认解读(如不符请告知)
Q1 "本部门管理的所有文档"——文档当前不直接挂部门,只有 category_id 与 created_by 默认引入"文档所属部门 = 创建者所在部门"语义;同时保留文档级 ACL(doc_permission)覆盖细粒度授权
Q2 部门管理员能否管理子部门文档?(如机关 → 作战部,作战部管理员能否看机关文档?反之呢?) 默认 部门管理员 = 所管理部门及其所有后代部门(与现有"组织祖先链"语义一致)。部门管理员不能管理祖先部门
Q3 "权限申请"流程:用户向谁申请?申请什么? 默认指普通用户向上级/管理员申请具体文档或文档分类的额外权限,由部门管理员/超管审批;审批通过后落 doc_permission 表
Q4 部门管理员之间是否平级?一个部门能否有多个管理员? 默认一对多(一个部门可有多位管理员,权限等价),与现有"用户 × 组织"模型契合
Q5 "细粒度权限划分"的粒度:模块级 / 文档级 / 字段级? 默认文档级(沿用现有 doc_permission 表的 5 个 canXxx),不引入字段级
Q6 部门管理员能否创建/删除本部门用户? 默认可以管理本部门及下级部门的用户(增删改、重置密码),但不能改其他部门用户
Q7 部门管理员能否创建子部门? 默认不能(组织结构由超管维护),仅可对所辖部门的"文档/用户/权限"做管理

⚠️ 请逐项确认("Q1✓ Q2 改为..."等),歧义点会影响后续表结构与 API 设计。


二、总体架构设计

2.1 角色模型选型:保留 role_code 列,新增"部门管理员授权表"

为什么不引入完整 RBAC(Role/Permission/RolePermission/UserRole 多对多)?

  • 现系统 RoleCode 是写死枚举、allowedModules 静态映射,已被 JWT、装饰器、前端门控、f6Scope 静态测试等多处依赖;改为多对多会引发大爆炸。
  • 需求只有三种角色(超管/部门管理员/普通用户),不需要"自定义角色"能力。
  • 文档级细粒度已由 doc_permission 完整覆盖,不需要再做"角色 × 权限点矩阵"。

结论:在 sys_user.role_code 枚举中加一档 DEPT_ADMIN,用一张 sys_dept_admin(用户 × 组织)关联表表达"谁管哪些部门"。

2.2 文档归属语义:新增 organization_id 列

Document 当前没有组织归属字段,要实现"本部门文档"必须新增:

  • doc_document.organization_id:默认取创建者所属组织(上传时自动填充)
  • 历史数据回填脚本:按 created_by → user.organization_id 推导

2.3 数据范围(Data Scope)三层模型

角色 文档范围 用户管理范围
SUPER_ADMIN(即 ADMIN) 全部 全部
DEPT_ADMIN 所管理部门及后代的文档 + 显式 ACL 授权 所管理部门及后代的用户
USER 仅显式 ACL 授权 + visibility_type=ALL_AUTHENTICATED 仅自己

"本部门文档"对普通用户可见 = 文档 organization_id ∈ 用户所属组织的祖先链时,普通用户享有 VIEW 权限。

2.4 权限申请工作流

普通用户/部门管理员  ──提交申请──►  待审批
         │                          │
         │                          ├── 部门管理员审批(仅本部门申请)
         │                          └── 超级管理员审批(任意申请)
         │
         ▼
   通过 → 落 doc_permission 表
   驳回 → 标记驳回理由

申请单要素:申请人、目标文档(或分类)、申请动作(view/download/edit 等)、申请理由、审批人、状态、审批意见。


三、现状调研(已完成)

3.1 后端已有基础

维度 现状
组织树 Organization 已是层级模型(parent_id + org_path),CRUD + 软删 + 乐观锁齐全,已有"祖先链"和"后代遍历"工具函数
用户生命周期 建改删/重置密码/禁用/自我保护、最后管理员保护、auth_version 主动失效机制完备
二角色体系 RoleCode.USER / RoleCode.ADMIN(backend/dms/common/enums.py:8-10),CHECK 约束(backend/dms/models/user.py:18-21)
文档级 ACL doc_permission 表 + evaluate_plan_access 统一判定入口(backend/dms/services/authorization_service.py:101-158)+ 全量差异保存 + 子方案动态继承主案 + 附件通过主案传递
三种可见性策略 ALL_AUTHENTICATED / ORGANIZATION / CUSTOM(backend/dms/models/document.py:149-153)
审计基础设施 22 种动作 + 8 种目标类型,business_audit 辅助函数统一落表

3.2 后端关键缺口

  1. 无 DEPT_ADMIN 角色:role_code CHECK 限制为 ('USER', 'ADMIN')
  2. 无"用户 × 组织"管理员授权表
  3. Document 无组织归属字段:无法表达"本部门文档"
  4. 无数据范围中间态:列表查询对 ADMIN 一律全量、对 USER 一律按 ACL 过滤,无 DEPT_ADMIN 中间档
  5. 无权限申请/审批端点
  6. 审计 CHECK 约束:若新增 ASSIGN_DEPT_ADMIN / GRANT_DEPT_ADMIN 等动作,需扩展 CHECK(0004 已有先例)

3.3 前端已有基础

维度 现状
认证 完整的登录/退出/会话恢复,tokenStore 区分 sessionStorage/localStorage
组织管理 UI 完整 CRUD(树形、关键词、排序、状态、parent 校验防环)DirectoryPage.tsx:217-487
用户管理 UI 完整 CRUD + 重置密码 + 组织树筛选(含下级)+ 分页 DirectoryPage.tsx:763-1271
文档级 ACL UI 完整的 F6PermissionModal:三档 visibility + 组织/人员主体 + 5 个布尔动作 + 子案继承
模块级导航门控 allowedModules 驱动导航条 + isPageAllowed 拦截跳转
角色枚举 仅 USER / ADMIN 两档(frontend/src/shared/api/enums.ts:1-5)

3.4 前端关键缺口

  1. 无 DEPT_ADMIN 角色:RoleCode 仅 USER/ADMIN
  2. 无部门管理员概念:OrganizationNode 无 managerId、UserSummary 无 managedOrgIds
  3. 无"我的申请/待我审批"页面:不存在 features/requests/ 或 features/approvals/
  4. 无权限申请 API 客户端
  5. 角色门控写死在 App.tsx:App.tsx:2986-3014 三处 roleCode === RoleCode.ADMIN 硬编码
  6. 双重门控耦合:导航按 allowedModules、路由按 roleCode,必须同步
  7. f6Scope.test.ts:23 静态断言脆弱:用 readFileSync 做字符串匹配,新增角色门控时需更新

四、实现阶段(共 6 阶段,建议按顺序)

阶段 1:数据库迁移 0006_rbac_dept_admin

新建文件:backend/migrations/versions/0006_rbac_dept_admin.py

变更项:

  1. sys_user.role_code CHECK 扩展为 ('USER', 'ADMIN', 'DEPT_ADMIN')
  2. 新建 sys_dept_admin 表:

    CREATE TABLE sys_dept_admin (
     id BIGINT PK,
     user_id BIGINT NOT NULL,         -- FK sys_user
     organization_id BIGINT NOT NULL, -- FK sys_organization
     granted_by BIGINT NOT NULL,      -- FK sys_user (授予人)
     granted_at DATETIME NOT NULL,
     row_version INT NOT NULL DEFAULT 0,
     created_at/updated_at/created_by/updated_by + is_deleted/deleted_at (mixin),
     UNIQUE(user_id, organization_id, active_marker),
     CHECK (user_id <> organization_id) -- 防呆
    )
    
  3. doc_document 加列 organization_id BIGINT NULL + 冗余 organization_name + 索引 ix_doc_document_org_active

  4. sys_audit_log.action_type CHECK 扩展:ASSIGN_DEPT_ADMIN / REVOKE_DEPT_ADMIN / SUBMIT_ACCESS_REQUEST / APPROVE_ACCESS_REQUEST / REJECT_ACCESS_REQUEST

  5. sys_audit_log.target_type CHECK 扩展:DEPT_ADMIN_GRANT / ACCESS_REQUEST

  6. 新建 doc_access_request 表(权限申请单):

    CREATE TABLE doc_access_request (
     id BIGINT PK,
     requester_id BIGINT NOT NULL,       -- FK sys_user
     document_id BIGINT NULL,            -- 申请具体文档
     category_id BIGINT NULL,            -- 申请某分类下所有文档(与 document_id 互斥)
     requested_actions VARCHAR(64) NOT NULL, -- "VIEW,DOWNLOAD" 逗号分隔
     reason TEXT NOT NULL,
     status VARCHAR(16) NOT NULL,        -- PENDING/APPROVED/REJECTED/CANCELLED
     approver_id BIGINT NULL,
     approver_comment TEXT NULL,
     approved_actions VARCHAR(64) NULL,  -- 审批通过的实际动作(可缩减)
     submitted_at DATETIME NOT NULL,
     decided_at DATETIME NULL,
     row_version INT NOT NULL DEFAULT 0,
     ... (mixin)
     CHECK (status IN ('PENDING','APPROVED','REJECTED','CANCELLED')),
     CHECK ((document_id IS NULL) <> (category_id IS NULL)) -- 二选一
    )
    
  7. 历史数据回填:UPDATE doc_document d JOIN sys_user u ON u.id = d.created_by SET d.organization_id = u.organization_id WHERE d.organization_id IS NULL

验证:alembic upgrade head + 回滚脚本 downgrade() 完整。

复杂度:中(约束较多但都是 ADD COLUMN / CREATE TABLE)


阶段 2:后端模型与服务层

新建 backend/dms/models/dept_admin.py:

  • DeptAdminGrant ORM 模型(继承 BusinessTableMixin)

新建 backend/dms/models/access_request.py:

  • AccessRequest ORM 模型,含 requested_actions / approved_actions 的 list 序列化辅助方法

修改 backend/dms/models/document.py:

  • 加 organization_id + organization_name 列 + relationship

修改 backend/dms/models/user.py + backend/dms/common/enums.py:8-10:

  • RoleCode 加 DEPT_ADMIN

新建 backend/dms/services/dept_admin_service.py:

  • list_grants(user_id=None, org_id=None) — 查询授权
  • grant(user_id, org_id, granted_by) — 新增授权(含自环检测、组织存在性、用户角色升级到 DEPT_ADMIN)
  • revoke(grant_id, row_version, revoked_by) — 撤销授权(撤销后若用户无其他授权则降级回 USER)
  • managed_org_ids(user_id) — 返回用户管理的全部 org_id(含后代)
  • is_dept_admin_of(user_id, org_id) — 判定

新建 backend/dms/services/access_request_service.py:

  • submit(requester_id, payload) — 提交申请(不允许申请自己已有权限的)
  • list_for_approver(approver_id, status, page) — 待我审批列表(按数据范围过滤)
  • list_for_requester(requester_id, status, page) — 我的申请
  • approve(request_id, approver_id, payload) — 通过(落 doc_permission,发审计)
  • reject(request_id, approver_id, payload) — 驳回

修改 backend/dms/services/authorization_service.py(核心改造):

  • evaluate_plan_access 新增分支:
    • SUPER_ADMIN → 放行(已存在)
    • DEPT_ADMIN → 若文档 organization_id ∈ 用户的 managed_org_ids(含后代)→ 放行
    • USER → 若文档 organization_id == 用户 organization_id 且 status=PUBLISHED → VIEW 放行("本部门文档默认可见")
  • plan_allowed_actions 同步扩展返回值

修改 backend/dms/services/document_query_service.py:318:

  • list_documents 增加 DEPT_ADMIN 分支:WHERE doc.organization_id IN (managed_org_ids ∪ descendants)

修改 backend/dms/services/user_service.py:142:

  • list_users 增加 DEPT_ADMIN 分支:仅能查询所辖部门的用户

修改 backend/dms/services/user_service.py:35-43:

  • ALLOWED_MODULES 字典:DEPT_ADMIN 看到 DOCUMENT_BROWSER / ORGANIZATION_USER_MANAGEMENT / AUDIT_LOG(不含 BACKEND_MANAGEMENT)

修改 backend/dms/services/document_mutation_service.py:

  • 上传文档时自动填充 organization_id = creator.organization_id
  • DEPT_ADMIN 可在所辖部门内 CRUD,USER 不可创建(维持现状)

复杂度:高(authorization_service 是核心路径,改造需配套单测)


阶段 3:API 端点

新建 backend/dms/api/v1/dept_admin.py:

  • GET /api/v1/dept-admins?userId=&orgId= — 列表(仅 ADMIN)
  • POST /api/v1/dept-admins — 授予(仅 ADMIN)
  • DELETE /api/v1/dept-admins/<id>?rowVersion= — 撤销(仅 ADMIN)

新建 backend/dms/api/v1/access_requests.py:

  • POST /api/v1/access-requests — 提交(任意已认证用户)
  • GET /api/v1/access-requests/mine?status=&page= — 我的申请
  • GET /api/v1/access-requests/pending?page= — 待我审批
  • POST /api/v1/access-requests/<id>/approve — 通过
  • POST /api/v1/access-requests/<id>/reject — 驳回
  • POST /api/v1/access-requests/<id>/cancel — 取消(仅本人 + PENDING)

修改 backend/dms/security/decorators.py:

  • 新增 @dept_admin_or_admin_required 装饰器
  • 或更通用的 @require_data_scope(resource_type) 检查

修改 backend/dms/api/v1/organizations.py 与 users.py:

  • 把 @admin_required 调整为:列表查询允许 DEPT_ADMIN 但结果按 scope 过滤;写操作仍限 ADMIN

修改 backend/dms/api/v1/users.py:68-77 /users/me:

  • 在 user_summary 中增加 managedOrgIds: Id[] 字段(仅 DEPT_ADMIN 非空)

修改 backend/dms/security/jwt_tokens.py:

  • claims 增加 managedOrgIds(避免每次请求都查表),或仅在 /me 返回

复杂度:中


阶段 4:前端类型与 API 客户端

修改 frontend/src/shared/api/enums.ts:

  • RoleCode 加 DEPT_ADMIN
  • 新增 AccessRequestStatus(PENDING/APPROVED/REJECTED/CANCELLED)
  • AuditActionType 加 5 项(前端枚举与后端同步)

修改 frontend/src/features/auth/authTypes.ts:

  • UserSummary 加 managedOrgIds: Id[](默认空数组)

修改 frontend/src/features/auth/authApi.test.ts + authSession.test.ts + directoryApi.test.ts:

  • mock 数据补 managedOrgIds: []

新建 frontend/src/features/dept-admin/deptAdminTypes.ts:

  • DeptAdminGrant / DeptAdminGrantListQuery / DeptAdminGrantRequest

新建 frontend/src/features/dept-admin/deptAdminApi.ts:

  • list / grant / revoke

新建 frontend/src/features/access-requests/requestTypes.ts:

  • AccessRequest / AccessRequestCreateRequest / AccessRequestApproveRequest / AccessRequestStatus

新建 frontend/src/features/access-requests/requestApi.ts:

  • submit / listMine / listPending / approve / reject / cancel

修改 frontend/src/features/auth/authSession.ts:

  • AuthenticatedPage 加 "requests"
  • AllowedModule 加 ACCESS_REQUEST
  • MODULE_PAGES / MODULE_ORDER 同步映射

复杂度:中


阶段 5:前端页面与组件

修改 frontend/src/app/App.tsx:2986-3014:

  • 关键:把 roleCode === RoleCode.ADMIN 抽象为 <RequireRole roles={[ADMIN, DEPT_ADMIN]}> 组件
  • 同步更新 f6Scope.test.ts:23 静态断言(改为允许 DEPT_ADMIN 但仍禁止与 BIND_ATTACHMENT 共现)

修改 frontend/src/features/directory/DirectoryPage.tsx:

  • 角色下拉从 Object.values(RoleCode) 改为字典枚举 + 中文标签
  • DEPT_ADMIN 登录时:左侧组织树仅显示所辖部门及下级,右侧用户表同范围过滤
  • 新增"部门管理员授权"面板:超管可见,UI 为"用户 × 组织"矩阵 / 双向 Picker

修改 frontend/src/features/directory/DirectoryPage.tsx:1430-1435(UserEditorModal):

  • DEPT_ADMIN 创建用户时 organizationId 下拉限制为所辖部门

新建 frontend/src/features/access-requests/RequestsPage.tsx:

  • 顶部 Tab:我的申请 / 待我审批
  • 我的申请:列表 + "新建申请"按钮(弹窗选文档/分类 + 动作复选 + 理由)
  • 待我审批:列表 + 通过/驳回按钮(驳回必填意见,通过可缩减动作集)

新建 frontend/src/features/access-requests/RequestEditorModal.tsx:

  • 选目标:从可见文档列表 / 从分类树挑选
  • 动作复选:5 个 canXxx
  • 理由文本域

修改 frontend/src/app/App.tsx 路由:

  • 加 {page === "requests" && <RequestsPage .../>}

修改 frontend/src/shared/api/enums.ts:60-69 AllowedAction保持不变(申请单用同一套动作)

复杂度:高(DirectoryPage 1700 行,需小心改造;新增 RequestsPage 全新页面)


阶段 6:单元测试 & 端到端验证

后端:

  • backend/tests/dms/test_dept_admin.py — 授权/撤销/角色升级降级/自环
  • backend/tests/dms/test_access_request_workflow.py — 提交/审批/落表/状态机
  • backend/tests/dms/test_authorization_dept_admin.py — 数据范围三层模型
  • backend/tests/dms/test_user_scope_dept_admin.py — 用户列表 scope 过滤

前端:

  • deptAdminApi.test.ts / requestApi.test.ts — 类型守卫 + 端点契约
  • directoryTypes.test.ts / authSession.test.ts — 已有测试补字段
  • 新建 appScope.test.ts 扩展断言:requests 页面门控按 RBAC 三档角色

手动验收(用户参与):

  1. 超管授予 A 为部门 X 管理员 → A 登录看到 X 及其子部门
  2. A 创建用户 B(仅能在 X 范围内选组织)→ B 默认只能看 X 的文档
  3. B 提交"对文档 D 的下载权限申请" → A 审批通过 → B 可下载 D
  4. A 给 B 配置"文档 E 仅查看" → B 无法下载 E
  5. 超管撤销 A 的部门管理员授权 → A 降级为 USER,所有扩展权限失效

复杂度:中


五、依赖项

  • 无新外部依赖(不引入 Redis/Celery 等)
  • 复用现有:Argon2 / JWT / SQLAlchemy / Alembic / Vitest / pytest
  • 审批流不引入工作流引擎,用状态机足够

六、风险与缓解

风险 等级 缓解
R1: evaluate_plan_access 改造引入性能回归(每条文档都查 managed_org_ids) 高 在 dept_admin_service 加内存缓存(请求级),或把 managed_org_ids 拍平成 IN 子句一次查出
R2: 文档归属语义变更影响历史数据 高 迁移脚本必须回填 organization_id;回填逻辑:SELECT d.id, u.organization_id FROM doc_document d JOIN sys_user u ON u.id = d.created_by WHERE d.organization_id IS NULL
R3: 普通用户"本部门可见"语义与现有 visibility_type=ORGANIZATION 冲突(语义重叠) 中 保留 visibility_type 做显式授权;新增的"本部门默认可见"独立判定,两者取并集。文档落库时统一两者语义
R4: JWT claims 加 managedOrgIds 后撤销授权无法实时生效 中 撤销时 User.auth_version += 1 强制 token 失效(已有机制)
R5: 前端 f6Scope.test.ts:23 静态断言脆弱 中 同步更新断言内容,增加 DEPT_ADMIN 白名单
R6: 已有 mock 数据 265 个测试用例可能因类型新增字段大面积破坏 中 阶段 2 完成后立即跑全量测试,预期破坏点 5-8 处,按文件批量补字段
R7: 部门管理员与"挂载附件 / 配置文档权限"功能交叉 中 DEPT_ADMIN 在所辖范围内享有 BIND_ATTACHMENT / CONFIG_PERMISSION(沿用文档级 allowed_actions 动态计算,无需新增字段)
R8: App.tsx 3000+ 行 + DirectoryPage 1700 行,改造易引入回归 中 拆分 <RequireRole> 抽象 + 不动既有功能分支,仅扩展角色判定集

七、工作量估算

阶段 后端 前端 测试 合计
1. 迁移 2h - 0.5h 2.5h
2. 模型与服务 5h - 2h 7h
3. API 3h - 1h 4h
4. 前端类型与 API - 2h 1h 3h
5. 前端页面 - 6h 1.5h 7.5h
6. 测试与验收 - - 3h 3h
总计 10h 8h 9h 27h

整体复杂度:高(核心授权路径改造 + 新增完整工作流)


八、待用户确认事项

请逐项回复,确认后才开始编码:

  1. 歧义点 Q1–Q7 是否需要调整?
  2. 角色模型选型:同意"保留 role_code 列 + 新增 sys_dept_admin 关联表"还是希望走完整 RBAC(自定义角色 + 权限点矩阵)?
  3. 权限申请工作流是否需要?还是仅需"管理员直接配置权限",跳过申请审批?
  4. App.tsx 拆分:是否同意在改造同时把 <RequireRole> 抽出来(小幅重构)?还是希望本次保持最小改动不重构?
  5. 阶段顺序:是否同意按 阶段1→6 顺序推进?或希望先做某个阶段验证后再继续?

九、关键文件锚点(便于后续实现时直接跳转)

后端

路径 说明
backend/dms/common/enums.py:8-10 RoleCode 枚举(加 DEPT_ADMIN)
backend/dms/common/enums.py:13-17 AllowedModule 静态枚举
backend/dms/common/enums.py:68-75 AllowedAction 文档级动作
backend/dms/models/user.py:15-78 User 模型
backend/dms/models/organization.py:22-65 Organization 模型
backend/dms/models/document.py:25-215 Document 模型(加 organization_id)
backend/dms/models/permission.py:23-87 doc_permission 表
backend/dms/services/authorization_service.py:101-158 evaluate_plan_access 核心授权逻辑
backend/dms/services/authorization_service.py:161-217 plan_allowed_actions / attachment_allowed_actions
backend/dms/services/document_query_service.py:318-389 list_documents Python 层过滤
backend/dms/services/user_service.py:35-43 ALLOWED_MODULES 字典
backend/dms/services/user_service.py:142-181 list_users
backend/dms/services/permission_service.py:286-420 save_permissions 全量差异保存
backend/dms/api/v1/users.py:68-77 /users/me 端点
backend/dms/api/v1/organizations.py 组织端点
backend/dms/security/decorators.py:77-84 admin_required 装饰器
backend/dms/security/jwt_tokens.py:34-43 issue_access_token claims
backend/migrations/versions/0005_category_flat_mode.py 最新迁移版本(下一版应为 0006)

前端

路径 说明
frontend/src/shared/api/enums.ts:1-5 RoleCode
frontend/src/shared/api/enums.ts:7-13 AllowedModule
frontend/src/shared/api/enums.ts:60-69 AllowedAction
frontend/src/features/auth/authTypes.ts:21-35 UserSummary
frontend/src/features/auth/authSession.ts:9-55 AuthenticatedPage / MODULE_PAGES / getNavigationItems
frontend/src/features/auth/__tests__/authApi.test.ts:10-28 user mock(需补字段)
frontend/src/features/auth/__tests__/authSession.test.ts:29-43 user mock(需补字段)
frontend/src/features/directory/directoryTypes.ts:13-23 OrganizationNode
frontend/src/features/directory/DirectoryPage.tsx:113-115 角色下拉硬编码(需扩展)
frontend/src/features/directory/DirectoryPage.tsx:1430-1435 UserEditorModal 角色选择
frontend/src/features/permissions/permissionTypes.ts:9-44 文档级 ACL 类型
frontend/src/features/permissions/PermissionModal.tsx:138-533 F6 文档权限弹窗
frontend/src/app/App.tsx:107 Page 类型
frontend/src/app/App.tsx:2986-3014 角色门控三处(需抽象)
frontend/src/features/permissions/__tests__/f6Scope.test.ts:23 静态断言(需更新)

文档结束。等待用户确认后再启动阶段 1 编码。