| | |
| | | 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; |
| | |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TimingTaskScheduler { |
| | | |
| | | @Autowired |
| | | private Scheduler scheduler; |
| | | private final Scheduler scheduler; |
| | | |
| | | /** |
| | | * 添加新任务到调度器 |
| | |
| | | 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); |
| | | } |
| | | |
| | |
| | | public void rescheduleTimingTask(TimingTask task) throws SchedulerException { |
| | | 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; |
| | | } |
| | | |
| | | // 构建新触发器 |
| | | 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()) |
| | | // 构建CronTrigger,确保持久化配置 |
| | | CronTrigger newTrigger = TriggerBuilder.newTrigger() |
| | | .withIdentity(triggerKey) // 唯一标识,用于持久化存储 |
| | | .withDescription(task.getTaskName() + "_TRIGGER") // 触发器描述 |
| | | .forJob(oldTrigger.getJobKey()) // 关联对应的Job |
| | | .withSchedule(CronScheduleBuilder |
| | | .cronSchedule(convertToCronExpression(task)) |
| | | .withMisfireHandlingInstructionDoNothing() // 错过执行时的策略(根据业务调整) |
| | | ) |
| | | // 设置开始时间(若为null或已过期则立即生效) |
| | | .startAt(resolveStartTime(task)) |
| | | .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()); |
| | | } |
| | | |
| | | /** |
| | |
| | | /** |
| | | * 删除任务 |
| | | */ |
| | | 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) { |
| | | JobDataMap jobDataMap = new JobDataMap(); |
| | | jobDataMap.put("taskId", task.getId()); |
| | | // 1. 构建唯一JobKey(基于任务ID,确保重启后能识别) |
| | | JobKey jobKey = new JobKey("timingTask_" + task.getId()); |
| | | |
| | | // 2. 封装任务数据(仅使用基本类型,确保可序列化) |
| | | JobDataMap jobDataMap = new JobDataMap(); |
| | | jobDataMap.put("taskId", task.getId()); // 任务ID(Long,可序列化) |
| | | jobDataMap.put("taskName", task.getTaskName()); // 任务名称(String,可序列化) |
| | | jobDataMap.put("taskType", task.getFrequencyType()); // 任务类型(String) |
| | | // 按需添加其他必要的基本类型参数 |
| | | |
| | | // 3. 构建JobDetail,设置持久化相关属性 |
| | | return JobBuilder.newJob(TimingTaskJob.class) |
| | | .withIdentity("timingTask_" + task.getId()) |
| | | .withDescription(task.getTaskName()) |
| | | .usingJobData(jobDataMap) |
| | | .storeDurably() |
| | | .withIdentity(jobKey) // 唯一标识,用于持久化存储 |
| | | .withDescription(task.getTaskName()) // 任务描述,存入数据库 |
| | | .usingJobData(jobDataMap) // 绑定任务数据 |
| | | .storeDurably(true) // 即使没有触发器关联也持久化保存 |
| | | .requestRecovery(true) // 当调度器崩溃后恢复时,重新执行未完成的任务 |
| | | .build(); |
| | | } |
| | | |
| | | private Trigger buildJobTrigger(TimingTask task, JobDetail jobDetail) { |
| | | // 1. 构建唯一TriggerKey(基于任务ID) |
| | | TriggerKey triggerKey = new TriggerKey("trigger_" + task.getId()); |
| | | |
| | | // 2. 生成Cron表达式(原逻辑不变) |
| | | String cronExpression = convertToCronExpression(task); |
| | | |
| | | TriggerBuilder<CronTrigger> triggerBuilder = TriggerBuilder.newTrigger() |
| | | .withIdentity("trigger_" + task.getId()) |
| | | .withDescription(task.getTaskName()) |
| | | .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)); |
| | | |
| | | if (jobDetail != null) { |
| | | triggerBuilder.forJob(jobDetail); |
| | | } |
| | | |
| | | if (task.getNextExecutionTime() != null) { |
| | | triggerBuilder.startAt(Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())); |
| | | } |
| | | |
| | | return triggerBuilder.build(); |
| | | // 3. 构建CronTrigger,确保持久化配置 |
| | | return TriggerBuilder.newTrigger() |
| | | .withIdentity(triggerKey) // 唯一标识,用于持久化存储 |
| | | .withDescription(task.getTaskName() + "_TRIGGER") // 触发器描述 |
| | | .forJob(jobDetail) // 关联对应的Job |
| | | .withSchedule(CronScheduleBuilder |
| | | .cronSchedule(cronExpression) |
| | | .withMisfireHandlingInstructionDoNothing() // 错过执行时的策略(根据业务调整) |
| | | ) |
| | | // 4. 设置开始时间(若为null或已过期则立即生效) |
| | | .startAt(resolveStartTime(task)) |
| | | .build(); |
| | | } |
| | | private String convertToCronExpression(TimingTask task) { |
| | | // 参数校验 |