src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,61 @@ package com.ruoyi.device.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.device.pojo.DeviceMaintenance; import com.ruoyi.device.pojo.MaintenanceTask; import com.ruoyi.device.service.MaintenanceTaskService; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.AjaxResult; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @author :yys * @date : 2025/12/22 14:58 */ @Api(tags = "设å¤ä¿å »å®æ¶ä»»å¡ç®¡ç") @RestController @RequestMapping("/deviceMaintenanceTask") public class MaintenanceTaskController extends BaseController { @Autowired private MaintenanceTaskService maintenanceTaskService; @GetMapping("/listPage") @ApiOperation(value = "设å¤ä¿å »å®æ¶ä»»å¡å表") public AjaxResult listPage(Page page, MaintenanceTask maintenanceTask) { return maintenanceTaskService.listPage(page,maintenanceTask); } @PostMapping("/add") @ApiOperation(value = "æ·»å 设å¤ä¿å »å®æ¶ä»»å¡") @Log(title = "设å¤ä¿å »å®æ¶ä»»å¡", businessType = BusinessType.INSERT) public AjaxResult add(@RequestBody MaintenanceTask maintenanceTask) { return maintenanceTaskService.add(maintenanceTask); } @PostMapping("/update") @ApiOperation(value = "ä¿®æ¹è®¾å¤ä¿å »å®æ¶ä»»å¡") @Log(title = "设å¤ä¿å »å®æ¶ä»»å¡", businessType = BusinessType.UPDATE) public AjaxResult update(@RequestBody MaintenanceTask maintenanceTask) { return maintenanceTaskService.updateByMaintenanceTaskId(maintenanceTask); } @DeleteMapping("/delete") @ApiOperation(value = "å é¤è®¾å¤ä¿å »å®æ¶ä»»å¡") @Log(title = "设å¤ä¿å »å®æ¶ä»»å¡", businessType = BusinessType.DELETE) public AjaxResult delete(@RequestBody List<Long> ids) { return maintenanceTaskService.delete(ids); } } src/main/java/com/ruoyi/device/mapper/MaintenanceTaskMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,12 @@ package com.ruoyi.device.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.device.pojo.MaintenanceTask; import com.ruoyi.inspectiontask.pojo.TimingTask; /** * @author :yys * @date : 2025/12/22 14:56 */ public interface MaintenanceTaskMapper extends BaseMapper<MaintenanceTask> { } src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
@@ -1,6 +1,7 @@ package com.ruoyi.device.pojo; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.framework.aspectj.lang.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import com.baomidou.mybatisplus.annotation.*; @@ -22,6 +23,21 @@ @ApiModelProperty("设å¤å°è´¦id") private Long deviceLedgerId; @ApiModelProperty("ä¿å »ä»»å¡id") private Long maintenanceTaskId; @ApiModelProperty(value = "颿¬¡") private String frequencyType; @ApiModelProperty(value = "颿¬¡è¯¦æ ") private String frequencyDetail; @ApiModelProperty(value = "䏿¬¡æ§è¡æ¶é´") private LocalDateTime nextExecutionTime; @ApiModelProperty(value = "æåæ§è¡æ¶é´") private LocalDateTime lastExecutionTime; private String deviceName; src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,109 @@ package com.ruoyi.device.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.framework.aspectj.lang.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDate; import java.time.LocalDateTime; /** * @author :yys * @date : 2025/9/19 10:27 */ @Data @ApiModel @TableName("maintenance_task") public class MaintenanceTask { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "è§æ ¼åå·") private String deviceModel; /** * 主é®ID */ @TableId(type = IdType.AUTO) private Long id; @ApiModelProperty(value = "设å¤åç§°") @Excel(name = "ä¿å »ä»»å¡åç§°") private String taskName; @ApiModelProperty(value = "设å¤id") private Long taskId; @ApiModelProperty(value = "颿¬¡") @Excel(name = "颿¬¡") private String frequencyType; @ApiModelProperty(value = "颿¬¡è¯¦æ ") @Excel(name = "å¼å§æ¥æä¸æ¶é´") private String frequencyDetail; @ApiModelProperty(value = "䏿¬¡æ§è¡æ¶é´") private LocalDateTime nextExecutionTime; @ApiModelProperty(value = "æåæ§è¡æ¶é´") private LocalDateTime lastExecutionTime; @ApiModelProperty(value = "æ¯å¦æ¿æ´»") private boolean isActive; @ApiModelProperty(value = "夿³¨") @Excel(name = "夿³¨") private String remarks; @ApiModelProperty(value = "å½å ¥äººid") private Long registrantId; @ApiModelProperty(value = "å½å ¥äºº") @Excel(name = "å½å ¥äºº") private String registrant; @ApiModelProperty(value = "å½å ¥æ¥æ") @Excel(name = "å½å ¥æ¥æ", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate registrationDate; @ApiModelProperty(value = "ç¶æ") private String status; @ApiModelProperty(value = "软å 餿 å¿ï¼0=æªå é¤ï¼1=å·²å é¤") private Integer deleted; @TableField(exist = false) private String dateStr; @ApiModelProperty(value = "å建该记å½çç¨æ·") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) private Integer createUser; @ApiModelProperty(value = "è®°å½å建æ¶é´") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) // @JsonFormat(pattern = "yyyy-MM-dd") // @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDateTime createTime; @ApiModelProperty(value = "æåä¿®æ¹è¯¥è®°å½çç¨æ·") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT_UPDATE) private Integer updateUser; @ApiModelProperty(value = "è®°å½æåæ´æ°æ¶é´") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; @ApiModelProperty(value = "ç§æ·ID") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) private Long tenantId; } src/main/java/com/ruoyi/device/service/MaintenanceTaskService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,25 @@ package com.ruoyi.device.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.device.pojo.DeviceMaintenance; import com.ruoyi.device.pojo.MaintenanceTask; import com.ruoyi.framework.web.domain.AjaxResult; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; /** * @author :yys * @date : 2025/12/22 14:56 */ public interface MaintenanceTaskService extends IService<MaintenanceTask> { AjaxResult listPage(Page page, MaintenanceTask maintenanceTask); AjaxResult add(MaintenanceTask maintenanceTask); AjaxResult updateByMaintenanceTaskId(MaintenanceTask maintenanceTask); AjaxResult delete(List<Long> ids); } src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,248 @@ package com.ruoyi.device.service.impl; import com.ruoyi.device.pojo.DeviceMaintenance; import com.ruoyi.device.pojo.MaintenanceTask; 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.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.time.DayOfWeek; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.YearMonth; import java.util.HashSet; import java.util.List; import java.util.Set; @Component @DisallowConcurrentExecution // ç¦æ¢å¹¶åæ§è¡åä¸ä¸ªJob public class MaintenanceTaskJob implements Job, Serializable { private static final long serialVersionUID = 1L; // å¿ é¡»å®ä¹åºååID @Autowired private DeviceMaintenanceServiceImpl deviceMaintenanceService; @Autowired private JdbcTemplate jdbcTemplate; @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, new BeanPropertyRowMapper<>(MaintenanceTask.class), taskId ); MaintenanceTask timingTask = tasks.isEmpty() ? null : tasks.get(0); if (timingTask == null) { throw new JobExecutionException("MaintenanceTaskJobæ¾ä¸å°å®æ¶ä»»å¡: " + taskId); } // 2. å建并ä¿åå·¡æ£ä»»å¡è®°å½ - è¿å°±æ¯æ¨æä¾ç代ç åºè¯¥æ¾çä½ç½® DeviceMaintenance deviceMaintenance = createInspectionTask(timingTask); deviceMaintenanceService.save(deviceMaintenance); // 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 ); } } catch (Exception e) { throw new JobExecutionException(e); } } // è¿å°±æ¯æ¨æä¾ç代ç å°è£ æçæ¹æ³ private DeviceMaintenance createInspectionTask(MaintenanceTask timingTask) { DeviceMaintenance inspectionTask = new DeviceMaintenance(); // å¤å¶åºæ¬å±æ§ inspectionTask.setDeviceName(timingTask.getTaskName()); inspectionTask.setMaintenanceTaskId(timingTask.getId()); inspectionTask.setDeviceLedgerId(timingTask.getTaskId()); inspectionTask.setMaintenancePlanTime(LocalDateTime.now()); inspectionTask.setFrequencyType(timingTask.getFrequencyType()); inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail()); inspectionTask.setTenantId(timingTask.getTenantId()); inspectionTask.setStatus(0); inspectionTask.setDeviceModel(timingTask.getDeviceModel()); inspectionTask.setCreateUser(Integer.parseInt(timingTask.getRegistrantId().toString())); inspectionTask.setUpdateTime(LocalDateTime.now()); inspectionTask.setCreateTime(LocalDateTime.now()); inspectionTask.setUpdateUser(Integer.parseInt(timingTask.getRegistrantId().toString())); return inspectionTask; } /** * 计ç®ä¸æ¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) { try { switch (frequencyType) { case "DAILY": return calculateDailyNextTime(frequencyDetail, currentTime); case "WEEKLY": return calculateWeeklyNextTime(frequencyDetail, currentTime); case "MONTHLY": return calculateMonthlyNextTime(frequencyDetail, currentTime); case "QUARTERLY": return calculateQuarterlyNextTime(frequencyDetail, currentTime); default: throw new IllegalArgumentException("䏿¯æçé¢çç±»å: " + frequencyType); } } catch (Exception e) { throw new RuntimeException("计ç®ä¸æ¬¡æ§è¡æ¶é´å¤±è´¥: " + e.getMessage(), e); } } /** * è®¡ç®æ¯æ¥ä»»å¡ç䏿¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) { LocalTime executionTime = LocalTime.parse(timeStr); // è§£ææ ¼å¼ "HH:mm" 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]); // æ¶é´é¨å // è§£æææå (æ¯æå¤ä¸ªææ) 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) .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 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); } 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() ); } /** * è§£æææå å符串 */ 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); } } return days; } } src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,278 @@ package com.ruoyi.device.service.impl; import com.ruoyi.device.pojo.MaintenanceTask; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalTime; import java.time.ZoneId; import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.Date; import java.util.stream.Collectors; /** * @author :yys * @date : 2025/12/22 15:16 */ @Service @Slf4j public class MaintenanceTaskScheduler { @Autowired private Scheduler scheduler; /** * æ·»å æ°ä»»å¡å°è°åº¦å¨ */ public void scheduleMaintenanceTask(MaintenanceTask task){ try { JobDetail jobDetail = buildJobDetail(task); Trigger trigger = buildJobTrigger(task, jobDetail); scheduler.scheduleJob(jobDetail, trigger); }catch (SchedulerException e){ log.error("SchedulerException scheduleMaintenanceTask ERROR",e); throw new RuntimeException(e); } } /** * æ´æ°å·²æä»»å¡ */ public void rescheduleMaintenanceTask(MaintenanceTask task){ try{ TriggerKey triggerKey = new TriggerKey("triggerMaintenanceTask_" + task.getId()); // è·åç°æè§¦åå¨å¹¶è½¬æ¢ä¸º CronTrigger Trigger oldTrigger = scheduler.getTrigger(triggerKey); if (!(oldTrigger instanceof CronTrigger)) { throw new SchedulerException("Existing trigger is not a CronTrigger"); } // 3. æå»ºCronTriggerï¼ç¡®ä¿æä¹ åé ç½® CronTrigger newTrigger = TriggerBuilder.newTrigger() .withIdentity(triggerKey) // å¯ä¸æ è¯ï¼ç¨äºæä¹ ååå¨ .withDescription(task.getTaskName() + "_TRIGGER") // 触åå¨æè¿° .forJob(oldTrigger.getJobKey()) // å ³è对åºçJob .withSchedule(CronScheduleBuilder .cronSchedule(convertToCronExpression(task)) // éè¿æ§è¡æ¶ççç¥ï¼æ ¹æ®ä¸å¡è°æ´ï¼ ) // 4. 设置å¼å§æ¶é´ï¼è¥ä¸ºnullåç«å³çæï¼ .startAt(task.getNextExecutionTime() != null ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant()) : new Date()) .build(); scheduler.rescheduleJob(triggerKey, newTrigger); }catch (SchedulerException e){ log.error("SchedulerException rescheduleMaintenanceTask ERROR",e); throw new RuntimeException(e); } } /** * æåä»»å¡ */ public void pauseMaintenanceTask(Long taskId) throws SchedulerException { JobKey jobKey = new JobKey("MaintenanceTask_" + taskId); scheduler.pauseJob(jobKey); } /** * æ¢å¤ä»»å¡ */ public void resumeMaintenanceTask(Long taskId) throws SchedulerException { JobKey jobKey = new JobKey("MaintenanceTask_" + taskId); scheduler.resumeJob(jobKey); } /** * å é¤ä»»å¡ */ public void unscheduleMaintenanceTask(Long taskId){ try { JobKey jobKey = new JobKey("MaintenanceTask_" + taskId); scheduler.deleteJob(jobKey); }catch (SchedulerException e){ log.error("SchedulerException unscheduleMaintenanceTask ERROR",e); throw new RuntimeException(e); } } private JobDetail buildJobDetail(MaintenanceTask task) { // 1. æå»ºå¯ä¸JobKeyï¼åºäºä»»å¡IDï¼ç¡®ä¿éå¯åè½è¯å«ï¼ JobKey jobKey = new JobKey("MaintenanceTask_" + task.getId()); // 2. å°è£ 任塿°æ®ï¼ä» 使ç¨åºæ¬ç±»åï¼ç¡®ä¿å¯åºååï¼ JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("maintenanceTaskId", task.getId()); // ä»»å¡IDï¼Longï¼å¯åºååï¼ jobDataMap.put("taskName", task.getTaskName()); // ä»»å¡åç§°ï¼Stringï¼å¯åºååï¼ jobDataMap.put("taskType", task.getFrequencyType()); // ä»»å¡ç±»åï¼Stringï¼ // æéæ·»å å ¶ä»å¿ è¦çåºæ¬ç±»ååæ° // 3. æå»ºJobDetailï¼è®¾ç½®æä¹ åç¸å ³å±æ§ return JobBuilder.newJob(MaintenanceTaskJob.class) .withIdentity(jobKey) // å¯ä¸æ è¯ï¼ç¨äºæä¹ ååå¨ .withDescription(task.getTaskName()) // ä»»å¡æè¿°ï¼åå ¥æ°æ®åº .usingJobData(jobDataMap) // ç»å®ä»»å¡æ°æ® .storeDurably(true) // å³ä½¿æ²¡æè§¦åå¨å ³èä¹æä¹ åä¿å .requestRecovery(true) // å½è°åº¦å¨å´©æºåæ¢å¤æ¶ï¼éæ°æ§è¡æªå®æçä»»å¡ .build(); } private Trigger buildJobTrigger(MaintenanceTask task, JobDetail jobDetail) { // 1. æå»ºå¯ä¸TriggerKeyï¼åºäºä»»å¡IDï¼ TriggerKey triggerKey = new TriggerKey("triggerMaintenanceTask_" + task.getId()); // 2. çæCron表达å¼ï¼åé»è¾ä¸åï¼ String cronExpression = convertToCronExpression(task); // 3. æå»ºCronTriggerï¼ç¡®ä¿æä¹ åé ç½® return TriggerBuilder.newTrigger() .withIdentity(triggerKey) // å¯ä¸æ è¯ï¼ç¨äºæä¹ ååå¨ .withDescription(task.getTaskName() + "_TRIGGER") // 触åå¨æè¿° .forJob(jobDetail) // å ³è对åºçJob .withSchedule(CronScheduleBuilder .cronSchedule(cronExpression) .withMisfireHandlingInstructionDoNothing() // éè¿æ§è¡æ¶ççç¥ï¼æ ¹æ®ä¸å¡è°æ´ï¼ ) // 4. 设置å¼å§æ¶é´ï¼è¥ä¸ºnullåç«å³çæï¼ .startAt(task.getNextExecutionTime() != null ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant()) : new Date()) .build(); } private String convertToCronExpression(MaintenanceTask task) { // åæ°æ ¡éª if (task == null || task.getFrequencyType() == null || task.getFrequencyDetail() == null) { throw new IllegalArgumentException("ä»»å¡åæ°ä¸è½ä¸ºç©º"); } // 使ç¨switchç¡®ä¿æ¡ä»¶äºæ¥ String frequencyType = task.getFrequencyType().toUpperCase(); // ç»ä¸è½¬ä¸ºå¤§åæ¯è¾ switch (frequencyType) { case "DAILY": return convertDailyToCron(task.getFrequencyDetail()); case "WEEKLY": return convertWeeklyToCron(task.getFrequencyDetail()); case "MONTHLY": return convertMonthlyToCron(task.getFrequencyDetail()); case "QUARTERLY": return convertQuarterlyToCron(task.getFrequencyDetail()); default: throw new IllegalArgumentException("䏿¯æçé¢çç±»å: " + task.getFrequencyType()); } } // æ¯æ¥ä»»å¡è½¬æ¢ private String convertDailyToCron(String frequencyDetail) { LocalTime time = parseTime(frequencyDetail); return String.format("0 %d %d * * ?", time.getMinute(), time.getHour()); } // æ¯å¨ä»»å¡è½¬æ¢ private String convertWeeklyToCron(String frequencyDetail) { String[] parts = validateAndSplit(frequencyDetail, ",", 2); String daysOfWeek = convertDayNamesToCron(parts[0]); LocalTime time = parseTime(parts[1]); return String.format("0 %d %d ? * %s", time.getMinute(), time.getHour(), daysOfWeek); } // æ¯æä»»å¡è½¬æ¢ private String convertMonthlyToCron(String frequencyDetail) { String[] parts = validateAndSplit(frequencyDetail, ",", 2); int day = validateDayOfMonth(parts[0]); LocalTime time = parseTime(parts[1]); return String.format("0 %d %d %d * ?", time.getMinute(), time.getHour(), day); } // æ¯å£åº¦ä»»å¡è½¬æ¢ private String convertQuarterlyToCron(String frequencyDetail) { String[] parts = validateAndSplit(frequencyDetail, ",", 3); int month = validateMonth(parts[0]); // éªè¯æä»½(1-12) int day = validateDayOfMonth(parts[1]); // éªè¯æ¥æ LocalTime time = parseTime(parts[2]); // è§£ææ¶é´ // 计ç®å£åº¦èµ·å§æä»½(1æ=1, 4æ=4, 7æ=7, 10æ=10) int quarterStartMonth = ((month - 1) / 3) * 3 + 1; return String.format("0 %d %d %d %d/3 ?", time.getMinute(), time.getHour(), day, quarterStartMonth); } // æ°å¢éªè¯æä»½çæ¹æ³(1-12) private int validateMonth(String monthStr) { try { int month = Integer.parseInt(monthStr); if (month < 1 || month > 12) { throw new IllegalArgumentException("æä»½å¿ é¡»å¨1-12ä¹é´"); } return month; } catch (NumberFormatException e) { throw new IllegalArgumentException("æ æçæä»½æ ¼å¼"); } } // è¾ å©æ¹æ³ï¼è§£ææ¶é´ private LocalTime parseTime(String timeStr) { try { return LocalTime.parse(timeStr); } catch (DateTimeParseException e) { throw new IllegalArgumentException("æ¶é´æ ¼å¼å¿ 须为HH:mm", e); } } // è¾ å©æ¹æ³ï¼éªè¯å¹¶åå²å符串 private String[] validateAndSplit(String input, String delimiter, int expectedParts) { String[] parts = input.split(delimiter); if (parts.length != expectedParts) { throw new IllegalArgumentException( String.format("æ ¼å¼é误ï¼åºä¸º%dé¨åç¨'%s'åé", expectedParts, delimiter)); } return parts; } // è¾ å©æ¹æ³ï¼éªè¯æä»½ä¸çæ¥ private int validateDayOfMonth(String dayStr) { int day = Integer.parseInt(dayStr); if (day < 1 || day > 31) { throw new IllegalArgumentException("æ¥æå¿ é¡»å¨1-31ä¹é´"); } return day; } // è¾ å©æ¹æ³ï¼éªè¯å£åº¦ä¸çæ private int validateMonthInQuarter(String monthStr) { int month = Integer.parseInt(monthStr); if (month < 1 || month > 3) { throw new IllegalArgumentException("å£åº¦æä»½å¿ é¡»æ¯1ã2æ3"); } return month; } // è½¬æ¢ææå åç§° private String convertDayNamesToCron(String dayNames) { return Arrays.stream(dayNames.split("\\|")) .map(this::convertSingleDayName) .collect(Collectors.joining(",")); } // 转æ¢å个ææå åç§° private String convertSingleDayName(String dayName) { switch (dayName.toUpperCase()) { case "MON": return "MON"; case "TUE": return "TUE"; case "WED": return "WED"; case "THU": return "THU"; case "FRI": return "FRI"; case "SAT": return "SAT"; case "SUN": return "SUN"; default: throw new IllegalArgumentException("æ æçææå : " + dayName); } } } src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,119 @@ package com.ruoyi.device.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.device.mapper.MaintenanceTaskMapper; import com.ruoyi.device.pojo.DeviceMaintenance; import com.ruoyi.device.pojo.MaintenanceTask; import com.ruoyi.device.service.MaintenanceTaskService; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.inspectiontask.dto.TimingTaskDto; import com.ruoyi.inspectiontask.pojo.TimingTask; import com.ruoyi.inspectiontask.service.impl.TimingTaskServiceImpl; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; /** * @author :yys * @date : 2025/12/22 14:57 */ @Service @Slf4j public class MaintenanceTaskServiceImpl extends ServiceImpl<MaintenanceTaskMapper, MaintenanceTask> implements MaintenanceTaskService { @Autowired private MaintenanceTaskMapper maintenanceTaskMapper; @Autowired private SysUserMapper sysUserMapper; @Autowired private TimingTaskServiceImpl timingTaskService; @Autowired private MaintenanceTaskScheduler maintenanceTaskScheduler; @Override public AjaxResult listPage(Page page, MaintenanceTask maintenanceTask) { Page<MaintenanceTask> taskPage = maintenanceTaskMapper.selectPage(page, null); // 2. å¦ææ²¡ææ°æ®ï¼ç´æ¥è¿å空å页 if (taskPage.getRecords().isEmpty()) { return AjaxResult.success(taskPage); } // 3. æ¶éææéè¦æ¥è¯¢çç¨æ·ID Set<Long> userIds = new HashSet<>(); // æ¶éç»è®°äººID taskPage.getRecords().forEach(task -> { if (task.getRegistrantId() != null) { userIds.add(task.getRegistrantId()); } }); // 4. æ¹éæ¥è¯¢ç¨æ·ä¿¡æ¯ Map<Long, String> userNickNameMap = new HashMap<>(); if (!userIds.isEmpty()) { List<SysUser> users = sysUserMapper.selectUserByIds((new ArrayList<>(userIds))); users.forEach(user -> userNickNameMap.put(user.getUserId(), user.getNickName())); } taskPage.getRecords().forEach(task -> { // 设置ç»è®°äººæµç§° if (task.getRegistrantId() != null) { task.setRegistrant(userNickNameMap.getOrDefault(task.getRegistrantId(), "æªç¥ç¨æ·")); } }); return AjaxResult.success(taskPage); } @Override public AjaxResult add(MaintenanceTask maintenanceTask) { maintenanceTask.setActive(true); // 计ç®é¦æ¬¡æ§è¡æ¶é´ TimingTask task = new TimingTask(); task.setFrequencyType(maintenanceTask.getFrequencyType()); task.setFrequencyDetail(maintenanceTask.getFrequencyDetail()); LocalDateTime firstExecutionTime = timingTaskService.calculateFirstExecutionTime(task); maintenanceTask.setNextExecutionTime(firstExecutionTime); int insert = maintenanceTaskMapper.insert(maintenanceTask); if (insert > 0) { maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask); } return AjaxResult.success("æ·»å æå"); } @Override public AjaxResult updateByMaintenanceTaskId(MaintenanceTask maintenanceTask) { MaintenanceTask maintenanceTask1 = maintenanceTaskMapper.selectById(maintenanceTask.getId()); if (maintenanceTask1 == null) { return AjaxResult.warn("æ²¡ææ¤æ°æ®"); } BeanUtils.copyProperties(maintenanceTask, maintenanceTask1); int update = maintenanceTaskMapper.updateById(maintenanceTask1); if (update > 0) { maintenanceTaskScheduler.rescheduleMaintenanceTask(maintenanceTask1); } return AjaxResult.success("æ´æ°æå"); } @Override public AjaxResult delete(List<Long> ids) { int delete = maintenanceTaskMapper.deleteBatchIds(ids); if (delete > 0) { ids.forEach(id -> { maintenanceTaskScheduler.unscheduleMaintenanceTask(id); }); } return AjaxResult.success("å 餿å"); } } src/main/java/com/ruoyi/inspectiontask/controller/TimingTaskController.java
@@ -3,6 +3,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.R; import com.ruoyi.inspectiontask.dto.TimingTaskDto; @@ -58,6 +60,7 @@ */ @PostMapping("/addOrEditTimingTask") @ApiOperation(value = "æ°å¢ä¿®æ¹å®æ¶ä»»å¡") @Log(title = "宿¶ä»»å¡", businessType = BusinessType.INSERT) public R addOrEditTimingTask(@RequestBody TimingTaskDto timingTaskDto) throws SchedulerException { return R.ok(timingTaskService.addOrEditTimingTask(timingTaskDto)); } @@ -67,6 +70,7 @@ */ @DeleteMapping("/delTimingTask") @ApiOperation(value = "å é¤å®æ¶ä»»å¡") @Log(title = "宿¶ä»»å¡", businessType = BusinessType.DELETE) public R remove(@RequestBody Long[] ids) { return R.ok(timingTaskService.delByIds(ids)); } src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
@@ -84,9 +84,13 @@ /** * å é¤ä»»å¡ */ public void unscheduleTimingTask(Long taskId) throws SchedulerException { JobKey jobKey = new JobKey("timingTask_" + taskId); scheduler.deleteJob(jobKey); public void unscheduleTimingTask(Long taskId){ try { JobKey jobKey = new JobKey("timingTask_" + taskId); scheduler.deleteJob(jobKey); }catch (SchedulerException e){ throw new RuntimeException(e); } } private JobDetail buildJobDetail(TimingTask task) { src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
@@ -155,7 +155,7 @@ } } private LocalDateTime calculateFirstExecutionTime(TimingTask task) { public LocalDateTime calculateFirstExecutionTime(TimingTask task) { // æ ¹æ®é¢çç±»åå详æ 计ç®é¦æ¬¡æ§è¡æ¶é´ String frequencyType = task.getFrequencyType(); if ("DAILY".equals(frequencyType)) { @@ -455,7 +455,13 @@ @Override public int delByIds(Long[] ids) { return timingTaskMapper.deleteBatchIds(Arrays.asList(ids)); int i = timingTaskMapper.deleteBatchIds(Arrays.asList(ids)); if(i > 0){ for (Long id : ids) { timingTaskScheduler.unscheduleTimingTask(id); } } return i; } } src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -10,15 +10,15 @@ import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.framework.web.page.TableDataInfo; import com.ruoyi.production.mapper.SalesLedgerWorkMapper; import com.ruoyi.production.pojo.SalesLedgerWork; import com.ruoyi.sales.dto.InvoiceLedgerDto; import com.ruoyi.sales.dto.SalesLedgerDto; import com.ruoyi.sales.mapper.InvoiceLedgerMapper; import com.ruoyi.sales.mapper.InvoiceRegistrationProductMapper; import com.ruoyi.sales.mapper.ReceiptPaymentMapper; import com.ruoyi.sales.pojo.InvoiceLedger; import com.ruoyi.sales.pojo.InvoiceRegistrationProduct; import com.ruoyi.sales.pojo.ReceiptPayment; import com.ruoyi.sales.pojo.SalesLedger; import com.ruoyi.sales.mapper.SalesLedgerProductMapper; import com.ruoyi.sales.pojo.*; import com.ruoyi.sales.service.ICommonFileService; import com.ruoyi.sales.service.ISalesLedgerService; import lombok.AllArgsConstructor; @@ -28,10 +28,7 @@ import javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.*; import java.util.stream.Collectors; /** @@ -57,6 +54,12 @@ @Autowired private ReceiptPaymentMapper receiptPaymentMapper; @Autowired private SalesLedgerProductMapper salesLedgerProductMapper; @Autowired private SalesLedgerWorkMapper salesLedgerWorkMapper; /** * æ¥è¯¢éå®å°è´¦å表 @@ -205,9 +208,6 @@ } List<Long> salesLedgerIds = iPage.getRecords().stream().map(SalesLedger::getId).collect(Collectors.toList()); List<InvoiceLedgerDto> invoiceLedgerDtoList = invoiceLedgerMapper.invoicedTotal(salesLedgerIds); if(CollectionUtils.isEmpty(invoiceLedgerDtoList)){ return iPage; } // 计ç®å款éé¢ï¼å¾ 忬¾éé¢ List<InvoiceRegistrationProduct> invoiceRegistrationProducts = invoiceRegistrationProductMapper.selectList(new LambdaQueryWrapper<InvoiceRegistrationProduct>() .in(InvoiceRegistrationProduct::getSalesLedgerId, salesLedgerIds)); @@ -218,6 +218,42 @@ if(!CollectionUtils.isEmpty(invoiceLedgers)){ receiptPayments = receiptPaymentMapper.selectList(new LambdaQueryWrapper<ReceiptPayment>() .in(ReceiptPayment::getInvoiceLedgerId, invoiceLedgers.stream().map(InvoiceLedger::getId).collect(Collectors.toList()))); } // è·åéå®äº§åæ°é List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper .selectList(new LambdaQueryWrapper<SalesLedgerProduct>() .eq(SalesLedgerProduct::getType, 1) .in(SalesLedgerProduct::getSalesLedgerId, salesLedgerIds)); Map<Long, BigDecimal> salesLedgerProductCountMap = new HashMap<>(); if (!CollectionUtils.isEmpty(salesLedgerProducts)) { salesLedgerProductCountMap = salesLedgerProducts .stream() .filter(Objects::nonNull) .collect(Collectors.groupingBy( SalesLedgerProduct::getSalesLedgerId, Collectors.reducing( BigDecimal.ZERO, SalesLedgerProduct::getQuantity, BigDecimal::add ) )); } // è·åéå®äº§åç产æ¥å·¥æ°é List<SalesLedgerWork> salesLedgerWorks = salesLedgerWorkMapper.selectList(new LambdaQueryWrapper<SalesLedgerWork>() .in(SalesLedgerWork::getSalesLedgerId, salesLedgerIds)); Map<Long, BigDecimal> salesLedgerWorkCountMap = new HashMap<>(); if (!CollectionUtils.isEmpty(salesLedgerWorks)) { salesLedgerWorkCountMap = salesLedgerWorks .stream() .filter(Objects::nonNull) .collect(Collectors.groupingBy( SalesLedgerWork::getSalesLedgerId, Collectors.reducing( BigDecimal.ZERO, SalesLedgerWork::getFinishedNum, BigDecimal::add ) )); } for (SalesLedger salesLedger : iPage.getRecords()) { boolean existFlag = false; @@ -257,6 +293,8 @@ salesLedger.setNoInvoiceAmountTotal(salesLedger.getContractAmount()); } salesLedger.setInvoiceTotal(invoiceTotal); // 夿æ¯å¦æ¯çäº§å®æ salesLedger.setProductionStatus(salesLedgerProductCountMap.get(salesLedger.getId()) == null ? "æªå¼å§" : salesLedgerProductCountMap.get(salesLedger.getId()).compareTo(salesLedgerWorkCountMap.get(salesLedger.getId())) > 0 ? "ç产ä¸" : "已宿"); } if (ObjectUtils.isNotEmpty(salesLedgerDto.getStatus())) { if (salesLedgerDto.getStatus()) { src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -140,5 +140,9 @@ @ApiModelProperty(value = "仿¬¾æ¹å¼") private String paymentMethod; @TableField(exist = false) @ApiModelProperty(value = "çäº§ç¶æ") private String productionStatus = "æªå¼å§"; }