From 033b938766a5d9b1939c6d1d4480e4341423ac77 Mon Sep 17 00:00:00 2001 From: wangli <274027703@qq.com> Date: Wed, 3 Sep 2025 15:00:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(REQ-4418)=20-=20=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...orkflowEngineStarterAutoConfiguration.java | 5 +- .../starter/api/WorkflowCoreService.java | 18 +-- .../starter/api/WorkflowManageService.java | 14 ++ .../mq/check/ImplementationReadyChecker.java | 132 ++++++++++++++---- 4 files changed, 123 insertions(+), 46 deletions(-) diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/WorkflowEngineStarterAutoConfiguration.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/WorkflowEngineStarterAutoConfiguration.java index 5770407f5..fba9e97e6 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/WorkflowEngineStarterAutoConfiguration.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/WorkflowEngineStarterAutoConfiguration.java @@ -158,9 +158,8 @@ public class WorkflowEngineStarterAutoConfiguration { } @Bean - public ImplementationReadyChecker implementationReadyChecker(WorkflowCoreService workflowCoreService, - Environment environment) { - return new ImplementationReadyChecker(workflowCoreService, environment); + public ImplementationReadyChecker implementationReadyChecker(WorkflowCoreService workflowCoreService) { + return new ImplementationReadyChecker(workflowCoreService); } // @Bean diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java index f8bacce7f..883e85801 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java @@ -1,8 +1,6 @@ package cn.axzo.workflow.starter.api; import cn.axzo.workflow.common.annotation.InvokeMode; -import cn.axzo.workflow.common.enums.AdminDataSource; -import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; import cn.axzo.workflow.common.model.dto.SignFileDTO; import cn.axzo.workflow.common.model.dto.SimpleDocDTO; import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO; @@ -141,16 +139,12 @@ public interface WorkflowCoreService { @InvokeMode(SYNC) Boolean setTimeoutCallback(@Validated @RequestBody BpmnActivityTimeoutCallbackDTO dto); - @Operation(summary = "获取指定枚举类型的枚举值信息") - @GetMapping("/api/function/enum/admin-data-source/get") - @InvokeMode(SYNC) - AdminDataSource getAdminDataSourceEnum(@RequestParam String enumValue); - - @Operation(summary = "获取指定枚举类型的枚举值信息") - @GetMapping("/api/function/enum/process-instance-result/get") - @InvokeMode(SYNC) - BpmnProcessInstanceResultEnum getFileTypeEnum(@RequestParam String enumValue); - + /** + * 用于 Starter 检测必接事件的告警 + * + * @param dto + * @return + */ @Operation(summary = "发送钉钉消息") @PostMapping("/api/function/dingtalk/alter") @InvokeMode(SYNC) diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java index 05efbb7f1..8a7f95145 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java @@ -2,6 +2,8 @@ package cn.axzo.workflow.starter.api; import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.Manageable; +import cn.axzo.workflow.common.enums.AdminDataSource; +import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; import cn.axzo.workflow.common.model.dto.print.PrintFieldDTO; import cn.axzo.workflow.common.model.request.admin.ProcessAdminCreateDTO; import cn.axzo.workflow.common.model.request.admin.ProcessAdminDeleteDTO; @@ -194,6 +196,18 @@ public interface WorkflowManageService { @InvokeMode(SYNC) Boolean trigger(@NotBlank(message = "触发 ID 不能为空") @RequestParam String triggerId); + @Operation(summary = "获取指定枚举类型的枚举值信息") + @GetMapping("/api/function/enum/admin-data-source/get") + @Manageable + @InvokeMode(SYNC) + AdminDataSource getAdminDataSourceEnum(@RequestParam String enumValue); + + @Operation(summary = "获取指定枚举类型的枚举值信息") + @GetMapping("/api/function/enum/process-instance-result/get") + @Manageable + @InvokeMode(SYNC) + BpmnProcessInstanceResultEnum getFileTypeEnum(@RequestParam String enumValue); + @PostMapping("/api/form/admin/form/page") @InvokeMode(SYNC) @Manageable diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/check/ImplementationReadyChecker.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/check/ImplementationReadyChecker.java index 882334864..40e6d8bee 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/check/ImplementationReadyChecker.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/check/ImplementationReadyChecker.java @@ -1,15 +1,23 @@ package cn.axzo.workflow.starter.mq.check; +import cn.axzo.framework.rocketmq.DefaultEventConsumer; +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.framework.rocketmq.EventHandlerRepository; +import cn.axzo.workflow.common.enums.ProcessInstanceEventEnum; import cn.axzo.workflow.common.model.request.feature.DingTalkStarterAlterDTO; import cn.axzo.workflow.common.model.response.mq.ProcessInstanceDTO; import cn.axzo.workflow.starter.api.WorkflowCoreService; import cn.axzo.workflow.starter.handler.ProcessInstanceEventHandler; +import com.google.common.collect.ListMultimap; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.core.env.Environment; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -34,28 +42,60 @@ public class ImplementationReadyChecker implements ApplicationListener EVENT_CODES = Arrays.asList( + ProcessInstanceEventEnum.PROCESS_INSTANCE_COMPLETED.getEventCode(), + ProcessInstanceEventEnum.PROCESS_INSTANCE_CANCELLED.getEventCode(), + ProcessInstanceEventEnum.PROCESS_INSTANCE_REJECTED.getEventCode(), + ProcessInstanceEventEnum.PROCESS_INSTANCE_ABORTED.getEventCode() + ); private final WorkflowCoreService workflowCoreService; - private final Environment environment; - public ImplementationReadyChecker(WorkflowCoreService workflowCoreService, - Environment environment) { + public ImplementationReadyChecker(WorkflowCoreService workflowCoreService) { this.workflowCoreService = workflowCoreService; - this.environment = environment; } - @Override public void onApplicationEvent(ApplicationReadyEvent event) { ApplicationContext context = event.getApplicationContext(); - // 获取所有实现了目标接口的Bean - Map handlers = context.getBeansOfType(TARGET_INTERFACE); - if (handlers.isEmpty()) { - log.warn("未找到实现 {} 接口的Bean, 请确保至少有一个实现类被正确扫描和注册。", TARGET_INTERFACE.getName()); + boolean[] methodImplemented = checkUnImplementedMethodsWithInterface(context); + if (getBinaryResult(methodImplemented) == 15) { + log.info("Congratulations, passed the verification!"); + log.info("祝贺,已通过必接事件的校验!但仍然请确保您的实现类逻辑正确无误,不允许出现空实现,否则生产问题自行负责!"); + return; + } + log.info("将继续通过 EventConsumer 进行检查..."); + methodImplemented = checkUnImplementedMethodsWithEventConsumer(context); + if (getBinaryResult(methodImplemented) == 15) { + log.info("Congratulations, passed the verification!"); + log.info("祝贺,已通过必接事件的校验!但仍然请确保您的实现类逻辑正确无误,不允许出现空实现,否则生产问题自行负责!"); + } else { + log.error("----------------------------------------"); + log.error("警告:未通过必接事件的校验!未实现的方法:{},请确保使用了实现了 {} 接口的Bean被Spring扫描,或者在EventConsumer注册了 ProcessInstanceEventEnum 的 EventCode。", String.join(", ", getUnImplementedMethods(methodImplemented)), TARGET_INTERFACE.getName()); + log.error("----------------------------------------"); + sendAlter(context, getUnImplementedMethods(methodImplemented)); } + } + + private void sendAlter(ApplicationContext context, List unImplementedMethods) { + Environment environment = context.getEnvironment(); + DingTalkStarterAlterDTO dto = new DingTalkStarterAlterDTO(); + dto.setApplicationName(environment.getProperty("spring.application.name")); + dto.setProfile(environment.getProperty("spring.profiles.active")); + dto.setAlterContent("必接事件实现类检查未通过,未实现的方法: " + String.join(", ", unImplementedMethods) + "。请确保至少有一个实现类覆盖这些方法以处理相应的事件。"); + workflowCoreService.sendDingtalk(dto); + } + + private static boolean[] checkUnImplementedMethodsWithInterface(ApplicationContext context) { // 记录每个方法是否被实现 boolean[] methodImplemented = new boolean[4]; Arrays.fill(methodImplemented, false); + // 获取所有实现了目标接口的Bean + Map handlers = context.getBeansOfType(TARGET_INTERFACE); + if (handlers.isEmpty()) { + log.warn("未找到实现 {} 接口的Bean ", TARGET_INTERFACE.getName()); + return methodImplemented; + } // 检查每个实现类 for (ProcessInstanceEventHandler handler : handlers.values()) { @@ -75,7 +115,58 @@ public class ImplementationReadyChecker implements ApplicationListener unimplementedMethods = getUnImplementedMethods(methodImplemented); + log.info("实现 {} 接口的Bean数量: {}", TARGET_INTERFACE.getName(), handlers.size()); + log.info("方法实现情况:(二进制:{}),(十进制:{})", String.format("%4s", Integer.toBinaryString(binaryResult)).replace(' ', '0'), binaryResult); + if (!unimplementedMethods.isEmpty()) { + log.warn("未通过 Starter 提供的 {} 接口实现中找到以下方法的实现: {}。请确保至少有一个实现类覆盖这些方法以处理相应的事件。", TARGET_INTERFACE.getName(), String.join(", ", unimplementedMethods)); + } + return methodImplemented; + } + private static boolean[] checkUnImplementedMethodsWithEventConsumer(ApplicationContext context) { + // 记录每个方法是否被实现 + boolean[] methodImplemented = new boolean[4]; + Arrays.fill(methodImplemented, false); + + Map beansOfType = context.getBeansOfType(EventConsumer.class); + beansOfType.forEach((name, c) -> { + if (c instanceof DefaultEventConsumer) { + try { + Field repoField = DefaultEventConsumer.class.getDeclaredField("handlerRepository"); + repoField.setAccessible(true); + EventHandlerRepository repo = (EventHandlerRepository) repoField.get(c); + + Field handlersField = EventHandlerRepository.class.getDeclaredField("handlers"); + handlersField.setAccessible(true); + @SuppressWarnings("unchecked") + ListMultimap eventHandlers = + (ListMultimap) handlersField.get(repo); + + for (int i = 0; i < EVENT_CODES.size(); i++) { + methodImplemented[i] = eventHandlers.keySet().contains(EVENT_CODES.get(i)); + } + } catch (Exception e) { + log.warn("通过 EventConsumer 检测必接事件异常:{}", e.getMessage(), e); + } + } + }); + return methodImplemented; + } + + private static List getUnImplementedMethods(boolean[] methodImplemented) { + // 收集未实现的方法 + List unimplementedMethods = new ArrayList<>(); + for (int i = 0; i < methodImplemented.length; i++) { + if (!methodImplemented[i]) { + unimplementedMethods.add(METHOD_NAMES.get(i)); + } + } + return unimplementedMethods; + } + + private static int getBinaryResult(boolean[] methodImplemented) { // 生成4位二进制数 int binaryResult = 0; for (int i = 0; i < methodImplemented.length; i++) { @@ -85,27 +176,6 @@ public class ImplementationReadyChecker implements ApplicationListener unimplementedMethods = new ArrayList<>(); - for (int i = 0; i < methodImplemented.length; i++) { - if (!methodImplemented[i]) { - unimplementedMethods.add(METHOD_NAMES.get(i)); - } - } - - log.info("实现 {} 接口的Bean数量: {}", TARGET_INTERFACE.getName(), handlers.size()); - log.info("方法实现情况:(二进制:{}),(十进制:{})", String.format("%4s", Integer.toBinaryString(binaryResult)).replace(' ', '0'), binaryResult); - if (!unimplementedMethods.isEmpty()) { - log.error("以下方法未被任何实现类覆盖: {}。请确保至少有一个实现类覆盖这些方法以处理相应的事件。", String.join(", ", unimplementedMethods)); - DingTalkStarterAlterDTO dto = new DingTalkStarterAlterDTO(); - dto.setApplicationName(environment.getProperty("spring.application.name")); - dto.setProfile(environment.getProperty("spring.profiles.active")); - dto.setAlterContent("必接事件实现类检查未通过,未实现的方法: " + String.join(", ", unimplementedMethods) + "。请确保至少有一个实现类覆盖这些方法以处理相应的事件。"); - workflowCoreService.sendDingtalk(dto); - } else { - log.info("Congratulations, passed the verification!"); - log.info("祝贺,已通过必接事件的校验!但仍然请确保您的实现类逻辑正确无误,不允许出现空实现,否则生产问题自行负责!"); - } + return binaryResult; } }