通用解析

This commit is contained in:
戚辰先生 2024-12-10 21:58:42 +08:00
parent 220e930040
commit 2b90d32d8d
6 changed files with 200 additions and 21 deletions

View File

@ -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 "";
}

View File

@ -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<String> requiredFields;
/**
* 委托表达式
*/
private String delegateExpression;
}

View File

@ -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<String, BpmnNodeConfig> nodeConfigs = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
// 扫描所有带有@BpmnNode注解的类
Map<String, Object> 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);
}
}

View File

@ -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<ServiceTask> {
// Shell任务的必需字段
private static final List<String> 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<ServiceTask> {
setupTaskProperties(element, config);
// 验证必需字段
validateRequiredFields(element);
validateRequiredFields(element, config);
// 处理扩展字段
handleExtensionFields(element, config);
@ -65,9 +64,10 @@ public class ShellTaskHandler implements BpmnNodeHandler<ServiceTask> {
* 设置任务委托表达式
*/
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<ServiceTask> {
/**
* 验证必需字段
*/
private void validateRequiredFields(ServiceTask element) {
List<FieldExtension> 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 '{}'",

View File

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

View File

@ -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("<flowable:field name=\"script\">"));
assertTrue(xml.contains("<flowable:field name=\"workDir\">"));
// 验证节点配置
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("<endEvent"));
// 验证连线
assertTrue(xml.contains("<sequenceFlow"));
// 验证节点配置
assertTrue(nodeManager.hasNodeType("shellTask"));
assertNotNull(nodeManager.getNodeConfig("shellTask"));
}
@Test
void testNodeRegistration() {
// 验证节点注册
assertTrue(nodeManager.hasNodeType("shellTask"));
// 验证Shell任务节点配置
var config = nodeManager.getNodeConfig("shellTask");
assertNotNull(config);
assertEquals("Shell脚本", config.getName());
assertTrue(config.isAsync());
assertTrue(config.getRequiredFields().contains("script"));
assertTrue(config.getRequiredFields().contains("workDir"));
assertEquals("${shellTaskDelegate}", config.getDelegateExpression());
}
}