Quartz+SpringBoot实现一个任务对应多个触发器的添加、暂停、恢复、删除、查询所有,每个方法都有解释

提示: 任务和触发器唯一标识由组和名组成。

dto对象
package com.sifan.erp.dto;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

@Data
public class ScheduleInfoDto implements Serializable {

@ApiModelProperty(value = "服务名称")
private String serveName;

// @ApiModelProperty(value = “接口地址”)
// private String interfaceAddress;

@ApiModelProperty(value = "任务描述")
private String descName;

@ApiModelProperty(value = "任务名称")
private String jobName;

@ApiModelProperty(value = "任务组")
private String jobGroup;

@ApiModelProperty(value = "任务类名")
private String jobClassName;

@ApiModelProperty(value = "触发器名称")
private String triggerName;

@ApiModelProperty(value = "触发器组")
private String triggerGroup;

@ApiModelProperty(value = "表达式")
private String cronExpression;

@ApiModelProperty(value = "表达式中文")
private String cronExpressionZH;

@ApiModelProperty(value = "任务创建者名字")
private String userName;

@ApiModelProperty(value = "任务创建时间")
private String createdTime;

@ApiModelProperty(value = "任务更新时间")
private String updatedTime;

@ApiModelProperty(value = "触发器描述")
private String triggerDescription;
/**
 * 状态 :
 * NONE,
 * NORMAL,
 * PAUSED,
 * COMPLETE,
 * ERROR,
 * BLOCKED;
 */
@ApiModelProperty(value = "触发器状态")
private String triggerState;

@ApiModelProperty(value = "新触发器名称")
private String newTriggerName;

@ApiModelProperty(value = "新触发器组")
private String newTriggerGroup;
@ApiModelProperty(value = "上一次执行时间")
private String previousFireTime;

}
Job类,实际执行的类:通过jobDataMap拿到创建任务时存的参数
package com.sifan.erp.common;

