# 员工调动导入员工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` 之后添加: ```java @Resource private CcdiBaseStaffMapper baseStaffMapper; ``` **Step 2: 验证编译** Run: `cd .worktrees/staff-transfer-validation && mvn clean compile -q` Expected: 编译成功,无错误 **Step 3: 提交** ```bash 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` 方法之后添加: ```java /** * 批量验证员工ID是否存在 * * @param excelList Excel数据列表 * @param taskId 任务ID * @param failures 失败记录列表(会追加验证失败的记录) * @return 存在的员工ID集合 */ private Set batchValidateStaffIds(List excelList, String taskId, List failures) { // 1. 提取并去重员工ID Set 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 wrapper = new LambdaQueryWrapper<>(); wrapper.select(CcdiBaseStaff::getStaffId) .in(CcdiBaseStaff::getStaffId, allStaffIds); List existingStaff = baseStaffMapper.selectList(wrapper); Set 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: 提交** ```bash 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: 编写检查方法** ```java /** * 检查某行数据是否已在失败列表中 * * @param excel Excel数据 * @param failures 失败记录列表 * @return true-已失败,false-未失败 */ private boolean isRowAlreadyFailed(CcdiStaffTransferExcel excel, List 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: 提交** ```bash 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行,将: ```java List newRecords = new ArrayList<>(); List failures = new ArrayList<>(); // 批量查询已存在的唯一键组合 ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的调动记录", excelList.size()); Set existingKeys = getExistingTransferKeys(excelList); ImportLogUtils.logBatchQueryComplete(log, taskId, "调动记录", existingKeys.size()); ``` 修改为: ```java List newRecords = new ArrayList<>(); List failures = new ArrayList<>(); // 批量验证员工ID是否存在 Set existingStaffIds = batchValidateStaffIds(excelList, taskId, failures); // 批量查询已存在的唯一键组合 ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的调动记录", excelList.size()); Set existingKeys = getExistingTransferKeys(excelList); ImportLogUtils.logBatchQueryComplete(log, taskId, "调动记录", existingKeys.size()); ``` **Step 2: 验证编译** Run: `cd .worktrees/staff-transfer-validation && mvn clean compile -q` Expected: 编译成功,无错误 **Step 3: 提交** ```bash 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行,将: ```java // 分类数据 for (int i = 0; i < excelList.size(); i++) { CcdiStaffTransferExcel excel = excelList.get(i); try { ``` 修改为: ```java // 分类数据 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: 提交** ```bash 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测试文件** ```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: 提交** ```bash 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文档** 在现有的员工调动导入接口文档中,添加错误情况说明: ```markdown ### 员工调动导入 **接口地址:** `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: 最终提交** ```bash 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 " ``` --- ## 实施后任务 ### 合并到主分支 **Step 1: 切换到dev_1分支** ```bash cd D:\ccdi\ccdi git checkout dev_1 git pull origin dev_1 ``` **Step 2: 合并feature分支** ```bash git merge feat/staff-transfer-staff-id-validation --no-ff ``` **Step 3: 推送到远程** ```bash git push origin dev_1 ``` **Step 4: 清理worktree** ```bash 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都无效的调动记录