You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4.0 KiB

ADR-0012: 角色管理模块架构

Status

Accepted

Context

ADR-0005(原型侧)决定角色功能权限保存时由后端补全祖先节点。当前后端的角色管理散落在 SystemController/api/system/roles/* 端点中,仅实现了基础 CRUD + 菜单分配,缺少:祖先补全、校验规则、权限控制、角色详情接口、内置角色保护。需要设计完整的角色管理模块架构。

Decision

1. 独立 RoleController,路径 /api/roles/*

SystemController 中移除全部角色端点,新建 RoleController,路径 /api/roles/*。与 ResourceController 的独立模式一致。

2. 分步保存:基本信息 + 权限分配分离

  • POST /api/roles/saveOrUpdate — 保存角色基本信息(name, code, dataScope, sort, remark)
  • POST /api/roles/assign-resources — 分配功能权限(resourceIds,带祖先补全)
  • GET /api/roles/detail — 返回角色基本信息 + resourceIds(已补全的完整集合)

前端角色编辑页 Tab 分页各自保存,互不耦合。

3. 术语统一:resourceIds

API 层统一使用 resourceIds(与 ADR-0011 的 resource 术语体系一致),底层表名 sys_role_menu 不变。menuIds 是泄漏抽象——catalog 和 button 不是"菜单"。

4. 祖先补全逻辑放在 ISysMenuService

ISysMenuService 新增 getAncestorIds(Collection<Long> menuIds) 方法。沿 parentId 向上遍历是 sys_menu 表的固有操作,属于 menu service 的领域职责。SysRoleServiceImpl.assignResources 调用它拿到补全后的集合再写库。

实现方式:批量查全表构建 ID→parentId 映射,内存中遍历,避免逐节点查 DB。

5. 权限控制:纯 hasAuthority + 数据种子

角色管理端点使用 @PreAuthorize("hasAuthority('crm:role:xxx')") 而非 hasRole('ADMIN')。权限码通过 button 节点的 perms 字段注入(ADR-0011 权限码并集机制)。

引导问题(ADMIN 需要持有 crm:role:xxx 才能管理角色)通过数据初始化器解决:在 sys_menu 中创建角色管理的 button 权限点,并通过幂等初始化器分配给 ADMIN 角色。

ResourceControllerhasRole('ADMIN') 不同——角色管理是细粒度权限的首个实践场景,为后续模块渐进迁移到 hasAuthority 模式树立模式。

6. 内置角色保护:SysRole.builtin 字段

SysRole 新增 Boolean builtin 字段。builtin=true 的角色禁止删除、禁止改 roleCode。ADMIN 是典型内置角色。builtin 字段由数据初始化器设置,创建角色接口不允许设 builtin=true

7. 校验规则

  • roleCode 唯一性:保存时查 DB,roleCode 已存在且 id 非当前记录则拒绝。
  • 内置角色保护builtin=true 的角色拒绝删除、拒绝改 roleCode。
  • dataScope 范围校验:dataScope 必须在 1-4 范围内(DataScopeEnum),非法值拒绝。

8. 删除级联:sys_role_menu + sys_user_role

deleteRoleCascade 同时清理 sys_role_menusys_user_role。事务保护,三表操作原子性。内置角色保护已前置防止 ADMIN 误删。

9. 数据初始化器幂等化

DataInitializer 的"整体跳过"改为"逐项检查":ADMIN 角色已存在就只补 builtin=true,button 权限点不存在就创建,角色-权限绑定不存在就补建。每次启动都跑,只补缺的。

Consequences

  • SystemController 不再持有角色端点,前端需将 /api/system/roles/* 调用迁移到 /api/roles/*
  • SysRole 表新增 builtin 列(ddl-auto: update 自动加)。
  • ISysMenuService 新增 getAncestorIds 方法,SysRoleServiceImpl 新增对它的依赖。
  • DataInitializer 重构为幂等模式,每次启动执行增量检查。
  • 角色管理权限码(crm:role:list/detail/save/delete/assign)作为 button 节点写入 sys_menu,通过初始化器分配给 ADMIN 角色。
  • ResourceControllerhasRole('ADMIN') 暂不迁移,作为后续渐进迁移的对照。