Handler.cs 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using SharedKernel.Results;
  4. using Domain.Entities.Donations.ValueObject;
  5. using Microsoft.EntityFrameworkCore;
  6. namespace Application.Features.Api.DonationRemote.GetCrewMembers;
  7. internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Result<Response>>
  8. {
  9. public async Task<Result<Response>> Handle(Query request, CancellationToken ct)
  10. {
  11. var channel = await db.Channel.AsNoTracking()
  12. .Where(c => c.WidgetToken == request.WidgetToken && c.IsActive)
  13. .Select(c => new { c.ID, c.MemberID })
  14. .FirstOrDefaultAsync(ct);
  15. if (channel is null)
  16. {
  17. return Result.Failure<Response>(Error.NotFound("Channel.NotFound", "채널을 찾을 수 없습니다."));
  18. }
  19. if (channel.MemberID != request.RequesterMemberID)
  20. {
  21. return Result.Failure<Response>(Error.Forbidden("Remote.Forbidden", "권한이 없습니다."));
  22. }
  23. var crew = await db.Crew.AsNoTracking()
  24. .Where(c => c.ID == request.CrewID && c.ChannelID == channel.ID && c.IsActive)
  25. .Select(c => new { c.ID, c.Name })
  26. .FirstOrDefaultAsync(ct);
  27. if (crew is null)
  28. {
  29. return Result.Failure<Response>(Error.NotFound("Crew.NotFound", "크루를 찾을 수 없습니다."));
  30. }
  31. var activeSessionID = await db.CrewSession.AsNoTracking()
  32. .Where(s => s.CrewID == crew.ID && s.Status == CrewSessionStatus.Active)
  33. .OrderByDescending(s => s.CreatedAt)
  34. .Select(s => (int?)s.ID)
  35. .FirstOrDefaultAsync(ct);
  36. // ID > 0 방어: 시드/수동 SQL로 ID=0 row가 들어간 케이스 차단
  37. var members = await db.CrewMember.AsNoTracking()
  38. .Where(m => m.CrewID == crew.ID && m.IsActive && m.ID > 0)
  39. .OrderBy(m => m.SortOrder).ThenBy(m => m.JoinedAt)
  40. .Select(m => new CrewMemberItem(
  41. m.ID,
  42. m.MemberID,
  43. m.Nickname,
  44. m.Role,
  45. m.SortOrder
  46. ))
  47. .ToListAsync(ct);
  48. return Result.Success(new Response(crew.ID, crew.Name, activeSessionID, members));
  49. }
  50. }