feat(all): 小版本更新

- 新增任务重试接口,支持指定节点重试
- 添加获取最后一次任务执行记录的功能
- 完善任务执行逻辑,区分普通任务和重试任务
- 优化任务模板获取逻辑,增加变更类型参数
- 增强任务状态判断,防止已完成任务被重复执行
- 补充任务实例明细的批量删除和重建机制
- 提供任务执行时长格式化工具方法
- 增加任务状态枚举描述获取方法
- 注释掉Redisson配置类代码
- 新增任务实例VO模型用于返回历史执行记录
This commit is contained in:
2025-10-21 18:09:21 +08:00
parent ae614d7516
commit df012af194
26 changed files with 848 additions and 294 deletions
+2 -10
View File
@@ -80,17 +80,9 @@ https://github.com/odboy-tianjun/cutejava
![CuteFileUpload](./cutejava-front/docs/images/CuteFileUpload.png) ![CuteFileUpload](./cutejava-front/docs/images/CuteFileUpload.png)
- CuteFileDragUpload 支持拖拽上传文件到OSS, 必须结合Minio使用(上传完成后必须销毁组件) - CuteFileDragUpload 支持拖拽上传文件到OSS, 必须结合Minio使用(上传完成后必须销毁组件)
![CuteFileDragUpload](./cutejava-front/docs/images/CuteFileDragUpload.png) ![CuteFileDragUpload](./cutejava-front/docs/images/CuteFileDragUpload.png)
- CutePipelineNode 流水线节点组件
![CutePipelineNode](./cutejava-front/docs/images/CutePipelineNode.png)
#### 系统模块 #### 系统模块
- cutejava-framework:系统基础框架。提供核心功能,如:权限、数据权限、异常处理、缓存、工具类、接口限流、接口防重、接口幂等。 - cutejava-framework:系统基础框架。提供核心功能,如:权限、数据权限、异常处理、缓存、工具类、接口限流、接口防重、接口幂等。
- cutejava-system:系统功能模块。提供用户、角色、菜单、部门、岗位、字典、定时任务、邮件、Minio存储、服务监控等功能 - cutejava-module-system:系统功能模块。提供用户、角色、菜单、部门、岗位、字典、定时任务、邮件、Minio存储、服务监控等功能
- cutejava-devops:devops模块,已实现流水线功能。参考阿里云云效,提供流水线功能,可配置流水线任务,并支持任务依赖、任务参数、任务参数校验、任务参数回填。 - cutejava-module-task:串行任务模块,已实现流水线功能。参考阿里云云效,提供流水线功能,可配置流水线任务,并支持任务依赖、任务参数、任务参数校验、任务参数回填。
#### 特别鸣谢
- 感谢 [elunez](https://github.com/elunez/eladmin-mp) 大佬提供的基础框架
- 感谢 [aizuda](https://mongoplus.com/) 大佬提供的Mongodb框架
- 建表参考 'cutejava/doc/mongodb.md'
@@ -16,7 +16,7 @@
<cute-product-line-select v-model="form.productLine" @detail="onProductLineDetailChange" /> <cute-product-line-select v-model="form.productLine" @detail="onProductLineDetailChange" />
</el-form-item> </el-form-item>
<el-form-item label="产品线Pro" prop="productLinePro"> <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-form-item> <el-form-item>
<el-button type="primary" @click="onSubmit('form')">提 交</el-button> <el-button type="primary" @click="onSubmit('form')">提 交</el-button>
@@ -68,81 +68,6 @@ export default {
' repositories:\n' + ' repositories:\n' +
' # 是否启用Redis存储(关闭防止出现 Multiple Spring Data modules found, entering strict repository configuration mode)\n' + ' # 是否启用Redis存储(关闭防止出现 Multiple Spring Data modules found, entering strict repository configuration mode)\n' +
' enabled: false\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' + 'mybatis-plus:\n' +
' configuration:\n' + ' configuration:\n' +
' # 开启 Mybatis 二级缓存,默认为 true\n' + ' # 开启 Mybatis 二级缓存,默认为 true\n' +
@@ -174,19 +99,6 @@ export default {
' # 是否控制台 print mybatisplus-plus 的 LOGO\n' + ' # 是否控制台 print mybatisplus-plus 的 LOGO\n' +
' banner: true\n' + ' banner: true\n' +
'app:\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' + ' # 登录\n' +
' login:\n' + ' login:\n' +
' # 是否限制单用户登录\n' + ' # 是否限制单用户登录\n' +
@@ -235,8 +147,8 @@ export default {
' minio:\n' + ' minio:\n' +
' endpoint: http://192.168.100.128:9000\n' + ' endpoint: http://192.168.100.128:9000\n' +
' bucketName: cutejava\n' + ' bucketName: cutejava\n' +
' accessKey: 8o5oVzqvKby5TzaGcfmx\n' + ' accessKey: xxxxxx\n' +
' secretKey: 8BiK5UbmvXGJNVQ98CGiohHzT1N1FMRZqHRdJllj\n' + ' secretKey: xxxxxx\n' +
' captcha:\n' + ' captcha:\n' +
' expireTime: 300\n' + ' expireTime: 300\n' +
'## 启用debug\n' + '## 启用debug\n' +
@@ -1,97 +1,97 @@
/* ///*
* Copyright 2021-2025 Odboy // * Copyright 2021-2025 Odboy
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package cn.odboy.framework.redis; //package cn.odboy.framework.redis;
//
import cn.hutool.core.util.StrUtil; //import cn.hutool.core.util.StrUtil;
import cn.odboy.util.CsSystemUtil; //import cn.odboy.util.CsSystemUtil;
import lombok.extern.slf4j.Slf4j; //import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson; //import org.redisson.Redisson;
import org.redisson.api.RedissonClient; //import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec; //import org.redisson.client.codec.StringCodec;
import org.redisson.config.Config; //import org.redisson.config.Config;
import org.redisson.config.EqualJitterDelay; //import org.redisson.config.EqualJitterDelay;
import org.redisson.config.SingleServerConfig; //import org.redisson.config.SingleServerConfig;
import org.redisson.config.TransportMode; //import org.redisson.config.TransportMode;
import org.springframework.beans.factory.annotation.Value; //import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; //import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; //import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean; //import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; //import org.springframework.context.annotation.Configuration;
//
import java.time.Duration; //import java.time.Duration;
//
@Slf4j //@Slf4j
@Configuration //@Configuration
@AutoConfigureBefore(RedisAutoConfiguration.class) //@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedissonConfiguration { //public class RedissonConfiguration {
//
@Value("${spring.redis.host}") // @Value("${spring.redis.host}")
private String redisHost; // private String redisHost;
//
@Value("${spring.redis.port}") // @Value("${spring.redis.port}")
private int redisPort; // private int redisPort;
//
@Value("${spring.redis.database}") // @Value("${spring.redis.database}")
private int redisDatabase; // private int redisDatabase;
//
@Value("${spring.redis.password:}") // @Value("${spring.redis.password:}")
private String redisPassword; // private String redisPassword;
//
@Value("${spring.redis.timeout:5000}") // @Value("${spring.redis.timeout:5000}")
private int timeout; // private int timeout;
//
@Value("${spring.redis.lettuce.pool.max-active:64}") // @Value("${spring.redis.lettuce.pool.max-active:64}")
private int connectionPoolSize; // private int connectionPoolSize;
//
@Value("${spring.redis.lettuce.pool.min-idle:16}") // @Value("${spring.redis.lettuce.pool.min-idle:16}")
private int connectionMinimumIdleSize; // private int connectionMinimumIdleSize;
//
@Bean // @Bean
public RedissonClient redissonClient() { // public RedissonClient redissonClient() {
Config config = new Config(); // Config config = new Config();
config.setThreads(CsSystemUtil.getCpuCount()); // config.setThreads(CsSystemUtil.getCpuCount());
config.setNettyThreads(0); // config.setNettyThreads(0);
config.setCodec(new StringCodec()); // config.setCodec(new StringCodec());
config.setTransportMode(TransportMode.NIO); // config.setTransportMode(TransportMode.NIO);
SingleServerConfig singleServerConfig = config.useSingleServer(); // SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://" + redisHost + ":" + redisPort); // singleServerConfig.setAddress("redis://" + redisHost + ":" + redisPort);
singleServerConfig.setDatabase(redisDatabase); // singleServerConfig.setDatabase(redisDatabase);
singleServerConfig.setTimeout(timeout); // singleServerConfig.setTimeout(timeout);
singleServerConfig.setConnectionPoolSize(connectionPoolSize); // singleServerConfig.setConnectionPoolSize(connectionPoolSize);
singleServerConfig.setConnectionMinimumIdleSize(connectionMinimumIdleSize); // singleServerConfig.setConnectionMinimumIdleSize(connectionMinimumIdleSize);
if (StrUtil.isNotBlank(redisPassword)) { // if (StrUtil.isNotBlank(redisPassword)) {
singleServerConfig.setPassword(redisPassword); // singleServerConfig.setPassword(redisPassword);
} // }
singleServerConfig.setIdleConnectionTimeout(10000); // singleServerConfig.setIdleConnectionTimeout(10000);
singleServerConfig.setConnectTimeout(10000); // singleServerConfig.setConnectTimeout(10000);
singleServerConfig.setTimeout(3000); // singleServerConfig.setTimeout(3000);
singleServerConfig.setRetryAttempts(3); // singleServerConfig.setRetryAttempts(3);
// singleServerConfig.setRetryInterval(1500); // // singleServerConfig.setRetryInterval(1500);
singleServerConfig.setRetryDelay(new EqualJitterDelay(Duration.ofSeconds(3), Duration.ofSeconds(5))); // singleServerConfig.setRetryDelay(new EqualJitterDelay(Duration.ofSeconds(3), Duration.ofSeconds(5)));
singleServerConfig.setSubscriptionsPerConnection(5); // singleServerConfig.setSubscriptionsPerConnection(5);
singleServerConfig.setSubscriptionConnectionMinimumIdleSize(1); // singleServerConfig.setSubscriptionConnectionMinimumIdleSize(1);
singleServerConfig.setSubscriptionConnectionPoolSize(50); // singleServerConfig.setSubscriptionConnectionPoolSize(50);
singleServerConfig.setConnectionMinimumIdleSize(32); // singleServerConfig.setConnectionMinimumIdleSize(32);
singleServerConfig.setConnectionPoolSize(64); // singleServerConfig.setConnectionPoolSize(64);
singleServerConfig.setDnsMonitoringInterval(5000); // singleServerConfig.setDnsMonitoringInterval(5000);
RedissonClient redissonClient = Redisson.create(config); // RedissonClient redissonClient = Redisson.create(config);
log.info("Redisson客户端 初始化完毕"); // log.info("Redisson客户端 初始化完毕");
return redissonClient; // return redissonClient;
} // }
} //}
//
//
@@ -22,6 +22,12 @@ import lombok.experimental.UtilityClass;
import java.util.Date; import java.util.Date;
/**
* 时间相关工具
*
* @author odboy
* @date 2025-10-01
*/
@UtilityClass @UtilityClass
public class CsDateUtil { public class CsDateUtil {
/** /**
@@ -50,4 +56,95 @@ public class CsDateUtil {
public static String getNowDateStr() { public static String getNowDateStr() {
return DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN); 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", "执行失败"); Fail("fail", "执行失败");
private final String code; private final String code;
private final String name; 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;
}
} }
@@ -20,12 +20,14 @@ import cn.odboy.annotation.AnonymousAccess;
import cn.odboy.task.constant.TaskChangeTypeEnum; import cn.odboy.task.constant.TaskChangeTypeEnum;
import cn.odboy.task.core.TaskManage; import cn.odboy.task.core.TaskManage;
import cn.odboy.task.dal.dataobject.TaskInstanceInfoTb; import cn.odboy.task.dal.dataobject.TaskInstanceInfoTb;
import cn.odboy.task.dal.model.TaskInstanceInfoVo;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@Slf4j @Slf4j
@@ -38,7 +40,7 @@ public class TaskTestsController {
@AnonymousAccess @AnonymousAccess
@GetMapping(value = "/testCreate") @GetMapping(value = "/testCreate")
public ResponseEntity<?> 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)); log.info("任务创建成功,实例为:{}", JSON.toJSONString(instanceInfo));
return ResponseEntity.ok(instanceInfo); return ResponseEntity.ok(instanceInfo);
} }
@@ -46,7 +48,7 @@ public class TaskTestsController {
@AnonymousAccess @AnonymousAccess
@GetMapping(value = "/testCreateAfterStop") @GetMapping(value = "/testCreateAfterStop")
public ResponseEntity<?> 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)); log.info("任务创建成功,实例为:{}", JSON.toJSONString(instanceInfo));
ThreadUtil.execAsync(() -> { ThreadUtil.execAsync(() -> {
ThreadUtil.safeSleep(5000); ThreadUtil.safeSleep(5000);
@@ -54,4 +56,23 @@ public class TaskTestsController {
}); });
return ResponseEntity.ok(null); 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.hutool.core.util.StrUtil;
import cn.odboy.framework.context.CsSpringBeanHolder; 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.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.dal.model.TaskTemplateNodeVo;
import cn.odboy.task.service.TaskInstanceDetailService; import cn.odboy.task.service.TaskInstanceDetailService;
import cn.odboy.task.service.TaskInstanceInfoService; import cn.odboy.task.service.TaskInstanceInfoService;
import cn.odboy.task.service.TaskTemplateInfoService; import cn.odboy.task.service.TaskInstanceStepDetailService;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.quartz.InterruptableJob; import org.quartz.InterruptableJob;
@@ -53,44 +54,115 @@ public class TaskJobBean extends QuartzJobBean implements InterruptableJob {
// ========================== 获取代理类 ========================== // ========================== 获取代理类 ==========================
TaskInstanceInfoService taskInstanceInfoService = CsSpringBeanHolder.getBean(TaskInstanceInfoService.class); TaskInstanceInfoService taskInstanceInfoService = CsSpringBeanHolder.getBean(TaskInstanceInfoService.class);
TaskInstanceDetailService taskInstanceDetailService = CsSpringBeanHolder.getBean(TaskInstanceDetailService.class); TaskInstanceDetailService taskInstanceDetailService = CsSpringBeanHolder.getBean(TaskInstanceDetailService.class);
TaskTemplateInfoService taskTemplateInfoService = CsSpringBeanHolder.getBean(TaskTemplateInfoService.class);
// ========================== 获取参数 ========================== // ========================== 获取参数 ==========================
JobDataMap dataMap = context.getMergedJobDataMap(); JobDataMap dataMap = context.getMergedJobDataMap();
long id = dataMap.getLong("id"); 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); TaskInstanceInfoTb taskInstanceInfoVo = taskInstanceInfoService.getById(id);
if (taskInstanceInfoVo == null) { String templateInfo = taskInstanceInfoVo.getTemplate();
taskInstanceInfoService.fastFailWithMessage(id, "没有查询到任务编排模板");
return;
}
String templateInfo = taskInstanceInfoVo.getTemplateInfo();
List<TaskTemplateNodeVo> taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class); List<TaskTemplateNodeVo> taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class);
if (taskTemplateNodeVos == null || taskTemplateNodeVos.isEmpty()) { // ========================== 判断是否重试任务 ==========================
taskInstanceInfoService.fastFailWithMessage(id, "没有查询到任务编排明细"); String retryNodeCode = dataMap.getString("retryNodeCode");
return; 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<>(); List<TaskInstanceDetailTb> taskInstanceDetails = taskTemplateNodeVos.stream().map(taskTemplateNodeVo -> buildTaskInstanceDetail(id, taskTemplateNodeVo)).collect(Collectors.toList());
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); taskInstanceDetailService.saveBatch(taskInstanceDetails);
Map<String, Long> codeIdMap = taskInstanceDetails.stream().collect(Collectors.toMap(TaskInstanceDetailTb::getBizCode, TaskInstanceDetailTb::getId)); 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 { try {
for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodeVos) { for (TaskTemplateNodeVo taskTemplateNodeVo : taskTemplateNodes) {
String code = taskTemplateNodeVo.getCode(); String code = taskTemplateNodeVo.getCode();
try { try {
TaskStepExecutor executor = CsSpringBeanHolder.getBean("taskStep" + getBeanAlias(code)); 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; package cn.odboy.task.core;
import cn.hutool.core.bean.BeanUtil;
import cn.odboy.framework.exception.web.BadRequestException; import cn.odboy.framework.exception.web.BadRequestException;
import cn.odboy.task.constant.TaskChangeTypeEnum; import cn.odboy.task.constant.TaskChangeTypeEnum;
import cn.odboy.task.constant.TaskStatusEnum; 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.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.TaskInstanceInfoService;
import cn.odboy.task.service.TaskTemplateInfoService;
import cn.odboy.util.CsDateUtil;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.quartz.*; import org.quartz.*;
@@ -28,7 +37,9 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List;
@Slf4j @Slf4j
@Component @Component
@@ -36,44 +47,62 @@ public class TaskManage {
@Resource @Resource
private Scheduler scheduler; private Scheduler scheduler;
@Autowired @Autowired
private TaskTemplateInfoService taskTemplateInfoService;
@Autowired
private TaskInstanceInfoService taskInstanceInfoService; private TaskInstanceInfoService taskInstanceInfoService;
@Autowired
private TaskInstanceDetailService taskInstanceDetailService;
/** /**
* 创建任务单 * 创建任务单
* *
* @param contextName 上下文名称,这里特指应用名 * @param contextName 上下文名称,这里特指应用名
* @param changeType 变更类型 * @param changeTypeEnum 变更类型
* @param envAlias 环境别名 * @param envAlias 环境别名
* @param source 来源 * @param source 来源
* @param reason 变更原因 * @param reason 变更原因
* @param dataMap 任务参数 * @param dataMap 任务参数
*/ */
@Transactional(rollbackFor = Exception.class) @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) { if (dataMap == null) {
dataMap = new JobDataMap(); 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("contextName", contextName);
// 开发语言、资源版本 // 开发语言、资源版本
dataMap.put("language", language); dataMap.put("language", language);
dataMap.put("envAlias", envAlias); dataMap.put("envAlias", envAlias);
// 变更类型
dataMap.put("changeType", changeTypeEnum.getCode());
// ========================== 创建任务 ========================== // ========================== 创建任务 ==========================
TaskInstanceInfoTb newInstance = new TaskInstanceInfoTb(); TaskInstanceInfoTb newInstance = new TaskInstanceInfoTb();
newInstance.setContextName(contextName); newInstance.setContextName(contextName);
newInstance.setChangeType(changeType.getCode()); newInstance.setLanguage(language);
newInstance.setChangeType(changeTypeEnum.getCode());
newInstance.setEnvAlias(envAlias); newInstance.setEnvAlias(envAlias);
newInstance.setStatus(TaskStatusEnum.Running.getCode()); newInstance.setStatus(TaskStatusEnum.Running.getCode());
newInstance.setFinishTime(null); newInstance.setFinishTime(null);
newInstance.setSource(source); newInstance.setSource(source);
newInstance.setReason(reason); newInstance.setReason(reason);
newInstance.setTemplate(templateInfo);
newInstance.setJobData(JSON.toJSONString(dataMap)); newInstance.setJobData(JSON.toJSONString(dataMap));
taskInstanceInfoService.save(newInstance); taskInstanceInfoService.save(newInstance);
dataMap.put("id", newInstance.getId()); dataMap.put("id", newInstance.getId());
// ========================== 执行任务 ========================== // ========================== 执行任务 ==========================
JobKey jobKey = JobKey.jobKey(changeType.getCode(), contextName); JobKey jobKey = JobKey.jobKey(changeTypeEnum.getCode(), contextName);
TriggerKey triggerKey = TriggerKey.triggerKey(changeType.getCode(), contextName); TriggerKey triggerKey = TriggerKey.triggerKey(changeTypeEnum.getCode(), contextName);
JobDetail jobDetail = JobBuilder.newJob(TaskJobBean.class).withIdentity(jobKey).usingJobData(dataMap).build(); JobDetail jobDetail = JobBuilder.newJob(TaskJobBean.class).withIdentity(jobKey).usingJobData(dataMap).build();
Trigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startNow().build(); Trigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startNow().build();
try { try {
@@ -114,4 +143,89 @@ public class TaskManage {
throw new RuntimeException(e); 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;
}
} }
@@ -72,6 +72,13 @@ public class TaskInstanceInfoTb extends CsObject {
@TableField("context_name") @TableField("context_name")
private String contextName; private String contextName;
/**
* 语言
*/
@ApiModelProperty("语言")
@TableField("`language`")
private String language;
/** /**
* 变更类型 * 变更类型
*/ */
@@ -114,6 +121,13 @@ public class TaskInstanceInfoTb extends CsObject {
@ApiModelProperty("变更原因") @ApiModelProperty("变更原因")
private String reason; private String reason;
/**
* 任务模板
*/
@TableField("template")
@ApiModelProperty("template")
private String template;
/** /**
* QuartzJob参数 * QuartzJob参数
*/ */
@@ -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;
}
@@ -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;
}
@@ -31,5 +31,17 @@ import org.apache.ibatis.annotations.Param;
*/ */
@Mapper @Mapper
public interface TaskTemplateInfoMapper extends BaseMapper<TaskTemplateInfoTb> { 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
);
} }
@@ -19,6 +19,8 @@ import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import org.quartz.JobDataMap; import org.quartz.JobDataMap;
import java.util.List;
/** /**
* <p> * <p>
* 任务实例明细 服务类 * 任务实例明细 服务类
@@ -30,7 +32,11 @@ import org.quartz.JobDataMap;
public interface TaskInstanceDetailService extends IService<TaskInstanceDetailTb> { public interface TaskInstanceDetailService extends IService<TaskInstanceDetailTb> {
void fastFailWithInfo(Long instanceId, String code, String message); 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);
} }
@@ -36,4 +36,7 @@ public interface TaskInstanceInfoService extends IService<TaskInstanceInfoTb> {
void fastSuccessWithData(Long id, JobDataMap dataMap); 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);
} }
@@ -18,6 +18,8 @@ package cn.odboy.task.service;
import cn.odboy.task.dal.dataobject.TaskInstanceStepDetailTb; import cn.odboy.task.dal.dataobject.TaskInstanceStepDetailTb;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/** /**
* <p> * <p>
* 任务实例步骤明细 服务类 * 任务实例步骤明细 服务类
@@ -42,4 +44,11 @@ public interface TaskInstanceStepDetailService extends IService<TaskInstanceStep
* @param stepDesc 步骤描述 * @param stepDesc 步骤描述
*/ */
void fail(Long instanceDetailId, String stepDesc); void fail(Long instanceDetailId, String stepDesc);
/**
* 根据明细ID删除步骤明细
*
* @param instanceDetailIds
*/
void removeByInstanceDetailIds(List<Long> instanceDetailIds);
} }
@@ -28,5 +28,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
* @since 2025-09-28 * @since 2025-09-28
*/ */
public interface TaskTemplateInfoService extends IService<TaskTemplateInfoTb> { public interface TaskTemplateInfoService extends IService<TaskTemplateInfoTb> {
TaskTemplateInfoVo getTemplateInfoByECL(String envAlias, String contextName, String language); TaskTemplateInfoVo getTemplateInfoByECL(String envAlias, String contextName, String language, String changeType);
} }
@@ -15,6 +15,7 @@
*/ */
package cn.odboy.task.service.impl; package cn.odboy.task.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.odboy.task.constant.TaskStatusEnum; import cn.odboy.task.constant.TaskStatusEnum;
import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb; import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb;
import cn.odboy.task.dal.mysql.TaskInstanceDetailMapper; import cn.odboy.task.dal.mysql.TaskInstanceDetailMapper;
@@ -25,6 +26,7 @@ import org.quartz.JobDataMap;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* <p> * <p>
@@ -40,12 +42,10 @@ public class TaskInstanceDetailServiceImpl extends ServiceImpl<TaskInstanceDetai
@Override @Override
public void fastFailWithInfo(Long instanceId, String code, String executeInfo) { public void fastFailWithInfo(Long instanceId, String code, String executeInfo) {
TaskInstanceDetailTb updRecord = new TaskInstanceDetailTb(); TaskInstanceDetailTb updRecord = new TaskInstanceDetailTb();
updRecord.setFinishTime(new Date());
updRecord.setExecuteInfo(executeInfo); updRecord.setExecuteInfo(executeInfo);
updRecord.setExecuteStatus(TaskStatusEnum.Fail.getCode()); updRecord.setExecuteStatus(TaskStatusEnum.Fail.getCode());
lambdaUpdate() lambdaUpdate().eq(TaskInstanceDetailTb::getInstanceId, instanceId).eq(TaskInstanceDetailTb::getBizCode, code).update(updRecord);
.eq(TaskInstanceDetailTb::getInstanceId, instanceId)
.eq(TaskInstanceDetailTb::getBizCode, code)
.update(updRecord);
} }
@Override @Override
@@ -54,10 +54,7 @@ public class TaskInstanceDetailServiceImpl extends ServiceImpl<TaskInstanceDetai
updRecord.setFinishTime(new Date()); updRecord.setFinishTime(new Date());
updRecord.setExecuteInfo(executeInfo); updRecord.setExecuteInfo(executeInfo);
updRecord.setExecuteStatus(TaskStatusEnum.Success.getCode()); updRecord.setExecuteStatus(TaskStatusEnum.Success.getCode());
lambdaUpdate() lambdaUpdate().eq(TaskInstanceDetailTb::getInstanceId, instanceId).eq(TaskInstanceDetailTb::getBizCode, code).update(updRecord);
.eq(TaskInstanceDetailTb::getInstanceId, instanceId)
.eq(TaskInstanceDetailTb::getBizCode, code)
.update(updRecord);
} }
@Override @Override
@@ -67,9 +64,19 @@ public class TaskInstanceDetailServiceImpl extends ServiceImpl<TaskInstanceDetai
updRecord.setExecuteInfo("运行中"); updRecord.setExecuteInfo("运行中");
updRecord.setExecuteParams(JSON.toJSONString(dataMap)); updRecord.setExecuteParams(JSON.toJSONString(dataMap));
updRecord.setExecuteStatus(TaskStatusEnum.Running.getCode()); updRecord.setExecuteStatus(TaskStatusEnum.Running.getCode());
lambdaUpdate() lambdaUpdate().eq(TaskInstanceDetailTb::getInstanceId, instanceId).eq(TaskInstanceDetailTb::getBizCode, code).update(updRecord);
.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();
} }
} }
@@ -80,4 +80,28 @@ public class TaskInstanceInfoServiceImpl extends ServiceImpl<TaskInstanceInfoMap
updRecord.setJobData(JSON.toJSONString(dataMap)); updRecord.setJobData(JSON.toJSONString(dataMap));
updateById(updRecord); 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();
}
} }
@@ -15,14 +15,18 @@
*/ */
package cn.odboy.task.service.impl; package cn.odboy.task.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.odboy.framework.exception.web.BadRequestException; import cn.odboy.framework.exception.web.BadRequestException;
import cn.odboy.task.constant.TaskStatusEnum; import cn.odboy.task.constant.TaskStatusEnum;
import cn.odboy.task.dal.dataobject.TaskInstanceStepDetailTb; import cn.odboy.task.dal.dataobject.TaskInstanceStepDetailTb;
import cn.odboy.task.dal.mysql.TaskInstanceStepDetailMapper; import cn.odboy.task.dal.mysql.TaskInstanceStepDetailMapper;
import cn.odboy.task.service.TaskInstanceStepDetailService; import cn.odboy.task.service.TaskInstanceStepDetailService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
/** /**
* <p> * <p>
* 任务实例步骤明细 服务实现类 * 任务实例步骤明细 服务实现类
@@ -52,4 +56,14 @@ public class TaskInstanceStepDetailServiceImpl extends ServiceImpl<TaskInstanceS
save(stepDetail); save(stepDetail);
throw new BadRequestException(stepDesc); throw new BadRequestException(stepDesc);
} }
@Override
public void removeByInstanceDetailIds(List<Long> instanceDetailIds) {
if (CollUtil.isEmpty(instanceDetailIds)) {
return;
}
remove(new LambdaQueryWrapper<TaskInstanceStepDetailTb>()
.in(TaskInstanceStepDetailTb::getInstanceDetailId, instanceDetailIds)
);
}
} }
@@ -34,7 +34,7 @@ import org.springframework.stereotype.Service;
public class TaskTemplateInfoServiceImpl extends ServiceImpl<TaskTemplateInfoMapper, TaskTemplateInfoTb> implements TaskTemplateInfoService { public class TaskTemplateInfoServiceImpl extends ServiceImpl<TaskTemplateInfoMapper, TaskTemplateInfoTb> implements TaskTemplateInfoService {
@Override @Override
public TaskTemplateInfoVo getTemplateInfoByECL(String envAlias, String contextName, String language) { public TaskTemplateInfoVo getTemplateInfoByECL(String envAlias, String contextName, String language, String changeType) {
return baseMapper.selectTemplateInfoByECL(envAlias, contextName, language); return baseMapper.selectTemplateInfoByECL(envAlias, contextName, language, changeType);
} }
} }
@@ -14,5 +14,6 @@
WHERE t1.context_name = #{contextName} WHERE t1.context_name = #{contextName}
AND t2.env_alias = #{envAlias} AND t2.env_alias = #{envAlias}
AND t2.`language` = #{language} AND t2.`language` = #{language}
AND t2.`change_type` = #{changeType}
</select> </select>
</mapper> </mapper>
@@ -53,10 +53,10 @@ spring:
druid: druid:
db-type: com.alibaba.druid.pool.DruidDataSource db-type: com.alibaba.druid.pool.DruidDataSource
# sql监控 # sql监控
# driverClassName: com.p6spy.engine.spy.P6SpyDriver 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 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 # driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.100.128:23306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true # url: jdbc:mysql://192.168.100.128:23306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root username: root
password: kd123456 password: kd123456
# 初始连接数,建议设置为与最小空闲连接数相同 # 初始连接数,建议设置为与最小空闲连接数相同
-17
View File
@@ -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';
+40 -10
View File
@@ -10,8 +10,6 @@
Target Server Type : MySQL Target Server Type : MySQL
Target Server Version : 80025 (8.0.25) Target Server Version : 80025 (8.0.25)
File Encoding : 65001 File Encoding : 65001
Date: 09/10/2025 12:25:16
*/ */
SET NAMES utf8mb4; SET NAMES utf8mb4;
@@ -26,7 +24,7 @@ CREATE TABLE `demo_time_table_copy1` (
`create_time` datetime NOT NULL COMMENT '创建时间', `create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '修改时间', `update_time` datetime NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE 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 -- Records of demo_time_table_copy1
@@ -44,7 +42,7 @@ CREATE TABLE `demo_user_time_logic_table` (
`update_time` datetime NOT NULL COMMENT '修改时间', `update_time` datetime NOT NULL COMMENT '修改时间',
`available` tinyint(1) NOT NULL DEFAULT 1 COMMENT '数据有效性', `available` tinyint(1) NOT NULL DEFAULT 1 COMMENT '数据有效性',
PRIMARY KEY (`id`) USING BTREE 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 -- 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_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '修改人',
`update_time` datetime NOT NULL COMMENT '修改时间', `update_time` datetime NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE 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 -- Records of demo_user_time_table
@@ -221,7 +219,7 @@ CREATE TABLE `qrtz_scheduler_state` (
-- Records of 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 ('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 -- Table structure for qrtz_simple_triggers
@@ -355,7 +353,7 @@ CREATE TABLE `system_dict` (
`create_time` datetime NULL DEFAULT NULL COMMENT '创建日期', `create_time` datetime NULL DEFAULT NULL COMMENT '创建日期',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE 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 -- 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 (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 (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 (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 -- Table structure for system_dict_detail
@@ -380,7 +379,7 @@ CREATE TABLE `system_dict_detail` (
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE, PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_dict_id`(`dict_id` ASC) 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 -- 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 (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 (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 (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 -- 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, `exception_detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,
`create_time` datetime NOT NULL, `create_time` datetime NOT NULL,
PRIMARY KEY (`id`) USING BTREE 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 -- 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 '异常详情', `exception_detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '异常详情',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE 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 -- 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, 1);
INSERT INTO `system_roles_menus` VALUES (122, 2); INSERT INTO `system_roles_menus` VALUES (122, 2);
INSERT INTO `system_roles_menus` VALUES (125, 1); 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, 1);
INSERT INTO `system_roles_menus` VALUES (126, 2);
INSERT INTO `system_roles_menus` VALUES (127, 1); 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, 1);
INSERT INTO `system_roles_menus` VALUES (128, 2);
INSERT INTO `system_roles_menus` VALUES (129, 1); 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, 1);
INSERT INTO `system_roles_menus` VALUES (130, 2);
INSERT INTO `system_roles_menus` VALUES (132, 1); INSERT INTO `system_roles_menus` VALUES (132, 1);
INSERT INTO `system_roles_menus` VALUES (132, 2);
-- ---------------------------- -- ----------------------------
-- Table structure for system_user -- Table structure for system_user
@@ -1203,12 +1231,14 @@ CREATE TABLE `task_instance_info` (
`create_time` datetime NOT NULL COMMENT '创建时间', `create_time` datetime NOT NULL COMMENT '创建时间',
`update_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 '名称', `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 '变更类型', `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 '环境别名', `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失败)', `status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '状态(running进行中 success成功 fail失败)',
`finish_time` datetime NULL DEFAULT NULL COMMENT '完成时间', `finish_time` datetime NULL DEFAULT NULL COMMENT '完成时间',
`source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT 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 '变更原因', `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参数', `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 '异常信息', `error_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '异常信息',
PRIMARY KEY (`id`) USING BTREE PRIMARY KEY (`id`) USING BTREE
+6 -6
View File
@@ -106,12 +106,12 @@
<artifactId>spring-boot-starter-data-redis</artifactId> <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.redisson/redisson --> <!-- &lt;!&ndash; https://mvnrepository.com/artifact/org.redisson/redisson &ndash;&gt;-->
<dependency> <!-- <dependency>-->
<groupId>org.redisson</groupId> <!-- <groupId>org.redisson</groupId>-->
<artifactId>redisson</artifactId> <!-- <artifactId>redisson</artifactId>-->
<version>3.50.0</version> <!-- <version>3.50.0</version>-->
</dependency> <!-- </dependency>-->
<!-- Spring boot websocket --> <!-- Spring boot websocket -->
<dependency> <dependency>