From db4316f3d95fc9af91d99a56293e2855da1e1dbd Mon Sep 17 00:00:00 2001 From: yinhuaiwei Date: Mon, 29 Dec 2025 10:37:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E8=AE=A1=E5=88=92=E6=97=A5=E5=8E=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=AE=9E=E7=8E=B0(=E9=92=88=E5=AF=B9=E6=9C=AA?= =?UTF-8?q?=E6=9D=A5=E4=BB=BB=E5=8A=A1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CalenderController.java | 10 + .../service/TaskCalendarGenerator.java | 338 ++++++++++++++++++ .../service/impl/CalenderServiceImpl.java | 14 +- .../service/impl/PatrolTaskServiceImpl.java | 31 +- 4 files changed, 380 insertions(+), 13 deletions(-) create mode 100644 inspect-main/inspect-main-task/src/main/java/com/inspect/calender/service/TaskCalendarGenerator.java diff --git a/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/controller/CalenderController.java b/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/controller/CalenderController.java index 56c58cb..752ad45 100644 --- a/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/controller/CalenderController.java +++ b/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/controller/CalenderController.java @@ -3,6 +3,7 @@ package com.inspect.calender.controller; import com.inspect.calender.domain.DailyTaskStatsDTO; import com.inspect.calender.domain.MonthlyTaskStatsDTO; import com.inspect.calender.service.CalenderService; +import com.inspect.calender.service.TaskCalendarGenerator; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; @@ -19,6 +20,8 @@ import java.util.List; public class CalenderController { @Resource private CalenderService calenderService; + @Resource + private TaskCalendarGenerator taskCalendarGenerator; @ApiOperation("获取指定年份的月度信息") @GetMapping("/year") @@ -28,6 +31,7 @@ public class CalenderController { throw new IllegalArgumentException("无效的年份"); } List list = calenderService.getMonthlyStatsByYear(year); + log.info("获取指定年份的信息成功, year: {}, list: {}", year, list); return list; } @@ -68,4 +72,10 @@ public class CalenderController { log.info("获取指定日期的信息成功, year: {}, month: {}, list: {}", year,month, list); return list; } + + @GetMapping("/invalid") + public String invalid() { + taskCalendarGenerator.onTaskChange(); + return "ok"; + } } diff --git a/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/service/TaskCalendarGenerator.java b/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/service/TaskCalendarGenerator.java new file mode 100644 index 0000000..4808c7e --- /dev/null +++ b/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/service/TaskCalendarGenerator.java @@ -0,0 +1,338 @@ +package com.inspect.calender.service; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.inspect.base.core.enums.ExecStatus; +import com.inspect.base.core.enums.IntervalType; +import com.inspect.base.core.utils.StringUtils; +import com.inspect.calender.domain.DailyTaskStatsDTO; +import com.inspect.calender.domain.MonthlyTaskStatsDTO; +import com.inspect.calender.enums.TaskStateEnum; +import com.inspect.task.domain.PatrolTask; +import com.inspect.task.mapper.PatrolTaskMapper; +import com.inspect.taskstatus.mapper.PatrolTaskStatusMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.*; +import java.time.temporal.TemporalAdjusters; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 未来任务生成器 + */ +@Service +@Slf4j +public class TaskCalendarGenerator { + /* + * 未来任务缓存的过期时间 + * 预生成未来1个月计划(本月和下一个月) + */ + private static final Integer FUTURE_CALENDAR = 60; + @Resource + private PatrolTaskMapper patrolTaskMapper; + private final LoadingCache>> futureCalendarCache = + Caffeine.newBuilder() + .maximumSize(5) + .expireAfterWrite(12, TimeUnit.HOURS) + .build(this::generate); + @Resource + private PatrolTaskStatusMapper patrolTaskStatusMapper; + + // 定时任务:每天凌晨或任务变更后主动刷新缓存 + @Scheduled(cron = "0 50 0 * * ?") + public void refreshCalendarCache() { + log.info("[任务执行日历]定时刷新未来任务日历缓存"); + futureCalendarCache.invalidateAll(); + futureCalendarCache.refresh(FUTURE_CALENDAR); + } + + /** + * 任务信息变更时,主动刷新日历缓存 + */ + public void onTaskChange() { + log.info("[任务执行日历]任务信息变化,日历缓存失效"); + futureCalendarCache.invalidateAll(); + } + + public List getMonthlyStatsByYear(int year) { + List existingStats = patrolTaskStatusMapper.countMonthlyStatsByYear(year); + // 获取未来预生成的计划任务(缓存) + Map> futureCalendarMap = futureCalendarCache.get(FUTURE_CALENDAR); + // 验证缓存数据是否为空 + if (futureCalendarMap == null) { + return existingStats; + } + + LocalDateTime currentTime = LocalDateTime.now(); + LocalDate currentDate = currentTime.toLocalDate(); + for (Map.Entry> entry : futureCalendarMap.entrySet()) { + LocalDate date = entry.getKey(); + List futureTasks = entry.getValue(); + + // 检查当前日期是否在指定年份范围内 + if (date.getYear() == year) { + long count = 0; + if (date.equals(currentDate)) { + if (futureTasks != null) { + count = futureTasks.stream() + .filter(dto -> dto.getStartTime() != null) + .filter(dto -> dto.getStartTime().isAfter(currentTime)) + .count(); + } + } else if (date.isAfter(currentDate)) { + if (futureTasks != null) { + count = futureTasks.size(); + } + } + // 确保月份索引在有效范围内 + int monthIndex = date.getMonthValue() - 1; + if (monthIndex < existingStats.size()) { + MonthlyTaskStatsDTO monthlyStats = existingStats.get(date.getMonthValue() - 1); + monthlyStats.setPending(monthlyStats.getPending() + count); + monthlyStats.setTotal(monthlyStats.getTotal() + count); + } + } + } + + return existingStats; + } + + public List getFullCalendar(int year, int month) { + return getFullCalendar(year, month, 0); + } + + public List getFullCalendar(int year, int month, int day) { + List existingList; + if (day == 0) { + existingList = patrolTaskStatusMapper.selectDailyStatsByYearAndMonth(year, month); + } else { + existingList = patrolTaskStatusMapper.selectDailyStatsByYearAndMonthAndDay(year, month, day); + } + existingList.forEach(DailyTaskStatsDTO::formatTimeAndStatus); + + // 将已生成任务按日期分组 + Map> fullCalendarMap = existingList.stream() + .collect(Collectors.groupingBy(dto -> dto.getStartTime().toLocalDate(), TreeMap::new, Collectors.toList())); + + // 2. 获取未来预生成的计划任务(缓存) + Map> futureCalendarMap = futureCalendarCache.get(FUTURE_CALENDAR); + + if (day != 0) { + LocalDate targetDate = LocalDate.of(year, month, day); + Map> tempMap = new TreeMap<>(); + tempMap.computeIfAbsent(targetDate, k -> new ArrayList<>()).addAll(futureCalendarMap.getOrDefault(targetDate, new ArrayList<>())); + futureCalendarMap = tempMap; + } + LocalDateTime currentTime = LocalDateTime.now(); + LocalDate currentDate = currentTime.toLocalDate(); + for (Map.Entry> entry : futureCalendarMap.entrySet()) { + LocalDate date = entry.getKey(); + List futureTasks = entry.getValue(); + // 检查当前日期是否在指定年月范围内 + if (date.getYear() == year && date.getMonthValue() == month) { + List existingTasks = fullCalendarMap.computeIfAbsent(date, k -> new ArrayList<>()); + // 将未来计划任务添加到当前日期的任务列表中(如果该任务尚未存在) + for (DailyTaskStatsDTO futureTask : futureTasks) { + if (date.equals(currentDate)) { + // 获取 startTime 最晚的任务 + if (currentTime.isBefore(futureTask.getStartTime())) { + existingTasks.add(futureTask); + } + } else { + existingTasks.add(futureTask); + } + } + } + } + // 对每一天的任务按开始时间排序 + fullCalendarMap.values().forEach(list -> + list.sort(Comparator.comparing(DailyTaskStatsDTO::getStartTime))); + + return fullCalendarMap.values() + .stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + } + + /** + * 获取未来 1 个月天的任务执行日历 + * (本月和下个月) + */ + public Map> generate(Integer days) { + Map> calendarMap = new TreeMap<>(); + LocalDateTime beginTime = LocalDate.now().atTime(0, 0, 0); + + LocalDateTime endTime = LocalDate.now().plusMonths(1) + .with(TemporalAdjusters.lastDayOfMonth()) + .atTime(23, 59, 59); + + // 获取所有启用的任务 + List patrolTaskList = patrolTaskMapper.selectPatrolTaskList(PatrolTask.builder().isEnable("0").build()); + log.info("查询所有启用任务计划,数量:{}", patrolTaskList.size()); + + // 逐个任务解析,生成未来任务 + for (PatrolTask task : patrolTaskList) { + if (StringUtils.isEmpty(task.getDevType())) { + continue; + } + try { + // 1.定期执行 + if (ExecStatus.REGULAR.getCode().equals(task.getExecutionStatus())) { + if (task.getFixedStartTime() != null) { + LocalDateTime startTime = task.getFixedStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + // 判断开始时间是否在范围内 + if (startTime.isAfter(beginTime) && startTime.isBefore(endTime)) { + DailyTaskStatsDTO dto = DailyTaskStatsDTO.builder() + .id(task.getTaskCode()) + .name(task.getTaskName()) + .taskState(TaskStateEnum.PENDING.getCode()) + .startTime(startTime) + .build(); + dto.formatTimeAndStatus(); + + calendarMap.computeIfAbsent(startTime.toLocalDate(), k -> new ArrayList<>()).add(dto); + } + log.info("定时任务{}【{}】生成完毕", task.getTaskId(), task.getTaskName()); + } + } + // 2.周期执行 + else if (ExecStatus.PERIODIC.getCode().equals(task.getExecutionStatus())) { + LocalDateTime cycleStartDateTime = task.getCycleStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + LocalDateTime cycleEndTime = task.getCycleEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + + LocalDateTime startLdt = beginTime.isBefore(cycleStartDateTime) ? cycleStartDateTime : beginTime; + LocalDateTime endLdt = endTime.isBefore(cycleEndTime) ? endTime : cycleEndTime; + + String[] executeTimes = task.getCycleExecuteTime().split(StringUtils.COMMA); + // 按周 + if (!StringUtils.isEmpty(task.getCycleWeek())) { + String[] weeks = task.getCycleWeek().split(StringUtils.COMMA); + // 解析星期列表 + List weekDays = Arrays.stream(weeks).map(this::parseDayOfWeek).collect(Collectors.toList()); + + log.info("周期任务(周){}【{}】生成开始,开始时间:{},结束时间:{},执行时间:{}", task.getTaskId(), task.getTaskName(), startLdt, endLdt, executeTimes); + // 判断范围内的每一天的星期是否为目标星期 + for (LocalDateTime date = startLdt; date.isBefore(endLdt); date = date.plusDays(1)) { + // 判断是否为目标星期 + for (int i = 0; i < weekDays.size(); i++) { + DayOfWeek weekDay = weekDays.get(i); + if (date.getDayOfWeek().equals(weekDay)) { + LocalTime executeTime = LocalTime.parse(executeTimes[i]); + LocalDateTime startTime = LocalDateTime.of(date.toLocalDate(), executeTime); + DailyTaskStatsDTO dto = DailyTaskStatsDTO.builder() + .id(task.getTaskCode()) + .name(task.getTaskName()) + .taskState(TaskStateEnum.PENDING.getCode()) + .startTime(startTime) + .build(); + dto.formatTimeAndStatus(); + + calendarMap.computeIfAbsent(startTime.toLocalDate(), k -> new ArrayList<>()).add(dto); + } + } + } + log.info("周期任务(周){}【{}】生成完毕", task.getTaskId(), task.getTaskName()); + } + // 按月 + else if (!StringUtils.isEmpty(task.getCycleMonth())) { + String[] monthDays = task.getCycleMonth().split(StringUtils.COMMA); + List targetDays = Arrays.stream(monthDays).map(Integer::parseInt).collect(Collectors.toList()); + + log.info("周期任务(月){}【{}】生成开始,开始时间:{},结束时间:{},执行时间:{}", task.getTaskId(), task.getTaskName(), startLdt, endLdt, executeTimes); + for (LocalDateTime date = startLdt; date.isBefore(endLdt); date = date.plusDays(1)) { + // 判断是否为目标日期 + for (int i = 0; i < targetDays.size(); i++) { + int targetDay = targetDays.get(i); + if (date.getDayOfMonth() == targetDay) { + LocalTime executeTime = LocalTime.parse(executeTimes[i]); + LocalDateTime startTime = LocalDateTime.of(date.toLocalDate(), executeTime); + DailyTaskStatsDTO dto = DailyTaskStatsDTO.builder() + .id(task.getTaskCode()) + .name(task.getTaskName()) + .taskState(TaskStateEnum.PENDING.getCode()) + .startTime(startTime) + .build(); + dto.formatTimeAndStatus(); + + calendarMap.computeIfAbsent(startTime.toLocalDate(), k -> new ArrayList<>()).add(dto); + } + } + } + log.info("周期任务(月){}【{}】生成完毕", task.getTaskId(), task.getTaskName()); + } + } + // 3.间隔执行 + else if (ExecStatus.INTERVAL.getCode().equals(task.getExecutionStatus())) { + // 小时间隔 + if (IntervalType.BY_HOUR.getCode().equals(task.getIntervalType())) { + // 按小时执行 + int intervalNumber = task.getIntervalNumber(); + if (intervalNumber > 0) { + LocalDateTime intervalStartTime = task.getIntervalStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + LocalDateTime intervalEndTime = task.getIntervalEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + LocalTime executeTime = LocalTime.parse(task.getIntervalExecuteTime()); + + LocalDateTime startLdt = beginTime.isBefore(intervalStartTime) ? intervalStartTime : beginTime; + LocalDateTime endLdt = endTime.isAfter(intervalEndTime) ? intervalEndTime : endTime; + + startLdt = LocalDateTime.of(startLdt.toLocalDate(), executeTime); + for (LocalDateTime date = startLdt; date.isBefore(endLdt); date = date.plusHours(intervalNumber)) { + // 判断是否为目标星期 + DailyTaskStatsDTO dto = DailyTaskStatsDTO.builder() + .name(task.getTaskName()) + .taskState(TaskStateEnum.PENDING.getCode()) + .startTime(date) + .build(); + dto.formatTimeAndStatus(); + + calendarMap.computeIfAbsent(date.toLocalDate(), k -> new ArrayList<>()).add(dto); + } + } + + } + log.info("间隔任务(月){}【{}】生成完毕", task.getTaskId(), task.getTaskName()); + } + + } catch (Exception e) { + // 无效任务,跳过该任务 + log.error("任务异常,解析失败,taskId:{}", task.getTaskId(), e); + } + } + int totalCount = calendarMap.values().stream() + .mapToInt(List::size) + .sum(); + log.info("未来任务生成完毕,总任务数量:{}", totalCount); + // 将每一天的任务按开始时间排序 + calendarMap.values().forEach(list -> list.sort(Comparator.comparing(DailyTaskStatsDTO::getStartTime))); + return calendarMap; + } + + /** + * 将中文星期转换为DayOfWeek枚举 + */ + private DayOfWeek parseDayOfWeek(String week) { + switch (week) { + case "星期一": + return DayOfWeek.MONDAY; + case "星期二": + return DayOfWeek.TUESDAY; + case "星期三": + return DayOfWeek.WEDNESDAY; + case "星期四": + return DayOfWeek.THURSDAY; + case "星期五": + return DayOfWeek.FRIDAY; + case "星期六": + return DayOfWeek.SATURDAY; + case "星期日": + return DayOfWeek.SUNDAY; + default: + throw new IllegalArgumentException("不支持的星期: " + week); + } + } +} diff --git a/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/service/impl/CalenderServiceImpl.java b/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/service/impl/CalenderServiceImpl.java index 0bf243f..4f3f1ab 100644 --- a/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/service/impl/CalenderServiceImpl.java +++ b/inspect-main/inspect-main-task/src/main/java/com/inspect/calender/service/impl/CalenderServiceImpl.java @@ -3,7 +3,7 @@ package com.inspect.calender.service.impl; import com.inspect.calender.domain.DailyTaskStatsDTO; import com.inspect.calender.domain.MonthlyTaskStatsDTO; import com.inspect.calender.service.CalenderService; -import com.inspect.taskstatus.mapper.PatrolTaskStatusMapper; +import com.inspect.calender.service.TaskCalendarGenerator; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -12,24 +12,20 @@ import java.util.List; @Service public class CalenderServiceImpl implements CalenderService { @Resource - private PatrolTaskStatusMapper patrolTaskStatusMapper; + private TaskCalendarGenerator taskCalendarGenerator; @Override public List getMonthlyStatsByYear(Integer year) { - return patrolTaskStatusMapper.countMonthlyStatsByYear(year); + return taskCalendarGenerator.getMonthlyStatsByYear(year); } @Override public List getDailyStatsByYearAndMonth(Integer year, Integer month) { - List list = patrolTaskStatusMapper.selectDailyStatsByYearAndMonth(year, month); - list.forEach(DailyTaskStatsDTO::formatTimeAndStatus); - return list; + return taskCalendarGenerator.getFullCalendar(year, month); } @Override public List getDailyStatsByYearAndMonthAndDay(Integer year, Integer month, Integer day) { - List list = patrolTaskStatusMapper.selectDailyStatsByYearAndMonthAndDay(year, month, day); - list.forEach(DailyTaskStatsDTO::formatTimeAndStatus); - return list; + return taskCalendarGenerator.getFullCalendar(year, month, day); } } diff --git a/inspect-main/inspect-main-task/src/main/java/com/inspect/task/service/impl/PatrolTaskServiceImpl.java b/inspect-main/inspect-main-task/src/main/java/com/inspect/task/service/impl/PatrolTaskServiceImpl.java index 7f20199..8bc3412 100644 --- a/inspect-main/inspect-main-task/src/main/java/com/inspect/task/service/impl/PatrolTaskServiceImpl.java +++ b/inspect-main/inspect-main-task/src/main/java/com/inspect/task/service/impl/PatrolTaskServiceImpl.java @@ -3,10 +3,12 @@ package com.inspect.task.service.impl; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.inspect.analysis.domain.ResultAnalysis; import com.inspect.analysis.mapper.ResultAnalysisMapper; import com.inspect.base.core.utils.DateUtils; import com.inspect.base.core.utils.HttpClientUtils; import com.inspect.base.core.utils.StringUtils; +import com.inspect.calender.service.TaskCalendarGenerator; import com.inspect.partrolresult.util.PrintUtil; import com.inspect.resultmain.controller.PatrolTaskResultMainController; import com.inspect.resultmain.domain.PatrolTaskResultMain; @@ -41,6 +43,9 @@ public class PatrolTaskServiceImpl implements IPatrolTaskService { @Autowired private PatrolTaskResultMainController patrolTaskResultMainController; + @Autowired + private TaskCalendarGenerator taskCalendarGenerator; + final ResultAnalysisMapper resultAnalysisMapper; @Autowired @@ -96,23 +101,33 @@ public class PatrolTaskServiceImpl implements IPatrolTaskService { public int insertPatrolTask(PatrolTask patrolTask) { patrolTask.setCreateTime(DateUtils.getNowDate()); this.patrolTaskMapper.insertPatrolTask(patrolTask); + + taskCalendarGenerator.onTaskChange(); return patrolTask.getTaskId().intValue(); } public int updatePatrolTask(PatrolTask patrolTask) { - return this.patrolTaskMapper.updatePatrolTask(patrolTask); + int count = this.patrolTaskMapper.updatePatrolTask(patrolTask); + taskCalendarGenerator.onTaskChange(); + return count; } public int updatePatrolTaskTime(String taskId) { - return this.patrolTaskMapper.updatePatrolTaskTime(taskId); + int count = this.patrolTaskMapper.updatePatrolTaskTime(taskId); + taskCalendarGenerator.onTaskChange(); + return count; } public int deletePatrolTaskByTaskIds(Long[] taskIds) { - return this.patrolTaskMapper.deletePatrolTaskByTaskIds(taskIds); + int count = this.patrolTaskMapper.deletePatrolTaskByTaskIds(taskIds); + taskCalendarGenerator.onTaskChange(); + return count; } public int deletePatrolTaskByTaskId(Long taskId) { - return this.patrolTaskMapper.deletePatrolTaskByTaskId(taskId); + int count = this.patrolTaskMapper.deletePatrolTaskByTaskId(taskId); + taskCalendarGenerator.onTaskChange(); + return count; } public List> selectTaskRationalGroupByType() { @@ -220,6 +235,14 @@ public class PatrolTaskServiceImpl implements IPatrolTaskService { } catch (Exception e) { log.info("controller correctionAlgorithm HttpClientUtils.get execute exception: {}", e.getMessage()); } + if(!StringUtils.isEmpty(patrolData.getLineId())) { + ResultAnalysis resultAnalysis = new ResultAnalysis(); + resultAnalysis.setLineId(Long.valueOf(patrolData.getLineId())); + resultAnalysis.setResStatus("4"); + resultAnalysis.setDescription("已完成纠偏"); + log.info("controller correctionAlgorithm updateResultAnalysis: {}", resultAnalysis); + resultAnalysisMapper.updateResultAnalysis(resultAnalysis); + } } /**