2026-02-06 09:01:33 +08:00
|
|
|
|
# 导入逻辑优化设计文档
|
|
|
|
|
|
|
|
|
|
|
|
## 文档信息
|
|
|
|
|
|
|
|
|
|
|
|
- **创建日期**:2026-02-05
|
|
|
|
|
|
- **版本**:1.0
|
|
|
|
|
|
- **作者**:Claude Code
|
|
|
|
|
|
- **状态**:待实施
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 背景和目标
|
|
|
|
|
|
|
|
|
|
|
|
### 1.1 背景
|
|
|
|
|
|
|
|
|
|
|
|
当前系统中的导入功能采用"存在则更新,不存在则插入"的逻辑:
|
|
|
|
|
|
- 需要区分新增和更新两种操作
|
|
|
|
|
|
- 使用复杂的条件判断和数据分类逻辑
|
|
|
|
|
|
- 批量更新操作依赖特殊的 SQL 语法(CASE WHEN),容易出现语法错误
|
|
|
|
|
|
- 代码逻辑复杂,维护成本高
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 目标
|
|
|
|
|
|
|
|
|
|
|
|
优化导入逻辑,简化代码实现:
|
|
|
|
|
|
- 统一采用"先删除后插入"的策略
|
|
|
|
|
|
- 移除复杂的更新操作和条件判断
|
|
|
|
|
|
- 提高代码可维护性和可读性
|
|
|
|
|
|
- 保证数据一致性和事务完整性
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 需求分析
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 功能需求
|
|
|
|
|
|
|
|
|
|
|
|
#### 核心需求
|
|
|
|
|
|
1. **导入策略变更**:将"存在则更新"改为"先删后插"
|
|
|
|
|
|
2. **删除范围**:只删除导入数据中已存在的记录
|
|
|
|
|
|
3. **唯一性判断**:使用业务唯一键判断记录是否存在
|
|
|
|
|
|
4. **审计字段**:重新插入的数据,所有审计字段使用当前时间
|
|
|
|
|
|
5. **冲突处理**:批量删除所有使用相同业务键的记录
|
|
|
|
|
|
|
|
|
|
|
|
#### 影响模块
|
|
|
|
|
|
- 员工信息管理(`ccdi_employee`)
|
|
|
|
|
|
- 中介库个人管理(`ccdi_biz_intermediary`)
|
|
|
|
|
|
- 中介库实体管理(`ccdi_enterprise_base_info`)
|
|
|
|
|
|
- 员工招聘信息管理(`ccdi_staff_recruitment`)
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 非功能需求
|
|
|
|
|
|
|
|
|
|
|
|
- **性能**:批量操作,2-3次数据库往返
|
|
|
|
|
|
- **事务性**:所有操作在同一事务中,保证原子性
|
|
|
|
|
|
- **兼容性**:前端调用方式保持不变
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 设计方案
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 整体架构
|
|
|
|
|
|
|
|
|
|
|
|
新的导入逻辑采用三阶段流程:
|
|
|
|
|
|
|
|
|
|
|
|
#### 阶段 1:数据验证与收集
|
|
|
|
|
|
- 遍历所有导入数据,验证必填字段和数据格式
|
|
|
|
|
|
- 收集所有业务唯一键
|
|
|
|
|
|
- 检查导入数据内部的重复性
|
|
|
|
|
|
- 验证通过的数据放入待处理列表
|
|
|
|
|
|
|
|
|
|
|
|
#### 阶段 2:批量删除
|
|
|
|
|
|
- 根据收集的业务唯一键列表,执行批量删除操作
|
|
|
|
|
|
- SQL:`DELETE FROM table WHERE unique_key IN (...)`
|
|
|
|
|
|
- 删除所有匹配的旧记录,包括重复的记录
|
|
|
|
|
|
|
|
|
|
|
|
#### 阶段 3:批量插入
|
|
|
|
|
|
- 批量插入所有验证通过的数据
|
|
|
|
|
|
- SQL:`INSERT INTO table (...) VALUES (...), (...), ...`
|
|
|
|
|
|
- 所有审计字段使用当前时间
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 数据流图
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
导入数据(Excel)
|
|
|
|
|
|
↓
|
|
|
|
|
|
【阶段 1】数据验证与收集
|
|
|
|
|
|
├→ 验证必填字段和数据格式
|
|
|
|
|
|
├→ 检查导入数据内部重复
|
|
|
|
|
|
├→ 收集业务唯一键
|
|
|
|
|
|
└→ 构建待插入列表
|
|
|
|
|
|
↓
|
|
|
|
|
|
【阶段 2】批量删除已存在记录
|
|
|
|
|
|
└→ DELETE FROM table WHERE unique_key IN (...)
|
|
|
|
|
|
↓
|
|
|
|
|
|
【阶段 3】批量插入所有数据
|
|
|
|
|
|
└→ INSERT INTO table (...) VALUES (...)
|
|
|
|
|
|
↓
|
|
|
|
|
|
返回导入结果(成功数量、失败详情)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.3 各模块业务键定义
|
|
|
|
|
|
|
|
|
|
|
|
| 模块 | 表名 | 业务键 | 说明 |
|
|
|
|
|
|
|------|------|--------|------|
|
|
|
|
|
|
| 员工信息 | `ccdi_employee` | `id_card` | 身份证号 |
|
|
|
|
|
|
| 中介库个人 | `ccdi_biz_intermediary` | `person_id` | 个人证件号 |
|
|
|
|
|
|
| 中介库实体 | `ccdi_enterprise_base_info` | `social_credit_code` | 统一社会信用代码 |
|
|
|
|
|
|
| 招聘信息 | `ccdi_staff_recruitment` | `recruit_id` | 招聘项目编号 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 详细设计
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 数据库层设计
|
|
|
|
|
|
|
|
|
|
|
|
#### 4.1.1 新增 Mapper 方法
|
|
|
|
|
|
|
|
|
|
|
|
每个模块需要添加对应的批量删除方法:
|
|
|
|
|
|
|
|
|
|
|
|
**员工信息模块**:
|
|
|
|
|
|
```java
|
|
|
|
|
|
// CcdiEmployeeMapper.java
|
|
|
|
|
|
int deleteBatchByIdCard(@Param("list") List<String> idCards);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**中介库个人模块**:
|
|
|
|
|
|
```java
|
|
|
|
|
|
// CcdiBizIntermediaryMapper.java
|
|
|
|
|
|
int deleteBatchByPersonId(@Param("list") List<String> personIds);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**中介库实体模块**:
|
|
|
|
|
|
```java
|
|
|
|
|
|
// CcdiEnterpriseBaseInfoMapper.java
|
|
|
|
|
|
int deleteBatchBySocialCreditCode(@Param("list") List<String> socialCreditCodes);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**招聘信息模块**:
|
|
|
|
|
|
```java
|
|
|
|
|
|
// CcdiStaffRecruitmentMapper.java
|
|
|
|
|
|
int deleteBatchByRecruitId(@Param("list") List<String> recruitIds);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 4.1.2 Mapper XML 实现
|
|
|
|
|
|
|
|
|
|
|
|
所有删除 SQL 使用统一的模式:
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<delete id="deleteBatchByXxx">
|
|
|
|
|
|
DELETE FROM {table_name}
|
|
|
|
|
|
WHERE {unique_key_column} IN
|
|
|
|
|
|
<foreach collection="list" item="item" open="(" separator="," close=")">
|
|
|
|
|
|
#{item}
|
|
|
|
|
|
</foreach>
|
|
|
|
|
|
</delete>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**示例(员工信息)**:
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<!-- CcdiEmployeeMapper.xml -->
|
|
|
|
|
|
<delete id="deleteBatchByIdCard">
|
|
|
|
|
|
DELETE FROM ccdi_employee
|
|
|
|
|
|
WHERE id_card IN
|
|
|
|
|
|
<foreach collection="list" item="item" open="(" separator="," close=")">
|
|
|
|
|
|
#{item}
|
|
|
|
|
|
</foreach>
|
|
|
|
|
|
</delete>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 服务层设计
|
|
|
|
|
|
|
|
|
|
|
|
#### 4.2.1 通用导入方法模板
|
|
|
|
|
|
|
|
|
|
|
|
所有模块的导入方法遵循统一的实现模式:
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Override
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
|
public String importXxx(List<XxxExcel> excelList, Boolean isUpdateSupport) {
|
|
|
|
|
|
// 参数校验
|
|
|
|
|
|
if (StringUtils.isNull(excelList) || excelList.isEmpty()) {
|
|
|
|
|
|
return "至少需要一条数据";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第一阶段:数据验证和收集
|
|
|
|
|
|
List<XxxEntity> validList = new ArrayList<>();
|
|
|
|
|
|
List<String> errorMessages = new ArrayList<>();
|
|
|
|
|
|
Set<String> uniqueKeys = new HashSet<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (XxxExcel excel : excelList) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 转换并验证
|
|
|
|
|
|
XxxAddDTO addDTO = new XxxAddDTO();
|
|
|
|
|
|
BeanUtils.copyProperties(excel, addDTO);
|
|
|
|
|
|
validateXxxDataBasic(addDTO);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查导入数据内部是否重复
|
|
|
|
|
|
String uniqueKey = getUniqueKey(addDTO);
|
|
|
|
|
|
if (!uniqueKeys.add(uniqueKey)) {
|
|
|
|
|
|
throw new RuntimeException("导入文件中该" + getUniqueKeyName() + "重复");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转换为实体,设置审计字段
|
|
|
|
|
|
XxxEntity entity = new XxxEntity();
|
|
|
|
|
|
BeanUtils.copyProperties(addDTO, entity);
|
|
|
|
|
|
entity.setCreateBy("导入");
|
|
|
|
|
|
entity.setUpdateBy("导入");
|
|
|
|
|
|
|
|
|
|
|
|
validList.add(entity);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
errorMessages.add(String.format("%s 导入失败:%s",
|
|
|
|
|
|
getDisplayName(excel), e.getMessage()));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第二阶段:批量删除已存在的记录
|
|
|
|
|
|
if (!validList.isEmpty()) {
|
|
|
|
|
|
List<String> uniqueKeyList = new ArrayList<>(uniqueKeys);
|
|
|
|
|
|
mapper.deleteBatchByUniqueKey(uniqueKeyList);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第三阶段:批量插入所有数据
|
|
|
|
|
|
if (!validList.isEmpty()) {
|
|
|
|
|
|
mapper.insertBatch(validList);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第四阶段:返回结果
|
|
|
|
|
|
if (!errorMessages.isEmpty()) {
|
|
|
|
|
|
throw buildFailureException(validList.size(), errorMessages);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return buildSuccessMessage(validList.size());
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 4.2.2 员工信息导入方法(示例)
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// CcdiEmployeeServiceImpl.java
|
|
|
|
|
|
@Override
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
|
public String importEmployee(List<CcdiEmployeeExcel> excelList, Boolean isUpdateSupport) {
|
|
|
|
|
|
if (StringUtils.isNull(excelList) || excelList.isEmpty()) {
|
|
|
|
|
|
return "至少需要一条数据";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第一阶段:数据验证和收集
|
|
|
|
|
|
List<CcdiEmployee> validEmployees = new ArrayList<>();
|
|
|
|
|
|
List<String> errorMessages = new ArrayList<>();
|
|
|
|
|
|
Set<String> idCards = new HashSet<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (CcdiEmployeeExcel excel : excelList) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 转换并验证
|
|
|
|
|
|
CcdiEmployeeAddDTO addDTO = new CcdiEmployeeAddDTO();
|
|
|
|
|
|
BeanUtils.copyProperties(excel, addDTO);
|
|
|
|
|
|
validateEmployeeDataBasic(addDTO);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查导入数据内部是否重复
|
|
|
|
|
|
if (!idCards.add(addDTO.getIdCard())) {
|
|
|
|
|
|
throw new RuntimeException("导入文件中该身份证号重复");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转换为实体,设置审计字段
|
|
|
|
|
|
CcdiEmployee employee = new CcdiEmployee();
|
|
|
|
|
|
BeanUtils.copyProperties(addDTO, employee);
|
|
|
|
|
|
employee.setCreateBy("导入");
|
|
|
|
|
|
employee.setUpdateBy("导入");
|
|
|
|
|
|
|
|
|
|
|
|
validEmployees.add(employee);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
errorMessages.add(String.format("%s 导入失败:%s",
|
|
|
|
|
|
excel.getName(), e.getMessage()));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第二阶段:批量删除已存在的记录
|
|
|
|
|
|
if (!validEmployees.isEmpty()) {
|
|
|
|
|
|
employeeMapper.deleteBatchByIdCard(new ArrayList<>(idCards));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第三阶段:批量插入所有数据
|
|
|
|
|
|
if (!validEmployees.isEmpty()) {
|
|
|
|
|
|
employeeMapper.insertBatch(validEmployees);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 第四阶段:返回结果
|
|
|
|
|
|
if (!errorMessages.isEmpty()) {
|
|
|
|
|
|
StringBuilder failureMsg = new StringBuilder();
|
|
|
|
|
|
failureMsg.append("很抱歉,导入完成!成功 ")
|
|
|
|
|
|
.append(validEmployees.size())
|
|
|
|
|
|
.append(" 条,失败 ")
|
|
|
|
|
|
.append(errorMessages.size())
|
|
|
|
|
|
.append(" 条,错误如下:");
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < errorMessages.size(); i++) {
|
|
|
|
|
|
failureMsg.append("<br/>")
|
|
|
|
|
|
.append(i + 1)
|
|
|
|
|
|
.append("、")
|
|
|
|
|
|
.append(errorMessages.get(i));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
throw new RuntimeException(failureMsg.toString());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return "恭喜您,数据已全部导入成功!共 " + validEmployees.size() + " 条";
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 事务管理
|
|
|
|
|
|
|
|
|
|
|
|
#### 事务边界
|
|
|
|
|
|
|
|
|
|
|
|
整个导入操作使用 `@Transactional` 注解,确保原子性:
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
|
public String importXxx(List<XxxExcel> excelList, Boolean isUpdateSupport) {
|
|
|
|
|
|
// 所有数据库操作在一个事务中
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 事务保证
|
|
|
|
|
|
|
|
|
|
|
|
| 场景 | 处理方式 | 结果 |
|
|
|
|
|
|
|------|----------|------|
|
|
|
|
|
|
| 批量删除失败 | 自动回滚 | 不影响现有数据 |
|
|
|
|
|
|
| 批量插入失败 | 自动回滚 | 已删除的数据恢复 |
|
|
|
|
|
|
| 数据验证失败 | 不执行数据库操作 | 直接返回错误信息 |
|
|
|
|
|
|
|
|
|
|
|
|
### 4.4 错误处理
|
|
|
|
|
|
|
|
|
|
|
|
#### 分层错误处理策略
|
|
|
|
|
|
|
|
|
|
|
|
**1. 数据验证层**
|
|
|
|
|
|
- 捕获单条数据的验证错误(必填字段、格式校验)
|
|
|
|
|
|
- 记录到失败列表,不影响其他数据
|
|
|
|
|
|
- 验证通过的数据继续处理
|
|
|
|
|
|
|
|
|
|
|
|
**2. 数据库操作层**
|
|
|
|
|
|
- 删除/插入失败时抛出异常,触发事务回滚
|
|
|
|
|
|
- 捕获 `DuplicateKeyException`、`DataIntegrityViolationException` 等
|
|
|
|
|
|
- 转换为用户友好的错误消息
|
|
|
|
|
|
|
|
|
|
|
|
**3. 统一返回**
|
|
|
|
|
|
- 全部成功:返回成功消息 + 统计信息
|
|
|
|
|
|
- 部分失败(验证阶段):返回详细错误列表
|
|
|
|
|
|
- 数据库失败:事务回滚,返回系统错误提示
|
|
|
|
|
|
|
|
|
|
|
|
### 4.5 数据一致性保障
|
|
|
|
|
|
|
|
|
|
|
|
#### 场景 1:导入数据中业务键重复
|
|
|
|
|
|
|
|
|
|
|
|
**示例**:导入文件中有两条记录的身份证号都是 `110101199001011234`
|
|
|
|
|
|
|
|
|
|
|
|
**处理结果**:
|
|
|
|
|
|
- 数据库中的旧记录被删除(如果存在)
|
|
|
|
|
|
- 导入文件中的最后一条记录被插入
|
|
|
|
|
|
- 第一条记录在验证阶段被检测为重复,记录到错误列表
|
|
|
|
|
|
|
|
|
|
|
|
#### 场景 2:数据库中存在重复记录
|
|
|
|
|
|
|
|
|
|
|
|
**示例**:数据库中有两条记录的身份证号都是 `110101199001011234`(历史数据问题)
|
|
|
|
|
|
|
|
|
|
|
|
**处理结果**:
|
|
|
|
|
|
- 批量删除操作会删除所有身份证号匹配的记录
|
|
|
|
|
|
- 插入新的记录
|
|
|
|
|
|
- 自动修复了数据不一致问题
|
|
|
|
|
|
|
|
|
|
|
|
#### 场景 3:并发导入
|
|
|
|
|
|
|
|
|
|
|
|
**示例**:用户 A 和用户 B 同时导入包含相同身份证号的数据
|
|
|
|
|
|
|
|
|
|
|
|
**处理结果**:
|
|
|
|
|
|
- 依赖数据库事务隔离级别和锁机制
|
|
|
|
|
|
- 后提交的事务可能产生 `DuplicateKeyException`
|
|
|
|
|
|
- 事务回滚,返回错误提示
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 实施计划
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 修改文件清单(11 个文件)
|
|
|
|
|
|
|
|
|
|
|
|
#### 员工信息管理模块
|
2026-02-24 17:12:11 +08:00
|
|
|
|
1. `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/mapper/CcdiEmployeeMapper.java`
|
|
|
|
|
|
2. `ruoyi-info-collection/src/main/resources/mapper/ccdi/CcdiEmployeeMapper.xml`
|
|
|
|
|
|
3. `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiEmployeeServiceImpl.java`
|
2026-02-06 09:01:33 +08:00
|
|
|
|
|
|
|
|
|
|
#### 中介库管理模块(个人和实体)
|
2026-02-24 17:12:11 +08:00
|
|
|
|
4. `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/mapper/CcdiBizIntermediaryMapper.java`
|
|
|
|
|
|
5. `ruoyi-info-collection/src/main/resources/mapper/ccdi/CcdiBizIntermediaryMapper.xml`
|
|
|
|
|
|
6. `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/mapper/CcdiEnterpriseBaseInfoMapper.java`
|
|
|
|
|
|
7. `ruoyi-info-collection/src/main/resources/mapper/ccdi/CcdiEnterpriseBaseInfoMapper.xml`
|
|
|
|
|
|
8. `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiIntermediaryServiceImpl.java`
|
2026-02-06 09:01:33 +08:00
|
|
|
|
- 修改 `importIntermediaryPerson` 方法
|
|
|
|
|
|
- 修改 `importIntermediaryEntity` 方法
|
|
|
|
|
|
|
|
|
|
|
|
#### 员工招聘信息管理模块
|
2026-02-24 17:12:11 +08:00
|
|
|
|
9. `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/mapper/CcdiStaffRecruitmentMapper.java`
|
|
|
|
|
|
10. `ruoyi-info-collection/src/main/resources/mapper/ccdi/CcdiStaffRecruitmentMapper.xml`
|
|
|
|
|
|
11. `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffRecruitmentServiceImpl.java`
|
2026-02-06 09:01:33 +08:00
|
|
|
|
|
|
|
|
|
|
### 5.2 实施步骤
|
|
|
|
|
|
|
|
|
|
|
|
#### 步骤 1:员工信息模块(验证方案)
|
|
|
|
|
|
1. 添加 `deleteBatchByIdCard` 方法到 Mapper 接口
|
|
|
|
|
|
2. 在 Mapper XML 中实现删除 SQL
|
|
|
|
|
|
3. 重构 `importEmployee` 方法
|
|
|
|
|
|
4. 生成测试脚本并验证功能
|
|
|
|
|
|
5. **验证通过后,继续其他模块**
|
|
|
|
|
|
|
|
|
|
|
|
#### 步骤 2:中介库模块
|
|
|
|
|
|
1. 添加个人表的批量删除方法
|
|
|
|
|
|
2. 添加实体表的批量删除方法
|
|
|
|
|
|
3. 重构两个导入方法
|
|
|
|
|
|
4. 测试验证
|
|
|
|
|
|
|
|
|
|
|
|
#### 步骤 3:招聘信息模块
|
|
|
|
|
|
1. 添加批量删除方法
|
|
|
|
|
|
2. 重构导入方法
|
|
|
|
|
|
3. 测试验证
|
|
|
|
|
|
|
|
|
|
|
|
#### 步骤 4:清理和优化
|
|
|
|
|
|
1. 移除不再使用的 `updateBatch` 方法(如果存在)
|
|
|
|
|
|
2. 更新 API 文档
|
|
|
|
|
|
3. 代码审查
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 测试计划
|
|
|
|
|
|
|
|
|
|
|
|
#### 单元测试
|
|
|
|
|
|
- 测试批量删除 SQL 语法正确性
|
|
|
|
|
|
- 测试批量插入 SQL 语法正确性
|
|
|
|
|
|
- 测试事务回滚机制
|
|
|
|
|
|
|
|
|
|
|
|
#### 集成测试
|
|
|
|
|
|
- 测试全新数据导入(数据库中不存在)
|
|
|
|
|
|
- 测试更新数据导入(数据库中已存在)
|
|
|
|
|
|
- 测试混合数据导入(部分存在,部分不存在)
|
|
|
|
|
|
- 测试导入数据内部重复
|
|
|
|
|
|
- 测试数据库中存在重复记录的清理
|
|
|
|
|
|
|
|
|
|
|
|
#### 性能测试
|
|
|
|
|
|
- 测试 100 条数据的导入性能
|
|
|
|
|
|
- 测试 1000 条数据的导入性能
|
|
|
|
|
|
- 对比优化前后的性能差异
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 风险评估
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 技术风险
|
|
|
|
|
|
|
|
|
|
|
|
| 风险 | 影响 | 概率 | 缓解措施 |
|
|
|
|
|
|
|------|------|------|----------|
|
|
|
|
|
|
| 批量删除 SQL 性能问题 | 中 | 低 | 确保 business_key 有索引 |
|
|
|
|
|
|
| 事务超时 | 中 | 低 | 监控事务执行时间,必要时调整超时配置 |
|
|
|
|
|
|
| 并发冲突 | 低 | 中 | 依赖数据库事务隔离机制 |
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 业务风险
|
|
|
|
|
|
|
|
|
|
|
|
| 风险 | 影响 | 概率 | 缓解措施 |
|
|
|
|
|
|
|------|------|------|----------|
|
|
|
|
|
|
| 历史数据丢失(审计字段重置) | 中 | 低 | 在文档中说明,告知用户 |
|
|
|
|
|
|
| 用户误操作导入错误数据 | 高 | 中 | 前端增加确认提示 |
|
|
|
|
|
|
|
|
|
|
|
|
### 6.3 兼容性风险
|
|
|
|
|
|
|
|
|
|
|
|
| 风险 | 影响 | 概率 | 缓解措施 |
|
|
|
|
|
|
|------|------|------|----------|
|
|
|
|
|
|
| 前端依赖 `isUpdateSupport` 参数 | 低 | 低 | 参数保留但不使用 |
|
|
|
|
|
|
| 其他系统调用导入接口 | 低 | 低 | 保持接口签名不变 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 7. 优势与劣势
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 优势
|
|
|
|
|
|
|
|
|
|
|
|
1. **代码简化**
|
|
|
|
|
|
- 移除复杂的条件判断和数据分类逻辑
|
|
|
|
|
|
- 统一的实现模式,易于维护
|
|
|
|
|
|
- 代码行数减少约 30%
|
|
|
|
|
|
|
|
|
|
|
|
2. **性能优化**
|
|
|
|
|
|
- 数据库操作从 3-4 次减少到 2-3 次
|
|
|
|
|
|
- 不再需要复杂的批量更新 SQL
|
|
|
|
|
|
- 批量删除和批量插入都使用索引,性能更好
|
|
|
|
|
|
|
|
|
|
|
|
3. **数据一致性**
|
|
|
|
|
|
- 自动清理重复数据
|
|
|
|
|
|
- 事务保证原子性
|
|
|
|
|
|
- 减少数据不一致的可能性
|
|
|
|
|
|
|
|
|
|
|
|
4. **可维护性**
|
|
|
|
|
|
- 代码逻辑清晰易懂
|
|
|
|
|
|
- 各模块实现模式统一
|
|
|
|
|
|
- 新增模块导入功能时可直接复用
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 劣势
|
|
|
|
|
|
|
|
|
|
|
|
1. **审计字段丢失**
|
|
|
|
|
|
- `create_time` 和 `create_by` 会被重置为当前值
|
|
|
|
|
|
- 无法保留原始创建时间
|
|
|
|
|
|
- **缓解措施**:在文档中明确说明,如果需要保留历史记录,可以考虑使用软删除或历史表
|
|
|
|
|
|
|
|
|
|
|
|
2. **并发性能**
|
|
|
|
|
|
- 高并发情况下可能产生事务冲突
|
|
|
|
|
|
- **缓解措施**:导入功能通常是管理员操作,并发概率较低
|
|
|
|
|
|
|
|
|
|
|
|
3. **参数失效**
|
|
|
|
|
|
- `isUpdateSupport` 参数失去原有意义
|
|
|
|
|
|
- **缓解措施**:保留参数以保持接口兼容性,内部不再使用
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 后续优化建议
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 短期优化
|
|
|
|
|
|
|
|
|
|
|
|
1. **添加导入进度提示**
|
|
|
|
|
|
- 对于大量数据导入,前端显示导入进度
|
|
|
|
|
|
- 避免用户长时间等待
|
|
|
|
|
|
|
|
|
|
|
|
2. **优化错误消息**
|
|
|
|
|
|
- 提供更详细的错误信息
|
|
|
|
|
|
- 帮助用户快速定位问题
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 长期优化
|
|
|
|
|
|
|
|
|
|
|
|
1. **异步导入**
|
|
|
|
|
|
- 对于超大文件(>10000条),使用异步处理
|
|
|
|
|
|
- 导入完成后通知用户
|
|
|
|
|
|
|
|
|
|
|
|
2. **导入历史记录**
|
|
|
|
|
|
- 记录每次导入的操作日志
|
|
|
|
|
|
- 支持导入历史查询和回滚
|
|
|
|
|
|
|
|
|
|
|
|
3. **数据校验增强**
|
|
|
|
|
|
- 添加更多业务规则校验
|
|
|
|
|
|
- 支持自定义校验规则
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 9. 附录
|
|
|
|
|
|
|
|
|
|
|
|
### 9.1 术语表
|
|
|
|
|
|
|
|
|
|
|
|
| 术语 | 说明 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| 业务键 | 业务层面判断记录唯一性的字段(如身份证号) |
|
|
|
|
|
|
| 审计字段 | 记录数据创建和修改信息的字段(create_time, create_by, update_time, update_by) |
|
|
|
|
|
|
| 批量操作 | 一次数据库操作处理多条记录 |
|
|
|
|
|
|
| 事务 | 保证一组数据库操作原子性的机制 |
|
|
|
|
|
|
|
|
|
|
|
|
### 9.2 参考资料
|
|
|
|
|
|
|
|
|
|
|
|
- [MyBatis 官方文档 - 动态 SQL](https://mybatis.org/mybatis-3/zh/dynamic-sql.html)
|
|
|
|
|
|
- [MySQL 批量插入最佳实践](https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html)
|
|
|
|
|
|
- [Spring 事务管理](https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/annotations.html)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
**文档结束**
|