liding
3 天以前 49e1bc66ebaf696ebd3fc3ed33d65c8795fd3cde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package com.ruoyi.business.service.impl;
 
import com.ruoyi.business.entity.TimingTask;
import com.ruoyi.business.task.TimingTaskJob;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
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());
 
        // 获取现有触发器并转换为 CronTrigger
        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
        if (!(oldTrigger instanceof CronTrigger)) {
            throw new SchedulerException("Existing trigger is not a CronTrigger");
        }
 
        // 构建新触发器
        Trigger newTrigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withDescription(task.getTaskName())
                .withSchedule(CronScheduleBuilder.cronSchedule(convertToCronExpression(task)))
                .startAt(Date.from(task.getNexExecutionTime().atZone(ZoneId.systemDefault()).toInstant()))
                .forJob(oldTrigger.getJobKey())
                .build();
 
        scheduler.rescheduleJob(triggerKey, newTrigger);
    }
 
    /**
     * 暂停任务
     */
    public void pauseTimingTask(Long taskId) throws SchedulerException {
        JobKey jobKey = new JobKey("timingTask_" + taskId);
        scheduler.pauseJob(jobKey);
    }
 
    /**
     * 恢复任务
     */
    public void resumeTimingTask(Long taskId) throws SchedulerException {
        JobKey jobKey = new JobKey("timingTask_" + taskId);
        scheduler.resumeJob(jobKey);
    }
 
    /**
     * 删除任务
     */
    public void unscheduleTimingTask(Long taskId) throws SchedulerException {
        JobKey jobKey = new JobKey("timingTask_" + taskId);
        scheduler.deleteJob(jobKey);
    }
 
    private JobDetail buildJobDetail(TimingTask task) {
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("taskId", task.getId());
 
        return JobBuilder.newJob(TimingTaskJob.class)
                .withIdentity("timingTask_" + task.getId())
                .withDescription(task.getTaskName())
                .usingJobData(jobDataMap)
                .storeDurably()
                .build();
    }
 
    private Trigger buildJobTrigger(TimingTask task, JobDetail jobDetail) {
        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.getNexExecutionTime() != null) {
            triggerBuilder.startAt(Date.from(task.getNexExecutionTime().atZone(ZoneId.systemDefault()).toInstant()));
        }
 
        return triggerBuilder.build();
    }
 
    private String convertToCronExpression(TimingTask task) {
        // 参数校验
        if (task == null || task.getFrequencyType() == null || task.getFrequencyDetail() == null) {
            throw new IllegalArgumentException("任务参数不能为空");
        }
 
        // 使用switch确保条件互斥
        return switch (task.getFrequencyType().toUpperCase()) { // 统一转为大写比较
            case "DAILY" -> convertDailyToCron(task.getFrequencyDetail());
            case "WEEKLY" -> convertWeeklyToCron(task.getFrequencyDetail());
            case "MONTHLY" -> convertMonthlyToCron(task.getFrequencyDetail());
            case "QUARTERLY" -> convertQuarterlyToCron(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]);  // 验证月份(1-12)
        int day = validateDayOfMonth(parts[1]);  // 验证日期
        LocalTime time = parseTime(parts[2]);  // 解析时间
 
        // 计算季度起始月份(1月=1, 4月=4, 7月=7, 10月=10)
        int quarterStartMonth = ((month - 1) / 3) * 3 + 1;
 
        return String.format("0 %d %d %d %d/3 ?",
                time.getMinute(),
                time.getHour(),
                day,
                quarterStartMonth);
    }
 
    // 新增验证月份的方法(1-12)
    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 int validateMonthInQuarter(String monthStr) {
        int month = Integer.parseInt(monthStr);
        if (month < 1 || month > 3) {
            throw new IllegalArgumentException("季度月份必须是1、2或3");
        }
        return month;
    }
 
    // 转换星期几名称
    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);
        }
    }
}