import com.amazon.spapi.client.ApiException;
import com.sifan.erp.domain.Aws;
import com.sifan.erp.domain.SellerAccount;
import com.sifan.erp.service.AwsService;
import com.sifan.erp.service.OrderService;
import com.sifan.erp.service.SellerAccountService;
import com.sifan.erp.utils.RedisUtil;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@DisallowConcurrentExecution //不允许并发执行
public class AmazonDataCaptureJob implements Job {
private static final Logger log = LoggerFactory.getLogger(AmazonDataCaptureJob.class);
@Resource
private OrderService orderService;
@Resource
private SellerAccountService sellerAccountService;
@Resource
private AwsService awsService;
@Resource
private RedisUtil redisUtil;

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    JobDataMap jobDataMap = jobExecutionContext.getTrigger().getJobDataMap();
    Object obj = jobDataMap.get("userId");
    if (obj == null) {
        log.error("因为userId为空,定时任务AmazonDataCaptureJob执行失败");
        return;
    }
    Integer userId = (Integer) obj;
    List<SellerAccount> accounts = sellerAccountService.getAccountsByUserId(userId);
    accounts.forEach(account -> {
        //拉取亚马逊订单数据到数据库
        Aws aws = awsService.getIsUsedAws(account.getAccountId());
        AmazonApi.authAws(account, aws, redisUtil);
        pullAmazonOrderData(orderService, account.getAccountId());
        //更新订单数据
        try {
            orderService.updateShipmentStatus(account.getAccountId());
        } catch (ApiException e) {
            log.error("api异常:{}", e);
        }
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    log.info("AmazonDataCaptureJob任务成功执行,userId = {}", userId);
}

private void pullAmazonOrderData(OrderService orderService, Integer sellerId) {
    //获取最新订单时间 比如2022-09-05T15:21:20Z
    String recentOrderTime = orderService.getRecentOrderTime();
    try {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(Constants.DATE_FORMAT);
        Date parse = simpleDateFormat.parse(recentOrderTime);
        //对最新时间加一秒处理 比如2022-09-05T15:21:21Z
        Date date = new Date(parse.getTime() + 1000L);
        String format = simpleDateFormat.format(date);
        //根据数据库订单最新时间的下一秒去亚马逊拉取数据
        orderService.saveOrdersList(format, sellerId);
    } catch (ApiException | ParseException e) {
        log.error("定时抓取订单数据异常,异常信息 {}", e);
    }
}

}
1、添加功能:根据前端传来的信息自己决定是添加任务还是在任务下添加触发器、里面有些业务逻辑可以忽略。
/**
* 添加定时任务Job
*/
@SneakyThrows
@Override
public String addSchedule(Integer loginUserId, ScheduleInfoDto dto) {
log.info(“添加任务 任务信息为 {}”, dto);
//任务组名后缀,保证不同用户创建任务不冲突
String userSuf = “_” + loginUserId;
JobKey jobKey = new JobKey(dto.getJobName(), dto.getJobGroup() + userSuf);
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
//获取新的触发器名和组
String triggerGroup = dto.getNewTriggerGroup();
String triggerName = dto.getNewTriggerName();
Trigger taskTrigger = null;
for (int i = 0; i < triggers.size(); i++) {
Trigger trigger = triggers.get(i);
TriggerKey key = trigger.getKey();
String name = key.getName();
String group = key.getGroup();
if (StrUtil.equals(name, triggerName) && StrUtil.equals(group, triggerGroup)) {
taskTrigger = trigger;
break;
}
}

    //如果trigger存在,并且任务存在
    if (taskTrigger != null) {
        if (scheduler.checkExists(jobKey)) {
            return "该任务已存在!";
        }
    }

    if (StrUtil.isEmpty(dto.getJobClassName())) {
        return "任务类名为空,请输入正确的类名";
    }
    if (StrUtil.isEmpty(dto.getTriggerName())) {
        dto.setTriggerName("jobTrigger" + IdUtil.fastSimpleUUID());
    }
    if (StrUtil.isEmpty(dto.getTriggerGroup())) {
        dto.setTriggerGroup("jobTriggerGroup" + IdUtil.fastSimpleUUID());
    }
    JobDataMap jobDataMap = new JobDataMap();
    jobDataMap.put("userId", loginUserId);
    //任务信息
    JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(dto.getJobClassName()))
            .withIdentity(dto.getJobName(), dto.getJobGroup() + userSuf)
            .withDescription(dto.getDescName())
            .storeDurably()
            .build();
    //触发器信息
    CronTrigger cronTrigger = TriggerBuilder.newTrigger()
            .withIdentity(dto.getNewTriggerName(), dto.getNewTriggerGroup() + userSuf)
            .forJob(dto.getJobName(), dto.getJobGroup() + userSuf)
            .startNow()
            .usingJobData(jobDataMap)
            .withSchedule(CronScheduleBuilder.cronSchedule(dto.getCronExpression()))
            .withDescription(dto.getTriggerDescription())
            .build();
    //如果任务名、组存在,则在任务上添加触发器
    Date date = null;
    if (scheduler.checkExists(jobKey)) {
        date = scheduler.scheduleJob(cronTrigger);
    } else {
        //如果任务不存在,添加任务和触发器
        date = scheduler.scheduleJob(jobDetail, cronTrigger);
    }
    if (date == null) {
        return "任务添加失败";
    }
    //记录是谁添加了定时任务
    QrtzUser qrtzUser = new QrtzUser();
    qrtzUser.setUserId(loginUserId);
    qrtzUser.setGroupName(dto.getJobGroup());
    qrtzUser.setJobName(dto.getJobName());
    qrtzUser.setUpdatedTime(new Date());
    qrtzUser.setTriggerGroup(dto.getNewTriggerGroup());
    qrtzUser.setTriggerName(dto.getNewTriggerName());
    //插入记录
    qrtzUserMapper.insert(qrtzUser);
    log.info("任务添加成功,任务信息:{}", dto);
    return "任务添加成功";
}

解释:任务存在时,添加触发器,通过cronTrigger.forJob(任务名,任务组) 方法把触发器和任务联系起来,调用

scheduler.scheduleJob(cronTrigger);添加触发器

任务不存在时,添加任务,通过

scheduler.scheduleJob(jobDetail, cronTrigger);方法添加任务,jobDetail也可以写在cronTrigger里面,目的都是把信息传进去。

通过jobDataMap传递参数

注意:如果发生异常,异常会被lombok的

@SneakyThrows注解捕获,再通过SpringMVC的全局异常处理器能很好地处理

2、暂停功能:暂停触发器
/**
* 暂停定时任务
*
* @param triggerName
* @param triggerGroup
* @param loginUserId
*/
@Override
public void pauseSchedule(String triggerName, String triggerGroup, Integer loginUserId) {
try {
scheduler.pauseTrigger(new TriggerKey(triggerName, triggerGroup + “_” + loginUserId));
log.info(“触发器名:{},触发器组:{} ,暂停成功”, triggerName, triggerGroup);
} catch (SchedulerException e) {
log.error(“触发器名:{},触发器组:{},暂停异常,{}”, triggerName, triggerGroup, e);
}
}
解释:暂停触发器很简单就是调用下面的方法

