# 02 · 角色 × 模块档位存储 Type: grilling Status: resolved Blocked by: 01 ## Question 角色对每个模块的档位怎么存?现在是 `sys_role.data_scope` 单列(一个全局档位)。 要定的点: - 子表结构:如 `sys_role_data_scope`(role_id, module_id, level),role×module 唯一。 - 旧 `sys_role.data_scope` 列的处置:删除 / 保留作某种默认 / 迁移后废弃(迁移策略在 03)。 - 角色对某模块**未配档位**时的语义:沿用「无角色/未配按最窄档 SELF」(现 `PermissionResolverImpl` 的约定),还是别的默认? - `DataScopeEnum` / `CUSTOM(5)` 是否仍适用(现角色不用 CUSTOM)。 依赖 01 定下的模块标识(module_id 指向什么)。 ## Answer ### 1. 子表结构 ```sql sys_role_data_scope ( id bigint PK AUTO_INCREMENT, role_id bigint NOT NULL, module_code varchar(50) NOT NULL, -- 引用 sys_data_scope_module.code,不引用 id data_scope tinyint NOT NULL, -- 1=SELF, 2=DEPT, 3=DEPT_AND_CHILDREN, 4=ALL UNIQUE KEY uk_role_module (role_id, module_code) ) ``` - `module_code` 用 string 而非 bigint id,与 `@DataScope(module = "...")` 注解同源,与 crm-dict「业务数据只存字典编码」约定一致。 - PermissionResolver 查角色×模块档位时直接拿到 code,不需 join 注册表。 - `data_scope` 存 1-4(tinyint),与现有 `sys_role.data_scope` 存储格式一致。 ### 2. 旧 sys_role.data_scope 列:迁移后删除 - 新存储完全取代旧列。 - 迁移过程(03 票负责):把旧 `sys_role.data_scope` 的值复制到新表每个已知模块的行,然后删除旧列与 `SysRole.dataScope` 字段。 - 终态:`sys_role` 表不再有 `data_scope` 列,`SysRole` 实体不再有 `dataScope` 字段。 ### 3. 角色对某模块未配档位时:默认 SELF(最窄档) - 角色×模块无行 = SELF(与现有「无角色 = SELF」约定一致)。 - 多角色取最宽时,未配模块的角色贡献 SELF(不放宽任何人的可见范围)。 - 新增模块后存量角色自动走 SELF(fail safe),管理员再按需放宽。 ### 4. CUSTOM(5):删除 - 从 `DataScopeEnum` 移除 `CUSTOM(5)`,只保留 `SELF(1)/DEPT(2)/DEPT_AND_CHILD(3)/ALL(4)`,与 `DataScopeLevel` 对齐。 - `SysRoleServiceImpl` 中 `filter(e != DataScopeEnum.CUSTOM)` 可简化为直接用全部枚举值。 - `DataScopeEnum` 仍保留(用于初始化与校验),不与 `DataScopeLevel` 合并(二者分层不同:DataScopeEnum 在 crm-auth 应用层有 label,DataScopeLevel 在 crm-base 基础设施层无 label)。 ### 对下游工单的影响 - **03(迁移)**:依赖 02 的存储结构已定,可以设计迁移规则——把旧 `sys_role.data_scope` 复制到新表每个已知模块的行。03 现已解阻(was Blocked by 02)。 - **04(上下文值对象)**:PermissionResolver 需改为对每个模块在所有角色的该模块档位里取最宽(未配 = SELF)。`sys_role_data_scope` 表的查询:按 `role_id` 批量取所有行,按 `module_code` 分组取最宽。 - **05(拦截器)**:拦截器需从上下文按 `module_code` 取 `VisibilityScope`,不再取单一全局 scope。 - **07(API 契约)**:角色保存接口从传单个 `dataScope` 改为传 `List<{moduleCode, dataScope}>`;角色详情接口返回 per-module 档位列表。