using Application.Abstractions.Data; using Application.Abstractions.Hub; using Application.Abstractions.Messaging; using SharedKernel; using SharedKernel.Results; using Domain.Entities.Donations.ValueObject; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Crew.RemoveMember; internal sealed class Handler( IAppDbContext db, ICrewHubBroadcaster crewBroadcaster ) : ICommandHandler { public async Task Handle(Command request, CancellationToken ct) { var member = await db.CrewMember .FirstOrDefaultAsync(m => m.ID == request.CrewMemberID, ct); if (member is null) { return Result.Failure(Error.NotFound("Crew.MemberNotFound", "크루원을 찾을 수 없습니다.")); } // 진행 중인 세션이 있으면 삭제 불가 var hasActiveSession = await db.CrewSession .AnyAsync(s => s.CrewID == member.CrewID && s.Status != CrewSessionStatus.Ended, ct); if (hasActiveSession) { return Result.Failure(Error.Conflict("Crew.ActiveSessionExists", "진행 중인 크루 방송이 있어 삭제할 수 없습니다.")); } // EF Remove 후 추적 해제될 수 있으니 broadcast 에 필요한 ID 미리 캐싱 var crewID = member.CrewID; var crewMemberID = member.ID; var memberID = member.MemberID; db.CrewMember.Remove(member); await db.SaveChangesAsync(ct); // 운영자 그룹에 삭제 broadcast await crewBroadcaster.BroadcastMemberChangeAsync( crewID: crewID, kind: "removed", data: new { crewMemberID, memberID }, ct: ct ); return Result.Success(); } }