diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/BaseNodeDelegate.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/BaseNodeDelegate.java index b0631455..a6571c9f 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/BaseNodeDelegate.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/BaseNodeDelegate.java @@ -18,6 +18,26 @@ import org.springframework.beans.factory.annotation.Autowired; @Slf4j public abstract class BaseNodeDelegate 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 private ObjectMapper objectMapper; @@ -47,12 +67,30 @@ public abstract class BaseNodeDelegate implements JavaDelegate { // 执行具体的任务逻辑 executeInternal(execution, panelVars, localVars); + // 如果节点没有设置状态,默认设置成功状态 + if (execution.getVariable(EXECUTION_STATUS) == null) { + setExecutionStatus(execution, STATUS_SUCCESS); + } + } catch (Exception e) { + // 设置失败状态 + setExecutionStatus(execution, STATUS_FAILURE); log.error("Task execution failed", 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变量的类型 * diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/GatewayNodeLocalVariables.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/GatewayNodeLocalVariables.java new file mode 100644 index 00000000..a4283c37 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/GatewayNodeLocalVariables.java @@ -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; + +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/GatewayNodePanelVariables.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/GatewayNodePanelVariables.java new file mode 100644 index 00000000..3152d48f --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/GatewayNodePanelVariables.java @@ -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; +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/uiVariables/Circle.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/uiVariables/Circle.java index a0a9bbed..15ae43c9 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/uiVariables/Circle.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/uiVariables/Circle.java @@ -8,7 +8,8 @@ public class Circle { @SchemaProperty( title = "半径", description = "圆的半径", - required = true + required = true, + defaultValue = "5" ) private Integer r; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeConfig.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeConfig.java index 785aba7b..b7420e1d 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeConfig.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeConfig.java @@ -3,32 +3,12 @@ package com.qqchen.deploy.backend.workflow.dto.definition.workflow; import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Data; -/** - * 边配置 - * @author cascade - * @date 2024-12-11 - */ @Data @JsonInclude(JsonInclude.Include.NON_NULL) // 只序列化非空字段 public class WorkflowDefinitionEdgeConfig { - /** - * 条件 - */ - private String condition; - /** - * 表达式 - */ - private String expression; - - /** - * 条件表达式 - */ - private String conditionExpression; - - /** - * 类型(sequence/message/association) - */ private String type; + private WorkflowDefinitionEdgeConfigCondition condition; + } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeConfigCondition.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeConfigCondition.java new file mode 100644 index 00000000..6134900e --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeConfigCondition.java @@ -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; + +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeSize.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeSize.java deleted file mode 100644 index cdb0dafb..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdgeSize.java +++ /dev/null @@ -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; -} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraph.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraph.java index fbb9ce19..5838e794 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraph.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraph.java @@ -3,21 +3,16 @@ package com.qqchen.deploy.backend.workflow.dto.definition.workflow; import lombok.Data; import java.util.List; -/** - * 工作流图形数据传输对象 - * @author cascade - * @date 2024-12-11 - */ @Data public class WorkflowDefinitionGraph { /** * 节点列表 */ - private List nodes; + private List nodes; /** * 边列表 */ - private List edges; + private List edges; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdge.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraphEdge.java similarity index 81% rename from backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdge.java rename to backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraphEdge.java index e179ce60..59df4396 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionEdge.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraphEdge.java @@ -9,7 +9,7 @@ import java.util.Map; * @date 2024-12-11 */ @Data -public class WorkflowDefinitionEdge { +public class WorkflowDefinitionGraphEdge { /** * 边ID */ @@ -35,8 +35,4 @@ public class WorkflowDefinitionEdge { */ private WorkflowDefinitionEdgeConfig config; - /** - * 边属性 - */ - private Map properties; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionNode.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraphNode.java similarity index 93% rename from backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionNode.java rename to backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraphNode.java index 7ed9ccd3..fc55c1ed 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionNode.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionGraphNode.java @@ -5,8 +5,6 @@ import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import java.util.Map; - /** * 工作流节点数据传输对象 * @@ -14,7 +12,7 @@ import java.util.Map; * @date 2024-12-11 */ @Data -public class WorkflowDefinitionNode { +public class WorkflowDefinitionGraphNode { /** * 节点ID diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionPosition.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionPosition.java deleted file mode 100644 index 8661aa05..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowDefinitionPosition.java +++ /dev/null @@ -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; -} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowNodeGraph.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowNodeGraph.java deleted file mode 100644 index b782f915..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/workflow/WorkflowNodeGraph.java +++ /dev/null @@ -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 types) { - this.ports = new Ports(); - Map 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 groups; - - public Ports() { - // 默认构造函数 - this.groups = new HashMap<>(); - } - - /** - * 获取端口类型列表 - * - * @return 端口类型列表("in"/"out") - */ - public List 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() { - // 默认构造函数 - } - } -} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowNodeDefinition.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowNodeDefinition.java index 3bc3b99e..a436f669 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowNodeDefinition.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowNodeDefinition.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.qqchen.deploy.backend.framework.annotation.LogicDelete; 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.workflow.WorkflowNodeGraph; import com.qqchen.deploy.backend.workflow.enums.NodeCategoryEnums; import com.vladmihalcea.hibernate.type.json.JsonType; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java index 8d7aeabe..ceabf342 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java @@ -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.panelVariables.DeployNodePanelVariables; 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.StartNodePanelVariables; 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 java.util.Arrays; - /** - * 工作流节点类型枚举 + * 工作��节点类型枚举 * 定义了工作流中所有可用的节点类型及其UI配置 */ @Getter @@ -38,12 +36,7 @@ public enum NodeTypeEnums { NodeUiVariables.class, BpmnNodeTypeEnums.START_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, BpmnNodeTypeEnums.END_EVENT, NodeCategoryEnums.EVENT, - "工作流结束", - new WorkflowNodeGraph() - .setShape("circle") - .setSize(40, 40) - .setStyle("#fff1f0", "#ff4d4f", "stop") - .configPorts(Arrays.asList("in")) + "工作流结束" ), SCRIPT_NODE( "SCRIPT_NODE", @@ -78,12 +66,7 @@ public enum NodeTypeEnums { NodeUiVariables.class, BpmnNodeTypeEnums.SERVICE_TASK, NodeCategoryEnums.TASK, - "脚本执行任务", - new WorkflowNodeGraph() - .setShape("rect") - .setSize(120, 60) - .setStyle("#ffffff", "#1890ff", "code") - .configPorts(Arrays.asList("in", "out")) + "脚本执行任务" ), DEPLOY_NODE( "DEPLOY_NODE", @@ -94,8 +77,18 @@ public enum NodeTypeEnums { NodeUiVariables.class, BpmnNodeTypeEnums.SERVICE_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 WorkflowNodeGraph uiConfig; // UI配置 - NodeTypeEnums( String code, String name, @@ -261,8 +252,8 @@ public enum NodeTypeEnums { Class uiVariables, BpmnNodeTypeEnums bpmnType, NodeCategoryEnums category, - String description, - WorkflowNodeGraph uiConfig) { + String description + ) { this.code = code; this.name = name; this.localVariables = localVariables; @@ -272,7 +263,6 @@ public enum NodeTypeEnums { this.bpmnType = bpmnType; this.category = category; this.description = description; - this.uiConfig = uiConfig; } /** diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/BpmnConverter.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/BpmnConverter.java index 2407aa12..a7b27587 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/BpmnConverter.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/BpmnConverter.java @@ -1,9 +1,9 @@ package com.qqchen.deploy.backend.workflow.util; 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.WorkflowDefinitionNode; +import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraphNode; import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.BpmnAutoLayout; @@ -101,12 +101,12 @@ public class BpmnConverter { * @param process 流程定义对象 * @return 节点ID映射关系 */ - private Map convertNodes(List nodes, Process process) { + private Map convertNodes(List nodes, Process process) { // 步骤2.1:创建节点ID映射 Map idMapping = new HashMap<>(); // 步骤2.2:遍历并转换每个节点 - for (WorkflowDefinitionNode node : nodes) { + for (WorkflowDefinitionGraphNode node : nodes) { log.debug("转换节点: {}, 类型: {}", node.getNodeName(), node.getNodeCode()); convertSingleNode(node, process, idMapping); } @@ -121,7 +121,7 @@ public class BpmnConverter { * @param process 流程定义对象 * @param idMapping 节点ID映射关系 */ - private void convertSingleNode(WorkflowDefinitionNode node, Process process, Map idMapping) { + private void convertSingleNode(WorkflowDefinitionGraphNode node, Process process, Map idMapping) { try { // 步骤2.3:获取节点对应的BPMN元素类型 @SuppressWarnings("unchecked") @@ -158,7 +158,7 @@ public class BpmnConverter { * @param node 工作流节点定义 * @param process 当前流程 */ - private void configureFlowElement(FlowElement element, WorkflowDefinitionNode node, Process process) { + private void configureFlowElement(FlowElement element, WorkflowDefinitionGraphNode node, Process process) { // 步骤1:创建基本的扩展元素 Map> extensionElements = createBaseExtensionElements(); @@ -200,7 +200,7 @@ public class BpmnConverter { * @param process 当前流程 * @param extensionElements 扩展元素 */ - private void configureServiceTask(ServiceTask serviceTask, WorkflowDefinitionNode node, Process process, Map> extensionElements) { + private void configureServiceTask(ServiceTask serviceTask, WorkflowDefinitionGraphNode node, Process process, Map> extensionElements) { if (node.getLocalVariables() != null && node.getLocalVariables().has("delegate")) { String delegate = node.getLocalVariables().get("delegate").asText(); serviceTask.setImplementationType("delegateExpression"); @@ -233,7 +233,7 @@ public class BpmnConverter { * @param extensionElements 扩展元素 * @param node 工作流节点定义 */ - private void addExecutionVariables(Map> extensionElements, WorkflowDefinitionNode node) { + private void addExecutionVariables(Map> extensionElements, WorkflowDefinitionGraphNode node) { // 添加panelVariables变量 if (node.getPanelVariables() != null) { ExtensionElement fieldElement = new ExtensionElement(); @@ -422,8 +422,8 @@ public class BpmnConverter { * @param idMapping 节点ID映射 * @param process 流程定义 */ - private void convertEdgesToSequenceFlows(List edges, Map idMapping, Process process) { - for (WorkflowDefinitionEdge edge : edges) { + private void convertEdgesToSequenceFlows(List edges, Map idMapping, Process process) { + for (WorkflowDefinitionGraphEdge edge : edges) { log.debug("转换连线: from {} to {}", edge.getFrom(), edge.getTo()); SequenceFlow flow = new SequenceFlow(); diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/BpmnConverter1.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/BpmnConverter1.java index 0164a533..6a5e8956 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/BpmnConverter1.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/BpmnConverter1.java @@ -1,9 +1,9 @@ package com.qqchen.deploy.backend.workflow.util; 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.WorkflowDefinitionNode; +import com.qqchen.deploy.backend.workflow.dto.definition.workflow.WorkflowDefinitionGraphNode; import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.BpmnAutoLayout; @@ -68,7 +68,7 @@ public class BpmnConverter1 { Map idMapping = new HashMap<>(); // 转换节点 - for (WorkflowDefinitionNode node : graph.getNodes()) { + for (WorkflowDefinitionGraphNode node : graph.getNodes()) { log.debug("转换节点: {}, 类型: {}", node.getNodeName(), node.getNodeCode()); // 通过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()); SequenceFlow flow = new SequenceFlow(); @@ -130,7 +130,7 @@ public class BpmnConverter1 { * @param node 工作流节点定义 * @param process 当前流程 */ - private void configureFlowElement(FlowElement element, WorkflowDefinitionNode node, Process process) { + private void configureFlowElement(FlowElement element, WorkflowDefinitionGraphNode node, Process process) { // 为所有节点添加执行监听器 Map> extensionElements = new HashMap<>(); List executionListeners = new ArrayList<>(); diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/SchemaGenerator.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/SchemaGenerator.java deleted file mode 100644 index 7d5b20f9..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/SchemaGenerator.java +++ /dev/null @@ -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(); - - // 设置基本信�� - 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 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; - } - - /** - * 处理基础字���(code、name、description) - */ - private static void processBaseFields(ObjectNode properties, List 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 required, Class configClass) { - Set 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 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"; - } - } -} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/SchemaGeneratorMain.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/SchemaGeneratorMain.java deleted file mode 100644 index b3701193..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/util/SchemaGeneratorMain.java +++ /dev/null @@ -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(); - } - } -} diff --git a/backend/src/test/java/com/qqchen/deploy/backend/workflow/util/SchemaGeneratorTest.java b/backend/src/test/java/com/qqchen/deploy/backend/workflow/util/SchemaGeneratorTest.java deleted file mode 100644 index 3199ba0a..00000000 --- a/backend/src/test/java/com/qqchen/deploy/backend/workflow/util/SchemaGeneratorTest.java +++ /dev/null @@ -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")); - } -}