feat: 导入功能改为批量插入和批量更新

## 修改内容

### Mapper 接口
- CcdiBizIntermediaryMapper: 新增 insertBatch() 和 updateBatch() 方法
- CcdiEnterpriseBaseInfoMapper: 新增 insertBatch() 和 updateBatch() 方法

### Mapper XML
- 新增 CcdiBizIntermediaryMapper.xml: 实现个人中介的批量插入和更新
- 新增 CcdiEnterpriseBaseInfoMapper.xml: 实现实体中介的批量插入和更新
  - 批量插入使用 VALUES (...), (...), (...) 语法
  - 批量更新使用 foreach 分隔多条 UPDATE 语句

### Service 实现
- importIntermediaryPerson(): 改为两轮处理模式
  - 第一轮:数据验证和分类(区分插入和更新)
  - 第二轮:批量插入新记录 + 批量更新已存在记录

- importIntermediaryEntity(): 改为两轮处理模式
  - 第一轮:数据验证和分类(区分插入和更新)
  - 第二轮:批量插入新记录 + 批量更新已存在记录

## 性能优化
- 从原来的逐条插入/更新改为批量操作
- 减少数据库连接次数,提升大数据量导入性能
- 事务一致性保证,全部成功或全部回滚

编译验证:通过
This commit is contained in:
wkc
2026-02-04 19:52:29 +08:00
parent cca2e620b5
commit 1af2677c05
5 changed files with 238 additions and 22 deletions

View File

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ccdi.domain.CcdiBizIntermediary;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 个人中介Mapper接口
*
@@ -13,4 +15,19 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CcdiBizIntermediaryMapper extends BaseMapper<CcdiBizIntermediary> {
/**
* 批量插入个人中介
*
* @param list 个人中介列表
* @return 插入行数
*/
int insertBatch(List<CcdiBizIntermediary> list);
/**
* 批量更新个人中介
*
* @param list 个人中介列表
* @return 更新行数
*/
int updateBatch(List<CcdiBizIntermediary> list);
}

View File

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ccdi.domain.CcdiEnterpriseBaseInfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 实体中介Mapper接口
*
@@ -13,4 +15,19 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CcdiEnterpriseBaseInfoMapper extends BaseMapper<CcdiEnterpriseBaseInfo> {
/**
* 批量插入实体中介
*
* @param list 实体中介列表
* @return 插入行数
*/
int insertBatch(List<CcdiEnterpriseBaseInfo> list);
/**
* 批量更新实体中介
*
* @param list 实体中介列表
* @return 更新行数
*/
int updateBatch(List<CcdiEnterpriseBaseInfo> list);
}

View File

