import { invoke } from '@tauri-apps/api/core'; import { MaterialSearchRequest, MaterialSearchResponse, MaterialSearchResult, GenerateSearchQueryRequest, GenerateSearchQueryResponse, MaterialSearchConfig, OutfitRecommendation, } from '../types/outfitRecommendation'; /** * 素材库检索API服务 * 遵循 Tauri 开发规范的API服务设计原则 */ export class MaterialSearchService { /** * 为穿搭方案生成智能检索条件 */ static async generateSearchQuery( recommendation: OutfitRecommendation, options?: { include_colors?: boolean; include_styles?: boolean; include_occasions?: boolean; include_seasons?: boolean; } ): Promise { try { const request: GenerateSearchQueryRequest = { recommendation, options, }; console.log('🔍 生成素材检索条件:', request); const response = await invoke( 'generate_material_search_query', { request } ); console.log('✅ 检索条件生成成功:', response); return response; } catch (error) { console.error('❌ 检索条件生成失败:', error); throw new Error(`检索条件生成失败: ${error}`); } } /** * 执行素材库检索 */ static async searchMaterials( request: MaterialSearchRequest ): Promise { try { console.log('🔍 执行素材库检索:', request); const response = await invoke( 'search_materials_for_outfit', { request } ); console.log('✅ 素材检索成功:', response); return response; } catch (error) { console.error('❌ 素材检索失败:', error); throw new Error(`素材检索失败: ${error}`); } } /** * 快速素材检索(使用默认配置) */ static async quickSearch( recommendationId: string, query: string, page: number = 1, pageSize: number = 9 ): Promise { try { console.log('🔍 快速素材检索:', { recommendationId, query, page, pageSize }); const response = await invoke( 'quick_material_search', { recommendationId, query, page, pageSize, } ); console.log('✅ 快速检索成功:', response); return response; } catch (error) { console.error('❌ 快速检索失败:', error); throw new Error(`快速检索失败: ${error}`); } } /** * 执行分页检索 */ static async searchWithPagination( request: MaterialSearchRequest, page: number, pageSize: number = 9 ): Promise { try { const paginatedRequest: MaterialSearchRequest = { ...request, pagination: { page, page_size: pageSize, }, }; return await this.searchMaterials(paginatedRequest); } catch (error) { console.error('Failed to perform paginated material search:', error); throw new Error(`分页检索失败: ${error}`); } } /** * 为穿搭方案生成并执行检索 */ static async generateAndSearch( recommendation: OutfitRecommendation, page: number = 1, pageSize: number = 9, options?: { include_colors?: boolean; include_styles?: boolean; include_occasions?: boolean; include_seasons?: boolean; } ): Promise<{ queryResponse: GenerateSearchQueryResponse; searchResponse: MaterialSearchResponse; }> { try { // 1. 生成检索条件 const queryResponse = await this.generateSearchQuery(recommendation, options); // 2. 执行检索 const searchRequest: MaterialSearchRequest = { query: queryResponse.query, recommendation_id: recommendation.id, search_config: queryResponse.search_config, pagination: { page, page_size: pageSize, }, }; const searchResponse = await this.searchMaterials(searchRequest); return { queryResponse, searchResponse, }; } catch (error) { console.error('Failed to generate and search materials:', error); throw new Error(`生成并检索失败: ${error}`); } } /** * 获取检索统计信息 */ static getSearchStats(response: MaterialSearchResponse): { totalResults: number; searchTime: number; averageScore: number; topCategories: string[]; } { const totalResults = response.total_size; const searchTime = response.search_time_ms; // 计算平均评分 const averageScore = response.results.length > 0 ? response.results.reduce((sum, result) => sum + result.relevance_score, 0) / response.results.length : 0; // 统计热门类别 const categoryCount: Record = {}; response.results.forEach(result => { result.products.forEach(product => { categoryCount[product.category] = (categoryCount[product.category] || 0) + 1; }); }); const topCategories = Object.entries(categoryCount) .sort(([, a], [, b]) => b - a) .slice(0, 5) .map(([category]) => category); return { totalResults, searchTime, averageScore, topCategories, }; } /** * 格式化搜索时间 */ static formatSearchTime(timeMs: number): string { if (timeMs < 1000) { return `${timeMs}ms`; } else if (timeMs < 60000) { return `${(timeMs / 1000).toFixed(1)}s`; } else { return `${Math.floor(timeMs / 60000)}m ${Math.floor((timeMs % 60000) / 1000)}s`; } } /** * 格式化相关性评分 */ static formatRelevanceScore(score: number): string { return `${(score * 100).toFixed(1)}%`; } /** * 检查是否有更多结果 */ static hasMoreResults(response: MaterialSearchResponse): boolean { return response.current_page < response.total_pages; } /** * 检查是否有上一页 */ static hasPreviousResults(response: MaterialSearchResponse): boolean { return response.current_page > 1; } /** * 计算总页数 */ static getTotalPages(totalSize: number, pageSize: number): number { return Math.ceil(totalSize / pageSize); } /** * 生成搜索摘要 */ static generateSearchSummary(response: MaterialSearchResponse, query: string): string { const { total_size, search_time_ms, results } = response; const timeStr = this.formatSearchTime(search_time_ms); if (total_size === 0) { return `未找到与"${query}"相关的素材`; } const avgScore = results.length > 0 ? results.reduce((sum, r) => sum + r.relevance_score, 0) / results.length : 0; return `找到 ${total_size} 个相关素材,用时 ${timeStr},平均相关度 ${this.formatRelevanceScore(avgScore)}`; } /** * 获取页面范围信息 */ static getPageRangeInfo(currentPage: number, pageSize: number, totalSize: number): { start: number; end: number; total: number; } { const start = (currentPage - 1) * pageSize + 1; const end = Math.min(currentPage * pageSize, totalSize); return { start, end, total: totalSize, }; } /** * 创建默认搜索配置 */ static createDefaultSearchConfig(): MaterialSearchConfig { return { relevance_threshold: 'HIGH', categories: [], environments: [], color_filters: {}, design_styles: {}, max_results: 50, }; } /** * 验证搜索请求 */ static validateSearchRequest(request: MaterialSearchRequest): string[] { const errors: string[] = []; if (!request.query || request.query.trim().length === 0) { errors.push('搜索查询不能为空'); } if (!request.recommendation_id || request.recommendation_id.trim().length === 0) { errors.push('穿搭方案ID不能为空'); } if (request.pagination.page < 1) { errors.push('页码必须大于0'); } if (request.pagination.page_size < 1 || request.pagination.page_size > 100) { errors.push('每页大小必须在1-100之间'); } return errors; } /** * 批量处理搜索结果 */ static async batchProcessResults( results: MaterialSearchResult[], processor: (result: MaterialSearchResult) => Promise ): Promise { try { const processedResults = await Promise.all( results.map(result => processor(result)) ); return processedResults; } catch (error) { console.error('Failed to batch process results:', error); throw new Error(`批量处理失败: ${error}`); } } } /** * 导出默认实例 */ export default MaterialSearchService;