集成xxl-job,修改common目录结构

This commit is contained in:
panxuejie 2026-01-04 15:50:35 +08:00
parent a60f0a805b
commit 142a9a2456
28 changed files with 855 additions and 133 deletions

View File

@ -6,42 +6,28 @@
<artifactId>tacit-parent</artifactId> <artifactId>tacit-parent</artifactId>
<groupId>com.tacit</groupId> <groupId>com.tacit</groupId>
<version>1.0.0-SNAPSHOT</version> <version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>tacit-common</artifactId> <artifactId>common-core</artifactId>
<name>Tacit Common</name> <name>Common Core</name>
<description>Common module for Tacit microservices</description> <description>Core utilities and global handlers module for Tacit microservices</description>
<dependencies> <dependencies>
<!-- Spring Boot --> <!-- Common Model -->
<dependency>
<groupId>com.tacit</groupId>
<artifactId>common-model</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- Spring Boot Web -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<!-- Spring Cloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Spring Cloud Bootstrap (Required for Spring Boot 3.x to load bootstrap.yml) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- JJWT --> <!-- JJWT -->
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>
@ -58,19 +44,6 @@
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Commons Lang3 --> <!-- Commons Lang3 -->
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
@ -78,17 +51,6 @@
</dependency> </dependency>
<!-- Testing --> <!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>

View File

