diff --git a/crates/erp-diary/src/service/sticker_service.rs b/crates/erp-diary/src/service/sticker_service.rs index 7f4a8ab..37ce427 100644 --- a/crates/erp-diary/src/service/sticker_service.rs +++ b/crates/erp-diary/src/service/sticker_service.rs @@ -1,8 +1,8 @@ // 贴纸服务 — 贴纸包与贴纸管理 use sea_orm::{ - ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, PaginatorTrait, - QueryFilter, QueryOrder, Set, + ActiveModelTrait, ColumnTrait, ConnectionTrait, DatabaseConnection, EntityTrait, + PaginatorTrait, QueryFilter, QueryOrder, Set, }; use uuid::Uuid; @@ -16,7 +16,8 @@ pub struct StickerService; impl StickerService { /// 获取贴纸包列表 /// - /// 返回所有可用的贴纸包,按分类和名称排序。 + /// 使用 SQL GROUP BY 批量获取贴纸计数,替代逐包 COUNT 查询。 + /// 性能: 2 次查询(packs + counts)替代 N+1 次。 pub async fn list_sticker_packs( tenant_id: Uuid, category: Option, @@ -36,24 +37,47 @@ impl StickerService { .all(db) .await?; - let mut result = Vec::with_capacity(packs.len()); - for pack in packs { - let sticker_count = sticker::Entity::find() - .filter(sticker::Column::PackId.eq(pack.id)) - .filter(sticker::Column::DeletedAt.is_null()) - .count(db) - .await? as i32; + // 批量获取所有贴纸包的贴纸计数 — 单次 SQL GROUP BY(替代 N+1 查询) + let pack_ids: Vec = packs.iter().map(|p| p.id).collect(); + let count_map: std::collections::HashMap = if !pack_ids.is_empty() { + let sql = r#" + SELECT pack_id, COUNT(*) AS count + FROM stickers + WHERE pack_id = ANY($1) + AND deleted_at IS NULL + GROUP BY pack_id + "#; - result.push(StickerPackResp { + let stmt = sea_orm::Statement::from_sql_and_values( + sea_orm::DatabaseBackend::Postgres, + sql, + [pack_ids.into()], + ); + + let rows = db.query_all(stmt).await?; + rows.into_iter() + .filter_map(|row| { + let pack_id: Uuid = row.try_get_by_index::(0).ok()?; + let count: i64 = row.try_get_by_index::(1).ok()?; + Some((pack_id, count)) + }) + .collect() + } else { + std::collections::HashMap::new() + }; + + let result: Vec = packs + .into_iter() + .map(|pack| StickerPackResp { id: pack.id, name: pack.name, description: pack.description, cover_image_url: pack.thumbnail_url, - sticker_count, + sticker_count: *count_map.get(&pack.id).unwrap_or(&0) as i32, is_free: pack.is_free, category: pack.category, - }); - } + }) + .collect(); Ok(result) }