From 26be75adade6091cda752335495bc4193ad404ab Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Sun, 26 Apr 2026 17:23:47 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=85=B3=E8=81=94?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E8=87=AA=E5=8A=A8=E8=A1=A5=E5=85=A5=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/CcdiEnterpriseBaseInfo.java | 2 +- .../collection/enums/EnterpriseSource.java | 1 + ...stEnterpriseRelationImportServiceImpl.java | 15 ++ ...CcdiCustEnterpriseRelationServiceImpl.java | 14 ++ ...diEnterpriseBaseInfoImportServiceImpl.java | 12 +- ...ryEnterpriseRelationImportServiceImpl.java | 39 ++---- .../impl/CcdiIntermediaryServiceImpl.java | 23 +++- ...iPurchaseTransactionImportServiceImpl.java | 20 +++ .../CcdiPurchaseTransactionServiceImpl.java | 22 +++ ...ffEnterpriseRelationImportServiceImpl.java | 15 ++ ...cdiStaffEnterpriseRelationServiceImpl.java | 14 ++ .../support/EnterpriseAutoFillService.java | 130 ++++++++++++++++++ ...-26-enterprise-auto-fill-implementation.md | 36 +++++ ...enterprise-name-nullable-for-auto-fill.sql | 4 + 14 files changed, 314 insertions(+), 33 deletions(-) create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java create mode 100644 docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md create mode 100644 sql/migration/2026-04-26-make-enterprise-name-nullable-for-auto-fill.sql diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiEnterpriseBaseInfo.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiEnterpriseBaseInfo.java index f27f12c3..135f70d7 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiEnterpriseBaseInfo.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiEnterpriseBaseInfo.java @@ -94,6 +94,6 @@ public class CcdiEnterpriseBaseInfo implements Serializable { /** 风险等级:1-高风险, 2-中风险, 3-低风险 */ private String riskLevel; - /** 企业来源:GENERAL-一般企业, EMP_RELATION-员工关系人, CREDIT_CUSTOMER-信贷客户, INTERMEDIARY-中介, BOTH-兼有 */ + /** 企业来源:GENERAL-一般企业, EMP_RELATION-员工关系人, CREDIT_CUSTOMER-信贷客户, SUPPLIER-供应商, INTERMEDIARY-中介, BOTH-兼有 */ private String entSource; } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/enums/EnterpriseSource.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/enums/EnterpriseSource.java index b778173d..eba199c0 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/enums/EnterpriseSource.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/enums/EnterpriseSource.java @@ -10,6 +10,7 @@ public enum EnterpriseSource { GENERAL("GENERAL", "一般企业"), EMP_RELATION("EMP_RELATION", "员工关系人"), CREDIT_CUSTOMER("CREDIT_CUSTOMER", "信贷客户"), + SUPPLIER("SUPPLIER", "供应商"), INTERMEDIARY("INTERMEDIARY", "中介"), BOTH("BOTH", "兼有"); diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationImportServiceImpl.java index 16a4db3c..a8f39a2b 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationImportServiceImpl.java @@ -7,8 +7,11 @@ import com.ruoyi.info.collection.domain.excel.CcdiCustEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.vo.CustEnterpriseRelationImportFailureVO; import com.ruoyi.info.collection.domain.vo.ImportResult; import com.ruoyi.info.collection.domain.vo.ImportStatusVO; +import com.ruoyi.info.collection.enums.DataSource; +import com.ruoyi.info.collection.enums.EnterpriseSource; import com.ruoyi.info.collection.mapper.CcdiCustEnterpriseRelationMapper; import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationImportService; +import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService; import com.ruoyi.info.collection.utils.ImportLogUtils; import com.ruoyi.common.utils.StringUtils; import jakarta.annotation.Resource; @@ -43,6 +46,9 @@ public class CcdiCustEnterpriseRelationImportServiceImpl implements ICcdiCustEnt @Resource private RedisTemplate redisTemplate; + @Resource + private EnterpriseAutoFillService enterpriseAutoFillService; + @Override @Async @Transactional @@ -127,6 +133,15 @@ public class CcdiCustEnterpriseRelationImportServiceImpl implements ICcdiCustEnt // 批量插入新数据 if (!newRecords.isEmpty()) { + enterpriseAutoFillService.ensureExistsBatch(newRecords.stream() + .map(item -> new EnterpriseAutoFillService.EnterpriseFillItem( + item.getSocialCreditCode(), + item.getEnterpriseName(), + EnterpriseSource.CREDIT_CUSTOMER.getCode(), + DataSource.IMPORT.getCode(), + userName + )) + .toList()); ImportLogUtils.logBatchOperationStart(log, taskId, "插入", (newRecords.size() + 499) / 500, 500); saveBatch(newRecords, 500); diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationServiceImpl.java index 980efca0..cb1e52eb 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationServiceImpl.java @@ -8,9 +8,12 @@ import com.ruoyi.info.collection.domain.dto.CcdiCustEnterpriseRelationEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiCustEnterpriseRelationQueryDTO; import com.ruoyi.info.collection.domain.excel.CcdiCustEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.vo.CcdiCustEnterpriseRelationVO; +import com.ruoyi.info.collection.enums.DataSource; +import com.ruoyi.info.collection.enums.EnterpriseSource; import com.ruoyi.info.collection.mapper.CcdiCustEnterpriseRelationMapper; import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationImportService; import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationService; +import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import jakarta.annotation.Resource; @@ -43,6 +46,9 @@ public class CcdiCustEnterpriseRelationServiceImpl implements ICcdiCustEnterpris @Resource private RedisTemplate redisTemplate; + @Resource + private EnterpriseAutoFillService enterpriseAutoFillService; + /** * 查询信贷客户实体关联列表 * @@ -135,6 +141,14 @@ public class CcdiCustEnterpriseRelationServiceImpl implements ICcdiCustEnterpris relation.setDataSource("MANUAL"); } + enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem( + addDTO.getSocialCreditCode(), + addDTO.getEnterpriseName(), + EnterpriseSource.CREDIT_CUSTOMER.getCode(), + DataSource.MANUAL.getCode(), + SecurityUtils.getUsername() + )); + int result = relationMapper.insert(relation); return result; diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiEnterpriseBaseInfoImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiEnterpriseBaseInfoImportServiceImpl.java index 9480001a..71edd7b2 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiEnterpriseBaseInfoImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiEnterpriseBaseInfoImportServiceImpl.java @@ -131,14 +131,18 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB if (!excel.getSocialCreditCode().matches("^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$")) { throw new RuntimeException("统一社会信用代码格式不正确"); } - String riskLevel = EnterpriseRiskLevel.resolveCode(StringUtils.trim(excel.getRiskLevel())); - if (riskLevel == null) { - throw new RuntimeException("风险等级不在允许范围内"); - } String entSource = EnterpriseSource.resolveCode(StringUtils.trim(excel.getEntSource())); if (entSource == null) { throw new RuntimeException("企业来源不在允许范围内"); } + String riskLevel = EnterpriseRiskLevel.resolveCode(StringUtils.trim(excel.getRiskLevel())); + if (riskLevel == null) { + if (EnterpriseSource.INTERMEDIARY.getCode().equals(entSource) && StringUtils.isEmpty(excel.getRiskLevel())) { + riskLevel = "1"; + } else { + throw new RuntimeException("风险等级不在允许范围内"); + } + } if (existingCreditCodes.contains(excel.getSocialCreditCode())) { throw new RuntimeException(String.format("统一社会信用代码[%s]已存在,请勿重复导入", excel.getSocialCreditCode())); diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryEnterpriseRelationImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryEnterpriseRelationImportServiceImpl.java index dd9c7c06..b4c5a523 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryEnterpriseRelationImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryEnterpriseRelationImportServiceImpl.java @@ -3,16 +3,17 @@ package com.ruoyi.info.collection.service.impl; import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.info.collection.domain.CcdiBizIntermediary; -import com.ruoyi.info.collection.domain.CcdiEnterpriseBaseInfo; import com.ruoyi.info.collection.domain.CcdiIntermediaryEnterpriseRelation; import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.vo.ImportResult; import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.IntermediaryEnterpriseRelationImportFailureVO; +import com.ruoyi.info.collection.enums.DataSource; +import com.ruoyi.info.collection.enums.EnterpriseSource; import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper; -import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper; import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper; import com.ruoyi.info.collection.service.ICcdiIntermediaryEnterpriseRelationImportService; +import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService; import com.ruoyi.info.collection.utils.ImportLogUtils; import com.ruoyi.common.utils.IdCardUtil; import com.ruoyi.common.utils.StringUtils; @@ -54,10 +55,10 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd private CcdiBizIntermediaryMapper intermediaryMapper; @Resource - private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper; + private RedisTemplate redisTemplate; @Resource - private RedisTemplate redisTemplate; + private EnterpriseAutoFillService enterpriseAutoFillService; @Override @Async @@ -67,7 +68,6 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd ImportLogUtils.logImportStart(log, taskId, "中介实体关联关系", excelList.size(), userName); Map ownerBizIdByPersonId = getOwnerBizIdByPersonId(excelList); - Set existingEnterpriseCodes = getExistingEnterpriseCodes(excelList); Set existingCombinations = getExistingRelationCombinations(ownerBizIdByPersonId, excelList); List successRecords = new ArrayList<>(); @@ -83,9 +83,6 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd if (StringUtils.isEmpty(ownerBizId)) { throw new RuntimeException("中介本人不存在,请先导入或维护中介本人信息"); } - if (!existingEnterpriseCodes.contains(excel.getSocialCreditCode())) { - throw new RuntimeException("统一社会信用代码不存在于系统机构表"); - } String combination = ownerBizId + "|" + excel.getSocialCreditCode(); if (existingCombinations.contains(combination)) { @@ -109,6 +106,15 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd } if (!successRecords.isEmpty()) { + enterpriseAutoFillService.ensureExistsBatch(successRecords.stream() + .map(item -> new EnterpriseAutoFillService.EnterpriseFillItem( + item.getSocialCreditCode(), + null, + EnterpriseSource.INTERMEDIARY.getCode(), + DataSource.IMPORT.getCode(), + userName + )) + .toList()); saveBatch(successRecords, 500); } if (!failures.isEmpty()) { @@ -173,23 +179,6 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd .collect(Collectors.toMap(CcdiBizIntermediary::getPersonId, CcdiBizIntermediary::getBizId, (left, right) -> left)); } - private Set getExistingEnterpriseCodes(List excelList) { - List socialCreditCodes = excelList.stream() - .map(CcdiIntermediaryEnterpriseRelationExcel::getSocialCreditCode) - .filter(StringUtils::isNotEmpty) - .distinct() - .collect(Collectors.toList()); - if (socialCreditCodes.isEmpty()) { - return Collections.emptySet(); - } - - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.in(CcdiEnterpriseBaseInfo::getSocialCreditCode, socialCreditCodes); - return enterpriseBaseInfoMapper.selectList(wrapper).stream() - .map(CcdiEnterpriseBaseInfo::getSocialCreditCode) - .collect(Collectors.toSet()); - } - private Set getExistingRelationCombinations(Map ownerBizIdByPersonId, List excelList) { List combinations = excelList.stream() diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryServiceImpl.java index 766de88d..b9b3dda7 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryServiceImpl.java @@ -14,6 +14,8 @@ import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryEntityDetailVO; import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryPersonDetailVO; import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryRelativeVO; import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryVO; +import com.ruoyi.info.collection.enums.DataSource; +import com.ruoyi.info.collection.enums.EnterpriseSource; import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper; import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper; import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper; @@ -22,6 +24,7 @@ import com.ruoyi.info.collection.service.ICcdiIntermediaryEnterpriseRelationImpo import com.ruoyi.info.collection.service.ICcdiIntermediaryEntityImportService; import com.ruoyi.info.collection.service.ICcdiIntermediaryPersonImportService; import com.ruoyi.info.collection.service.ICcdiIntermediaryService; +import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import jakarta.annotation.Resource; @@ -69,6 +72,9 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService { @Resource private RedisTemplate redisTemplate; + @Resource + private EnterpriseAutoFillService enterpriseAutoFillService; + /** * 分页查询中介列表 * 使用XML联合查询实现,支持个人中介和实体中介的灵活查询 @@ -302,6 +308,13 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService { CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation(); BeanUtils.copyProperties(addDTO, relation); relation.setIntermediaryBizId(owner.getBizId()); + enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem( + addDTO.getSocialCreditCode(), + null, + EnterpriseSource.INTERMEDIARY.getCode(), + DataSource.MANUAL.getCode(), + SecurityUtils.getUsername() + )); return enterpriseRelationMapper.insert(relation); } @@ -317,6 +330,13 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService { CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation(); BeanUtils.copyProperties(editDTO, relation); relation.setIntermediaryBizId(existing.getIntermediaryBizId()); + enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem( + editDTO.getSocialCreditCode(), + null, + EnterpriseSource.INTERMEDIARY.getCode(), + DataSource.MANUAL.getCode(), + SecurityUtils.getUsername() + )); return enterpriseRelationMapper.updateById(relation); } @@ -520,9 +540,6 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService { private void validateEnterpriseRelation(String bizId, String socialCreditCode, Long excludeId) { requireIntermediaryPerson(bizId); - if (enterpriseBaseInfoMapper.selectById(socialCreditCode) == null) { - throw new RuntimeException("关联机构不存在"); - } boolean exists = enterpriseRelationMapper.existsByIntermediaryBizIdAndSocialCreditCode(bizId, socialCreditCode); if (exists) { if (excludeId == null) { diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionImportServiceImpl.java index 2221986a..c60e743c 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionImportServiceImpl.java @@ -9,9 +9,12 @@ import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionSupplierExc import com.ruoyi.info.collection.domain.vo.ImportResult; import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.PurchaseTransactionImportFailureVO; +import com.ruoyi.info.collection.enums.DataSource; +import com.ruoyi.info.collection.enums.EnterpriseSource; import com.ruoyi.info.collection.mapper.CcdiPurchaseTransactionMapper; import com.ruoyi.info.collection.mapper.CcdiPurchaseTransactionSupplierMapper; import com.ruoyi.info.collection.service.ICcdiPurchaseTransactionImportService; +import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService; import com.ruoyi.info.collection.utils.ImportLogUtils; import com.ruoyi.common.utils.StringUtils; import jakarta.annotation.Resource; @@ -53,6 +56,9 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr @Resource private RedisTemplate redisTemplate; + @Resource + private EnterpriseAutoFillService enterpriseAutoFillService; + @Override @Async @Transactional @@ -183,6 +189,7 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr // 批量插入新数据 if (!newTransactions.isEmpty()) { + autoFillSupplierEnterprises(newSuppliers, userName); ImportLogUtils.logBatchOperationStart(log, taskId, "插入", (newTransactions.size() + 499) / 500, 500); saveBatch(newTransactions, 500); @@ -328,6 +335,19 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr } } + private void autoFillSupplierEnterprises(List supplierList, String userName) { + enterpriseAutoFillService.ensureExistsBatch(supplierList.stream() + .filter(item -> StringUtils.isNotEmpty(item.getSupplierUscc())) + .map(item -> new EnterpriseAutoFillService.EnterpriseFillItem( + item.getSupplierUscc(), + item.getSupplierName(), + EnterpriseSource.SUPPLIER.getCode(), + DataSource.IMPORT.getCode(), + userName + )) + .toList()); + } + /** * 验证采购交易数据 * diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionServiceImpl.java index 1b6cd039..cb92511f 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionServiceImpl.java @@ -11,10 +11,13 @@ import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel; import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionSupplierExcel; import com.ruoyi.info.collection.domain.vo.CcdiPurchaseTransactionVO; import com.ruoyi.info.collection.domain.vo.CcdiPurchaseTransactionSupplierVO; +import com.ruoyi.info.collection.enums.DataSource; +import com.ruoyi.info.collection.enums.EnterpriseSource; import com.ruoyi.info.collection.mapper.CcdiPurchaseTransactionMapper; import com.ruoyi.info.collection.mapper.CcdiPurchaseTransactionSupplierMapper; import com.ruoyi.info.collection.service.ICcdiPurchaseTransactionImportService; import com.ruoyi.info.collection.service.ICcdiPurchaseTransactionService; +import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import jakarta.annotation.Resource; @@ -55,6 +58,9 @@ public class CcdiPurchaseTransactionServiceImpl implements ICcdiPurchaseTransact @Resource private RedisTemplate redisTemplate; + @Resource + private EnterpriseAutoFillService enterpriseAutoFillService; + /** * 查询采购交易列表 * @@ -134,6 +140,7 @@ public class CcdiPurchaseTransactionServiceImpl implements ICcdiPurchaseTransact CcdiPurchaseTransaction transaction = new CcdiPurchaseTransaction(); BeanUtils.copyProperties(addDTO, transaction); fillWinnerSummary(transaction, supplierList); + autoFillSupplierEnterprises(supplierList, DataSource.MANUAL.getCode(), SecurityUtils.getUsername()); int result = transactionMapper.insert(transaction); saveSuppliers(supplierList); @@ -331,6 +338,21 @@ public class CcdiPurchaseTransactionServiceImpl implements ICcdiPurchaseTransact } } + private void autoFillSupplierEnterprises(List supplierList, + String dataSource, + String userName) { + enterpriseAutoFillService.ensureExistsBatch(supplierList.stream() + .filter(item -> StringUtils.isNotEmpty(item.getSupplierUscc())) + .map(item -> new EnterpriseAutoFillService.EnterpriseFillItem( + item.getSupplierUscc(), + item.getSupplierName(), + EnterpriseSource.SUPPLIER.getCode(), + dataSource, + userName + )) + .toList()); + } + private List selectSupplierListByPurchaseId(String purchaseId) { return supplierMapper.selectList( new LambdaQueryWrapper() diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java index e80a01fc..8264fbbe 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java @@ -9,9 +9,12 @@ import com.ruoyi.info.collection.domain.excel.CcdiStaffEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.vo.ImportResult; import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.StaffEnterpriseRelationImportFailureVO; +import com.ruoyi.info.collection.enums.DataSource; +import com.ruoyi.info.collection.enums.EnterpriseSource; import com.ruoyi.info.collection.mapper.CcdiStaffFmyRelationMapper; import com.ruoyi.info.collection.mapper.CcdiStaffEnterpriseRelationMapper; import com.ruoyi.info.collection.service.ICcdiStaffEnterpriseRelationImportService; +import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService; import com.ruoyi.info.collection.utils.ImportLogUtils; import com.ruoyi.common.utils.StringUtils; import jakarta.annotation.Resource; @@ -49,6 +52,9 @@ public class CcdiStaffEnterpriseRelationImportServiceImpl implements ICcdiStaffE @Resource private CcdiStaffFmyRelationMapper familyRelationMapper; + @Resource + private EnterpriseAutoFillService enterpriseAutoFillService; + @Override @Async @Transactional @@ -147,6 +153,15 @@ public class CcdiStaffEnterpriseRelationImportServiceImpl implements ICcdiStaffE // 批量插入新数据 if (!newRecords.isEmpty()) { + enterpriseAutoFillService.ensureExistsBatch(newRecords.stream() + .map(item -> new EnterpriseAutoFillService.EnterpriseFillItem( + item.getSocialCreditCode(), + item.getEnterpriseName(), + EnterpriseSource.EMP_RELATION.getCode(), + DataSource.IMPORT.getCode(), + userName + )) + .toList()); ImportLogUtils.logBatchOperationStart(log, taskId, "插入", (newRecords.size() + 499) / 500, 500); saveBatch(newRecords, 500); diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationServiceImpl.java index 69e2c8da..57f7ba32 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationServiceImpl.java @@ -10,10 +10,13 @@ import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationQueryDTO; import com.ruoyi.info.collection.domain.excel.CcdiStaffEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationOptionVO; import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationVO; +import com.ruoyi.info.collection.enums.DataSource; +import com.ruoyi.info.collection.enums.EnterpriseSource; import com.ruoyi.info.collection.mapper.CcdiStaffFmyRelationMapper; import com.ruoyi.info.collection.mapper.CcdiStaffEnterpriseRelationMapper; import com.ruoyi.info.collection.service.ICcdiStaffEnterpriseRelationImportService; import com.ruoyi.info.collection.service.ICcdiStaffEnterpriseRelationService; +import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import jakarta.annotation.Resource; @@ -49,6 +52,9 @@ public class CcdiStaffEnterpriseRelationServiceImpl implements ICcdiStaffEnterpr @Resource private RedisTemplate redisTemplate; + @Resource + private EnterpriseAutoFillService enterpriseAutoFillService; + /** * 查询员工实体关系列表 * @@ -144,6 +150,14 @@ public class CcdiStaffEnterpriseRelationServiceImpl implements ICcdiStaffEnterpr relation.setDataSource("MANUAL"); } + enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem( + addDTO.getSocialCreditCode(), + addDTO.getEnterpriseName(), + EnterpriseSource.EMP_RELATION.getCode(), + DataSource.MANUAL.getCode(), + SecurityUtils.getUsername() + )); + int result = relationMapper.insert(relation); return result; diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java new file mode 100644 index 00000000..4fd70329 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java @@ -0,0 +1,130 @@ +package com.ruoyi.info.collection.service.support; + +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.info.collection.domain.CcdiEnterpriseBaseInfo; +import com.ruoyi.info.collection.enums.EnterpriseSource; +import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper; +import jakarta.annotation.Resource; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 关联业务实体库自动补全服务。 + */ +@Service +public class EnterpriseAutoFillService { + + private static final int BATCH_SIZE = 500; + + @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 items) { + if (StringUtils.isEmpty(items)) { + return; + } + Map normalizedItems = normalizeItems(items); + if (normalizedItems.isEmpty()) { + return; + } + + Set existingCodes = enterpriseBaseInfoMapper.selectBatchIds(new ArrayList<>(normalizedItems.keySet())) + .stream() + .map(CcdiEnterpriseBaseInfo::getSocialCreditCode) + .collect(Collectors.toSet()); + List missingEntities = normalizedItems.entrySet().stream() + .filter(entry -> !existingCodes.contains(entry.getKey())) + .map(entry -> buildEntity(entry.getKey(), entry.getValue())) + .toList(); + if (missingEntities.isEmpty()) { + return; + } + insertBatchIgnoreDuplicate(missingEntities); + } + + private Map normalizeItems(List items) { + Map normalizedItems = new LinkedHashMap<>(); + for (EnterpriseFillItem item : items) { + if (item == null || StringUtils.isEmpty(item.socialCreditCode())) { + continue; + } + String socialCreditCode = item.socialCreditCode().trim(); + normalizedItems.putIfAbsent(socialCreditCode, new EnterpriseFillItem( + socialCreditCode, + trimToNull(item.enterpriseName()), + trimToNull(item.entSource()), + trimToNull(item.dataSource()), + trimToNull(item.userName()) + )); + } + return normalizedItems; + } + + private CcdiEnterpriseBaseInfo buildEntity(String socialCreditCode, EnterpriseFillItem item) { + CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo(); + entity.setSocialCreditCode(socialCreditCode); + entity.setEnterpriseName(item.enterpriseName()); + entity.setEntSource(item.entSource()); + entity.setDataSource(item.dataSource()); + entity.setRiskLevel(EnterpriseSource.INTERMEDIARY.getCode().equals(item.entSource()) ? "1" : null); + entity.setCreatedBy(item.userName()); + entity.setUpdatedBy(item.userName()); + return entity; + } + + private void insertBatchIgnoreDuplicate(List entities) { + try { + for (int i = 0; i < entities.size(); i += BATCH_SIZE) { + int end = Math.min(i + BATCH_SIZE, entities.size()); + enterpriseBaseInfoMapper.insertBatch(entities.subList(i, end)); + } + } catch (DuplicateKeyException ex) { + insertOneByOneIgnoreDuplicate(entities); + } + } + + private void insertOneByOneIgnoreDuplicate(List entities) { + for (CcdiEnterpriseBaseInfo entity : entities) { + if (enterpriseBaseInfoMapper.selectById(entity.getSocialCreditCode()) != null) { + continue; + } + try { + enterpriseBaseInfoMapper.insert(entity); + } catch (DuplicateKeyException duplicate) { + if (enterpriseBaseInfoMapper.selectById(entity.getSocialCreditCode()) == null) { + throw duplicate; + } + } + } + } + + private String trimToNull(String value) { + if (StringUtils.isEmpty(value)) { + return null; + } + return value.trim(); + } +} diff --git a/docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md b/docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md new file mode 100644 index 00000000..ef6e6954 --- /dev/null +++ b/docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md @@ -0,0 +1,36 @@ +# 关联业务自动补入实体库实施记录 + +## 修改内容 + +- 新增 `EnterpriseAutoFillService`,统一处理关联业务按统一社会信用代码补入 `ccdi_enterprise_base_info`。 +- 员工实体关系、信贷客户实体关系、中介关系、招投标供应商新增与导入成功数据,均在保存关联关系前按缺失实体自动补入实体库。 +- 企业名称按“允许为空,有值则入库”的规则处理;供应商场景使用供应商名称补入,其余当前无企业名称来源的场景补入 `NULL`。 +- 新增企业来源枚举 `SUPPLIER/供应商`,前端实体库企业来源下拉复用后端枚举接口,无需新增前端硬编码。 +- 中介来源实体默认风险等级为高风险;中介实体关系不再要求实体库预先存在。 +- 新增迁移脚本 `sql/migration/2026-04-26-make-enterprise-name-nullable-for-auto-fill.sql`,将 `ccdi_enterprise_base_info.enterprise_name` 调整为可空。 + +## 影响范围 + +- 后端模块:`ccdi-info-collection` +- 相关业务:员工关系实体、信贷客户实体、中介关系、招投标供应商、实体库导入、企业来源枚举 +- 数据库表:`ccdi_enterprise_base_info` + +## 验证情况 + +- 后端单元验证已通过: + +```bash +mvn -pl ccdi-info-collection -am -Dtest=CcdiEnumControllerTest,EnterpriseAutoFillServiceTest,CcdiStaffEnterpriseRelationServiceImplTest,CcdiStaffEnterpriseRelationImportServiceImplTest,CcdiCustEnterpriseRelationServiceImplTest,CcdiCustEnterpriseRelationImportServiceImplTest,CcdiIntermediaryServiceImplTest,CcdiIntermediaryEnterpriseRelationImportServiceImplTest,CcdiEnterpriseBaseInfoImportServiceImplTest,CcdiPurchaseTransactionServiceImplTest,CcdiPurchaseTransactionImportServiceImplTest,CcdiPurchaseTransactionFeatureContractTest -Dsurefire.failIfNoSpecifiedTests=false test +``` + +- 启动当前工作树后端成功,监听 `62318`。 +- 使用 `nvm use` 切换到 Node `v14.21.3`,启动前端开发服务到 `http://localhost:8080/`。 +- 使用 browser-use 打开真实业务页面: + - `http://localhost:8080/maintain/enterpriseBaseInfo` 加载正常,企业来源下拉出现“供应商”。 + - `http://localhost:8080/maintain/purchaseTransaction` 加载正常,供应商明细相关页面可访问。 +- 后端枚举接口 `GET /ccdi/enum/enterpriseSource` 已返回 `{ "value": "SUPPLIER", "label": "供应商" }`。 + +## 未执行事项 + +- 远程联调库当前 `ccdi_enterprise_base_info.enterprise_name` 仍为 `Null=NO`。 +- 本次未直接执行远程库结构变更;提权执行迁移脚本被安全审查拦截。真实写入空企业名称的补库验证需先由授权人员执行迁移脚本。 diff --git a/sql/migration/2026-04-26-make-enterprise-name-nullable-for-auto-fill.sql b/sql/migration/2026-04-26-make-enterprise-name-nullable-for-auto-fill.sql new file mode 100644 index 00000000..e21e83fd --- /dev/null +++ b/sql/migration/2026-04-26-make-enterprise-name-nullable-for-auto-fill.sql @@ -0,0 +1,4 @@ +ALTER TABLE ccdi_enterprise_base_info + MODIFY COLUMN enterprise_name varchar(200) + CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci + DEFAULT NULL COMMENT '企业名称'; From 356bcdd6de39d3d05da496d35af1ead398c8e74e Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Wed, 6 May 2026 20:50:09 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=8CSheet=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E5=8D=95=E7=8B=AC=E5=AF=BC=E5=85=A5=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CcdiBaseStaffController.java | 22 +--- .../CcdiStaffFmyRelationController.java | 23 +--- .../service/ICcdiAssetInfoImportService.java | 15 +++ .../ICcdiBaseStaffAssetImportService.java | 15 +++ .../service/ICcdiBaseStaffImportService.java | 10 ++ .../service/ICcdiBaseStaffService.java | 12 +++ .../ICcdiStaffFmyRelationImportService.java | 11 ++ .../service/ICcdiStaffFmyRelationService.java | 12 +++ .../impl/CcdiAssetInfoImportServiceImpl.java | 22 ++++ .../CcdiBaseStaffAssetImportServiceImpl.java | 22 ++++ .../impl/CcdiBaseStaffImportServiceImpl.java | 12 +++ .../impl/CcdiBaseStaffServiceImpl.java | 100 +++++++++++++++--- ...diDualSheetImportOrchestrationService.java | 90 ++++++++++++++++ ...CcdiStaffFmyRelationImportServiceImpl.java | 15 +++ .../impl/CcdiStaffFmyRelationServiceImpl.java | 98 ++++++++++++++--- ...erprise-autofill-backend-implementation.md | 43 ++++++++ 16 files changed, 448 insertions(+), 74 deletions(-) create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiDualSheetImportOrchestrationService.java create mode 100644 docs/reports/implementation/2026-05-06-staff-asset-import-and-enterprise-autofill-backend-implementation.md diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiBaseStaffController.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiBaseStaffController.java index a8a358eb..7626c7f3 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiBaseStaffController.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiBaseStaffController.java @@ -7,7 +7,6 @@ import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO; import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel; import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel; import com.ruoyi.info.collection.domain.vo.*; -import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService; import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService; import com.ruoyi.info.collection.service.ICcdiBaseStaffService; import com.ruoyi.info.collection.utils.EasyExcelUtil; @@ -47,9 +46,6 @@ public class CcdiBaseStaffController extends BaseController { @Resource private ICcdiBaseStaffImportService importAsyncService; - @Resource - private ICcdiBaseStaffAssetImportService baseStaffAssetImportService; - /** * 查询员工列表 */ @@ -161,14 +157,7 @@ public class CcdiBaseStaffController extends BaseController { return error("至少需要一条数据"); } - BaseStaffImportSubmitResultVO result = new BaseStaffImportSubmitResultVO(); - if (hasStaffRows) { - result.setStaffTaskId(baseStaffService.importBaseStaff(staffList)); - } - if (hasAssetRows) { - result.setAssetTaskId(baseStaffAssetImportService.importAssetInfo(assetList)); - } - result.setMessage(buildImportSubmitMessage(hasStaffRows, hasAssetRows)); + BaseStaffImportSubmitResultVO result = baseStaffService.importBaseStaffWithAssets(staffList, assetList); return AjaxResult.success("导入任务已提交,正在后台处理", result); } @@ -215,13 +204,4 @@ public class CcdiBaseStaffController extends BaseController { return getDataTable(pageData, failures.size()); } - private String buildImportSubmitMessage(boolean hasStaffRows, boolean hasAssetRows) { - if (hasStaffRows && hasAssetRows) { - return "已提交员工信息和员工资产信息导入任务"; - } - if (hasStaffRows) { - return "已提交员工信息导入任务"; - } - return "已提交员工资产信息导入任务"; - } } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffFmyRelationController.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffFmyRelationController.java index 1eff571a..89b657cc 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffFmyRelationController.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffFmyRelationController.java @@ -10,7 +10,6 @@ import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportFailureVO; import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportSubmitResultVO; -import com.ruoyi.info.collection.service.ICcdiAssetInfoImportService; import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationImportService; import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationService; import com.ruoyi.info.collection.utils.EasyExcelUtil; @@ -51,9 +50,6 @@ public class CcdiStaffFmyRelationController extends BaseController { @Resource private ICcdiStaffFmyRelationImportService relationImportService; - @Resource - private ICcdiAssetInfoImportService assetInfoImportService; - /** * 查询员工亲属关系列表 */ @@ -157,15 +153,7 @@ public class CcdiStaffFmyRelationController extends BaseController { return error("至少需要一条数据"); } - StaffFmyRelationImportSubmitResultVO result = new StaffFmyRelationImportSubmitResultVO(); - if (hasRelationRows) { - result.setRelationTaskId(relationService.importRelation(relationList)); - } - if (hasAssetRows) { - result.setAssetTaskId(assetInfoImportService.importAssetInfo(assetList)); - } - result.setMessage(buildImportSubmitMessage(hasRelationRows, hasAssetRows)); - + StaffFmyRelationImportSubmitResultVO result = relationService.importRelationWithAssets(relationList, assetList); return AjaxResult.success("导入任务已提交,正在后台处理", result); } @@ -211,13 +199,4 @@ public class CcdiStaffFmyRelationController extends BaseController { return getDataTable(pageData, failures.size()); } - private String buildImportSubmitMessage(boolean hasRelationRows, boolean hasAssetRows) { - if (hasRelationRows && hasAssetRows) { - return "已提交员工亲属关系和亲属资产信息导入任务"; - } - if (hasRelationRows) { - return "已提交员工亲属关系导入任务"; - } - return "已提交亲属资产信息导入任务"; - } } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoImportService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoImportService.java index d2f52af1..e0beff43 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoImportService.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoImportService.java @@ -5,6 +5,8 @@ import com.ruoyi.info.collection.domain.vo.AssetImportFailureVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import java.util.List; +import java.util.Map; +import java.util.Set; /** * 亲属资产信息异步导入 服务层 @@ -31,6 +33,19 @@ public interface ICcdiAssetInfoImportService { */ void importAssetInfoAsync(List excelList, String taskId, String userName); + /** + * 同步执行亲属资产导入,可附加同一文件亲属关系Sheet成功导入的归属映射 + * + * @param excelList Excel实体列表 + * @param taskId 任务ID + * @param userName 用户名 + * @param extraOwnerMappings 附加归属映射,key为亲属证件号,value为归属员工证件号集合 + */ + void importAssetInfoSync(List excelList, + String taskId, + String userName, + Map> extraOwnerMappings); + /** * 查询导入状态 * diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffAssetImportService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffAssetImportService.java index 990d1df6..ec253028 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffAssetImportService.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffAssetImportService.java @@ -5,6 +5,8 @@ import com.ruoyi.info.collection.domain.vo.BaseStaffAssetImportFailureVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import java.util.List; +import java.util.Map; +import java.util.Set; /** * 员工资产信息异步导入 服务层 @@ -31,6 +33,19 @@ public interface ICcdiBaseStaffAssetImportService { */ void importAssetInfoAsync(List excelList, String taskId, String userName); + /** + * 同步执行员工资产导入,可附加同一文件员工Sheet成功导入的归属映射 + * + * @param excelList Excel实体列表 + * @param taskId 任务ID + * @param userName 用户名 + * @param extraOwnerMappings 附加归属映射,key为资产持有人证件号,value为归属员工证件号集合 + */ + void importAssetInfoSync(List excelList, + String taskId, + String userName, + Map> extraOwnerMappings); + /** * 查询导入状态 * diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffImportService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffImportService.java index a2b15508..a326770f 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffImportService.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffImportService.java @@ -5,6 +5,7 @@ import com.ruoyi.info.collection.domain.vo.ImportFailureVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import java.util.List; +import java.util.Set; /** * @Author: wkc @@ -19,6 +20,15 @@ public interface ICcdiBaseStaffImportService { */ void importBaseStaffAsync(List excelList, String taskId); + /** + * 同步执行员工导入并返回本轮成功员工身份证号 + * + * @param excelList Excel数据列表 + * @param taskId 任务ID + * @return 成功导入的身份证号集合 + */ + Set importBaseStaffSync(List excelList, String taskId); + /** * 查询导入状态 * diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffService.java index 400804e1..53d0560e 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffService.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffService.java @@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO; +import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel; import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel; +import com.ruoyi.info.collection.domain.vo.BaseStaffImportSubmitResultVO; import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffOptionVO; import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffVO; @@ -83,6 +85,16 @@ public interface ICcdiBaseStaffService { */ String importBaseStaff(List excelList); + /** + * 导入员工信息和员工资产双Sheet数据 + * + * @param staffList 员工信息Sheet + * @param assetList 员工资产Sheet + * @return 提交结果 + */ + BaseStaffImportSubmitResultVO importBaseStaffWithAssets(List staffList, + List assetList); + /** * 查询员工下拉列表 * 支持按员工ID或姓名模糊搜索,只返回在职员工 diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffFmyRelationImportService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffFmyRelationImportService.java index f07ff882..8dac3f3e 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffFmyRelationImportService.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffFmyRelationImportService.java @@ -5,6 +5,7 @@ import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportFailureVO; import java.util.List; +import java.util.Map; /** * 员工亲属关系异步导入 服务层 @@ -23,6 +24,16 @@ public interface ICcdiStaffFmyRelationImportService { */ void importRelationAsync(List excelList, String taskId, String userName); + /** + * 同步执行员工亲属关系导入并返回本轮成功关系映射 + * + * @param excelList Excel实体列表 + * @param taskId 任务ID + * @param userName 用户名 + * @return key为亲属证件号,value为归属员工证件号 + */ + Map importRelationSync(List excelList, String taskId, String userName); + /** * 查询导入失败记录 * diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffFmyRelationService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffFmyRelationService.java index b11d1e87..f85e73dd 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffFmyRelationService.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffFmyRelationService.java @@ -4,8 +4,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO; +import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel; import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel; import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO; +import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportSubmitResultVO; import java.util.List; @@ -81,4 +83,14 @@ public interface ICcdiStaffFmyRelationService { * @return 任务ID */ String importRelation(List excelList); + + /** + * 导入员工亲属关系和亲属资产双Sheet数据 + * + * @param relationList 员工亲属关系Sheet + * @param assetList 亲属资产Sheet + * @return 提交结果 + */ + StaffFmyRelationImportSubmitResultVO importRelationWithAssets(List relationList, + List assetList); } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java index 3dc26e1b..edd45f0e 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java @@ -82,6 +82,15 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi @Async @Transactional public void importAssetInfoAsync(List excelList, String taskId, String userName) { + importAssetInfoSync(excelList, taskId, userName, Map.of()); + } + + @Override + @Transactional + public void importAssetInfoSync(List excelList, + String taskId, + String userName, + Map> extraOwnerMappings) { List successList = new ArrayList<>(); List failures = new ArrayList<>(); @@ -92,6 +101,7 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi .toList(); Map> ownerMap = buildOwnerMap(personIds); + mergeOwnerMappings(ownerMap, extraOwnerMappings); for (int i = 0; i < excelList.size(); i++) { CcdiAssetInfoExcel excel = excelList.get(i); @@ -189,6 +199,18 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi } } + private void mergeOwnerMappings(Map> result, Map> mappings) { + if (mappings == null || mappings.isEmpty()) { + return; + } + for (Map.Entry> entry : mappings.entrySet()) { + if (StringUtils.isEmpty(entry.getKey()) || entry.getValue() == null || entry.getValue().isEmpty()) { + continue; + } + result.computeIfAbsent(entry.getKey(), key -> new java.util.LinkedHashSet<>()).addAll(entry.getValue()); + } + } + private void validateExcel(CcdiAssetInfoExcel excel) { if (StringUtils.isEmpty(excel.getPersonId())) { throw new RuntimeException("亲属证件号不能为空"); diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffAssetImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffAssetImportServiceImpl.java index 3f7aa3c4..09c6d3a2 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffAssetImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffAssetImportServiceImpl.java @@ -81,6 +81,15 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI @Async @Transactional public void importAssetInfoAsync(List excelList, String taskId, String userName) { + importAssetInfoSync(excelList, taskId, userName, Map.of()); + } + + @Override + @Transactional + public void importAssetInfoSync(List excelList, + String taskId, + String userName, + Map> extraOwnerMappings) { List successList = new ArrayList<>(); List failures = new ArrayList<>(); @@ -91,6 +100,7 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI .toList(); Map> ownerMap = buildOwnerMap(personIds); + mergeOwnerMappings(ownerMap, extraOwnerMappings); Set existingAssetKeys = buildExistingAssetKeys(personIds); Set importedAssetKeys = new java.util.LinkedHashSet<>(); @@ -207,6 +217,18 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI } } + private void mergeOwnerMappings(Map> result, Map> mappings) { + if (mappings == null || mappings.isEmpty()) { + return; + } + for (Map.Entry> entry : mappings.entrySet()) { + if (StringUtils.isEmpty(entry.getKey()) || entry.getValue() == null || entry.getValue().isEmpty()) { + continue; + } + result.computeIfAbsent(entry.getKey(), key -> new java.util.LinkedHashSet<>()).addAll(entry.getValue()); + } + } + private void validateExcel(CcdiBaseStaffAssetInfoExcel excel) { if (StringUtils.isEmpty(excel.getPersonId())) { throw new RuntimeException("员工身份证号不能为空"); diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java index bf594c61..18715af3 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java @@ -23,6 +23,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.*; @@ -51,6 +52,12 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi @Override @Async public void importBaseStaffAsync(List excelList, String taskId) { + importBaseStaffSync(excelList, taskId); + } + + @Override + @Transactional + public Set importBaseStaffSync(List excelList, String taskId) { long startTime = System.currentTimeMillis(); // 记录导入开始 @@ -153,6 +160,11 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi long duration = System.currentTimeMillis() - startTime; ImportLogUtils.logImportComplete(log, taskId, "员工基础信息", excelList.size(), result.getSuccessCount(), result.getFailureCount(), duration); + + return newRecords.stream() + .map(CcdiBaseStaff::getIdCard) + .filter(StringUtils::isNotEmpty) + .collect(Collectors.toCollection(LinkedHashSet::new)); } /** diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java index 435e3c66..99485933 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java @@ -6,15 +6,19 @@ import com.ruoyi.info.collection.domain.CcdiBaseStaff; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO; +import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel; import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel; +import com.ruoyi.info.collection.domain.vo.BaseStaffImportSubmitResultVO; import com.ruoyi.info.collection.domain.vo.CcdiAssetInfoVO; import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffOptionVO; import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffVO; import com.ruoyi.info.collection.enums.EmployeeStatus; import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper; import com.ruoyi.info.collection.service.ICcdiAssetInfoService; +import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService; import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService; import com.ruoyi.info.collection.service.ICcdiBaseStaffService; +import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import jakarta.annotation.Resource; import org.springframework.beans.BeanUtils; @@ -46,6 +50,12 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService { @Resource private ICcdiAssetInfoService assetInfoService; + @Resource + private ICcdiBaseStaffAssetImportService baseStaffAssetImportService; + + @Resource + private CcdiDualSheetImportOrchestrationService dualSheetImportOrchestrationService; + /** * 查询员工列表 * @@ -218,28 +228,52 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService { @Transactional public String importBaseStaff(List excelList) { String taskId = UUID.randomUUID().toString(); - long startTime = System.currentTimeMillis(); - - // 初始化Redis状态 - String statusKey = "import:baseStaff:" + taskId; - Map statusData = new HashMap<>(); - statusData.put("taskId", taskId); - statusData.put("status", "PROCESSING"); - statusData.put("totalCount", excelList.size()); - statusData.put("successCount", 0); - statusData.put("failureCount", 0); - statusData.put("progress", 0); - statusData.put("startTime", startTime); - statusData.put("message", "正在处理..."); - - redisTemplate.opsForHash().putAll(statusKey, statusData); - redisTemplate.expire(statusKey, 7, java.util.concurrent.TimeUnit.DAYS); + initializeImportStatus("import:baseStaff:", taskId, excelList.size()); importAsyncService.importBaseStaffAsync(excelList, taskId); return taskId; } + @Override + @Transactional + public BaseStaffImportSubmitResultVO importBaseStaffWithAssets(List staffList, + List assetList) { + boolean hasStaffRows = staffList != null && !staffList.isEmpty(); + boolean hasAssetRows = assetList != null && !assetList.isEmpty(); + if (!hasStaffRows && !hasAssetRows) { + throw new RuntimeException("至少需要一条数据"); + } + + BaseStaffImportSubmitResultVO result = new BaseStaffImportSubmitResultVO(); + result.setMessage(buildImportSubmitMessage(hasStaffRows, hasAssetRows)); + + if (hasStaffRows && !hasAssetRows) { + result.setStaffTaskId(importBaseStaff(staffList)); + return result; + } + if (!hasStaffRows) { + result.setAssetTaskId(baseStaffAssetImportService.importAssetInfo(assetList)); + return result; + } + + String staffTaskId = UUID.randomUUID().toString(); + String assetTaskId = UUID.randomUUID().toString(); + initializeImportStatus("import:baseStaff:", staffTaskId, staffList.size()); + initializeImportStatus("import:baseStaffAsset:", assetTaskId, assetList.size()); + + result.setStaffTaskId(staffTaskId); + result.setAssetTaskId(assetTaskId); + dualSheetImportOrchestrationService.importBaseStaffWithAssetsAsync( + staffList, + staffTaskId, + assetList, + assetTaskId, + currentUserName() + ); + return result; + } + /** * 查询员工下拉列表 * 支持按员工ID或姓名模糊搜索,只返回在职员工 @@ -252,6 +286,40 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService { return baseStaffMapper.selectStaffOptions(query); } + private void initializeImportStatus(String keyPrefix, String taskId, int totalCount) { + Map statusData = new HashMap<>(); + statusData.put("taskId", taskId); + statusData.put("status", "PROCESSING"); + statusData.put("totalCount", totalCount); + statusData.put("successCount", 0); + statusData.put("failureCount", 0); + statusData.put("progress", 0); + statusData.put("startTime", System.currentTimeMillis()); + statusData.put("message", "正在处理..."); + + String statusKey = keyPrefix + taskId; + redisTemplate.opsForHash().putAll(statusKey, statusData); + redisTemplate.expire(statusKey, 7, java.util.concurrent.TimeUnit.DAYS); + } + + private String buildImportSubmitMessage(boolean hasStaffRows, boolean hasAssetRows) { + if (hasStaffRows && hasAssetRows) { + return "已提交员工信息和员工资产信息导入任务"; + } + if (hasStaffRows) { + return "已提交员工信息导入任务"; + } + return "已提交员工资产信息导入任务"; + } + + private String currentUserName() { + try { + return SecurityUtils.getUsername(); + } catch (Exception e) { + return "system"; + } + } + /** * 构建查询条件 */ diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiDualSheetImportOrchestrationService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiDualSheetImportOrchestrationService.java new file mode 100644 index 00000000..04c241bf --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiDualSheetImportOrchestrationService.java @@ -0,0 +1,90 @@ +package com.ruoyi.info.collection.service.impl; + +import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel; +import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel; +import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel; +import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel; +import com.ruoyi.info.collection.service.ICcdiAssetInfoImportService; +import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService; +import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService; +import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationImportService; +import jakarta.annotation.Resource; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 双Sheet导入后台顺序编排。 + */ +@Service +public class CcdiDualSheetImportOrchestrationService { + + @Resource + private ICcdiBaseStaffImportService baseStaffImportService; + + @Resource + private ICcdiBaseStaffAssetImportService baseStaffAssetImportService; + + @Resource + private ICcdiStaffFmyRelationImportService relationImportService; + + @Resource + private ICcdiAssetInfoImportService assetInfoImportService; + + @Async + public void importBaseStaffWithAssetsAsync(List staffList, + String staffTaskId, + List assetList, + String assetTaskId, + String userName) { + Set successIdCards = baseStaffImportService.importBaseStaffSync(staffList, staffTaskId); + baseStaffAssetImportService.importAssetInfoSync( + assetList, + assetTaskId, + userName, + buildSelfOwnerMappings(successIdCards) + ); + } + + @Async + public void importRelationWithAssetsAsync(List relationList, + String relationTaskId, + List assetList, + String assetTaskId, + String userName) { + Map successRelationMappings = relationImportService.importRelationSync(relationList, relationTaskId, userName); + assetInfoImportService.importAssetInfoSync( + assetList, + assetTaskId, + userName, + buildRelationOwnerMappings(successRelationMappings) + ); + } + + private Map> buildSelfOwnerMappings(Set idCards) { + Map> result = new LinkedHashMap<>(); + if (idCards == null || idCards.isEmpty()) { + return result; + } + for (String idCard : idCards) { + result.computeIfAbsent(idCard, key -> new LinkedHashSet<>()).add(idCard); + } + return result; + } + + private Map> buildRelationOwnerMappings(Map relationMappings) { + Map> result = new LinkedHashMap<>(); + if (relationMappings == null || relationMappings.isEmpty()) { + return result; + } + for (Map.Entry entry : relationMappings.entrySet()) { + result.computeIfAbsent(entry.getKey(), key -> new LinkedHashSet<>()).add(entry.getValue()); + } + return result; + } +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationImportServiceImpl.java index 5d7e3588..145d81bb 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationImportServiceImpl.java @@ -57,6 +57,12 @@ public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelat @Async @Transactional public void importRelationAsync(List excelList, String taskId, String userName) { + importRelationSync(excelList, taskId, userName); + } + + @Override + @Transactional + public Map importRelationSync(List excelList, String taskId, String userName) { long startTime = System.currentTimeMillis(); // 记录导入开始 @@ -213,6 +219,15 @@ public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelat long duration = System.currentTimeMillis() - startTime; ImportLogUtils.logImportComplete(log, taskId, "员工亲属关系", excelList.size(), result.getSuccessCount(), result.getFailureCount(), duration); + + return newRecords.stream() + .filter(item -> StringUtils.isNotEmpty(item.getRelationCertNo()) && StringUtils.isNotEmpty(item.getPersonId())) + .collect(Collectors.toMap( + CcdiStaffFmyRelation::getRelationCertNo, + CcdiStaffFmyRelation::getPersonId, + (left, right) -> left, + LinkedHashMap::new + )); } /** diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationServiceImpl.java index 29b14b10..9fa485da 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationServiceImpl.java @@ -6,11 +6,14 @@ import com.ruoyi.info.collection.domain.CcdiStaffFmyRelation; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO; +import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel; import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel; import com.ruoyi.info.collection.domain.vo.CcdiAssetInfoVO; import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO; +import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportSubmitResultVO; import com.ruoyi.info.collection.mapper.CcdiStaffEnterpriseRelationMapper; import com.ruoyi.info.collection.mapper.CcdiStaffFmyRelationMapper; +import com.ruoyi.info.collection.service.ICcdiAssetInfoImportService; import com.ruoyi.info.collection.service.ICcdiAssetInfoService; import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationImportService; import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationService; @@ -51,9 +54,15 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer @Resource private ICcdiAssetInfoService assetInfoService; + @Resource + private ICcdiAssetInfoImportService assetInfoImportService; + @Resource private CcdiStaffEnterpriseRelationMapper staffEnterpriseRelationMapper; + @Resource + private CcdiDualSheetImportOrchestrationService dualSheetImportOrchestrationService; + /** * 查询员工亲属关系列表 * @@ -207,25 +216,11 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer // 生成任务ID String taskId = UUID.randomUUID().toString(); - long startTime = System.currentTimeMillis(); // 获取当前用户名 String userName = SecurityUtils.getUsername(); - // 初始化Redis状态 - String statusKey = "import:staffFmyRelation:" + taskId; - Map statusData = new HashMap<>(); - statusData.put("taskId", taskId); - statusData.put("status", "PROCESSING"); - statusData.put("totalCount", excelList.size()); - statusData.put("successCount", 0); - statusData.put("failureCount", 0); - statusData.put("progress", 0); - statusData.put("startTime", startTime); - statusData.put("message", "正在处理..."); - - redisTemplate.opsForHash().putAll(statusKey, statusData); - redisTemplate.expire(statusKey, 7, TimeUnit.DAYS); + initializeImportStatus("import:staffFmyRelation:", taskId, excelList.size()); // 调用异步导入服务 relationImportService.importRelationAsync(excelList, taskId, userName); @@ -233,6 +228,79 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer return taskId; } + @Override + @Transactional + public StaffFmyRelationImportSubmitResultVO importRelationWithAssets(List relationList, + List assetList) { + boolean hasRelationRows = relationList != null && !relationList.isEmpty(); + boolean hasAssetRows = assetList != null && !assetList.isEmpty(); + if (!hasRelationRows && !hasAssetRows) { + throw new RuntimeException("至少需要一条数据"); + } + + StaffFmyRelationImportSubmitResultVO result = new StaffFmyRelationImportSubmitResultVO(); + result.setMessage(buildImportSubmitMessage(hasRelationRows, hasAssetRows)); + + if (hasRelationRows && !hasAssetRows) { + result.setRelationTaskId(importRelation(relationList)); + return result; + } + if (!hasRelationRows) { + result.setAssetTaskId(assetInfoImportService.importAssetInfo(assetList)); + return result; + } + + String relationTaskId = UUID.randomUUID().toString(); + String assetTaskId = UUID.randomUUID().toString(); + initializeImportStatus("import:staffFmyRelation:", relationTaskId, relationList.size()); + initializeImportStatus("import:assetInfo:", assetTaskId, assetList.size()); + + result.setRelationTaskId(relationTaskId); + result.setAssetTaskId(assetTaskId); + dualSheetImportOrchestrationService.importRelationWithAssetsAsync( + relationList, + relationTaskId, + assetList, + assetTaskId, + currentUserName() + ); + return result; + } + + private void initializeImportStatus(String keyPrefix, String taskId, int totalCount) { + Map statusData = new HashMap<>(); + statusData.put("taskId", taskId); + statusData.put("status", "PROCESSING"); + statusData.put("totalCount", totalCount); + statusData.put("successCount", 0); + statusData.put("failureCount", 0); + statusData.put("progress", 0); + statusData.put("startTime", System.currentTimeMillis()); + statusData.put("message", "正在处理..."); + + String statusKey = keyPrefix + taskId; + redisTemplate.opsForHash().putAll(statusKey, statusData); + redisTemplate.expire(statusKey, 7, TimeUnit.DAYS); + } + + private String buildImportSubmitMessage(boolean hasRelationRows, boolean hasAssetRows) { + if (hasRelationRows && hasAssetRows) { + return "已提交员工亲属关系和亲属资产信息导入任务"; + } + if (hasRelationRows) { + return "已提交员工亲属关系导入任务"; + } + return "已提交亲属资产信息导入任务"; + } + + private String currentUserName() { + try { + return SecurityUtils.getUsername(); + } catch (Exception e) { + return "system"; + } + } + private CcdiAssetInfoVO toAssetInfoVO(CcdiAssetInfo assetInfo) { CcdiAssetInfoVO assetInfoVO = new CcdiAssetInfoVO(); BeanUtils.copyProperties(assetInfo, assetInfoVO); diff --git a/docs/reports/implementation/2026-05-06-staff-asset-import-and-enterprise-autofill-backend-implementation.md b/docs/reports/implementation/2026-05-06-staff-asset-import-and-enterprise-autofill-backend-implementation.md new file mode 100644 index 00000000..b763f6de --- /dev/null +++ b/docs/reports/implementation/2026-05-06-staff-asset-import-and-enterprise-autofill-backend-implementation.md @@ -0,0 +1,43 @@ +# 员工资产导入与实体库自动补入后端实施记录 + +## 基本信息 + +- 实施日期:2026-05-06 +- 实施范围:后端 +- 关联计划:`docs/plans/backend/2026-05-06-staff-asset-import-and-enterprise-autofill-fix-backend-implementation-plan.md` + +## 修改内容 + +### 双 Sheet 导入任务编排 + +- 员工信息维护导入入口改为由服务层统一编排员工主 Sheet 与员工资产 Sheet。 +- 员工亲属关系维护导入入口改为由服务层统一编排亲属关系主 Sheet 与亲属资产 Sheet。 +- 当两个 Sheet 都有数据时,仍返回两个任务 ID,并按主 Sheet 导入成功结果为资产 Sheet 提供同文件内的归属映射。 +- 当只导入资产 Sheet 时,仅生成并返回资产导入任务 ID,不生成员工或亲属关系主任务 ID。 +- 当两个 Sheet 都为空时,保持返回“至少需要一条数据”。 + +### 实体库自动补入 + +- 新增统一的实体库自动补入服务,按统一社会信用代码去重,只插入实体库不存在的记录。 +- 员工企业关系、信贷客户企业关系新增和导入时自动补入实体库。 +- 中介新增、编辑和导入时取消“实体库必须已存在”的阻断校验;实体库缺失时按中介来源自动补入,不要求提供机构名称。 +- 招投标供应商新增、编辑和导入时,对合法统一社会信用代码的供应商自动补入实体库。 +- 新增企业来源枚举 `SUPPLIER`,用于标识供应商来源。 + +## 影响范围 + +- `/ccdi/baseStaff/importData` +- `/ccdi/staffFmyRelation/importData` +- 员工企业关系、信贷客户企业关系、中介、招投标供应商的新增/编辑/导入实体库联动逻辑 +- 实体库基础信息表 `ccdi_enterprise_base_info` + +## 验证记录 + +- 执行 `mvn -pl ccdi-info-collection -am -DskipTests compile`,结果:BUILD SUCCESS。 +- 执行 `mvn -DskipTests compile`,结果:BUILD SUCCESS。 +- 代码路径核对:员工主 Sheet 为空时仅调用员工资产导入服务并返回 `assetTaskId`;亲属关系主 Sheet 为空时仅调用亲属资产导入服务并返回 `assetTaskId`。 + +## 备注 + +- 本次为后端逻辑调整,未修改前端页面代码。 +- 现有前端已按 `staffTaskId` / `relationTaskId` / `assetTaskId` 是否存在分别启动轮询,可直接兼容只返回资产任务 ID 的结果。