package com.ruoyi.device.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.device.mapper.MaintenanceTaskMapper; import com.ruoyi.device.pojo.DeviceMaintenance; import com.ruoyi.device.pojo.MaintenanceTask; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.YearMonth; import java.time.LocalDate; import java.time.DayOfWeek; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * 设备保养任务测试类 * * 查询数据库中的保养任务,根据登记日期、频次、开始日期与时间来生成保养记录 * 从登记日期到今天,按频次生成所有符合的记录 */ @SpringBootTest public class MaintenanceTaskJobTest { @Autowired private MaintenanceTaskMapper maintenanceTaskMapper; @Autowired private DeviceMaintenanceServiceImpl deviceMaintenanceService; @Autowired private JdbcTemplate jdbcTemplate; /** * 测试:按日期分散生成保养记录 * 从登记日期到今天,按日期顺序,每天的所有设备保养记录一起生成 */ @Test void testGenerateOneByOne() { // 查询所有启用的保养任务 List tasks = maintenanceTaskMapper.selectList( new LambdaQueryWrapper() .eq(MaintenanceTask::getIsActive, 1) .eq(MaintenanceTask::getDeleted, 0) ); if (tasks.isEmpty()) { System.out.println("=== 没有找到保养任务 ==="); return; } // 找出最早和最晚的登记日期 LocalDate earliestDate = tasks.stream() .map(MaintenanceTask::getRegistrationDate) .filter(Objects::nonNull) .min(LocalDate::compareTo) .orElse(LocalDate.now()); LocalDate latestDate = LocalDate.now(); System.out.println("=== 登记日期范围: " + earliestDate + " 至 " + latestDate + " ==="); System.out.println("=== 共找到 " + tasks.size() + " 个保养任务 ===\n"); // 按日期顺序生成,每天生成一次 LocalDate currentDate = earliestDate; int totalRecords = 0; while (!currentDate.isAfter(latestDate)) { final LocalDate date = currentDate; final List recordsToSave = new ArrayList<>(); // 找出当天需要保养的所有任务 for (MaintenanceTask task : tasks) { LocalDate startDate = task.getRegistrationDate() != null ? task.getRegistrationDate() : LocalDate.now(); if (date.isBefore(startDate)) { continue; // 跳过登记日期之前的日期 } // 检查该任务的频次是否匹配当天 if (isExecutionDate(task.getFrequencyType(), task.getFrequencyDetail(), startDate, date)) { LocalTime time = getExecutionTime(task.getFrequencyDetail()); LocalDateTime executionDateTime = LocalDateTime.of(date, time); DeviceMaintenance record = createMaintenanceRecord(task, executionDateTime); recordsToSave.add(record); } } // 如果当天有记录,批量保存 if (!recordsToSave.isEmpty()) { for (DeviceMaintenance record : recordsToSave) { try { deviceMaintenanceService.save(record); System.out.println(" → 计划日期: " + date + " | 设备: " + record.getDeviceName() + " | 记录ID: " + record.getId()); } catch (Exception e) { System.out.println(" ✗ 计划日期: " + date + " | 设备: " + record.getDeviceName() + " | 失败: " + e.getMessage()); } } totalRecords += recordsToSave.size(); System.out.println(" === 日期 " + date + " 共生成 " + recordsToSave.size() + " 条记录 ===\n"); } currentDate = currentDate.plusDays(1); } System.out.println("=== 执行完成: 共生成 " + totalRecords + " 条记录 ==="); } /** * 检查指定日期是否符合任务的执行频次 */ private boolean isExecutionDate(String frequencyType, String frequencyDetail, LocalDate startDate, LocalDate checkDate) { if (checkDate.isBefore(startDate)) { return false; } switch (frequencyType) { case "DAILY": return true; // 每天都需要执行 case "WEEKLY": return isWeeklyMatch(frequencyDetail, checkDate); case "MONTHLY": return isMonthlyMatch(frequencyDetail, checkDate); case "QUARTERLY": return isQuarterlyMatch(frequencyDetail, checkDate); default: return false; } } private boolean isWeeklyMatch(String detail, LocalDate date) { String[] parts = detail.split(","); String dayOfWeekStr = parts[0]; DayOfWeek targetDay = parseDayOfWeek(dayOfWeekStr); return date.getDayOfWeek() == targetDay; } private boolean isMonthlyMatch(String detail, LocalDate date) { String[] parts = detail.split(","); int targetDay = Integer.parseInt(parts[0]); return date.getDayOfMonth() == targetDay; } private boolean isQuarterlyMatch(String detail, LocalDate date) { String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); // 季度中的第几个月 int targetDay = Integer.parseInt(parts[1]); int currentMonth = date.getMonthValue(); int monthInQuarter = (currentMonth - 1) % 3 + 1; return monthInQuarter == quarterMonth && date.getDayOfMonth() == targetDay; } private LocalTime getExecutionTime(String frequencyDetail) { String[] parts = frequencyDetail.split(","); if (parts.length >= 2 && (parts.length == 2 || parts.length == 3)) { return LocalTime.parse(parts[parts.length - 1]); } return LocalTime.of(9, 0); // 默认时间 } /** * 测试:根据所有保养任务生成设备保养记录 * 按顺序处理每个任务,从登记日期到今天,按频次依次生成所有符合的记录 */ @Test void testGenerateMaintenanceRecordForAllTasks() { // 查询所有启用的保养任务,按ID排序 List tasks = maintenanceTaskMapper.selectList( new LambdaQueryWrapper() .eq(MaintenanceTask::getIsActive, 1) .eq(MaintenanceTask::getDeleted, 0) .orderByAsc(MaintenanceTask::getId) ); System.out.println("=== 共找到 " + tasks.size() + " 个保养任务 ===\n"); int totalRecords = 0; for (MaintenanceTask task : tasks) { try { // 按顺序生成该任务所有符合频次的记录 int recordCount = generateRecordsForTaskSequentially(task); totalRecords += recordCount; System.out.println("✓ 任务ID: " + task.getId() + " | " + task.getTaskName() + " | 登记日期: " + task.getRegistrationDate() + " | 生成记录数: " + recordCount); } catch (Exception e) { System.out.println("✗ 任务ID: " + task.getId() + " | " + task.getTaskName() + " | 失败: " + e.getMessage()); } } System.out.println("\n=== 执行完成: 共生成 " + totalRecords + " 条记录 ==="); } /** * 为单个任务按顺序生成所有符合频次的记录 */ private int generateRecordsForTaskSequentially(MaintenanceTask task) { LocalDate startDate = task.getRegistrationDate(); if (startDate == null) { startDate = LocalDate.now(); } LocalDate endDate = LocalDate.now(); // 根据频次获取所有需要执行的日期 List executionDates = getExecutionDates(task.getFrequencyType(), task.getFrequencyDetail(), startDate, endDate); if (executionDates.isEmpty()) { return 0; } int count = 0; for (LocalDateTime executionDate : executionDates) { try { // 按顺序生成保养记录 DeviceMaintenance record = createMaintenanceRecord(task, executionDate); deviceMaintenanceService.save(record); count++; } catch (Exception e) { System.out.println(" ✗ 日期: " + executionDate + " | 失败: " + e.getMessage()); } } // 更新任务的下次执行时间 if (count > 0) { // first_execution = 第一条记录的日期 = last_execution_time LocalDateTime firstExecution = executionDates.get(0); // next_execution = 最后一条记录的下一次执行日期 LocalDateTime lastExecution = executionDates.get(executionDates.size() - 1); LocalDateTime nextExecution = calculateNextExecutionTime(task.getFrequencyType(), task.getFrequencyDetail(), lastExecution); updateTaskExecutionTime(task.getId(), firstExecution, nextExecution); } return count; } /** * 获取指定日期范围内所有符合频次的执行时间 */ private List getExecutionDates(String frequencyType, String frequencyDetail, LocalDate startDate, LocalDate endDate) { List dates = new ArrayList<>(); switch (frequencyType) { case "DAILY": dates.addAll(getDailyDates(startDate, endDate, frequencyDetail)); break; case "WEEKLY": dates.addAll(getWeeklyDates(startDate, endDate, frequencyDetail)); break; case "MONTHLY": dates.addAll(getMonthlyDates(startDate, endDate, frequencyDetail)); break; case "QUARTERLY": dates.addAll(getQuarterlyDates(startDate, endDate, frequencyDetail)); break; } return dates; } private List getDailyDates(LocalDate startDate, LocalDate endDate, String timeStr) { List dates = new ArrayList<>(); LocalTime time = LocalTime.parse(timeStr); LocalDate current = startDate; while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusDays(1); } return dates; } private List getMonthlyDates(LocalDate startDate, LocalDate endDate, String detail) { List dates = new ArrayList<>(); String[] parts = detail.split(","); int dayOfMonth = Integer.parseInt(parts[0]); LocalTime time = LocalTime.parse(parts[1]); // 从登记日期所在月找第一个符合条件的日期 int actualDay = Math.min(dayOfMonth, startDate.lengthOfMonth()); LocalDate current = startDate.withDayOfMonth(actualDay); // 如果这个日期在登记日期之前,往后推一个月 if (current.isBefore(startDate)) { current = current.plusMonths(1); actualDay = Math.min(dayOfMonth, current.lengthOfMonth()); current = current.withDayOfMonth(actualDay); } while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusMonths(1); actualDay = Math.min(dayOfMonth, current.lengthOfMonth()); current = current.withDayOfMonth(actualDay); } return dates; } private List getWeeklyDates(LocalDate startDate, LocalDate endDate, String detail) { List dates = new ArrayList<>(); String[] parts = detail.split(","); String dayOfWeekStr = parts[0]; LocalTime time = LocalTime.parse(parts[1]); java.time.DayOfWeek targetDay = parseDayOfWeek(dayOfWeekStr); LocalDate current = startDate; while (!current.isAfter(endDate)) { if (current.getDayOfWeek() == targetDay) { dates.add(LocalDateTime.of(current, time)); } current = current.plusDays(1); } return dates; } private List getQuarterlyDates(LocalDate startDate, LocalDate endDate, String detail) { List dates = new ArrayList<>(); String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); int dayOfMonth = Integer.parseInt(parts[1]); LocalTime time = LocalTime.parse(parts[2]); int currentMonth = startDate.getMonthValue(); int targetMonth = ((currentMonth - 1) / 3) * 3 + quarterMonth; int yearAdjust = 0; if (targetMonth > 12) { targetMonth -= 12; yearAdjust = 1; } int actualDay = Math.min(dayOfMonth, YearMonth.of(startDate.getYear() + yearAdjust, targetMonth).lengthOfMonth()); LocalDate current = startDate.withYear(startDate.getYear() + yearAdjust) .withMonth(targetMonth) .withDayOfMonth(actualDay); while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusMonths(3); actualDay = Math.min(dayOfMonth, current.lengthOfMonth()); current = current.withDayOfMonth(actualDay); } return dates; } private java.time.DayOfWeek parseDayOfWeek(String dayOfWeekStr) { switch (dayOfWeekStr.toUpperCase()) { case "MON": return java.time.DayOfWeek.MONDAY; case "TUE": return java.time.DayOfWeek.TUESDAY; case "WED": return java.time.DayOfWeek.WEDNESDAY; case "THU": return java.time.DayOfWeek.THURSDAY; case "FRI": return java.time.DayOfWeek.FRIDAY; case "SAT": return java.time.DayOfWeek.SATURDAY; case "SUN": return java.time.DayOfWeek.SUNDAY; default: throw new IllegalArgumentException("无效的星期几: " + dayOfWeekStr); } } /** * 创建保养记录 */ private DeviceMaintenance createMaintenanceRecord(MaintenanceTask task, LocalDateTime executionDate) { DeviceMaintenance record = new DeviceMaintenance(); record.setDeviceName(task.getTaskName()); record.setMaintenanceTaskId(task.getId()); record.setDeviceLedgerId(task.getTaskId()); record.setMaintenancePlanTime(executionDate); record.setMaintenanceActuallyTime(executionDate); record.setMaintenanceActuallyName(task.getMaintenancePerson()); record.setFrequencyType(task.getFrequencyType()); record.setFrequencyDetail(task.getFrequencyDetail()); record.setTenantId(task.getTenantId()); record.setStatus(0); // 待保养 record.setDeviceModel(task.getDeviceModel()); record.setMachineryCategory(task.getMachineryCategory()); record.setCreateUser(Integer.parseInt(task.getRegistrantId().toString())); record.setUpdateTime(executionDate); record.setCreateTime(executionDate); record.setUpdateUser(Integer.parseInt(task.getRegistrantId().toString())); return record; } /** * 更新任务的执行时间 */ private void updateTaskExecutionTime(Long taskId, LocalDateTime lastExecutionTime, LocalDateTime nextExecutionTime) { String sql = "UPDATE maintenance_task SET last_execution_time = ?, next_execution_time = ? WHERE id = ?"; jdbcTemplate.update(sql, lastExecutionTime, nextExecutionTime, taskId); } /** * 计算下次执行时间 */ private LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) { 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); }; } private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) { LocalTime executionTime = LocalTime.parse(timeStr); LocalDateTime nextTime = LocalDateTime.of(current.toLocalDate(), executionTime); return current.isBefore(nextTime) ? nextTime : nextTime.plusDays(1); } private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); int dayOfMonth = Integer.parseInt(parts[0]); LocalTime time = LocalTime.parse(parts[1]); return current.plusMonths(1) .withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(1).toLocalDate().lengthOfMonth())) .with(time); } private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime current) { return current.plusWeeks(1); } private LocalDateTime calculateQuarterlyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); int dayOfMonth = Integer.parseInt(parts[1]); LocalTime time = LocalTime.parse(parts[2]); int currentMonthInQuarter = (current.getMonthValue() - 1) % 3 + 1; YearMonth targetYearMonth; if (currentMonthInQuarter < quarterMonth) { targetYearMonth = YearMonth.from(current).plusMonths(quarterMonth - currentMonthInQuarter); } else { targetYearMonth = YearMonth.from(current).plusMonths(3 - currentMonthInQuarter + quarterMonth); } int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth()); return LocalDateTime.of(targetYearMonth.getYear(), targetYearMonth.getMonthValue(), adjustedDay, time.getHour(), time.getMinute()); } }