using Application.Abstractions.Messaging; using Application.Abstractions.Data; using SharedKernel.Results; using Domain.Entities.Common.ValueObject; using Domain.Entities.Wallets.ValueObject; using Microsoft.EntityFrameworkCore; namespace Application.Features.Admin.Member.Wallet.List.Charge; public sealed class Handler(IAppDbContext db) : ICommandHandler { public async Task Handle(Command request, CancellationToken ct) { var wallet = await db.Wallet.Include(x => x.Balances).Include(x => x.Transactions).FirstOrDefaultAsync(x => x.ID == request.WalletID, ct); if (wallet is null) { return Result.Failure(Error.NotFound("Wallet.NotFound", "지갑을 찾을 수 없습니다.")); } if (request.BalanceType == WalletBalanceType.Locked) { return Result.Failure(Error.Problem("Wallet.LockedNotAdjustable", "잠금 잔액은 직접 조정할 수 없습니다.")); } var amount = Money.KRW(Math.Abs(request.Amount)); var reason = request.Amount > 0 ? "관리자 충전" : "관리자 차감"; if (request.Amount > 0) { if (request.BalanceType.HasValue) { wallet.AdjustIncrease(request.BalanceType.Value, amount, reason, memo: request.Memo); } else { wallet.AdjustIncrease(amount, reason, memo: request.Memo); } } else if (request.Amount < 0) { if (request.BalanceType.HasValue) { // 지정 잔액 단일 차감 — 구지갑은 해당 유형 row 가 없을 수 있어 컬렉션에서 직접 조회 var typeBalance = wallet.Balances.SingleOrDefault(x => x.Type == request.BalanceType.Value)?.Amount.Value ?? 0; if (typeBalance < amount.Value) { return Result.Failure(Error.Problem("Wallet.InsufficientBalance", "선택한 잔액 유형의 차감 가능 금액이 부족합니다.")); } wallet.AdjustDecrease(request.BalanceType.Value, amount, reason, memo: request.Memo); } else { if (wallet.GetTotalAvailable().Value < amount.Value) { return Result.Failure(Error.Problem("Wallet.InsufficientBalance", "차감 가능한 잔액이 부족합니다.")); } wallet.AdjustDecreaseAcrossBalances(amount, reason, memo: request.Memo); } } else { return Result.Failure(Error.Problem("Wallet.AmountRequired", "충전 금액을 입력해주세요.")); } await db.SaveChangesAsync(ct); return Result.Success(); } }