using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Domain.Entities.Wallets.Policy; using Microsoft.EntityFrameworkCore; using SharedKernel.Results; namespace Application.Features.Api.MyPage.Dropdown; public sealed class Handler(IAppDbContext db) : IQueryHandler> { public async Task> Handle(Query request, CancellationToken ct) { var member = await db.Member.AsNoTracking() .Where(m => m.ID == request.MemberID) .Select(m => new { m.SID, m.Name, m.Thumb, m.IsCreator }) .FirstOrDefaultAsync(ct); if (member is null) { return Result.Failure(Error.NotFound("Member.NotFound", "회원을 찾을 수 없습니다.")); } // 후원 가능 잔액 (SpendPolicy 순서의 잔액 합산) var spendableBalance = 0m; var wallet = await db.Wallet.AsNoTracking() .Include(w => w.Balances) .FirstOrDefaultAsync(w => w.MemberID == request.MemberID, ct); if (wallet is not null) { foreach (var balanceType in SpendPolicy.DefaultSpendOrder) { var balance = wallet.Balances.FirstOrDefault(b => b.Type == balanceType); if (balance is not null) { spendableBalance += balance.Amount.Value; } } } // 채널 조회 int? channelID = null; decimal? withdrawableBalance = null; var channel = await db.Channel.AsNoTracking() .FirstOrDefaultAsync(c => c.MemberID == request.MemberID && c.IsActive, ct); if (channel is not null) { channelID = channel.ID; if (wallet is not null) { var donationBalance = wallet.Balances .FirstOrDefault(b => b.Type == Domain.Entities.Wallets.ValueObject.WalletBalanceType.Donation); withdrawableBalance = donationBalance?.Amount.Value ?? 0; } } return new Response( member.SID, member.Name, member.Thumb, member.IsCreator, channelID, (int)spendableBalance, withdrawableBalance.HasValue ? (int)withdrawableBalance.Value : null ); } }