通用解析
This commit is contained in:
parent
220e930040
commit
2b90d32d8d
@ -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 "";
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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 '{}'",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user