feat(flow): 新增流式执行模式与SSE支持
新增流式执行模式,通过SSE实时推送节点执行事件与日志 重构HTTP执行器与中间件,提取通用HTTP客户端组件 优化前端测试面板,支持流式模式切换与实时日志展示 更新依赖版本并修复密码哈希的随机数生成器问题 修复前端节点类型映射问题,确保Code节点表单可用
This commit is contained in:
@ -1,3 +1,12 @@
|
||||
//! 模块:流程 DSL 与自由布局 Design JSON 的解析、校验与构建。
|
||||
//! 主要内容:
|
||||
//! - FlowDSL/NodeDSL/EdgeDSL:较为“表述性”的简化 DSL 结构(用于外部接口/入库)。
|
||||
//! - DesignSyntax/NodeSyntax/EdgeSyntax:与前端自由布局 JSON 对齐的结构(含 source_port_id 等)。
|
||||
//! - validate_design:基础校验(节点 ID 唯一、至少包含一个 start 与一个 end、边的引用合法)。
|
||||
//! - build_chain_from_design:将自由布局 JSON 转换为内部 ChainDef(含条件节点 AND 组装等启发式与兼容逻辑)。
|
||||
//! - chain_from_design_json:统一入口,支持字符串/对象两种输入,做兼容字段回填后再校验并构建。
|
||||
//! 说明:尽量保持向后兼容;在条件节点的出边组装上采用启发式(例如:单出边 + 多条件 => 组装为 AND 条件组)。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use anyhow::bail;
|
||||
@ -5,36 +14,53 @@ use anyhow::bail;
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FlowDSL {
|
||||
#[serde(default)]
|
||||
/// 流程名称(可选)
|
||||
pub name: String,
|
||||
#[serde(default, alias = "executionMode")]
|
||||
/// 执行模式(兼容前端 executionMode),如:sync/async(目前仅占位)
|
||||
pub execution_mode: Option<String>,
|
||||
/// 节点列表(按声明顺序)
|
||||
pub nodes: Vec<NodeDSL>,
|
||||
#[serde(default)]
|
||||
/// 边列表(from -> to,可选 condition)
|
||||
pub edges: Vec<EdgeDSL>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NodeDSL {
|
||||
/// 节点唯一 ID(字符串)
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
/// 节点类型:start / end / task / condition(开始/结束/任务/条件)
|
||||
pub kind: String, // 节点类型:start / end / task / condition(开始/结束/任务/条件)
|
||||
#[serde(default)]
|
||||
/// 节点显示名称(可选)
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
/// 任务标识(绑定执行器),如 http/db/variable/script_*(可选)
|
||||
pub task: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct EdgeDSL {
|
||||
/// 起点节点 ID(别名:source/from)
|
||||
#[serde(alias = "source", alias = "from", rename = "from")]
|
||||
pub from: String,
|
||||
/// 终点节点 ID(别名:target/to)
|
||||
#[serde(alias = "target", alias = "to", rename = "to")]
|
||||
pub to: String,
|
||||
#[serde(default)]
|
||||
/// 条件表达式(字符串):
|
||||
/// - 若为 JSON 字符串(以 { 或 [ 开头),则按 JSON 条件集合进行求值;
|
||||
/// - 否则按 Rhai 表达式求值;
|
||||
/// - 空字符串/None 表示无条件。
|
||||
pub condition: Option<String>,
|
||||
}
|
||||
|
||||
impl From<FlowDSL> for super::domain::ChainDef {
|
||||
/// 将简化 DSL 转换为内部 ChainDef:
|
||||
/// - kind 映射:start/end/condition/其他->task;支持 decision 别名 -> condition。
|
||||
/// - 直接搬运 edges 的 from/to/condition。
|
||||
fn from(v: FlowDSL) -> Self {
|
||||
super::domain::ChainDef {
|
||||
name: v.name,
|
||||
@ -71,34 +97,47 @@ impl From<FlowDSL> for super::domain::ChainDef {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DesignSyntax {
|
||||
#[serde(default)]
|
||||
/// 设计名称(可选)
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
/// 节点集合(自由布局)
|
||||
pub nodes: Vec<NodeSyntax>,
|
||||
#[serde(default)]
|
||||
/// 边集合(自由布局)
|
||||
pub edges: Vec<EdgeSyntax>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NodeSyntax {
|
||||
/// 节点 ID
|
||||
pub id: String,
|
||||
#[serde(rename = "type", default)]
|
||||
/// 前端类型:start | end | condition | http | db | task | script_*(用于推断具体执行器)
|
||||
pub kind: String, // 取值: start | end | condition | http | db | task(开始/结束/条件/HTTP/数据库/通用任务)
|
||||
#[serde(default)]
|
||||
/// 节点附加数据:title/conditions/scripts 等
|
||||
pub data: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct EdgeSyntax {
|
||||
/// 起点(兼容 sourceNodeID/source/from)
|
||||
#[serde(alias = "sourceNodeID", alias = "source", alias = "from")]
|
||||
pub from: String,
|
||||
/// 终点(兼容 targetNodeID/target/to)
|
||||
#[serde(alias = "targetNodeID", alias = "target", alias = "to")]
|
||||
pub to: String,
|
||||
#[serde(default)]
|
||||
/// 源端口 ID:用于条件节点端口到条件 key 的兼容映射;
|
||||
/// 特殊值 and/all/group/true 表示将节点内所有 conditions 的 value 组装为 AND 组。
|
||||
pub source_port_id: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
/// 从 design_json(前端流程 JSON)构建 ChainDef
|
||||
/// 设计级别校验:
|
||||
/// - 节点 ID 唯一且非空;
|
||||
/// - 至少一个 start 与一个 end;
|
||||
/// - 边的 from/to 必须指向已知节点。
|
||||
fn validate_design(design: &DesignSyntax) -> anyhow::Result<()> {
|
||||
use std::collections::HashSet;
|
||||
let mut ids = HashSet::new();
|
||||
@ -129,6 +168,13 @@ fn validate_design(design: &DesignSyntax) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 将自由布局 DesignSyntax 转换为内部 ChainDef:
|
||||
/// - 节点:推断 kind/name/task(含 scripts 与 inline script/expr 的兼容);
|
||||
/// - 边:
|
||||
/// * 条件节点:支持 source_port_id 到 data.conditions 的旧版映射;
|
||||
/// * 当 source_port_id 为空或为 and/all/group/true,取 conditions 的 value 组成 AND 组;
|
||||
/// * 启发式:若条件节点仅一条出边且包含多个 conditions,即便 source_port_id 指向具体 key,也回退为 AND 组;
|
||||
/// * 非条件节点:不处理条件。
|
||||
fn build_chain_from_design(design: &DesignSyntax) -> anyhow::Result<super::domain::ChainDef> {
|
||||
use super::domain::{ChainDef, NodeDef, NodeId, NodeKind, LinkDef};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user