| | |
| | | package com.ruoyi.device.service.impl; |
| | | |
| | | import com.ruoyi.device.mapper.DeviceLedgerMapper; |
| | | import com.ruoyi.device.pojo.DeviceLedger; |
| | | import com.ruoyi.device.pojo.DeviceMaintenance; |
| | | import com.ruoyi.device.pojo.MaintenanceTask; |
| | | import org.quartz.*; |
| | | import org.quartz.DisallowConcurrentExecution; |
| | | import org.quartz.Job; |
| | | import org.quartz.JobDataMap; |
| | | import org.quartz.JobExecutionContext; |
| | | import org.quartz.JobExecutionException; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.jdbc.core.BeanPropertyRowMapper; |
| | | import org.springframework.jdbc.core.JdbcTemplate; |
| | |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | import java.time.YearMonth; |
| | | import java.util.Arrays; |
| | | import java.util.ArrayList; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | @Component |
| | | @DisallowConcurrentExecution // 禁止并发执行同一个Job |
| | | @DisallowConcurrentExecution |
| | | public class MaintenanceTaskJob implements Job, Serializable { |
| | | private static final long serialVersionUID = 1L; // 必须定义序列化ID |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Autowired |
| | | private DeviceMaintenanceServiceImpl deviceMaintenanceService; |
| | |
| | | @Autowired |
| | | private JdbcTemplate jdbcTemplate; |
| | | |
| | | @Autowired |
| | | private DeviceLedgerMapper deviceLedgerMapper; |
| | | |
| | | @Override |
| | | public void execute(JobExecutionContext context) throws JobExecutionException { |
| | | JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); |
| | | // 修复类型转换错误,正确获取taskId |
| | | Long taskId = jobDataMap.getLong("maintenanceTaskId"); |
| | | |
| | | try { |
| | | // 3. 尝试查询你的业务数据 |
| | | // 通过JDBC模板查询定时任务信息,使用参数化查询防止SQL注入 |
| | | String yourSql = "SELECT * FROM maintenance_task where id = ?"; |
| | | List<MaintenanceTask> tasks = jdbcTemplate.query( |
| | | yourSql, |
| | |
| | | ); |
| | | MaintenanceTask timingTask = tasks.isEmpty() ? null : tasks.get(0); |
| | | if (timingTask == null) { |
| | | throw new JobExecutionException("MaintenanceTaskJob找不到定时任务: " + taskId); |
| | | throw new JobExecutionException("MaintenanceTaskJob找不到定时任务 " + taskId); |
| | | } |
| | | |
| | | // 2. 创建并保存巡检任务记录 - 这就是您提供的代码应该放的位置 |
| | | DeviceMaintenance deviceMaintenance = createInspectionTask(timingTask); |
| | | deviceMaintenanceService.save(deviceMaintenance); |
| | | List<Long> deviceIds = resolveDeviceIds(timingTask); |
| | | List<DeviceMaintenance> maintenanceList = new ArrayList<>(); |
| | | for (Long deviceId : deviceIds) { |
| | | int quantity = resolveQuantity(deviceId); |
| | | for (int i = 0; i < quantity; i++) { |
| | | maintenanceList.add(createInspectionTask(timingTask, deviceId)); |
| | | } |
| | | } |
| | | deviceMaintenanceService.saveBatch(maintenanceList); |
| | | |
| | | // 3. 更新定时任务的执行时间 |
| | | if (!tasks.isEmpty()) { |
| | | MaintenanceTask task = tasks.get(0); |
| | | |
| | | // 更新最后执行时间为当前时间 |
| | | LocalDateTime lastExecutionTime = LocalDateTime.now(); |
| | | |
| | | // 计算下次执行时间 |
| | | LocalDateTime nextExecutionTime = calculateNextExecutionTime( |
| | | task.getFrequencyType(), |
| | | task.getFrequencyDetail(), |
| | | lastExecutionTime |
| | | ); |
| | | |
| | | // 执行更新操作 |
| | | String updateSql = "UPDATE maintenance_task " + |
| | | "SET last_execution_time = ?, next_execution_time = ? " + |
| | | "WHERE id = ?"; |
| | | |
| | | jdbcTemplate.update( |
| | | updateSql, |
| | | lastExecutionTime, |
| | | nextExecutionTime, |
| | | taskId |
| | | ); |
| | | String updateSql = "UPDATE maintenance_task SET last_execution_time = ?, next_execution_time = ? WHERE id = ?"; |
| | | jdbcTemplate.update(updateSql, lastExecutionTime, nextExecutionTime, taskId); |
| | | } |
| | | } catch (Exception e) { |
| | | throw new JobExecutionException(e); |
| | | } |
| | | } |
| | | |
| | | // 这就是您提供的代码封装成的方法 |
| | | private DeviceMaintenance createInspectionTask(MaintenanceTask timingTask) { |
| | | private DeviceMaintenance createInspectionTask(MaintenanceTask timingTask, Long deviceLedgerId) { |
| | | DeviceMaintenance inspectionTask = new DeviceMaintenance(); |
| | | |
| | | // 复制基本属性 |
| | | inspectionTask.setDeviceName(timingTask.getTaskName()); |
| | | DeviceLedger deviceLedger = deviceLedgerMapper.selectById(deviceLedgerId); |
| | | inspectionTask.setDeviceName(deviceLedger != null ? deviceLedger.getDeviceName() : timingTask.getTaskName()); |
| | | inspectionTask.setMaintenanceTaskId(timingTask.getId()); |
| | | inspectionTask.setDeviceLedgerId(timingTask.getTaskId()); |
| | | inspectionTask.setDeviceLedgerId(deviceLedgerId); |
| | | inspectionTask.setAreaId(timingTask.getAreaId() != null ? timingTask.getAreaId() : (deviceLedger != null ? deviceLedger.getAreaId() : null)); |
| | | inspectionTask.setMaintenancePlanTime(LocalDateTime.now()); |
| | | inspectionTask.setFrequencyType(timingTask.getFrequencyType()); |
| | | inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail()); |
| | | inspectionTask.setTenantId(timingTask.getTenantId()); |
| | | inspectionTask.setStatus(0); |
| | | inspectionTask.setDeviceModel(timingTask.getDeviceModel()); |
| | | inspectionTask.setDeviceModel(deviceLedger != null ? deviceLedger.getDeviceModel() : timingTask.getDeviceModel()); |
| | | inspectionTask.setCreateUser(Integer.parseInt(timingTask.getRegistrantId().toString())); |
| | | inspectionTask.setUpdateTime(LocalDateTime.now()); |
| | | inspectionTask.setCreateTime(LocalDateTime.now()); |
| | |
| | | return inspectionTask; |
| | | } |
| | | |
| | | private List<Long> resolveDeviceIds(MaintenanceTask timingTask) { |
| | | if (timingTask.getDeviceLedgerIdsStr() != null && !timingTask.getDeviceLedgerIdsStr().trim().isEmpty()) { |
| | | return Arrays.stream(timingTask.getDeviceLedgerIdsStr().split(",")) |
| | | .map(String::trim) |
| | | .filter(item -> !item.isEmpty()) |
| | | .map(Long::valueOf) |
| | | .distinct() |
| | | .collect(java.util.stream.Collectors.toList()); |
| | | } |
| | | List<Long> ids = new ArrayList<>(); |
| | | if (timingTask.getTaskId() != null) { |
| | | ids.add(timingTask.getTaskId()); |
| | | } |
| | | return ids; |
| | | } |
| | | |
| | | /** |
| | | * 计算下次执行时间 |
| | | */ |
| | | private LocalDateTime calculateNextExecutionTime(String frequencyType, |
| | | String frequencyDetail, |
| | | LocalDateTime currentTime) { |
| | | private int resolveQuantity(Long deviceLedgerId) { |
| | | try { |
| | | String sql = "SELECT number FROM device_ledger WHERE id = ?"; |
| | | Number quantity = jdbcTemplate.queryForObject(sql, Number.class, deviceLedgerId); |
| | | return quantity == null || quantity.intValue() < 1 ? 1 : quantity.intValue(); |
| | | } catch (Exception ignored) { |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | private LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) { |
| | | try { |
| | | switch (frequencyType) { |
| | | case "DAILY": |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 计算每日任务的下次执行时间 |
| | | */ |
| | | private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) { |
| | | LocalTime executionTime = LocalTime.parse(timeStr); // 解析格式 "HH:mm" |
| | | LocalTime executionTime = LocalTime.parse(timeStr); |
| | | LocalDateTime nextTime = LocalDateTime.of(current.toLocalDate(), executionTime); |
| | | |
| | | // 如果今天的时间已过,则安排明天 |
| | | return current.isBefore(nextTime) ? nextTime : nextTime.plusDays(1); |
| | | } |
| | | |
| | | /** |
| | | * 计算每周任务的下次执行时间 |
| | | */ |
| | | private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime current) { |
| | | String[] parts = detail.split(","); |
| | | String dayOfWeekStr = parts[0]; // 如 "MON" 或 "MON|WED|FRI" |
| | | LocalTime time = LocalTime.parse(parts[1]); // 时间部分 |
| | | |
| | | // 解析星期几(支持多个星期) |
| | | String dayOfWeekStr = parts[0]; |
| | | LocalTime time = LocalTime.parse(parts[1]); |
| | | Set<DayOfWeek> targetDays = parseDayOfWeeks(dayOfWeekStr); |
| | | |
| | | // 从当前时间开始找下一个符合条件的星期几 |
| | | LocalDateTime nextTime = current; |
| | | while (true) { |
| | | nextTime = nextTime.plusDays(1); |
| | | if (targetDays.contains(nextTime.getDayOfWeek())) { |
| | | return LocalDateTime.of(nextTime.toLocalDate(), time); |
| | | } |
| | | |
| | | // 防止无限循环(理论上不会发生) |
| | | if (nextTime.isAfter(current.plusYears(1))) { |
| | | throw new RuntimeException("无法找到下次执行时间"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 计算每月任务的下次执行时间 |
| | | */ |
| | | private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime current) { |
| | | String[] parts = detail.split(","); |
| | | int dayOfMonth = Integer.parseInt(parts[0]); |
| | | LocalTime time = LocalTime.parse(parts[1]); |
| | | |
| | | // 从下个月开始计算 |
| | | LocalDateTime nextTime = current.plusMonths(1) |
| | | return current.plusMonths(1) |
| | | .withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(1).toLocalDate().lengthOfMonth())) |
| | | .with(time); |
| | | |
| | | return nextTime; |
| | | } |
| | | |
| | | /** |
| | | * 计算每季度任务的下次执行时间 |
| | | */ |
| | | private LocalDateTime calculateQuarterlyNextTime(String detail, LocalDateTime current) { |
| | | String[] parts = detail.split(","); |
| | | int quarterMonth = Integer.parseInt(parts[0]); // 1=第1个月,2=第2个月,3=第3个月 |
| | | int quarterMonth = Integer.parseInt(parts[0]); |
| | | int dayOfMonth = Integer.parseInt(parts[1]); |
| | | LocalTime time = LocalTime.parse(parts[2]); |
| | | |
| | | // 计算当前季度 |
| | | int currentQuarter = (current.getMonthValue() - 1) / 3 + 1; |
| | | int currentMonthInQuarter = (current.getMonthValue() - 1) % 3 + 1; |
| | | |
| | | YearMonth targetYearMonth; |
| | | if (currentMonthInQuarter < quarterMonth) { |
| | | // 本季度内还有执行机会 |
| | | targetYearMonth = YearMonth.from(current) |
| | | .plusMonths(quarterMonth - currentMonthInQuarter); |
| | | targetYearMonth = YearMonth.from(current).plusMonths(quarterMonth - currentMonthInQuarter); |
| | | } else { |
| | | // 需要到下个季度 |
| | | targetYearMonth = YearMonth.from(current) |
| | | .plusMonths(3 - currentMonthInQuarter + quarterMonth); |
| | | targetYearMonth = YearMonth.from(current).plusMonths(3 - currentMonthInQuarter + quarterMonth); |
| | | } |
| | | |
| | | // 处理月末日期 |
| | | int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth()); |
| | | |
| | | return LocalDateTime.of( |
| | |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * 解析星期几字符串 |
| | | */ |
| | | private Set<DayOfWeek> parseDayOfWeeks(String dayOfWeekStr) { |
| | | Set<DayOfWeek> days = new HashSet<>(); |
| | | String[] dayStrs = dayOfWeekStr.split("\\|"); |
| | | |
| | | 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; |
| | | default: throw new IllegalArgumentException("无效的星期几: " + 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; |
| | | default: |
| | | throw new IllegalArgumentException("无效的星期几: " + dayStr); |
| | | } |
| | | } |
| | | |
| | | return days; |
| | | } |
| | | } |