| | |
| | | import com.ruoyi.inspectiontask.service.TimingTaskService; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import jakarta.annotation.PostConstruct; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.quartz.SchedulerException; |
| | |
| | | private static final int ENABLED = 1; |
| | | private static final int DISABLED = 0; |
| | | |
| | | /** |
| | | * 启动时把已启用但未注册到 Quartz 的巡检任务补注册, |
| | | * 避免调度记录丢失后定时任务静默不执行 |
| | | */ |
| | | @PostConstruct |
| | | public void restoreScheduledTasks() { |
| | | try { |
| | | List<TimingTask> tasks = timingTaskMapper.selectList(new LambdaQueryWrapper<TimingTask>() |
| | | .eq(TimingTask::getIsEnabled, ENABLED) |
| | | .eq(TimingTask::getDeleted, 0)); |
| | | for (TimingTask task : tasks) { |
| | | try { |
| | | LocalDateTime nextTime = calculateFirstExecutionTime(task); |
| | | if (!Objects.equals(task.getNextExecutionTime(), nextTime)) { |
| | | task.setNextExecutionTime(nextTime); |
| | | timingTaskMapper.updateById(task); |
| | | } |
| | | timingTaskScheduler.ensureScheduled(task); |
| | | } catch (Exception e) { |
| | | log.error("恢复巡检定时任务失败: id={}, name={}", task.getId(), task.getTaskName(), e); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("恢复巡检定时任务异常", e); |
| | | } |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public IPage<TimingTaskDto> selectTimingTaskList(Page<TimingTask> page, TimingTask timingTask) { |
| | |
| | | if (timingTask.getIsEnabled() != null) { |
| | | queryWrapper.eq(TimingTask::getIsEnabled, timingTask.getIsEnabled()); |
| | | } |
| | | queryWrapper.orderByDesc(TimingTask::getCreateTime); |
| | | IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, queryWrapper); |
| | | |
| | | // 2. 如果没有数据,直接返回空分页 |
| | |
| | | } |
| | | return result; |
| | | } else { |
| | | |
| | | |
| | | // 频率变化时重算下次执行时间,避免页面显示与实际调度不一致 |
| | | if (!Objects.equals(oldTimingTask.getFrequencyType(), timingTask.getFrequencyType()) |
| | | || !Objects.equals(oldTimingTask.getFrequencyDetail(), timingTask.getFrequencyDetail())) { |
| | | timingTask.setNextExecutionTime(calculateFirstExecutionTime(timingTask)); |
| | | } |
| | | int result = timingTaskMapper.updateById(timingTask); |
| | | if (result > 0) { |
| | | boolean oldEnabled = isEnabled(oldTimingTask == null ? null : oldTimingTask.getIsEnabled(), oldTimingTask != null && oldTimingTask.isActive()); |
| | |
| | | // 如果是每月执行,计算下个月的具体日期 |
| | | return calculateMonthlyFirstExecution(task.getFrequencyDetail()); |
| | | } else if ("QUARTERLY".equals(frequencyType)) { |
| | | // 自定义频率,如每小时、每30分钟等 |
| | | return calculateCustomFirstExecution(task.getFrequencyDetail()); |
| | | // 季度执行,格式为 月,日,时间 |
| | | return calculateQuarterlyFirstExecution(task.getFrequencyDetail()); |
| | | } else { |
| | | throw new IllegalArgumentException("不支持的频率类型: " + task.getFrequencyType()); |
| | | } |
| | |
| | | return targetDateTime; |
| | | } |
| | | |
| | | private LocalDateTime calculateCustomFirstExecution(String frequencyDetail) { |
| | | return null; |
| | | /** |
| | | * 计算季度任务的首次执行时间,格式为 月,日,时间(如 01,15,14:00) |
| | | * 执行月份按季度起点对齐,与 convertQuarterlyToCron 生成的 cron 保持一致 |
| | | */ |
| | | private LocalDateTime calculateQuarterlyFirstExecution(String frequencyDetail) { |
| | | String[] parts = frequencyDetail.split(","); |
| | | if (parts.length != 3) { |
| | | throw new IllegalArgumentException("参数格式错误,应为'01,15,14:00'格式"); |
| | | } |
| | | int month = Integer.parseInt(parts[0].trim()); |
| | | int dayOfMonth = Integer.parseInt(parts[1].trim()); |
| | | LocalTime time = LocalTime.parse(parts[2].trim(), DateTimeFormatter.ofPattern("HH:mm")); |
| | | if (month < 1 || month > 12) { |
| | | throw new IllegalArgumentException("月份必须在1-12之间: " + month); |
| | | } |
| | | if (dayOfMonth < 1 || dayOfMonth > 31) { |
| | | throw new IllegalArgumentException("日期必须在1-31之间: " + dayOfMonth); |
| | | } |
| | | |
| | | // 按季度起点对齐执行月份(1、4、7、10 月) |
| | | int quarterStartMonth = ((month - 1) / 3) * 3 + 1; |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | int currentYear = now.getYear(); |
| | | for (int i = 0; i < 8; i++) { |
| | | int totalMonth = quarterStartMonth + i * 3; |
| | | int year = currentYear + (totalMonth - 1) / 12; |
| | | int targetMonth = ((totalMonth - 1) % 12) + 1; |
| | | int adjustedDay = Math.min(dayOfMonth, YearMonth.of(year, targetMonth).lengthOfMonth()); |
| | | LocalDateTime candidate = LocalDateTime.of(year, targetMonth, adjustedDay, time.getHour(), time.getMinute()); |
| | | if (candidate.isAfter(now)) { |
| | | return candidate; |
| | | } |
| | | } |
| | | throw new IllegalStateException("无法计算季度任务的首次执行时间: " + frequencyDetail); |
| | | } |
| | | |
| | | @Override |