@ -13,8 +13,8 @@ import java.util.Map;
@Slf4j @Slf4j
public class JwtUtils { public class JwtUtils {
// private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(CommonConstant.JWT_SECRET.getBytes(StandardCharsets.UTF_8)); private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(CommonConstant.JWT_SECRET.getBytes(StandardCharsets.UTF_8));
private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256); // private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
/** /**
* 生成JWT令牌 * 生成JWT令牌
@ -25,12 +25,22 @@ public class JwtUtils {
Date now = new Date(); Date now = new Date();
Date expireDate = new Date(now.getTime() + CommonConstant.JWT_EXPIRE_TIME); Date expireDate = new Date(now.getTime() + CommonConstant.JWT_EXPIRE_TIME);
return Jwts.builder() log.info("JWT生成参数: claims={}, now={}, expireDate={}, secretKey={}", claims, now, expireDate, SECRET_KEY);
.setClaims(claims)
.setIssuedAt(now) String token = null;
.setExpiration(expireDate) try {
.signWith(SECRET_KEY, SignatureAlgorithm.HS256) token = Jwts.builder()
.compact(); .setClaims(claims)
.setIssuedAt(now)
.setExpiration(expireDate)
.signWith(SECRET_KEY, SignatureAlgorithm.HS256)
.compact();
log.info("JWT生成成功: {}", token);
} catch (Exception e) {
log.error("JWT生成失败: {}", e.getMessage(), e);
}
return token;
} }
public static Long getUserIdFromToken(String token) { public static Long getUserIdFromToken(String token) {
Claims claims = parseToken(token); Claims claims = parseToken(token);

View File

@ -24,7 +24,13 @@ public class JwtUtilsTest {
claims.put("userId", 1L); claims.put("userId", 1L);
claims.put("username", "testuser"); claims.put("username", "testuser");
claims.put("role", "admin"); claims.put("role", "admin");
token = JwtUtils.generateToken(claims); try {
System.out.println("Before generateToken: claims = " + claims);
token = JwtUtils.generateToken(claims);
System.out.println("After generateToken: token = " + token);
} catch (Exception e) {
e.printStackTrace();
}
} }
@Test @Test

View File

@ -0,0 +1,57 @@
<?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>
<artifactId>tacit-parent</artifactId>
<groupId>com.tacit</groupId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-feign</artifactId>
<name>Common Feign</name>
<description>Feign clients module for Tacit microservices</description>
<dependencies>
<!-- Common Model -->
<dependency>
<groupId>com.tacit</groupId>
<artifactId>common-model</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- Spring Cloud Alibaba Nacos Discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,52 @@
<?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>
<artifactId>tacit-parent</artifactId>
<groupId>com.tacit</groupId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-model</artifactId>
<name>Common Model</name>
<description>Model, constant and exception module for Tacit microservices</description>
<dependencies>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<scope>provided</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -12,7 +12,7 @@ public class CommonConstant {
// JWT相关常量 // JWT相关常量
public static final String JWT_HEADER = "Authorization"; public static final String JWT_HEADER = "Authorization";
public static final String JWT_PREFIX = "Bearer "; public static final String JWT_PREFIX = "Bearer ";
public static final String JWT_SECRET = "tacit_app_secret_key_2024"; public static final String JWT_SECRET = "tacit_app_secret_key_2024_secure_strong_key_for_jwt_token";
// 7天 // 7天
public static final Long JWT_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L; public static final Long JWT_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L;

30
common/pom.xml Normal file
View File

@ -0,0 +1,30 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.tacit</groupId>
<artifactId>tacit-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>common</artifactId>
<packaging>pom</packaging>
<name>Common Aggregator</name>
<description>Aggregator module for common submodules</description>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
<module>common-model</module>
<module>common-feign</module>
<module>common-core</module>
<module>xxljob</module>
</modules>
</project>

201
common/xxljob/README.md Normal file
View File

@ -0,0 +1,201 @@
# XXL-Job Starter
一个基于XXL-Job的Spring Boot Starter提供自动配置、服务发现和容器部署支持。
## 功能特性
- ✅ Spring Boot自动配置简化XXL-Job集成
- ✅ 支持Nacos服务发现自动获取XXL-Job Admin地址
- ✅ 容器部署支持自动处理IP和端口映射
- ✅ 执行器地址自动补全协议前缀(http://)
- ✅ 环境变量动态配置,灵活适配不同部署环境
- ✅ 完善的日志记录和错误处理
## 快速开始
### 1. 添加依赖
在你的Spring Boot项目中添加以下依赖
```xml
<dependency>
<groupId>com.tacit</groupId>
<artifactId>xxljob</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
```
### 2. 启用XXL-Job
在Spring Boot主类上添加`@EnableXxlJob`注解:
```java
@SpringBootApplication
@EnableXxlJob
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
```
### 3. 配置XXL-Job
在`application.yml`或`bootstrap.yml`中添加XXL-Job配置
```yaml
xxl:
job:
admin:
# XXL-Job Admin地址多个地址用逗号分隔
addresses: http://localhost:8080/xxl-job-admin
# 可选调度中心通讯TOKEN
access-token: default_token
executor:
# 执行器AppName默认使用spring.application.name
appname: my-xxl-job-executor
# 执行器IP默认为空表示自动获取
ip:
# 执行器端口默认9099
port: 9999
# 执行器通讯TOKEN
access-token: default_token
# 执行器日志路径
log-path: logs/applogs/xxl-job/jobhandler
# 执行器日志保存天数
log-retention-days: 30
```
### 4. 创建JobHandler
```java
@Component
public class MyJobHandler {
@XxlJob("myJobHandler")
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-Job, Hello World.");
System.out.println("执行任务: " + param);
return ReturnT.SUCCESS;
}
}
```
## 配置说明
### 核心配置项
| 配置项 | 说明 | 默认值 |
|-------|------|-------|
| xxl.job.admin.addresses | XXL-Job Admin地址 | - |
| xxl.job.admin.access-token | 调度中心通讯TOKEN | - |
| xxl.job.executor.appname | 执行器AppName | spring.application.name |
| xxl.job.executor.ip | 执行器IP | 自动获取 |
| xxl.job.executor.port | 执行器端口 | 9099 |
| xxl.job.executor.access-token | 执行器通讯TOKEN | - |
| xxl.job.executor.log-path | 执行器日志路径 | logs/applogs/xxl-job/jobhandler |
| xxl.job.executor.log-retention-days | 日志保存天数 | 30 |
### 容器部署环境变量
| 环境变量 | 说明 |
|---------|------|
| XXL_JOB_EXECUTOR_IP | 容器真实IP宿主机IP |
| XXL_JOB_EXECUTOR_PORT | 容器映射后的真实端口 |
| XXL_JOB_ADMIN_ADDRESSES | XXL-Job Admin地址 |
## 高级特性
### 自动服务发现
如果未配置`xxl.job.admin.addresses`Starter会自动从Nacos注册中心发现XXL-Job Admin服务服务名包含"xxl-job-admin")。
### 容器部署支持
在Docker或Kubernetes环境中执行器会自动处理IP和端口映射
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-executor
spec:
replicas: 1
template:
spec:
containers:
- name: my-executor
image: my-executor:latest
ports:
- containerPort: 9999
env:
- name: XXL_JOB_EXECUTOR_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: XXL_JOB_EXECUTOR_PORT
value: "30099" # Kubernetes节点端口
- name: spring.application.name
value: my-executor
```
### 自定义执行器地址
```yaml
xxl:
job:
executor:
# 完整的执行器地址,会自动补全协议前缀
address: 192.168.3.67:9999
ip: 192.168.3.67
port: 9999
```
## 常见问题
### 1. 执行器注册失败
- 检查`xxl.job.admin.addresses`配置是否正确
- 检查网络连接是否正常
- 检查防火墙设置
### 2. 任务执行失败,提示"no protocol"
这是因为执行器地址缺少协议前缀导致的,请确保配置的地址包含`http://`前缀或使用Starter的自动补全功能。
### 3. 容器环境下执行器地址不正确
请配置`XXL_JOB_EXECUTOR_IP`和`XXL_JOB_EXECUTOR_PORT`环境变量指定宿主机的IP和映射后的端口。
## 开发和贡献
### 构建项目
```bash
mvn clean install -DskipTests
```
### 目录结构
```
src/
├── main/
│ ├── java/
│ │ └── com/tacit/starter/xxljob/
│ │ ├── annotation/ # 注解类
│ │ ├── properties/ # 配置属性类
│ │ └── XxlJobAutoConfiguration.java # 自动配置类
│ └── resources/
│ └── META-INF/
│ └── spring.factories # Spring Boot自动配置入口
```
## 版本依赖
- Spring Boot 2.x
- XXL-Job Core 2.5.0
- Spring Cloud Alibaba Nacos Discovery
## 许可证
MIT License

44
common/xxljob/pom.xml Normal file
View File

@ -0,0 +1,44 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.tacit</groupId>
<artifactId>tacit-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>xxljob</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<xxl-job.version>2.5.0</xxl-job.version>
</properties>
<dependencies>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,129 @@
package com.tacit.starter.xxljob;
import com.tacit.starter.xxljob.properties.XxlExecutorProperties;
import com.tacit.starter.xxljob.properties.XxlJobProperties;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Auther: xia
* @Date: 2026/1/4 13:56
* @Description: com.tacit.starter.xxljob
* @version: 1.0
*/
@EnableConfigurationProperties(XxlJobProperties.class)
public class XxlJobAutoConfiguration {
private Logger log = LoggerFactory.getLogger(XxlJobAutoConfiguration.class);
/**
* 服务名称 包含 XXL-JOB 则说明是 Admin
*/
private static final String XXL_JOB_ADMIN = "xxl-job-admin";
/**
* 配置xxl-job 执行器提供自动发现 xxl-job 能力
*
* @param xxlJobProperties xxl 配置
* @param environment 环境变量
* @param discoveryClient 注册发现客户端
* @return XxlJobSpringExecutor
*/
@Bean
public XxlJobSpringExecutor xxlJobSpringExecutor(XxlJobProperties xxlJobProperties, Environment environment, DiscoveryClient discoveryClient) {
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
XxlExecutorProperties executor = xxlJobProperties.getExecutor();
// 应用名默认为服务名
String appName = executor.getAppname();
if (!StringUtils.hasText(appName)) {
appName = environment.getProperty("spring.application.name");
}
// 处理执行器通讯TOKEN
String accessToken = executor.getAccessToken();
// 从环境变量获取
String envAccessToken = environment.getProperty("xxl.job.accessToken");
if (StringUtils.hasText(envAccessToken)) {
accessToken = envAccessToken;
}
// 从admin配置获取如果admin和executor使用相同的token
String adminAccessToken = xxlJobProperties.getAdmin().getAccessToken();
if (!StringUtils.hasText(accessToken) && StringUtils.hasText(adminAccessToken)) {
accessToken = adminAccessToken;
}
xxlJobSpringExecutor.setAppname(appName);
// 处理执行器IP支持容器环境下的真实IP获取
String executorIp = executor.getIp();
// 优先从环境变量获取容器的真实IP宿主机IP
String envIp = environment.getProperty("XXL_JOB_EXECUTOR_IP");
if (StringUtils.hasText(envIp)) {
executorIp = envIp;
}
xxlJobSpringExecutor.setIp(executorIp);
// 处理执行器端口支持容器环境下的端口映射
Integer executorPort = executor.getPort();
// 优先从环境变量获取容器映射后的真实端口
String envPort = environment.getProperty("XXL_JOB_EXECUTOR_PORT");
if (StringUtils.hasText(envPort)) {
try {
executorPort = Integer.parseInt(envPort);
} catch (NumberFormatException e) {
log.warn("Invalid XXL_JOB_EXECUTOR_PORT value: {}", envPort);
}
}
xxlJobSpringExecutor.setPort(executorPort);
// 处理执行器地址确保包含http://协议前缀
String address = executor.getAddress();
if (!StringUtils.hasText(address) && StringUtils.hasText(executorIp) && executorPort > 0) {
// 如果没有配置address但配置了IP和端口自动构建地址
address = String.format("http://%s:%d", executorIp, executorPort);
} else if (StringUtils.hasText(address) && !address.startsWith("http://") && !address.startsWith("https://")) {
address = "http://" + address;
}
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(executor.getLogPath());
xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays());
// 配置XXL-Job Admin地址
String adminAddresses = xxlJobProperties.getAdmin().getAddresses();
if (!StringUtils.hasText(adminAddresses)) {
// 从注册中心自动发现XXL-Job Admin
try {
List<String> serviceIds = discoveryClient.getServices();
if (serviceIds != null && !serviceIds.isEmpty()) {
List<String> adminUrls = serviceIds.stream()
.filter(serviceId -> serviceId.toLowerCase().contains(XXL_JOB_ADMIN))
.flatMap(serviceId -> discoveryClient.getInstances(serviceId).stream())
.map(instance -> String.format("http://%s:%s/xxl-job-admin", instance.getHost(), instance.getPort()))
.distinct()
.collect(Collectors.toList());
if (!adminUrls.isEmpty()) {
adminAddresses = String.join(",", adminUrls);
log.info("Auto discovered XXL-Job Admin addresses: {}", adminAddresses);
} else {
log.warn("No XXL-Job Admin service found in registry");
}
}
} catch (Exception e) {
log.error("Failed to discover XXL-Job Admin from registry", e);
}
}
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
log.info("init-xxlJobSpringExecutor: " + xxlJobSpringExecutor.toString());
return xxlJobSpringExecutor;
}
}

