src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,60 @@ package com.ruoyi.device.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 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,11 @@ package com.ruoyi.device.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.device.pojo.MaintenanceTask; /** * @author :yys * @date : 2025/12/22 14:56 */ public interface MaintenanceTaskMapper extends BaseMapper<MaintenanceTask> { } src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
@@ -26,6 +26,21 @@ @ApiModelProperty("设å¤ä¿å »éé¢") private BigDecimal maintenancePrice; @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/DeviceRepair.java
@@ -52,9 +52,12 @@ @ApiModelProperty("ç»´ä¿®ç»æ") private String maintenanceResult; @ApiModelProperty("ç¶æ 0 å¾ ç»´ä¿® 1å®ç»") @ApiModelProperty("ç¶æ 0 å¾ ç»´ä¿® 1å®ç» 2å¾ å®¡æ ¸ 3å®¡æ ¸ä¸éè¿") private Integer status; @ApiModelProperty("å®¡æ ¸äºº") private String auditName; @ApiModelProperty("å建æ¶é´") @TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,113 @@ 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 = "å®¡æ ¸äºº") @Excel(name = "å®¡æ ¸äºº") private String auditName; @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,22 @@ 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.MaintenanceTask; import com.ruoyi.framework.web.domain.AjaxResult; 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/DeviceRepairServiceImpl.java
@@ -54,6 +54,7 @@ DeviceLedger byId = deviceLedgerService.getById(deviceRepair.getDeviceLedgerId()); deviceRepair.setDeviceName(byId.getDeviceName()); deviceRepair.setDeviceModel(byId.getDeviceModel()); deviceRepair.setStatus(2); boolean save = this.save(deviceRepair); if (save){ return AjaxResult.success(); src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,243 @@ package com.ruoyi.device.service.impl; import com.ruoyi.device.pojo.DeviceMaintenance; import com.ruoyi.device.pojo.MaintenanceTask; 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 = ? and status = 'å®¡æ ¸éè¿'"; 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.MaintenanceTask; import com.ruoyi.device.service.MaintenanceTaskService; import com.ruoyi.framework.web.domain.AjaxResult; 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.util.*; /** * @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) { if(StringUtils.isEmpty(maintenanceTask.getAuditName())) { return AjaxResult.warn("请填åå®¡æ ¸äºº"); } maintenanceTask.setActive(true); // 计ç®é¦æ¬¡æ§è¡æ¶é´ TimingTask task = new TimingTask(); task.setFrequencyType(maintenanceTask.getFrequencyType()); task.setFrequencyDetail(maintenanceTask.getFrequencyDetail()); LocalDateTime firstExecutionTime = timingTaskService.calculateFirstExecutionTime(task); maintenanceTask.setNextExecutionTime(firstExecutionTime); maintenanceTask.setStatus("å¾ å®¡æ ¸"); 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/service/impl/TimingTaskServiceImpl.java
@@ -144,7 +144,7 @@ } } private LocalDateTime calculateFirstExecutionTime(TimingTask task) { public LocalDateTime calculateFirstExecutionTime(TimingTask task) { // æ ¹æ®é¢çç±»åå详æ 计ç®é¦æ¬¡æ§è¡æ¶é´ String frequencyType = task.getFrequencyType(); if ("DAILY".equals(frequencyType)) { src/main/java/com/ruoyi/lavorissue/controller/LavorIssueController.java
@@ -130,6 +130,27 @@ return AjaxResult.success(statisticsLaborIssue); } @ApiOperation("åæ¾è¿åº¦-æ»è®¡") @GetMapping("/progressTotal") public AjaxResult progressTotal(LaborIssue laborIssue) throws Exception { Map<String, Object> list = laborIssueService.progressTotal(laborIssue); return AjaxResult.success(list); } @ApiOperation("é¢åè¿åº¦å æ¯") @GetMapping("/progressPercent") public AjaxResult progressPercent(LaborIssue laborIssue) throws Exception { Map<String, Object> list = laborIssueService.progressPercent(laborIssue); return AjaxResult.success(list); } @ApiOperation("è¿åº¦åå¸") @GetMapping("/progressDistribution") public AjaxResult progressDistribution(LaborIssue laborIssue) throws Exception { Map<String, Object> list = laborIssueService.progressDistribution(laborIssue); return AjaxResult.success(list); } /** src/main/java/com/ruoyi/lavorissue/service/LavorIssueService.java
@@ -31,4 +31,10 @@ void exportCopy(HttpServletResponse response, LaborIssue laborIssue) throws UnsupportedEncodingException; List<Map<String, Object>> statisticsList(LaborIssue laborIssue); Map<String, Object> progressTotal(LaborIssue laborIssue); Map<String, Object> progressPercent(LaborIssue laborIssue); Map<String, Object> progressDistribution(LaborIssue laborIssue); } src/main/java/com/ruoyi/lavorissue/service/impl/LavorIssueServiceImpl.java
@@ -93,6 +93,102 @@ return records; } @Override public Map<String, Object> progressTotal(LaborIssue req) { StartAndEndDateDto startAndEndDateDto = getStartAndEndDateDto(req.getSeason(), req.getIssueDate()); req.setStartDate(startAndEndDateDto.getStartDate()); req.setEndDate(startAndEndDateDto.getEndDate()); List<LaborIssue> laborIssueIPage = lavorIssueMapper.list(req); Map<String, Object> map = new HashMap<>(); map.put("total", 0); // åæ¾æ»æ°é // é¢åæ°é map.put("adopted", 0); // æªé¢åæ°é map.put("notAdopted", 0); // é¢å宿ç map.put("adoptedPercent", 0); if(!CollectionUtils.isEmpty(laborIssueIPage)){ long sum = laborIssueIPage.stream().mapToLong(LaborIssue::getNum).sum(); map.put("total", sum); long sum1 = laborIssueIPage .stream() .filter(laborIssue -> laborIssue.getAdoptedDate() != null) .mapToLong(LaborIssue::getNum) .sum(); map.put("adopted", sum1); map.put("notAdopted", sum - sum1); map.put("adoptedPercent", sum1 * 100.0 / sum); } return map; } @Override public Map<String, Object> progressPercent(LaborIssue req) { StartAndEndDateDto startAndEndDateDto = getStartAndEndDateDto(req.getSeason(), req.getIssueDate()); req.setStartDate(startAndEndDateDto.getStartDate()); req.setEndDate(startAndEndDateDto.getEndDate()); List<LaborIssue> laborIssueIPage = lavorIssueMapper.list(req); Map<String, Object> map = new HashMap<>(); // é¢åæ°é map.put("adopted", 0); // æªé¢åæ°é map.put("notAdopted", 0); if(!CollectionUtils.isEmpty(laborIssueIPage)){ long sum = laborIssueIPage.stream().mapToLong(LaborIssue::getNum).sum(); long sum1 = laborIssueIPage .stream() .filter(laborIssue -> laborIssue.getAdoptedDate() != null) .mapToLong(LaborIssue::getNum) .sum(); map.put("adopted", sum1); map.put("notAdopted", sum - sum1); } return map; } @Override public Map<String, Object> progressDistribution(LaborIssue req) { StartAndEndDateDto startAndEndDateDto = getStartAndEndDateDto(req.getSeason(), req.getIssueDate()); req.setStartDate(startAndEndDateDto.getStartDate()); req.setEndDate(startAndEndDateDto.getEndDate()); List<LaborIssue> laborIssueIPage = lavorIssueMapper.list(req); Map<String, Object> map = new HashMap<>(); if(!CollectionUtils.isEmpty(laborIssueIPage)){ // æ ¹æ®åæ¾æ¥æï¼é¢ç¨æ¥æè®¡ç®åæ¶å·²é¢åï¼åæ¶æªé¢åï¼è¶ æ¶å·²é¢åï¼è¶ æ¶æªé¢åæ°æ® // åæ¶å·²é¢å List<List<Long>> list = new ArrayList<>(); List<Long> sumList = new ArrayList<>(); long sum = laborIssueIPage.stream() .filter(laborIssue -> laborIssue.getAdoptedDate() != null && laborIssue.getAdoptedDate().getTime() <= laborIssue.getIssueDate().getTime()) .mapToLong(LaborIssue::getNum) .sum(); // åæ¶æªé¢å long sum1 = laborIssueIPage.stream() .filter(laborIssue -> laborIssue.getAdoptedDate() == null && laborIssue.getIssueDate().getTime() <= new Date().getTime()) .mapToLong(LaborIssue::getNum) .sum(); sumList.add(sum); sumList.add(sum1); list.add(sumList); List<Long> sumList1 = new ArrayList<>(); // è¶ æ¶å·²é¢å long sum2 = laborIssueIPage.stream() .filter(laborIssue -> laborIssue.getAdoptedDate() != null && laborIssue.getAdoptedDate().getTime() > laborIssue.getIssueDate().getTime()) .mapToLong(LaborIssue::getNum) .sum(); // è¶ æ¶æªé¢å long sum3 = laborIssueIPage.stream() .filter(laborIssue -> laborIssue.getAdoptedDate() == null && laborIssue.getIssueDate().getTime() > new Date().getTime()) .mapToLong(LaborIssue::getNum) .sum(); sumList1.add(sum2); sumList1.add(sum3); list.add(sumList1); map.put("series", list); } return map; } public StartAndEndDateDto getStartAndEndDateDto(Integer season,Date payDate){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); StartAndEndDateDto startAndEndDateDto = new StartAndEndDateDto();