This commit is contained in:
lychang
2026-07-20 02:15:39 +08:00
parent 1700585a36
commit be581977f3
10 changed files with 399 additions and 115 deletions

141
src/cache/mod.rs vendored
View File

@@ -1,5 +1,6 @@
//! 缓存层 — 内存缓存管理
use crate::db::Database;
use crate::models::*;
use std::collections::HashMap;
use std::sync::Arc;
@@ -88,14 +89,23 @@ impl AlbumsCache {
#[derive(Debug, Clone, Default)]
pub struct TagCache {
pub popular_tags: Vec<TagStat>,
/// tag → 包含该标签的相册列表(倒排索引,供按标签搜索用)
pub tag_albums: HashMap<String, Vec<AlbumInfo>>,
pub last_updated: chrono::DateTime<chrono::Utc>,
}
impl TagCache {
pub fn update(&mut self, tags: Vec<TagStat>) {
pub fn update(&mut self, tags: Vec<TagStat>, tag_albums: HashMap<String, Vec<AlbumInfo>>) {
self.popular_tags = tags;
self.tag_albums = tag_albums;
self.last_updated = chrono::Utc::now();
info!("标签缓存已更新,共 {} 个热门标签", self.popular_tags.len());
info!("标签缓存已更新,共 {} 个热门标签,{} 个标签倒排索引项",
self.popular_tags.len(), self.tag_albums.len());
}
/// 按标签搜索相册(从缓存中检索)
pub fn search_albums(&self, tag: &str) -> Vec<AlbumInfo> {
self.tag_albums.get(tag).cloned().unwrap_or_default()
}
}
@@ -103,30 +113,127 @@ impl TagCache {
#[derive(Debug, Clone, Default)]
pub struct PersonCache {
pub persons: Vec<PersonWithCounts>,
/// 搜索索引:关键词小写 → person_id 集合
pub search_index: HashMap<String, Vec<i64>>,
/// person_id → person 投影(用于按 id 快速访问)
pub id_map: HashMap<i64, PersonWithCounts>,
pub last_updated: chrono::DateTime<chrono::Utc>,
}
impl PersonCache {
pub fn update(&mut self, persons: Vec<PersonWithCounts>) {
self.search_index.clear();
self.id_map.clear();
for p in &persons {
// 构建搜索索引(姓名 + 所有别名)
let name_lower = p.name.to_lowercase();
for word in name_lower.split_whitespace() {
if word.len() >= 2 {
self.search_index.entry(word.to_string()).or_default().push(p.id);
}
}
self.search_index.entry(name_lower.clone()).or_default().push(p.id);
// 显示名
if let Some(ref dn) = p.display_name {
let dn_lower = dn.to_lowercase();
for word in dn_lower.split_whitespace() {
if word.len() >= 2 {
self.search_index.entry(word.to_string()).or_default().push(p.id);
}
}
self.search_index.entry(dn_lower).or_default().push(p.id);
}
// 别名
for alias in &p.aliases {
let alias_lower = alias.to_lowercase();
for word in alias_lower.split_whitespace() {
if word.len() >= 2 {
self.search_index.entry(word.to_string()).or_default().push(p.id);
}
}
self.search_index.entry(alias_lower).or_default().push(p.id);
}
self.id_map.insert(p.id, p.clone());
}
self.persons = persons;
self.last_updated = chrono::Utc::now();
info!("人物缓存已更新,共 {} 人", self.persons.len());
info!("人物缓存已更新,共 {} 人,{} 个搜索索引项",
self.persons.len(), self.search_index.len());
}
}
/// 共享缓存
pub struct AppCache {
pub albums: Arc<RwLock<AlbumsCache>>,
pub tags: Arc<RwLock<TagCache>>,
pub persons: Arc<RwLock<PersonCache>>,
}
/// 从缓存搜索人物(按姓名/别名)
pub fn search(&self, query: &str) -> Vec<PersonWithCounts> {
let q = query.to_lowercase().trim().to_string();
if q.is_empty() || q.len() < 2 { return Vec::new(); }
impl AppCache {
pub fn new() -> Self {
Self {
albums: Arc::new(RwLock::new(AlbumsCache::new())),
tags: Arc::new(RwLock::new(TagCache::default())),
persons: Arc::new(RwLock::new(PersonCache::default())),
let mut seen = std::collections::HashSet::new();
// 1. 完整匹配
if let Some(ids) = self.search_index.get(&q) {
for id in ids {
if seen.insert(*id) {}
}
}
// 2. 单词匹配
for word in q.split_whitespace() {
if word.len() >= 2 {
if let Some(ids) = self.search_index.get(word) {
for id in ids {
seen.insert(*id);
}
}
}
}
// 3. 模糊匹配
if seen.is_empty() {
for p in &self.persons {
let full = format!("{} {} {}", p.name.to_lowercase(),
p.display_name.as_deref().unwrap_or(""),
p.aliases.join(" ").to_lowercase());
if full.contains(&q) {
seen.insert(p.id);
}
}
}
seen.iter()
.filter_map(|id| self.id_map.get(id).cloned())
.collect()
}
}
}
/// 刷新所有缓存(相册 + 标签 + 人物)
pub async fn refresh_all_caches(
db: &Database,
albums_cache: &Arc<RwLock<AlbumsCache>>,
persons_cache: &Arc<RwLock<PersonCache>>,
tags_cache: &Arc<RwLock<TagCache>>,
) {
// 1. 刷新相册
if let Ok(albums) = db.get_all_albums().await {
let mut c = albums_cache.write().await;
c.update(albums.clone());
// 2. 刷新标签
let popular_tags = db.get_popular_tags(200).await;
let tag_albums = db.get_all_tag_albums().await;
let mut tc = tags_cache.write().await;
tc.update(popular_tags, tag_albums);
}
// 3. 刷新人物
let persons = db.get_all_persons_with_aliases().await;
let mut pc = persons_cache.write().await;
pc.update(persons);
info!("全部缓存已刷新");
}
/// 仅刷新人物缓存(管线执行后调用)
pub async fn refresh_persons_cache(db: &Database, persons_cache: &Arc<RwLock<PersonCache>>) {
let persons = db.get_all_persons_with_aliases().await;
let mut pc = persons_cache.write().await;
pc.update(persons);
}