feat(ws): 新增WebSocket实时通信支持与SSE独立服务
重构中间件结构,新增ws模块实现WebSocket流程执行实时推送 将SSE服务拆分为独立端口监听,默认8866 优化前端流式模式切换,支持WS/SSE协议选择 统一流式事件处理逻辑,完善错误处理与取消机制 更新Cargo.toml依赖,添加WebSocket相关库 调整代码组织结构,规范导入分组与注释
This commit is contained in:
@ -1,15 +1,40 @@
|
||||
// std
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::{RwLock, Mutex};
|
||||
use futures::future::join_all;
|
||||
|
||||
use rhai::Engine;
|
||||
use tracing::info;
|
||||
use std::time::Instant;
|
||||
|
||||
// === 表达式评估支持:thread_local 引擎与 AST 缓存,避免全局 Sync/Send 限制 ===
|
||||
use std::cell::RefCell;
|
||||
use rhai::AST;
|
||||
// third-party
|
||||
use futures::future::join_all;
|
||||
use regex::Regex;
|
||||
use rhai::{AST, Engine};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tracing::info;
|
||||
|
||||
// crate
|
||||
use crate::flow::executors::condition::eval_condition_json;
|
||||
|
||||
// super
|
||||
use super::{
|
||||
context::{DriveOptions, ExecutionMode},
|
||||
domain::{ChainDef, NodeKind},
|
||||
task::TaskRegistry,
|
||||
};
|
||||
|
||||
// 结构体:紧随 use
|
||||
pub struct FlowEngine {
|
||||
pub tasks: TaskRegistry,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DriveError {
|
||||
pub node_id: String,
|
||||
pub ctx: serde_json::Value,
|
||||
pub logs: Vec<String>,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
// === 表达式评估支持:thread_local 引擎与 AST 缓存,避免全局 Sync/Send 限制 ===
|
||||
|
||||
// 模块:流程执行引擎(engine.rs)
|
||||
// 作用:驱动 ChainDef 流程图,支持:
|
||||
@ -132,12 +157,6 @@ pub(crate) fn eval_rhai_expr_json(expr: &str, ctx: &serde_json::Value) -> Option
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
use super::{context::{DriveOptions, ExecutionMode}, domain::{ChainDef, NodeKind}, task::TaskRegistry};
|
||||
use crate::flow::executors::condition::eval_condition_json;
|
||||
|
||||
pub struct FlowEngine {
|
||||
pub tasks: TaskRegistry,
|
||||
}
|
||||
|
||||
impl FlowEngine {
|
||||
pub fn new(tasks: TaskRegistry) -> Self { Self { tasks } }
|
||||
@ -491,14 +510,6 @@ impl Default for FlowEngine {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DriveError {
|
||||
pub node_id: String,
|
||||
pub ctx: serde_json::Value,
|
||||
pub logs: Vec<String>,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DriveError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// third-party
|
||||
use anyhow::Result;
|
||||
use serde_json::Value as V;
|
||||
use tracing::info;
|
||||
|
||||
// 业务函数
|
||||
pub(crate) fn eval_condition_json(ctx: &serde_json::Value, cond: &serde_json::Value) -> Result<bool> {
|
||||
// 新增:若 cond 为数组,按 AND 语义评估(全部为 true 才为 true)
|
||||
if let Some(arr) = cond.as_array() {
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
// third-party
|
||||
use async_trait::async_trait;
|
||||
use serde_json::{json, Value};
|
||||
use tracing::info;
|
||||
|
||||
use crate::flow::task::Executor;
|
||||
// crate
|
||||
use crate::flow::domain::{NodeDef, NodeId};
|
||||
use crate::flow::task::Executor;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DbTask;
|
||||
|
||||
@ -1,12 +1,17 @@
|
||||
use async_trait::async_trait;
|
||||
use serde_json::{Value, json, Map};
|
||||
use tracing::info;
|
||||
// std
|
||||
use std::collections::HashMap;
|
||||
|
||||
// third-party
|
||||
use async_trait::async_trait;
|
||||
use serde_json::{json, Map, Value};
|
||||
use tracing::info;
|
||||
|
||||
// crate
|
||||
use crate::flow::domain::{NodeDef, NodeId};
|
||||
use crate::flow::task::Executor;
|
||||
use crate::middlewares::http_client::{execute_http, HttpClientOptions, HttpRequest};
|
||||
|
||||
use crate::flow::task::Executor;
|
||||
use crate::flow::domain::{NodeDef, NodeId};
|
||||
|
||||
// 结构体:紧随 use
|
||||
#[derive(Default)]
|
||||
pub struct HttpTask;
|
||||
|
||||
@ -18,6 +23,7 @@ struct HttpOpts {
|
||||
http1_only: bool,
|
||||
}
|
||||
|
||||
// 业务实现与函数:置于最后
|
||||
#[async_trait]
|
||||
impl Executor for HttpTask {
|
||||
async fn execute(&self, node_id: &NodeId, _node: &NodeDef, ctx: &mut Value) -> anyhow::Result<()> {
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
use async_trait::async_trait
|
||||
;
|
||||
// std
|
||||
use std::fs;
|
||||
use std::time::Instant;
|
||||
|
||||
// third-party
|
||||
use async_trait::async_trait;
|
||||
use serde_json::Value;
|
||||
use tracing::{debug, info};
|
||||
use std::time::Instant;
|
||||
use std::fs;
|
||||
|
||||
use crate::flow::task::Executor;
|
||||
// crate
|
||||
use crate::flow::domain::{NodeDef, NodeId};
|
||||
use crate::flow::task::Executor;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScriptJsTask;
|
||||
|
||||
fn read_node_script_file(ctx: &Value, node_id: &str, lang_key: &str) -> Option<String> {
|
||||
if let Some(nodes) = ctx.get("nodes").and_then(|v| v.as_object()) {
|
||||
@ -123,9 +129,6 @@ fn exec_js_file(node_id: &NodeId, path: &str, ctx: &mut Value) -> anyhow::Result
|
||||
exec_js_script(node_id, &code, ctx)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScriptJsTask;
|
||||
|
||||
#[async_trait]
|
||||
impl Executor for ScriptJsTask {
|
||||
async fn execute(&self, node_id: &NodeId, _node: &NodeDef, ctx: &mut Value) -> anyhow::Result<()> {
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
// std
|
||||
use std::time::Instant;
|
||||
|
||||
// third-party
|
||||
use async_trait::async_trait;
|
||||
use serde_json::Value;
|
||||
use tracing::{debug, info};
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::flow::task::Executor;
|
||||
// crate
|
||||
use crate::flow::domain::{NodeDef, NodeId};
|
||||
use crate::flow::task::Executor;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScriptPythonTask;
|
||||
|
||||
fn read_node_script_file(ctx: &Value, node_id: &str, lang_key: &str) -> Option<String> {
|
||||
if let Some(nodes) = ctx.get("nodes").and_then(|v| v.as_object()) {
|
||||
@ -20,9 +27,6 @@ fn truncate_str(s: &str, max: usize) -> String {
|
||||
if s.len() <= max { s } else { format!("{}…", &s[..max]) }
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScriptPythonTask;
|
||||
|
||||
#[async_trait]
|
||||
impl Executor for ScriptPythonTask {
|
||||
async fn execute(&self, node_id: &NodeId, _node: &NodeDef, ctx: &mut Value) -> anyhow::Result<()> {
|
||||
|
||||
@ -1,13 +1,19 @@
|
||||
use serde_json::Value;
|
||||
use tracing::{debug, info};
|
||||
// std
|
||||
use std::fs;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::flow::domain::NodeId;
|
||||
// third-party
|
||||
use async_trait::async_trait;
|
||||
use serde_json::Value;
|
||||
use tracing::{debug, info};
|
||||
|
||||
// crate
|
||||
use crate::flow::domain::{NodeDef, NodeId};
|
||||
use crate::flow::engine::eval_rhai_expr_json;
|
||||
use crate::flow::task::Executor;
|
||||
use crate::flow::domain::NodeDef;
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScriptRhaiTask;
|
||||
|
||||
fn truncate_str(s: &str, max: usize) -> String {
|
||||
let s = s.replace('\n', " ").replace('\r', " ");
|
||||
@ -80,9 +86,6 @@ fn read_node_script_file(ctx: &Value, node_id: &str) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScriptRhaiTask;
|
||||
|
||||
#[async_trait]
|
||||
impl Executor for ScriptRhaiTask {
|
||||
async fn execute(&self, node_id: &NodeId, _node: &NodeDef, ctx: &mut Value) -> anyhow::Result<()> {
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
// third-party
|
||||
use async_trait::async_trait;
|
||||
use serde_json::{Value, json};
|
||||
use tracing::info;
|
||||
|
||||
use crate::flow::task::Executor;
|
||||
// crate
|
||||
use crate::flow::domain::{NodeDef, NodeId};
|
||||
use crate::flow::engine::eval_rhai_expr_json;
|
||||
use crate::flow::task::Executor;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct VariableTask;
|
||||
|
||||
@ -17,9 +17,9 @@ pub trait FlowLogHandler: Send + Sync {
|
||||
async fn log_error(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()>;
|
||||
|
||||
/// 记录流程执行失败(包含部分输出与累计日志)
|
||||
async fn log_error_detail(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
async fn log_error_detail(&self, _flow_id: &str, _flow_code: Option<&str>, _input: &Value, _output: &Value, _logs: &[String], error_msg: &str, _operator: Option<(i64, String)>, _started_at: DateTime<FixedOffset>, _duration_ms: i64) -> anyhow::Result<()> {
|
||||
// 默认实现:退化为仅错误信息
|
||||
self.log_error(flow_id, flow_code, input, error_msg, operator, started_at, duration_ms).await
|
||||
self.log_error(_flow_id, _flow_code, _input, error_msg, _operator, _started_at, _duration_ms).await
|
||||
}
|
||||
|
||||
/// 记录流程执行成功
|
||||
|
||||
Reference in New Issue
Block a user