using Application.Abstractions.Data; using Application.Abstractions.Messaging; using SharedKernel.Results; using Domain.Entities.Donations; using Domain.Entities.Donations.ValueObject; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.DonationRemote.PlayNow; internal sealed class Handler(IAppDbContext db) : ICommandHandler> { public async Task> Handle(Command r, CancellationToken ct) { var alert = await db.DonationAlert.Include(a => a.Donation).ThenInclude(d => d!.Channel).Include(a => a.Donation).ThenInclude(d => d!.CrewMember).FirstOrDefaultAsync(a => a.ID == r.AlertID, ct); if (alert is null || alert.Donation is null || alert.Donation.Channel is null) { return Result.Failure(Error.NotFound("DonationAlert.NotFound", "알림을 찾을 수 없습니다.")); } if (alert.Donation.Channel.MemberID != r.RequesterMemberID) { return Result.Failure(Error.Forbidden("Remote.Forbidden", "권한이 없습니다.")); } // Failed/Skipped/Ignored/Delivered → Queued 로 되돌림 (재생을 위해) if (alert.Status is AlertStatus.Failed or AlertStatus.Skipped) { alert.Resend(); } else if (alert.Status is AlertStatus.Ignored or AlertStatus.Delivered) { // 도메인 메서드 없음 → Resend가 두 케이스만 허용. 새 Attempt 생성으로 우회. // Alert 새로 생성하지 않고 단순 SignalR broadcast로 재생 트리거 } var attempt = DonationAlertAttempt.Create(alert.DonationID, alert.ID); await db.DonationAlertAttempt.AddAsync(attempt, ct); await db.SaveChangesAsync(ct); return Result.Success(new Response( alert.ID, alert.DonationID, alert.CorrelationID, alert.Donation.SendName, alert.Donation.Amount, alert.Donation.NetAmount, alert.Donation.Message, alert.Donation.ChannelID, alert.Donation.Channel.Name, alert.Donation.Channel.WidgetToken!, alert.Donation.CrewMemberID, alert.Donation.CrewMember?.Nickname, alert.Donation.IsTest )); } }