use crate::data::models::outfit_matching::{ OutfitMatching, CreateOutfitMatchingRequest, UpdateOutfitMatchingRequest, OutfitMatchingQueryOptions, OutfitMatchingStats, MatchingType, MatchingScoreDetails, MatchingSuggestion, MatchingOutfitItem, SmartMatchingRequest, SmartMatchingResult, ConfidenceLevel }; use crate::data::models::outfit_item::{OutfitItem, OutfitCategory, OutfitStyle}; use crate::data::models::outfit_analysis::ColorHSV; use crate::data::repositories::outfit_matching_repository::OutfitMatchingRepository; use crate::data::repositories::outfit_item_repository::OutfitItemRepository; use crate::business::errors::BusinessError; use anyhow::Result; use std::sync::Arc; use std::collections::HashMap; /// 服装搭配业务服务 /// 遵循 Tauri 开发规范的业务逻辑层设计原则 pub struct OutfitMatchingService { repository: Arc, item_repository: Arc, } impl OutfitMatchingService { /// 创建新的服装搭配服务实例 pub fn new( repository: Arc, item_repository: Arc ) -> Self { Self { repository, item_repository } } /// 创建服装搭配 pub async fn create_matching(&self, request: CreateOutfitMatchingRequest) -> Result { // 验证输入数据 self.validate_create_request(&request)?; // 检查项目中是否存在相同名称的搭配 let existing_matchings = self.repository.list(&OutfitMatchingQueryOptions { project_id: Some(request.project_id.clone()), filters: None, sort_by: None, sort_order: None, limit: None, offset: None, })?; if existing_matchings.iter().any(|m| m.matching_name == request.matching_name) { return Err(BusinessError::DuplicateName(request.matching_name).into()); } // 检查所有单品是否存在 let mut items = Vec::new(); for item_id in &request.item_ids { let item = self.item_repository.get_by_id(item_id)? .ok_or_else(|| BusinessError::NotFound(format!("服装单品不存在: {}", item_id)))?; items.push(item); } // 创建搭配记录 let matching = self.repository.create(&request)?; // 计算搭配评分和建议 let (score_details, suggestions) = self.calculate_matching_score_and_suggestions(&items); // 构建搭配单品列表 let matching_items = self.build_matching_items(&items); // 提取色彩搭配方案 let color_palette = self.extract_color_palette(&items); // 更新搭配记录 let mut update_request = UpdateOutfitMatchingRequest { matching_name: None, matching_type: None, item_ids: None, occasion_tags: None, season_tags: None, style_description: None, is_favorite: None, }; // 更新搭配记录(在实际实现中,这些字段应该直接在创建时设置) // 这里模拟更新操作 self.repository.update(&matching.id, &update_request)?; // 返回完整的搭配记录 let updated_matching = self.repository.get_by_id(&matching.id)? .ok_or_else(|| BusinessError::NotFound(format!("搭配记录不存在: {}", matching.id)))?; Ok(updated_matching) } /// 获取服装搭配列表 pub async fn get_matchings(&self, options: OutfitMatchingQueryOptions) -> Result> { Ok(self.repository.list(&options)?) } /// 根据ID获取服装搭配 pub async fn get_matching_by_id(&self, id: &str) -> Result> { if id.trim().is_empty() { return Err(BusinessError::InvalidInput("ID不能为空".to_string()).into()); } Ok(self.repository.get_by_id(id)?) } /// 更新服装搭配 pub async fn update_matching(&self, id: &str, request: UpdateOutfitMatchingRequest) -> Result<()> { if id.trim().is_empty() { return Err(BusinessError::InvalidInput("ID不能为空".to_string()).into()); } // 检查搭配是否存在 let matching = self.repository.get_by_id(id)? .ok_or_else(|| BusinessError::NotFound(format!("搭配记录不存在: {}", id)))?; // 检查名称是否重复 if let Some(name) = &request.matching_name { if name != &matching.matching_name { let existing_matchings = self.repository.list(&OutfitMatchingQueryOptions { project_id: Some(matching.project_id.clone()), filters: None, sort_by: None, sort_order: None, limit: None, offset: None, })?; if existing_matchings.iter().any(|m| m.matching_name == *name) { return Err(BusinessError::DuplicateName(name.clone()).into()); } } } // 更新搭配记录 self.repository.update(id, &request)?; Ok(()) } /// 删除服装搭配 pub async fn delete_matching(&self, id: &str) -> Result<()> { if id.trim().is_empty() { return Err(BusinessError::InvalidInput("ID不能为空".to_string()).into()); } // 检查搭配是否存在 if self.repository.get_by_id(id)?.is_none() { return Err(BusinessError::NotFound(format!("搭配记录不存在: {}", id)).into()); } self.repository.delete(id)?; Ok(()) } /// 增加穿着次数 pub async fn increment_wear_count(&self, id: &str) -> Result<()> { if id.trim().is_empty() { return Err(BusinessError::InvalidInput("ID不能为空".to_string()).into()); } // 检查搭配是否存在 if self.repository.get_by_id(id)?.is_none() { return Err(BusinessError::NotFound(format!("搭配记录不存在: {}", id)).into()); } self.repository.increment_wear_count(id)?; Ok(()) } /// 智能搭配推荐 pub async fn smart_matching(&self, request: SmartMatchingRequest) -> Result { // 验证请求 if request.project_id.trim().is_empty() { return Err(BusinessError::InvalidInput("项目ID不能为空".to_string()).into()); } // 获取项目中的所有单品 let all_items = self.item_repository.list(&crate::data::models::outfit_item::OutfitItemQueryOptions { project_id: Some(request.project_id.clone()), category: None, styles: None, color_similarity_threshold: None, target_color: None, brand: None, tags: None, limit: None, offset: None, })?; // 过滤掉排除的单品 let candidate_items: Vec = if let Some(exclude_ids) = &request.exclude_item_ids { all_items.into_iter() .filter(|item| !exclude_ids.contains(&item.id)) .collect() } else { all_items }; // 如果指定了基础单品,则基于它进行搭配 let mut base_item: Option = None; if let Some(base_id) = &request.base_item_id { base_item = self.item_repository.get_by_id(base_id)?; if base_item.is_none() { return Err(BusinessError::NotFound(format!("基础单品不存在: {}", base_id)).into()); } } // 生成搭配组合 let matchings = self.generate_outfit_combinations( &candidate_items, base_item.as_ref(), request.preferred_styles.as_deref(), request.target_occasion.as_deref(), request.target_season.as_deref(), request.color_preferences.as_deref(), request.max_results.unwrap_or(5) )?; // 构建推荐结果 let result = SmartMatchingResult { recommended_matchings: matchings, reasoning: "基于您的偏好和搭配规则生成的推荐搭配".to_string(), confidence_score: 0.85, // 示例置信度 }; Ok(result) } /// 生成服装搭配组合 fn generate_outfit_combinations( &self, items: &[OutfitItem], base_item: Option<&OutfitItem>, preferred_styles: Option<&[OutfitStyle]>, target_occasion: Option<&str>, target_season: Option<&str>, color_preferences: Option<&[ColorHSV]>, max_results: u32 ) -> Result> { // 按类别分组单品 let mut items_by_category: HashMap> = HashMap::new(); for item in items { items_by_category.entry(item.category.clone()) .or_insert_with(Vec::new) .push(item); } // 生成搭配组合(简化版) let mut matchings = Vec::new(); // 如果有基础单品,则基于它生成搭配 if let Some(base) = base_item { // 为每个类别选择最匹配的单品 let mut matching_items = vec![base.clone()]; for (category, category_items) in &items_by_category { // 跳过与基础单品相同类别的单品 if *category == base.category { continue; } // 选择最匹配的单品 if let Some(best_match) = self.find_best_matching_item( base, category_items, preferred_styles, color_preferences ) { matching_items.push(best_match.clone()); } } // 创建搭配 if matching_items.len() > 1 { let matching = self.create_matching_from_items( &matching_items, MatchingType::StyleConsistent, target_occasion, target_season ); matchings.push(matching); } } else { // 没有基础单品,生成多种搭配组合 // 这里简化实现,实际应用中应该有更复杂的算法 // 尝试上装+下装的组合 if let (Some(tops), Some(bottoms)) = ( items_by_category.get(&OutfitCategory::Top), items_by_category.get(&OutfitCategory::Bottom) ) { for top in tops.iter().take(3) { for bottom in bottoms.iter().take(3) { let mut outfit_items = vec![(*top).clone(), (*bottom).clone()]; // 可能添加外套 if let Some(outerwears) = items_by_category.get(&OutfitCategory::Outerwear) { if let Some(outerwear) = outerwears.first() { outfit_items.push((*outerwear).clone()); } } // 可能添加鞋子 if let Some(footwears) = items_by_category.get(&OutfitCategory::Footwear) { if let Some(footwear) = footwears.first() { outfit_items.push((*footwear).clone()); } } let matching = self.create_matching_from_items( &outfit_items, MatchingType::StyleConsistent, target_occasion, target_season ); matchings.push(matching); if matchings.len() >= max_results as usize { break; } } if matchings.len() >= max_results as usize { break; } } } // 尝试连衣裙+配饰的组合 if matchings.len() < max_results as usize { if let Some(dresses) = items_by_category.get(&OutfitCategory::Dress) { for dress in dresses.iter().take(2) { let mut outfit_items = vec![(*dress).clone()]; // 添加配饰 if let Some(accessories) = items_by_category.get(&OutfitCategory::Accessory) { if let Some(accessory) = accessories.first() { outfit_items.push((*accessory).clone()); } } // 可能添加外套 if let Some(outerwears) = items_by_category.get(&OutfitCategory::Outerwear) { if let Some(outerwear) = outerwears.first() { outfit_items.push((*outerwear).clone()); } } let matching = self.create_matching_from_items( &outfit_items, MatchingType::StyleConsistent, target_occasion, target_season ); matchings.push(matching); if matchings.len() >= max_results as usize { break; } } } } } // 限制结果数量 matchings.truncate(max_results as usize); Ok(matchings) } /// 查找最匹配的单品 fn find_best_matching_item<'a>( &self, base_item: &OutfitItem, candidates: &[&'a OutfitItem], preferred_styles: Option<&[OutfitStyle]>, color_preferences: Option<&[ColorHSV]> ) -> Option<&'a OutfitItem> { if candidates.is_empty() { return None; } // 计算每个候选项的匹配分数 let mut scored_candidates: Vec<(&OutfitItem, f64)> = candidates.iter() .map(|item| { let mut score = 0.0; // 颜色匹配度 (权重: 0.4) let color_similarity = base_item.color_primary.similarity(&item.color_primary); score += color_similarity * 0.4; // 风格匹配度 (权重: 0.3) let style_similarity = self.calculate_style_similarity(&base_item.styles, &item.styles); score += style_similarity * 0.3; // 偏好风格匹配 (权重: 0.2) if let Some(styles) = preferred_styles { let preferred_match = item.styles.iter() .filter(|style| styles.contains(style)) .count() as f64 / styles.len().max(1) as f64; score += preferred_match * 0.2; } // 偏好颜色匹配 (权重: 0.1) if let Some(colors) = color_preferences { let mut best_color_match: f64 = 0.0; for color in colors { let match_score = item.color_primary.similarity(color); best_color_match = best_color_match.max(match_score); } score += best_color_match * 0.1; } (*item, score) }) .collect(); // 按分数降序排序 scored_candidates.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); // 返回最高分的候选项 scored_candidates.first().map(move |(item, _)| *item) } /// 从单品列表创建搭配 fn create_matching_from_items( &self, items: &[OutfitItem], matching_type: MatchingType, occasion: Option<&str>, season: Option<&str> ) -> OutfitMatching { // 计算搭配评分和建议 let (score_details, suggestions) = self.calculate_matching_score_and_suggestions(items); // 构建搭配单品列表 let matching_items = self.build_matching_items(items); // 提取色彩搭配方案 let color_palette = self.extract_color_palette(items); // 生成风格描述 let style_description = self.generate_style_description(items); // 构建场合标签 let occasion_tags = if let Some(occ) = occasion { vec![occ.to_string()] } else { Vec::new() }; // 构建季节标签 let season_tags = if let Some(sea) = season { vec![sea.to_string()] } else { Vec::new() }; // 创建搭配记录 OutfitMatching { id: uuid::Uuid::new_v4().to_string(), project_id: items.first().map(|i| i.project_id.clone()).unwrap_or_default(), matching_name: format!("搭配 {}", chrono::Utc::now().format("%Y%m%d%H%M%S")), matching_type, items: matching_items, score_details, suggestions, occasion_tags, season_tags, style_description, color_palette, is_favorite: false, wear_count: 0, last_worn_date: None, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), } } /// 计算搭配评分和建议 fn calculate_matching_score_and_suggestions(&self, items: &[OutfitItem]) -> (MatchingScoreDetails, Vec) { // 计算颜色和谐度 let color_harmony_score = self.calculate_color_harmony(items); // 计算风格一致性 let style_consistency_score = self.calculate_style_consistency(items); // 计算比例协调度 let proportion_score = 0.75; // 简化实现 // 计算场合适宜度 let occasion_appropriateness = 0.8; // 简化实现 // 计算时尚度 let trend_factor = 0.7; // 简化实现 // 创建评分详情 let mut score_details = MatchingScoreDetails { color_harmony_score, style_consistency_score, proportion_score, occasion_appropriateness, trend_factor, overall_score: 0.0, }; // 计算综合评分 score_details.calculate_overall_score(); // 生成搭配建议 let mut suggestions = Vec::new(); // 如果颜色和谐度较低,添加颜色建议 if color_harmony_score < 0.6 { suggestions.push(MatchingSuggestion { suggestion_type: "颜色调整".to_string(), description: "考虑选择更协调的颜色组合,可以尝试使用互补色或类似色".to_string(), priority: 4, }); } // 如果风格一致性较低,添加风格建议 if style_consistency_score < 0.6 { suggestions.push(MatchingSuggestion { suggestion_type: "风格优化".to_string(), description: "搭配中的单品风格不够统一,建议选择风格更一致的单品".to_string(), priority: 3, }); } (score_details, suggestions) } /// 计算颜色和谐度 fn calculate_color_harmony(&self, items: &[OutfitItem]) -> f64 { if items.len() <= 1 { return 1.0; // 单个单品默认和谐度为1.0 } let mut total_similarity = 0.0; let mut comparison_count = 0; // 计算所有单品之间的颜色相似度 for i in 0..items.len() { for j in i+1..items.len() { let similarity = items[i].color_primary.similarity(&items[j].color_primary); total_similarity += similarity; comparison_count += 1; } } if comparison_count == 0 { return 1.0; } // 返回平均相似度 total_similarity / comparison_count as f64 } /// 计算风格一致性 fn calculate_style_consistency(&self, items: &[OutfitItem]) -> f64 { if items.len() <= 1 { return 1.0; // 单个单品默认一致性为1.0 } // 收集所有风格 let mut all_styles = Vec::new(); for item in items { for style in &item.styles { if !all_styles.contains(style) { all_styles.push(style.clone()); } } } if all_styles.is_empty() { return 0.5; // 没有风格信息,返回中等一致性 } // 计算每个单品与所有风格的匹配度 let mut total_consistency = 0.0; for item in items { let mut style_matches = 0; for style in &all_styles { if item.styles.contains(style) { style_matches += 1; } } let item_consistency = style_matches as f64 / all_styles.len() as f64; total_consistency += item_consistency; } // 返回平均一致性 total_consistency / items.len() as f64 } /// 构建搭配单品列表 fn build_matching_items(&self, items: &[OutfitItem]) -> Vec { items.iter().map(|item| { MatchingOutfitItem { item_id: item.id.clone(), item_name: item.name.clone(), category: item.category.clone(), color_primary: item.color_primary.clone(), styles: item.styles.clone(), role_in_outfit: self.determine_item_role(item), image_url: item.image_urls.first().cloned(), } }).collect() } /// 确定单品在搭配中的角色 fn determine_item_role(&self, item: &OutfitItem) -> String { match item.category { OutfitCategory::Top | OutfitCategory::Bottom | OutfitCategory::Dress => "主角".to_string(), OutfitCategory::Outerwear => "重点".to_string(), OutfitCategory::Footwear | OutfitCategory::Accessory => "配角".to_string(), _ => "点缀".to_string(), } } /// 提取色彩搭配方案 fn extract_color_palette(&self, items: &[OutfitItem]) -> Vec { let mut palette = Vec::new(); for item in items { palette.push(item.color_primary.clone()); if let Some(secondary) = &item.color_secondary { palette.push(secondary.clone()); } } // 去重 palette.dedup_by(|a, b| a.similarity(b) > 0.9); palette } /// 生成风格描述 fn generate_style_description(&self, items: &[OutfitItem]) -> String { if items.is_empty() { return "未知风格".to_string(); } // 统计风格出现频率 let mut style_counts: HashMap = HashMap::new(); for item in items { for style in &item.styles { *style_counts.entry(style.clone()).or_insert(0) += 1; } } // 找出最常见的风格 let mut sorted_styles: Vec<(OutfitStyle, usize)> = style_counts.into_iter().collect(); sorted_styles.sort_by(|a, b| b.1.cmp(&a.1)); if sorted_styles.is_empty() { return "简约搭配".to_string(); } // 生成描述 let main_style = &sorted_styles[0].0; let style_name = main_style.to_string(); if sorted_styles.len() == 1 || sorted_styles[0].1 > sorted_styles[1].1 { format!("{}风格搭配", style_name) } else { let second_style = &sorted_styles[1].0; format!("{}与{}混搭风格", style_name, second_style.to_string()) } } /// 计算风格相似度 fn calculate_style_similarity(&self, styles1: &[OutfitStyle], styles2: &[OutfitStyle]) -> f64 { if styles1.is_empty() || styles2.is_empty() { return 0.5; // 默认中等相似度 } let common_styles = styles1.iter() .filter(|style1| styles2.contains(style1)) .count(); let total_unique_styles = styles1.len() + styles2.len() - common_styles; if total_unique_styles == 0 { 1.0 } else { common_styles as f64 / total_unique_styles as f64 } } /// 获取服装搭配统计信息 pub async fn get_matching_stats(&self, project_id: Option<&str>) -> Result { Ok(self.repository.get_stats(project_id)?) } /// 验证创建请求 fn validate_create_request(&self, request: &CreateOutfitMatchingRequest) -> Result<()> { if request.project_id.trim().is_empty() { return Err(BusinessError::InvalidInput("项目ID不能为空".to_string()).into()); } if request.matching_name.trim().is_empty() { return Err(BusinessError::InvalidInput("搭配名称不能为空".to_string()).into()); } if request.item_ids.is_empty() { return Err(BusinessError::InvalidInput("搭配单品列表不能为空".to_string()).into()); } Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::data::models::outfit_analysis::ColorHSV; use crate::data::models::outfit_item::{OutfitCategory, OutfitStyle}; #[test] fn test_calculate_color_harmony() { // 创建测试用的服装单品 let items = vec![ OutfitItem { id: "1".to_string(), project_id: "test".to_string(), analysis_id: None, name: "红色T恤".to_string(), category: OutfitCategory::Top, brand: None, model: None, color_primary: ColorHSV::new(0.0, 0.8, 0.9), // 红色 color_secondary: None, styles: vec![OutfitStyle::Casual], design_elements: vec![], size: None, material: None, price: None, purchase_date: None, image_urls: vec![], tags: vec![], notes: None, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), }, OutfitItem { id: "2".to_string(), project_id: "test".to_string(), analysis_id: None, name: "红色裤子".to_string(), category: OutfitCategory::Bottom, brand: None, model: None, color_primary: ColorHSV::new(0.02, 0.75, 0.85), // 相似的红色 color_secondary: None, styles: vec![OutfitStyle::Casual], design_elements: vec![], size: None, material: None, price: None, purchase_date: None, image_urls: vec![], tags: vec![], notes: None, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), }, ]; // 创建模拟的仓库 let matching_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let item_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let service = OutfitMatchingService::new(matching_repo, item_repo); let harmony_score = service.calculate_color_harmony(&items); // 相似颜色应该有较高的和谐度 assert!(harmony_score > 0.7); } #[test] fn test_calculate_style_consistency() { let items = vec![ OutfitItem { id: "1".to_string(), project_id: "test".to_string(), analysis_id: None, name: "休闲T恤".to_string(), category: OutfitCategory::Top, brand: None, model: None, color_primary: ColorHSV::new(0.0, 0.8, 0.9), color_secondary: None, styles: vec![OutfitStyle::Casual, OutfitStyle::Trendy], design_elements: vec![], size: None, material: None, price: None, purchase_date: None, image_urls: vec![], tags: vec![], notes: None, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), }, OutfitItem { id: "2".to_string(), project_id: "test".to_string(), analysis_id: None, name: "休闲裤子".to_string(), category: OutfitCategory::Bottom, brand: None, model: None, color_primary: ColorHSV::new(0.6, 0.5, 0.8), color_secondary: None, styles: vec![OutfitStyle::Casual], design_elements: vec![], size: None, material: None, price: None, purchase_date: None, image_urls: vec![], tags: vec![], notes: None, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), }, ]; let matching_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let item_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let service = OutfitMatchingService::new(matching_repo, item_repo); let consistency_score = service.calculate_style_consistency(&items); // 有共同风格的单品应该有较高的一致性 assert!(consistency_score > 0.5); } #[test] fn test_determine_item_role() { let matching_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let item_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let service = OutfitMatchingService::new(matching_repo, item_repo); let top_item = OutfitItem { id: "1".to_string(), project_id: "test".to_string(), analysis_id: None, name: "T恤".to_string(), category: OutfitCategory::Top, brand: None, model: None, color_primary: ColorHSV::new(0.0, 0.8, 0.9), color_secondary: None, styles: vec![], design_elements: vec![], size: None, material: None, price: None, purchase_date: None, image_urls: vec![], tags: vec![], notes: None, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), }; let role = service.determine_item_role(&top_item); assert_eq!(role, "主角"); let accessory_item = OutfitItem { category: OutfitCategory::Accessory, ..top_item.clone() }; let accessory_role = service.determine_item_role(&accessory_item); assert_eq!(accessory_role, "配角"); } #[test] fn test_generate_style_description() { let items = vec![ OutfitItem { id: "1".to_string(), project_id: "test".to_string(), analysis_id: None, name: "休闲T恤".to_string(), category: OutfitCategory::Top, brand: None, model: None, color_primary: ColorHSV::new(0.0, 0.8, 0.9), color_secondary: None, styles: vec![OutfitStyle::Casual, OutfitStyle::Casual], // 重复的休闲风格 design_elements: vec![], size: None, material: None, price: None, purchase_date: None, image_urls: vec![], tags: vec![], notes: None, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), }, ]; let matching_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let item_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let service = OutfitMatchingService::new(matching_repo, item_repo); let description = service.generate_style_description(&items); assert!(description.contains("休闲")); } #[test] fn test_validate_create_request() { let matching_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let item_repo = std::sync::Arc::new(unsafe { std::mem::zeroed() }); let service = OutfitMatchingService::new(matching_repo, item_repo); // 测试有效请求 let valid_request = CreateOutfitMatchingRequest { project_id: "test_project".to_string(), matching_name: "测试搭配".to_string(), matching_type: MatchingType::StyleConsistent, item_ids: vec!["item1".to_string(), "item2".to_string()], occasion_tags: vec![], season_tags: vec![], style_description: None, }; assert!(service.validate_create_request(&valid_request).is_ok()); // 测试无效请求 - 空项目ID let invalid_request = CreateOutfitMatchingRequest { project_id: "".to_string(), ..valid_request.clone() }; assert!(service.validate_create_request(&invalid_request).is_err()); // 测试无效请求 - 空搭配名称 let invalid_request = CreateOutfitMatchingRequest { matching_name: "".to_string(), ..valid_request.clone() }; assert!(service.validate_create_request(&invalid_request).is_err()); // 测试无效请求 - 空单品列表 let invalid_request = CreateOutfitMatchingRequest { item_ids: vec![], ..valid_request.clone() }; assert!(service.validate_create_request(&invalid_request).is_err()); } }