Browse Source

fix(opportunity): 转商机契约补省市两级地区,修丢字段脏数据

根因:转商机命令 CreateOpportunityCmd 只带单个 regionCode,而源头线索与
目的地商机主表都存 provinceCode+cityCode 两级(均 not null)。命令丢了一
级信息,商机侧被迫两列同填一个 code → 省份列存市/区 code,列表区划回显错。
这是命令契约丢信息,非缺拆分算法(两级值源头本就都有)。

修(补契约字段,非拆分):
- CreateOpportunityCmd:regionCode → provinceCode + cityCode
- ConvertOpportunityParam:同拆两字段(转商机弹窗地区选择器本就省市两级)
- LeadServiceImpl:必填校验拆两字段 + 组装 cmd 透传 param 的省/市
- OpportunityCreationPortImpl:分别 set 两列,删同填占位与「省市拆分待专票」TODO
- 单测:断言省/市两级分别落库(110000/110100);既有 LeadServiceImplTest 同步拆

mvn -pl crm-opportunity -am test 152 全绿 + crm-lead 116 全绿。
master
luoweijian 2 weeks ago
parent
commit
e4e4cc2347
  1. 7
      crm-lead/src/main/java/com/crm/lead/domain/param/ConvertOpportunityParam.java
  2. 6
      crm-lead/src/main/java/com/crm/lead/port/outbound/CreateOpportunityCmd.java
  3. 5
      crm-lead/src/main/java/com/crm/lead/service/impl/LeadServiceImpl.java
  4. 3
      crm-lead/src/test/java/com/crm/lead/service/impl/LeadServiceImplTest.java
  5. 15
      crm-opportunity/src/main/java/com/crm/opportunity/port/inbound/impl/OpportunityCreationPortImpl.java
  6. 5
      crm-opportunity/src/test/java/com/crm/opportunity/port/inbound/OpportunityCreationPortImplTest.java

7
crm-lead/src/main/java/com/crm/lead/domain/param/ConvertOpportunityParam.java

