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.

30 lines
2.4 KiB

# ADR-0018: 数据权限按业务模块可配
**Date:** 2026-08-11
**Status:** Accepted
## Context
原数据权限机制为「单一全局档位」:`SysRole.dataScope` 存一个 1-4 的整数,对用户所有受控表统一应用。这意味着一个角色对客户模块和线索模块只能有相同的可见范围(如全部可见或仅本人),无法满足「销售对客户看全部、对线索看本部门」这类差异化需求。
## Decision
将数据权限从「单一全局档位」改造为「按业务模块分别配置档位」:
1. **模块注册表** — 新增 `sys_data_scope_module` 表(code/name/sort/status/builtin),数据驱动,管理员可 CRUD。
2. **角色×模块档位** — 新增 `sys_role_data_scope(role_id, module_code, data_scope)` 表,一个角色对每个模块各有一档。删除 `SysRole.dataScope` 列。
3. **注解声明**`@DataScope` 注解新增必填 `module` 属性,引用注册表 code。启动时 `PermissionConfig` 校验 code 在注册表中存在,不存在则拒绝启动。
4. **上下文值对象**`DataVisibility` 持有 `Map<String, DataScopeLevel>` 替代单个 level;`DataVisibilityContext.currentScope(moduleCode)` 加参数取该模块的 scope。
5. **拦截器顺序调换** — 先解析 SQL 找表 → 查注解(含 module)→ 用 `anno.module()` 从上下文取该模块的 scope → 注入 WHERE。
6. **解析器每模块取最宽**`PermissionResolverImpl``sys_role_data_scope``module_code` 分组,每组取最宽 level(ADR-0008 规则下沉到每模块)。未配模块默认 SELF(最窄档,fail safe)。
7. **删除 CUSTOM(5)**`DataScopeEnum.CUSTOM` 从枚举中移除。
8. **API 契约**`RoleController.saveOrUpdate``moduleScopes` JSON 字符串表单字段替代 `dataScope`;新增 `GET /api/data-scope/modules` 列出可用模块。
9. **迁移策略** — 一次性 SQL 脚本(单事务,CROSS JOIN 旧值,DROP 旧列)。
## Consequences
- 角色配置从「选一个档位」变为「每模块各选一档」,前端角色编辑页需要改造。
- `DataVisibility` API 破坏性变更:`visibilityScope()` → `visibilityScope(String moduleCode)`,所有调用方需加参数。
- 写侧不受影响:`currentOwnership()` 是身份维度(userId/deptId),与档位正交。
- 迁移脚本将旧 `sys_role.data_scope` 值通过 CROSS JOIN 展开到所有注册模块,保留原有行为。