| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Domain.Entities.Common.ValueObject;
- using Domain.Entities.Donations;
- using Domain.Entities.Donations.ValueObject;
- using Domain.Entities.Wallets;
- using Domain.Entities.Wallets.ValueObject;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Results;
- namespace Application.Features.Api.Studio.Wallet.RequestWithdraw;
- internal sealed class Handler(IAppDbContext db) : ICommandHandler<Command, Result<Response>>
- {
- private const int MinWithdrawAmount = 40000;
- public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
- {
- // 금액 검증
- if (request.Amount < MinWithdrawAmount)
- {
- return Result.Failure<Response>(Error.Problem("Amount", $"최소 출금 금액은 {MinWithdrawAmount:N0}원입니다."));
- }
- // 채널 확인
- var channel = await db.Channel.AsNoTracking().FirstOrDefaultAsync(c => c.MemberID == request.MemberID && c.IsActive, ct);
- if (channel is null)
- {
- return Result.Failure<Response>(Error.NotFound("Channel", "채널 정보를 찾을 수 없습니다."));
- }
- // 계좌 확인
- var account = await db.SettlementAccount.AsNoTracking().FirstOrDefaultAsync(a => a.ID == request.AccountID && a.MemberID == request.MemberID, ct);
- if (account is null)
- {
- return Result.Failure<Response>(Error.Problem("Account", "정산 계좌를 선택해 주세요."));
- }
- // Wallet 조회 (Tracked — 잔액 변경 필요)
- var wallet = await db.Wallet
- .Include(w => w.Balances)
- .FirstOrDefaultAsync(w => w.MemberID == request.MemberID, ct);
- if (wallet is null)
- {
- return Result.Failure<Response>(Error.Problem("Wallet", "지갑 정보를 찾을 수 없습니다."));
- }
- var donationBal = wallet.Balances.FirstOrDefault(b => b.Type == WalletBalanceType.Donation);
- if (donationBal is null || (int)donationBal.Amount.Value < request.Amount)
- {
- return Result.Failure<Response>(Error.Problem("Amount", "출금 가능 잔액이 부족합니다."));
- }
- // 이미 대기 중인 출금이 있는지 확인
- var hasPending = await db.WithdrawalRequest.AnyAsync(
- w => w.MemberID == request.MemberID && (w.Status == WithdrawalStatus.Pending || w.Status == WithdrawalStatus.Processing), ct);
- if (hasPending)
- {
- return Result.Failure<Response>(Error.Problem("Withdrawal", "이미 처리 중인 출금 요청이 있습니다."));
- }
- // Wallet 잔액 Lock
- wallet.LockForWithdrawal(Money.KRW(request.Amount));
- // 출금 요청 생성
- var withdrawal = WithdrawalRequest.Create(
- channel.ID, request.MemberID, request.Amount,
- account.BankCode, account.BankName, account.AccountNumber, account.AccountHolder
- );
- db.WithdrawalRequest.Add(withdrawal);
- await db.SaveChangesAsync(ct);
- return Result.Success(new Response("출금 신청이 완료되었습니다."));
- }
- }
|