@ -27,8 +27,11 @@ public class ConvertOpportunityParam {
@Schema(description = "意向客户 id(票 11;下拉选客户得到,引用 A4 客户模块。A4 下拉未就绪前可空)")
private Long customerId;
@Schema(description = "地区国标 code(必填)")
private String regionCode;
@Schema(description = "省份国标 code(必填)")
private String provinceCode;
@Schema(description = "市/区国标 code(必填)")
private String cityCode;
@Schema(description = "备注(自由文本,可空)")
private String remark;

6
crm-lead/src/main/java/com/crm/lead/port/outbound/CreateOpportunityCmd.java

@ -12,7 +12,8 @@ package com.crm.lead.port.outbound;
* @param customerId 意向客户 id 11下拉选客户得到引用 A4 客户模块
* <b>暂可空</b>A4 客户下拉未就绪前前端传 null商机侧跳过建关联子表
* A4 就绪后收紧为必填 11 终态契约
* @param regionCode 地区国标 codevarchar 12必填
* @param provinceCode 省份国标 code从线索带出必填
* @param cityCode /区国标 code从线索带出必填
* @param remark 备注自由文本可空
* @param sourceLeadName 线索名称快照
* @param sourcePhone 联系电话快照
@ -28,7 +29,8 @@ public record CreateOpportunityCmd(
String partyA,
String intendedCustomer,
Long customerId,
String regionCode,
String provinceCode,
String cityCode,
String remark,
String sourceLeadName,
String sourcePhone,

5
crm-lead/src/main/java/com/crm/lead/service/impl/LeadServiceImpl.java

@ -346,7 +346,8 @@ public class LeadServiceImpl extends BaseServiceImpl<LeadMapper, Lead>
if (StrUtil.isBlank(param.getOpportunityName())
|| StrUtil.isBlank(param.getIndustryCode())
|| StrUtil.isBlank(param.getIntendedCustomer())
|| StrUtil.isBlank(param.getRegionCode())) {
|| StrUtil.isBlank(param.getProvinceCode())
|| StrUtil.isBlank(param.getCityCode())) {
throw new BusinessErrorException(LeadConstants.CODE_LEAD_INVALID,
"商机名称、行业、意向客户、地区为必填");
}
@ -362,7 +363,7 @@ public class LeadServiceImpl extends BaseServiceImpl<LeadMapper, Lead>
opportunityId = port.createOpportunity(new CreateOpportunityCmd(
lead.getId(), param.getOpportunityName(), param.getIndustryCode(),
param.getPartyA(), param.getIntendedCustomer(), param.getCustomerId(),
param.getRegionCode(),
param.getProvinceCode(), param.getCityCode(),
param.getRemark(), lead.getLeadName(), lead.getPhone(),
lead.getProductCode(), lead.getOwnerUserId(),
lead.getOwnerDeptIdSnapshot(), lead.getPoolId()));

3
crm-lead/src/test/java/com/crm/lead/service/impl/LeadServiceImplTest.java

@ -182,7 +182,8 @@ class LeadServiceImplTest {
param.setOpportunityName("测试商机");
param.setIndustryCode("it");
param.setIntendedCustomer("测试客户");
param.setRegionCode("440100");
param.setProvinceCode("440000");
param.setCityCode("440100");
return param;
}

15
crm-opportunity/src/main/java/com/crm/opportunity/port/inbound/impl/OpportunityCreationPortImpl.java

@ -25,13 +25,10 @@ import java.time.LocalDateTime;
* <p><b> 12 唯一性预检</b>新建前查 {@code source_lead_id} 是否已被占用命中即抛业务错误
* 避免依赖 DB 唯一键冲突走错误路径DB {@code uk(source_lead_id)} 仍作并发最终防线</p>
*
* <p><b> 13 关联客户</b>{@code cmd.customerId()} 非空时建一条 {@code is_primary_intended=1}
* <p><b> A4 走法甲</b>{@code cmd.customerId()} 非空时建一条 {@code is_primary_intended=1}
* 主要意向客户子表 + 刷主表冗余 {@code primary_customer_id/name} 03 C3
* <b>customerId 走法甲 11</b>A4 客户下拉未就绪前 customerId 可空此时跳过建子表 /
* 不刷冗余不倒退现有自由文本转商机A4 就绪后收紧为必填</p>
*
* <p><b>省市 TODO</b>cmd 仅带单个 {@code regionCode}主表分 {@code provinceCode}/{@code cityCode}
* 两列且 not null省市拆分逻辑无 spec非本票职责暂两列同填 regionCode 占位省市拆分专票</p>
* A4 客户下拉未就绪前 customerId 可空此时跳过建子表 / 不刷冗余不倒退现有自由文本转商机
* A4 就绪后收紧为必填</p>
*/
@Service
@RequiredArgsConstructor
@ -74,9 +71,9 @@ public class OpportunityCreationPortImpl implements OpportunityCreationPort {
opp.setIndustryCode(cmd.industryCode());
opp.setPartyA(cmd.partyA());
opp.setRemark(cmd.remark());
// 省市 TODO:单 regionCode → 两列 not null,暂同填占位,待省市拆分专票
opp.setProvinceCode(cmd.regionCode());
opp.setCityCode(cmd.regionCode());
// 省/市国标 code(与线索同构两级,由转商机弹窗地区选择器带出)
opp.setProvinceCode(cmd.provinceCode());
opp.setCityCode(cmd.cityCode());
// 归属 / 来源快照(从线索带出)
opp.setOwnerUserId(cmd.ownerUserId());
opp.setOwnerDeptId(cmd.ownerDeptId());

5
crm-opportunity/src/test/java/com/crm/opportunity/port/inbound/OpportunityCreationPortImplTest.java

@ -48,7 +48,7 @@ class OpportunityCreationPortImplTest {
private CreateOpportunityCmd cmd(Long customerId) {
return new CreateOpportunityCmd(
LEAD_ID, "商机A", "ind01", "甲方X", "意向客户名", customerId,
"110100", "备注", "线索A", "13800000000", "prod01",
"110000", "110100", "备注", "线索A", "13800000000", "prod01",
100L, 200L, 300L);
}
@ -128,6 +128,9 @@ class OpportunityCreationPortImplTest {
assertThat(saved.getSourceLeadId()).isEqualTo(LEAD_ID);
assertThat(saved.getClaimTime()).isNotNull();
assertThat(saved.getPrimaryCustomerNameSnapshot()).isEqualTo("意向客户名");
// 省/市两级分别落库(不再同填同一 code)
assertThat(saved.getProvinceCode()).isEqualTo("110000");
assertThat(saved.getCityCode()).isEqualTo("110100");
}
@Test

Loading…
Cancel
Save