feat: 基础架构重构

This commit is contained in:
2025-08-08 14:38:54 +08:00
parent 0f13428101
commit b7edee7688
7 changed files with 1795 additions and 0 deletions

View File

@@ -0,0 +1,278 @@
//! ComfyUI 管理器
//! 统一的 SDK 管理器,负责连接管理、健康检查和配置管理
use anyhow::{Result, anyhow};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
use tracing::{info, warn, error, debug};
use comfyui_sdk::client::ComfyUIClient;
use comfyui_sdk::types::{ComfyUIClientConfig, SystemStats, QueueStatus, ObjectInfo};
use crate::data::models::comfyui::{ComfyUIConfig, ValidationResult};
/// ComfyUI 连接状态
#[derive(Debug, Clone, PartialEq)]
pub enum ConnectionStatus {
/// 未连接
Disconnected,
/// 连接中
Connecting,
/// 已连接
Connected,
/// 连接失败
Failed(String),
}
/// ComfyUI 管理器
/// 提供统一的 ComfyUI 服务管理接口
pub struct ComfyUIManager {
/// SDK 客户端
client: Arc<RwLock<Option<ComfyUIClient>>>,
/// 配置信息
config: Arc<RwLock<ComfyUIConfig>>,
/// 连接状态
status: Arc<RwLock<ConnectionStatus>>,
/// 最后健康检查时间
last_health_check: Arc<RwLock<Option<std::time::Instant>>>,
}
impl ComfyUIManager {
/// 创建新的 ComfyUI 管理器
pub fn new(config: ComfyUIConfig) -> Result<Self> {
// 验证配置
let validation = config.validate();
if !validation.valid {
return Err(anyhow!("配置验证失败: {:?}", validation.errors));
}
Ok(Self {
client: Arc::new(RwLock::new(None)),
config: Arc::new(RwLock::new(config)),
status: Arc::new(RwLock::new(ConnectionStatus::Disconnected)),
last_health_check: Arc::new(RwLock::new(None)),
})
}
/// 连接到 ComfyUI 服务
pub async fn connect(&self) -> Result<()> {
info!("开始连接 ComfyUI 服务");
// 更新状态为连接中
*self.status.write().await = ConnectionStatus::Connecting;
let config = self.config.read().await;
let sdk_config = config.to_sdk_config();
match ComfyUIClient::new(sdk_config) {
Ok(client) => {
// 测试连接
match client.get_system_stats().await {
Ok(stats) => {
*self.client.write().await = Some(client);
*self.status.write().await = ConnectionStatus::Connected;
*self.last_health_check.write().await = Some(std::time::Instant::now());
info!("ComfyUI 连接成功,系统信息: OS={}, Python={}",
stats.system.os, stats.system.python_version);
Ok(())
}
Err(e) => {
let error_msg = format!("连接测试失败: {}", e);
*self.status.write().await = ConnectionStatus::Failed(error_msg.clone());
Err(anyhow!(error_msg))
}
}
}
Err(e) => {
let error_msg = format!("创建客户端失败: {}", e);
*self.status.write().await = ConnectionStatus::Failed(error_msg.clone());
Err(anyhow!(error_msg))
}
}
}
/// 断开连接
pub async fn disconnect(&self) -> Result<()> {
info!("断开 ComfyUI 连接");
*self.client.write().await = None;
*self.status.write().await = ConnectionStatus::Disconnected;
*self.last_health_check.write().await = None;
Ok(())
}
/// 检查是否已连接
pub async fn is_connected(&self) -> bool {
matches!(*self.status.read().await, ConnectionStatus::Connected)
}
/// 获取连接状态
pub async fn get_connection_status(&self) -> ConnectionStatus {
self.status.read().await.clone()
}
/// 健康检查
pub async fn health_check(&self) -> Result<bool> {
let client_guard = self.client.read().await;
let client = match client_guard.as_ref() {
Some(client) => client,
None => {
warn!("客户端未连接,健康检查失败");
return Ok(false);
}
};
match client.get_system_stats().await {
Ok(_) => {
*self.last_health_check.write().await = Some(std::time::Instant::now());
debug!("健康检查通过");
Ok(true)
}
Err(e) => {
warn!("健康检查失败: {}", e);
// 如果健康检查失败,更新连接状态
*self.status.write().await = ConnectionStatus::Failed(format!("健康检查失败: {}", e));
Ok(false)
}
}
}
/// 获取系统信息
pub async fn get_system_info(&self) -> Result<SystemStats> {
let client_guard = self.client.read().await;
let client = match client_guard.as_ref() {
Some(client) => client,
None => return Err(anyhow!("客户端未连接")),
};
client.get_system_stats().await
.map_err(|e| anyhow!("获取系统信息失败: {}", e))
}
/// 获取队列状态
pub async fn get_queue_status(&self) -> Result<QueueStatus> {
let client_guard = self.client.read().await;
let client = match client_guard.as_ref() {
Some(client) => client,
None => return Err(anyhow!("客户端未连接")),
};
client.get_queue().await
.map_err(|e| anyhow!("获取队列状态失败: {}", e))
}
/// 获取对象信息
pub async fn get_object_info(&self) -> Result<ObjectInfo> {
let client_guard = self.client.read().await;
let client = match client_guard.as_ref() {
Some(client) => client,
None => return Err(anyhow!("客户端未连接")),
};
client.get_object_info().await
.map_err(|e| anyhow!("获取对象信息失败: {}", e))
}
/// 更新配置
pub async fn update_config(&self, new_config: ComfyUIConfig) -> Result<()> {
// 验证新配置
let validation = new_config.validate();
if !validation.valid {
return Err(anyhow!("配置验证失败: {:?}", validation.errors));
}
let mut config = self.config.write().await;
let old_base_url = config.base_url.clone();
*config = new_config;
// 如果 URL 发生变化,需要重新连接
if old_base_url != config.base_url {
drop(config); // 释放锁
info!("配置已更新URL 发生变化,需要重新连接");
self.disconnect().await?;
} else {
info!("配置已更新");
}
Ok(())
}
/// 获取当前配置
pub async fn get_config(&self) -> ComfyUIConfig {
self.config.read().await.clone()
}
/// 获取客户端引用(用于高级操作)
pub async fn get_client(&self) -> Result<Arc<ComfyUIClient>> {
let client_guard = self.client.read().await;
match client_guard.as_ref() {
Some(client) => {
// 创建一个新的 Arc 来避免持有锁
Ok(Arc::new(client.clone()))
}
None => Err(anyhow!("客户端未连接")),
}
}
/// 自动重连
pub async fn auto_reconnect(&self) -> Result<()> {
info!("尝试自动重连");
// 先断开现有连接
self.disconnect().await?;
// 等待一段时间后重连
tokio::time::sleep(Duration::from_secs(2)).await;
// 重新连接
self.connect().await
}
/// 检查是否需要健康检查
pub async fn should_health_check(&self, interval: Duration) -> bool {
let last_check = self.last_health_check.read().await;
match *last_check {
Some(last) => last.elapsed() >= interval,
None => true,
}
}
/// 获取连接统计信息
pub async fn get_connection_stats(&self) -> ConnectionStats {
let status = self.status.read().await.clone();
let last_check = *self.last_health_check.read().await;
let config = self.config.read().await;
ConnectionStats {
status,
base_url: config.base_url.clone(),
last_health_check: last_check,
timeout_seconds: config.timeout_seconds,
retry_attempts: config.retry_attempts,
}
}
}
/// 连接统计信息
#[derive(Debug, Clone)]
pub struct ConnectionStats {
pub status: ConnectionStatus,
pub base_url: String,
pub last_health_check: Option<std::time::Instant>,
pub timeout_seconds: u64,
pub retry_attempts: u32,
}
impl std::fmt::Display for ConnectionStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConnectionStatus::Disconnected => write!(f, "未连接"),
ConnectionStatus::Connecting => write!(f, "连接中"),
ConnectionStatus::Connected => write!(f, "已连接"),
ConnectionStatus::Failed(msg) => write!(f, "连接失败: {}", msg),
}
}
}

