Files
ccdi/docs/plans/backend/2026-04-26-enterprise-auto-fill-backend-implementation.md

17 KiB
Raw Blame History

关联业务自动补入实体库 Backend Implementation Plan

执行约束: 按当前项目 AGENTS.md 执行;未获得用户明确要求时不启用 subagent。Steps use checkbox (- [ ]) syntax for tracking.

Goal: 新建和导入员工亲属实体关联、中介实体关联、信贷客户实体关联、招投标供应商时,实体库缺失的企业自动写入 ccdi_enterprise_base_info

Architecture: 新增一个后端内部实体库自动补全服务,统一处理“已存在不覆盖、缺失则最小插入、同批去重、来源和风险等级映射”。各业务 Service 在业务校验通过、业务数据落库前调用该能力;EnterpriseSource 枚举新增 SUPPLIER 并继续由现有 /ccdi/enum/enterpriseSource 接口驱动前端。

Tech Stack: Java 21, Spring Boot 3, MyBatis Plus, JUnit 5, Mockito, Maven.


File Structure

  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/enums/EnterpriseSource.java
    • 新增 SUPPLIER("SUPPLIER", "供应商")
  • Create: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java
    • 内部补全服务,封装单条和批量实体补入。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationServiceImpl.java
    • 新建员工亲属实体关联前补实体库。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java
    • 导入成功行批量补实体库。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationServiceImpl.java
    • 新建信贷客户实体关联前补实体库。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationImportServiceImpl.java
    • 导入成功行批量补实体库。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryServiceImpl.java
    • 中介实体关联新建时取消实体库必须已存在校验,改为补实体库。
    • 中介库管理新增实体时风险等级默认高风险。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryEnterpriseRelationImportServiceImpl.java
    • 取消“机构表不存在”失败条件,改为成功行批量补实体库。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiEnterpriseBaseInfoImportServiceImpl.java
    • 中介库管理导入实体风险等级默认高风险。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionServiceImpl.java
    • 招投标新建时供应商补实体库。
  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionImportServiceImpl.java
    • 招投标导入成功采购事项的供应商批量补实体库。
  • Test: existing unit tests under ccdi-info-collection/src/test/java/com/ruoyi/info/collection/
    • 扩展或新增对应 Service/Import/Controller 测试。
  • Create: docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md
    • 记录修改内容、影响范围、验证情况。

Task 1: EnterpriseSource 枚举与接口契约

Files:

  • Modify: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/enums/EnterpriseSource.java

  • Modify: ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiEnumControllerTest.java

  • Step 1: 写失败测试

CcdiEnumControllerTest#getEnterpriseSourceOptions_shouldReturnConfiguredOptions 中断言返回值包含 SUPPLIER/供应商

assertTrue(data.stream()
    .map(EnumOptionVO.class::cast)
    .anyMatch(option ->
        "SUPPLIER".equals(option.getValue()) && "供应商".equals(option.getLabel())));
  • Step 2: 运行测试确认失败

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiEnumControllerTest test

Expected: FAIL提示未找到 SUPPLIER

  • Step 3: 实现枚举

EnterpriseSource 中新增:

SUPPLIER("SUPPLIER", "供应商"),

保持 containsresolveCodegetDescByCode 通过 values() 自动生效。

  • Step 4: 运行测试确认通过

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiEnumControllerTest test

Expected: PASS。

Task 2: 实体库自动补全服务

Files:

  • Create: ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java

  • Test: ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillServiceTest.java

  • Step 1: 写服务测试

覆盖以下行为:

  • 已存在实体不插入、不覆盖。
  • 缺失实体插入最小记录。
  • 中介来源写 riskLevel=1
  • 员工亲属、信贷客户、供应商来源写 riskLevel=null
  • 批量同一信用代码只插一次,并使用首次有效名称。
  • 插入时遇到主键重复按已存在处理。

核心断言示例:

assertEquals("SUPPLIER", captured.getEntSource());
assertNull(captured.getRiskLevel());
assertEquals("IMPORT", captured.getDataSource());
  • Step 2: 运行测试确认失败

Run:

mvn -pl ccdi-info-collection -Dtest=EnterpriseAutoFillServiceTest test

Expected: FAIL类不存在。

  • Step 3: 实现服务接口

