puzhibing
2026-03-12 d2ddb03ee094d1d546fd92ce1b7876a7d3ae0657
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
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;
 
/**
 * 打卡定时任务
 * <p>
 * 判断是否迟到或者缺卡
 * </p>
 *
 * @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(cron = "0 */5 * * * ?")
    public void clockInScheduleTask() {
        log.info("========== 开始执行打卡检查 ==========");
        try {
            // 查询下班卡在今天的
            List<XpSchedulingDetail> clockOutPlanList = xpSchedulingDetailService.list(Wrappers.lambdaQuery(XpSchedulingDetail.class)
                    .likeRight(XpSchedulingDetail::getPlanTime, LocalDate.now()));
 
            if(!CollectionUtils.isEmpty(clockOutPlanList)){
                List<Long> fleetIds = clockOutPlanList.stream().map(XpSchedulingDetail::getFleetId).collect(Collectors.toList());
                // 查询所有车队的绑定人员
                List<XpFleetOperations> xpFleetOperations = xpFleetOperationsClient.getOperationsIdsByFleetIds(fleetIds).getData();
                // 查询班次
                List<Long> shiftIds = clockOutPlanList.stream().map(XpSchedulingDetail::getShiftId).collect(Collectors.toList());
                List<XpShift> xpShifts = xpShiftService.list(Wrappers.lambdaQuery(XpShift.class)
                        .eq(XpShift::getId, shiftIds));
                log.info("========== 查询到{}个车队 ==========", xpFleetOperations.size());
                if(!CollectionUtils.isEmpty(xpShifts) && !CollectionUtils.isEmpty(xpFleetOperations)){
                    // 处理上班卡缺卡情况
                    // 过滤当前时间在班次下班打卡时间之后的数据   今日已过下班的班次
                    List<XpShift> shiftList = xpShifts.stream().filter(xpShift ->
                            DateUtils.stringToLocalDateTime(LocalDate.now() + " " + xpShift.getEndTime() + ":00").isBefore(LocalDateTime.now())).collect(Collectors.toList());
                    List<Long> ids = shiftList.stream().map(XpShift::getId).collect(Collectors.toList());
                    if(!CollectionUtils.isEmpty(ids)){
                        log.info("========== 进入上班过期的班次 ==========");
                        // 获取过期当前班次的排班明细  覆盖
                        List<XpSchedulingDetail> clockOutPlans = clockOutPlanList.stream().filter(xpSchedulingDetail -> ids.contains(xpSchedulingDetail.getShiftId())).collect(Collectors.toList());
                        List<Long> fleetIdIn = clockOutPlans.stream().map(XpSchedulingDetail::getFleetId).collect(Collectors.toList());
                        List<Long> operationsUserIds = xpFleetOperations.stream().filter(x -> fleetIdIn.contains(x.getFleetId()))
                                .map(XpFleetOperations::getOperationsUserId).distinct().collect(Collectors.toList());
                        // 批量查询人员打卡信息
                        List<XpClockIn> clockInList = xpClockInService.list(Wrappers.lambdaQuery(XpClockIn.class)
                                .eq(XpClockIn::getAppUserId, operationsUserIds));
                        // 判断有哪些人员没有打卡信息
                        List<Long> 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<XpClockIn> noClockInList = new ArrayList<>();
                            for (Long noClockInUserId : noClockInUserIds) {
                                // 通过用户id找到车队id
                                List<XpFleetOperations> operations = xpFleetOperations.stream().filter(x -> x.getOperationsUserId().equals(noClockInUserId)).collect(Collectors.toList());
                                if(!CollectionUtils.isEmpty(operations)){
                                    Long fleetId = operations.get(0).getFleetId();
                                    // 通过车队id和已过期的班次获取排班明细
                                    List<XpSchedulingDetail> 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<XpShift> shiftListFive = xpShifts.stream().filter(xpShift ->
                            DateUtils.stringToLocalDateTime(LocalDate.now() + " " + xpShift.getEndTime() + ":00").plusHours(5).isBefore(LocalDateTime.now())).collect(Collectors.toList());
                    List<Long> fiveIds = shiftListFive.stream().map(XpShift::getId).collect(Collectors.toList());
                    if(!CollectionUtils.isEmpty(fiveIds)){
                        log.info("========== 进入下班过期的班次 ==========");
                        // 获取过期当前班次的排班明细  覆盖
                        List<XpSchedulingDetail> fiveClockOutPlans = clockOutPlanList.stream().filter(xpSchedulingDetail -> fiveIds.contains(xpSchedulingDetail.getShiftId())).collect(Collectors.toList());
                        List<Long> fiveFleetIdIn = fiveClockOutPlans.stream().map(XpSchedulingDetail::getFleetId).collect(Collectors.toList());
                        // 查询所有车队的绑定人员
                        if(!CollectionUtils.isEmpty(xpFleetOperations)){
                            List<Long> operationsUserIdsFive = xpFleetOperations.stream().filter(x -> fiveFleetIdIn.contains(x.getFleetId()))
                                    .map(XpFleetOperations::getOperationsUserId).distinct().collect(Collectors.toList());
                            // 批量查询人员打卡信息
                            List<XpClockIn> 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("========== 修改为下班缺卡 ==========");
                            }
                        }
                    }
                }
            }
 
        } catch (Exception e) {
            log.error("执行打卡检查任务执行失败", e);
        }
    }
}