diff --git a/doc/api-docs/api/员工亲属关系导入API文档.md b/doc/api-docs/api/员工亲属关系导入API文档.md new file mode 100644 index 0000000..2e56b2e --- /dev/null +++ b/doc/api-docs/api/员工亲属关系导入API文档.md @@ -0,0 +1,195 @@ +# 员工亲属关系导入 API 文档 + +## 概述 + +员工亲属关系导入模块提供员工亲属关系的批量导入功能。 + +**基础路径**: `/ccdi/staffFmyRelation` + +**权限标识前缀**: `ccdi:staffFmyRelation` + +**数据表**: `ccdi_cust_fmy_relation` + +**关联表**: +- `ccdi_base_staff` - 员工基础信息表(通过id_card关联) + +--- + +## API 接口 + +### 1. 异步导入员工亲属关系 + +**接口地址**: `POST /ccdi/staffFmyRelation/importData` + +**权限要求**: `ccdi:staffFmyRelation:import` + +**请求参数**: FormData + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| file | File | 是 | Excel文件 | +| updateSupport | Boolean | 否 | 是否更新已存在的记录(默认false) | + +**响应示例**: +```json +{ + "code": 200, + "msg": "导入任务已提交,正在后台处理", + "data": { + "taskId": "abc123-def456-ghi789", + "status": "PROCESSING", + "message": "导入任务已提交,正在后台处理" + } +} +``` + +**导入流程**: +1. 上传Excel文件 +2. 后台立即返回taskId +3. 使用taskId轮询查询导入状态 +4. 导入完成后查看失败记录(如有) + +**导入验证规则**: + +导入时会验证以下字段: + +| 字段名 | 验证规则 | 错误提示 | +|--------|---------|---------| +| 员工身份证号 | 必须在员工信息表(ccdi_base_staff)中存在 | "第N行: 身份证号[XXX]不存在于员工信息表中,请先添加员工信息" | +| 关系类型 | 不能为空,必须在字典中存在 | "第N行: 关系类型不能为空" | +| 关系人姓名 | 不能为空 | "第N行: 关系人姓名不能为空" | +| 关系人证件类型 | 不能为空 | "第N行: 关系人证件类型不能为空" | +| 关系人证件号码 | 不能为空 | "第N行: 关系人证件号码不能为空" | +| 手机号码1 | 如果填写,必须为有效手机号 | "第N行: 手机号码1格式不正确" | +| 手机号码2 | 如果填写,必须为有效手机号 | "第N行: 手机号码2格式不正确" | +| 性别 | 如果填写,必须是"男"、"女"、"其他"或"M"、"F"、"O" | "第N行: 性别只能是:男、女、其他 或 M、F、O" | + +**性能优化**: +- 采用批量预验证方式,仅1次数据库查询身份证号存在性 +- 批量查询已存在的身份证号+关系人证件号码组合,避免重复导入 + +--- + +### 2. 查询导入状态 + +**接口地址**: `GET /ccdi/staffFmyRelation/importStatus/{taskId}` + +**权限要求**: `ccdi:staffFmyRelation:import` + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| taskId | String | 是 | 导入任务ID | + +**响应示例**: +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "taskId": "abc123-def456-ghi789", + "status": "COMPLETED", + "total": 100, + "successCount": 95, + "failureCount": 5, + "message": "导入完成" + } +} +``` + +**状态说明**: + +| 状态 | 说明 | +|------|------| +| PENDING | 等待处理 | +| PROCESSING | 处理中 | +| SUCCESS | 全部成功 | +| PARTIAL_SUCCESS | 部分成功 | +| FAILED | 处理失败 | + +--- + +### 3. 查询导入失败记录 + +**接口地址**: `GET /ccdi/staffFmyRelation/importFailures/{taskId}` + +**权限要求**: `ccdi:staffFmyRelation:import` + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| taskId | String | 是 | 导入任务ID | + +**响应示例**: +```json +{ + "code": 200, + "msg": "查询成功", + "data": [ + { + "personId": "999999999999999999", + "relationType": "父亲", + "relationName": "张三", + "relationCertType": "身份证", + "relationCertNo": "110101195501017890", + "errorMessage": "第2行: 身份证号[999999999999999999]不存在于员工信息表中,请先添加员工信息", + "rowNumber": 2 + } + ] +} +``` + +**失败记录字段说明**: + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| personId | String | 员工身份证号 | +| relationType | String | 关系类型 | +| relationName | String | 关系人姓名 | +| relationCertType | String | 关系人证件类型 | +| relationCertNo | String | 关系人证件号码 | +| errorMessage | String | 错误信息 | +| rowNumber | Integer | Excel行号 | + +--- + +## Excel 模板字段说明 + +| 字段名 | 是否必填 | 说明 | +|--------|---------|------| +| 员工身份证号 | 是 | 必须在员工信息表中存在 | +| 关系类型 | 是 | 下拉选择字典 | +| 关系人姓名 | 是 | 不能为空 | +| 性别 | 否 | 下拉选择字典 | +| 出生日期 | 否 | 日期格式 | +| 关系人证件类型 | 是 | 下拉选择字典 | +| 关系人证件号码 | 是 | 不能为空 | +| 手机号码1 | 否 | 手机号格式 | +| 手机号码2 | 否 | 手机号格式 | +| 微信名称1-3 | 否 | 自由输入 | +| 详细联系地址 | 否 | 自由输入 | +| 关系详细描述 | 否 | 自由输入 | +| 生效日期 | 否 | 日期格式 | +| 失效日期 | 否 | 日期格式 | +| 备注 | 否 | 自由输入 | + +--- + +## 错误码说明 + +| 错误码 | 说明 | +|--------|------| +| 200 | 操作成功 | +| 401 | 未授权 | +| 403 | 无权限 | +| 500 | 服务器错误 | + +--- + +## 更新日志 + +**2026-02-11**: +- 新增员工身份证号存在性校验 +- 优化导入性能,采用批量预验证方式 diff --git a/doc/api-docs/api/员工实体关系导入API文档.md b/doc/api-docs/api/员工实体关系导入API文档.md new file mode 100644 index 0000000..6306b0d --- /dev/null +++ b/doc/api-docs/api/员工实体关系导入API文档.md @@ -0,0 +1,178 @@ +# 员工实体关系导入 API 文档 + +## 概述 + +员工实体关系导入模块提供员工与企业实体关系的批量导入功能。 + +**基础路径**: `/ccdi/staffEnterpriseRelation` + +**权限标识前缀**: `ccdi:staffEnterpriseRelation` + +**数据表**: `ccdi_cust_enterprise_relation` + +**关联表**: +- `ccdi_base_staff` - 员工基础信息表(通过id_card关联) + +--- + +## API 接口 + +### 1. 异步导入员工实体关系 + +**接口地址**: `POST /ccdi/staffEnterpriseRelation/importData` + +**权限要求**: `ccdi:staffEnterpriseRelation:import` + +**请求参数**: FormData + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| file | File | 是 | Excel文件 | +| updateSupport | Boolean | 否 | 是否更新已存在的记录(默认false) | + +**响应示例**: +```json +{ + "code": 200, + "msg": "导入任务已提交,正在后台处理", + "data": { + "taskId": "abc123-def456-ghi789", + "status": "PROCESSING", + "message": "导入任务已提交,正在后台处理" + } +} +``` + +**导入流程**: +1. 上传Excel文件 +2. 后台立即返回taskId +3. 使用taskId轮询查询导入状态 +4. 导入完成后查看失败记录(如有) + +**导入验证规则**: + +导入时会验证以下字段: + +| 字段名 | 验证规则 | 错误提示 | +|--------|---------|---------| +| 身份证号 | 必须在员工信息表(ccdi_base_staff)中存在 | "第N行: 身份证号[XXX]不存在于员工信息表中,请先添加员工信息" | +| 统一社会信用代码 | 必须为18位有效统一社会信用代码 | "第N行: 统一社会信用代码格式不正确" | +| 企业名称 | 不能为空,长度不超过200字符 | "第N行: 企业名称不能为空" 或 "企业名称长度不能超过200个字符" | + +**性能优化**: +- 采用批量预验证方式,仅1次数据库查询身份证号存在性 +- 批量查询已存在的身份证号+统一社会信用代码组合,避免重复导入 + +--- + +### 2. 查询导入状态 + +**接口地址**: `GET /ccdi/staffEnterpriseRelation/importStatus/{taskId}` + +**权限要求**: `ccdi:staffEnterpriseRelation:import` + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| taskId | String | 是 | 导入任务ID | + +**响应示例**: +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "taskId": "abc123-def456-ghi789", + "status": "COMPLETED", + "total": 100, + "successCount": 95, + "failureCount": 5, + "message": "导入完成" + } +} +``` + +**状态说明**: + +| 状态 | 说明 | +|------|------| +| PENDING | 等待处理 | +| PROCESSING | 处理中 | +| SUCCESS | 全部成功 | +| PARTIAL_SUCCESS | 部分成功 | +| FAILED | 处理失败 | + +--- + +### 3. 查询导入失败记录 + +**接口地址**: `GET /ccdi/staffEnterpriseRelation/importFailures/{taskId}` + +**权限要求**: `ccdi:staffEnterpriseRelation:import` + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| taskId | String | 是 | 导入任务ID | + +**响应示例**: +```json +{ + "code": 200, + "msg": "查询成功", + "data": [ + { + "personId": "999999999999999999", + "socialCreditCode": "91110000987654321X", + "enterpriseName": "测试企业", + "relationPersonPost": "总经理", + "errorMessage": "第2行: 身份证号[999999999999999999]不存在于员工信息表中,请先添加员工信息", + "rowNumber": 2 + } + ] +} +``` + +**失败记录字段说明**: + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| personId | String | 身份证号 | +| socialCreditCode | String | 统一社会信用代码 | +| enterpriseName | String | 企业名称 | +| relationPersonPost | String | 关联人在企业的职务 | +| errorMessage | String | 错误信息 | +| rowNumber | Integer | Excel行号 | + +--- + +## Excel 模板字段说明 + +| 字段名 | 是否必填 | 说明 | +|--------|---------|------| +| 身份证号 | 是 | 必须在员工信息表中存在 | +| 统一社会信用代码 | 是 | 18位有效统一社会信用代码 | +| 企业名称 | 是 | 长度不超过200字符 | +| 关联人在企业的职务 | 否 | 长度不超过100字符 | +| 补充说明 | 否 | 备注信息 | + +--- + +## 错误码说明 + +| 错误码 | 说明 | +|--------|------| +| 200 | 操作成功 | +| 401 | 未授权 | +| 403 | 无权限 | +| 500 | 服务器错误 | + +--- + +## 更新日志 + +**2026-02-11**: +- 新增员工身份证号存在性校验 +- 优化导入性能,采用批量预验证方式 diff --git a/doc/api-docs/api/员工调动记录管理API文档.md b/doc/api-docs/api/员工调动记录管理API文档.md index 8f1673d..a14c5a7 100644 --- a/doc/api-docs/api/员工调动记录管理API文档.md +++ b/doc/api-docs/api/员工调动记录管理API文档.md @@ -327,6 +327,21 @@ 3. 使用taskId轮询查询导入状态 4. 导入完成后查看失败记录(如有) +**导入验证规则**: + +导入时会验证以下字段: + +| 字段名 | 验证规则 | 错误提示 | +|--------|---------|---------| +| 员工ID | 必须在员工信息表(ccdi_base_staff)中存在 | "第N行: 员工ID XXX 不存在" | +| 调动前部门ID | 必须在部门表(sys_dept)中存在 | "第N行: 调动前部门ID XXX 不存在" | +| 调动后部门ID | 必须在部门表(sys_dept)中存在 | "第N行: 调动后部门ID XXX 不存在" | +| 调动日期 | 必须符合yyyy-MM-dd格式 | "第N行: 调动日期格式不正确" | + +**性能优化**: +- 采用批量预验证方式,仅1次数据库查询员工ID存在性 +- 从2次遍历优化为1次遍历,提升导入性能 + --- ### 9. 查询导入状态 diff --git a/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java b/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java index a2f06f8..070e204 100644 --- a/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java +++ b/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java @@ -1,12 +1,15 @@ package com.ruoyi.ccdi.service.impl; import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.ccdi.domain.CcdiBaseStaff; import com.ruoyi.ccdi.domain.CcdiStaffEnterpriseRelation; import com.ruoyi.ccdi.domain.dto.CcdiStaffEnterpriseRelationAddDTO; import com.ruoyi.ccdi.domain.excel.CcdiStaffEnterpriseRelationExcel; import com.ruoyi.ccdi.domain.vo.ImportResult; import com.ruoyi.ccdi.domain.vo.ImportStatusVO; import com.ruoyi.ccdi.domain.vo.StaffEnterpriseRelationImportFailureVO; +import com.ruoyi.ccdi.mapper.CcdiBaseStaffMapper; import com.ruoyi.ccdi.mapper.CcdiStaffEnterpriseRelationMapper; import com.ruoyi.ccdi.service.ICcdiStaffEnterpriseRelationImportService; import com.ruoyi.ccdi.utils.ImportLogUtils; @@ -43,6 +46,9 @@ public class CcdiStaffEnterpriseRelationImportServiceImpl implements ICcdiStaffE @Resource private RedisTemplate redisTemplate; + @Resource + private CcdiBaseStaffMapper baseStaffMapper; + @Override @Async @Transactional @@ -55,6 +61,28 @@ public class CcdiStaffEnterpriseRelationImportServiceImpl implements ICcdiStaffE List newRecords = new ArrayList<>(); List failures = new ArrayList<>(); + // 批量验证员工身份证号是否存在 + Set excelPersonIds = excelList.stream() + .map(CcdiStaffEnterpriseRelationExcel::getPersonId) + .filter(StringUtils::isNotEmpty) + .collect(Collectors.toSet()); + + Set existingPersonIds = new HashSet<>(); + if (!excelPersonIds.isEmpty()) { + ImportLogUtils.logBatchQueryStart(log, taskId, "员工身份证号", excelPersonIds.size()); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.select(CcdiBaseStaff::getIdCard) + .in(CcdiBaseStaff::getIdCard, excelPersonIds); + + List existingStaff = baseStaffMapper.selectList(wrapper); + existingPersonIds = existingStaff.stream() + .map(CcdiBaseStaff::getIdCard) + .collect(Collectors.toSet()); + + ImportLogUtils.logBatchQueryComplete(log, taskId, "员工身份证号", existingPersonIds.size()); + } + // 批量查询已存在的person_id + social_credit_code组合 ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的员工企业关系组合", excelList.size()); Set existingCombinations = getExistingCombinations(excelList); @@ -75,6 +103,13 @@ public class CcdiStaffEnterpriseRelationImportServiceImpl implements ICcdiStaffE // 验证数据 validateRelationData(addDTO); + // 身份证号存在性检查(在基本验证之后) + if (!existingPersonIds.contains(excel.getPersonId())) { + throw new RuntimeException(String.format( + "第%d行: 身份证号[%s]不存在于员工信息表中,请先添加员工信息", + i + 1, excel.getPersonId())); + } + String combination = excel.getPersonId() + "|" + excel.getSocialCreditCode(); CcdiStaffEnterpriseRelation relation = new CcdiStaffEnterpriseRelation(); diff --git a/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffFmyRelationImportServiceImpl.java b/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffFmyRelationImportServiceImpl.java index 2e1eb40..aff8d2a 100644 --- a/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffFmyRelationImportServiceImpl.java +++ b/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffFmyRelationImportServiceImpl.java @@ -1,6 +1,8 @@ package com.ruoyi.ccdi.service.impl; import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.ccdi.domain.CcdiBaseStaff; import com.ruoyi.ccdi.domain.CcdiStaffFmyRelation; import com.ruoyi.ccdi.domain.dto.CcdiStaffFmyRelationAddDTO; import com.ruoyi.ccdi.domain.excel.CcdiStaffFmyRelationExcel; @@ -8,6 +10,7 @@ import com.ruoyi.ccdi.domain.vo.ImportResult; import com.ruoyi.ccdi.domain.vo.ImportStatusVO; import com.ruoyi.ccdi.domain.vo.StaffFmyRelationImportFailureVO; import com.ruoyi.ccdi.enums.GenderEnum; +import com.ruoyi.ccdi.mapper.CcdiBaseStaffMapper; import com.ruoyi.ccdi.mapper.CcdiStaffFmyRelationMapper; import com.ruoyi.ccdi.service.ICcdiStaffFmyRelationImportService; import com.ruoyi.ccdi.utils.ImportLogUtils; @@ -24,6 +27,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * 员工亲属关系异步导入服务层处理 @@ -43,6 +47,9 @@ public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelat @Resource private RedisTemplate redisTemplate; + @Resource + private CcdiBaseStaffMapper baseStaffMapper; + @Override @Async @Transactional @@ -55,14 +62,32 @@ public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelat List newRecords = new ArrayList<>(); List failures = new ArrayList<>(); + // 批量验证员工身份证号是否存在 + Set excelPersonIds = excelList.stream() + .map(CcdiStaffFmyRelationExcel::getPersonId) + .filter(StringUtils::isNotEmpty) + .collect(Collectors.toSet()); + + Set existingPersonIds = new HashSet<>(); + if (!excelPersonIds.isEmpty()) { + ImportLogUtils.logBatchQueryStart(log, taskId, "员工身份证号", excelPersonIds.size()); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.select(CcdiBaseStaff::getIdCard) + .in(CcdiBaseStaff::getIdCard, excelPersonIds); + + List existingStaff = baseStaffMapper.selectList(wrapper); + existingPersonIds = existingStaff.stream() + .map(CcdiBaseStaff::getIdCard) + .collect(Collectors.toSet()); + + ImportLogUtils.logBatchQueryComplete(log, taskId, "员工身份证号", existingPersonIds.size()); + } + // ========== 第一步:批量唯一性校验 ========== - // 1. 提取Excel中所有的personId和relationCertNo - Set excelPersonIds = new HashSet<>(); + // 1. 提取Excel中所有的relationCertNo(personId已在前面提取) Set excelRelationCertNos = new HashSet<>(); for (CcdiStaffFmyRelationExcel excel : excelList) { - if (StringUtils.isNotEmpty(excel.getPersonId())) { - excelPersonIds.add(excel.getPersonId()); - } if (StringUtils.isNotEmpty(excel.getRelationCertNo())) { excelRelationCertNos.add(excel.getRelationCertNo()); } @@ -70,7 +95,7 @@ public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelat // 2. 批量查询数据库中已存在的记录 Set existingKeys = new HashSet<>(); - if (!excelPersonIds.isEmpty() && !excelRelationCertNos.isEmpty()) { + if (!excelRelationCertNos.isEmpty()) { ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的亲属关系", excelList.size()); List existingRecords = relationMapper.selectExistingRelations( new ArrayList<>(excelPersonIds), @@ -100,6 +125,13 @@ public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelat // 验证数据 validateRelationData(addDTO); + // 身份证号存在性检查(在基本验证之后) + if (!existingPersonIds.contains(excel.getPersonId())) { + throw new RuntimeException(String.format( + "第%d行: 身份证号[%s]不存在于员工信息表中,请先添加员工信息", + i + 1, excel.getPersonId())); + } + // 生成唯一键 String uniqueKey = excel.getPersonId() + "|" + excel.getRelationCertNo(); diff --git a/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java b/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java index fb239a8..f91885b 100644 --- a/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java +++ b/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiStaffTransferImportServiceImpl.java @@ -65,8 +65,27 @@ public class CcdiStaffTransferImportServiceImpl implements ICcdiStaffTransferImp List newRecords = new ArrayList<>(); List failures = new ArrayList<>(); - // 批量验证员工ID是否存在 - Set existingStaffIds = batchValidateStaffIds(excelList, taskId, failures); + // 批量查询员工ID存在性 + Set excelStaffIds = excelList.stream() + .map(CcdiStaffTransferExcel::getStaffId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + Set existingStaffIds = new HashSet<>(); + if (!excelStaffIds.isEmpty()) { + ImportLogUtils.logBatchQueryStart(log, taskId, "员工ID", excelStaffIds.size()); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.select(CcdiBaseStaff::getStaffId) + .in(CcdiBaseStaff::getStaffId, excelStaffIds); + + List existingStaff = baseStaffMapper.selectList(wrapper); + existingStaffIds = existingStaff.stream() + .map(CcdiBaseStaff::getStaffId) + .collect(Collectors.toSet()); + + ImportLogUtils.logBatchQueryComplete(log, taskId, "员工ID", existingStaffIds.size()); + } // 批量查询已存在的唯一键组合 ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的调动记录", excelList.size()); @@ -80,12 +99,14 @@ public class CcdiStaffTransferImportServiceImpl implements ICcdiStaffTransferImp for (int i = 0; i < excelList.size(); i++) { CcdiStaffTransferExcel excel = excelList.get(i); - // 跳过已在预验证阶段失败的记录 - if (isRowAlreadyFailed(excel, failures)) { - continue; - } - try { + // 员工ID存在性检查 + if (excel.getStaffId() != null && !existingStaffIds.contains(excel.getStaffId())) { + throw new RuntimeException(String.format( + "第%d行: 员工ID %s 不存在", + i + 1, excel.getStaffId())); + } + // 转换为AddDTO进行验证 CcdiStaffTransferAddDTO addDTO = new CcdiStaffTransferAddDTO(); BeanUtils.copyProperties(excel, addDTO); @@ -356,75 +377,4 @@ public class CcdiStaffTransferImportServiceImpl implements ICcdiStaffTransferImp return JSON.parseArray(JSON.toJSONString(failuresObj), StaffTransferImportFailureVO.class); } - - /** - * 批量验证员工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; - } - - /** - * 检查某行数据是否已在失败列表中 - * - * @param excel Excel数据 - * @param failures 失败记录列表 - * @return true-已失败,false-未失败 - */ - private boolean isRowAlreadyFailed(CcdiStaffTransferExcel excel, - List failures) { - return failures.stream() - .anyMatch(f -> Objects.equals(f.getStaffId(), excel.getStaffId()) - && Objects.equals(f.getTransferDate(), excel.getTransferDate()) - && Objects.equals(f.getDeptIdBefore(), excel.getDeptIdBefore()) - && Objects.equals(f.getDeptIdAfter(), excel.getDeptIdAfter())); - } }