| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Microsoft.EntityFrameworkCore;
- namespace Application.Features.Api.Store.Inventory.Groups;
- internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
- {
- public async Task<Response> Handle(Query request, CancellationToken ct)
- {
- // 1단계: ProductID 단위 그룹 + aggregations (EF Core 가 SQL 로 깔끔히 번역).
- // anonymous key + 다중 join + Response 생성자 projection 을 한 번에 시도하면
- // translation 실패하므로, 그룹 aggregations 만 먼저 가져온다.
- var groupedQuery = db.MemberInventory.AsNoTracking()
- .Where(i => i.MemberID == request.MemberID && i.DeletedAt == null)
- .GroupBy(i => i.OrderItem!.ProductID)
- .Select(g => new
- {
- ProductID = g.Key,
- TotalCount = g.Count(),
- UnusedCount = g.Count(x => x.UsedAt == null),
- EarliestAcquiredAt = g.Min(x => x.AcquiredAt)
- });
- var total = await groupedQuery.CountAsync(ct);
- var pagedGroups = await groupedQuery
- .OrderByDescending(g => g.EarliestAcquiredAt)
- .Skip((request.Page - 1) * request.PerPage)
- .Take(request.PerPage)
- .ToListAsync(ct);
- if (pagedGroups.Count == 0)
- {
- return new Response(total, []);
- }
- // 2단계: 표시 페이지의 Product + Game 메타데이터 한 번에 fetch.
- var productIDs = pagedGroups.Select(g => g.ProductID).ToList();
- var products = await db.Product.AsNoTracking()
- .Where(p => productIDs.Contains(p.ID))
- .Select(p => new
- {
- p.ID,
- p.Name,
- p.Thumbnail,
- GameName = p.Game!.KorName
- })
- .ToListAsync(ct);
- var productMap = products.ToDictionary(p => p.ID);
- // 3단계: 정렬 순서 유지하면서 stitch.
- var list = pagedGroups.Select(g =>
- {
- var meta = productMap.GetValueOrDefault(g.ProductID);
- return new Response.GroupRow(
- g.ProductID,
- meta?.Name ?? string.Empty,
- meta?.Thumbnail,
- meta?.GameName ?? string.Empty,
- g.TotalCount,
- g.UnusedCount,
- g.EarliestAcquiredAt
- );
- }).ToList();
- return new Response(total, list);
- }
- }
|