Handler.cs 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Domain.Entities.Developers.ValueObject;
  4. using Microsoft.EntityFrameworkCore;
  5. using SharedKernel.Results;
  6. namespace Application.Features.Api.V1.Purchases.List;
  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 page = Math.Max(1, request.Page);
  12. var size = Math.Clamp(request.Size, 1, 100);
  13. var query = db.ApiPurchase.AsNoTracking().AsQueryable();
  14. // 호출 주체 범위 제한 — OAuth2: 해당 앱 / PAT: 본인 소유 앱 전체
  15. if (request.ApplicationID.HasValue)
  16. {
  17. query = query.Where(c => c.ApplicationID == request.ApplicationID.Value);
  18. }
  19. else if (request.OwnerMemberID.HasValue)
  20. {
  21. query = query.Where(c => c.Application!.OwnerMemberID == request.OwnerMemberID.Value);
  22. }
  23. else
  24. {
  25. return Result.Failure<Response>(Error.Unauthorized("Purchase.CallerUnknown", "호출 주체를 식별할 수 없습니다."));
  26. }
  27. // 상태 필터 (pending / confirmed / canceled)
  28. if (!string.IsNullOrWhiteSpace(request.Status))
  29. {
  30. if (!Enum.TryParse<ApiPurchaseStatus>(request.Status.Trim(), ignoreCase: true, out var status))
  31. {
  32. return Result.Failure<Response>(Error.Problem("Purchase.StatusInvalid", "status 는 pending / confirmed / canceled 중 하나여야 합니다."));
  33. }
  34. query = query.Where(c => c.Status == status);
  35. }
  36. if (request.From.HasValue)
  37. {
  38. query = query.Where(c => c.CreatedAt >= request.From.Value);
  39. }
  40. if (request.To.HasValue)
  41. {
  42. query = query.Where(c => c.CreatedAt <= request.To.Value);
  43. }
  44. var total = await query.CountAsync(ct);
  45. var items = await query
  46. .OrderByDescending(c => c.ID)
  47. .Skip((page - 1) * size)
  48. .Take(size)
  49. .Select(c => new Response.Row(
  50. c.ID,
  51. c.OrderID,
  52. (int)c.Marketplace,
  53. c.Channel!.DonationCode,
  54. c.Game!.Code ?? string.Empty,
  55. c.OrderPrice,
  56. c.CommissionRate,
  57. c.CommissionAmount,
  58. c.Status.ToString(),
  59. c.ConfirmDueAt,
  60. c.ConfirmedAt,
  61. c.CanceledAt,
  62. c.CreatedAt
  63. ))
  64. .ToListAsync(ct);
  65. return Result.Success(new Response(total, page, size, items));
  66. }
  67. }