巡检任务频次增加“每年”支持
- TimingTaskScheduler:新增 YEARLY 的 cron 转换(默认 07:00)
- TimingTaskServiceImpl / TimingTaskJob:补充每年频次的首次/下次执行时间计算,frequencyDetail 格式为“月,日”,兼容旧的“月,日,HH:mm”
Co-Authored-By: Claude Code <noreply@anthropic.com>
| | |
| | | LocalDateTime currentTime) { |
| | | try { |
| | | switch (frequencyType) { |
| | | case "YEARLY": |
| | | return calculateYearlyNextTime(frequencyDetail, currentTime); |
| | | case "DAILY": |
| | | return calculateDailyNextTime(frequencyDetail, currentTime); |
| | | case "WEEKLY": |
| | |
| | | } |
| | | } |
| | | |
| | | /** 每年巡检默认执行时间:07:00(页面上不精确到时分) */ |
| | | private static final LocalTime DEFAULT_EXECUTION_TIME = LocalTime.of(7, 0); |
| | | |
| | | /** |
| | | * 计算每年任务的下次执行时间 |
| | | * frequencyDetail 格式为 "月,日",可选 "月,日,HH:mm"(默认 07:00) |
| | | */ |
| | | private LocalDateTime calculateYearlyNextTime(String detail, LocalDateTime current) { |
| | | String[] parts = detail.split(","); |
| | | int month = Integer.parseInt(parts[0].trim()); |
| | | int day = Integer.parseInt(parts[1].trim()); |
| | | LocalTime time = parts.length >= 3 ? LocalTime.parse(parts[2].trim()) : DEFAULT_EXECUTION_TIME; |
| | | |
| | | int year = current.getYear(); |
| | | LocalDateTime target = LocalDateTime.of( |
| | | year, month, Math.min(day, YearMonth.of(year, month).lengthOfMonth()), time.getHour(), time.getMinute()); |
| | | if (!current.isBefore(target)) { |
| | | year++; |
| | | target = LocalDateTime.of( |
| | | year, month, Math.min(day, YearMonth.of(year, month).lengthOfMonth()), time.getHour(), time.getMinute()); |
| | | } |
| | | return target; |
| | | } |
| | | |
| | | /** |
| | | * 计算每日任务的下次执行时间 |
| | | */ |
| | |
| | | // 使用switch确保条件互斥 |
| | | String frequencyType = task.getFrequencyType().toUpperCase(); // 统一转为大写比较 |
| | | switch (frequencyType) { |
| | | case "YEARLY": |
| | | return convertYearlyToCron(task.getFrequencyDetail()); |
| | | case "DAILY": |
| | | return convertDailyToCron(task.getFrequencyDetail()); |
| | | case "WEEKLY": |
| | |
| | | } |
| | | } |
| | | |
| | | // 每年任务转换(frequencyDetail 格式为 "月,日",默认 07:00 执行) |
| | | private String convertYearlyToCron(String frequencyDetail) { |
| | | String[] parts = validateAndSplit(frequencyDetail, ",", 2); |
| | | int month = validateMonth(parts[0]); |
| | | int day = validateDayOfMonth(parts[1]); |
| | | return String.format("0 0 7 %d %d ? *", day, month); |
| | | } |
| | | |
| | | // 每日任务转换 |
| | | private String convertDailyToCron(String frequencyDetail) { |
| | | LocalTime time = parseTime(frequencyDetail); |
| | |
| | | } |
| | | } |
| | | |
| | | /** 每年巡检默认执行时间:07:00(页面上不精确到时分) */ |
| | | private static final LocalTime DEFAULT_EXECUTION_TIME = LocalTime.of(7, 0); |
| | | |
| | | private LocalDateTime calculateDailyFirstExecution(String frequencyDetail) { |
| | | // frequencyDetail可能是具体时间,如 "14:30" |
| | | LocalTime executionTime = LocalTime.parse(frequencyDetail); |
| | |
| | | |
| | | private LocalDateTime calculateAnnualFirstExecution(String frequencyDetail) { |
| | | String[] parts = frequencyDetail.split(","); |
| | | if (parts.length != 3) { |
| | | throw new IllegalArgumentException("参数格式错误,应为'12,31,09:00'格式"); |
| | | // 每年频率不需要精确到时分:默认格式为 "月,日",兼容旧的 "月,日,HH:mm" |
| | | if (parts.length != 2 && parts.length != 3) { |
| | | throw new IllegalArgumentException("参数格式错误,应为'12,31'格式"); |
| | | } |
| | | |
| | | int month; |
| | |
| | | throw new IllegalArgumentException("月份必须在1-12之间,日期必须在1-31之间"); |
| | | } |
| | | |
| | | LocalTime time = LocalTime.parse(parts[2].trim(), DateTimeFormatter.ofPattern("HH:mm")); |
| | | LocalTime time = parts.length == 3 |
| | | ? LocalTime.parse(parts[2].trim(), DateTimeFormatter.ofPattern("HH:mm")) |
| | | : DEFAULT_EXECUTION_TIME; |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | int year = now.getYear(); |
| | | LocalDateTime target = LocalDateTime.of(LocalDate.of(year, month, Math.min(day, YearMonth.of(year, month).lengthOfMonth())), time); |
| | |
| | | LocalDateTime currentTime) { |
| | | try { |
| | | switch (frequencyType) { |
| | | case "YEARLY": |
| | | return calculateYearlyNextTime(frequencyDetail, currentTime); |
| | | case "DAILY": |
| | | return calculateDailyNextTime(frequencyDetail, currentTime); |
| | | case "WEEKLY": |
| | |
| | | } |
| | | |
| | | /** |
| | | * 计算每年任务的下次执行时间 |
| | | * frequencyDetail 格式为 "月,日",兼容 "月,日,HH:mm"(默认 07:00) |
| | | */ |
| | | private LocalDateTime calculateYearlyNextTime(String detail, LocalDateTime current) { |
| | | String[] parts = detail.split(","); |
| | | int month = Integer.parseInt(parts[0].trim()); |
| | | int day = Integer.parseInt(parts[1].trim()); |
| | | LocalTime time = parts.length >= 3 ? LocalTime.parse(parts[2].trim()) : DEFAULT_EXECUTION_TIME; |
| | | |
| | | int year = current.getYear(); |
| | | LocalDateTime target = LocalDateTime.of( |
| | | year, month, Math.min(day, YearMonth.of(year, month).lengthOfMonth()), time.getHour(), time.getMinute()); |
| | | if (!current.isBefore(target)) { |
| | | year++; |
| | | target = LocalDateTime.of( |
| | | year, month, Math.min(day, YearMonth.of(year, month).lengthOfMonth()), time.getHour(), time.getMinute()); |
| | | } |
| | | return target; |
| | | } |
| | | |
| | | /** |
| | | * 计算每日任务的下次执行时间 |
| | | */ |
| | | private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) { |