package com.xiaopeng.maintenance.schedule; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.xiaopeng.api.feignClient.account.XpOperationUserNotificationClient; import com.xiaopeng.api.model.account.XpOperationsUser; import com.xiaopeng.api.model.account.XpOperationsUserNotification; import com.xiaopeng.api.model.maintenance.XpTask; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.maintenance.TaskScheduleConstants; import com.xiaopeng.common.core.enums.NotificationTypeEnum; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.maintenance.mapper.XpTaskMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.util.CollectionUtils; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; /** * 事故任务超时检查定时任务 * *

每分钟执行一次,判断事故任务是否超时并发送通知。

* * @author mitao * @since 2026-03-14 */ @Deprecated public class AccidentTaskTimeoutCheckScheduleTask { private static final Logger log = LoggerFactory.getLogger(AccidentTaskTimeoutCheckScheduleTask.class); private static final String TASK_NAME = "事故任务超时检查定时任务"; private static final String PUSH_TITLE = "事故超时警示"; private static final String CONTENT_TEMPLATE = "车辆%s在%s发生的紧急事故已超时!"; @Autowired private XpTaskMapper taskMapper; @Autowired private XpOperationUserNotificationClient xpOperationUserNotificationClient; @Autowired private AccidentTaskNotificationSupport notificationSupport; /** * 每分钟执行一次 */ @Scheduled(cron = "0 * * * * ?") public void executeAccidentTaskTimeoutCheck() { log.info("========== 开始执行{} ==========", TASK_NAME); try { LocalDateTime now = LocalDateTime.now(); List tasks = queryTimeoutAccidentTasks(now); if (CollectionUtils.isEmpty(tasks)) { log.info("未找到超时事故任务,跳过执行"); return; } Set vehicleIds = tasks.stream() .map(XpTask::getVehicleId) .filter(Objects::nonNull) .collect(Collectors.toSet()); Map vehicleMap = notificationSupport.fetchVehicles(vehicleIds); Set allUserIds = new HashSet<>(); for (XpTask task : tasks) { allUserIds.addAll(notificationSupport.parseUserIds(task.getHandleUserId())); } Map userMap = notificationSupport.fetchOperationUsers(allUserIds); int notifiedTaskCount = 0; int notificationCount = 0; int pushCount = 0; List allNotifications = new ArrayList<>(); for (XpTask task : tasks) { try { if (task == null || task.getId() == null) { continue; } Set userIds = notificationSupport.parseUserIds(task.getHandleUserId()); if (CollectionUtils.isEmpty(userIds)) { log.warn("事故任务ID={} 未配置有效处理人,跳过通知", task.getId()); continue; } int affectedRows = markTimeoutNoticeSent(task.getId(), now); if (affectedRows <= 0) { continue; } List users = userIds.stream() .map(userMap::get) .filter(Objects::nonNull) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(users)) { log.warn("事故任务ID={} 未匹配到有效运维人员,跳过通知", task.getId()); continue; } String plateNumber = resolvePlateNumber(vehicleMap.get(task.getVehicleId())); String vehicleLocation = StringUtils.defaultString(task.getVehicleLocation()); String content = String.format(CONTENT_TEMPLATE, plateNumber, vehicleLocation); String bodyJson = notificationSupport.buildPushBodyJson( content, NotificationTypeEnum.ACCIDENT_TIMEOUT.getCode(), task.getId() ); allNotifications.addAll(notificationSupport.buildNotifications( users, NotificationTypeEnum.ACCIDENT_TIMEOUT.getCode(), content, task.getId(), bodyJson )); pushCount += notificationSupport.pushToUsersByDeviceType(users, PUSH_TITLE, bodyJson); notifiedTaskCount++; } catch (Exception e) { log.error("处理超时事故任务通知失败,任务ID={}", task == null ? null : task.getId(), e); } } if (!CollectionUtils.isEmpty(allNotifications)) { xpOperationUserNotificationClient.addNotificationBatch(allNotifications); notificationCount = allNotifications.size(); } log.info("========== {} 执行完成,任务数: {},通知记录数: {},推送次数: {} ==========", TASK_NAME, notifiedTaskCount, notificationCount, pushCount); } catch (Exception e) { log.error("{}执行失败", TASK_NAME, e); } } /** * 查询超时事故任务:endTime < now 且未发送超时通知 */ private List queryTimeoutAccidentTasks(LocalDateTime now) { return taskMapper.selectList(new LambdaQueryWrapper() .eq(XpTask::getTaskCategory, TaskScheduleConstants.TASK_CATEGORY_ACCIDENT) .in(XpTask::getStatus, TaskScheduleConstants.TASK_STATUS_PENDING_ARRIVE, TaskScheduleConstants.TASK_STATUS_PENDING) .isNotNull(XpTask::getEndTime) .lt(XpTask::getEndTime, now) .eq(XpTask::getExpiredNoticeSent, 0)); } /** * 原子更新(抢占式)标记超时通知已发送 */ private int markTimeoutNoticeSent(Long taskId, LocalDateTime now) { return taskMapper.update(null, new LambdaUpdateWrapper() .set(XpTask::getExpiredNoticeSent, 1) .eq(XpTask::getId, taskId) .eq(XpTask::getTaskCategory, TaskScheduleConstants.TASK_CATEGORY_ACCIDENT) .in(XpTask::getStatus, TaskScheduleConstants.TASK_STATUS_PENDING_ARRIVE, TaskScheduleConstants.TASK_STATUS_PENDING) .isNotNull(XpTask::getEndTime) .lt(XpTask::getEndTime, now) .eq(XpTask::getExpiredNoticeSent, 0)); } private String resolvePlateNumber(XpVehicle vehicle) { return vehicle == null ? "" : StringUtils.defaultString(vehicle.getPlateNumber()); } }