using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Domain.Entities.Payments.ValueObject; using Microsoft.EntityFrameworkCore; namespace Application.Features.Admin.Payment.Danal.Confirm.Search; public sealed class Handler(IAppDbContext db) : IQueryHandler { public async Task Handle(Query request, CancellationToken ct) { var query = db.DanalConfirm.AsNoTracking().Include(x => x.Member).AsQueryable(); // 키워드 검색 if (!string.IsNullOrWhiteSpace(request.Keyword)) { query = request.Search switch { 1 => query.Where(x => x.TransactionID.Contains(request.Keyword)), 2 => query.Where(x => x.OrderID.Contains(request.Keyword)), 3 => query.Where(x => x.Member != null && x.Member.ID.ToString().Contains(request.Keyword)), 4 => query.Where(x => x.Member != null && x.Member.Name != null && x.Member.Name.Contains(request.Keyword)), 5 => query.Where(x => x.Member != null && x.Member.SID.Contains(request.Keyword)), _ => query }; } // 기간 필터 if (!string.IsNullOrWhiteSpace(request.StartAt) && DateTime.TryParse(request.StartAt, out var startAt)) { query = query.Where(x => x.CreatedAt >= startAt); } if (!string.IsNullOrWhiteSpace(request.EndAt) && DateTime.TryParse(request.EndAt, out var endAt)) { query = query.Where(x => x.CreatedAt <= endAt); } // 탭별 건수 (PayMethod 필터 전) var counts = await query.GroupBy(c => 1).Select(c => new Response.MethodCounts { Card = c.Count(x => x.Method == PaymentMethod.Card), VirtualAccount = c.Count(x => x.Method == PaymentMethod.VirtualAccount), Mobile = c.Count(x => x.Method == PaymentMethod.Mobile), Transfer = c.Count(x => x.Method == PaymentMethod.Transfer) }).FirstOrDefaultAsync(ct) ?? new Response.MethodCounts(); if (request.PayMethod.HasValue) { query = query.Where(x => x.Method == request.PayMethod.Value); } var total = await query.CountAsync(ct); var skip = (request.Page - 1) * request.PerPage; var list = await query .OrderByDescending(x => x.ID) .Skip(skip) .Take(request.PerPage) .Select(x => new { x.ID, x.MemberID, MemberEmail = x.Member != null ? x.Member.Email : string.Empty, MemberName = x.Member != null ? x.Member.Name : null, x.Method, x.TransactionID, x.OrderID, x.Amount, x.TotalAmount, x.ResponseCode, x.ResponseMessage, x.PaymentOrderID, x.CreatedAt }) .ToListAsync(ct); // 같은 PaymentOrderID로 DanalCancel 있는지 조회 (성공한 취소만) var orderIDs = list.Select(x => x.PaymentOrderID).Distinct().ToArray(); var cancels = await db.DanalCancel.AsNoTracking() .Where(c => orderIDs.Contains(c.PaymentOrderID) && c.ResponseCode == "0000") .Select(c => new { c.PaymentOrderID, c.CancelType, c.CancelledAmount }) .ToListAsync(ct); var cancelByOrder = cancels.GroupBy(c => c.PaymentOrderID).ToDictionary( g => g.Key, g => g.First() ); var rows = list.Select((x, idx) => { cancelByOrder.TryGetValue(x.PaymentOrderID, out var cancel); return new Response.Row { Num = total - skip - idx, ID = x.ID, MemberID = x.MemberID, MemberEmail = x.MemberEmail, MemberName = x.MemberName, Method = x.Method, TransactionID = x.TransactionID, OrderID = x.OrderID, Amount = x.Amount, TotalAmount = x.TotalAmount, ResponseCode = x.ResponseCode, ResponseMessage = x.ResponseMessage, CancelType = cancel?.CancelType, CancelledAmount = cancel?.CancelledAmount, CreatedAt = x.CreatedAt }; }).ToList(); return new Response { Total = total, Counts = counts, List = rows }; } }