@@ -0,0 +1,48 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<parent> | |||
<artifactId>ym-pass</artifactId> | |||
<groupId>com.cnbm</groupId> | |||
<version>1.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>ym-schedule-task</artifactId> | |||
<properties> | |||
<maven.compiler.source>8</maven.compiler.source> | |||
<maven.compiler.target>8</maven.compiler.target> | |||
<quartz.version>2.3.2</quartz.version> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.cnbm</groupId> | |||
<artifactId>ym-common</artifactId> | |||
<version>1.0-SNAPSHOT</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.cnbm</groupId> | |||
<artifactId>ym-admin</artifactId> | |||
<version>1.0-SNAPSHOT</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.quartz-scheduler</groupId> | |||
<artifactId>quartz</artifactId> | |||
<version>${quartz.version}</version> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>com.mchange</groupId> | |||
<artifactId>c3p0</artifactId> | |||
</exclusion> | |||
<exclusion> | |||
<groupId>com.zaxxer</groupId> | |||
<artifactId>HikariCP-java6</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -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; | |||
} | |||
} |
@@ -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<PageData<ScheduleJobDTO>> page(@ApiIgnore @RequestParam Map<String, Object> params){ | |||
PageData<ScheduleJobDTO> page = scheduleJobService.page(params); | |||
return new Result<PageData<ScheduleJobDTO>>().ok(page); | |||
} | |||
@GetMapping("{id}") | |||
@ApiOperation("信息") | |||
@PreAuthorize("@ex.hasAuthority('sys:schedule:info')") | |||
public Result<ScheduleJobDTO> info(@PathVariable("id") Long id){ | |||
ScheduleJobDTO schedule = scheduleJobService.get(id); | |||
return new Result<ScheduleJobDTO>().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(); | |||
} | |||
} |
@@ -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<PageData<ScheduleJobLogDTO>> page(@ApiIgnore @RequestParam Map<String, Object> params){ | |||
PageData<ScheduleJobLogDTO> page = scheduleJobLogService.page(params); | |||
return new Result<PageData<ScheduleJobLogDTO>>().ok(page); | |||
} | |||
@GetMapping("{id}") | |||
@ApiOperation("信息") | |||
@PreAuthorize("@ex.hasAuthority('sys:schedule:log')") | |||
public Result<ScheduleJobLogDTO> info(@PathVariable("id") Long id){ | |||
ScheduleJobLogDTO log = scheduleJobLogService.get(id); | |||
return new Result<ScheduleJobLogDTO>().ok(log); | |||
} | |||
} |
@@ -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<ScheduleJobEntity> { | |||
/** | |||
* 批量更新状态 | |||
*/ | |||
int updateBatch(Map<String, Object> map); | |||
} |
@@ -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<ScheduleJobLogEntity> { | |||
} |
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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<ScheduleJobEntity> 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); | |||
} | |||
} | |||
} | |||
} |
@@ -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<ScheduleJobLogEntity> { | |||
PageData<ScheduleJobLogDTO> page(Map<String, Object> params); | |||
ScheduleJobLogDTO get(Long id); | |||
} |
@@ -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<ScheduleJobEntity> { | |||
PageData<ScheduleJobDTO> page(Map<String, Object> 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); | |||
} | |||
@@ -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<ScheduleJobLogDao, ScheduleJobLogEntity> implements ScheduleJobLogService { | |||
@Override | |||
public PageData<ScheduleJobLogDTO> page(Map<String, Object> params) { | |||
IPage<ScheduleJobLogEntity> page = baseDao.selectPage( | |||
getPage(params, Constant.CREATE_DATE, false), | |||
getWrapper(params) | |||
); | |||
return getPageData(page, ScheduleJobLogDTO.class); | |||
} | |||
private QueryWrapper<ScheduleJobLogEntity> getWrapper(Map<String, Object> params){ | |||
String jobId = (String)params.get("jobId"); | |||
QueryWrapper<ScheduleJobLogEntity> 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); | |||
} | |||
} |
@@ -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<ScheduleJobDao, ScheduleJobEntity> implements ScheduleJobService { | |||
@Autowired | |||
private Scheduler scheduler; | |||
@Override | |||
public PageData<ScheduleJobDTO> page(Map<String, Object> params) { | |||
IPage<ScheduleJobEntity> 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<ScheduleJobEntity> getWrapper(Map<String, Object> params){ | |||
String beanName = (String)params.get("beanName"); | |||
QueryWrapper<ScheduleJobEntity> 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<String, Object> 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()); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |