package com.ruoyi.device.service.impl;
|
|
import com.ruoyi.device.pojo.MaintenanceTask;
|
import lombok.extern.slf4j.Slf4j;
|
import org.quartz.CronScheduleBuilder;
|
import org.quartz.CronTrigger;
|
import org.quartz.JobBuilder;
|
import org.quartz.JobDataMap;
|
import org.quartz.JobDetail;
|
import org.quartz.JobKey;
|
import org.quartz.Scheduler;
|
import org.quartz.SchedulerException;
|
import org.quartz.Trigger;
|
import org.quartz.TriggerBuilder;
|
import org.quartz.TriggerKey;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import java.time.LocalTime;
|
import java.time.ZoneId;
|
import java.time.format.DateTimeParseException;
|
import java.util.Arrays;
|
import java.util.Date;
|
import java.util.stream.Collectors;
|
|
@Service
|
@Slf4j
|
public class MaintenanceTaskScheduler {
|
|
@Autowired
|
private Scheduler scheduler;
|
|
public void scheduleMaintenanceTask(MaintenanceTask task) {
|
try {
|
JobDetail jobDetail = buildJobDetail(task);
|
Trigger trigger = buildJobTrigger(task, jobDetail);
|
scheduler.scheduleJob(jobDetail, trigger);
|
} catch (SchedulerException e) {
|
log.error("SchedulerException scheduleMaintenanceTask ERROR", e);
|
throw new RuntimeException(e);
|
}
|
}
|
|
public void rescheduleMaintenanceTask(MaintenanceTask task) {
|
try {
|
TriggerKey triggerKey = new TriggerKey("triggerMaintenanceTask_" + task.getId());
|
Trigger oldTrigger = scheduler.getTrigger(triggerKey);
|
if (!(oldTrigger instanceof CronTrigger)) {
|
throw new SchedulerException("Existing trigger is not a CronTrigger");
|
}
|
|
CronTrigger newTrigger = TriggerBuilder.newTrigger()
|
.withIdentity(triggerKey)
|
.withDescription(task.getTaskName() + "_TRIGGER")
|
.forJob(oldTrigger.getJobKey())
|
.withSchedule(CronScheduleBuilder.cronSchedule(convertToCronExpression(task)))
|
.startAt(task.getNextExecutionTime() != null
|
? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
|
: new Date())
|
.build();
|
scheduler.rescheduleJob(triggerKey, newTrigger);
|
} catch (SchedulerException e) {
|
log.error("SchedulerException rescheduleMaintenanceTask ERROR", e);
|
throw new RuntimeException(e);
|
}
|
}
|
|
public void pauseMaintenanceTask(Long taskId) throws SchedulerException {
|
scheduler.pauseJob(new JobKey("MaintenanceTask_" + taskId));
|
}
|
|
public void resumeMaintenanceTask(Long taskId) throws SchedulerException {
|
scheduler.resumeJob(new JobKey("MaintenanceTask_" + taskId));
|
}
|
|
public void unscheduleMaintenanceTask(Long taskId) {
|
try {
|
scheduler.deleteJob(new JobKey("MaintenanceTask_" + taskId));
|
} catch (SchedulerException e) {
|
log.error("SchedulerException unscheduleMaintenanceTask ERROR", e);
|
throw new RuntimeException(e);
|
}
|
}
|
|
private JobDetail buildJobDetail(MaintenanceTask task) {
|
JobKey jobKey = new JobKey("MaintenanceTask_" + task.getId());
|
JobDataMap jobDataMap = new JobDataMap();
|
jobDataMap.put("maintenanceTaskId", task.getId());
|
jobDataMap.put("taskName", task.getTaskName());
|
jobDataMap.put("taskType", task.getFrequencyType());
|
|
return JobBuilder.newJob(MaintenanceTaskJob.class)
|
.withIdentity(jobKey)
|
.withDescription(task.getTaskName())
|
.usingJobData(jobDataMap)
|
.storeDurably(true)
|
.requestRecovery(true)
|
.build();
|
}
|
|
private Trigger buildJobTrigger(MaintenanceTask task, JobDetail jobDetail) {
|
TriggerKey triggerKey = new TriggerKey("triggerMaintenanceTask_" + task.getId());
|
String cronExpression = convertToCronExpression(task);
|
return TriggerBuilder.newTrigger()
|
.withIdentity(triggerKey)
|
.withDescription(task.getTaskName() + "_TRIGGER")
|
.forJob(jobDetail)
|
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)
|
.withMisfireHandlingInstructionDoNothing())
|
.startAt(task.getNextExecutionTime() != null
|
? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
|
: new Date())
|
.build();
|
}
|
|
private String convertToCronExpression(MaintenanceTask task) {
|
if (task == null || task.getFrequencyType() == null || task.getFrequencyDetail() == null) {
|
throw new IllegalArgumentException("任务参数不能为空");
|
}
|
|
String frequencyType = task.getFrequencyType().toUpperCase();
|
switch (frequencyType) {
|
case "DAILY":
|
return convertDailyToCron(task.getFrequencyDetail());
|
case "WEEKLY":
|
return convertWeeklyToCron(task.getFrequencyDetail());
|
case "MONTHLY":
|
return convertMonthlyToCron(task.getFrequencyDetail());
|
case "QUARTERLY":
|
return convertQuarterlyToCron(task.getFrequencyDetail());
|
case "YEARLY":
|
return convertYearlyToCron(task.getFrequencyDetail());
|
default:
|
throw new IllegalArgumentException("不支持的频率类型: " + task.getFrequencyType());
|
}
|
}
|
|
private String convertDailyToCron(String frequencyDetail) {
|
LocalTime time = parseTime(frequencyDetail);
|
return String.format("0 %d %d * * ?", time.getMinute(), time.getHour());
|
}
|
|
private String convertWeeklyToCron(String frequencyDetail) {
|
String[] parts = validateAndSplit(frequencyDetail, ",", 2);
|
String daysOfWeek = convertDayNamesToCron(parts[0]);
|
LocalTime time = parseTime(parts[1]);
|
return String.format("0 %d %d ? * %s", time.getMinute(), time.getHour(), daysOfWeek);
|
}
|
|
private String convertMonthlyToCron(String frequencyDetail) {
|
String[] parts = validateAndSplit(frequencyDetail, ",", 2);
|
int day = validateDayOfMonth(parts[0]);
|
LocalTime time = parseTime(parts[1]);
|
return String.format("0 %d %d %d * ?", time.getMinute(), time.getHour(), day);
|
}
|
|
private String convertQuarterlyToCron(String frequencyDetail) {
|
String[] parts = validateAndSplit(frequencyDetail, ",", 3);
|
int month = validateMonth(parts[0]);
|
int day = validateDayOfMonth(parts[1]);
|
LocalTime time = parseTime(parts[2]);
|
|
int quarterStartMonth = ((month - 1) / 3) * 3 + 1;
|
return String.format("0 %d %d %d %d/3 ?", time.getMinute(), time.getHour(), day, quarterStartMonth);
|
}
|
|
private String convertYearlyToCron(String frequencyDetail) {
|
String[] parts = validateAndSplit(frequencyDetail, ",", 3);
|
int month = validateMonth(parts[0]);
|
int day = validateDayOfMonth(parts[1]);
|
LocalTime time = parseTime(parts[2]);
|
return String.format("0 %d %d %d %d ?", time.getMinute(), time.getHour(), day, month);
|
}
|
|
private int validateMonth(String monthStr) {
|
try {
|
int month = Integer.parseInt(monthStr);
|
if (month < 1 || month > 12) {
|
throw new IllegalArgumentException("月份必须在 1-12 之间");
|
}
|
return month;
|
} catch (NumberFormatException e) {
|
throw new IllegalArgumentException("无效的月份格式");
|
}
|
}
|
|
private LocalTime parseTime(String timeStr) {
|
try {
|
return LocalTime.parse(timeStr);
|
} catch (DateTimeParseException e) {
|
throw new IllegalArgumentException("时间格式必须为 HH:mm", e);
|
}
|
}
|
|
private String[] validateAndSplit(String input, String delimiter, int expectedParts) {
|
String[] parts = input.split(delimiter);
|
if (parts.length != expectedParts) {
|
throw new IllegalArgumentException(String.format("格式错误,应为 %d 部分,以 '%s' 分隔", expectedParts, delimiter));
|
}
|
return parts;
|
}
|
|
private int validateDayOfMonth(String dayStr) {
|
int day = Integer.parseInt(dayStr);
|
if (day < 1 || day > 31) {
|
throw new IllegalArgumentException("日期必须在 1-31 之间");
|
}
|
return day;
|
}
|
|
private String convertDayNamesToCron(String dayNames) {
|
return Arrays.stream(dayNames.split("\\|"))
|
.map(this::convertSingleDayName)
|
.collect(Collectors.joining(","));
|
}
|
|
private String convertSingleDayName(String dayName) {
|
switch (dayName.toUpperCase()) {
|
case "MON":
|
return "MON";
|
case "TUE":
|
return "TUE";
|
case "WED":
|
return "WED";
|
case "THU":
|
return "THU";
|
case "FRI":
|
return "FRI";
|
case "SAT":
|
return "SAT";
|
case "SUN":
|
return "SUN";
|
default:
|
throw new IllegalArgumentException("无效的星期: " + dayName);
|
}
|
}
|
}
|