scheduler.pauseTrigger(new TriggerKey(triggerName, triggerGroup + “_” + loginUserId));

如果是暂停任务的话调用scheduler.pauseJob(JobKey.jobKey());这个方法会暂停该任务下面的所有触发器。

3、恢复功能:跟暂停功能相反,可以通过

scheduler.resumeJob(JobKey.jobKey());方法恢复任务
/**
* 恢复定时任务
*
* @param triggerName
* @param triggerGroup
* @param loginUserId
/
@Override
public void resumeSchedule(String triggerName, String triggerGroup, Integer loginUserId) {
try {
scheduler.resumeTrigger(new TriggerKey(triggerName, triggerGroup + “_” + loginUserId));
log.info(“触发器名:{},触发器组:{} ,恢复成功”, triggerName, triggerGroup);
} catch (SchedulerException e) {
log.error(“触发器名:{},触发器组:{},恢复异常,{}”, triggerName, triggerGroup, e);
}
}
4、更新触发器功能:这个功能和前面有点不一样,说是替换触发器更合适,它是把旧触发器替换我新触发器,达到更新的功能
/
*
* 更新定时任务
*/
@SneakyThrows
@Override
public String updateSchedule(Integer loginUserId, ScheduleInfoDto dto) {
JobKey jobKey = new JobKey(dto.getJobName(), dto.getJobGroup());
if (ObjectUtil.isEmpty(loginUserId)) {
return “请先登录再操作,任务更新失败”;
}
//检查任务key是否存在
if (scheduler.checkExists(jobKey)) {
//老的触发器
Trigger oldTrigger = null;
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
//遍历任务的所有触发器,拿到老的触发器
for (int i = 0; i < triggers.size(); i++) {
Trigger trigger = triggers.get(i);
TriggerKey key = trigger.getKey();
String group = key.getGroup();
String name = key.getName();
if (StrUtil.equals(name, dto.getTriggerName()) && StrUtil.equals(group, dto.getTriggerGroup())) {
oldTrigger = trigger;
log.info(“成功拿到老触发器”);
break;
}
}
if (null == oldTrigger) {
return “老触发器不存在,更新失败”;
}
//获取前端传来的触发器组、名 ,如果没有则自动生成一个
String triggerName = dto.getNewTriggerName();
String triggerGroup = dto.getNewTriggerGroup();
if (StrUtil.isEmpty(triggerName)) {
triggerName = UUID.randomUUID().toString().replace(“-”, “”);
}
if (StrUtil.isEmpty(triggerGroup)) {
triggerGroup = UUID.randomUUID().toString().replace(“-”, “”);
}
//设置新的触发器
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerName, triggerGroup)
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(dto.getCronExpression()))
.withDescription(dto.getTriggerDescription())
.build();
//把老触发器换成新触发器
Date date = scheduler.rescheduleJob(oldTrigger.getKey(), cronTrigger);
// 如果触发器更新成功会返回一个date
if (null == date) {
return “任务更新失败”;
}
UpdateWrapper qrtzUserUpdateWrapper = new UpdateWrapper<>();
qrtzUserUpdateWrapper.eq(“user_id”, loginUserId).eq(“job_name”, dto.getJobName()).eq(“group_name”, dto.getJobGroup());
//把触发器替换成功的时间作为任务更新时间
qrtzUserUpdateWrapper.set(“updated_time”, date);
qrtzUserService.update(qrtzUserUpdateWrapper);
return “任务更新成功”;
} else {
return “任务不存在,不能执行更新操作”;
}
}
解释:通过

scheduler.rescheduleJob(oldTrigger.getKey(), cronTrigger);方法更新触发器,第一个参数是老触发器、第二个参数是新触发器,老触发器通过遍历任务下面所有触发器,比对触发器名字和组,拿到老触发器;新触发器通过

TriggerBuilder.newTrigger()…build(),这个建造方法获取

