浏览代码

fix(akds): prioritize personnel sync scheduling

malk 2 周之前
父节点
当前提交
fdb2eb8497

+ 6 - 6
mjava-akdsbeisen/src/main/java/com/malk/timer/PersonnelSyncTimer.java

@@ -13,7 +13,7 @@ import java.util.Map;
 
 /**
  * 钉钉 -> 宜搭 人员档案 定时全量增量同步
- * 工作日 (MON-FRI) 每天 06:00 / 13:00 / 19:00 各一次 fullSync (limit 取配置 personnel-sync.limitFirstN, 生产为 0 即真·全量)
+ * 工作日 (MON-FRI) 每天 05:30 / 13:15 / 18:30 各一次 fullSync (limit 取配置 personnel-sync.limitFirstN, 生产为 0 即真·全量)
  * 共 3 次/工作日; 周末不跑
  */
 @Slf4j
@@ -32,12 +32,12 @@ public class PersonnelSyncTimer {
     private static final int MAX_ATTEMPTS = 3;
     private static final long RETRY_DELAY_MS = 60_000L;
 
-    /** 工作日 06:00 / 13:00 / 19:00 各一次全量同步(单方法挂 3 条 cron,离散时点无法用单条 cron 表达) */
-    @Scheduled(cron = "0 0 6 ? * MON-FRI")
-    @Scheduled(cron = "0 0 13 ? * MON-FRI")
-    @Scheduled(cron = "0 0 19 ? * MON-FRI")
+    /** 工作日 05:30 / 13:15 / 18:30 各一次全量同步(单方法挂 3 条 cron,离散时点无法用单条 cron 表达) */
+    @Scheduled(cron = "0 30 5 ? * MON-FRI")
+    @Scheduled(cron = "0 15 13 ? * MON-FRI")
+    @Scheduled(cron = "0 30 18 ? * MON-FRI")
     public void scheduledFullSync() {
-        runFullSync("06/13/19");
+        runFullSync("05:30/13:15/18:30");
     }
 
     private void runFullSync(String tag) {

+ 54 - 5
mjava-akdsbeisen/src/main/java/com/malk/timer/ReSubmitApprovalTimer.java

@@ -8,8 +8,10 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Bean;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 
 import java.util.Map;
 import java.util.Set;
@@ -18,7 +20,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 /**
  * 补发起工时审批定时(bug 1 修复)
  * <p>
- * 每天 05:20、12:55 触发;先让 04:20、12:20 回写兜底消化已完成审批,再扫描最近 7 个自然日发生修改的汇总记录,
+ * 每天 07:00、16:00 触发;先让 04:20、12:20 回写兜底消化已完成审批,再扫描最近 7 个自然日发生修改的汇总记录,
  * 按实际业务 (month, userId) 去重,
  * 走 ReSubmitApprovalService.resubmit 逻辑:queryProcess(RUNNING) 有则 update / 无则 create。
  * <p>
@@ -45,12 +47,28 @@ public class ReSubmitApprovalTimer {
     @Autowired
     private MonthClosingService monthClosingService;
 
+    @Autowired
+    private ThreadPoolTaskScheduler resubmitTaskScheduler;
+
     private static final AtomicBoolean RUNNING = new AtomicBoolean(false);
+    private static final AtomicBoolean DISPATCHED = new AtomicBoolean(false);
+
+    /**
+     * 长补发任务使用独立调度线程,避免占用人员同步及其他短任务的默认调度线程。
+     */
+    @Bean(name = "resubmitTaskScheduler")
+    public ThreadPoolTaskScheduler resubmitTaskScheduler() {
+        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
+        scheduler.setPoolSize(1);
+        scheduler.setThreadNamePrefix("resubmit-scheduler-");
+        scheduler.setWaitForTasksToCompleteOnShutdown(true);
+        scheduler.setAwaitTerminationSeconds(30);
+        return scheduler;
+    }
 
     /**
-     * 每天凌晨 05:20 兜底补发起
+     * 每天 07:00 兜底补发起
      */
-    @Scheduled(cron = "0 20 5 * * ?")
     public void scanAndReSubmit() {
         if (!RUNNING.compareAndSet(false, true)) {
             log.warn("[补发起兜底] 上次执行未结束,跳过本次触发");
@@ -99,12 +117,43 @@ public class ReSubmitApprovalTimer {
     }
 
     /**
-     * prd 每天中午 12:55 再执行一次补发兜底,与 12:45 应报工时增量任务错开。
+     * prd 每天 16:00 再执行一次补发兜底,与中午时段的应报工时任务错开。
      * 封账月份仍由 {@link #scanAndReSubmit()} 内的实时封账校验统一跳过。
      */
-    @Scheduled(cron = "0 55 12 * * ?")
     public void scanAndReSubmitAtNoon() {
         scanAndReSubmit();
     }
 
+    /**
+     * 默认调度线程只负责投递,实际长任务在线程池中执行,避免阻塞其他定时任务。
+     */
+    @Scheduled(cron = "0 0 7 * * ?")
+    public void scheduledMorningResubmit() {
+        dispatchResubmitTask();
+    }
+
+    @Scheduled(cron = "0 0 16 * * ?")
+    public void scheduledAfternoonResubmit() {
+        dispatchResubmitTask();
+    }
+
+    private void dispatchResubmitTask() {
+        if (!DISPATCHED.compareAndSet(false, true)) {
+            log.warn("[补发起兜底] 长任务已在执行或排队,跳过本次投递");
+            return;
+        }
+        try {
+            resubmitTaskScheduler.execute(() -> {
+                try {
+                    scanAndReSubmit();
+                } finally {
+                    DISPATCHED.set(false);
+                }
+            });
+        } catch (RuntimeException ex) {
+            DISPATCHED.set(false);
+            log.error("[补发起兜底] 长任务投递失败", ex);
+        }
+    }
+
 }

+ 0 - 12
mjava-akdsbeisen/src/main/java/com/malk/timer/WorkHoursCalcTimer.java

@@ -18,18 +18,6 @@ public class WorkHoursCalcTimer {
     @Autowired
     private WorkHoursCalcService workHoursCalcService;
 
-    // 每月1号凌晨2点:全量同步当月应填报工时
-    @Scheduled(cron = "0 0 2 1 * ?")
-    public void calcMonthlyFullSync() {
-        log.info("开始执行应填报工时【全量】同步任务");
-        try {
-            workHoursCalcService.calculateAndSyncMonthlyHours(null);
-            log.info("应填报工时全量同步任务执行完成");
-        } catch (Exception e) {
-            log.error("应填报工时全量同步任务执行失败", e);
-        }
-    }
-
     // prd: 增量同步每天 2 次(03:30 / 12:45),全员 × 近 7 个自然日内工作日窗口补漏
     // fixme: 已存在记录一律 skip (补漏语义, 字段刷新走 /workhours/backfill-* 接口);
     //        单条 cron 的时/分字段相互独立无法表达多个离散时点, 用 @Scheduled 可重复注解挂 2 条独立 cron

+ 3 - 3
mjava-akdsbeisen/src/test/java/com/malk/timer/ReSubmitApprovalTimerTest.java

@@ -24,11 +24,11 @@ import static org.mockito.Mockito.when;
 class ReSubmitApprovalTimerTest {
 
     @Test
-    void shouldScheduleNoonFallbackAtTwelveFiftyFive() throws NoSuchMethodException {
-        Method method = ReSubmitApprovalTimer.class.getDeclaredMethod("scanAndReSubmitAtNoon");
+    void shouldScheduleAfternoonFallbackAtSixteenHundred() throws NoSuchMethodException {
+        Method method = ReSubmitApprovalTimer.class.getDeclaredMethod("scheduledAfternoonResubmit");
         Scheduled scheduled = method.getAnnotation(Scheduled.class);
 
-        assertEquals("0 55 12 * * ?", scheduled.cron());
+        assertEquals("0 0 16 * * ?", scheduled.cron());
     }
 
     @Test

+ 4 - 4
mjava-akdsbeisen/src/test/java/com/malk/timer/WorkHoursTimerScheduleTest.java

@@ -70,12 +70,12 @@ public class WorkHoursTimerScheduleTest {
     @Test
     public void approvalWriteBackShouldRunBeforeApprovalResubmit() throws Exception {
         Method writeBack = ApprovalWriteBackTimer.class.getMethod("scanAndWriteBack");
-        Method resubmit = ReSubmitApprovalTimer.class.getMethod("scanAndReSubmit");
-        Method noonResubmit = ReSubmitApprovalTimer.class.getMethod("scanAndReSubmitAtNoon");
+        Method resubmit = ReSubmitApprovalTimer.class.getMethod("scheduledMorningResubmit");
+        Method noonResubmit = ReSubmitApprovalTimer.class.getMethod("scheduledAfternoonResubmit");
 
         assertEquals(setOf("0 20 4 * * ?", "0 20 12 * * ?"), scheduledCrons(writeBack));
-        assertEquals(setOf("0 20 5 * * ?"), scheduledCrons(resubmit));
-        assertEquals(setOf("0 55 12 * * ?"), scheduledCrons(noonResubmit));
+        assertEquals(setOf("0 0 7 * * ?"), scheduledCrons(resubmit));
+        assertEquals(setOf("0 0 16 * * ?"), scheduledCrons(noonResubmit));
     }
 
     @Test