From 4fe319de451078b3c44eae1112978206e87fad6a Mon Sep 17 00:00:00 2001 From: liyd <1419499670@qq.com> Date: Mon, 5 Dec 2022 19:15:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E8=B0=83=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/nl/AppRun.java | 5 - .../nl/modules/quartz/config/JobRunner.java | 45 ++++ .../modules/quartz/config/QuartzConfig.java | 57 +++++ .../quartz/rest/QuartzJobController.java | 92 ++++++++ .../quartz/service/QuartzJobService.java | 79 +++++++ .../service/impl/QuartzJobServiceImpl.java | 219 ++++++++++++++++++ .../org/nl/modules/quartz/task/TestTask.java | 26 +++ .../nl/modules/quartz/utils/ExecutionJob.java | 102 ++++++++ .../nl/modules/quartz/utils/QuartzManage.java | 156 +++++++++++++ .../modules/quartz/utils/QuartzRunnable.java | 45 ++++ .../java/org/nl/modules/system/wql/sys.xls | Bin 254464 -> 262144 bytes nladmin-ui/src/views/system/timing/index.vue | 87 ++++--- nladmin-ui/src/views/system/timing/log.vue | 28 +-- 13 files changed, 878 insertions(+), 63 deletions(-) create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/config/JobRunner.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/config/QuartzConfig.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/rest/QuartzJobController.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/service/QuartzJobService.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/service/impl/QuartzJobServiceImpl.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/task/TestTask.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/ExecutionJob.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/QuartzManage.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/QuartzRunnable.java diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/AppRun.java b/nladmin-system/nlsso-server/src/main/java/org/nl/AppRun.java index 230144d..4add80b 100644 --- a/nladmin-system/nlsso-server/src/main/java/org/nl/AppRun.java +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/AppRun.java @@ -35,11 +35,6 @@ import org.springframework.web.bind.annotation.RestController; @EnableTransactionManagement @EnableMethodCache(basePackages = "org.nl") @EnableCreateCacheAnnotation -@ComponentScan( - excludeFilters = { - @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.nl.modules.quartz.*"), - @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.github.loki4j.*")} -) public class AppRun { public static void main(String[] args) { diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/config/JobRunner.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/config/JobRunner.java new file mode 100644 index 0000000..06483c9 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/config/JobRunner.java @@ -0,0 +1,45 @@ +package org.nl.modules.quartz.config; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import lombok.RequiredArgsConstructor; +import netscape.javascript.JSObject; +import org.nl.modules.quartz.utils.QuartzManage; +import org.nl.modules.wql.core.bean.WQLObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.function.Consumer; + +/** + * @author: lyd + * @description: + * @Date: 2022/12/5 + */ +@Component +@RequiredArgsConstructor +@Order(100) +public class JobRunner implements ApplicationRunner { + private static final Logger log = LoggerFactory.getLogger(JobRunner.class); +// private final QuartzJobRepository quartzJobRepository; + private final QuartzManage quartzManage; + + /** + * 项目启动时重新激活启用的定时任务 + * + * @param applicationArguments / + */ + @Override + public void run(ApplicationArguments applicationArguments) { + log.info("--------------------注入定时任务---------------------"); + WQLObject jobTab = WQLObject.getWQLObject("sys_quartz_job"); + JSONArray quartzJobs = jobTab.query("is_pause = '0'").getResultJSONArray(0); + quartzJobs.forEach( job -> quartzManage.addJob((JSONObject) job)); + log.info("--------------------定时任务注入完成---------------------"); + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/config/QuartzConfig.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/config/QuartzConfig.java new file mode 100644 index 0000000..f0743ff --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/config/QuartzConfig.java @@ -0,0 +1,57 @@ +package org.nl.modules.quartz.config; + +import org.quartz.Scheduler; +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.AdaptableJobFactory; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.stereotype.Component; + +/** + * @author: lyd + * @description: 定时任务配置 + * @Date: 2022/12/5 + */ +@Configuration +public class QuartzConfig { + + /** + * 解决Job中注入Spring Bean为null的问题 + */ + @Component("quartzJobFactory") + public static class QuartzJobFactory extends AdaptableJobFactory { + + private final AutowireCapableBeanFactory capableBeanFactory; + + public QuartzJobFactory(AutowireCapableBeanFactory capableBeanFactory) { + this.capableBeanFactory = capableBeanFactory; + } + + @Override + protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { + + //调用父类的方法 + Object jobInstance = super.createJobInstance(bundle); + capableBeanFactory.autowireBean(jobInstance); + return jobInstance; + } + } + + /** + * 注入scheduler到spring + * @param quartzJobFactory / + * @return Scheduler + * @throws Exception / + */ + @Bean(name = "scheduler") + public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception { + SchedulerFactoryBean factoryBean=new SchedulerFactoryBean(); + factoryBean.setJobFactory(quartzJobFactory); + factoryBean.afterPropertiesSet(); + Scheduler scheduler=factoryBean.getScheduler(); + scheduler.start(); + return scheduler; + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/rest/QuartzJobController.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/rest/QuartzJobController.java new file mode 100644 index 0000000..260eb49 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/rest/QuartzJobController.java @@ -0,0 +1,92 @@ +package org.nl.modules.quartz.rest; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.alibaba.fastjson.JSONObject; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.nl.modules.logging.annotation.Log; +import org.nl.modules.quartz.service.QuartzJobService; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; +import java.util.Set; + +/** + * @author: lyd + * @description: + * @Date: 2022/12/5 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/jobs") +@Api(tags = "系统:定时任务管理") +public class QuartzJobController { + private final QuartzJobService quartzJobService; + + @GetMapping + @Log("查询点位") + @ApiOperation("查询点位") + //@SaCheckPermission("timing:list") + public ResponseEntity query(@RequestParam Map whereJson, Pageable page) { + return new ResponseEntity<>(quartzJobService.queryAll(whereJson, page), HttpStatus.OK); + } + + @ApiOperation("查询任务执行日志") + @GetMapping(value = "/logs") + @SaCheckPermission("timing:list") + public ResponseEntity queryJobLog(@RequestParam Map criteria, Pageable pageable) { + return new ResponseEntity<>(quartzJobService.queryAllLog(criteria, pageable), HttpStatus.OK); + } + + @Log("新增定时任务") + @ApiOperation("新增定时任务") + @PostMapping +// @SaCheckPermission("timing:add") + public ResponseEntity create(@RequestBody JSONObject resources) { + quartzJobService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改定时任务") + @ApiOperation("修改定时任务") + @PutMapping +// @SaCheckPermission("timing:edit") + public ResponseEntity update(@RequestBody JSONObject resources) { + quartzJobService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("更改定时任务状态") + @ApiOperation("更改定时任务状态") + @PutMapping(value = "/{id}") +// @SaCheckPermission("timing:edit") + public ResponseEntity update(@PathVariable Long id) { + quartzJobService.updateIsPause(quartzJobService.findById(id)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("执行定时任务") + @ApiOperation("执行定时任务") + @PutMapping(value = "/exec/{id}") +// @SaCheckPermission("timing:edit") + public ResponseEntity execution(@PathVariable Long id) { + quartzJobService.execution(quartzJobService.findById(id)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除定时任务") + @ApiOperation("删除定时任务") + @DeleteMapping +// @SaCheckPermission("timing:del") + public ResponseEntity delete(@RequestBody Set ids) { + quartzJobService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/service/QuartzJobService.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/service/QuartzJobService.java new file mode 100644 index 0000000..abf9d40 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/service/QuartzJobService.java @@ -0,0 +1,79 @@ +package org.nl.modules.quartz.service; + +import com.alibaba.fastjson.JSONObject; +import org.springframework.data.domain.Pageable; + +import java.util.Map; +import java.util.Set; + +/** + * @author: lyd + * @description: + * @Date: 2022/12/5 + */ +public interface QuartzJobService { + + /** + * 分页查询 + * @param whereJson + * @param page + * @return + */ + Object queryAll(Map whereJson, Pageable page); + + /** + * 添加 + * @param resources + */ + void create(JSONObject resources); + + /** + * 日志查询 + * @param criteria + * @param pageable + * @return + */ + Object queryAllLog(Map criteria, Pageable pageable); + + /** + * 修改任务 + * @param resources + */ + void update(JSONObject resources); + + /** + * 根据ID查询 + * + * @param id ID + * @return / + */ + JSONObject findById(Long id); + + /** + * 立即执行定时任务 + * + * @param quartzJob / + */ + void execution(JSONObject quartzJob); + + /** + * 更改定时任务状态 + * + * @param job / + */ + void updateIsPause(JSONObject job); + + /** + * 执行子任务 + * + * @param tasks / + * @throws InterruptedException / + */ + void executionSubJob(String[] tasks) throws InterruptedException; + + /** + * 批量删除 + * @param ids + */ + void delete(Set ids); +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/service/impl/QuartzJobServiceImpl.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/service/impl/QuartzJobServiceImpl.java new file mode 100644 index 0000000..30745d6 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/service/impl/QuartzJobServiceImpl.java @@ -0,0 +1,219 @@ +package org.nl.modules.quartz.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import lombok.RequiredArgsConstructor; +import org.nl.modules.common.exception.BadRequestException; +import org.nl.modules.common.utils.RedisUtils; +import org.nl.modules.common.utils.SecurityUtils; +import org.nl.modules.quartz.service.QuartzJobService; +import org.nl.modules.quartz.utils.QuartzManage; +import org.nl.modules.wql.core.bean.ResultBean; +import org.nl.modules.wql.core.bean.WQLObject; +import org.nl.modules.wql.util.WqlUtil; +import org.quartz.CronExpression; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author: lyd + * @description: + * @Date: 2022/12/5 + */ +@RequiredArgsConstructor +@Service(value = "quartzJobService") +public class QuartzJobServiceImpl implements QuartzJobService { + + private final RedisUtils redisUtils; + private final QuartzManage quartzManage; + + @Override + public Object queryAll(Map whereJson, Pageable page) { + WQLObject quartzTab = WQLObject.getWQLObject("sys_quartz_job"); + String param = "1 = 1"; + if (ObjectUtil.isNotEmpty(whereJson.get("job_name"))) { + String job_name = whereJson.get("job_name").toString(); + param = param + " AND job_name like '%" + job_name + "%'"; + } + ResultBean rb = quartzTab.pagequery(WqlUtil.getHttpContext(page), param, "update_time desc"); + final JSONObject json = rb.pageResult(); + return json; + } + + /** + * 添加 + * + * @param resources + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void create(JSONObject resources) { + if (!CronExpression.isValidExpression(resources.getString("cron_expression"))) { + throw new BadRequestException("cron表达式格式错误"); + } + WQLObject quartzTab = WQLObject.getWQLObject("sys_quartz_job"); + Long userId = SecurityUtils.getCurrentUserId(); + String nickName = SecurityUtils.getCurrentNickName(); + String now = DateUtil.now(); + resources.put("job_id", IdUtil.getSnowflake(1,1).nextId()); + resources.put("create_id", userId); + resources.put("create_name", nickName); + resources.put("create_time", now); + resources.put("update_optid", userId); + resources.put("update_optname", nickName); + resources.put("update_time", now); + quartzTab.insert(resources); + // 添加任务 + quartzManage.addJob(resources); + } + + /** + * 日志查询 + * + * @param criteria + * @param pageable + * @return + */ + @Override + public Object queryAllLog(Map criteria, Pageable pageable) { + WQLObject logTab = WQLObject.getWQLObject("sys_quartz_log"); + String param = "1=1"; + if (ObjectUtil.isNotEmpty(criteria.get("job_name"))) { + param = param + " AND job_name = '" + criteria.get("job_name").toString() + "'"; + } + if (ObjectUtil.isNotEmpty(criteria.get("is_success"))) { + param = param + " AND is_success = '" + criteria.get("is_success").toString() + "'"; + } + if (ObjectUtil.isNotEmpty(criteria.get("createTime"))) { + param = param + " AND create_time >= " + criteria.get("begin_time") + " AND" + + " create_time <= " + criteria.get("end_time"); + } + ResultBean rb = logTab.pagequery(WqlUtil.getHttpContext(pageable), param, "create_time desc"); + final JSONObject json = rb.pageResult(); + return json; + } + + /** + * 修改任务 + * + * @param resources + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void update(JSONObject resources) { + WQLObject jobTab = WQLObject.getWQLObject("sys_quartz_job"); + JSONObject entity = this.findById(resources.getLong("job_id")); + if (entity == null) { + throw new BadRequestException("被删除或无权限,操作失败!"); + } + if (!CronExpression.isValidExpression(resources.getString("cron_expression"))) { + throw new BadRequestException("cron表达式格式错误"); + } + if (StrUtil.isNotEmpty(resources.getString("sub_task"))) { + List tasks = Arrays.asList(resources.getString("sub_task").split("[,,]")); + if (tasks.contains(resources.getString("job_id"))) { + throw new BadRequestException("子任务中不能添加当前任务ID"); + } + } + resources.put("update_optid", SecurityUtils.getCurrentUserId()); + resources.put("update_optname", SecurityUtils.getCurrentNickName()); + resources.put("update_time", DateUtil.now()); + jobTab.update(resources); + quartzManage.updateJobCron(resources); + } + + /** + * 根据ID查询 + * + * @param id ID + * @return / + */ + @Override + public JSONObject findById(Long id) { + WQLObject jobTab = WQLObject.getWQLObject("sys_quartz_job"); + JSONObject entity = jobTab.query("job_id = '" + id + "'").uniqueResult(0); + return entity; + } + + /** + * 立即执行定时任务 + * + * @param quartzJob / + */ + @Override + public void execution(JSONObject quartzJob) { + quartzManage.runJobNow(quartzJob); + } + + /** + * 更改定时任务状态 + * + * @param quartzJob / + */ + @Override + public void updateIsPause(JSONObject quartzJob) { + WQLObject jobTab = WQLObject.getWQLObject("sys_quartz_job"); + if (quartzJob.getString("is_pause").equals("1")) { + quartzManage.resumeJob(quartzJob); + quartzJob.put("is_pause", "0"); + } else { + quartzManage.pauseJob(quartzJob); + quartzJob.put("is_pause", "1"); + } + jobTab.update(quartzJob); + } + + /** + * 执行子任务 + * + * @param tasks / + * @throws InterruptedException / + */ + @Override + public void executionSubJob(String[] tasks) throws InterruptedException { + for (String id : tasks) { + JSONObject quartzJob = findById(Long.parseLong(id)); + // 执行任务 + String uuid = IdUtil.simpleUUID(); + quartzJob.put("job_id", uuid); + // 执行任务 + execution(quartzJob); + // 获取执行状态,如果执行失败则停止后面的子任务执行 + Boolean result = (Boolean) redisUtils.get(uuid); + while (result == null) { + // 休眠5秒,再次获取子任务执行情况 + Thread.sleep(5000); + result = (Boolean) redisUtils.get(uuid); + } + if (!result) { + redisUtils.del(uuid); + break; + } + } + } + + /** + * 批量删除 + * + * @param ids + */ + @Override + public void delete(Set ids) { + WQLObject jobTab = WQLObject.getWQLObject("sys_quartz_job"); + WQLObject logTab = WQLObject.getWQLObject("sys_quartz_log"); + for (Long id : ids) { + JSONObject quartzJob = findById(id); + quartzManage.deleteJob(quartzJob); + jobTab.delete(quartzJob); + } + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/task/TestTask.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/task/TestTask.java new file mode 100644 index 0000000..49a0114 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/task/TestTask.java @@ -0,0 +1,26 @@ +package org.nl.modules.quartz.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @author: lyd + * @description: 测试任务 + * @Date: 2022/12/5 + */ +@Slf4j +@Component +public class TestTask { + + public void run(){ + log.info("run 执行成功"); + } + + public void run1(String str){ + log.info("run1 执行成功,参数为: {}" + str); + } + + public void run2(){ + log.info("run2 执行成功"); + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/ExecutionJob.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/ExecutionJob.java new file mode 100644 index 0000000..b334d3f --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/ExecutionJob.java @@ -0,0 +1,102 @@ +package org.nl.modules.quartz.utils; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.nl.config.thread.ThreadPoolExecutorUtil; +import org.nl.modules.common.utils.RedisUtils; +import org.nl.modules.common.utils.ThrowableUtil; +import org.nl.modules.quartz.service.QuartzJobService; +import org.nl.modules.wql.core.bean.WQLObject; +import org.nl.modules.wql.util.SpringContextHolder; +import org.quartz.JobExecutionContext; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * @author: lyd + * @description: + * @Date: 2022/12/5 + */ +@Async +@SuppressWarnings({"unchecked", "all"}) +@Slf4j +public class ExecutionJob extends QuartzJobBean { + + /** + * 该处仅供参考 + */ + private final static ThreadPoolExecutor EXECUTOR = ThreadPoolExecutorUtil.getPoll(); + + @Override + public void executeInternal(JobExecutionContext context) { + JSONObject quartzJob = (JSONObject) context.getMergedJobDataMap().get(QuartzManage.JOB_KEY); + // 获取spring bean + QuartzJobService quartzJobService = SpringContextHolder.getBean(QuartzJobService.class); + RedisUtils redisUtils = SpringContextHolder.getBean(RedisUtils.class); + WQLObject logTab = WQLObject.getWQLObject("sys_quartz_log"); + + String uuid = quartzJob.getString("job_id"); + + JSONObject logDto = new JSONObject(); + logDto.put("log_id", IdUtil.getSnowflake(1,1).nextId()); + logDto.put("create_time", DateUtil.now()); // ???? + logDto.put("job_name", quartzJob.getString("job_name")); + logDto.put("bean_name", quartzJob.getString("bean_name")); + logDto.put("method_name", quartzJob.getString("method_name")); + logDto.put("params", quartzJob.getString("params")); + long startTime = System.currentTimeMillis(); + logDto.put("cron_expression", quartzJob.getString("cron_expression")); + try { + // 执行任务 + System.out.println("--------------------------------------------------------------"); + System.out.println("任务开始执行,任务名称:" + quartzJob.getString("bean_name")); + QuartzRunnable task = new QuartzRunnable(quartzJob.getString("bean_name"), quartzJob.getString("method_name"), + quartzJob.getString("params")); + Future future = EXECUTOR.submit(task); + future.get(); + long times = System.currentTimeMillis() - startTime; + logDto.put("time", times); + if (StrUtil.isNotEmpty(uuid)) { + redisUtils.set(uuid, true); + } + // 任务状态 + logDto.put("is_success", "1"); + System.out.println("任务执行完毕,任务名称:" + quartzJob.getString("job_name") + ", 执行时间:" + times + "毫秒"); + System.out.println("--------------------------------------------------------------"); + // 判断是否存在子任务 + if (StrUtil.isNotEmpty(quartzJob.getString("sub_task"))) { + String[] tasks = quartzJob.getString("sub_task").split("[,,]"); + // 执行子任务 + quartzJobService.executionSubJob(tasks); + } + } catch (Exception e) { + if (StrUtil.isNotEmpty(uuid)) { + redisUtils.set(uuid, false); + } + System.out.println("任务执行失败,任务名称:" + quartzJob.getString("job_name")); + System.out.println("--------------------------------------------------------------"); + long times = System.currentTimeMillis() - startTime; + logDto.put("time", times); + // 任务状态 0:成功 1:失败 + logDto.put("is_success", "0"); + logDto.put("exception_detail", ThrowableUtil.getStackTrace(e)); + // 任务如果失败了则暂停 + if (quartzJob.getString("pause_after_failure") != null && quartzJob.getString("pause_after_failure").equals("1")) { + quartzJob.put("is_pause", "0"); + //更新状态 + quartzJobService.updateIsPause(quartzJob); + } + //异常时候打印日志 + log.info(logDto.toString()); + logTab.insert(logDto); + } finally { + } + } + +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/QuartzManage.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/QuartzManage.java new file mode 100644 index 0000000..3fff0f8 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/QuartzManage.java @@ -0,0 +1,156 @@ +package org.nl.modules.quartz.utils; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.nl.modules.common.exception.BadRequestException; +import org.quartz.*; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Date; + +import static org.quartz.TriggerBuilder.newTrigger; + +/** + * @author: lyd + * @description: 任务管理 + * @Date: 2022/12/5 + */ +@Slf4j +@Component +public class QuartzManage { + private static final String JOB_NAME = "TASK_"; + public static final String JOB_KEY = "JOB_KEY"; + + @Resource(name = "scheduler") + private Scheduler scheduler; + + public void addJob(JSONObject quartzJob){ + try { + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class). + withIdentity(JOB_NAME + quartzJob.getString("job_id")).build(); + + //通过触发器名和cron 表达式创建 Trigger + Trigger cronTrigger = newTrigger() + .withIdentity(JOB_NAME + quartzJob.getString("job_id")) + .startNow() + .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getString("cron_expression"))) + .build(); + + cronTrigger.getJobDataMap().put(QuartzManage.JOB_KEY, quartzJob); + + //重置启动时间 + ((CronTriggerImpl)cronTrigger).setStartTime(new Date()); + + //执行定时任务 + scheduler.scheduleJob(jobDetail,cronTrigger); + + // 暂停任务 + if (quartzJob.getString("is_pause").equals("1")) { + pauseJob(quartzJob); + } + } catch (Exception e){ + log.error("创建定时任务失败", e); + throw new BadRequestException("创建定时任务失败"); + } + } + + /** + * 执行任务 + * @param quartzJob + */ + public void runJobNow(JSONObject quartzJob) { + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getString("job_id")); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if(trigger == null) { + addJob(quartzJob); + } + JobDataMap dataMap = new JobDataMap(); + dataMap.put(QuartzManage.JOB_KEY, quartzJob); + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getString("job_id")); + scheduler.triggerJob(jobKey,dataMap); + } catch (Exception e){ + log.error("定时任务执行失败", e); + throw new BadRequestException("定时任务执行失败"); + } + } + + /** + * 暂停一个job + * @param quartzJob / + */ + public void pauseJob(JSONObject quartzJob){ + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getString("job_id")); + scheduler.pauseJob(jobKey); + } catch (Exception e){ + log.error("定时任务暂停失败", e); + throw new BadRequestException("定时任务暂停失败"); + } + } + + /** + * 更新job cron表达式 + * @param quartzJob / + */ + public void updateJobCron(JSONObject quartzJob) { + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getString("job_id")); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if(trigger == null){ + addJob(quartzJob); + trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + } + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getString("cron_expression")); + trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + //重置启动时间 + ((CronTriggerImpl)trigger).setStartTime(new Date()); + trigger.getJobDataMap().put(QuartzManage.JOB_KEY, quartzJob); + + scheduler.rescheduleJob(triggerKey, trigger); + // 暂停任务 + if (quartzJob.getString("is_pause").equals("1")) { + pauseJob(quartzJob); + } + } catch (Exception e){ + log.error("更新定时任务失败", e); + throw new BadRequestException("更新定时任务失败"); + } + } + + /** + * 恢复一个job + * @param quartzJob / + */ + public void resumeJob(JSONObject quartzJob) { + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getString("job_id")); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if(trigger == null) { + addJob(quartzJob); + } + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getString("job_id")); + scheduler.resumeJob(jobKey); + } catch (Exception e){ + log.error("恢复定时任务失败", e); + throw new BadRequestException("恢复定时任务失败"); + } + } + + public void deleteJob(JSONObject quartzJob) { + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getString("job_id")); + scheduler.pauseJob(jobKey); + scheduler.deleteJob(jobKey); + } catch (Exception e){ + log.error("删除定时任务失败", e); + throw new BadRequestException("删除定时任务失败"); + } + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/QuartzRunnable.java b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/QuartzRunnable.java new file mode 100644 index 0000000..da1b4d7 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/quartz/utils/QuartzRunnable.java @@ -0,0 +1,45 @@ +package org.nl.modules.quartz.utils; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.nl.modules.wql.util.SpringContextHolder; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +/** + * @author: lyd + * @description: 执行定时任务 + * @Date: 2022/12/5 + */ +@Slf4j +public class QuartzRunnable implements Callable { + + private final Object target; + private final Method method; + private final String params; + + QuartzRunnable(String beanName, String methodName, String params) + throws NoSuchMethodException, SecurityException { + this.target = SpringContextHolder.getBean(beanName); + this.params = params; + + if (StrUtil.isNotEmpty(params)) { + this.method = target.getClass().getDeclaredMethod(methodName, String.class); + } else { + this.method = target.getClass().getDeclaredMethod(methodName); + } + } + + @Override + public Object call() throws Exception { + ReflectionUtils.makeAccessible(method); + if (StrUtil.isNotEmpty(params)) { + method.invoke(target, params); + } else { + method.invoke(target); + } + return null; + } +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/modules/system/wql/sys.xls b/nladmin-system/nlsso-server/src/main/java/org/nl/modules/system/wql/sys.xls index 9c7e6d37bb832f7a9b5f729ce53cd61a19a2e1b4..b30935b33c4abdadde72a66d280cb3f7fddb7ba0 100644 GIT binary patch delta 42045 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 z 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 EA3`leoB#j- delta 34352 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 u(~;{@=2u574=m66)!}QDH@odP#Qzt^A^L>? diff --git a/nladmin-ui/src/views/system/timing/index.vue b/nladmin-ui/src/views/system/timing/index.vue index ca1474c..6317f79 100644 --- a/nladmin-ui/src/views/system/timing/index.vue +++ b/nladmin-ui/src/views/system/timing/index.vue @@ -4,7 +4,7 @@
- +
@@ -23,21 +23,21 @@ - - + + - - + + - - + + - + - +