合并实体库自动补入与双Sheet导入修复

This commit is contained in:
wkc
2026-05-06 20:53:29 +08:00
30 changed files with 762 additions and 107 deletions

View File

@@ -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 "已提交员工资产信息导入任务";
}
}

View File

@@ -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 "已提交亲属资产信息导入任务";
}
}

View File

@@ -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;
}

View File

@@ -10,6 +10,7 @@ public enum EnterpriseSource {
GENERAL("GENERAL", "一般企业"),
EMP_RELATION("EMP_RELATION", "员工关系人"),
CREDIT_CUSTOMER("CREDIT_CUSTOMER", "信贷客户"),
SUPPLIER("SUPPLIER", "供应商"),
INTERMEDIARY("INTERMEDIARY", "中介"),
BOTH("BOTH", "兼有");

View File

@@ -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<CcdiAssetInfoExcel> excelList, String taskId, String userName);
/**
* 同步执行亲属资产导入可附加同一文件亲属关系Sheet成功导入的归属映射
*
* @param excelList Excel实体列表
* @param taskId 任务ID
* @param userName 用户名
* @param extraOwnerMappings 附加归属映射key为亲属证件号value为归属员工证件号集合
*/
void importAssetInfoSync(List<CcdiAssetInfoExcel> excelList,
String taskId,
String userName,
Map<String, Set<String>> extraOwnerMappings);
/**
* 查询导入状态
*

View File

@@ -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<CcdiBaseStaffAssetInfoExcel> excelList, String taskId, String userName);
/**
* 同步执行员工资产导入可附加同一文件员工Sheet成功导入的归属映射
*
* @param excelList Excel实体列表
* @param taskId 任务ID
* @param userName 用户名
* @param extraOwnerMappings 附加归属映射key为资产持有人证件号value为归属员工证件号集合
*/
void importAssetInfoSync(List<CcdiBaseStaffAssetInfoExcel> excelList,
String taskId,
String userName,
Map<String, Set<String>> extraOwnerMappings);
/**
* 查询导入状态
*

View File

@@ -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<CcdiBaseStaffExcel> excelList, String taskId);
/**
* 同步执行员工导入并返回本轮成功员工身份证号
*
* @param excelList Excel数据列表
* @param taskId 任务ID
* @return 成功导入的身份证号集合
*/
Set<String> importBaseStaffSync(List<CcdiBaseStaffExcel> excelList, String taskId);
/**
* 查询导入状态
*

View File

@@ -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<CcdiBaseStaffExcel> excelList);
/**
* 导入员工信息和员工资产双Sheet数据
*
* @param staffList 员工信息Sheet
* @param assetList 员工资产Sheet
* @return 提交结果
*/
BaseStaffImportSubmitResultVO importBaseStaffWithAssets(List<CcdiBaseStaffExcel> staffList,
List<CcdiBaseStaffAssetInfoExcel> assetList);
/**
* 查询员工下拉列表
* 支持按员工ID或姓名模糊搜索只返回在职员工

View File

@@ -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<CcdiStaffFmyRelationExcel> excelList, String taskId, String userName);
/**
* 同步执行员工亲属关系导入并返回本轮成功关系映射
*
* @param excelList Excel实体列表
* @param taskId 任务ID
* @param userName 用户名
* @return key为亲属证件号value为归属员工证件号
*/
Map<String, String> importRelationSync(List<CcdiStaffFmyRelationExcel> excelList, String taskId, String userName);
/**
* 查询导入失败记录
*

View File

@@ -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<CcdiStaffFmyRelationExcel> excelList);
/**
* 导入员工亲属关系和亲属资产双Sheet数据
*
* @param relationList 员工亲属关系Sheet
* @param assetList 亲属资产Sheet
* @return 提交结果
*/
StaffFmyRelationImportSubmitResultVO importRelationWithAssets(List<CcdiStaffFmyRelationExcel> relationList,
List<CcdiAssetInfoExcel> assetList);
}

View File

