package com.xiaopeng.maintenance.schedule;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.xiaopeng.api.feignClient.account.XpOperationUserClient;
|
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient;
|
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
|
import com.xiaopeng.api.model.account.XpOperationsRole;
|
import com.xiaopeng.api.model.maintenance.XpTask;
|
import com.xiaopeng.api.model.maintenance.XpTaskRule;
|
import com.xiaopeng.api.model.maintenance.XpTaskRuleRole;
|
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
|
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
|
import com.xiaopeng.common.core.domain.R;
|
import com.xiaopeng.api.vo.maintenance.TaskScheduleConstants;
|
import com.xiaopeng.common.core.utils.SensitiveDataCryptoUtil;
|
import com.xiaopeng.maintenance.mapper.XpTaskMapper;
|
import com.xiaopeng.maintenance.mapper.XpTaskRuleMapper;
|
import com.xiaopeng.maintenance.mapper.XpTaskRuleRoleMapper;
|
import com.xiaopeng.maintenance.service.VehicleLocationFillService;
|
import com.xiaopeng.maintenance.utils.UUIDUtil;
|
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 org.springframework.util.StringUtils;
|
|
import java.text.SimpleDateFormat;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.LocalTime;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
/**
|
* 车辆日常检测定时任务
|
* <p>
|
* 根据任务规则配置,在指定时间自动创建车辆日常检测任务,
|
* 并下发给关联角色的运维人员
|
* </p>
|
*
|
* @author mitao
|
* @since 2026-02-27
|
*/
|
@Component
|
public class VehicleInspectionScheduleTask {
|
|
private static final Logger log = LoggerFactory.getLogger(VehicleInspectionScheduleTask.class);
|
|
/**
|
* 任务名称
|
*/
|
private static final String TASK_NAME = "车辆日常检测定时任务";
|
|
@Autowired
|
private XpTaskRuleMapper taskRuleMapper;
|
|
@Autowired
|
private XpTaskRuleRoleMapper taskRuleRoleMapper;
|
|
@Autowired
|
private XpTaskMapper taskMapper;
|
|
@Autowired
|
private XpVehicleClient xpVehicleClient;
|
|
@Autowired
|
private XpOperationUserClient xpOperationUserClient;
|
|
@Autowired
|
private XpFleetOperationsClient xpFleetOperationsClient;
|
|
@Autowired
|
private VehicleLocationFillService vehicleLocationFillService;
|
|
/**
|
* 每分钟检查一次是否需要执行任务下发
|
* <p>
|
* 检查逻辑:
|
* 1. 获取任务规则配置
|
* 2. 判断日常检测功能是否启用
|
* 3. 判断当前时间是否为配置的任务下发时间
|
* 4. 查询所有运营中的车辆
|
* 5. 根据检测频次判断是否需要创建任务
|
* 6. 创建任务并分配给相关角色的运维人员
|
* </p>
|
*/
|
@Scheduled(cron = "0 * * * * ?")
|
public void executeInspectionTask() {
|
log.info("========== 开始执行车辆日常检测定时任务检查 ==========");
|
|
// 1. 获取任务规则配置
|
XpTaskRule taskRule = getTaskRule();
|
if (taskRule == null) {
|
log.info("未找到任务规则配置,跳过执行");
|
return;
|
}
|
|
// 2. 判断日常检测功能是否启用
|
if (!Boolean.TRUE.equals(taskRule.getVehicleInspection())) {
|
log.info("车辆日常检测功能未启用,跳过执行");
|
return;
|
}
|
|
// 3. 判断当前时间是否为配置的任务下发时间
|
if (!isExecuteTime(taskRule.getVehicleInspectionTime())) {
|
log.debug("当前时间不是任务下发时间,跳过执行");
|
return;
|
}
|
|
log.info("开始执行车辆日常检测任务下发,检测频次: 每{}天/次, 紧急程度: P{}",
|
taskRule.getVehicleInspectionFrequency(),
|
taskRule.getVehicleInspectionLevel());
|
|
try {
|
// 执行任务创建
|
int createdCount = createInspectionTasks(taskRule);
|
|
log.info("========== 车辆日常检测定时任务执行完成,创建任务数: {} ==========", createdCount);
|
|
} catch (Exception e) {
|
log.error("车辆日常检测定时任务执行失败", e);
|
}
|
}
|
|
/**
|
* 获取任务规则配置
|
*
|
* @return 任务规则
|
*/
|
private XpTaskRule getTaskRule() {
|
return taskRuleMapper.selectOne(new LambdaQueryWrapper<XpTaskRule>().last("limit 1"));
|
}
|
|
/**
|
* 判断当前时间是否为任务下发时间
|
* <p>
|
* 精确到分钟级别的时间匹配
|
* </p>
|
*
|
* @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();
|
}
|
|
/**
|
* 创建车辆日常检测任务
|
*
|
* @param taskRule 任务规则配置
|
* @return 创建的任务数量
|
*/
|
private int createInspectionTasks(XpTaskRule taskRule) {
|
// 1. 获取任务接收角色的运维人员
|
List<Long> roleOperationUserIds = getOperationUserIdsByRoleType(TaskScheduleConstants.RULE_ROLE_TYPE_INSPECTION);
|
if (CollectionUtils.isEmpty(roleOperationUserIds)) {
|
log.warn("未找到车辆日常检测任务接收角色的运维人员");
|
return 0;
|
}
|
|
Set<Long> roleOperationUserIdSet = new HashSet<>(roleOperationUserIds);
|
log.info("找到 {} 个运维人员可接收日常检测任务", roleOperationUserIdSet.size());
|
|
// 2. 获取所有运营中的车辆
|
List<XpVehicle> vehicles = getOperatingVehicles();
|
if (CollectionUtils.isEmpty(vehicles)) {
|
log.info("没有运营中的车辆,无需创建检测任务");
|
return 0;
|
}
|
|
log.info("找到 {} 辆运营中的车辆", vehicles.size());
|
|
// 3. 过滤出今日需要检测的车辆
|
List<XpVehicle> needInspectionVehicles = filterVehiclesNeedInspection(vehicles, taskRule.getVehicleInspectionFrequency());
|
if (CollectionUtils.isEmpty(needInspectionVehicles)) {
|
log.info("今日没有需要检测的车辆");
|
return 0;
|
}
|
|
log.info("过滤后有 {} 辆车辆需要今日检测", needInspectionVehicles.size());
|
|
// 4. 创建任务并分配给运维人员
|
Map<Long, Long> vehicleIdToFleetIdCache = new HashMap<>();
|
Map<Long, Set<Long>> fleetIdToOperationUserIdsCache = new HashMap<>();
|
return createTasksAndAssign(needInspectionVehicles, roleOperationUserIdSet, taskRule,
|
vehicleIdToFleetIdCache, fleetIdToOperationUserIdsCache);
|
}
|
|
/**
|
* 根据角色类型获取运维人员ID列表
|
*
|
* @param roleType 角色类型
|
* @return 运维人员ID列表
|
*/
|
private List<Long> getOperationUserIdsByRoleType(Integer roleType) {
|
// 1. 获取该类型对应的角色ID
|
List<XpTaskRuleRole> ruleRoles = taskRuleRoleMapper.selectList(
|
new LambdaQueryWrapper<XpTaskRuleRole>()
|
.eq(XpTaskRuleRole::getRuleType, roleType)
|
);
|
|
if (CollectionUtils.isEmpty(ruleRoles)) {
|
return Collections.emptyList();
|
}
|
|
// 2. 获取角色对应的运维人员
|
Set<Long> userIds = new HashSet<>();
|
for (XpTaskRuleRole ruleRole : ruleRoles) {
|
try {
|
R<List<XpOperationsRole>> result = xpOperationUserClient.getXpOperationsRoleByRoleId(ruleRole.getRoleId());
|
if (result != null && result.getData() != null) {
|
userIds.addAll(
|
result.getData().stream()
|
.map(XpOperationsRole::getOperationsUserId)
|
.collect(Collectors.toList())
|
);
|
}
|
} catch (Exception e) {
|
log.error("获取角色[{}]对应的运维人员失败", ruleRole.getRoleId(), e);
|
}
|
}
|
|
return new ArrayList<>(userIds);
|
}
|
|
/**
|
* 获取所有运营中的车辆
|
*
|
* @return 车辆列表
|
*/
|
private List<XpVehicle> getOperatingVehicles() {
|
try {
|
R<List<XpVehicle>> 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)
|
.collect(Collectors.toList());
|
}
|
} catch (Exception e) {
|
log.error("获取车辆列表失败", e);
|
}
|
return Collections.emptyList();
|
}
|
|
/**
|
* 过滤出需要检测的车辆
|
* <p>
|
* 根据检测频次判断:vehicleInspectionFrequency 表示每X天检测一次
|
* 查询车辆上次检测时间,如果距今天数 >= 频次配置,则需要检测
|
* </p>
|
*
|
* @param vehicles 所有车辆
|
* @param inspectionFrequency 检测频次(天)
|
* @return 需要检测的车辆列表
|
*/
|
private List<XpVehicle> filterVehiclesNeedInspection(List<XpVehicle> vehicles, Integer inspectionFrequency) {
|
// 默认每天检测一次
|
if (inspectionFrequency == null || inspectionFrequency <= 0) {
|
inspectionFrequency = 1;
|
}
|
|
LocalDate today = LocalDate.now();
|
List<XpVehicle> needInspectionVehicles = new ArrayList<>();
|
|
for (XpVehicle vehicle : vehicles) {
|
// 查询该车辆最近一次检测任务
|
XpTask lastTask = taskMapper.selectOne(
|
new LambdaQueryWrapper<XpTask>()
|
.eq(XpTask::getVehicleId, vehicle.getId())
|
.eq(XpTask::getTaskCategory, TaskScheduleConstants.TASK_CATEGORY_ROUTINE)
|
.eq(XpTask::getTaskTypeId, TaskScheduleConstants.TASK_TYPE_DETECTION)
|
.orderByDesc(XpTask::getCreateTime)
|
.last("limit 1")
|
);
|
|
// 如果从未检测过,需要检测
|
if (lastTask == null || lastTask.getCreateTime() == null) {
|
needInspectionVehicles.add(vehicle);
|
continue;
|
}
|
|
// 计算距离上次检测的天数
|
LocalDate lastInspectionDate = lastTask.getCreateTime().toLocalDate();
|
long daysSinceLastInspection = java.time.temporal.ChronoUnit.DAYS.between(lastInspectionDate, today);
|
|
// 如果距离上次检测天数 >= 频次配置,则需要检测
|
if (daysSinceLastInspection >= inspectionFrequency) {
|
needInspectionVehicles.add(vehicle);
|
}
|
}
|
|
return needInspectionVehicles;
|
}
|
|
/**
|
* 创建任务并分配给运维人员
|
*
|
* @param vehicles 需要检测的车辆列表
|
* @param roleOperationUserIds 配置角色下的运维人员ID集合
|
* @param taskRule 任务规则
|
* @param vehicleIdToFleetIdCache 车辆->车队缓存
|
* @param fleetIdToOperationUserIdsCache 车队->人员缓存
|
* @return 创建的任务数量
|
*/
|
private int createTasksAndAssign(List<XpVehicle> vehicles, Set<Long> roleOperationUserIds, XpTaskRule taskRule,
|
Map<Long, Long> vehicleIdToFleetIdCache,
|
Map<Long, Set<Long>> fleetIdToOperationUserIdsCache) {
|
List<XpTask> tasksToSave = new ArrayList<>();
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
|
int skippedCount = 0;
|
|
Map<String, VehiclePosition> positionByVin = vehicleLocationFillService.loadPositionByVin(
|
vehicles.stream()
|
.map(XpVehicle::getVehicleVin)
|
.filter(StringUtils::hasLength)
|
.distinct()
|
.collect(Collectors.toList())
|
);
|
|
for (XpVehicle vehicle : vehicles) {
|
List<Long> handleUserIds = resolveHandleUserIds(vehicle.getId(), roleOperationUserIds,
|
vehicleIdToFleetIdCache, fleetIdToOperationUserIdsCache);
|
if (CollectionUtils.isEmpty(handleUserIds)) {
|
skippedCount++;
|
continue;
|
}
|
|
String belongUserIds = handleUserIds.stream().map(String::valueOf).collect(Collectors.joining(","));
|
|
XpTask task = new XpTask();
|
|
// 生成任务编号
|
String taskCode = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
|
task.setTaskCode(taskCode);
|
|
// 设置任务基本信息
|
task.setTaskCategory(TaskScheduleConstants.TASK_CATEGORY_ROUTINE);
|
task.setTaskTypeId(TaskScheduleConstants.TASK_TYPE_DETECTION);
|
task.setTaskUrgencyLevel(taskRule.getVehicleInspectionLevel() != null
|
? taskRule.getVehicleInspectionLevel()
|
: TaskScheduleConstants.URGENCY_LEVEL_P2);
|
task.setTaskDescription("系统自动下发的车辆日常检测任务");
|
|
// 设置车辆信息
|
task.setVehicleId(vehicle.getId());
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
vehicleLocationFillService.fillFromVin(task, vin, positionByVin);
|
|
// 设置任务时限 常规任务限时3天
|
task.setEndTime(LocalDateTime.now().plusDays(3));
|
|
// 设置任务状态为待到达
|
task.setStatus(TaskScheduleConstants.TASK_STATUS_PENDING);
|
|
// 设置任务接收人(车辆所属车队关联人员与配置角色人员取交集)
|
task.setHandleUserId(belongUserIds);
|
|
// 设置创建信息
|
task.setReportTime(LocalDateTime.now());
|
task.setCreateBy(TaskScheduleConstants.SCHEDULE_CREATOR);
|
|
tasksToSave.add(task);
|
}
|
|
// 批量保存任务
|
if (!CollectionUtils.isEmpty(tasksToSave)) {
|
for (XpTask task : tasksToSave) {
|
task.setReportTime(LocalDateTime.now());
|
task.setReportUserId(0L);
|
taskMapper.insert(task);
|
}
|
log.info("成功创建 {} 个车辆日常检测任务", tasksToSave.size());
|
}
|
if (skippedCount > 0) {
|
log.warn("车辆日常检测任务:跳过 {} 辆车(无可分配处理人)", skippedCount);
|
}
|
|
return tasksToSave.size();
|
}
|
|
private List<Long> resolveHandleUserIds(Long vehicleId, Set<Long> roleOperationUserIds,
|
Map<Long, Long> vehicleIdToFleetIdCache,
|
Map<Long, Set<Long>> fleetIdToOperationUserIdsCache) {
|
if (vehicleId == null) {
|
log.warn("车辆日常检测定时任务分配处理人失败:vehicleId为空");
|
return Collections.emptyList();
|
}
|
|
Long fleetId = vehicleIdToFleetIdCache.get(vehicleId);
|
if (!vehicleIdToFleetIdCache.containsKey(vehicleId)) {
|
try {
|
R<Long> fleetIdResult = xpVehicleClient.findFleetIdByVehicleId(vehicleId);
|
fleetId = fleetIdResult != null ? fleetIdResult.getData() : null;
|
} catch (Exception e) {
|
log.error("车辆日常检测定时任务:根据车辆查询车队失败,vehicleId={}", vehicleId, e);
|
fleetId = null;
|
}
|
vehicleIdToFleetIdCache.put(vehicleId, fleetId);
|
}
|
|
if (fleetId == null) {
|
log.warn("车辆日常检测定时任务跳过:车辆未关联车队,vehicleId={}", vehicleId);
|
return Collections.emptyList();
|
}
|
|
Set<Long> fleetOperationUserIds = fleetIdToOperationUserIdsCache.get(fleetId);
|
if (!fleetIdToOperationUserIdsCache.containsKey(fleetId)) {
|
try {
|
R<List<Long>> opsResult = xpFleetOperationsClient.getOperationsIdsByFleetId(fleetId);
|
List<Long> opsIds = opsResult != null ? opsResult.getData() : null;
|
fleetOperationUserIds = CollectionUtils.isEmpty(opsIds) ? Collections.emptySet() : new HashSet<>(opsIds);
|
} catch (Exception e) {
|
log.error("车辆日常检测定时任务:根据车队查询关联人员失败,fleetId={}, vehicleId={}", fleetId, vehicleId, e);
|
fleetOperationUserIds = Collections.emptySet();
|
}
|
fleetIdToOperationUserIdsCache.put(fleetId, fleetOperationUserIds);
|
}
|
|
if (CollectionUtils.isEmpty(fleetOperationUserIds)) {
|
log.warn("车辆日常检测定时任务跳过:车队未关联人员,fleetId={}, vehicleId={}", fleetId, vehicleId);
|
return Collections.emptyList();
|
}
|
|
List<Long> finalUserIds = fleetOperationUserIds.stream()
|
.filter(roleOperationUserIds::contains)
|
.sorted()
|
.collect(Collectors.toList());
|
if (CollectionUtils.isEmpty(finalUserIds)) {
|
log.warn("车辆日常检测定时任务跳过:车队人员与角色人员无交集,fleetId={}, vehicleId={}", fleetId, vehicleId);
|
return Collections.emptyList();
|
}
|
|
return finalUserIds;
|
}
|
}
|