using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Domain.Entities.Store.ValueObject; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Store.Orders.List; internal sealed class Handler(IAppDbContext db) : IQueryHandler { public async Task Handle(Query request, CancellationToken ct) { var query = db.Order.AsNoTracking() .Include(o => o.Channel) .Include(o => o.Items) .ThenInclude(i => i.Product) .Where(o => o.MemberID == request.MemberID); if (request.Status.HasValue) { query = query.Where(o => o.Status == request.Status.Value); } else { // Pending(미결제) 은 사용자 뷰에서 제외 query = query.Where(o => o.Status != OrderStatus.Pending); } // 기간 필터: Year 명시 → 해당 연도 / 미명시 → 최근 6개월 (CreatedAt UTC 기준) if (request.Year.HasValue) { var start = new DateTime(request.Year.Value, 1, 1, 0, 0, 0, DateTimeKind.Utc); var end = start.AddYears(1); query = query.Where(o => o.CreatedAt >= start && o.CreatedAt < end); } else { var sixMonthsAgo = DateTime.UtcNow.AddMonths(-6); query = query.Where(o => o.CreatedAt >= sixMonthsAgo); } // 상품명 검색 — 주문 라인 중 1개라도 매칭되면 주문 노출 if (!string.IsNullOrWhiteSpace(request.Keyword)) { var kw = request.Keyword.Trim(); query = query.Where(o => o.Items.Any(i => i.Product!.Name.Contains(kw))); } var total = await query.CountAsync(ct); var list = await query .OrderByDescending(o => o.ID) .Skip((request.Page - 1) * request.PerPage) .Take(request.PerPage) .Select(o => new { o.ID, o.OrderNumber, o.TotalAmount, o.Status, o.ChannelID, ChannelName = o.Channel != null ? o.Channel.Name : null, HasPhysical = o.Items.Any(i => i.Product!.Type == ProductType.Physical), HasDigital = o.Items.Any(i => i.Product!.Type == ProductType.Digital), o.CreatedAt, o.PaidAt, ItemCount = o.Items.Count, FirstItemName = o.Items.OrderBy(i => i.ID).Select(i => i.Product!.Name).FirstOrDefault() ?? "", FirstItemThumbnail = o.Items.OrderBy(i => i.ID).Select(i => i.Product!.Thumbnail).FirstOrDefault(), ShipmentStatus = db.Shipment.Where(s => s.OrderID == o.ID).Select(s => (ShipmentStatus?)s.Status).FirstOrDefault(), HasActiveRefundRequest = db.OrderRefund.Any(r => r.OrderID == o.ID && r.Status == RefundStatus.Requested) }) .ToListAsync(ct); return new Response( total, [.. list.Select(c => new Response.Row( c.ID, c.OrderNumber, c.TotalAmount, c.Status, c.ChannelID, c.ChannelName, c.HasPhysical, c.HasDigital, c.CreatedAt, c.PaidAt, c.ItemCount, c.FirstItemName, c.FirstItemThumbnail, c.ShipmentStatus, c.HasActiveRefundRequest ))] ); } }