package com.xiaopeng.maintenance.schedule; import cn.hutool.core.lang.Snowflake; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; import com.xiaopeng.api.feignClient.operation.XpCruisePlanClient; import com.xiaopeng.api.feignClient.order.XpCruiseOrderClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.maintenance.XpTaskRule; import com.xiaopeng.api.model.order.XpCruiseOrder; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.api.vo.automaticPilot.protobuf.TaskActionType; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.maintenance.mapper.XpTaskRuleMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.time.LocalTime; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; /** * 车辆收车定时任务 *

* 根据任务规则配置,在指定时间自动向所有车辆发起收车指令 *

* * @author mitao * @since 2026-02-27 */ @Component public class VehicleCollectionScheduleTask { private static final Logger log = LoggerFactory.getLogger(VehicleCollectionScheduleTask.class); /** * 任务名称 */ private static final String TASK_NAME = "车辆收车定时任务"; @Autowired private XpTaskRuleMapper taskRuleMapper; @Autowired private XpVehicleClient xpVehicleClient; @Autowired private XpCruiseOrderClient xpCruiseOrderClient; @Autowired private MongoTemplate mongoTemplate; @Autowired private OrderMissionClient orderMissionClient; @Autowired private XpCruisePlanClient xpCruisePlanClient; @Resource(name = "hutoolSnowflake") private Snowflake snowflake; /** * 每分钟检查一次是否需要执行收车任务 *

* 检查逻辑: * 1. 获取任务规则配置 * 2. 判断收车功能是否启用 * 3. 判断当前时间是否为配置的收车时间 * 4. 获取所有运营中的车辆 * 5. 向每辆车发送收车指令 *

*/ @Scheduled(cron = "0 * * * * ?") public void executeCollectionTask() { log.info("========== 开始执行车辆收车定时任务检查 =========="); // 1. 获取任务规则配置 XpTaskRule taskRule = getTaskRule(); if (taskRule == null) { log.info("未找到任务规则配置,跳过执行"); return; } // 2. 判断收车功能是否启用 if (!Boolean.TRUE.equals(taskRule.getVehicleCollection())) { log.info("车辆收车功能未启用,跳过执行"); return; } // 3. 判断当前时间是否为收车时间 if (!isExecuteTime(taskRule.getVehicleCollectionTime())) { log.debug("当前时间不是收车时间,跳过执行"); return; } log.info("开始执行车辆收车任务"); try { // 执行收车逻辑 int processedCount = executeVehicleCollection(); log.info("========== 车辆收车定时任务执行完成,处理车辆数: {} ==========", processedCount); } catch (Exception e) { log.error("车辆收车定时任务执行失败", e); } } /** * 获取任务规则配置 * * @return 任务规则 */ private XpTaskRule getTaskRule() { return taskRuleMapper.selectOne(new LambdaQueryWrapper().last("limit 1")); } /** * 判断当前时间是否为收车时间 *

* 精确到分钟级别的时间匹配 *

* * @param executeTime 配置的执行时间 * @return 是否应该执行 */ private boolean isExecuteTime(LocalTime executeTime) { if (executeTime == null) { log.warn("收车时间未配置"); return false; } LocalTime now = LocalTime.now(); // 精确到分钟匹配 return executeTime.getHour() == now.getHour() && executeTime.getMinute() == now.getMinute(); } /** * 执行车辆收车逻辑 * * @return 处理的车辆数量 */ private int executeVehicleCollection() { // 1. 获取所有运营中的车辆 List vehicles = getOperatingVehicles(); if (CollectionUtils.isEmpty(vehicles)) { log.info("没有运营中的车辆,无需执行收车"); return 0; } log.info("找到 {} 辆运营中的车辆需要收车", vehicles.size()); // 2. 向每辆车发送收车指令 int processedCount = 0; for (XpVehicle vehicle : vehicles) { try { sendCollectionCommand(vehicle); processedCount++; } catch (Exception e) { log.error("向车辆[{}]发送收车指令失败", vehicle.getPlateNumber(), e); } } return processedCount; } /** * 获取所有运营中的车辆 * * @return 车辆列表 */ private List getOperatingVehicles() { try { R> result = xpVehicleClient.listVehicle(); if (result != null && result.getData() != null) { // 过滤运营中的车辆 (vehicleType=2 表示运营, operatingStatus=1 表示运营中) return result.getData().stream() .filter(v -> v.getVehicleType() != null && v.getVehicleType() == 2) .filter(v -> v.getOperatingStatus() != null && v.getOperatingStatus() == 1) .filter(v -> v.getState() != null && v.getState() == 0) .collect(Collectors.toList()); } } catch (Exception e) { log.error("获取车辆列表失败", e); } return Collections.emptyList(); } /** * 向车辆发送收车指令 * * @param xpVehicle 车辆信息 */ private void sendCollectionCommand(XpVehicle xpVehicle) { // 收车 关闭当前巡游单子 生成新的返程单子 找到收车站点 XpCruiseOrder oldCruiseOrder = xpCruiseOrderClient.cancelCruiseOrderByVin(xpVehicle.getVehicleVin()).getData(); // mongodb 获取当前最新经纬度位置 VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class); XpCruiseOrder xpCruiseOrder = new XpCruiseOrder(); if(vehiclePosition!=null){ double x = vehiclePosition.getPoint().getX(); double y = vehiclePosition.getPoint().getY(); xpCruiseOrder.setStartLon(x); xpCruiseOrder.setStartLat(y); } xpCruiseOrder.setVehicleId(xpVehicle.getId()); xpCruiseOrder.setFleetId(xpVehicle.getFleetId()); xpCruiseOrder.setOrderType(2); xpCruiseOrder.setStatus(1); xpCruiseOrder.setOrderReverse(1); xpCruiseOrder.setLastOrderId(oldCruiseOrder.getId()); xpCruiseOrder.setOrderReverse(1); xpCruiseOrder.setXpuTaskId(snowflake.nextId() + ""); // 找到收车站点 R returnCruisePoint = xpCruisePlanClient.findReturnCruisePoint(xpCruiseOrder); if (returnCruisePoint.getData() != null) { XpCruiseOrder cruiseOrder = returnCruisePoint.getData(); //不停车替换新任务 cruiseOrder.setLastOrderId1("XU" + oldCruiseOrder.getId()); cruiseOrder.setLastXpuTaskId(oldCruiseOrder.getXpuTaskId()); cruiseOrder.setTaskActionType(TaskActionType.kTerminateForNewTask); xpCruiseOrderClient.addCruiseOrder(cruiseOrder); } log.info("向车辆[{}]发送收车指令", xpVehicle.getPlateNumber()); } }