在工作中出现了这样一个需求,用户可以制定多个任务,每个任务可以定时分配给多个人。任务的属性包括:任务巡检周期、任务工作日历、巡检人员。
这就需要我们开发一个动态的定时任务功能,而且这个定时任务是可以随时新增、更改、停止的。刚开始接到这个需求的时候我就直接说,我一两天开发不完QwQ,但其实这个功能一点也不难,只是我不知道还有ThreadPoolTaskScheduler这种简便的开发工具罢了,自己菜怨不得需求难。
但是一开始还是走了一些弯路的:
package cn.dxsc.safety.web.config;
import cn.dxsc.safety.common.util.ChinaHolidaysUtils;
import cn.dxsc.safety.common.util.StreamUtils;
import cn.dxsc.safety.common.util.StringUtils;
import cn.dxsc.safety.dao.mapper.TaskDistributionMapper;
import cn.dxsc.safety.dao.mapper.TroubleCheckTaskDistributionMapper;
import cn.dxsc.safety.entity.po.TaskDistribution;
import cn.dxsc.safety.entity.po.TroubleCheckTaskDistribution;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class AutoDistributionTask implements SchedulingConfigurer {
//这里有一些是我的业务代码,没有删
@Autowired
private TroubleCheckTaskDistributionMapper troubleCheckTaskDistributionMapper;
@Autowired
private TaskDistributionMapper taskDistributionMapper;
private List<TroubleCheckTaskDistribution> troubleCheckTaskDistributions;
/**
* 初始化时调用
* 目的:为动态定时任务赋予初始值
*/
@PostConstruct
public void doConstruct() {
this.troubleCheckTaskDistributions = troubleCheckTaskDistributionMapper.selectList(null);
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
for (TroubleCheckTaskDistribution troubleCheckTaskDistribution : troubleCheckTaskDistributions) {
//第一个动态定时任务
// 动态使用cron表达式设置循环间隔
taskRegistrar.addTriggerTask(() -> System.out.println("Current time: " + DateUtil.now()), triggerContext -> {
// 使用CronTrigger触发器,可动态修改cron表达式来操作循环规则
CronTrigger cronTrigger = new CronTrigger(troubleCheckTaskDistribution.getCron());
// //--------此处替换执行操作
// String workType = troubleCheckTaskDistribution.getWorkType();
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
// boolean publicHolidays = false;
// try {
// publicHolidays = ChinaHolidaysUtils.isPublicHolidays(simpleDateFormat.format(new Date()));
// } catch (IOException e) {
// e.printStackTrace();
// }
// //判断工作日类型
// if(new Date().after(troubleCheckTaskDistribution.getBeginTime()) && !StringUtils.isEmpty(workType))
// if ((workType.equals("法定工作日") && publicHolidays)||(workType.equals("非法定工作日") && !publicHolidays)||workType.equals("每天")){
// List<TaskDistribution> taskDistributions = taskDistributionMapper.selectList(new LambdaQueryWrapper<TaskDistribution>().eq(TaskDistribution::getTroubleCheckTaskDistributionId, troubleCheckTaskDistribution.getId()))
// .stream().filter(StreamUtils.distinctByKey(TaskDistribution::getCheckPeople)).collect(Collectors.toList());
// if(taskDistributions.size() != 0){
// TaskDistribution taskDistribution = new TaskDistribution();
// taskDistribution.setBeginTime(new Date());
// taskDistribution.setTroubleCheckTaskDistributionId(troubleCheckTaskDistribution.getId());
// taskDistribution.setCheckCompany(taskDistributions.get(0).getCheckCompany());
// taskDistributionMapper.insert(taskDistribution);
// }
// }
return cronTrigger.nextExecutionTime(triggerContext);
});
}
}
}
一开始百度到了这样一个方法,可以为动态定时任务赋予初始值,在doConstruct()里获取到了当前数据库里所有的任务的,然后加入到taskRegistrar里面去执行,获取每个任务生成的cron表达式,然后处理业务巴拉巴拉。然后系统一部署后我就不能在新增、修改、删除定时任务了,这怎么能行。
马上,第二种方法:
1、首先创建配置类
package cn.dxsc.safety.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
/**
* @author mrChen
* @date 2024/2/23 9:16
*/
@Configuration
public class ThreadPoolConfig {
public static ConcurrentHashMap<String, ScheduledFuture> cache = new ConcurrentHashMap<String, ScheduledFuture>();
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(100);
threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
threadPoolTaskScheduler.setAwaitTerminationSeconds(10);
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
return threadPoolTaskScheduler;
}
}
2、然后是一个任务添加、删除的统一处理
package cn.dxsc.safety.web.config;
import org.springframework.scheduling.SchedulingException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
/**
* @author mrChen
* @date 2024/2/22 21:39
*/
@Component
public class CronTask {
final
ThreadPoolTaskScheduler threadPoolTaskScheduler;
private Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<String, ScheduledFuture<?>>();
public CronTask(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
this.threadPoolTaskScheduler = threadPoolTaskScheduler;
}
/**
* 添加任务
*
* @param taskId
* @param triggerTask
*/
public void addTriggerTask(String taskId, TriggerTask triggerTask) {
if (taskFutures.containsKey(taskId)) {
throw new SchedulingException("the taskId[" + taskId + "] was added.");
}
ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger());
taskFutures.put(taskId, schedule);
}
/**
* 取消任务
*
* @param taskId
*/
public void cancelTriggerTask(String taskId) {
ScheduledFuture<?> future = taskFutures.get(taskId);
if (future != null) {
future.cancel(true);
}
taskFutures.remove(taskId);
}
}
3、最后是我的业务代码
@PutMapping("/update")
@ApiOperation("根据主键修改")
public R update(@RequestBody TroubleCheckTaskDistributionDto dto) {
TroubleCheckTaskDistribution h=new TroubleCheckTaskDistribution();
BeanUtils.copyProperties(dto, h);
String inspCycle = dto.getInspCycle();
String inspUnit = dto.getInspUnit();
Date beginTime = dto.getBeginTime();
int year = beginTime.getYear();
int month = beginTime.getMonth() + 1;
int day = beginTime.getDay() + 4;
int hours = beginTime.getHours();
int minutes = beginTime.getMinutes();
int seconds = beginTime.getSeconds();
//生成cron
String cron = "";
switch (inspUnit) {
case "小时":
cron = seconds+" "+minutes+" 0/" + inspCycle + " * * ?";
break;
case "天":
cron = seconds+" "+minutes+" "+hours+" */"+inspCycle+" * ?";//在这个表达式中,“001"表示任务每天的凌晨1点开始执行,"*/3"表示任务每隔三天执行一次,“*"表示任何月份都可以执行,“?”表示不考虑星期几的条件。
break;
case "月":
cron = seconds+" "+minutes+" "+hours+" "+day +" "+"1/"+inspCycle+" ?";
break;
case "年":
cron = seconds+" "+minutes+" "+hours+" "+day+" "+month+" ?";
break;
}
h.setCron(cron);
boolean b = iTroubleCheckTaskDistributionService.updateById(h);
if (taskHashMap.size() >= 50){
return R.fail("所设置的任务已经超标!");
}
if(this.taskHashMap.containsKey(h.getId())) {
cronTask.cancelTriggerTask(String.valueOf(h.getId()));
taskHashMap.remove(h.getId());
}
this.taskHashMap.put(h.getId(), cron);
TriggerTask triggerTask = new TriggerTask(
new Runnable() {
@Override
public void run() {
System.out.println("Current time: " + DateUtil.now() + "执行任务:" + h.getId());
//--------此处替换执行操作
String workType = h.getWorkType();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
boolean publicHolidays = false;
try {
publicHolidays = ChinaHolidaysUtils.isPublicHolidays(simpleDateFormat.format(new Date()));
} catch (IOException e) {
e.printStackTrace();
}
//判断工作日类型
if (new Date().after(h.getBeginTime()) && !StringUtils.isEmpty(workType))
if ((workType.equals("法定工作日") && publicHolidays) || (workType.equals("非法定工作日") && !publicHolidays) || workType.equals("每天")) {
// List<TaskDistribution> taskDistributions = taskDistributionMapper.selectList(new LambdaQueryWrapper<TaskDistribution>().eq(TaskDistribution::getTroubleCheckTaskDistributionId, h.getId()))
// .stream().filter(StreamUtils.distinctByKey(TaskDistribution::getCheckPeople)).collect(Collectors.toList());
List<TaskDistribution> taskDistributions = dto.getTaskDistributions();
if (taskDistributions.size() != 0) {
for (TaskDistribution taskDistribution : taskDistributions) {
taskDistribution.setBeginTime(new Date());
taskDistribution.setId(null);
taskDistributionMapper.insert(taskDistribution);
}
}
}
}
},
new CronTrigger(this.taskHashMap.get(h.getId()))
);
cronTask.addTriggerTask(String.valueOf(h.getId()), triggerTask);
return b ? R.ok() : R.fail();
}
一个集坚强与信心于一身的菇凉。