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.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.common.core.domain.R;
import com.xiaopeng.maintenance.constant.TaskScheduleConstants;
import com.xiaopeng.maintenance.mapper.XpTaskMapper;
import com.xiaopeng.maintenance.mapper.XpTaskRuleMapper;
import com.xiaopeng.maintenance.mapper.XpTaskRuleRoleMapper;
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 java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
* 年检/保险策略定时任务
*
* 根据任务规则配置,在车辆保险、年检、运营资质即将到期时自动创建提醒任务,
* 并下发给关联角色的运维人员
*
*
* @author mitao
* @since 2026-02-27
*/
@Component
public class InsuranceInspectionScheduleTask {
private static final Logger log = LoggerFactory.getLogger(InsuranceInspectionScheduleTask.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;
/**
* 每天凌晨1点执行一次
*
* 检查逻辑:
* 1. 获取任务规则配置
* 2. 判断年检/保险策略功能是否启用
* 3. 获取所有运营中的车辆
* 4. 分别检查三种到期情况并创建任务:
* - 保险到期
* - 年检到期
* - 运营资质到期
*
*/
@Scheduled(cron = "0 0 1 * * ?")
public void executeInsuranceInspectionTask() {
log.info("========== 开始执行年检/保险策略定时任务检查 ==========");
// 1. 获取任务规则配置
XpTaskRule taskRule = getTaskRule();
if (taskRule == null) {
log.info("未找到任务规则配置,跳过执行");
return;
}
// 2. 判断年检/保险策略功能是否启用
if (!Boolean.TRUE.equals(taskRule.getInsurance())) {
log.info("年检/保险策略功能未启用,跳过执行");
return;
}
log.info("开始执行年检/保险策略任务下发,保险提前{}天, 年检提前{}天, 运营资质提前{}天, 紧急程度: P{}",
taskRule.getInsuranceExpires(),
taskRule.getInsuranceInspectionExpires(),
taskRule.getOperationQualificationExpires(),
taskRule.getInsuranceLevel());
try {
// 执行任务创建
int createdCount = createExpireTasks(taskRule);
log.info("========== 年检/保险策略定时任务执行完成,创建任务数: {} ==========", createdCount);
} catch (Exception e) {
log.error("年检/保险策略定时任务执行失败", e);
}
}
/**
* 获取任务规则配置
*
* @return 任务规则
*/
private XpTaskRule getTaskRule() {
return taskRuleMapper.selectOne(new LambdaQueryWrapper().last("limit 1"));
}
/**
* 创建到期提醒任务
*
* @param taskRule 任务规则配置
* @return 创建的任务数量
*/
private int createExpireTasks(XpTaskRule taskRule) {
// 1. 获取任务接收角色的运维人员
List operationUserIds = getOperationUserIdsByRoleType(TaskScheduleConstants.RULE_ROLE_TYPE_INSURANCE);
if (CollectionUtils.isEmpty(operationUserIds)) {
log.warn("未找到年检/保险任务接收角色的运维人员");
return 0;
}
log.info("找到 {} 个运维人员可接收年检/保险任务", operationUserIds.size());
// 2. 获取所有运营中的车辆
List vehicles = getOperatingVehicles();
if (CollectionUtils.isEmpty(vehicles)) {
log.info("没有运营中的车辆,无需创建到期提醒任务");
return 0;
}
log.info("找到 {} 辆运营中的车辆", vehicles.size());
int totalCreatedCount = 0;
// 3. 检查保险到期
totalCreatedCount += checkAndCreateInsuranceExpireTasks(vehicles, operationUserIds, taskRule);
// 4. 检查年检到期
totalCreatedCount += checkAndCreateInspectionExpireTasks(vehicles, operationUserIds, taskRule);
// 5. 检查运营资质到期
totalCreatedCount += checkAndCreateQualificationExpireTasks(vehicles, operationUserIds, taskRule);
return totalCreatedCount;
}
/**
* 检查并创建保险到期任务
*
* @param vehicles 车辆列表
* @param operationUserIds 运维人员ID列表
* @param taskRule 任务规则
* @return 创建的任务数量
*/
private int checkAndCreateInsuranceExpireTasks(List vehicles, List operationUserIds, XpTaskRule taskRule) {
if (taskRule.getInsuranceExpires() == null || taskRule.getInsuranceExpires() <= 0) {
log.info("保险到期提醒天数未配置,跳过保险到期检查");
return 0;
}
List needWarnVehicles = vehicles.stream()
.filter(v -> isWithinExpireWarningDays(v.getInsuranceEndTime(), taskRule.getInsuranceExpires()))
.filter(v -> !hasUnfinishedTask(v.getId(), TaskScheduleConstants.TASK_TYPE_INSURANCE_EXPIRE))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(needWarnVehicles)) {
log.info("没有需要保险到期提醒的车辆");
return 0;
}
log.info("找到 {} 辆车辆需要保险到期提醒", needWarnVehicles.size());
return createTasksForExpireType(
needWarnVehicles,
operationUserIds,
taskRule,
TaskScheduleConstants.TASK_TYPE_INSURANCE_EXPIRE,
"保险即将到期"
);
}
/**
* 检查并创建年检到期任务
*
* @param vehicles 车辆列表
* @param operationUserIds 运维人员ID列表
* @param taskRule 任务规则
* @return 创建的任务数量
*/
private int checkAndCreateInspectionExpireTasks(List vehicles, List operationUserIds, XpTaskRule taskRule) {
if (taskRule.getInsuranceInspectionExpires() == null || taskRule.getInsuranceInspectionExpires() <= 0) {
log.info("年检到期提醒天数未配置,跳过年检到期检查");
return 0;
}
List needWarnVehicles = vehicles.stream()
.filter(v -> isWithinExpireWarningDays(v.getAnnualInspectionDate(), taskRule.getInsuranceInspectionExpires()))
.filter(v -> !hasUnfinishedTask(v.getId(), TaskScheduleConstants.TASK_TYPE_INSPECTION_EXPIRE))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(needWarnVehicles)) {
log.info("没有需要年检到期提醒的车辆");
return 0;
}
log.info("找到 {} 辆车辆需要年检到期提醒", needWarnVehicles.size());
return createTasksForExpireType(
needWarnVehicles,
operationUserIds,
taskRule,
TaskScheduleConstants.TASK_TYPE_INSPECTION_EXPIRE,
"年检即将到期"
);
}
/**
* 检查并创建运营资质到期任务
*
* @param vehicles 车辆列表
* @param operationUserIds 运维人员ID列表
* @param taskRule 任务规则
* @return 创建的任务数量
*/
private int checkAndCreateQualificationExpireTasks(List vehicles, List operationUserIds, XpTaskRule taskRule) {
if (taskRule.getOperationQualificationExpires() == null || taskRule.getOperationQualificationExpires() <= 0) {
log.info("运营资质到期提醒天数未配置,跳过运营资质到期检查");
return 0;
}
List needWarnVehicles = vehicles.stream()
.filter(v -> isWithinExpireWarningDays(v.getQualificationExpireTime(), taskRule.getOperationQualificationExpires()))
.filter(v -> !hasUnfinishedTask(v.getId(), TaskScheduleConstants.TASK_TYPE_QUALIFICATION_EXPIRE))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(needWarnVehicles)) {
log.info("没有需要运营资质到期提醒的车辆");
return 0;
}
log.info("找到 {} 辆车辆需要运营资质到期提醒", needWarnVehicles.size());
return createTasksForExpireType(
needWarnVehicles,
operationUserIds,
taskRule,
TaskScheduleConstants.TASK_TYPE_QUALIFICATION_EXPIRE,
"运营资质即将到期"
);
}
/**
* 判断是否在到期提醒范围内
*
* @param expireTime 到期时间
* @param daysBeforeExpire 提前X天提醒
* @return 是否需要提醒
*/
private boolean isWithinExpireWarningDays(LocalDateTime expireTime, Integer daysBeforeExpire) {
if (expireTime == null || daysBeforeExpire == null || daysBeforeExpire <= 0) {
return false;
}
LocalDate today = LocalDate.now();
LocalDate expireDate = expireTime.toLocalDate();
long daysUntilExpire = ChronoUnit.DAYS.between(today, expireDate);
// 距离到期天数 <= 配置的提前天数,且未过期(daysUntilExpire >= 0)
return daysUntilExpire >= 0 && daysUntilExpire <= daysBeforeExpire;
}
/**
* 检查是否已存在未完成的同类型任务
*
* 避免重复创建到期提醒任务
*
*
* @param vehicleId 车辆ID
* @param taskTypeId 任务类型ID
* @return 是否存在未完成任务
*/
private boolean hasUnfinishedTask(Long vehicleId, String taskTypeId) {
XpTask existingTask = taskMapper.selectOne(
new LambdaQueryWrapper()
.eq(XpTask::getVehicleId, vehicleId)
.eq(XpTask::getTaskTypeId, taskTypeId)
.in(XpTask::getStatus,
TaskScheduleConstants.TASK_STATUS_PENDING_ARRIVE,
TaskScheduleConstants.TASK_STATUS_PENDING)
.last("limit 1")
);
return existingTask != null;
}
/**
* 为指定到期类型创建任务
*
* @param vehicles 需要提醒的车辆列表
* @param operationUserIds 运维人员ID列表
* @param taskRule 任务规则
* @param taskTypeId 任务类型ID
* @param taskDescription 任务描述
* @return 创建的任务数量
*/
private int createTasksForExpireType(List vehicles, List operationUserIds,
XpTaskRule taskRule, String taskTypeId, String taskDescription) {
List tasksToSave = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
// 将所有配置角色的运维人员ID拼接,任务分配给该角色下的所有运维人员
String belongUserIds = operationUserIds.stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
for (XpVehicle vehicle : vehicles) {
XpTask task = new XpTask();
// 生成任务编号
String taskCode = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
task.setTaskCode(taskCode);
// 设置任务基本信息
task.setTaskCategory(TaskScheduleConstants.TASK_CATEGORY_ROUTINE);
task.setTaskTypeId(taskTypeId);
task.setTaskUrgencyLevel(taskRule.getInsuranceLevel() != null
? taskRule.getInsuranceLevel()
: TaskScheduleConstants.URGENCY_LEVEL_P2);
task.setTaskDescription("系统自动下发的" + taskDescription + "提醒任务");
// 设置车辆信息
task.setVehicleId(vehicle.getId());
// 设置任务时限
if (taskRule.getProcessingTimeLimit() != null) {
task.setEndTime(LocalDateTime.now().plusMinutes(taskRule.getProcessingTimeLimit()));
}
// 设置任务状态为待到达
task.setStatus(TaskScheduleConstants.TASK_STATUS_PENDING_ARRIVE);
// 设置任务接收人(配置角色下的所有运维人员都可以看到)
task.setHandleUserId(belongUserIds);
task.setReportTime(LocalDateTime.now());
task.setReportUserId(0L);
// 设置创建信息
task.setReportTime(LocalDateTime.now());
task.setCreateBy(TaskScheduleConstants.SCHEDULE_CREATOR);
tasksToSave.add(task);
}
// 批量保存任务
if (!CollectionUtils.isEmpty(tasksToSave)) {
for (XpTask task : tasksToSave) {
taskMapper.insert(task);
}
log.info("成功创建 {} 个{}提醒任务", tasksToSave.size(), taskDescription);
}
return tasksToSave.size();
}
/**
* 根据角色类型获取运维人员ID列表
*
* @param roleType 角色类型
* @return 运维人员ID列表
*/
private List getOperationUserIdsByRoleType(Integer roleType) {
// 1. 获取该类型对应的角色ID
List ruleRoles = taskRuleRoleMapper.selectList(
new LambdaQueryWrapper()
.eq(XpTaskRuleRole::getRuleType, roleType)
);
if (CollectionUtils.isEmpty(ruleRoles)) {
return Collections.emptyList();
}
// 2. 获取角色对应的运维人员
Set userIds = new HashSet<>();
for (XpTaskRuleRole ruleRole : ruleRoles) {
try {
R> 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 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)
.collect(Collectors.toList());
}
} catch (Exception e) {
log.error("获取车辆列表失败", e);
}
return Collections.emptyList();
}
}