feat(REQ-4418) - 提交

This commit is contained in:
wangli 2025-09-03 15:00:40 +08:00
parent 344bdfd2ee
commit 033b938766
4 changed files with 123 additions and 46 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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<Applicati
"onAborted"
);
private static final List<Event.EventCode> 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<String, ProcessInstanceEventHandler> 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<String> 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<String, ProcessInstanceEventHandler> 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<Applicati
}
}
}
int binaryResult = getBinaryResult(methodImplemented);
List<String> 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<String, EventConsumer> 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<Event.EventCode, EventHandler> eventHandlers =
(ListMultimap<Event.EventCode, EventHandler>) 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<String> getUnImplementedMethods(boolean[] methodImplemented) {
// 收集未实现的方法
List<String> 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<Applicati
binaryResult += weight;
}
}
// 收集未实现的方法
List<String> 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;
}
}