diff --git a/Cargo.lock b/Cargo.lock index e8a0b9e..ec1b579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2571,6 +2571,7 @@ dependencies = [ "serde_json", "serde_yaml", "sha2", + "sysinfo", "tauri", "tauri-build", "tauri-plugin-dialog", @@ -2686,6 +2687,15 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -4440,6 +4450,21 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows 0.52.0", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -5825,6 +5850,16 @@ dependencies = [ "windows-version", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.58.0" @@ -5857,6 +5892,15 @@ dependencies = [ "windows-core 0.61.2", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.58.0" diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index d9cf5e4..3816a62 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -58,6 +58,7 @@ url = "2.5.4" urlencoding = "2.1" bincode = "1.3" zip = "0.6" +sysinfo = "0.30" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["sysinfoapi"] } diff --git a/apps/desktop/src-tauri/src/data/models/workflow_execution_environment.rs b/apps/desktop/src-tauri/src/data/models/workflow_execution_environment.rs index 044f710..7d3984d 100644 --- a/apps/desktop/src-tauri/src/data/models/workflow_execution_environment.rs +++ b/apps/desktop/src-tauri/src/data/models/workflow_execution_environment.rs @@ -78,10 +78,9 @@ impl From for HealthStatus { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WorkflowExecutionEnvironment { pub id: Option, - + // 基本信息 pub name: String, - #[serde(rename = "type")] pub environment_type: EnvironmentType, pub description: Option, @@ -124,7 +123,6 @@ pub struct WorkflowExecutionEnvironment { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CreateExecutionEnvironmentRequest { pub name: String, - #[serde(rename = "type")] pub environment_type: EnvironmentType, pub description: Option, pub base_url: String, diff --git a/apps/desktop/src-tauri/src/data/repositories/workflow_execution_environment_repository.rs b/apps/desktop/src-tauri/src/data/repositories/workflow_execution_environment_repository.rs index a1532f3..7d5e7eb 100644 --- a/apps/desktop/src-tauri/src/data/repositories/workflow_execution_environment_repository.rs +++ b/apps/desktop/src-tauri/src/data/repositories/workflow_execution_environment_repository.rs @@ -259,6 +259,7 @@ impl WorkflowExecutionEnvironmentRepository { } if set_clauses.is_empty() { + info!("没有需要更新的字段,跳过更新,ID: {}", id); return Ok(()); // 没有需要更新的字段 } @@ -274,12 +275,14 @@ impl WorkflowExecutionEnvironmentRepository { set_clauses.join(", ") ); + info!("执行更新SQL: {}, 更新字段: {:?}", sql, set_clauses); + let param_refs: Vec<&dyn rusqlite::ToSql> = params.iter().map(|p| p.as_ref()).collect(); - conn.execute(&sql, param_refs.as_slice()) + let rows_affected = conn.execute(&sql, param_refs.as_slice()) .map_err(|e| anyhow!("更新工作流执行环境失败: {}", e))?; - info!("成功更新工作流执行环境,ID: {}", id); + info!("成功更新工作流执行环境,ID: {}, 影响行数: {}", id, rows_affected); Ok(()) } diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index b503c0e..e2b9d21 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -603,10 +603,12 @@ pub fn run() { commands::workflow_commands::get_execution_results, commands::workflow_commands::cancel_execution, commands::workflow_commands::get_execution_history, + commands::workflow_commands::get_system_statistics, commands::workflow_commands::get_execution_environments, commands::workflow_commands::create_execution_environment, commands::workflow_commands::update_execution_environment, - commands::workflow_commands::delete_execution_environment + commands::workflow_commands::delete_execution_environment, + commands::workflow_commands::health_check_execution_environment ]) .setup(|app| { // 初始化日志系统 diff --git a/apps/desktop/src-tauri/src/presentation/commands/workflow_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/workflow_commands.rs index d199569..7211e1c 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/workflow_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/workflow_commands.rs @@ -12,8 +12,10 @@ use crate::data::models::workflow_execution_environment::{ }; use crate::app_state::AppState; use anyhow::Result; +use serde::{Deserialize, Serialize}; use tauri::State; use tracing::info; +use chrono::Timelike; /// 获取所有工作流模板 #[tauri::command] @@ -146,19 +148,48 @@ pub async fn execute_workflow_with_mapping( .clone() }; - // 创建ComfyUI服务 - let comfyui_settings = crate::config::ComfyUISettings::default(); - let comfyui_service = crate::business::services::comfyui_service::ComfyUIService::new(comfyui_settings); + // 使用通用工作流服务(支持环境管理) + let universal_service = { + let service_guard = state.get_universal_workflow_service() + .map_err(|e| format!("获取通用工作流服务失败: {}", e))?; - // 创建工作流执行服务 - let execution_service = crate::business::services::workflow_execution_service::WorkflowExecutionService::new(comfyui_service); + service_guard.as_ref() + .ok_or_else(|| "通用工作流服务未初始化,请先配置执行环境".to_string())? + .clone() + }; + + // 转换请求格式到新的统一格式 + let unified_request = crate::business::services::universal_workflow_service::ExecuteWorkflowRequest { + workflow_identifier: format!("template_{}", request.workflow_template_id), + version: None, + input_data: request.input_data, + environment_id: request.execution_environment_id, + user_id: None, + session_id: None, + metadata: request.execution_config_override, + }; // 执行工作流 - let response = execution_service.execute_workflow(request, &template_repo, &execution_repo).await + let response = universal_service.execute_workflow(unified_request).await .map_err(|e| format!("执行工作流失败: {}", e))?; + // 转换响应格式 + let converted_response = crate::data::models::outfit_photo_generation::ExecuteWorkflowResponse { + execution_id: response.execution_id, + status: match response.status { + crate::data::models::workflow_execution_record::ExecutionStatus::Running => "running".to_string(), + crate::data::models::workflow_execution_record::ExecutionStatus::Completed => "completed".to_string(), + crate::data::models::workflow_execution_record::ExecutionStatus::Failed => "failed".to_string(), + crate::data::models::workflow_execution_record::ExecutionStatus::Cancelled => "cancelled".to_string(), + crate::data::models::workflow_execution_record::ExecutionStatus::Pending => "pending".to_string(), + crate::data::models::workflow_execution_record::ExecutionStatus::Timeout => "timeout".to_string(), + }, + comfyui_prompt_id: response.comfyui_prompt_id, + error_message: response.error_message, + }; + info!("工作流执行成功,执行记录ID: {}", response.execution_id); - Ok(response) + Ok(converted_response) } @@ -275,6 +306,16 @@ pub async fn cancel_execution( Ok(()) } +/// 执行历史分页响应 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ExecutionHistoryResponse { + pub records: Vec, + pub total_count: u32, + pub page: u32, + pub page_size: u32, + pub total_pages: u32, +} + /// 获取执行历史 #[tauri::command] pub async fn get_execution_history( @@ -282,7 +323,7 @@ pub async fn get_execution_history( limit: Option, offset: Option, state: State<'_, AppState>, -) -> Result, String> { +) -> Result { info!("获取执行历史"); // 获取工作流执行记录仓库 @@ -293,19 +334,35 @@ pub async fn get_execution_history( .ok_or_else(|| "工作流执行记录仓库未初始化".to_string())?; // 查询执行历史 - let records = if let (Some(limit), Some(offset)) = (limit, offset) { + let (records, total_count, page, page_size) = if let (Some(limit), Some(offset)) = (limit, offset) { let page = (offset / limit.max(1)) as u32; let page_size = limit as u32; - let (records, _total) = repo.find_with_pagination(filter.as_ref(), page, page_size) + let (records, total) = repo.find_with_pagination(filter.as_ref(), page, page_size) .map_err(|e| format!("分页查询执行历史失败: {}", e))?; - records + (records, total, page, page_size) } else { - repo.find_all(filter.as_ref()) - .map_err(|e| format!("查询执行历史失败: {}", e))? + // 如果没有分页参数,返回所有记录 + let records = repo.find_all(filter.as_ref()) + .map_err(|e| format!("查询执行历史失败: {}", e))?; + let total = records.len() as u32; + (records, total, 0, total) }; - info!("成功获取 {} 条执行历史记录", records.len()); - Ok(records) + let total_pages = if page_size > 0 { + (total_count + page_size - 1) / page_size + } else { + 1 + }; + + info!("成功获取 {} 条执行历史记录,总数: {}", records.len(), total_count); + + Ok(ExecutionHistoryResponse { + records, + total_count, + page, + page_size, + total_pages, + }) } /// 获取执行环境列表 @@ -366,7 +423,7 @@ pub async fn update_execution_environment( request: UpdateExecutionEnvironmentRequest, state: State<'_, AppState>, ) -> Result { - info!("更新执行环境: {}", id); + info!("更新执行环境: {}, 请求数据: {:?}", id, request); // 获取工作流执行环境仓库 let repo_guard = state.get_workflow_execution_environment_repository() @@ -375,6 +432,13 @@ pub async fn update_execution_environment( let repo = repo_guard.as_ref() .ok_or_else(|| "工作流执行环境仓库未初始化".to_string())?; + // 查询更新前的环境 + let old_environment = repo.find_by_id(id) + .map_err(|e| format!("查询更新前的执行环境失败: {}", e))? + .ok_or_else(|| format!("执行环境不存在,ID: {}", id))?; + + info!("更新前的环境数据: {:?}", old_environment); + // 更新执行环境 repo.update(id, &request) .map_err(|e| format!("更新执行环境失败: {}", e))?; @@ -384,7 +448,7 @@ pub async fn update_execution_environment( .map_err(|e| format!("查询更新后的执行环境失败: {}", e))? .ok_or_else(|| "更新后的执行环境未找到".to_string())?; - info!("成功更新执行环境,ID: {}", id); + info!("成功更新执行环境,ID: {}, 更新后数据: {:?}", id, environment); Ok(environment) } @@ -411,6 +475,43 @@ pub async fn delete_execution_environment( Ok(()) } +/// 健康检查执行环境 +#[tauri::command] +pub async fn health_check_execution_environment( + id: i64, + state: State<'_, AppState>, +) -> Result { + info!("健康检查执行环境: {}", id); + + // 获取工作流执行环境仓库并克隆以避免所有权问题 + let repo = { + let repo_guard = state.get_workflow_execution_environment_repository() + .map_err(|e| format!("获取工作流执行环境仓库失败: {}", e))?; + + repo_guard.as_ref() + .ok_or_else(|| "工作流执行环境仓库未初始化".to_string())? + .clone() + }; + + // 获取环境信息 + let _environment = repo.find_by_id(id) + .map_err(|e| format!("查询执行环境失败: {}", e))? + .ok_or_else(|| format!("执行环境不存在,ID: {}", id))?; + + // 执行健康检查 - 使用公共方法 + let health_results = repo.health_check_all().await + .map_err(|e| format!("执行健康检查失败: {}", e))?; + + // 查找当前环境的健康状态 + let health_status = health_results.iter() + .find(|(env_id, _)| *env_id == id) + .map(|(_, status)| status.clone()) + .unwrap_or(crate::data::models::workflow_execution_environment::HealthStatus::Unknown); + + info!("执行环境健康检查完成,ID: {}, 状态: {:?}", id, health_status); + Ok(health_status) +} + /// 导出工作流模板 #[tauri::command] pub async fn export_workflow_template( @@ -634,6 +735,157 @@ pub async fn delete_execution_result_files( }) } +/// 系统统计信息响应 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SystemStatistics { + pub execution_stats: ExecutionStats, + pub environment_stats: EnvironmentStats, + pub system_resources: SystemResources, + pub performance_metrics: PerformanceMetrics, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ExecutionStats { + pub total_executions_today: u32, + pub successful_executions_today: u32, + pub failed_executions_today: u32, + pub average_execution_time_seconds: f64, + pub current_queue_size: u32, + pub active_executions: u32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EnvironmentStats { + pub total_environments: u32, + pub healthy_environments: u32, + pub unhealthy_environments: u32, + pub average_response_time_ms: f64, + pub total_capacity: u32, + pub used_capacity: u32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SystemResources { + pub cpu_usage_percent: f64, + pub memory_usage_percent: f64, + pub disk_usage_percent: f64, + pub network_status: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PerformanceMetrics { + pub requests_per_minute: u32, + pub average_response_time_ms: f64, + pub error_rate_percent: f64, + pub uptime_hours: f64, +} + +/// 获取系统统计信息 +#[tauri::command] +pub async fn get_system_statistics( + state: State<'_, AppState>, +) -> Result { + info!("获取系统统计信息"); + + // 获取工作流执行记录仓库 + let repo_guard = state.get_workflow_execution_record_repository() + .map_err(|e| format!("获取工作流执行记录仓库失败: {}", e))?; + + let repo = repo_guard.as_ref() + .ok_or_else(|| "工作流执行记录仓库未初始化".to_string())?; + + // 获取环境仓库 + let env_repo_guard = state.get_workflow_execution_environment_repository() + .map_err(|e| format!("获取执行环境仓库失败: {}", e))?; + + let env_repo = env_repo_guard.as_ref() + .ok_or_else(|| "执行环境仓库未初始化".to_string())?; + + // 获取今天的执行统计 + let today = chrono::Utc::now().date_naive(); + let today_filter = crate::data::models::workflow_execution_record::ExecutionRecordFilter { + date_from: Some(today.and_hms_opt(0, 0, 0).unwrap().and_utc()), + date_to: Some(today.and_hms_opt(23, 59, 59).unwrap().and_utc()), + ..Default::default() + }; + + let execution_stats_result = repo.get_execution_statistics(Some(&today_filter)) + .map_err(|e| format!("获取执行统计失败: {}", e))?; + + // 获取环境统计 + let environments = env_repo.find_all(None) + .map_err(|e| format!("获取环境列表失败: {}", e))?; + + let healthy_envs = environments.iter() + .filter(|env| env.health_status == crate::data::models::workflow_execution_environment::HealthStatus::Healthy) + .count() as u32; + + let total_capacity: u32 = environments.iter() + .map(|env| env.max_concurrent_jobs as u32) + .sum(); + + // 获取当前活跃执行 + let active_filter = crate::data::models::workflow_execution_record::ExecutionRecordFilter { + status: Some(crate::data::models::workflow_execution_record::ExecutionStatus::Running), + ..Default::default() + }; + + let active_executions = repo.find_all(Some(&active_filter)) + .map_err(|e| format!("获取活跃执行失败: {}", e))?; + + // 获取队列中的执行 + let pending_filter = crate::data::models::workflow_execution_record::ExecutionRecordFilter { + status: Some(crate::data::models::workflow_execution_record::ExecutionStatus::Pending), + ..Default::default() + }; + + let pending_executions = repo.find_all(Some(&pending_filter)) + .map_err(|e| format!("获取队列执行失败: {}", e))?; + + // 计算系统资源使用情况(简化实现) + let cpu_usage = get_cpu_usage().unwrap_or(0.0); + let memory_usage = get_memory_usage().unwrap_or(0.0); + let disk_usage = get_disk_usage().unwrap_or(0.0); + + Ok(SystemStatistics { + execution_stats: ExecutionStats { + total_executions_today: execution_stats_result.total_executions, + successful_executions_today: execution_stats_result.completed_executions, + failed_executions_today: execution_stats_result.failed_executions, + average_execution_time_seconds: execution_stats_result.average_duration_seconds.unwrap_or(0.0), + current_queue_size: pending_executions.len() as u32, + active_executions: active_executions.len() as u32, + }, + environment_stats: EnvironmentStats { + total_environments: environments.len() as u32, + healthy_environments: healthy_envs, + unhealthy_environments: environments.len() as u32 - healthy_envs, + average_response_time_ms: environments.iter() + .filter_map(|env| env.average_response_time_ms) + .map(|t| t as f64) + .sum::() / environments.len().max(1) as f64, + total_capacity, + used_capacity: active_executions.len() as u32, + }, + system_resources: SystemResources { + cpu_usage_percent: cpu_usage, + memory_usage_percent: memory_usage, + disk_usage_percent: disk_usage, + network_status: "connected".to_string(), + }, + performance_metrics: PerformanceMetrics { + requests_per_minute: calculate_requests_per_minute(&execution_stats_result), + average_response_time_ms: execution_stats_result.average_duration_seconds.unwrap_or(0.0) * 1000.0, + error_rate_percent: if execution_stats_result.total_executions > 0 { + (execution_stats_result.failed_executions as f64 / execution_stats_result.total_executions as f64) * 100.0 + } else { + 0.0 + }, + uptime_hours: get_uptime_hours(), + }, + }) +} + /// 获取存储统计信息 #[tauri::command] pub async fn get_storage_statistics( @@ -685,3 +937,81 @@ pub async fn batch_cleanup_execution_results( errors: Vec::new(), }) } + +// 辅助函数:获取CPU使用率 +fn get_cpu_usage() -> Option { + use sysinfo::System; + + let mut system = System::new_all(); + system.refresh_cpu(); + + // 等待一小段时间以获得准确的CPU使用率 + std::thread::sleep(std::time::Duration::from_millis(200)); + system.refresh_cpu(); + + let cpu_usage: f64 = system.cpus().iter() + .map(|cpu| cpu.cpu_usage() as f64) + .sum::() / system.cpus().len() as f64; + + Some(cpu_usage) +} + +// 辅助函数:获取内存使用率 +fn get_memory_usage() -> Option { + use sysinfo::System; + + let mut system = System::new_all(); + system.refresh_memory(); + + let total_memory = system.total_memory(); + let used_memory = system.used_memory(); + + if total_memory > 0 { + Some((used_memory as f64 / total_memory as f64) * 100.0) + } else { + None + } +} + +// 辅助函数:获取磁盘使用率 +fn get_disk_usage() -> Option { + use sysinfo::Disks; + + let disks = Disks::new_with_refreshed_list(); + + // 获取主磁盘的使用率 + if let Some(disk) = disks.iter().next() { + let total_space = disk.total_space(); + let available_space = disk.available_space(); + let used_space = total_space - available_space; + + if total_space > 0 { + Some((used_space as f64 / total_space as f64) * 100.0) + } else { + None + } + } else { + None + } +} + +// 辅助函数:计算每分钟请求数 +fn calculate_requests_per_minute(stats: &crate::data::repositories::workflow_execution_record_repository::ExecutionStatistics) -> u32 { + // 基于今天的总执行数估算每分钟请求数 + let total_today = stats.total_executions; + let hours_passed = chrono::Utc::now().hour() + 1; // 至少1小时 + let minutes_passed = hours_passed * 60; + + if minutes_passed > 0 { + (total_today * 60) / minutes_passed + } else { + 0 + } +} + +// 辅助函数:获取系统运行时间 +fn get_uptime_hours() -> f64 { + use sysinfo::System; + + System::uptime() as f64 / 3600.0 // 转换为小时 +} diff --git a/apps/desktop/src/components/workflow/EnvironmentConfigurator.tsx b/apps/desktop/src/components/workflow/EnvironmentConfigurator.tsx index dab24b7..4a4b639 100644 --- a/apps/desktop/src/components/workflow/EnvironmentConfigurator.tsx +++ b/apps/desktop/src/components/workflow/EnvironmentConfigurator.tsx @@ -146,6 +146,8 @@ export const EnvironmentConfigurator: React.FC = ( // 初始化表单数据 useEffect(() => { if (environment) { + console.log('EnvironmentConfigurator 接收到环境数据:', environment); + console.log('环境类型:', environment.environment_type, '类型:', typeof environment.environment_type); setFormData({ name: environment.name, environment_type: environment.environment_type, @@ -159,6 +161,7 @@ export const EnvironmentConfigurator: React.FC = ( max_execution_time_seconds: environment.max_execution_time_seconds, tags: environment.tags || [] }); + console.log('设置的表单数据 environment_type:', environment.environment_type); } else { // 重置为默认值 setFormData({ diff --git a/apps/desktop/src/components/workflow/ExecutionHistoryList.tsx b/apps/desktop/src/components/workflow/ExecutionHistoryList.tsx index 5e261bb..eec3898 100644 --- a/apps/desktop/src/components/workflow/ExecutionHistoryList.tsx +++ b/apps/desktop/src/components/workflow/ExecutionHistoryList.tsx @@ -53,6 +53,15 @@ interface WorkflowExecutionRecord { updated_at: string; } +// 执行历史分页响应接口 +interface ExecutionHistoryResponse { + records: WorkflowExecutionRecord[]; + total_count: number; + page: number; + page_size: number; + total_pages: number; +} + // 筛选器接口 interface ExecutionRecordFilter { workflow_template_id?: number; @@ -136,16 +145,14 @@ export const ExecutionHistoryList: React.FC = ({ filterConditions.date_from = dateFrom.toISOString(); } - const result = await invoke('get_execution_history', { + const result = await invoke('get_execution_history', { filter: filterConditions, limit: pageSize, offset: (currentPage - 1) * pageSize }); - setRecords(result); - - // TODO: 获取总数来计算总页数 - setTotalPages(Math.ceil(result.length / pageSize)); + setRecords(result.records); + setTotalPages(result.total_pages); } catch (err) { console.error('加载执行历史失败:', err); diff --git a/apps/desktop/src/components/workflow/MonitoringDashboard.tsx b/apps/desktop/src/components/workflow/MonitoringDashboard.tsx index b5585b8..b8d384c 100644 --- a/apps/desktop/src/components/workflow/MonitoringDashboard.tsx +++ b/apps/desktop/src/components/workflow/MonitoringDashboard.tsx @@ -40,6 +40,15 @@ interface WorkflowExecutionRecord { updated_at: string; } +// 执行历史分页响应接口 +interface ExecutionHistoryResponse { + records: WorkflowExecutionRecord[]; + total_count: number; + page: number; + page_size: number; + total_pages: number; +} + // 系统统计信息接口 interface SystemStatistics { execution_stats: { @@ -128,24 +137,24 @@ export const MonitoringDashboard: React.FC = ({ // 加载活跃执行 const loadActiveExecutions = useCallback(async () => { try { - const result = await invoke('get_execution_history', { + const result = await invoke('get_execution_history', { filter: { status: 'running' // 只获取正在执行的 }, limit: 50, offset: 0 }); - + // 同时获取等待中的执行 - const pendingResult = await invoke('get_execution_history', { + const pendingResult = await invoke('get_execution_history', { filter: { status: 'pending' }, limit: 50, offset: 0 }); - - setActiveExecutions([...result, ...pendingResult]); + + setActiveExecutions([...result.records, ...pendingResult.records]); } catch (err) { console.error('加载活跃执行失败:', err); // 使用模拟数据 @@ -156,45 +165,44 @@ export const MonitoringDashboard: React.FC = ({ // 加载系统统计 const loadSystemStats = useCallback(async () => { try { - // TODO: 实现实际的系统统计API - // const result = await invoke('get_system_statistics'); - // setSystemStats(result); - - // 暂时使用模拟数据 - const mockStats: SystemStatistics = { + const result = await invoke('get_system_statistics'); + setSystemStats(result); + } catch (err) { + console.error('加载系统统计失败:', err); + + // 如果API调用失败,使用基础的模拟数据作为后备 + const fallbackStats: SystemStatistics = { execution_stats: { - total_executions_today: 156, - successful_executions_today: 142, - failed_executions_today: 14, - average_execution_time_seconds: 45.6, + total_executions_today: 0, + successful_executions_today: 0, + failed_executions_today: 0, + average_execution_time_seconds: 0, current_queue_size: activeExecutions.filter(e => e.status === 'pending').length, active_executions: activeExecutions.filter(e => e.status === 'running').length }, environment_stats: { - total_environments: 3, - healthy_environments: 2, - unhealthy_environments: 1, - average_response_time_ms: 1250, - total_capacity: 16, + total_environments: 0, + healthy_environments: 0, + unhealthy_environments: 0, + average_response_time_ms: 0, + total_capacity: 0, used_capacity: activeExecutions.filter(e => e.status === 'running').length }, system_resources: { - cpu_usage_percent: Math.random() * 30 + 20, // 20-50% - memory_usage_percent: Math.random() * 20 + 40, // 40-60% - disk_usage_percent: Math.random() * 10 + 65, // 65-75% - network_status: 'connected' + cpu_usage_percent: 0, + memory_usage_percent: 0, + disk_usage_percent: 0, + network_status: 'disconnected' }, performance_metrics: { - requests_per_minute: 24, - average_response_time_ms: 1250, - error_rate_percent: 8.97, - uptime_hours: 72.5 + requests_per_minute: 0, + average_response_time_ms: 0, + error_rate_percent: 0, + uptime_hours: 0 } }; - - setSystemStats(mockStats); - } catch (err) { - console.error('加载系统统计失败:', err); + + setSystemStats(fallbackStats); } }, [activeExecutions]); diff --git a/apps/desktop/src/pages/WorkflowPage.tsx b/apps/desktop/src/pages/WorkflowPage.tsx index 06c4df3..6872f7f 100644 --- a/apps/desktop/src/pages/WorkflowPage.tsx +++ b/apps/desktop/src/pages/WorkflowPage.tsx @@ -144,50 +144,108 @@ export const WorkflowPage: React.FC = () => { }; const handleEditEnvironment = (env: WorkflowExecutionEnvironment) => { + console.log('编辑环境,接收到的数据:', env); + console.log('环境类型:', env.environment_type, '类型:', typeof env.environment_type); setSelectedEnvironment(env); setIsEnvironmentConfiguratorOpen(true); }; const handleDeleteEnvironment = async (envId: number) => { try { - // TODO: 实现删除逻辑 + // 确认删除 + const confirmed = window.confirm('确定要删除这个执行环境吗?此操作不可撤销。'); + if (!confirmed) return; + console.log('删除执行环境:', envId); + await invoke('delete_execution_environment', { id: envId }); + + console.log('环境删除成功'); + // 刷新页面以显示最新数据 + window.location.reload(); } catch (error) { console.error('删除环境失败:', error); + alert(`删除环境失败: ${error}`); } }; const handleHealthCheckEnvironment = async (envId: number) => { try { - // TODO: 实现健康检查逻辑 console.log('健康检查环境:', envId); + // 调用健康检查命令 + const healthStatus = await invoke('health_check_execution_environment', { id: envId }); + console.log('健康检查完成,状态:', healthStatus); + + // 刷新页面以显示最新的健康状态 + window.location.reload(); } catch (error) { console.error('健康检查失败:', error); + alert(`健康检查失败: ${error}`); } }; const handleToggleEnvironmentActive = async (envId: number, isActive: boolean) => { try { - // TODO: 实现激活状态切换逻辑 console.log('切换环境激活状态:', envId, isActive); + const updatedEnvironment = await invoke('update_execution_environment', { + id: envId, + request: { + is_active: isActive + } + }); + console.log('激活状态切换成功:', updatedEnvironment); + + // 刷新页面以显示最新状态 + window.location.reload(); } catch (error) { console.error('切换激活状态失败:', error); + alert(`切换激活状态失败: ${error}`); } }; const handleSaveEnvironment = async (envData: CreateEnvironmentRequest) => { try { if (selectedEnvironment) { - // TODO: 实现更新逻辑 + // 更新现有环境 - 简化逻辑,直接传递所有字段 console.log('更新环境:', envData); + const updateRequest = { + name: envData.name, + description: envData.description, + base_url: envData.base_url, + api_key: envData.api_key, + connection_config_json: envData.connection_config_json, + supported_workflow_types: envData.supported_workflow_types, + max_concurrent_jobs: envData.max_concurrent_jobs, + priority: envData.priority, + max_memory_mb: envData.max_memory_mb, + max_execution_time_seconds: envData.max_execution_time_seconds, + metadata_json: envData.metadata_json, + tags: envData.tags, + }; + + console.log('发送更新请求:', updateRequest); + const updatedEnvironment = await invoke('update_execution_environment', { + id: selectedEnvironment.id, + request: updateRequest + }); + console.log('更新成功,返回数据:', updatedEnvironment); } else { - // TODO: 实现创建逻辑 + // 创建新环境 console.log('创建环境:', envData); + await invoke('create_execution_environment', { request: envData }); } + setIsEnvironmentConfiguratorOpen(false); setSelectedEnvironment(null); + + // TODO: 添加成功提示 + console.log('环境保存成功'); + + // 刷新页面以显示最新数据(临时方案) + window.location.reload(); } catch (error) { console.error('保存环境失败:', error); + // TODO: 添加错误提示UI + alert(`保存环境失败: ${error}`); } }; diff --git a/apps/desktop/src/types/workflow.ts b/apps/desktop/src/types/workflow.ts index 374ad3c..0b7edbd 100644 --- a/apps/desktop/src/types/workflow.ts +++ b/apps/desktop/src/types/workflow.ts @@ -161,6 +161,15 @@ export interface WorkflowExecutionRecord { updated_at: string; } +// 执行历史分页响应接口 +export interface ExecutionHistoryResponse { + records: WorkflowExecutionRecord[]; + total_count: number; + page: number; + page_size: number; + total_pages: number; +} + // 执行环境接口 export interface WorkflowExecutionEnvironment { id: number;