This commit is contained in:
asp_ly 2024-12-14 21:58:26 +08:00
parent 296b5d3b9e
commit f0c964ea43
11 changed files with 528 additions and 871 deletions

View File

@ -34,67 +34,54 @@ public class ShellTaskDelegate implements JavaDelegate {
private ManagementService managementService;
private Expression script;
private Expression workDir;
private Expression env;
// 用于存储实时输出的Map
private static final Map<String, StringBuilder> outputMap = new ConcurrentHashMap<>();
private static final Map<String, StringBuilder> errorMap = new ConcurrentHashMap<>();
private void processInputStream(InputStream inputStream, String processInstanceId, NodeLogTypeEnums logType) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
// 发布日志事件
eventPublisher.publishEvent(new ShellLogEvent(processInstanceId, line, logType));
// 同时保存到StringBuilder中
if (logType == NodeLogTypeEnums.STDOUT) {
StringBuilder output = outputMap.get(processInstanceId);
synchronized (output) {
output.append(line).append("\n");
}
log.info("Shell output: {}", line);
} else {
StringBuilder error = errorMap.get(processInstanceId);
synchronized (error) {
error.append(line).append("\n");
}
log.error("Shell error: {}", line);
}
}
} catch (IOException e) {
log.error("Error reading process output", e);
}
}
@Override
public void execute(DelegateExecution execution) {
log.info("ShellTaskDelegate开始执行, processInstanceId={}, executionId={}",
execution.getProcessInstanceId(), execution.getId());
// 从字段注入中获取值
String scriptValue = script != null ? script.getValue(execution).toString() : null;
String workDirValue = workDir != null ? workDir.getValue(execution).toString() : null;
@SuppressWarnings("unchecked")
Map<String, String> envValue = env != null ? (Map<String, String>) env.getValue(execution) : null;
log.info("字段注入的值: script={}, workDir={}, env={}", scriptValue, workDirValue, envValue);
// 如果流程变量中有值优先使用流程变量
if (execution.hasVariable("script")) {
scriptValue = (String) execution.getVariable("script");
log.info("从流程变量获取到script={}", scriptValue);
}
if (execution.hasVariable("workDir")) {
workDirValue = (String) execution.getVariable("workDir");
log.info("从流程变量获取到workDir={}", workDirValue);
}
if (execution.hasVariable("env")) {
@SuppressWarnings("unchecked")
Map<String, String> envFromVar = (Map<String, String>) execution.getVariable("env");
envValue = envFromVar;
log.info("从流程变量获取到env={}", envValue);
}
if (scriptValue == null) {
log.error("脚本内容为空,执行失败");
handleFailure(execution, "Script is required but not provided");
return;
}
try {
log.info("准备执行脚本: {}", scriptValue);
// 使用processInstanceId而不是executionId
String processInstanceId = execution.getProcessInstanceId();
outputMap.put(processInstanceId, new StringBuilder());
@ -137,7 +124,7 @@ public class ShellTaskDelegate implements JavaDelegate {
}
// 执行命令
log.info("Executing shell script: {}", scriptValue);
log.info("执行shell脚本: {}", scriptValue);
Process process = processBuilder.start();
// 使用BufferedReader实时读取输出
@ -178,28 +165,56 @@ public class ShellTaskDelegate implements JavaDelegate {
errorMap.remove(processInstanceId);
if (exitCode != 0) {
log.error("Shell script execution failed with exit code: {}", exitCode);
log.error("Error output: {}", finalError);
handleFailure(execution, "Shell script execution failed with exit code: " + exitCode);
log.error("Shell脚本执行失败,退出码: {}", exitCode);
log.error("错误输出: {}", finalError);
handleFailure(execution, "Shell脚本执行失败,退出码: " + exitCode);
return;
}
log.info("Shell script executed successfully");
log.debug("Script output: {}", finalOutput);
log.info("Shell脚本执行成功");
log.debug("脚本输出: {}", finalOutput);
} catch (Exception e) {
log.error("Shell script execution failed", e);
handleFailure(execution, "Shell script execution failed: " + e.getMessage());
log.error("Shell脚本执行失败", e);
handleFailure(execution, "Shell脚本执行失败: " + e.getMessage());
}
}
private void processInputStream(InputStream inputStream, String processInstanceId, NodeLogTypeEnums logType) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
// 发布日志事件
eventPublisher.publishEvent(new ShellLogEvent(processInstanceId, line, logType));
// 同时保存到StringBuilder中
if (logType == NodeLogTypeEnums.STDOUT) {
StringBuilder output = outputMap.get(processInstanceId);
synchronized (output) {
output.append(line).append("\n");
}
log.info("Shell output: {}", line);
} else {
StringBuilder error = errorMap.get(processInstanceId);
synchronized (error) {
error.append(line).append("\n");
}
log.error("Shell error: {}", line);
}
}
} catch (IOException e) {
log.error("Error reading process output", e);
}
}
private void handleFailure(DelegateExecution execution, String errorMessage) {
String processInstanceId = execution.getProcessInstanceId();
try {
// 直接终止流程实例
runtimeService.deleteProcessInstance(processInstanceId, errorMessage);
} catch (Exception e) {
log.error("Error while handling shell task failure for process instance: {}", processInstanceId, e);
log.error("处理Shell任务失败时出现错误流程实例: {}", processInstanceId, e);
}
}
}

View File

