feat(REQ-4418) - 提交
This commit is contained in:
parent
344bdfd2ee
commit
033b938766
@ -158,9 +158,8 @@ public class WorkflowEngineStarterAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ImplementationReadyChecker implementationReadyChecker(WorkflowCoreService workflowCoreService,
|
public ImplementationReadyChecker implementationReadyChecker(WorkflowCoreService workflowCoreService) {
|
||||||
Environment environment) {
|
return new ImplementationReadyChecker(workflowCoreService);
|
||||||
return new ImplementationReadyChecker(workflowCoreService, environment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Bean
|
// @Bean
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
package cn.axzo.workflow.starter.api;
|
package cn.axzo.workflow.starter.api;
|
||||||
|
|
||||||
import cn.axzo.workflow.common.annotation.InvokeMode;
|
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.SignFileDTO;
|
||||||
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
|
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
|
||||||
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO;
|
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO;
|
||||||
@ -141,16 +139,12 @@ public interface WorkflowCoreService {
|
|||||||
@InvokeMode(SYNC)
|
@InvokeMode(SYNC)
|
||||||
Boolean setTimeoutCallback(@Validated @RequestBody BpmnActivityTimeoutCallbackDTO dto);
|
Boolean setTimeoutCallback(@Validated @RequestBody BpmnActivityTimeoutCallbackDTO dto);
|
||||||
|
|
||||||
@Operation(summary = "获取指定枚举类型的枚举值信息")
|
/**
|
||||||
@GetMapping("/api/function/enum/admin-data-source/get")
|
* 用于 Starter 检测必接事件的告警
|
||||||
@InvokeMode(SYNC)
|
*
|
||||||
AdminDataSource getAdminDataSourceEnum(@RequestParam String enumValue);
|
* @param dto
|
||||||
|
* @return
|
||||||
@Operation(summary = "获取指定枚举类型的枚举值信息")
|
*/
|
||||||
@GetMapping("/api/function/enum/process-instance-result/get")
|
|
||||||
@InvokeMode(SYNC)
|
|
||||||
BpmnProcessInstanceResultEnum getFileTypeEnum(@RequestParam String enumValue);
|
|
||||||
|
|
||||||
@Operation(summary = "发送钉钉消息")
|
@Operation(summary = "发送钉钉消息")
|
||||||
@PostMapping("/api/function/dingtalk/alter")
|
@PostMapping("/api/function/dingtalk/alter")
|
||||||
@InvokeMode(SYNC)
|
@InvokeMode(SYNC)
|
||||||
|
|||||||
@ -2,6 +2,8 @@ package cn.axzo.workflow.starter.api;
|
|||||||
|
|
||||||
import cn.axzo.workflow.common.annotation.InvokeMode;
|
import cn.axzo.workflow.common.annotation.InvokeMode;
|
||||||
import cn.axzo.workflow.common.annotation.Manageable;
|
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.dto.print.PrintFieldDTO;
|
||||||
import cn.axzo.workflow.common.model.request.admin.ProcessAdminCreateDTO;
|
import cn.axzo.workflow.common.model.request.admin.ProcessAdminCreateDTO;
|
||||||
import cn.axzo.workflow.common.model.request.admin.ProcessAdminDeleteDTO;
|
import cn.axzo.workflow.common.model.request.admin.ProcessAdminDeleteDTO;
|
||||||
@ -194,6 +196,18 @@ public interface WorkflowManageService {
|
|||||||
@InvokeMode(SYNC)
|
@InvokeMode(SYNC)
|
||||||
Boolean trigger(@NotBlank(message = "触发 ID 不能为空") @RequestParam String triggerId);
|
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")
|
@PostMapping("/api/form/admin/form/page")
|
||||||
@InvokeMode(SYNC)
|
@InvokeMode(SYNC)
|
||||||
@Manageable
|
@Manageable
|
||||||
|
|||||||
@ -1,15 +1,23 @@
|
|||||||
package cn.axzo.workflow.starter.mq.check;
|
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.request.feature.DingTalkStarterAlterDTO;
|
||||||
import cn.axzo.workflow.common.model.response.mq.ProcessInstanceDTO;
|
import cn.axzo.workflow.common.model.response.mq.ProcessInstanceDTO;
|
||||||
import cn.axzo.workflow.starter.api.WorkflowCoreService;
|
import cn.axzo.workflow.starter.api.WorkflowCoreService;
|
||||||
import cn.axzo.workflow.starter.handler.ProcessInstanceEventHandler;
|
import cn.axzo.workflow.starter.handler.ProcessInstanceEventHandler;
|
||||||
|
import com.google.common.collect.ListMultimap;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -34,28 +42,60 @@ public class ImplementationReadyChecker implements ApplicationListener<Applicati
|
|||||||
"onAborted"
|
"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 WorkflowCoreService workflowCoreService;
|
||||||
private final Environment environment;
|
|
||||||
|
|
||||||
public ImplementationReadyChecker(WorkflowCoreService workflowCoreService,
|
public ImplementationReadyChecker(WorkflowCoreService workflowCoreService) {
|
||||||
Environment environment) {
|
|
||||||
this.workflowCoreService = workflowCoreService;
|
this.workflowCoreService = workflowCoreService;
|
||||||
this.environment = environment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(ApplicationReadyEvent event) {
|
public void onApplicationEvent(ApplicationReadyEvent event) {
|
||||||
ApplicationContext context = event.getApplicationContext();
|
ApplicationContext context = event.getApplicationContext();
|
||||||
// 获取所有实现了目标接口的Bean
|
boolean[] methodImplemented = checkUnImplementedMethodsWithInterface(context);
|
||||||
Map<String, ProcessInstanceEventHandler> handlers = context.getBeansOfType(TARGET_INTERFACE);
|
if (getBinaryResult(methodImplemented) == 15) {
|
||||||
if (handlers.isEmpty()) {
|
log.info("Congratulations, passed the verification!");
|
||||||
log.warn("未找到实现 {} 接口的Bean, 请确保至少有一个实现类被正确扫描和注册。", TARGET_INTERFACE.getName());
|
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];
|
boolean[] methodImplemented = new boolean[4];
|
||||||
Arrays.fill(methodImplemented, false);
|
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()) {
|
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位二进制数
|
// 生成4位二进制数
|
||||||
int binaryResult = 0;
|
int binaryResult = 0;
|
||||||
for (int i = 0; i < methodImplemented.length; i++) {
|
for (int i = 0; i < methodImplemented.length; i++) {
|
||||||
@ -85,27 +176,6 @@ public class ImplementationReadyChecker implements ApplicationListener<Applicati
|
|||||||
binaryResult += weight;
|
binaryResult += weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return binaryResult;
|
||||||
// 收集未实现的方法
|
|
||||||
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("祝贺,已通过必接事件的校验!但仍然请确保您的实现类逻辑正确无误,不允许出现空实现,否则生产问题自行负责!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user