using Application.Abstractions.Data; using Application.Abstractions.Messaging; using SharedKernel.Results; using Domain.Entities.Donations; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Crew.StartSession; internal sealed class Handler(IAppDbContext db) : ICommandHandler> { public async Task> Handle(Command request, CancellationToken ct) { var crew = await db.Crew .Include(c => c.Members.Where(m => m.IsActive)) .FirstOrDefaultAsync(c => c.ID == request.CrewID && c.IsActive, ct); if (crew is null) { return Result.Failure(Error.NotFound("Crew.NotFound", "크루를 찾을 수 없습니다.")); } if (!crew.Members.Any()) { return Result.Failure(Error.Problem("Crew.NoMembers", "크루원이 없습니다.")); } // 같은 채널에 이미 진행 중인 크루 세션이 있는지 확인 (채널 레벨 가드) var channelHasActiveSession = await db.CrewSession .AnyAsync(s => s.Crew!.ChannelID == crew.ChannelID && s.Status != Domain.Entities.Donations.ValueObject.CrewSessionStatus.Ended, ct); if (channelHasActiveSession) { return Result.Failure(Error.Conflict("Crew.ChannelHasActiveSession", "이미 이 채널에서 진행 중인 크루 방송이 있습니다.")); } // 세션 생성 (Inviting) var session = CrewSession.Create(request.CrewID, request.Title); db.CrewSession.Add(session); await db.SaveChangesAsync(ct); // 각 크루원에 대해 동의 레코드 생성 foreach (var member in crew.Members) { var consent = CrewSessionConsent.Create(session.ID, member.ID); db.CrewSessionConsent.Add(consent); // 크루원별 집계 초기화 var summary = CrewDonationSummary.Create(session.ID, member.ID); db.CrewDonationSummary.Add(summary); } await db.SaveChangesAsync(ct); // 알림/쪽지 발송은 Endpoint에서 SignalR + NotificationService로 처리 return Result.Success(new Response(session.ID)); } }