394 lines
12 KiB
Markdown
394 lines
12 KiB
Markdown
|
|
# 员工导入服务规范合规审查报告
|
|||
|
|
|
|||
|
|
**审查时间**: 2026-02-09
|
|||
|
|
**审查文件**: `CcdiEmployeeImportServiceImpl.java`
|
|||
|
|
**审查类型**: 规范合规最终审查
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、审查结果总览
|
|||
|
|
|
|||
|
|
### ✅ 最终评估:**完全合规**
|
|||
|
|
|
|||
|
|
**综合评分**: 100/100
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、详细审查清单
|
|||
|
|
|
|||
|
|
### 1. 功能完整性检查 (25分)
|
|||
|
|
|
|||
|
|
#### ✅ 批量查询实现 (25/25分)
|
|||
|
|
|
|||
|
|
| 检查项 | 要求 | 实际情况 | 状态 |
|
|||
|
|
|--------|------|----------|------|
|
|||
|
|
| 调用 getExistingIdCards | 批量查询身份证号 | 第50行已调用 | ✅ |
|
|||
|
|
| existingIdCards 集合 | 存储数据库已存在身份证号 | 第50行已创建 | ✅ |
|
|||
|
|
| processedIdCards 集合 | 跟踪Excel内已处理身份证号 | 第54行已创建 | ✅ |
|
|||
|
|
| processedEmployeeIds 集合 | 跟踪Excel内已处理柜员号 | 第53行已创建 | ✅ |
|
|||
|
|
|
|||
|
|
**证据代码**:
|
|||
|
|
```java
|
|||
|
|
// 第49-50行:批量查询
|
|||
|
|
Set<Long> existingIds = getExistingEmployeeIds(excelList);
|
|||
|
|
Set<String> existingIdCards = getExistingIdCards(excelList);
|
|||
|
|
|
|||
|
|
// 第53-54行:Excel内处理跟踪
|
|||
|
|
Set<Long> processedEmployeeIds = new HashSet<>();
|
|||
|
|
Set<String> processedIdCards = new HashSet<>();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. 实现正确性检查 (25分)
|
|||
|
|
|
|||
|
|
#### ✅ 检查顺序 (25/25分)
|
|||
|
|
|
|||
|
|
**设计规范要求的检查顺序**:
|
|||
|
|
1. ✅ 数据库重复检查
|
|||
|
|
2. ✅ Excel内柜员号重复检查
|
|||
|
|
3. ✅ Excel内身份证号重复检查
|
|||
|
|
|
|||
|
|
**实际实现顺序**:
|
|||
|
|
|
|||
|
|
**新增分支** (第90-101行):
|
|||
|
|
```java
|
|||
|
|
} else {
|
|||
|
|
// 柜员号不存在,检查Excel内重复
|
|||
|
|
if (processedEmployeeIds.contains(excel.getEmployeeId())) { // 2. 柜员号
|
|||
|
|
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
|
|||
|
|
}
|
|||
|
|
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
|
|||
|
|
processedIdCards.contains(excel.getIdCard())) { // 3. 身份证号
|
|||
|
|
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
|
|||
|
|
}
|
|||
|
|
newRecords.add(employee);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**更新分支** (第72-88行):
|
|||
|
|
```java
|
|||
|
|
if (existingIds.contains(excel.getEmployeeId())) {
|
|||
|
|
if (!isUpdateSupport) {
|
|||
|
|
throw new RuntimeException("柜员号已存在且未启用更新支持");
|
|||
|
|
}
|
|||
|
|
// 更新模式: 检查Excel内重复
|
|||
|
|
if (processedEmployeeIds.contains(excel.getEmployeeId())) { // 2. 柜员号
|
|||
|
|
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
|
|||
|
|
}
|
|||
|
|
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
|
|||
|
|
processedIdCards.contains(excel.getIdCard())) { // 3. 身份证号
|
|||
|
|
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
|
|||
|
|
}
|
|||
|
|
updateRecords.add(employee);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**评价**: 完全符合设计规范,检查顺序正确。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### ✅ if-else分支结构 (25/25分)
|
|||
|
|
|
|||
|
|
**设计规范**: 完整的双分支结构
|
|||
|
|
- **数据库存在分支**: 处理更新模式
|
|||
|
|
- **数据库不存在分支**: 处理新增模式
|
|||
|
|
|
|||
|
|
**实际实现**:
|
|||
|
|
```java
|
|||
|
|
// 第72-88行:数据库存在分支
|
|||
|
|
if (existingIds.contains(excel.getEmployeeId())) {
|
|||
|
|
// 更新模式检查
|
|||
|
|
// ...
|
|||
|
|
updateRecords.add(employee);
|
|||
|
|
} else {
|
|||
|
|
// 第90-101行:数据库不存在分支
|
|||
|
|
// 新增模式检查
|
|||
|
|
// ...
|
|||
|
|
newRecords.add(employee);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**评价**: 分支结构完整,逻辑清晰。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### ✅ 标记时机正确 (25/25分)
|
|||
|
|
|
|||
|
|
**设计规范**: 只在记录成功通过所有验证并确定要插入时,才标记为"已处理"
|
|||
|
|
|
|||
|
|
**实际实现**:
|
|||
|
|
```java
|
|||
|
|
// 第71-110行:完整的验证流程
|
|||
|
|
if (existingIds.contains(excel.getEmployeeId())) {
|
|||
|
|
// 验证Excel内重复
|
|||
|
|
// ...
|
|||
|
|
updateRecords.add(employee); // 确定插入
|
|||
|
|
} else {
|
|||
|
|
// 验证Excel内重复
|
|||
|
|
// ...
|
|||
|
|
newRecords.add(employee); // 确定插入
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 第104-110行:统一标记(两个分支后)
|
|||
|
|
// 统一标记为已处理(两个分支都会执行到这里)
|
|||
|
|
if (excel.getEmployeeId() != null) {
|
|||
|
|
processedEmployeeIds.add(excel.getEmployeeId());
|
|||
|
|
}
|
|||
|
|
if (StringUtils.isNotEmpty(excel.getIdCard())) {
|
|||
|
|
processedIdCards.add(excel.getIdCard());
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**评价**: 标记时机完全正确,只有成功通过验证的记录才会被标记。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### ✅ 空值处理正确 (25/25分)
|
|||
|
|
|
|||
|
|
**设计规范**: 只有非空的字段才参与重复检测和标记
|
|||
|
|
|
|||
|
|
**实际实现**:
|
|||
|
|
|
|||
|
|
**检测时**:
|
|||
|
|
```java
|
|||
|
|
// 第82-85行:身份证号空值检查
|
|||
|
|
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
|
|||
|
|
processedIdCards.contains(excel.getIdCard())) {
|
|||
|
|
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**标记时**:
|
|||
|
|
```java
|
|||
|
|
// 第105-110行:空值检查
|
|||
|
|
if (excel.getEmployeeId() != null) {
|
|||
|
|
processedEmployeeIds.add(excel.getEmployeeId());
|
|||
|
|
}
|
|||
|
|
if (StringUtils.isNotEmpty(excel.getIdCard())) {
|
|||
|
|
processedIdCards.add(excel.getIdCard());
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**评价**: 空值处理完全正确,符合设计规范。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### ✅ 更新模式处理 (25/25分)
|
|||
|
|
|
|||
|
|
**设计规范**: 更新模式下也要进行Excel内重复检查
|
|||
|
|
|
|||
|
|
**实际实现**:
|
|||
|
|
```java
|
|||
|
|
// 第72-88行:更新模式分支
|
|||
|
|
if (existingIds.contains(excel.getEmployeeId())) {
|
|||
|
|
if (!isUpdateSupport) {
|
|||
|
|
throw new RuntimeException("柜员号已存在且未启用更新支持");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新模式: 检查Excel内重复
|
|||
|
|
if (processedEmployeeIds.contains(excel.getEmployeeId())) {
|
|||
|
|
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
|
|||
|
|
}
|
|||
|
|
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
|
|||
|
|
processedIdCards.contains(excel.getIdCard())) {
|
|||
|
|
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 通过检查,添加到更新列表
|
|||
|
|
updateRecords.add(employee);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**评价**: 更新模式下完整实现了Excel内重复检查。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. 代码一致性检查 (25分)
|
|||
|
|
|
|||
|
|
#### ✅ 与参考实现风格一致 (25/25分)
|
|||
|
|
|
|||
|
|
**参考实现** (`CcdiIntermediaryEntityImportServiceImpl.java`):
|
|||
|
|
```java
|
|||
|
|
if (existingCreditCodes.contains(excel.getSocialCreditCode())) {
|
|||
|
|
// 数据库存在,直接报错
|
|||
|
|
throw new RuntimeException(String.format("统一社会信用代码[%s]已存在,请勿重复导入", excel.getSocialCreditCode()));
|
|||
|
|
} else if (excelProcessedIds.contains(excel.getSocialCreditCode())) {
|
|||
|
|
// Excel内重复
|
|||
|
|
throw new RuntimeException(String.format("统一社会信用代码[%s]在导入文件中重复,已跳过此条记录", excel.getSocialCreditCode()));
|
|||
|
|
} else {
|
|||
|
|
newRecords.add(entity);
|
|||
|
|
excelProcessedIds.add(excel.getSocialCreditCode()); // 标记为已处理
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**当前实现** (`CcdiEmployeeImportServiceImpl.java`):
|
|||
|
|
```java
|
|||
|
|
if (existingIds.contains(excel.getEmployeeId())) {
|
|||
|
|
// 更新模式检查
|
|||
|
|
updateRecords.add(employee);
|
|||
|
|
} else {
|
|||
|
|
// 新增模式检查
|
|||
|
|
if (processedEmployeeIds.contains(excel.getEmployeeId())) {
|
|||
|
|
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
|
|||
|
|
}
|
|||
|
|
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
|
|||
|
|
processedIdCards.contains(excel.getIdCard())) {
|
|||
|
|
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
|
|||
|
|
}
|
|||
|
|
newRecords.add(employee);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 统一标记
|
|||
|
|
if (excel.getEmployeeId() != null) {
|
|||
|
|
processedEmployeeIds.add(excel.getEmployeeId());
|
|||
|
|
}
|
|||
|
|
if (StringUtils.isNotEmpty(excel.getIdCard())) {
|
|||
|
|
processedIdCards.add(excel.getIdCard());
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**一致性分析**:
|
|||
|
|
- ✅ 错误消息格式完全一致
|
|||
|
|
- ✅ 使用 String.format 进行消息格式化
|
|||
|
|
- ✅ 异常处理方式一致
|
|||
|
|
- ✅ 批量查询模式一致
|
|||
|
|
- ✅ 标记逻辑清晰易懂
|
|||
|
|
|
|||
|
|
**评价**: 代码风格与参考实现保持高度一致。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### ✅ 错误消息格式符合要求 (25/25分)
|
|||
|
|
|
|||
|
|
**设计规范要求**:
|
|||
|
|
- 柜员号: "柜员号[XXX]在导入文件中重复,已跳过此条记录"
|
|||
|
|
- 身份证号: "身份证号[XXX]在导入文件中重复,已跳过此条记录"
|
|||
|
|
|
|||
|
|
**实际实现**:
|
|||
|
|
```java
|
|||
|
|
// 第80行:柜员号错误消息
|
|||
|
|
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
|
|||
|
|
|
|||
|
|
// 第84行:身份证号错误消息
|
|||
|
|
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
|
|||
|
|
|
|||
|
|
// 第93行:柜员号错误消息(新增分支)
|
|||
|
|
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
|
|||
|
|
|
|||
|
|
// 第97行:身份证号错误消息(新增分支)
|
|||
|
|
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**评价**: 错误消息格式完全符合设计规范要求。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. 方法签名更新检查 (25分)
|
|||
|
|
|
|||
|
|
#### ✅ validateEmployeeData 方法签名更新 (25/25分)
|
|||
|
|
|
|||
|
|
**设计规范**: 添加 existingIdCards 参数
|
|||
|
|
|
|||
|
|
**实际实现** (第280行):
|
|||
|
|
```java
|
|||
|
|
/**
|
|||
|
|
* 验证员工数据
|
|||
|
|
*
|
|||
|
|
* @param addDTO 新增DTO
|
|||
|
|
* @param isUpdateSupport 是否支持更新
|
|||
|
|
* @param existingIds 已存在的员工ID集合(导入场景使用,传null表示单条新增)
|
|||
|
|
* @param existingIdCards 已存在的身份证号集合(导入场景使用,传null表示单条新增)
|
|||
|
|
*/
|
|||
|
|
public void validateEmployeeData(CcdiEmployeeAddDTO addDTO, Boolean isUpdateSupport, Set<Long> existingIds, Set<String> existingIdCards) {
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**方法调用** (第66行):
|
|||
|
|
```java
|
|||
|
|
validateEmployeeData(addDTO, isUpdateSupport, existingIds, existingIdCards);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**批量查询结果使用** (第324行):
|
|||
|
|
```java
|
|||
|
|
// 使用批量查询的结果检查身份证号唯一性
|
|||
|
|
if (existingIdCards != null && existingIdCards.contains(addDTO.getIdCard())) {
|
|||
|
|
throw new RuntimeException("该身份证号已存在");
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**评价**: 方法签名更新完整,参数传递正确,批量查询结果正确使用。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、代码质量评价
|
|||
|
|
|
|||
|
|
### 优点总结
|
|||
|
|
|
|||
|
|
1. **性能优化**: 使用批量查询替代单条查询,显著提升性能
|
|||
|
|
2. **逻辑清晰**: 双分支结构清晰,易于理解和维护
|
|||
|
|
3. **错误处理完善**: 所有异常情况都有明确的错误消息
|
|||
|
|
4. **空值安全**: 正确处理空值情况,避免空指针异常
|
|||
|
|
5. **注释清晰**: 关键步骤都有清晰的注释说明
|
|||
|
|
6. **符合规范**: 完全符合设计规范和参考实现风格
|
|||
|
|
|
|||
|
|
### 与参考实现的差异说明
|
|||
|
|
|
|||
|
|
**差异点**: 当前实现使用了双分支结构(更新/新增),而参考实现使用单分支结构
|
|||
|
|
|
|||
|
|
**原因分析**:
|
|||
|
|
- 参考实现是纯新增模式(不支持更新)
|
|||
|
|
- 当前实现支持更新模式,需要区分更新和新增两种场景
|
|||
|
|
|
|||
|
|
**评价**: 这是合理的差异,双分支结构更适合支持更新模式的场景。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、测试建议
|
|||
|
|
|
|||
|
|
### 建议测试场景
|
|||
|
|
|
|||
|
|
1. **Excel内柜员号重复测试**
|
|||
|
|
- 准备3条相同柜员号的记录
|
|||
|
|
- 验证只有第一条成功,后2条失败
|
|||
|
|
- 验证错误消息格式正确
|
|||
|
|
|
|||
|
|
2. **Excel内身份证号重复测试**
|
|||
|
|
- 准备3条相同身份证号的记录
|
|||
|
|
- 验证只有第一条成功,后2条失败
|
|||
|
|
- 验证错误消息格式正确
|
|||
|
|
|
|||
|
|
3. **数据库重复 + Excel内重复测试**
|
|||
|
|
- 准备柜员号在数据库存在,且在Excel内重复的记录
|
|||
|
|
- 验证更新模式下Excel内重复检查生效
|
|||
|
|
|
|||
|
|
4. **空值处理测试**
|
|||
|
|
- 准备身份证号为空的记录
|
|||
|
|
- 验证空值不参与重复检测
|
|||
|
|
|
|||
|
|
5. **更新模式测试**
|
|||
|
|
- 启用更新支持
|
|||
|
|
- 验证Excel内重复检查在更新模式下生效
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、最终结论
|
|||
|
|
|
|||
|
|
### ✅ 完全合规
|
|||
|
|
|
|||
|
|
**评分**: 100/100
|
|||
|
|
|
|||
|
|
**合规要点**:
|
|||
|
|
- ✅ 功能完整性: 25/25分
|
|||
|
|
- ✅ 实现正确性: 25/25分
|
|||
|
|
- ✅ 代码一致性: 25/25分
|
|||
|
|
- ✅ 方法签名更新: 25/25分
|
|||
|
|
|
|||
|
|
**审批意见**: 该实现完全符合设计规范要求,可以进行代码合并。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**审查人**: Claude
|
|||
|
|
**审查日期**: 2026-02-09
|