@@ -285,7 +285,7 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
}
/**
* 导入个人中介数据
* 导入个人中介数据(批量操作)
*
* @param list Excel实体列表
* @param updateSupport 是否更新支持
@@ -303,8 +303,16 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
for (CcdiIntermediaryPersonExcel excel : list) {
// 待插入和更新的列表
List<CcdiBizIntermediary> insertList = new ArrayList<>();
List<CcdiBizIntermediary> updateList = new ArrayList<>();
List<String> personIds = new ArrayList<>();
// 第一轮:数据验证和分类
for (int i = 0; i < list.size(); i++) {
try {
CcdiIntermediaryPersonExcel excel = list.get(i);
// 验证数据
if (StringUtils.isEmpty(excel.getName())) {
throw new RuntimeException("姓名不能为空");
@@ -319,29 +327,26 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
person.setPersonType("中介");
person.setDataSource("IMPORT");
personIds.add(excel.getPersonId());
// 检查唯一性
if (!checkPersonIdUnique(excel.getPersonId(), null)) {
if (updateSupport) {
// 更新模式:查询已存在记录并更新
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CcdiBizIntermediary::getPersonId, excel.getPersonId());
CcdiBizIntermediary existingPerson = bizIntermediaryMapper.selectOne(wrapper);
if (existingPerson != null) {
person.setBizId(existingPerson.getBizId());
bizIntermediaryMapper.updateById(person);
}
// 需要更新,暂时加入更新列表
updateList.add(person);
} else {
throw new RuntimeException("该证件号已存在");
}
} else {
bizIntermediaryMapper.insert(person);
// 新数据,加入插入列表
insertList.add(person);
}
successNum++;
successMsg.append("<br/>").append(successNum).append("").append(excel.getName()).append(" 导入成功");
} catch (Exception e) {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("").append(excel.getName()).append(" 导入失败:");
failureMsg.append("<br/>").append(failureNum).append("").append(list.get(i).getName()).append(" 导入失败:");
failureMsg.append(e.getMessage());
}
}
@@ -349,14 +354,49 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
if (failureNum > 0) {
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new RuntimeException(failureMsg.toString());
} else {
}
// 第二轮:批量处理
try {
// 批量插入新记录
if (!insertList.isEmpty()) {
bizIntermediaryMapper.insertBatch(insertList);
}
// 批量更新已存在的记录
if (!updateList.isEmpty()) {
// 查询已存在记录的bizId
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CcdiBizIntermediary::getPersonId, personIds);
List<CcdiBizIntermediary> existingList = bizIntermediaryMapper.selectList(wrapper);
// 建立personId到bizId的映射
java.util.Map<String, String> personIdToBizIdMap = new java.util.HashMap<>();
for (CcdiBizIntermediary existing : existingList) {
personIdToBizIdMap.put(existing.getPersonId(), existing.getBizId());
}
// 设置bizId到更新列表
for (CcdiBizIntermediary person : updateList) {
String bizId = personIdToBizIdMap.get(person.getPersonId());
if (bizId != null) {
person.setBizId(bizId);
}
}
// 批量更新
bizIntermediaryMapper.updateBatch(updateList);
}
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + "");
return successMsg.toString();
} catch (Exception e) {
throw new RuntimeException("批量操作失败:" + e.getMessage());
}
}
/**
* 导入实体中介数据
* 导入实体中介数据(批量操作)
*
* @param list Excel实体列表
* @param updateSupport 是否更新支持
@@ -374,8 +414,16 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
for (CcdiIntermediaryEntityExcel excel : list) {
// 待插入和更新的列表
List<CcdiEnterpriseBaseInfo> insertList = new ArrayList<>();
List<CcdiEnterpriseBaseInfo> updateList = new ArrayList<>();
List<String> socialCreditCodes = new ArrayList<>();
// 第一轮:数据验证和分类
for (int i = 0; i < list.size(); i++) {
try {
CcdiIntermediaryEntityExcel excel = list.get(i);
// 验证数据
if (StringUtils.isEmpty(excel.getEnterpriseName())) {
throw new RuntimeException("机构名称不能为空");
@@ -390,26 +438,29 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
// 检查唯一性
if (StringUtils.isNotEmpty(excel.getSocialCreditCode())) {
socialCreditCodes.add(excel.getSocialCreditCode());
if (!checkSocialCreditCodeUnique(excel.getSocialCreditCode(), null)) {
if (updateSupport) {
// 更新模式
enterpriseBaseInfoMapper.updateById(entity);
// 需要更新,加入更新列表
updateList.add(entity);
} else {
throw new RuntimeException("该统一社会信用代码已存在");
}
} else {
enterpriseBaseInfoMapper.insert(entity);
// 新数据,加入插入列表
insertList.add(entity);
}
} else {
// 没有统一社会信用代码,直接插入
enterpriseBaseInfoMapper.insert(entity);
// 没有统一社会信用代码,直接加入插入列表
insertList.add(entity);
}
successNum++;
successMsg.append("<br/>").append(successNum).append("").append(excel.getEnterpriseName()).append(" 导入成功");
} catch (Exception e) {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("").append(excel.getEnterpriseName()).append(" 导入失败:");
failureMsg.append("<br/>").append(failureNum).append("").append(list.get(i).getEnterpriseName()).append(" 导入失败:");
failureMsg.append(e.getMessage());
}
}
@@ -417,9 +468,25 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
if (failureNum > 0) {
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new RuntimeException(failureMsg.toString());
} else {
}
// 第二轮:批量处理
try {
// 批量插入新记录
if (!insertList.isEmpty()) {
enterpriseBaseInfoMapper.insertBatch(insertList);
}
// 批量更新已存在的记录
if (!updateList.isEmpty()) {
// 批量更新socialCreditCode已在实体中
enterpriseBaseInfoMapper.updateBatch(updateList);
}
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + "");
return successMsg.toString();
} catch (Exception e) {
throw new RuntimeException("批量操作失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ccdi.mapper.CcdiBizIntermediaryMapper">
<!-- 批量插入个人中介 -->
<insert id="insertBatch" parameterType="java.util.List">
INSERT INTO ccdi_biz_intermediary (
biz_id, person_type, person_sub_type, relation_type,
name, gender, id_type, person_id, mobile, wechat_no,
contact_address, company, social_credit_code, position,
related_num_id, data_source, remark,
created_by, updated_by, create_time, update_time
) VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.bizId}, #{item.personType}, #{item.personSubType}, #{item.relationType},
#{item.name}, #{item.gender}, #{item.idType}, #{item.personId}, #{item.mobile}, #{item.wechatNo},
#{item.contactAddress}, #{item.company}, #{item.socialCreditCode}, #{item.position},
#{item.relatedNumId}, #{item.dataSource}, #{item.remark},
#{item.createdBy}, #{item.updatedBy}, #{item.createTime}, #{item.updateTime}
)
</foreach>
</insert>
<!-- 批量更新个人中介 -->
<update id="updateBatch" parameterType="java.util.List">
<foreach collection="list" item="item" separator=";">
UPDATE ccdi_biz_intermediary
<set>
<if test="item.personType != null">person_type = #{item.personType},</if>
<if test="item.personSubType != null">person_sub_type = #{item.personSubType},</if>
<if test="item.relationType != null">relation_type = #{item.relationType},</if>
<if test="item.name != null and item.name != ''">name = #{item.name},</if>
<if test="item.gender != null">gender = #{item.gender},</if>
<if test="item.idType != null">id_type = #{item.idType},</if>
<if test="item.personId != null and item.personId != ''">person_id = #{item.personId},</if>
<if test="item.mobile != null">mobile = #{item.mobile},</if>
<if test="item.wechatNo != null">wechat_no = #{item.wechatNo},</if>
<if test="item.contactAddress != null">contact_address = #{item.contactAddress},</if>
<if test="item.company != null">company = #{item.company},</if>
<if test="item.socialCreditCode != null">social_credit_code = #{item.socialCreditCode},</if>
<if test="item.position != null">position = #{item.position},</if>
<if test="item.relatedNumId != null">related_num_id = #{item.relatedNumId},</if>
<if test="item.dataSource != null">data_source = #{item.dataSource},</if>
<if test="item.remark != null">remark = #{item.remark},</if>
<if test="item.updatedBy != null">updated_by = #{item.updatedBy},</if>
update_time = #{item.updateTime}
</set>
WHERE biz_id = #{item.bizId}
</foreach>
</update>
</mapper>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ccdi.mapper.CcdiEnterpriseBaseInfoMapper">
<!-- 批量插入实体中介 -->
<insert id="insertBatch" parameterType="java.util.List">
INSERT INTO ccdi_enterprise_base_info (
social_credit_code, enterprise_name, enterprise_type, enterprise_nature,
industry_class, industry_name, establish_date, register_address,
legal_representative, legal_cert_type, legal_cert_no,
shareholder1, shareholder2, shareholder3, shareholder4, shareholder5,
status, risk_level, ent_source, data_source,
created_by, updated_by, create_time, update_time
) VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.socialCreditCode}, #{item.enterpriseName}, #{item.enterpriseType}, #{item.enterpriseNature},
#{item.industryClass}, #{item.industryName}, #{item.establishDate}, #{item.registerAddress},
#{item.legalRepresentative}, #{item.legalCertType}, #{item.legalCertNo},
#{item.shareholder1}, #{item.shareholder2}, #{item.shareholder3}, #{item.shareholder4}, #{item.shareholder5},
#{item.status}, #{item.riskLevel}, #{item.entSource}, #{item.dataSource},
#{item.createdBy}, #{item.updatedBy}, #{item.createTime}, #{item.updateTime}
)
</foreach>
</insert>
<!-- 批量更新实体中介 -->
<update id="updateBatch" parameterType="java.util.List">
<foreach collection="list" item="item" separator=";">
UPDATE ccdi_enterprise_base_info
<set>
<if test="item.enterpriseName != null and item.enterpriseName != ''">enterprise_name = #{item.enterpriseName},</if>
<if test="item.enterpriseType != null">enterprise_type = #{item.enterpriseType},</if>
<if test="item.enterpriseNature != null">enterprise_nature = #{item.enterpriseNature},</if>
<if test="item.industryClass != null">industry_class = #{item.industryClass},</if>
<if test="item.industryName != null">industry_name = #{item.industryName},</if>
<if test="item.establishDate != null">establish_date = #{item.establishDate},</if>
<if test="item.registerAddress != null">register_address = #{item.registerAddress},</if>
<if test="item.legalRepresentative != null">legal_representative = #{item.legalRepresentative},</if>
<if test="item.legalCertType != null">legal_cert_type = #{item.legalCertType},</if>
<if test="item.legalCertNo != null">legal_cert_no = #{item.legalCertNo},</if>
<if test="item.shareholder1 != null">shareholder1 = #{item.shareholder1},</if>
<if test="item.shareholder2 != null">shareholder2 = #{item.shareholder2},</if>
<if test="item.shareholder3 != null">shareholder3 = #{item.shareholder3},</if>
<if test="item.shareholder4 != null">shareholder4 = #{item.shareholder4},</if>
<if test="item.shareholder5 != null">shareholder5 = #{item.shareholder5},</if>
<if test="item.status != null">status = #{item.status},</if>
<if test="item.riskLevel != null">risk_level = #{item.riskLevel},</if>
<if test="item.entSource != null">ent_source = #{item.entSource},</if>
<if test="item.dataSource != null">data_source = #{item.dataSource},</if>
<if test="item.updatedBy != null">updated_by = #{item.updatedBy},</if>
update_time = #{item.updateTime}
</set>
WHERE social_credit_code = #{item.socialCreditCode}
</foreach>
</update>
</mapper>