修复执行环境编辑更新功能和编译错误

主要修复内容:
1. 修复执行环境编辑更新功能
   - 简化前端更新逻辑,直接传递所有字段而非复杂的条件判断
   - 添加详细的调试日志用于问题排查
   - 修复数据刷新机制,保存后自动刷新页面
   - 完善错误处理和用户提示

2. 修复编译错误
   - 修复 sysinfo 0.30 API 变化问题
   - 修复 ExecutionStatistics 字段不匹配问题
   - 添加 chrono::Timelike trait 导入
   - 修复 MutexGuard 所有权问题

3. 完善环境管理功能
   - 实现健康检查命令 health_check_execution_environment
   - 完善删除、激活状态切换等功能
   - 添加确认对话框和成功提示

4. 修复数据序列化问题
   - 移除 environment_type 字段的 serde rename 属性
   - 确保前后端数据格式一致

技术细节:
- 后端使用 UniversalWorkflowService 支持多环境管理
- 前端使用 window.location.reload() 临时解决数据刷新问题
- 添加详细日志便于调试和问题排查
This commit is contained in:
imeepos
2025-08-08 11:30:15 +08:00
parent ef48e1907f
commit d5ef15a6d1
11 changed files with 528 additions and 65 deletions

44
Cargo.lock generated
View File

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

View File

@@ -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"] }

View File

@@ -81,7 +81,6 @@ pub struct WorkflowExecutionEnvironment {
// 基本信息
pub name: String,
#[serde(rename = "type")]
pub environment_type: EnvironmentType,
pub description: Option<String>,
@@ -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<String>,
pub base_url: String,

View File

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

View File

@@ -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| {
// 初始化日志系统

View File

@@ -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<WorkflowExecutionRecord>,
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<i32>,
offset: Option<i32>,
state: State<'_, AppState>,
) -> Result<Vec<WorkflowExecutionRecord>, String> {
) -> Result<ExecutionHistoryResponse, String> {
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<WorkflowExecutionEnvironment, String> {
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<crate::data::models::workflow_execution_environment::HealthStatus, String> {
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<SystemStatistics, String> {
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::<f64>() / 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<f64> {
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::<f64>() / system.cpus().len() as f64;
Some(cpu_usage)
}
// 辅助函数:获取内存使用率
fn get_memory_usage() -> Option<f64> {
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<f64> {
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 // 转换为小时
}

View File

@@ -146,6 +146,8 @@ export const EnvironmentConfigurator: React.FC<EnvironmentConfiguratorProps> = (
// 初始化表单数据
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<EnvironmentConfiguratorProps> = (
max_execution_time_seconds: environment.max_execution_time_seconds,
tags: environment.tags || []
});
console.log('设置的表单数据 environment_type:', environment.environment_type);
} else {
// 重置为默认值
setFormData({

View File

@@ -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<ExecutionHistoryListProps> = ({
filterConditions.date_from = dateFrom.toISOString();
}
const result = await invoke<WorkflowExecutionRecord[]>('get_execution_history', {
const result = await invoke<ExecutionHistoryResponse>('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);

View File

@@ -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,7 +137,7 @@ export const MonitoringDashboard: React.FC<MonitoringDashboardProps> = ({
// 加载活跃执行
const loadActiveExecutions = useCallback(async () => {
try {
const result = await invoke<WorkflowExecutionRecord[]>('get_execution_history', {
const result = await invoke<ExecutionHistoryResponse>('get_execution_history', {
filter: {
status: 'running' // 只获取正在执行的
},
@@ -137,7 +146,7 @@ export const MonitoringDashboard: React.FC<MonitoringDashboardProps> = ({
});
// 同时获取等待中的执行
const pendingResult = await invoke<WorkflowExecutionRecord[]>('get_execution_history', {
const pendingResult = await invoke<ExecutionHistoryResponse>('get_execution_history', {
filter: {
status: 'pending'
},
@@ -145,7 +154,7 @@ export const MonitoringDashboard: React.FC<MonitoringDashboardProps> = ({
offset: 0
});
setActiveExecutions([...result, ...pendingResult]);
setActiveExecutions([...result.records, ...pendingResult.records]);
} catch (err) {
console.error('加载活跃执行失败:', err);
// 使用模拟数据
@@ -156,45 +165,44 @@ export const MonitoringDashboard: React.FC<MonitoringDashboardProps> = ({
// 加载系统统计
const loadSystemStats = useCallback(async () => {
try {
// TODO: 实现实际的系统统计API
// const result = await invoke<SystemStatistics>('get_system_statistics');
// setSystemStats(result);
const result = await invoke<SystemStatistics>('get_system_statistics');
setSystemStats(result);
} catch (err) {
console.error('加载系统统计失败:', err);
// 暂时使用模拟数据
const mockStats: SystemStatistics = {
// 如果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]);

View File

@@ -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}`);
}
};

View File

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