View File

@ -0,0 +1,25 @@
package com.tacit.starter.xxljob.annotation;
import com.tacit.starter.xxljob.XxlJobAutoConfiguration;
import org.springframework.context.annotation.Import;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Auther: xia
* @Date: 2026/1/4 13:51
* @Description: com.tacit.starter.xxljob.annotation
* @version: 1.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({XxlJobAutoConfiguration.class})
public @interface EnableXxlJob {
}

View File

@ -0,0 +1,22 @@
package com.tacit.starter.xxljob.properties;
import lombok.Data;
/**
* @Auther: xia
* @Date: 2026/1/4 13:54
* @Description: com.tacit.starter.xxljob.properties
* @version: 1.0
*/
@Data
public class XxlAdminProperties {
/**
* 调度中心部署跟地址 [选填]如调度中心集群部署存在多个地址则用逗号分隔 执行器将会使用该地址进行"执行器心跳注册""任务结果回调"为空则关闭自动注册
*/
private String addresses;
/**
* 调度中心通讯TOKEN [选填]非空时启用
*/
private String accessToken;
}

View File

@ -0,0 +1,51 @@
package com.tacit.starter.xxljob.properties;
import lombok.Data;
/**
* @Auther: xia
* @Date: 2026/1/4 13:56
* @Description: com.tacit.starter.xxljob.properties
* @version: 1.0
*/
@Data
public class XxlExecutorProperties {
/**
* 执行器AppName [选填]执行器心跳注册分组依据为空则关闭自动注册
*/
private String appname;
/**
* 服务注册地址,优先使用该配置作为注册地址 为空时使用内嵌服务 IP:PORT 作为注册地址 从而更灵活的支持容器类型执行器动态IP和动态映射端口问题
*/
private String address;
/**
* 执行器IP [选填]默认为空表示自动获取IP多网卡时可手动设置指定IP 该IP不会绑定Host仅作为通讯实用地址信息用于 "执行器注册"
* "调度中心请求并触发任务"
*/
private String ip;
/**
* 执行器端口号 [选填]小于等于0则自动获取默认端口为9099单机部署多个执行器时注意要配置不同执行器端口
*/
private Integer port = 0;
/**
* 执行器通讯TOKEN [必填]从配置文件中取不到值时使用默认值
*/
private String accessToken = "";
/**
* 执行器运行日志文件存储磁盘路径 [选填] 需要对该路径拥有读写权限为空则使用默认路径
*/
private String logPath = "logs/applogs/xxl-job/jobhandler";
/**
* 执行器日志保存天数 [选填] 值大于3时生效启用执行器Log文件定期清理功能否则不生效
*/
private Integer logRetentionDays = 30;
}

