3 天以前 c477506a6d672f71c6353608f3cf3424d8026790
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java
@@ -1,29 +1,41 @@
package com.ruoyi.inspectiontask.service.impl;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.device.mapper.DeviceLedgerMapper;
import com.ruoyi.device.pojo.DeviceLedger;
import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper;
import com.ruoyi.inspectiontask.mapper.TimingTaskMapper;
import com.ruoyi.inspectiontask.pojo.InspectionTask;
import com.ruoyi.inspectiontask.pojo.TimingTask;
import com.ruoyi.inspectiontask.service.TimingTaskService;
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 org.springframework.stereotype.Component;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Component
@DisallowConcurrentExecution // 禁止并发执行同一个Job
@DisallowConcurrentExecution
public class TimingTaskJob implements Job, Serializable {
    private static final long serialVersionUID = 1L; // 必须定义序列化ID
    private static final long serialVersionUID = 1L;
    @Autowired
    private TimingTaskMapper timingTaskMapper;
@@ -37,93 +49,96 @@
    @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("taskId");
        try {
            // 3. 尝试查询你的业务数据
            // 通过JDBC模板查询定时任务信息,使用参数化查询防止SQL注入
            String yourSql = "SELECT * FROM timing_task where id = ?";
            String sql = "SELECT * FROM timing_task WHERE id = ?";
            List<TimingTask> tasks = jdbcTemplate.query(
                    yourSql,
                    sql,
                    new BeanPropertyRowMapper<>(TimingTask.class),
                    taskId
            );
            TimingTask timingTask = tasks.isEmpty() ? null : tasks.get(0);
            if (timingTask == null) {
                throw new JobExecutionException("找不到定时任务: " + taskId);
                throw new JobExecutionException("鎵句笉鍒板畾鏃朵换鍔? " + taskId);
            }
//            if (!timingTask.isActive()) {
//                throw new JobExecutionException("定时任务已禁用: " + taskId);
//            }
            // 2. 创建并保存巡检任务记录 - 这就是您提供的代码应该放的位置
            InspectionTask inspectionTask = createInspectionTask(timingTask);
            inspectionTaskMapper.insert(inspectionTask);
            List<Long> deviceIds = resolveTaskIds(timingTask);
            if (deviceIds.isEmpty()) {
                throw new JobExecutionException("瀹氭椂浠诲姟鏈厤缃澶? " + taskId);
            }
            // 3. 更新定时任务的执行时间
            for (Long deviceId : deviceIds) {
                DeviceLedger deviceLedger = deviceLedgerMapper.selectById(deviceId);
                if (deviceLedger == null) {
                    continue;
                }
                int count = getDeviceCount(deviceLedger.getNumber());
                for (int i = 0; i < count; i++) {
                    inspectionTaskMapper.insert(createInspectionTask(timingTask, deviceLedger));
                }
            }
            if (!tasks.isEmpty()) {
                TimingTask task = tasks.get(0);
                // 更新最后执行时间为当前时间
                LocalDateTime lastExecutionTime = LocalDateTime.now();
                // 计算下次执行时间
                LocalDateTime nextExecutionTime = calculateNextExecutionTime(
                        task.getFrequencyType(),
                        task.getFrequencyDetail(),
                        timingTask.getFrequencyType(),
                        timingTask.getFrequencyDetail(),
                        lastExecutionTime
                );
                // 执行更新操作
                String updateSql = "UPDATE timing_task " +
                        "SET last_execution_time = ?, next_execution_time = ? " +
                        "WHERE id = ?";
                jdbcTemplate.update(
                        updateSql,
                        lastExecutionTime,
                        nextExecutionTime,
                        taskId
                );
                String updateSql = "UPDATE timing_task SET last_execution_time = ?, next_execution_time = ? WHERE id = ?";
                jdbcTemplate.update(updateSql, lastExecutionTime, nextExecutionTime, taskId);
            }
//            timingTaskService.updateTaskExecutionTime(taskId);
            // 4. 记录执行日志
//            timingTaskService.recordExecutionLog(taskId, true, "任务执行成功,生成巡检任务ID: " + inspectionTask.getId());
        } catch (Exception e) {
//            timingTaskService.recordExecutionLog(taskId, false, "任务执行失败: " + e.getMessage());
            throw new JobExecutionException(e);
        }
    }
    // 这就是您提供的代码封装成的方法
    private InspectionTask createInspectionTask(TimingTask timingTask) {
        InspectionTask inspectionTask = new InspectionTask();
    private List<Long> resolveTaskIds(TimingTask timingTask) {
        if (StringUtils.isNotBlank(timingTask.getTaskIdsStr())) {
            return Arrays.stream(timingTask.getTaskIdsStr().split(","))
                    .filter(StringUtils::isNotBlank)
                    .map(String::trim)
                    .map(Long::valueOf)
                    .distinct()
                    .collect(Collectors.toList());
        }
        if (timingTask.getTaskId() != null) {
            return new ArrayList<>(Arrays.asList(timingTask.getTaskId().longValue()));
        }
        return new ArrayList<>();
    }
        // 复制基本属性
        inspectionTask.setTaskName(timingTask.getTaskName());
        inspectionTask.setTaskId(timingTask.getTaskId());
    private int getDeviceCount(BigDecimal number) {
        if (number == null) {
            return 1;
        }
        int count = number.intValue();
        return count > 0 ? count : 1;
    }
    private InspectionTask createInspectionTask(TimingTask timingTask, DeviceLedger deviceLedger) {
        InspectionTask inspectionTask = new InspectionTask();
        inspectionTask.setTaskName(deviceLedger.getDeviceName());
        inspectionTask.setTaskId(deviceLedger.getId().intValue());
        inspectionTask.setAreaId(deviceLedger.getAreaId());
        inspectionTask.setInspectorId(timingTask.getInspectorIds());
        inspectionTask.setInspectionLocation(timingTask.getInspectionLocation());
        inspectionTask.setRemarks("自动生成自定时任务ID: " + timingTask.getId());
        inspectionTask.setRemarks("鑷姩鐢熸垚鑷畾鏃朵换鍔D: " + timingTask.getId());
        inspectionTask.setRegistrantId(timingTask.getRegistrantId());
        inspectionTask.setRegistrant(timingTask.getRegistrant());
        inspectionTask.setFrequencyType(timingTask.getFrequencyType());
        inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail());
        inspectionTask.setTenantId(timingTask.getTenantId());
        return inspectionTask;
    }
    /**
     * 计算下次执行时间
     */
    private LocalDateTime calculateNextExecutionTime(String frequencyType,
                                                     String frequencyDetail,
                                                     LocalDateTime currentTime) {
@@ -137,94 +152,64 @@
                    return calculateMonthlyNextTime(frequencyDetail, currentTime);
                case "QUARTERLY":
                    return calculateQuarterlyNextTime(frequencyDetail, currentTime);
                case "YEARLY":
                    return calculateYearlyNextTime(frequencyDetail, currentTime);
                default:
                    throw new IllegalArgumentException("不支持的频率类型: " + frequencyType);
                    throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + frequencyType);
            }
        } catch (Exception e) {
            throw new RuntimeException("计算下次执行时间失败: " + e.getMessage(), e);
            throw new RuntimeException("璁$畻涓嬫鎵ц鏃堕棿澶辫触: " + e.getMessage(), e);
        }
    }
    /**
     * 计算每日任务的下次执行时间
     */
    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("无法找到下次执行时间");
                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(
                targetYearMonth.getYear(),
                targetYearMonth.getMonthValue(),
@@ -234,23 +219,52 @@
        );
    }
    /**
     * 解析星期几字符串
     */
    private LocalDateTime calculateYearlyNextTime(String detail, LocalDateTime current) {
        String[] parts = detail.split(",");
        int month = Integer.parseInt(parts[0]);
        int dayOfMonth = Integer.parseInt(parts[1]);
        LocalTime time = LocalTime.parse(parts[2]);
        YearMonth targetYearMonth = YearMonth.of(current.getYear(), month);
        int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth());
        LocalDateTime target = LocalDateTime.of(current.getYear(), month, adjustedDay, time.getHour(), time.getMinute());
        if (!target.isAfter(current)) {
            targetYearMonth = YearMonth.of(current.getYear() + 1, month);
            adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth());
            target = LocalDateTime.of(current.getYear() + 1, month, adjustedDay, time.getHour(), time.getMinute());
        }
        return target;
    }
    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);
            }
        }