From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/support/LimsSxtQuyangJihuaRenwuPlanner.java |   69 ++++++++++++++++++++++++++++++++++
 1 files changed, 69 insertions(+), 0 deletions(-)

diff --git a/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/support/LimsSxtQuyangJihuaRenwuPlanner.java b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/support/LimsSxtQuyangJihuaRenwuPlanner.java
new file mode 100644
index 0000000..a3da5d3
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/support/LimsSxtQuyangJihuaRenwuPlanner.java
@@ -0,0 +1,69 @@
+package jnpf.limsService.support;
+
+import java.time.LocalDate;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/** 姘寸郴缁� Renwu 鍛ㄦ湡鏃ユ湡璁$畻銆� */
+public final class LimsSxtQuyangJihuaRenwuPlanner {
+
+    private LimsSxtQuyangJihuaRenwuPlanner() {
+    }
+
+    public static List<LocalDate> executionDates(LocalDate start, LocalDate end, String cycleType, int interval) {
+        validate(start, end, cycleType, interval);
+        String normalizedCycleType = cycleType.trim().toLowerCase(Locale.ROOT);
+        List<LocalDate> dates = new ArrayList<>();
+        for (LocalDate candidate = start; !candidate.isAfter(end); candidate = candidate.plusDays(1)) {
+            if (matchesCycle(start, candidate, normalizedCycleType, interval)) {
+                dates.add(candidate);
+            }
+        }
+        return dates;
+    }
+
+    private static boolean matchesCycle(LocalDate start, LocalDate candidate, String cycleType, int interval) {
+        long daysDiff = ChronoUnit.DAYS.between(start, candidate);
+        long step = executionStep(interval);
+        switch (cycleType) {
+            case "daily": return daysDiff % step == 0;
+            case "weekly": return candidate.getDayOfWeek() == start.getDayOfWeek() && (daysDiff / 7) % step == 0;
+            case "monthly": return isMatchingDayOfMonth(start, candidate) && monthsBetween(start, candidate) % step == 0;
+            case "quarterly":
+                long monthDiff = monthsBetween(start, candidate);
+                return isMatchingDayOfMonth(start, candidate) && monthDiff % 3 == 0 && (monthDiff / 3) % step == 0;
+            case "yearly": return isMatchingYearlyDate(start, candidate) && (candidate.getYear() - start.getYear()) % step == 0;
+            default: throw new IllegalArgumentException("鏈煡鐨勫彇鏍疯鍒掑懆鏈熺被鍨嬶細" + cycleType);
+        }
+    }
+
+    private static long executionStep(int interval) {
+        return (long) interval + 1;
+    }
+
+    private static long monthsBetween(LocalDate start, LocalDate candidate) {
+        return ChronoUnit.MONTHS.between(start.withDayOfMonth(1), candidate.withDayOfMonth(1));
+    }
+
+    private static boolean isMatchingDayOfMonth(LocalDate start, LocalDate candidate) {
+        return start.getDayOfMonth() == start.lengthOfMonth()
+                ? candidate.getDayOfMonth() == candidate.lengthOfMonth()
+                : start.getDayOfMonth() == candidate.getDayOfMonth();
+    }
+
+    private static boolean isMatchingYearlyDate(LocalDate start, LocalDate candidate) {
+        if (start.getMonthValue() == 2 && start.getDayOfMonth() == 29) {
+            return candidate.getMonthValue() == 2 && candidate.getDayOfMonth() == (candidate.isLeapYear() ? 29 : 28);
+        }
+        return start.getMonthValue() == candidate.getMonthValue() && start.getDayOfMonth() == candidate.getDayOfMonth();
+    }
+
+    private static void validate(LocalDate start, LocalDate end, String cycleType, int interval) {
+        if (start == null || end == null) throw new IllegalArgumentException("鍙栨牱璁″垝璧锋鏃ユ湡涓嶈兘涓虹┖");
+        if (end.isBefore(start)) throw new IllegalArgumentException("鍙栨牱璁″垝缁撴潫鏃ユ湡涓嶈兘鏃╀簬寮�濮嬫棩鏈�");
+        if (cycleType == null || cycleType.trim().isEmpty()) throw new IllegalArgumentException("鍙栨牱璁″垝鍛ㄦ湡绫诲瀷涓嶈兘涓虹┖");
+        if (interval < 0) throw new IllegalArgumentException("鍙栨牱璁″垝鍛ㄦ湡闂撮殧涓嶈兘灏忎簬闆�");
+    }
+}

--
Gitblit v1.8.0