View File

@ -0,0 +1,23 @@
package com.tacit.starter.xxljob.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
/**
* @Auther: xia
* @Date: 2026/1/4 13:57
* @Description: com.tacit.starter.xxljob.properties
* @version: 1.0
*/
@Data
@ConfigurationProperties(prefix = "xxl.job")
public class XxlJobProperties {
@NestedConfigurationProperty
private XxlAdminProperties admin = new XxlAdminProperties();
@NestedConfigurationProperty
private XxlExecutorProperties executor = new XxlExecutorProperties();
}

View File

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tacit.starter.xxljob.XxlJobAutoConfiguration

90
pom.xml
View File

@ -17,7 +17,7 @@
<module>tacit-gateway</module> <module>tacit-gateway</module>
<module>tacit-admin</module> <module>tacit-admin</module>
<module>tacit-app-api</module> <module>tacit-app-api</module>
<module>tacit-common</module> <module>common</module>
</modules> </modules>
<properties> <properties>
@ -31,6 +31,7 @@
<resilience4j.version>2.1.0</resilience4j.version> <resilience4j.version>2.1.0</resilience4j.version>
<springdoc.version>2.3.0</springdoc.version> <springdoc.version>2.3.0</springdoc.version>
<commons-lang3.version>3.14.0</commons-lang3.version> <commons-lang3.version>3.14.0</commons-lang3.version>
<junit-jupiter.version>5.10.1</junit-jupiter.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@ -69,13 +70,6 @@
<version>${mybatis-plus.version}</version> <version>${mybatis-plus.version}</version>
</dependency> </dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- JJWT --> <!-- JJWT -->
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>
@ -120,6 +114,32 @@
<version>${commons-lang3.version}</version> <version>${commons-lang3.version}</version>
</dependency> </dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- Resilience4j --> <!-- Resilience4j -->
<!-- Spring Boot 3集成模块提供自动配置、注解支持和健康指标是与Spring Boot集成的核心依赖 --> <!-- Spring Boot 3集成模块提供自动配置、注解支持和健康指标是与Spring Boot集成的核心依赖 -->
<dependency> <dependency>
@ -166,6 +186,44 @@
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<dependencies>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<!-- Commons Lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<repositories> <repositories>
<repository> <repository>
@ -217,6 +275,22 @@
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<useSystemClassLoader>true</useSystemClassLoader>
<argLine>--enable-preview</argLine>
</configuration>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -53,19 +53,27 @@
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency> </dependency>
<!-- Common Module --> <!-- Common Modules -->
<dependency> <dependency>
<groupId>com.tacit</groupId> <groupId>com.tacit</groupId>
<artifactId>tacit-common</artifactId> <artifactId>common-model</artifactId>
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
</dependency> </dependency>
<!-- Lombok -->
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>com.tacit</groupId>
<artifactId>lombok</artifactId> <artifactId>common-feign</artifactId>
<version>${lombok.version}</version> <version>${project.parent.version}</version>
<scope>provided</scope> </dependency>
<dependency>
<groupId>com.tacit</groupId>
<artifactId>common-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- XXL Job -->
<dependency>
<groupId>com.tacit</groupId>
<artifactId>xxljob</artifactId>
<version>${project.parent.version}</version>
</dependency> </dependency>
<!-- Resilience4j --> <!-- Resilience4j -->
@ -74,12 +82,6 @@
<artifactId>resilience4j-spring-boot3</artifactId> <artifactId>resilience4j-spring-boot3</artifactId>
</dependency> </dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,24 @@
package com.tacit.admin.job;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.stereotype.Component;
/**
* @Auther: xia
* @Date: 2026/1/4 15:01
* @Description: com.tacit.admin.job
* @version: 1.0
*/
@Component
public class TestJob {
@XxlJob("myJobHandler")
public ReturnT<String> execute(String param) throws Exception {
System.out.println("执行任务: " + param);
return ReturnT.SUCCESS;
}
}

