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
30 lines
2.4 KiB
|
4 weeks ago
|
# 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 展开到所有注册模块,保留原有行为。
|