View File

@@ -0,0 +1,315 @@
//! 配置管理服务
//! 统一管理应用配置,支持配置验证和环境适配
use anyhow::{Result, anyhow};
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{info, warn, error, debug};
use crate::config::{AppConfig, ComfyUISettings};
use crate::data::models::comfyui::{ComfyUIConfig, ValidationResult};
/// 环境类型
#[derive(Debug, Clone, PartialEq)]
pub enum Environment {
/// 开发环境
Development,
/// 测试环境
Testing,
/// 生产环境
Production,
}
impl Environment {
/// 从环境变量获取环境类型
pub fn from_env() -> Self {
match std::env::var("MIXVIDEO_ENV").as_deref() {
Ok("production") | Ok("prod") => Environment::Production,
Ok("testing") | Ok("test") => Environment::Testing,
_ => Environment::Development,
}
}
/// 获取环境名称
pub fn name(&self) -> &'static str {
match self {
Environment::Development => "development",
Environment::Testing => "testing",
Environment::Production => "production",
}
}
}
/// 配置管理器
pub struct ConfigManager {
/// 应用配置
app_config: Arc<RwLock<AppConfig>>,
/// 当前环境
environment: Environment,
/// 配置文件路径
config_path: Option<String>,
}
impl ConfigManager {
/// 创建新的配置管理器
pub fn new(app_config: AppConfig, config_path: Option<String>) -> Self {
let environment = Environment::from_env();
info!("配置管理器初始化,环境: {}", environment.name());
Self {
app_config: Arc::new(RwLock::new(app_config)),
environment,
config_path,
}
}
/// 获取当前环境
pub fn get_environment(&self) -> &Environment {
&self.environment
}
/// 获取应用配置
pub async fn get_app_config(&self) -> AppConfig {
self.app_config.read().await.clone()
}
/// 更新应用配置
pub async fn update_app_config(&self, config: AppConfig) -> Result<()> {
// 验证配置
let validation = self.validate_app_config(&config).await?;
if !validation.valid {
return Err(anyhow!("配置验证失败: {:?}", validation.errors));
}
// 更新配置
*self.app_config.write().await = config;
// 保存到文件
self.save_config().await?;
info!("应用配置已更新");
Ok(())
}
/// 获取 ComfyUI 配置
pub async fn get_comfyui_config(&self) -> ComfyUIConfig {
let app_config = self.app_config.read().await;
let mut config = app_config.comfyui_settings.to_comfyui_config();
// 根据环境调整配置
self.apply_environment_config(&mut config);
config
}
/// 更新 ComfyUI 配置
pub async fn update_comfyui_config(&self, config: ComfyUIConfig) -> Result<()> {
// 验证配置
let validation = config.validate();
if !validation.valid {
return Err(anyhow!("ComfyUI 配置验证失败: {:?}", validation.errors));
}
// 更新应用配置中的 ComfyUI 设置
{
let mut app_config = self.app_config.write().await;
app_config.comfyui_settings.update_from_comfyui_config(&config);
}
// 保存到文件
self.save_config().await?;
info!("ComfyUI 配置已更新");
Ok(())
}
/// 获取 ComfyUI 设置
pub async fn get_comfyui_settings(&self) -> ComfyUISettings {
let app_config = self.app_config.read().await;
app_config.comfyui_settings.clone()
}
/// 更新 ComfyUI 设置
pub async fn update_comfyui_settings(&self, settings: ComfyUISettings) -> Result<()> {
// 验证设置
let validation = settings.validate();
if !validation.valid {
return Err(anyhow!("ComfyUI 设置验证失败: {:?}", validation.errors));
}
// 更新配置
{
let mut app_config = self.app_config.write().await;
app_config.comfyui_settings = settings;
}
// 保存到文件
self.save_config().await?;
info!("ComfyUI 设置已更新");
Ok(())
}
/// 重置 ComfyUI 配置为默认值
pub async fn reset_comfyui_config(&self) -> Result<()> {
let default_settings = ComfyUISettings::default();
self.update_comfyui_settings(default_settings).await
}
/// 验证应用配置
async fn validate_app_config(&self, config: &AppConfig) -> Result<ValidationResult> {
use crate::data::models::comfyui::{ValidationResult, ValidationError};
let mut result = ValidationResult::success();
// 验证主题
if !["light", "dark", "auto"].contains(&config.theme.as_str()) {
result.add_error(ValidationError::new("theme", "无效的主题设置"));
}
// 验证语言
if !["zh-CN", "en-US"].contains(&config.language.as_str()) {
result.add_error(ValidationError::new("language", "无效的语言设置"));
}
// 验证窗口大小
if config.window_size.width <= 0.0 || config.window_size.height <= 0.0 {
result.add_error(ValidationError::new("window_size", "窗口大小必须大于 0"));
}
// 验证 ComfyUI 设置
let comfyui_validation = config.comfyui_settings.validate();
for error in comfyui_validation.errors {
result.add_error(error);
}
Ok(result)
}
/// 根据环境调整配置
fn apply_environment_config(&self, config: &mut ComfyUIConfig) {
match self.environment {
Environment::Development => {
// 开发环境:启用详细日志,较短超时
config.timeout_seconds = std::cmp::min(config.timeout_seconds, 120);
config.retry_attempts = std::cmp::min(config.retry_attempts, 2);
debug!("应用开发环境配置");
}
Environment::Testing => {
// 测试环境:中等超时,适中重试
config.timeout_seconds = std::cmp::min(config.timeout_seconds, 180);
config.retry_attempts = std::cmp::min(config.retry_attempts, 3);
debug!("应用测试环境配置");
}
Environment::Production => {
// 生产环境:较长超时,更多重试
config.timeout_seconds = std::cmp::max(config.timeout_seconds, 300);
config.retry_attempts = std::cmp::max(config.retry_attempts, 3);
debug!("应用生产环境配置");
}
}
}
/// 保存配置到文件
async fn save_config(&self) -> Result<()> {
if let Some(config_path) = &self.config_path {
let app_config = self.app_config.read().await;
let config_json = serde_json::to_string_pretty(&*app_config)?;
tokio::fs::write(config_path, config_json).await
.map_err(|e| anyhow!("保存配置文件失败: {}", e))?;
debug!("配置已保存到: {}", config_path);
}
Ok(())
}
/// 从文件加载配置
pub async fn load_config(config_path: &str) -> Result<AppConfig> {
match tokio::fs::read_to_string(config_path).await {
Ok(content) => {
let config: AppConfig = serde_json::from_str(&content)
.map_err(|e| anyhow!("解析配置文件失败: {}", e))?;
info!("从文件加载配置: {}", config_path);
Ok(config)
}
Err(_) => {
warn!("配置文件不存在,使用默认配置: {}", config_path);
Ok(AppConfig::default())
}
}
}
/// 导出配置
pub async fn export_config(&self) -> Result<String> {
let app_config = self.app_config.read().await;
serde_json::to_string_pretty(&*app_config)
.map_err(|e| anyhow!("导出配置失败: {}", e))
}
/// 导入配置
pub async fn import_config(&self, config_json: &str) -> Result<()> {
let config: AppConfig = serde_json::from_str(config_json)
.map_err(|e| anyhow!("解析配置失败: {}", e))?;
self.update_app_config(config).await
}
/// 获取配置统计信息
pub async fn get_config_stats(&self) -> ConfigStats {
let app_config = self.app_config.read().await;
ConfigStats {
environment: self.environment.clone(),
config_path: self.config_path.clone(),
comfyui_enabled: app_config.comfyui_settings.enabled,
sdk_enabled: app_config.comfyui_settings.is_sdk_enabled(),
recent_projects_count: app_config.recent_projects.len(),
}
}
/// 检查配置健康状态
pub async fn health_check(&self) -> Result<ConfigHealthStatus> {
let app_config = self.app_config.read().await;
let validation = self.validate_app_config(&*app_config).await?;
let status = if validation.valid {
ConfigHealthStatus::Healthy
} else {
ConfigHealthStatus::Unhealthy(validation.errors)
};
Ok(status)
}
}
/// 配置统计信息
#[derive(Debug, Clone)]
pub struct ConfigStats {
pub environment: Environment,
pub config_path: Option<String>,
pub comfyui_enabled: bool,
pub sdk_enabled: bool,
pub recent_projects_count: usize,
}
/// 配置健康状态
#[derive(Debug, Clone)]
pub enum ConfigHealthStatus {
/// 健康
Healthy,
/// 不健康
Unhealthy(Vec<crate::data::models::comfyui::ValidationError>),
}
impl std::fmt::Display for ConfigHealthStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConfigHealthStatus::Healthy => write!(f, "健康"),
ConfigHealthStatus::Unhealthy(errors) => {
write!(f, "不健康 ({} 个错误)", errors.len())
}
}
}
}

