use async_trait::async_trait; use serde_json::Value; use tracing::{debug, info}; use std::time::Instant; use crate::flow::task::Executor; use crate::flow::domain::{NodeDef, NodeId}; fn read_node_script_file(ctx: &Value, node_id: &str, lang_key: &str) -> Option { if let Some(nodes) = ctx.get("nodes").and_then(|v| v.as_object()) { if let Some(m) = nodes.get(node_id).and_then(|v| v.get("scripts")).and_then(|v| v.as_object()) { return m.get(lang_key).and_then(|v| v.as_str()).map(|s| s.to_string()); } } None } fn truncate_str(s: &str, max: usize) -> String { let s = s.replace('\n', " ").replace('\r', " "); if s.len() <= max { s } else { format!("{}…", &s[..max]) } } #[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<()> { let start = Instant::now(); // 优先 nodes..scripts.js 指定的脚本文件路径 if let Some(path) = read_node_script_file(ctx, &node_id.0, "js") { let preview = truncate_str(&path, 120); info!(target = "udmin.flow", node=%node_id.0, file=%preview, "script_js task: JavaScript file execution not implemented yet (skipped)"); return Ok(()); } // 兼容 inline 配置(暂不执行,仅提示) let inline = ctx.get("script") .or_else(|| ctx.get("expr")) .and_then(|v| v.as_str()) .map(|s| s.to_string()); if let Some(code) = inline { let preview = truncate_str(&code, 200); debug!(target = "udmin.flow", node=%node_id.0, preview=%preview, "script_js task: inline script provided, but execution not implemented"); let _elapsed = start.elapsed().as_millis(); info!(target = "udmin.flow", node=%node_id.0, "script_js task: JavaScript execution not implemented yet (skipped)"); return Ok(()); } info!(target = "udmin.flow", node=%node_id.0, "script_js task: no script found, skip"); Ok(()) } }