Handler.cs 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Application.Abstractions.Paper;
  4. using Application.Helpers;
  5. using Microsoft.EntityFrameworkCore;
  6. namespace Application.Features.Api.Paper.GetLeaderboard;
  7. /// <summary>
  8. /// 모의투자 리더보드 (익명 허용) — Redis 캐시로 순위 페이지 조회 후 회원(SID/닉네임/썸네일)·지표 결합 (d4 M3 리더보드 백엔드 구현).
  9. /// </summary>
  10. internal sealed class Handler(IAppDbContext db, IPaperLeaderboardCache cache) : IQueryHandler<Query, Response>
  11. {
  12. private const int PageSize = 50;
  13. public async Task<Response> Handle(Query request, CancellationToken ct)
  14. {
  15. var sort = PaperLeaderboardMetrics.ParseSort(request.Sort);
  16. var period = PaperLeaderboardMetrics.ParsePeriod(request.Period);
  17. var page = request.Page < 1 ? 1 : request.Page;
  18. var minFillsForRank = await db.Config.AsNoTracking().OrderByDescending(c => c.ID).Select(c => c.Paper.MinFillsForRank).FirstOrDefaultAsync(ct);
  19. if (minFillsForRank <= 0)
  20. {
  21. minFillsForRank = 3;
  22. }
  23. var offset = (page - 1) * PageSize;
  24. var pageRows = await cache.GetPageAsync(sort, period, offset, PageSize, db, minFillsForRank, ct);
  25. if (pageRows.Count == 0)
  26. {
  27. return new Response(PaperLeaderboardMetrics.SortToString(sort), PaperLeaderboardMetrics.PeriodToString(period), page, PageSize, []);
  28. }
  29. // 전체 지표 산출 후 memberID 로 매핑 (배치 리더보드 규모 소량 가정)
  30. var metrics = await PaperLeaderboardMetrics.ComputeAllAsync(db, period, minFillsForRank, ct);
  31. var metricByMember = metrics.ToDictionary(c => c.MemberID);
  32. var memberIDs = pageRows.Select(c => c.MemberID).ToList();
  33. var members = await db.Member.AsNoTracking()
  34. .Where(c => memberIDs.Contains(c.ID))
  35. .Select(c => new { c.ID, c.SID, c.Name, c.Thumb })
  36. .ToListAsync(ct);
  37. var memberByID = members.ToDictionary(c => c.ID);
  38. var rows = new List<Row>(pageRows.Count);
  39. foreach (var pr in pageRows)
  40. {
  41. if (!memberByID.TryGetValue(pr.MemberID, out var m))
  42. {
  43. continue;
  44. }
  45. metricByMember.TryGetValue(pr.MemberID, out var metric);
  46. rows.Add(new Row(
  47. pr.Rank + 1,
  48. m.SID,
  49. m.Name,
  50. m.Thumb,
  51. metric?.ReturnBp ?? 0,
  52. metric?.WinRateBp ?? 0,
  53. metric?.MddBp ?? 0,
  54. metric?.FillCount ?? 0));
  55. }
  56. return new Response(PaperLeaderboardMetrics.SortToString(sort), PaperLeaderboardMetrics.PeriodToString(period), page, PageSize, rows);
  57. }
  58. }