From 5a5df7b6529a62233bd2541a9abbb7bd588e9a4d Mon Sep 17 00:00:00 2001
From: maven <2163098428@qq.com>
Date: 星期一, 22 十二月 2025 17:54:27 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/hckx' into hckx
---
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java | 278 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 278 insertions(+), 0 deletions(-)
diff --git a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
new file mode 100644
index 0000000..384862b
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
@@ -0,0 +1,278 @@
+package com.ruoyi.device.service.impl;
+
+import com.ruoyi.device.pojo.MaintenanceTask;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.*;
+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;
+
+/**
+ * @author :yys
+ * @date : 2025/12/22 15:16
+ */
+@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());
+
+ // 鑾峰彇鐜版湁瑙﹀彂鍣ㄥ苟杞崲涓� CronTrigger
+ Trigger oldTrigger = scheduler.getTrigger(triggerKey);
+ if (!(oldTrigger instanceof CronTrigger)) {
+ throw new SchedulerException("Existing trigger is not a CronTrigger");
+ }
+
+ // 3. 鏋勫缓CronTrigger锛岀‘淇濇寔涔呭寲閰嶇疆
+ CronTrigger newTrigger = TriggerBuilder.newTrigger()
+ .withIdentity(triggerKey) // 鍞竴鏍囪瘑锛岀敤浜庢寔涔呭寲瀛樺偍
+ .withDescription(task.getTaskName() + "_TRIGGER") // 瑙﹀彂鍣ㄦ弿杩�
+ .forJob(oldTrigger.getJobKey()) // 鍏宠仈瀵瑰簲鐨凧ob
+ .withSchedule(CronScheduleBuilder
+ .cronSchedule(convertToCronExpression(task)) // 閿欒繃鎵ц鏃剁殑绛栫暐锛堟牴鎹笟鍔¤皟鏁达級
+ )
+ // 4. 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鍒欑珛鍗崇敓鏁堬級
+ .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 {
+ JobKey jobKey = new JobKey("MaintenanceTask_" + taskId);
+ scheduler.pauseJob(jobKey);
+ }
+
+ /**
+ * 鎭㈠浠诲姟
+ */
+ public void resumeMaintenanceTask(Long taskId) throws SchedulerException {
+ JobKey jobKey = new JobKey("MaintenanceTask_" + taskId);
+ scheduler.resumeJob(jobKey);
+ }
+
+ /**
+ * 鍒犻櫎浠诲姟
+ */
+ public void unscheduleMaintenanceTask(Long taskId){
+ try {
+ JobKey jobKey = new JobKey("MaintenanceTask_" + taskId);
+ scheduler.deleteJob(jobKey);
+ }catch (SchedulerException e){
+ log.error("SchedulerException unscheduleMaintenanceTask ERROR",e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private JobDetail buildJobDetail(MaintenanceTask task) {
+ // 1. 鏋勫缓鍞竴JobKey锛堝熀浜庝换鍔D锛岀‘淇濋噸鍚悗鑳借瘑鍒級
+ JobKey jobKey = new JobKey("MaintenanceTask_" + task.getId());
+
+ // 2. 灏佽浠诲姟鏁版嵁锛堜粎浣跨敤鍩烘湰绫诲瀷锛岀‘淇濆彲搴忓垪鍖栵級
+ JobDataMap jobDataMap = new JobDataMap();
+ jobDataMap.put("maintenanceTaskId", task.getId()); // 浠诲姟ID锛圠ong锛屽彲搴忓垪鍖栵級
+ jobDataMap.put("taskName", task.getTaskName()); // 浠诲姟鍚嶇О锛圫tring锛屽彲搴忓垪鍖栵級
+ jobDataMap.put("taskType", task.getFrequencyType()); // 浠诲姟绫诲瀷锛圫tring锛�
+ // 鎸夐渶娣诲姞鍏朵粬蹇呰鐨勫熀鏈被鍨嬪弬鏁�
+
+ // 3. 鏋勫缓JobDetail锛岃缃寔涔呭寲鐩稿叧灞炴��
+ return JobBuilder.newJob(MaintenanceTaskJob.class)
+ .withIdentity(jobKey) // 鍞竴鏍囪瘑锛岀敤浜庢寔涔呭寲瀛樺偍
+ .withDescription(task.getTaskName()) // 浠诲姟鎻忚堪锛屽瓨鍏ユ暟鎹簱
+ .usingJobData(jobDataMap) // 缁戝畾浠诲姟鏁版嵁
+ .storeDurably(true) // 鍗充娇娌℃湁瑙﹀彂鍣ㄥ叧鑱斾篃鎸佷箙鍖栦繚瀛�
+ .requestRecovery(true) // 褰撹皟搴﹀櫒宕╂簝鍚庢仮澶嶆椂锛岄噸鏂版墽琛屾湭瀹屾垚鐨勪换鍔�
+ .build();
+ }
+
+ private Trigger buildJobTrigger(MaintenanceTask task, JobDetail jobDetail) {
+ // 1. 鏋勫缓鍞竴TriggerKey锛堝熀浜庝换鍔D锛�
+ TriggerKey triggerKey = new TriggerKey("triggerMaintenanceTask_" + task.getId());
+
+ // 2. 鐢熸垚Cron琛ㄨ揪寮忥紙鍘熼�昏緫涓嶅彉锛�
+ String cronExpression = convertToCronExpression(task);
+
+ // 3. 鏋勫缓CronTrigger锛岀‘淇濇寔涔呭寲閰嶇疆
+ return TriggerBuilder.newTrigger()
+ .withIdentity(triggerKey) // 鍞竴鏍囪瘑锛岀敤浜庢寔涔呭寲瀛樺偍
+ .withDescription(task.getTaskName() + "_TRIGGER") // 瑙﹀彂鍣ㄦ弿杩�
+ .forJob(jobDetail) // 鍏宠仈瀵瑰簲鐨凧ob
+ .withSchedule(CronScheduleBuilder
+ .cronSchedule(cronExpression)
+ .withMisfireHandlingInstructionDoNothing() // 閿欒繃鎵ц鏃剁殑绛栫暐锛堟牴鎹笟鍔¤皟鏁达級
+ )
+ // 4. 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鍒欑珛鍗崇敓鏁堬級
+ .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("浠诲姟鍙傛暟涓嶈兘涓虹┖");
+ }
+
+ // 浣跨敤switch纭繚鏉′欢浜掓枼
+ 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());
+ 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]); // 瑙f瀽鏃堕棿
+
+ // 璁$畻瀛e害璧峰鏈堜唤(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("鏃堕棿鏍煎紡蹇呴』涓篐H: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("瀛e害鏈堜唤蹇呴』鏄�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);
+ }
+ }
+
+}
--
Gitblit v1.9.3