Merge branch '20260204'
# Conflicts: # .idea/compiler.xml # .idea/vcs.xml # lidee-admin/src/main/resources/application-local.yaml # lidee-admin/target/classes/application-local.yaml # lidee-admin/target/lidee-admin.jar # lidee-admin/target/lidee-admin.jar.original # lidee-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-core/src/main/java/com/lideeyunji/core/framework/entity/ReportFieldEntity.java # lidee-core/src/main/java/com/lideeyunji/core/framework/service/impl/ReportServiceImpl.java # lidee-core/target/classes/com/lideeyunji/core/framework/entity/ReportFieldEntity.class # lidee-core/target/classes/com/lideeyunji/core/framework/service/impl/ReportServiceImpl.class # lidee-core/target/lidee-core-2.2.4.jar # lidee-core/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-module/lidee-module-api/target/lidee-module-api-2.2.4.jar # lidee-module/lidee-module-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-module/lidee-module-biz/target/lidee-module-biz-2.2.4.jar # lidee-module/lidee-module-biz/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-service/lidee-service-infra-api/target/lidee-service-infra-api-2.2.4.jar # lidee-service/lidee-service-infra-biz/target/lidee-service-infra-biz-2.2.4.jar # lidee-service/lidee-service-system-api/target/lidee-service-system-api-2.2.4.jar # lidee-service/lidee-service-system-biz/target/lidee-service-system-biz-2.2.4.jar # lidee-tool/tool-common/target/tool-common-2.2.4.jar # lidee-tool/tool-spring-boot-starter-ai/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-tool/tool-spring-boot-starter-ai/target/tool-spring-boot-starter-ai-2.2.4.jar # lidee-tool/tool-spring-boot-starter-captcha/target/tool-spring-boot-starter-captcha-2.2.4.jar # lidee-tool/tool-spring-boot-starter-dict/target/tool-spring-boot-starter-dict-2.2.4.jar # lidee-tool/tool-spring-boot-starter-excel/target/tool-spring-boot-starter-excel-2.2.4.jar # lidee-tool/tool-spring-boot-starter-exception/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-tool/tool-spring-boot-starter-exception/target/tool-spring-boot-starter-exception-2.2.4.jar # lidee-tool/tool-spring-boot-starter-file/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-tool/tool-spring-boot-starter-file/target/tool-spring-boot-starter-file-2.2.4.jar # lidee-tool/tool-spring-boot-starter-flowable/target/tool-spring-boot-starter-flowable-2.2.4.jar # lidee-tool/tool-spring-boot-starter-ip/target/tool-spring-boot-starter-ip-2.2.4.jar # lidee-tool/tool-spring-boot-starter-job/target/tool-spring-boot-starter-job-2.2.4.jar # lidee-tool/tool-spring-boot-starter-monitor/target/tool-spring-boot-starter-monitor-2.2.4.jar # lidee-tool/tool-spring-boot-starter-mybatis/target/tool-spring-boot-starter-mybatis-2.2.4.jar # lidee-tool/tool-spring-boot-starter-operatelog/target/tool-spring-boot-starter-operatelog-2.2.4.jar # lidee-tool/tool-spring-boot-starter-permission/target/tool-spring-boot-starter-permission-2.2.4.jar # lidee-tool/tool-spring-boot-starter-protection/target/tool-spring-boot-starter-protection-2.2.4.jar # lidee-tool/tool-spring-boot-starter-redis/target/tool-spring-boot-starter-redis-2.2.4.jar # lidee-tool/tool-spring-boot-starter-security/target/tool-spring-boot-starter-security-2.2.4.jar # lidee-tool/tool-spring-boot-starter-sql/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-tool/tool-spring-boot-starter-sql/target/tool-spring-boot-starter-sql-2.2.4.jar # lidee-tool/tool-spring-boot-starter-tenant/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-tool/tool-spring-boot-starter-tenant/target/tool-spring-boot-starter-tenant-2.2.4.jar # lidee-tool/tool-spring-boot-starter-test/target/tool-spring-boot-starter-test-2.2.4.jar # lidee-tool/tool-spring-boot-starter-web/target/tool-spring-boot-starter-web-2.2.4.jar # lidee-tool/tool-spring-boot-starter-websocket/target/tool-spring-boot-starter-websocket-2.2.4.jar # lidee-tool/tool-spring-boot-starter-yunji/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst # lidee-tool/tool-spring-boot-starter-yunji/target/tool-spring-boot-starter-yunji-2.2.4.jar # logs/lideeyunji-error.log # logs/lideeyunji-info.log
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.lideeyunji</groupId>
|
||||
<artifactId>lidee-tool</artifactId>
|
||||
<version>2.2.4</version>
|
||||
</parent>
|
||||
<groupId>com.lideeyunji</groupId>
|
||||
<artifactId>tool-spring-boot-starter-job</artifactId>
|
||||
<version>2.2.4</version>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>任务拓展
|
||||
1. 定时任务,基于 Quartz 拓展
|
||||
2. 异步任务,基于 Spring Async 拓展</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lideeyunji</groupId>
|
||||
<artifactId>tool-common</artifactId>
|
||||
</dependency>
|
||||
<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>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?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.lideeyunji</groupId>
|
||||
<artifactId>lidee-tool</artifactId>
|
||||
<version>${lidee.version}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>tool-spring-boot-starter-job</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>任务拓展
|
||||
1. 定时任务,基于 Quartz 拓展
|
||||
2. 异步任务,基于 Spring Async 拓展
|
||||
</description>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lideeyunji</groupId>
|
||||
<artifactId>tool-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>
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.lideeyunji.tool.framework.quartz.config;
|
||||
|
||||
import com.alibaba.ttl.TtlRunnable;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
/**
|
||||
* 异步任务 Configuration
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableAsync
|
||||
public class AsyncAutoConfiguration {
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.lideeyunji.tool.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, // 每次重试间隔
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.lideeyunji.tool.framework.quartz.core.handler;
|
||||
|
||||
/**
|
||||
* 任务处理器
|
||||
*
|
||||
* @author 金灯剑客
|
||||
*/
|
||||
public interface JobHandler {
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*
|
||||
* @param param 参数
|
||||
* @return 结果
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
String execute(String param) throws Exception;
|
||||
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package com.lideeyunji.tool.framework.quartz.core.handler;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import com.lideeyunji.tool.framework.quartz.core.enums.JobDataKeyEnum;
|
||||
import com.lideeyunji.tool.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.time.LocalDateTime;
|
||||
|
||||
import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage;
|
||||
|
||||
/**
|
||||
* 基础 Job 调用者,负责调用 {@link JobHandler#execute(String)} 执行任务
|
||||
*
|
||||
* @author 金灯剑客
|
||||
*/
|
||||
@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;
|
||||
LocalDateTime startTime = LocalDateTime.now();
|
||||
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, LocalDateTime startTime, String data, Throwable exception,
|
||||
JobExecutionContext executionContext) {
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
// 处理是否成功
|
||||
boolean success = exception == null;
|
||||
if (!success) {
|
||||
data = getRootCauseMessage(exception);
|
||||
}
|
||||
// 更新日志
|
||||
try {
|
||||
jobLogFrameworkService.updateJobLogResultAsync(jobLogId, endTime, (int) LocalDateTimeUtil.between(startTime, endTime).toMillis(), 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.lideeyunji.tool.framework.quartz.core.service;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Job 日志 Framework Service 接口
|
||||
*
|
||||
* @author 金灯剑客
|
||||
*/
|
||||
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 = "开始时间") LocalDateTime 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 = "结束时间不能为空") LocalDateTime endTime,
|
||||
@NotNull(message = "运行时长不能为空") Integer duration,
|
||||
boolean success, String result);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.lideeyunji.tool.framework.quartz.core.util;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import org.quartz.CronExpression;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Quartz Cron 表达式的工具类
|
||||
*
|
||||
* @author 金灯剑客
|
||||
*/
|
||||
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<LocalDateTime> 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<LocalDateTime> nextTimes = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
Date nextTime = cron.getNextValidTimeAfter(now);
|
||||
nextTimes.add(LocalDateTimeUtil.of(nextTime));
|
||||
// 切换现在,为下一个触发时间;
|
||||
now = nextTime;
|
||||
}
|
||||
return nextTimes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* 1. 定时任务,采用 Quartz 实现进程内的任务执行。
|
||||
* 考虑到高可用,使用 Quartz 自带的 MySQL 集群方案。
|
||||
*
|
||||
* 2. 异步任务,采用 Spring Async 异步执行。
|
||||
*/
|
||||
package com.lideeyunji.tool.framework.quartz;
|
||||
@@ -1,2 +0,0 @@
|
||||
com.lideeyunji.tool.framework.quartz.config.QuartzAutoConfiguration
|
||||
com.lideeyunji.tool.framework.quartz.config.AsyncAutoConfiguration
|
||||
Reference in New Issue
Block a user