View File

@ -53,27 +53,23 @@
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency> </dependency>
<!-- Common Module --> <!-- Common Modules -->
<dependency> <dependency>
<groupId>com.tacit</groupId> <groupId>com.tacit</groupId>
<artifactId>tacit-common</artifactId> <artifactId>common-model</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.tacit</groupId>
<artifactId>common-feign</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.tacit</groupId>
<artifactId>common-core</artifactId>
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
</dependency> </dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -26,28 +26,22 @@
<artifactId>spring-boot-starter-security</artifactId> <artifactId>spring-boot-starter-security</artifactId>
</dependency> </dependency>
<!-- Common Module --> <!-- Common Modules -->
<dependency> <dependency>
<groupId>com.tacit</groupId> <groupId>com.tacit</groupId>
<artifactId>tacit-common</artifactId> <artifactId>common-model</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.tacit</groupId>
<artifactId>common-core</artifactId>
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</exclusion> </exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</exclusion>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
@ -57,20 +51,18 @@
<artifactId>spring-cloud-starter-bootstrap</artifactId> <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency> </dependency>
<!-- Lombok --> <!-- Spring Cloud Alibaba Nacos Config -->
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>lombok</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- Testing --> <!-- Spring Cloud Alibaba Nacos Discovery -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -5,9 +5,7 @@ import com.tacit.common.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;

View File

@ -48,3 +48,25 @@ logging:
org.springframework.http.server.reactive: debug org.springframework.http.server.reactive: debug
org.springframework.web.reactive: debug org.springframework.web.reactive: debug
reactor.netty: debug reactor.netty: debug
# XXL-Job Configuration
xxl:
job:
admin:
# XXL-Job Admin地址多个地址用逗号分隔
addresses: http://localhost:8085/xxl-job-admin
# 可选调度中心通讯TOKEN
access-token: default_token
executor:
# 执行器AppName默认使用spring.application.name
appname: tacit-gateway
# 执行器IP默认为空表示自动获取
ip:
# 执行器端口默认9099
port: 9999
# 执行器通讯TOKEN
access-token: default_token
# 执行器日志路径
log-path: logs/applogs/xxl-job/jobhandler
# 执行器日志保存天数
log-retention-days: 30