创建内部记录类型和方法:

@Service
public class EnterpriseAutoFillService {
    @Resource
    private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper;

    public record EnterpriseFillItem(
        String socialCreditCode,
        String enterpriseName,
        String entSource,
        String dataSource,
        String userName
    ) {}

    @Transactional
    public void ensureExists(EnterpriseFillItem item) {
        ensureExistsBatch(List.of(item));
    }

    @Transactional
    public void ensureExistsBatch(List<EnterpriseFillItem> items) {
        // trim、过滤空信用代码、按 socialCreditCode 首次出现去重
        // selectBatchIds 查询已存在记录
        // 组装 CcdiEnterpriseBaseInfo 最小实体
        // riskLevel: INTERMEDIARY -> "1",其他 -> null
        // dataSource: MANUAL 或 IMPORT
        // 分批调用 enterpriseBaseInfoMapper.insertBatch
        // 捕获 DuplicateKeyException 后继续逐条 selectById/insert重复则忽略
    }
}

实现注意:

  • 不调用 CcdiEnterpriseBaseInfoServiceImpl#insertEnterpriseBaseInfo,避免复用手工新增风险等级校验。

  • 对非中介来源显式 setRiskLevel(null)

  • 不更新已存在实体。

  • enterpriseName 使用来源业务已通过校验的名称,不增加额外兜底。

  • Step 4: 运行服务测试

Run:

mvn -pl ccdi-info-collection -Dtest=EnterpriseAutoFillServiceTest test

Expected: PASS。

Task 3: 员工亲属实体关联接入

Files:

  • Modify: CcdiStaffEnterpriseRelationServiceImpl.java

  • Modify: CcdiStaffEnterpriseRelationImportServiceImpl.java

  • Modify: CcdiStaffEnterpriseRelationServiceImplTest.java

  • Modify: CcdiStaffEnterpriseRelationImportServiceImplTest.java

  • Step 1: 写新建测试

insertRelation_shouldAllowValidFamily 中注入 EnterpriseAutoFillService mock并验证

verify(enterpriseAutoFillService).ensureExists(argThat(item ->
    "91310000123456789A".equals(item.socialCreditCode())
        && "测试企业".equals(item.enterpriseName())
        && "EMP_RELATION".equals(item.entSource())
        && "MANUAL".equals(item.dataSource())));
  • Step 2: 写导入测试

扩展导入测试,验证成功行调用批量补入,失败行不进入补入集合。

  • Step 3: 运行员工亲属测试确认失败

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiStaffEnterpriseRelationServiceImplTest,CcdiStaffEnterpriseRelationImportServiceImplTest test

Expected: FAIL尚未调用自动补全服务。

  • Step 4: 实现新建接入

insertRelation 中,existsByPersonIdAndSocialCreditCode 通过后、relationMapper.insert 前调用:

enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem(
    addDTO.getSocialCreditCode(),
    addDTO.getEnterpriseName(),
    EnterpriseSource.EMP_RELATION.getCode(),
    DataSource.MANUAL.getCode(),
    SecurityUtils.getUsername()
));
  • Step 5: 实现导入接入

importRelationAsync 成功构建 newRecords 后、saveBatch(newRecords, 500) 前,按成功记录组装补入集合:

enterpriseAutoFillService.ensureExistsBatch(newRecords.stream()
    .map(item -> new EnterpriseFillItem(item.getSocialCreditCode(), item.getEnterpriseName(),
        EnterpriseSource.EMP_RELATION.getCode(), DataSource.IMPORT.getCode(), userName))
    .toList());
  • Step 6: 运行员工亲属测试

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiStaffEnterpriseRelationServiceImplTest,CcdiStaffEnterpriseRelationImportServiceImplTest test

Expected: PASS。

Task 4: 信贷客户实体关联接入

Files:

  • Modify: CcdiCustEnterpriseRelationServiceImpl.java

  • Modify: CcdiCustEnterpriseRelationImportServiceImpl.java

  • Test: add CcdiCustEnterpriseRelationServiceImplTest.java if missing

  • Test: add or extend CcdiCustEnterpriseRelationImportServiceImplTest.java

  • Step 1: 写新建测试

验证 insertRelation 成功时调用自动补全:

