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.

59 lines
4.8 KiB

4 weeks ago
# 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
<!-- 每关一票追加一行:closed 票名(链接) — 一句话 gist -->
- [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<moduleCode, DataScopeLevel>` 替代单 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 不在范围。