package com.xiaopeng.maintenance.schedule; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.xiaopeng.api.feignClient.account.XpOperationUserClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.maintenance.XpClockIn; import com.xiaopeng.api.model.maintenance.XpSchedulingDetail; import com.xiaopeng.api.model.maintenance.XpShift; import com.xiaopeng.api.model.vehicleConvoy.XpFleetOperations; import com.xiaopeng.common.core.utils.DateUtils; import com.xiaopeng.maintenance.mapper.XpClockInMapper; import com.xiaopeng.maintenance.mapper.XpTaskMapper; import com.xiaopeng.maintenance.mapper.XpTaskRuleMapper; import com.xiaopeng.maintenance.mapper.XpTaskRuleRoleMapper; import com.xiaopeng.maintenance.service.IXpClockInService; import com.xiaopeng.maintenance.service.IXpSchedulingDetailService; import com.xiaopeng.maintenance.service.IXpShiftService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * 打卡定时任务 *

* 判断是否迟到或者缺卡 *

* * @author xiaochen * @since 2026-02-27 */ @Component public class ClockInScheduleTask { private static final Logger log = LoggerFactory.getLogger(ClockInScheduleTask.class); @Autowired private IXpClockInService xpClockInService; @Autowired private IXpSchedulingDetailService xpSchedulingDetailService; @Autowired private XpFleetOperationsClient xpFleetOperationsClient; @Autowired private IXpShiftService xpShiftService; /** * 每5分钟执行一次 */ @Scheduled(fixedRate = 60*1000) public void clockInScheduleTask() { log.info("========== 开始执行打卡检查 =========="); try { // 查询下班卡在今天的 List clockOutPlanList = xpSchedulingDetailService.list(Wrappers.lambdaQuery(XpSchedulingDetail.class) .likeRight(XpSchedulingDetail::getPlanTime, LocalDate.now())); // 下班卡在昨天的 List clockOutPlanListLast = xpSchedulingDetailService.list(Wrappers.lambdaQuery(XpSchedulingDetail.class) .likeRight(XpSchedulingDetail::getPlanTime, LocalDate.now().minusDays(1))); if(!CollectionUtils.isEmpty(clockOutPlanList)){ List fleetIds = clockOutPlanList.stream().map(XpSchedulingDetail::getFleetId).distinct().collect(Collectors.toList()); // 查询所有车队的绑定人员 List xpFleetOperations = xpFleetOperationsClient.getOperationsIdsByFleetIds(fleetIds).getData(); // 查询班次 List shiftIds = clockOutPlanList.stream().map(XpSchedulingDetail::getShiftId).collect(Collectors.toList()); List xpShifts = xpShiftService.list(Wrappers.lambdaQuery(XpShift.class) .in(XpShift::getId, shiftIds)); log.info("========== 查询到{}个车队 ==========", xpFleetOperations.size()); if(!CollectionUtils.isEmpty(xpShifts) && !CollectionUtils.isEmpty(xpFleetOperations)){ // 处理上班卡缺卡情况 // 过滤当前时间在班次下班打卡时间之后的数据 今日已过下班的班次 List shiftList = xpShifts.stream().filter(xpShift -> DateUtils.stringToLocalDateTime(LocalDate.now() + " " + xpShift.getEndTime() + ":00").isBefore(LocalDateTime.now())).collect(Collectors.toList()); List ids = shiftList.stream().map(XpShift::getId).collect(Collectors.toList()); if(!CollectionUtils.isEmpty(ids)){ log.info("========== 进入上班过期的班次 =========="); // 获取过期当前班次的排班明细 覆盖 List clockOutPlans = clockOutPlanList.stream().filter(xpSchedulingDetail -> ids.contains(xpSchedulingDetail.getShiftId())).collect(Collectors.toList()); List fleetIdIn = clockOutPlans.stream().map(XpSchedulingDetail::getFleetId).collect(Collectors.toList()); List operationsUserIds = xpFleetOperations.stream().filter(x -> fleetIdIn.contains(x.getFleetId())) .map(XpFleetOperations::getOperationsUserId).distinct().collect(Collectors.toList()); if(!CollectionUtils.isEmpty(operationsUserIds)){ int createdCount = 0; for (Long operationsUserId : operationsUserIds) { List operations = xpFleetOperations.stream() .filter(x -> x.getOperationsUserId().equals(operationsUserId)) .collect(Collectors.toList()); if(CollectionUtils.isEmpty(operations)){ continue; } Long fleetId = operations.get(0).getFleetId(); List clockOutPlanExpiredList = clockOutPlanList.stream() .filter(xpSchedulingDetail -> xpSchedulingDetail.getFleetId().equals(fleetId) && ids.contains(xpSchedulingDetail.getShiftId())) .collect(Collectors.toList()); if(CollectionUtils.isEmpty(clockOutPlanExpiredList)){ continue; } Long shiftId = clockOutPlanExpiredList.get(0).getShiftId(); boolean created = xpClockInService.createMissingClockInIfAbsent(operationsUserId, shiftId, LocalDate.now()); if (created) { createdCount++; } } log.info("========== 新增上班缺卡记录{}条 ==========", createdCount); } } // 处理下班卡缺卡情况 // 过滤当前时间在班次下班打卡时间之后5小时的数据 今日已过下班打卡时间5小时后的班次 List shiftListFive = xpShifts.stream().filter(xpShift -> DateUtils.stringToLocalDateTime(LocalDate.now() + " " + xpShift.getEndTime() + ":00").plusMinutes(1).isBefore(LocalDateTime.now())).collect(Collectors.toList()); // DateUtils.stringToLocalDateTime(LocalDate.now() + " " + xpShift.getEndTime() + ":00").plusHours(5).isBefore(LocalDateTime.now())).collect(Collectors.toList()); List fiveIds = shiftListFive.stream().map(XpShift::getId).collect(Collectors.toList()); if(!CollectionUtils.isEmpty(fiveIds)){ log.info("========== 进入下班过期的班次 =========="); List clockOutPlans = clockOutPlanList.stream().filter(xpSchedulingDetail -> fiveIds.contains(xpSchedulingDetail.getShiftId())).collect(Collectors.toList()); List fleetIdOut = clockOutPlans.stream().map(XpSchedulingDetail::getFleetId).collect(Collectors.toList()); // 查询所有车队的绑定人员 List xpFleetOperationsList = xpFleetOperationsClient.getOperationsIdsByFleetIds(fleetIdOut).getData(); // 获取过期当前班次的排班明细 覆盖 List fiveClockOutPlans = clockOutPlanList.stream().filter(xpSchedulingDetail -> fiveIds.contains(xpSchedulingDetail.getShiftId())).collect(Collectors.toList()); List fiveFleetIdIn = fiveClockOutPlans.stream().map(XpSchedulingDetail::getFleetId).collect(Collectors.toList()); // 查询所有车队的绑定人员 if(!CollectionUtils.isEmpty(xpFleetOperationsList)){ List operationsUserIdsFive = xpFleetOperationsList.stream().filter(x -> fiveFleetIdIn.contains(x.getFleetId())) .map(XpFleetOperations::getOperationsUserId).distinct().collect(Collectors.toList()); // 批量查询人员打卡信息 List clockInListFive = xpClockInService.list(Wrappers.lambdaQuery(XpClockIn.class) .in(XpClockIn::getAppUserId, operationsUserIdsFive).in(XpClockIn::getShiftId, fiveIds) .isNull(XpClockIn::getClockOutTime)); log.info("========== 获取到{}个人员没有下班打卡信息 ==========", clockInListFive.size()); if(!CollectionUtils.isEmpty(clockInListFive)){ for (XpClockIn xpClockIn : clockInListFive) { xpClockIn.setClockOutTime(LocalDateTime.now()); xpClockIn.setClockOutResult(3); } xpClockInService.updateBatchById(clockInListFive); log.info("========== 修改为下班缺卡 =========="); } } } } } if (!CollectionUtils.isEmpty(clockOutPlanListLast)){ List fleetIds = clockOutPlanListLast.stream().map(XpSchedulingDetail::getFleetId).distinct().collect(Collectors.toList()); // 查询所有车队的绑定人员 List xpFleetOperations = xpFleetOperationsClient.getOperationsIdsByFleetIds(fleetIds).getData(); // 查询班次 List shiftIds = clockOutPlanListLast.stream().map(XpSchedulingDetail::getShiftId).collect(Collectors.toList()); List xpShifts = xpShiftService.list(Wrappers.lambdaQuery(XpShift.class) .in(XpShift::getId, shiftIds)); log.info("========== 查询到{}个车队 ==========", xpFleetOperations.size()); // 处理下班卡缺卡情况 // 过滤当前时间在班次下班打卡时间之后5小时的数据 今日已过下班打卡时间5小时后的班次 List shiftListFive = xpShifts.stream().filter(xpShift -> DateUtils.stringToLocalDateTime(LocalDate.now().minusDays(1) + " " + xpShift.getEndTime() + ":00").plusMinutes(1).isBefore(LocalDateTime.now())).collect(Collectors.toList()); // DateUtils.stringToLocalDateTime(LocalDate.now().minusDays(1) + " " + xpShift.getEndTime() + ":00").plusHours(5).isBefore(LocalDateTime.now())).collect(Collectors.toList()); List fiveIds = shiftListFive.stream().map(XpShift::getId).collect(Collectors.toList()); if(!CollectionUtils.isEmpty(fiveIds)){ log.info("========== 进入下班过期的班次 =========="); // 获取过期当前班次的排班明细 覆盖 List fiveClockOutPlans = clockOutPlanListLast.stream().filter(xpSchedulingDetail -> fiveIds.contains(xpSchedulingDetail.getShiftId())).collect(Collectors.toList()); List fiveFleetIdIn = fiveClockOutPlans.stream().map(XpSchedulingDetail::getFleetId).collect(Collectors.toList()); // 查询所有车队的绑定人员 if(!CollectionUtils.isEmpty(xpFleetOperations)){ List operationsUserIdsFive = xpFleetOperations.stream().filter(x -> fiveFleetIdIn.contains(x.getFleetId())) .map(XpFleetOperations::getOperationsUserId).distinct().collect(Collectors.toList()); // 批量查询人员打卡信息 List clockInListFive = xpClockInService.list(Wrappers.lambdaQuery(XpClockIn.class) .in(XpClockIn::getAppUserId, operationsUserIdsFive) .in(XpClockIn::getShiftId, fiveIds) .likeRight(XpClockIn::getClockInTime,LocalDate.now().minusDays(1)) .isNull(XpClockIn::getClockOutTime)); log.info("========== 获取到{}个人员没有下班打卡信息 ==========", clockInListFive.size()); if(!CollectionUtils.isEmpty(clockInListFive)){ for (XpClockIn xpClockIn : clockInListFive) { xpClockIn.setClockOutTime(LocalDateTime.now()); xpClockIn.setClockOutResult(3); } xpClockInService.updateBatchById(clockInListFive); log.info("========== 修改为下班缺卡 =========="); } } } } } catch (Exception e) { log.error("执行打卡检查任务执行失败", e); } } }