From 933626f24fbda50acc674324678a91a8e646d2bd Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Wed, 11 Feb 2026 11:16:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E5=91=98=E5=B7=A5ID=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 提取Excel中所有员工ID并去重 - 批量查询数据库中存在的员工ID - 标记不存在的员工ID为失败记录 - 记录详细的验证日志 - 新增ImportLogUtils工具类用于统一日志格式 Co-Authored-By: Claude Sonnet 4.5 --- .../CcdiStaffTransferImportServiceImpl.java | 59 +++++ .../com/ruoyi/ccdi/utils/ImportLogUtils.java | 248 ++++++++++++++++++ 2 files changed, 307 insertions(+) create mode 100644 ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/utils/ImportLogUtils.java 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 7d1e860..86da3ea 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 @@ -2,6 +2,7 @@ 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.CcdiStaffTransfer; import com.ruoyi.ccdi.domain.dto.CcdiStaffTransferAddDTO; import com.ruoyi.ccdi.domain.excel.CcdiStaffTransferExcel; @@ -11,11 +12,13 @@ import com.ruoyi.ccdi.domain.vo.StaffTransferImportFailureVO; import com.ruoyi.ccdi.mapper.CcdiBaseStaffMapper; import com.ruoyi.ccdi.mapper.CcdiStaffTransferMapper; import com.ruoyi.ccdi.service.ICcdiStaffTransferImportService; +import com.ruoyi.ccdi.utils.ImportLogUtils; import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.utils.DictUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.system.mapper.SysDeptMapper; import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Async; @@ -33,6 +36,7 @@ import java.util.stream.Collectors; * @author ruoyi * @date 2026-02-10 */ +@Slf4j @Service @EnableAsync public class CcdiStaffTransferImportServiceImpl implements ICcdiStaffTransferImportService { @@ -314,4 +318,59 @@ 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; + } } diff --git a/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/utils/ImportLogUtils.java b/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/utils/ImportLogUtils.java new file mode 100644 index 0000000..2c725b5 --- /dev/null +++ b/ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/utils/ImportLogUtils.java @@ -0,0 +1,248 @@ +package com.ruoyi.ccdi.utils; + +import org.slf4j.Logger; + +/** + * 导入日志工具类 + * 提供统一的日志格式和进度计算 + * + * @author ruoyi + * @date 2026-02-11 + */ +public class ImportLogUtils { + + /** + * 记录导入开始 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param moduleName 模块名称 + * @param totalCount 总数据量 + * @param userName 操作人 + */ + public static void logImportStart(Logger log, String taskId, String moduleName, + int totalCount, String userName) { + log.info("[任务ID: {}] 开始异步导入{},数据量: {}条,操作人: {}", + taskId, moduleName, totalCount, userName); + } + + /** + * 记录批量查询开始 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param queryDesc 查询描述 + * @param queryCount 查询数量 + */ + public static void logBatchQueryStart(Logger log, String taskId, String queryDesc, int queryCount) { + log.info("[任务ID: {}] 批量查询{},查询数量: {}个", taskId, queryDesc, queryCount); + } + + /** + * 记录批量查询完成 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param queryDesc 查询描述 + * @param existingCount 已存在数量 + */ + public static void logBatchQueryComplete(Logger log, String taskId, String queryDesc, int existingCount) { + log.info("[任务ID: {}] 查询完成,已存在{}条", taskId, queryDesc, existingCount); + } + + /** + * 记录进度(智能判断是否需要输出) + * 每100条或每10%输出一次 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param current 当前处理数 + * @param total 总数 + * @param success 成功数 + * @param failure 失败数 + */ + public static void logProgress(Logger log, String taskId, int current, int total, + int success, int failure) { + if (current <= 0) { + return; + } + + // 每100条或每10%输出一次进度 + boolean shouldLog = (current % 100 == 0) || + (current * 10 / total > (current - 1) * 10 / total) || + (current == total); + + if (shouldLog) { + int progress = current * 100 / total; + log.info("[任务ID: {}] 数据处理进度: {}/{} ({}%), 成功: {}条, 失败: {}条", + taskId, current, total, progress, success, failure); + } + } + + /** + * 记录数据验证失败 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param rowNum 行号 + * @param errorMsg 错误消息 + * @param keyData 关键数据(可为null) + */ + public static void logValidationError(Logger log, String taskId, int rowNum, + String errorMsg, String keyData) { + log.warn("[任务ID: {}] [第{}行] 数据验证失败: {}", taskId, rowNum, errorMsg); + if (keyData != null && !keyData.isEmpty()) { + log.warn("[任务ID: {}] 失败数据详情: {}", taskId, keyData); + } + } + + /** + * 记录批量操作开始 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param operation 操作描述 + * @param totalBatch 总批次数 + * @param batchSize 每批大小 + */ + public static void logBatchOperationStart(Logger log, String taskId, String operation, + int totalBatch, int batchSize) { + log.info("[任务ID: {}] 开始批量{},总批次: {}, 每批: {}条", + taskId, operation, totalBatch, batchSize); + } + + /** + * 记录单个批次操作 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param operation 操作描述 + * @param batchNum 当前批次号 + * @param totalBatch 总批次数 + * @param batchSize 本批数量 + */ + public static void logBatchOperation(Logger log, String taskId, String operation, + int batchNum, int totalBatch, int batchSize) { + log.info("[任务ID: {}] 执行批次 {}/{}, 本批数量: {}条", + taskId, batchNum, totalBatch, batchSize); + } + + /** + * 记录单个批次完成 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param operation 操作描述 + * @param batchNum 当前批次号 + * @param totalBatch 总批次数 + * @param success 成功数量 + */ + public static void logBatchComplete(Logger log, String taskId, String operation, + int batchNum, int totalBatch, int success) { + log.info("[任务ID: {}] 批次 {}/{} {}完成,成功: {}条", + taskId, batchNum, totalBatch, operation, success); + } + + /** + * 记录Redis缓存操作 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param operation 操作描述(如"保存失败记录") + * @param count 数量 + */ + public static void logRedisOperation(Logger log, String taskId, String operation, int count) { + log.debug("[任务ID: {}] {}到Redis,数量: {}条", taskId, operation, count); + } + + /** + * 记录Redis缓存异常 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param operation 操作描述 + * @param e 异常 + */ + public static void logRedisError(Logger log, String taskId, String operation, Exception e) { + log.error("[任务ID: {}] {}到Redis失败,不影响导入结果", taskId, operation, e); + } + + /** + * 记录导入完成 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param moduleName 模块名称 + * @param total 总数 + * @param success 成功数 + * @param failure 失败数 + * @param duration 耗时(毫秒) + */ + public static void logImportComplete(Logger log, String taskId, String moduleName, + int total, int success, int failure, long duration) { + log.info("[任务ID: {}] {}导入完成!总数: {}条, 成功: {}条, 失败: {}条, 耗时: {}ms", + taskId, moduleName, total, success, failure, duration); + + // 如果有失败,记录失败汇总 + if (failure > 0) { + log.warn("[任务ID: {}] 导入完成,但有{}条数据失败,请查看失败记录详情", taskId, failure); + } + } + + /** + * 记录异常 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param errorMsg 错误描述 + * @param e 异常 + */ + public static void logException(Logger log, String taskId, String errorMsg, Exception e) { + log.error("[任务ID: {}] {}", taskId, errorMsg, e); + } + + /** + * 记录事务回滚 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param processed 已处理数量 + * @param total 总数量 + * @param success 成功数量 + * @param failure 失败数量 + * @param e 异常 + */ + public static void logTransactionRollback(Logger log, String taskId, int processed, + int total, int success, int failure, Exception e) { + log.error("[任务ID: {}] 导入失败,事务已回滚。已处理: {}/{}条", taskId, processed, total, e); + log.error("[任务ID: {}] 回滚前统计 - 新增: {}条, 失败: {}条", taskId, success, failure); + } + + /** + * 记录唯一性冲突 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param rowNum 行号 + * @param conflictDesc 冲突描述 + */ + public static void logUniqueConflict(Logger log, String taskId, int rowNum, String conflictDesc) { + log.warn("[任务ID: {}] [第{}行] {}", taskId, rowNum, conflictDesc); + } + + /** + * 记录失败原因统计 + * + * @param log 日志记录器 + * @param taskId 任务ID + * @param errorStats 错误统计Map + */ + public static void logErrorStatistics(Logger log, String taskId, java.util.Map errorStats) { + if (errorStats != null && !errorStats.isEmpty()) { + String statsStr = errorStats.entrySet().stream() + .map(entry -> entry.getKey() + "=" + entry.getValue() + "条") + .collect(java.util.stream.Collectors.joining(", ")); + log.warn("[任务ID: {}] 失败原因统计: {}", taskId, statsStr); + } + } +}