# Wayfinder map: 数据权限按业务模块可配 Labels: wayfinder:map ## Destination 后端把数据权限从「单一全局档位」改造为「按业务模块(线索/商机/客户/项目,可扩展)分别配置档位」的**可配置能力**: - 档位挂在**角色**上,一个角色对每个模块各有一档; - 多角色对**每个模块各自取最宽**(现「多角色取最宽」ADR-0008 的规则下沉到每模块); - 覆盖存储 / 权限计算 / 上下文传递 / SQL 拦截器四层的改造; - 产出角色配置**接口契约**与**前端对接文档**。 业务实体表(线索/商机/客户/项目)**不建**——它们目前在本 repo 尚不存在;本效 力只做「未来业务表接入时,登记模块即自动按模块过滤」的能力。地图只产**决策与实现工单**, 不写生产代码(跑完交后续 session coding)。 ## Notes - **域**:`crm-auth`(认证与权限)+ `crm-base`(数据权限值对象与拦截器基座)。charting 前已读 `crm-auth/CONTEXT.md`、`CONTEXT-MAP.md`。术语以域文档为准(数据范围档位 / 部门集合 / 一律不可见 / 多角色取最宽 ADR-0008 / 主部门 ADR-0005)。 - **Tracker**:本地 markdown,见 `docs/agents/issue-tracker.md` 的「Wayfinding operations」。 - **每次 session 应查的 skill**:默认 `/grilling` + `/domain-modeling`;research 票用 `/research` 子代理。 - **关键现状事实**(charting 时核实): - 现档位存 `sys_role.data_scope` 单列(`SysRole.dataScope`,`DataScopeEnum` 1..5,CUSTOM 不用于角色)。 - 计算在 `PermissionResolverImpl.resolve`:所有角色取**最宽一档**,结出一份 `DataVisibility`。 - 传递 `DataVisibilityContext`(ThreadLocal,放**一份** `DataVisibility`)。 - 生效 `DataScopeInterceptor`:拦所有带 `@DataScope` 的表 SELECT,**共用同一 scope**。 - `@DataScope` 现标在实体类上(`ownerColumn`/`deptColumn`),**无模块概念**;启动扫描 `com.crm` 建表名→注解映射(`PermissionConfig` + `DataScopeTables`)。 - **目前无任何业务实体标 `@DataScope`**,只有基类 `OwnedEntity`;四个业务模块实体本 repo 尚不存在。 - 写侧 `MetaObjectFillHandler` 填 owner/dept,与档位无关。 ## Decisions so far - [01-module-registry-model](issues/01-module-registry-model.md)(resolved)— 专用注册表 `sys_data_scope_module` + `@DataScope` 注解加 `module` 必填属性 + 启动校验拒绝悬空 - [02-role-module-scope-storage](issues/02-role-module-scope-storage.md)(resolved)— `sys_role_data_scope(role_id, module_code, data_scope)` 用 code 引用 + 旧列迁移后删 + 未配默认 SELF + 删 CUSTOM(5) - [06-write-side-ownership](issues/06-write-side-ownership.md)(resolved)— 确认写侧不受影响:`currentOwnership()` 是身份维度,与分模块档位正交,不产生实现工单 - [04-context-value-objects](issues/04-context-value-objects.md)(resolved)— DataVisibility 持 `Map` 替代单 level;`currentScope(moduleCode)` 加参数;`currentOwnership()` 不变;部门集合只算一次 - [05-interceptor-resolver](issues/05-interceptor-resolver.md)(resolved)— 拦截器顺序调换(先找表→取 module→取 scope);解析器每模块各取最宽组 map;权限码/菜单树/角色编码不动 - [03-migration-strategy](issues/03-migration-strategy.md)(resolved)— 一次性 SQL 脚本单事务(建两表+插模块+CROSS JOIN 旧值+DROP 旧列);DataInitializer 同步改用新表;SysRole.dataScope 字段删 - [07-api-contract-frontend-doc](issues/07-api-contract-frontend-doc.md)(resolved)— saveOrUpdate 用 moduleScopes JSON 字符串表单字段(全量替换);detail 返回 moduleScopes 数组;page 去掉 dataScope;新增 GET /api/data-scope/modules 列模块 ## Not yet specified - ~~各票的具体技术选型细节~~ → **已毕业**(全部 7 票 resolved):每张工单的 Answer 节已定具体结构/代码示例/迁移 SQL。后续 session 可直接据 Answer 实施编码。 - ~~「模块可扩展」到底扩展到什么程度~~ → **已毕业**(01 resolved):加模块 = 注册表加一行(数据驱动)+ 实体加 `@DataScope(module="...")` 注解(需代码)。注册表决定模块列表,注解决定表→模块映射,两者启动时交叉校验。 ## Out of scope - **建线索/商机/客户/项目业务实体表**:本 repo 尚无这些实体,本效力只做数据权限可配能力,业务表由后续/别处负责。 - **前端页面实现**:只产接口契约 + 对接文档,前端 UI 不在范围。