update code level

This commit is contained in:
lychang
2026-07-21 02:17:46 +08:00
parent f572c7370d
commit 005688b58f
4 changed files with 54 additions and 22 deletions

View File

@@ -267,24 +267,30 @@ impl Database {
}).collect()
}
/// 查找重名相册,返回 (keep_id, [duplicate_ids])
pub async fn find_duplicate_albums(&self) -> Vec<(Uuid, Vec<Uuid>)> {
/// 查找重名相册,返回结构:每个元素为 (keep_id, keep_document, [(dup_id, dup_document)])
pub async fn find_duplicate_albums(&self) -> Vec<(Uuid, String, Vec<(Uuid, String)>)> {
let rows = sqlx::query(
r#"SELECT id, title FROM albums WHERE title != '' ORDER BY created_at ASC"#
r#"SELECT id, title, document FROM albums WHERE title != '' ORDER BY created_at ASC"#
).fetch_all(&self.pool).await.unwrap_or_default();
let mut title_map: std::collections::HashMap<String, Vec<Uuid>> = std::collections::HashMap::new();
// title → [(id, document)]
let mut title_map: std::collections::HashMap<String, Vec<(Uuid, String)>> = std::collections::HashMap::new();
for r in &rows {
let title: String = r.get("title");
let id_str: String = r.get("id");
let doc: String = r.get("document");
if let Ok(id) = Uuid::parse_str(&id_str) {
title_map.entry(title).or_default().push(id);
title_map.entry(title).or_default().push((id, doc));
}
}
title_map.into_iter()
.filter(|(_, ids)| ids.len() > 1)
.map(|(_, ids)| (ids[0], ids[1..].to_vec()))
.filter(|(_, items)| items.len() > 1)
.map(|(_, items)| {
let keep = items[0].clone(); // (keep_id, keep_document)
let dups = items[1..].to_vec();
(keep.0, keep.1, dups)
})
.collect()
}

View File

@@ -266,15 +266,33 @@ pub async fn phase_delete_empty_albums(db: &Database) -> usize {
// Phase Z1:去重同名相册
// ════════════════════════════════════════════════
/// 查找同名的相册,只保留第一个(最早创建的),删除其余
pub async fn phase_dedup_albums(db: &Database) -> usize {
/// 查找同名的相册,只保留第一个(最早创建的),删除其余(包括文件夹和 search.db 数据)
pub async fn phase_dedup_albums(db: &Database, search_db: &SearchDb) -> usize {
let dups = db.find_duplicate_albums().await;
let images_dir = &db.config.storage.images_dir;
let mut total_deleted = 0usize;
for (keep, to_delete) in &dups {
let deleted = db.delete_albums_batch(to_delete).await;
total_deleted += deleted;
for id in to_delete {
println!("[管线·PhaseZ1] 删除重复相册 {}(保留 {})", id, keep);
for (keep_id, keep_document, to_delete) in &dups {
let dup_ids: Vec<uuid::Uuid> = to_delete.iter().map(|(id, _)| *id).collect();
// 删除数据库记录
db.delete_albums_batch(&dup_ids).await;
// 删除 search.db 数据
for (id, _) in to_delete {
search_db.delete_album(&id.to_string()).await.ok();
}
// 删除磁盘文件夹
for (_, doc) in to_delete {
let folder = images_dir.join(doc);
if folder.exists() {
if let Err(e) = std::fs::remove_dir_all(&folder) {
eprintln!("[管线·PhaseZ1] 删除文件夹失败 {}: {}", folder.display(), e);
} else {
println!("[管线·PhaseZ1] 删除文件夹 {}", folder.display());
}
}
}
total_deleted += to_delete.len();
for (id, doc) in to_delete {
println!("[管线·PhaseZ1] 删除重复相册 {} ({}),保留 {} ({})", id, doc, keep_id, keep_document);
}
}
if total_deleted == 0 {
@@ -316,12 +334,12 @@ pub struct PipelineResult {
/// Phase ③ 增量解析脏集 → 只扫脏集
/// Phase ④ 人物合并 + 重计 → 1次人物扫描
/// Phase ⑤ 后校验标签中的人名
pub async fn run_pipeline(db: &Database) -> PipelineResult {
pub async fn run_pipeline(db: &Database, search_db: &SearchDb) -> PipelineResult {
// Phase Z0:删除空相册
let empty_albums_deleted = phase_delete_empty_albums(db).await;
// Phase Z1:去重同名相册
let dup_albums_deleted = phase_dedup_albums(db).await;
// Phase Z1:去重同名相册(含删除文件夹和 search.db 数据)
let dup_albums_deleted = phase_dedup_albums(db, search_db).await;
// Phase ①:合并预处理(1 次相册扫描)
let pre = phase_preprocess_combined(db).await;

View File

@@ -122,8 +122,16 @@ async fn main() -> std::io::Result<()> {
Err(e) => error!("[启动] 导入失败: {}", e),
}
// ② 查找新相册 / 半成品
info!("[启动] ② 查找新相册...");
// ② 清理:删除空相册 + 去重
info!("[启动] ② 清理空相册和重复相册...");
let empty = engine::pipeline::phase_delete_empty_albums(&boot_db).await;
let dedup = engine::pipeline::phase_dedup_albums(&boot_db, &boot_search_db).await;
if empty > 0 || dedup > 0 {
info!("[启动] 清理完成:删除空相册 {},去重删除 {}", empty, dedup);
}
// ③ 查找新相册 / 半成品
info!("[启动] ③ 查找新相册...");
let dirty = engine::pipeline::phase_collect_new_albums(&boot_db).await;
let new_count = dirty.len();
if new_count > 0 {
@@ -134,8 +142,8 @@ async fn main() -> std::io::Result<()> {
info!("[启动] 没有新相册");
}
// ③ 刷新所有内存缓存(从 search.db 读取)
info!("[启动] ③ 刷新所有内存缓存...");
// ④ 刷新所有内存缓存(从 search.db 读取)
info!("[启动] ④ 刷新所有内存缓存...");
cache::refresh_all_caches(&boot_db, &boot_search_db, &boot_albums_cache, &boot_persons_cache, &boot_tags_cache).await;
info!("[启动] 启动管线执行完毕(新相册={})", new_count);
});

View File

@@ -8,7 +8,7 @@ pub async fn pipeline_run(
db: web::Data<Arc<Database>>,
search_db: web::Data<Arc<SearchDb>>,
) -> impl Responder {
let result = engine::pipeline::run_pipeline(db.get_ref()).await;
let result = engine::pipeline::run_pipeline(db.get_ref(), search_db.get_ref()).await;
let synced = engine::pipeline::sync_albums_to_search_db(db.get_ref(), search_db.get_ref()).await;
HttpResponse::Ok().json(serde_json::json!({