assertEquals("CREDIT_CUSTOMER", item.entSource());
assertEquals("MANUAL", item.dataSource());
  • Step 2: 写导入测试

准备一条成功、一条重复组合失败,验证只有成功行传入 ensureExistsBatch

  • Step 3: 运行测试确认失败

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiCustEnterpriseRelationServiceImplTest,CcdiCustEnterpriseRelationImportServiceImplTest test

Expected: FAIL尚未调用自动补全服务。

  • Step 4: 实现新建接入

insertRelation 唯一性校验后、插入前调用自动补全,来源 CREDIT_CUSTOMER,数据来源 MANUAL

  • Step 5: 实现导入接入

importRelationAsync 成功记录批量插入前调用自动补全,来源 CREDIT_CUSTOMER,数据来源 IMPORT

  • Step 6: 运行测试

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiCustEnterpriseRelationServiceImplTest,CcdiCustEnterpriseRelationImportServiceImplTest test

Expected: PASS。

Task 5: 中介实体关联和中介实体管理规则

Files:

  • Modify: CcdiIntermediaryServiceImpl.java

  • Modify: CcdiIntermediaryEnterpriseRelationImportServiceImpl.java

  • Modify: CcdiEnterpriseBaseInfoImportServiceImpl.java

  • Modify: CcdiIntermediaryServiceImplTest.java

  • Modify: CcdiIntermediaryEnterpriseRelationImportServiceImplTest.java

  • Modify: CcdiEnterpriseBaseInfoImportServiceImplTest.java

  • Step 1: 写中介实体关联新建测试

验证实体库缺失不再抛“关联机构不存在”,而是调用自动补全并插入关联:

when(enterpriseRelationMapper.existsByIntermediaryBizIdAndSocialCreditCode("owner-biz", uscc)).thenReturn(false);
verify(enterpriseAutoFillService).ensureExists(argThat(item ->
    "INTERMEDIARY".equals(item.entSource()) && "MANUAL".equals(item.dataSource())));
verify(enterpriseRelationMapper).insert(any(CcdiIntermediaryEnterpriseRelation.class));
  • Step 2: 写中介实体关联导入测试

将现有 importEnterpriseRelationAsync_shouldFailWhenEnterpriseDoesNotExist 改成成功场景,断言:

  • 不再产生失败记录。

  • 调用 ensureExistsBatch

  • 插入关联记录。

  • Step 3: 写中介库管理默认高风险测试

CcdiEnterpriseBaseInfoImportServiceImplTest 增加:

excel.setRiskLevel(null);
excel.setEntSource("中介");
CcdiEnterpriseBaseInfo entity = service.validateAndBuildEntity(excel, Set.of(), new HashSet<>(), "admin");
assertEquals("1", entity.getRiskLevel());
assertEquals("INTERMEDIARY", entity.getEntSource());

CcdiIntermediaryServiceImplTest 验证 insertIntermediaryEntity 未传风险等级时写入 1

  • Step 4: 运行测试确认失败

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiIntermediaryServiceImplTest,CcdiIntermediaryEnterpriseRelationImportServiceImplTest,CcdiEnterpriseBaseInfoImportServiceImplTest test

Expected: FAIL。

  • Step 5: 实现新建接入

修改 validateEnterpriseRelation:保留中介本人和重复组合校验,删除 enterpriseBaseInfoMapper.selectById(socialCreditCode) == null 抛错。

insertIntermediaryEnterpriseRelation 插入前调用自动补全,来源 INTERMEDIARY,数据来源 MANUAL

  • Step 6: 实现导入接入

在导入服务中删除 getExistingEnterpriseCodes 的失败判断。成功记录插入前按 Excel 行组装实体补入,来源 INTERMEDIARY,数据来源 IMPORT

  • Step 7: 实现中介实体默认高风险

insertIntermediaryEntity 中,如果 riskLevel 为空,设置为 "1"

CcdiEnterpriseBaseInfoImportServiceImpl#validateAndBuildEntity 中,当解析出的 entSourceINTERMEDIARYriskLevel 为空时,设置 "1"

  • Step 8: 运行测试

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiIntermediaryServiceImplTest,CcdiIntermediaryEnterpriseRelationImportServiceImplTest,CcdiEnterpriseBaseInfoImportServiceImplTest test

