**
一:corn时间转换工具类
```java
package com.ypkj.provider.utils;
import com.ypkj.provider.cron.CronPatternConstant;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author cgz
* @version 1.0
* @date 2023/5/23 11:02
*/
public enum GenerateCronUtil {
/**
* 单例
*/
INSTANCE;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd HH:mm:ss");
private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
private final SimpleDateFormat weekFormat = new SimpleDateFormat("E");
public static void generateCronByPeriodAndTime(String formatTime) {
}
/**
* 根据执行周期和初次执行时间,生成cron表达式
*
* @param period 执行周期
* @param beginTime 初次执行时间
* @return cron表达式
*/
public String generateCronByPeriodAndTime(PeriodEnum period, String beginTime) {
Date parsedDate;
try {
if(period.name().equals("WEEKLY")){
parsedDate = simpleDateFormat.parse(beginTime);
}else{
parsedDate = dateFormat.parse(beginTime);
}
} catch (ParseException e) {
return "error";
}
String[] dateAndTime = beginTime.split(" ");
String date = dateAndTime[0];
String time = dateAndTime[1];
String[] splitDate = date.split("/");
String month = splitDate[0];
String day = splitDate[1];
String[] splitTime = time.split(":");
String hour = splitTime[0];
String minute = splitTime[1];
String second = splitTime[2];
String cron = "";
switch (period) {
case ONCE:
cron = String.format(CronPatternConstant.ONCE_CRON_PATTERN, second, minute, hour, day, month);
break;
case DAILY:
cron = String.format(CronPatternConstant.DAILY_CRON_PATTERN, second, minute, hour);
break;
case WEEKLY:
String week = weekFormat.format(parsedDate);
String weekCode = WeekEnum.nameOf(week).getCode();
cron = String.format(CronPatternConstant.WEEKLY_CRON_PATTERN, second, minute, hour, weekCode);
break;
case MONTHLY:
cron = String.format(CronPatternConstant.MONTHLY_CRON_PATTERN, second, minute, hour, day);
break;
default:
break;
}
return cron;
}
public static void main(String[] args) {
String time = "2023/05/30 12:10:20";
String weeklyCron = GenerateCronUtil.INSTANCE.generateCronByPeriodAndTime(PeriodEnum.WEEKLY, time);
String monthlyCron = GenerateCronUtil.INSTANCE.generateCronByPeriodAndTime(PeriodEnum.MONTHLY, time);
System.out.println("每周执行cron:" + weeklyCron);
}
}
package com.ypkj.provider.utils;
/**
* 执行周期枚举
* @author cgz
* @version 1.0
* @date 2023/5/23 10:59
*/
public enum PeriodEnum {
/**
* 执行一次
*/
ONCE,
/**
* 每天
*/
DAILY,
/**
* 每周
*/
WEEKLY,
/**
* 每月
*/
MONTHLY
}
package com.ypkj.provider.utils;
import com.google.common.base.Strings;
import com.ypkj.common.utils.StringUtils;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* * 星期枚举
* @author cgz
* @version 1.0
* @date 2023/5/23 11:00
*/
@Getter
public enum WeekEnum {
/**
* 周一
*/
Monday("星期一", "Mon"),
/**
* 周二
*/
Tuesday("星期二", "Tue"),
/**
* 周三
*/
Wednesday("星期三", "Wed"),
/**
* 周四
*/
Thursday("星期四", "Thu"),
/**
* 周五
*/
Friday("星期五", "Fri"),
/**
* 周六
*/
Saturday("星期六", "Sat"),
/**
* 周日
*/
Sunday("星期日", "Sun"),
;
private static final Map<String, WeekEnum> CODE_ROLE_MAP = new HashMap<>();
private static final Map<String, WeekEnum> NAME_ROLE_MAP = new HashMap<>();
static {
for (WeekEnum type : WeekEnum.values()) {
NAME_ROLE_MAP.put(type.name, type);
CODE_ROLE_MAP.put(type.code, type);
}
}
private final String name;
private final String code;
WeekEnum(final String name, final String code) {
this.name = name;
this.code = code;
}
/**
* to WeekEnum by code.
*
* @param code code
* @return WeekEnum
*/
public static WeekEnum codeOf(final String code) {
if (StringUtils.isBlank(code)) {
return Sunday;
}
WeekEnum matchType = CODE_ROLE_MAP.get(code);
return Objects.isNull(matchType) ? Sunday : matchType;
}
/**
* to WeekEnum by name.
*
* @param name name
* @return WeekEnum
*/
public static WeekEnum nameOf(final String name) {
if (Strings.isNullOrEmpty(name)) {
return Sunday;
}
WeekEnum matchType = NAME_ROLE_MAP.get(name);
return Objects.isNull(matchType) ? Sunday : matchType;
}
}
package com.ypkj.provider.cron;
/**
* cron表达式模板
* @author cgz
* @version 1.0
* @date 2023/5/23 11:03
*/
public interface CronPatternConstant {
/**
* 执行单次cron表达式模板
* eg: 59 59 23 1 12 ? 2022 (2022-12-01 23:59:59执行一次)
*/
String ONCE_CRON_PATTERN = "%s %s %s %s %s ?";
/**
* 每天执行cron表达式模板
* eg: 59 59 23 * * ? (每日23:59:59执行)
*/
String DAILY_CRON_PATTERN = "%s %s %s * * ?";
/**
* 每周执行cron表达式模板
* eg: 59 59 23 ? * Fri (每周五23:59:59执行)
*/
String WEEKLY_CRON_PATTERN = "%s %s %s ? * %s";
/**
* 每月执行cron表达式模板
* eg: 59 59 23 8 * ? (每月8号23:59:59执行)
*/
String MONTHLY_CRON_PATTERN = "%s %s %s %s * ?";
}
二:定时任务工具类
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
/**
* @ClassName: ScheduleUtils
* @Description: 定时任务类
* @author lixk
* @date 2017年8月16日 下午1:27:13
* @version [1.0, 2017年8月16日]
* @since version 1.0
*/
@Slf4j
public class ScheduleUtils {
// task集合。Task里面包含ScheduledFuture,ScheduledFuture是延时执行的关键类
private static final Map<Integer, Task> TASK_MANAGER = new HashMap<Integer, Task>();
// 定时器线程池
private static final ScheduledExecutorService EXECUTOR_POOL = Executors.newScheduledThreadPool(10);
// 定时任务队列
private static final BlockingQueue<Task> TASK_QUEUE = new LinkedBlockingQueue<Task>();
// 静态初始化方法。用于跑定时任务队列,队列里有任务就取出来执行。
static {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
@Override
public void run() {
while (true) {
try {
Task task = TASK_QUEUE.take();
// 任务有效,则执行任务
if (task.isEffective()) {
task.execute();
}
} catch (Exception e) {
log.error("定时任务执行异常:", e);
}
}
}
});
executor.shutdown();
}
/**
*
* @Title: add
* @Description: 添加动态定时任务
* @param JobDto
*/
public synchronized static void add(JobDto JobDto) throws Exception {
// 终结执行中的任务
cancel(JobDto.getId());
Task task = new Task(TASK_QUEUE, EXECUTOR_POOL, JobDto.getClassName(), JobDto.getMethodName(), JobDto.getCron(), JobDto);
TASK_MANAGER.put(JobDto.getId(), task);
// 将任务加入队列
TASK_QUEUE.put(task);
}
/**
*
* @Title: cancel
* @Description: 取消动态定时任务
* @param taskId
*/
public synchronized static void cancel(Integer taskId) {
if (taskId == null) {
return;
}
Task task = TASK_MANAGER.get(taskId);
if (task != null) {
// 关闭任务,停止任务线程
task.setEffective(false);
ScheduledFuture<?> future = task.getScheduledFuture();
if (future != null) {
future.cancel(true);
}
}
TASK_MANAGER.remove(taskId);
}
/**
*
* @ClassName: Task
* @Description: 任务内部类
* @author lixk
* @date 2017年8月16日 下午7:38:44
* @version [1.0, 2017年8月16日]
* @since version 1.0
*/
public static class Task {
private BlockingQueue<Task> queue; // 任务队列
private CronTrigger trigger; // cron触发器
private ScheduledExecutorService executor; // 定时器线程池
private Class<?> clazz; // 反射类名
private Object targetObject; // 反射对象
private Method method; // 反射方法
private Task self; // task对象自己
private JobDto jobDto; // 任务对象。里面可放自定义业务数据。
private ScheduledFuture<?> scheduledFuture; // task对象的future
private boolean effective = true; // task对象状态
public Task(BlockingQueue<Task> queue, ScheduledExecutorService executor,
String className, String methodName, String cron, JobDto jobDto) throws Exception {
this.queue = queue;
this.executor = executor;
this.trigger = new CronTrigger(cron);
this.clazz = Class.forName(className);
// this.targetObject = clazz.newInstance(); // 此处原本是反射获取的对象,由于获取到的对象里面的依赖注入为null,所以换成下面的getBean方式来获取对象
this.targetObject = SpringContextHolder.getBean(clazz);
// 调用目标方法。xxx.class为传入参数对象(可根据自己的业务进行调整)。
this.method = clazz.getDeclaredMethod(methodName, JobDto.class);
this.self = this;
this.jobDto = jobDto;
}
public void execute() throws Exception {
Date now = new Date();
// 感觉cron表达式没什么必要...,反正到这里都要转成delay(等待时间),后续应该可以优化成直接用执行时间来搞,研究时间有限,先这样
long delay = trigger.next(now).getTime() - now.getTime(); // 等待时间
// schedule(Runnable command, long delay, TimeUnit unit) 创建并执行在给定延迟后启用的一次性操作。
this.scheduledFuture = executor.schedule(new Runnable() {
@Override
public void run() {
try {
// 执行任务
log.warn("动态定时器执行任务,jobDto={}", JSON.toJSONString(jobDto));
method.invoke(targetObject, jobDto);
} catch (Exception e) {
log.error("定时任务执行异常:", e);
}
// 可以根据自己业务判断是否将任务重新加入队列
// finally {
// // 把当前任务加入队列
// try {
// queue.put(self);
// } catch (InterruptedException e) {
// logger.error("添加定时任务到队列异常:", e);
// }
// }
}
}, delay, TimeUnit.MILLISECONDS);
}
public ScheduledFuture<?> getScheduledFuture() {
return scheduledFuture;
}
public boolean isEffective() {
return effective;
}
public void setEffective(boolean effective) {
this.effective = effective;
}
}
}
/**************************************** cron表达式解析工具类 *******************************************/
/**
* Date sequence generator for a
* <a href="http://www.manpagez.com/man/5/crontab/">Crontab pattern</a>,
* allowing clients to specify a pattern that the sequence matches.
*
* <p>
* The pattern is a list of six single space-separated fields: representing
* second, minute, hour, day, month, weekday. Month and weekday names can be
* given as the first three letters of the English names.
*
* <p>
* Example patterns:
* <ul>
* <li>"0 0 * * * *" = the top of every hour of every day.</li>
* <li>"*/10 * * * * *" = every ten seconds.</li>
* <li>"0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day.</li>
* <li>"0 0 6,19 * * *" = 6:00 AM and 7:00 PM every day.</li>
* <li>"0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every
* day.</li>
* <li>"0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays</li>
* <li>"0 0 0 25 12 ?" = every Christmas Day at midnight</li>
* </ul>
*
* @author Dave Syer
* @author Juergen Hoeller
* @since 3.0
* @see CronTrigger
*/
class CronTrigger {
private final String expression;
private final TimeZone timeZone;
private final BitSet months = new BitSet(12);
private final BitSet daysOfMonth = new BitSet(31);
private final BitSet daysOfWeek = new BitSet(7);
private final BitSet hours = new BitSet(24);
private final BitSet minutes = new BitSet(60);
private final BitSet seconds = new BitSet(60);
/**
* Construct a {@link CronTrigger} from the pattern provided, using the
* default {@link TimeZone}.
*
* @param expression
* a space-separated list of time fields
* @throws IllegalArgumentException
* if the pattern cannot be parsed
* @see TimeZone#getDefault()
*/
public CronTrigger(String expression) {
this(expression, TimeZone.getDefault());
}
/**
* Construct a {@link CronTrigger} from the pattern provided, using the
* specified {@link TimeZone}.
*
* @param expression
* a space-separated list of time fields
* @param timeZone
* the TimeZone to use for generated trigger times
* @throws IllegalArgumentException
* if the pattern cannot be parsed
*/
public CronTrigger(String expression, TimeZone timeZone) {
this.expression = expression;
this.timeZone = timeZone;
parse(expression);
}
/**
* Return the cron pattern that this sequence generator has been built for.
*/
String getExpression() {
return this.expression;
}
/**
* Get the next {@link Date} in the sequence matching the Cron pattern and
* after the value provided. The return value will have a whole number of
* seconds, and will be after the input value.
*
* @param date
* a seed value
* @return the next value matching the pattern
*/
public Date next(Date date) {
/*
* The plan:
*
* 1 Start with whole second (rounding up if necessary)
*
* 2 If seconds match move on, otherwise find the next match: 2.1 If
* next match is in the next minute then roll forwards
*
* 3 If minute matches move on, otherwise find the next match 3.1 If
* next match is in the next hour then roll forwards 3.2 Reset the
* seconds and go to 2
*
* 4 If hour matches move on, otherwise find the next match 4.1 If next
* match is in the next day then roll forwards, 4.2 Reset the minutes
* and seconds and go to 2
*/
Calendar calendar = new GregorianCalendar();
calendar.setTimeZone(this.timeZone);
calendar.setTime(date);
// First, just reset the milliseconds and try to calculate from there...
calendar.set(Calendar.MILLISECOND, 0);
long originalTimestamp = calendar.getTimeInMillis();
doNext(calendar, calendar.get(Calendar.YEAR));
if (calendar.getTimeInMillis() == originalTimestamp) {
// We arrived at the original timestamp - round up to the next whole
// second and try again...
calendar.add(Calendar.SECOND, 1);
doNext(calendar, calendar.get(Calendar.YEAR));
}
return calendar.getTime();
}
private void doNext(Calendar calendar, int dot) {
List<Integer> resets = new ArrayList<Integer>();
int second = calendar.get(Calendar.SECOND);
List<Integer> emptyList = Collections.emptyList();
int updateSecond = findNext(this.seconds, second, calendar, Calendar.SECOND, Calendar.MINUTE, emptyList);
if (second == updateSecond) {
resets.add(Calendar.SECOND);
}
int minute = calendar.get(Calendar.MINUTE);
int updateMinute = findNext(this.minutes, minute, calendar, Calendar.MINUTE, Calendar.HOUR_OF_DAY, resets);
if (minute == updateMinute) {
resets.add(Calendar.MINUTE);
} else {
doNext(calendar, dot);
}
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int updateHour = findNext(this.hours, hour, calendar, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_WEEK, resets);
if (hour == updateHour) {
resets.add(Calendar.HOUR_OF_DAY);
} else {
doNext(calendar, dot);
}
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, resets);
if (dayOfMonth == updateDayOfMonth) {
resets.add(Calendar.DAY_OF_MONTH);
} else {
doNext(calendar, dot);
}
int month = calendar.get(Calendar.MONTH);
int updateMonth = findNext(this.months, month, calendar, Calendar.MONTH, Calendar.YEAR, resets);
if (month != updateMonth) {
if (calendar.get(Calendar.YEAR) - dot > 4) {
throw new IllegalArgumentException("Invalid cron expression \"" + this.expression + "\" led to runaway search for next trigger");
}
doNext(calendar, dot);
}
}
private int findNextDay(Calendar calendar, BitSet daysOfMonth, int dayOfMonth, BitSet daysOfWeek, int dayOfWeek, List<Integer> resets) {
int count = 0;
int max = 366;
// the DAY_OF_WEEK values in java.util.Calendar start with 1 (Sunday),
// but in the cron pattern, they start with 0, so we subtract 1 here
while ((!daysOfMonth.get(dayOfMonth) || !daysOfWeek.get(dayOfWeek - 1)) && count++ < max) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
reset(calendar, resets);
}
if (count >= max) {
throw new IllegalArgumentException("Overflow in day for expression \"" + this.expression + "\"");
}
return dayOfMonth;
}
/**
* Search the bits provided for the next set bit after the value provided,
* and reset the calendar.
*
* @param bits
* a {@link BitSet} representing the allowed values of the field
* @param value
* the current value of the field
* @param calendar
* the calendar to increment as we move through the bits
* @param field
* the field to increment in the calendar (@see {@link Calendar}
* for the static constants defining valid fields)
* @param lowerOrders
* the Calendar field ids that should be reset (i.e. the ones of
* lower significance than the field of interest)
* @return the value of the calendar field that is next in the sequence
*/
private int findNext(BitSet bits, int value, Calendar calendar, int field, int nextField, List<Integer> lowerOrders) {
int nextValue = bits.nextSetBit(value);
// roll over if needed
if (nextValue == -1) {
calendar.add(nextField, 1);
reset(calendar, Arrays.asList(field));
nextValue = bits.nextSetBit(0);
}
if (nextValue != value) {
calendar.set(field, nextValue);
reset(calendar, lowerOrders);
}
return nextValue;
}
/**
* Reset the calendar setting all the fields provided to zero.
*/
private void reset(Calendar calendar, List<Integer> fields) {
for (int field : fields) {
calendar.set(field, field == Calendar.DAY_OF_MONTH ? 1 : 0);
}
}
// Parsing logic invoked by the constructor
/**
* Parse the given pattern expression.
*/
private void parse(String expression) throws IllegalArgumentException {
String[] fields = expression.split(" ");
if (!areValidCronFields(fields)) {
throw new IllegalArgumentException(String.format("Cron expression must consist of 6 fields (found %d in \"%s\")", fields.length, expression));
}
setNumberHits(this.seconds, fields[0], 0, 60);
setNumberHits(this.minutes, fields[1], 0, 60);
setNumberHits(this.hours, fields[2], 0, 24);
setDaysOfMonth(this.daysOfMonth, fields[3]);
setMonths(this.months, fields[4]);
setDays(this.daysOfWeek, replaceOrdinals(fields[5], "SUN,MON,TUE,WED,THU,FRI,SAT"), 8);
if (this.daysOfWeek.get(7)) {
// Sunday can be represented as 0 or 7
this.daysOfWeek.set(0);
this.daysOfWeek.clear(7);
}
}
/**
* Replace the values in the comma-separated list (case insensitive) with
* their index in the list.
*
* @return a new String with the values from the list replaced
*/
private String replaceOrdinals(String value, String commaSeparatedList) {
String[] list = commaSeparatedList.split(",");
for (int i = 0; i < list.length; i++) {
String item = list[i].toUpperCase();
value = value.toUpperCase().replace(item, "" + i);
}
return value;
}
private void setDaysOfMonth(BitSet bits, String field) {
int max = 31;
// Days of month start with 1 (in Cron and Calendar) so add one
setDays(bits, field, max + 1);
// ... and remove it from the front
bits.clear(0);
}
private void setDays(BitSet bits, String field, int max) {
if (field.contains("?")) {
field = "*";
}
setNumberHits(bits, field, 0, max);
}
private void setMonths(BitSet bits, String value) {
int max = 12;
value = replaceOrdinals(value, "FOO,JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC");
BitSet months = new BitSet(13);
// Months start with 1 in Cron and 0 in Calendar, so push the values
// first into a longer bit set
setNumberHits(months, value, 1, max + 1);
// ... and then rotate it to the front of the months
for (int i = 1; i <= max; i++) {
if (months.get(i)) {
bits.set(i - 1);
}
}
}
private void setNumberHits(BitSet bits, String value, int min, int max) {
String[] fields = value.split(",");
for (String field : fields) {
if (!field.contains("/")) {
// Not an incrementer so it must be a range (possibly empty)
int[] range = getRange(field, min, max);
bits.set(range[0], range[1] + 1);
} else {
String[] split = field.split("/");
if (split.length > 2) {
throw new IllegalArgumentException("Incrementer has more than two fields: '" + field + "' in expression \"" + this.expression + "\"");
}
int[] range = getRange(split[0], min, max);
if (!split[0].contains("-")) {
range[1] = max - 1;
}
int delta = Integer.valueOf(split[1]);
if (delta <= 0) {
throw new IllegalArgumentException("Incrementer delta must be 1 or higher: '" + field + "' in expression \"" + this.expression + "\"");
}
for (int i = range[0]; i <= range[1]; i += delta) {
bits.set(i);
}
}
}
}
private int[] getRange(String field, int min, int max) {
int[] result = new int[2];
if (field.contains("*")) {
result[0] = min;
result[1] = max - 1;
return result;
}
if (!field.contains("-")) {
result[0] = result[1] = Integer.valueOf(field);
} else {
String[] split = field.split("-");
if (split.length > 2) {
throw new IllegalArgumentException("Range has more than two fields: '" + field + "' in expression \"" + this.expression + "\"");
}
result[0] = Integer.valueOf(split[0]);
result[1] = Integer.valueOf(split[1]);
}
if (result[0] >= max || result[1] >= max) {
throw new IllegalArgumentException("Range exceeds maximum (" + max + "): '" + field + "' in expression \"" + this.expression + "\"");
}
if (result[0] < min || result[1] < min) {
throw new IllegalArgumentException("Range less than minimum (" + min + "): '" + field + "' in expression \"" + this.expression + "\"");
}
if (result[0] > result[1]) {
throw new IllegalArgumentException("Invalid inverted range: '" + field + "' in expression \"" + this.expression + "\"");
}
return result;
}
/**
* Determine whether the specified expression represents a valid cron
* pattern.
* <p>
* Specifically, this method verifies that the expression contains six
* fields separated by single spaces.
*
* @param expression
* the expression to evaluate
* @return {@code true} if the given expression is a valid cron expression
* @since 4.3
*/
public static boolean isValidExpression(String expression) {
String[] fields = expression.split(" ");
return areValidCronFields(fields);
}
private static boolean areValidCronFields(String[] fields) {
return (fields != null && fields.length == 6);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CronTrigger)) {
return false;
}
CronTrigger otherCron = (CronTrigger) other;
return (this.months.equals(otherCron.months) && this.daysOfMonth.equals(otherCron.daysOfMonth) && this.daysOfWeek.equals(otherCron.daysOfWeek) && this.hours.equals(otherCron.hours) && this.minutes.equals(otherCron.minutes) && this.seconds.equals(otherCron.seconds));
}
@Override
public int hashCode() {
return (17 * this.months.hashCode() + 29 * this.daysOfMonth.hashCode() + 37 * this.daysOfWeek.hashCode() + 41 * this.hours.hashCode() + 53 * this.minutes.hashCode() + 61 * this.seconds.hashCode());
}
@Override
public String toString() {
return getClass().getSimpleName() + ": " + this.expression;
}
public static void main(String[] args) {
CronTrigger cron = new CronTrigger("0 * * 7 * *");
System.out.println(cron.next(new Date()));
}
}
**
三:定时任务具体代码实现
/**
* 主键id
*/
@ApiModelProperty(value = "主键id")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 定时任务名称
*/
@ApiModelProperty(value = "定时任务名称")
private String jobName;
@ApiModelProperty(value = "会议定时任务类型 1更改为会议进行中 2更改会议为已结束")
private int jobType;
/**
* 会议id
*/
@ApiModelProperty(value = "业务id")
private Integer bussinessId;
/**
* 定时任务类名
*/
@ApiModelProperty(value = "定时任务类名")
private String className;
/**
* 定时任务方法名
*/
@ApiModelProperty(value = "定时任务方法名")
private String methodName;
/**
* 定时任务cron表达式
*/
@ApiModelProperty(value = "定时任务cron表达式")
private String cron;
/**
* 定时任务执行时间
*/
@ApiModelProperty(value = "定时任务执行时间")
private Date executeTime;
/**
* 创建人
*/
@ApiModelProperty(value = "创建人")
private String createId;
/**
* 修改人
*/
@ApiModelProperty(value = "修改人")
private String modifyId;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
/**
* 修改时间
*/
@ApiModelProperty(value = "修改时间")
private Date gmtModify;
/**
*/
@ApiModelProperty(value = "删除标志位:00存在,01删除")
private String delete_flag;
动态定时器接口
/**
* 定时任务初始化方法,将数据库的定时任务加入线程
*/
void init();
/**
* 会议创建时,新增动态定时任务,到点把会议状态改为进行中
* @param projectMeetingId
*/
void addDynamicTask(Integer bussinessId);
/**
* 修改会议时候,更改定时任务的时间
* @param
*/
void updateDynamicTask(Integer bussinessId);
/**
*
* @param projectMeetingId
*/
void deleteByMeetingId(Integer bussinessId);
/**
*
* @param taskId
*/
void deleteByTaskId(Integer taskId);
@Data
public class JobDto {
/**
* 定时任务id
*/
private Integer id;
/**
* 定时任务名称
*/
private String jobName;
/**
* 自定义业务id
*/
private Integer businessId;
/**
* 定时任务类名
*/
private String className;
/**
* 定时任务方法名
*/
private String methodName;
/**
* 定时任务cron表达式
*/
private String cron;
/**
* 定时任务状态 1、启用;2、停用
*/
private Stirng delete_flag;
}
*/
//编辑会议时候更改定时任务
@Override
public void updateDynamicTask(Integer bussinessId) {
log.info("项目会议更新定时任务start,bussinessId={}", JSON.toJSONString(bussinessId));
if (Objects.isNull(bussinessId)) {
log.error("更新定时任务失败,项目会议为空");
}
//先查询定时任务业务数据
//查询定时任务
//判断定时任务是否存在,如果存在修改定时任务时间,不存在则添加定时任务往数据,并且把定时任务添加到线程任务重
try {
} catch (Exception e) {
e.printStackTrace();
}
log.info("项目会议更新定时任务end");
}
@Override
public void addDynamicTask(Integer businessId) {
log.info("项目会议新增定时任务start,businessId={}", JSON.toJSONString(businessId));
//先查询业务数据
if (Objects.isNull(businessId)) {
log.error("新增定时任务失败,项目会议为空");
}
//新增定时任务
try {
//1.添加定时任务
//2.把定时任务添加到线程中
} catch (Exception e) {
e.printStackTrace();
}
log.info("项目会议新增定时任务end");
}
//添加定时任务,把会议从未开始改成进行中
public void addMeetingChangeStatusDynamicTask(Business business) throws Exception {
//定时任务执行时间。
Date executeTime = business.getStartTime();
if (executeTime.getTime() <= System.currentTimeMillis()) {
log.error("addMeetingChangeDynamicTask,当前系统时间大于等于任务执行时间");
return;
}
String cron = CronUtils.getCron(executeTime);
// 数据库新增【更改会议状态】定时任务
ProjectMeetingDynamicTask task = new ProjectMeetingDynamicTask();
task.setProjectMeetingId(projectMeeting.getId());
task.setStatus((byte) 1);
task.setCreateId(userInfoComponent.getCurrentUserId());
task.setGmtCreate(new Date());
task.setJobName("定时任务名"));//这个随便取
task.setClassName("类路径")//比如:com.test.DynamicTaskServiceImpl
task.setMethodName("方法路径");//定义方法比如:changeMeetingStatus。定时器启动的时候会根据反射去执行该方法
task.setProjectMeetingJobType("01");
task.setCron(cron);
task.setExecuteTime(executeTime);
save(task);
// 线程新增定时任务
addJobByTask(task);
}
/**
* @Title: init
* @Description: 项目启动时,将数据库的定时任务加入线程
*/
@Override
@Async
public void init() {
// 获取定时任务列表
List<MeetingDynamicTask> dynamicTaskList = MeetingDynamicTaskMapper
.selectList(new LambdaQueryWrapper<MeetingDynamicTask>()
.eq(ProjectMeetingDynamicTask::getStatus, 1)
// 任务执行时间大于等于当前系统时间
.ge(MeetingDynamicTask::getExecuteTime, new Date()));
new Thread(() -> {
for (MeetingDynamicTask dynamicTask : dynamicTaskList) {
log.info("定时任务初始化,dynamicTask={}", JSON.toJSONString(dynamicTask));
JobDto jobDto = new JobDto();
BeanUtils.copyProperties(dynamicTask, jobDto);
jobDto.setBusinessId(bussinessId);
try {
ScheduleUtils.add(jobDto);
} catch (Exception e) {
log.error("定时任务初始化异常", e);
}
}
}).start();
}
/**
* @param task (往数据库添加定时时间,顺便添加定时任务)
* @throws Exception
*/
private void addJobByTask(MeetingDynamicTask task) throws Exception {
JobDto jobDto = new JobDto();
BeanUtils.copyProperties(task, jobDto);
jobDto.setBusinessId(task.getProjectMeetingId());
ScheduleUtils.add(jobDto);
}
/**
* @param task(往数据库修改定时时间,顺便修改定时任务)
* @throws Exception
*/
private void updateJobByTask(MeetingDynamicTask task) throws Exception {
//取消任务
ScheduleUtils.cancel(task.getId());
//新增任务
JobDto jobDto = new JobDto();
BeanUtils.copyProperties(task, jobDto);
jobDto.setBusinessId(task.getProjectMeetingId());
ScheduleUtils.add(jobDto);
}
/**
* 根据业务id删除定时任务
*
* @param projectMeetingId
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteByMeetingId(Integer businessId) {
log.info("项目会议删除定时任务start,projectMeetingId={}", JSON.toJSONString(businessId));
// 获取定时任务
List<MeetingDynamicTask> taskList = MeetingDynamicTaskMapper.selectList(new LambdaQueryWrapper<MeetingDynamicTask>()
.eq(MeetingDynamicTask::businessId, businessId).eq(MeetingDynamicTask::getStatus, 1));
taskList.forEach(task -> {
// 数据库停用定时任务
task.setStatus("01");
MeetingDynamicTaskMapper.updateById(task);
// 线程取消定时任务
ScheduleUtils.cancel(task.getId());
});
log.info("项目会议删除定时任务end");
}
/**
* 映射方法到定时任务中
*
* @param jobDto
*/
public void changeMeetingStatus(JobDto jobDto) {
log.info("方法changeMeetingStatus。start,jobDto={}", JSON.toJSONString(jobDto));
Bussiness bussiness= bussinessMapper.selectById(jobDto.getBusinessId());
if (Objects.isNull(projectMeeting)) {
log.error("changeMeetingStatus失败,项目会议为空");
}
bussiness.setMeetingStatus((byte) 1);
bussiness.setModifyId("系统自动");
bussiness.setGmtModify(new Date());
bussiness.updateById(businessId);
deleteByTaskId(jobDto.getId());
}
四:开机自启动
/**
* @author cgz
* @version 1.0
* @date 2023/6/9 15:55
* 初始化定时任务防止重启服务,定时任务失效
*/
@Component
@Slf4j
public class ApplicationTask implements ApplicationRunner {
@Autowired
private IScPlanDynamicTaskService iScPlanDynamicTaskService;
@Override
public void run(ApplicationArguments args) throws Exception {
iScPlanDynamicTaskService.init();
log.info("初始化定时任务");
}
}
版权声明:本文为m0_47944994原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。