From b6ffc60331263ebc42e282247bce11db3c17cff1 Mon Sep 17 00:00:00 2001 From: iven Date: Wed, 3 Jun 2026 15:51:05 +0800 Subject: [PATCH] =?UTF-8?q?perf(diary):=20sticker=5Fservice=20=E6=89=B9?= =?UTF-8?q?=E9=87=8F=20GROUP=20BY=20=E6=9B=BF=E4=BB=A3=20N+1=20=E8=B4=B4?= =?UTF-8?q?=E7=BA=B8=E8=AE=A1=E6=95=B0=20=E2=80=94=208a-C04?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - list_sticker_packs: 单次 SQL GROUP BY pack_id 获取所有计数 - 2 次查询(packs + counts)替代 N+1 次 - 使用 PostgreSQL ANY() 传递 UUID 数组 - 测试 80/80 通过 --- .../erp-diary/src/service/sticker_service.rs | 52 ++++++++++++++----- 1 file changed, 38 insertions(+), 14 deletions(-) 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) }