- 修复WebSocket错误消息解析失败问题,支持更多错误字段格式 - 改进执行错误处理,收到错误时立即中断而不是等待超时 - 修复ComfyUI队列API响应格式不匹配问题,支持数组格式的队列数据 - 添加灵活的队列项解析逻辑,支持整数和字符串类型的prompt_id - 在EventEmitter中添加错误存储功能,支持实时错误状态检查 - 更新所有相关的队列状态转换代码 解决的问题: 1. WebSocket消息解析错误:missing field 'message' 2. 执行错误后仍等待超时的问题 3. 队列API响应解析错误:invalid type integer/string expected 4. 批量处理中错误统计不准确的问题
961 lines
33 KiB
Rust
961 lines
33 KiB
Rust
//! 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(),
|
||
}
|
||
}
|