View File

@@ -0,0 +1,375 @@
//! 执行引擎
//! 负责工作流和模板的执行管理
use anyhow::{Result, anyhow};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
use tracing::{info, warn, error, debug};
use comfyui_sdk::templates::WorkflowInstance;
use comfyui_sdk::types::{ParameterValues, PromptRequest, ExecutionOptions};
use crate::business::services::comfyui_manager::ComfyUIManager;
use crate::business::services::template_engine::TemplateEngine;
use crate::data::models::comfyui::{ExecutionModel, ExecutionStatus, WorkflowModel};
use crate::data::repositories::comfyui_repository::ComfyUIRepository;
/// 执行结果
#[derive(Debug, Clone)]
pub struct ExecutionResult {
/// 执行记录 ID
pub execution_id: String,
/// ComfyUI 提示 ID
pub prompt_id: String,
/// 执行状态
pub status: ExecutionStatus,
/// 输出文件 URLs
pub output_urls: Vec<String>,
/// 节点输出详情
pub node_outputs: Option<HashMap<String, serde_json::Value>>,
/// 执行时间(毫秒)
pub execution_time: Option<u64>,
/// 错误信息
pub error_message: Option<String>,
}
/// 执行配置
#[derive(Debug, Clone)]
pub struct ExecutionConfig {
/// 超时时间
pub timeout: Duration,
/// 优先级
pub priority: Option<i32>,
/// 是否等待完成
pub wait_for_completion: bool,
/// 进度回调间隔
pub progress_interval: Duration,
}
impl Default for ExecutionConfig {
fn default() -> Self {
Self {
timeout: Duration::from_secs(300), // 5分钟默认超时
priority: None,
wait_for_completion: true,
progress_interval: Duration::from_secs(1),
}
}
}
/// 执行引擎
/// 提供工作流和模板的执行管理功能
pub struct ExecutionEngine {
/// ComfyUI 管理器
manager: Arc<ComfyUIManager>,
/// 模板引擎
template_engine: Arc<TemplateEngine>,
/// 数据仓库
repository: Arc<ComfyUIRepository>,
/// 正在执行的任务
running_executions: Arc<RwLock<HashMap<String, ExecutionModel>>>,
}
impl ExecutionEngine {
/// 创建新的执行引擎
pub fn new(
manager: Arc<ComfyUIManager>,
template_engine: Arc<TemplateEngine>,
repository: Arc<ComfyUIRepository>,
) -> Self {
Self {
manager,
template_engine,
repository,
running_executions: Arc::new(RwLock::new(HashMap::new())),
}
}
/// 执行工作流
pub async fn execute_workflow(
&self,
workflow: &WorkflowModel,
parameters: Option<ParameterValues>,
config: ExecutionConfig,
) -> Result<ExecutionResult> {
info!("开始执行工作流: {} ({})", workflow.name, workflow.id);
// 检查连接状态
if !self.manager.is_connected().await {
return Err(anyhow!("ComfyUI 服务未连接"));
}
// 获取客户端
let client = self.manager.get_client().await?;
// 创建提示请求
let prompt_request = PromptRequest {
prompt: workflow.workflow_data.clone().into_iter().collect(),
client_id: None,
extra_data: None,
};
// 提交工作流
let prompt_response = client.http().queue_prompt(&prompt_request).await
.map_err(|e| anyhow!("提交工作流失败: {}", e))?;
let prompt_id = prompt_response.prompt_id.clone();
// 创建执行记录
let mut execution = ExecutionModel::for_workflow(
workflow.id.clone(),
prompt_id.clone(),
parameters,
);
execution.update_status(ExecutionStatus::Running);
// 保存执行记录
self.repository.create_execution(&execution).await?;
// 添加到运行中的任务
{
let mut running = self.running_executions.write().await;
running.insert(execution.id.clone(), execution.clone());
}
debug!("工作流已提交prompt_id: {}, execution_id: {}", prompt_id, execution.id);
// 如果需要等待完成
if config.wait_for_completion {
self.wait_for_completion(&execution.id, config).await
} else {
Ok(ExecutionResult {
execution_id: execution.id,
prompt_id,
status: ExecutionStatus::Running,
output_urls: Vec::new(),
node_outputs: None,
execution_time: None,
error_message: None,
})
}
}
/// 执行模板
pub async fn execute_template(
&self,
template_id: &str,
parameters: ParameterValues,
config: ExecutionConfig,
) -> Result<ExecutionResult> {
info!("开始执行模板: {}", template_id);
// 检查连接状态
if !self.manager.is_connected().await {
return Err(anyhow!("ComfyUI 服务未连接"));
}
// 创建模板实例
let instance = self.template_engine.create_instance(template_id, parameters.clone()).await?;
// 获取客户端
let client = self.manager.get_client().await?;
// 创建提示请求
let prompt_request = PromptRequest {
prompt: instance.get_workflow().clone().into_iter().collect(),
client_id: None,
extra_data: None,
};
// 提交工作流
let prompt_response = client.http().queue_prompt(&prompt_request).await
.map_err(|e| anyhow!("提交模板失败: {}", e))?;
let prompt_id = prompt_response.prompt_id.clone();
// 创建执行记录
let mut execution = ExecutionModel::for_template(
template_id.to_string(),
prompt_id.clone(),
parameters,
);
execution.update_status(ExecutionStatus::Running);
// 保存执行记录
self.repository.create_execution(&execution).await?;
// 添加到运行中的任务
{
let mut running = self.running_executions.write().await;
running.insert(execution.id.clone(), execution.clone());
}
debug!("模板已提交prompt_id: {}, execution_id: {}", prompt_id, execution.id);
// 如果需要等待完成
if config.wait_for_completion {
self.wait_for_completion(&execution.id, config).await
} else {
Ok(ExecutionResult {
execution_id: execution.id,
prompt_id,
status: ExecutionStatus::Running,
output_urls: Vec::new(),
node_outputs: None,
execution_time: None,
error_message: None,
})
}
}
/// 取消执行
pub async fn cancel_execution(&self, execution_id: &str) -> Result<()> {
info!("取消执行: {}", execution_id);
// 从运行中的任务移除
let execution = {
let mut running = self.running_executions.write().await;
running.remove(execution_id)
};
if let Some(mut execution) = execution {
// 更新状态为已取消
execution.update_status(ExecutionStatus::Cancelled);
// 更新数据库
self.repository.update_execution(&execution).await?;
// TODO: 调用 ComfyUI API 取消任务
// 这需要 SDK 支持取消操作
info!("执行已取消: {}", execution_id);
Ok(())
} else {
Err(anyhow!("执行记录不存在或已完成: {}", execution_id))
}
}
/// 获取执行状态
pub async fn get_execution_status(&self, execution_id: &str) -> Result<ExecutionResult> {
// 先检查运行中的任务
{
let running = self.running_executions.read().await;
if let Some(execution) = running.get(execution_id) {
return Ok(ExecutionResult {
execution_id: execution.id.clone(),
prompt_id: execution.prompt_id.clone(),
status: execution.status.clone(),
output_urls: execution.output_urls.clone(),
node_outputs: execution.node_outputs.clone(),
execution_time: execution.execution_time,
error_message: execution.error_message.clone(),
});
}
}
// 从数据库查询
match self.repository.get_execution(execution_id).await? {
Some(execution) => Ok(ExecutionResult {
execution_id: execution.id,
prompt_id: execution.prompt_id,
status: execution.status,
output_urls: execution.output_urls,
node_outputs: execution.node_outputs,
execution_time: execution.execution_time,
error_message: execution.error_message,
}),
None => Err(anyhow!("执行记录不存在: {}", execution_id)),
}
}
/// 等待执行完成
async fn wait_for_completion(&self, execution_id: &str, config: ExecutionConfig) -> Result<ExecutionResult> {
let start_time = std::time::Instant::now();
loop {
// 检查超时
if start_time.elapsed() >= config.timeout {
// 超时,取消执行
let _ = self.cancel_execution(execution_id).await;
return Err(anyhow!("执行超时: {}", execution_id));
}
// 获取当前状态
let status = self.get_execution_status(execution_id).await?;
match status.status {
ExecutionStatus::Completed => {
info!("执行完成: {}", execution_id);
return Ok(status);
}
ExecutionStatus::Failed => {
error!("执行失败: {}", execution_id);
return Ok(status);
}
ExecutionStatus::Cancelled => {
warn!("执行已取消: {}", execution_id);
return Ok(status);
}
ExecutionStatus::Running | ExecutionStatus::Pending => {
// 继续等待
tokio::time::sleep(config.progress_interval).await;
}
}
}
}
/// 获取执行历史
pub async fn get_execution_history(&self, limit: Option<u32>) -> Result<Vec<ExecutionResult>> {
let executions = self.repository.list_executions(limit, None).await?;
let results = executions
.into_iter()
.map(|execution| ExecutionResult {
execution_id: execution.id,
prompt_id: execution.prompt_id,
status: execution.status,
output_urls: execution.output_urls,
node_outputs: execution.node_outputs,
execution_time: execution.execution_time,
error_message: execution.error_message,
})
.collect();
Ok(results)
}
/// 清理已完成的执行记录
pub async fn cleanup_completed_executions(&self) {
let mut running = self.running_executions.write().await;
let mut to_remove = Vec::new();
for (id, execution) in running.iter() {
if execution.is_completed() {
to_remove.push(id.clone());
}
}
for id in to_remove {
running.remove(&id);
}
if !running.is_empty() {
debug!("清理完成,剩余运行中的任务: {}", running.len());
}
}
/// 获取运行统计信息
pub async fn get_execution_stats(&self) -> ExecutionStats {
let running = self.running_executions.read().await;
ExecutionStats {
running_count: running.len(),
running_executions: running.keys().cloned().collect(),
}
}
}
/// 执行统计信息
#[derive(Debug, Clone)]
pub struct ExecutionStats {
pub running_count: usize,
pub running_executions: Vec<String>,
}

