新增主设备部件根据cron表达式自动创建任务

This commit is contained in:
weitang 2025-06-05 17:58:27 +08:00
parent 87c6d31eb4
commit 26a4b19724
7 changed files with 324 additions and 2 deletions

View File

@ -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<AlgorithmClassComponent> page) {
public ResponseResult getAlgorithmClassComponentPage(String algorithmId, String stationCode,
Page<AlgorithmClassComponent> page) {
Page<AlgorithmClassComponent> 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();
}
}

View File

@ -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;
}

View File

@ -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<AlgorithmClass
boolean batchAddAlgorithmClassComponent(List<AlgorithmClassComponent> algorithmClassComponentList);
boolean setAlgorithmClassComponentStatus(String id, String isenable) throws ParseException;
boolean createAlgorithmTaskList(AlgorithmClassComponent algorithmClassComponent) throws ParseException;
}

View File

@ -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<AlgorithmCla
@Resource
private AlgorithmDeviceMapper algorithmDeviceMapper;
@Resource
private AlgorithmTaskManager algorithmTaskManager;
/**********************************
* 用途说明: 算法关联主设备部件
* 参数说明 algorithmId 算法分类id
@ -69,4 +83,75 @@ public class AlgorithmClassComponentServiceImpl extends ServiceImpl<AlgorithmCla
public boolean batchAddAlgorithmClassComponent(List<AlgorithmClassComponent> 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<AlgorithmClassComponent> 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<Date> 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);
}
}
}

View File

@ -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());
}
}
}

View File

@ -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<String, Scheduler> 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<JobKey> matcher = GroupMatcher.jobGroupEquals(JOB_GROUP_PREFIX + stationCode);
for (JobKey jobKey : scheduler.getJobKeys(matcher)) {
if (jobKey.getName().startsWith(jobNamePrefix)) {
try {
// 获取所有关联触发器
List<? extends Trigger> 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();
}
}
}
}

View File

@ -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<Date> 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<Date> 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();
}
}
}