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.
 
 
 
 
 
 

217 lines
12 KiB

# -*- coding: utf-8 -*-
"""票 07 客户侧接线脚本(常量 + Service create/update + 导入执行器聚合 + 测试构造点)"""
import io, sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
# 1) CustomerConstants:字典分组常量(插在类尾 } 之前)
p = 'crm-customer/src/main/java/com/crm/customer/constant/CustomerConstants.java'
txt = open(p, encoding='utf-8').read()
assert txt.rstrip().endswith('}')
idx = txt.rstrip().rfind('}')
block = ('\n /*-------- 字典分组编码(crm-dict 字段-分组绑定,202609 引用计数接线)--------*/\n\n'
' String DICT_GROUP_CUSTOMER_TYPE = "customer_type";\n\n'
' String DICT_GROUP_INDUSTRY = "industry";\n')
txt = txt.rstrip()[:idx].rstrip() + '\n' + block + '}\n'
open(p, 'w', encoding='utf-8').write(txt)
print('CustomerConstants ok')
# 2) CustomerServiceImpl:依赖 + create/update 随迁 + detail 字面量换常量
p = 'crm-customer/src/main/java/com/crm/customer/service/impl/CustomerServiceImpl.java'
txt = open(p, encoding='utf-8').read()
old = (' private final CustomerOwnershipWriter ownershipWriter;\n'
' private final CustomerNoGenerator noGenerator;\n')
new = (' private final CustomerOwnershipWriter ownershipWriter;\n'
' private final CustomerNoGenerator noGenerator;\n'
' private final com.crm.dict.service.DictReferenceService dictReferenceService;\n')
assert old in txt, 'svc fields anchor'
txt = txt.replace(old, new, 1)
old = (' public CustomerServiceImpl(CustomerOplogWriter oplogWriter,\n'
' CustomerDedupService dedupService,\n'
' DictQueryService dictQueryService,\n'
' CompanyLookupPort companyLookupPort,\n'
' CustomerOwnershipWriter ownershipWriter,\n'
' CustomerNoGenerator noGenerator) {\n')
new = (' public CustomerServiceImpl(CustomerOplogWriter oplogWriter,\n'
' CustomerDedupService dedupService,\n'
' DictQueryService dictQueryService,\n'
' CompanyLookupPort companyLookupPort,\n'
' CustomerOwnershipWriter ownershipWriter,\n'
' CustomerNoGenerator noGenerator,\n'
' com.crm.dict.service.DictReferenceService dictReferenceService) {\n')
assert old in txt, 'svc ctor anchor'
txt = txt.replace(old, new, 1)
old = ' this.noGenerator = noGenerator;\n }'
new = ' this.noGenerator = noGenerator;\n this.dictReferenceService = dictReferenceService;\n }'
assert old in txt, 'svc ctor body anchor'
txt = txt.replace(old, new, 1)
# create:save 成功后三字典字段入账
old = (' oplogWriter.write(entity.getId(), CustomerConstants.ACTION_CREATE,\n'
' "新增客户【" + entity.getCustomerName() + "");\n'
' return savedResult(entity);')
new = (' // 字典编码引用计数随迁(202609 拍板 1):三个字典字段入账,与创建同一事务\n'
' dictReferenceService.applyFieldChange(CustomerConstants.DICT_GROUP_CUSTOMER_TYPE,\n'
' null, entity.getCustomerType());\n'
' dictReferenceService.applyFieldChange(CustomerConstants.DICT_GROUP_INDUSTRY,\n'
' null, entity.getIndustryCode());\n'
' dictReferenceService.applyFieldChange(CustomerConstants.DICT_GROUP_INDUSTRY,\n'
' null, entity.getIndustryChildCode());\n'
' oplogWriter.write(entity.getId(), CustomerConstants.ACTION_CREATE,\n'
' "新增客户【" + entity.getCustomerName() + "");\n'
' return savedResult(entity);')
assert old in txt, 'svc create anchor'
txt = txt.replace(old, new, 1)
# update:拷贝前抓旧值,CAS 成功后随迁
old = (' // 先 diff(copy 会污染旧值)再全量覆盖;version 一并拷贝以驱动 CAS 比对\n'
' List<String> changes = diffFields(exist, dto);')
new = (' // 先 diff(copy 会污染旧值)再全量覆盖;version 一并拷贝以驱动 CAS 比对\n'
' // 字典字段旧值在拷贝前抓取(202609 拍板 1 引用计数随迁用)\n'
' String oldCustomerType = exist.getCustomerType();\n'
' String oldIndustryCode = exist.getIndustryCode();\n'
' String oldIndustryChildCode = exist.getIndustryChildCode();\n'
' List<String> changes = diffFields(exist, dto);')
assert old in txt, 'svc update pre-copy anchor'
txt = txt.replace(old, new, 1)
old = (' if (!updated) {\n'
' throw new BusinessErrorException(CustomerConstants.CODE_CAS_FAIL,\n'
' "客户已被他人修改,请刷新后重试");\n'
' }\n')
new = (' if (!updated) {\n'
' throw new BusinessErrorException(CustomerConstants.CODE_CAS_FAIL,\n'
' "客户已被他人修改,请刷新后重试");\n'
' }\n'
' // 字典编码引用计数随迁(202609 拍板 1):全量覆盖语义,与编辑同一事务\n'
' dictReferenceService.applyFieldChange(CustomerConstants.DICT_GROUP_CUSTOMER_TYPE,\n'
' oldCustomerType, exist.getCustomerType());\n'
' dictReferenceService.applyFieldChange(CustomerConstants.DICT_GROUP_INDUSTRY,\n'
' oldIndustryCode, exist.getIndustryCode());\n'
' dictReferenceService.applyFieldChange(CustomerConstants.DICT_GROUP_INDUSTRY,\n'
' oldIndustryChildCode, exist.getIndustryChildCode());\n')
assert old in txt, 'svc update post-cas anchor'
txt = txt.replace(old, new, 1)
# detail:字面量换常量
txt = txt.replace('getNames("customer_type",', 'getNames(CustomerConstants.DICT_GROUP_CUSTOMER_TYPE,', 1)
txt = txt.replace('getNames("industry",', 'getNames(CustomerConstants.DICT_GROUP_INDUSTRY,', 1)
open(p, 'w', encoding='utf-8').write(txt)
print('CustomerServiceImpl ok')
# 3) CustomerImportExecutor:依赖 + 聚合 delta + 收尾 batchApply
p = 'crm-customer/src/main/java/com/crm/customer/task/CustomerImportExecutor.java'
txt = open(p, encoding='utf-8').read()
old = (' private final CustomerOwnershipWriter ownershipWriter;\n'
' private final CustomerNoGenerator noGenerator;\n')
new = (' private final CustomerOwnershipWriter ownershipWriter;\n'
' private final CustomerNoGenerator noGenerator;\n'
' private final com.crm.dict.service.DictReferenceService dictReferenceService;\n')
assert old in txt, 'exec fields anchor'
txt = txt.replace(old, new, 1)
# run 循环:声明聚合容器
old = (' // 文件内编号 → 客户 id(新增行落库后回填 / 命中行直接放),供联系人 sheet 挂接\n'
' Map<String, Long> customerIdByNo = new HashMap<>();\n')
new = (' // 文件内编号 → 客户 id(新增行落库后回填 / 命中行直接放),供联系人 sheet 挂接\n'
' Map<String, Long> customerIdByNo = new HashMap<>();\n'
' // 字典编码引用变化聚合(202609 拍板 1 / ADR-0015:导入路径禁逐行增减,收尾一次 batchApply)\n'
' Map<String, Map<String, Integer>> dictDeltas = new HashMap<>();\n')
assert old in txt, 'exec loop anchor'
txt = txt.replace(old, new, 1)
# 两个调用点传容器
old = 'Customer created = createCustomer(p, task, submitter);'
new = 'Customer created = createCustomer(p, task, submitter, dictDeltas);'
assert old in txt, 'exec create call anchor'
txt = txt.replace(old, new, 1)
old = 'if (applyUpdate(p, task)) {'
new = 'if (applyUpdate(p, task, dictDeltas)) {'
assert old in txt, 'exec update call anchor'
txt = txt.replace(old, new, 1)
# 客户 sheet 循环结束后冲账(联系人行与字典无关,越早冲账越少飘移窗口)
old = ' // ===== 联系人 sheet:原子单元独立,失败不回滚主记录 ====='
new = (' // 字典编码引用计数收尾冲账(客户 sheet 全部落库后一次 batchApply)\n'
' dictReferenceService.batchApply(dictDeltas);\n\n'
' // ===== 联系人 sheet:原子单元独立,失败不回滚主记录 =====')
assert old in txt, 'exec flush anchor'
txt = txt.replace(old, new, 1)
# createCustomer 签名 + 入账
old = (' private Customer createCustomer(CustomerImportAnalyzer.RowPlan p,\n'
' CustomerImportTask task, AuthUser submitter) {')
new = (' private Customer createCustomer(CustomerImportAnalyzer.RowPlan p,\n'
' CustomerImportTask task, AuthUser submitter,\n'
' Map<String, Map<String, Integer>> dictDeltas) {')
assert old in txt, 'exec create sig anchor'
txt = txt.replace(old, new, 1)
old = (' customerMapper.insert(c);\n'
' oplogWriter.writeSystem(c.getId(), CustomerConstants.ACTION_CREATE,\n'
' "导入任务【" + task.getId() + "】新增客户");\n'
' return c;\n }')
new = (' customerMapper.insert(c);\n'
' accumulateDictDelta(dictDeltas, CustomerConstants.DICT_GROUP_CUSTOMER_TYPE, null, c.getCustomerType());\n'
' accumulateDictDelta(dictDeltas, CustomerConstants.DICT_GROUP_INDUSTRY, null, c.getIndustryCode());\n'
' oplogWriter.writeSystem(c.getId(), CustomerConstants.ACTION_CREATE,\n'
' "导入任务【" + task.getId() + "】新增客户");\n'
' return c;\n }')
assert old in txt, 'exec create body anchor'
txt = txt.replace(old, new, 1)
# applyUpdate 签名 + 旧值抓取 + 入账
old = (' private boolean applyUpdate(CustomerImportAnalyzer.RowPlan p, CustomerImportTask task) {\n'
' Customer c = p.getMatched();\n'
' CustomerImportRow r = p.getRow();\n'
' boolean changed = false;\n')
new = (' private boolean applyUpdate(CustomerImportAnalyzer.RowPlan p, CustomerImportTask task,\n'
' Map<String, Map<String, Integer>> dictDeltas) {\n'
' Customer c = p.getMatched();\n'
' CustomerImportRow r = p.getRow();\n'
' boolean changed = false;\n'
' String oldCustomerType = c.getCustomerType();\n'
' String oldIndustryCode = c.getIndustryCode();\n')
assert old in txt, 'exec update sig anchor'
txt = txt.replace(old, new, 1)
old = (' if (!changed) {\n'
' return false;\n'
' }\n'
' customerMapper.updateById(c);')
new = (' if (!changed) {\n'
' return false;\n'
' }\n'
' accumulateDictDelta(dictDeltas, CustomerConstants.DICT_GROUP_CUSTOMER_TYPE, oldCustomerType, c.getCustomerType());\n'
' accumulateDictDelta(dictDeltas, CustomerConstants.DICT_GROUP_INDUSTRY, oldIndustryCode, c.getIndustryCode());\n'
' customerMapper.updateById(c);')
assert old in txt, 'exec update body anchor'
txt = txt.replace(old, new, 1)
# 聚合助手(挂在 insertFail 前)
old = ' private void insertFail(CustomerImportTask task, String sheetName, int rowNum,'
new = (' /** 行级字典 delta 聚合:空白归一 null、相等不动,正加负减并入嵌套 map(收尾一次 batchApply) */\n'
' private static void accumulateDictDelta(Map<String, Map<String, Integer>> deltas,\n'
' String groupCode, String oldCode, String newCode) {\n'
' String oldCodeNorm = isBlank(oldCode) ? null : oldCode.trim();\n'
' String newCodeNorm = isBlank(newCode) ? null : newCode.trim();\n'
' if (oldCodeNorm == null ? newCodeNorm == null : oldCodeNorm.equals(newCodeNorm)) {\n'
' return;\n'
' }\n'
' if (oldCodeNorm != null) {\n'
' deltas.computeIfAbsent(groupCode, k -> new HashMap<>()).merge(oldCodeNorm, -1, Integer::sum);\n'
' }\n'
' if (newCodeNorm != null) {\n'
' deltas.computeIfAbsent(groupCode, k -> new HashMap<>()).merge(newCodeNorm, 1, Integer::sum);\n'
' }\n'
' }\n\n'
' private void insertFail(CustomerImportTask task, String sheetName, int rowNum,')
assert old in txt, 'exec helper anchor'
txt = txt.replace(old, new, 1)
open(p, 'w', encoding='utf-8').write(txt)
print('CustomerImportExecutor ok')