View File

@@ -35,9 +35,17 @@ pub mod conversation_service;
pub mod jianying_export;
pub mod template_segment_weight_service;
pub mod directory_settings_service;
// 旧的 ComfyUI 服务(将被逐步替换)
pub mod comfyui_service;
pub mod comfyui_sdk_service;
pub mod comfyui_integration_service;
// 新的基于 SDK 的 ComfyUI 服务
pub mod comfyui_manager;
pub mod template_engine;
pub mod execution_engine;
pub mod realtime_monitor;
pub mod config_manager;
pub mod outfit_photo_generation_service;
pub mod workflow_management_service;
pub mod error_handling_service;

View File

@@ -0,0 +1,453 @@
//! 实时监控服务
//! 负责 WebSocket 连接和实时事件处理
use anyhow::{Result, anyhow};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{RwLock, mpsc, broadcast};
use tracing::{info, warn, error, debug};
use comfyui_sdk::types::events::{ComfyUIEvent, ExecutionEvent, QueueEvent, ProgressEvent};
use crate::business::services::comfyui_manager::ComfyUIManager;
use crate::data::models::comfyui::{ExecutionModel, ExecutionStatus};
use crate::data::repositories::comfyui_repository::ComfyUIRepository;
/// 实时事件类型
#[derive(Debug, Clone)]
pub enum RealtimeEvent {
/// 执行开始
ExecutionStarted {
prompt_id: String,
execution_id: Option<String>,
},
/// 执行进度更新
ExecutionProgress {
prompt_id: String,
execution_id: Option<String>,
progress: f32,
current_node: Option<String>,
total_nodes: Option<u32>,
},
/// 执行完成
ExecutionCompleted {
prompt_id: String,
execution_id: Option<String>,
outputs: HashMap<String, serde_json::Value>,
output_urls: Vec<String>,
},
/// 执行失败
ExecutionFailed {
prompt_id: String,
execution_id: Option<String>,
error: String,
},
/// 队列状态更新
QueueUpdated {
running_count: u32,
pending_count: u32,
},
/// 连接状态变化
ConnectionChanged {
connected: bool,
message: String,
},
}
/// 实时监控服务
pub struct RealtimeMonitor {
/// ComfyUI 管理器
manager: Arc<ComfyUIManager>,
/// 数据仓库
repository: Arc<ComfyUIRepository>,
/// 事件广播器
event_sender: broadcast::Sender<RealtimeEvent>,
/// WebSocket 连接状态
websocket_connected: Arc<RwLock<bool>>,
/// 提示 ID 到执行 ID 的映射
prompt_execution_map: Arc<RwLock<HashMap<String, String>>>,
/// 监控任务句柄
monitor_handle: Arc<RwLock<Option<tokio::task::JoinHandle<()>>>>,
}
impl RealtimeMonitor {
/// 创建新的实时监控服务
pub fn new(
manager: Arc<ComfyUIManager>,
repository: Arc<ComfyUIRepository>,
) -> Self {
let (event_sender, _) = broadcast::channel(1000);
Self {
manager,
repository,
event_sender,
websocket_connected: Arc::new(RwLock::new(false)),
prompt_execution_map: Arc::new(RwLock::new(HashMap::new())),
monitor_handle: Arc::new(RwLock::new(None)),
}
}
/// 启动实时监控
pub async fn start(&self) -> Result<()> {
info!("启动实时监控服务");
// 检查是否已经在运行
{
let handle = self.monitor_handle.read().await;
if handle.is_some() {
return Err(anyhow!("实时监控服务已在运行"));
}
}
// 启动监控任务
let monitor_task = self.spawn_monitor_task().await?;
{
let mut handle = self.monitor_handle.write().await;
*handle = Some(monitor_task);
}
info!("实时监控服务已启动");
Ok(())
}
/// 停止实时监控
pub async fn stop(&self) -> Result<()> {
info!("停止实时监控服务");
let handle = {
let mut handle_guard = self.monitor_handle.write().await;
handle_guard.take()
};
if let Some(handle) = handle {
handle.abort();
info!("实时监控服务已停止");
}
// 更新连接状态
*self.websocket_connected.write().await = false;
Ok(())
}
/// 订阅实时事件
pub fn subscribe(&self) -> broadcast::Receiver<RealtimeEvent> {
self.event_sender.subscribe()
}
/// 注册执行映射
pub async fn register_execution(&self, prompt_id: String, execution_id: String) {
let mut map = self.prompt_execution_map.write().await;
map.insert(prompt_id, execution_id);
}
/// 获取执行 ID
async fn get_execution_id(&self, prompt_id: &str) -> Option<String> {
let map = self.prompt_execution_map.read().await;
map.get(prompt_id).cloned()
}
/// 生成监控任务
async fn spawn_monitor_task(&self) -> Result<tokio::task::JoinHandle<()>> {
let manager = Arc::clone(&self.manager);
let repository = Arc::clone(&self.repository);
let event_sender = self.event_sender.clone();
let websocket_connected = Arc::clone(&self.websocket_connected);
let prompt_execution_map = Arc::clone(&self.prompt_execution_map);
let handle = tokio::spawn(async move {
let mut reconnect_interval = Duration::from_secs(5);
let max_reconnect_interval = Duration::from_secs(60);
loop {
// 检查 ComfyUI 连接状态
if !manager.is_connected().await {
warn!("ComfyUI 未连接,等待连接...");
tokio::time::sleep(Duration::from_secs(5)).await;
continue;
}
// 尝试建立 WebSocket 连接
match Self::connect_websocket(&manager, &event_sender, &websocket_connected, &prompt_execution_map, &repository).await {
Ok(_) => {
info!("WebSocket 连接已建立");
reconnect_interval = Duration::from_secs(5); // 重置重连间隔
}
Err(e) => {
error!("WebSocket 连接失败: {}", e);
// 发送连接状态事件
let _ = event_sender.send(RealtimeEvent::ConnectionChanged {
connected: false,
message: format!("WebSocket 连接失败: {}", e),
});
// 等待后重试
tokio::time::sleep(reconnect_interval).await;
// 增加重连间隔(指数退避)
reconnect_interval = std::cmp::min(
reconnect_interval * 2,
max_reconnect_interval,
);
}
}
}
});
Ok(handle)
}
/// 连接 WebSocket
async fn connect_websocket(
manager: &Arc<ComfyUIManager>,
event_sender: &broadcast::Sender<RealtimeEvent>,
websocket_connected: &Arc<RwLock<bool>>,
prompt_execution_map: &Arc<RwLock<HashMap<String, String>>>,
repository: &Arc<ComfyUIRepository>,
) -> Result<()> {
// 获取客户端
let client = manager.get_client().await?;
// 建立 WebSocket 连接
let mut websocket = client.websocket().connect().await?;
// 更新连接状态
*websocket_connected.write().await = true;
// 发送连接状态事件
let _ = event_sender.send(RealtimeEvent::ConnectionChanged {
connected: true,
message: "WebSocket 连接已建立".to_string(),
});
// 处理 WebSocket 消息
while let Some(event) = websocket.next_event().await {
match event {
Ok(comfyui_event) => {
if let Err(e) = Self::handle_comfyui_event(
comfyui_event,
event_sender,
prompt_execution_map,
repository,
).await {
error!("处理 ComfyUI 事件失败: {}", e);
}
}
Err(e) => {
error!("WebSocket 错误: {}", e);
break;
}
}
}
// 连接断开
*websocket_connected.write().await = false;
let _ = event_sender.send(RealtimeEvent::ConnectionChanged {
connected: false,
message: "WebSocket 连接已断开".to_string(),
});
Err(anyhow!("WebSocket 连接断开"))
}
/// 处理 ComfyUI 事件
async fn handle_comfyui_event(
event: ComfyUIEvent,
event_sender: &broadcast::Sender<RealtimeEvent>,
prompt_execution_map: &Arc<RwLock<HashMap<String, String>>>,
repository: &Arc<ComfyUIRepository>,
) -> Result<()> {
match event {
ComfyUIEvent::Execution(exec_event) => {
Self::handle_execution_event(exec_event, event_sender, prompt_execution_map, repository).await?;
}
ComfyUIEvent::Progress(progress_event) => {
Self::handle_progress_event(progress_event, event_sender, prompt_execution_map).await?;
}
ComfyUIEvent::Queue(queue_event) => {
Self::handle_queue_event(queue_event, event_sender).await?;
}
_ => {
debug!("收到其他类型事件: {:?}", event);
}
}
Ok(())
}
/// 处理执行事件
async fn handle_execution_event(
event: ExecutionEvent,
event_sender: &broadcast::Sender<RealtimeEvent>,
prompt_execution_map: &Arc<RwLock<HashMap<String, String>>>,
repository: &Arc<ComfyUIRepository>,
) -> Result<()> {
let execution_id = {
let map = prompt_execution_map.read().await;
map.get(&event.prompt_id).cloned()
};
match event.event_type.as_str() {
"execution_start" => {
let _ = event_sender.send(RealtimeEvent::ExecutionStarted {
prompt_id: event.prompt_id.clone(),
execution_id: execution_id.clone(),
});
// 更新数据库中的执行状态
if let Some(exec_id) = execution_id {
if let Ok(Some(mut execution)) = repository.get_execution(&exec_id).await {
execution.update_status(ExecutionStatus::Running);
let _ = repository.update_execution(&execution).await;
}
}
}
"execution_complete" => {
// 提取输出信息
let outputs = event.data.unwrap_or_default();
let output_urls = Self::extract_output_urls(&outputs);
let _ = event_sender.send(RealtimeEvent::ExecutionCompleted {
prompt_id: event.prompt_id.clone(),
execution_id: execution_id.clone(),
outputs: outputs.clone(),
output_urls: output_urls.clone(),
});
// 更新数据库中的执行状态
if let Some(exec_id) = execution_id {
if let Ok(Some(mut execution)) = repository.get_execution(&exec_id).await {
execution.set_results(outputs, output_urls);
execution.node_outputs = Some(outputs);
let _ = repository.update_execution(&execution).await;
}
}
// 清理映射
{
let mut map = prompt_execution_map.write().await;
map.remove(&event.prompt_id);
}
}
"execution_error" => {
let error_msg = event.data
.and_then(|data| data.get("error"))
.and_then(|e| e.as_str())
.unwrap_or("未知错误")
.to_string();
let _ = event_sender.send(RealtimeEvent::ExecutionFailed {
prompt_id: event.prompt_id.clone(),
execution_id: execution_id.clone(),
error: error_msg.clone(),
});
// 更新数据库中的执行状态
if let Some(exec_id) = execution_id {
if let Ok(Some(mut execution)) = repository.get_execution(&exec_id).await {
execution.set_error(error_msg);
let _ = repository.update_execution(&execution).await;
}
}
// 清理映射
{
let mut map = prompt_execution_map.write().await;
map.remove(&event.prompt_id);
}
}
_ => {
debug!("未处理的执行事件类型: {}", event.event_type);
}
}
Ok(())
}
/// 处理进度事件
async fn handle_progress_event(
event: ProgressEvent,
event_sender: &broadcast::Sender<RealtimeEvent>,
prompt_execution_map: &Arc<RwLock<HashMap<String, String>>>,
) -> Result<()> {
let execution_id = {
let map = prompt_execution_map.read().await;
map.get(&event.prompt_id).cloned()
};
let _ = event_sender.send(RealtimeEvent::ExecutionProgress {
prompt_id: event.prompt_id,
execution_id,
progress: event.progress,
current_node: event.current_node,
total_nodes: event.total_nodes,
});
Ok(())
}
/// 处理队列事件
async fn handle_queue_event(
event: QueueEvent,
event_sender: &broadcast::Sender<RealtimeEvent>,
) -> Result<()> {
let _ = event_sender.send(RealtimeEvent::QueueUpdated {
running_count: event.running_count,
pending_count: event.pending_count,
});
Ok(())
}
/// 提取输出 URLs
fn extract_output_urls(outputs: &HashMap<String, serde_json::Value>) -> Vec<String> {
let mut urls = Vec::new();
for (_, output) in outputs {
if let Some(images) = output.get("images").and_then(|v| v.as_array()) {
for image in images {
if let Some(filename) = image.get("filename").and_then(|v| v.as_str()) {
// 构建完整的 URL这里需要根据实际的 ComfyUI 配置调整)
let url = format!("/view?filename={}", filename);
urls.push(url);
}
}
}
}
urls
}
/// 检查 WebSocket 连接状态
pub async fn is_websocket_connected(&self) -> bool {
*self.websocket_connected.read().await
}
/// 获取监控统计信息
pub async fn get_monitor_stats(&self) -> MonitorStats {
let websocket_connected = *self.websocket_connected.read().await;
let prompt_map_size = self.prompt_execution_map.read().await.len();
let is_running = self.monitor_handle.read().await.is_some();
MonitorStats {
is_running,
websocket_connected,
tracked_executions: prompt_map_size,
event_subscribers: self.event_sender.receiver_count(),
}
}
}
/// 监控统计信息
#[derive(Debug, Clone)]
pub struct MonitorStats {
pub is_running: bool,
pub websocket_connected: bool,
pub tracked_executions: usize,
pub event_subscribers: usize,
}

