docs: 添加采购交易导入功能优化设计文档
设计目标: - 采用后台异步处理+通知提示,避免弹窗阻塞用户操作 - 完全复用员工信息维护的导入逻辑 - 支持查看导入失败记录 - 实现状态持久化 主要设计内容: - 整体架构和用户交互流程 - 前端组件结构和状态管理 - UI组件修改方案 - 核心方法实现(10个方法) - 完整修改清单和测试要点 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package com.ruoyi.ccdi.domain.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 实体中介导入失败记录VO
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-06
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "实体中介导入失败记录")
|
||||
public class IntermediaryEntityImportFailureVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "机构名称")
|
||||
private String enterpriseName;
|
||||
|
||||
@Schema(description = "统一社会信用代码")
|
||||
private String socialCreditCode;
|
||||
|
||||
@Schema(description = "主体类型")
|
||||
private String enterpriseType;
|
||||
|
||||
@Schema(description = "企业性质")
|
||||
private String enterpriseNature;
|
||||
|
||||
@Schema(description = "法定代表人")
|
||||
private String legalRepresentative;
|
||||
|
||||
@Schema(description = "成立日期")
|
||||
private Date establishDate;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMessage;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.ruoyi.ccdi.domain.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 个人中介导入失败记录VO
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-06
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "个人中介导入失败记录")
|
||||
public class IntermediaryPersonImportFailureVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "姓名")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "证件号码")
|
||||
private String personId;
|
||||
|
||||
@Schema(description = "人员类型")
|
||||
private String personType;
|
||||
|
||||
@Schema(description = "性别")
|
||||
private String gender;
|
||||
|
||||
@Schema(description = "手机号码")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "所在公司")
|
||||
private String company;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMessage;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.ruoyi.ccdi.service;
|
||||
|
||||
import com.ruoyi.ccdi.domain.excel.CcdiIntermediaryEntityExcel;
|
||||
import com.ruoyi.ccdi.domain.vo.ImportStatusVO;
|
||||
import com.ruoyi.ccdi.domain.vo.IntermediaryEntityImportFailureVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 实体中介异步导入Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-06
|
||||
*/
|
||||
public interface ICcdiIntermediaryEntityImportService {
|
||||
|
||||
/**
|
||||
* 异步导入实体中介数据
|
||||
*
|
||||
* @param excelList Excel数据列表
|
||||
* @param isUpdateSupport 是否更新已存在的数据
|
||||
* @param taskId 任务ID
|
||||
* @param userName 当前用户名(用于审计字段)
|
||||
*/
|
||||
void importEntityAsync(List<CcdiIntermediaryEntityExcel> excelList,
|
||||
Boolean isUpdateSupport,
|
||||
String taskId,
|
||||
String userName);
|
||||
|
||||
/**
|
||||
* 查询导入状态
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 导入状态信息
|
||||
*/
|
||||
ImportStatusVO getImportStatus(String taskId);
|
||||
|
||||
/**
|
||||
* 获取导入失败记录
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 失败记录列表
|
||||
*/
|
||||
List<IntermediaryEntityImportFailureVO> getImportFailures(String taskId);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.ruoyi.ccdi.service;
|
||||
|
||||
import com.ruoyi.ccdi.domain.excel.CcdiIntermediaryPersonExcel;
|
||||
import com.ruoyi.ccdi.domain.vo.ImportStatusVO;
|
||||
import com.ruoyi.ccdi.domain.vo.IntermediaryPersonImportFailureVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 个人中介异步导入Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-06
|
||||
*/
|
||||
public interface ICcdiIntermediaryPersonImportService {
|
||||
|
||||
/**
|
||||
* 异步导入个人中介数据
|
||||
*
|
||||
* @param excelList Excel数据列表
|
||||
* @param isUpdateSupport 是否更新已存在的数据
|
||||
* @param taskId 任务ID
|
||||
* @param userName 当前用户名(用于审计字段)
|
||||
*/
|
||||
void importPersonAsync(List<CcdiIntermediaryPersonExcel> excelList,
|
||||
Boolean isUpdateSupport,
|
||||
String taskId,
|
||||
String userName);
|
||||
|
||||
/**
|
||||
* 查询导入状态
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 导入状态信息
|
||||
*/
|
||||
ImportStatusVO getImportStatus(String taskId);
|
||||
|
||||
/**
|
||||
* 获取导入失败记录
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 失败记录列表
|
||||
*/
|
||||
List<IntermediaryPersonImportFailureVO> getImportFailures(String taskId);
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package com.ruoyi.ccdi.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.ccdi.domain.CcdiEnterpriseBaseInfo;
|
||||
import com.ruoyi.ccdi.domain.excel.CcdiIntermediaryEntityExcel;
|
||||
import com.ruoyi.ccdi.domain.vo.ImportResult;
|
||||
import com.ruoyi.ccdi.domain.vo.ImportStatusVO;
|
||||
import com.ruoyi.ccdi.domain.vo.IntermediaryEntityImportFailureVO;
|
||||
import com.ruoyi.ccdi.mapper.CcdiEnterpriseBaseInfoMapper;
|
||||
import com.ruoyi.ccdi.service.ICcdiIntermediaryEntityImportService;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
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.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 实体中介异步导入Service实现
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-06
|
||||
*/
|
||||
@Service
|
||||
@EnableAsync
|
||||
public class CcdiIntermediaryEntityImportServiceImpl implements ICcdiIntermediaryEntityImportService {
|
||||
|
||||
@Resource
|
||||
private CcdiEnterpriseBaseInfoMapper entityMapper;
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Override
|
||||
@Async
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void importEntityAsync(List<CcdiIntermediaryEntityExcel> excelList,
|
||||
Boolean isUpdateSupport,
|
||||
String taskId,
|
||||
String userName) {
|
||||
List<CcdiEnterpriseBaseInfo> newRecords = new ArrayList<>();
|
||||
List<CcdiEnterpriseBaseInfo> updateRecords = new ArrayList<>();
|
||||
List<IntermediaryEntityImportFailureVO> failures = new ArrayList<>();
|
||||
|
||||
// 1. 批量查询已存在的统一社会信用代码
|
||||
Set<String> existingCreditCodes = getExistingCreditCodes(excelList);
|
||||
|
||||
// 2. 分类数据
|
||||
for (int i = 0; i < excelList.size(); i++) {
|
||||
CcdiIntermediaryEntityExcel excel = excelList.get(i);
|
||||
|
||||
try {
|
||||
// 验证数据
|
||||
validateEntityData(excel, isUpdateSupport, existingCreditCodes);
|
||||
|
||||
CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo();
|
||||
BeanUtils.copyProperties(excel, entity);
|
||||
|
||||
// 设置数据来源和审计字段
|
||||
entity.setDataSource("IMPORT");
|
||||
entity.setEntSource("INTERMEDIARY");
|
||||
entity.setCreatedBy(userName);
|
||||
|
||||
if (existingCreditCodes.contains(excel.getSocialCreditCode())) {
|
||||
if (isUpdateSupport) {
|
||||
// 更新模式:设置更新人
|
||||
entity.setUpdatedBy(userName);
|
||||
updateRecords.add(entity);
|
||||
} else {
|
||||
throw new RuntimeException("该统一社会信用代码已存在");
|
||||
}
|
||||
} else {
|
||||
newRecords.add(entity);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
IntermediaryEntityImportFailureVO failure = new IntermediaryEntityImportFailureVO();
|
||||
BeanUtils.copyProperties(excel, failure);
|
||||
failure.setErrorMessage(e.getMessage());
|
||||
failures.add(failure);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 批量插入新数据
|
||||
if (!newRecords.isEmpty()) {
|
||||
saveBatch(newRecords, 500);
|
||||
}
|
||||
|
||||
// 4. 批量更新已有数据(先删除再插入)
|
||||
if (!updateRecords.isEmpty() && isUpdateSupport) {
|
||||
// 先批量删除已存在的记录
|
||||
List<String> creditCodes = updateRecords.stream()
|
||||
.map(CcdiEnterpriseBaseInfo::getSocialCreditCode)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
LambdaQueryWrapper<CcdiEnterpriseBaseInfo> deleteWrapper = new LambdaQueryWrapper<>();
|
||||
deleteWrapper.in(CcdiEnterpriseBaseInfo::getSocialCreditCode, creditCodes);
|
||||
entityMapper.delete(deleteWrapper);
|
||||
|
||||
// 批量插入更新后的数据
|
||||
entityMapper.insertBatch(updateRecords);
|
||||
}
|
||||
|
||||
// 5. 保存失败记录到Redis
|
||||
if (!failures.isEmpty()) {
|
||||
String failuresKey = "import:intermediary-entity:" + taskId + ":failures";
|
||||
redisTemplate.opsForValue().set(failuresKey, failures, 7, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
// 6. 更新最终状态
|
||||
ImportResult result = new ImportResult();
|
||||
result.setTotalCount(excelList.size());
|
||||
result.setSuccessCount(newRecords.size() + updateRecords.size());
|
||||
result.setFailureCount(failures.size());
|
||||
|
||||
String finalStatus = result.getFailureCount() == 0 ? "SUCCESS" : "PARTIAL_SUCCESS";
|
||||
updateImportStatus(taskId, finalStatus, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportStatusVO getImportStatus(String taskId) {
|
||||
String key = "import:intermediary-entity:" + taskId;
|
||||
Boolean hasKey = redisTemplate.hasKey(key);
|
||||
|
||||
if (Boolean.FALSE.equals(hasKey)) {
|
||||
throw new RuntimeException("任务不存在或已过期");
|
||||
}
|
||||
|
||||
Map<Object, Object> statusMap = redisTemplate.opsForHash().entries(key);
|
||||
|
||||
ImportStatusVO statusVO = new ImportStatusVO();
|
||||
statusVO.setTaskId((String) statusMap.get("taskId"));
|
||||
statusVO.setStatus((String) statusMap.get("status"));
|
||||
statusVO.setTotalCount((Integer) statusMap.get("totalCount"));
|
||||
statusVO.setSuccessCount((Integer) statusMap.get("successCount"));
|
||||
statusVO.setFailureCount((Integer) statusMap.get("failureCount"));
|
||||
statusVO.setProgress((Integer) statusMap.get("progress"));
|
||||
statusVO.setStartTime((Long) statusMap.get("startTime"));
|
||||
statusVO.setEndTime((Long) statusMap.get("endTime"));
|
||||
statusVO.setMessage((String) statusMap.get("message"));
|
||||
|
||||
return statusVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntermediaryEntityImportFailureVO> getImportFailures(String taskId) {
|
||||
String key = "import:intermediary-entity:" + taskId + ":failures";
|
||||
Object failuresObj = redisTemplate.opsForValue().get(key);
|
||||
|
||||
if (failuresObj == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return JSON.parseArray(JSON.toJSONString(failuresObj), IntermediaryEntityImportFailureVO.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询已存在的统一社会信用代码
|
||||
*/
|
||||
private Set<String> getExistingCreditCodes(List<CcdiIntermediaryEntityExcel> excelList) {
|
||||
List<String> creditCodes = excelList.stream()
|
||||
.map(CcdiIntermediaryEntityExcel::getSocialCreditCode)
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (creditCodes.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<CcdiEnterpriseBaseInfo> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.in(CcdiEnterpriseBaseInfo::getSocialCreditCode, creditCodes);
|
||||
List<CcdiEnterpriseBaseInfo> existingEntities = entityMapper.selectList(wrapper);
|
||||
|
||||
return existingEntities.stream()
|
||||
.map(CcdiEnterpriseBaseInfo::getSocialCreditCode)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存
|
||||
*/
|
||||
private void saveBatch(List<CcdiEnterpriseBaseInfo> list, int batchSize) {
|
||||
// 使用真正的批量插入,分批次执行以提高性能
|
||||
for (int i = 0; i < list.size(); i += batchSize) {
|
||||
int end = Math.min(i + batchSize, list.size());
|
||||
List<CcdiEnterpriseBaseInfo> subList = list.subList(i, end);
|
||||
entityMapper.insertBatch(subList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新导入状态
|
||||
*/
|
||||
private void updateImportStatus(String taskId, String status, ImportResult result) {
|
||||
String key = "import:intermediary-entity:" + taskId;
|
||||
Map<String, Object> statusData = new HashMap<>();
|
||||
statusData.put("status", status);
|
||||
statusData.put("successCount", result.getSuccessCount());
|
||||
statusData.put("failureCount", result.getFailureCount());
|
||||
statusData.put("progress", 100);
|
||||
statusData.put("endTime", System.currentTimeMillis());
|
||||
|
||||
if ("SUCCESS".equals(status)) {
|
||||
statusData.put("message", "全部成功!共导入" + result.getTotalCount() + "条数据");
|
||||
} else {
|
||||
statusData.put("message", "成功" + result.getSuccessCount() + "条,失败" + result.getFailureCount() + "条");
|
||||
}
|
||||
|
||||
redisTemplate.opsForHash().putAll(key, statusData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证实体中介数据
|
||||
*
|
||||
* @param excel Excel数据
|
||||
* @param isUpdateSupport 是否支持更新
|
||||
* @param existingCreditCodes 已存在的统一社会信用代码集合
|
||||
*/
|
||||
private void validateEntityData(CcdiIntermediaryEntityExcel excel,
|
||||
Boolean isUpdateSupport,
|
||||
Set<String> existingCreditCodes) {
|
||||
// 验证必填字段:机构名称
|
||||
if (StringUtils.isEmpty(excel.getEnterpriseName())) {
|
||||
throw new RuntimeException("机构名称不能为空");
|
||||
}
|
||||
|
||||
// 验证必填字段:统一社会信用代码
|
||||
if (StringUtils.isEmpty(excel.getSocialCreditCode())) {
|
||||
throw new RuntimeException("统一社会信用代码不能为空");
|
||||
}
|
||||
|
||||
// 如果统一社会信用代码已存在但未启用更新支持,抛出异常
|
||||
if (existingCreditCodes.contains(excel.getSocialCreditCode()) && !isUpdateSupport) {
|
||||
throw new RuntimeException("该统一社会信用代码已存在");
|
||||
}
|
||||
|
||||
// 如果统一社会信用代码不存在,检查唯一性
|
||||
if (!existingCreditCodes.contains(excel.getSocialCreditCode())) {
|
||||
LambdaQueryWrapper<CcdiEnterpriseBaseInfo> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CcdiEnterpriseBaseInfo::getSocialCreditCode, excel.getSocialCreditCode());
|
||||
if (entityMapper.selectCount(wrapper) > 0) {
|
||||
throw new RuntimeException("该统一社会信用代码已存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package com.ruoyi.ccdi.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.ccdi.domain.CcdiBizIntermediary;
|
||||
import com.ruoyi.ccdi.domain.excel.CcdiIntermediaryPersonExcel;
|
||||
import com.ruoyi.ccdi.domain.vo.ImportResult;
|
||||
import com.ruoyi.ccdi.domain.vo.ImportStatusVO;
|
||||
import com.ruoyi.ccdi.domain.vo.IntermediaryPersonImportFailureVO;
|
||||
import com.ruoyi.ccdi.mapper.CcdiBizIntermediaryMapper;
|
||||
import com.ruoyi.ccdi.service.ICcdiIntermediaryPersonImportService;
|
||||
import com.ruoyi.common.utils.IdCardUtil;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
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.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 个人中介异步导入Service实现
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-06
|
||||
*/
|
||||
@Service
|
||||
@EnableAsync
|
||||
public class CcdiIntermediaryPersonImportServiceImpl implements ICcdiIntermediaryPersonImportService {
|
||||
|
||||
@Resource
|
||||
private CcdiBizIntermediaryMapper intermediaryMapper;
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Override
|
||||
@Async
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void importPersonAsync(List<CcdiIntermediaryPersonExcel> excelList,
|
||||
Boolean isUpdateSupport,
|
||||
String taskId,
|
||||
String userName) {
|
||||
List<CcdiBizIntermediary> newRecords = new ArrayList<>();
|
||||
List<CcdiBizIntermediary> updateRecords = new ArrayList<>();
|
||||
List<IntermediaryPersonImportFailureVO> failures = new ArrayList<>();
|
||||
|
||||
// 1. 批量查询已存在的证件号
|
||||
Set<String> existingPersonIds = getExistingPersonIds(excelList);
|
||||
|
||||
// 2. 分类数据
|
||||
for (int i = 0; i < excelList.size(); i++) {
|
||||
CcdiIntermediaryPersonExcel excel = excelList.get(i);
|
||||
|
||||
try {
|
||||
// 验证数据
|
||||
validatePersonData(excel, isUpdateSupport, existingPersonIds);
|
||||
|
||||
CcdiBizIntermediary intermediary = new CcdiBizIntermediary();
|
||||
BeanUtils.copyProperties(excel, intermediary);
|
||||
|
||||
// 设置数据来源和审计字段
|
||||
intermediary.setDataSource("IMPORT");
|
||||
intermediary.setCreatedBy(userName);
|
||||
|
||||
if (existingPersonIds.contains(excel.getPersonId())) {
|
||||
if (isUpdateSupport) {
|
||||
// 更新模式:设置更新人
|
||||
intermediary.setUpdatedBy(userName);
|
||||
updateRecords.add(intermediary);
|
||||
} else {
|
||||
throw new RuntimeException("该证件号码已存在");
|
||||
}
|
||||
} else {
|
||||
newRecords.add(intermediary);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
IntermediaryPersonImportFailureVO failure = new IntermediaryPersonImportFailureVO();
|
||||
BeanUtils.copyProperties(excel, failure);
|
||||
failure.setErrorMessage(e.getMessage());
|
||||
failures.add(failure);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 批量插入新数据
|
||||
if (!newRecords.isEmpty()) {
|
||||
saveBatch(newRecords, 500);
|
||||
}
|
||||
|
||||
// 4. 批量更新已有数据(先删除再插入)
|
||||
if (!updateRecords.isEmpty() && isUpdateSupport) {
|
||||
// 先批量删除已存在的记录
|
||||
List<String> personIds = updateRecords.stream()
|
||||
.map(CcdiBizIntermediary::getPersonId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
LambdaQueryWrapper<CcdiBizIntermediary> deleteWrapper = new LambdaQueryWrapper<>();
|
||||
deleteWrapper.in(CcdiBizIntermediary::getPersonId, personIds);
|
||||
intermediaryMapper.delete(deleteWrapper);
|
||||
|
||||
// 批量插入更新后的数据
|
||||
intermediaryMapper.insertBatch(updateRecords);
|
||||
}
|
||||
|
||||
// 5. 保存失败记录到Redis
|
||||
if (!failures.isEmpty()) {
|
||||
String failuresKey = "import:intermediary:" + taskId + ":failures";
|
||||
redisTemplate.opsForValue().set(failuresKey, failures, 7, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
// 6. 更新最终状态
|
||||
ImportResult result = new ImportResult();
|
||||
result.setTotalCount(excelList.size());
|
||||
result.setSuccessCount(newRecords.size() + updateRecords.size());
|
||||
result.setFailureCount(failures.size());
|
||||
|
||||
String finalStatus = result.getFailureCount() == 0 ? "SUCCESS" : "PARTIAL_SUCCESS";
|
||||
updateImportStatus(taskId, finalStatus, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportStatusVO getImportStatus(String taskId) {
|
||||
String key = "import:intermediary:" + taskId;
|
||||
Boolean hasKey = redisTemplate.hasKey(key);
|
||||
|
||||
if (Boolean.FALSE.equals(hasKey)) {
|
||||
throw new RuntimeException("任务不存在或已过期");
|
||||
}
|
||||
|
||||
Map<Object, Object> statusMap = redisTemplate.opsForHash().entries(key);
|
||||
|
||||
ImportStatusVO statusVO = new ImportStatusVO();
|
||||
statusVO.setTaskId((String) statusMap.get("taskId"));
|
||||
statusVO.setStatus((String) statusMap.get("status"));
|
||||
statusVO.setTotalCount((Integer) statusMap.get("totalCount"));
|
||||
statusVO.setSuccessCount((Integer) statusMap.get("successCount"));
|
||||
statusVO.setFailureCount((Integer) statusMap.get("failureCount"));
|
||||
statusVO.setProgress((Integer) statusMap.get("progress"));
|
||||
statusVO.setStartTime((Long) statusMap.get("startTime"));
|
||||
statusVO.setEndTime((Long) statusMap.get("endTime"));
|
||||
statusVO.setMessage((String) statusMap.get("message"));
|
||||
|
||||
return statusVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntermediaryPersonImportFailureVO> getImportFailures(String taskId) {
|
||||
String key = "import:intermediary:" + taskId + ":failures";
|
||||
Object failuresObj = redisTemplate.opsForValue().get(key);
|
||||
|
||||
if (failuresObj == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return JSON.parseArray(JSON.toJSONString(failuresObj), IntermediaryPersonImportFailureVO.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询已存在的证件号
|
||||
*/
|
||||
private Set<String> getExistingPersonIds(List<CcdiIntermediaryPersonExcel> excelList) {
|
||||
List<String> personIds = excelList.stream()
|
||||
.map(CcdiIntermediaryPersonExcel::getPersonId)
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (personIds.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.in(CcdiBizIntermediary::getPersonId, personIds);
|
||||
List<CcdiBizIntermediary> existingIntermediaries = intermediaryMapper.selectList(wrapper);
|
||||
|
||||
return existingIntermediaries.stream()
|
||||
.map(CcdiBizIntermediary::getPersonId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存
|
||||
*/
|
||||
private void saveBatch(List<CcdiBizIntermediary> list, int batchSize) {
|
||||
// 使用真正的批量插入,分批次执行以提高性能
|
||||
for (int i = 0; i < list.size(); i += batchSize) {
|
||||
int end = Math.min(i + batchSize, list.size());
|
||||
List<CcdiBizIntermediary> subList = list.subList(i, end);
|
||||
intermediaryMapper.insertBatch(subList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新导入状态
|
||||
*/
|
||||
private void updateImportStatus(String taskId, String status, ImportResult result) {
|
||||
String key = "import:intermediary:" + taskId;
|
||||
Map<String, Object> statusData = new HashMap<>();
|
||||
statusData.put("status", status);
|
||||
statusData.put("successCount", result.getSuccessCount());
|
||||
statusData.put("failureCount", result.getFailureCount());
|
||||
statusData.put("progress", 100);
|
||||
statusData.put("endTime", System.currentTimeMillis());
|
||||
|
||||
if ("SUCCESS".equals(status)) {
|
||||
statusData.put("message", "全部成功!共导入" + result.getTotalCount() + "条数据");
|
||||
} else {
|
||||
statusData.put("message", "成功" + result.getSuccessCount() + "条,失败" + result.getFailureCount() + "条");
|
||||
}
|
||||
|
||||
redisTemplate.opsForHash().putAll(key, statusData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证个人中介数据
|
||||
*
|
||||
* @param excel Excel数据
|
||||
* @param isUpdateSupport 是否支持更新
|
||||
* @param existingPersonIds 已存在的证件号集合
|
||||
*/
|
||||
private void validatePersonData(CcdiIntermediaryPersonExcel excel,
|
||||
Boolean isUpdateSupport,
|
||||
Set<String> existingPersonIds) {
|
||||
// 验证必填字段:姓名
|
||||
if (StringUtils.isEmpty(excel.getName())) {
|
||||
throw new RuntimeException("姓名不能为空");
|
||||
}
|
||||
|
||||
// 验证必填字段:证件号码
|
||||
if (StringUtils.isEmpty(excel.getPersonId())) {
|
||||
throw new RuntimeException("证件号码不能为空");
|
||||
}
|
||||
|
||||
// 验证证件号码格式
|
||||
String idCardError = IdCardUtil.getErrorMessage(excel.getPersonId());
|
||||
if (idCardError != null) {
|
||||
throw new RuntimeException("证件号码" + idCardError);
|
||||
}
|
||||
|
||||
// 如果证件号已存在但未启用更新支持,抛出异常
|
||||
if (existingPersonIds.contains(excel.getPersonId()) && !isUpdateSupport) {
|
||||
throw new RuntimeException("该证件号码已存在");
|
||||
}
|
||||
|
||||
// 如果证件号不存在,检查唯一性
|
||||
if (!existingPersonIds.contains(excel.getPersonId())) {
|
||||
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CcdiBizIntermediary::getPersonId, excel.getPersonId());
|
||||
if (intermediaryMapper.selectCount(wrapper) > 0) {
|
||||
throw new RuntimeException("该证件号码已存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user