package com.ruoyi.inspectiontask.service.impl; import com.ruoyi.inspectiontask.pojo.TimingTask; 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 public class TimingTaskScheduler { @Autowired private Scheduler scheduler; public void scheduleTimingTask(TimingTask task) throws SchedulerException { JobDetail jobDetail = buildJobDetail(task); Trigger trigger = buildJobTrigger(task, jobDetail); scheduler.scheduleJob(jobDetail, trigger); } public void rescheduleTimingTask(TimingTask task) throws SchedulerException { TriggerKey triggerKey = new TriggerKey("trigger_" + 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); } public void pauseTimingTask(Long taskId) throws SchedulerException { scheduler.pauseJob(new JobKey("timingTask_" + taskId)); } public void resumeTimingTask(Long taskId) throws SchedulerException { scheduler.resumeJob(new JobKey("timingTask_" + taskId)); } public void unscheduleTimingTask(Long taskId) { try { scheduler.deleteJob(new JobKey("timingTask_" + taskId)); } catch (SchedulerException e) { throw new RuntimeException(e); } } private JobDetail buildJobDetail(TimingTask task) { JobKey jobKey = new JobKey("timingTask_" + task.getId()); JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("taskId", task.getId()); jobDataMap.put("taskName", task.getTaskName()); jobDataMap.put("taskType", task.getFrequencyType()); return JobBuilder.newJob(TimingTaskJob.class) .withIdentity(jobKey) .withDescription(task.getTaskName()) .usingJobData(jobDataMap) .storeDurably(true) .requestRecovery(true) .build(); } private Trigger buildJobTrigger(TimingTask task, JobDetail jobDetail) { TriggerKey triggerKey = new TriggerKey("trigger_" + 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(TimingTask 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); } } }