@@ -82,6 +82,15 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
@Async
@Transactional
public void importAssetInfoAsync(List<CcdiAssetInfoExcel> excelList, String taskId, String userName) {
importAssetInfoSync(excelList, taskId, userName, Map.of());
}
@Override
@Transactional
public void importAssetInfoSync(List<CcdiAssetInfoExcel> excelList,
String taskId,
String userName,
Map<String, Set<String>> extraOwnerMappings) {
List<CcdiAssetInfo> successList = new ArrayList<>();
List<AssetImportFailureVO> failures = new ArrayList<>();
@@ -92,6 +101,7 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
.toList();
Map<String, Set<String>> 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<String, Set<String>> result, Map<String, Set<String>> mappings) {
if (mappings == null || mappings.isEmpty()) {
return;
}
for (Map.Entry<String, Set<String>> 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("亲属证件号不能为空");

View File

@@ -81,6 +81,15 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
@Async
@Transactional
public void importAssetInfoAsync(List<CcdiBaseStaffAssetInfoExcel> excelList, String taskId, String userName) {
importAssetInfoSync(excelList, taskId, userName, Map.of());
}
@Override
@Transactional
public void importAssetInfoSync(List<CcdiBaseStaffAssetInfoExcel> excelList,
String taskId,
String userName,
Map<String, Set<String>> extraOwnerMappings) {
List<CcdiAssetInfo> successList = new ArrayList<>();
List<BaseStaffAssetImportFailureVO> failures = new ArrayList<>();
@@ -91,6 +100,7 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
.toList();
Map<String, Set<String>> ownerMap = buildOwnerMap(personIds);
mergeOwnerMappings(ownerMap, extraOwnerMappings);
Set<String> existingAssetKeys = buildExistingAssetKeys(personIds);
Set<String> importedAssetKeys = new java.util.LinkedHashSet<>();
@@ -207,6 +217,18 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
}
}
private void mergeOwnerMappings(Map<String, Set<String>> result, Map<String, Set<String>> mappings) {
if (mappings == null || mappings.isEmpty()) {
return;
}
for (Map.Entry<String, Set<String>> 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("员工身份证号不能为空");

View File

@@ -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<CcdiBaseStaffExcel> excelList, String taskId) {
importBaseStaffSync(excelList, taskId);
}
@Override
@Transactional
public Set<String> importBaseStaffSync(List<CcdiBaseStaffExcel> 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));
}
/**

View File

@@ -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<CcdiBaseStaffExcel> excelList) {
String taskId = UUID.randomUUID().toString();
long startTime = System.currentTimeMillis();
// 初始化Redis状态
String statusKey = "import:baseStaff:" + taskId;
Map<String, Object> 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<CcdiBaseStaffExcel> staffList,
List<CcdiBaseStaffAssetInfoExcel> 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<String, Object> 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";
}
}
/**
* 构建查询条件
*/

View File

@@ -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<String, Object> 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);

View File

@@ -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<String, Object> 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;

View File

@@ -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<CcdiBaseStaffExcel> staffList,
String staffTaskId,
List<CcdiBaseStaffAssetInfoExcel> assetList,
String assetTaskId,
String userName) {
Set<String> successIdCards = baseStaffImportService.importBaseStaffSync(staffList, staffTaskId);
baseStaffAssetImportService.importAssetInfoSync(
assetList,
assetTaskId,
userName,
buildSelfOwnerMappings(successIdCards)
);
}
@Async
public void importRelationWithAssetsAsync(List<CcdiStaffFmyRelationExcel> relationList,
String relationTaskId,
List<CcdiAssetInfoExcel> assetList,
String assetTaskId,
String userName) {
Map<String, String> successRelationMappings = relationImportService.importRelationSync(relationList, relationTaskId, userName);
assetInfoImportService.importAssetInfoSync(
assetList,
assetTaskId,
userName,
buildRelationOwnerMappings(successRelationMappings)
);
}
private Map<String, Set<String>> buildSelfOwnerMappings(Set<String> idCards) {
Map<String, Set<String>> 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<String, Set<String>> buildRelationOwnerMappings(Map<String, String> relationMappings) {
Map<String, Set<String>> result = new LinkedHashMap<>();
if (relationMappings == null || relationMappings.isEmpty()) {
return result;
}
for (Map.Entry<String, String> entry : relationMappings.entrySet()) {
result.computeIfAbsent(entry.getKey(), key -> new LinkedHashSet<>()).add(entry.getValue());
}
return result;
}
}

