using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Domain.Entities.Donations; using Domain.Entities.Donations.ValueObject; using Microsoft.EntityFrameworkCore; using SharedKernel.Results; namespace Application.Features.Api.DonationRemote.ResendAlert; internal sealed class Handler(IAppDbContext db) : ICommandHandler> { public async Task> Handle(Command request, 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 == request.AlertID, ct); if (alert is null || alert.Donation is null || alert.Donation.Channel is null) { return Result.Failure(Error.NotFound("DonationAlert.NotFound", "알림을 찾을 수 없습니다.")); } // 권한 체크 — 본인 채널 알림만 재전송 가능 (PlayNow 와 동일 패턴) if (alert.Donation.Channel.MemberID != request.RequesterMemberID) { return Result.Failure(Error.Forbidden("Remote.Forbidden", "권한이 없습니다.")); } // 이미 큐에 있거나 재생 중이면 거부 — 그 외 종결 상태(Failed/Skipped/Ignored/Delivered) 전부 재전송 허용 if (alert.Status is AlertStatus.Queued or AlertStatus.Playing) { return Result.Failure(Error.Problem("DonationAlert.AlreadyActive", "이미 대기 중이거나 재생 중인 알림입니다.")); } alert.Resend(); 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 )); } }