feat(ws): 新增WebSocket实时通信支持与SSE独立服务

重构中间件结构,新增ws模块实现WebSocket流程执行实时推送
将SSE服务拆分为独立端口监听,默认8866
优化前端流式模式切换,支持WS/SSE协议选择
统一流式事件处理逻辑,完善错误处理与取消机制
更新Cargo.toml依赖,添加WebSocket相关库
调整代码组织结构,规范导入分组与注释
This commit is contained in:
2025-09-21 22:15:33 +08:00
parent dd7857940f
commit 30716686ed
23 changed files with 805 additions and 101 deletions

View File

@ -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() {

View File

@ -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;

View File

@ -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<()> {

View File

@ -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<()> {

View File

@ -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<()> {

View File

@ -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<()> {

View File

@ -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;