View File

@@ -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()));

View File

@@ -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<String, Object> redisTemplate;
@Resource
private RedisTemplate<String, Object> redisTemplate;
private EnterpriseAutoFillService enterpriseAutoFillService;
@Override
@Async
@@ -67,7 +68,6 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
ImportLogUtils.logImportStart(log, taskId, "中介实体关联关系", excelList.size(), userName);
Map<String, String> ownerBizIdByPersonId = getOwnerBizIdByPersonId(excelList);
Set<String> existingEnterpriseCodes = getExistingEnterpriseCodes(excelList);
Set<String> existingCombinations = getExistingRelationCombinations(ownerBizIdByPersonId, excelList);
List<CcdiIntermediaryEnterpriseRelation> 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<String> getExistingEnterpriseCodes(List<CcdiIntermediaryEnterpriseRelationExcel> excelList) {
List<String> socialCreditCodes = excelList.stream()
.map(CcdiIntermediaryEnterpriseRelationExcel::getSocialCreditCode)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
if (socialCreditCodes.isEmpty()) {
return Collections.emptySet();
}
LambdaQueryWrapper<CcdiEnterpriseBaseInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CcdiEnterpriseBaseInfo::getSocialCreditCode, socialCreditCodes);
return enterpriseBaseInfoMapper.selectList(wrapper).stream()
.map(CcdiEnterpriseBaseInfo::getSocialCreditCode)
.collect(Collectors.toSet());
}
private Set<String> getExistingRelationCombinations(Map<String, String> ownerBizIdByPersonId,
List<CcdiIntermediaryEnterpriseRelationExcel> excelList) {
List<String> combinations = excelList.stream()

View File

@@ -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<String, Object> 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) {

View File

@@ -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<String, Object> 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<CcdiPurchaseTransactionSupplier> 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());
}
/**
* 验证采购交易数据
*

View File

@@ -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<String, Object> 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<CcdiPurchaseTransactionSupplier> 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<CcdiPurchaseTransactionSupplierVO> selectSupplierListByPurchaseId(String purchaseId) {
return supplierMapper.selectList(
new LambdaQueryWrapper<CcdiPurchaseTransactionSupplier>()

View File

@@ -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);

View File

@@ -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<String, Object> 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;

View File

@@ -57,6 +57,12 @@ public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelat
@Async
@Transactional
public void importRelationAsync(List<CcdiStaffFmyRelationExcel> excelList, String taskId, String userName) {
importRelationSync(excelList, taskId, userName);
}
@Override
@Transactional
public Map<String, String> importRelationSync(List<CcdiStaffFmyRelationExcel> 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
));
}
/**

View File

@@ -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<String, Object> 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<CcdiStaffFmyRelationExcel> relationList,
List<CcdiAssetInfoExcel> 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<String, Object> 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);

View File

@@ -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<EnterpriseFillItem> items) {
if (StringUtils.isEmpty(items)) {
return;
}
Map<String, EnterpriseFillItem> normalizedItems = normalizeItems(items);
if (normalizedItems.isEmpty()) {
return;
}
Set<String> existingCodes = enterpriseBaseInfoMapper.selectBatchIds(new ArrayList<>(normalizedItems.keySet()))
.stream()
.map(CcdiEnterpriseBaseInfo::getSocialCreditCode)
.collect(Collectors.toSet());
List<CcdiEnterpriseBaseInfo> 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<String, EnterpriseFillItem> normalizeItems(List<EnterpriseFillItem> items) {
Map<String, EnterpriseFillItem> 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<CcdiEnterpriseBaseInfo> 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<CcdiEnterpriseBaseInfo> 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();
}
}

View File

@@ -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`
- 本次未直接执行远程库结构变更;提权执行迁移脚本被安全审查拦截。真实写入空企业名称的补库验证需先由授权人员执行迁移脚本。

View File

@@ -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 的结果。

View File

@@ -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 '企业名称';