View File

@@ -0,0 +1,301 @@
//! 模板引擎
//! 负责工作流模板的管理、验证和实例化
use anyhow::{Result, anyhow};
use std::collections::HashMap;
use std::sync::Arc;
use tracing::{info, warn, error, debug};
use comfyui_sdk::templates::{WorkflowTemplate, WorkflowInstance, TemplateManager};
use comfyui_sdk::types::{ParameterValues, ValidationResult, ValidationError, ComfyUIWorkflow};
use crate::data::models::comfyui::{TemplateModel, WorkflowModel};
use crate::data::repositories::comfyui_repository::ComfyUIRepository;
/// 模板引擎
/// 提供模板管理和实例化功能
pub struct TemplateEngine {
/// 数据仓库
repository: Arc<ComfyUIRepository>,
/// SDK 模板管理器
template_manager: TemplateManager,
/// 内存中的模板缓存
template_cache: Arc<tokio::sync::RwLock<HashMap<String, TemplateModel>>>,
}
impl TemplateEngine {
/// 创建新的模板引擎
pub fn new(repository: Arc<ComfyUIRepository>) -> Self {
Self {
repository,
template_manager: TemplateManager::new(),
template_cache: Arc::new(tokio::sync::RwLock::new(HashMap::new())),
}
}
/// 加载模板
pub async fn load_template(&self, template_id: &str) -> Result<TemplateModel> {
// 先检查缓存
{
let cache = self.template_cache.read().await;
if let Some(template) = cache.get(template_id) {
debug!("从缓存加载模板: {}", template_id);
return Ok(template.clone());
}
}
// 从数据库加载
match self.repository.get_template(template_id).await? {
Some(template) => {
// 更新缓存
{
let mut cache = self.template_cache.write().await;
cache.insert(template_id.to_string(), template.clone());
}
debug!("从数据库加载模板: {}", template_id);
Ok(template)
}
None => Err(anyhow!("模板不存在: {}", template_id)),
}
}
/// 获取所有模板
pub async fn list_templates(&self, enabled_only: bool) -> Result<Vec<TemplateModel>> {
self.repository.list_templates(enabled_only).await
}
/// 按分类获取模板
pub async fn get_templates_by_category(&self, category: &str) -> Result<Vec<TemplateModel>> {
self.repository.get_templates_by_category(category).await
}
/// 创建模板
pub async fn create_template(&self, template: TemplateModel) -> Result<()> {
// 验证模板
self.validate_template_structure(&template)?;
// 保存到数据库
self.repository.create_template(&template).await?;
// 更新缓存
{
let mut cache = self.template_cache.write().await;
cache.insert(template.id.clone(), template.clone());
}
info!("创建模板成功: {} ({})", template.name, template.id);
Ok(())
}
/// 更新模板
pub async fn update_template(&self, template: TemplateModel) -> Result<()> {
// 验证模板
self.validate_template_structure(&template)?;
// 更新数据库
self.repository.update_template(&template).await?;
// 更新缓存
{
let mut cache = self.template_cache.write().await;
cache.insert(template.id.clone(), template.clone());
}
info!("更新模板成功: {} ({})", template.name, template.id);
Ok(())
}
/// 删除模板
pub async fn delete_template(&self, template_id: &str) -> Result<()> {
// 从数据库删除
self.repository.delete_template(template_id).await?;
// 从缓存删除
{
let mut cache = self.template_cache.write().await;
cache.remove(template_id);
}
info!("删除模板成功: {}", template_id);
Ok(())
}
/// 验证模板参数
pub async fn validate_parameters(&self, template_id: &str, parameters: &ParameterValues) -> Result<ValidationResult> {
let template = self.load_template(template_id).await?;
Ok(template.validate_parameters(parameters))
}
/// 创建模板实例
pub async fn create_instance(&self, template_id: &str, parameters: ParameterValues) -> Result<WorkflowInstance> {
let template = self.load_template(template_id).await?;
// 验证参数
let validation = template.validate_parameters(&parameters);
if !validation.valid {
return Err(anyhow!("参数验证失败: {:?}", validation.errors));
}
// 使用 SDK 创建工作流模板
let workflow_template = WorkflowTemplate::new(
template.template_data.clone(),
template.parameter_schema.clone(),
)?;
// 创建实例
let instance = workflow_template.create_instance(parameters)?;
debug!("创建模板实例成功: {} -> {}", template_id, instance.get_id());
Ok(instance)
}
/// 从工作流创建模板
pub async fn create_template_from_workflow(&self, workflow: &WorkflowModel, parameter_schema: HashMap<String, comfyui_sdk::types::ParameterSchema>) -> Result<TemplateModel> {
// 创建模板元数据
let template_metadata = comfyui_sdk::types::TemplateMetadata {
id: uuid::Uuid::new_v4().to_string(),
name: format!("{} Template", workflow.name),
description: workflow.description.clone(),
version: Some(workflow.version.clone()),
author: None,
tags: Some(workflow.tags.clone()),
category: workflow.category.clone(),
created_at: Some(chrono::Utc::now()),
updated_at: Some(chrono::Utc::now()),
};
// 创建模板数据
let template_data = comfyui_sdk::types::WorkflowTemplateData {
metadata: template_metadata.clone(),
workflow: workflow.workflow_data.clone(),
parameters: parameter_schema.clone(),
};
// 创建模板模型
let template = TemplateModel::new(
template_metadata.name.clone(),
template_data,
parameter_schema,
);
Ok(template)
}
/// 预览模板实例(不执行)
pub async fn preview_instance(&self, template_id: &str, parameters: ParameterValues) -> Result<ComfyUIWorkflow> {
let instance = self.create_instance(template_id, parameters).await?;
Ok(instance.get_workflow().clone())
}
/// 获取模板的参数定义
pub async fn get_parameter_schema(&self, template_id: &str) -> Result<HashMap<String, comfyui_sdk::types::ParameterSchema>> {
let template = self.load_template(template_id).await?;
Ok(template.parameter_schema)
}
/// 验证模板结构
fn validate_template_structure(&self, template: &TemplateModel) -> Result<()> {
// 检查模板名称
if template.name.trim().is_empty() {
return Err(anyhow!("模板名称不能为空"));
}
// 检查工作流数据
if template.template_data.workflow.is_empty() {
return Err(anyhow!("工作流数据不能为空"));
}
// 检查参数定义
if template.parameter_schema.is_empty() {
warn!("模板 {} 没有定义参数", template.id);
}
// 验证工作流结构
for (node_id, node) in &template.template_data.workflow {
if node.class_type.trim().is_empty() {
return Err(anyhow!("节点 {} 的 class_type 不能为空", node_id));
}
}
Ok(())
}
/// 清除模板缓存
pub async fn clear_cache(&self) {
let mut cache = self.template_cache.write().await;
cache.clear();
info!("模板缓存已清除");
}
/// 预热模板缓存
pub async fn warm_cache(&self) -> Result<()> {
let templates = self.repository.list_templates(true).await?;
{
let mut cache = self.template_cache.write().await;
for template in templates {
cache.insert(template.id.clone(), template);
}
}
info!("模板缓存预热完成");
Ok(())
}
/// 获取缓存统计信息
pub async fn get_cache_stats(&self) -> CacheStats {
let cache = self.template_cache.read().await;
CacheStats {
cached_templates: cache.len(),
cache_keys: cache.keys().cloned().collect(),
}
}
/// 搜索模板
pub async fn search_templates(&self, query: &str) -> Result<Vec<TemplateModel>> {
let all_templates = self.repository.list_templates(true).await?;
let query_lower = query.to_lowercase();
let filtered_templates: Vec<TemplateModel> = all_templates
.into_iter()
.filter(|template| {
template.name.to_lowercase().contains(&query_lower) ||
template.description.as_ref().map_or(false, |desc| desc.to_lowercase().contains(&query_lower)) ||
template.tags.iter().any(|tag| tag.to_lowercase().contains(&query_lower))
})
.collect();
Ok(filtered_templates)
}
/// 导出模板
pub async fn export_template(&self, template_id: &str) -> Result<String> {
let template = self.load_template(template_id).await?;
serde_json::to_string_pretty(&template)
.map_err(|e| anyhow!("导出模板失败: {}", e))
}
/// 导入模板
pub async fn import_template(&self, template_json: &str) -> Result<String> {
let mut template: TemplateModel = serde_json::from_str(template_json)
.map_err(|e| anyhow!("解析模板失败: {}", e))?;
// 生成新的 ID 避免冲突
template.id = uuid::Uuid::new_v4().to_string();
template.created_at = chrono::Utc::now();
template.updated_at = chrono::Utc::now();
// 创建模板
self.create_template(template.clone()).await?;
Ok(template.id)
}
}
/// 缓存统计信息
#[derive(Debug, Clone)]
pub struct CacheStats {
pub cached_templates: usize,
pub cache_keys: Vec<String>,
}

