From f086c57f64ff30b719d0fd51cbb62a2e5d0240aa Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期二, 20 十月 2026 14:00:26 +0800
Subject: [PATCH] chore: 和鑫电器配置文件

---
 src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java |  459 +++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 397 insertions(+), 62 deletions(-)

diff --git a/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java b/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
index 511fbd7..e4156bd 100644
--- a/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
+++ b/src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
@@ -1,25 +1,46 @@
 package com.ruoyi.inspectiontask.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.inspectiontask.dto.TimingTaskDto;
-import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper;
+import com.ruoyi.inspectiontask.mapper.InspectionItemMapper;
+import com.ruoyi.inspectiontask.mapper.TimingTaskItemMapper;
 import com.ruoyi.inspectiontask.mapper.TimingTaskMapper;
+import com.ruoyi.inspectiontask.pojo.InspectionItem;
 import com.ruoyi.inspectiontask.pojo.TimingTask;
+import com.ruoyi.inspectiontask.pojo.TimingTaskItem;
 import com.ruoyi.inspectiontask.service.TimingTaskService;
 import com.ruoyi.project.system.domain.SysUser;
 import com.ruoyi.project.system.mapper.SysUserMapper;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.quartz.SchedulerException;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.time.*;
-import java.util.*;
+import java.time.DayOfWeek;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -28,25 +49,33 @@
  */
 @Service
 @Slf4j
