完成中介库导入改造

This commit is contained in:
wkc
2026-04-20 15:17:31 +08:00
parent 60a7906eb3
commit 6385778e4c
31 changed files with 1566 additions and 373 deletions

View File

@@ -2,10 +2,10 @@ package com.ruoyi.info.collection.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.info.collection.domain.dto.*;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEntityExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryPersonExcel;
import com.ruoyi.info.collection.domain.vo.*;
import com.ruoyi.info.collection.service.ICcdiIntermediaryEntityImportService;
import com.ruoyi.info.collection.service.ICcdiIntermediaryEnterpriseRelationImportService;
import com.ruoyi.info.collection.service.ICcdiIntermediaryPersonImportService;
import com.ruoyi.info.collection.service.ICcdiIntermediaryService;
import com.ruoyi.info.collection.utils.EasyExcelUtil;
@@ -46,7 +46,7 @@ public class CcdiIntermediaryController extends BaseController {
private ICcdiIntermediaryPersonImportService personImportService;
@Resource
private ICcdiIntermediaryEntityImportService entityImportService;
private ICcdiIntermediaryEnterpriseRelationImportService enterpriseRelationImportService;
/**
* 查询中介列表
@@ -277,10 +277,10 @@ public class CcdiIntermediaryController extends BaseController {
/**
* 下载实体中介导入模板
*/
@Operation(summary = "下载实体中介导入模板")
@PostMapping("/importEntityTemplate")
public void importEntityTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiIntermediaryEntityExcel.class, "实体中介信息");
@Operation(summary = "下载中介实体关联关系导入模板")
@PostMapping("/importEnterpriseRelationTemplate")
public void importEnterpriseRelationTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiIntermediaryEnterpriseRelationExcel.class, "中介实体关联关系信息");
}
/**
@@ -313,20 +313,19 @@ public class CcdiIntermediaryController extends BaseController {
/**
* 导入实体中介数据(异步)
*/
@Operation(summary = "导入实体中介数据")
@Operation(summary = "导入中介实体关联关系数据")
@PreAuthorize("@ss.hasPermi('ccdi:intermediary:import')")
@Log(title = "实体中介", businessType = BusinessType.IMPORT)
@PostMapping("/importEntityData")
public AjaxResult importEntityData(MultipartFile file) throws Exception {
List<CcdiIntermediaryEntityExcel> list = EasyExcelUtil.importExcel(
file.getInputStream(), CcdiIntermediaryEntityExcel.class);
@Log(title = "中介实体关联关系", businessType = BusinessType.IMPORT)
@PostMapping("/importEnterpriseRelationData")
public AjaxResult importEnterpriseRelationData(MultipartFile file) throws Exception {
List<CcdiIntermediaryEnterpriseRelationExcel> list = EasyExcelUtil.importExcel(
file.getInputStream(), CcdiIntermediaryEnterpriseRelationExcel.class);
if (list == null || list.isEmpty()) {
return error("至少需要一条数据");
}
// 提交异步任务
String taskId = intermediaryService.importIntermediaryEntity(list);
String taskId = intermediaryService.importIntermediaryEnterpriseRelation(list);
// 立即返回,不等待后台任务完成
ImportResultVO result = new ImportResultVO();
@@ -383,12 +382,12 @@ public class CcdiIntermediaryController extends BaseController {
/**
* 查询实体中介导入状态
*/
@Operation(summary = "查询实体中介导入状态")
@Operation(summary = "查询中介实体关联关系导入状态")
@PreAuthorize("@ss.hasPermi('ccdi:intermediary:import')")
@GetMapping("/importEntityStatus/{taskId}")
public AjaxResult getEntityImportStatus(@PathVariable String taskId) {
@GetMapping("/importEnterpriseRelationStatus/{taskId}")
public AjaxResult getEnterpriseRelationImportStatus(@PathVariable String taskId) {
try {
ImportStatusVO status = entityImportService.getImportStatus(taskId);
ImportStatusVO status = enterpriseRelationImportService.getImportStatus(taskId);
return success(status);
} catch (Exception e) {
return error(e.getMessage());
@@ -396,18 +395,18 @@ public class CcdiIntermediaryController extends BaseController {
}
/**
* 查询实体中介导入失败记录
* 查询中介实体关联关系导入失败记录
*/
@Operation(summary = "查询实体中介导入失败记录")
@Operation(summary = "查询中介实体关联关系导入失败记录")
@PreAuthorize("@ss.hasPermi('ccdi:intermediary:import')")
@GetMapping("/importEntityFailures/{taskId}")
public TableDataInfo getEntityImportFailures(
@GetMapping("/importEnterpriseRelationFailures/{taskId}")
public TableDataInfo getEnterpriseRelationImportFailures(
@PathVariable String taskId,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
List<IntermediaryEntityImportFailureVO> failures =
entityImportService.getImportFailures(taskId);
List<IntermediaryEnterpriseRelationImportFailureVO> failures =
enterpriseRelationImportService.getImportFailures(taskId);
// 手动分页
int fromIndex = (pageNum - 1) * pageSize;
@@ -418,7 +417,7 @@ public class CcdiIntermediaryController extends BaseController {
return getDataTable(new ArrayList<>(), failures.size());
}
List<IntermediaryEntityImportFailureVO> pageData = failures.subList(fromIndex, toIndex);
List<IntermediaryEnterpriseRelationImportFailureVO> pageData = failures.subList(fromIndex, toIndex);
return getDataTable(pageData, failures.size());
}

View File

@@ -63,7 +63,7 @@ public class CcdiBizIntermediary implements Serializable {
/** 职位 */
private String position;
/** 关联人员ID */
/** 关联中介本人证件号码 */
private String relatedNumId;
/** 数据来源MANUAL:手动录入, SYSTEM:系统同步, IMPORT:批量导入, API:接口获取 */

View File

@@ -67,8 +67,8 @@ public class CcdiIntermediaryPersonAddDTO implements Serializable {
@Size(max = 100, message = "职位长度不能超过100个字符")
private String position;
@Schema(description = "关联人员ID")
@Size(max = 50, message = "关联人员ID长度不能超过50个字符")
@Schema(description = "关联中介本人证件号码")
@Size(max = 50, message = "关联中介本人证件号码长度不能超过50个字符")
private String relatedNumId;
@Schema(description = "关联关系")

View File

@@ -70,8 +70,8 @@ public class CcdiIntermediaryPersonEditDTO implements Serializable {
@Size(max = 100, message = "职位长度不能超过100个字符")
private String position;
@Schema(description = "关联人员ID")
@Size(max = 50, message = "关联人员ID长度不能超过50个字符")
@Schema(description = "关联中介本人证件号码")
@Size(max = 50, message = "关联中介本人证件号码长度不能超过50个字符")
private String relatedNumId;
@Schema(description = "关联关系")

View File

@@ -0,0 +1,38 @@
package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 中介实体关联关系导入对象
*/
@Data
public class CcdiIntermediaryEnterpriseRelationExcel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 中介本人证件号码 */
@ExcelProperty(value = "中介本人证件号码*", index = 0)
@ColumnWidth(24)
private String ownerPersonId;
/** 统一社会信用代码 */
@ExcelProperty(value = "统一社会信用代码*", index = 1)
@ColumnWidth(24)
private String socialCreditCode;
/** 关联人职务 */
@ExcelProperty(value = "关联人职务", index = 2)
@ColumnWidth(20)
private String relationPersonPost;
/** 备注 */
@ExcelProperty(value = "备注", index = 3)
@ColumnWidth(30)
private String remark;
}

View File

@@ -34,6 +34,7 @@ public class CcdiIntermediaryPersonExcel implements Serializable {
/** 人员子类型 */
@ExcelProperty(value = "人员子类型", index = 2)
@ColumnWidth(15)
@DictDropdown(dictType = "ccdi_person_sub_type")
private String personSubType;
/** 性别 */
@@ -83,19 +84,13 @@ public class CcdiIntermediaryPersonExcel implements Serializable {
@ColumnWidth(15)
private String position;
/** 关联人员ID */
@ExcelProperty(value = "关联人员ID", index = 12)
@ColumnWidth(15)
/** 关联中介本人证件号码 */
@ExcelProperty(value = "关联中介本人证件号码", index = 12)
@ColumnWidth(24)
private String relatedNumId;
/** 关系类型 */
@ExcelProperty(value = "关系类型", index = 13)
@ColumnWidth(15)
@DictDropdown(dictType = "ccdi_relation_type")
private String relationType;
/** 备注 */
@ExcelProperty(value = "备注", index = 14)
@ExcelProperty(value = "备注", index = 13)
@ColumnWidth(30)
private String remark;
}

View File

@@ -63,7 +63,7 @@ public class CcdiIntermediaryPersonDetailVO implements Serializable {
@Schema(description = "职位")
private String position;
@Schema(description = "关联人员ID")
@Schema(description = "关联中介本人证件号码")
private String relatedNumId;
@Schema(description = "关联关系")

View File

@@ -21,7 +21,7 @@ public class CcdiIntermediaryRelativeVO implements Serializable {
@Schema(description = "人员ID")
private String bizId;
@Schema(description = "所属中介ID")
@Schema(description = "关联中介本人证件号码")
private String relatedNumId;
@Schema(description = "姓名")

View File

@@ -0,0 +1,33 @@
package com.ruoyi.info.collection.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 中介实体关联关系导入失败记录
*/
@Data
@Schema(description = "中介实体关联关系导入失败记录")
public class IntermediaryEnterpriseRelationImportFailureVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "中介本人证件号码")
private String ownerPersonId;
@Schema(description = "统一社会信用代码")
private String socialCreditCode;
@Schema(description = "关联人职务")
private String relationPersonPost;
@Schema(description = "备注")
private String remark;
@Schema(description = "错误信息")
private String errorMessage;
}

View File

@@ -22,21 +22,45 @@ public class IntermediaryPersonImportFailureVO implements Serializable {
@Schema(description = "姓名")
private String name;
@Schema(description = "证件号码")
private String personId;
@Schema(description = "人员类型")
private String personType;
@Schema(description = "人员子类型")
private String personSubType;
@Schema(description = "性别")
private String gender;
@Schema(description = "证件类型")
private String idType;
@Schema(description = "证件号码")
private String personId;
@Schema(description = "手机号码")
private String mobile;
@Schema(description = "微信号")
private String wechatNo;
@Schema(description = "联系地址")
private String contactAddress;
@Schema(description = "所在公司")
private String company;
@Schema(description = "企业统一信用码")
private String socialCreditCode;
@Schema(description = "职位")
private String position;
@Schema(description = "关联中介本人证件号码")
private String relatedNumId;
@Schema(description = "备注")
private String remark;
@Schema(description = "错误信息")
private String errorMessage;
}

View File

@@ -14,10 +14,14 @@ import java.util.List;
@Mapper
public interface CcdiIntermediaryEnterpriseRelationMapper extends BaseMapper<CcdiIntermediaryEnterpriseRelation> {
int insertBatch(@Param("list") List<CcdiIntermediaryEnterpriseRelation> list);
List<CcdiIntermediaryEnterpriseRelationVO> selectByIntermediaryBizId(@Param("bizId") String bizId);
CcdiIntermediaryEnterpriseRelationVO selectDetailById(@Param("id") Long id);
boolean existsByIntermediaryBizIdAndSocialCreditCode(@Param("bizId") String bizId,
@Param("socialCreditCode") String socialCreditCode);
List<String> batchExistsByCombinations(@Param("combinations") List<String> combinations);
}

View File

@@ -0,0 +1,38 @@
package com.ruoyi.info.collection.service;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.domain.vo.IntermediaryEnterpriseRelationImportFailureVO;
import java.util.List;
/**
* 中介实体关联关系异步导入服务接口
*/
public interface ICcdiIntermediaryEnterpriseRelationImportService {
/**
* 异步导入中介实体关联关系
*
* @param excelList Excel数据
* @param taskId 任务ID
* @param userName 当前用户名
*/
void importAsync(List<CcdiIntermediaryEnterpriseRelationExcel> excelList, String taskId, String userName);
/**
* 查询导入状态
*
* @param taskId 任务ID
* @return 导入状态
*/
ImportStatusVO getImportStatus(String taskId);
/**
* 查询导入失败记录
*
* @param taskId 任务ID
* @return 失败记录
*/
List<IntermediaryEnterpriseRelationImportFailureVO> getImportFailures(String taskId);
}

View File

@@ -7,7 +7,7 @@ import com.ruoyi.info.collection.domain.vo.IntermediaryPersonImportFailureVO;
import java.util.List;
/**
* 个人中介异步导入Service接口
* 中介信息异步导入Service接口
*
* @author ruoyi
* @date 2026-02-06
@@ -15,7 +15,7 @@ import java.util.List;
public interface ICcdiIntermediaryPersonImportService {
/**
* 异步导入个人中介数据
* 异步导入中介信息
*
* @param excelList Excel数据列表
* @param taskId 任务ID

View File

@@ -2,6 +2,7 @@ package com.ruoyi.info.collection.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.info.collection.domain.dto.*;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEntityExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryPersonExcel;
import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryEnterpriseRelationVO;
@@ -168,7 +169,7 @@ public interface ICcdiIntermediaryService {
int deleteIntermediaryByIds(String[] ids);
/**
* 校验人员ID唯一性
* 校验中介本人证件号码唯一性
*
* @param personId 人员ID
* @param bizId 排除的人员ID
@@ -193,6 +194,14 @@ public interface ICcdiIntermediaryService {
*/
String importIntermediaryPerson(List<CcdiIntermediaryPersonExcel> list);
/**
* 导入中介实体关联关系
*
* @param list Excel实体列表
* @return 任务ID
*/
String importIntermediaryEnterpriseRelation(List<CcdiIntermediaryEnterpriseRelationExcel> list);
/**
* 导入实体中介数据
*

View File

@@ -0,0 +1,266 @@
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.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.utils.ImportLogUtils;
import com.ruoyi.common.utils.IdCardUtil;
import com.ruoyi.common.utils.StringUtils;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 中介实体关联关系异步导入实现
*/
@Service
@EnableAsync
public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcdiIntermediaryEnterpriseRelationImportService {
private static final Logger log = LoggerFactory.getLogger(CcdiIntermediaryEnterpriseRelationImportServiceImpl.class);
private static final String STATUS_KEY_PREFIX = "import:intermediary-enterprise-relation:";
@Resource
private CcdiIntermediaryEnterpriseRelationMapper relationMapper;
@Resource
private CcdiBizIntermediaryMapper intermediaryMapper;
@Resource
private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Override
@Async
@Transactional(rollbackFor = Exception.class)
public void importAsync(List<CcdiIntermediaryEnterpriseRelationExcel> excelList, String taskId, String userName) {
long startTime = System.currentTimeMillis();
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<>();
List<IntermediaryEnterpriseRelationImportFailureVO> failures = new ArrayList<>();
Set<String> processedCombinations = new HashSet<>();
for (int i = 0; i < excelList.size(); i++) {
CcdiIntermediaryEnterpriseRelationExcel excel = excelList.get(i);
try {
validateExcel(excel);
String ownerBizId = ownerBizIdByPersonId.get(excel.getOwnerPersonId());
if (StringUtils.isEmpty(ownerBizId)) {
throw new RuntimeException("中介本人不存在,请先导入或维护中介本人信息");
}
if (!existingEnterpriseCodes.contains(excel.getSocialCreditCode())) {
throw new RuntimeException("统一社会信用代码不存在于系统机构表");
}
String combination = ownerBizId + "|" + excel.getSocialCreditCode();
if (existingCombinations.contains(combination)) {
throw new RuntimeException("中介实体关联关系已存在,请勿重复导入");
}
if (!processedCombinations.add(combination)) {
throw new RuntimeException("同一中介本人与统一社会信用代码组合在导入文件中重复");
}
CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation();
BeanUtils.copyProperties(excel, relation);
relation.setIntermediaryBizId(ownerBizId);
relation.setCreatedBy(userName);
relation.setUpdatedBy(userName);
successRecords.add(relation);
} catch (Exception e) {
failures.add(createFailureVO(excel, e.getMessage()));
ImportLogUtils.logValidationError(log, taskId, i + 1, e.getMessage(),
String.format("中介本人证件号码=%s, 统一社会信用代码=%s", excel.getOwnerPersonId(), excel.getSocialCreditCode()));
}
}
if (!successRecords.isEmpty()) {
saveBatch(successRecords, 500);
}
if (!failures.isEmpty()) {
redisTemplate.opsForValue().set(failureKey(taskId), failures, 7, TimeUnit.DAYS);
}
ImportResult result = new ImportResult();
result.setTotalCount(excelList.size());
result.setSuccessCount(successRecords.size());
result.setFailureCount(failures.size());
updateImportStatus(taskId, result);
long duration = System.currentTimeMillis() - startTime;
ImportLogUtils.logImportComplete(log, taskId, "中介实体关联关系",
excelList.size(), result.getSuccessCount(), result.getFailureCount(), duration);
}
@Override
public ImportStatusVO getImportStatus(String taskId) {
String key = statusKey(taskId);
if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
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<IntermediaryEnterpriseRelationImportFailureVO> getImportFailures(String taskId) {
Object failuresObj = redisTemplate.opsForValue().get(failureKey(taskId));
if (failuresObj == null) {
return Collections.emptyList();
}
return JSON.parseArray(JSON.toJSONString(failuresObj), IntermediaryEnterpriseRelationImportFailureVO.class);
}
private Map<String, String> getOwnerBizIdByPersonId(List<CcdiIntermediaryEnterpriseRelationExcel> excelList) {
List<String> ownerPersonIds = excelList.stream()
.map(CcdiIntermediaryEnterpriseRelationExcel::getOwnerPersonId)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
if (ownerPersonIds.isEmpty()) {
return Collections.emptyMap();
}
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CcdiBizIntermediary::getPersonSubType, "本人")
.in(CcdiBizIntermediary::getPersonId, ownerPersonIds);
return intermediaryMapper.selectList(wrapper).stream()
.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()
.map(excel -> {
String ownerBizId = ownerBizIdByPersonId.get(excel.getOwnerPersonId());
if (StringUtils.isEmpty(ownerBizId) || StringUtils.isEmpty(excel.getSocialCreditCode())) {
return null;
}
return ownerBizId + "|" + excel.getSocialCreditCode();
})
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
if (combinations.isEmpty()) {
return Collections.emptySet();
}
return new HashSet<>(relationMapper.batchExistsByCombinations(combinations));
}
private void validateExcel(CcdiIntermediaryEnterpriseRelationExcel excel) {
if (StringUtils.isEmpty(excel.getOwnerPersonId())) {
throw new RuntimeException("中介本人证件号码不能为空");
}
if (StringUtils.isEmpty(excel.getSocialCreditCode())) {
throw new RuntimeException("统一社会信用代码不能为空");
}
String ownerPersonIdError = IdCardUtil.getErrorMessage(excel.getOwnerPersonId());
if (ownerPersonIdError != null) {
throw new RuntimeException("中介本人证件号码" + ownerPersonIdError);
}
if (StringUtils.isNotEmpty(excel.getRelationPersonPost()) && excel.getRelationPersonPost().length() > 100) {
throw new RuntimeException("关联人职务长度不能超过100个字符");
}
if (StringUtils.isNotEmpty(excel.getRemark()) && excel.getRemark().length() > 500) {
throw new RuntimeException("备注长度不能超过500个字符");
}
}
private IntermediaryEnterpriseRelationImportFailureVO createFailureVO(CcdiIntermediaryEnterpriseRelationExcel excel,
String errorMessage) {
IntermediaryEnterpriseRelationImportFailureVO failure = new IntermediaryEnterpriseRelationImportFailureVO();
BeanUtils.copyProperties(excel, failure);
failure.setErrorMessage(errorMessage);
return failure;
}
private void saveBatch(List<CcdiIntermediaryEnterpriseRelation> list, int batchSize) {
for (int i = 0; i < list.size(); i += batchSize) {
int end = Math.min(i + batchSize, list.size());
relationMapper.insertBatch(list.subList(i, end));
}
}
private void updateImportStatus(String taskId, ImportResult result) {
Map<String, Object> statusData = new HashMap<>();
statusData.put("status", result.getFailureCount() == 0 ? "SUCCESS" : "PARTIAL_SUCCESS");
statusData.put("successCount", result.getSuccessCount());
statusData.put("failureCount", result.getFailureCount());
statusData.put("progress", 100);
statusData.put("endTime", System.currentTimeMillis());
statusData.put("message", result.getFailureCount() == 0
? "全部成功!共导入" + result.getTotalCount() + "条数据"
: "成功" + result.getSuccessCount() + "条,失败" + result.getFailureCount() + "");
redisTemplate.opsForHash().putAll(statusKey(taskId), statusData);
}
private String statusKey(String taskId) {
return STATUS_KEY_PREFIX + taskId;
}
private String failureKey(String taskId) {
return statusKey(taskId) + ":failures";
}
}

View File

@@ -22,15 +22,18 @@ import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 个人中介异步导入Service实现
*
* @author ruoyi
* @date 2026-02-06
* 中介信息异步导入实现
*/
@Service
@EnableAsync
@@ -38,6 +41,8 @@ public class CcdiIntermediaryPersonImportServiceImpl implements ICcdiIntermediar
private static final Logger log = LoggerFactory.getLogger(CcdiIntermediaryPersonImportServiceImpl.class);
private static final String STATUS_KEY_PREFIX = "import:intermediary:";
@Resource
private CcdiBizIntermediaryMapper intermediaryMapper;
@@ -47,110 +52,104 @@ public class CcdiIntermediaryPersonImportServiceImpl implements ICcdiIntermediar
@Override
@Async
@Transactional(rollbackFor = Exception.class)
public void importPersonAsync(List<CcdiIntermediaryPersonExcel> excelList,
String taskId,
String userName) {
public void importPersonAsync(List<CcdiIntermediaryPersonExcel> excelList, String taskId, String userName) {
long startTime = System.currentTimeMillis();
ImportLogUtils.logImportStart(log, taskId, "中介信息", excelList.size(), userName);
// 记录导入开始
ImportLogUtils.logImportStart(log, taskId, "个人中介", excelList.size(), userName);
List<CcdiBizIntermediary> newRecords = new ArrayList<>();
List<CcdiIntermediaryPersonExcel> ownerRows = new ArrayList<>();
List<CcdiIntermediaryPersonExcel> relativeRows = new ArrayList<>();
List<IntermediaryPersonImportFailureVO> failures = new ArrayList<>();
// 批量查询已存在的证件号
ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的证件号", excelList.size());
Set<String> existingPersonIds = getExistingPersonIds(excelList);
ImportLogUtils.logBatchQueryComplete(log, taskId, "证件号", existingPersonIds.size());
// 用于检测Excel内部的重复ID
Set<String> excelProcessedIds = new HashSet<>();
// 分类数据
for (int i = 0; i < excelList.size(); i++) {
CcdiIntermediaryPersonExcel excel = excelList.get(i);
try {
// 验证数据
validatePersonData(excel, existingPersonIds);
CcdiBizIntermediary intermediary = new CcdiBizIntermediary();
BeanUtils.copyProperties(excel, intermediary);
// 设置数据来源和审计字段
intermediary.setDataSource("IMPORT");
intermediary.setCreatedBy(userName);
intermediary.setUpdatedBy(userName);
if (existingPersonIds.contains(excel.getPersonId())) {
// 证件号码在数据库中已存在,直接报错
throw new RuntimeException(String.format("证件号码[%s]已存在,请勿重复导入", excel.getPersonId()));
} else if (excelProcessedIds.contains(excel.getPersonId())) {
// 证件号码在Excel文件内部重复
throw new RuntimeException(String.format("证件号码[%s]在导入文件中重复,已跳过此条记录", excel.getPersonId()));
validateCommonRow(excel);
if (isOwnerRow(excel)) {
validateOwnerRow(excel);
ownerRows.add(excel);
} else {
newRecords.add(intermediary);
excelProcessedIds.add(excel.getPersonId()); // 标记为已处理
validateRelativeRow(excel);
relativeRows.add(excel);
}
// 记录进度
ImportLogUtils.logProgress(log, taskId, i + 1, excelList.size(),
newRecords.size(), failures.size());
} catch (Exception e) {
failures.add(createFailureVO(excel, e.getMessage()));
// 记录验证失败日志
String keyData = String.format("姓名=%s, 证件号码=%s",
excel.getName(), excel.getPersonId());
ImportLogUtils.logValidationError(log, taskId, i + 1, e.getMessage(), keyData);
ImportLogUtils.logValidationError(log, taskId, i + 1, e.getMessage(),
String.format("姓名=%s, 证件号码=%s", excel.getName(), excel.getPersonId()));
}
}
// 批量插入新数据
if (!newRecords.isEmpty()) {
ImportLogUtils.logBatchOperationStart(log, taskId, "插入",
(newRecords.size() + 499) / 500, 500);
saveBatch(newRecords, 500);
}
Set<String> existingOwnerPersonIds = getExistingOwnerPersonIds(ownerRows);
Set<String> existingOwnerRefs = getExistingOwnerRefs(relativeRows);
Set<String> existingRelativeCombinations = getExistingRelativeCombinations(relativeRows);
// 保存失败记录到Redis
if (!failures.isEmpty()) {
List<CcdiBizIntermediary> successRecords = new ArrayList<>();
Set<String> importedOwnerPersonIds = new HashSet<>();
for (CcdiIntermediaryPersonExcel ownerExcel : ownerRows) {
try {
String failuresKey = "import:intermediary:" + taskId + ":failures";
redisTemplate.opsForValue().set(failuresKey, failures, 7, TimeUnit.DAYS);
ImportLogUtils.logRedisOperation(log, taskId, "保存失败记录", failures.size());
String ownerPersonId = ownerExcel.getPersonId();
if (existingOwnerPersonIds.contains(ownerPersonId)) {
throw new RuntimeException(String.format("中介本人证件号码[%s]已存在,请勿重复导入", ownerPersonId));
}
if (!importedOwnerPersonIds.add(ownerPersonId)) {
throw new RuntimeException(String.format("中介本人证件号码[%s]在导入文件中重复", ownerPersonId));
}
successRecords.add(buildRecord(ownerExcel, userName, null));
} catch (Exception e) {
ImportLogUtils.logRedisError(log, taskId, "保存失败记录", e);
failures.add(createFailureVO(ownerExcel, e.getMessage()));
}
}
Set<String> validOwnerRefs = new HashSet<>(existingOwnerRefs);
validOwnerRefs.addAll(importedOwnerPersonIds);
Set<String> processedRelativeCombinations = new HashSet<>();
for (CcdiIntermediaryPersonExcel relativeExcel : relativeRows) {
try {
String ownerPersonId = relativeExcel.getRelatedNumId();
String combination = ownerPersonId + "|" + relativeExcel.getPersonId();
if (!validOwnerRefs.contains(ownerPersonId)) {
throw new RuntimeException(String.format("关联中介本人证件号码[%s]不存在", ownerPersonId));
}
if (existingRelativeCombinations.contains(combination)) {
throw new RuntimeException(String.format("同一中介本人名下证件号码[%s]的亲属已存在,请勿重复导入", relativeExcel.getPersonId()));
}
if (!processedRelativeCombinations.add(combination)) {
throw new RuntimeException(String.format("同一中介本人名下证件号码[%s]的亲属在导入文件中重复", relativeExcel.getPersonId()));
}
successRecords.add(buildRecord(relativeExcel, userName, ownerPersonId));
} catch (Exception e) {
failures.add(createFailureVO(relativeExcel, e.getMessage()));
}
}
if (!successRecords.isEmpty()) {
saveBatch(successRecords, 500);
}
if (!failures.isEmpty()) {
redisTemplate.opsForValue().set(failureKey(taskId), failures, 7, TimeUnit.DAYS);
}
ImportResult result = new ImportResult();
result.setTotalCount(excelList.size());
result.setSuccessCount(newRecords.size());
result.setSuccessCount(successRecords.size());
result.setFailureCount(failures.size());
updateImportStatus(taskId, result);
// 更新最终状态
String finalStatus = result.getFailureCount() == 0 ? "SUCCESS" : "PARTIAL_SUCCESS";
updateImportStatus(taskId, finalStatus, result);
// 记录导入完成
long duration = System.currentTimeMillis() - startTime;
ImportLogUtils.logImportComplete(log, taskId, "个人中介",
excelList.size(), result.getSuccessCount(), result.getFailureCount(), duration);
ImportLogUtils.logImportComplete(log, taskId, "中介信息",
excelList.size(), result.getSuccessCount(), result.getFailureCount(), duration);
}
@Override
public ImportStatusVO getImportStatus(String taskId) {
String key = "import:intermediary:" + taskId;
Boolean hasKey = redisTemplate.hasKey(key);
if (Boolean.FALSE.equals(hasKey)) {
String key = statusKey(taskId);
if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
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"));
@@ -161,83 +160,120 @@ public class CcdiIntermediaryPersonImportServiceImpl implements ICcdiIntermediar
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);
Object failuresObj = redisTemplate.opsForValue().get(failureKey(taskId));
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());
private boolean isOwnerRow(CcdiIntermediaryPersonExcel excel) {
return "本人".equals(excel.getPersonSubType());
}
if (personIds.isEmpty()) {
private void validateCommonRow(CcdiIntermediaryPersonExcel excel) {
if (StringUtils.isEmpty(excel.getName())) {
throw new RuntimeException("姓名不能为空");
}
if (StringUtils.isEmpty(excel.getPersonSubType())) {
throw new RuntimeException("人员子类型不能为空");
}
if (StringUtils.isEmpty(excel.getPersonId())) {
throw new RuntimeException("证件号码不能为空");
}
String idCardError = IdCardUtil.getErrorMessage(excel.getPersonId());
if (idCardError != null) {
throw new RuntimeException("证件号码" + idCardError);
}
}
private void validateOwnerRow(CcdiIntermediaryPersonExcel excel) {
if (StringUtils.isNotEmpty(excel.getRelatedNumId())) {
throw new RuntimeException("本人行关联中介本人证件号码必须为空");
}
}
private void validateRelativeRow(CcdiIntermediaryPersonExcel excel) {
if (StringUtils.isEmpty(excel.getRelatedNumId())) {
throw new RuntimeException("亲属行必须填写关联中介本人证件号码");
}
}
private Set<String> getExistingOwnerPersonIds(List<CcdiIntermediaryPersonExcel> ownerRows) {
List<String> ownerPersonIds = ownerRows.stream()
.map(CcdiIntermediaryPersonExcel::getPersonId)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
if (ownerPersonIds.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());
wrapper.eq(CcdiBizIntermediary::getPersonSubType, "本人")
.in(CcdiBizIntermediary::getPersonId, ownerPersonIds);
return intermediaryMapper.selectList(wrapper).stream()
.map(CcdiBizIntermediary::getPersonId)
.collect(Collectors.toSet());
}
/**
* 批量保存(使用ON DUPLICATE KEY UPDATE)
*/
private int saveBatchWithUpsert(List<CcdiBizIntermediary> list, int batchSize) {
int totalCount = 0;
for (int i = 0; i < list.size(); i += batchSize) {
int end = Math.min(i + batchSize, list.size());
List<CcdiBizIntermediary> subList = list.subList(i, end);
int count = intermediaryMapper.importPersonBatch(subList);
totalCount += count;
}
return totalCount;
}
/**
* 从数据库获取已存在的证件号
*/
private Set<String> getExistingPersonIdsFromDb(List<CcdiBizIntermediary> records) {
List<String> personIds = records.stream()
.map(CcdiBizIntermediary::getPersonId)
.filter(StringUtils::isNotEmpty)
.collect(Collectors.toList());
if (personIds.isEmpty()) {
private Set<String> getExistingOwnerRefs(List<CcdiIntermediaryPersonExcel> relativeRows) {
List<String> ownerRefs = relativeRows.stream()
.map(CcdiIntermediaryPersonExcel::getRelatedNumId)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
if (ownerRefs.isEmpty()) {
return Collections.emptySet();
}
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CcdiBizIntermediary::getPersonId, personIds);
List<CcdiBizIntermediary> existing = intermediaryMapper.selectList(wrapper);
return existing.stream()
.map(CcdiBizIntermediary::getPersonId)
.collect(Collectors.toSet());
wrapper.eq(CcdiBizIntermediary::getPersonSubType, "本人")
.in(CcdiBizIntermediary::getPersonId, ownerRefs);
return intermediaryMapper.selectList(wrapper).stream()
.map(CcdiBizIntermediary::getPersonId)
.collect(Collectors.toSet());
}
private Set<String> getExistingRelativeCombinations(List<CcdiIntermediaryPersonExcel> relativeRows) {
List<String> ownerRefs = relativeRows.stream()
.map(CcdiIntermediaryPersonExcel::getRelatedNumId)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
List<String> relativePersonIds = relativeRows.stream()
.map(CcdiIntermediaryPersonExcel::getPersonId)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
if (ownerRefs.isEmpty() || relativePersonIds.isEmpty()) {
return Collections.emptySet();
}
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
wrapper.ne(CcdiBizIntermediary::getPersonSubType, "本人")
.in(CcdiBizIntermediary::getRelatedNumId, ownerRefs)
.in(CcdiBizIntermediary::getPersonId, relativePersonIds);
return intermediaryMapper.selectList(wrapper).stream()
.map(item -> item.getRelatedNumId() + "|" + item.getPersonId())
.collect(Collectors.toSet());
}
private CcdiBizIntermediary buildRecord(CcdiIntermediaryPersonExcel excel, String userName, String ownerPersonId) {
CcdiBizIntermediary intermediary = new CcdiBizIntermediary();
BeanUtils.copyProperties(excel, intermediary);
intermediary.setRelatedNumId(ownerPersonId);
intermediary.setDataSource("IMPORT");
intermediary.setCreatedBy(userName);
intermediary.setUpdatedBy(userName);
return intermediary;
}
/**
* 创建失败记录VO
*/
private IntermediaryPersonImportFailureVO createFailureVO(CcdiIntermediaryPersonExcel excel, String errorMsg) {
IntermediaryPersonImportFailureVO failure = new IntermediaryPersonImportFailureVO();
BeanUtils.copyProperties(excel, failure);
@@ -245,73 +281,31 @@ public class CcdiIntermediaryPersonImportServiceImpl implements ICcdiIntermediar
return failure;
}
/**
* 创建失败记录VO(重载方法)
*/
private IntermediaryPersonImportFailureVO createFailureVO(CcdiBizIntermediary record, String errorMsg) {
CcdiIntermediaryPersonExcel excel = new CcdiIntermediaryPersonExcel();
BeanUtils.copyProperties(record, excel);
return createFailureVO(excel, errorMsg);
}
/**
* 批量保存
*/
private int saveBatch(List<CcdiBizIntermediary> list, int batchSize) {
// 使用真正的批量插入,分批次执行以提高性能
int totalCount = 0;
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);
int count = intermediaryMapper.insertBatch(subList);
totalCount += count;
intermediaryMapper.insertBatch(list.subList(i, end));
}
return totalCount;
}
/**
* 更新导入状态
*/
private void updateImportStatus(String taskId, String status, ImportResult result) {
String key = "import:intermediary:" + taskId;
private void updateImportStatus(String taskId, ImportResult result) {
Map<String, Object> statusData = new HashMap<>();
statusData.put("status", status);
statusData.put("status", result.getFailureCount() == 0 ? "SUCCESS" : "PARTIAL_SUCCESS");
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);
statusData.put("message", result.getFailureCount() == 0
? "全部成功!共导入" + result.getTotalCount() + "条数据"
: "成功" + result.getSuccessCount() + "条,失败" + result.getFailureCount() + "");
redisTemplate.opsForHash().putAll(statusKey(taskId), statusData);
}
/**
* 验证个人中介数据
*
* @param excel Excel数据
* @param existingPersonIds 已存在的证件号集合
*/
private void validatePersonData(CcdiIntermediaryPersonExcel excel,
Set<String> existingPersonIds) {
// 验证必填字段:姓名
if (StringUtils.isEmpty(excel.getName())) {
throw new RuntimeException("姓名不能为空");
}
private String statusKey(String taskId) {
return STATUS_KEY_PREFIX + taskId;
}
// 验证必填字段:证件号码
if (StringUtils.isEmpty(excel.getPersonId())) {
throw new RuntimeException("证件号码不能为空");
}
// 验证证件号码格式
String idCardError = IdCardUtil.getErrorMessage(excel.getPersonId());
if (idCardError != null) {
throw new RuntimeException("证件号码" + idCardError);
}
private String failureKey(String taskId) {
return statusKey(taskId) + ":failures";
}
}

View File

@@ -6,6 +6,7 @@ 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.dto.*;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEntityExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryPersonExcel;
import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryEnterpriseRelationVO;
@@ -17,6 +18,7 @@ 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.mapper.CcdiIntermediaryMapper;
import com.ruoyi.info.collection.service.ICcdiIntermediaryEnterpriseRelationImportService;
import com.ruoyi.info.collection.service.ICcdiIntermediaryEntityImportService;
import com.ruoyi.info.collection.service.ICcdiIntermediaryPersonImportService;
import com.ruoyi.info.collection.service.ICcdiIntermediaryService;
@@ -61,6 +63,9 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
@Resource
private ICcdiIntermediaryEntityImportService entityImportService;
@Resource
private ICcdiIntermediaryEnterpriseRelationImportService enterpriseRelationImportService;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@@ -101,8 +106,9 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
@Override
public List<CcdiIntermediaryRelativeVO> selectIntermediaryRelativeList(String bizId) {
CcdiBizIntermediary owner = requireIntermediaryPerson(bizId);
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CcdiBizIntermediary::getRelatedNumId, bizId)
wrapper.eq(CcdiBizIntermediary::getRelatedNumId, owner.getPersonId())
.ne(CcdiBizIntermediary::getPersonSubType, "本人")
.orderByDesc(CcdiBizIntermediary::getCreateTime);
return bizIntermediaryMapper.selectList(wrapper).stream().map(this::buildRelativeVo).toList();
@@ -187,8 +193,9 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
BeanUtils.copyProperties(editDTO, person);
person.setPersonSubType("本人");
person.setRelatedNumId(null);
return bizIntermediaryMapper.updateById(person);
int updated = bizIntermediaryMapper.updateById(person);
syncRelativeOwnerPersonId(existing.getPersonId(), editDTO.getPersonId());
return updated;
}
@Override
@@ -196,13 +203,13 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
public int insertIntermediaryRelative(String bizId, CcdiIntermediaryRelativeAddDTO addDTO) {
CcdiBizIntermediary owner = requireIntermediaryPerson(bizId);
validateRelativePersonSubType(addDTO.getPersonSubType());
if (!checkPersonIdUnique(addDTO.getPersonId(), null)) {
throw new RuntimeException("证件号已存在");
if (!checkRelativePersonUnique(owner.getPersonId(), addDTO.getPersonId(), null)) {
throw new RuntimeException("中介本人下已存在相同证件号亲属");
}
CcdiBizIntermediary relative = new CcdiBizIntermediary();
BeanUtils.copyProperties(addDTO, relative);
relative.setRelatedNumId(owner.getBizId());
relative.setRelatedNumId(owner.getPersonId());
relative.setDataSource("MANUAL");
return bizIntermediaryMapper.insert(relative);
}
@@ -216,8 +223,8 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
}
validateRelativePersonSubType(editDTO.getPersonSubType());
if (StringUtils.isNotEmpty(editDTO.getPersonId())
&& !checkPersonIdUnique(editDTO.getPersonId(), editDTO.getBizId())) {
throw new RuntimeException("证件号已存在");
&& !checkRelativePersonUnique(existing.getRelatedNumId(), editDTO.getPersonId(), editDTO.getBizId())) {
throw new RuntimeException("中介本人下已存在相同证件号亲属");
}
CcdiBizIntermediary relative = new CcdiBizIntermediary();
@@ -334,7 +341,8 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
if (intermediary != null) {
if (isIntermediaryPerson(intermediary)) {
bizIntermediaryMapper.delete(new LambdaQueryWrapper<CcdiBizIntermediary>()
.eq(CcdiBizIntermediary::getRelatedNumId, id));
.eq(CcdiBizIntermediary::getRelatedNumId, intermediary.getPersonId())
.ne(CcdiBizIntermediary::getPersonSubType, "本人"));
enterpriseRelationMapper.delete(new LambdaQueryWrapper<CcdiIntermediaryEnterpriseRelation>()
.eq(CcdiIntermediaryEnterpriseRelation::getIntermediaryBizId, id));
}
@@ -359,7 +367,8 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
@Override
public boolean checkPersonIdUnique(String personId, String bizId) {
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CcdiBizIntermediary::getPersonId, personId);
wrapper.eq(CcdiBizIntermediary::getPersonId, personId)
.eq(CcdiBizIntermediary::getPersonSubType, "本人");
if (StringUtils.isNotEmpty(bizId)) {
wrapper.ne(CcdiBizIntermediary::getBizId, bizId);
}
@@ -419,6 +428,31 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
return taskId;
}
@Override
@Transactional
public String importIntermediaryEnterpriseRelation(List<CcdiIntermediaryEnterpriseRelationExcel> list) {
String taskId = UUID.randomUUID().toString();
long startTime = System.currentTimeMillis();
String statusKey = "import:intermediary-enterprise-relation:" + taskId;
Map<String, Object> statusData = new HashMap<>();
statusData.put("taskId", taskId);
statusData.put("status", "PROCESSING");
statusData.put("totalCount", list.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);
String userName = SecurityUtils.getUsername();
enterpriseRelationImportService.importAsync(list, taskId, userName);
return taskId;
}
/**
* 导入实体中介数据(异步)
*
@@ -473,6 +507,17 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
}
}
private boolean checkRelativePersonUnique(String ownerPersonId, String personId, String excludeBizId) {
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CcdiBizIntermediary::getRelatedNumId, ownerPersonId)
.eq(CcdiBizIntermediary::getPersonId, personId)
.ne(CcdiBizIntermediary::getPersonSubType, "本人");
if (StringUtils.isNotEmpty(excludeBizId)) {
wrapper.ne(CcdiBizIntermediary::getBizId, excludeBizId);
}
return bizIntermediaryMapper.selectCount(wrapper) == 0;
}
private void validateEnterpriseRelation(String bizId, String socialCreditCode, Long excludeId) {
requireIntermediaryPerson(bizId);
if (enterpriseBaseInfoMapper.selectById(socialCreditCode) == null) {
@@ -490,6 +535,20 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
}
}
private void syncRelativeOwnerPersonId(String oldOwnerPersonId, String newOwnerPersonId) {
if (StringUtils.isEmpty(oldOwnerPersonId)
|| StringUtils.isEmpty(newOwnerPersonId)
|| oldOwnerPersonId.equals(newOwnerPersonId)) {
return;
}
CcdiBizIntermediary relative = new CcdiBizIntermediary();
relative.setRelatedNumId(newOwnerPersonId);
bizIntermediaryMapper.update(relative, new LambdaQueryWrapper<CcdiBizIntermediary>()
.eq(CcdiBizIntermediary::getRelatedNumId, oldOwnerPersonId)
.ne(CcdiBizIntermediary::getPersonSubType, "本人"));
}
private CcdiIntermediaryRelativeVO buildRelativeVo(CcdiBizIntermediary relative) {
CcdiIntermediaryRelativeVO vo = new CcdiIntermediaryRelativeVO();
BeanUtils.copyProperties(relative, vo);

View File

@@ -4,6 +4,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper">
<insert id="insertBatch" parameterType="java.util.List">
INSERT INTO ccdi_intermediary_enterprise_relation (
intermediary_biz_id, social_credit_code, relation_person_post, remark,
created_by, updated_by, create_time, update_time
) VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.intermediaryBizId}, #{item.socialCreditCode}, #{item.relationPersonPost}, #{item.remark},
#{item.createdBy}, #{item.updatedBy}, NOW(), NOW()
)
</foreach>
</insert>
<resultMap id="CcdiIntermediaryEnterpriseRelationVOResult"
type="com.ruoyi.info.collection.domain.vo.CcdiIntermediaryEnterpriseRelationVO">
<id property="id" column="id"/>
@@ -63,4 +76,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND social_credit_code = #{socialCreditCode}
</select>
<select id="batchExistsByCombinations" resultType="string">
SELECT CONCAT(intermediary_biz_id, '|', social_credit_code)
FROM ccdi_intermediary_enterprise_relation
WHERE CONCAT(intermediary_biz_id, '|', social_credit_code) IN
<foreach collection="combinations" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
</mapper>

View File

@@ -32,7 +32,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
child.create_time
FROM ccdi_biz_intermediary child
INNER JOIN ccdi_biz_intermediary parent
ON child.related_num_id COLLATE utf8mb4_general_ci = parent.biz_id COLLATE utf8mb4_general_ci
ON child.related_num_id COLLATE utf8mb4_general_ci = parent.person_id COLLATE utf8mb4_general_ci
AND parent.person_sub_type COLLATE utf8mb4_general_ci = '本人' COLLATE utf8mb4_general_ci
WHERE child.person_sub_type IS NOT NULL
AND child.person_sub_type COLLATE utf8mb4_general_ci != '本人' COLLATE utf8mb4_general_ci