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.
55 lines
5.3 KiB
55 lines
5.3 KiB
|
5 hours ago
|
# 03 公海规则「绑定商机(数量)」出参(P2 挂账毕业)
|
||
|
|
|
||
|
|
Type: task
|
||
|
|
Status: resolved
|
||
|
|
|
||
|
|
> Claimed 2026-09-08 wayfinder session。同日 resolved(见下 Answer)。
|
||
|
|
|
||
|
|
## Question
|
||
|
|
|
||
|
|
一期记账([gap-report.md](../../a7-parity/gap-report.md) §六):公海规则 detail 出参缺「绑定商机(数量)」,P2、依赖商机侧引用。现状预核:商机表无 pool_rule 引用列,运行时 `OpportunityPoolRuleMatcher.loadPublished().match(ownerDeptId)` 动态匹配——无持久绑定可数。
|
||
|
|
|
||
|
|
**票内拍板并落地**(推荐 B,同一期票 09 查重规则「引用计数=实时统计不落列」先例):
|
||
|
|
|
||
|
|
- A) opportunity 加 pool_rule_id 快照列(掉入/分配时落)——需 DDL + 存量回填,语义 = 历史绑定
|
||
|
|
- B) detail 动态统计:按规则 applyScope 匹配 ownerDeptId count 商机——零 DDL,语义 = 当前适用数。⚠️ 计数口径须与 `OpportunityPoolRuleMatcher.match` 语义一致:部门专用规则 = 该部门商机数;默认规则 = **不命中任何发布中部门专用规则的商机数**(match 兜底语义,非全部商机);公海态(无 owner)商机是否计入票内定。
|
||
|
|
|
||
|
|
**落地清单**:
|
||
|
|
|
||
|
|
1. 统计源走 seam(crm-rule 零反依赖,参照 UserTransferPort 先例):crm-base 定义计数 port,crm-opportunity 实现(按 applyScope 匹配 count),crm-rule 消费。
|
||
|
|
2. OpportunityPoolRule detail 出参补 `boundOppCount`;page 列表是否同步补列票内定(原型列头在列表页)。
|
||
|
|
3. bruno 文档同步(detail 出参表 + 若列表补列则 page 表)。
|
||
|
|
|
||
|
|
验收基线:编译过 + BOM 0 + crm-rule/crm-opportunity 单测全绿(含 seam 行为测试)。
|
||
|
|
|
||
|
|
## Answer
|
||
|
|
|
||
|
|
2026-09-08 执行落地,方案 B(detail 动态统计,零 DDL),全部验收基线达成。
|
||
|
|
|
||
|
|
**落地清单偏差两条(均已记入 [ADR-0035](../../../docs/adr/0035-pool-rule-bound-count-port-seam.md))**:
|
||
|
|
|
||
|
|
1. **计数 port 落位 crm-rule 而非票面 crm-base**:发现 ADR-0025(PoolLeadOccupationPort)是同消费方(crm-rule)同方向既有出站 port 先例,且 crm-opportunity 本就依赖 crm-rule(消费匹配引擎)→ 端口落位 `com.crm.rule.port.outbound.PoolRuleOpportunityCountPort`,实现归 crm-opportunity `adapter` 包(对称 ADR-0025 的 PoolLeadOccupationAdapter),crm-base 不承载规则域词汇,**零 pom 改动**。
|
||
|
|
2. **page 列表不补列**:票面「原型列头在列表页」前提被原型证据推翻——v29 列表页列头 10 项无「绑定商机」,详情页有「绑定商机:N 条」→ 仅 detail 回填,pageRules 零改动。
|
||
|
|
|
||
|
|
**口径三定(票内拍板项)**:
|
||
|
|
|
||
|
|
- 公海态(待领取)**照常计入**:release-pool 保留 `owner_dept_id` 作公海可见锚点,即运行时 match 的输入,claim 守卫按它匹配。
|
||
|
|
- 排除已转项目 `opp_status=5`(绝对终态,UserTransferPort 商机域计数同口径)。
|
||
|
|
- port 缺失(纯 rule 装配)fail-open 按 0 展示——展示型计数非守卫型,fail-safe。
|
||
|
|
- 默认规则计数 = **不命中任何发布中部门专用规则的商机数**(match 兜底语义,map 已钉):排除集 = 发布中专用规则的部门并集(发布约束保证一部门只被一条发布中规则覆盖);`NOT IN` 对 NULL 行恒 UNKNOWN,wrapper 显式 `isNull().or().notIn()` 分支。
|
||
|
|
|
||
|
|
**实现清单(8 文件)**:
|
||
|
|
|
||
|
|
- crm-rule:`PoolRuleOpportunityCountPort`(新建,countByOwnerDepts / countOutsideOwnerDepts)、`OpportunityPoolRuleDTO` +`boundOppCount`(Long,仅详情回填)、`OpportunityPoolRuleServiceImpl`(getRuleDetail 无条件回填 + `countBoundOpp` + `publishedSpecificDeptIds`,ObjectProvider 可选注入)、IOpportunityPoolRuleService javadoc。
|
||
|
|
- crm-opportunity:`PoolRuleOpportunityCountAdapter`(新建,LambdaQueryWrapper 实统计,BaseEntity @TableLogic 自动滤软删)。
|
||
|
|
- 测试:`OpportunityPoolRuleServiceImplTest` +5 用例(deptSpecific 入参断言 / defaultRule 排除集 / 非发布中 verifyNoInteractions / 非默认通用 0 / port 缺失 0,共 32 绿);`PoolRuleOpportunityCountAdapterTest` 新建 4 用例(`getSqlSegment()` + `paramNameValuePairs` 断言 wrapper 物理列与参数,空集合免查 verifyNoInteractions)。
|
||
|
|
- 文档:ADR-0035、crm-rule / crm-opportunity 两份 CONTEXT.md 各补条目(含 _Avoid_ 行)。
|
||
|
|
|
||
|
|
**bruno-sync 对账(范围 = crm-rule 代码维度,URL 前缀 `/api/rule/**`)**:
|
||
|
|
|
||
|
|
- 更新 1:`公海规则详情.bru` 响应 data 结构表 deptNames 行后补 `boundOppCount | string` 行(全局 Jackson Long→String 序列化,类型同 id 为 string;说明取源码 @Schema 初稿)。
|
||
|
|
- 实测响应示例 JSON **不动**——「实测响应摘录(20260828)」为人认可终稿,当时字段尚不存在,改即伪造实测记录。
|
||
|
|
- 分页.bru 不补:BaseDTO 类级 `@JsonInclude(NON_NULL)` → page 不回填时 JSON 无此键,结构表补行反而撒谎(与偏差 2 自洽);版本列表.bru 不动(D-22 独立精简 `OpportunityPoolRuleVersionDTO`,无此字段);save-draft 入参表不动(入参语义白名单,纯出参字段不收);发布/停用/删除/复制响应无 DTO 展开不动。
|
||
|
|
|
||
|
|
**验证**:`mvn test -pl crm-rule,crm-opportunity -am` EXIT=0(surefire 32+4 用例全绿);全仓 Java 文件 BOM 扫描 0;文档仓 patch 后 no-BOM 复核过。
|