From c9c2296d6091d941f2add41b2d14d97446226bc7 Mon Sep 17 00:00:00 2001 From: zhc077 <565291854> Date: Wed, 12 Apr 2023 15:53:37 +0800 Subject: [PATCH] =?UTF-8?q?'=E8=B0=83=E5=BA=A6=E4=BF=AE=E6=94=B9'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NewsWebMagicController.java | 29 +- .../webmagic/service/NewsPageProcessor.java | 2 +- .../cms/webmagic/task/NewsAsyncJob.java | 53 ++++ .../cms/webmagic/task/NewsWebMagicTask.java | 140 ++++----- .../cms/webmagic/task/SampleJobTest.java | 22 ++ .../controller/QuartzJobController.java | 283 ++++++++++++++++++ .../modules/quartz/entity/QuartzJob.java | 84 ++++++ .../jeecg/modules/quartz/job/AsyncJob.java | 35 +++ .../jeecg/modules/quartz/job/SampleJob.java | 23 ++ .../modules/quartz/job/SampleParamJob.java | 32 ++ .../quartz/mapper/QuartzJobMapper.java | 25 ++ .../quartz/mapper/xml/QuartzJobMapper.xml | 9 + .../quartz/service/IQuartzJobService.java | 67 +++++ .../service/impl/QuartzJobServiceImpl.java | 183 +++++++++++ 14 files changed, 908 insertions(+), 79 deletions(-) create mode 100644 jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/NewsAsyncJob.java create mode 100644 jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/SampleJobTest.java create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/controller/QuartzJobController.java create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/entity/QuartzJob.java create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/AsyncJob.java create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleJob.java create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleParamJob.java create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/mapper/QuartzJobMapper.java create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/mapper/xml/QuartzJobMapper.xml create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/service/IQuartzJobService.java create mode 100644 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java diff --git a/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/controller/NewsWebMagicController.java b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/controller/NewsWebMagicController.java index 60497e5..565a87b 100644 --- a/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/controller/NewsWebMagicController.java +++ b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/controller/NewsWebMagicController.java @@ -29,13 +29,26 @@ public class NewsWebMagicController { String page1 = "http://sie.huanghuai.edu.cn/index.php/item-list-category-13151.shtml"; String pageOther = "http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-"; String url = ""; - for (int i = 60; i <= 65; i++) { - if (i == 1) { - url = page1; - } else { - url = pageOther + (i + 1) + ".html"; - } - log.info("the page {} url:{}", i, url); + try { + Spider.create(pageProcessor) + .addUrl(page1) + // 抓取到的数据存数据库 + .addPipeline(pipeline) + // 开启2个线程抓取 + .thread(1) + // 异步启动爬虫 + .start(); + } catch (Exception ex) { + log.error("数据抓取异常:", ex.getMessage()); + log.error("定时抓取数据线程执行异常", ex); + } +// for (int i = 60; i <= 65; i++) { +// if (i == 1) { +// url = page1; +// } else { +// url = pageOther + (i + 1) + ".html"; +// } +// log.info("the page {} url:{}", i, url); // try { // Spider.create(pageProcessor) // .addUrl(url) @@ -49,6 +62,6 @@ public class NewsWebMagicController { // log.error("第[{}]页数据抓取异常:", i, ex.getMessage()); // log.error("定时抓取数据线程执行异常", ex); // } - } +// } } } diff --git a/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/service/NewsPageProcessor.java b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/service/NewsPageProcessor.java index 7b56a2c..c5b3af7 100644 --- a/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/service/NewsPageProcessor.java +++ b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/service/NewsPageProcessor.java @@ -24,7 +24,7 @@ public class NewsPageProcessor implements PageProcessor { @Override public void process(Page page) { -// log.info("page.url = " + page.getUrl()); + log.info("the page.url ****** = " + page.getUrl()); // log.info("page= " + page.getHtml().toString()); /** diff --git a/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/NewsAsyncJob.java b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/NewsAsyncJob.java new file mode 100644 index 0000000..d977409 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/NewsAsyncJob.java @@ -0,0 +1,53 @@ +package org.jeecg.modules.cms.webmagic.task; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.util.DateUtils; +import org.jeecg.modules.cms.webmagic.service.NewsPageProcessor; +import org.jeecg.modules.cms.webmagic.service.NewsPipeline; +import org.quartz.*; +import org.springframework.beans.factory.annotation.Autowired; +import us.codecraft.webmagic.Spider; + +/** + * @Description: 同步定时任务 + *

+ * 此处的同步是指 当定时任务的执行时间大于任务的时间间隔时 + * 会等待第一个任务执行完成才会走第二个任务 + * @author: taoyan + * @date: 2020年06月19日 + */ +@PersistJobDataAfterExecution +@DisallowConcurrentExecution +@Slf4j +public class NewsAsyncJob implements Job { + + @Autowired + private NewsPipeline pipeline; + + @Autowired + private NewsPageProcessor pageProcessor; + + @Override + public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { + log.info(" NewsAsyncJob Execution key:" + jobExecutionContext.getJobDetail().getKey()); + log.info(String.format("NewsAsyncJob-开始时间:" + DateUtils.getTimestamp())); + + String urlPage1 = "http://sie.huanghuai.edu.cn/index.php/item-list-category-13151.shtml"; +// String urlPageOther = "http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-"; +// String url = ""; + try { + Spider.create(pageProcessor) + .addUrl(urlPage1) + // 抓取到的数据存数据库 + .addPipeline(pipeline) + // 开启2个线程抓取 + .thread(1) + // 异步启动爬虫 + .start(); + } catch (Exception ex) { + log.error("数据抓取异常:", ex.getMessage()); + log.error("定时抓取数据线程执行异常", ex); + } + } + +} diff --git a/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/NewsWebMagicTask.java b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/NewsWebMagicTask.java index e5e16ae..95a236a 100644 --- a/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/NewsWebMagicTask.java +++ b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/NewsWebMagicTask.java @@ -1,71 +1,71 @@ -package org.jeecg.modules.cms.webmagic.task; - -import lombok.extern.slf4j.Slf4j; -import org.jeecg.modules.cms.webmagic.service.NewsPageProcessor; -import org.jeecg.modules.cms.webmagic.service.NewsPipeline; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import us.codecraft.webmagic.Spider; - -@Slf4j -@Component -public class NewsWebMagicTask { - - @Autowired - private NewsPipeline pipeline; - - @Autowired - private NewsPageProcessor pageProcessor; - - // 每天23点执行一次 - @Scheduled(cron = "0 0 23 * * ?") - //@Scheduled(cron = "0 40 15 * * ?") - // 每隔1分钟执行一次 -// @Scheduled(cron = "0 */3 * * * ?") - public void crawl() { - //第1页 http://sie.huanghuai.edu.cn/index.php/item-list-category-13151.shtml - //第2页 http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-2.html - //第3页 http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-3.html - // ...... - //第65页 http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-65.html - - String urlPage1 = "http://sie.huanghuai.edu.cn/index.php/item-list-category-13151.shtml"; - String urlPageOther = "http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-"; - String url = ""; - try { - Spider.create(pageProcessor) - .addUrl(urlPage1) - // 抓取到的数据存数据库 - .addPipeline(pipeline) - // 开启2个线程抓取 - .thread(3) - // 异步启动爬虫 - .start(); - } catch (Exception ex) { - log.error("数据抓取异常:", ex.getMessage()); - log.error("定时抓取数据线程执行异常", ex); - } -// for (int i = 1; i <= 9; i++) { -// if (i == 1) { -// url = urlPage1; -// } else { -// url = urlPageOther + (i + 1) + ".html"; -// } -// log.info("the page {} url:{}", i, url); -// try { -// Spider.create(pageProcessor) -// .addUrl(url) -// // 抓取到的数据存数据库 -// .addPipeline(pipeline) -// // 开启2个线程抓取 -// .thread(3) -// // 异步启动爬虫 -// .start(); -// } catch (Exception ex) { -// log.error("第[{}]页数据抓取异常:", i, ex.getMessage()); -// log.error("定时抓取数据线程执行异常", ex); -// } +//package org.jeecg.modules.cms.webmagic.task; +// +//import lombok.extern.slf4j.Slf4j; +//import org.jeecg.modules.cms.webmagic.service.NewsPageProcessor; +//import org.jeecg.modules.cms.webmagic.service.NewsPipeline; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.scheduling.annotation.Scheduled; +//import org.springframework.stereotype.Component; +//import us.codecraft.webmagic.Spider; +// +//@Slf4j +//@Component +//public class NewsWebMagicTask { +// +// @Autowired +// private NewsPipeline pipeline; +// +// @Autowired +// private NewsPageProcessor pageProcessor; +// +// // 每天23点执行一次 +//// @Scheduled(cron = "0 0 23 * * ?") +// //@Scheduled(cron = "0 40 15 * * ?") +// // 每隔1分钟执行一次 +//// @Scheduled(cron = "0 */3 * * * ?") +// public void crawl() { +// //第1页 http://sie.huanghuai.edu.cn/index.php/item-list-category-13151.shtml +// //第2页 http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-2.html +// //第3页 http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-3.html +// // ...... +// //第65页 http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-65.html +// +// String urlPage1 = "http://sie.huanghuai.edu.cn/index.php/item-list-category-13151.shtml"; +// String urlPageOther = "http://sie.huanghuai.edu.cn/index.php/item-list-category-13151-page-"; +// String url = ""; +// try { +// Spider.create(pageProcessor) +// .addUrl(urlPage1) +// // 抓取到的数据存数据库 +// .addPipeline(pipeline) +// // 开启2个线程抓取 +// .thread(1) +// // 异步启动爬虫 +// .start(); +// } catch (Exception ex) { +// log.error("数据抓取异常:", ex.getMessage()); +// log.error("定时抓取数据线程执行异常", ex); // } - } -} +//// for (int i = 1; i <= 9; i++) { +//// if (i == 1) { +//// url = urlPage1; +//// } else { +//// url = urlPageOther + (i + 1) + ".html"; +//// } +//// log.info("the page {} url:{}", i, url); +//// try { +//// Spider.create(pageProcessor) +//// .addUrl(url) +//// // 抓取到的数据存数据库 +//// .addPipeline(pipeline) +//// // 开启2个线程抓取 +//// .thread(3) +//// // 异步启动爬虫 +//// .start(); +//// } catch (Exception ex) { +//// log.error("第[{}]页数据抓取异常:", i, ex.getMessage()); +//// log.error("定时抓取数据线程执行异常", ex); +//// } +//// } +// } +//} diff --git a/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/SampleJobTest.java b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/SampleJobTest.java new file mode 100644 index 0000000..0dad758 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-cms/src/main/java/org/jeecg/modules/cms/webmagic/task/SampleJobTest.java @@ -0,0 +1,22 @@ +package org.jeecg.modules.cms.webmagic.task; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.util.DateUtils; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * 示例不带参定时任务 + * + * @Author Scott + */ +@Slf4j +public class SampleJobTest implements Job { + + @Override + public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { + log.info(" >>>>>>>>SampleJobTest Execution key:"+jobExecutionContext.getJobDetail().getKey()); + log.info(String.format(" 普通定时任务 SampleJobTest,时间:" + DateUtils.getTimestamp())); + } +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/controller/QuartzJobController.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/controller/QuartzJobController.java new file mode 100644 index 0000000..34b2bcb --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/controller/QuartzJobController.java @@ -0,0 +1,283 @@ +package org.jeecg.modules.quartz.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.ImportExcelUtil; +import org.jeecg.modules.quartz.entity.QuartzJob; +import org.jeecg.modules.quartz.service.IQuartzJobService; +import org.jeecgframework.poi.excel.ExcelImportUtil; +import org.jeecgframework.poi.excel.def.NormalExcelConstants; +import org.jeecgframework.poi.excel.entity.ExportParams; +import org.jeecgframework.poi.excel.entity.ImportParams; +import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * @Description: 定时任务在线管理 + * @Author: jeecg-boot + * @Date: 2019-01-02 + * @Version:V1.0 + */ +@RestController +@RequestMapping("/sys/quartzJob") +@Slf4j +@Api(tags = "定时任务接口") +public class QuartzJobController { + @Autowired + private IQuartzJobService quartzJobService; + @Autowired + private Scheduler scheduler; + + /** + * 分页列表查询 + * + * @param quartzJob + * @param pageNo + * @param pageSize + * @param req + * @return + */ + @RequestMapping(value = "/list", method = RequestMethod.GET) + public Result queryPageList(QuartzJob quartzJob, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) { + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(quartzJob, req.getParameterMap()); + Page page = new Page(pageNo, pageSize); + IPage pageList = quartzJobService.page(page, queryWrapper); + return Result.ok(pageList); + + } + + /** + * 添加定时任务 + * + * @param quartzJob + * @return + */ + //@RequiresRoles("admin") + @RequestMapping(value = "/add", method = RequestMethod.POST) + public Result add(@RequestBody QuartzJob quartzJob) { + quartzJobService.saveAndScheduleJob(quartzJob); + return Result.ok("创建定时任务成功"); + } + + /** + * 更新定时任务 + * + * @param quartzJob + * @return + */ + //@RequiresRoles("admin") + @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST}) + public Result eidt(@RequestBody QuartzJob quartzJob) { + try { + quartzJobService.editAndScheduleJob(quartzJob); + } catch (SchedulerException e) { + log.error(e.getMessage(), e); + return Result.error("更新定时任务失败!"); + } + return Result.ok("更新定时任务成功!"); + } + + /** + * 通过id删除 + * + * @param id + * @return + */ + //@RequiresRoles("admin") + @RequestMapping(value = "/delete", method = RequestMethod.DELETE) + public Result delete(@RequestParam(name = "id", required = true) String id) { + QuartzJob quartzJob = quartzJobService.getById(id); + if (quartzJob == null) { + return Result.error("未找到对应实体"); + } + quartzJobService.deleteAndStopJob(quartzJob); + return Result.ok("删除成功!"); + + } + + /** + * 批量删除 + * + * @param ids + * @return + */ + //@RequiresRoles("admin") + @RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE) + public Result deleteBatch(@RequestParam(name = "ids", required = true) String ids) { + if (ids == null || "".equals(ids.trim())) { + return Result.error("参数不识别!"); + } + for (String id : Arrays.asList(ids.split(","))) { + QuartzJob job = quartzJobService.getById(id); + quartzJobService.deleteAndStopJob(job); + } + return Result.ok("删除定时任务成功!"); + } + + /** + * 暂停定时任务 + * + * @param id + * @return + */ + //@RequiresRoles("admin") + @GetMapping(value = "/pause") + @ApiOperation(value = "停止定时任务") + public Result pauseJob(@RequestParam(name = "id") String id) { + QuartzJob job = quartzJobService.getById(id); + if (job == null) { + return Result.error("定时任务不存在!"); + } + quartzJobService.pause(job); + return Result.ok("停止定时任务成功"); + } + + /** + * 启动定时任务 + * + * @param id + * @return + */ + //@RequiresRoles("admin") + @GetMapping(value = "/resume") + @ApiOperation(value = "启动定时任务") + public Result resumeJob(@RequestParam(name = "id") String id) { + QuartzJob job = quartzJobService.getById(id); + if (job == null) { + return Result.error("定时任务不存在!"); + } + quartzJobService.resumeJob(job); + //scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim())); + return Result.ok("启动定时任务成功"); + } + + /** + * 通过id查询 + * + * @param id + * @return + */ + @RequestMapping(value = "/queryById", method = RequestMethod.GET) + public Result queryById(@RequestParam(name = "id", required = true) String id) { + QuartzJob quartzJob = quartzJobService.getById(id); + return Result.ok(quartzJob); + } + + /** + * 导出excel + * + * @param request + * @param quartzJob + */ + @RequestMapping(value = "/exportXls") + public ModelAndView exportXls(HttpServletRequest request, QuartzJob quartzJob) { + // Step.1 组装查询条件 + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(quartzJob, request.getParameterMap()); + // Step.2 AutoPoi 导出Excel + ModelAndView mv = new ModelAndView(new JeecgEntityExcelView()); + List pageList = quartzJobService.list(queryWrapper); + // 导出文件名称 + mv.addObject(NormalExcelConstants.FILE_NAME, "定时任务列表"); + mv.addObject(NormalExcelConstants.CLASS, QuartzJob.class); + //获取当前登录用户 + //update-begin---author:wangshuai ---date:20211227 for:[JTC-116]导出人写死了------------ + LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("定时任务列表数据", "导出人:" + user.getRealname(), "导出信息")); + //update-end---author:wangshuai ---date:20211227 for:[JTC-116]导出人写死了------------ + mv.addObject(NormalExcelConstants.DATA_LIST, pageList); + return mv; + } + + /** + * 通过excel导入数据 + * + * @param request + * @param response + * @return + */ + @RequestMapping(value = "/importExcel", method = RequestMethod.POST) + public Result importExcel(HttpServletRequest request, HttpServletResponse response) throws IOException { + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + Map fileMap = multipartRequest.getFileMap(); + // 错误信息 + List errorMessage = new ArrayList<>(); + int successLines = 0, errorLines = 0; + for (Map.Entry entity : fileMap.entrySet()) { + // 获取上传文件对象 + MultipartFile file = entity.getValue(); + ImportParams params = new ImportParams(); + params.setTitleRows(2); + params.setHeadRows(1); + params.setNeedSave(true); + try { + List listQuartzJobs = ExcelImportUtil.importExcel(file.getInputStream(), QuartzJob.class, params); + //add-begin-author:taoyan date:20210909 for:导入定时任务,并不会被启动和调度,需要手动点击启动,才会加入调度任务中 #2986 +// for(QuartzJob job: listQuartzJobs){ +// job.setStatus(CommonConstant.STATUS_DISABLE); +// } + List list = ImportExcelUtil.importDateSave(listQuartzJobs, IQuartzJobService.class, errorMessage, CommonConstant.SQL_INDEX_UNIQ_JOB_CLASS_NAME); + //add-end-author:taoyan date:20210909 for:导入定时任务,并不会被启动和调度,需要手动点击启动,才会加入调度任务中 #2986 + errorLines += list.size(); + successLines += (listQuartzJobs.size() - errorLines); + } catch (Exception e) { + log.error(e.getMessage(), e); + return Result.error("文件导入失败!"); + } finally { + try { + file.getInputStream().close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return ImportExcelUtil.imporReturnRes(errorLines, successLines, errorMessage); + } + + /** + * 立即执行 + * + * @param id + * @return + */ + //@RequiresRoles("admin") + @GetMapping("/execute") + public Result execute(@RequestParam(name = "id", required = true) String id) { + QuartzJob quartzJob = quartzJobService.getById(id); + if (quartzJob == null) { + return Result.error("未找到对应实体"); + } + try { + quartzJobService.execute(quartzJob); + } catch (Exception e) { + //e.printStackTrace(); + log.info("定时任务 立即执行失败>>" + e.getMessage()); + return Result.error("执行失败!"); + } + return Result.ok("执行成功!"); + } + +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/entity/QuartzJob.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/entity/QuartzJob.java new file mode 100644 index 0000000..ebaae1f --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/entity/QuartzJob.java @@ -0,0 +1,84 @@ +package org.jeecg.modules.quartz.entity; + +import java.io.Serializable; + +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Data; + +/** + * @Description: 定时任务在线管理 + * @Author: jeecg-boot + * @Date: 2019-01-02 + * @Version: V1.0 + */ +@Data +@TableName("sys_quartz_job") +public class QuartzJob implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_ID) + private String id; + /** + * 创建人 + */ + private String createBy; + /** + * 创建时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date createTime; + /** + * 删除状态 + */ + private Integer delFlag; + /** + * 修改人 + */ + private String updateBy; + /** + * 修改时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date updateTime; + /** + * 任务类名 + */ + @Excel(name = "任务类名", width = 40) + private String jobClassName; + /** + * cron表达式 + */ + @Excel(name = "cron表达式", width = 30) + private String cronExpression; + /** + * 参数 + */ + @Excel(name = "参数", width = 15) + private String parameter; + /** + * 描述 + */ + @Excel(name = "描述", width = 40) + private String description; + /** + * 状态 0正常 -1停止 + */ + @Excel(name = "状态", width = 15, dicCode = "quartz_status") + @Dict(dicCode = "quartz_status") + private Integer status; + +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/AsyncJob.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/AsyncJob.java new file mode 100644 index 0000000..61b9dcf --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/AsyncJob.java @@ -0,0 +1,35 @@ +package org.jeecg.modules.quartz.job; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.util.DateUtils; +import org.quartz.*; + +/** + * @Description: 同步定时任务测试 + * + * 此处的同步是指 当定时任务的执行时间大于任务的时间间隔时 + * 会等待第一个任务执行完成才会走第二个任务 + * + * + * @author: taoyan + * @date: 2020年06月19日 + */ +@PersistJobDataAfterExecution +@DisallowConcurrentExecution +@Slf4j +public class AsyncJob implements Job { + + @Override + public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { + log.info(" --- 同步任务调度开始 --- "); + try { + //此处模拟任务执行时间 5秒 任务表达式配置为每秒执行一次:0/1 * * * * ? * + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + //测试发现 每5秒执行一次 + log.info(" --- 执行完毕,时间:"+DateUtils.now()+"---"); + } + +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleJob.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleJob.java new file mode 100644 index 0000000..db37777 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleJob.java @@ -0,0 +1,23 @@ +package org.jeecg.modules.quartz.job; + +import org.jeecg.common.util.DateUtils; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +import lombok.extern.slf4j.Slf4j; + +/** + * 示例不带参定时任务 + * + * @Author Scott + */ +@Slf4j +public class SampleJob implements Job { + + @Override + public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { + log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey()); + log.info(String.format(" Jeecg-Boot 普通定时任务 SampleJob ! 时间:" + DateUtils.getTimestamp())); + } +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleParamJob.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleParamJob.java new file mode 100644 index 0000000..db2a71c --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleParamJob.java @@ -0,0 +1,32 @@ +package org.jeecg.modules.quartz.job; + +import org.jeecg.common.util.DateUtils; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +import lombok.extern.slf4j.Slf4j; + +/** + * 示例带参定时任务 + * + * @Author Scott + */ +@Slf4j +public class SampleParamJob implements Job { + + /** + * 若参数变量名修改 QuartzJobController中也需对应修改 + */ + private String parameter; + + public void setParameter(String parameter) { + this.parameter = parameter; + } + + @Override + public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { + log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey()); + log.info( String.format("welcome %s! Jeecg-Boot 带参数定时任务 SampleParamJob ! 时间:" + DateUtils.now(), this.parameter)); + } +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/mapper/QuartzJobMapper.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/mapper/QuartzJobMapper.java new file mode 100644 index 0000000..a84c3c6 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/mapper/QuartzJobMapper.java @@ -0,0 +1,25 @@ +package org.jeecg.modules.quartz.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Param; +import org.jeecg.modules.quartz.entity.QuartzJob; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * @Description: 定时任务在线管理 + * @Author: jeecg-boot + * @Date: 2019-01-02 + * @Version: V1.0 + */ +public interface QuartzJobMapper extends BaseMapper { + + /** + * 根据jobClassName查询 + * @param jobClassName 任务类名 + * @return + */ + public List findByJobClassName(@Param("jobClassName") String jobClassName); + +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/mapper/xml/QuartzJobMapper.xml b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/mapper/xml/QuartzJobMapper.xml new file mode 100644 index 0000000..2f6f0a0 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/mapper/xml/QuartzJobMapper.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/service/IQuartzJobService.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/service/IQuartzJobService.java new file mode 100644 index 0000000..cf90d13 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/service/IQuartzJobService.java @@ -0,0 +1,67 @@ +package org.jeecg.modules.quartz.service; + +import java.util.List; + +import org.jeecg.modules.quartz.entity.QuartzJob; +import org.quartz.SchedulerException; + +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * @Description: 定时任务在线管理 + * @Author: jeecg-boot + * @Date: 2019-04-28 + * @Version: V1.1 + */ +public interface IQuartzJobService extends IService { + + /** + * 通过类名寻找定时任务 + * @param jobClassName 类名 + * @return List + */ + List findByJobClassName(String jobClassName); + + /** + * 保存定时任务 + * @param quartzJob + * @return boolean + */ + boolean saveAndScheduleJob(QuartzJob quartzJob); + + /** + * 编辑定时任务 + * @param quartzJob + * @return boolean + * @throws SchedulerException + */ + boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException; + + /** + * 删除定时任务 + * @param quartzJob + * @return boolean + */ + boolean deleteAndStopJob(QuartzJob quartzJob); + + /** + * 恢复定时任务 + * @param quartzJob + * @return + */ + boolean resumeJob(QuartzJob quartzJob); + + /** + * 执行定时任务 + * @param quartzJob + * @throws Exception + */ + void execute(QuartzJob quartzJob) throws Exception; + + /** + * 暂停任务 + * @param quartzJob + * @throws SchedulerException + */ + void pause(QuartzJob quartzJob); +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java new file mode 100644 index 0000000..67e8b52 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java @@ -0,0 +1,183 @@ +package org.jeecg.modules.quartz.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.util.DateUtils; +import org.jeecg.modules.quartz.entity.QuartzJob; +import org.jeecg.modules.quartz.mapper.QuartzJobMapper; +import org.jeecg.modules.quartz.service.IQuartzJobService; +import org.quartz.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; + +/** + * @Description: 定时任务在线管理 + * @Author: jeecg-boot + * @Date: 2019-04-28 + * @Version: V1.1 + */ +@Slf4j +@Service +public class QuartzJobServiceImpl extends ServiceImpl implements IQuartzJobService { + @Autowired + private QuartzJobMapper quartzJobMapper; + @Autowired + private Scheduler scheduler; + + /** + * 立即执行的任务分组 + */ + private static final String JOB_TEST_GROUP = "test_group"; + + @Override + public List findByJobClassName(String jobClassName) { + return quartzJobMapper.findByJobClassName(jobClassName); + } + + /** + * 保存&启动定时任务 + */ + @Override + @Transactional(rollbackFor = JeecgBootException.class) + public boolean saveAndScheduleJob(QuartzJob quartzJob) { + // DB设置修改 + quartzJob.setDelFlag(CommonConstant.DEL_FLAG_0); + boolean success = this.save(quartzJob); + if (success) { + if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) { + // 定时器添加 + this.schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter()); + } + } + return success; + } + + /** + * 恢复定时任务 + */ + @Override + @Transactional(rollbackFor = JeecgBootException.class) + public boolean resumeJob(QuartzJob quartzJob) { + schedulerDelete(quartzJob.getId()); + schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter()); + quartzJob.setStatus(CommonConstant.STATUS_NORMAL); + return this.updateById(quartzJob); + } + + /** + * 编辑&启停定时任务 + * @throws SchedulerException + */ + @Override + @Transactional(rollbackFor = JeecgBootException.class) + public boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException { + if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) { + schedulerDelete(quartzJob.getId()); + schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter()); + }else{ + scheduler.pauseJob(JobKey.jobKey(quartzJob.getId())); + } + return this.updateById(quartzJob); + } + + /** + * 删除&停止删除定时任务 + */ + @Override + @Transactional(rollbackFor = JeecgBootException.class) + public boolean deleteAndStopJob(QuartzJob job) { + schedulerDelete(job.getId()); + boolean ok = this.removeById(job.getId()); + return ok; + } + + @Override + public void execute(QuartzJob quartzJob) throws Exception { + String jobName = quartzJob.getJobClassName().trim(); + Date startDate = new Date(); + String ymd = DateUtils.date2Str(startDate,DateUtils.yyyymmddhhmmss.get()); + String identity = jobName + ymd; + //3秒后执行 只执行一次 + // update-begin--author:sunjianlei ---- date:20210511--- for:定时任务立即执行,延迟3秒改成0.1秒------- + startDate.setTime(startDate.getTime() + 100L); + // update-end--author:sunjianlei ---- date:20210511--- for:定时任务立即执行,延迟3秒改成0.1秒------- + // 定义一个Trigger + SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger() + .withIdentity(identity, JOB_TEST_GROUP) + .startAt(startDate) + .build(); + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(getClass(jobName).getClass()).withIdentity(identity).usingJobData("parameter", quartzJob.getParameter()).build(); + // 将trigger和 jobDetail 加入这个调度 + scheduler.scheduleJob(jobDetail, trigger); + // 启动scheduler + scheduler.start(); + } + + @Override + @Transactional(rollbackFor = JeecgBootException.class) + public void pause(QuartzJob quartzJob){ + schedulerDelete(quartzJob.getId()); + quartzJob.setStatus(CommonConstant.STATUS_DISABLE); + this.updateById(quartzJob); + } + + /** + * 添加定时任务 + * + * @param jobClassName + * @param cronExpression + * @param parameter + */ + private void schedulerAdd(String id, String jobClassName, String cronExpression, String parameter) { + try { + // 启动调度器 + scheduler.start(); + + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(id).usingJobData("parameter", parameter).build(); + + // 表达式调度构建器(即任务执行的时间) + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression); + + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(id).withSchedule(scheduleBuilder).build(); + + scheduler.scheduleJob(jobDetail, trigger); + } catch (SchedulerException e) { + throw new JeecgBootException("创建定时任务失败", e); + } catch (RuntimeException e) { + throw new JeecgBootException(e.getMessage(), e); + }catch (Exception e) { + throw new JeecgBootException("后台找不到该类名:" + jobClassName, e); + } + } + + /** + * 删除定时任务 + * + * @param id + */ + private void schedulerDelete(String id) { + try { + scheduler.pauseTrigger(TriggerKey.triggerKey(id)); + scheduler.unscheduleJob(TriggerKey.triggerKey(id)); + scheduler.deleteJob(JobKey.jobKey(id)); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new JeecgBootException("删除定时任务失败"); + } + } + + private static Job getClass(String classname) throws Exception { + Class class1 = Class.forName(classname); + return (Job) class1.newInstance(); + } + +}