use serde::{Deserialize, Serialize}; use std::collections::HashMap; use tracing::info; use uuid::Uuid; #[derive(Debug, Serialize, Deserialize)] pub struct Album { pub id: Uuid, pub title: String, pub description: Option, pub cover_image_id: Option, pub image_count: i64, // 新增图片数量字段 pub created_at: String, pub updated_at: String, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Image { pub id: Uuid, pub album_id: Uuid, pub filename: String, pub original_url: Option, pub title: Option, pub description: Option, pub file_size: i64, pub width: i32, pub height: i32, pub mime_type: String, pub created_at: String, } #[derive(Debug, Serialize, Deserialize)] pub struct AlbumWithImages { pub album: Album, pub images: Vec, pub image_count: i64, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct AlbumInfo { pub id: Uuid, pub title: String, pub document: String, pub description: Option, pub cover_image_id: Option, pub image_count: i64, pub created_at: String, } #[derive(Debug, Serialize, Deserialize)] pub struct CreateAlbum { pub title: String, pub document: String, pub description: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CreateImage { pub album_id: Uuid, pub filename: String, pub original_url: Option, pub title: Option, pub description: Option, pub file_size: i64, pub width: i32, pub height: i32, pub mime_type: String, } #[derive(Debug, Serialize, Deserialize)] pub struct Favorite { pub id: Uuid, pub user_id: String, pub image_id: Uuid, pub album_id: Uuid, pub created_at: String, } #[derive(Debug, Serialize, Deserialize)] pub struct CreateFavorite { pub image_id: Uuid, pub album_id: Uuid, } #[derive(Debug, Serialize, Deserialize)] pub struct FavoriteWithImage { pub favorite: Favorite, pub image: Image, pub album_title: String, } #[derive(Debug, Serialize, Deserialize)] pub struct FavoriteStatus { pub is_favorited: bool, pub favorite_id: Option, } #[derive(Debug, Deserialize)] pub struct AlbumIndex { pub title: String, pub images: Vec, } // 内存缓存结构 #[derive(Debug, Clone)] pub struct AlbumsCache { pub albums: Vec, pub search_index: HashMap>, pub last_updated: chrono::DateTime, } impl AlbumsCache { pub fn new() -> Self { Self { albums: Vec::new(), search_index: HashMap::new(), last_updated: chrono::Utc::now(), } } pub fn is_expired(&self) -> bool { let now = chrono::Utc::now(); let duration = now - self.last_updated; duration.num_minutes() >= 30 // 30分钟过期 } pub fn update(&mut self, albums: Vec) { self.albums = albums; // 不再克隆整个向量,而是移动所有权 self.last_updated = chrono::Utc::now(); // 构建搜索索引 self.search_index.clear(); for album in &self.albums { let title_lower = album.title.to_lowercase(); // 为每个单词创建索引 for word in title_lower.split_whitespace() { if word.len() >= 2 { // 只索引长度>=2的单词 self.search_index .entry(word.to_string()) .or_insert_with(Vec::new) .push(album.clone()); // 这里仍需要克隆单个元素 } } // 为完整标题创建索引 self.search_index .entry(title_lower) .or_insert_with(Vec::new) .push(album.clone()); // 这里仍需要克隆单个元素 } info!( "相册缓存已更新,共 {} 个相册,{} 个搜索索引项", self.albums.len(), self.search_index.len() ); } pub fn search(&self, query: &str) -> Vec { let query_lower = query.to_lowercase().trim().to_string(); if query_lower.is_empty() { return Vec::new(); } let mut results = Vec::new(); let mut seen_ids = std::collections::HashSet::new(); // 1. 搜索完整匹配(最高优先级) if let Some(matches) = self.search_index.get(&query_lower) { for album in matches { if seen_ids.insert(album.id) { results.push(album.clone()); } } } // 2. 搜索单词匹配 for word in query_lower.split_whitespace() { if word.len() >= 2 { // 只搜索长度>=2的单词 if let Some(matches) = self.search_index.get(word) { for album in matches { if seen_ids.insert(album.id) { results.push(album.clone()); } } } } } // 3. 模糊搜索:在相册标题和描述中搜索包含查询词的结果 if results.is_empty() { for album in &self.albums { let title_lower = album.title.to_lowercase(); let description_lower = album .description .as_ref() .map(|d| d.to_lowercase()) .unwrap_or_default(); // 检查标题或描述是否包含搜索词 if title_lower.contains(&query_lower) || description_lower.contains(&query_lower) { if seen_ids.insert(album.id) { results.push(album.clone()); } } } } // 4. 如果还没有结果,尝试部分匹配(更宽松的搜索) if results.is_empty() && query_lower.len() >= 2 { for album in &self.albums { let title_lower = album.title.to_lowercase(); // 检查标题是否包含搜索词的任何部分 if query_lower .split_whitespace() .any(|word| word.len() >= 2 && title_lower.contains(word)) { if seen_ids.insert(album.id) { results.push(album.clone()); } } } } results } }