using Application.Abstractions.Hub; using Infrastructure.Hubs; using Web.Api.Common; using Web.Api.Extensions; using MediatR; using Microsoft.AspNetCore.SignalR; using System.Security.Claims; namespace Web.Api.Endpoints.Donation; /// 리모콘 — 테스트 후원 (Wallet/PG 우회, IsTest 플래그로 통계 반영 여부 결정) internal sealed class RemoteTestDonate : IEndpoint { public void MapEndpoint(IEndpointRouteBuilder app) { app.MapPost("api/donation/remote/test-donate", async ( TestDonateRequest body, ClaimsPrincipal user, ISender sender, IHubContext donationHub, Application.Abstractions.Data.IAppDbContext db, CancellationToken ct ) => { var memberID = user.GetRequiredMemberID(); var command = new Application.Features.Api.DonationRemote.TestDonate.Command( WidgetToken: body.WidgetToken, SendName: body.SendName, Amount: body.Amount, Message: body.Message, CrewMemberID: body.CrewMemberID, IsTest: body.IsTest, RequesterMemberID: memberID ); var result = await sender.Send(command, ct); if (result.IsFailure) { return CustomResults.Problem(result); } var data = result.Value; // SignalR broadcast — Alert 위젯 var alertPayload = new { alertID = data.AlertID, donationID = data.DonationID, correlationID = data.CorrelationID, sponsorMemberID = memberID, sendName = data.SendName, amount = data.Amount, netAmount = data.NetAmount, message = data.Message, channelID = data.ChannelID, channelName = data.ChannelName, crewMemberID = data.CrewMemberID, crewMemberNickname = data.CrewMemberNickname, isTest = data.IsTest, createdAt = DateTime.UtcNow }; await donationHub.Clients.Group(data.WidgetToken).ReceiveAlert(alertPayload); // ⚠️ Goal/Rank/Crew broadcast는 알림 재생 종료 시점(DonationHub.AlertCompleted)에 처리됨 // 크루 후원이고 활성 세션이 있는 경우 — 크루 위젯 갱신 if (data.CrewSessionID.HasValue && data.CrewMemberID.HasValue) { var rankResult = await sender.Send( new Application.Features.Api.Crew.GetCrewRanking.Query(data.CrewSessionID.Value), ct ); if (rankResult.IsSuccess) { await donationHub.Clients.Group(data.WidgetToken).ReceiveCrewUpdate(new { rankResult.Value.List, rankResult.Value.TotalAmount }); } } return ApiResponse.Ok(data); }) .WithTags("DonationRemote") .RequireAuthorization(); } public sealed record TestDonateRequest( string WidgetToken, string SendName, int Amount, string? Message, int? CrewMemberID, bool IsTest ); }