Files
ccdi/assets/plans/2026-02-11-staff-transfer-validation-implementation.md
2026-03-03 16:14:16 +08:00

15 KiB
Raw Blame History

员工调动导入员工ID校验功能实施计划

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

目标: 在员工调动导入功能中添加员工ID存在性校验确保只导入有效员工的调动记录

架构: 采用批量预验证模式在数据处理循环前执行一次批量数据库查询验证所有员工ID不存在的记录提前标记为失败并跳过后续处理

技术栈: Spring Boot 3.5.8, MyBatis Plus 3.5.10, Java 17, Redis


Task 1: 添加 CcdiBaseStaffMapper 依赖注入

文件:

  • 修改: ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java:48

Step 1: 添加依赖注入字段

在第48行 SysDeptMapper deptMapper 之后添加:

@Resource
private CcdiBaseStaffMapper baseStaffMapper;

Step 2: 验证编译

Run: cd .worktrees/staff-transfer-validation && mvn clean compile -q Expected: 编译成功,无错误

Step 3: 提交

cd .worktrees/staff-transfer-validation
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java
git commit -m "feat: 添加CcdiBaseStaffMapper依赖注入

为员工调动导入服务添加员工信息Mapper用于批量验证员工ID存在性"

Task 2: 实现批量验证员工ID方法

文件:

  • 修改: ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java ( 在文件末尾添加私有方法)

Step 1: 编写批量验证方法

getImportFailures 方法之后添加:

/**
 * 批量验证员工ID是否存在
 *
 * @param excelList Excel数据列表
 * @param taskId 任务ID
 * @param failures 失败记录列表(会追加验证失败的记录)
 * @return 存在的员工ID集合
 */