5、删除触发器,如果任务下面已经没有了触发器则删除任务
/**
* 删除定时任务
*
* @param triggerName
* @param triggerGroup
* @param loginUserId
*/
@Override
public void deleteSchedule(String triggerName, String triggerGroup, Integer loginUserId, String jobName, String jobGroup) {
try {
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup + “_” + loginUserId);
//删除触发器之前暂停触发器
scheduler.pauseTrigger(triggerKey);
//删除触发器
boolean res = scheduler.unscheduleJob(triggerKey);
//如果触发器删除失败
log.info(“定时任务删除结果 {}”, res);
if (!res) {
return;
}
QueryWrapper qw = new QueryWrapper<>();
qw.eq(“trigger_name”, triggerName);
qw.eq(“trigger_group”, triggerGroup);
qrtzUserMapper.delete(qw);
log.info(“触发器名:{},触发器组:{} ,删除成功”, triggerName, triggerGroup);
//如果任务下没有触发器,删除任务
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
if (triggersOfJob.size() == 0) {
scheduler.pauseJob(jobKey);
boolean r = scheduler.deleteJob(jobKey);
log.info(“任务名:{},任务组:{} ,删除结果 {}”, jobName, jobGroup, r);
}
} catch (SchedulerException e) {
log.error(“任务名:{},任务组:{},删除异常,{}”, triggerName, triggerGroup, e);
}
}
解释:删除触发器

scheduler.pauseTrigger(triggerKey);

scheduler.unscheduleJob(triggerKey);

删除任务

scheduler.pauseJob(jobKey);

scheduler.deleteJob(jobKey);

需要注意的是:删除之前,需要先暂停,不然删除会失败

6、查询所有任务所有触发器
/**
* 获取所有定时任务*
* QrtzUser中的任务组+userId = 真实的任务组
* 触发器组_userId = 真实的触发器组*
*
* @param loginUserId
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
@Override
@SneakyThrows
public List getAllSchedule(Integer loginUserId) {
if (loginUserId == null) {
log.info(“用户id为空”);
return null;
}
String userSuf = "
" + loginUserId;
log.info(“获取用户id = {} 的所有定时任务”, loginUserId);
List list = new ArrayList<>();
QueryWrapper qw = new QueryWrapper<>();
qw.eq(“user_id”, loginUserId);
List qrtzUsers = qrtzUserMapper.selectList(qw);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
qrtzUsers.forEach(qrtzUser -> {
String triggerGroup = qrtzUser.getTriggerGroup();
String triggerName = qrtzUser.getTriggerName();
String jobName = qrtzUser.getJobName();
String jobGroup = qrtzUser.getGroupName() + userSuf;
JobKey jobKey = new JobKey(jobName, jobGroup);
try {
//获取任务详情
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup + userSuf);
//获取触发器
Trigger trigger = scheduler.getTrigger(triggerKey);
ScheduleInfoDto scheduleInfoDto = new ScheduleInfoDto();
scheduleInfoDto.setJobClassName(jobDetail.getJobClass().toString());
scheduleInfoDto.setDescName(jobDetail.getDescription());
scheduleInfoDto.setJobGroup(qrtzUser.getGroupName());
scheduleInfoDto.setJobName(jobName);
User user = userService.getById(loginUserId);
//设置创建者
scheduleInfoDto.setUserName(user.getUsername());
if (ObjectUtil.isNotEmpty(qrtzUser.getCreatedTime())) {
scheduleInfoDto.setCreatedTime(simpleDateFormat.format(qrtzUser.getCreatedTime()));
}
if (ObjectUtil.isNotEmpty(qrtzUser.getUpdatedTime())) {
scheduleInfoDto.setUpdatedTime(simpleDateFormat.format(qrtzUser.getUpdatedTime()));
}
//获取触发器名
scheduleInfoDto.setTriggerName(trigger.getKey().getName());
//获取触发器组
scheduleInfoDto.setTriggerGroup(triggerGroup);
scheduleInfoDto.setTriggerDescription(trigger.getDescription());
try {
//获取任务状态
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
scheduleInfoDto.setTriggerState(triggerState.name());
//获取Cron表达式
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
scheduleInfoDto.setCronExpression(cronExpression);
scheduleInfoDto.setCronExpressionZH(CronExpParserUtil.translateToChinese(cronExpression));
}
} catch (SchedulerException e) {
e.printStackTrace();
}
list.add(scheduleInfoDto);

        } catch (SchedulerException e) {
            e.printStackTrace();
            return;
        }
    });
    return list;
}

解释:通过任务名和任务组,调用

scheduler.getJobDetail(jobKey);拿到任务信息,通过任务遍历任务下面所有触发器,就能拿到所有触发器信息,包括Cron表达式、状态、描述、名字、组名等。
如果你不知道任务名、任务组通过

GroupMatcher<JobKey> matchers =GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matchers);

ils/127304207

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值