diff --git a/src-tauri/src/commands/ai/commands.rs b/src-tauri/src/commands/ai/commands.rs index 3c82c56..dffe744 100644 --- a/src-tauri/src/commands/ai/commands.rs +++ b/src-tauri/src/commands/ai/commands.rs @@ -10,7 +10,7 @@ use df_core::types::new_id; use df_storage::models::AiProviderRecord; use crate::state::AppState; -use crate::commands::now_millis; +use crate::commands::{err_str, now_millis}; use super::agentic::{run_agentic_loop, try_continue_agent_loop}; use super::audit::audit_finalize; @@ -230,7 +230,7 @@ pub async fn ai_chat_clear(state: State<'_, AppState>) -> Result<(), String> { .ai_conversations .clear_messages(&id) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; } Ok(()) } @@ -311,7 +311,7 @@ fn mask_api_key(key: &str) -> String { /// 列出所有已配置的 AI 提供商(is_default 真相源为 DB,重启不丢) #[tauri::command] pub async fn ai_list_providers(state: State<'_, AppState>) -> Result, String> { - let mut providers = state.ai_providers.list_all().await.map_err(|e| e.to_string())?; + let mut providers = state.ai_providers.list_all().await.map_err(err_str)?; // IPC 不传明文 api_key(FR-S1):前端编辑用空 apiKey 表示不改,mask 后前端 realm 不持有明文。 // 迁移后 DB api_key 空 → 从 keyring 取真实密钥再 mask(前端看到 mask 但不持有明文) for p in &mut providers { @@ -339,7 +339,7 @@ pub async fn ai_save_provider( // 编辑已有提供商时保留原 created_at,避免被覆盖 let created_at = match &id { Some(pid) => state.ai_providers.get_by_id(pid).await - .map_err(|e| e.to_string())? + .map_err(err_str)? .map(|p| p.created_at) .unwrap_or_else(now_millis), None => now_millis(), @@ -347,11 +347,11 @@ pub async fn ai_save_provider( // is_default:编辑保留原值;新建时若全表尚无默认则设为默认(首个自动默认,避免无默认可用) let is_default = match &id { Some(pid) => state.ai_providers.get_by_id(pid).await - .map_err(|e| e.to_string())? + .map_err(err_str)? .map(|p| p.is_default) .unwrap_or(false), None => !state.ai_providers.list_all().await - .map_err(|e| e.to_string())? + .map_err(err_str)? .iter().any(|p| p.is_default), }; // FR-S1:密钥存 OS keyring,DB api_key 列恒空(不入明文)。 @@ -369,7 +369,7 @@ pub async fn ai_save_provider( // 兜底:发现未迁移态先即时迁移补密钥,迁移成功后再让下方清 DB 明文(收敛到迁移完成态); // 迁移失败则 Err 阻断保存且 INSERT OR REPLACE 不执行 → DB 明文保留,绝不劣化现状。 let old = state.ai_providers.get_by_id(pid).await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; if let Some(old) = old { if !old.api_key.is_empty() && super::secret::get_provider_secret(pid).is_none() @@ -409,7 +409,7 @@ pub async fn ai_save_provider( .ai_providers .insert(record) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(id) } @@ -424,18 +424,18 @@ pub async fn ai_set_provider( .ai_providers .get_by_id(&provider_id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("提供商不存在: {}", provider_id))?; // 互斥写 DB:目标 is_default=true,其余=false。仅写变化的记录。 - let providers = state.ai_providers.list_all().await.map_err(|e| e.to_string())?; + let providers = state.ai_providers.list_all().await.map_err(err_str)?; for p in &providers { let should = p.id == provider_id; if p.is_default != should { let mut updated = p.clone(); updated.is_default = should; updated.updated_at = now_millis(); - state.ai_providers.update_full(&updated).await.map_err(|e| e.to_string())?; + state.ai_providers.update_full(&updated).await.map_err(err_str)?; } } @@ -450,7 +450,7 @@ pub async fn ai_delete_provider( state: State<'_, AppState>, provider_id: String, ) -> Result<(), String> { - state.ai_providers.delete(&provider_id).await.map_err(|e| e.to_string())?; + state.ai_providers.delete(&provider_id).await.map_err(err_str)?; // CR-260615-01:DB 已删则清 keyring 残留密钥(失败仅 warn 不阻断——无 DB 消费方, // 残留 keyring 不可复活;同 id 复用也不会读到旧密钥,因 set 覆盖写) if let Err(e) = super::secret::delete_provider_secret(&provider_id) { @@ -518,7 +518,7 @@ pub async fn ai_conversation_list( ) -> Result, String> { let limit = limit.unwrap_or(50); let include_archived = include_archived.unwrap_or(false); - let records = state.ai_conversations.list_all().await.map_err(|e| e.to_string())?; + let records = state.ai_conversations.list_all().await.map_err(err_str)?; // list_all 已按 created_at DESC(最新在前);默认排除归档 + 截断 limit let summaries: Vec = records.iter() .filter(|r| include_archived || !r.archived) @@ -551,7 +551,7 @@ pub async fn ai_conversation_switch( conversation_id: String, ) -> Result { let record = state.ai_conversations.get_by_id(&conversation_id).await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("对话不存在: {}", conversation_id))?; let messages: Vec = serde_json::from_str(&record.messages) @@ -591,7 +591,7 @@ pub async fn ai_conversation_delete( state: State<'_, AppState>, conversation_id: String, ) -> Result<(), String> { - state.ai_conversations.delete(&conversation_id).await.map_err(|e| e.to_string())?; + state.ai_conversations.delete(&conversation_id).await.map_err(err_str)?; let mut session = state.ai_session.lock().await; if session.active_conversation_id.as_deref() == Some(&conversation_id) { @@ -614,7 +614,7 @@ pub async fn ai_conversation_rename( return Err("标题不能为空".to_string()); } state.ai_conversations.update_field(&conversation_id, "title", &title) - .await.map_err(|e| e.to_string())?; + .await.map_err(err_str)?; Ok(()) } @@ -628,7 +628,7 @@ pub async fn ai_conversation_archive( state.ai_conversations .set_archived(&conversation_id, archived) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(()) } diff --git a/src-tauri/src/commands/ai/knowledge_inject.rs b/src-tauri/src/commands/ai/knowledge_inject.rs index c309d84..53c58aa 100644 --- a/src-tauri/src/commands/ai/knowledge_inject.rs +++ b/src-tauri/src/commands/ai/knowledge_inject.rs @@ -13,6 +13,8 @@ use df_core::types::new_id; use crate::state::{AppState, ExtractTrigger, LlmConcurrency}; +use crate::commands::err_str; + use super::{AiSession}; /// 按配置构建 embedding provider + model。None = 配置缺失/provider 不存在。 @@ -240,7 +242,7 @@ pub async fn trigger_extraction_now(state: &AppState) -> Result { session.active_conversation_id.clone() }; let conv_id = conv_id.ok_or_else(|| "当前无活跃对话".to_string())?; - let provider_config = super::prompt::get_active_provider(state).await.map_err(|e| e.to_string())?; + let provider_config = super::prompt::get_active_provider(state).await.map_err(err_str)?; let db = state.db.clone(); let llm_concurrency = state.llm_concurrency.clone(); tauri::async_runtime::spawn(async move { diff --git a/src-tauri/src/commands/ai/prompt.rs b/src-tauri/src/commands/ai/prompt.rs index 13a9cbf..beaef4a 100644 --- a/src-tauri/src/commands/ai/prompt.rs +++ b/src-tauri/src/commands/ai/prompt.rs @@ -2,6 +2,7 @@ use df_storage::models::AiProviderRecord; +use crate::commands::err_str; use crate::state::AppState; /// 获取当前活跃提供商配置 @@ -12,7 +13,7 @@ pub(crate) async fn get_active_provider(state: &AppState) -> Result Result, ) -> Result, String> { match status { - Some(s) => state.ideas.query("status", &s).await.map_err(|e| e.to_string()), - None => state.ideas.list_all().await.map_err(|e| e.to_string()), + Some(s) => state.ideas.query("status", &s).await.map_err(err_str), + None => state.ideas.list_all().await.map_err(err_str), } } @@ -66,7 +66,7 @@ pub async fn create_idea( .ideas .insert(record.clone()) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(record) } @@ -82,13 +82,13 @@ pub async fn update_idea( .ideas .update_field(&id, &field, &value) .await - .map_err(|e| e.to_string()) + .map_err(err_str) } /// 删除灵感 #[tauri::command] pub async fn delete_idea(state: State<'_, AppState>, id: String) -> Result { - state.ideas.delete(&id).await.map_err(|e| e.to_string()) + state.ideas.delete(&id).await.map_err(err_str) } /// 将灵感晋升为项目 — 复用 df-project 领域逻辑创建项目,回写灵感 status=promoted/promoted_to @@ -101,7 +101,7 @@ pub async fn promote_idea( .ideas .get_by_id(&id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("灵感不存在: {id}"))?; if record.promoted_to.is_some() { @@ -131,7 +131,7 @@ pub async fn promote_idea( .projects .insert(project_record) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; // 回写灵感:status=promoted + promoted_to(update_full 单事务覆盖可变字段) // 补偿删除:第二步失败时回滚第一步已建的 project,保证最终一致性(非原子,但防项目存留而 @@ -175,7 +175,7 @@ pub async fn evaluate_idea( .ideas .get_by_id(&id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("灵感不存在: {id}"))?; let idea = record_to_idea(&record); @@ -186,7 +186,7 @@ pub async fn evaluate_idea( // 对抗式评估 let eval = df_ideas::adversarial::AdversarialEngine::evaluate(&idea) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; // 组装前端扁平结构(与 Ideas.vue 的 AdversarialEval interface 对齐) let positive_strength = eval.positive.confidence; @@ -243,7 +243,7 @@ pub async fn evaluate_idea( .ideas .update_full(&updated) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(updated) } diff --git a/src-tauri/src/commands/knowledge.rs b/src-tauri/src/commands/knowledge.rs index 24bb9f9..51b1f6e 100644 --- a/src-tauri/src/commands/knowledge.rs +++ b/src-tauri/src/commands/knowledge.rs @@ -12,7 +12,7 @@ use df_storage::models::{KnowledgeEventRecord, KnowledgeRecord}; use crate::state::{AppState, KnowledgeConfig}; use super::knowledge_timeline::KnowledgeTimeline; -use super::now_millis; +use super::{err_str, now_millis}; use serde::Serialize; /// 创建知识入参 @@ -76,10 +76,10 @@ pub async fn knowledge_list( ) -> Result, String> { match status { // 显式查 archived 时原样返回(含归档项) - Some(s) if s == "archived" => state.knowledge.list_by_status("archived").await.map_err(|e| e.to_string()), - Some(s) => state.knowledge.list_by_status(&s).await.map_err(|e| e.to_string()), + Some(s) if s == "archived" => state.knowledge.list_by_status("archived").await.map_err(err_str), + Some(s) => state.knowledge.list_by_status(&s).await.map_err(err_str), // 默认:列出非 archived 的全部(单查询 status != 'archived') - None => state.knowledge.list_non_archived().await.map_err(|e| e.to_string()), + None => state.knowledge.list_non_archived().await.map_err(err_str), } } @@ -93,7 +93,7 @@ pub async fn knowledge_get( .knowledge .get_by_id(&id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("知识不存在: {id}")) } @@ -108,7 +108,7 @@ pub async fn knowledge_search( .knowledge .search(&input.query, input.kind.as_deref(), limit) .await - .map_err(|e| e.to_string()) + .map_err(err_str) } /// 创建知识(始终 candidate 状态) — MCP create tool @@ -138,7 +138,7 @@ pub async fn knowledge_create( .knowledge .insert(record.clone()) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; // 生命线:手动录入产生(fire-and-forget) KnowledgeTimeline::new(&state.db) .record_created(&record.id, "manual") @@ -161,7 +161,7 @@ pub async fn knowledge_update_status( .knowledge .get_by_id(&id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("知识不存在: {id}"))?; let old_status = record.status.clone(); @@ -179,7 +179,7 @@ pub async fn knowledge_update_status( .knowledge .update_full(&updated) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; // 生命线:状态变更(fire-and-forget;to=published=审核通过,to=archived=归档) KnowledgeTimeline::new(&state.db) @@ -205,7 +205,7 @@ pub async fn knowledge_record_reuse( .knowledge .increment_reuse_count(&id) .await - .map_err(|e| e.to_string()) + .map_err(err_str) } /// 审核收件箱 — 列出 candidate(按 confidence 语义排序) @@ -217,7 +217,7 @@ pub async fn knowledge_list_candidates( .knowledge .list_by_status("candidate") .await - .map_err(|e| e.to_string()) + .map_err(err_str) } /// 归档(软删除) — UPDATE status='archived' @@ -300,13 +300,13 @@ pub async fn knowledge_get_detail( .knowledge .get_by_id(&id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("知识不存在: {id}"))?; let events = state .knowledge_events .list_by_knowledge(&id) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(KnowledgeDetailPayload { knowledge, events }) } @@ -321,7 +321,7 @@ pub async fn knowledge_update( .knowledge .get_by_id(&id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("知识不存在: {id}"))?; if let Some(v) = input.title { record.title = v; @@ -344,7 +344,7 @@ pub async fn knowledge_update( .knowledge .update_full(&record) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(record) } @@ -363,13 +363,13 @@ pub async fn knowledge_events( .knowledge_events .list_by_knowledge_type(&knowledge_id, &et, limit) .await - .map_err(|e| e.to_string()) + .map_err(err_str) } None => state .knowledge_events .list_by_knowledge(&knowledge_id) .await - .map_err(|e| e.to_string()), + .map_err(err_str), } } diff --git a/src-tauri/src/commands/knowledge_timeline.rs b/src-tauri/src/commands/knowledge_timeline.rs index a5757c4..d2afdc8 100644 --- a/src-tauri/src/commands/knowledge_timeline.rs +++ b/src-tauri/src/commands/knowledge_timeline.rs @@ -12,7 +12,7 @@ use df_storage::crud::KnowledgeEventsRepo; use df_storage::db::Database; use df_storage::models::KnowledgeEventRecord; -use super::now_millis; +use super::{err_str, now_millis}; /// 事件类型常量(生命线节点;归档复用 status_changed 的 to=archived,不单列) pub const EVENT_CREATED: &str = "created"; @@ -57,7 +57,7 @@ impl KnowledgeTimeline { KnowledgeEventsRepo::new(&self.db) .insert(record) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(()) } diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index a059deb..e2a49ed 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -17,3 +17,9 @@ pub mod workflow; pub(crate) fn now_millis() -> String { df_core::now_millis().to_string() } + +/// .map_err 错误格式化统一入口 — 当前行为零变化(仅 e.to_string()), +/// 集中收敛为后续加日志/分类/类型名等扩展留点,替代各处散落的 .map_err(|e| e.to_string())。 +pub fn err_str(e: E) -> String { + e.to_string() +} diff --git a/src-tauri/src/commands/project.rs b/src-tauri/src/commands/project.rs index fd4f1cf..16faf7f 100644 --- a/src-tauri/src/commands/project.rs +++ b/src-tauri/src/commands/project.rs @@ -12,7 +12,7 @@ use df_storage::models::ProjectRecord; use crate::state::AppState; -use super::now_millis; +use super::{err_str, now_millis}; /// 创建项目入参 #[derive(Debug, Deserialize)] @@ -32,7 +32,7 @@ pub struct CreateProjectInput { /// 列出未删除项目(过滤回收站) #[tauri::command] pub async fn list_projects(state: State<'_, AppState>) -> Result, String> { - state.projects.list_active().await.map_err(|e| e.to_string()) + state.projects.list_active().await.map_err(err_str) } /// 创建项目,返回完整记录 @@ -59,9 +59,9 @@ pub async fn create_project( let root = std::path::PathBuf::from(p); let detected = tokio::task::spawn_blocking(move || detect_stack(&root)) .await - .map_err(|e| e.to_string())? - .map_err(|e| e.to_string())?; - serde_json::to_string(&detected).map_err(|e| e.to_string())? + .map_err(err_str)? + .map_err(err_str)?; + serde_json::to_string(&detected).map_err(err_str)? } }; (Some(p.to_string()), Some(stack_json)) @@ -85,7 +85,7 @@ pub async fn create_project( .projects .insert(record.clone()) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(record) } @@ -153,14 +153,14 @@ pub async fn import_project( let stack_json = match want_stack.as_deref().map(str::trim).filter(|s| !s.is_empty()) { Some(s) => s.to_string(), None => { - let detected = detect_stack(&root).map_err(|e| e.to_string())?; - serde_json::to_string(&detected).map_err(|e| e.to_string())? + let detected = detect_stack(&root).map_err(err_str)?; + serde_json::to_string(&detected).map_err(err_str)? } }; Ok((name, description, stack_json)) }) .await - .map_err(|e| e.to_string())??; + .map_err(err_str)??; let now = now_millis(); let record = ProjectRecord { @@ -178,7 +178,7 @@ pub async fn import_project( .projects .insert(record.clone()) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(record) } @@ -192,7 +192,7 @@ pub async fn get_project( .projects .get_by_id(&id) .await - .map_err(|e| e.to_string()) + .map_err(err_str) } /// 更新项目单个字段(字段名走 df-storage 白名单校验) @@ -207,13 +207,13 @@ pub async fn update_project( .projects .update_field(&id, &field, &value) .await - .map_err(|e| e.to_string()) + .map_err(err_str) } /// 删除项目(软删 → 回收站,可恢复) #[tauri::command] pub async fn delete_project(state: State<'_, AppState>, id: String) -> Result { - state.projects.soft_delete(&id).await.map_err(|e| e.to_string()) + state.projects.soft_delete(&id).await.map_err(err_str) } /// 列出回收站项目(deleted_at IS NOT NULL) @@ -221,13 +221,13 @@ pub async fn delete_project(state: State<'_, AppState>, id: String) -> Result, ) -> Result, String> { - state.projects.list_deleted().await.map_err(|e| e.to_string()) + state.projects.list_deleted().await.map_err(err_str) } /// 恢复项目(从回收站还原,清 deleted_at) #[tauri::command] pub async fn restore_project(state: State<'_, AppState>, id: String) -> Result { - state.projects.restore(&id).await.map_err(|e| e.to_string()) + state.projects.restore(&id).await.map_err(err_str) } /// 彻底删除项目(级联物理删 branches/releases/tasks,不可恢复) @@ -237,7 +237,7 @@ pub async fn purge_project(state: State<'_, AppState>, id: String) -> Result Result, String> { let root = std::path::PathBuf::from(&path); - tokio::task::spawn_blocking(move || detect_stack(&root).map_err(|e| e.to_string())) + tokio::task::spawn_blocking(move || detect_stack(&root).map_err(err_str)) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? } /// 检查目录是否已被其他项目绑定(防重复绑定)。返回占用项目(若有)。 @@ -298,24 +298,24 @@ pub async fn relocate_project_path( let root = std::path::PathBuf::from(&new_path); let stack = tokio::task::spawn_blocking(move || detect_stack(&root)) .await - .map_err(|e| e.to_string())? - .map_err(|e| e.to_string())?; - let stack_json = serde_json::to_string(&stack).map_err(|e| e.to_string())?; + .map_err(err_str)? + .map_err(err_str)?; + let stack_json = serde_json::to_string(&stack).map_err(err_str)?; state .projects .update_field(&id, "path", &new_path) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; state .projects .update_field(&id, "stack", &stack_json) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; state .projects .get_by_id(&id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| "项目不存在".to_string()) } @@ -364,11 +364,11 @@ pub async fn scan_project_with_ai( Ok::<_, anyhow::Error>((stack, sample)) }) .await - .map_err(|e| e.to_string())? - .map_err(|e| e.to_string())?; + .map_err(err_str)? + .map_err(err_str)?; // 2. 取默认 provider(优先 is_default,否则首个) - let providers = state.ai_providers.list_all().await.map_err(|e| e.to_string())?; + let providers = state.ai_providers.list_all().await.map_err(err_str)?; let pc = providers .iter() .find(|p| p.is_default) diff --git a/src-tauri/src/commands/settings.rs b/src-tauri/src/commands/settings.rs index d9f8a9c..95f8132 100644 --- a/src-tauri/src/commands/settings.rs +++ b/src-tauri/src/commands/settings.rs @@ -9,13 +9,15 @@ use tauri::State; use crate::state::AppState; +use super::err_str; + /// 取单个 key 的值(JSON 字符串),不存在返回 None #[tauri::command] pub async fn settings_get( state: State<'_, AppState>, key: String, ) -> Result, String> { - state.settings.get(&key).await.map_err(|e| e.to_string()) + state.settings.get(&key).await.map_err(err_str) } /// 写 key/value(`INSERT OR REPLACE`),刷新 updated_at @@ -25,7 +27,7 @@ pub async fn settings_set( key: String, value: String, ) -> Result { - state.settings.set(&key, &value).await.map_err(|e| e.to_string()) + state.settings.set(&key, &value).await.map_err(err_str) } /// 取全部 key/value(前端启动时一次性拉回恢复偏好) @@ -33,7 +35,7 @@ pub async fn settings_set( pub async fn settings_get_all( state: State<'_, AppState>, ) -> Result, String> { - let rows = state.settings.get_all().await.map_err(|e| e.to_string())?; + let rows = state.settings.get_all().await.map_err(err_str)?; Ok(rows.into_iter().collect()) } @@ -43,5 +45,5 @@ pub async fn settings_delete( state: State<'_, AppState>, key: String, ) -> Result { - state.settings.delete(&key).await.map_err(|e| e.to_string()) + state.settings.delete(&key).await.map_err(err_str) } diff --git a/src-tauri/src/commands/task.rs b/src-tauri/src/commands/task.rs index 49b4d42..19a6f3c 100644 --- a/src-tauri/src/commands/task.rs +++ b/src-tauri/src/commands/task.rs @@ -8,7 +8,7 @@ use df_storage::models::TaskRecord; use crate::state::AppState; -use super::now_millis; +use super::{err_str, now_millis}; /// 创建任务入参 #[derive(Debug, Deserialize)] @@ -37,7 +37,7 @@ pub async fn list_tasks( Some(pid) => state.tasks.query("project_id", &pid).await, None => state.tasks.list_all().await, }; - result.map_err(|e| e.to_string()) + result.map_err(err_str) } /// 按 id 查任务,找不到返回 Err(供前端详情页) @@ -50,7 +50,7 @@ pub async fn get_task_by_id( .tasks .get_by_id(&id) .await - .map_err(|e| e.to_string())? + .map_err(err_str)? .ok_or_else(|| format!("任务 {} 不存在", id)) } @@ -79,7 +79,7 @@ pub async fn create_task( .tasks .insert(record.clone()) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; Ok(record) } @@ -104,11 +104,11 @@ pub async fn update_task( .tasks .update_field(&id, &field, &value) .await - .map_err(|e| e.to_string()) + .map_err(err_str) } /// 删除任务 #[tauri::command] pub async fn delete_task(state: State<'_, AppState>, id: String) -> Result { - state.tasks.delete(&id).await.map_err(|e| e.to_string()) + state.tasks.delete(&id).await.map_err(err_str) } diff --git a/src-tauri/src/commands/workflow.rs b/src-tauri/src/commands/workflow.rs index 86f362f..4d6dc46 100644 --- a/src-tauri/src/commands/workflow.rs +++ b/src-tauri/src/commands/workflow.rs @@ -16,7 +16,7 @@ use df_workflow::executor::DagExecutor; use crate::state::AppState; -use super::now_millis; +use super::{err_str, now_millis}; /// 转发到前端的事件载荷 #[derive(Debug, Clone, Serialize)] @@ -41,11 +41,11 @@ pub async fn run_workflow( config: serde_json::Value, ) -> Result { // 1. 先构建运行时 DAG,校验失败直接返回,不落库 - let runtime_dag = state.registry.build_dag(&dag).map_err(|e| e.to_string())?; + let runtime_dag = state.registry.build_dag(&dag).map_err(err_str)?; // 2. 写入执行记录(status=running) let execution_id = new_id(); - let dag_json = serde_json::to_string(&dag).map_err(|e| e.to_string())?; + let dag_json = serde_json::to_string(&dag).map_err(err_str)?; let record = WorkflowRecord { id: execution_id.clone(), name, @@ -61,7 +61,7 @@ pub async fn run_workflow( .workflows .insert(record) .await - .map_err(|e| e.to_string())?; + .map_err(err_str)?; // 3. 订阅事件总线,把事件转发给前端(收到完成/失败事件后退出) let mut rx = state.event_bus.subscribe(); @@ -173,7 +173,7 @@ pub async fn run_workflow( pub async fn list_workflow_executions( state: State<'_, AppState>, ) -> Result, String> { - state.workflows.list_all().await.map_err(|e| e.to_string()) + state.workflows.list_all().await.map_err(err_str) } /// 按 ID 查询工作流执行记录 @@ -186,7 +186,7 @@ pub async fn get_workflow_execution( .workflows .get_by_id(&id) .await - .map_err(|e| e.to_string()) + .map_err(err_str) } /// 发送人工审批响应