通用解析
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;
|
package com.qqchen.deploy.backend.workflow.handler.impl;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
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.enums.BpmnNodeType;
|
||||||
import com.qqchen.deploy.backend.workflow.handler.BpmnNodeHandler;
|
import com.qqchen.deploy.backend.workflow.handler.BpmnNodeHandler;
|
||||||
import com.qqchen.deploy.backend.workflow.model.BpmnNodeConfig;
|
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.flowable.bpmn.model.ServiceTask;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@ -20,14 +18,15 @@ import java.util.Optional;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
|
@BpmnNode(
|
||||||
|
type = "shellTask",
|
||||||
|
name = "Shell脚本",
|
||||||
|
async = true,
|
||||||
|
requiredFields = {"script", "workDir"},
|
||||||
|
delegateExpression = "${shellTaskDelegate}"
|
||||||
|
)
|
||||||
public class ShellTaskHandler implements BpmnNodeHandler<ServiceTask> {
|
public class ShellTaskHandler implements BpmnNodeHandler<ServiceTask> {
|
||||||
|
|
||||||
// Shell任务的必需字段
|
|
||||||
private static final List<String> REQUIRED_FIELDS = Arrays.asList(
|
|
||||||
BpmnConstants.ShellTaskConfig.SCRIPT,
|
|
||||||
BpmnConstants.ShellTaskConfig.WORK_DIR
|
|
||||||
);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(JsonNode nodeData, ServiceTask element, BpmnNodeConfig config) {
|
public void handle(JsonNode nodeData, ServiceTask element, BpmnNodeConfig config) {
|
||||||
// 设置任务委托表达式
|
// 设置任务委托表达式
|
||||||
@ -37,7 +36,7 @@ public class ShellTaskHandler implements BpmnNodeHandler<ServiceTask> {
|
|||||||
setupTaskProperties(element, config);
|
setupTaskProperties(element, config);
|
||||||
|
|
||||||
// 验证必需字段
|
// 验证必需字段
|
||||||
validateRequiredFields(element);
|
validateRequiredFields(element, config);
|
||||||
|
|
||||||
// 处理扩展字段
|
// 处理扩展字段
|
||||||
handleExtensionFields(element, config);
|
handleExtensionFields(element, config);
|
||||||
@ -65,9 +64,10 @@ public class ShellTaskHandler implements BpmnNodeHandler<ServiceTask> {
|
|||||||
* 设置任务委托表达式
|
* 设置任务委托表达式
|
||||||
*/
|
*/
|
||||||
private void setupTaskDelegate(ServiceTask element) {
|
private void setupTaskDelegate(ServiceTask element) {
|
||||||
element.setImplementationType(BpmnConstants.DelegateExpression.TYPE);
|
BpmnNode annotation = this.getClass().getAnnotation(BpmnNode.class);
|
||||||
element.setImplementation(BpmnConstants.DelegateExpression.SHELL_TASK);
|
element.setImplementationType("delegateExpression");
|
||||||
element.setAsynchronous(true); // Shell任务默认异步执行
|
element.setImplementation(annotation.delegateExpression());
|
||||||
|
element.setAsynchronous(annotation.async());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,10 +85,10 @@ public class ShellTaskHandler implements BpmnNodeHandler<ServiceTask> {
|
|||||||
/**
|
/**
|
||||||
* 验证必需字段
|
* 验证必需字段
|
||||||
*/
|
*/
|
||||||
private void validateRequiredFields(ServiceTask element) {
|
private void validateRequiredFields(ServiceTask element, BpmnNodeConfig config) {
|
||||||
List<FieldExtension> fields = element.getFieldExtensions();
|
BpmnNode annotation = this.getClass().getAnnotation(BpmnNode.class);
|
||||||
for (String requiredField : REQUIRED_FIELDS) {
|
for (String requiredField : annotation.requiredFields()) {
|
||||||
boolean fieldExists = fields.stream()
|
boolean fieldExists = element.getFieldExtensions().stream()
|
||||||
.anyMatch(field -> field.getFieldName().equals(requiredField));
|
.anyMatch(field -> field.getFieldName().equals(requiredField));
|
||||||
if (!fieldExists) {
|
if (!fieldExists) {
|
||||||
log.warn("Required field '{}' is missing for shell task '{}'",
|
log.warn("Required field '{}' is missing for shell task '{}'",
|
||||||
|
|||||||
@ -4,12 +4,12 @@ spring:
|
|||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://localhost:3306/deploy-ease-platform?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&createDatabaseIfNotExist=true
|
url: jdbc:mysql://localhost:3306/deploy-ease-platform?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&createDatabaseIfNotExist=true
|
||||||
username: root
|
username: root
|
||||||
password: root
|
password: ServBay.dev
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: update
|
ddl-auto: update
|
||||||
show-sql: true
|
show-sql: false
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
format_sql: false
|
format_sql: false
|
||||||
@ -52,7 +52,7 @@ logging:
|
|||||||
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE
|
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE
|
||||||
org.hibernate.SQL: INFO
|
org.hibernate.SQL: INFO
|
||||||
org.hibernate.type.descriptor.sql: TRACE
|
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
|
org.hibernate.orm.jdbc.bind: INFO
|
||||||
com.qqchen.deploy.backend.framework.utils.EntityPathResolver: DEBUG
|
com.qqchen.deploy.backend.framework.utils.EntityPathResolver: DEBUG
|
||||||
com.qqchen.deploy.backend: DEBUG
|
com.qqchen.deploy.backend: DEBUG
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.util;
|
package com.qqchen.deploy.backend.workflow.util;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
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.handler.impl.ShellTaskHandler;
|
||||||
import com.qqchen.deploy.backend.workflow.parser.BpmnNodeConfigParser;
|
import com.qqchen.deploy.backend.workflow.parser.BpmnNodeConfigParser;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
@ -8,24 +9,40 @@ import org.flowable.bpmn.model.Process;
|
|||||||
import org.flowable.bpmn.model.ServiceTask;
|
import org.flowable.bpmn.model.ServiceTask;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BPMN转换器测试
|
||||||
|
*/
|
||||||
|
@ContextConfiguration(classes = {BpmnNodeManager.class})
|
||||||
class BpmnConverterTest {
|
class BpmnConverterTest {
|
||||||
|
|
||||||
private BpmnConverter bpmnConverter;
|
private BpmnConverter bpmnConverter;
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
|
private BpmnNodeManager nodeManager;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
// 创建Spring上下文
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||||
|
context.register(BpmnNodeManager.class, ShellTaskHandler.class);
|
||||||
|
context.refresh();
|
||||||
|
|
||||||
|
// 获取必要的Bean
|
||||||
objectMapper = new ObjectMapper();
|
objectMapper = new ObjectMapper();
|
||||||
|
nodeManager = context.getBean(BpmnNodeManager.class);
|
||||||
|
|
||||||
|
// 创建BpmnConverter
|
||||||
bpmnConverter = new BpmnConverter(
|
bpmnConverter = new BpmnConverter(
|
||||||
objectMapper,
|
objectMapper,
|
||||||
Collections.singletonList(new ShellTaskHandler()),
|
Collections.singletonList(context.getBean(ShellTaskHandler.class)),
|
||||||
new BpmnNodeConfigParser()
|
new BpmnNodeConfigParser()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -77,6 +94,12 @@ class BpmnConverterTest {
|
|||||||
assertTrue(xml.contains("flowable:delegateExpression=\"${shellTaskDelegate}\""));
|
assertTrue(xml.contains("flowable:delegateExpression=\"${shellTaskDelegate}\""));
|
||||||
assertTrue(xml.contains("<flowable:field name=\"script\">"));
|
assertTrue(xml.contains("<flowable:field name=\"script\">"));
|
||||||
assertTrue(xml.contains("<flowable:field name=\"workDir\">"));
|
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
|
@Test
|
||||||
@ -98,5 +121,24 @@ class BpmnConverterTest {
|
|||||||
assertTrue(xml.contains("<endEvent"));
|
assertTrue(xml.contains("<endEvent"));
|
||||||
// 验证连线
|
// 验证连线
|
||||||
assertTrue(xml.contains("<sequenceFlow"));
|
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