From 2b90d32d8d24d61428f6dd73258588e977ee699a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=88=9A=E8=BE=B0=E5=85=88=E7=94=9F?= Date: Tue, 10 Dec 2024 21:58:42 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E7=94=A8=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/workflow/annotation/BpmnNode.java | 37 +++++++++++ .../workflow/config/BpmnNodeConfig.java | 39 ++++++++++++ .../workflow/config/BpmnNodeManager.java | 61 +++++++++++++++++++ .../handler/impl/ShellTaskHandler.java | 34 +++++------ backend/src/main/resources/application.yml | 6 +- .../workflow/util/BpmnConverterTest.java | 44 ++++++++++++- 6 files changed, 200 insertions(+), 21 deletions(-) create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/annotation/BpmnNode.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/config/BpmnNodeConfig.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/config/BpmnNodeManager.java diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/annotation/BpmnNode.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/annotation/BpmnNode.java new file mode 100644 index 00000000..11b654e7 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/annotation/BpmnNode.java @@ -0,0 +1,37 @@ +package com.qqchen.deploy.backend.workflow.annotation; + +import java.lang.annotation.*; + +/** + * BPMN节点注解 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface BpmnNode { + + /** + * 节点类型 + */ + String type(); + + /** + * 节点名称 + */ + String name() default ""; + + /** + * 是否异步执行 + */ + boolean async() default false; + + /** + * 必需的字段 + */ + String[] requiredFields() default {}; + + /** + * 委托表达式 + */ + String delegateExpression() default ""; +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/config/BpmnNodeConfig.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/config/BpmnNodeConfig.java new file mode 100644 index 00000000..aa38fff0 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/config/BpmnNodeConfig.java @@ -0,0 +1,39 @@ +package com.qqchen.deploy.backend.workflow.config; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +/** + * BPMN节点配置 + */ +@Data +@Builder +public class BpmnNodeConfig { + + /** + * 节点类型 + */ + private String type; + + /** + * 节点名称 + */ + private String name; + + /** + * 是否异步执行 + */ + private boolean async; + + /** + * 必需的字段 + */ + private List requiredFields; + + /** + * 委托表达式 + */ + private String delegateExpression; +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/config/BpmnNodeManager.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/config/BpmnNodeManager.java new file mode 100644 index 00000000..b5dc6ecf --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/config/BpmnNodeManager.java @@ -0,0 +1,61 @@ +package com.qqchen.deploy.backend.workflow.config; + +import com.qqchen.deploy.backend.workflow.annotation.BpmnNode; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * BPMN节点管理器 + */ +@Slf4j +@Component +public class BpmnNodeManager { + + @Autowired + private ApplicationContext applicationContext; + + private final Map nodeConfigs = new ConcurrentHashMap<>(); + + @PostConstruct + public void init() { + // 扫描所有带有@BpmnNode注解的类 + Map beans = applicationContext.getBeansWithAnnotation(BpmnNode.class); + + beans.forEach((name, bean) -> { + BpmnNode annotation = bean.getClass().getAnnotation(BpmnNode.class); + if (annotation != null) { + BpmnNodeConfig config = BpmnNodeConfig.builder() + .type(annotation.type()) + .name(annotation.name()) + .async(annotation.async()) + .requiredFields(Arrays.asList(annotation.requiredFields())) + .delegateExpression(annotation.delegateExpression()) + .build(); + + nodeConfigs.put(annotation.type(), config); + log.info("Registered BPMN node type: {}", annotation.type()); + } + }); + } + + /** + * 获取节点配置 + */ + public BpmnNodeConfig getNodeConfig(String type) { + return nodeConfigs.get(type); + } + + /** + * 检查节点类型是否存在 + */ + public boolean hasNodeType(String type) { + return nodeConfigs.containsKey(type); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/handler/impl/ShellTaskHandler.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/handler/impl/ShellTaskHandler.java index e0b27357..f1f44d22 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/handler/impl/ShellTaskHandler.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/handler/impl/ShellTaskHandler.java @@ -1,7 +1,7 @@ package com.qqchen.deploy.backend.workflow.handler.impl; import com.fasterxml.jackson.databind.JsonNode; -import com.qqchen.deploy.backend.workflow.constants.BpmnConstants; +import com.qqchen.deploy.backend.workflow.annotation.BpmnNode; import com.qqchen.deploy.backend.workflow.enums.BpmnNodeType; import com.qqchen.deploy.backend.workflow.handler.BpmnNodeHandler; import com.qqchen.deploy.backend.workflow.model.BpmnNodeConfig; @@ -10,8 +10,6 @@ import org.flowable.bpmn.model.FieldExtension; import org.flowable.bpmn.model.ServiceTask; import org.springframework.stereotype.Component; -import java.util.Arrays; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -20,14 +18,15 @@ import java.util.Optional; */ @Slf4j @Component +@BpmnNode( + type = "shellTask", + name = "Shell脚本", + async = true, + requiredFields = {"script", "workDir"}, + delegateExpression = "${shellTaskDelegate}" +) public class ShellTaskHandler implements BpmnNodeHandler { - // Shell任务的必需字段 - private static final List REQUIRED_FIELDS = Arrays.asList( - BpmnConstants.ShellTaskConfig.SCRIPT, - BpmnConstants.ShellTaskConfig.WORK_DIR - ); - @Override public void handle(JsonNode nodeData, ServiceTask element, BpmnNodeConfig config) { // 设置任务委托表达式 @@ -37,7 +36,7 @@ public class ShellTaskHandler implements BpmnNodeHandler { setupTaskProperties(element, config); // 验证必需字段 - validateRequiredFields(element); + validateRequiredFields(element, config); // 处理扩展字段 handleExtensionFields(element, config); @@ -65,9 +64,10 @@ public class ShellTaskHandler implements BpmnNodeHandler { * 设置任务委托表达式 */ private void setupTaskDelegate(ServiceTask element) { - element.setImplementationType(BpmnConstants.DelegateExpression.TYPE); - element.setImplementation(BpmnConstants.DelegateExpression.SHELL_TASK); - element.setAsynchronous(true); // Shell任务默认异步执行 + BpmnNode annotation = this.getClass().getAnnotation(BpmnNode.class); + element.setImplementationType("delegateExpression"); + element.setImplementation(annotation.delegateExpression()); + element.setAsynchronous(annotation.async()); } /** @@ -85,10 +85,10 @@ public class ShellTaskHandler implements BpmnNodeHandler { /** * 验证必需字段 */ - private void validateRequiredFields(ServiceTask element) { - List fields = element.getFieldExtensions(); - for (String requiredField : REQUIRED_FIELDS) { - boolean fieldExists = fields.stream() + private void validateRequiredFields(ServiceTask element, BpmnNodeConfig config) { + BpmnNode annotation = this.getClass().getAnnotation(BpmnNode.class); + for (String requiredField : annotation.requiredFields()) { + boolean fieldExists = element.getFieldExtensions().stream() .anyMatch(field -> field.getFieldName().equals(requiredField)); if (!fieldExists) { log.warn("Required field '{}' is missing for shell task '{}'", diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index cf56559a..240ede3f 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -4,12 +4,12 @@ spring: datasource: url: jdbc:mysql://localhost:3306/deploy-ease-platform?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&createDatabaseIfNotExist=true username: root - password: root + password: ServBay.dev driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update - show-sql: true + show-sql: false properties: hibernate: format_sql: false @@ -52,7 +52,7 @@ logging: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE org.hibernate.SQL: INFO org.hibernate.type.descriptor.sql: TRACE - org.hibernate.type.descriptor.sql.BasicBinder: INFO + org.hibernate.type.descriptor.sql.BasicBinder: TRACE org.hibernate.orm.jdbc.bind: INFO com.qqchen.deploy.backend.framework.utils.EntityPathResolver: DEBUG com.qqchen.deploy.backend: DEBUG diff --git a/backend/src/test/java/com/qqchen/deploy/backend/workflow/util/BpmnConverterTest.java b/backend/src/test/java/com/qqchen/deploy/backend/workflow/util/BpmnConverterTest.java index e64ee1a6..bc9d84f8 100644 --- a/backend/src/test/java/com/qqchen/deploy/backend/workflow/util/BpmnConverterTest.java +++ b/backend/src/test/java/com/qqchen/deploy/backend/workflow/util/BpmnConverterTest.java @@ -1,6 +1,7 @@ package com.qqchen.deploy.backend.workflow.util; import com.fasterxml.jackson.databind.ObjectMapper; +import com.qqchen.deploy.backend.workflow.config.BpmnNodeManager; import com.qqchen.deploy.backend.workflow.handler.impl.ShellTaskHandler; import com.qqchen.deploy.backend.workflow.parser.BpmnNodeConfigParser; import org.flowable.bpmn.model.BpmnModel; @@ -8,24 +9,40 @@ import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.ServiceTask; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.io.ClassPathResource; +import org.springframework.test.context.ContextConfiguration; import java.nio.charset.StandardCharsets; import java.util.Collections; import static org.junit.jupiter.api.Assertions.*; +/** + * BPMN转换器测试 + */ +@ContextConfiguration(classes = {BpmnNodeManager.class}) class BpmnConverterTest { private BpmnConverter bpmnConverter; private ObjectMapper objectMapper; + private BpmnNodeManager nodeManager; @BeforeEach void setUp() { + // 创建Spring上下文 + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(BpmnNodeManager.class, ShellTaskHandler.class); + context.refresh(); + + // 获取必要的Bean objectMapper = new ObjectMapper(); + nodeManager = context.getBean(BpmnNodeManager.class); + + // 创建BpmnConverter bpmnConverter = new BpmnConverter( objectMapper, - Collections.singletonList(new ShellTaskHandler()), + Collections.singletonList(context.getBean(ShellTaskHandler.class)), new BpmnNodeConfigParser() ); } @@ -77,6 +94,12 @@ class BpmnConverterTest { assertTrue(xml.contains("flowable:delegateExpression=\"${shellTaskDelegate}\"")); assertTrue(xml.contains("")); assertTrue(xml.contains("")); + + // 验证节点配置 + assertTrue(nodeManager.hasNodeType("shellTask")); + assertNotNull(nodeManager.getNodeConfig("shellTask")); + assertEquals("Shell脚本", nodeManager.getNodeConfig("shellTask").getName()); + assertTrue(nodeManager.getNodeConfig("shellTask").isAsync()); } @Test @@ -98,5 +121,24 @@ class BpmnConverterTest { assertTrue(xml.contains("