+@RequiredArgsConstructor
 public class TimingTaskServiceImpl extends ServiceImpl<TimingTaskMapper, TimingTask> implements TimingTaskService {
 
-    @Autowired
-    private TimingTaskMapper timingTaskMapper;
+    private final TimingTaskMapper timingTaskMapper;
+    private final TimingTaskScheduler timingTaskScheduler;
+    private final SysUserMapper sysUserMapper;
+    private final InspectionItemMapper inspectionItemMapper;
+    private final TimingTaskItemMapper timingTaskItemMapper;
 
-    @Autowired
-    private InspectionTaskMapper inspectionTaskMapper;
-
-    @Autowired
-    private TimingTaskScheduler timingTaskScheduler;
-
-    @Autowired
-    private SysUserMapper sysUserMapper;
-
+    private static final int ENABLED = 1;
+    private static final int DISABLED = 0;
 
     @Override
     public IPage<TimingTaskDto> selectTimingTaskList(Page<TimingTask> page, TimingTask timingTask) {
         // 1. 鍏堝垎椤垫煡璇㈠畾鏃朵换鍔℃暟鎹�
-        IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, null);
+        // 鏋勫缓鏌ヨ鏉′欢
+        LambdaQueryWrapper<TimingTask> queryWrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.isNotBlank(timingTask.getTaskName())) {
+            queryWrapper.like(TimingTask::getTaskName, timingTask.getTaskName());
+        }
+        queryWrapper.eq(TimingTask::getDeleted, 0);
+        if (timingTask.getIsEnabled() != null) {
+            queryWrapper.eq(TimingTask::getIsEnabled, timingTask.getIsEnabled());
+        }
+        // 鎸� ID 鍊掑簭鎺掑簭
+        queryWrapper.orderByDesc(TimingTask::getId);
+        IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, queryWrapper);
 
         // 2. 濡傛灉娌℃湁鏁版嵁锛岀洿鎺ヨ繑鍥炵┖鍒嗛〉
         if (taskPage.getRecords().isEmpty()) {
@@ -65,6 +94,7 @@
 
         // 鏀堕泦宸℃浜篒D锛堝涓狪D浠ラ�楀彿鍒嗛殧锛�
         taskPage.getRecords().forEach(task -> {
+            task.setDateStr(task.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
             if (StringUtils.isNotBlank(task.getInspectorIds())) {
                 Arrays.stream(task.getInspectorIds().split(","))
                         .filter(StringUtils::isNotBlank)
@@ -76,7 +106,7 @@
         // 4. 鎵归噺鏌ヨ鐢ㄦ埛淇℃伅
         Map<Long, String> userNickNameMap = new HashMap<>();
         if (!userIds.isEmpty()) {
-            List<SysUser> users = sysUserMapper.selectUserByIds((List<Long>) userIds);
+            List<SysUser> users = sysUserMapper.selectUserByIds((new ArrayList<>(userIds)));
             users.forEach(user -> userNickNameMap.put(user.getUserId(), user.getNickName()));
         }
 
@@ -93,15 +123,25 @@
 
             // 璁剧疆宸℃浜烘樀绉板垪琛�
             if (StringUtils.isNotBlank(task.getInspectorIds())) {
-                List<String> inspectorNickNames = Arrays.stream(task.getInspectorIds().split(","))
-                        .filter(StringUtils::isNotBlank)
-                        .map(idStr -> {
-                            Long id = Long.valueOf(idStr);
-                            return userNickNameMap.getOrDefault(id, "鏈煡鐢ㄦ埛");
-                        })
-                        .toList();
+                List<String> inspectorNickNames = new ArrayList<>();
+                for (String idStr : task.getInspectorIds().split(",")) {
+                    if (StringUtils.isNotBlank(idStr)) {
+                        Long id = Long.valueOf(idStr);
+                        inspectorNickNames.add(userNickNameMap.getOrDefault(id, "鏈煡鐢ㄦ埛"));
+                    }
+                }
                 dto.setInspector(inspectorNickNames);
             }
+
+            List<TimingTaskItem> taskItems = timingTaskItemMapper.selectList(
+                    new LambdaQueryWrapper<TimingTaskItem>()
+                            .eq(TimingTaskItem::getTimingTaskId, task.getId())
+                            .orderByAsc(TimingTaskItem::getSortNum));
+            dto.setInspectionItems(taskItems);
+            dto.setInspectionItemIds(taskItems.stream()
+                    .map(TimingTaskItem::getInspectionItemId)
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList()));
 
             return dto;
         }).collect(Collectors.toList());
@@ -115,20 +155,46 @@
     @Override
     @Transactional
     public int addOrEditTimingTask(TimingTaskDto timingTaskDto) throws SchedulerException {
+        TimingTask oldTimingTask = null;
+        if (Objects.nonNull(timingTaskDto.getId())) {
+            oldTimingTask = timingTaskMapper.selectById(timingTaskDto.getId());
+            if (oldTimingTask == null) {
+                throw new IllegalArgumentException("瀹氭椂浠诲姟涓嶅瓨鍦�");
+            }
+        }
+        List<InspectionItem> selectedItems = resolveSelectedItems(timingTaskDto, oldTimingTask);
+        List<TimingTaskItem> customItems = normalizeCustomItems(timingTaskDto.getCustomItems());
+        if (selectedItems.isEmpty() && customItems.isEmpty()) {
+            throw new ServiceException("璇烽�夋嫨鎴栨坊鍔犺嚦灏戜竴涓贰妫�鍐呭");
+        }
         TimingTask timingTask = new TimingTask();
         BeanUtils.copyProperties(timingTaskDto, timingTask);
+        timingTask.setIsEnabled(resolveEnabledValue(timingTask.getIsEnabled(), oldTimingTask));
+        timingTask.setActive(ENABLED == timingTask.getIsEnabled());
+        // 1. 瑙f瀽瀛楃涓蹭负 LocalDate锛堝彧鍖呭惈骞存湀鏃ワ級
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        LocalDate localDate = LocalDate.now();
+        if (StringUtils.isNotEmpty(timingTaskDto.getDateStr())) {
+            localDate = LocalDate.parse(timingTaskDto.getDateStr(), formatter);
+        }
+        // 2. 鑾峰彇褰撳墠绯荤粺鐨� LocalTime锛堝寘鍚椂鍒嗙锛�
+        LocalTime currentTime = LocalTime.now();
 
+        // 3. 鍚堝苟 LocalDate 鍜屽綋鍓� LocalTime 涓� LocalDateTime
+        LocalDateTime localDateTime = LocalDateTime.of(localDate, currentTime);
+        timingTask.setCreateTime(localDateTime);
         // 璁剧疆鍒涘缓浜轰俊鎭拰榛樿鍊�
         if (Objects.isNull(timingTaskDto.getId())) {
             timingTask.setRegistrationDate(LocalDate.now());
-            timingTask.setActive(true);
 
             // 璁$畻棣栨鎵ц鏃堕棿
             LocalDateTime firstExecutionTime = calculateFirstExecutionTime(timingTask);
             timingTask.setNextExecutionTime(firstExecutionTime);
-
             int result = timingTaskMapper.insert(timingTask);
             if (result > 0) {
+                saveTaskItems(timingTask, selectedItems, customItems);
+            }
+            if (result > 0 && isEnabled(timingTask.getIsEnabled(), timingTask.isActive())) {
                 // 鏂板鎴愬姛鍚庢坊鍔犲埌璋冨害鍣�
                 timingTaskScheduler.scheduleTimingTask(timingTask);
             }
@@ -136,30 +202,131 @@
         } else {
             int result = timingTaskMapper.updateById(timingTask);
             if (result > 0) {
-                // 鏇存柊鎴愬姛鍚庨噸鏂拌皟搴︿换鍔�
-                timingTaskScheduler.rescheduleTimingTask(timingTask);
+                timingTaskItemMapper.delete(new LambdaQueryWrapper<TimingTaskItem>()
+                        .eq(TimingTaskItem::getTimingTaskId, timingTask.getId()));
+                saveTaskItems(timingTask, selectedItems, customItems);
+                boolean oldEnabled = isEnabled(
+                        oldTimingTask == null ? null : oldTimingTask.getIsEnabled(),
+                        oldTimingTask != null && oldTimingTask.isActive());
+                boolean newEnabled = isEnabled(timingTask.getIsEnabled(), timingTask.isActive());
+                if (!newEnabled) {
+                    timingTaskScheduler.unscheduleTimingTask(timingTask.getId());
+                } else if (oldEnabled) {
+                    // 鏇存柊鎴愬姛鍚庨噸鏂拌皟搴︿换鍔�
+                    timingTaskScheduler.rescheduleTimingTask(timingTask);
+                } else {
+                    // 浠庣鐢ㄦ敼涓哄惎鐢ㄦ椂閲嶆柊鍒涘缓璋冨害浠诲姟
+                    timingTaskScheduler.scheduleTimingTask(timingTask);
+                }
             }
             return result;
         }
     }
 
-    private LocalDateTime calculateFirstExecutionTime(TimingTask task) {
+    private List<InspectionItem> resolveSelectedItems(TimingTask request, TimingTask oldTask) {
+        List<Long> itemIds = request.getInspectionItemIds();
+        if (itemIds == null && oldTask != null) {
+            itemIds = timingTaskItemMapper.selectList(new LambdaQueryWrapper<TimingTaskItem>()
+                            .eq(TimingTaskItem::getTimingTaskId, oldTask.getId()))
+                    .stream().map(TimingTaskItem::getInspectionItemId).filter(Objects::nonNull).toList();
+        }
+        if (request.getTaskId() == null || StringUtils.isBlank(request.getFrequencyType())) {
+            throw new ServiceException("璁惧鍜屽贰妫�棰戠巼涓嶈兘涓虹┖");
+        }
+        if (itemIds == null || itemIds.isEmpty()) {
+            return new ArrayList<>();
+        }
+        if (new HashSet<>(itemIds).size() != itemIds.size()) {
+            throw new ServiceException("宸℃鍐呭涓嶈兘閲嶅閫夋嫨");
+        }
+        List<InspectionItem> items = inspectionItemMapper.selectBatchIds(itemIds);
+        if (items.size() != itemIds.size()) {
+            throw new ServiceException("鎵�閫夊贰妫�鍐呭涓嶅瓨鍦�");
+        }
+        Long deviceId = request.getTaskId().longValue();
+        String frequency = request.getFrequencyType();
+        Set<Long> oldItemIds = oldTask == null ? Collections.emptySet()
+                : new HashSet<>(timingTaskItemMapper.selectList(new LambdaQueryWrapper<TimingTaskItem>()
+                        .eq(TimingTaskItem::getTimingTaskId, oldTask.getId()))
+                        .stream().map(TimingTaskItem::getInspectionItemId).filter(Objects::nonNull).toList());
+        boolean invalid = items.stream().anyMatch(item ->
+                (item.getDeviceLedgerId() != null && !deviceId.equals(item.getDeviceLedgerId()))
+                        || !(frequency.equals(item.getFrequencyType()) || "ALL".equals(item.getFrequencyType()))
+                        || (item.getDeleted() != null && item.getDeleted() == 1
+                        || item.getIsActive() == null || item.getIsActive() != 1) && !oldItemIds.contains(item.getId()));
+        if (invalid) {
+            throw new ServiceException("鎵�閫夊贰妫�鍐呭涓庤澶囨垨棰戠巼涓嶅尮閰�");
+        }
+        Map<Long, InspectionItem> itemMap = items.stream()
+                .collect(Collectors.toMap(InspectionItem::getId, Function.identity()));
+        return itemIds.stream().map(itemMap::get).toList();
+    }
+
+    private List<TimingTaskItem> normalizeCustomItems(List<TimingTaskItem> customItems) {
+        if (customItems == null) {
+            return new ArrayList<>();
+        }
+        return customItems.stream()
+                .filter(Objects::nonNull)
+                .filter(item -> StringUtils.isNotBlank(item.getItemProject())
+                        && StringUtils.isNotBlank(item.getItemContent()))
+                .peek(item -> {
+                    item.setItemProject(item.getItemProject().trim());
+                    item.setItemContent(item.getItemContent().trim());
+                })
+                .toList();
+    }
+
+    private void saveTaskItems(
+            TimingTask task,
+            List<InspectionItem> selectedItems,
+            List<TimingTaskItem> customItems) {
+        for (int index = 0; index < selectedItems.size(); index++) {
+            InspectionItem source = selectedItems.get(index);
+            TimingTaskItem taskItem = new TimingTaskItem();
+            taskItem.setTimingTaskId(task.getId());
+            taskItem.setInspectionItemId(source.getId());
+            taskItem.setItemProject(source.getItemProject());
+            taskItem.setItemContent(source.getItemContent());
+            taskItem.setSortNum(index);
+            taskItem.setTenantId(task.getTenantId());
+            taskItem.setDeptId(task.getDeptId());
+            timingTaskItemMapper.insert(taskItem);
+        }
+        for (int index = 0; index < customItems.size(); index++) {
+            TimingTaskItem source = customItems.get(index);
+            TimingTaskItem taskItem = new TimingTaskItem();
+            taskItem.setTimingTaskId(task.getId());
+            taskItem.setItemProject(source.getItemProject());
+            taskItem.setItemContent(source.getItemContent());
+            taskItem.setSortNum(selectedItems.size() + index);
+            taskItem.setTenantId(task.getTenantId());
+            taskItem.setDeptId(task.getDeptId());
+            timingTaskItemMapper.insert(taskItem);
+        }
+    }
+
+    public LocalDateTime calculateFirstExecutionTime(TimingTask task) {
         // 鏍规嵁棰戠巼绫诲瀷鍜岃鎯呰绠楅娆℃墽琛屾椂闂�
-        return switch (task.getFrequencyType()) {
-            case "DAILY" ->
-                    // 濡傛灉鏄瘡澶╂墽琛岋紝璁$畻浠婂ぉ鎴栨槑澶╃殑鍏蜂綋鏃堕棿
-                    calculateDailyFirstExecution(task.getFrequencyDetail());
-            case "WEEKLY" ->
-                    // 濡傛灉鏄瘡鍛ㄦ墽琛岋紝璁$畻涓嬪懆鐨勫叿浣撴槦鏈熷嚑
-                    calculateWeeklyFirstExecution(task.getFrequencyDetail());
-            case "MONTHLY" ->
-                    // 濡傛灉鏄瘡鏈堟墽琛岋紝璁$畻涓嬩釜鏈堢殑鍏蜂綋鏃ユ湡
-                    calculateMonthlyFirstExecution(task.getFrequencyDetail());
-            case "QUARTERLY" ->
-                    // 鑷畾涔夐鐜囷紝濡傛瘡灏忔椂銆佹瘡30鍒嗛挓绛�
-                    calculateCustomFirstExecution(task.getFrequencyDetail());
-            default -> throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + task.getFrequencyType());
-        };
+        if (task == null || StringUtils.isBlank(task.getFrequencyDetail())) {
+            throw new IllegalArgumentException("璇峰~鍐欎换鍔℃墽琛屾棩鏈熷拰鏃堕棿");
+        }
+        String frequencyType = task.getFrequencyType();
+        if ("DAILY".equals(frequencyType)) {
+            // 濡傛灉鏄瘡澶╂墽琛岋紝璁$畻浠婂ぉ鎴栨槑澶╃殑鍏蜂綋鏃堕棿
+            return calculateDailyFirstExecution(task.getFrequencyDetail());
+        } else if ("WEEKLY".equals(frequencyType)) {
+            // 濡傛灉鏄瘡鍛ㄦ墽琛岋紝璁$畻涓嬪懆鐨勫叿浣撴槦鏈熷嚑
+            return calculateWeeklyFirstExecution(task.getFrequencyDetail());
+        } else if ("MONTHLY".equals(frequencyType)) {
+            // 濡傛灉鏄瘡鏈堟墽琛岋紝璁$畻涓嬩釜鏈堢殑鍏蜂綋鏃ユ湡
+            return calculateMonthlyFirstExecution(task.getFrequencyDetail());
+        } else if ("QUARTERLY".equals(frequencyType)) {
+            // 鑷畾涔夐鐜囷紝濡傛瘡灏忔椂銆佹瘡30鍒嗛挓绛�
+            return calculateCustomFirstExecution(task.getFrequencyDetail());
+        } else {
+            throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + task.getFrequencyType());
+        }
     }
 
     private LocalDateTime calculateDailyFirstExecution(String frequencyDetail) {
@@ -172,16 +339,136 @@
         return now.isBefore(todayExecution) ? todayExecution : todayExecution.plusDays(1);
     }
 
+    // 鏄犲皠鏄熸湡绠�鍐欎笌DayOfWeek
+    private static final Map<String, DayOfWeek> WEEK_DAY_MAP = new HashMap<>();
+
+    static {
+        WEEK_DAY_MAP.put("MON", DayOfWeek.MONDAY);
+        WEEK_DAY_MAP.put("TUE", DayOfWeek.TUESDAY);
+        WEEK_DAY_MAP.put("WED", DayOfWeek.WEDNESDAY);
+        WEEK_DAY_MAP.put("THU", DayOfWeek.THURSDAY);
+        WEEK_DAY_MAP.put("FRI", DayOfWeek.FRIDAY);
+        WEEK_DAY_MAP.put("SAT", DayOfWeek.SATURDAY);
+        WEEK_DAY_MAP.put("SUN", DayOfWeek.SUNDAY);
+    }
+
     private LocalDateTime calculateWeeklyFirstExecution(String frequencyDetail) {
-        return null;
+        // 瑙f瀽杈撳叆鍙傛暟
+        String[] parts = frequencyDetail.split(",");
+        if (parts.length != 2) {
+            throw new IllegalArgumentException("鍙傛暟鏍煎紡閿欒锛屽簲涓�'MON,13:43'鏍煎紡");
+        }
+
+        String weekDayStr = parts[0].trim();
+        String timeStr = parts[1].trim();
+
+        // 鑾峰彇瀵瑰簲鐨勬槦鏈熷嚑
+        DayOfWeek targetDay = WEEK_DAY_MAP.get(weekDayStr);
+        if (targetDay == null) {
+            throw new IllegalArgumentException("鏃犳晥鐨勬槦鏈熺畝鍐�: " + weekDayStr);
+        }
+
+        // 瑙f瀽鏃堕棿
+        LocalTime targetTime = LocalTime.parse(timeStr, DateTimeFormatter.ofPattern("HH:mm"));
+
+        // 鑾峰彇褰撳墠鏃堕棿
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime targetDateTime = now.with(targetDay).with(targetTime);
+
+        // 濡傛灉璁$畻鍑虹殑鏃堕棿鍦ㄥ綋鍓嶆椂闂翠箣鍓嶏紝鍒欏姞涓�鍛�
+        if (targetDateTime.isBefore(now)) {
+            targetDateTime = targetDateTime.plusWeeks(1);
+        }
+
+        return targetDateTime;
     }
 
     private LocalDateTime calculateMonthlyFirstExecution(String frequencyDetail) {
-        return null;
+        // 瑙f瀽杈撳叆鍙傛暟
+        String[] parts = frequencyDetail.split(",");
+        if (parts.length != 2) {
+            throw new IllegalArgumentException("鍙傛暟鏍煎紡閿欒锛屽簲涓�'03,17:00'鏍煎紡");
+        }
+
+        String dayStr = parts[0].trim();
+        String timeStr = parts[1].trim();
+
+        // 瑙f瀽鏃ユ湡
+        int dayOfMonth;
+        try {
+            dayOfMonth = Integer.parseInt(dayStr);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("鏃犳晥鐨勬棩鏈熸牸寮�: " + dayStr, e);
+        }
+
+        // 楠岃瘉鏃ユ湡鏈夋晥鎬э紙1-31涔嬮棿锛�
+        if (dayOfMonth < 1 || dayOfMonth > 31) {
+            throw new IllegalArgumentException("鏃ユ湡蹇呴』鍦�1-31涔嬮棿: " + dayOfMonth);
+        }
+
+        // 瑙f瀽鏃堕棿
+        LocalTime targetTime;
+        try {
+            targetTime = LocalTime.parse(timeStr, DateTimeFormatter.ofPattern("HH:mm"));
+        } catch (DateTimeException e) {
+            throw new IllegalArgumentException("鏃犳晥鐨勬椂闂存牸寮�: " + timeStr, e);
+        }
+
+        // 鑾峰彇褰撳墠鏃堕棿
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime targetDateTime = now.withDayOfMonth(dayOfMonth).with(targetTime);
+
+        // 妫�鏌ユ棩鏈熸槸鍚﹁鑷姩璋冩暣锛堝31鏃ュ湪灏忔湀浼氳璋冩暣锛�
+        boolean isDateAdjusted = targetDateTime.getDayOfMonth() != dayOfMonth;
+
+        // 濡傛灉鐩爣鏃堕棿鍦ㄥ綋鍓嶆椂闂翠箣鍓嶏紝鎴栬�呮棩鏈熻绯荤粺鑷姩璋冩暣浜�
+        if (targetDateTime.isBefore(now) || isDateAdjusted) {
+            // 璁$畻涓嬩釜鏈堢殑鏃ユ湡
+            LocalDateTime nextMonth = now.plusMonths(1);
+            // 灏濊瘯璁剧疆涓嬩釜鏈堢殑鐩爣鏃ユ湡
+            LocalDateTime nextMonthTarget = nextMonth.withDayOfMonth(dayOfMonth).with(targetTime);
+
+            // 濡傛灉涓嬩釜鏈堢殑鏃ユ湡涔熻璋冩暣浜嗭紝灏辩敤涓嬩釜鏈堢殑鏈�鍚庝竴澶�
+            if (nextMonthTarget.getDayOfMonth() != dayOfMonth) {
+                // 姝g‘鑾峰彇涓嬩釜鏈堢殑鏈�鍚庝竴澶╋紙淇isLeapYear璋冪敤闂锛�
+                int lastDayOfMonth = nextMonth.getMonth().length(
+                        Year.of(nextMonth.getYear()).isLeap()
+                );
+                nextMonthTarget = nextMonth.withDayOfMonth(lastDayOfMonth).with(targetTime);
+            }
+
+            targetDateTime = nextMonthTarget;
+        }
+
+        return targetDateTime;
     }
 
     private LocalDateTime calculateCustomFirstExecution(String frequencyDetail) {
-        return null;
+        String[] parts = frequencyDetail.split(",");
+        if (parts.length != 3) {
+            throw new IllegalArgumentException("瀛e害棰戠巼鏍煎紡閿欒锛屽簲涓�'瀛e害鍐呮湀浠�,鏃ユ湡,鏃堕棿'");
+        }
+        int monthInQuarter = Integer.parseInt(parts[0]);
+        int dayOfMonth = Integer.parseInt(parts[1]);
+        LocalTime time = LocalTime.parse(parts[2], DateTimeFormatter.ofPattern("HH:mm"));
+        if (monthInQuarter < 1 || monthInQuarter > 3 || dayOfMonth < 1 || dayOfMonth > 31) {
+            throw new IllegalArgumentException("瀛e害棰戠巼鏃ユ湡鏃犳晥");
+        }
+
+        LocalDateTime now = LocalDateTime.now();
+        int currentMonthInQuarter = (now.getMonthValue() - 1) % 3 + 1;
+        int monthsUntilTarget = monthInQuarter - currentMonthInQuarter;
+        if (monthsUntilTarget < 0) {
+            monthsUntilTarget += 3;
+        }
+        YearMonth targetMonth = YearMonth.from(now).plusMonths(monthsUntilTarget);
+        int targetDay = Math.min(dayOfMonth, targetMonth.lengthOfMonth());
+        LocalDateTime target = targetMonth.atDay(targetDay).atTime(time);
+        if (!target.isAfter(now)) {
+            targetMonth = targetMonth.plusMonths(3);
+            target = targetMonth.atDay(Math.min(dayOfMonth, targetMonth.lengthOfMonth())).atTime(time);
+        }
+        return target;
     }
 
     @Override
@@ -214,13 +501,18 @@
                                                      String frequencyDetail,
                                                      LocalDateTime currentTime) {
         try {
-            return switch (frequencyType) {
-                case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime);
-                case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime);
-                case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime);
-                case "QUARTERLY" -> calculateQuarterlyNextTime(frequencyDetail, currentTime);
-                default -> throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + frequencyType);
-            };
+            switch (frequencyType) {
+                case "DAILY":
+                    return calculateDailyNextTime(frequencyDetail, currentTime);
+                case "WEEKLY":
+                    return calculateWeeklyNextTime(frequencyDetail, currentTime);
+                case "MONTHLY":
+                    return calculateMonthlyNextTime(frequencyDetail, currentTime);
+                case "QUARTERLY":
+                    return calculateQuarterlyNextTime(frequencyDetail, currentTime);
+                default:
+                    throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + frequencyType);
+            }
         } catch (Exception e) {
             throw new RuntimeException("璁$畻涓嬫鎵ц鏃堕棿澶辫触: " + e.getMessage(), e);
         }