View File

@@ -135,6 +135,71 @@ impl ComfyUISettings {
pub fn get_sdk_timeout(&self) -> std::time::Duration {
std::time::Duration::from_secs(self.sdk_config.sdk_timeout_seconds)
}
/// 转换为新的 ComfyUI 配置
pub fn to_comfyui_config(&self) -> crate::data::models::comfyui::ComfyUIConfig {
use crate::data::models::comfyui::ComfyUIConfig;
ComfyUIConfig {
base_url: self.base_url(),
timeout_seconds: self.timeout_seconds,
retry_attempts: self.sdk_config.sdk_retry_attempts,
retry_delay_ms: 1000, // 默认 1 秒重试延迟
enable_websocket: true, // 默认启用 WebSocket
enable_cache: true, // 默认启用缓存
max_concurrency: 4, // 默认并发数
custom_headers: None, // 暂时不支持自定义头
}
}
/// 从新的 ComfyUI 配置更新设置
pub fn update_from_comfyui_config(&mut self, config: &crate::data::models::comfyui::ComfyUIConfig) {
// 解析 URL
if let Ok(url) = url::Url::parse(&config.base_url) {
if let Some(host) = url.host_str() {
self.server_address = host.to_string();
}
if let Some(port) = url.port() {
self.server_port = port;
}
}
self.timeout_seconds = config.timeout_seconds;
self.sdk_config.sdk_retry_attempts = config.retry_attempts;
}
/// 验证配置
pub fn validate(&self) -> crate::data::models::comfyui::ValidationResult {
use crate::data::models::comfyui::{ValidationResult, ValidationError};
let mut result = ValidationResult::success();
// 验证服务器地址
if self.server_address.trim().is_empty() {
result.add_error(ValidationError::new("server_address", "服务器地址不能为空"));
}
// 验证端口
if self.server_port == 0 {
result.add_error(ValidationError::new("server_port", "端口号必须大于 0"));
}
// 验证超时时间
if self.timeout_seconds == 0 {
result.add_error(ValidationError::new("timeout_seconds", "超时时间必须大于 0"));
}
// 验证 SDK 配置
if self.sdk_config.sdk_retry_attempts > 10 {
result.add_error(ValidationError::new("sdk_retry_attempts", "重试次数不应超过 10"));
}
if self.sdk_config.sdk_timeout_seconds == 0 {
result.add_error(ValidationError::new("sdk_timeout_seconds", "SDK 超时时间必须大于 0"));
}
result
}
}
impl Default for AppConfig {