package com.ruoyi.safe.service.impl;
|
|
import com.ruoyi.safe.pojo.SafeLineInspectionTask;
|
import lombok.RequiredArgsConstructor;
|
import org.quartz.CronScheduleBuilder;
|
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.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
|
@RequiredArgsConstructor
|
public class SafeLineInspectionTaskScheduler {
|
|
private static final String JOB_GROUP = "safeLineInspectionTask";
|
private static final String TRIGGER_GROUP = "safeLineInspectionTaskTrigger";
|
|
private final Scheduler scheduler;
|
|
public void scheduleTask(SafeLineInspectionTask task) throws SchedulerException {
|
JobDetail jobDetail = buildJobDetail(task);
|
if (scheduler.checkExists(jobDetail.getKey())) {
|
scheduler.deleteJob(jobDetail.getKey());
|
}
|
scheduler.scheduleJob(jobDetail, buildJobTrigger(task, jobDetail));
|
}
|
|
public void rescheduleTask(SafeLineInspectionTask task) throws SchedulerException {
|
TriggerKey triggerKey = getTriggerKey(task.getId());
|
Trigger oldTrigger = scheduler.getTrigger(triggerKey);
|
if (oldTrigger == null) {
|
scheduleTask(task);
|
return;
|
}
|
JobDetail jobDetail = scheduler.getJobDetail(oldTrigger.getJobKey());
|
if (jobDetail == null) {
|
scheduleTask(task);
|
return;
|
}
|
scheduler.rescheduleJob(triggerKey, buildJobTrigger(task, jobDetail));
|
}
|
|
public void unscheduleTask(Long taskId) {
|
try {
|
scheduler.deleteJob(getJobKey(taskId));
|
} catch (SchedulerException e) {
|
throw new RuntimeException(e);
|
}
|
}
|
|
private JobDetail buildJobDetail(SafeLineInspectionTask task) {
|
JobDataMap jobDataMap = new JobDataMap();
|
jobDataMap.put("taskId", task.getId());
|
jobDataMap.put("taskName", task.getInspectionName());
|
jobDataMap.put("frequencyType", task.getFrequencyType());
|
|
return JobBuilder.newJob(SafeLineInspectionTaskJob.class)
|
.withIdentity(getJobKey(task.getId()))
|
.withDescription(task.getInspectionName())
|
.usingJobData(jobDataMap)
|
.storeDurably(true)
|
.requestRecovery(true)
|
.build();
|
}
|
|
private Trigger buildJobTrigger(SafeLineInspectionTask task, JobDetail jobDetail) {
|
return TriggerBuilder.newTrigger()
|
.withIdentity(getTriggerKey(task.getId()))
|
.withDescription(task.getInspectionName() + "_TRIGGER")
|
.forJob(jobDetail)
|
.withSchedule(CronScheduleBuilder
|
.cronSchedule(convertToCronExpression(task))
|
.withMisfireHandlingInstructionDoNothing())
|
.startAt(task.getNextExecutionTime() != null
|
? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
|
: new Date())
|
.build();
|
}
|
|
private JobKey getJobKey(Long taskId) {
|
return new JobKey("safeLineInspectionTask_" + taskId, JOB_GROUP);
|
}
|
|
private TriggerKey getTriggerKey(Long taskId) {
|
return new TriggerKey("safeLineInspectionTaskTrigger_" + taskId, TRIGGER_GROUP);
|
}
|
|
private String convertToCronExpression(SafeLineInspectionTask task) {
|
if (task == null || task.getFrequencyType() == null || task.getFrequencyDetail() == null) {
|
throw new IllegalArgumentException("任务频率不能为空");
|
}
|
return switch (task.getFrequencyType().toUpperCase()) {
|
case "DAILY" -> convertDailyToCron(task.getFrequencyDetail());
|
case "WEEKLY" -> convertWeeklyToCron(task.getFrequencyDetail());
|
case "MONTHLY" -> convertMonthlyToCron(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 = Arrays.stream(parts[0].split("\\|"))
|
.map(this::convertSingleDayName)
|
.collect(Collectors.joining(","));
|
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[] validateAndSplit(String input, int expectedParts) {
|
String[] parts = input.split(",");
|
if (parts.length != expectedParts) {
|
throw new IllegalArgumentException("任务频率详情格式错误");
|
}
|
return Arrays.stream(parts).map(String::trim).toArray(String[]::new);
|
}
|
|
private LocalTime parseTime(String timeStr) {
|
try {
|
return LocalTime.parse(timeStr);
|
} catch (DateTimeParseException e) {
|
throw new IllegalArgumentException("时间格式必须为HH:mm", e);
|
}
|
}
|
|
private int validateDayOfMonth(String dayStr) {
|
int day = Integer.parseInt(dayStr);
|
if (day < 1 || day > 31) {
|
throw new IllegalArgumentException("日期必须在1-31之间");
|
}
|
return day;
|
}
|
|
private String convertSingleDayName(String dayName) {
|
return switch (dayName.toUpperCase()) {
|
case "MON" -> "MON";
|
case "TUE" -> "TUE";
|
case "WED" -> "WED";
|
case "THU" -> "THU";
|
case "FRI" -> "FRI";
|
case "SAT" -> "SAT";
|
case "SUN" -> "SUN";
|
default -> throw new IllegalArgumentException("无效的星期几: " + dayName);
|
};
|
}
|
}
|