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).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)
.eq(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());
// 批量查询人员打卡信息
List clockInList = xpClockInService.list(Wrappers.lambdaQuery(XpClockIn.class)
.eq(XpClockIn::getAppUserId, operationsUserIds));
// 判断有哪些人员没有打卡信息
List noClockInUserIds = operationsUserIds.stream().filter(id -> clockInList.stream().noneMatch(clockIn -> clockIn.getAppUserId().equals(id))).collect(Collectors.toList());
if(!CollectionUtils.isEmpty(noClockInUserIds)){
log.info("========== 获取到{}个人员没有打卡信息 ==========", noClockInUserIds.size());
// 添加上班缺卡信息
List noClockInList = new ArrayList<>();
for (Long noClockInUserId : noClockInUserIds) {
// 通过用户id找到车队id
List operations = xpFleetOperations.stream().filter(x -> x.getOperationsUserId().equals(noClockInUserId)).collect(Collectors.toList());
if(!CollectionUtils.isEmpty(operations)){
Long fleetId = operations.get(0).getFleetId();
// 通过车队id和已过期的班次获取排班明细
List clockOutPlanExpiredList = clockOutPlanList.stream()
.filter(xpSchedulingDetail -> xpSchedulingDetail.getFleetId().equals(fleetId)
&& ids.contains(xpSchedulingDetail.getShiftId())).collect(Collectors.toList());
if(!CollectionUtils.isEmpty(clockOutPlanExpiredList)){
Long shiftId = clockOutPlanExpiredList.get(0).getShiftId();
XpClockIn xpClockIn = new XpClockIn();
xpClockIn.setAppUserId(noClockInUserId);
xpClockIn.setClockInTime(LocalDateTime.now());
xpClockIn.setClockInResult(3);
xpClockIn.setShiftId(shiftId);
noClockInList.add(xpClockIn);
}
}
}
if (!CollectionUtils.isEmpty(noClockInList)){
log.info("添加上班缺卡信息:{}", noClockInList);
xpClockInService.saveOrUpdateBatch(noClockInList);
}
}
}
// 处理下班卡缺卡情况
// 过滤当前时间在班次下班打卡时间之后5小时的数据 今日已过下班打卡时间5小时后的班次
List shiftListFive = xpShifts.stream().filter(xpShift ->
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 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(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)
.eq(XpClockIn::getAppUserId, operationsUserIdsFive)
.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").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)
.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);
}
}
}