云
11 小时以前 41ba09635ca0e5d79117a4364c25b1a5e93c4f13
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
@@ -1,10 +1,11 @@
package com.ruoyi.inspectiontask.service.impl;
import com.ruoyi.inspectiontask.pojo.TimingTask;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
@@ -13,10 +14,10 @@
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class TimingTaskScheduler {
    @Autowired
    private Scheduler scheduler;
    private final Scheduler scheduler;
    /**
     * 添加新任务到调度器
@@ -24,6 +25,10 @@
    public void scheduleTimingTask(TimingTask task) throws SchedulerException {
        JobDetail jobDetail = buildJobDetail(task);
        Trigger trigger = buildJobTrigger(task, jobDetail);
        if (scheduler.checkExists(jobDetail.getKey())) {
            // 持久化的Job可能还在(例如历史数据直接写入、上次未清理),先移除旧的Job及其触发器
            scheduler.deleteJob(jobDetail.getKey());
        }
        scheduler.scheduleJob(jobDetail, trigger);
    }
@@ -31,38 +36,48 @@
     * 更新已有任务
     */
    public void rescheduleTimingTask(TimingTask task) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey("trigger_" + task.getId(), "TIMING_TASK_TRIGGER_GROUP");
        TriggerKey triggerKey = new TriggerKey("trigger_" + task.getId());
        // 获取现有触发器并转换为 CronTrigger
        // 获取现有触发器
        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
        if (!(oldTrigger instanceof CronTrigger)) {
            throw new SchedulerException("Existing trigger is not a CronTrigger");
            // 触发器不存在或类型不符(如任务由直接写库创建),重新注册而不是报错
            scheduleTimingTask(task);
            return;
        }
        // 3. 构建CronTrigger,确保持久化配置
        // 构建CronTrigger,确保持久化配置
        CronTrigger newTrigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)                // 唯一标识,用于持久化存储
                .withDescription(task.getTaskName() + "_TRIGGER") // 触发器描述
                .forJob(oldTrigger.getJobKey())                       // 关联对应的Job
                .withSchedule(CronScheduleBuilder
                        .cronSchedule(convertToCronExpression(task)) // 错过执行时的策略(根据业务调整)
                        .cronSchedule(convertToCronExpression(task))
                        .withMisfireHandlingInstructionDoNothing() // 错过执行时的策略(根据业务调整)
                )
                // 4. 设置开始时间(若为null则立即生效)
                .startAt(task.getNextExecutionTime() != null
                        ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
                        : new Date())
                // 设置开始时间(若为null或已过期则立即生效)
                .startAt(resolveStartTime(task))
                .build();
        // 构建新触发器
//        Trigger newTrigger = TriggerBuilder.newTrigger()
//                .withIdentity(triggerKey)
//                .withDescription(task.getTaskName())
//                .withSchedule(CronScheduleBuilder.cronSchedule(convertToCronExpression(task)))
//                .startAt(Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant()))
//                .forJob(oldTrigger.getJobKey())
//                .build();
        scheduler.rescheduleJob(triggerKey, newTrigger);
    }
    /**
     * 判断任务是否已注册到调度器
     */
    public boolean isRegistered(Long taskId) throws SchedulerException {
        return scheduler.checkExists(new TriggerKey("trigger_" + taskId));
    }
    /**
     * 触发器开始时间:为空或已过期时从当前时间起算,避免补跑历史
     */
    private Date resolveStartTime(TimingTask task) {
        LocalDateTime nextExecutionTime = task.getNextExecutionTime();
        if (nextExecutionTime == null || !nextExecutionTime.isAfter(LocalDateTime.now())) {
            return new Date();
        }
        return Date.from(nextExecutionTime.atZone(ZoneId.systemDefault()).toInstant());
    }
    /**
@@ -84,14 +99,18 @@
    /**
     * 删除任务
     */
    public void unscheduleTimingTask(Long taskId) throws SchedulerException {
        JobKey jobKey = new JobKey("timingTask_" + taskId);
        scheduler.deleteJob(jobKey);
    public void unscheduleTimingTask(Long taskId){
        try {
            JobKey jobKey = new JobKey("timingTask_" + taskId);
            scheduler.deleteJob(jobKey);
        }catch (SchedulerException e){
            throw new RuntimeException(e);
        }
    }
    private JobDetail buildJobDetail(TimingTask task) {
        // 1. 构建唯一JobKey(基于任务ID,确保重启后能识别)
        JobKey jobKey = new JobKey("timingTask_" + task.getId(), "TIMING_TASK_GROUP");
        JobKey jobKey = new JobKey("timingTask_" + task.getId());
        // 2. 封装任务数据(仅使用基本类型,确保可序列化)
        JobDataMap jobDataMap = new JobDataMap();
@@ -105,20 +124,20 @@
                .withIdentity(jobKey)                    // 唯一标识,用于持久化存储
                .withDescription(task.getTaskName())     // 任务描述,存入数据库
                .usingJobData(jobDataMap)                // 绑定任务数据
                .storeDurably()                          // 即使没有触发器关联也持久化保存
                .storeDurably(true)                          // 即使没有触发器关联也持久化保存
                .requestRecovery(true)                   // 当调度器崩溃后恢复时,重新执行未完成的任务
                .build();
    }
    private Trigger buildJobTrigger(TimingTask task, JobDetail jobDetail) {
        // 1. 构建唯一TriggerKey(基于任务ID)
        TriggerKey triggerKey = new TriggerKey("trigger_" + task.getId(), "TIMING_TASK_TRIGGER_GROUP");
        TriggerKey triggerKey = new TriggerKey("trigger_" + task.getId());
        // 2. 生成Cron表达式(原逻辑不变)
        String cronExpression = convertToCronExpression(task);
        // 3. 构建CronTrigger,确保持久化配置
        CronTrigger trigger = TriggerBuilder.newTrigger()
        return TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)                // 唯一标识,用于持久化存储
                .withDescription(task.getTaskName() + "_TRIGGER") // 触发器描述
                .forJob(jobDetail)                       // 关联对应的Job
@@ -126,12 +145,9 @@
                        .cronSchedule(cronExpression)
                        .withMisfireHandlingInstructionDoNothing() // 错过执行时的策略(根据业务调整)
                )
                // 4. 设置开始时间(若为null则立即生效)
                .startAt(task.getNextExecutionTime() != null
                        ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
                        : new Date())
                // 4. 设置开始时间(若为null或已过期则立即生效)
                .startAt(resolveStartTime(task))
                .build();
        return trigger;
    }
    private String convertToCronExpression(TimingTask task) {
        // 参数校验