Expected: PASS。

Task 6: 招投标供应商接入

Files:

  • Modify: CcdiPurchaseTransactionServiceImpl.java

  • Modify: CcdiPurchaseTransactionImportServiceImpl.java

  • Test: add CcdiPurchaseTransactionServiceImplTest.java if missing

  • Modify: CcdiPurchaseTransactionFeatureContractTest.java or add import service unit test

  • Step 1: 写新建测试

验证 insertTransaction 成功时,仅对 supplierUscc 不为空的供应商调用自动补全:

assertEquals("SUPPLIER", item.entSource());
assertEquals("MANUAL", item.dataSource());
assertEquals("供应商A", item.enterpriseName());
  • Step 2: 写导入测试

准备一个成功采购事项和一个失败采购事项,断言只有成功事项的供应商进入 ensureExistsBatch,且来源为 SUPPLIER、数据来源为 IMPORT

  • Step 3: 运行测试确认失败

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiPurchaseTransactionServiceImplTest,CcdiPurchaseTransactionImportServiceImplTest,CcdiPurchaseTransactionFeatureContractTest test

Expected: FAIL。

  • Step 4: 实现新建接入

insertTransaction 中,buildSupplierEntities 和校验完成后、写主从表前,收集供应商:

enterpriseAutoFillService.ensureExistsBatch(supplierList.stream()
    .filter(item -> StringUtils.isNotEmpty(item.getSupplierUscc()))
    .map(item -> new EnterpriseFillItem(item.getSupplierUscc(), item.getSupplierName(),
        EnterpriseSource.SUPPLIER.getCode(), DataSource.MANUAL.getCode(), SecurityUtils.getUsername()))
    .toList());
  • Step 5: 实现导入接入

importTransactionAsync 中,按成功构建的 newSuppliers 收集供应商实体,在 saveBatch(newTransactions, 500) 之前调用自动补全。失败事项的供应商不进入 newSuppliers,天然不补。

  • Step 6: 运行测试

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiPurchaseTransactionServiceImplTest,CcdiPurchaseTransactionImportServiceImplTest,CcdiPurchaseTransactionFeatureContractTest test

Expected: PASS。

Task 7: 集成验证与实施记录

Files:

  • Create: docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md

  • Step 1: 运行后端相关测试集合

Run:

mvn -pl ccdi-info-collection -Dtest=CcdiEnumControllerTest,EnterpriseAutoFillServiceTest,CcdiStaffEnterpriseRelationServiceImplTest,CcdiStaffEnterpriseRelationImportServiceImplTest,CcdiCustEnterpriseRelationServiceImplTest,CcdiCustEnterpriseRelationImportServiceImplTest,CcdiIntermediaryServiceImplTest,CcdiIntermediaryEnterpriseRelationImportServiceImplTest,CcdiEnterpriseBaseInfoImportServiceImplTest,CcdiPurchaseTransactionServiceImplTest,CcdiPurchaseTransactionImportServiceImplTest,CcdiPurchaseTransactionFeatureContractTest test

Expected: BUILD SUCCESS。

  • Step 2: 如涉及数据库实测,确认 risk_level 落库值

验证样本:

  • 员工亲属自动补入:risk_level IS NULL

  • 信贷客户自动补入:risk_level IS NULL

  • 招投标供应商自动补入:risk_level IS NULL

  • 中介自动补入:risk_level = '1'

  • Step 3: 写实施记录

实施记录至少包含:

# 关联业务自动补入实体库实施记录

## 修改内容
- 新增实体库自动补全服务
- 接入员工亲属、中介、信贷客户、招投标链路
- 新增 SUPPLIER 企业来源

## 影响范围
- ccdi-info-collection 后端服务
- 实体库管理企业来源枚举接口

## 验证情况
- 列出 Maven 测试命令与结果
- 列出页面或数据库验证结果
  • Step 4: 检查工作区

Run:

git status --short

Expected: 仅包含本次功能相关源码、测试和实施记录,不包含 .DS_Store 或生成测试文件。

  • Step 5: 提交后端改动
git add ccdi-info-collection/src/main/java/com/ruoyi/info/collection ccdi-info-collection/src/test/java/com/ruoyi/info/collection docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md
git commit -m "实现关联业务自动补入实体库"