diff --git a/pom.xml b/pom.xml index 8b41280..843a0c5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.18 + 2.4.5 com.bonus @@ -61,6 +61,13 @@ lombok provided + + + + org.springframework.boot + spring-boot-starter-quartz + + diff --git a/src/main/java/com/bonus/emergencyrap/constant/ScheduleConstants.java b/src/main/java/com/bonus/emergencyrap/constant/ScheduleConstants.java index dbb566a..6cf53a3 100644 --- a/src/main/java/com/bonus/emergencyrap/constant/ScheduleConstants.java +++ b/src/main/java/com/bonus/emergencyrap/constant/ScheduleConstants.java @@ -3,7 +3,7 @@ package com.bonus.emergencyrap.constant; /** * 任务调度通用常量 * - * @author ruoyi + * @author bonus */ public class ScheduleConstants { diff --git a/src/main/java/com/bonus/emergencyrap/task/config/QuartzConfig.java b/src/main/java/com/bonus/emergencyrap/task/config/QuartzConfig.java new file mode 100644 index 0000000..a8b7eb3 --- /dev/null +++ b/src/main/java/com/bonus/emergencyrap/task/config/QuartzConfig.java @@ -0,0 +1,43 @@ +package com.bonus.emergencyrap.task.config; + + +import org.quartz.Scheduler; +import org.quartz.spi.JobFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +import java.util.Properties; + +@Configuration +public class QuartzConfig { + + @Bean + public JobFactory jobFactory() { + return new SpringBeanJobFactory(); + } + + @Bean + public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) { + SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); + schedulerFactoryBean.setJobFactory(jobFactory); + + // 配置Quartz使用内存存储 + Properties properties = new Properties(); + properties.setProperty("org.quartz.scheduler.instanceName", "MyScheduler"); + properties.setProperty("org.quartz.scheduler.instanceId", "AUTO"); + properties.setProperty("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore"); + properties.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); + properties.setProperty("org.quartz.threadPool.threadCount", "5"); + + schedulerFactoryBean.setQuartzProperties(properties); + return schedulerFactoryBean; + } + + @Bean + public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) throws Exception { + return schedulerFactoryBean.getScheduler(); + } +} diff --git a/src/main/java/com/bonus/emergencyrap/task/job/ActuatorJob.java b/src/main/java/com/bonus/emergencyrap/task/job/ActuatorJob.java new file mode 100644 index 0000000..1d0e6e9 --- /dev/null +++ b/src/main/java/com/bonus/emergencyrap/task/job/ActuatorJob.java @@ -0,0 +1,42 @@ +package com.bonus.emergencyrap.task.job; + + +import com.alibaba.fastjson2.JSON; +import com.bonus.emergencyrap.constant.TextConstants; +import com.bonus.emergencyrap.utils.TextFileUtils; +import com.bonus.emergencyrap.utils.UploadFile; +import com.bonus.emergencyrap.vo.TaskVo; +import lombok.extern.slf4j.Slf4j; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; + +/** + * 执行任务定时器 + */ +@Component +@Slf4j +public class ActuatorJob implements Job { + private static final Logger logger = LoggerFactory.getLogger(ActuatorJob.class); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + log.info(context.getTrigger().getJobDataMap().get("taskId").toString()); + String taskId = context.getTrigger().getJobDataMap().get("taskId").toString(); + //文件进行解析 + String filePtah= UploadFile.getFilePath(TextConstants.TASK); + Map map= TextFileUtils.readFileMaps(filePtah); + TaskVo vo= JSON.parseObject(map.get(taskId), TaskVo.class); + System.err.println(vo.getTaskName()); + logger.info("SampleJob 执行时间: {}", currentTime); + logger.info("任务执行器开始执行,执行任务id=="+vo.getTaskId()+"任务名称是=="+vo.getTaskName()+"执行时间=="+vo.getCorn()); + } +} diff --git a/src/main/java/com/bonus/emergencyrap/task/job/ScanJob.java b/src/main/java/com/bonus/emergencyrap/task/job/ScanJob.java new file mode 100644 index 0000000..16b786d --- /dev/null +++ b/src/main/java/com/bonus/emergencyrap/task/job/ScanJob.java @@ -0,0 +1,62 @@ +package com.bonus.emergencyrap.task.job; + +import com.alibaba.fastjson2.JSON; +import com.bonus.emergencyrap.constant.TextConstants; +import com.bonus.emergencyrap.task.service.QuartzJobService; +import com.bonus.emergencyrap.utils.TextFileUtils; +import com.bonus.emergencyrap.utils.UploadFile; +import com.bonus.emergencyrap.vo.TaskVo; +import lombok.extern.slf4j.Slf4j; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 定时任务扫描 + */ +@Slf4j +@Component +public class ScanJob implements Job { + + @Autowired + private QuartzJobService service; + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + try { + String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + System.err.println(currentTime); + String filePtah= UploadFile.getFilePath(TextConstants.TASK); + //文件是否存在 + boolean isCz= TextFileUtils.exists(filePtah); + if(isCz){ + //文件进行解析 + Map map=TextFileUtils.readFileMaps(filePtah); + for (String key : map.keySet()) { + TaskVo vo= JSON.parseObject(map.get(key), TaskVo.class); + String status=vo.getStatus(); + //关闭直接删除 + if("0".equals(status)){ + service.deleteJob(vo); + }else{ + //添加或者修改定时任务 + service.addTask(vo); + } + } + } + System.err.println(context.getTrigger().getJobDataMap().get("taskId")); + log.info("定时任务扫描中scan...."); + } catch (Exception e) { + throw new JobExecutionException(e); + } + } +} diff --git a/src/main/java/com/bonus/emergencyrap/task/runner/JobInitializer.java b/src/main/java/com/bonus/emergencyrap/task/runner/JobInitializer.java new file mode 100644 index 0000000..a5fa4a1 --- /dev/null +++ b/src/main/java/com/bonus/emergencyrap/task/runner/JobInitializer.java @@ -0,0 +1,27 @@ +package com.bonus.emergencyrap.task.runner; + + +import com.bonus.emergencyrap.task.service.QuartzJobService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +@Component +public class JobInitializer implements CommandLineRunner { + private static final Logger logger = LoggerFactory.getLogger(JobInitializer.class); + + @Autowired + private QuartzJobService quartzJobService; + + @Override + public void run(String... args) throws Exception { + try { + // 启动定时任务 + quartzJobService.startSampleJob(); + } catch (Exception e) { + logger.error("启动定时任务失败", e); + } + } +} diff --git a/src/main/java/com/bonus/emergencyrap/task/service/QuartzJobService.java b/src/main/java/com/bonus/emergencyrap/task/service/QuartzJobService.java new file mode 100644 index 0000000..c523f91 --- /dev/null +++ b/src/main/java/com/bonus/emergencyrap/task/service/QuartzJobService.java @@ -0,0 +1,179 @@ +package com.bonus.emergencyrap.task.service; +import com.alibaba.fastjson2.JSON; +import com.bonus.emergencyrap.task.job.ActuatorJob; +import com.bonus.emergencyrap.task.job.ScanJob; + +import com.bonus.emergencyrap.vo.TaskVo; +import org.quartz.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; + + +@Service +public class QuartzJobService { + private static final Logger logger = LoggerFactory.getLogger(QuartzJobService.class); + + @Autowired + private Scheduler scheduler; + + // 从配置文件读取简单间隔时间(秒) + @Value("${task.interval.seconds}") + private int taskIntervalSeconds; + + public static String defeatGroup="EmergencyRap"; + + + + // 固定任务 用来扫描全部的定时任务 + public void startSampleJobWithSimpleTrigger() throws SchedulerException { + // 定义任务 + JobDetail jobDetail = JobBuilder.newJob(ScanJob.class) + .usingJobData("taskId","555555555555") + .withIdentity("scanJob", "scanJobGroup") + .withDescription("扫描器") + .storeDurably() + .build(); + + // 调度任务 + if (!scheduler.checkExists(jobDetail.getKey())) { + // 从配置文件获取间隔时间,创建触发器 + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity("scanJobTrigger", "scanJobGroup") + .usingJobData("taskId","555555555555") + .withDescription("扫描器") + .startNow() + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withIntervalInSeconds(taskIntervalSeconds) + .repeatForever()) + .build(); + scheduler.scheduleJob(jobDetail, trigger); + logger.info("已启动任务扫描,间隔时间: {}秒", taskIntervalSeconds); + } else { + logger.info("任务已存在,无需重复启动"); + } + } + + + // 使用cron表达式调度 + public void addTask(TaskVo vo) throws SchedulerException { + // 定义任务 + JobDetail jobDetail = JobBuilder.newJob(ActuatorJob.class) + .withIdentity(vo.getTaskId(), defeatGroup) + .withDescription("Cron定时任务") + .storeDurably() + .build(); + // 调度任务 + if (!scheduler.checkExists(jobDetail.getKey())) { + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity(vo.getTaskId(), defeatGroup) + .usingJobData("taskId",vo.getTaskId()) + .withDescription(vo.getCorn()) + .startNow() + .withSchedule(CronScheduleBuilder.cronSchedule(vo.getCorn())) + .build(); + scheduler.scheduleJob(jobDetail, trigger); + logger.info("已启动Cron任务,表达式: {}", vo.getCorn()); + } else { + updateJobTime(vo); + logger.info("Cron任务已存在,进行定时任务时间更新"); + } + } + + + + // 启动示例任务 + public void startSampleJob() throws SchedulerException { + // 可以根据需要选择启动哪种类型的任务 + startSampleJobWithSimpleTrigger(); + } + + // 暂停任务 + public void pauseJob(String jobName, String jobGroup) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobName, jobGroup); + if (scheduler.checkExists(jobKey)) { + scheduler.pauseJob(jobKey); + logger.info("已暂停任务: {}:{}", jobGroup, jobName); + } + } + + // 恢复任务 + public void resumeJob(String jobName, String jobGroup) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobName, jobGroup); + if (scheduler.checkExists(jobKey)) { + scheduler.resumeJob(jobKey); + logger.info("已恢复任务: {}:{}", jobGroup, jobName); + } + } + + // 删除任务 + public void deleteJob(TaskVo vo) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(vo.getTaskId(), defeatGroup); + if (scheduler.checkExists(jobKey)) { + scheduler.deleteJob(jobKey); + logger.info("已删除任务: {}:{}", defeatGroup, vo.getTaskId()); + } + } + + // 删除任务 + public void deleteJob(String jobName, String jobGroup) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobName, jobGroup); + if (scheduler.checkExists(jobKey)) { + scheduler.deleteJob(jobKey); + logger.info("已删除任务: {}:{}", jobGroup, jobName); + } + } + + /** + * 更新当前正在运行的任务的cron时间 + * @param + * @param + */ + private void updateJobTime(TaskVo vo) throws SchedulerException { + // 获取当前触发器 + TriggerKey triggerKey = TriggerKey.triggerKey(vo.getTaskId(), defeatGroup); + CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); + String oldTime = cronTrigger.getCronExpression(); + if (!oldTime.equalsIgnoreCase(vo.getCorn())) { + logger.info("定时任务已更新old和新的定时任务: {}:{}:{}", vo.getTaskName(),oldTime,vo.getCorn() ); + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity(vo.getTaskId(), defeatGroup) + .usingJobData("taskId",vo.getTaskId()) + .withDescription(vo.getCorn()) + .startNow() + .withSchedule(CronScheduleBuilder.cronSchedule(vo.getCorn())) + .build(); + //重置对应的job + scheduler.rescheduleJob(triggerKey, trigger); + } + + } + + // 刷新简单任务的执行间隔 + public void refreshSimpleJob(String jobName, String jobGroup, int newIntervalSeconds) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobName, jobGroup); + if (!scheduler.checkExists(jobKey)) { + throw new SchedulerException("任务不存在: " + jobGroup + ":" + jobName); + } + // 获取当前触发器 + TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); + Trigger oldTrigger = scheduler.getTrigger(triggerKey); + + // 创建新的触发器 + Trigger newTrigger = TriggerBuilder.newTrigger() + .withIdentity(triggerKey) + .forJob(jobKey) + .startNow() + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withIntervalInSeconds(newIntervalSeconds) + .repeatForever()) + .build(); + + // 替换触发器 + scheduler.rescheduleJob(triggerKey, newTrigger); + logger.info("已刷新简单任务 [{}:{}] 的执行间隔为 {} 秒", + jobGroup, jobName, newIntervalSeconds); + } +} diff --git a/src/main/java/com/bonus/emergencyrap/vo/TaskVo.java b/src/main/java/com/bonus/emergencyrap/vo/TaskVo.java index d842601..f7a92bd 100644 --- a/src/main/java/com/bonus/emergencyrap/vo/TaskVo.java +++ b/src/main/java/com/bonus/emergencyrap/vo/TaskVo.java @@ -93,5 +93,15 @@ public class TaskVo { */ private String isLogin; + /** + * 0 关闭 1 启用 + */ + private String status; + /** + * 定时任务表达式 + */ + private String corn; + + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d0b3196..e1e6625 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -62,6 +62,10 @@ token: # 令牌有效期(默认30分钟) expireTime: 30 +task: + interval: + seconds: 60 +