最新公众号管理平台后端
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4" />
|
||||
41
joju-framework/joju-spring-boot-starter-job/pom.xml
Normal file
41
joju-framework/joju-spring-boot-starter-job/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?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>
|
||||
<groupId>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>joju-spring-boot-starter-job</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>任务拓展
|
||||
1. 定时任务,基于 Quartz 拓展
|
||||
2. 异步任务,基于 Spring Async 拓展
|
||||
</description>
|
||||
<url>https://www.jojubanking.com</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.jojubanking.boot.framework.quartz.config;
|
||||
|
||||
import com.alibaba.ttl.TtlRunnable;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
/**
|
||||
* 异步任务 Configuration
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class JojuAsyncAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor threadPoolTaskExecutorBeanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (!(bean instanceof ThreadPoolTaskExecutor)) {
|
||||
return bean;
|
||||
}
|
||||
// 修改提交的任务,接入 TransmittableThreadLocal
|
||||
ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean;
|
||||
executor.setTaskDecorator(TtlRunnable::get);
|
||||
return executor;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.jojubanking.boot.framework.quartz.config;
|
||||
|
||||
import com.jojubanking.boot.framework.quartz.core.scheduler.SchedulerManager;
|
||||
import org.quartz.Scheduler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* 定时任务 Configuration
|
||||
*/
|
||||
@Configuration
|
||||
@EnableScheduling // 开启 Spring 自带的定时任务
|
||||
public class JojuQuartzAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public SchedulerManager schedulerManager(Scheduler scheduler) {
|
||||
return new SchedulerManager(scheduler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.jojubanking.boot.framework.quartz.core.enums;
|
||||
|
||||
/**
|
||||
* Quartz Job Data 的 key 枚举
|
||||
*/
|
||||
public enum JobDataKeyEnum {
|
||||
|
||||
JOB_ID,
|
||||
JOB_HANDLER_NAME,
|
||||
JOB_HANDLER_PARAM,
|
||||
JOB_RETRY_COUNT, // 最大重试次数
|
||||
JOB_RETRY_INTERVAL, // 每次重试间隔
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.jojubanking.boot.framework.quartz.core.handler;
|
||||
|
||||
/**
|
||||
* 任务处理器
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public interface JobHandler {
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*
|
||||
* @param param 参数
|
||||
* @return 结果
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
String execute(String param) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.jojubanking.boot.framework.quartz.core.handler;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import com.jojubanking.boot.framework.quartz.core.enums.JobDataKeyEnum;
|
||||
import com.jojubanking.boot.framework.quartz.core.service.JobLogFrameworkService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.quartz.PersistJobDataAfterExecution;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.scheduling.quartz.QuartzJobBean;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage;
|
||||
import static com.jojubanking.boot.framework.common.util.date.DateUtils.diff;
|
||||
|
||||
/**
|
||||
* 基础 Job 调用者,负责调用 {@link JobHandler#execute(String)} 执行任务
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@DisallowConcurrentExecution
|
||||
@PersistJobDataAfterExecution
|
||||
@Slf4j
|
||||
public class JobHandlerInvoker extends QuartzJobBean {
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Resource
|
||||
private JobLogFrameworkService jobLogFrameworkService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext executionContext) throws JobExecutionException {
|
||||
// 第一步,获得 Job 数据
|
||||
Long jobId = executionContext.getMergedJobDataMap().getLong(JobDataKeyEnum.JOB_ID.name());
|
||||
String jobHandlerName = executionContext.getMergedJobDataMap().getString(JobDataKeyEnum.JOB_HANDLER_NAME.name());
|
||||
String jobHandlerParam = executionContext.getMergedJobDataMap().getString(JobDataKeyEnum.JOB_HANDLER_PARAM.name());
|
||||
int refireCount = executionContext.getRefireCount();
|
||||
int retryCount = (Integer) executionContext.getMergedJobDataMap().getOrDefault(JobDataKeyEnum.JOB_RETRY_COUNT.name(), 0);
|
||||
int retryInterval = (Integer) executionContext.getMergedJobDataMap().getOrDefault(JobDataKeyEnum.JOB_RETRY_INTERVAL.name(), 0);
|
||||
|
||||
// 第二步,执行任务
|
||||
Long jobLogId = null;
|
||||
Date startTime = new Date();
|
||||
String data = null;
|
||||
Throwable exception = null;
|
||||
try {
|
||||
// 记录 Job 日志(初始)
|
||||
jobLogId = jobLogFrameworkService.createJobLog(jobId, startTime, jobHandlerName, jobHandlerParam, refireCount + 1);
|
||||
// 执行任务
|
||||
data = this.executeInternal(jobHandlerName, jobHandlerParam);
|
||||
} catch (Throwable ex) {
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
// 第三步,记录执行日志
|
||||
this.updateJobLogResultAsync(jobLogId, startTime, data, exception, executionContext);
|
||||
|
||||
// 第四步,处理有异常的情况
|
||||
handleException(exception, refireCount, retryCount, retryInterval);
|
||||
}
|
||||
|
||||
private String executeInternal(String jobHandlerName, String jobHandlerParam) throws Exception {
|
||||
// 获得 JobHandler 对象
|
||||
JobHandler jobHandler = applicationContext.getBean(jobHandlerName, JobHandler.class);
|
||||
Assert.notNull(jobHandler, "JobHandler 不会为空");
|
||||
// 执行任务
|
||||
return jobHandler.execute(jobHandlerParam);
|
||||
}
|
||||
|
||||
private void updateJobLogResultAsync(Long jobLogId, Date startTime, String data, Throwable exception,
|
||||
JobExecutionContext executionContext) {
|
||||
Date endTime = new Date();
|
||||
// 处理是否成功
|
||||
boolean success = exception == null;
|
||||
if (!success) {
|
||||
data = getRootCauseMessage(exception);
|
||||
}
|
||||
// 更新日志
|
||||
try {
|
||||
jobLogFrameworkService.updateJobLogResultAsync(jobLogId, endTime, (int) diff(endTime, startTime), success, data);
|
||||
} catch (Exception ex) {
|
||||
log.error("[executeInternal][Job({}) logId({}) 记录执行日志失败({}/{})]",
|
||||
executionContext.getJobDetail().getKey(), jobLogId, success, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleException(Throwable exception,
|
||||
int refireCount, int retryCount, int retryInterval) throws JobExecutionException {
|
||||
// 如果有异常,则进行重试
|
||||
if (exception == null) {
|
||||
return;
|
||||
}
|
||||
// 情况一:如果到达重试上限,则直接抛出异常即可
|
||||
if (refireCount >= retryCount) {
|
||||
throw new JobExecutionException(exception);
|
||||
}
|
||||
|
||||
// 情况二:如果未到达重试上限,则 sleep 一定间隔时间,然后重试
|
||||
// 这里使用 sleep 来实现,主要还是希望实现比较简单。因为,同一时间,不会存在大量失败的 Job。
|
||||
if (retryInterval > 0) {
|
||||
ThreadUtil.sleep(retryInterval);
|
||||
}
|
||||
// 第二个参数,refireImmediately = true,表示立即重试
|
||||
throw new JobExecutionException(exception, true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.jojubanking.boot.framework.quartz.core.scheduler;
|
||||
|
||||
import com.jojubanking.boot.framework.quartz.core.enums.JobDataKeyEnum;
|
||||
import com.jojubanking.boot.framework.quartz.core.handler.JobHandlerInvoker;
|
||||
import org.quartz.*;
|
||||
|
||||
/**
|
||||
* {@link org.quartz.Scheduler} 的管理器,负责创建任务
|
||||
*
|
||||
* 考虑到实现的简洁性,我们使用 jobHandlerName 作为唯一标识,即:
|
||||
* 1. Job 的 {@link JobDetail#getKey()}
|
||||
* 2. Trigger 的 {@link Trigger#getKey()}
|
||||
*
|
||||
* 另外,jobHandlerName 对应到 Spring Bean 的名字,直接调用
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class SchedulerManager {
|
||||
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public SchedulerManager(Scheduler scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 Job 到 Quartz 中
|
||||
*
|
||||
* @param jobId 任务编号
|
||||
* @param jobHandlerName 任务处理器的名字
|
||||
* @param jobHandlerParam 任务处理器的参数
|
||||
* @param cronExpression CRON 表达式
|
||||
* @param retryCount 重试次数
|
||||
* @param retryInterval 重试间隔
|
||||
* @throws SchedulerException 添加异常
|
||||
*/
|
||||
public void addJob(Long jobId, String jobHandlerName, String jobHandlerParam, String cronExpression,
|
||||
Integer retryCount, Integer retryInterval)
|
||||
throws SchedulerException {
|
||||
// 创建 JobDetail 对象
|
||||
JobDetail jobDetail = JobBuilder.newJob(JobHandlerInvoker.class)
|
||||
.usingJobData(JobDataKeyEnum.JOB_ID.name(), jobId)
|
||||
.usingJobData(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName)
|
||||
.withIdentity(jobHandlerName).build();
|
||||
// 创建 Trigger 对象
|
||||
Trigger trigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);
|
||||
// 新增调度
|
||||
scheduler.scheduleJob(jobDetail, trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 Job 到 Quartz
|
||||
*
|
||||
* @param jobHandlerName 任务处理器的名字
|
||||
* @param jobHandlerParam 任务处理器的参数
|
||||
* @param cronExpression CRON 表达式
|
||||
* @param retryCount 重试次数
|
||||
* @param retryInterval 重试间隔
|
||||
* @throws SchedulerException 更新异常
|
||||
*/
|
||||
public void updateJob(String jobHandlerName, String jobHandlerParam, String cronExpression,
|
||||
Integer retryCount, Integer retryInterval)
|
||||
throws SchedulerException {
|
||||
// 创建新 Trigger 对象
|
||||
Trigger newTrigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);
|
||||
// 修改调度
|
||||
scheduler.rescheduleJob(new TriggerKey(jobHandlerName), newTrigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 Quartz 中的 Job
|
||||
*
|
||||
* @param jobHandlerName 任务处理器的名字
|
||||
* @throws SchedulerException 删除异常
|
||||
*/
|
||||
public void deleteJob(String jobHandlerName) throws SchedulerException {
|
||||
scheduler.deleteJob(new JobKey(jobHandlerName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停 Quartz 中的 Job
|
||||
*
|
||||
* @param jobHandlerName 任务处理器的名字
|
||||
* @throws SchedulerException 暂停异常
|
||||
*/
|
||||
public void pauseJob(String jobHandlerName) throws SchedulerException {
|
||||
scheduler.pauseJob(new JobKey(jobHandlerName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动 Quartz 中的 Job
|
||||
*
|
||||
* @param jobHandlerName 任务处理器的名字
|
||||
* @throws SchedulerException 启动异常
|
||||
*/
|
||||
public void resumeJob(String jobHandlerName) throws SchedulerException {
|
||||
scheduler.resumeJob(new JobKey(jobHandlerName));
|
||||
scheduler.resumeTrigger(new TriggerKey(jobHandlerName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即触发一次 Quartz 中的 Job
|
||||
*
|
||||
* @param jobId 任务编号
|
||||
* @param jobHandlerName 任务处理器的名字
|
||||
* @param jobHandlerParam 任务处理器的参数
|
||||
* @throws SchedulerException 触发异常
|
||||
*/
|
||||
public void triggerJob(Long jobId, String jobHandlerName, String jobHandlerParam)
|
||||
throws SchedulerException {
|
||||
JobDataMap data = new JobDataMap(); // 无需重试,所以不设置 retryCount 和 retryInterval
|
||||
data.put(JobDataKeyEnum.JOB_ID.name(), jobId);
|
||||
data.put(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName);
|
||||
data.put(JobDataKeyEnum.JOB_HANDLER_PARAM.name(), jobHandlerParam);
|
||||
// 触发任务
|
||||
scheduler.triggerJob(new JobKey(jobHandlerName), data);
|
||||
}
|
||||
|
||||
private Trigger buildTrigger(String jobHandlerName, String jobHandlerParam, String cronExpression,
|
||||
Integer retryCount, Integer retryInterval) {
|
||||
return TriggerBuilder.newTrigger()
|
||||
.withIdentity(jobHandlerName)
|
||||
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
|
||||
.usingJobData(JobDataKeyEnum.JOB_HANDLER_PARAM.name(), jobHandlerParam)
|
||||
.usingJobData(JobDataKeyEnum.JOB_RETRY_COUNT.name(), retryCount)
|
||||
.usingJobData(JobDataKeyEnum.JOB_RETRY_INTERVAL.name(), retryInterval)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.jojubanking.boot.framework.quartz.core.service;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Job 日志 Framework Service 接口
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public interface JobLogFrameworkService {
|
||||
|
||||
/**
|
||||
* 创建 Job 日志
|
||||
*
|
||||
* @param jobId 任务编号
|
||||
* @param beginTime 开始时间
|
||||
* @param jobHandlerName Job 处理器的名字
|
||||
* @param jobHandlerParam Job 处理器的参数
|
||||
* @param executeIndex 第几次执行
|
||||
* @return Job 日志的编号
|
||||
*/
|
||||
Long createJobLog(@NotNull(message = "任务编号不能为空") Long jobId,
|
||||
@NotNull(message = "开始时间") Date beginTime,
|
||||
@NotEmpty(message = "Job 处理器的名字不能为空") String jobHandlerName,
|
||||
String jobHandlerParam,
|
||||
@NotNull(message = "第几次执行不能为空") Integer executeIndex);
|
||||
|
||||
/**
|
||||
* 更新 Job 日志的执行结果
|
||||
*
|
||||
* @param logId 日志编号
|
||||
* @param endTime 结束时间。因为是异步,避免记录时间不准去
|
||||
* @param duration 运行时长,单位:毫秒
|
||||
* @param success 是否成功
|
||||
* @param result 成功数据
|
||||
*/
|
||||
void updateJobLogResultAsync(@NotNull(message = "日志编号不能为空") Long logId,
|
||||
@NotNull(message = "结束时间不能为空") Date endTime,
|
||||
@NotNull(message = "运行时长不能为空") Integer duration,
|
||||
boolean success, String result);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.jojubanking.boot.framework.quartz.core.util;
|
||||
|
||||
import org.quartz.CronExpression;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Quartz Cron 表达式的工具类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class CronUtils {
|
||||
|
||||
/**
|
||||
* 校验 CRON 表达式是否有效
|
||||
*
|
||||
* @param cronExpression CRON 表达式
|
||||
* @return 是否有效
|
||||
*/
|
||||
public static boolean isValid(String cronExpression) {
|
||||
return CronExpression.isValidExpression(cronExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于 CRON 表达式,获得下 n 个满足执行的时间
|
||||
*
|
||||
* @param cronExpression CRON 表达式
|
||||
* @param n 数量
|
||||
* @return 满足条件的执行时间
|
||||
*/
|
||||
public static List<Date> getNextTimes(String cronExpression, int n) {
|
||||
// 获得 CronExpression 对象
|
||||
CronExpression cron;
|
||||
try {
|
||||
cron = new CronExpression(cronExpression);
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
// 从当前开始计算,n 个满足条件的
|
||||
Date now = new Date();
|
||||
List<Date> nextTimes = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
Date nextTime = cron.getNextValidTimeAfter(now);
|
||||
nextTimes.add(nextTime);
|
||||
// 切换现在,为下一个触发时间;
|
||||
now = nextTime;
|
||||
}
|
||||
return nextTimes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 1. 定时任务,采用 Quartz 实现进程内的任务执行。
|
||||
* 考虑到高可用,使用 Quartz 自带的 MySQL 集群方案。
|
||||
*
|
||||
* 2. 异步任务,采用 Spring Async 异步执行。
|
||||
*/
|
||||
package com.jojubanking.boot.framework.quartz;
|
||||
@@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.jojubanking.boot.framework.quartz.config.JojuQuartzAutoConfiguration,\
|
||||
com.jojubanking.boot.framework.quartz.config.JojuAsyncAutoConfiguration
|
||||
@@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.jojubanking.boot.framework.quartz.config.JojuQuartzAutoConfiguration,\
|
||||
com.jojubanking.boot.framework.quartz.config.JojuAsyncAutoConfiguration
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
#Generated by Maven
|
||||
#Wed Jan 14 17:48:30 CST 2026
|
||||
version=2.0.0-beta
|
||||
groupId=com.jojubanking.boot
|
||||
artifactId=joju-spring-boot-starter-job
|
||||
@@ -0,0 +1,9 @@
|
||||
com\jojubanking\boot\framework\quartz\core\scheduler\SchedulerManager.class
|
||||
com\jojubanking\boot\framework\quartz\core\service\JobLogFrameworkService.class
|
||||
com\jojubanking\boot\framework\quartz\config\JojuAsyncAutoConfiguration$1.class
|
||||
com\jojubanking\boot\framework\quartz\core\util\CronUtils.class
|
||||
com\jojubanking\boot\framework\quartz\core\handler\JobHandlerInvoker.class
|
||||
com\jojubanking\boot\framework\quartz\core\handler\JobHandler.class
|
||||
com\jojubanking\boot\framework\quartz\config\JojuAsyncAutoConfiguration.class
|
||||
com\jojubanking\boot\framework\quartz\config\JojuQuartzAutoConfiguration.class
|
||||
com\jojubanking\boot\framework\quartz\core\enums\JobDataKeyEnum.class
|
||||
@@ -0,0 +1,9 @@
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\core\handler\JobHandlerInvoker.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\config\JojuQuartzAutoConfiguration.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\package-info.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\core\enums\JobDataKeyEnum.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\core\scheduler\SchedulerManager.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\core\service\JobLogFrameworkService.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\config\JojuAsyncAutoConfiguration.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\core\util\CronUtils.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-job\src\main\java\com\jojubanking\boot\framework\quartz\core\handler\JobHandler.java
|
||||
@@ -0,0 +1 @@
|
||||
<http://www.iocoder.cn/Spring-Boot/Job/?joju>
|
||||
@@ -0,0 +1 @@
|
||||
<http://www.iocoder.cn/Spring-Boot/Async-Job/?joju>
|
||||
Reference in New Issue
Block a user