@@ -324,13 +616,27 @@
 
         for (String dayStr : dayStrs) {
             switch (dayStr) {
-                case "MON": days.add(DayOfWeek.MONDAY); break;
-                case "TUE": days.add(DayOfWeek.TUESDAY); break;
-                case "WED": days.add(DayOfWeek.WEDNESDAY); break;
-                case "THU": days.add(DayOfWeek.THURSDAY); break;
-                case "FRI": days.add(DayOfWeek.FRIDAY); break;
-                case "SAT": days.add(DayOfWeek.SATURDAY); break;
-                case "SUN": days.add(DayOfWeek.SUNDAY); break;
+                case "MON":
+                    days.add(DayOfWeek.MONDAY);
+                    break;
+                case "TUE":
+                    days.add(DayOfWeek.TUESDAY);
+                    break;
+                case "WED":
+                    days.add(DayOfWeek.WEDNESDAY);
+                    break;
+                case "THU":
+                    days.add(DayOfWeek.THURSDAY);
+                    break;
+                case "FRI":
+                    days.add(DayOfWeek.FRIDAY);
+                    break;
+                case "SAT":
+                    days.add(DayOfWeek.SATURDAY);
+                    break;
+                case "SUN":
+                    days.add(DayOfWeek.SUNDAY);
+                    break;
                 default: throw new IllegalArgumentException("鏃犳晥鐨勬槦鏈熷嚑: " + dayStr);
             }
         }
