新增: 工作流审批取消端到端(StateMachine Arc 共享 + 取消 IPC 注册表)

- StateMachine 内部 HashMap→Arc<Mutex<HashMap>>,clone 共享底层
- DagExecutor 加 state_machine() 访问器
- AppState 加 workflow_state_registry(execution_id→StateMachine)
- run_workflow 注册执行器状态机 + 完成清理
- cancel_workflow_node IPC 经 execution_id 取共享引用 set_cancelled,直达 HumanNode 轮询
- 前端取消按钮 + cancelHumanApproval store 方法
- 加 clone_shares 单测验证共享语义
This commit is contained in:
2026-06-14 15:19:36 +08:00
parent 0789a36030
commit 4aa689e110
7 changed files with 166 additions and 19 deletions

View File

@@ -100,8 +100,15 @@ pub async fn run_workflow(
let event_bus = state.event_bus.clone();
let db = state.db.clone();
let exec_id = execution_id.clone();
let state_registry = state.workflow_state_registry.clone();
tauri::async_runtime::spawn(async move {
let mut executor = DagExecutor::new(event_bus.clone(), exec_id.clone());
// 注册执行器状态机StateMachine 内部 Arc<Mutex>clone 共享底层 HashMap
// cancel_workflow_node IPC 经 execution_id 取此引用 set_cancelled直达运行中 HumanNode
state_registry
.lock()
.await
.insert(exec_id.clone(), executor.state_machine());
let result = executor.run(&runtime_dag, config).await;
let (status, error) = match &result {
@@ -121,6 +128,9 @@ pub async fn run_workflow(
tracing::error!("更新工作流完成时间失败: {}", e);
}
// 执行结束(成功/失败)清理状态注册表,防内存泄漏
state_registry.lock().await.remove(&exec_id);
// 执行失败时补发 WorkflowFailed执行器内部只发 NodeFailed
if let Some(error) = error {
event_bus
@@ -185,3 +195,29 @@ pub async fn approve_human_approval(
state.event_bus.send(event).await;
Ok(())
}
/// 取消工作流节点(人工审批取消)
///
/// 从执行器状态机注册表取出共享引用,调 `set_cancelled` 置目标节点为 Cancelled。
/// StateMachine 内部 Arc<Mutex> 共享底层 HashMapIPC 写入直达运行中阻塞节点HumanNode
/// 的 select! 轮询分支,其 is_cancelled 检测到后返回 Err "人工审批被取消"。
#[tauri::command]
pub async fn cancel_workflow_node(
state: State<'_, AppState>,
execution_id: String,
node_id: String,
) -> Result<(), String> {
// 经 execution_id 取执行器状态机(与 NodeContext.node_status 共享同一 HashMap
let registry = state.workflow_state_registry.lock().await;
let sm = match registry.get(&execution_id) {
Some(sm) => sm,
None => return Err(format!("工作流 {} 不存在或已结束", execution_id)),
};
sm.set_cancelled(node_id.clone());
tracing::info!(
"工作流节点取消execution_id={}, node_id={}",
execution_id,
node_id
);
Ok(())
}

View File

@@ -58,6 +58,7 @@ pub fn run() {
commands::workflow::list_workflow_executions,
commands::workflow::get_workflow_execution,
commands::workflow::approve_human_approval,
commands::workflow::cancel_workflow_node,
// AI 聊天
commands::ai::ai_chat_send,
commands::ai::ai_chat_stop,

View File

@@ -1,5 +1,6 @@
//! 应用全局状态 — 数据库、Repo、事件总线、节点注册表、AI 会话
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
@@ -16,6 +17,7 @@ use df_storage::crud::{
use df_storage::db::Database;
use df_workflow::eventbus::EventBus;
use df_workflow::registry::NodeRegistry;
use df_workflow::state::StateMachine;
use crate::commands::ai::AiSession;
@@ -172,6 +174,14 @@ pub struct AppState {
// ── LLM 并发控制 ──
/// LLM 调用并发上限(全局 + 单对话双层 Semaphore运行时可调
pub llm_concurrency: LlmConcurrency,
// ── 工作流执行状态 ──
/// 工作流执行 → 节点状态机注册表
///
/// run_workflow 创建执行器后注册其 state_machineStateMachine 内部 Arc<Mutex>
/// clone 共享底层 HashMap与下沉到 NodeContext.node_status 的是同一份);
/// cancel_workflow_node IPC 经 execution_id 取出后 set_cancelled
/// 直达运行中 HumanNode 的 is_cancelled 轮询。执行完成(成功/失败)后移除条目。
pub workflow_state_registry: Arc<Mutex<HashMap<String, StateMachine>>>,
}
impl AppState {
@@ -195,6 +205,7 @@ impl AppState {
knowledge_config: Arc::new(Mutex::new(KnowledgeConfig::default())),
settings: SettingsRepo::new(&db),
llm_concurrency: LlmConcurrency::new(3, 2),
workflow_state_registry: Arc::new(Mutex::new(HashMap::new())),
db,
event_bus: EventBus::new(),
registry: Arc::new(build_registry()),