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.

95 lines
7.8 KiB

4 weeks ago
# 线索域数据隔离在既有数据范围机制上的落地
Type: grilling
Status: blocked
Blocked by: 产品确认(权限与数据可见性-PRD.md)
## Question
三类角色(销售/部门负责人/超管)对线索、公海池的可见范围与操作权差异极大,全域横切。需定清如何用既有机制(`DataScopeEnum` + `DataScopeInterceptor` + `SysRoleDataScope` + ADR-0018 按模块可配)表达,而非新建机制。
需在本 ticket 定清:
1. **线索模块暴露的权限点**(`clue:*`)清单:公海查看/领取/关注、我的线索 CRUD、线索管理(分配/合并/删除/导入导出)、线索设置(公海池 CRUD/导入导出)分别对应哪些权限点。参考 crm-dict 的 `dict:*` 做法。
2. **数据范围维度**:线索的数据范围按什么过滤?
- 销售:SELF(仅本人领取的线索)+ 可见本部门/本团队公海线索;
- 部门负责人:DEPT / DEPT_AND_CHILD(本部门全部公海+私海);
- 超管:ALL。
- 这四档能否直接套 `DataScopeEnum`?公海线索"按公海池归属部门过滤" vs 私海线索"按领取人所属部门过滤",是不是两条不同的数据范围规则?
3. **公海池(crm-rule 侧)的数据范围**:部门负责人只见本部门池、超管全见——同样套 `DataScopeEnum` 还是另有规则。
4. **ADR-0018 的"按模块可配"如何应用于 crm-lead / crm-rule**:这两个模块是否各自注册为一个可配数据范围模块。
产出:权限点清单 + 数据范围规则表(角色 × 视图 × 过滤维度),写入 ## Answer。为四视图列表 ticket 和公海池实体 ticket 提供隔离规则。
## 已 grill 定稿的核心模型(数据隔离两层叠加)
**最终 WHERE = 第一层业务过滤 AND 第二层数据范围**,分工如下:
### 第一层 —— 业务过滤(service 显式 SQL,定"哪个视图")
- "我的线索" = `owner_id = 当前用户 AND status IN (…)`
- "我的关注" = `id IN (当前用户关注表)`
- "线索公海" = `status = 待领取 AND 公海池 ∈ 当前用户够得着的池`
- "线索管理" = 治理视图,业务条件按筛选项
- **"仅本人"的收窄完全靠第一层 `owner_id = 我`,不依赖第二层。**
### 第二层 —— 数据范围(DataScopeInterceptor 自动 AND,按角色档位)
- **lead 表只挂一个** `@DataScope(module = "lead", deptColumn = "dept_id")`,**只按 `dept_id` 做部门档兜底,不使用 owner 列**(owner 过滤下放到第一层)。
- 档位折算(既有机制):销售 → **DEPT(本部门,默认;可由管理员按 ADR-0008 调宽,需用户复核是否要放到全平台)**;部门负责人 → DEPT_AND_CHILD(本部门及下属);超管 → ALL。
- **`dept_id` 存"公海池的归属部门"**:待领取线索也有 dept_id(=其所属公海池的归属部门),第二层 `dept_id IN 本部门集` 才能正确圈出"本部门的公海线索";已领取线索的 dept_id 仍取所属池归属部门(不随领取人主部门变动——见下 Open question [I])。
### 这样为什么不冲突
三视图查同一张 lead 表,第二层注入的都是同一角色档位的 `dept_id IN(...)`(或 ALL 不注入),不打架;"我的 vs 公海"的区分完全由第一层业务条件承担。撞不到"一表两列"的机制上限。
## 已 grill 定稿(I / K 收口)
- **[I] 线索 `dept_id` 恒定、永不变动**(选 I-1)。依据:销售只能领取他所属部门公海池里的线索,故领取人与池必同部门。领取前 dept_id=池归属部门,领取后即使不改也恰等于领取人部门——两种做法结果一致,取简单:`dept_id` 恒等于所属公海池归属部门,全生命周期不改。
- **[K] 公海池表也做数据范围隔离,且复用 `lead` 模块**(选 K-1 + K-1a)。部门负责人只能管本部门池、超管管全部;crm-rule 侧池表挂 `@DataScope(module="lead", deptColumn="...池归属部门列")`,与线索共用同一档数据范围配置(池归属部门 = 线索 dept_id,业务上同源,无需分开配)。
## Open questions(已全部收口)
- **[J] 接口/按钮点位候选清单** — 已以“可被管控点位候选清单”形式产出(见下方 J 段)。
## 已 grill 定稿:权限模型(两套正交机制)
线索域的访问控制 = **两套彼此正交、机制不同**的东西,spec 必须分开写:
### 机制一:功能权限 = present-then-enforce(运行期配置驱动,非硬编码)
- 由**权限点管理**录入才控制;**未录入即放行**(不是代码里预定义固定清单)。
- 每个权限点录两样:**权限编码**(= 前端按钮编码,控按钮显隐)+ **API URL**(= 后端接口权限,控接口调用)。
- 因此 05 **不产出硬编码权限编码表**,而产出 **crm-lead / crm-rule 对外暴露的"可被管控点位"候选**:接口 URL 清单 + 关键前端按钮点位,供管理员按需录入权限点管理。想控哪个录哪个。
### 机制二:数据范围 = 代码强制、始终生效(与权限点配不配无关)
- `@DataScope(module="lead")` + `DataScopeInterceptor` 注入,**always-on**。
- 档位:销售 DEPT(本部门)、部门负责人 DEPT_AND_CHILD、超管 ALL;公海池表复用 `lead` 模块(K-1a)。
- 不受"该操作有没有配功能权限点"影响——即使某按钮没被纳管,数据范围过滤照样生效。
> 两者关系:功能权限管"这个按钮/接口能不能用(配了才管)";数据范围管"能作用到哪些行(代码恒管)"。二者 AND 叠加,互不替代。
## J:可被管控点位候选清单(供权限点管理录入,非硬编码)
按原型三角色 × 操作矩阵整理的**候选点位**(管理员择需录入;此处仅为"有哪些点位可纳管"的备选表,不是运行期一定生效的清单):
**crm-lead 接口/按钮候选**:公海查看·领取·关注、我的线索 查看·新增·编辑·导入·导出、反馈(含暂存/文件)、转商机、释放、关注/取关、线索管理页访问、分配/批量分配、线索合并、单条/批量删除、管理侧导入·导出、回收任意私海线索、激活失效线索(状态机 F2)。
**crm-rule 接口/按钮候选**(公海池配置):线索设置-公海池 查看·新增·编辑·删除·导入导出。
> 原型里"部门负责人只管本部门/超管管全部"这类**部门收窄由机制二(数据范围)自动完成**,不靠功能权限点表达。编码格式(前端按钮编码 + API URL)在权限点管理录入时给定,spec 不预置固定编码串。
## 【挂起】可见性模型升级为三机制 — 等产品确认
原"两层 AND"模型无法表达"拉人进团队成员/设协作人即越过部门天花板"。已升级为三机制,产出对齐稿 `权限与数据可见性-PRD.md`
1. 功能权限(present-then-enforce)
2. 数据可见范围(按模块四档天花板,多角色取最宽)—既有
3. 行级授权(通行证,越过天花板)—新增跨模块通用能力
组合: `能否看到某数据 = 页面业务条件 AND (落在数据可见范围 OR 已被行级授权)`
行级授权=crm-auth/crm-base机制增强(超线索边界,需独立ticket): 通用表 sys_row_grant + 拦截器 OR/EXISTS 注入 + @DataScope声明授权来源翻译。建模难点(粒度不一致): 商机=按单条授权; 线索池负责人/协作人=按池(动态集合)授权。resource_type记"授权对象类型"(可≠被查数据类型),查询时翻译授权对象→可见数据(直接/间接)。
### 待产品确认(挂起)
- [PRD-1]行级授权触发动作清单是否完整
- [PRD-2]被授权者权限边界(只读/可操作,是否分级)
- [PRD-3]移除授权即时失效确认
- [D]线索是否存在跨部门逐条指派(决定要不要resource_type=lead直接授权通道)
- [E]池负责人/协作人授权范围(仅池内线索/兼池配置可管)