diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/BaseScheduleJob.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/BaseScheduleJob.java new file mode 100644 index 0000000000..db38b52bc9 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/BaseScheduleJob.java @@ -0,0 +1,28 @@ +package io.metersphere.sdk.sechedule; + +import io.metersphere.sdk.util.LogUtils; +import org.quartz.*; + +public abstract class BaseScheduleJob implements Job { + + protected String resourceId; + + protected String userId; + + protected String expression; + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + JobKey jobKey = context.getTrigger().getJobKey(); + JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); + this.resourceId = jobDataMap.getString("resourceId"); + this.userId = jobDataMap.getString("userId"); + this.expression = jobDataMap.getString("expression"); + + LogUtils.info(jobKey.getGroup() + " Running: " + resourceId); + LogUtils.info("CronExpression: " + expression); + businessExecute(context); + } + + protected abstract void businessExecute(JobExecutionContext context); +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/BaseScheduleService.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/BaseScheduleService.java new file mode 100644 index 0000000000..e318ccd43d --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/BaseScheduleService.java @@ -0,0 +1,80 @@ +package io.metersphere.sdk.sechedule; + +import io.metersphere.system.domain.Schedule; +import io.metersphere.system.domain.ScheduleExample; +import io.metersphere.system.mapper.ScheduleMapper; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.quartz.JobKey; +import org.quartz.TriggerKey; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +@Transactional(rollbackFor = Exception.class) +public class BaseScheduleService { + + @Resource + private ScheduleMapper scheduleMapper; + @Resource + private ScheduleManager scheduleManager; + + public void addSchedule(Schedule schedule) { + schedule.setId(UUID.randomUUID().toString()); + schedule.setCreateTime(System.currentTimeMillis()); + schedule.setUpdateTime(System.currentTimeMillis()); + scheduleMapper.insert(schedule); + } + + + public Schedule getSchedule(String ScheduleId) { + return scheduleMapper.selectByPrimaryKey(ScheduleId); + } + + public int editSchedule(Schedule schedule) { + schedule.setUpdateTime(System.currentTimeMillis()); + return scheduleMapper.updateByPrimaryKeySelective(schedule); + } + + public Schedule getScheduleByResource(String resourceId, String job) { + ScheduleExample example = new ScheduleExample(); + example.createCriteria().andResourceIdEqualTo(resourceId).andJobEqualTo(job); + List schedules = scheduleMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(schedules)) { + return schedules.get(0); + } + return null; + } + + public int deleteByResourceId(String resourceId, String group) { + ScheduleExample scheduleExample = new ScheduleExample(); + scheduleExample.createCriteria().andResourceIdEqualTo(resourceId); + removeJob(resourceId, group); + return scheduleMapper.deleteByExample(scheduleExample); + } + + public int deleteByResourceIds(List resourceIds, String group) { + ScheduleExample scheduleExample = new ScheduleExample(); + scheduleExample.createCriteria().andResourceIdIn(resourceIds); + for (String resourceId : resourceIds) { + removeJob(resourceId, group); + } + return scheduleMapper.deleteByExample(scheduleExample); + } + + + public int deleteByProjectId(String projectId) { + ScheduleExample scheduleExample = new ScheduleExample(); + scheduleExample.createCriteria().andProjectIdEqualTo(projectId); + List schedules = scheduleMapper.selectByExample(scheduleExample); + schedules.forEach(item -> { + removeJob(item.getKey(), item.getJob()); + }); + return scheduleMapper.deleteByExample(scheduleExample); + } + + private void removeJob(String key, String job) { + scheduleManager.removeJob(new JobKey(key, job), new TriggerKey(key, job)); + } +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/ScheduleManager.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/ScheduleManager.java new file mode 100644 index 0000000000..b99b766f0c --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/sechedule/ScheduleManager.java @@ -0,0 +1,155 @@ +package io.metersphere.sdk.sechedule; + +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.domain.Schedule; +import jakarta.annotation.Resource; +import org.quartz.*; + +/** + * @Description: 定时任务管理类 + */ +public class ScheduleManager { + + @Resource + private Scheduler scheduler; + + /** + * 添加 simpleJob + */ + public void addSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class cls, int repeatIntervalTime, JobDataMap jobDataMap) + throws SchedulerException { + + JobBuilder jobBuilder = JobBuilder.newJob(cls).withIdentity(jobKey); + if (jobDataMap != null) { + jobBuilder.usingJobData(jobDataMap); + } + + SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey) + .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInHours(repeatIntervalTime).repeatForever()) + .startNow().build(); + + scheduler.scheduleJob(jobBuilder.build(), trigger); + } + + /** + * 添加 cronJob + */ + public void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, JobDataMap jobDataMap) { + try { + LogUtils.info("addCronJob: " + triggerKey.getName() + "," + triggerKey.getGroup()); + JobBuilder jobBuilder = JobBuilder.newJob(jobClass).withIdentity(jobKey); + if (jobDataMap != null) { + jobBuilder.usingJobData(jobDataMap); + } + + TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger(); + triggerBuilder.withIdentity(triggerKey); + triggerBuilder.startNow(); + triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); + CronTrigger trigger = (CronTrigger) triggerBuilder.build(); + scheduler.scheduleJob(jobBuilder.build(), trigger); + + } catch (Exception e) { + LogUtils.error(e); + throw new MSException("定时任务配置异常: " + e.getMessage()); + } + } + + public void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron) { + addCronJob(jobKey, triggerKey, jobClass, cron, null); + } + + /** + * 修改 cronTrigger + */ + public void modifyCronJobTime(TriggerKey triggerKey, String cron) throws SchedulerException { + + LogUtils.info("modifyCronJobTime: " + triggerKey.getName() + "," + triggerKey.getGroup()); + try { + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + if (trigger == null) { + return; + } + + String oldTime = trigger.getCronExpression(); + if (!oldTime.equalsIgnoreCase(cron)) { + /* 方式一 :调用 rescheduleJob 开始 */ + TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();// 触发器 + triggerBuilder.withIdentity(triggerKey);// 触发器名,触发器组 + triggerBuilder.startNow(); // 立即执行 + triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); // 触发器时间设定 + trigger = (CronTrigger) triggerBuilder.build(); // 创建Trigger对象 + scheduler.rescheduleJob(triggerKey, trigger); // 修改一个任务的触发时间 + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * @Description: 根据job和trigger删除任务 + */ + public void removeJob(JobKey jobKey, TriggerKey triggerKey) { + try { + LogUtils.info("RemoveJob: " + jobKey.getName() + "," + jobKey.getGroup()); + scheduler.pauseTrigger(triggerKey); + scheduler.unscheduleJob(triggerKey); + scheduler.deleteJob(jobKey); + } catch (Exception e) { + LogUtils.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + + + public static void startJobs(Scheduler schedule) { + try { + schedule.start(); + } catch (Exception e) { + LogUtils.error(e); + throw new RuntimeException(e); + } + } + + + public void shutdownJobs(Scheduler schedule) { + try { + if (!schedule.isShutdown()) { + schedule.shutdown(); + } + } catch (Exception e) { + LogUtils.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + + /** + * 添加或修改 cronJob + */ + public void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, JobDataMap jobDataMap) + throws SchedulerException { + + LogUtils.info("AddOrUpdateCronJob: " + jobKey.getName() + "," + triggerKey.getGroup()); + + if (scheduler.checkExists(triggerKey)) { + modifyCronJobTime(triggerKey, cron); + } else { + addCronJob(jobKey, triggerKey, jobClass, cron, jobDataMap); + } + } + + public void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron) throws SchedulerException { + addOrUpdateCronJob(jobKey, triggerKey, jobClass, cron, null); + } + + public JobDataMap getDefaultJobDataMap(Schedule schedule, String expression, String userId) { + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put("resourceId", schedule.getResourceId()); + jobDataMap.put("expression", expression); + jobDataMap.put("userId", userId); + jobDataMap.put("config", schedule.getConfig()); + jobDataMap.put("projectId", schedule.getProjectId()); + return jobDataMap; + } +}