feat(all): 小版本更新
- 新增任务重试接口,支持指定节点重试 - 添加获取最后一次任务执行记录的功能 - 完善任务执行逻辑,区分普通任务和重试任务 - 优化任务模板获取逻辑,增加变更类型参数 - 增强任务状态判断,防止已完成任务被重复执行 - 补充任务实例明细的批量删除和重建机制 - 提供任务执行时长格式化工具方法 - 增加任务状态枚举描述获取方法 - 注释掉Redisson配置类代码 - 新增任务实例VO模型用于返回历史执行记录
This commit is contained in:
@@ -80,17 +80,9 @@ https://github.com/odboy-tianjun/cutejava
|
||||

|
||||
- CuteFileDragUpload 支持拖拽上传文件到OSS, 必须结合Minio使用(上传完成后必须销毁组件)
|
||||

|
||||
- CutePipelineNode 流水线节点组件
|
||||

|
||||
|
||||
#### 系统模块
|
||||
|
||||
- cutejava-framework:系统基础框架。提供核心功能,如:权限、数据权限、异常处理、缓存、工具类、接口限流、接口防重、接口幂等。
|
||||
- cutejava-system:系统功能模块。提供用户、角色、菜单、部门、岗位、字典、定时任务、邮件、Minio存储、服务监控等功能
|
||||
- cutejava-devops:devops模块,已实现流水线功能。参考阿里云云效,提供流水线功能,可配置流水线任务,并支持任务依赖、任务参数、任务参数校验、任务参数回填。
|
||||
|
||||
#### 特别鸣谢
|
||||
|
||||
- 感谢 [elunez](https://github.com/elunez/eladmin-mp) 大佬提供的基础框架
|
||||
- 感谢 [aizuda](https://mongoplus.com/) 大佬提供的Mongodb框架
|
||||
- 建表参考 'cutejava/doc/mongodb.md'
|
||||
- cutejava-module-system:系统功能模块。提供用户、角色、菜单、部门、岗位、字典、定时任务、邮件、Minio存储、服务监控等功能
|
||||
- cutejava-module-task:串行任务模块,已实现流水线功能。参考阿里云云效,提供流水线功能,可配置流水线任务,并支持任务依赖、任务参数、任务参数校验、任务参数回填。
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<cute-product-line-select v-model="form.productLine" @detail="onProductLineDetailChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产品线Pro" prop="productLinePro">
|
||||
<cute-product-line-select-pro v-model="form.productLine" @detail="onProductLineDetailProChange" />
|
||||
<cute-product-line-select-pro v-model="form.productLinePro" @detail="onProductLineDetailProChange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit('form')">提 交</el-button>
|
||||
|
||||
@@ -68,81 +68,6 @@ export default {
|
||||
' repositories:\n' +
|
||||
' # 是否启用Redis存储(关闭防止出现 Multiple Spring Data modules found, entering strict repository configuration mode)\n' +
|
||||
' enabled: false\n' +
|
||||
' datasource:\n' +
|
||||
' druid:\n' +
|
||||
' db-type: com.alibaba.druid.pool.DruidDataSource\n' +
|
||||
' driverClassName: com.p6spy.engine.spy.P6SpyDriver\n' +
|
||||
' url: jdbc:p6spy:mysql://192.168.100.128:23306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true\n' +
|
||||
' username: root\n' +
|
||||
' password: lm,101208..,.\n' +
|
||||
' # 初始连接数,建议设置为与最小空闲连接数相同\n' +
|
||||
' initial-size: 2\n' +
|
||||
' # 最小空闲连接数,保持足够的空闲连接以应对请求\n' +
|
||||
' min-idle: 2\n' +
|
||||
' # 最大连接数,根据并发需求适当增加\n' +
|
||||
' max-active: 8\n' +
|
||||
' # 获取连接超时时间(毫秒),调整以满足响应时间要求\n' +
|
||||
' max-wait: 3000\n' +
|
||||
' # 启用KeepAlive机制,保持长连接\n' +
|
||||
' keep-alive: true\n' +
|
||||
' # 连接有效性检测间隔时间(毫秒),定期检查连接的健康状态\n' +
|
||||
' time-between-eviction-runs-millis: 60000\n' +
|
||||
' # 连接在池中最小生存时间(毫秒),确保连接在池中至少存在一段时间\n' +
|
||||
' min-evictable-idle-time-millis: 300000\n' +
|
||||
' # 连接在池中最大生存时间(毫秒),防止连接在池中停留过长\n' +
|
||||
' max-evictable-idle-time-millis: 900000\n' +
|
||||
' # 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除\n' +
|
||||
' test-while-idle: true\n' +
|
||||
' # 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个\n' +
|
||||
' test-on-borrow: true\n' +
|
||||
' # 是否在归还到池中前进行检验\n' +
|
||||
' test-on-return: false\n' +
|
||||
' # 停用 com_ping 探活机制\n' +
|
||||
' use-ping-method: false\n' +
|
||||
' # 检测连接是否有效\n' +
|
||||
' validation-query: SELECT 1\n' +
|
||||
' # 配置监控统计\n' +
|
||||
' web-stat-filter:\n' +
|
||||
' enabled: true\n' +
|
||||
' url-pattern: /*\n' +
|
||||
' # 开启session统计功能\n' +
|
||||
' session-stat-enable: true\n' +
|
||||
' exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"\n' +
|
||||
' # 缺省sessionStatMaxCount是1000个\n' +
|
||||
' session-stat-max-count: 1000\n' +
|
||||
' stat-view-servlet:\n' +
|
||||
' enabled: true\n' +
|
||||
' url-pattern: /druid/*\n' +
|
||||
' reset-enable: false\n' +
|
||||
' filter:\n' +
|
||||
' stat:\n' +
|
||||
' enabled: true\n' +
|
||||
' # 记录慢SQL\n' +
|
||||
' log-slow-sql: true\n' +
|
||||
' slow-sql-millis: 2000\n' +
|
||||
' merge-sql: true\n' +
|
||||
' wall:\n' +
|
||||
' config:\n' +
|
||||
' multi-statement-allow: true\n' +
|
||||
' aop-patterns: "cn.odboy.mapper.*,cn.odboy.service.*,cn.odboy.*.mapper.*,cn.odboy.*.service.*"\n' +
|
||||
' redis:\n' +
|
||||
' database: ${REDIS_DB:1}\n' +
|
||||
' host: ${REDIS_HOST:192.168.100.128}\n' +
|
||||
' port: ${REDIS_PORT:26379}\n' +
|
||||
' password: ${REDIS_PWD:lm,101208..,.}\n' +
|
||||
' # 连接超时时间\n' +
|
||||
' timeout: 5000\n' +
|
||||
' # 连接池配置\n' +
|
||||
' lettuce:\n' +
|
||||
' pool:\n' +
|
||||
' # 连接池最大连接数\n' +
|
||||
' max-active: 30\n' +
|
||||
' # 连接池最大阻塞等待时间(毫秒),负值表示没有限制\n' +
|
||||
' max-wait: -1\n' +
|
||||
' # 连接池中的最大空闲连接数\n' +
|
||||
' max-idle: 20\n' +
|
||||
' # 连接池中的最小空闲连接数\n' +
|
||||
' min-idle: 1\n' +
|
||||
'mybatis-plus:\n' +
|
||||
' configuration:\n' +
|
||||
' # 开启 Mybatis 二级缓存,默认为 true\n' +
|
||||
@@ -174,19 +99,6 @@ export default {
|
||||
' # 是否控制台 print mybatisplus-plus 的 LOGO\n' +
|
||||
' banner: true\n' +
|
||||
'app:\n' +
|
||||
' # 密码加密传输,前端公钥加密,后端私钥解密\n' +
|
||||
' rsa:\n' +
|
||||
' private-key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==\n' +
|
||||
' # 鉴权\n' +
|
||||
' jwt:\n' +
|
||||
' # 必须使用最少88位的Base64对该令牌进行编码\n' +
|
||||
' base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=\n' +
|
||||
' # 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html\n' +
|
||||
' token-validity-in-seconds: 14400000\n' +
|
||||
' # token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期\n' +
|
||||
' detect: 1800000\n' +
|
||||
' # 续期时间范围,默认1小时,单位毫秒\n' +
|
||||
' renew: 3600000\n' +
|
||||
' # 登录\n' +
|
||||
' login:\n' +
|
||||
' # 是否限制单用户登录\n' +
|
||||
@@ -235,8 +147,8 @@ export default {
|
||||
' minio:\n' +
|
||||
' endpoint: http://192.168.100.128:9000\n' +
|
||||
' bucketName: cutejava\n' +
|
||||
' accessKey: 8o5oVzqvKby5TzaGcfmx\n' +
|
||||
' secretKey: 8BiK5UbmvXGJNVQ98CGiohHzT1N1FMRZqHRdJllj\n' +
|
||||
' accessKey: xxxxxx\n' +
|
||||
' secretKey: xxxxxx\n' +
|
||||
' captcha:\n' +
|
||||
' expireTime: 300\n' +
|
||||
'## 启用debug\n' +
|
||||
|
||||
+97
-97
@@ -1,97 +1,97 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.redis;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.odboy.util.CsSystemUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.client.codec.StringCodec;
|
||||
import org.redisson.config.Config;
|
||||
import org.redisson.config.EqualJitterDelay;
|
||||
import org.redisson.config.SingleServerConfig;
|
||||
import org.redisson.config.TransportMode;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@AutoConfigureBefore(RedisAutoConfiguration.class)
|
||||
public class RedissonConfiguration {
|
||||
|
||||
@Value("${spring.redis.host}")
|
||||
private String redisHost;
|
||||
|
||||
@Value("${spring.redis.port}")
|
||||
private int redisPort;
|
||||
|
||||
@Value("${spring.redis.database}")
|
||||
private int redisDatabase;
|
||||
|
||||
@Value("${spring.redis.password:}")
|
||||
private String redisPassword;
|
||||
|
||||
@Value("${spring.redis.timeout:5000}")
|
||||
private int timeout;
|
||||
|
||||
@Value("${spring.redis.lettuce.pool.max-active:64}")
|
||||
private int connectionPoolSize;
|
||||
|
||||
@Value("${spring.redis.lettuce.pool.min-idle:16}")
|
||||
private int connectionMinimumIdleSize;
|
||||
|
||||
@Bean
|
||||
public RedissonClient redissonClient() {
|
||||
Config config = new Config();
|
||||
config.setThreads(CsSystemUtil.getCpuCount());
|
||||
config.setNettyThreads(0);
|
||||
config.setCodec(new StringCodec());
|
||||
config.setTransportMode(TransportMode.NIO);
|
||||
SingleServerConfig singleServerConfig = config.useSingleServer();
|
||||
singleServerConfig.setAddress("redis://" + redisHost + ":" + redisPort);
|
||||
singleServerConfig.setDatabase(redisDatabase);
|
||||
singleServerConfig.setTimeout(timeout);
|
||||
singleServerConfig.setConnectionPoolSize(connectionPoolSize);
|
||||
singleServerConfig.setConnectionMinimumIdleSize(connectionMinimumIdleSize);
|
||||
if (StrUtil.isNotBlank(redisPassword)) {
|
||||
singleServerConfig.setPassword(redisPassword);
|
||||
}
|
||||
singleServerConfig.setIdleConnectionTimeout(10000);
|
||||
singleServerConfig.setConnectTimeout(10000);
|
||||
singleServerConfig.setTimeout(3000);
|
||||
singleServerConfig.setRetryAttempts(3);
|
||||
// singleServerConfig.setRetryInterval(1500);
|
||||
singleServerConfig.setRetryDelay(new EqualJitterDelay(Duration.ofSeconds(3), Duration.ofSeconds(5)));
|
||||
singleServerConfig.setSubscriptionsPerConnection(5);
|
||||
singleServerConfig.setSubscriptionConnectionMinimumIdleSize(1);
|
||||
singleServerConfig.setSubscriptionConnectionPoolSize(50);
|
||||
singleServerConfig.setConnectionMinimumIdleSize(32);
|
||||
singleServerConfig.setConnectionPoolSize(64);
|
||||
singleServerConfig.setDnsMonitoringInterval(5000);
|
||||
RedissonClient redissonClient = Redisson.create(config);
|
||||
log.info("Redisson客户端 初始化完毕");
|
||||
return redissonClient;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///*
|
||||
// * Copyright 2021-2025 Odboy
|
||||
// *
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// */
|
||||
//
|
||||
//package cn.odboy.framework.redis;
|
||||
//
|
||||
//import cn.hutool.core.util.StrUtil;
|
||||
//import cn.odboy.util.CsSystemUtil;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.redisson.Redisson;
|
||||
//import org.redisson.api.RedissonClient;
|
||||
//import org.redisson.client.codec.StringCodec;
|
||||
//import org.redisson.config.Config;
|
||||
//import org.redisson.config.EqualJitterDelay;
|
||||
//import org.redisson.config.SingleServerConfig;
|
||||
//import org.redisson.config.TransportMode;
|
||||
//import org.springframework.beans.factory.annotation.Value;
|
||||
//import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
//import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//
|
||||
//import java.time.Duration;
|
||||
//
|
||||
//@Slf4j
|
||||
//@Configuration
|
||||
//@AutoConfigureBefore(RedisAutoConfiguration.class)
|
||||
//public class RedissonConfiguration {
|
||||
//
|
||||
// @Value("${spring.redis.host}")
|
||||
// private String redisHost;
|
||||
//
|
||||
// @Value("${spring.redis.port}")
|
||||
// private int redisPort;
|
||||
//
|
||||
// @Value("${spring.redis.database}")
|
||||
// private int redisDatabase;
|
||||
//
|
||||
// @Value("${spring.redis.password:}")
|
||||
// private String redisPassword;
|
||||
//
|
||||
// @Value("${spring.redis.timeout:5000}")
|
||||
// private int timeout;
|
||||
//
|
||||
// @Value("${spring.redis.lettuce.pool.max-active:64}")
|
||||
// private int connectionPoolSize;
|
||||
//
|
||||
// @Value("${spring.redis.lettuce.pool.min-idle:16}")
|
||||
// private int connectionMinimumIdleSize;
|
||||
//
|
||||
// @Bean
|
||||
// public RedissonClient redissonClient() {
|
||||
// Config config = new Config();
|
||||
// config.setThreads(CsSystemUtil.getCpuCount());
|
||||
// config.setNettyThreads(0);
|
||||
// config.setCodec(new StringCodec());
|
||||
// config.setTransportMode(TransportMode.NIO);
|
||||
// SingleServerConfig singleServerConfig = config.useSingleServer();
|
||||
// singleServerConfig.setAddress("redis://" + redisHost + ":" + redisPort);
|
||||
// singleServerConfig.setDatabase(redisDatabase);
|
||||
// singleServerConfig.setTimeout(timeout);
|
||||
// singleServerConfig.setConnectionPoolSize(connectionPoolSize);
|
||||
// singleServerConfig.setConnectionMinimumIdleSize(connectionMinimumIdleSize);
|
||||
// if (StrUtil.isNotBlank(redisPassword)) {
|
||||
// singleServerConfig.setPassword(redisPassword);
|
||||
// }
|
||||
// singleServerConfig.setIdleConnectionTimeout(10000);
|
||||
// singleServerConfig.setConnectTimeout(10000);
|
||||
// singleServerConfig.setTimeout(3000);
|
||||
// singleServerConfig.setRetryAttempts(3);
|
||||
// // singleServerConfig.setRetryInterval(1500);
|
||||
// singleServerConfig.setRetryDelay(new EqualJitterDelay(Duration.ofSeconds(3), Duration.ofSeconds(5)));
|
||||
// singleServerConfig.setSubscriptionsPerConnection(5);
|
||||
// singleServerConfig.setSubscriptionConnectionMinimumIdleSize(1);
|
||||
// singleServerConfig.setSubscriptionConnectionPoolSize(50);
|
||||
// singleServerConfig.setConnectionMinimumIdleSize(32);
|
||||
// singleServerConfig.setConnectionPoolSize(64);
|
||||
// singleServerConfig.setDnsMonitoringInterval(5000);
|
||||
// RedissonClient redissonClient = Redisson.create(config);
|
||||
// log.info("Redisson客户端 初始化完毕");
|
||||
// return redissonClient;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
|
||||
@@ -22,6 +22,12 @@ import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间相关工具
|
||||
*
|
||||
* @author odboy
|
||||
* @date 2025-10-01
|
||||
*/
|
||||
@UtilityClass
|
||||
public class CsDateUtil {
|
||||
/**
|
||||
@@ -50,4 +56,95 @@ public class CsDateUtil {
|
||||
public static String getNowDateStr() {
|
||||
return DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化到毫秒
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 格式化后的时间,X天X时X分X秒X毫秒
|
||||
*/
|
||||
public static String formatMillsDuration(Date startTime, Date endTime) {
|
||||
if (startTime == null || endTime == null) {
|
||||
// 什么都不返回
|
||||
return "";
|
||||
}
|
||||
|
||||
long betweenSeconds = DateUtil.between(startTime, endTime, cn.hutool.core.date.DateUnit.SECOND);
|
||||
|
||||
long days = betweenSeconds / (24 * 60 * 60);
|
||||
long hours = (betweenSeconds % (24 * 60 * 60)) / (60 * 60);
|
||||
long minutes = (betweenSeconds % (60 * 60)) / 60;
|
||||
long seconds = betweenSeconds % 60;
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
if (days > 0) {
|
||||
result.append(days).append("天");
|
||||
}
|
||||
|
||||
if (hours > 0) {
|
||||
result.append(hours).append("时");
|
||||
}
|
||||
|
||||
if (minutes > 0) {
|
||||
result.append(minutes).append("分");
|
||||
}
|
||||
|
||||
if (seconds > 0 || result.length() == 0) {
|
||||
result.append(seconds).append("秒");
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化到秒
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 格式化后的时间,X天X时X分X秒
|
||||
*/
|
||||
public static String formatSecondsDuration(Date startTime, Date endTime) {
|
||||
if (startTime == null || endTime == null) {
|
||||
// 什么都不返回
|
||||
return "";
|
||||
}
|
||||
|
||||
long betweenMs = DateUtil.betweenMs(startTime, endTime);
|
||||
long days = betweenMs / (24 * 60 * 60 * 1000);
|
||||
long hours = (betweenMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000);
|
||||
long minutes = (betweenMs % (60 * 60 * 1000)) / (60 * 1000);
|
||||
long seconds = (betweenMs % (60 * 1000)) / 1000;
|
||||
long milliseconds = betweenMs % 1000;
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
if (days > 0) {
|
||||
result.append(days).append("天");
|
||||
}
|
||||
|
||||
if (hours > 0) {
|
||||
result.append(hours).append("时");
|
||||
}
|
||||
|
||||
if (minutes > 0) {
|
||||
result.append(minutes).append("分");
|
||||
}
|
||||
|
||||
if (seconds > 0) {
|
||||
result.append(seconds).append("秒");
|
||||
}
|
||||
|
||||
if (milliseconds > 0) {
|
||||
result.append(milliseconds).append("毫秒");
|
||||
}
|
||||
|
||||
// 如果所有单位都是0,则显示0毫秒
|
||||
if (result.length() == 0) {
|
||||
result.append("0毫秒");
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,13 @@ public enum TaskStatusEnum {
|
||||
Fail("fail", "执行失败");
|
||||
private final String code;
|
||||
private final String name;
|
||||
|
||||
public static String getDesc(String code) {
|
||||
for (TaskStatusEnum taskStatusEnum : TaskStatusEnum.values()) {
|
||||
if (taskStatusEnum.code.equals(code)) {
|
||||
return taskStatusEnum.getName();
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
+23
-2
@@ -20,12 +20,14 @@ import cn.odboy.annotation.AnonymousAccess;
|
||||
import cn.odboy.task.constant.TaskChangeTypeEnum;
|
||||
import cn.odboy.task.core.TaskManage;
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceInfoTb;
|
||||
import cn.odboy.task.dal.model.TaskInstanceInfoVo;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@@ -38,7 +40,7 @@ public class TaskTestsController {
|
||||
@AnonymousAccess
|
||||
@GetMapping(value = "/testCreate")
|
||||
public ResponseEntity<?> testCreate() {
|
||||
TaskInstanceInfoTb instanceInfo = taskManage.createJob("cutejava", TaskChangeTypeEnum.AppApply, "java", "daily", "cutejava", "功能测试", null);
|
||||
TaskInstanceInfoTb instanceInfo = taskManage.createJob("cutejava", TaskChangeTypeEnum.AppContainerDeploy, "java", "daily", "cutejava", "功能测试", null);
|
||||
log.info("任务创建成功,实例为:{}", JSON.toJSONString(instanceInfo));
|
||||
return ResponseEntity.ok(instanceInfo);
|
||||
}
|
||||
@@ -46,7 +48,7 @@ public class TaskTestsController {
|
||||
@AnonymousAccess
|
||||
@GetMapping(value = "/testCreateAfterStop")
|
||||
public ResponseEntity<?> testCreateAfterStop() {
|
||||
TaskInstanceInfoTb instanceInfo = taskManage.createJob("cutejava", TaskChangeTypeEnum.AppApply, "java", "daily", "cutejava", "功能测试", null);
|
||||
TaskInstanceInfoTb instanceInfo = taskManage.createJob("cutejava", TaskChangeTypeEnum.AppContainerDeploy, "java", "daily", "cutejava", "功能测试", null);
|
||||
log.info("任务创建成功,实例为:{}", JSON.toJSONString(instanceInfo));
|
||||
ThreadUtil.execAsync(() -> {
|
||||
ThreadUtil.safeSleep(5000);
|
||||
@@ -54,4 +56,23 @@ public class TaskTestsController {
|
||||
});
|
||||
return ResponseEntity.ok(null);
|
||||
}
|
||||
|
||||
@AnonymousAccess
|
||||
@GetMapping(value = "/testRetry")
|
||||
public ResponseEntity<?> testRetry(@RequestParam Long instanceId, @RequestParam String retryNodeCode) {
|
||||
TaskInstanceInfoTb instanceInfo = taskManage.retryJob(instanceId, retryNodeCode);
|
||||
log.info("任务重试成功,实例为:{}", JSON.toJSONString(instanceInfo));
|
||||
return ResponseEntity.ok(null);
|
||||
}
|
||||
|
||||
@AnonymousAccess
|
||||
@GetMapping(value = "/last")
|
||||
public ResponseEntity<?> testLast() {
|
||||
String contextName = "cutejava";
|
||||
String language = "java";
|
||||
String changeType = TaskChangeTypeEnum.AppContainerDeploy.getCode();
|
||||
String envAlias = "daily";
|
||||
TaskInstanceInfoVo instanceInfo = taskManage.getLastInfo(contextName, language, envAlias, changeType);
|
||||
return ResponseEntity.ok(instanceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,13 @@ package cn.odboy.task.core;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.odboy.framework.context.CsSpringBeanHolder;
|
||||
import cn.odboy.framework.exception.web.BadRequestException;
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb;
|
||||
import cn.odboy.task.dal.model.TaskTemplateInfoVo;
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceInfoTb;
|
||||
import cn.odboy.task.dal.model.TaskTemplateNodeVo;
|
||||
import cn.odboy.task.service.TaskInstanceDetailService;
|
||||
import cn.odboy.task.service.TaskInstanceInfoService;
|
||||
import cn.odboy.task.service.TaskTemplateInfoService;
|
||||
import cn.odboy.task.service.TaskInstanceStepDetailService;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.InterruptableJob;
|
||||
@@ -53,44 +54,115 @@ public class TaskJobBean extends QuartzJobBean implements InterruptableJob {
|
||||
// ========================== 获取代理类 ==========================
|
||||
TaskInstanceInfoService taskInstanceInfoService = CsSpringBeanHolder.getBean(TaskInstanceInfoService.class);
|
||||
TaskInstanceDetailService taskInstanceDetailService = CsSpringBeanHolder.getBean(TaskInstanceDetailService.class);
|
||||
TaskTemplateInfoService taskTemplateInfoService = CsSpringBeanHolder.getBean(TaskTemplateInfoService.class);
|
||||
// ========================== 获取参数 ==========================
|
||||
JobDataMap dataMap = context.getMergedJobDataMap();
|
||||
long id = dataMap.getLong("id");
|
||||
// 应用名、资源类型
|
||||
String contextName = dataMap.getString("contextName");
|
||||
// 开发语言、资源版本
|
||||
String language = dataMap.getString("language");
|
||||
String envAlias = dataMap.getString("envAlias");
|
||||
// ========================== 获取任务编排模板 ==========================
|
||||
TaskTemplateInfoVo taskInstanceInfoVo = taskTemplateInfoService.getTemplateInfoByECL(envAlias, contextName, language);
|
||||
if (taskInstanceInfoVo == null) {
|
||||
taskInstanceInfoService.fastFailWithMessage(id, "没有查询到任务编排模板");
|
||||
return;
|
||||
}
|
||||
String templateInfo = taskInstanceInfoVo.getTemplateInfo();
|
||||
TaskInstanceInfoTb taskInstanceInfoVo = taskInstanceInfoService.getById(id);
|
||||
String templateInfo = taskInstanceInfoVo.getTemplate();
|
||||
List<TaskTemplateNodeVo> taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class);
|
||||
if (taskTemplateNodeVos == null || taskTemplateNodeVos.isEmpty()) {
|
||||
taskInstanceInfoService.fastFailWithMessage(id, "没有查询到任务编排明细");
|
||||
return;
|
||||
// ========================== 判断是否重试任务 ==========================
|
||||
String retryNodeCode = dataMap.getString("retryNodeCode");
|
||||
if (StrUtil.isBlank(retryNodeCode)) {
|
||||
executeNormalTask(taskInstanceInfoService, taskInstanceDetailService, id, dataMap, taskTemplateNodeVos);
|
||||
} else {
|
||||
executeRetryTask(taskInstanceInfoService, taskInstanceDetailService, id, dataMap, taskTemplateNodeVos, retryNodeCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行普通任务(非重试任务)
|
||||
*
|
||||
* @param taskInstanceInfoService 任务实例信息服务
|
||||
* @param taskInstanceDetailService 任务实例明细服务
|
||||
* @param id 任务实例ID
|
||||
* @param dataMap 数据映射
|
||||
* @param taskTemplateNodeVos 模板节点列表
|
||||
*/
|
||||
private void executeNormalTask(TaskInstanceInfoService taskInstanceInfoService, TaskInstanceDetailService taskInstanceDetailService, long id, JobDataMap dataMap, List<TaskTemplateNodeVo> taskTemplateNodeVos) {
|
||||
// ========================== 初始化执行明细 ==========================
|
||||
List<TaskInstanceDetailTb> taskInstanceDetails = new ArrayList<>();
|
||||
for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodeVos) {
|
||||
TaskInstanceDetailTb taskInstanceDetail = new TaskInstanceDetailTb();
|
||||
taskInstanceDetail.setInstanceId(id);
|
||||
taskInstanceDetail.setFinishTime(null);
|
||||
taskInstanceDetail.setBizCode(taskTemplateNodeVo.getCode());
|
||||
taskInstanceDetail.setBizName(taskTemplateNodeVo.getName());
|
||||
taskInstanceDetail.setExecuteInfo("未开始");
|
||||
taskInstanceDetail.setExecuteStatus("pending");
|
||||
taskInstanceDetails.add(taskInstanceDetail);
|
||||
}
|
||||
List<TaskInstanceDetailTb> taskInstanceDetails = taskTemplateNodeVos.stream().map(taskTemplateNodeVo -> buildTaskInstanceDetail(id, taskTemplateNodeVo)).collect(Collectors.toList());
|
||||
taskInstanceDetailService.saveBatch(taskInstanceDetails);
|
||||
Map<String, Long> codeIdMap = taskInstanceDetails.stream().collect(Collectors.toMap(TaskInstanceDetailTb::getBizCode, TaskInstanceDetailTb::getId));
|
||||
// ========================== 顺序执行 ==========================
|
||||
executeTaskSteps(taskInstanceInfoService, taskInstanceDetailService, id, dataMap, taskTemplateNodeVos, codeIdMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行重试任务
|
||||
*
|
||||
* @param taskInstanceInfoService 任务实例信息服务
|
||||
* @param taskInstanceDetailService 任务实例明细服务
|
||||
* @param id 任务实例ID
|
||||
* @param dataMap 数据映射
|
||||
* @param taskTemplateNodeVos 模板节点列表
|
||||
* @param retryNodeCode 重试节点编码
|
||||
*/
|
||||
private void executeRetryTask(TaskInstanceInfoService taskInstanceInfoService, TaskInstanceDetailService taskInstanceDetailService, long id, JobDataMap dataMap, List<TaskTemplateNodeVo> taskTemplateNodeVos, String retryNodeCode) {
|
||||
boolean isFound = false;
|
||||
// ========================== 初始化执行明细 ==========================
|
||||
List<TaskInstanceDetailTb> taskInstanceDetails = new ArrayList<>();
|
||||
List<TaskTemplateNodeVo> taskTemplateNodeRetryList = new ArrayList<>();
|
||||
for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodeVos) {
|
||||
if (isFound) {
|
||||
taskInstanceDetails.add(buildTaskInstanceDetail(id, taskTemplateNodeVo));
|
||||
taskTemplateNodeRetryList.add(taskTemplateNodeVo);
|
||||
continue;
|
||||
}
|
||||
if (taskTemplateNodeVo.getCode().equals(retryNodeCode)) {
|
||||
if (!taskTemplateNodeVo.getRetry()) {
|
||||
throw new BadRequestException("节点 '" + taskTemplateNodeVo.getName() + "' 不支持重试");
|
||||
}
|
||||
taskInstanceDetails.add(buildTaskInstanceDetail(id, taskTemplateNodeVo));
|
||||
taskTemplateNodeRetryList.add(taskTemplateNodeVo);
|
||||
isFound = true;
|
||||
}
|
||||
}
|
||||
TaskInstanceStepDetailService taskInstanceStepDetailService = CsSpringBeanHolder.getBean(TaskInstanceStepDetailService.class);
|
||||
List<String> bizCodeList = taskInstanceDetails.stream().map(TaskInstanceDetailTb::getBizCode).distinct().collect(Collectors.toList());
|
||||
List<TaskInstanceDetailTb> taskInstanceDetailTbs = taskInstanceDetailService.queryByInstanceIdAndBizCodeList(id, bizCodeList);
|
||||
List<Long> taskInstanceDetailIds = taskInstanceDetailTbs.stream().map(TaskInstanceDetailTb::getId).collect(Collectors.toList());
|
||||
// 根据ID删除明细
|
||||
taskInstanceDetailService.removeByIds(taskInstanceDetailIds);
|
||||
// 根据明细删除步骤
|
||||
taskInstanceStepDetailService.removeByInstanceDetailIds(taskInstanceDetailIds);
|
||||
taskInstanceDetailService.saveBatch(taskInstanceDetails);
|
||||
Map<String, Long> codeIdMap = taskInstanceDetails.stream().collect(Collectors.toMap(TaskInstanceDetailTb::getBizCode, TaskInstanceDetailTb::getId));
|
||||
// ========================== 顺序执行 ==========================
|
||||
executeTaskSteps(taskInstanceInfoService, taskInstanceDetailService, id, dataMap, taskTemplateNodeRetryList, codeIdMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建任务实例明细对象
|
||||
*
|
||||
* @param id 实例ID
|
||||
* @param taskTemplateNodeVo 模板节点
|
||||
* @return 任务实例明细对象
|
||||
*/
|
||||
private TaskInstanceDetailTb buildTaskInstanceDetail(long id, TaskTemplateNodeVo taskTemplateNodeVo) {
|
||||
TaskInstanceDetailTb taskInstanceDetail = new TaskInstanceDetailTb();
|
||||
taskInstanceDetail.setInstanceId(id);
|
||||
taskInstanceDetail.setFinishTime(null);
|
||||
taskInstanceDetail.setBizCode(taskTemplateNodeVo.getCode());
|
||||
taskInstanceDetail.setBizName(taskTemplateNodeVo.getName());
|
||||
taskInstanceDetail.setExecuteInfo("未开始");
|
||||
taskInstanceDetail.setExecuteStatus("pending");
|
||||
return taskInstanceDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务步骤
|
||||
*
|
||||
* @param taskInstanceInfoService 任务实例信息服务
|
||||
* @param taskInstanceDetailService 任务实例明细服务
|
||||
* @param id 实例ID
|
||||
* @param dataMap 数据映射
|
||||
* @param taskTemplateNodes 需要执行的任务节点列表
|
||||
* @param codeIdMap 节点编码与明细ID映射
|
||||
*/
|
||||
private void executeTaskSteps(TaskInstanceInfoService taskInstanceInfoService, TaskInstanceDetailService taskInstanceDetailService, Long id, JobDataMap dataMap, List<TaskTemplateNodeVo> taskTemplateNodes, Map<String, Long> codeIdMap) {
|
||||
try {
|
||||
for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodeVos) {
|
||||
for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodes) {
|
||||
String code = taskTemplateNodeVo.getCode();
|
||||
try {
|
||||
TaskStepExecutor executor = CsSpringBeanHolder.getBean("taskStep" + getBeanAlias(code));
|
||||
@@ -124,3 +196,146 @@ public class TaskJobBean extends QuartzJobBean implements InterruptableJob {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//@Slf4j
|
||||
//public class TaskJobBean extends QuartzJobBean implements InterruptableJob {
|
||||
// private Thread subMainThread = null;
|
||||
//
|
||||
// private String getBeanAlias(String code) {
|
||||
// // node_init
|
||||
// String[] s = code.split("_");
|
||||
// return Arrays.stream(s).map(StrUtil::upperFirst).collect(Collectors.joining());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void executeInternal(JobExecutionContext context) {
|
||||
// this.subMainThread = Thread.currentThread();
|
||||
// // ========================== 获取代理类 ==========================
|
||||
// TaskInstanceInfoService taskInstanceInfoService = CsSpringBeanHolder.getBean(TaskInstanceInfoService.class);
|
||||
// TaskInstanceDetailService taskInstanceDetailService = CsSpringBeanHolder.getBean(TaskInstanceDetailService.class);
|
||||
// // ========================== 获取参数 ==========================
|
||||
// JobDataMap dataMap = context.getMergedJobDataMap();
|
||||
// long id = dataMap.getLong("id");
|
||||
// // ========================== 获取任务编排模板 ==========================
|
||||
// TaskInstanceInfoTb taskInstanceInfoVo = taskInstanceInfoService.getById(id);
|
||||
// String templateInfo = taskInstanceInfoVo.getTemplate();
|
||||
// List<TaskTemplateNodeVo> taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class);
|
||||
// // ========================== 判断是否重试任务 ==========================
|
||||
// String retryNodeCode = dataMap.getString("retryNodeCode");
|
||||
// if (StrUtil.isBlank(retryNodeCode)) {
|
||||
// // ========================== 初始化执行明细 ==========================
|
||||
// List<TaskInstanceDetailTb> taskInstanceDetails = new ArrayList<>();
|
||||
// for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodeVos) {
|
||||
// TaskInstanceDetailTb taskInstanceDetail = new TaskInstanceDetailTb();
|
||||
// taskInstanceDetail.setInstanceId(id);
|
||||
// taskInstanceDetail.setFinishTime(null);
|
||||
// taskInstanceDetail.setBizCode(taskTemplateNodeVo.getCode());
|
||||
// taskInstanceDetail.setBizName(taskTemplateNodeVo.getName());
|
||||
// taskInstanceDetail.setExecuteInfo("未开始");
|
||||
// taskInstanceDetail.setExecuteStatus("pending");
|
||||
// taskInstanceDetails.add(taskInstanceDetail);
|
||||
// }
|
||||
// taskInstanceDetailService.saveBatch(taskInstanceDetails);
|
||||
// Map<String, Long> codeIdMap = taskInstanceDetails.stream().collect(Collectors.toMap(TaskInstanceDetailTb::getBizCode, TaskInstanceDetailTb::getId));
|
||||
// // ========================== 顺序执行 ==========================
|
||||
// try {
|
||||
// for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodeVos) {
|
||||
// String code = taskTemplateNodeVo.getCode();
|
||||
// try {
|
||||
// TaskStepExecutor executor = CsSpringBeanHolder.getBean("taskStep" + getBeanAlias(code));
|
||||
// executor.execute(codeIdMap.getOrDefault(code, null), dataMap, taskTemplateNodeVo, new TaskStepCallback() {
|
||||
// @Override
|
||||
// public void onStart() {
|
||||
// taskInstanceDetailService.fastStart(id, code, dataMap);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFinish(String executeInfo) {
|
||||
// taskInstanceDetailService.fastSuccessWithInfo(id, code, executeInfo);
|
||||
// }
|
||||
// });
|
||||
// } catch (Exception e) {
|
||||
// taskInstanceDetailService.fastFailWithInfo(id, code, e.getMessage());
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// }
|
||||
// taskInstanceInfoService.fastSuccessWithData(id, dataMap);
|
||||
// } catch (Exception e) {
|
||||
// log.error("任务执行失败", e);
|
||||
// taskInstanceInfoService.fastFailWithMessageData(id, e.getMessage(), dataMap);
|
||||
// }
|
||||
// } else {
|
||||
// boolean isFound = false;
|
||||
// // ========================== 初始化执行明细 ==========================
|
||||
// List<TaskInstanceDetailTb> taskInstanceDetails = new ArrayList<>();
|
||||
// List<TaskTemplateNodeVo> taskTemplateNodeRetrys = new ArrayList<>();
|
||||
// for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodeVos) {
|
||||
// if (isFound) {
|
||||
// TaskInstanceDetailTb taskInstanceDetail = new TaskInstanceDetailTb();
|
||||
// taskInstanceDetail.setInstanceId(id);
|
||||
// taskInstanceDetail.setFinishTime(null);
|
||||
// taskInstanceDetail.setBizCode(taskTemplateNodeVo.getCode());
|
||||
// taskInstanceDetail.setBizName(taskTemplateNodeVo.getName());
|
||||
// taskInstanceDetail.setExecuteInfo("未开始");
|
||||
// taskInstanceDetail.setExecuteStatus("pending");
|
||||
// taskInstanceDetails.add(taskInstanceDetail);
|
||||
// taskTemplateNodeRetrys.add(taskTemplateNodeVo);
|
||||
// continue;
|
||||
// }
|
||||
// if (taskTemplateNodeVo.getCode().equals(retryNodeCode)) {
|
||||
// if (!taskTemplateNodeVo.getRetry()) {
|
||||
// throw new BadRequestException("节点 " + retryNodeCode + "不支持重试");
|
||||
// }
|
||||
// TaskInstanceDetailTb taskInstanceDetail = new TaskInstanceDetailTb();
|
||||
// taskInstanceDetail.setInstanceId(id);
|
||||
// taskInstanceDetail.setFinishTime(null);
|
||||
// taskInstanceDetail.setBizCode(taskTemplateNodeVo.getCode());
|
||||
// taskInstanceDetail.setBizName(taskTemplateNodeVo.getName());
|
||||
// taskInstanceDetail.setExecuteInfo("未开始");
|
||||
// taskInstanceDetail.setExecuteStatus("pending");
|
||||
// taskInstanceDetails.add(taskInstanceDetail);
|
||||
// taskTemplateNodeRetrys.add(taskTemplateNodeVo);
|
||||
// isFound = true;
|
||||
// }
|
||||
// }
|
||||
// taskInstanceDetailService.removeByInstanceIdAndBizCodeList(id, taskInstanceDetails.stream().map(TaskInstanceDetailTb::getBizCode).distinct().collect(Collectors.toList()));
|
||||
// taskInstanceDetailService.saveBatch(taskInstanceDetails);
|
||||
// Map<String, Long> codeIdMap = taskInstanceDetails.stream().collect(Collectors.toMap(TaskInstanceDetailTb::getBizCode, TaskInstanceDetailTb::getId));
|
||||
// // ========================== 顺序执行 ==========================
|
||||
// try {
|
||||
// for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodeRetrys) {
|
||||
// String code = taskTemplateNodeVo.getCode();
|
||||
// try {
|
||||
// TaskStepExecutor executor = CsSpringBeanHolder.getBean("taskStep" + getBeanAlias(code));
|
||||
// executor.execute(codeIdMap.getOrDefault(code, null), dataMap, taskTemplateNodeVo, new TaskStepCallback() {
|
||||
// @Override
|
||||
// public void onStart() {
|
||||
// taskInstanceDetailService.fastStart(id, code, dataMap);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFinish(String executeInfo) {
|
||||
// taskInstanceDetailService.fastSuccessWithInfo(id, code, executeInfo);
|
||||
// }
|
||||
// });
|
||||
// } catch (Exception e) {
|
||||
// taskInstanceDetailService.fastFailWithInfo(id, code, e.getMessage());
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// }
|
||||
// taskInstanceInfoService.fastSuccessWithData(id, dataMap);
|
||||
// } catch (Exception e) {
|
||||
// log.error("任务执行失败", e);
|
||||
// taskInstanceInfoService.fastFailWithMessageData(id, e.getMessage(), dataMap);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void interrupt() {
|
||||
// if (subMainThread != null) {
|
||||
// subMainThread.stop();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -15,11 +15,20 @@
|
||||
*/
|
||||
package cn.odboy.task.core;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.odboy.framework.exception.web.BadRequestException;
|
||||
import cn.odboy.task.constant.TaskChangeTypeEnum;
|
||||
import cn.odboy.task.constant.TaskStatusEnum;
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb;
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceInfoTb;
|
||||
import cn.odboy.task.dal.model.TaskInstanceInfoVo;
|
||||
import cn.odboy.task.dal.model.TaskInstanceNodeVo;
|
||||
import cn.odboy.task.dal.model.TaskTemplateInfoVo;
|
||||
import cn.odboy.task.dal.model.TaskTemplateNodeVo;
|
||||
import cn.odboy.task.service.TaskInstanceDetailService;
|
||||
import cn.odboy.task.service.TaskInstanceInfoService;
|
||||
import cn.odboy.task.service.TaskTemplateInfoService;
|
||||
import cn.odboy.util.CsDateUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.*;
|
||||
@@ -28,7 +37,9 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -36,44 +47,62 @@ public class TaskManage {
|
||||
@Resource
|
||||
private Scheduler scheduler;
|
||||
@Autowired
|
||||
private TaskTemplateInfoService taskTemplateInfoService;
|
||||
@Autowired
|
||||
private TaskInstanceInfoService taskInstanceInfoService;
|
||||
@Autowired
|
||||
private TaskInstanceDetailService taskInstanceDetailService;
|
||||
|
||||
/**
|
||||
* 创建任务单
|
||||
*
|
||||
* @param contextName 上下文名称,这里特指应用名
|
||||
* @param changeType 变更类型
|
||||
* @param envAlias 环境别名
|
||||
* @param source 来源
|
||||
* @param reason 变更原因
|
||||
* @param dataMap 任务参数
|
||||
* @param contextName 上下文名称,这里特指应用名
|
||||
* @param changeTypeEnum 变更类型
|
||||
* @param envAlias 环境别名
|
||||
* @param source 来源
|
||||
* @param reason 变更原因
|
||||
* @param dataMap 任务参数
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public TaskInstanceInfoTb createJob(String contextName, TaskChangeTypeEnum changeType, String language, String envAlias, String source, String reason, JobDataMap dataMap) {
|
||||
public TaskInstanceInfoTb createJob(String contextName, TaskChangeTypeEnum changeTypeEnum, String language, String envAlias, String source, String reason, JobDataMap dataMap) {
|
||||
if (dataMap == null) {
|
||||
dataMap = new JobDataMap();
|
||||
}
|
||||
// ========================== 获取任务编排模板 ==========================
|
||||
TaskTemplateInfoVo taskInstanceInfoVo = taskTemplateInfoService.getTemplateInfoByECL(envAlias, contextName, language, changeTypeEnum.getCode());
|
||||
if (taskInstanceInfoVo == null) {
|
||||
throw new BadRequestException("没有查询到任务编排模板");
|
||||
}
|
||||
String templateInfo = taskInstanceInfoVo.getTemplateInfo();
|
||||
List<TaskTemplateNodeVo> taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class);
|
||||
if (taskTemplateNodeVos == null || taskTemplateNodeVos.isEmpty()) {
|
||||
throw new BadRequestException("没有查询到任务编排明细");
|
||||
}
|
||||
// ========================== 传递参数 ==========================
|
||||
// 应用名、资源类型
|
||||
dataMap.put("contextName", contextName);
|
||||
// 开发语言、资源版本
|
||||
dataMap.put("language", language);
|
||||
dataMap.put("envAlias", envAlias);
|
||||
// 变更类型
|
||||
dataMap.put("changeType", changeTypeEnum.getCode());
|
||||
// ========================== 创建任务 ==========================
|
||||
TaskInstanceInfoTb newInstance = new TaskInstanceInfoTb();
|
||||
newInstance.setContextName(contextName);
|
||||
newInstance.setChangeType(changeType.getCode());
|
||||
newInstance.setLanguage(language);
|
||||
newInstance.setChangeType(changeTypeEnum.getCode());
|
||||
newInstance.setEnvAlias(envAlias);
|
||||
newInstance.setStatus(TaskStatusEnum.Running.getCode());
|
||||
newInstance.setFinishTime(null);
|
||||
newInstance.setSource(source);
|
||||
newInstance.setReason(reason);
|
||||
newInstance.setTemplate(templateInfo);
|
||||
newInstance.setJobData(JSON.toJSONString(dataMap));
|
||||
taskInstanceInfoService.save(newInstance);
|
||||
dataMap.put("id", newInstance.getId());
|
||||
// ========================== 执行任务 ==========================
|
||||
JobKey jobKey = JobKey.jobKey(changeType.getCode(), contextName);
|
||||
TriggerKey triggerKey = TriggerKey.triggerKey(changeType.getCode(), contextName);
|
||||
JobKey jobKey = JobKey.jobKey(changeTypeEnum.getCode(), contextName);
|
||||
TriggerKey triggerKey = TriggerKey.triggerKey(changeTypeEnum.getCode(), contextName);
|
||||
JobDetail jobDetail = JobBuilder.newJob(TaskJobBean.class).withIdentity(jobKey).usingJobData(dataMap).build();
|
||||
Trigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startNow().build();
|
||||
try {
|
||||
@@ -114,4 +143,89 @@ public class TaskManage {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public TaskInstanceInfoTb retryJob(Long id, String retryNodeCode) {
|
||||
// ========================== 查询任务 ==========================
|
||||
TaskInstanceInfoTb taskInstanceInfoTb = taskInstanceInfoService.getById(id);
|
||||
if (taskInstanceInfoTb == null) {
|
||||
throw new BadRequestException("任务不存在");
|
||||
}
|
||||
// ========================== 失败的任务才能重试 ==========================
|
||||
if (TaskStatusEnum.Success.getCode().equals(taskInstanceInfoTb.getStatus())) {
|
||||
throw new BadRequestException("任务已执行完毕,无法重试");
|
||||
}
|
||||
// 参数重放
|
||||
JobDataMap dataMap = JSON.parseObject(taskInstanceInfoTb.getJobData(), JobDataMap.class);
|
||||
if (dataMap == null) {
|
||||
dataMap = new JobDataMap();
|
||||
}
|
||||
taskInstanceInfoTb.setStatus(TaskStatusEnum.Running.getCode());
|
||||
taskInstanceInfoTb.setFinishTime(null);
|
||||
taskInstanceInfoService.updateById(taskInstanceInfoTb);
|
||||
// ========================== 执行任务 ==========================
|
||||
dataMap.put("retryNodeCode", retryNodeCode);
|
||||
String changeType = taskInstanceInfoTb.getChangeType();
|
||||
String contextName = taskInstanceInfoTb.getContextName();
|
||||
JobKey jobKey = JobKey.jobKey(changeType, contextName);
|
||||
TriggerKey triggerKey = TriggerKey.triggerKey(changeType, contextName);
|
||||
JobDetail jobDetail = JobBuilder.newJob(TaskJobBean.class).withIdentity(jobKey).usingJobData(dataMap).build();
|
||||
Trigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startNow().build();
|
||||
try {
|
||||
scheduler.scheduleJob(jobDetail, cronTrigger);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
throw new BadRequestException("任务已存在,跳过加载");
|
||||
} catch (SchedulerException e) {
|
||||
log.error("任务执行失败", e);
|
||||
throw new BadRequestException(e);
|
||||
}
|
||||
return taskInstanceInfoTb;
|
||||
}
|
||||
|
||||
public TaskInstanceInfoVo getLastInfo(String contextName, String language, String envAlias, String changeType) {
|
||||
TaskInstanceInfoVo record = new TaskInstanceInfoVo();
|
||||
|
||||
TaskInstanceInfoTb historyInstance = taskInstanceInfoService.getLastHistoryInstance(contextName, language, envAlias, changeType);
|
||||
if (historyInstance == null) {
|
||||
// 仅返回模板
|
||||
TaskTemplateInfoVo templateInfo = taskTemplateInfoService.getTemplateInfoByECL(envAlias, contextName, language, changeType);
|
||||
record.setTemplate(templateInfo.getTemplateInfo());
|
||||
return record;
|
||||
}
|
||||
|
||||
record = BeanUtil.copyProperties(historyInstance, TaskInstanceInfoVo.class);
|
||||
record.setHistory(buildNodeList(record));
|
||||
|
||||
TaskInstanceInfoTb runningInstance = taskInstanceInfoService.getLastRunningInstance(contextName, language, envAlias, changeType);
|
||||
if (runningInstance != null) {
|
||||
TaskInstanceInfoVo taskInstanceInfoVo = BeanUtil.copyProperties(runningInstance, TaskInstanceInfoVo.class);
|
||||
record.setCurrent(buildNodeList(taskInstanceInfoVo));
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
private List<TaskInstanceNodeVo> buildNodeList(TaskInstanceInfoVo record) {
|
||||
List<TaskInstanceNodeVo> records = new ArrayList<>();
|
||||
|
||||
// 节点明细
|
||||
List<TaskInstanceDetailTb> taskInstanceDetails = taskInstanceDetailService.queryByInstanceId(record.getId());
|
||||
for (TaskInstanceDetailTb taskInstanceDetail : taskInstanceDetails) {
|
||||
TaskInstanceNodeVo instanceNodeVo = new TaskInstanceNodeVo();
|
||||
instanceNodeVo.setCode(taskInstanceDetail.getBizCode());
|
||||
instanceNodeVo.setName(taskInstanceDetail.getBizName());
|
||||
instanceNodeVo.setStartTime(taskInstanceDetail.getStartTime());
|
||||
instanceNodeVo.setFinishTime(taskInstanceDetail.getFinishTime());
|
||||
if (taskInstanceDetail.getFinishTime() == null) {
|
||||
instanceNodeVo.setDurationDesc(CsDateUtil.formatSecondsDuration(taskInstanceDetail.getStartTime(), new Date()));
|
||||
} else {
|
||||
instanceNodeVo.setDurationDesc(CsDateUtil.formatSecondsDuration(taskInstanceDetail.getStartTime(), taskInstanceDetail.getFinishTime()));
|
||||
}
|
||||
instanceNodeVo.setRunningDesc(taskInstanceDetail.getExecuteInfo());
|
||||
instanceNodeVo.setStatus(taskInstanceDetail.getExecuteStatus());
|
||||
instanceNodeVo.setStatusDesc(TaskStatusEnum.getDesc(taskInstanceDetail.getExecuteStatus()));
|
||||
records.add(instanceNodeVo);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -72,6 +72,13 @@ public class TaskInstanceInfoTb extends CsObject {
|
||||
@TableField("context_name")
|
||||
private String contextName;
|
||||
|
||||
/**
|
||||
* 语言
|
||||
*/
|
||||
@ApiModelProperty("语言")
|
||||
@TableField("`language`")
|
||||
private String language;
|
||||
|
||||
/**
|
||||
* 变更类型
|
||||
*/
|
||||
@@ -114,6 +121,13 @@ public class TaskInstanceInfoTb extends CsObject {
|
||||
@ApiModelProperty("变更原因")
|
||||
private String reason;
|
||||
|
||||
/**
|
||||
* 任务模板
|
||||
*/
|
||||
@TableField("template")
|
||||
@ApiModelProperty("template")
|
||||
private String template;
|
||||
|
||||
/**
|
||||
* QuartzJob参数
|
||||
*/
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package cn.odboy.task.dal.model;
|
||||
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceInfoTb;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务实例数据
|
||||
*
|
||||
* @author odboy
|
||||
* @date 2025-10-01
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TaskInstanceInfoVo extends TaskInstanceInfoTb {
|
||||
/**
|
||||
* 进行中
|
||||
*/
|
||||
private List<TaskInstanceNodeVo> current;
|
||||
/**
|
||||
* 历史已完成或异常
|
||||
*/
|
||||
private List<TaskInstanceNodeVo> history;
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.task.dal.model;
|
||||
|
||||
import cn.odboy.base.CsObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 任务节点数据
|
||||
*
|
||||
* @author odboy
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TaskInstanceNodeVo extends CsObject {
|
||||
/**
|
||||
* 业务编码
|
||||
*/
|
||||
protected String code;
|
||||
/**
|
||||
* 业务名称
|
||||
*/
|
||||
protected String name;
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
protected Date startTime;
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
protected Date finishTime;
|
||||
/**
|
||||
* 耗时多久
|
||||
*/
|
||||
protected String durationDesc;
|
||||
/**
|
||||
* 进行中描述
|
||||
*/
|
||||
protected String runningDesc;
|
||||
/**
|
||||
* 节点状态编码
|
||||
*/
|
||||
protected String status;
|
||||
/**
|
||||
* 节点状态描述
|
||||
*/
|
||||
protected String statusDesc;
|
||||
}
|
||||
+13
-1
@@ -31,5 +31,17 @@ import org.apache.ibatis.annotations.Param;
|
||||
*/
|
||||
@Mapper
|
||||
public interface TaskTemplateInfoMapper extends BaseMapper<TaskTemplateInfoTb> {
|
||||
TaskTemplateInfoVo selectTemplateInfoByECL(@Param("envAlias") String envAlias, @Param("contextName") String contextName, @Param("language") String language);
|
||||
/**
|
||||
* @param envAlias 环境别称
|
||||
* @param contextName 应用名称、资源类型
|
||||
* @param language 语言、版本
|
||||
* @param changeType 变更类型
|
||||
* @return /
|
||||
*/
|
||||
TaskTemplateInfoVo selectTemplateInfoByECL(
|
||||
@Param("envAlias") String envAlias,
|
||||
@Param("contextName") String contextName,
|
||||
@Param("language") String language,
|
||||
@Param("changeType") String changeType
|
||||
);
|
||||
}
|
||||
|
||||
+8
-2
@@ -19,6 +19,8 @@ import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.quartz.JobDataMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 任务实例明细 服务类
|
||||
@@ -30,7 +32,11 @@ import org.quartz.JobDataMap;
|
||||
public interface TaskInstanceDetailService extends IService<TaskInstanceDetailTb> {
|
||||
void fastFailWithInfo(Long instanceId, String code, String message);
|
||||
|
||||
void fastSuccessWithInfo(Long id, String code, String executeInfo);
|
||||
void fastSuccessWithInfo(Long instanceId, String code, String executeInfo);
|
||||
|
||||
void fastStart(Long id, String code, JobDataMap dataMap);
|
||||
void fastStart(Long instanceId, String code, JobDataMap dataMap);
|
||||
|
||||
List<TaskInstanceDetailTb> queryByInstanceIdAndBizCodeList(Long instanceId, List<String> bizCodes);
|
||||
|
||||
List<TaskInstanceDetailTb> queryByInstanceId(Long instanceId);
|
||||
}
|
||||
|
||||
+3
@@ -36,4 +36,7 @@ public interface TaskInstanceInfoService extends IService<TaskInstanceInfoTb> {
|
||||
|
||||
void fastSuccessWithData(Long id, JobDataMap dataMap);
|
||||
|
||||
TaskInstanceInfoTb getLastRunningInstance(String contextName, String language, String envAlias, String changeType);
|
||||
|
||||
TaskInstanceInfoTb getLastHistoryInstance(String contextName, String language, String envAlias, String changeType);
|
||||
}
|
||||
|
||||
+9
@@ -18,6 +18,8 @@ package cn.odboy.task.service;
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceStepDetailTb;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 任务实例步骤明细 服务类
|
||||
@@ -42,4 +44,11 @@ public interface TaskInstanceStepDetailService extends IService<TaskInstanceStep
|
||||
* @param stepDesc 步骤描述
|
||||
*/
|
||||
void fail(Long instanceDetailId, String stepDesc);
|
||||
|
||||
/**
|
||||
* 根据明细ID删除步骤明细
|
||||
*
|
||||
* @param instanceDetailIds
|
||||
*/
|
||||
void removeByInstanceDetailIds(List<Long> instanceDetailIds);
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,5 +28,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
public interface TaskTemplateInfoService extends IService<TaskTemplateInfoTb> {
|
||||
TaskTemplateInfoVo getTemplateInfoByECL(String envAlias, String contextName, String language);
|
||||
TaskTemplateInfoVo getTemplateInfoByECL(String envAlias, String contextName, String language, String changeType);
|
||||
}
|
||||
|
||||
+19
-12
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package cn.odboy.task.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.odboy.task.constant.TaskStatusEnum;
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb;
|
||||
import cn.odboy.task.dal.mysql.TaskInstanceDetailMapper;
|
||||
@@ -25,6 +26,7 @@ import org.quartz.JobDataMap;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -40,12 +42,10 @@ public class TaskInstanceDetailServiceImpl extends ServiceImpl<TaskInstanceDetai
|
||||
@Override
|
||||
public void fastFailWithInfo(Long instanceId, String code, String executeInfo) {
|
||||
TaskInstanceDetailTb updRecord = new TaskInstanceDetailTb();
|
||||
updRecord.setFinishTime(new Date());
|
||||
updRecord.setExecuteInfo(executeInfo);
|
||||
updRecord.setExecuteStatus(TaskStatusEnum.Fail.getCode());
|
||||
lambdaUpdate()
|
||||
.eq(TaskInstanceDetailTb::getInstanceId, instanceId)
|
||||
.eq(TaskInstanceDetailTb::getBizCode, code)
|
||||
.update(updRecord);
|
||||
lambdaUpdate().eq(TaskInstanceDetailTb::getInstanceId, instanceId).eq(TaskInstanceDetailTb::getBizCode, code).update(updRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,10 +54,7 @@ public class TaskInstanceDetailServiceImpl extends ServiceImpl<TaskInstanceDetai
|
||||
updRecord.setFinishTime(new Date());
|
||||
updRecord.setExecuteInfo(executeInfo);
|
||||
updRecord.setExecuteStatus(TaskStatusEnum.Success.getCode());
|
||||
lambdaUpdate()
|
||||
.eq(TaskInstanceDetailTb::getInstanceId, instanceId)
|
||||
.eq(TaskInstanceDetailTb::getBizCode, code)
|
||||
.update(updRecord);
|
||||
lambdaUpdate().eq(TaskInstanceDetailTb::getInstanceId, instanceId).eq(TaskInstanceDetailTb::getBizCode, code).update(updRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,9 +64,19 @@ public class TaskInstanceDetailServiceImpl extends ServiceImpl<TaskInstanceDetai
|
||||
updRecord.setExecuteInfo("运行中");
|
||||
updRecord.setExecuteParams(JSON.toJSONString(dataMap));
|
||||
updRecord.setExecuteStatus(TaskStatusEnum.Running.getCode());
|
||||
lambdaUpdate()
|
||||
.eq(TaskInstanceDetailTb::getInstanceId, instanceId)
|
||||
.eq(TaskInstanceDetailTb::getBizCode, code)
|
||||
.update(updRecord);
|
||||
lambdaUpdate().eq(TaskInstanceDetailTb::getInstanceId, instanceId).eq(TaskInstanceDetailTb::getBizCode, code).update(updRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TaskInstanceDetailTb> queryByInstanceIdAndBizCodeList(Long instanceId, List<String> bizCodeList) {
|
||||
if (CollUtil.isEmpty(bizCodeList)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
return lambdaQuery().eq(TaskInstanceDetailTb::getInstanceId, instanceId).in(TaskInstanceDetailTb::getBizCode, bizCodeList).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TaskInstanceDetailTb> queryByInstanceId(Long instanceId) {
|
||||
return lambdaQuery().eq(TaskInstanceDetailTb::getInstanceId, instanceId).orderByAsc(TaskInstanceDetailTb::getId).list();
|
||||
}
|
||||
}
|
||||
|
||||
+24
@@ -80,4 +80,28 @@ public class TaskInstanceInfoServiceImpl extends ServiceImpl<TaskInstanceInfoMap
|
||||
updRecord.setJobData(JSON.toJSONString(dataMap));
|
||||
updateById(updRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskInstanceInfoTb getLastRunningInstance(String contextName, String language, String envAlias, String changeType) {
|
||||
return lambdaQuery()
|
||||
.eq(TaskInstanceInfoTb::getContextName, contextName)
|
||||
.eq(TaskInstanceInfoTb::getLanguage, language)
|
||||
.eq(TaskInstanceInfoTb::getEnvAlias, envAlias)
|
||||
.eq(TaskInstanceInfoTb::getChangeType, changeType)
|
||||
.eq(TaskInstanceInfoTb::getStatus, TaskStatusEnum.Running.getCode())
|
||||
.orderByDesc(TaskInstanceInfoTb::getId)
|
||||
.one();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskInstanceInfoTb getLastHistoryInstance(String contextName, String language, String envAlias, String changeType) {
|
||||
return lambdaQuery()
|
||||
.eq(TaskInstanceInfoTb::getContextName, contextName)
|
||||
.eq(TaskInstanceInfoTb::getLanguage, language)
|
||||
.eq(TaskInstanceInfoTb::getEnvAlias, envAlias)
|
||||
.eq(TaskInstanceInfoTb::getChangeType, changeType)
|
||||
.orderByDesc(TaskInstanceInfoTb::getId)
|
||||
.last("LIMIT 1")
|
||||
.one();
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -15,14 +15,18 @@
|
||||
*/
|
||||
package cn.odboy.task.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.odboy.framework.exception.web.BadRequestException;
|
||||
import cn.odboy.task.constant.TaskStatusEnum;
|
||||
import cn.odboy.task.dal.dataobject.TaskInstanceStepDetailTb;
|
||||
import cn.odboy.task.dal.mysql.TaskInstanceStepDetailMapper;
|
||||
import cn.odboy.task.service.TaskInstanceStepDetailService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 任务实例步骤明细 服务实现类
|
||||
@@ -52,4 +56,14 @@ public class TaskInstanceStepDetailServiceImpl extends ServiceImpl<TaskInstanceS
|
||||
save(stepDetail);
|
||||
throw new BadRequestException(stepDesc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeByInstanceDetailIds(List<Long> instanceDetailIds) {
|
||||
if (CollUtil.isEmpty(instanceDetailIds)) {
|
||||
return;
|
||||
}
|
||||
remove(new LambdaQueryWrapper<TaskInstanceStepDetailTb>()
|
||||
.in(TaskInstanceStepDetailTb::getInstanceDetailId, instanceDetailIds)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -34,7 +34,7 @@ import org.springframework.stereotype.Service;
|
||||
public class TaskTemplateInfoServiceImpl extends ServiceImpl<TaskTemplateInfoMapper, TaskTemplateInfoTb> implements TaskTemplateInfoService {
|
||||
|
||||
@Override
|
||||
public TaskTemplateInfoVo getTemplateInfoByECL(String envAlias, String contextName, String language) {
|
||||
return baseMapper.selectTemplateInfoByECL(envAlias, contextName, language);
|
||||
public TaskTemplateInfoVo getTemplateInfoByECL(String envAlias, String contextName, String language, String changeType) {
|
||||
return baseMapper.selectTemplateInfoByECL(envAlias, contextName, language, changeType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@
|
||||
WHERE t1.context_name = #{contextName}
|
||||
AND t2.env_alias = #{envAlias}
|
||||
AND t2.`language` = #{language}
|
||||
AND t2.`change_type` = #{changeType}
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -53,10 +53,10 @@ spring:
|
||||
druid:
|
||||
db-type: com.alibaba.druid.pool.DruidDataSource
|
||||
# sql监控
|
||||
# driverClassName: com.p6spy.engine.spy.P6SpyDriver
|
||||
# url: jdbc:p6spy:mysql://192.168.100.128:23306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://192.168.100.128:23306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
|
||||
driverClassName: com.p6spy.engine.spy.P6SpyDriver
|
||||
url: jdbc:p6spy:mysql://192.168.100.128:23306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
|
||||
# driverClassName: com.mysql.cj.jdbc.Driver
|
||||
# url: jdbc:mysql://192.168.100.128:23306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: kd123456
|
||||
# 初始连接数,建议设置为与最小空闲连接数相同
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
TRUNCATE TABLE system_operation_log;
|
||||
TRUNCATE TABLE system_oss_storage;
|
||||
TRUNCATE TABLE system_quartz_log;
|
||||
TRUNCATE TABLE task_instance_info;
|
||||
TRUNCATE TABLE task_instance_detail;
|
||||
TRUNCATE TABLE task_instance_step_detail;
|
||||
update system_dept set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update system_dict set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update system_dict_detail set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update system_job set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update system_local_storage set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update system_menu set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update system_quartz_job set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update system_role set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update system_user set create_by='admin', update_by='admin', create_time = '2021-03-20 22:04:13', update_time = '2021-03-20 22:04:13';
|
||||
update task_pipeline_config set create_by='admin', create_time = '2021-03-20 22:04:13';
|
||||
update task_template_info set create_by='admin', create_time = '2021-03-20 22:04:13';
|
||||
@@ -10,8 +10,6 @@
|
||||
Target Server Type : MySQL
|
||||
Target Server Version : 80025 (8.0.25)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 09/10/2025 12:25:16
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
@@ -26,7 +24,7 @@ CREATE TABLE `demo_time_table_copy1` (
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of demo_time_table_copy1
|
||||
@@ -44,7 +42,7 @@ CREATE TABLE `demo_user_time_logic_table` (
|
||||
`update_time` datetime NOT NULL COMMENT '修改时间',
|
||||
`available` tinyint(1) NOT NULL DEFAULT 1 COMMENT '数据有效性',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of demo_user_time_logic_table
|
||||
@@ -61,7 +59,7 @@ CREATE TABLE `demo_user_time_table` (
|
||||
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '修改人',
|
||||
`update_time` datetime NOT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of demo_user_time_table
|
||||
@@ -221,7 +219,7 @@ CREATE TABLE `qrtz_scheduler_state` (
|
||||
-- Records of qrtz_scheduler_state
|
||||
-- ----------------------------
|
||||
INSERT INTO `qrtz_scheduler_state` VALUES ('ClusteredScheduler', 'DESKTOP-FM44BLS1742558233429', 1742558255052, 20000);
|
||||
INSERT INTO `qrtz_scheduler_state` VALUES ('cutejava', 'auto', 1759044047208, 10000);
|
||||
INSERT INTO `qrtz_scheduler_state` VALUES ('cutejava', 'auto', 1760951912083, 10000);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for qrtz_simple_triggers
|
||||
@@ -355,7 +353,7 @@ CREATE TABLE `system_dict` (
|
||||
`create_time` datetime NULL DEFAULT NULL COMMENT '创建日期',
|
||||
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '数据字典' ROW_FORMAT = DYNAMIC;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '数据字典' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_dict
|
||||
@@ -363,6 +361,7 @@ CREATE TABLE `system_dict` (
|
||||
INSERT INTO `system_dict` VALUES (1, 'user_status', '用户状态', 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict` VALUES (4, 'dept_status', '部门状态', 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict` VALUES (5, 'job_status', '岗位状态', 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict` VALUES (8, 'test_status', '测试状态', 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_dict_detail
|
||||
@@ -380,7 +379,7 @@ CREATE TABLE `system_dict_detail` (
|
||||
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_dict_id`(`dict_id` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '数据字典详情' ROW_FORMAT = DYNAMIC;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '数据字典详情' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_dict_detail
|
||||
@@ -391,6 +390,28 @@ INSERT INTO `system_dict_detail` VALUES (3, 4, '启用', 'true', 1, 'admin', 'ad
|
||||
INSERT INTO `system_dict_detail` VALUES (4, 4, '停用', 'false', 2, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (5, 5, '启用', 'true', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (6, 5, '停用', 'false', 2, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (7, 8, '打瞌睡01', 'ks01', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (8, 8, '打瞌睡2', 'ks02', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (9, 8, '打瞌睡3', 'ks03', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (10, 8, '打瞌睡4', 'ks04', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (11, 8, '打瞌睡5', 'ks05', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (12, 8, '打瞌睡6', 'ks06', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (13, 8, '打瞌睡7', 'ks07', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (14, 8, '打瞌睡8', 'ks08', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (15, 8, '打瞌睡9', 'ks09', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (16, 8, '打瞌睡10', 'ks10', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (17, 8, '打瞌睡11', 'ks11', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (18, 8, '打瞌睡12', 'ks12', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (19, 8, '打瞌睡13', 'ks13', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (20, 8, '打瞌睡14', 'ks14', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (21, 8, '打瞌睡15', 'ks15', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (22, 8, '打瞌睡16', 'ks16', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (23, 8, '打瞌睡17', 'ks17', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (24, 8, '打瞌睡18', 'ks18', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (25, 8, '打瞌睡19', 'ks19', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (26, 8, '打瞌睡20', 'ks20', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (27, 8, '打瞌睡21', 'ks21', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
INSERT INTO `system_dict_detail` VALUES (28, 8, '打瞌睡22', 'ks22', 1, 'admin', 'admin', '2021-03-20 22:04:13', '2021-03-20 22:04:13');
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_email_config
|
||||
@@ -571,7 +592,7 @@ CREATE TABLE `system_operation_log` (
|
||||
`exception_detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,
|
||||
`create_time` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '审计日志' ROW_FORMAT = DYNAMIC;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '审计日志' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_operation_log
|
||||
@@ -654,7 +675,7 @@ CREATE TABLE `system_quartz_log` (
|
||||
`exception_detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '异常详情',
|
||||
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '定时任务日志' ROW_FORMAT = DYNAMIC;
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '定时任务日志' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_quartz_log
|
||||
@@ -793,12 +814,19 @@ INSERT INTO `system_roles_menus` VALUES (121, 2);
|
||||
INSERT INTO `system_roles_menus` VALUES (122, 1);
|
||||
INSERT INTO `system_roles_menus` VALUES (122, 2);
|
||||
INSERT INTO `system_roles_menus` VALUES (125, 1);
|
||||
INSERT INTO `system_roles_menus` VALUES (125, 2);
|
||||
INSERT INTO `system_roles_menus` VALUES (126, 1);
|
||||
INSERT INTO `system_roles_menus` VALUES (126, 2);
|
||||
INSERT INTO `system_roles_menus` VALUES (127, 1);
|
||||
INSERT INTO `system_roles_menus` VALUES (127, 2);
|
||||
INSERT INTO `system_roles_menus` VALUES (128, 1);
|
||||
INSERT INTO `system_roles_menus` VALUES (128, 2);
|
||||
INSERT INTO `system_roles_menus` VALUES (129, 1);
|
||||
INSERT INTO `system_roles_menus` VALUES (129, 2);
|
||||
INSERT INTO `system_roles_menus` VALUES (130, 1);
|
||||
INSERT INTO `system_roles_menus` VALUES (130, 2);
|
||||
INSERT INTO `system_roles_menus` VALUES (132, 1);
|
||||
INSERT INTO `system_roles_menus` VALUES (132, 2);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_user
|
||||
@@ -1203,12 +1231,14 @@ CREATE TABLE `task_instance_info` (
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL COMMENT '修改时间',
|
||||
`context_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '名称',
|
||||
`language` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '语言',
|
||||
`change_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更类型',
|
||||
`env_alias` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '环境别名',
|
||||
`status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '状态(running进行中 success成功 fail失败)',
|
||||
`finish_time` datetime NULL DEFAULT NULL COMMENT '完成时间',
|
||||
`source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源',
|
||||
`reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更原因',
|
||||
`template` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '任务模板',
|
||||
`job_data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'QuartzJob参数',
|
||||
`error_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '异常信息',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
|
||||
+6
-6
@@ -106,12 +106,12 @@
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>3.50.0</version>
|
||||
</dependency>
|
||||
<!-- <!– https://mvnrepository.com/artifact/org.redisson/redisson –>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.redisson</groupId>-->
|
||||
<!-- <artifactId>redisson</artifactId>-->
|
||||
<!-- <version>3.50.0</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- Spring boot websocket -->
|
||||
<dependency>
|
||||
|
||||
Reference in New Issue
Block a user