Files
mixvideo-v2/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_commands.rs
imeepos 2075a731d2 fix: 修复ComfyUI队列API响应格式解析问题
- 修复WebSocket错误消息解析失败问题,支持更多错误字段格式
- 改进执行错误处理,收到错误时立即中断而不是等待超时
- 修复ComfyUI队列API响应格式不匹配问题,支持数组格式的队列数据
- 添加灵活的队列项解析逻辑,支持整数和字符串类型的prompt_id
- 在EventEmitter中添加错误存储功能,支持实时错误状态检查
- 更新所有相关的队列状态转换代码

解决的问题:
1. WebSocket消息解析错误:missing field 'message'
2. 执行错误后仍等待超时的问题
3. 队列API响应解析错误:invalid type integer/string expected
4. 批量处理中错误统计不准确的问题
2025-08-11 11:09:27 +08:00

961 lines
33 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! ComfyUI V2 命令层
//! 基于新的 SDK 架构重写的 Tauri 命令
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tauri::State;
use tracing::{info, warn, error, debug};
use crate::app_state::AppState;
use crate::business::services::{
comfyui_v2_service::{ComfyUIV2Service, ComfyUIV2Config, ConnectionStatus, SystemInfo, QueueStatus},
template_engine::{TemplateEngine, CacheStats},
execution_engine::{ExecutionEngine, ExecutionResult, ExecutionConfig, ExecutionStats},
realtime_monitor_v2::{RealtimeMonitorV2, RealtimeEventV2, MonitorStats},
config_manager::{ConfigManager, ConfigStats, ConfigHealthStatus},
error_handler::{ComfyUIError, ErrorHandleResult, ErrorStats},
service_manager::ServiceManager,
queue_manager::QueueManager,
};
use crate::data::models::comfyui::{
WorkflowModel, TemplateModel, ExecutionModel, ExecutionStatus, ComfyUIConfig,
ParameterValues, ValidationResult,
};
use crate::data::repositories::comfyui_repository::ComfyUIRepository;
use comfyui_sdk::types::{ComfyUIWorkflow, ComfyUINode};
use crate::business::services::comfyui_manager::ComfyUIManager;
// ==================== 辅助函数 ====================
/// 将前端的 ComfyUIV2Config 转换为后端的 ComfyUIConfig
fn convert_v2_config_to_config(v2_config: ComfyUIV2Config) -> ComfyUIConfig {
// 处理超时时间:前端传递的是毫秒,后端需要秒
let timeout_seconds = match v2_config.timeout {
Some(timeout_ms) => timeout_ms / 1000,
None => 300, // 默认5分钟
};
// 处理重试延迟:从前端配置中提取或使用默认值
let retry_delay_ms = v2_config.retry_delay_ms.unwrap_or(1000);
// 处理WebSocket启用状态
let enable_websocket = v2_config.enable_websocket.unwrap_or(true);
// 处理自定义头部
let custom_headers = v2_config.custom_headers;
ComfyUIConfig {
base_url: v2_config.base_url,
timeout_seconds,
retry_attempts: v2_config.retry_attempts.unwrap_or(3),
retry_delay_ms,
enable_websocket,
enable_cache: v2_config.enable_cache.unwrap_or(true),
max_concurrency: v2_config.max_concurrency.unwrap_or(4),
custom_headers,
}
}
// ==================== 请求类型定义 ====================
/// 创建工作流请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateWorkflowRequest {
pub name: String,
pub description: Option<String>,
pub workflow_json: serde_json::Value,
pub tags: Option<Vec<String>>,
}
/// 更新工作流请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateWorkflowRequest {
pub id: String,
pub name: Option<String>,
pub description: Option<String>,
pub workflow_json: Option<serde_json::Value>,
pub tags: Option<Vec<String>>,
}
/// 批量删除工作流请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchDeleteWorkflowsRequest {
pub workflow_ids: Vec<String>,
}
/// 导出工作流请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportWorkflowRequest {
pub workflow_ids: Vec<String>,
pub include_metadata: Option<bool>,
}
/// 导入工作流请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportWorkflowRequest {
pub workflow_data: serde_json::Value,
pub overwrite_existing: Option<bool>,
}
/// 创建模板请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateTemplateRequest {
pub name: String,
pub description: Option<String>,
pub template_json: serde_json::Value,
pub parameters: Option<serde_json::Value>,
pub tags: Option<Vec<String>>,
}
/// 执行模板请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecuteTemplateRequest {
pub template_id: String,
pub parameters: serde_json::Value,
pub priority: Option<u32>,
}
// ==================== 响应类型定义 ====================
/// 连接状态响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectionStatusResponse {
pub connected: bool,
pub status: String,
pub base_url: String,
pub last_health_check: Option<String>,
pub timeout_seconds: u64,
pub retry_attempts: u32,
}
/// 系统信息响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemInfoResponse {
pub os: String,
pub python_version: String,
pub embedded_python: bool,
pub devices: Vec<DeviceInfoResponse>,
}
/// 设备信息响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceInfoResponse {
pub name: String,
pub device_type: String,
pub vram_total: u64,
pub vram_free: u64,
pub torch_vram_total: u64,
pub torch_vram_free: u64,
}
/// 队列状态响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueueStatusResponse {
pub running_count: u32,
pub pending_count: u32,
pub running_tasks: Vec<QueueTaskResponse>,
pub pending_tasks: Vec<QueueTaskResponse>,
}
/// 队列任务响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueueTaskResponse {
pub prompt_id: String,
pub number: u32,
pub status: String,
}
/// 健康检查响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheckResponse {
pub healthy: bool,
pub connection_status: String,
pub websocket_connected: bool,
pub last_check_time: Option<String>,
pub error_message: Option<String>,
}
// ==================== 基础连接管理命令 ====================
/// 连接到 ComfyUI 服务
/// 直接接收config参数匹配前端传递的格式
#[tauri::command]
pub async fn comfyui_v2_connect(
config: ComfyUIV2Config,
state: State<'_, AppState>,
) -> Result<ConnectionStatusResponse, String> {
info!("Command: comfyui_v2_connect - 配置: {:?}", config);
// 获取配置管理器
let config_manager = get_config_manager(&state).await?;
// 转换前端配置为后端配置
let converted_config = convert_v2_config_to_config(config);
info!("转换后的配置: base_url={}, timeout_seconds={}",
converted_config.base_url, converted_config.timeout_seconds);
// 先更新配置到配置管理器
config_manager.update_comfyui_config(converted_config.clone()).await
.map_err(|e| {
error!("更新配置失败: {}", e);
format!("更新配置失败: {}", e)
})?;
// 获取或初始化服务管理器
let service_manager = state.get_comfyui_manager().await
.map_err(|e| {
error!("获取服务管理器失败: {}", e);
format!("获取服务管理器失败: {}", e)
})?;
// 通过服务管理器重新配置并连接
service_manager.reconfigure(converted_config).await
.map_err(|e| {
error!("重新配置服务失败: {}", e);
format!("重新配置服务失败: {}", e)
})?;
// 获取 ComfyUI 管理器并尝试连接
let comfyui_manager = service_manager.get_comfyui_manager().await
.map_err(|e| {
error!("获取ComfyUI管理器失败: {}", e);
format!("获取ComfyUI管理器失败: {}", e)
})?;
// 尝试连接
match comfyui_manager.connect().await {
Ok(_) => {
let stats = comfyui_manager.get_connection_stats().await;
info!("ComfyUI 连接成功");
Ok(ConnectionStatusResponse {
connected: true,
status: stats.status.to_string(),
base_url: stats.base_url,
last_health_check: stats.last_health_check.map(|t| format!("{:?}", t)),
timeout_seconds: stats.timeout_seconds,
retry_attempts: stats.retry_attempts,
})
}
Err(e) => {
error!("ComfyUI 连接失败: {}", e);
Err(format!("连接失败: {}", e))
}
}
}
/// 断开 ComfyUI 连接
#[tauri::command]
pub async fn comfyui_v2_disconnect(
state: State<'_, AppState>,
) -> Result<String, String> {
info!("Command: comfyui_v2_disconnect");
// 获取服务管理器
let service_manager = state.get_comfyui_manager().await
.map_err(|e| {
error!("获取服务管理器失败: {}", e);
format!("获取服务管理器失败: {}", e)
})?;
// 获取 ComfyUI 管理器并断开连接
match service_manager.get_comfyui_manager().await {
Ok(comfyui_manager) => {
match comfyui_manager.disconnect().await {
Ok(_) => {
info!("ComfyUI 连接已断开");
Ok("连接已断开".to_string())
}
Err(e) => {
error!("断开连接失败: {}", e);
Err(format!("断开连接失败: {}", e))
}
}
}
Err(e) => {
error!("获取ComfyUI管理器失败: {}", e);
Err(format!("获取ComfyUI管理器失败: {}", e))
}
}
}
/// 检查连接状态
#[tauri::command]
pub async fn comfyui_v2_get_connection_status(
state: State<'_, AppState>,
) -> Result<ConnectionStatusResponse, String> {
info!("Command: comfyui_v2_get_connection_status");
// 获取服务管理器
let service_manager = match state.get_comfyui_manager().await {
Ok(manager) => manager,
Err(e) => {
error!("获取服务管理器失败: {}", e);
// 如果服务管理器未初始化,返回默认的未连接状态
return Ok(ConnectionStatusResponse {
connected: false,
status: "未初始化".to_string(),
base_url: "http://localhost:8188".to_string(),
last_health_check: None,
timeout_seconds: 300,
retry_attempts: 3,
});
}
};
// 获取 ComfyUI 管理器并检查连接状态
match service_manager.get_comfyui_manager().await {
Ok(comfyui_manager) => {
let stats = comfyui_manager.get_connection_stats().await;
Ok(ConnectionStatusResponse {
connected: comfyui_manager.is_connected().await,
status: format!("{:?}", stats.status),
base_url: stats.base_url,
last_health_check: stats.last_health_check.map(|t| format!("{:?}", t)),
timeout_seconds: stats.timeout_seconds,
retry_attempts: stats.retry_attempts,
})
}
Err(e) => {
error!("获取ComfyUI管理器失败: {}", e);
Ok(ConnectionStatusResponse {
connected: false,
status: "管理器未初始化".to_string(),
base_url: "http://localhost:8188".to_string(),
last_health_check: None,
timeout_seconds: 300,
retry_attempts: 3,
})
}
}
}
/// 健康检查
#[tauri::command]
pub async fn comfyui_v2_health_check(
state: State<'_, AppState>,
) -> Result<HealthCheckResponse, String> {
info!("Command: comfyui_v2_health_check");
// TODO: 实现健康检查逻辑
Ok(HealthCheckResponse {
healthy: false,
connection_status: "未连接".to_string(),
websocket_connected: false,
last_check_time: None,
error_message: Some("服务未初始化".to_string()),
})
}
/// 获取系统信息
#[tauri::command]
pub async fn comfyui_v2_get_system_info(
state: State<'_, AppState>,
) -> Result<SystemInfoResponse, String> {
info!("Command: comfyui_v2_get_system_info");
let service_manager = state.get_comfyui_manager().await
.map_err(|e| format!("获取ComfyUI管理器失败: {}", e))?;
let comfyui_manager = service_manager.get_comfyui_manager().await
.map_err(|e| format!("获取ComfyUI管理器失败: {}", e))?;
match comfyui_manager.get_system_info().await {
Ok(stats) => Ok(convert_system_info(stats)),
Err(e) => {
error!("获取系统信息失败: {}", e);
Err(format!("获取系统信息失败: {}", e))
}
}
}
/// 获取队列基本状态
#[tauri::command]
pub async fn comfyui_v2_get_queue_basic_status(
state: State<'_, AppState>,
) -> Result<QueueStatusResponse, String> {
info!("Command: comfyui_v2_get_queue_basic_status");
let service_manager = state.get_comfyui_manager().await
.map_err(|e| format!("获取ComfyUI管理器失败: {}", e))?;
let comfyui_manager = service_manager.get_comfyui_manager().await
.map_err(|e| format!("获取ComfyUI管理器失败: {}", e))?;
match comfyui_manager.get_queue_status().await {
Ok(status) => Ok(QueueStatusResponse {
running_count: status.queue_running.len() as u32,
pending_count: status.queue_pending.len() as u32,
running_tasks: Vec::new(), // TODO: 实现详细任务信息
pending_tasks: Vec::new(), // TODO: 实现详细任务信息
}),
Err(e) => {
error!("获取队列状态失败: {}", e);
Err(format!("获取队列状态失败: {}", e))
}
}
}
// 配置管理命令已移动到 comfyui_v2_config_commands.rs
// ==================== 统计和监控命令 ====================
/// 获取配置统计信息
#[tauri::command]
pub async fn comfyui_v2_get_config_stats(
state: State<'_, AppState>,
) -> Result<ConfigStats, String> {
info!("Command: comfyui_v2_get_config_stats");
let config_manager = get_config_manager(&state).await?;
let stats = config_manager.get_config_stats().await;
Ok(stats)
}
/// 获取配置健康状态
#[tauri::command]
pub async fn comfyui_v2_get_config_health(
state: State<'_, AppState>,
) -> Result<String, String> {
info!("Command: comfyui_v2_get_config_health");
let config_manager = get_config_manager(&state).await?;
match config_manager.health_check().await {
Ok(status) => Ok(status.to_string()),
Err(e) => Err(format!("健康检查失败: {}", e)),
}
}
// ==================== 辅助函数 ====================
/// 获取配置管理器
async fn get_config_manager(state: &State<'_, AppState>) -> Result<std::sync::Arc<ConfigManager>, String> {
state.get_config_manager().await
.map_err(|e| format!("获取配置管理器失败: {}", e))
}
/// 获取服务管理器
async fn get_service_manager(state: &State<'_, AppState>) -> Result<std::sync::Arc<crate::business::services::service_manager::ServiceManager>, String> {
state.get_comfyui_manager().await
.map_err(|e| format!("获取ComfyUI服务管理器失败: {}", e))
}
/// 转换系统信息
fn convert_system_info(info: comfyui_sdk::types::SystemStats) -> SystemInfoResponse {
SystemInfoResponse {
os: info.system.os,
python_version: info.system.python_version,
embedded_python: info.system.embedded_python,
devices: info.devices.into_iter().map(|device| DeviceInfoResponse {
name: device.name,
device_type: device.device_type,
vram_total: device.vram_total,
vram_free: device.vram_free,
torch_vram_total: device.torch_vram_total,
torch_vram_free: device.torch_vram_free,
}).collect(),
}
}
/// 转换队列状态
fn convert_queue_status(status: comfyui_sdk::types::QueueStatus) -> QueueStatusResponse {
let running_items = status.get_running_items();
let pending_items = status.get_pending_items();
QueueStatusResponse {
running_count: running_items.len() as u32,
pending_count: pending_items.len() as u32,
running_tasks: running_items.into_iter().map(|item| QueueTaskResponse {
prompt_id: item.prompt_id,
number: item.number,
status: "running".to_string(),
}).collect(),
pending_tasks: pending_items.into_iter().map(|item| QueueTaskResponse {
prompt_id: item.prompt_id,
number: item.number,
status: "pending".to_string(),
}).collect(),
}
}
// ==================== 工作流管理命令 ====================
/// 工作流响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowResponse {
pub id: String,
pub name: String,
pub description: Option<String>,
pub workflow_data: HashMap<String, serde_json::Value>,
pub version: String,
pub created_at: String,
pub updated_at: String,
pub enabled: bool,
pub tags: Vec<String>,
pub category: Option<String>,
}
/// 创建工作流
#[tauri::command]
pub async fn comfyui_v2_create_workflow(
request: CreateWorkflowRequest,
state: State<'_, AppState>,
) -> Result<WorkflowResponse, String> {
info!("Command: comfyui_v2_create_workflow - {}", request.name);
let service_manager = get_service_manager(&state).await?;
let repository = service_manager.get_repository();
// 转换工作流数据
let workflow_data: ComfyUIWorkflow = serde_json::from_value(request.workflow_json.clone())
.map_err(|e| format!("解析工作流数据失败: {}", e))?;
// 创建工作流模型
let mut workflow = WorkflowModel::new(request.name, workflow_data);
// 设置可选字段
workflow.description = request.description;
workflow.tags = request.tags.unwrap_or_default();
// 保存到数据库
repository.create_workflow(&workflow)
.map_err(|e| format!("创建工作流失败: {}", e))?;
Ok(convert_workflow_model(&workflow))
}
/// 获取工作流列表(实际查询模板表,因为模板和工作流是同一概念)
#[tauri::command]
pub async fn comfyui_v2_list_workflows(
enabled_only: Option<bool>,
state: State<'_, AppState>,
) -> Result<Vec<WorkflowResponse>, String> {
info!("Command: comfyui_v2_list_workflows");
let service_manager = get_service_manager(&state).await?;
let template_engine = service_manager.get_template_engine().await
.map_err(|e| format!("获取模板引擎失败: {}", e))?;
// 查询模板列表(模板和工作流是同一概念)
let templates = template_engine.list_templates(enabled_only.unwrap_or(true)).await
.map_err(|e| format!("获取模板列表失败: {}", e))?;
// 将模板转换为工作流响应格式
let responses = templates.iter().map(convert_template_to_workflow_response).collect();
Ok(responses)
}
/// 获取单个工作流(实际查询模板表,因为模板和工作流是同一概念)
#[tauri::command]
pub async fn comfyui_v2_get_workflow(
workflow_id: String,
state: State<'_, AppState>,
) -> Result<WorkflowResponse, String> {
info!("Command: comfyui_v2_get_workflow - {}", workflow_id);
let service_manager = get_service_manager(&state).await?;
let template_engine = service_manager.get_template_engine().await
.map_err(|e| format!("获取模板引擎失败: {}", e))?;
// 查询模板(模板和工作流是同一概念)
match template_engine.load_template(&workflow_id).await {
Ok(template) => Ok(convert_template_to_workflow_response(&template)),
Err(e) => {
if e.to_string().contains("模板不存在") {
Err("工作流不存在".to_string())
} else {
Err(format!("获取工作流失败: {}", e))
}
}
}
}
/// 更新工作流
#[tauri::command]
pub async fn comfyui_v2_update_workflow(
request: UpdateWorkflowRequest,
state: State<'_, AppState>,
) -> Result<WorkflowResponse, String> {
info!("Command: comfyui_v2_update_workflow - {}", request.id);
let service_manager = get_service_manager(&state).await?;
let repository = service_manager.get_repository();
// 创建ComfyUI V2服务实例
let comfyui_service = {
let realtime_monitor = Arc::new(crate::business::services::realtime_monitor_v2::RealtimeMonitorV2::new(
Arc::new(crate::business::services::comfyui_manager::ComfyUIManager::new(
crate::data::models::comfyui::ComfyUIConfig::default()
).map_err(|e| format!("创建ComfyUI管理器失败: {}", e))?),
repository.clone(),
None
));
let queue_manager = Arc::new(crate::business::services::queue_manager::QueueManager::new(
repository.clone(),
None
));
let template_engine = service_manager.get_template_engine().await
.map_err(|e| format!("获取模板引擎失败: {}", e))?;
crate::business::services::comfyui_v2_service::ComfyUIV2Service::new(
repository.clone(),
realtime_monitor,
queue_manager,
template_engine,
)
};
// 调用服务层更新工作流
match comfyui_service.update_workflow(request.id.clone(), request).await {
Ok(workflow) => {
info!("工作流更新成功: {}", workflow.id);
Ok(convert_workflow_model(&workflow))
}
Err(e) => {
error!("更新工作流失败: {}", e);
Err(format!("更新工作流失败: {}", e))
}
}
}
/// 删除工作流请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteWorkflowRequest {
#[serde(rename = "workflowId")]
pub workflow_id: String,
}
/// 删除工作流(实际删除模板,因为模板和工作流是同一概念)
#[tauri::command]
pub async fn comfyui_v2_delete_workflow(
request: DeleteWorkflowRequest,
state: State<'_, AppState>,
) -> Result<String, String> {
info!("Command: comfyui_v2_delete_workflow - {}", request.workflow_id);
let service_manager = get_service_manager(&state).await?;
let template_engine = service_manager.get_template_engine().await
.map_err(|e| format!("获取模板引擎失败: {}", e))?;
// 删除模板(模板和工作流是同一概念)
template_engine.delete_template(&request.workflow_id).await
.map_err(|e| format!("删除工作流失败: {}", e))?;
Ok("工作流删除成功".to_string())
}
/// 调试:列出所有工作流
#[tauri::command]
pub async fn comfyui_v2_debug_list_workflows(
state: State<'_, AppState>,
) -> Result<Vec<WorkflowResponse>, String> {
info!("Command: comfyui_v2_debug_list_workflows");
let service_manager = get_service_manager(&state).await?;
let repository = service_manager.get_repository();
// 获取所有工作流(包括禁用的)
let workflows = repository.list_workflows(false)
.map_err(|e| format!("获取工作流列表失败: {}", e))?;
let workflow_responses: Vec<WorkflowResponse> = workflows.into_iter()
.map(|workflow| convert_workflow_model(&workflow))
.collect();
info!("找到 {} 个工作流", workflow_responses.len());
Ok(workflow_responses)
}
/// 批量删除工作流
#[tauri::command]
pub async fn comfyui_v2_batch_delete_workflows(
request: BatchDeleteWorkflowsRequest,
state: State<'_, AppState>,
) -> Result<Vec<String>, String> {
info!("Command: comfyui_v2_batch_delete_workflows - {} 个工作流", request.workflow_ids.len());
let service_manager = get_service_manager(&state).await?;
let template_engine = service_manager.get_template_engine().await
.map_err(|e| format!("获取模板引擎失败: {}", e))?;
let mut results = Vec::new();
let mut errors = Vec::new();
// 逐个删除工作流
for workflow_id in request.workflow_ids {
match template_engine.delete_template(&workflow_id).await {
Ok(_) => {
results.push(format!("工作流删除成功: {}", workflow_id));
info!("工作流删除成功: {}", workflow_id);
}
Err(e) => {
let error_msg = format!("删除工作流失败 {}: {}", workflow_id, e);
errors.push(error_msg.clone());
error!("{}", error_msg);
}
}
}
// 如果有错误,返回错误信息
if !errors.is_empty() {
return Err(format!("批量删除部分失败: {}", errors.join("; ")));
}
Ok(results)
}
/// 按分类获取工作流
#[tauri::command]
pub async fn comfyui_v2_get_workflows_by_category(
category: String,
state: State<'_, AppState>,
) -> Result<Vec<WorkflowResponse>, String> {
info!("Command: comfyui_v2_get_workflows_by_category - {}", category);
let service_manager = get_service_manager(&state).await?;
let repository = service_manager.get_repository();
// 直接使用仓库按分类获取工作流
match repository.get_workflows_by_category(&category) {
Ok(workflows) => {
info!("分类 {} 下有 {} 个工作流", category, workflows.len());
let responses = workflows.iter().map(convert_workflow_model).collect();
Ok(responses)
}
Err(e) => {
error!("按分类获取工作流失败: {}", e);
Err(format!("按分类获取工作流失败: {}", e))
}
}
}
/// 搜索工作流
#[tauri::command]
pub async fn comfyui_v2_search_workflows(
query: String,
state: State<'_, AppState>,
) -> Result<Vec<WorkflowResponse>, String> {
info!("Command: comfyui_v2_search_workflows - {}", query);
let service_manager = get_service_manager(&state).await?;
let repository = service_manager.get_repository();
// 直接使用仓库搜索工作流
match repository.search_workflows(&query) {
Ok(workflows) => {
info!("搜索到 {} 个工作流", workflows.len());
let responses = workflows.iter().map(convert_workflow_model).collect();
Ok(responses)
}
Err(e) => {
error!("搜索工作流失败: {}", e);
Err(format!("搜索工作流失败: {}", e))
}
}
}
/// 导出工作流
#[tauri::command]
pub async fn comfyui_v2_export_workflows(
request: ExportWorkflowRequest,
state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
info!("Command: comfyui_v2_export_workflows - {} 个工作流", request.workflow_ids.len());
let service_manager = get_service_manager(&state).await?;
let repository = service_manager.get_repository();
let mut exported_workflows = Vec::new();
let mut errors = Vec::new();
// 逐个获取工作流数据
for workflow_id in request.workflow_ids {
match repository.get_workflow(&workflow_id) {
Ok(Some(workflow)) => {
let mut export_data = serde_json::json!({
"id": workflow.id,
"name": workflow.name,
"description": workflow.description,
"workflow_data": workflow.workflow_data,
"version": workflow.version,
"tags": workflow.tags,
"category": workflow.category,
});
// 如果包含元数据,添加时间戳等信息
if request.include_metadata.unwrap_or(true) {
export_data["created_at"] = serde_json::Value::String(workflow.created_at.to_rfc3339());
export_data["updated_at"] = serde_json::Value::String(workflow.updated_at.to_rfc3339());
export_data["enabled"] = serde_json::Value::Bool(workflow.enabled);
}
exported_workflows.push(export_data);
}
Ok(None) => {
errors.push(format!("工作流不存在: {}", workflow_id));
}
Err(e) => {
errors.push(format!("获取工作流失败 {}: {}", workflow_id, e));
}
}
}
let result = serde_json::json!({
"workflows": exported_workflows,
"export_time": chrono::Utc::now().to_rfc3339(),
"total_count": exported_workflows.len(),
"errors": errors
});
info!("成功导出 {} 个工作流", exported_workflows.len());
Ok(result)
}
/// 导入工作流
#[tauri::command]
pub async fn comfyui_v2_import_workflows(
request: ImportWorkflowRequest,
state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
info!("Command: comfyui_v2_import_workflows");
let service_manager = get_service_manager(&state).await?;
let repository = service_manager.get_repository();
// 解析导入数据
let import_data = request.workflow_data;
let workflows_array = import_data.get("workflows")
.and_then(|v| v.as_array())
.ok_or_else(|| "导入数据格式错误缺少workflows数组".to_string())?;
let mut imported_count = 0;
let mut skipped_count = 0;
let mut errors = Vec::new();
for workflow_data in workflows_array {
// 解析工作流数据
let name = workflow_data.get("name")
.and_then(|v| v.as_str())
.ok_or_else(|| "工作流缺少name字段".to_string())?;
let description = workflow_data.get("description")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let workflow_json = workflow_data.get("workflow_data")
.ok_or_else(|| "工作流缺少workflow_data字段".to_string())?
.clone();
let tags = workflow_data.get("tags")
.and_then(|v| v.as_array())
.map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect());
// 检查是否已存在同名工作流
let existing_workflows = repository.list_workflows(false)
.map_err(|e| format!("检查现有工作流失败: {}", e))?;
let name_exists = existing_workflows.iter().any(|w| w.name == name);
if name_exists && !request.overwrite_existing.unwrap_or(false) {
skipped_count += 1;
continue;
}
// 创建工作流请求
let create_request = CreateWorkflowRequest {
name: name.to_string(),
description,
workflow_json,
tags,
};
// 创建ComfyUI V2服务实例并创建工作流
let comfyui_service = {
let realtime_monitor = Arc::new(crate::business::services::realtime_monitor_v2::RealtimeMonitorV2::new(
Arc::new(crate::business::services::comfyui_manager::ComfyUIManager::new(
crate::data::models::comfyui::ComfyUIConfig::default()
).map_err(|e| format!("创建ComfyUI管理器失败: {}", e))?),
repository.clone(),
None
));
let queue_manager = Arc::new(crate::business::services::queue_manager::QueueManager::new(
repository.clone(),
None
));
let template_engine = service_manager.get_template_engine().await
.map_err(|e| format!("获取模板引擎失败: {}", e))?;
crate::business::services::comfyui_v2_service::ComfyUIV2Service::new(
repository.clone(),
realtime_monitor,
queue_manager,
template_engine,
)
};
match comfyui_service.create_workflow(create_request).await {
Ok(_) => {
imported_count += 1;
info!("成功导入工作流: {}", name);
}
Err(e) => {
errors.push(format!("导入工作流失败 {}: {}", name, e));
error!("导入工作流失败 {}: {}", name, e);
}
}
}
let result = serde_json::json!({
"imported_count": imported_count,
"skipped_count": skipped_count,
"total_count": workflows_array.len(),
"errors": errors,
"import_time": chrono::Utc::now().to_rfc3339()
});
info!("导入完成:成功 {} 个,跳过 {} 个", imported_count, skipped_count);
Ok(result)
}
/// 转换工作流模型为响应
fn convert_workflow_model(workflow: &WorkflowModel) -> WorkflowResponse {
WorkflowResponse {
id: workflow.id.clone(),
name: workflow.name.clone(),
description: workflow.description.clone(),
workflow_data: workflow.workflow_data.iter()
.map(|(k, v)| (k.clone(), serde_json::to_value(v).unwrap_or(serde_json::Value::Null)))
.collect(),
version: workflow.version.clone(),
created_at: workflow.created_at.to_rfc3339(),
updated_at: workflow.updated_at.to_rfc3339(),
enabled: workflow.enabled,
tags: workflow.tags.clone(),
category: workflow.category.clone(),
}
}
/// 转换模板模型为工作流响应(因为模板和工作流是同一概念)
fn convert_template_to_workflow_response(template: &crate::data::models::comfyui::TemplateModel) -> WorkflowResponse {
WorkflowResponse {
id: template.id.clone(),
name: template.name.clone(),
description: template.description.clone(),
workflow_data: template.template_data.workflow.iter()
.map(|(k, v)| (k.clone(), serde_json::to_value(v).unwrap_or(serde_json::Value::Null)))
.collect(),
version: template.version.clone(),
created_at: template.created_at.to_rfc3339(),
updated_at: template.updated_at.to_rfc3339(),
enabled: template.enabled,
tags: template.tags.clone(),
category: template.category.clone(),
}
}