From 78725a4c25328bd59f7ef18569c1aee614b8c4a9 Mon Sep 17 00:00:00 2001 From: Odboy Date: Sun, 26 Oct 2025 09:00:00 +0800 Subject: [PATCH] =?UTF-8?q?refactor(task):=E9=87=8D=E6=9E=84=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=B0=83=E5=BA=A6=E4=B8=8E=E6=89=A7=E8=A1=8C=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 统一使用 TaskJobKeys 常量替代硬编码字符串参数 - 优化线程变量命名 subMainThread -> workThread - 完善任务创建时的参数校验逻辑 - 优化任务模板解析异常处理 - 调整任务调度构建逻辑为独立方法 buildAndSchedule - 修复任务暂停逻辑 resumeTrigger -> pauseTrigger - 增强获取任务模板空值判断逻辑 - 清理冗余代码与无用注释 - 使用 Lombok 注解简化依赖注入方式 --- .../exception/BadRequestException.java | 29 +-- .../framework/exception/ServerException.java | 2 + .../exception/{web => }/handler/ApiError.java | 92 ++++----- .../handler/GlobalExceptionHandler.java | 182 +++++++++--------- .../exception/web/BadRequestException.java | 46 ----- .../system/framework/quartz/QuartzManage.java | 8 +- .../cn/odboy/task/constant/TaskJobKeys.java | 16 ++ .../java/cn/odboy/task/core/TaskJobBean.java | 156 +-------------- .../java/cn/odboy/task/core/TaskManage.java | 108 ++++++----- 9 files changed, 249 insertions(+), 390 deletions(-) create mode 100644 cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/ServerException.java rename cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/{web => }/handler/ApiError.java (96%) rename cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/{web => }/handler/GlobalExceptionHandler.java (97%) delete mode 100644 cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/BadRequestException.java create mode 100644 cutejava/cutejava-module-task/src/main/java/cn/odboy/task/constant/TaskJobKeys.java diff --git a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/BadRequestException.java b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/BadRequestException.java index 82e80836..75f1458c 100644 --- a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/BadRequestException.java +++ b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/BadRequestException.java @@ -1,19 +1,20 @@ /* - * Copyright 2021-2025 Odboy + * 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 + * 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 + * 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. + * 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.exception; + +package cn.odboy.framework.exception.web; import lombok.Getter; import org.springframework.http.HttpStatus; @@ -36,4 +37,10 @@ public class BadRequestException extends RuntimeException { super(msg); this.status = status.value(); } + + public BadRequestException(Throwable cause) { + // FIXME 20250827 修复完整异常链暴露给用户的问题 + super(cause.getMessage()); + this.status = BAD_REQUEST.value(); + } } diff --git a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/ServerException.java b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/ServerException.java new file mode 100644 index 00000000..35bb3f05 --- /dev/null +++ b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/ServerException.java @@ -0,0 +1,2 @@ +package cn.odboy.framework.exception.web;public class ServerException { +} diff --git a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/handler/ApiError.java b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/ApiError.java similarity index 96% rename from cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/handler/ApiError.java rename to cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/ApiError.java index b02a2120..f313955a 100644 --- a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/handler/ApiError.java +++ b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/ApiError.java @@ -1,46 +1,46 @@ -/* - * 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.exception.web.handler; - -import lombok.Data; - -@Data -public final class ApiError { - - private Integer status = 400; - private Long timestamp; - private String message; - - private ApiError() { - timestamp = System.currentTimeMillis(); - } - - public static ApiError error(final String message) { - ApiError apiError = new ApiError(); - apiError.setMessage(message); - return apiError; - } - - public static ApiError error(final Integer status, final String message) { - ApiError apiError = new ApiError(); - apiError.setStatus(status); - apiError.setMessage(message); - return apiError; - } -} - - +/* + * 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.exception.web.handler; + +import lombok.Data; + +@Data +public final class ApiError { + + private Integer status = 400; + private Long timestamp; + private String message; + + private ApiError() { + timestamp = System.currentTimeMillis(); + } + + public static ApiError error(final String message) { + ApiError apiError = new ApiError(); + apiError.setMessage(message); + return apiError; + } + + public static ApiError error(final Integer status, final String message) { + ApiError apiError = new ApiError(); + apiError.setStatus(status); + apiError.setMessage(message); + return apiError; + } +} + + diff --git a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/handler/GlobalExceptionHandler.java b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/GlobalExceptionHandler.java similarity index 97% rename from cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/handler/GlobalExceptionHandler.java rename to cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/GlobalExceptionHandler.java index 22d66c99..286040d9 100644 --- a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/handler/GlobalExceptionHandler.java +++ b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/GlobalExceptionHandler.java @@ -1,91 +1,91 @@ -/* - * 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.exception.web.handler; - -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.odboy.framework.exception.web.BadRequestException; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.validation.FieldError; -import org.springframework.validation.ObjectError; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; -/** - * 全局异常处理 - * - * @author odboy - */ -@Slf4j -@RestControllerAdvice -public class GlobalExceptionHandler { - - /** - * 处理所有不可知的异常 - */ - @ExceptionHandler(Throwable.class) - public ResponseEntity handleException(Throwable e) { - // 打印堆栈信息 - log.error(ExceptionUtil.stacktraceToString(e)); - return buildResponseEntity(ApiError.error(e.getMessage())); - } - - /** - * BadCredentialsException - */ - @ExceptionHandler(BadCredentialsException.class) - public ResponseEntity badCredentialsException(BadCredentialsException e) { - // 打印堆栈信息 - String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage(); - log.error(message); - return buildResponseEntity(ApiError.error(message)); - } - - /** - * 处理自定义异常 - */ - @ExceptionHandler(value = BadRequestException.class) - public ResponseEntity badRequestException(BadRequestException e) { - // 打印堆栈信息 - log.error(ExceptionUtil.stacktraceToString(e)); - return buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage())); - } - - /** - * 处理所有接口数据验证异常 - */ - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { - // 打印堆栈信息 - log.error(ExceptionUtil.stacktraceToString(e)); - ObjectError objectError = e.getBindingResult().getAllErrors().get(0); - String message = objectError.getDefaultMessage(); - if (objectError instanceof FieldError) { - message = ((FieldError) objectError).getField() + ": " + message; - } - return buildResponseEntity(ApiError.error(message)); - } - - /** - * 统一返回 - */ - private ResponseEntity buildResponseEntity(ApiError apiError) { - return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus())); - } -} +/* + * 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.exception.web.handler; + +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.odboy.framework.exception.web.BadRequestException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +/** + * 全局异常处理 + * + * @author odboy + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 处理所有不可知的异常 + */ + @ExceptionHandler(Throwable.class) + public ResponseEntity handleException(Throwable e) { + // 打印堆栈信息 + log.error(ExceptionUtil.stacktraceToString(e)); + return buildResponseEntity(ApiError.error(e.getMessage())); + } + + /** + * BadCredentialsException + */ + @ExceptionHandler(BadCredentialsException.class) + public ResponseEntity badCredentialsException(BadCredentialsException e) { + // 打印堆栈信息 + String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage(); + log.error(message); + return buildResponseEntity(ApiError.error(message)); + } + + /** + * 处理自定义异常 + */ + @ExceptionHandler(value = BadRequestException.class) + public ResponseEntity badRequestException(BadRequestException e) { + // 打印堆栈信息 + log.error(ExceptionUtil.stacktraceToString(e)); + return buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage())); + } + + /** + * 处理所有接口数据验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + // 打印堆栈信息 + log.error(ExceptionUtil.stacktraceToString(e)); + ObjectError objectError = e.getBindingResult().getAllErrors().get(0); + String message = objectError.getDefaultMessage(); + if (objectError instanceof FieldError) { + message = ((FieldError) objectError).getField() + ": " + message; + } + return buildResponseEntity(ApiError.error(message)); + } + + /** + * 统一返回 + */ + private ResponseEntity buildResponseEntity(ApiError apiError) { + return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus())); + } +} diff --git a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/BadRequestException.java b/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/BadRequestException.java deleted file mode 100644 index 349b3d70..00000000 --- a/cutejava/cutejava-framework/src/main/java/cn/odboy/framework/exception/web/BadRequestException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.exception.web; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -import static org.springframework.http.HttpStatus.BAD_REQUEST; - -/** - * 统一异常处理 - */ -@Getter -public class BadRequestException extends RuntimeException { - - private Integer status = BAD_REQUEST.value(); - - public BadRequestException(String msg) { - super(msg); - } - - public BadRequestException(HttpStatus status, String msg) { - super(msg); - this.status = status.value(); - } - - public BadRequestException(Throwable cause) { - // FIXME 20250827 修复完整异常链暴露给用户的问题 - super(cause.getMessage()); - this.status = BAD_REQUEST.value(); - } -} diff --git a/cutejava/cutejava-module-system/src/main/java/cn/odboy/system/framework/quartz/QuartzManage.java b/cutejava/cutejava-module-system/src/main/java/cn/odboy/system/framework/quartz/QuartzManage.java index c7c75c11..1811c3b0 100644 --- a/cutejava/cutejava-module-system/src/main/java/cn/odboy/system/framework/quartz/QuartzManage.java +++ b/cutejava/cutejava-module-system/src/main/java/cn/odboy/system/framework/quartz/QuartzManage.java @@ -41,16 +41,16 @@ public class QuartzManage { JobDetail jobDetail = JobBuilder.newJob(ExecutionJobBean.class).withIdentity(JOB_NAME + quartzJob.getId()).build(); // 通过触发器名和cron 表达式创建 Trigger - Trigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(JOB_NAME + quartzJob.getId()).startNow().withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression())).build(); + Trigger trigger = TriggerBuilder.newTrigger().withIdentity(JOB_NAME + quartzJob.getId()).startNow().withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression())).build(); - cronTrigger.getJobDataMap().put(SystemQuartzJobTb.JOB_KEY, quartzJob); + trigger.getJobDataMap().put(SystemQuartzJobTb.JOB_KEY, quartzJob); // 重置启动时间 - ((CronTriggerImpl) cronTrigger).setStartTime(new Date()); + ((CronTriggerImpl) trigger).setStartTime(new Date()); // 执行定时任务,如果是持久化的,这里会报错,捕获输出 try { - scheduler.scheduleJob(jobDetail, cronTrigger); + scheduler.scheduleJob(jobDetail, trigger); } catch (ObjectAlreadyExistsException e) { log.warn("定时任务已存在,跳过加载"); } diff --git a/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/constant/TaskJobKeys.java b/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/constant/TaskJobKeys.java new file mode 100644 index 00000000..783c7597 --- /dev/null +++ b/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/constant/TaskJobKeys.java @@ -0,0 +1,16 @@ +package cn.odboy.task.constant; + +/** + * 任务共用Keys + * + * @author odboy + * @date 2025-10-25 + */ +public interface TaskJobKeys { + String ID = "id"; + String CONTEXT_NAME = "contextName"; + String LANGUAGE = "language"; + String ENV_ALIAS = "envAlias"; + String CHANGE_TYPE = "changeType"; + String RETRY_NODE_CODE = "retryNodeCode"; +} diff --git a/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/core/TaskJobBean.java b/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/core/TaskJobBean.java index f493157d..e2a7693e 100644 --- a/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/core/TaskJobBean.java +++ b/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/core/TaskJobBean.java @@ -19,6 +19,7 @@ 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.constant.TaskJobKeys; import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb; import cn.odboy.task.dal.dataobject.TaskInstanceInfoTb; import cn.odboy.task.dal.model.TaskTemplateNodeVo; @@ -40,7 +41,7 @@ import java.util.stream.Collectors; @Slf4j public class TaskJobBean extends QuartzJobBean implements InterruptableJob { - private Thread subMainThread = null; + private Thread workThread = null; private String getBeanAlias(String code) { // node_init @@ -50,19 +51,19 @@ public class TaskJobBean extends QuartzJobBean implements InterruptableJob { @Override public void executeInternal(JobExecutionContext context) { - this.subMainThread = Thread.currentThread(); + this.workThread = Thread.currentThread(); // ========================== 获取代理类 ========================== TaskInstanceInfoService taskInstanceInfoService = CsSpringBeanHolder.getBean(TaskInstanceInfoService.class); TaskInstanceDetailService taskInstanceDetailService = CsSpringBeanHolder.getBean(TaskInstanceDetailService.class); // ========================== 获取参数 ========================== JobDataMap dataMap = context.getMergedJobDataMap(); - long id = dataMap.getLong("id"); + long id = dataMap.getLong(TaskJobKeys.ID); // ========================== 获取任务编排模板 ========================== TaskInstanceInfoTb taskInstanceInfoVo = taskInstanceInfoService.getById(id); String templateInfo = taskInstanceInfoVo.getTemplate(); List taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class); // ========================== 判断是否重试任务 ========================== - String retryNodeCode = dataMap.getString("retryNodeCode"); + String retryNodeCode = dataMap.getString(TaskJobKeys.RETRY_NODE_CODE); if (StrUtil.isBlank(retryNodeCode)) { executeNormalTask(taskInstanceInfoService, taskInstanceDetailService, id, dataMap, taskTemplateNodeVos); } else { @@ -191,151 +192,8 @@ public class TaskJobBean extends QuartzJobBean implements InterruptableJob { @Override public void interrupt() { - if (subMainThread != null) { - subMainThread.stop(); + if (workThread != null) { + workThread.stop(); } } } - - -//@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 taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class); -// // ========================== 判断是否重试任务 ========================== -// String retryNodeCode = dataMap.getString("retryNodeCode"); -// if (StrUtil.isBlank(retryNodeCode)) { -// // ========================== 初始化执行明细 ========================== -// List 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 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 taskInstanceDetails = new ArrayList<>(); -// List 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 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(); -// } -// } -//} diff --git a/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/core/TaskManage.java b/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/core/TaskManage.java index 2c438abc..690eaba2 100644 --- a/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/core/TaskManage.java +++ b/cutejava/cutejava-module-task/src/main/java/cn/odboy/task/core/TaskManage.java @@ -16,8 +16,11 @@ package cn.odboy.task.core; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import cn.odboy.framework.context.CsSpringBeanHolder; import cn.odboy.framework.exception.web.BadRequestException; import cn.odboy.task.constant.TaskChangeTypeEnum; +import cn.odboy.task.constant.TaskJobKeys; import cn.odboy.task.constant.TaskStatusEnum; import cn.odboy.task.dal.dataobject.TaskInstanceDetailTb; import cn.odboy.task.dal.dataobject.TaskInstanceInfoTb; @@ -30,34 +33,29 @@ import cn.odboy.task.service.TaskInstanceInfoService; import cn.odboy.task.service.TaskTemplateInfoService; import cn.odboy.util.CsDateUtil; import com.alibaba.fastjson2.JSON; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.quartz.*; -import org.springframework.beans.factory.annotation.Autowired; -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 +@RequiredArgsConstructor public class TaskManage { - @Resource - private Scheduler scheduler; - @Autowired - private TaskTemplateInfoService taskTemplateInfoService; - @Autowired - private TaskInstanceInfoService taskInstanceInfoService; - @Autowired - private TaskInstanceDetailService taskInstanceDetailService; + private final Scheduler scheduler; + private final TaskTemplateInfoService taskTemplateInfoService; + private final TaskInstanceInfoService taskInstanceInfoService; + private final TaskInstanceDetailService taskInstanceDetailService; /** - * 创建任务单 + * 创建任务单(注:同一应用同一变更类型无法并发多实例) * * @param contextName 上下文名称,这里特指应用名 * @param changeTypeEnum 变更类型 + * @param language 开发语言、资源版本 * @param envAlias 环境别名 * @param source 来源 * @param reason 变更原因 @@ -65,6 +63,21 @@ public class TaskManage { */ @Transactional(rollbackFor = Exception.class) public TaskInstanceInfoTb createJob(String contextName, TaskChangeTypeEnum changeTypeEnum, String language, String envAlias, String source, String reason, JobDataMap dataMap) { + if (StrUtil.isBlank(contextName)) { + throw new BadRequestException("参数contextName必填"); + } + if (changeTypeEnum == null) { + throw new BadRequestException("参数changeTypeEnum必填"); + } + if (StrUtil.isBlank(language)) { + throw new BadRequestException("参数language必填"); + } + if (StrUtil.isBlank(envAlias)) { + throw new BadRequestException("参数envAlias必填"); + } + if (StrUtil.isBlank(source)) { + throw new BadRequestException("参数source必填"); + } if (dataMap == null) { dataMap = new JobDataMap(); } @@ -74,19 +87,33 @@ public class TaskManage { throw new BadRequestException("没有查询到任务编排模板"); } String templateInfo = taskInstanceInfoVo.getTemplateInfo(); - List taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class); + List taskTemplateNodeVos; + try { + taskTemplateNodeVos = JSON.parseArray(templateInfo, TaskTemplateNodeVo.class); + } catch (Exception e) { + log.error("任务编排模板解析失败", e); + throw new BadRequestException("任务编排模板解析失败"); + } 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()); // ========================== 创建任务 ========================== + TaskManage taskManage = CsSpringBeanHolder.getBean(TaskManage.class); + TaskInstanceInfoTb newInstance = taskManage.saveTaskInstanceInfoTb(contextName, changeTypeEnum, language, envAlias, source, reason, dataMap, templateInfo); + dataMap.put(TaskJobKeys.ID, newInstance.getId()); + // 应用名、资源类型 + dataMap.put(TaskJobKeys.CONTEXT_NAME, contextName); + // 开发语言、资源版本 + dataMap.put(TaskJobKeys.LANGUAGE, language); + dataMap.put(TaskJobKeys.ENV_ALIAS, envAlias); + // 变更类型 + dataMap.put(TaskJobKeys.CHANGE_TYPE, changeTypeEnum.getCode()); + buildAndSchedule(changeTypeEnum.getCode(), contextName, dataMap); + return newInstance; + } + + @Transactional(rollbackFor = Exception.class) + public TaskInstanceInfoTb saveTaskInstanceInfoTb(String contextName, TaskChangeTypeEnum changeTypeEnum, String language, String envAlias, String source, String reason, JobDataMap dataMap, String templateInfo) { TaskInstanceInfoTb newInstance = new TaskInstanceInfoTb(); newInstance.setContextName(contextName); newInstance.setLanguage(language); @@ -99,21 +126,24 @@ public class TaskManage { newInstance.setTemplate(templateInfo); newInstance.setJobData(JSON.toJSONString(dataMap)); taskInstanceInfoService.save(newInstance); - dataMap.put("id", newInstance.getId()); + return newInstance; + } + + private void buildAndSchedule(String changeTypeEnum, String contextName, JobDataMap dataMap) { // ========================== 执行任务 ========================== - JobKey jobKey = JobKey.jobKey(changeTypeEnum.getCode(), contextName); - TriggerKey triggerKey = TriggerKey.triggerKey(changeTypeEnum.getCode(), contextName); + JobKey jobKey = JobKey.jobKey(changeTypeEnum, contextName); + TriggerKey triggerKey = TriggerKey.triggerKey(changeTypeEnum, contextName); JobDetail jobDetail = JobBuilder.newJob(TaskJobBean.class).withIdentity(jobKey).usingJobData(dataMap).build(); - Trigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startNow().build(); + Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startNow().build(); try { - scheduler.scheduleJob(jobDetail, cronTrigger); + scheduler.scheduleJob(jobDetail, trigger); } catch (ObjectAlreadyExistsException e) { + log.error("任务jobKey={},triggerKey={}任务已存在,跳过加载", jobKey.getName(), triggerKey.getName(), e); throw new BadRequestException("任务已存在,跳过加载"); } catch (SchedulerException e) { - log.error("任务执行失败", e); + log.error("任务jobKey={},triggerKey={}执行失败", jobKey.getName(), triggerKey.getName(), e); throw new BadRequestException(e); } - return newInstance; } @Transactional(rollbackFor = Exception.class) @@ -132,7 +162,7 @@ public class TaskManage { JobKey jobKey = JobKey.jobKey(changeType, contextName); TriggerKey triggerKey = TriggerKey.triggerKey(changeType, contextName); // 停止触发器 - scheduler.resumeTrigger(triggerKey); + scheduler.pauseTrigger(triggerKey); // 移除触发器 scheduler.unscheduleJob(triggerKey); // 删除并中断任务 @@ -164,21 +194,10 @@ public class TaskManage { taskInstanceInfoTb.setFinishTime(null); taskInstanceInfoService.updateById(taskInstanceInfoTb); // ========================== 执行任务 ========================== - dataMap.put("retryNodeCode", retryNodeCode); + dataMap.put(TaskJobKeys.RETRY_NODE_CODE, 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); - } + buildAndSchedule(changeType, contextName, dataMap); return taskInstanceInfoTb; } @@ -189,6 +208,9 @@ public class TaskManage { if (historyInstance == null) { // 仅返回模板 TaskTemplateInfoVo templateInfo = taskTemplateInfoService.getTemplateInfoByECL(envAlias, contextName, language, changeType); + if (templateInfo == null) { + throw new BadRequestException("没有查询到任务编排模板"); + } record.setTemplate(templateInfo.getTemplateInfo()); return record; }