feat: 实现批量验证员工ID方法
- 提取Excel中所有员工ID并去重 - 批量查询数据库中存在的员工ID - 标记不存在的员工ID为失败记录 - 记录详细的验证日志 - 新增ImportLogUtils工具类用于统一日志格式 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package com.ruoyi.ccdi.service.impl;
|
|||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.CcdiStaffTransfer;
|
||||||
import com.ruoyi.ccdi.domain.dto.CcdiStaffTransferAddDTO;
|
import com.ruoyi.ccdi.domain.dto.CcdiStaffTransferAddDTO;
|
||||||
import com.ruoyi.ccdi.domain.excel.CcdiStaffTransferExcel;
|
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.CcdiBaseStaffMapper;
|
||||||
import com.ruoyi.ccdi.mapper.CcdiStaffTransferMapper;
|
import com.ruoyi.ccdi.mapper.CcdiStaffTransferMapper;
|
||||||
import com.ruoyi.ccdi.service.ICcdiStaffTransferImportService;
|
import com.ruoyi.ccdi.service.ICcdiStaffTransferImportService;
|
||||||
|
import com.ruoyi.ccdi.utils.ImportLogUtils;
|
||||||
import com.ruoyi.common.core.domain.entity.SysDept;
|
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||||
import com.ruoyi.common.utils.DictUtils;
|
import com.ruoyi.common.utils.DictUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.system.mapper.SysDeptMapper;
|
import com.ruoyi.system.mapper.SysDeptMapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
@@ -33,6 +36,7 @@ import java.util.stream.Collectors;
|
|||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
* @date 2026-02-10
|
* @date 2026-02-10
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
public class CcdiStaffTransferImportServiceImpl implements ICcdiStaffTransferImportService {
|
public class CcdiStaffTransferImportServiceImpl implements ICcdiStaffTransferImportService {
|
||||||
@@ -314,4 +318,59 @@ public class CcdiStaffTransferImportServiceImpl implements ICcdiStaffTransferImp
|
|||||||
|
|
||||||
return JSON.parseArray(JSON.toJSONString(failuresObj), StaffTransferImportFailureVO.class);
|
return JSON.parseArray(JSON.toJSONString(failuresObj), StaffTransferImportFailureVO.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量验证员工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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<String, Long> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user