From 26a4b19724b844c2137492bb1c48210fbb781b6f Mon Sep 17 00:00:00 2001 From: weitang Date: Thu, 5 Jun 2025 17:58:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=BB=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E9=83=A8=E4=BB=B6=E6=A0=B9=E6=8D=AEcron=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E8=87=AA=E5=8A=A8=E5=88=9B=E5=BB=BA=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AlgorithmClassComponentController.java | 13 +- .../domain/AlgorithmClassComponent.java | 11 ++ .../IAlgorithmClassComponentService.java | 4 + .../AlgorithmClassComponentServiceImpl.java | 85 +++++++++++ .../service/impl/AlgorithmTaskJob.java | 26 ++++ .../service/impl/AlgorithmTaskManager.java | 141 ++++++++++++++++++ .../yfd/platform/utils/CronScheduleUtils.java | 46 ++++++ 7 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmTaskJob.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmTaskManager.java create mode 100644 riis-system/src/main/java/com/yfd/platform/utils/CronScheduleUtils.java diff --git a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/controller/AlgorithmClassComponentController.java b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/controller/AlgorithmClassComponentController.java index 4445fcb..0510ff8 100644 --- a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/controller/AlgorithmClassComponentController.java +++ b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/controller/AlgorithmClassComponentController.java @@ -11,6 +11,7 @@ import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.text.ParseException; import java.util.List; /** @@ -32,9 +33,10 @@ public class AlgorithmClassComponentController { @GetMapping("/getAlgorithmClassComponentPage") @ApiOperation("算法关联主设备部件") - public ResponseResult getAlgorithmClassComponentPage(String algorithmId,String stationCode, Page page) { + public ResponseResult getAlgorithmClassComponentPage(String algorithmId, String stationCode, + Page page) { Page algorithmClassComponentPage = - algorithmClassComponentService.getAlgorithmClassComponentPage(algorithmId,stationCode, page); + algorithmClassComponentService.getAlgorithmClassComponentPage(algorithmId, stationCode, page); return ResponseResult.successData(algorithmClassComponentPage); } @@ -59,4 +61,11 @@ public class AlgorithmClassComponentController { return ResponseResult.error(); } } + + @PostMapping("/setAlgorithmClassComponentStatus") + @ApiOperation("设置算法关联主设备部件状态") + public ResponseResult setAlgorithmClassComponentStatus(String id, String isenable) throws ParseException { + boolean isOK = algorithmClassComponentService.setAlgorithmClassComponentStatus(id, isenable); + return ResponseResult.success(); + } } diff --git a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/domain/AlgorithmClassComponent.java b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/domain/AlgorithmClassComponent.java index 3f5798b..cea67f9 100644 --- a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/domain/AlgorithmClassComponent.java +++ b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/domain/AlgorithmClassComponent.java @@ -3,6 +3,7 @@ package com.yfd.platform.modules.algorithm.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; + import java.io.Serializable; import io.swagger.annotations.ApiModel; @@ -105,5 +106,15 @@ public class AlgorithmClassComponent implements Serializable { @ApiModelProperty("部件名称") private String componentName; + /** + * 是否可用;0:初始状态;1:启用 + */ + @ApiModelProperty("是否可用;0:初始状态;1:启用") + private String isenable; + /** + * cron表达式 + */ + @ApiModelProperty("cron表达式") + private String cronValue; } diff --git a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/IAlgorithmClassComponentService.java b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/IAlgorithmClassComponentService.java index 36f8511..97d7d58 100644 --- a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/IAlgorithmClassComponentService.java +++ b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/IAlgorithmClassComponentService.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.modules.algorithm.domain.AlgorithmClassComponent; import com.baomidou.mybatisplus.extension.service.IService; +import java.text.ParseException; import java.util.List; /** @@ -33,4 +34,7 @@ public interface IAlgorithmClassComponentService extends IService algorithmClassComponentList); + boolean setAlgorithmClassComponentStatus(String id, String isenable) throws ParseException; + + boolean createAlgorithmTaskList(AlgorithmClassComponent algorithmClassComponent) throws ParseException; } diff --git a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmClassComponentServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmClassComponentServiceImpl.java index c591478..e3e8043 100644 --- a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmClassComponentServiceImpl.java +++ b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmClassComponentServiceImpl.java @@ -1,7 +1,10 @@ package com.yfd.platform.modules.algorithm.service.impl; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.modules.algorithm.domain.AlgorithmClassComponent; @@ -9,9 +12,17 @@ import com.yfd.platform.modules.algorithm.domain.AlgorithmDevice; import com.yfd.platform.modules.algorithm.mapper.AlgorithmClassComponentMapper; import com.yfd.platform.modules.algorithm.mapper.AlgorithmDeviceMapper; import com.yfd.platform.modules.algorithm.service.IAlgorithmClassComponentService; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.utils.CronScheduleUtils; +import org.quartz.CronExpression; +import org.quartz.TriggerUtils; +import org.quartz.impl.triggers.CronTriggerImpl; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; import java.util.List; /** @@ -29,6 +40,9 @@ public class AlgorithmClassComponentServiceImpl extends ServiceImpl algorithmClassComponentList) { return this.saveOrUpdateBatch(algorithmClassComponentList); } + + @Override + public boolean setAlgorithmClassComponentStatus(String id, String isenable) throws ParseException { + AlgorithmClassComponent algorithmClassComponent = this.getById(id); + + if (algorithmClassComponent == null) { + return false; + } + if (isenable.equals(algorithmClassComponent.getIsenable())) { + throw new RuntimeException("当前状态与要设置的状态一致"); + } + if ("1".equals(isenable)) { + //TODO 根据cron表达式设置定时任务 + this.createAlgorithmTaskList(algorithmClassComponent); + } + if ("0".equals(isenable)) { + this.deleteAllTasksByComponentId(algorithmClassComponent); + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(AlgorithmClassComponent::getId, id).set(AlgorithmClassComponent::getIsenable, isenable); + return this.update(updateWrapper); + } + + private void deleteAllTasksByComponentId(AlgorithmClassComponent component) { + // 调用 Quartz 工具类删除任务 + try { + algorithmTaskManager.deleteAllTasksByComponentId(component.getId(), component.getStationCode()); + } catch (Exception e) { + throw new RuntimeException("删除定时任务失败:" + e.getMessage(), e); + } + } + + @Override + public boolean createAlgorithmTaskList(AlgorithmClassComponent algorithmClassComponent) throws ParseException { + // 判断cron表达式有效性 + if (StrUtil.isBlank(algorithmClassComponent.getCronValue())) { + return false; + } + String cronValue = algorithmClassComponent.getCronValue(); + if (!CronExpression.isValidExpression(cronValue)) { + throw new RuntimeException("cron表达式格式错误"); + } + List fireTimes = CronScheduleUtils.getFireTimes(cronValue, 30); + // 判断是否有定时任务 + if (fireTimes.isEmpty()) { + throw new RuntimeException("当前cron表达式没有生成任务"); + } + if (fireTimes.size() > 180) { + throw new RuntimeException("当前cron表达式生成任务数量超过最大数量"); + } + for (Date fireTime : fireTimes) { + setToDoTask(algorithmClassComponent, DateUtil.formatDateTime(fireTime)); + } + return true; + } + + /** + * 设置待办任务的时间 + * 此方法用于根据提供的算法类组件和触发时间来安排待办任务 + * 它不返回任何值,但可能根据需要更新或创建待办任务 + * + * @param algorithmClassComponent 算法类组件,代表需要执行的任务的类型或相关信息 + * @param fireTime 任务触发时间,采用字符串形式,具体的格式需要根据上下文或文档确定 + */ + private void setToDoTask(AlgorithmClassComponent algorithmClassComponent, String fireTime) { + try { + algorithmTaskManager.addTask(algorithmClassComponent, fireTime); + } catch (Exception e) { + throw new RuntimeException("添加定时任务失败:" + e.getMessage(), e); + } + } } diff --git a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmTaskJob.java b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmTaskJob.java new file mode 100644 index 0000000..b1d4427 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmTaskJob.java @@ -0,0 +1,26 @@ +package com.yfd.platform.modules.algorithm.service.impl; + +import com.yfd.platform.modules.algorithm.domain.AlgorithmClassComponent; +import com.yfd.platform.modules.algorithm.service.IAlgorithmParamsService; +import com.yfd.platform.utils.SpringContextHolder; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +public class AlgorithmTaskJob implements Job { + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + + IAlgorithmParamsService algorithmParamsService = SpringContextHolder.getBean(IAlgorithmParamsService.class); + + // 获取传递的任务参数 + Object jobData = context.getMergedJobDataMap().get("algorithmClassComponent"); + if (jobData instanceof AlgorithmClassComponent) { + AlgorithmClassComponent component = (AlgorithmClassComponent) jobData; + algorithmParamsService.callAlgorithmAnalyse(component.getAlgorithmId(), component.getComponentId()); + System.out.println("执行算法任务:" + component.getComponentId() + + ",触发时间:" + context.getScheduledFireTime()); + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmTaskManager.java b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmTaskManager.java new file mode 100644 index 0000000..eb3199f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/service/impl/AlgorithmTaskManager.java @@ -0,0 +1,141 @@ +package com.yfd.platform.modules.algorithm.service.impl; + +import com.yfd.platform.modules.algorithm.domain.AlgorithmClassComponent; +import org.quartz.*; +import org.quartz.impl.DirectSchedulerFactory; +import org.quartz.impl.StdSchedulerFactory; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.simpl.RAMJobStore; +import org.quartz.simpl.SimpleThreadPool; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.*; + +/** + * 算法任务管理器 + */ +@Component +public class AlgorithmTaskManager { + + private static final String JOB_NAME_PREFIX = "ALGORITHM_COMPONENT_"; + private static final String JOB_GROUP_PREFIX = "STATION_"; + + // 存储每个 stationCode 对应的 Scheduler + private Map schedulers = new HashMap<>(); + + /** + * 添加定时任务 + * + * @param component 算法类组件 + * @param fireTime 触发时间(格式:yyyy-MM-dd HH:mm:ss) + */ + public void addTask(AlgorithmClassComponent component, String fireTime) throws Exception { + String stationCode = component.getStationCode(); + String jobId = component.getId(); + + Scheduler scheduler = getOrCreateScheduler(stationCode); + + // 使用 jobId + fireTime.hashCode() 构建唯一任务 Key + String uniqueKey = jobId + "_" + fireTime.hashCode(); + JobKey jobKey = JobKey.jobKey(JOB_NAME_PREFIX + uniqueKey, JOB_GROUP_PREFIX + stationCode); + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME_PREFIX + uniqueKey, JOB_GROUP_PREFIX + stationCode); + + // 如果任务已存在,先删除 + if (scheduler.checkExists(jobKey)) { + scheduler.pauseJob(jobKey); + scheduler.unscheduleJob(triggerKey); + scheduler.deleteJob(jobKey); + } + + // 创建任务详情 + JobDetail jobDetail = JobBuilder.newJob(AlgorithmTaskJob.class) + .withIdentity(jobKey) + .build(); + + Date planDate = org.apache.commons.lang3.time.DateUtils.parseDate(fireTime, "yyyy-MM-dd HH:mm:ss"); + + // 创建一次性触发器 + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity(triggerKey) + .startAt(planDate) + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withRepeatCount(0)) + .build(); + + // 传递参数给任务 + jobDetail.getJobDataMap().put("algorithmClassComponent", component); + + // 注册并启动任务 + scheduler.scheduleJob(jobDetail, trigger); + scheduler.start(); + } + + /** + * 删除指定ID的所有任务 + * + * @param id 主设备部件ID + */ + /** + * 删除指定算法组件的所有定时任务(基于 jobId 前缀匹配) + * + * @param id 主设备部件ID + * @param stationCode 变电站编码 + */ + public void deleteAllTasksByComponentId(String id, String stationCode) throws Exception { + Scheduler scheduler = getOrCreateScheduler(stationCode); + + // 构建任务名前缀,如 ALGORITHM_COMPONENT_123_ + String jobNamePrefix = JOB_NAME_PREFIX + id + "_"; + GroupMatcher matcher = GroupMatcher.jobGroupEquals(JOB_GROUP_PREFIX + stationCode); + + for (JobKey jobKey : scheduler.getJobKeys(matcher)) { + if (jobKey.getName().startsWith(jobNamePrefix)) { + try { + // 获取所有关联触发器 + List triggers = scheduler.getTriggersOfJob(jobKey); + for (Trigger trigger : triggers) { + TriggerKey triggerKey = trigger.getKey(); + // 暂停触发器 + scheduler.pauseTrigger(triggerKey); + // 移除触发器 + scheduler.unscheduleJob(triggerKey); + } + scheduler.deleteJob(jobKey); // 删除任务 + System.out.println("成功删除任务:" + jobKey); + + } catch (Exception e) { + System.err.println("删除任务失败:" + jobKey + ",原因:" + e.getMessage()); + } + } + } + } + + + /** + * 获取或创建指定变电站的调度器 + */ + private Scheduler getOrCreateScheduler(String stationCode) throws Exception { + Scheduler scheduler = schedulers.get(stationCode); + if (scheduler == null || !scheduler.isStarted()) { + DirectSchedulerFactory factory = DirectSchedulerFactory.getInstance(); + factory.createScheduler(stationCode, stationCode, + new SimpleThreadPool(5, Thread.NORM_PRIORITY), + new RAMJobStore()); + scheduler = factory.getScheduler(stationCode); + schedulers.put(stationCode, scheduler); + } + return scheduler; + } + + /** + * 关闭所有调度器 + */ + public void shutdownAllSchedulers() throws SchedulerException { + for (Scheduler scheduler : schedulers.values()) { + if (scheduler.isStarted()) { + scheduler.shutdown(); + } + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/CronScheduleUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/CronScheduleUtils.java new file mode 100644 index 0000000..e7a7bb9 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/CronScheduleUtils.java @@ -0,0 +1,46 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; +import org.quartz.CronExpression; +import org.quartz.TriggerUtils; +import org.quartz.impl.triggers.CronTriggerImpl; + +import java.text.ParseException; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.List; + +public class CronScheduleUtils { + + public static List getFireTimes(String cron, int futureDays) throws ParseException { + if (!CronExpression.isValidExpression(cron)) { + throw new IllegalArgumentException("Invalid cron expression: " + cron); + } + + CronTriggerImpl trigger = new CronTriggerImpl(); + trigger.setCronExpression(cron); + + LocalDateTime now = LocalDateTime.now(); + LocalDateTime endDate = now.plusDays(futureDays).with(LocalTime.MAX); + + Date beginDate = Date.from(now.atZone(ZoneId.systemDefault()).toInstant()); + Date endDateAsDate = Date.from(endDate.atZone(ZoneId.systemDefault()).toInstant()); + return TriggerUtils.computeFireTimesBetween(trigger, null, beginDate, endDateAsDate); + } + + public static void main(String[] args) { + try { + List fireTimes = getFireTimes("30 54,55,56,57,58 17 ? 6 5 ", 30); + for (Date fireTime : fireTimes) { + // 将时间转成yyyy-MM-dd HH:mm:ss格式的时间字符串 + String fireTimeStr = LocalDateTimeUtil.format(LocalDateTimeUtil.of(fireTime), DatePattern.NORM_DATETIME_PATTERN); + System.out.println(fireTimeStr); + } + } catch (ParseException e) { + e.printStackTrace(); + } + } +}