628 lines
21 KiB
Java
628 lines
21 KiB
Java
package com.flowable.devops.config;
|
||
|
||
import com.fasterxml.jackson.databind.JsonNode;
|
||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||
import com.flowable.devops.entity.NodeType;
|
||
import com.flowable.devops.service.NodeTypeService;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.beans.factory.annotation.Value;
|
||
import org.springframework.boot.CommandLineRunner;
|
||
import org.springframework.core.annotation.Order;
|
||
import org.springframework.stereotype.Component;
|
||
|
||
import java.util.List;
|
||
|
||
/**
|
||
* 数据初始化组件
|
||
*
|
||
* 在应用启动时加载默认节点类型和示例数据
|
||
*/
|
||
@Slf4j
|
||
@Component
|
||
@Order(1) // 确保在其他初始化组件之前执行
|
||
public class DataInitializer implements CommandLineRunner {
|
||
|
||
@Autowired
|
||
private NodeTypeService nodeTypeService;
|
||
|
||
@Value("${flowable-devops.node-types.load-defaults:true}")
|
||
private boolean loadDefaultNodeTypes;
|
||
|
||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||
|
||
@Override
|
||
public void run(String... args) throws Exception {
|
||
log.info("开始数据初始化...");
|
||
|
||
if (loadDefaultNodeTypes) {
|
||
initializeDefaultNodeTypes();
|
||
}
|
||
|
||
log.info("数据初始化完成");
|
||
}
|
||
|
||
/**
|
||
* 初始化默认节点类型
|
||
*/
|
||
private void initializeDefaultNodeTypes() {
|
||
log.info("加载默认节点类型...");
|
||
|
||
try {
|
||
List<NodeType> defaultNodeTypes = createDefaultNodeTypes();
|
||
|
||
for (NodeType nodeType : defaultNodeTypes) {
|
||
try {
|
||
// 检查节点类型是否已存在
|
||
try {
|
||
nodeTypeService.getNodeType(nodeType.getId());
|
||
log.debug("节点类型已存在,跳过: {}", nodeType.getId());
|
||
continue;
|
||
} catch (Exception e) {
|
||
// 节点类型不存在,继续创建
|
||
}
|
||
|
||
NodeType created = nodeTypeService.createNodeType(nodeType);
|
||
log.info("创建默认节点类型: {} ({})", created.getName(), created.getId());
|
||
} catch (Exception e) {
|
||
log.warn("创建默认节点类型失败: {} - {}", nodeType.getId(), e.getMessage());
|
||
}
|
||
}
|
||
|
||
// 刷新注册表
|
||
nodeTypeService.refreshRegistry();
|
||
log.info("默认节点类型加载完成,共 {} 个节点类型", defaultNodeTypes.size());
|
||
|
||
} catch (Exception e) {
|
||
log.error("加载默认节点类型失败", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建默认节点类型列表
|
||
*/
|
||
private List<NodeType> createDefaultNodeTypes() throws Exception {
|
||
return List.of(
|
||
// 1. 开始节点
|
||
createStartNodeType(),
|
||
|
||
// 2. 结束节点
|
||
createEndNodeType(),
|
||
|
||
// 3. 脚本任务节点
|
||
createScriptTaskNodeType(),
|
||
|
||
// 4. HTTP请求节点
|
||
createHttpRequestNodeType(),
|
||
|
||
// 5. 用户任务节点(审批节点)
|
||
createUserTaskNodeType(),
|
||
|
||
// 6. 条件分支节点
|
||
createExclusiveGatewayNodeType(),
|
||
|
||
// 7. 并行分支节点
|
||
createParallelGatewayNodeType(),
|
||
|
||
// 8. 数据转换节点
|
||
createDataTransformNodeType()
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 开始节点
|
||
*/
|
||
private NodeType createStartNodeType() throws Exception {
|
||
NodeType nodeType = new NodeType();
|
||
nodeType.setId("start");
|
||
nodeType.setName("开始");
|
||
nodeType.setDisplayName("开始");
|
||
nodeType.setDescription("工作流开始节点");
|
||
nodeType.setCategory(NodeType.NodeCategory.OTHER);
|
||
nodeType.setIcon("play-circle");
|
||
nodeType.setImplementationClass("com.flowable.devops.workflow.node.StartNode");
|
||
nodeType.setEnabled(true);
|
||
nodeType.setDisplayOrder(10);
|
||
|
||
// 字段定义 - 开始节点通常不需要配置字段
|
||
String fieldsJson = "[]";
|
||
nodeType.setFields(objectMapper.readTree(fieldsJson));
|
||
|
||
// 输出模式
|
||
String outputSchemaJson = """
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"startTime": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "开始时间"
|
||
},
|
||
"initiator": {
|
||
"type": "string",
|
||
"description": "发起人"
|
||
}
|
||
}
|
||
}
|
||
""";
|
||
nodeType.setOutputSchema(objectMapper.readTree(outputSchemaJson));
|
||
|
||
return nodeType;
|
||
}
|
||
|
||
/**
|
||
* 结束节点
|
||
*/
|
||
private NodeType createEndNodeType() throws Exception {
|
||
NodeType nodeType = new NodeType();
|
||
nodeType.setId("end");
|
||
nodeType.setName("结束");
|
||
nodeType.setDisplayName("结束");
|
||
nodeType.setDescription("工作流结束节点");
|
||
nodeType.setCategory(NodeType.NodeCategory.OTHER);
|
||
nodeType.setIcon("stop-circle");
|
||
nodeType.setImplementationClass("com.flowable.devops.workflow.node.EndNode");
|
||
nodeType.setEnabled(true);
|
||
nodeType.setDisplayOrder(20);
|
||
|
||
// 字段定义
|
||
String fieldsJson = """
|
||
[
|
||
{
|
||
"name": "result",
|
||
"label": "结束结果",
|
||
"type": "select",
|
||
"required": false,
|
||
"defaultValue": "completed",
|
||
"options": [
|
||
{"label": "完成", "value": "completed"},
|
||
{"label": "取消", "value": "cancelled"},
|
||
{"label": "中止", "value": "aborted"}
|
||
]
|
||
}
|
||
]
|
||
""";
|
||
nodeType.setFields(objectMapper.readTree(fieldsJson));
|
||
|
||
// 输出模式
|
||
String outputSchemaJson = """
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"endTime": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "结束时间"
|
||
},
|
||
"result": {
|
||
"type": "string",
|
||
"description": "执行结果"
|
||
}
|
||
}
|
||
}
|
||
""";
|
||
nodeType.setOutputSchema(objectMapper.readTree(outputSchemaJson));
|
||
|
||
return nodeType;
|
||
}
|
||
|
||
/**
|
||
* 脚本任务节点
|
||
*/
|
||
private NodeType createScriptTaskNodeType() throws Exception {
|
||
NodeType nodeType = new NodeType();
|
||
nodeType.setId("script-task");
|
||
nodeType.setName("脚本任务");
|
||
nodeType.setDisplayName("脚本任务");
|
||
nodeType.setDescription("执行脚本代码的任务节点");
|
||
nodeType.setCategory(NodeType.NodeCategory.LOGIC);
|
||
nodeType.setIcon("code");
|
||
nodeType.setImplementationClass("com.flowable.devops.workflow.node.ScriptTaskNode");
|
||
nodeType.setEnabled(true);
|
||
nodeType.setDisplayOrder(100);
|
||
|
||
// 字段定义
|
||
String fieldsJson = """
|
||
[
|
||
{
|
||
"name": "script",
|
||
"label": "脚本内容",
|
||
"type": "textarea",
|
||
"required": true,
|
||
"placeholder": "输入要执行的脚本代码",
|
||
"rows": 5
|
||
},
|
||
{
|
||
"name": "language",
|
||
"label": "脚本语言",
|
||
"type": "select",
|
||
"required": true,
|
||
"defaultValue": "javascript",
|
||
"options": [
|
||
{"label": "JavaScript", "value": "javascript"},
|
||
{"label": "Groovy", "value": "groovy"},
|
||
{"label": "Python", "value": "python"}
|
||
]
|
||
},
|
||
{
|
||
"name": "timeout",
|
||
"label": "超时时间(秒)",
|
||
"type": "number",
|
||
"required": false,
|
||
"defaultValue": 30,
|
||
"min": 1,
|
||
"max": 300
|
||
}
|
||
]
|
||
""";
|
||
nodeType.setFields(objectMapper.readTree(fieldsJson));
|
||
|
||
// 输出模式
|
||
String outputSchemaJson = """
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"result": {
|
||
"type": "object",
|
||
"description": "脚本执行结果"
|
||
},
|
||
"executionTime": {
|
||
"type": "number",
|
||
"description": "执行耗时(毫秒)"
|
||
},
|
||
"status": {
|
||
"type": "string",
|
||
"enum": ["success", "error"],
|
||
"description": "执行状态"
|
||
}
|
||
}
|
||
}
|
||
""";
|
||
nodeType.setOutputSchema(objectMapper.readTree(outputSchemaJson));
|
||
|
||
return nodeType;
|
||
}
|
||
|
||
/**
|
||
* HTTP请求节点
|
||
*/
|
||
private NodeType createHttpRequestNodeType() throws Exception {
|
||
NodeType nodeType = new NodeType();
|
||
nodeType.setId("http-request");
|
||
nodeType.setName("HTTP请求");
|
||
nodeType.setDescription("发送HTTP请求的任务节点");
|
||
nodeType.setCategory(NodeType.NodeCategory.API);
|
||
nodeType.setIcon("globe");
|
||
nodeType.setEnabled(true);
|
||
nodeType.setDisplayOrder(200);
|
||
|
||
// 字段定义
|
||
String fieldsJson = """
|
||
[
|
||
{
|
||
"name": "url",
|
||
"label": "请求URL",
|
||
"type": "text",
|
||
"required": true,
|
||
"placeholder": "http://example.com/api"
|
||
},
|
||
{
|
||
"name": "method",
|
||
"label": "请求方法",
|
||
"type": "select",
|
||
"required": true,
|
||
"defaultValue": "GET",
|
||
"options": [
|
||
{"label": "GET", "value": "GET"},
|
||
{"label": "POST", "value": "POST"},
|
||
{"label": "PUT", "value": "PUT"},
|
||
{"label": "DELETE", "value": "DELETE"}
|
||
]
|
||
},
|
||
{
|
||
"name": "headers",
|
||
"label": "请求头",
|
||
"type": "keyvalue",
|
||
"required": false,
|
||
"description": "HTTP请求头"
|
||
},
|
||
{
|
||
"name": "body",
|
||
"label": "请求体",
|
||
"type": "textarea",
|
||
"required": false,
|
||
"placeholder": "JSON格式的请求体内容"
|
||
},
|
||
{
|
||
"name": "timeout",
|
||
"label": "超时时间(秒)",
|
||
"type": "number",
|
||
"required": false,
|
||
"defaultValue": 30
|
||
}
|
||
]
|
||
""";
|
||
nodeType.setFields(objectMapper.readTree(fieldsJson));
|
||
|
||
// 输出模式
|
||
String outputSchemaJson = """
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"status": {
|
||
"type": "number",
|
||
"description": "HTTP状态码"
|
||
},
|
||
"headers": {
|
||
"type": "object",
|
||
"description": "响应头"
|
||
},
|
||
"body": {
|
||
"type": "object",
|
||
"description": "响应体"
|
||
},
|
||
"responseTime": {
|
||
"type": "number",
|
||
"description": "响应时间(毫秒)"
|
||
}
|
||
}
|
||
}
|
||
""";
|
||
nodeType.setOutputSchema(objectMapper.readTree(outputSchemaJson));
|
||
|
||
return nodeType;
|
||
}
|
||
|
||
/**
|
||
* 用户任务节点(审批节点)
|
||
*/
|
||
private NodeType createUserTaskNodeType() throws Exception {
|
||
NodeType nodeType = new NodeType();
|
||
nodeType.setId("user-task");
|
||
nodeType.setName("用户任务");
|
||
nodeType.setDescription("需要人工处理的审批任务节点");
|
||
nodeType.setCategory(NodeType.NodeCategory.OTHER);
|
||
nodeType.setIcon("user-check");
|
||
nodeType.setEnabled(true);
|
||
nodeType.setDisplayOrder(300);
|
||
|
||
// 字段定义
|
||
String fieldsJson = """
|
||
[
|
||
{
|
||
"name": "assignee",
|
||
"label": "分配给",
|
||
"type": "text",
|
||
"required": false,
|
||
"placeholder": "用户ID或表达式"
|
||
},
|
||
{
|
||
"name": "candidateUsers",
|
||
"label": "候选用户",
|
||
"type": "text",
|
||
"required": false,
|
||
"placeholder": "用户ID列表,逗号分隔"
|
||
},
|
||
{
|
||
"name": "candidateGroups",
|
||
"label": "候选组",
|
||
"type": "text",
|
||
"required": false,
|
||
"placeholder": "组ID列表,逗号分隔"
|
||
},
|
||
{
|
||
"name": "dueDate",
|
||
"label": "截止时间",
|
||
"type": "text",
|
||
"required": false,
|
||
"placeholder": "时间表达式或具体时间"
|
||
},
|
||
{
|
||
"name": "priority",
|
||
"label": "优先级",
|
||
"type": "number",
|
||
"required": false,
|
||
"defaultValue": 50,
|
||
"min": 1,
|
||
"max": 100
|
||
},
|
||
{
|
||
"name": "formKey",
|
||
"label": "表单Key",
|
||
"type": "text",
|
||
"required": false,
|
||
"placeholder": "关联的表单标识"
|
||
}
|
||
]
|
||
""";
|
||
nodeType.setFields(objectMapper.readTree(fieldsJson));
|
||
|
||
// 输出模式
|
||
String outputSchemaJson = """
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"approved": {
|
||
"type": "boolean",
|
||
"description": "是否通过审批"
|
||
},
|
||
"approver": {
|
||
"type": "string",
|
||
"description": "审批人"
|
||
},
|
||
"approvalTime": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "审批时间"
|
||
},
|
||
"comment": {
|
||
"type": "string",
|
||
"description": "审批意见"
|
||
}
|
||
}
|
||
}
|
||
""";
|
||
nodeType.setOutputSchema(objectMapper.readTree(outputSchemaJson));
|
||
|
||
return nodeType;
|
||
}
|
||
|
||
/**
|
||
* 条件分支节点
|
||
*/
|
||
private NodeType createExclusiveGatewayNodeType() throws Exception {
|
||
NodeType nodeType = new NodeType();
|
||
nodeType.setId("exclusive-gateway");
|
||
nodeType.setName("条件分支");
|
||
nodeType.setDescription("基于条件的排他分支节点");
|
||
nodeType.setCategory(NodeType.NodeCategory.LOGIC);
|
||
nodeType.setIcon("git-branch");
|
||
nodeType.setEnabled(true);
|
||
nodeType.setDisplayOrder(400);
|
||
|
||
// 字段定义
|
||
String fieldsJson = """
|
||
[
|
||
{
|
||
"name": "name",
|
||
"label": "分支名称",
|
||
"type": "text",
|
||
"required": false,
|
||
"placeholder": "分支节点的名称"
|
||
}
|
||
]
|
||
""";
|
||
nodeType.setFields(objectMapper.readTree(fieldsJson));
|
||
|
||
// 输出模式
|
||
String outputSchemaJson = """
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"selectedPath": {
|
||
"type": "string",
|
||
"description": "选择的分支路径"
|
||
},
|
||
"evaluationTime": {
|
||
"type": "number",
|
||
"description": "条件评估耗时(毫秒)"
|
||
}
|
||
}
|
||
}
|
||
""";
|
||
nodeType.setOutputSchema(objectMapper.readTree(outputSchemaJson));
|
||
|
||
return nodeType;
|
||
}
|
||
|
||
/**
|
||
* 并行分支节点
|
||
*/
|
||
private NodeType createParallelGatewayNodeType() throws Exception {
|
||
NodeType nodeType = new NodeType();
|
||
nodeType.setId("parallel-gateway");
|
||
nodeType.setName("并行分支");
|
||
nodeType.setDescription("并行执行多个分支的节点");
|
||
nodeType.setCategory(NodeType.NodeCategory.LOGIC);
|
||
nodeType.setIcon("share");
|
||
nodeType.setEnabled(true);
|
||
nodeType.setDisplayOrder(410);
|
||
|
||
// 字段定义
|
||
String fieldsJson = """
|
||
[
|
||
{
|
||
"name": "name",
|
||
"label": "并行节点名称",
|
||
"type": "text",
|
||
"required": false,
|
||
"placeholder": "并行节点的名称"
|
||
}
|
||
]
|
||
""";
|
||
nodeType.setFields(objectMapper.readTree(fieldsJson));
|
||
|
||
// 输出模式
|
||
String outputSchemaJson = """
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"parallelPaths": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "string"
|
||
},
|
||
"description": "并行分支路径"
|
||
},
|
||
"forkTime": {
|
||
"type": "string",
|
||
"format": "date-time",
|
||
"description": "分支创建时间"
|
||
}
|
||
}
|
||
}
|
||
""";
|
||
nodeType.setOutputSchema(objectMapper.readTree(outputSchemaJson));
|
||
|
||
return nodeType;
|
||
}
|
||
|
||
/**
|
||
* 数据转换节点
|
||
*/
|
||
private NodeType createDataTransformNodeType() throws Exception {
|
||
NodeType nodeType = new NodeType();
|
||
nodeType.setId("data-transform");
|
||
nodeType.setName("数据转换");
|
||
nodeType.setDescription("数据映射和转换节点");
|
||
nodeType.setCategory(NodeType.NodeCategory.TRANSFORM);
|
||
nodeType.setIcon("refresh-cw");
|
||
nodeType.setEnabled(true);
|
||
nodeType.setDisplayOrder(500);
|
||
|
||
// 字段定义
|
||
String fieldsJson = """
|
||
[
|
||
{
|
||
"name": "mappings",
|
||
"label": "字段映射",
|
||
"type": "keyvalue",
|
||
"required": true,
|
||
"description": "输入字段到输出字段的映射关系"
|
||
},
|
||
{
|
||
"name": "expression",
|
||
"label": "转换表达式",
|
||
"type": "textarea",
|
||
"required": false,
|
||
"placeholder": "JUEL表达式,用于复杂数据转换",
|
||
"rows": 3
|
||
}
|
||
]
|
||
""";
|
||
nodeType.setFields(objectMapper.readTree(fieldsJson));
|
||
|
||
// 输出模式
|
||
String outputSchemaJson = """
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"transformedData": {
|
||
"type": "object",
|
||
"description": "转换后的数据"
|
||
},
|
||
"transformedFields": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "string"
|
||
},
|
||
"description": "被转换的字段列表"
|
||
}
|
||
}
|
||
}
|
||
""";
|
||
nodeType.setOutputSchema(objectMapper.readTree(outputSchemaJson));
|
||
|
||
return nodeType;
|
||
}
|
||
} |