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.
 
 
 
 
 

5.3 KiB

03 公海规则「绑定商机(数量)」出参(P2 挂账毕业)

Type: task Status: resolved

Claimed 2026-09-08 wayfinder session。同日 resolved(见下 Answer)。

Question

一期记账(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

  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 复核过。