Files
rust-imager/src/models.rs
2026-03-28 12:36:26 +08:00

230 lines
6.5 KiB
Rust

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<String>,
pub cover_image_id: Option<Uuid>,
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<String>,
pub title: Option<String>,
pub description: Option<String>,
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<Image>,
pub image_count: i64,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AlbumInfo {
pub id: Uuid,
pub title: String,
pub document: String,
pub description: Option<String>,
pub cover_image_id: Option<String>,
pub image_count: i64,
pub created_at: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateAlbum {
pub title: String,
pub document: String,
pub description: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CreateImage {
pub album_id: Uuid,
pub filename: String,
pub original_url: Option<String>,
pub title: Option<String>,
pub description: Option<String>,
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<Uuid>,
}
#[derive(Debug, Deserialize)]
pub struct AlbumIndex {
pub title: String,
pub images: Vec<String>,
}
// 内存缓存结构
#[derive(Debug, Clone)]
pub struct AlbumsCache {
pub albums: Vec<AlbumInfo>,
pub search_index: HashMap<String, Vec<AlbumInfo>>,
pub last_updated: chrono::DateTime<chrono::Utc>,
}
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<AlbumInfo>) {
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<AlbumInfo> {
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
}
}