反序列化问题。

This commit is contained in:
dengqichen 2024-12-24 17:13:08 +08:00
parent 57f1a8d90f
commit 1914c67566
19 changed files with 145 additions and 895 deletions

View File

@ -18,6 +18,26 @@ import org.springframework.beans.factory.annotation.Autowired;
@Slf4j @Slf4j
public abstract class BaseNodeDelegate<P, L> implements JavaDelegate { public abstract class BaseNodeDelegate<P, L> implements JavaDelegate {
/**
* 执行状态变量名
*/
protected static final String EXECUTION_STATUS = "executionStatus";
/**
* 执行状态成功
*/
protected static final String STATUS_SUCCESS = "success";
/**
* 执行状态失败
*/
protected static final String STATUS_FAILURE = "failure";
/**
* 执行状态中止
*/
protected static final String STATUS_ABORTED = "aborted";
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@ -47,12 +67,30 @@ public abstract class BaseNodeDelegate<P, L> implements JavaDelegate {
// 执行具体的任务逻辑 // 执行具体的任务逻辑
executeInternal(execution, panelVars, localVars); executeInternal(execution, panelVars, localVars);
// 如果节点没有设置状态默认设置成功状态
if (execution.getVariable(EXECUTION_STATUS) == null) {
setExecutionStatus(execution, STATUS_SUCCESS);
}
} catch (Exception e) { } catch (Exception e) {
// 设置失败状态
setExecutionStatus(execution, STATUS_FAILURE);
log.error("Task execution failed", e); log.error("Task execution failed", e);
throw new RuntimeException("Task execution failed: " + e.getMessage(), e); throw new RuntimeException("Task execution failed: " + e.getMessage(), e);
} }
} }
/**
* 设置执行状态
*
* @param execution 执行上下文
* @param status 状态值
*/
protected void setExecutionStatus(DelegateExecution execution, String status) {
execution.setVariable(EXECUTION_STATUS, status);
log.debug("Set execution status: {}", status);
}
/** /**
* 获取Panel变量的类型 * 获取Panel变量的类型
* *

View File

@ -0,0 +1,19 @@
package com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables;
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class GatewayNodeLocalVariables extends BaseNodeLocalVariables {
@SchemaProperty(
title = "委派者",
description = "委派者",
defaultValue = "${gatewayNodeDelegate}",
required = true
)
private String delegate;
}

View File

@ -0,0 +1,30 @@
package com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables;
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
public class GatewayNodePanelVariables extends BaseNodePanelVariables {
@SchemaProperty(
title = "网关类型",
description = "网关类型",
required = true,
enumValues = {
"EXCLUSIVE_GATEWAY", // 排他网关
"PARALLEL_GATEWAY", // 并行网关
"INCLUSIVE_GATEWAY" // 包容网关
},
enumNames = {
"排他网关",
"并行网关",
"包容网关"
},
defaultValue = "EXCLUSIVE_GATEWAY"
)
private String gatewayType;
}

View File

@ -8,7 +8,8 @@ public class Circle {
@SchemaProperty( @SchemaProperty(
title = "半径", title = "半径",
description = "圆的半径", description = "圆的半径",
required = true required = true,
defaultValue = "5"
) )
private Integer r; private Integer r;

View File

@ -3,32 +3,12 @@ package com.qqchen.deploy.backend.workflow.dto.definition.workflow;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data; import lombok.Data;
/**
* 边配置
* @author cascade
* @date 2024-12-11
*/
@Data @Data
@JsonInclude(JsonInclude.Include.NON_NULL) // 只序列化非空字段 @JsonInclude(JsonInclude.Include.NON_NULL) // 只序列化非空字段
public class WorkflowDefinitionEdgeConfig { public class WorkflowDefinitionEdgeConfig {
/**
* 条件
*/
private String condition;
/**
* 表达式
*/
private String expression;
/**
* 条件表达式
*/
private String conditionExpression;
/**
* 类型sequence/message/association
*/
private String type; private String type;
private WorkflowDefinitionEdgeConfigCondition condition;
} }

View File

@ -0,0 +1,16 @@
package com.qqchen.deploy.backend.workflow.dto.definition.workflow;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL) // 只序列化非空字段
public class WorkflowDefinitionEdgeConfigCondition {
private String type;
private String expression;
private int priority;
}

View File

@ -1,21 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto.definition.workflow;
import lombok.Data;
/**
* 节点大小
* @author cascade
* @date 2024-12-11
*/
@Data
public class WorkflowDefinitionEdgeSize {
/**
* 宽度
*/
private Integer width;
/**
* 高度
*/
private Integer height;
}

View File

@ -3,21 +3,16 @@ package com.qqchen.deploy.backend.workflow.dto.definition.workflow;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
/**
* 工作流图形数据传输对象
* @author cascade
* @date 2024-12-11
*/
@Data @Data
public class WorkflowDefinitionGraph { public class WorkflowDefinitionGraph {
/** /**
* 节点列表 * 节点列表
*/ */
private List<WorkflowDefinitionNode> nodes; private List<WorkflowDefinitionGraphNode> nodes;
/** /**
* 边列表 * 边列表
*/ */
private List<WorkflowDefinitionEdge> edges; private List<WorkflowDefinitionGraphEdge> edges;
} }

