using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Domain.Entities.Wallets.ValueObject; using Microsoft.EntityFrameworkCore; namespace Application.Features.Admin.Donation.List.Export; public sealed class Handler(IAppDbContext db) : IQueryHandler { public async Task Handle(Query request, CancellationToken ct) { var query = db.Donation.AsNoTracking().Include(x => x.Sponsor).Include(x => x.Receiver).AsQueryable(); if (!string.IsNullOrWhiteSpace(request.Keyword)) { query = request.Search switch { 1 => query.Where(x => x.Sponsor != null && x.Sponsor.ID.ToString().Contains(request.Keyword)), 2 => query.Where(x => x.Sponsor != null && x.Sponsor.SID.Contains(request.Keyword)), 3 => query.Where(x => x.Sponsor != null && x.Sponsor.Name != null && x.Sponsor.Name.Contains(request.Keyword)), 4 => query.Where(x => x.Receiver != null && x.Receiver.ID.ToString().Contains(request.Keyword)), 5 => query.Where(x => x.Receiver != null && x.Receiver.SID.Contains(request.Keyword)), 6 => query.Where(x => x.Receiver != null && x.Receiver.Name != null && x.Receiver.Name.Contains(request.Keyword)), 7 => query.Where(x => x.SendName.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); } var list = await query.OrderByDescending(x => x.ID).Select(x => new { x.ID, x.Amount, x.SendName, x.Message, x.CreatedAt, x.SponsorMemberID, x.ReceiverMemberID, SponsorEmail = x.Sponsor != null ? x.Sponsor.Email : string.Empty, SponsorName = x.Sponsor != null ? x.Sponsor.Name : null, SponsorID = x.Sponsor != null ? x.Sponsor.ID : 0, ReceiverEmail = x.Receiver != null ? x.Receiver.Email : string.Empty, ReceiverName = x.Receiver != null ? x.Receiver.Name : null, ReceiverID = x.Receiver != null ? x.Receiver.ID : 0 }).ToListAsync(ct); var memberIDs = list.Select(x => x.SponsorMemberID).Concat(list.Select(x => x.ReceiverMemberID)).Distinct().ToArray(); var wallets = await db.Wallet.AsNoTracking().Where(w => memberIDs.Contains(w.MemberID)).Include(w => w.Balances).ToListAsync(ct); var balanceByMember = wallets.ToDictionary( w => w.MemberID, w => new { Total = (long)w.Balances.Where(b => b.Type != WalletBalanceType.Locked).Sum(b => b.Amount.Value), Donation = (long)(w.Balances.FirstOrDefault(b => b.Type == WalletBalanceType.Donation)?.Amount.Value ?? 0) }); var rows = list.Select(x => { var sponsorBalance = balanceByMember.TryGetValue(x.SponsorMemberID, out var sb) ? sb.Total : 0L; var receiverBalance = balanceByMember.TryGetValue(x.ReceiverMemberID, out var rb) ? rb.Donation : 0L; return new Dictionary { ["ID"] = x.ID, ["후원금액"] = x.Amount, ["보낸이름"] = x.SendName, ["후원자"] = $"[{x.SponsorID}] {x.SponsorEmail}, {x.SponsorName ?? "-"}", ["후원자잔고"] = sponsorBalance, ["피후원자"] = $"[{x.ReceiverID}] {x.ReceiverEmail}, {x.ReceiverName ?? "-"}", ["피후원자잔고"] = receiverBalance, ["전달내용"] = x.Message ?? string.Empty, ["등록일시KST"] = x.CreatedAt.AddHours(9).ToString("yyyy-MM-dd HH:mm:ss") }; }).ToList(); return new Response { Rows = rows }; } }