@@ -338,11 +644,40 @@
         return days;
     }
 
+    private Integer resolveEnabledValue(Integer requestEnabled, TimingTask oldTimingTask) {
+        if (requestEnabled != null) {
+            return requestEnabled;
+        }
+        if (oldTimingTask != null) {
+            if (oldTimingTask.getIsEnabled() != null) {
+                return oldTimingTask.getIsEnabled();
+            }
+            return oldTimingTask.isActive() ? ENABLED : DISABLED;
+        }
+        return ENABLED;
+    }
 
+    private boolean isEnabled(Integer enabledValue, boolean activeFallback) {
+        if (enabledValue != null) {
+            return ENABLED == enabledValue;
+        }
+        return activeFallback;
+    }
 
     @Override
     public int delByIds(Long[] ids) {
-        return timingTaskMapper.deleteBatchIds(Arrays.asList(ids));
+        if (ids == null || ids.length == 0) {
+            return 0;
+        }
+        int i = timingTaskMapper.deleteBatchIds(Arrays.asList(ids));
+        if (i > 0) {
+            timingTaskItemMapper.delete(new LambdaQueryWrapper<TimingTaskItem>()
+                    .in(TimingTaskItem::getTimingTaskId, Arrays.asList(ids)));
+            for (Long id : ids) {
+                timingTaskScheduler.unscheduleTimingTask(id);
+            }
+        }
+        return i;
     }
 
 }

--
Gitblit v1.9.3