View File

@ -9,7 +9,7 @@ import java.util.Map;
* @date 2024-12-11 * @date 2024-12-11
*/ */
@Data @Data
public class WorkflowDefinitionEdge { public class WorkflowDefinitionGraphEdge {
/** /**
* 边ID * 边ID
*/ */
@ -35,8 +35,4 @@ public class WorkflowDefinitionEdge {
*/ */
private WorkflowDefinitionEdgeConfig config; private WorkflowDefinitionEdgeConfig config;
/**
* 边属性
*/
private Map<String, Object> properties;
} }

View File

@ -5,8 +5,6 @@ import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.util.Map;
/** /**
* 工作流节点数据传输对象 * 工作流节点数据传输对象
* *
@ -14,7 +12,7 @@ import java.util.Map;
* @date 2024-12-11 * @date 2024-12-11
*/ */
@Data @Data
public class WorkflowDefinitionNode { public class WorkflowDefinitionGraphNode {
/** /**
* 节点ID * 节点ID

View File

@ -1,25 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto.definition.workflow;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
/**
* 位置数据传输对象
* @author cascade
* @date 2024-12-11
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WorkflowDefinitionPosition {
/**
* X坐标
*/
private Double x;
/**
* Y坐标
*/
private Double y;
}

View File

@ -1,195 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto.definition.workflow;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 节点UI配置类
* 用于定义工作流节点的UI展示属性
*/
@Data
@Accessors(chain = true)
public class WorkflowNodeGraph {
private String shape;
private Size size;
private Style style;
private Ports ports;
private Position position;
public WorkflowNodeGraph setSize(int width, int height) {
this.size = new Size(width, height);
return this;
}
public WorkflowNodeGraph setStyle(String fill, String stroke, String icon) {
this.style = new Style(fill, stroke, icon);
return this;
}
public WorkflowNodeGraph setPosition(int x, int y) {
this.position = new Position(x, y);
return this;
}
public WorkflowNodeGraph configPorts(List<String> types) {
this.ports = new Ports();
Map<String, PortGroup> groups = new HashMap<>();
types.forEach(type -> {
PortGroup group = new PortGroup();
group.setPosition(type.equals("in") ? "left" : "right");
PortCircle circle = new PortCircle();
circle.setR(4);
circle.setFill("#ffffff");
circle.setStroke("#1890ff");
PortAttrs attrs = new PortAttrs();
attrs.setCircle(circle);
group.setAttrs(attrs);
groups.put(type, group);
});
this.ports.setGroups(groups);
return this;
}
/**
* 节点大小配置
*/
@Data
public static class Size {
private int width;
private int height;
public Size() {
// 默认构造函数
}
public Size(int width, int height) {
this.width = width;
this.height = height;
}
}
/**
* 节点位置配置
*/
@Data
public static class Position {
private int x;
private int y;
public Position() {
// 默认构造函数
}
public Position(int x, int y) {
this.x = x;
this.y = y;
}
}
/**
* 节点样式配置
*/
@Data
public static class Style {
private String fill; // 填充颜色
private String stroke; // 边框颜色
private String icon; // 图标名称
private String iconColor; // 图标颜色
private int strokeWidth = 2;// 边框宽度
public Style() {
// 默认构造函数
}
public Style(String fill, String stroke, String icon) {
this.fill = fill;
this.stroke = stroke;
this.icon = icon;
this.iconColor = stroke;
}
}
/**
* 节点连接点配置
*/
@Data
public static class Ports {
private Map<String, PortGroup> groups;
public Ports() {
// 默认构造函数
this.groups = new HashMap<>();
}
/**
* 获取端口类型列表
*
* @return 端口类型列表"in"/"out"
*/
public List<String> getTypes() {
if (groups == null) {
return new ArrayList<>();
}
return new ArrayList<>(groups.keySet());
}
}
@Data
public static class PortGroup {
private String position;
private PortAttrs attrs;
public PortGroup() {
// 默认构造函数
}
}
@Data
public static class PortAttrs {
private PortCircle circle;
public PortAttrs() {
// 默认构造函数
}
}
@Data
public static class PortCircle {
private int r;
private String fill;
private String stroke;
private boolean magnet;
public PortCircle() {
// 默认构造函数
}
}
}

View File

@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete; import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity; import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.workflow.dto.definition.node.uiVariables.NodeUiVariables; import com.qqchen.deploy.backend.workflow.dto.definition.node.uiVariables.NodeUiVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowNodeGraph;
import com.qqchen.deploy.backend.workflow.enums.NodeCategoryEnums; import com.qqchen.deploy.backend.workflow.enums.NodeCategoryEnums;
import com.vladmihalcea.hibernate.type.json.JsonType; import com.vladmihalcea.hibernate.type.json.JsonType;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -7,16 +7,14 @@ import com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables.Dep
import com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables.ScriptNodeLocalVariables; import com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables.ScriptNodeLocalVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.DeployNodePanelVariables; import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.DeployNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.EndNodePanelVariables; import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.EndNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.GatewayNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.ScriptNodePanelVariables; import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.ScriptNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.StartNodePanelVariables; import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.StartNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.uiVariables.NodeUiVariables; import com.qqchen.deploy.backend.workflow.dto.definition.node.uiVariables.NodeUiVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowNodeGraph;
import lombok.Getter; import lombok.Getter;
import java.util.Arrays;
/** /**
* 工作节点类型枚举 * 工作<EFBFBD><EFBFBD>节点类型枚举
* 定义了工作流中所有可用的节点类型及其UI配置 * 定义了工作流中所有可用的节点类型及其UI配置
*/ */
@Getter @Getter
@ -38,12 +36,7 @@ public enum NodeTypeEnums {
NodeUiVariables.class, NodeUiVariables.class,
BpmnNodeTypeEnums.START_EVENT, BpmnNodeTypeEnums.START_EVENT,
NodeCategoryEnums.EVENT, NodeCategoryEnums.EVENT,
"工作流开始", // 节点简要描述 "工作流开始"
new WorkflowNodeGraph() // UI配置
.setShape("circle")
.setSize(40, 40)
.setStyle("#e8f7ff", "#1890ff", "play-circle")
.configPorts(Arrays.asList("out"))
), ),
/** /**
@ -62,12 +55,7 @@ public enum NodeTypeEnums {
NodeUiVariables.class, NodeUiVariables.class,
BpmnNodeTypeEnums.END_EVENT, BpmnNodeTypeEnums.END_EVENT,
NodeCategoryEnums.EVENT, NodeCategoryEnums.EVENT,
"工作流结束", "工作流结束"
new WorkflowNodeGraph()
.setShape("circle")
.setSize(40, 40)
.setStyle("#fff1f0", "#ff4d4f", "stop")
.configPorts(Arrays.asList("in"))
), ),
SCRIPT_NODE( SCRIPT_NODE(
"SCRIPT_NODE", "SCRIPT_NODE",
@ -78,12 +66,7 @@ public enum NodeTypeEnums {
NodeUiVariables.class, NodeUiVariables.class,
BpmnNodeTypeEnums.SERVICE_TASK, BpmnNodeTypeEnums.SERVICE_TASK,
NodeCategoryEnums.TASK, NodeCategoryEnums.TASK,
"脚本执行任务", "脚本执行任务"
new WorkflowNodeGraph()
.setShape("rect")
.setSize(120, 60)
.setStyle("#ffffff", "#1890ff", "code")
.configPorts(Arrays.asList("in", "out"))
), ),
DEPLOY_NODE( DEPLOY_NODE(
"DEPLOY_NODE", "DEPLOY_NODE",
@ -94,8 +77,18 @@ public enum NodeTypeEnums {
NodeUiVariables.class, NodeUiVariables.class,
BpmnNodeTypeEnums.SERVICE_TASK, BpmnNodeTypeEnums.SERVICE_TASK,
NodeCategoryEnums.TASK, NodeCategoryEnums.TASK,
"构建任务", "构建任务"
null ),
GATEWAY_NODE(
"GATEWAY_NODE",
"网关节点",
null,
GatewayNodePanelVariables.class,
null,
NodeUiVariables.class,
BpmnNodeTypeEnums.EXCLUSIVE_GATEWAY,
NodeCategoryEnums.GATEWAY,
"条件分支控制"
); );
// //
// /** // /**
@ -250,8 +243,6 @@ public enum NodeTypeEnums {
private final String description; // 节点详细描述 private final String description; // 节点详细描述
private final WorkflowNodeGraph uiConfig; // UI配置
NodeTypeEnums( NodeTypeEnums(
String code, String code,
String name, String name,
@ -261,8 +252,8 @@ public enum NodeTypeEnums {
Class<?> uiVariables, Class<?> uiVariables,
BpmnNodeTypeEnums bpmnType, BpmnNodeTypeEnums bpmnType,
NodeCategoryEnums category, NodeCategoryEnums category,
String description, String description
WorkflowNodeGraph uiConfig) { ) {
this.code = code; this.code = code;
this.name = name; this.name = name;
this.localVariables = localVariables; this.localVariables = localVariables;
@ -272,7 +263,6 @@ public enum NodeTypeEnums {
this.bpmnType = bpmnType; this.bpmnType = bpmnType;
this.category = category; this.category = category;
this.description = description; this.description = description;
this.uiConfig = uiConfig;
} }
/** /**

View File

@ -1,9 +1,9 @@
package com.qqchen.deploy.backend.workflow.util; package com.qqchen.deploy.backend.workflow.util;
import com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants; import com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionEdge; import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraphEdge;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraph; import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraph;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionNode; import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraphNode;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums; import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.BpmnAutoLayout; import org.flowable.bpmn.BpmnAutoLayout;
@ -101,12 +101,12 @@ public class BpmnConverter {
* @param process 流程定义对象 * @param process 流程定义对象
* @return 节点ID映射关系 * @return 节点ID映射关系
*/ */
private Map<String, String> convertNodes(List<WorkflowDefinitionNode> nodes, Process process) { private Map<String, String> convertNodes(List<WorkflowDefinitionGraphNode> nodes, Process process) {
// 步骤2.1创建节点ID映射 // 步骤2.1创建节点ID映射
Map<String, String> idMapping = new HashMap<>(); Map<String, String> idMapping = new HashMap<>();
// 步骤2.2遍历并转换每个节点 // 步骤2.2遍历并转换每个节点
for (WorkflowDefinitionNode node : nodes) { for (WorkflowDefinitionGraphNode node : nodes) {
log.debug("转换节点: {}, 类型: {}", node.getNodeName(), node.getNodeCode()); log.debug("转换节点: {}, 类型: {}", node.getNodeName(), node.getNodeCode());
convertSingleNode(node, process, idMapping); convertSingleNode(node, process, idMapping);
} }
@ -121,7 +121,7 @@ public class BpmnConverter {
* @param process 流程定义对象 * @param process 流程定义对象
* @param idMapping 节点ID映射关系 * @param idMapping 节点ID映射关系
*/ */
private void convertSingleNode(WorkflowDefinitionNode node, Process process, Map<String, String> idMapping) { private void convertSingleNode(WorkflowDefinitionGraphNode node, Process process, Map<String, String> idMapping) {
try { try {
// 步骤2.3获取节点对应的BPMN元素类型 // 步骤2.3获取节点对应的BPMN元素类型
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -158,7 +158,7 @@ public class BpmnConverter {
* @param node 工作流节点定义 * @param node 工作流节点定义
* @param process 当前流程 * @param process 当前流程
*/ */
private void configureFlowElement(FlowElement element, WorkflowDefinitionNode node, Process process) { private void configureFlowElement(FlowElement element, WorkflowDefinitionGraphNode node, Process process) {
// 步骤1创建基本的扩展元素 // 步骤1创建基本的扩展元素
Map<String, List<ExtensionElement>> extensionElements = createBaseExtensionElements(); Map<String, List<ExtensionElement>> extensionElements = createBaseExtensionElements();
@ -200,7 +200,7 @@ public class BpmnConverter {
* @param process 当前流程 * @param process 当前流程
* @param extensionElements 扩展元素 * @param extensionElements 扩展元素
*/ */
private void configureServiceTask(ServiceTask serviceTask, WorkflowDefinitionNode node, Process process, Map<String, List<ExtensionElement>> extensionElements) { private void configureServiceTask(ServiceTask serviceTask, WorkflowDefinitionGraphNode node, Process process, Map<String, List<ExtensionElement>> extensionElements) {
if (node.getLocalVariables() != null && node.getLocalVariables().has("delegate")) { if (node.getLocalVariables() != null && node.getLocalVariables().has("delegate")) {
String delegate = node.getLocalVariables().get("delegate").asText(); String delegate = node.getLocalVariables().get("delegate").asText();
serviceTask.setImplementationType("delegateExpression"); serviceTask.setImplementationType("delegateExpression");
@ -233,7 +233,7 @@ public class BpmnConverter {
* @param extensionElements 扩展元素 * @param extensionElements 扩展元素
* @param node 工作流节点定义 * @param node 工作流节点定义
*/ */
private void addExecutionVariables(Map<String, List<ExtensionElement>> extensionElements, WorkflowDefinitionNode node) { private void addExecutionVariables(Map<String, List<ExtensionElement>> extensionElements, WorkflowDefinitionGraphNode node) {
// 添加panelVariables变量 // 添加panelVariables变量
if (node.getPanelVariables() != null) { if (node.getPanelVariables() != null) {
ExtensionElement fieldElement = new ExtensionElement(); ExtensionElement fieldElement = new ExtensionElement();
@ -422,8 +422,8 @@ public class BpmnConverter {
* @param idMapping 节点ID映射 * @param idMapping 节点ID映射
* @param process 流程定义 * @param process 流程定义
*/ */
private void convertEdgesToSequenceFlows(List<WorkflowDefinitionEdge> edges, Map<String, String> idMapping, Process process) { private void convertEdgesToSequenceFlows(List<WorkflowDefinitionGraphEdge> edges, Map<String, String> idMapping, Process process) {
for (WorkflowDefinitionEdge edge : edges) { for (WorkflowDefinitionGraphEdge edge : edges) {
log.debug("转换连线: from {} to {}", edge.getFrom(), edge.getTo()); log.debug("转换连线: from {} to {}", edge.getFrom(), edge.getTo());
SequenceFlow flow = new SequenceFlow(); SequenceFlow flow = new SequenceFlow();

View File

@ -1,9 +1,9 @@
package com.qqchen.deploy.backend.workflow.util; package com.qqchen.deploy.backend.workflow.util;
import com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants; import com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionEdge; import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraphEdge;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraph; import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraph;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionNode; import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraphNode;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums; import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.BpmnAutoLayout; import org.flowable.bpmn.BpmnAutoLayout;
@ -68,7 +68,7 @@ public class BpmnConverter1 {
Map<String, String> idMapping = new HashMap<>(); Map<String, String> idMapping = new HashMap<>();
// 转换节点 // 转换节点
for (WorkflowDefinitionNode node : graph.getNodes()) { for (WorkflowDefinitionGraphNode node : graph.getNodes()) {
log.debug("转换节点: {}, 类型: {}", node.getNodeName(), node.getNodeCode()); log.debug("转换节点: {}, 类型: {}", node.getNodeName(), node.getNodeCode());
// 通过NodeTypeEnums获取对应的BpmnTypeEnums中定义的实例类型 // 通过NodeTypeEnums获取对应的BpmnTypeEnums中定义的实例类型
@ -91,7 +91,7 @@ public class BpmnConverter1 {
} }
// 转换连线 // 转换连线
for (WorkflowDefinitionEdge edge : graph.getEdges()) { for (WorkflowDefinitionGraphEdge edge : graph.getEdges()) {
log.debug("转换连线: from {} to {}", edge.getFrom(), edge.getTo()); log.debug("转换连线: from {} to {}", edge.getFrom(), edge.getTo());
SequenceFlow flow = new SequenceFlow(); SequenceFlow flow = new SequenceFlow();
@ -130,7 +130,7 @@ public class BpmnConverter1 {
* @param node 工作流节点定义 * @param node 工作流节点定义
* @param process 当前流程 * @param process 当前流程
*/ */
private void configureFlowElement(FlowElement element, WorkflowDefinitionNode node, Process process) { private void configureFlowElement(FlowElement element, WorkflowDefinitionGraphNode node, Process process) {
// 为所有节点添加执行监听器 // 为所有节点添加执行监听器
Map<String, List<ExtensionElement>> extensionElements = new HashMap<>(); Map<String, List<ExtensionElement>> extensionElements = new HashMap<>();
List<ExtensionElement> executionListeners = new ArrayList<>(); List<ExtensionElement> executionListeners = new ArrayList<>();

View File

@ -1,396 +0,0 @@
package com.qqchen.deploy.backend.workflow.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
import com.qqchen.deploy.backend.workflow.annotation.SchemaPropertyDataSource;
import com.qqchen.deploy.backend.workflow.annotation.SchemaPropertyDataSourceParam;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.BaseNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.EndNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.ScriptNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.StartNodePanelVariables;
import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowNodeGraph;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Schema生成器
* 用于生成工作流节点的配置Schema和UI Schema
*/
public class SchemaGenerator {
private static final ObjectMapper mapper = new ObjectMapper();
/**
* 获取节点类型对应的配置类
*/
private static Class<?> getConfigClassForNodeType(NodeTypeEnums nodeType) {
switch (nodeType) {
case START_EVENT:
return StartNodePanelVariables.class;
case END_EVENT:
return EndNodePanelVariables.class;
case SCRIPT_NODE:
return ScriptNodePanelVariables.class;
// 其他节点类型的配置类
// case USER_TASK:
// case SERVICE_TASK:
// case EXCLUSIVE_GATEWAY:
// case PARALLEL_GATEWAY:
// case SUBPROCESS:
// case CALL_ACTIVITY:
// TODO: 为其他节点类型添加对应的配置类
// return BaseNodeConfig.class;
default:
return BaseNodePanelVariables.class;
}
}
/**
* 生成所有节点类型的schema
*/
public static JsonNode generateAllNodeTypeSchemas() {
ArrayNode schemas = mapper.createArrayNode();
// 遍历所有节点类型
for (NodeTypeEnums nodeType : NodeTypeEnums.values()) {
// 获取节点类型对应的配置类
Class<?> configClass = getConfigClassForNodeType(nodeType);
// 生成该节点的schema
ObjectNode nodeSchema = generateNodeSchema(nodeType, configClass);
schemas.add(nodeSchema);
}
return schemas;
}
/**
* 生成节点schema
*/
private static ObjectNode generateNodeSchema(NodeTypeEnums nodeType, Class<?> configClass) {
ObjectNode node = mapper.createObjectNode();
// 设置基本信<EFBFBD><EFBFBD>
node.put("code", nodeType.getCode());
node.put("name", nodeType.getName());
// node.put("description", nodeType.getShortDesc());
// 添加详细信息
ObjectNode details = mapper.createObjectNode();
details.put("description", nodeType.getDescription());
ArrayNode features = mapper.createArrayNode();
// nodeType.getFeatures().forEach(features::add);
details.set("features", features);
ArrayNode scenarios = mapper.createArrayNode();
// nodeType.getScenarios().forEach(scenarios::add);
details.set("scenarios", scenarios);
node.set("details", details);
// 生成配置schema并设置到configSchema字段
ObjectNode configSchema = generateConfigSchema(configClass);
node.set("configSchema", configSchema);
// 生成UI schema并设置到uiSchema字段
ObjectNode uiSchema = generateUiSchema(nodeType);
node.set("uiSchema", uiSchema);
return node;
}
/**
* 生成UI Schema
* 包含节点的UI相关配置如形状大小样式等
*/
private static ObjectNode generateUiSchema(NodeTypeEnums nodeType) {
ObjectNode uiSchema = mapper.createObjectNode();
WorkflowNodeGraph uiConfig = nodeType.getUiConfig();
// 设置基本形状和大小
uiSchema.put("shape", uiConfig.getShape());
ObjectNode size = mapper.createObjectNode();
size.put("width", uiConfig.getSize().getWidth());
size.put("height", uiConfig.getSize().getHeight());
uiSchema.set("size", size);
// 设置样式
ObjectNode style = mapper.createObjectNode();
style.put("fill", uiConfig.getStyle().getFill());
style.put("stroke", uiConfig.getStyle().getStroke());
style.put("strokeWidth", uiConfig.getStyle().getStrokeWidth());
style.put("icon", uiConfig.getStyle().getIcon());
style.put("iconColor", uiConfig.getStyle().getIconColor());
uiSchema.set("style", style);
// 设置连接点
ObjectNode ports = mapper.createObjectNode();
ObjectNode groups = mapper.createObjectNode();
uiConfig.getPorts().getGroups().forEach((key, group) -> {
ObjectNode groupNode = mapper.createObjectNode();
groupNode.put("position", group.getPosition());
ObjectNode attrs = mapper.createObjectNode();
ObjectNode circle = mapper.createObjectNode();
circle.put("r", group.getAttrs().getCircle().getR());
circle.put("fill", group.getAttrs().getCircle().getFill());
circle.put("stroke", group.getAttrs().getCircle().getStroke());
attrs.set("circle", circle);
groupNode.set("attrs", attrs);
groups.set(key, groupNode);
});
ports.set("groups", groups);
uiSchema.set("ports", ports);
return uiSchema;
}
/**
* 根据配置类生成schema
*/
private static ObjectNode generateConfigSchema(Class<?> configClass) {
ObjectNode schema = mapper.createObjectNode();
schema.put("type", "object");
// 创建一个有序的properties对象
ObjectNode properties = mapper.createObjectNode();
List<String> required = new ArrayList<>();
// 先处理基础字段按指定顺序
processBaseFields(properties, required, configClass);
// 处理其他字段
processRemainingFields(properties, required, configClass);
schema.set("properties", properties);
// 如果有必填字段添加到schema中
if (!required.isEmpty()) {
ArrayNode requiredArray = mapper.createArrayNode();
required.forEach(requiredArray::add);
schema.set("required", requiredArray);
}
return schema;
}
/**
* 处理基础字<EFBFBD><EFBFBD><EFBFBD>codenamedescription
*/
private static void processBaseFields(ObjectNode properties, List<String> required, Class<?> configClass) {
String[] baseFields = {"code", "name", "description"};
Class<?> currentClass = configClass;
while (currentClass != null && !currentClass.equals(Object.class)) {
for (String fieldName : baseFields) {
try {
Field field = currentClass.getDeclaredField(fieldName);
SchemaProperty annotation = field.getAnnotation(SchemaProperty.class);
if (annotation != null && !properties.has(fieldName)) {
processField(properties, required, field, annotation);
}
} catch (NoSuchFieldException ignored) {
// 字段不在当前类中继续查找父类
}
}
currentClass = currentClass.getSuperclass();
}
}
/**
* 处理剩余字段
*/
private static void processRemainingFields(ObjectNode properties, List<String> required, Class<?> configClass) {
Set<String> baseFields = Set.of("code", "name", "description");
Class<?> currentClass = configClass;
while (currentClass != null && !currentClass.equals(Object.class)) {
for (Field field : currentClass.getDeclaredFields()) {
String fieldName = field.getName();
if (!baseFields.contains(fieldName)) {
SchemaProperty annotation = field.getAnnotation(SchemaProperty.class);
if (annotation != null && !properties.has(fieldName)) {
processField(properties, required, field, annotation);
}
}
}
currentClass = currentClass.getSuperclass();
}
}
/**
* 处理字段
*/
private static void processField(ObjectNode properties, List<String> required, Field field, SchemaProperty annotation) {
ObjectNode fieldSchema = mapper.createObjectNode();
Class<?> fieldType = field.getType();
// 设置字段类型
String jsonType = getJsonSchemaType(fieldType);
fieldSchema.put("type", jsonType);
// 设置基本属性
fieldSchema.put("title", annotation.title());
if (!annotation.description().isEmpty()) {
fieldSchema.put("description", annotation.description());
}
// 处理hidden属性
if (annotation.hidden()) {
fieldSchema.put("hidden", true);
}
// 处理数据源属性
SchemaPropertyDataSource dataSource = annotation.dataSource();
if (!dataSource.type().isEmpty()) {
ObjectNode dataSourceNode = fieldSchema.putObject("x-data-source");
dataSourceNode.put("type", dataSource.type());
if (!dataSource.url().isEmpty()) {
dataSourceNode.put("url", dataSource.url());
}
dataSourceNode.put("valueField", dataSource.valueField());
dataSourceNode.put("labelField", dataSource.labelField());
// 处理依赖字段
if (dataSource.dependsOn().length > 0) {
ArrayNode dependsOn = dataSourceNode.putArray("dependsOn");
for (String dep : dataSource.dependsOn()) {
dependsOn.add(dep);
}
}
// 处理参数
if (dataSource.params().length > 0) {
ObjectNode params = dataSourceNode.putObject("params");
for (SchemaPropertyDataSourceParam param : dataSource.params()) {
params.put(param.name(), param.value());
}
}
}
// 处理format
if (!annotation.format().isEmpty()) {
fieldSchema.put("format", annotation.format());
}
// 处理默认值
if (!annotation.defaultValue().isEmpty()) {
setDefaultValue(fieldSchema, fieldType, annotation.defaultValue());
}
// 处理数值范围
if (isNumberType(fieldType)) {
if (annotation.minimum() != Integer.MIN_VALUE) {
fieldSchema.put("minimum", annotation.minimum());
}
if (annotation.maximum() != Integer.MAX_VALUE) {
fieldSchema.put("maximum", annotation.maximum());
}
}
// 处理枚举值
if (annotation.enumValues().length > 0) {
ArrayNode enumValues = fieldSchema.putArray("enum");
for (String value : annotation.enumValues()) {
enumValues.add(value);
}
// 如果有枚举显示名称
if (annotation.enumNames().length > 0) {
ArrayNode enumNames = fieldSchema.putArray("enumNames");
for (String name : annotation.enumNames()) {
enumNames.add(name);
}
}
}
// 处理数类型
if (jsonType.equals("array")) {
ObjectNode items = fieldSchema.putObject("items");
if (field.getType().isArray()) {
Class<?> componentType = field.getType().getComponentType();
items.put("type", getJsonSchemaType(componentType));
} else {
// 默认为字符串类型
items.put("type", "string");
}
}
// 处理对象类型
if (jsonType.equals("object") && Map.class.isAssignableFrom(fieldType)) {
ObjectNode additionalProperties = fieldSchema.putObject("additionalProperties");
additionalProperties.put("type", "string");
}
// 如果是必填字段添加到required列表
if (annotation.required()) {
required.add(field.getName());
}
properties.set(field.getName(), fieldSchema);
}
/**
* 设置默认值根据字段类型进行类型转换
*/
private static void setDefaultValue(ObjectNode fieldSchema, Class<?> fieldType, String defaultValue) {
if (isNumberType(fieldType)) {
try {
if (fieldType == Integer.class || fieldType == int.class) {
fieldSchema.put("default", Integer.parseInt(defaultValue));
} else if (fieldType == Long.class || fieldType == long.class) {
fieldSchema.put("default", Long.parseLong(defaultValue));
} else if (fieldType == Double.class || fieldType == double.class) {
fieldSchema.put("default", Double.parseDouble(defaultValue));
} else if (fieldType == Float.class || fieldType == float.class) {
fieldSchema.put("default", Float.parseFloat(defaultValue));
}
} catch (NumberFormatException e) {
// 如果转换失败使用字符串形式
fieldSchema.put("default", defaultValue);
}
} else if (fieldType == Boolean.class || fieldType == boolean.class) {
fieldSchema.put("default", Boolean.parseBoolean(defaultValue));
} else {
fieldSchema.put("default", defaultValue);
}
}
/**
* 判断是否为数值类型
*/
private static boolean isNumberType(Class<?> type) {
return type == Integer.class || type == int.class ||
type == Long.class || type == long.class ||
type == Double.class || type == double.class ||
type == Float.class || type == float.class;
}
/**
* 获取JSON Schema类型
*/
private static String getJsonSchemaType(Class<?> type) {
if (type == String.class) {
return "string";
} else if (type == Integer.class || type == int.class || type == Long.class || type == long.class) {
return "integer";
} else if (type == Boolean.class || type == boolean.class) {
return "boolean";
} else if (type == Double.class || type == double.class || type == Float.class || type == float.class) {
return "number";
} else if (type.isArray() || List.class.isAssignableFrom(type)) {
return "array";
} else if (type.isEnum()) {
return "string";
} else {
return "object";
}
}
}

View File

@ -1,32 +0,0 @@
package com.qqchen.deploy.backend.workflow.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* Schema生成器的主类用于测试和生成Schema
*/
public class SchemaGeneratorMain {
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.enable(SerializationFeature.INDENT_OUTPUT);
}
private SchemaGeneratorMain() {
// 私有构造函数防止实例化
}
public static void main(String[] args) {
try {
// 生成所有节点类型的Schema
System.out.println("=== Node Type Schemas ===");
JsonNode schemas = SchemaGenerator.generateAllNodeTypeSchemas();
System.out.println(mapper.writeValueAsString(schemas));
} catch (Exception e) {
System.err.println("生成Schema时发生错误");
e.printStackTrace();
}
}
}

View File

@ -1,143 +0,0 @@
package com.qqchen.deploy.backend.workflow.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class SchemaGeneratorTest {
private final ObjectMapper mapper = new ObjectMapper();
@Test
void testGenerateAllNodeTypeSchemas() throws Exception {
JsonNode schemas = SchemaGenerator.generateAllNodeTypeSchemas();
// 将生成的schema打印出来以便查看
System.out.println("Generated Schemas:");
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schemas));
// 验证基本结构
assertTrue(schemas.isObject());
assertTrue(schemas.has("START"));
assertTrue(schemas.has("SCRIPT"));
assertTrue(schemas.has("GIT_CLONE"));
assertTrue(schemas.has("TASK"));
assertTrue(schemas.has("EVENT"));
assertTrue(schemas.has("END"));
// 验证START节点
JsonNode startNode = schemas.get("START");
assertEquals("START", startNode.get("code").asText());
assertEquals("开始", startNode.get("name").asText());
assertEquals("开始节点", startNode.get("description").asText());
JsonNode startSchema = startNode.get("configSchema");
assertTrue(startSchema.has("properties"));
JsonNode startProps = startSchema.get("properties");
assertTrue(startProps.has("name"));
assertTrue(startProps.has("description"));
assertTrue(startProps.has("triggerType"));
assertTrue(startProps.has("formKey"));
assertTrue(startProps.has("initiator"));
// 验证SCRIPT节点
JsonNode scriptNode = schemas.get("SCRIPT");
assertEquals("SCRIPT", scriptNode.get("code").asText());
assertEquals("脚本执行", scriptNode.get("name").asText());
assertEquals("执行脚本任务", scriptNode.get("description").asText());
JsonNode scriptSchema = scriptNode.get("configSchema");
JsonNode scriptProps = scriptSchema.get("properties");
// 验证基本属性
assertTrue(scriptProps.has("taskType"));
assertTrue(scriptProps.has("taskName"));
assertTrue(scriptProps.has("description"));
// 验证任务相关属性
assertTrue(scriptProps.has("timeoutDuration"));
assertTrue(scriptProps.has("timeoutStrategy"));
assertTrue(scriptProps.has("retryTimes"));
assertTrue(scriptProps.has("retryInterval"));
assertTrue(scriptProps.has("retryStrategy"));
// 验证脚本特有属性
assertTrue(scriptProps.has("script"));
assertTrue(scriptProps.has("language"));
assertTrue(scriptProps.has("interpreter"));
assertTrue(scriptProps.has("workingDirectory"));
// 验证GIT_CLONE节点
JsonNode gitNode = schemas.get("GIT_CLONE");
assertEquals("GIT_CLONE", gitNode.get("code").asText());
assertEquals("Git克隆", gitNode.get("name").asText());
assertEquals("克隆Git仓库", gitNode.get("description").asText());
JsonNode gitSchema = gitNode.get("configSchema");
JsonNode gitProps = gitSchema.get("properties");
assertTrue(gitProps.has("taskType"));
assertTrue(gitProps.has("taskName"));
assertTrue(gitProps.has("description"));
assertTrue(gitProps.has("repositoryUrl"));
assertTrue(gitProps.has("ref"));
assertTrue(gitProps.has("targetDirectory"));
// 验证TASK节点
JsonNode taskNode = schemas.get("TASK");
assertEquals("TASK", taskNode.get("code").asText());
assertEquals("任务", taskNode.get("name").asText());
assertEquals("执行任务", taskNode.get("description").asText());
JsonNode taskSchema = taskNode.get("configSchema");
JsonNode taskProps = taskSchema.get("properties");
assertTrue(taskProps.has("taskType"));
assertTrue(taskProps.has("taskName"));
assertTrue(taskProps.has("description"));
assertTrue(taskProps.has("enabled"));
assertTrue(taskProps.has("priority"));
assertTrue(taskProps.has("timeoutDuration"));
assertTrue(taskProps.has("timeoutStrategy"));
assertTrue(taskProps.has("retryTimes"));
assertTrue(taskProps.has("retryInterval"));
assertTrue(taskProps.has("retryStrategy"));
assertTrue(taskProps.has("assignee"));
assertTrue(taskProps.has("form"));
assertTrue(taskProps.has("inputVariables"));
assertTrue(taskProps.has("outputVariables"));
assertTrue(taskProps.has("taskListeners"));
// 验证EVENT节点
JsonNode eventNode = schemas.get("EVENT");
assertEquals("EVENT", eventNode.get("code").asText());
assertEquals("事件", eventNode.get("name").asText());
assertEquals("事件节点", eventNode.get("description").asText());
JsonNode eventSchema = eventNode.get("configSchema");
JsonNode eventProps = eventSchema.get("properties");
assertTrue(eventProps.has("name"));
assertTrue(eventProps.has("description"));
assertTrue(eventProps.has("eventType"));
// 验证required字段
JsonNode eventRequired = eventSchema.get("required");
assertTrue(eventRequired.isArray());
assertTrue(eventRequired.toString().contains("name"));
assertTrue(eventRequired.toString().contains("eventType"));
// 验证END节点
JsonNode endNode = schemas.get("END");
assertEquals("END", endNode.get("code").asText());
assertEquals("结束", endNode.get("name").asText());
assertEquals("结束节点", endNode.get("description").asText());
JsonNode endSchema = endNode.get("configSchema");
JsonNode endProps = endSchema.get("properties");
assertTrue(endProps.has("name"));
assertTrue(endProps.has("description"));
assertTrue(endProps.has("status"));
assertTrue(endProps.has("reason"));
// 验证required字段
JsonNode endRequired = endSchema.get("required");
assertTrue(endRequired.isArray());
assertTrue(endRequired.toString().contains("name"));
assertTrue(endRequired.toString().contains("status"));
}
}