@ -1,33 +1,79 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.Getter;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.ExclusiveGateway;
import org.flowable.bpmn.model.ScriptTask;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
/**
* BPMN节点类型枚举
*/
@Getter
public enum BpmnNodeTypeEnums {
START("start", "startEvent"),
END("end", "endEvent"),
USER_TASK("userTask", "userTask"),
SERVICE_TASK("serviceTask", "serviceTask"),
SHELL_TASK("shellTask", "serviceTask"),
EXCLUSIVE_GATEWAY("exclusiveGateway", "exclusiveGateway");
private final String shape;
START_EVENT("startEvent", StartEvent.class),
END_EVENT("endEvent", EndEvent.class),
USER_TASK("userTask", UserTask.class),
SERVICE_TASK("serviceTask", ServiceTask.class),
SCRIPT_TASK("scriptTask", ScriptTask.class),
EXCLUSIVE_GATEWAY("exclusiveGateway", ExclusiveGateway.class);
private final String bpmnType;
BpmnNodeTypeEnums(String shape, String bpmnType) {
this.shape = shape;
private final Object instance;
BpmnNodeTypeEnums(String bpmnType, Object instance) {
this.bpmnType = bpmnType;
this.instance = instance;
}
public static BpmnNodeTypeEnums fromShape(String shape) {
for (BpmnNodeTypeEnums type : values()) {
if (type.getShape().equals(shape)) {
return type;
}
}
return null;
}
/**
* Flowable 主要的节点类型包括
*
* 事件类型
* StartEvent (开始事件)
* EndEvent (结束事件)
* TimerStartEvent (定时开始事件)
* ErrorStartEvent (错误开始事件)
* MessageStartEvent (消息开始事件)
* SignalStartEvent (信号开始事件)
* IntermediateCatchEvent (中间捕获事件)
* IntermediateThrowEvent (中间抛出事件)
* 任务类型
* UserTask (用户任务)
* ServiceTask (服务任务)
* ScriptTask (脚本任务)
* MailTask (邮件任务)
* ManualTask (手动任务)
* ReceiveTask (接收任务)
* BusinessRuleTask (业务规则任务)
* CallActivity (调用活动)
* 网关类型
* ExclusiveGateway (排他网关)
* ParallelGateway (并行网关)
* InclusiveGateway (包容网关)
* EventBasedGateway (事件网关)
* ComplexGateway (复杂网关)
* 子流程
* SubProcess (子流程)
* EventSubProcess (事件子流程)
* Transaction (事务子流程)
* 边界事件
* BoundaryEvent (边界事件)
* ErrorBoundaryEvent (错误边界事件)
* TimerBoundaryEvent (定时边界事件)
* SignalBoundaryEvent (信号边界事件)
* 连线
* SequenceFlow (顺序流)
* MessageFlow (消息流)
*/
}

View File

@ -23,6 +23,7 @@ public enum NodeTypeEnums {
START_EVENT(
"START_EVENT", // 节点类型编码
"开始节点", // 节点显示名称
BpmnNodeTypeEnums.START_EVENT,
NodeCategoryEnums.EVENT,
"工作流的起点", // 节点简要描述
new WorkflowNodeGraph() // UI配置
@ -42,6 +43,7 @@ public enum NodeTypeEnums {
END_EVENT(
"END_EVENT",
"结束节点",
BpmnNodeTypeEnums.END_EVENT,
NodeCategoryEnums.EVENT,
"工作流的终点",
new WorkflowNodeGraph()
@ -53,6 +55,7 @@ public enum NodeTypeEnums {
SCRIPT_TASK(
"SCRIPT_TASK",
"脚本任务",
BpmnNodeTypeEnums.SERVICE_TASK,
NodeCategoryEnums.TASK,
"脚本执行任务",
new WorkflowNodeGraph()
@ -200,6 +203,8 @@ public enum NodeTypeEnums {
private final String name; // 节点显示名称
private final BpmnNodeTypeEnums bpmnType; // 对应的BPMN类型
private final NodeCategoryEnums category; //分类
private final String description; // 节点详细描述
@ -209,11 +214,13 @@ public enum NodeTypeEnums {
NodeTypeEnums(
String code,
String name,
BpmnNodeTypeEnums bpmnType,
NodeCategoryEnums category,
String description,
WorkflowNodeGraph uiConfig) {
this.code = code;
this.name = name;
this.bpmnType = bpmnType;
this.category = category;
this.description = description;
this.uiConfig = uiConfig;

View File

@ -10,7 +10,11 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public class ShellLogEvent {
private String processInstanceId;
private String logMessage;
private NodeLogTypeEnums logType;
}

View File

@ -1,44 +0,0 @@
package com.qqchen.deploy.backend.workflow.handler;
import com.fasterxml.jackson.databind.JsonNode;
import com.qqchen.deploy.backend.workflow.enums.BpmnNodeTypeEnums;
import com.qqchen.deploy.backend.workflow.model.BpmnNodeConfig;
import org.flowable.bpmn.model.FlowElement;
/**
* BPMN节点处理器接口
*
* @param <T> 流程元素类型
*/
public interface BpmnNodeHandler<T extends FlowElement> {
/**
* 处理节点数据
*
* @param nodeData JSON节点数据
* @param element 流程元素
* @param config 节点配置
*/
void handle(JsonNode nodeData, T element, BpmnNodeConfig config);
/**
* 创建流程元素
*
* @param config 节点配置
* @return 流程元素
*/
T createElement(BpmnNodeConfig config);
/**
* 获取处理器类型
*
* @return 节点类型
*/
BpmnNodeTypeEnums getType();
/**
* 获取处理的元素类型
*
* @return 元素类型Class
*/
Class<T> getElementType();
}

View File

@ -1,132 +0,0 @@
package com.qqchen.deploy.backend.workflow.handler.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.qqchen.deploy.backend.workflow.annotation.BpmnNode;
import com.qqchen.deploy.backend.workflow.enums.BpmnNodeTypeEnums;
import com.qqchen.deploy.backend.workflow.handler.BpmnNodeHandler;
import com.qqchen.deploy.backend.workflow.model.BpmnNodeConfig;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FieldExtension;
import org.flowable.bpmn.model.ServiceTask;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Optional;
/**
* Shell任务处理器
*/
@Slf4j
@Component
@BpmnNode(
type = "shellTask",
name = "Shell脚本",
async = true,
requiredFields = {"script", "workDir"},
delegateExpression = "${shellTaskDelegate}"
)
public class ShellTaskHandler implements BpmnNodeHandler<ServiceTask> {
@Override
public void handle(JsonNode nodeData, ServiceTask element, BpmnNodeConfig config) {
// 设置任务委托表达式
setupTaskDelegate(element);
// 设置任务属性
setupTaskProperties(element, config);
// 验证必需字段
validateRequiredFields(element, config);
// 处理扩展字段
handleExtensionFields(element, config);
}
@Override
public ServiceTask createElement(BpmnNodeConfig config) {
ServiceTask task = new ServiceTask();
task.setId(config.getId());
task.setName(config.getName());
return task;
}
@Override
public BpmnNodeTypeEnums getType() {
return BpmnNodeTypeEnums.SHELL_TASK;
}
@Override
public Class<ServiceTask> getElementType() {
return ServiceTask.class;
}
/**
* 设置任务委托表达式
*/
private void setupTaskDelegate(ServiceTask element) {
BpmnNode annotation = this.getClass().getAnnotation(BpmnNode.class);
element.setImplementationType("delegateExpression");
element.setImplementation(annotation.delegateExpression());
element.setAsynchronous(annotation.async());
}
/**
* 设置任务属性
*/
private void setupTaskProperties(ServiceTask element, BpmnNodeConfig config) {
Map<String, Object> properties = config.getProperties();
properties.forEach((key, value) -> {
if (value != null) {
addFieldExtension(element, key, value.toString());
}
});
}
/**
* 验证必需字段
*/
private void validateRequiredFields(ServiceTask element, BpmnNodeConfig config) {
BpmnNode annotation = this.getClass().getAnnotation(BpmnNode.class);
for (String requiredField : annotation.requiredFields()) {
boolean fieldExists = element.getFieldExtensions().stream()
.anyMatch(field -> field.getFieldName().equals(requiredField));
if (!fieldExists) {
log.warn("Required field '{}' is missing for shell task '{}'",
requiredField, element.getId());
}
}
}
/**
* 处理扩展字段
*/
private void handleExtensionFields(ServiceTask element, BpmnNodeConfig config) {
Map<String, String> extensions = config.getExtensions();
extensions.forEach((key, value) -> {
if (value != null) {
addFieldExtension(element, key, value);
}
});
}
/**
* 添加字段扩展
*/
private void addFieldExtension(ServiceTask serviceTask, String name, String value) {
// 检查字段是否已存在
Optional<FieldExtension> existingField = serviceTask.getFieldExtensions().stream()
.filter(field -> field.getFieldName().equals(name))
.findFirst();
if (existingField.isPresent()) {
// 更新现有字段
existingField.get().setStringValue(value);
} else {
// 添加新字段
FieldExtension field = new FieldExtension();
field.setFieldName(name);
field.setStringValue(value);
serviceTask.getFieldExtensions().add(field);
}
}
}

View File

@ -1,76 +0,0 @@
package com.qqchen.deploy.backend.workflow.parser;
import com.fasterxml.jackson.databind.JsonNode;
import com.qqchen.deploy.backend.workflow.enums.BpmnNodeTypeEnums;
import com.qqchen.deploy.backend.workflow.model.BpmnNodeConfig;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* BPMN节点配置解析器
*/
@Component
public class BpmnNodeConfigParser {
/**
* 解析节点配置
*
* @param node JSON节点数据
* @return 节点配置
*/
public BpmnNodeConfig parse(JsonNode node) {
BpmnNodeConfig config = new BpmnNodeConfig();
config.setId(node.path("id").asText());
config.setName(node.path("data").path("label").asText());
config.setType(BpmnNodeTypeEnums.fromShape(node.path("shape").asText()));
// 解析属性
JsonNode data = node.path("data");
// 处理通用属性
Map<String, Object> properties = new HashMap<>();
if (data.has("properties")) {
data.path("properties").fields()
.forEachRemaining(entry -> properties.put(
entry.getKey(),
parseValue(entry.getValue())
));
}
// 处理特定任务属性
if (data.has("serviceTask") && data.path("serviceTask").has("fields")) {
JsonNode fields = data.path("serviceTask").path("fields");
fields.fields().forEachRemaining(entry ->
properties.put(entry.getKey(), parseValue(entry.getValue()))
);
}
config.setProperties(properties);
// 解析扩展字段
Map<String, String> extensions = new HashMap<>();
if (data.has("extensions")) {
data.path("extensions").fields()
.forEachRemaining(entry -> extensions.put(
entry.getKey(),
entry.getValue().asText()
));
}
config.setExtensions(extensions);
return config;
}
/**
* 解析JSON值
*/
private Object parseValue(JsonNode value) {
if (value.isTextual()) return value.asText();
if (value.isNumber()) return value.numberValue();
if (value.isBoolean()) return value.asBoolean();
if (value.isObject() || value.isArray()) return value.toString();
return null;
}
}

View File

@ -24,7 +24,7 @@ public interface IWorkflowDefinitionService extends IBaseService<WorkflowDefinit
* @param dto 工作流定义DTO
* @return 工作流定义DTO
*/
WorkflowDefinitionDTO deployWorkflow(WorkflowDefinitionDTO dto);
void deployWorkflow(WorkflowDefinition workflowDefinition);
/**
* 启动工作流实例

View File

@ -4,6 +4,7 @@ import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.dto.WorkflowExecutionDTO;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceCreateDTO;
import com.qqchen.deploy.backend.workflow.dto.graph.WorkflowDefinitionGraph;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnums;
@ -13,6 +14,7 @@ import com.qqchen.deploy.backend.workflow.util.BpmnConverter;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
@ -69,64 +71,23 @@ public class WorkflowDefinitionServiceImpl extends BaseServiceImpl<WorkflowDefin
@Override
@Transactional(rollbackFor = Exception.class)
public WorkflowDefinitionDTO saveWorkflowDesign(WorkflowDefinitionDTO dto) throws Exception {
// 转换图形JSON为BPMN XML
String bpmnXml = bpmnConverter.convertToXml(null, dto.getKey());
dto.setFlowVersion(1);
dto.setBpmnXml(bpmnXml);
// // 创建工作流定义
// WorkflowDefinition definition = new WorkflowDefinition();
// definition.setName(dto.getName());
// definition.setKey(dto.getKey());
// definition.setDescription(dto.getDescription());
// definition.setGraphJson(dto.getGraphJson());
// definition.setBpmnXml(bpmnXml);
// definition.setFlowVersion(1); // 设置初始版本为1
//
// // 保存工作流定义
WorkflowDefinition definition = super.converter.toEntity(dto);
workflowDefinitionRepository.save(definition);
//
// // 部署工作流
// WorkflowDefinitionDTO deployDto = new WorkflowDefinitionDTO();
// deployDto.setId(definition.getId());
// deployDto.setName(definition.getName());
// deployDto.setKey(definition.getKey());
// deployDto.setBpmnXml(definition.getBpmnXml());
// deployDto.setDescription(definition.getDescription());
//// workflowDefinitionService.deployWorkflow(deployDto);
//
// // 手动转换为 WorkflowDesignDTO
// WorkflowDesignDTO result = new WorkflowDesignDTO();
// result.setId(definition.getId());
// result.setName(definition.getName());
// result.setKey(definition.getKey());
// result.setDescription(definition.getDescription());
// result.setGraphJson(definition.getGraphJson());
return super.converter.toDto(definition);
}
@Override
@Transactional(rollbackFor = Exception.class)
public WorkflowDefinitionDTO deployWorkflow(WorkflowDefinitionDTO dto) {
public void deployWorkflow(WorkflowDefinition workflowDefinition) {
try {
// 部署流程
Deployment deployment = repositoryService.createDeployment()
.addString(dto.getName() + ".bpmn20.xml", dto.getBpmnXml())
.name(dto.getName())
repositoryService.createDeployment()
.addString(workflowDefinition.getKey() + ".bpmn20.xml", workflowDefinition.getBpmnXml())
.name(workflowDefinition.getName())
.deploy();
log.info("Successfully deployed workflow: {}", dto.getName());
// 查找已存在的工作流定义
WorkflowDefinition definition = workflowDefinitionRepository.findById(dto.getId())
.orElseThrow(() -> new RuntimeException("Workflow definition not found: " + dto.getId()));
// 转换为DTO
dto.setFlowVersion(definition.getFlowVersion());
return dto;
log.info("Successfully deployed workflow: {}", workflowDefinition.getName());
} catch (Exception e) {
log.error("Failed to deploy workflow: {}", dto.getName(), e);
log.error("Failed to deploy workflow: {}", workflowDefinition.getName(), e);
throw new RuntimeException("Failed to deploy workflow", e);
}
}
@ -299,6 +260,20 @@ public class WorkflowDefinitionServiceImpl extends BaseServiceImpl<WorkflowDefin
stages.add(stage);
}
// 按开始时间排序
stages.sort((a, b) -> {
if (a.getStartTime() == null && b.getStartTime() == null) {
return 0;
}
if (a.getStartTime() == null) {
return 1;
}
if (b.getStartTime() == null) {
return -1;
}
return a.getStartTime().compareTo(b.getStartTime());
});
executionDTO.setStages(stages);
return executionDTO;
} catch (Exception e) {
@ -368,7 +343,11 @@ public class WorkflowDefinitionServiceImpl extends BaseServiceImpl<WorkflowDefin
public void publishedWorkflowDesign(Long workflowDefinitionId) {
WorkflowDefinition definition = workflowDefinitionRepository.findById(workflowDefinitionId)
.orElseThrow(() -> new RuntimeException("Workflow definition not found: " + workflowDefinitionId));
WorkflowDefinitionGraph graph = definition.getGraph();
definition.setBpmnXml(bpmnConverter.convertToXml(graph, definition.getKey()));
definition.setStatus(WorkflowStatusEnums.PUBLISHED);
this.deployWorkflow(definition);
definition.setFlowVersion(1);
workflowDefinitionRepository.save(definition);
log.info("Successfully published workflow definition: {}", workflowDefinitionId);
}

View File

@ -1,302 +1,163 @@
package com.qqchen.deploy.backend.workflow.util;
import com.qqchen.deploy.backend.workflow.dto.graph.*;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* BPMN 模型转换工具
* @author cascade
* @date 2024-12-11
*/
@Slf4j
@Component
public class BpmnConverter {
private final BpmnXMLConverter bpmnXmlConverter = new BpmnXMLConverter();
/**
* 将工作流图转换为BPMN XML
* @param graph 工作流图数据
* @param processKey 流程标识
* @return BPMN XML字符串
* 将工作流定义图转换为Flowable XML
*
* @param graph 工作流定义图
* @param processKey 流程定义的唯一标识
* @return Flowable XML字符串
* @throws RuntimeException 当转换失败时抛出
*/
public String convertToXml(WorkflowDefinitionGraph graph, String processKey) {
BpmnModel bpmnModel = convertToBpmnModel(graph, processKey);
byte[] xmlBytes = bpmnXmlConverter.convertToXML(bpmnModel);
return new String(xmlBytes, StandardCharsets.UTF_8);
}
try {
log.debug("开始转换工作流定义为XML, processKey: {}", processKey);
/**
* 将工作流图转换为BPMN模型
* @param graph 工作流图数据
* @param processKey 流程标识
* @return BPMN模型
*/
public BpmnModel convertToBpmnModel(WorkflowDefinitionGraph graph, String processKey) {
// 创建BPMN模型
BpmnModel bpmnModel = new BpmnModel();
Process process = new Process();
// 确保processKey符合NCName规则
String validProcessKey = processKey.replaceAll("[^a-zA-Z0-9-_.]", "_");
process.setId(validProcessKey);
process.setName(processKey); // 保持原始processKey作为显示名称
process.setExecutable(true);
bpmnModel.addProcess(process);
// 设置流程属性
process.setId(processKey);
// if (graph.getProperties() != null) {
// Map<String, Object> properties = graph.getProperties();
// process.setName(properties.get("name") != null ? properties.get("name").toString() : null);
// process.setDocumentation(properties.get("description") != null ? properties.get("description").toString() : null);
// }
// 创建节点ID到规范化ID的映射
Map<String, String> idMapping = new HashMap<>();
// 转换节点
// for (WorkflowDefinitionNode node : graph.getNodes()) {
// FlowElement element = convertNode(node);
// if (element != null) {
// process.addFlowElement(element);
// }
// }
//
// // 转换边
// for (WorkflowDefinitionEdge edge : graph.getEdges()) {
// SequenceFlow flow = convertEdge(edge);
// if (flow != null) {
// process.addFlowElement(flow);
// }
// }
for (WorkflowDefinitionNode node : graph.getNodes()) {
log.debug("转换节点: {}, 类型: {}", node.getName(), node.getCode());
bpmnModel.addProcess(process);
return bpmnModel;
// 通过NodeTypeEnums获取对应的BpmnTypeEnums中定义的实例类型
@SuppressWarnings("unchecked")
Class<? extends FlowElement> instanceClass = (Class<? extends FlowElement>) NodeTypeEnums.valueOf(node.getCode())
.getBpmnType()
.getInstance();
// 创建节点实例
FlowElement element = instanceClass.getDeclaredConstructor().newInstance();
String validId = sanitizeId(node.getId());
idMapping.put(node.getId(), validId);
element.setId(validId);
element.setName(node.getName());
// 设置节点特定属性
configureFlowElement(element, node);
process.addFlowElement(element);
}
/**
* 将工作流图转换为BPMN模型
* @param graph 工作流图数据
* @return BPMN模型
*/
public BpmnModel convertToBpmnModel(WorkflowDefinitionGraph graph) {
String processKey = null;
// if (graph.getProperties() != null) {
// Object key = graph.getProperties().get("key");
// processKey = key != null ? key.toString() : null;
// }
// if (processKey == null) {
// processKey = "process_" + System.currentTimeMillis();
// }
return convertToBpmnModel(graph, processKey);
}
// 转换连线
for (WorkflowDefinitionEdge edge : graph.getEdges()) {
log.debug("转换连线: from {} to {}", edge.getFrom(), edge.getTo());
/**
* 转换节点
* @param node 工作流节点
* @return 流程元素
*/
private FlowElement convertNode(WorkflowDefinitionNode node) {
if (node.getType() == null) {
return null;
}
switch (node.getType()) {
case START_EVENT:
return createStartEvent(node);
case END_EVENT:
return createEndEvent(node);
// case SERVICE_TASK:
// return createServiceTask(node);
// case USER_TASK:
// return createUserTask(node);
// case SCRIPT_TASK:
// return createScriptTask(node);
// case EXCLUSIVE_GATEWAY:
// return createExclusiveGateway(node);
// case PARALLEL_GATEWAY:
// return createParallelGateway(node);
// case SUBPROCESS:
// return createSubProcess(node);
// case CALL_ACTIVITY:
// return createCallActivity(node);
default:
return null;
}
}
/**
* 创建开始事件
* @param node 节点数据
* @return 开始事件
*/
private StartEvent createStartEvent(WorkflowDefinitionNode node) {
StartEvent startEvent = new StartEvent();
startEvent.setId(node.getId());
startEvent.setName(node.getName());
return startEvent;
}
/**
* 创建结束事件
* @param node 节点数据
* @return 结束事件
*/
private EndEvent createEndEvent(WorkflowDefinitionNode node) {
EndEvent endEvent = new EndEvent();
endEvent.setId(node.getId());
endEvent.setName(node.getName());
return endEvent;
}
/**
* 创建服务任务
* @param node 节点数据
* @return 服务任务
*/
private ServiceTask createServiceTask(WorkflowDefinitionNode node) {
ServiceTask serviceTask = new ServiceTask();
serviceTask.setId(node.getId());
serviceTask.setName(node.getName());
if (node.getConfig() != null) {
// WorkflowDefinitionEdgeNodeConfig config = node.getConfig();
// serviceTask.setImplementation(config.getImplementation());
// if (config.getFields() != null) {
// config.getFields().forEach((key, value) -> {
// FieldExtension field = new FieldExtension();
// field.setFieldName(key);
// field.setStringValue(value != null ? value.toString() : null);
// serviceTask.getFieldExtensions().add(field);
// });
// }
}
return serviceTask;
}
/**
* 创建用户任务
* @param node 节点数据
* @return 用户任务
*/
private UserTask createUserTask(WorkflowDefinitionNode node) {
UserTask userTask = new UserTask();
userTask.setId(node.getId());
userTask.setName(node.getName());
if (node.getConfig() != null) {
// NodeConfig config = node.getConfig();
// userTask.setAssignee(config.getAssignee());
// userTask.setCandidateUsers(config.getCandidateUsers());
// userTask.setCandidateGroups(config.getCandidateGroups());
// userTask.setDueDate(config.getDueDate());
// userTask.setPriority(config.getPriority() != null ? config.getPriority().toString() : null);
// userTask.setFormKey(config.getFormKey());
// userTask.setSkipExpression(config.getSkipExpression());
}
return userTask;
}
/**
* 创建脚本任务
* @param node 节点数据
* @return 脚本任务
*/
private ScriptTask createScriptTask(WorkflowDefinitionNode node) {
ScriptTask scriptTask = new ScriptTask();
scriptTask.setId(node.getId());
scriptTask.setName(node.getName());
if (node.getConfig() != null) {
// NodeConfig config = node.getConfig();
// scriptTask.setScriptFormat(config.getImplementation());
// if (config.getFields() != null && config.getFields().containsKey("script")) {
// scriptTask.setScript(config.getFields().get("script").toString());
// }
}
return scriptTask;
}
/**
* 创建排他网关
* @param node 节点数据
* @return 排他网关
*/
private ExclusiveGateway createExclusiveGateway(WorkflowDefinitionNode node) {
ExclusiveGateway gateway = new ExclusiveGateway();
gateway.setId(node.getId());
gateway.setName(node.getName());
return gateway;
}
/**
* 创建并行网关
* @param node 节点数据
* @return 并行网关
*/
private ParallelGateway createParallelGateway(WorkflowDefinitionNode node) {
ParallelGateway gateway = new ParallelGateway();
gateway.setId(node.getId());
gateway.setName(node.getName());
return gateway;
}
/**
* 创建子流程
* @param node 节点数据
* @return 子流程
*/
private SubProcess createSubProcess(WorkflowDefinitionNode node) {
SubProcess subProcess = new SubProcess();
subProcess.setId(node.getId());
subProcess.setName(node.getName());
return subProcess;
}
/**
* 创建调用活动
* @param node 节点数据
* @return 调用活动
*/
private CallActivity createCallActivity(WorkflowDefinitionNode node) {
CallActivity callActivity = new CallActivity();
callActivity.setId(node.getId());
callActivity.setName(node.getName());
if (node.getConfig() != null) {
// WorkflowDefinitionEdgeNodeConfig config = node.getConfig();
// callActivity.setCalledElement(config.getImplementation());
// if (config.getFields() != null) {
// config.getFields().forEach((key, value) -> {
// IOParameter parameter = new IOParameter();
// parameter.setSource(key);
// parameter.setTarget(value != null ? value.toString() : null);
// callActivity.getInParameters().add(parameter);
// });
// }
}
return callActivity;
}
/**
* 转换边
* @param edge 工作流边
* @return 序列流
*/
private SequenceFlow convertEdge(WorkflowDefinitionEdge edge) {
SequenceFlow flow = new SequenceFlow();
flow.setId(edge.getId());
flow.setId("flow_" + edge.getId().replaceAll("[^a-zA-Z0-9-_.]", "_"));
flow.setName(edge.getName());
// flow.setSourceRef(edge.getSource());
// flow.setTargetRef(edge.getTarget());
if (edge.getConfig() != null) {
WorkflowDefinitionEdgeConfig config = edge.getConfig();
if ("sequence".equals(config.getType())) {
if (config.getCondition() != null) {
((SequenceFlow) flow).setConditionExpression(config.getCondition());
flow.setSourceRef(idMapping.get(edge.getFrom()));
flow.setTargetRef(idMapping.get(edge.getTo()));
process.addFlowElement(flow);
}
// 自动布局
new BpmnAutoLayout(bpmnModel).execute();
// 转换为XML
BpmnXMLConverter converter = new BpmnXMLConverter();
byte[] bytes = converter.convertToXML(bpmnModel);
String xml = new String(bytes, StandardCharsets.UTF_8);
log.debug("工作流定义转换完成");
return xml;
} catch (Exception e) {
log.error("转换工作流定义为XML失败", e);
throw new RuntimeException("转换工作流定义为XML失败: " + e.getMessage(), e);
}
}
return flow;
private String sanitizeId(String id) {
return "node_" + id.replaceAll("[^a-zA-Z0-9-_.]", "_");
}
/**
* 配置流程节点的特定属性
* @param element 流程节点元素
* @param node 工作流节点定义
*/
private void configureFlowElement(FlowElement element, WorkflowDefinitionNode node) {
if (element instanceof ServiceTask) {
ServiceTask serviceTask = (ServiceTask) element;
// 设置委托表达式
String delegate = (String) node.getConfig().get("delegate");
serviceTask.setImplementationType("delegateExpression");
serviceTask.setImplementation(delegate);
serviceTask.setAsynchronous(true); // 设置为异步执行
// 添加字段注入和扩展属性
List<FieldExtension> fieldExtensions = new ArrayList<>();
Map<String, List<ExtensionElement>> extensionElements = new HashMap<>();
// 遍历所有配置项
node.getConfig().forEach((key, value) -> {
if (value != null && !"delegate".equals(key)) {
// 添加为字段注入
FieldExtension fieldExtension = new FieldExtension();
fieldExtension.setFieldName(key);
fieldExtension.setStringValue(String.valueOf(value));
fieldExtensions.add(fieldExtension);
// 同时也添加为扩展属性用于兼容性和可视化
addExtensionElement(extensionElements, key, String.valueOf(value));
}
});
serviceTask.setFieldExtensions(fieldExtensions);
serviceTask.setExtensionElements(extensionElements);
}
// 后续可以添加其他类型的节点配置
// else if (element instanceof UserTask) { ... }
}
/**
* 添加扩展属性
*/
private void addExtensionElement(Map<String, List<ExtensionElement>> extensionElements, String name, String value) {
if (value != null && !value.trim().isEmpty()) {
ExtensionElement extensionElement = new ExtensionElement();
extensionElement.setName(name);
extensionElement.setNamespace("http://flowable.org/bpmn");
extensionElement.setNamespacePrefix("flowable");
// 直接设置文本值让Flowable自己处理XML转义
extensionElement.setElementText(value);
extensionElements.computeIfAbsent(name, k -> new ArrayList<>()).add(extensionElement);
}
}
}

View File

@ -1,232 +1,229 @@
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.dto.graph.*;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 工作流定义图形生成工具类
* 用于生成示例工作流定义包括
* 1. 简单工作流开始 -> 脚本任务 -> 结束
* 2. 复杂工作流开始 -> 脚本任务A -> 脚本任务B -> 结束
*/
@Data
@Slf4j
public class WorkflowDefinitionGraph {
private static final ObjectMapper mapper = new ObjectMapper();
/**
* 节点列表
*/
private List<WorkflowDefinitionNode> nodes;
/**
* 边列表
*/
private List<WorkflowDefinitionEdge> edges;
/**
* 生成简单工作流
* 开始 -> 脚本任务 -> 结束
*/
public static WorkflowDefinitionGraph generateSimpleWorkflow() {
WorkflowDefinitionGraph graph = new WorkflowDefinitionGraph();
List<WorkflowDefinitionNode> nodes = new ArrayList<>();
List<WorkflowDefinitionEdge> edges = new ArrayList<>();
// 开始节点
WorkflowDefinitionNode startNode = createNode(
"startEvent1",
NodeTypeEnums.START_EVENT,
"开始",
100, 100,
createNodeConfig("开始节点", "启动流程")
);
nodes.add(startNode);
// 脚本任务节点
Map<String, Object> scriptConfig = createNodeConfig("脚本任务", "执行一个简单的Shell脚本");
scriptConfig.put("script", "echo 'Hello World'");
scriptConfig.put("language", "shell");
WorkflowDefinitionNode scriptNode = createNode(
"scriptTask1",
NodeTypeEnums.SCRIPT_TASK,
"执行脚本",
300, 100,
scriptConfig
);
nodes.add(scriptNode);
// 结束节点
WorkflowDefinitionNode endNode = createNode(
"endEvent1",
NodeTypeEnums.END_EVENT,
"结束",
500, 100,
createNodeConfig("结束节点", "流程结束")
);
nodes.add(endNode);
// 添加连线
edges.add(createEdge("flow1", "startEvent1", "scriptTask1", "开始到脚本"));
edges.add(createEdge("flow2", "scriptTask1", "endEvent1", "脚本到结束"));
graph.setNodes(nodes);
graph.setEdges(edges);
return graph;
}
/**
* 生成复杂工作流
* 开始 -> 脚本任务A -> 脚本任务B -> 结束
*/
public static WorkflowDefinitionGraph generateComplexWorkflow() {
WorkflowDefinitionGraph graph = new WorkflowDefinitionGraph();
List<WorkflowDefinitionNode> nodes = new ArrayList<>();
List<WorkflowDefinitionEdge> edges = new ArrayList<>();
// 开始节点
WorkflowDefinitionNode startNode = createNode(
"startEvent1",
NodeTypeEnums.START_EVENT,
"开始",
100, 100,
createNodeConfig("开始节点", "启动流程")
);
nodes.add(startNode);
// 脚本任务A
Map<String, Object> scriptConfigA = createNodeConfig("脚本任务A", "数据处理");
scriptConfigA.put("script", "process_data.sh");
scriptConfigA.put("language", "shell");
WorkflowDefinitionNode scriptNodeA = createNode(
"scriptTask1",
NodeTypeEnums.SCRIPT_TASK,
"数据处理",
300, 100,
scriptConfigA
);
nodes.add(scriptNodeA);
// 脚本任务B
Map<String, Object> scriptConfigB = createNodeConfig("脚本任务B", "生成报告");
scriptConfigB.put("script", "generate_report.sh");
scriptConfigB.put("language", "shell");
WorkflowDefinitionNode scriptNodeB = createNode(
"scriptTask2",
NodeTypeEnums.SCRIPT_TASK,
"生成报告",
500, 100,
scriptConfigB
);
nodes.add(scriptNodeB);
// 结束节点
WorkflowDefinitionNode endNode = createNode(
"endEvent1",
NodeTypeEnums.END_EVENT,
"结束",
700, 100,
createNodeConfig("结束节点", "流程结束")
);
nodes.add(endNode);
// 添加连线
edges.add(createEdge("flow1", "startEvent1", "scriptTask1", "开始到处理"));
edges.add(createEdge("flow2", "scriptTask1", "scriptTask2", "处理到报告"));
edges.add(createEdge("flow3", "scriptTask2", "endEvent1", "报告到结束"));
graph.setNodes(nodes);
graph.setEdges(edges);
return graph;
}
/**
* 创建节点
*/
private static WorkflowDefinitionNode createNode(String id, NodeTypeEnums type, String name, int x, int y, Map<String, Object> config) {
WorkflowDefinitionNode node = new WorkflowDefinitionNode();
node.setId(id);
node.setCode(type.getCode());
node.setType(type);
node.setName(name);
node.setConfig(config);
// 从枚举的uiConfig中获取配置并创建新的图形配置
WorkflowNodeGraph originalConfig = type.getUiConfig();
WorkflowNodeGraph nodeGraph = new WorkflowNodeGraph()
.setShape(originalConfig.getShape())
.setSize(originalConfig.getSize().getWidth(), originalConfig.getSize().getHeight())
.setStyle(originalConfig.getStyle().getFill(),
originalConfig.getStyle().getStroke(),
originalConfig.getStyle().getIcon())
.configPorts(originalConfig.getPorts().getTypes())
.setPosition(x, y);
node.setGraph(nodeGraph);
return node;
}
/**
* 创建节点配置
*/
private static Map<String, Object> createNodeConfig(String name, String description) {
Map<String, Object> config = new HashMap<>();
config.put("name", name);
config.put("description", description);
return config;
}
/**
* 创建连线
*/
private static WorkflowDefinitionEdge createEdge(String id, String from, String to, String name) {
return createEdge(id, from, to, name, null);
}
/**
* 创建带条件的连线
*/
private static WorkflowDefinitionEdge createEdge(String id, String from, String to, String name, String condition) {
WorkflowDefinitionEdge edge = new WorkflowDefinitionEdge();
edge.setId(id);
edge.setFrom(from);
edge.setTo(to);
edge.setName(name);
// 设置边配置
WorkflowDefinitionEdgeConfig config = new WorkflowDefinitionEdgeConfig();
config.setType("sequence");
if (condition != null) {
config.setCondition(condition);
config.setConditionExpression(condition);
}
edge.setConfig(config);
return edge;
}
public static void main(String[] args) {
try {
System.out.println("=== 简单工作流 ===");
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(generateSimpleWorkflow()));
System.out.println("\n=== 复杂工作流 ===");
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(generateComplexWorkflow()));
} catch (Exception e) {
log.error("生成工作流定义失败", e);
}
}
}
//package com.qqchen.deploy.backend.workflow.util;
//
//import com.fasterxml.jackson.databind.ObjectMapper;
//import com.qqchen.deploy.backend.workflow.dto.graph.*;
//import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnums;
//import lombok.Data;
//import lombok.extern.slf4j.Slf4j;
//
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//
///**
// * 工作流定义图形生成工具类
// * 用于生成示例工作流定义包括
// * 1. 简单工作流开始 -> 脚本任务 -> 结束
// * 2. 复杂工作流开始 -> 脚本任务A -> 脚本任务B -> 结束
// */
//@Data
//@Slf4j
//public class WorkflowDefinitionGraph {
// private static final ObjectMapper mapper = new ObjectMapper();
//
// /**
// * 节点列表
// */
// private List<WorkflowDefinitionNode> nodes;
//
// /**
// * 边列表
// */
// private List<WorkflowDefinitionEdge> edges;
//
// /**
// * 生成简单工作流
// * 开始 -> 脚本任务 -> 结束
// */
// public static WorkflowDefinitionGraph generateSimpleWorkflow() {
// WorkflowDefinitionGraph graph = new WorkflowDefinitionGraph();
// List<WorkflowDefinitionNode> nodes = new ArrayList<>();
// List<WorkflowDefinitionEdge> edges = new ArrayList<>();
//
// // 开始节点
// WorkflowDefinitionNode startNode = createNode(
// "startEvent1",
// NodeTypeEnums.START_EVENT,
// "开始",
// 100, 100,
// createNodeConfig("开始节点", "启动流程")
// );
// nodes.add(startNode);
//
// // 脚本任务节点
// Map<String, Object> scriptConfig = createNodeConfig("脚本任务", "执行一个简单的Shell脚本");
// scriptConfig.put("script", "echo 'Hello World'");
// scriptConfig.put("language", "shell");
//
// WorkflowDefinitionNode scriptNode = createNode(
// "scriptTask1",
// NodeTypeEnums.SCRIPT_TASK,
// "执行脚本",
// 300, 100,
// scriptConfig
// );
// nodes.add(scriptNode);
//
// // 结束节点
// WorkflowDefinitionNode endNode = createNode(
// "endEvent1",
// NodeTypeEnums.END_EVENT,
// "结束",
// 500, 100,
// createNodeConfig("结束节点", "流程结束")
// );
// nodes.add(endNode);
//
// // 添加连线
// edges.add(createEdge("flow1", "startEvent1", "scriptTask1", "开始到脚本"));
// edges.add(createEdge("flow2", "scriptTask1", "endEvent1", "脚本到结束"));
//
// graph.setNodes(nodes);
// graph.setEdges(edges);
// return graph;
// }
//
// /**
// * 生成复杂工作流
// * 开始 -> 脚本任务A -> 脚本任务B -> 结束
// */
// public static WorkflowDefinitionGraph generateComplexWorkflow() {
// WorkflowDefinitionGraph graph = new WorkflowDefinitionGraph();
// List<WorkflowDefinitionNode> nodes = new ArrayList<>();
// List<WorkflowDefinitionEdge> edges = new ArrayList<>();
//
// // 开始节点
// WorkflowDefinitionNode startNode = createNode(
// "startEvent1",
// NodeTypeEnums.START_EVENT,
// "开始",
// 100, 100,
// createNodeConfig("开始节点", "启动流程")
// );
// nodes.add(startNode);
//
// // 脚本任务A
// Map<String, Object> scriptConfigA = createNodeConfig("脚本任务A", "数据处理");
// scriptConfigA.put("script", "process_data.sh");
// scriptConfigA.put("language", "shell");
//
// WorkflowDefinitionNode scriptNodeA = createNode(
// "scriptTask1",
// NodeTypeEnums.SCRIPT_TASK,
// "数据处理",
// 300, 100,
// scriptConfigA
// );
// nodes.add(scriptNodeA);
//
// // 脚本任务B
// Map<String, Object> scriptConfigB = createNodeConfig("脚本任务B", "生成报告");
// scriptConfigB.put("script", "generate_report.sh");
// scriptConfigB.put("language", "shell");
//
// WorkflowDefinitionNode scriptNodeB = createNode(
// "scriptTask2",
// NodeTypeEnums.SCRIPT_TASK,
// "生成报告",
// 500, 100,
// scriptConfigB
// );
// nodes.add(scriptNodeB);
//
// // 结束节点
// WorkflowDefinitionNode endNode = createNode(
// "endEvent1",
// NodeTypeEnums.END_EVENT,
// "结束",
// 700, 100,
// createNodeConfig("结束节点", "流程结束")
// );
// nodes.add(endNode);
//
// // 添加连线
// edges.add(createEdge("flow1", "startEvent1", "scriptTask1", "开始到处理"));
// edges.add(createEdge("flow2", "scriptTask1", "scriptTask2", "处理到报告"));
// edges.add(createEdge("flow3", "scriptTask2", "endEvent1", "报告到结束"));
//
// graph.setNodes(nodes);
// graph.setEdges(edges);
// return graph;
// }
//
// /**
// * 创建节点
// */
// private static WorkflowDefinitionNode createNode(String id, NodeTypeEnums type, String name, int x, int y, Map<String, Object> config) {
// WorkflowDefinitionNode node = new WorkflowDefinitionNode();
// node.setId(id);
// node.setCode(type.getCode());
// node.setType(type);
// node.setName(name);
// node.setConfig(config);
//
// // 从枚举的uiConfig中获取配置并创建新的图形配置
// WorkflowNodeGraph originalConfig = type.getUiConfig();
// WorkflowNodeGraph nodeGraph = new WorkflowNodeGraph()
// .setShape(originalConfig.getShape())
// .setSize(originalConfig.getSize().getWidth(), originalConfig.getSize().getHeight())
// .setStyle(originalConfig.getStyle().getFill(),
// originalConfig.getStyle().getStroke(),
// originalConfig.getStyle().getIcon())
// .configPorts(originalConfig.getPorts().getTypes())
// .setPosition(x, y);
//
// node.setGraph(nodeGraph);
// return node;
// }
//
// /**
// * 创建节点配置
// */
// private static Map<String, Object> createNodeConfig(String name, String description) {
// Map<String, Object> config = new HashMap<>();
// config.put("name", name);
// config.put("description", description);
// return config;
// }
//
// /**
// * 创建连线
// */
// private static WorkflowDefinitionEdge createEdge(String id, String from, String to, String name) {
// return createEdge(id, from, to, name, null);
// }
//
// /**
// * 创建带条件的连线
// */
// private static WorkflowDefinitionEdge createEdge(String id, String from, String to, String name, String condition) {
// WorkflowDefinitionEdge edge = new WorkflowDefinitionEdge();
// edge.setId(id);
// edge.setFrom(from);
// edge.setTo(to);
// edge.setName(name);
//
// // 设置边配置
// WorkflowDefinitionEdgeConfig config = new WorkflowDefinitionEdgeConfig();
// config.setType("sequence");
// if (condition != null) {
// config.setCondition(condition);
// config.setConditionExpression(condition);
// }
// edge.setConfig(config);
//
// return edge;
// }
//
// public static void main(String[] args) {
// try {
// System.out.println("=== 简单工作流 ===");
// System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(generateSimpleWorkflow()));
// System.out.println("\n=== 复杂工作流 ===");
// System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(generateComplexWorkflow()));
// } catch (Exception e) {
// log.error("生成工作流定义失败", e);
// }
// }
//}