private Set<Long> batchValidateStaffIds(List<CcdiStaffTransferExcel> excelList,
                                        String taskId,
                                        List<StaffTransferImportFailureVO> failures) {
    // 1. 提取并去重员工ID
    Set<Long> allStaffIds = excelList.stream()
            .map(CcdiStaffTransferExcel::getStaffId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

    if (allStaffIds.isEmpty()) {
        return Collections.emptySet();
    }

    // 2. 批量查询存在的员工ID
    ImportLogUtils.logBatchQueryStart(log, taskId, "员工ID", allStaffIds.size());

    LambdaQueryWrapper<CcdiBaseStaff> wrapper = new LambdaQueryWrapper<>();
    wrapper.select(CcdiBaseStaff::getStaffId)
           .in(CcdiBaseStaff::getStaffId, allStaffIds);

    List<CcdiBaseStaff> existingStaff = baseStaffMapper.selectList(wrapper);
    Set<Long> existingStaffIds = existingStaff.stream()
            .map(CcdiBaseStaff::getStaffId)
            .collect(Collectors.toSet());

    ImportLogUtils.logBatchQueryComplete(log, taskId, "员工ID", existingStaffIds.size());

    // 3. 预验证并标记不存在的员工ID
    for (int i = 0; i < excelList.size(); i++) {
        CcdiStaffTransferExcel excel = excelList.get(i);
        Long staffId = excel.getStaffId();

        if (staffId != null && !existingStaffIds.contains(staffId)) {
            StaffTransferImportFailureVO failure = new StaffTransferImportFailureVO();
            BeanUtils.copyProperties(excel, failure);
            failure.setErrorMessage(String.format("第%d行: 员工ID %s 不存在", i + 1, staffId));
            failures.add(failure);

            String keyData = String.format("员工ID=%s", staffId);
            ImportLogUtils.logValidationError(log, taskId, i + 1,
                                             failure.getErrorMessage(), keyData);
        }
    }

    return existingStaffIds;
}

Step 2: 验证编译

Run: cd .worktrees/staff-transfer-validation && mvn clean compile -q Expected: 编译成功,无错误

Step 3: 提交

cd .worktrees/staff-transfer-validation
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java
git commit -m "feat: 实现批量验证员工ID方法

- 提取Excel中所有员工ID并去重
- 批量查询数据库中存在的员工ID
- 标记不存在的员工ID为失败记录
- 记录详细的验证日志"

Task 3: 实现检查行是否已失败方法

文件:

  • 修改: ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java (在 batchValidateStaffIds 方法之后)

Step 1: 编写检查方法

/**
 * 检查某行数据是否已在失败列表中
 *
 * @param excel Excel数据
 * @param failures 失败记录列表
 * @return true-已失败false-未失败
 */
private boolean isRowAlreadyFailed(CcdiStaffTransferExcel excel,
                                    List<StaffTransferImportFailureVO> failures) {
    return failures.stream()
            .anyMatch(f -> f.getStaffId().equals(excel.getStaffId())
                        && Objects.equals(f.getTransferDate(), excel.getTransferDate())
                        && Objects.equals(f.getDeptIdBefore(), excel.getDeptIdBefore())
                        && Objects.equals(f.getDeptIdAfter(), excel.getDeptIdAfter()));
}

Step 2: 验证编译

Run: cd .worktrees/staff-transfer-validation && mvn clean compile -q Expected: 编译成功,无错误

Step 3: 提交

cd .worktrees/staff-transfer-validation
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java
git commit -m "feat: 实现检查行是否已失败方法

通过比较员工ID、调动日期、调动前部门ID、调动后部门ID判断该行是否已在失败列表中"

Task 4: 在导入方法中调用批量验证

文件:

  • 修改: ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java:62-68

Step 1: 修改导入方法初始化部分

在第62-68行将:

List<CcdiStaffTransfer> newRecords = new ArrayList<>();
List<StaffTransferImportFailureVO> failures = new ArrayList<>();

// 批量查询已存在的唯一键组合
ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的调动记录", excelList.size());
Set<String> existingKeys = getExistingTransferKeys(excelList);
ImportLogUtils.logBatchQueryComplete(log, taskId, "调动记录", existingKeys.size());

修改为:

List<CcdiStaffTransfer> newRecords = new ArrayList<>();
List<StaffTransferImportFailureVO> failures = new ArrayList<>();

// 批量验证员工ID是否存在
Set<Long> existingStaffIds = batchValidateStaffIds(excelList, taskId, failures);

// 批量查询已存在的唯一键组合
ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的调动记录", excelList.size());
Set<String> existingKeys = getExistingTransferKeys(excelList);
ImportLogUtils.logBatchQueryComplete(log, taskId, "调动记录", existingKeys.size());

Step 2: 验证编译

Run: cd .worktrees/staff-transfer-validation && mvn clean compile -q Expected: 编译成功,无错误

Step 3: 提交

cd .worktrees/staff-transfer-validation
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java
git commit -m "feat: 在导入流程中添加员工ID批量验证

在数据处理循环前添加员工ID存在性验证阶段提前标记无效员工ID的记录"

Task 5: 在主循环中跳过已失败记录

文件:

  • 修改: ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java:73-78

Step 1: 修改主循环开始部分

在第73-78行将:

// 分类数据
for (int i = 0; i < excelList.size(); i++) {
    CcdiStaffTransferExcel excel = excelList.get(i);

    try {

修改为:

// 分类数据
for (int i = 0; i < excelList.size(); i++) {
    CcdiStaffTransferExcel excel = excelList.get(i);

    // 跳过已在预验证阶段失败的记录
    if (isRowAlreadyFailed(excel, failures)) {
        continue;
    }

    try {

Step 2: 验证编译

Run: cd .worktrees/staff-transfer-validation && mvn clean compile -q Expected: 编译成功,无错误

Step 3: 提交

cd .worktrees/staff-transfer-validation
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java
git commit -m "feat: 主循环跳过已失败的记录

在数据处理循环中添加检查逻辑,跳过已在预验证阶段标记为失败的记录"

Task 6: 编写测试脚本

文件:

  • 创建: doc/test-data/staff-transfer-validation-test.http

Step 1: 创建HTTP测试文件

### 员工调动导入员工ID验证测试

### 1. 获取登录Token
POST http://localhost:8080/login/test
Content-Type: application/x-www-form-urlencoded

username=admin&password=admin123

> {%
    client.global.set("token", response.body.token);
    client.log("Token: " + response.body.token);
%}

### 2. 测试正常导入所有员工ID存在
POST http://localhost:8080/ccdi/staffTransfer/import
Authorization: Bearer {{token}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="file"; filename="valid-staff-ids.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

< ./valid-staff-ids.xlsx
--boundary--

### 3. 测试部分员工ID不存在
POST http://localhost:8080/ccdi/staffTransfer/import
Authorization: Bearer {{token}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="file"; filename="partial-invalid-ids.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

< ./partial-invalid-ids.xlsx
--boundary--

### 4. 测试所有员工ID不存在
POST http://localhost:8080/ccdi/staffTransfer/import
Authorization: Bearer {{token}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="file"; filename="all-invalid-ids.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

< ./all-invalid-ids.xlsx
--boundary--

### 5. 查询导入状态
GET http://localhost:8080/ccdi/staffTransfer/import/status/{{taskId}}
Authorization: Bearer {{token}}

### 6. 获取失败记录
GET http://localhost:8080/ccdi/staffTransfer/import/failures/{{taskId}}
Authorization: Bearer {{token}}

Step 2: 提交

cd .worktrees/staff-transfer-validation
git add doc/test-data/staff-transfer-validation-test.http
git commit -m "test: 添加员工ID验证测试脚本

包含正常导入、部分无效、全部无效等测试场景"

Task 7: 生成本次修改的API文档

文件:

  • 修改: doc/interface-doc/ccdi/staff-transfer.md (如果文件不存在则创建)

Step 1: 更新API文档

在现有的员工调动导入接口文档中,添加错误情况说明:

### 员工调动导入

**接口地址:** `POST /ccdi/staffTransfer/import`

**请求参数:**
- file: Excel文件multipart/form-data

**响应格式:**
```json
{
  "code": 200,
  "msg": "导入任务已提交",
  "data": {
    "taskId": "uuid"
  }
}

错误情况:

错误类型 错误信息示例 说明
员工ID不存在 第3行: 员工ID 99999 不存在 该员工ID在员工信息表中不存在
员工ID为空 员工ID不能为空 Excel中未填写员工ID
调动类型无效 调动类型[xxx]无效 调动类型不在字典中
部门ID不存在 部门ID 999 不存在 调动前/后部门ID在部门表中不存在
记录重复 该员工在2026-01-01的调动记录已存在 数据库中已存在相同的调动记录

导入状态查询:

使用返回的 taskId 查询导入进度和结果。

失败记录查询:

导入失败或部分成功时,可通过 taskId 获取详细的失败记录列表。


**Step 2: 提交**

```bash
cd .worktrees/staff-transfer-validation
git add doc/interface-doc/ccdi/staff-transfer.md
git commit -m "docs: 更新员工调动导入API文档

添加员工ID验证相关的错误情况说明"

Task 8: 最终验证和测试

Step 1: 编译项目

Run: cd .worktrees/staff-transfer-validation && mvn clean compile -q Expected: 编译成功,无错误

Step 2: 运行测试(如果有单元测试)

Run: cd .worktrees/staff-transfer-validation && mvn test -Dtest=*StaffTransferImport* -q Expected: 测试通过

Step 3: 代码审查检查清单

  • 所有新增方法都有完整的JavaDoc注释
  • 错误信息包含行号,便于用户定位
  • 使用ImportLogUtils记录详细的验证日志
  • 仅执行1次数据库查询批量验证所有员工ID
  • 失败记录正确保存到Redis
  • 与现有导入逻辑保持一致(跳过失败记录继续处理)
  • 代码风格符合项目规范
  • 无hardcode的字符串或数字

Step 4: 最终提交

cd .worktrees/staff-transfer-validation
git add -A
git commit -m "feat: 完成员工调动导入员工ID校验功能

功能实现:
- 批量预验证员工ID存在性1次数据库查询
- 不存在的员工ID记录错误并跳过
- 错误信息包含Excel行号
- 完整的日志记录

技术实现:
- 新增 batchValidateStaffIds() 方法
- 新增 isRowAlreadyFailed() 方法
- 修改 importTransferAsync() 主流程
- 添加 CcdiBaseStaffMapper 依赖

测试:
- 添加HTTP测试脚本
- 更新API文档

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"

实施后任务

合并到主分支

Step 1: 切换到dev_1分支

cd D:\ccdi\ccdi
git checkout dev_1
git pull origin dev_1

Step 2: 合并feature分支

git merge feat/staff-transfer-staff-id-validation --no-ff

Step 3: 推送到远程

git push origin dev_1

Step 4: 清理worktree

git worktree remove .worktrees/staff-transfer-validation
git branch -d feat/staff-transfer-staff-id-validation

附录

相关文档

  • 设计文档: doc/plans/2026-02-11-staff-transfer-import-staff-id-validation-design.md
  • 员工调动接口文档: doc/interface-doc/ccdi/staff-transfer.md
  • 导入服务代码: ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java

依赖服务

  • 数据库: ccdi_intermediary_blacklist
  • Redis: 用于存储导入状态和失败记录

测试数据准备

需要在 doc/test-data/ 目录下准备测试Excel文件

  • valid-staff-ids.xlsx: 包含有效员工ID的调动记录
  • partial-invalid-ids.xlsx: 包含部分无效员工ID的调动记录
  • all-invalid-ids.xlsx: 所有员工ID都无效的调动记录