using Application.Abstractions.Hub; using Application.Abstractions.Notification; using Domain.Entities.Notifications.ValueObject; using Infrastructure.Hubs; using Web.Api.Common; using MediatR; using Microsoft.AspNetCore.SignalR; namespace Web.Api.Endpoints.Donation; /// 크루 방송 시작 — 세션 생성 + 크루원에게 초대 알림 internal sealed class CrewSessionStart : IEndpoint { public void MapEndpoint(IEndpointRouteBuilder app) { app.MapPost("api/crew/session/start", async ( Application.Features.Api.Crew.StartSession.Command body, ISender sender, IHubContext appHub, INotificationService notificationService, Application.Abstractions.Data.IAppDbContext db, CancellationToken ct ) => { var data = await sender.Send(body, ct); // 크루원들에게 초대 알림 발송 var consents = await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions .ToListAsync( Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions .AsNoTracking(db.CrewSessionConsent) .Where(c => c.CrewSessionID == data.CrewSessionID) .Join(db.CrewMember, c => c.CrewMemberID, m => m.ID, (c, m) => new { m.MemberID, m.Nickname }), ct ); foreach (var member in consents) { await notificationService.SendAsync( member.MemberID, NotificationType.CrewInvitation, "크루 방송 초대", $"크루 방송 '{body.Title}'에 초대되었습니다. 참여 동의를 해주세요.", $"/crew/consent/{data.CrewSessionID}", "CrewSession", data.CrewSessionID, null, ct ); // AppHub 실시간 푸시 await appHub.Clients.Group($"member:{member.MemberID}").ReceiveCrewInvitation(new { CrewSessionID = data.CrewSessionID, Title = body.Title, CrewID = body.CrewID }); } return ApiResponse.Ok(data); }) .WithTags("Crew") .RequireAuthorization(); } }