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