diff --git a/ym-schedule-task/pom.xml b/ym-schedule-task/pom.xml new file mode 100644 index 0000000..9e491d0 --- /dev/null +++ b/ym-schedule-task/pom.xml @@ -0,0 +1,48 @@ + + + + ym-pass + com.cnbm + 1.0-SNAPSHOT + + 4.0.0 + + ym-schedule-task + + + 8 + 8 + 2.3.2 + + + + + com.cnbm + ym-common + 1.0-SNAPSHOT + + + com.cnbm + ym-admin + 1.0-SNAPSHOT + + + org.quartz-scheduler + quartz + ${quartz.version} + + + com.mchange + c3p0 + + + com.zaxxer + HikariCP-java6 + + + + + + \ No newline at end of file diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/config/ScheduleConfig.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/config/ScheduleConfig.java new file mode 100644 index 0000000..a2c7744 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/config/ScheduleConfig.java @@ -0,0 +1,58 @@ +package com.cnbm.scheduletask.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +import javax.sql.DataSource; +import java.util.Properties; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:30 PM + * @Version 1.0 + */ +@Configuration +public class ScheduleConfig { + + @Bean + public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) { + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + factory.setDataSource(dataSource); + + //quartz参数 + Properties prop = new Properties(); + prop.put("org.quartz.scheduler.instanceName", "RenrenScheduler"); + prop.put("org.quartz.scheduler.instanceId", "AUTO"); + //线程池配置 + prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); + prop.put("org.quartz.threadPool.threadCount", "20"); + prop.put("org.quartz.threadPool.threadPriority", "5"); + //JobStore配置 + prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); + //集群配置 + prop.put("org.quartz.jobStore.isClustered", "true"); + prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); + prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); + + prop.put("org.quartz.jobStore.misfireThreshold", "12000"); + prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); + prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); + + //PostgreSQL数据库,需要打开此注释 + //prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"); + + factory.setQuartzProperties(prop); + + factory.setSchedulerName("RenrenScheduler"); + //延时启动 + factory.setStartupDelay(30); + factory.setApplicationContextSchedulerContextKey("applicationContextKey"); + //可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 + factory.setOverwriteExistingJobs(true); + //设置自动启动,默认为true + factory.setAutoStartup(true); + + return factory; + } +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/controller/ScheduleJobController.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/controller/ScheduleJobController.java new file mode 100644 index 0000000..31b0dbe --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/controller/ScheduleJobController.java @@ -0,0 +1,125 @@ +package com.cnbm.scheduletask.controller; + +import com.cnbm.admin.annotation.LogOperation; +import com.cnbm.common.constant.Constant; +import com.cnbm.common.page.PageData; +import com.cnbm.common.utils.Result; +import com.cnbm.common.validator.ValidatorUtils; +import com.cnbm.common.validator.group.AddGroup; +import com.cnbm.common.validator.group.DefaultGroup; +import com.cnbm.common.validator.group.UpdateGroup; +import com.cnbm.scheduletask.dto.ScheduleJobDTO; +import com.cnbm.scheduletask.service.ScheduleJobService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; + +import java.util.Map; + +/** + * @Author weihongyang + * @Date 2022/6/24 8:57 AM + * @Version 1.0 + */ +@RestController +@RequestMapping("/sys/schedule") +@Api(tags="定时任务") +public class ScheduleJobController { + @Autowired + private ScheduleJobService scheduleJobService; + + @GetMapping("page") + @ApiOperation("分页") + @ApiImplicitParams({ + @ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataTypeClass=Integer.class) , + @ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query",required = true, dataTypeClass=Integer.class) , + @ApiImplicitParam(name = Constant.ORDER_FIELD, value = "排序字段", paramType = "query", dataTypeClass=String.class) , + @ApiImplicitParam(name = Constant.ORDER, value = "排序方式,可选值(asc、desc)", paramType = "query", dataTypeClass=String.class) , + @ApiImplicitParam(name = "beanName", value = "beanName", paramType = "query", dataTypeClass=String.class) + }) + @PreAuthorize("@ex.hasAuthority('sys:schedule:page')") + public Result> page(@ApiIgnore @RequestParam Map params){ + PageData page = scheduleJobService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @ApiOperation("信息") + @PreAuthorize("@ex.hasAuthority('sys:schedule:info')") + public Result info(@PathVariable("id") Long id){ + ScheduleJobDTO schedule = scheduleJobService.get(id); + + return new Result().ok(schedule); + } + + @PostMapping + @ApiOperation("保存") + @LogOperation("保存") + @PreAuthorize("@ex.hasAuthority('sys:schedule:save')") + public Result save(@RequestBody ScheduleJobDTO dto){ + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + scheduleJobService.save(dto); + + return new Result(); + } + + @PutMapping + @ApiOperation("修改") + @LogOperation("修改") + @PreAuthorize("@ex.hasAuthority('sys:schedule:update')") + public Result update(@RequestBody ScheduleJobDTO dto){ + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + scheduleJobService.update(dto); + + return new Result(); + } + + @DeleteMapping + @ApiOperation("删除") + @LogOperation("删除") + @PreAuthorize("@ex.hasAuthority('sys:schedule:delete')") + public Result delete(@RequestBody Long[] ids){ + scheduleJobService.deleteBatch(ids); + + return new Result(); + } + + @PutMapping("/run") + @ApiOperation("立即执行") + @LogOperation("立即执行") + @PreAuthorize("@ex.hasAuthority('sys:schedule:run')") + public Result run(@RequestBody Long[] ids){ + scheduleJobService.run(ids); + + return new Result(); + } + + @PutMapping("/pause") + @ApiOperation("暂停") + @LogOperation("暂停") + @PreAuthorize("@ex.hasAuthority('sys:schedule:pause')") + public Result pause(@RequestBody Long[] ids){ + scheduleJobService.pause(ids); + + return new Result(); + } + + @PutMapping("/resume") + @ApiOperation("恢复") + @LogOperation("恢复") + @PreAuthorize("@ex.hasAuthority('sys:schedule:resume')") + public Result resume(@RequestBody Long[] ids){ + scheduleJobService.resume(ids); + + return new Result(); + } + +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/controller/ScheduleJobLogController.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/controller/ScheduleJobLogController.java new file mode 100644 index 0000000..073fb63 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/controller/ScheduleJobLogController.java @@ -0,0 +1,55 @@ +package com.cnbm.scheduletask.controller; + +import com.cnbm.common.constant.Constant; +import com.cnbm.common.page.PageData; +import com.cnbm.common.utils.Result; +import com.cnbm.scheduletask.dto.ScheduleJobLogDTO; +import com.cnbm.scheduletask.service.ScheduleJobLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; + +import java.util.Map; + +/** + * @Author weihongyang + * @Date 2022/6/24 8:58 AM + * @Version 1.0 + */ +@RestController +@RequestMapping("/sys/scheduleLog") +@Api(tags="定时任务日志") +public class ScheduleJobLogController { + @Autowired + private ScheduleJobLogService scheduleJobLogService; + + @GetMapping("page") + @ApiOperation("分页") + @ApiImplicitParams({ + @ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataTypeClass=Integer.class) , + @ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query",required = true, dataTypeClass=Integer.class) , + @ApiImplicitParam(name = Constant.ORDER_FIELD, value = "排序字段", paramType = "query", dataTypeClass=String.class) , + @ApiImplicitParam(name = Constant.ORDER, value = "排序方式,可选值(asc、desc)", paramType = "query", dataTypeClass=String.class) , + @ApiImplicitParam(name = "jobId", value = "jobId", paramType = "query", dataType="String") + }) + @PreAuthorize("@ex.hasAuthority('sys:schedule:log')") + public Result> page(@ApiIgnore @RequestParam Map params){ + PageData page = scheduleJobLogService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @ApiOperation("信息") + @PreAuthorize("@ex.hasAuthority('sys:schedule:log')") + public Result info(@PathVariable("id") Long id){ + ScheduleJobLogDTO log = scheduleJobLogService.get(id); + + return new Result().ok(log); + } +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dao/ScheduleJobDao.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dao/ScheduleJobDao.java new file mode 100644 index 0000000..64cf74b --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dao/ScheduleJobDao.java @@ -0,0 +1,21 @@ +package com.cnbm.scheduletask.dao; + +import com.cnbm.common.dao.BaseDao; +import com.cnbm.scheduletask.entity.ScheduleJobEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Map; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:48 PM + * @Version 1.0 + */ +@Mapper +public interface ScheduleJobDao extends BaseDao { + + /** + * 批量更新状态 + */ + int updateBatch(Map map); +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dao/ScheduleJobLogDao.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dao/ScheduleJobLogDao.java new file mode 100644 index 0000000..0fd5289 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dao/ScheduleJobLogDao.java @@ -0,0 +1,15 @@ +package com.cnbm.scheduletask.dao; + +import com.cnbm.common.dao.BaseDao; +import com.cnbm.scheduletask.entity.ScheduleJobLogEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:48 PM + * @Version 1.0 + */ +@Mapper +public interface ScheduleJobLogDao extends BaseDao { + +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dto/ScheduleJobDTO.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dto/ScheduleJobDTO.java new file mode 100644 index 0000000..d8e65b4 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dto/ScheduleJobDTO.java @@ -0,0 +1,56 @@ +package com.cnbm.scheduletask.dto; + +import com.cnbm.common.validator.group.AddGroup; +import com.cnbm.common.validator.group.DefaultGroup; +import com.cnbm.common.validator.group.UpdateGroup; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serializable; +import java.util.Date; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:49 PM + * @Version 1.0 + */ +@Data +@ApiModel(value = "定时任务") +public class ScheduleJobDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @ApiModelProperty(value = "spring bean名称") + @NotBlank(message = "{schedule.bean.require}", groups = DefaultGroup.class) + private String beanName; + + @ApiModelProperty(value = "参数") + private String params; + + @ApiModelProperty(value = "cron表达式") + @NotBlank(message = "{schedule.cron.require}", groups = DefaultGroup.class) + private String cronExpression; + + @ApiModelProperty(value = "任务状态 0:暂停 1:正常") + @Range(min=0, max=1, message = "{schedule.status.range}", groups = DefaultGroup.class) + private Integer status; + + @ApiModelProperty(value = "备注") + private String remark; + + @ApiModelProperty(value = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + +} + diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dto/ScheduleJobLogDTO.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dto/ScheduleJobLogDTO.java new file mode 100644 index 0000000..d77859b --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/dto/ScheduleJobLogDTO.java @@ -0,0 +1,45 @@ +package com.cnbm.scheduletask.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:49 PM + * @Version 1.0 + */ +@Data +@ApiModel(value = "定时任务日志") +public class ScheduleJobLogDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "id") + private Long id; + + @ApiModelProperty(value = "任务id") + private Long jobId; + + @ApiModelProperty(value = "spring bean名称") + private String beanName; + + @ApiModelProperty(value = "参数") + private String params; + + @ApiModelProperty(value = "任务状态 0:失败 1:成功") + private Integer status; + + @ApiModelProperty(value = "失败信息") + private String error; + + @ApiModelProperty(value = "耗时(单位:毫秒)") + private Integer times; + + @ApiModelProperty(value = "创建时间") + private Date createDate; + +} + diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/entity/ScheduleJobEntity.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/entity/ScheduleJobEntity.java new file mode 100644 index 0000000..c624fe5 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/entity/ScheduleJobEntity.java @@ -0,0 +1,53 @@ +package com.cnbm.scheduletask.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.cnbm.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:47 PM + * @Version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("schedule_job") +public class ScheduleJobEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * spring bean名称 + */ + private String beanName; + /** + * 参数 + */ + private String params; + /** + * cron表达式 + */ + private String cronExpression; + /** + * 任务状态 0:暂停 1:正常 + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/entity/ScheduleJobLogEntity.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/entity/ScheduleJobLogEntity.java new file mode 100644 index 0000000..ff0e7ab --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/entity/ScheduleJobLogEntity.java @@ -0,0 +1,54 @@ +package com.cnbm.scheduletask.entity; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:47 PM + * @Version 1.0 + */ +@Data +@TableName("schedule_job_log") +public class ScheduleJobLogEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId + private Long id; + /** + * 任务id + */ + private Long jobId; + /** + * spring bean名称 + */ + private String beanName; + /** + * 参数 + */ + private String params; + /** + * 任务状态 0:失败 1:成功 + */ + private Integer status; + /** + * 失败信息 + */ + private String error; + /** + * 耗时(单位:毫秒) + */ + private Integer times; + /** + * 创建时间 + */ + private Date createDate; + +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/init/JobCommandLineRunner.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/init/JobCommandLineRunner.java new file mode 100644 index 0000000..38168a5 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/init/JobCommandLineRunner.java @@ -0,0 +1,39 @@ +package com.cnbm.scheduletask.init; + +import com.cnbm.scheduletask.dao.ScheduleJobDao; +import com.cnbm.scheduletask.entity.ScheduleJobEntity; +import com.cnbm.scheduletask.utils.ScheduleUtils; +import org.quartz.CronTrigger; +import org.quartz.Scheduler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:46 PM + * @Version 1.0 + */ +@Component +public class JobCommandLineRunner implements CommandLineRunner { + @Autowired + private Scheduler scheduler; + @Autowired + private ScheduleJobDao scheduleJobDao; + + @Override + public void run(String... args) { + List scheduleJobList = scheduleJobDao.selectList(null); + for(ScheduleJobEntity scheduleJob : scheduleJobList){ + CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId()); + //如果不存在,则创建 + if(cronTrigger == null) { + ScheduleUtils.createScheduleJob(scheduler, scheduleJob); + }else { + ScheduleUtils.updateScheduleJob(scheduler, scheduleJob); + } + } + } +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/ScheduleJobLogService.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/ScheduleJobLogService.java new file mode 100644 index 0000000..4ff4ffd --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/ScheduleJobLogService.java @@ -0,0 +1,20 @@ +package com.cnbm.scheduletask.service; + +import com.cnbm.common.page.PageData; +import com.cnbm.common.service.BaseService; +import com.cnbm.scheduletask.dto.ScheduleJobLogDTO; +import com.cnbm.scheduletask.entity.ScheduleJobLogEntity; + +import java.util.Map; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:44 PM + * @Version 1.0 + */ +public interface ScheduleJobLogService extends BaseService { + + PageData page(Map params); + + ScheduleJobLogDTO get(Long id); +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/ScheduleJobService.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/ScheduleJobService.java new file mode 100644 index 0000000..a8faea3 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/ScheduleJobService.java @@ -0,0 +1,56 @@ +package com.cnbm.scheduletask.service; + +import com.cnbm.common.page.PageData; +import com.cnbm.common.service.BaseService; +import com.cnbm.scheduletask.dto.ScheduleJobDTO; +import com.cnbm.scheduletask.entity.ScheduleJobEntity; + +import java.util.Map; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:43 PM + * @Version 1.0 + */ +public interface ScheduleJobService extends BaseService { + + PageData page(Map params); + + ScheduleJobDTO get(Long id); + + /** + * 保存定时任务 + */ + void save(ScheduleJobDTO dto); + + /** + * 更新定时任务 + */ + void update(ScheduleJobDTO dto); + + /** + * 批量删除定时任务 + */ + void deleteBatch(Long[] ids); + + /** + * 批量更新定时任务状态 + */ + int updateBatch(Long[] ids, int status); + + /** + * 立即执行 + */ + void run(Long[] ids); + + /** + * 暂停运行 + */ + void pause(Long[] ids); + + /** + * 恢复运行 + */ + void resume(Long[] ids); +} + diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/impl/ScheduleJobLogServiceImpl.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/impl/ScheduleJobLogServiceImpl.java new file mode 100644 index 0000000..0dedb9e --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/impl/ScheduleJobLogServiceImpl.java @@ -0,0 +1,51 @@ +package com.cnbm.scheduletask.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.cnbm.common.constant.Constant; +import com.cnbm.common.page.PageData; +import com.cnbm.common.service.impl.BaseServiceImpl; +import com.cnbm.common.utils.ConvertUtils; +import com.cnbm.scheduletask.dao.ScheduleJobLogDao; +import com.cnbm.scheduletask.dto.ScheduleJobLogDTO; +import com.cnbm.scheduletask.entity.ScheduleJobLogEntity; +import com.cnbm.scheduletask.service.ScheduleJobLogService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:46 PM + * @Version 1.0 + */ +@Service +public class ScheduleJobLogServiceImpl extends BaseServiceImpl implements ScheduleJobLogService { + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + return getPageData(page, ScheduleJobLogDTO.class); + } + + private QueryWrapper getWrapper(Map params){ + String jobId = (String)params.get("jobId"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq(StringUtils.isNotBlank(jobId), "job_id", jobId); + + return wrapper; + } + + @Override + public ScheduleJobLogDTO get(Long id) { + ScheduleJobLogEntity entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, ScheduleJobLogDTO.class); + } + +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/impl/ScheduleJobServiceImpl.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/impl/ScheduleJobServiceImpl.java new file mode 100644 index 0000000..253f3e2 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/service/impl/ScheduleJobServiceImpl.java @@ -0,0 +1,127 @@ +package com.cnbm.scheduletask.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.cnbm.common.constant.Constant; +import com.cnbm.common.page.PageData; +import com.cnbm.common.service.impl.BaseServiceImpl; +import com.cnbm.common.utils.ConvertUtils; +import com.cnbm.scheduletask.dao.ScheduleJobDao; +import com.cnbm.scheduletask.dto.ScheduleJobDTO; +import com.cnbm.scheduletask.entity.ScheduleJobEntity; +import com.cnbm.scheduletask.service.ScheduleJobService; +import com.cnbm.scheduletask.utils.ScheduleUtils; +import org.apache.commons.lang3.StringUtils; +import org.quartz.Scheduler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:45 PM + * @Version 1.0 + */ +@Service +public class ScheduleJobServiceImpl extends BaseServiceImpl implements ScheduleJobService { + @Autowired + private Scheduler scheduler; + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + return getPageData(page, ScheduleJobDTO.class); + } + + @Override + public ScheduleJobDTO get(Long id) { + ScheduleJobEntity entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class); + } + + private QueryWrapper getWrapper(Map params){ + String beanName = (String)params.get("beanName"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.like(StringUtils.isNotBlank(beanName), "bean_name", beanName); + + return wrapper; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(ScheduleJobDTO dto) { + ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class); + + entity.setStatus(Constant.ScheduleStatus.NORMAL.getValue()); + this.insert(entity); + + ScheduleUtils.createScheduleJob(scheduler, entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(ScheduleJobDTO dto) { + ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class); + + ScheduleUtils.updateScheduleJob(scheduler, entity); + + this.updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteBatch(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.deleteScheduleJob(scheduler, id); + } + + //删除数据 + this.deleteBatchIds(Arrays.asList(ids)); + } + + @Override + public int updateBatch(Long[] ids, int status){ + Map map = new HashMap<>(2); + map.put("ids", ids); + map.put("status", status); + return baseDao.updateBatch(map); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void run(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.run(scheduler, this.selectById(id)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void pause(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.pauseJob(scheduler, id); + } + + updateBatch(ids, Constant.ScheduleStatus.PAUSE.getValue()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resume(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.resumeJob(scheduler, id); + } + + updateBatch(ids, Constant.ScheduleStatus.NORMAL.getValue()); + } + +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/task/ITask.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/task/ITask.java new file mode 100644 index 0000000..c375dd9 --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/task/ITask.java @@ -0,0 +1,16 @@ +package com.cnbm.scheduletask.task; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:41 PM + * @Version 1.0 + */ +public interface ITask { + + /** + * 执行定时任务接口 + * + * @param params 参数,多参数使用JSON数据 + */ + void run(String params); +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/task/TestTask.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/task/TestTask.java new file mode 100644 index 0000000..0eb2c1b --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/task/TestTask.java @@ -0,0 +1,20 @@ +package com.cnbm.scheduletask.task; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:41 PM + * @Version 1.0 + */ +@Component("testTask") +public class TestTask implements ITask{ + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public void run(String params){ + logger.debug("TestTask定时任务正在执行,参数为:{}", params); + } +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/utils/ScheduleJob.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/utils/ScheduleJob.java new file mode 100644 index 0000000..f7871ec --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/utils/ScheduleJob.java @@ -0,0 +1,70 @@ +package com.cnbm.scheduletask.utils; + +import com.cnbm.common.constant.Constant; +import com.cnbm.common.exception.ExceptionUtils; +import com.cnbm.common.utils.SpringContextUtils; +import com.cnbm.scheduletask.entity.ScheduleJobEntity; +import com.cnbm.scheduletask.entity.ScheduleJobLogEntity; +import com.cnbm.scheduletask.service.ScheduleJobLogService; +import org.quartz.JobExecutionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import java.lang.reflect.Method; +import java.util.Date; + +/** + * @Author weihongyang + * @Date 2022/6/23 4:38 PM + * @Version 1.0 + */ +public class ScheduleJob extends QuartzJobBean { + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + protected void executeInternal(JobExecutionContext context) { + ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap(). + get(ScheduleUtils.JOB_PARAM_KEY); + + //数据库保存执行记录 + ScheduleJobLogEntity log = new ScheduleJobLogEntity(); + log.setJobId(scheduleJob.getId()); + log.setBeanName(scheduleJob.getBeanName()); + log.setParams(scheduleJob.getParams()); + log.setCreateDate(new Date()); + + //任务开始时间 + long startTime = System.currentTimeMillis(); + + try { + //执行任务 + logger.info("任务准备执行,任务ID:{}", scheduleJob.getId()); + Object target = SpringContextUtils.getBean(scheduleJob.getBeanName()); + Method method = target.getClass().getDeclaredMethod("run", String.class); + method.invoke(target, scheduleJob.getParams()); + + //任务执行总时长 + long times = System.currentTimeMillis() - startTime; + log.setTimes((int)times); + //任务状态 + log.setStatus(Constant.SUCCESS); + + logger.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times); + } catch (Exception e) { + logger.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e); + + //任务执行总时长 + long times = System.currentTimeMillis() - startTime; + log.setTimes((int)times); + + //任务状态 + log.setStatus(Constant.FAIL); + log.setError(ExceptionUtils.getErrorStackTrace(e)); + }finally { + //获取spring bean + ScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class); + scheduleJobLogService.insert(log); + } + } +} diff --git a/ym-schedule-task/src/main/java/com/cnbm/scheduletask/utils/ScheduleUtils.java b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/utils/ScheduleUtils.java new file mode 100644 index 0000000..00b57de --- /dev/null +++ b/ym-schedule-task/src/main/java/com/cnbm/scheduletask/utils/ScheduleUtils.java @@ -0,0 +1,154 @@ +package com.cnbm.scheduletask.utils; + +import com.cnbm.common.constant.Constant; +import com.cnbm.common.exception.ErrorCode; +import com.cnbm.common.exception.RenException; +import com.cnbm.scheduletask.entity.ScheduleJobEntity; +import org.quartz.*; + + +/** + * @Author weihongyang + * @Date 2022/6/23 4:40 PM + * @Version 1.0 + */ +public class ScheduleUtils { + private final static String JOB_NAME = "TASK_"; + /** + * 任务调度参数key + */ + public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY"; + + /** + * 获取触发器key + */ + public static TriggerKey getTriggerKey(Long jobId) { + return TriggerKey.triggerKey(JOB_NAME + jobId); + } + + /** + * 获取jobKey + */ + public static JobKey getJobKey(Long jobId) { + return JobKey.jobKey(JOB_NAME + jobId); + } + + /** + * 获取表达式触发器 + */ + public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) { + try { + return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId)); + } catch (SchedulerException e) { + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + //构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build(); + + //表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) + .withMisfireHandlingInstructionDoNothing(); + + //按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build(); + + //放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob); + + scheduler.scheduleJob(jobDetail, trigger); + + //暂停任务 + if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){ + pauseJob(scheduler, scheduleJob.getId()); + } + } catch (SchedulerException e) { + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 更新定时任务 + */ + public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + TriggerKey triggerKey = getTriggerKey(scheduleJob.getId()); + + //表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) + .withMisfireHandlingInstructionDoNothing(); + + CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId()); + + //按新的cronExpression表达式重新构建trigger + trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + + //参数 + trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob); + + scheduler.rescheduleJob(triggerKey, trigger); + + //暂停任务 + if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){ + pauseJob(scheduler, scheduleJob.getId()); + } + + } catch (SchedulerException e) { + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 立即执行任务 + */ + public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + //参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(JOB_PARAM_KEY, scheduleJob); + + scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap); + } catch (SchedulerException e) { + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 暂停任务 + */ + public static void pauseJob(Scheduler scheduler, Long jobId) { + try { + scheduler.pauseJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 恢复任务 + */ + public static void resumeJob(Scheduler scheduler, Long jobId) { + try { + scheduler.resumeJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 删除定时任务 + */ + public static void deleteScheduleJob(Scheduler scheduler, Long jobId) { + try { + scheduler.deleteJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new RenException(ErrorCode.JOB_ERROR, e); + } + } +}