From 41ba09635ca0e5d79117a4364c25b1a5e93c4f13 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 29 九月 2026 18:22:38 +0800
Subject: [PATCH] ``` refactor(scheduler): 优化定时任务调度器的重复任务处理和启动注册机制
---
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java | 46 ++++++++---
src/main/java/com/ruoyi/device/init/MaintenanceTaskRegistrar.java | 48 ++++++++++++
src/main/java/com/ruoyi/inspectiontask/init/TimingTaskRegistrar.java | 48 ++++++++++++
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java | 55 ++++++++-----
4 files changed, 164 insertions(+), 33 deletions(-)
diff --git a/src/main/java/com/ruoyi/device/init/MaintenanceTaskRegistrar.java b/src/main/java/com/ruoyi/device/init/MaintenanceTaskRegistrar.java
new file mode 100644
index 0000000..59b4c6a
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/init/MaintenanceTaskRegistrar.java
@@ -0,0 +1,48 @@
+package com.ruoyi.device.init;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ruoyi.device.mapper.MaintenanceTaskMapper;
+import com.ruoyi.device.pojo.MaintenanceTask;
+import com.ruoyi.device.service.impl.MaintenanceTaskScheduler;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 鍚姩鏃舵妸宸插惎鐢ㄧ殑淇濆吇瀹氭椂浠诲姟娉ㄥ唽鍒拌皟搴﹀櫒锛屼繚璇佸巻鍙叉暟鎹紙鐩存帴鍐欏簱銆佷粠鏈粡杩� service锛変篃鑳借鎵ц
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class MaintenanceTaskRegistrar implements ApplicationRunner {
+
+ private final MaintenanceTaskMapper maintenanceTaskMapper;
+ private final MaintenanceTaskScheduler maintenanceTaskScheduler;
+
+ @Override
+ public void run(ApplicationArguments args) {
+ try {
+ List<MaintenanceTask> tasks = maintenanceTaskMapper.selectList(
+ new QueryWrapper<MaintenanceTask>().eq("deleted", 0).eq("is_active", 1));
+ int registered = 0;
+ for (MaintenanceTask task : tasks) {
+ try {
+ if (maintenanceTaskScheduler.isRegistered(task.getId())) {
+ continue;
+ }
+ maintenanceTaskScheduler.scheduleMaintenanceTask(task);
+ registered++;
+ } catch (Exception e) {
+ log.warn("淇濆吇瀹氭椂浠诲姟娉ㄥ唽澶辫触 id={} name={}", task.getId(), task.getTaskName(), e);
+ }
+ }
+ log.info("淇濆吇瀹氭椂浠诲姟娉ㄥ唽瀹屾垚锛氬緟娉ㄥ唽 {} 鏉★紝鏈鏂版敞鍐� {} 鏉�", tasks.size(), registered);
+ } catch (Exception e) {
+ log.error("淇濆吇瀹氭椂浠诲姟鍚姩娉ㄥ唽澶辫触", e);
+ }
+ }
+}
diff --git a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
index ec7a8e3..4b053fc 100644
--- a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
+++ b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
@@ -6,6 +6,7 @@
import org.quartz.*;
import org.springframework.stereotype.Service;
+import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
@@ -31,6 +32,10 @@
try {
JobDetail jobDetail = buildJobDetail(task);
Trigger trigger = buildJobTrigger(task, jobDetail);
+ if (scheduler.checkExists(jobDetail.getKey())) {
+ // 鎸佷箙鍖栫殑Job鍙兘杩樺湪锛堜緥濡傚巻鍙叉暟鎹洿鎺ュ啓鍏ャ�佷笂娆℃湭娓呯悊锛夛紝鍏堢Щ闄ゆ棫鐨凧ob鍙婂叾瑙﹀彂鍣�
+ scheduler.deleteJob(jobDetail.getKey());
+ }
scheduler.scheduleJob(jobDetail, trigger);
}catch (SchedulerException e){
log.error("SchedulerException scheduleMaintenanceTask ERROR",e);
@@ -45,30 +50,49 @@
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");
+ // 瑙﹀彂鍣ㄤ笉瀛樺湪鎴栫被鍨嬩笉绗︼紙濡備换鍔$敱鐩存帴鍐欏簱鍒涘缓锛夛紝閲嶆柊娉ㄥ唽鑰屼笉鏄姤閿�
+ scheduleMaintenanceTask(task);
+ return;
}
- // 3. 鏋勫缓CronTrigger锛岀‘淇濇寔涔呭寲閰嶇疆
+ // 鏋勫缓CronTrigger锛岀‘淇濇寔涔呭寲閰嶇疆
CronTrigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey) // 鍞竴鏍囪瘑锛岀敤浜庢寔涔呭寲瀛樺偍
.withDescription(task.getTaskName() + "_TRIGGER") // 瑙﹀彂鍣ㄦ弿杩�
.forJob(oldTrigger.getJobKey()) // 鍏宠仈瀵瑰簲鐨凧ob
.withSchedule(CronScheduleBuilder
- .cronSchedule(convertToCronExpression(task)) // 閿欒繃鎵ц鏃剁殑绛栫暐锛堟牴鎹笟鍔¤皟鏁达級
+ .cronSchedule(convertToCronExpression(task))
+ .withMisfireHandlingInstructionDoNothing() // 閿欒繃鎵ц鏃剁殑绛栫暐锛堟牴鎹笟鍔¤皟鏁达級
)
- // 4. 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鍒欑珛鍗崇敓鏁堬級
- .startAt(task.getNextExecutionTime() != null
- ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
- : new Date())
+ // 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鎴栧凡杩囨湡鍒欑珛鍗崇敓鏁堬級
+ .startAt(resolveStartTime(task))
.build();
scheduler.rescheduleJob(triggerKey, newTrigger);
}catch (SchedulerException e){
log.error("SchedulerException rescheduleMaintenanceTask ERROR",e);
throw new RuntimeException(e);
}
+ }
+
+ /**
+ * 鍒ゆ柇浠诲姟鏄惁宸叉敞鍐屽埌璋冨害鍣�
+ */
+ public boolean isRegistered(Long taskId) throws SchedulerException {
+ return scheduler.checkExists(new TriggerKey("triggerMaintenanceTask_" + taskId));
+ }
+
+ /**
+ * 瑙﹀彂鍣ㄥ紑濮嬫椂闂达細涓虹┖鎴栧凡杩囨湡鏃朵粠褰撳墠鏃堕棿璧风畻锛岄伩鍏嶈ˉ璺戝巻鍙�
+ */
+ private Date resolveStartTime(MaintenanceTask task) {
+ LocalDateTime nextExecutionTime = task.getNextExecutionTime();
+ if (nextExecutionTime == null || !nextExecutionTime.isAfter(LocalDateTime.now())) {
+ return new Date();
+ }
+ return Date.from(nextExecutionTime.atZone(ZoneId.systemDefault()).toInstant());
}
/**
@@ -137,10 +161,8 @@
.cronSchedule(cronExpression)
.withMisfireHandlingInstructionDoNothing() // 閿欒繃鎵ц鏃剁殑绛栫暐锛堟牴鎹笟鍔¤皟鏁达級
)
- // 4. 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鍒欑珛鍗崇敓鏁堬級
- .startAt(task.getNextExecutionTime() != null
- ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
- : new Date())
+ // 4. 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鎴栧凡杩囨湡鍒欑珛鍗崇敓鏁堬級
+ .startAt(resolveStartTime(task))
.build();
}
private String convertToCronExpression(MaintenanceTask task) {
diff --git a/src/main/java/com/ruoyi/inspectiontask/init/TimingTaskRegistrar.java b/src/main/java/com/ruoyi/inspectiontask/init/TimingTaskRegistrar.java
new file mode 100644
index 0000000..9cdc752
--- /dev/null
+++ b/src/main/java/com/ruoyi/inspectiontask/init/TimingTaskRegistrar.java
@@ -0,0 +1,48 @@
+package com.ruoyi.inspectiontask.init;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ruoyi.inspectiontask.mapper.TimingTaskMapper;
+import com.ruoyi.inspectiontask.pojo.TimingTask;
+import com.ruoyi.inspectiontask.service.impl.TimingTaskScheduler;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 鍚姩鏃舵妸宸插惎鐢ㄧ殑宸℃瀹氭椂浠诲姟娉ㄥ唽鍒拌皟搴﹀櫒锛屼繚璇佸巻鍙叉暟鎹紙鐩存帴鍐欏簱銆佷粠鏈粡杩� service锛変篃鑳借鎵ц
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class TimingTaskRegistrar implements ApplicationRunner {
+
+ private final TimingTaskMapper timingTaskMapper;
+ private final TimingTaskScheduler timingTaskScheduler;
+
+ @Override
+ public void run(ApplicationArguments args) {
+ try {
+ List<TimingTask> tasks = timingTaskMapper.selectList(
+ new QueryWrapper<TimingTask>().eq("deleted", 0).eq("is_enabled", 1));
+ int registered = 0;
+ for (TimingTask task : tasks) {
+ try {
+ if (timingTaskScheduler.isRegistered(task.getId())) {
+ continue;
+ }
+ timingTaskScheduler.scheduleTimingTask(task);
+ registered++;
+ } catch (Exception e) {
+ log.warn("宸℃瀹氭椂浠诲姟娉ㄥ唽澶辫触 id={} name={}", task.getId(), task.getTaskName(), e);
+ }
+ }
+ log.info("宸℃瀹氭椂浠诲姟娉ㄥ唽瀹屾垚锛氬緟娉ㄥ唽 {} 鏉★紝鏈鏂版敞鍐� {} 鏉�", tasks.size(), registered);
+ } catch (Exception e) {
+ log.error("宸℃瀹氭椂浠诲姟鍚姩娉ㄥ唽澶辫触", e);
+ }
+ }
+}
diff --git a/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java b/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
index a6179f6..fdcf395 100644
--- a/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
+++ b/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
@@ -5,6 +5,7 @@
import org.quartz.*;
import org.springframework.stereotype.Service;
+import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
@@ -24,6 +25,10 @@
public void scheduleTimingTask(TimingTask task) throws SchedulerException {
JobDetail jobDetail = buildJobDetail(task);
Trigger trigger = buildJobTrigger(task, jobDetail);
+ if (scheduler.checkExists(jobDetail.getKey())) {
+ // 鎸佷箙鍖栫殑Job鍙兘杩樺湪锛堜緥濡傚巻鍙叉暟鎹洿鎺ュ啓鍏ャ�佷笂娆℃湭娓呯悊锛夛紝鍏堢Щ闄ゆ棫鐨凧ob鍙婂叾瑙﹀彂鍣�
+ scheduler.deleteJob(jobDetail.getKey());
+ }
scheduler.scheduleJob(jobDetail, trigger);
}
@@ -33,36 +38,46 @@
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");
+ // 瑙﹀彂鍣ㄤ笉瀛樺湪鎴栫被鍨嬩笉绗︼紙濡備换鍔$敱鐩存帴鍐欏簱鍒涘缓锛夛紝閲嶆柊娉ㄥ唽鑰屼笉鏄姤閿�
+ scheduleTimingTask(task);
+ return;
}
- // 3. 鏋勫缓CronTrigger锛岀‘淇濇寔涔呭寲閰嶇疆
+ // 鏋勫缓CronTrigger锛岀‘淇濇寔涔呭寲閰嶇疆
CronTrigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey) // 鍞竴鏍囪瘑锛岀敤浜庢寔涔呭寲瀛樺偍
.withDescription(task.getTaskName() + "_TRIGGER") // 瑙﹀彂鍣ㄦ弿杩�
.forJob(oldTrigger.getJobKey()) // 鍏宠仈瀵瑰簲鐨凧ob
.withSchedule(CronScheduleBuilder
- .cronSchedule(convertToCronExpression(task)) // 閿欒繃鎵ц鏃剁殑绛栫暐锛堟牴鎹笟鍔¤皟鏁达級
+ .cronSchedule(convertToCronExpression(task))
+ .withMisfireHandlingInstructionDoNothing() // 閿欒繃鎵ц鏃剁殑绛栫暐锛堟牴鎹笟鍔¤皟鏁达級
)
- // 4. 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鍒欑珛鍗崇敓鏁堬級
- .startAt(task.getNextExecutionTime() != null
- ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
- : new Date())
+ // 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鎴栧凡杩囨湡鍒欑珛鍗崇敓鏁堬級
+ .startAt(resolveStartTime(task))
.build();
- // 鏋勫缓鏂拌Е鍙戝櫒
-// Trigger newTrigger = TriggerBuilder.newTrigger()
-// .withIdentity(triggerKey)
-// .withDescription(task.getTaskName())
-// .withSchedule(CronScheduleBuilder.cronSchedule(convertToCronExpression(task)))
-// .startAt(Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant()))
-// .forJob(oldTrigger.getJobKey())
-// .build();
-
scheduler.rescheduleJob(triggerKey, newTrigger);
+ }
+
+ /**
+ * 鍒ゆ柇浠诲姟鏄惁宸叉敞鍐屽埌璋冨害鍣�
+ */
+ public boolean isRegistered(Long taskId) throws SchedulerException {
+ return scheduler.checkExists(new TriggerKey("trigger_" + taskId));
+ }
+
+ /**
+ * 瑙﹀彂鍣ㄥ紑濮嬫椂闂达細涓虹┖鎴栧凡杩囨湡鏃朵粠褰撳墠鏃堕棿璧风畻锛岄伩鍏嶈ˉ璺戝巻鍙�
+ */
+ private Date resolveStartTime(TimingTask task) {
+ LocalDateTime nextExecutionTime = task.getNextExecutionTime();
+ if (nextExecutionTime == null || !nextExecutionTime.isAfter(LocalDateTime.now())) {
+ return new Date();
+ }
+ return Date.from(nextExecutionTime.atZone(ZoneId.systemDefault()).toInstant());
}
/**
@@ -130,10 +145,8 @@
.cronSchedule(cronExpression)
.withMisfireHandlingInstructionDoNothing() // 閿欒繃鎵ц鏃剁殑绛栫暐锛堟牴鎹笟鍔¤皟鏁达級
)
- // 4. 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鍒欑珛鍗崇敓鏁堬級
- .startAt(task.getNextExecutionTime() != null
- ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
- : new Date())
+ // 4. 璁剧疆寮�濮嬫椂闂达紙鑻ヤ负null鎴栧凡杩囨湡鍒欑珛鍗崇敓鏁堬級
+ .startAt(resolveStartTime(task))
.build();
}
private String convertToCronExpression(TimingTask task) {
--
Gitblit v1.9.3