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.
62 lines
6.1 KiB
62 lines
6.1 KiB
|
3 weeks ago
|
---
|
||
|
|
status: accepted
|
||
|
|
---
|
||
|
|
|
||
|
|
# 线索模块批量操作与视图统计接口补全(G1–G5)
|
||
|
|
|
||
|
|
线索原型(A2-1-1 / A2-1-4 / A7-3-1 + 分配/激活弹窗)要求**批量操作**与**顶部统计卡片**,但已实现代码只有单条流转、无批量、无统计。本 ADR 把接口完整性复核(grill-with-docs)定稿的 G1–G5 固化为架构决策。范围**只含 G1–G5**;G6(线索导入/导出)/ G7(线索合并)/ G8(公海池导入/导出)维持既有 Out of scope(见 `.scratch/clue-module/map.md`)。
|
||
|
|
|
||
|
|
决策来源:`.scratch/clue-module/线索业务-PRD.md` §13(2026 grill-with-docs,决策清单 B1–B9)。
|
||
|
|
|
||
|
|
## Decisions
|
||
|
|
|
||
|
|
### D1 批量操作:非原子、逐条 CAS、部分成功
|
||
|
|
|
||
|
|
批量领取/分配到池/分配到销售/释放/激活/删除,统一**逐条委派给对应单条操作**(各自独立事务),任一条失败只记入结果、不拖垮整批。承接 ADR-0021:批量的「失败」= 单条 CAS 行数 0 或单条守卫抛出的业务错误。
|
||
|
|
|
||
|
|
- URL:单条动词 + `-batch` 后缀(`/api/lead/claim-batch` 等),与既有短横线风格一致(B4)。
|
||
|
|
- 入参:`@RequestParam("ids") List<Long>`;批量分配双深度——到池带 `poolId`,到销售带 `userId`(与单条 `assign-pool`/`assign-user` 对称,B5)。
|
||
|
|
- 逐条独立事务的落地:`LeadServiceImpl` 用 `@Lazy` self-injection 走 AOP 代理调单条方法(沿用 `AuthServiceImpl` 既有模式),批量方法本身不加 `@Transactional`。
|
||
|
|
|
||
|
|
### D2 批量返回 `Result<BatchResult<F>>`(B2/B3)
|
||
|
|
|
||
|
|
- **`BatchResult<F>`(泛型骨架)放 crm-base/domain/result**,与 `Result`/`PageResult` 并列,只装 `total/successCount/failCount/List<F> failures`,对失败项内部结构无感知——保持 crm-base 非业务纯净。
|
||
|
|
- **线索域失败项 `LeadBatchFailItem`(leadId + `LeadBatchFailReason` + message)放 crm-lead**。
|
||
|
|
- **`LeadBatchFailReason` 新造语义枚举**(`ALREADY_CONVERTED` / `CONCURRENT_MODIFIED` / `OVER_HOLD_LIMIT` / `OVER_DAILY_LIMIT` / `STATUS_NOT_ALLOWED` / `NOT_OWNER`,各带 `code` 回指 ResultCode 65xxx,B6),供前端按失败类型聚合展示「成功 N 条 / 失败 M 条(各类型明细)」。
|
||
|
|
|
||
|
|
### D3 视图统计接口 `/api/lead/stats`(B7/B8)
|
||
|
|
|
||
|
|
- 入参**复用 `LeadPageParam`**,返回 `Result<LeadStatsDTO>`(total/claimed/converted/todayNew/undistributed)。
|
||
|
|
- **统计口径 = 当前视图数据集口径,非全库**:与 `/page` 吃完全相同的 viewType + 筛选 + `@DataScope` 部门天花板,只把「取一页」换成「按 status 分组计数」。接口通用、全量返回 5 个计数,前端按需取。
|
||
|
|
- `claimed`(「已被领取」,展示文案前端渲染)= status IN (已领取 3, 跟进中 4)。
|
||
|
|
|
||
|
|
### D4 复合卡片下钻:`LeadPageParam.status` 单值 → `statusIn` 多值(B9)
|
||
|
|
|
||
|
|
统计卡片可点击下钻,把该卡状态条件塞进 `LeadPageParam` 再调 `/page`。「已被领取」是 status IN(3,4) 的并集,单值 `Integer` 表达不了,故 `status` 升级为 `List<Integer> statusIn`,`/page` 与 `/stats` 共用。改动落在接口未联调/未发文档阶段,成本低。
|
||
|
|
|
||
|
|
### D5 公海池批量删除的失败项归属(跨模块依赖方向约束)
|
||
|
|
|
||
|
|
`pool/delete-batch` 属 crm-rule;但 `LeadBatchFailItem`/`LeadBatchFailReason` 在 crm-lead,而 **crm-lead 依赖 crm-rule,crm-rule 不能反向依赖 crm-lead(否则成环)**。故池批量删除**不能**复用线索的失败项类型。
|
||
|
|
|
||
|
|
**决策:crm-rule 自建失败项,复用 crm-base 的 `BatchResult<F>` 泛型骨架(方案 A)。**
|
||
|
|
|
||
|
|
- `PoolBatchFailReason`(crm-rule 域枚举,各值带 `code` 回指 `RuleConstants` 64xxx):`NOT_EXIST`(64004)、`HAS_ACTIVE_LEAD`(64005,池下有非终态线索)、外加 `UNKNOWN` 兜底。
|
||
|
|
- `PoolBatchFailItem`(poolId + `PoolBatchFailReason` + message)放 crm-rule。
|
||
|
|
- `pool/delete-batch` 返回 `Result<BatchResult<PoolBatchFailItem>>`,与线索侧对称。
|
||
|
|
- 与线索侧完全对称:crm-base 出泛型骨架,各业务域自持失败语义枚举 + 失败项,天然无环。
|
||
|
|
|
||
|
|
> 注:`CODE_POOL_HAS_ACTIVE_LEAD`(64005) 当前是**预留常量**——单条 `deletePool` 尚未实现「池下有非终态线索则拒删」的守卫。**本期不补该守卫**(grill 决策):`PoolBatchFailReason.HAS_ACTIVE_LEAD` 同样预留,本期池批量删除实际只会产出 `NOT_EXIST`/`UNKNOWN`。理由:该守卫是独立业务规则,且其查询需跨 crm-rule→线索表(又触及 crm-rule 不能依赖 crm-lead 的方向难题),值得单独 ADR/issue 设计查询归属,不在本批量补全范围。枚举值预留不影响契约,后续补守卫时零契约变更。
|
||
|
|
|
||
|
|
## Considered Options(D5)
|
||
|
|
|
||
|
|
- **A crm-rule 自建失败枚举/失败项(选中)**(`PoolBatchFailReason` + `PoolBatchFailItem`,同样装进 crm-base 的 `BatchResult<F>`)——各域自持失败语义,crm-base 泛型骨架复用,无环;池删除失败原因(池下有非终态线索 vs 不存在)可结构化返回,前端能分类提示。
|
||
|
|
- **B `pool/delete-batch` 返回 `BatchResult<Long>`**(failures 只装失败的 poolId,不带原因枚举)——被否:最省但丢失「为何失败」,前端无法分类展示,而「池下有非终态线索」是需要明确提示用户的业务态。
|
||
|
|
- **C 池批量删除本期不做**——被否:原型 A7-3-1 明确有「批量删除公海池」,且 A 成本可控。
|
||
|
|
|
||
|
|
## Consequences
|
||
|
|
|
||
|
|
- 新增 `crm-base` 通用件 `BatchResult<F>`,可被任意业务域批量接口复用。
|
||
|
|
- crm-lead 新增 `stats` 读接口与 6 个 `-batch` 写接口;`LeadPageParam` 契约变更(status→statusIn),需同步读侧 wrapper 与相关测试。
|
||
|
|
- 批量接口不引入新的上限校验逻辑——`OVER_HOLD_LIMIT`/`OVER_DAILY_LIMIT` 枚举值预留给单条操作后续补齐上限校验时自然生效(当前单条 `claimLead`/`assignToUser` 尚未实现上限校验,属既有 gap,不在本 ADR 范围)。
|
||
|
|
- D5 依赖方向约束:crm-base 出泛型 `BatchResult<F>`,crm-lead 与 crm-rule 各自持有本域失败项/失败原因枚举,避免跨业务域依赖成环——此模式作为后续任何模块批量接口的范式。
|