using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Application.Abstractions.Paper; using Application.Helpers; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Paper.GetLeaderboard; /// /// 모의투자 리더보드 (익명 허용) — Redis 캐시로 순위 페이지 조회 후 회원(SID/닉네임/썸네일)·지표 결합 (d4 M3 리더보드 백엔드 구현). /// internal sealed class Handler(IAppDbContext db, IPaperLeaderboardCache cache) : IQueryHandler { private const int PageSize = 50; public async Task Handle(Query request, CancellationToken ct) { var sort = PaperLeaderboardMetrics.ParseSort(request.Sort); var period = PaperLeaderboardMetrics.ParsePeriod(request.Period); var page = request.Page < 1 ? 1 : request.Page; var minFillsForRank = await db.Config.AsNoTracking().OrderByDescending(c => c.ID).Select(c => c.Paper.MinFillsForRank).FirstOrDefaultAsync(ct); if (minFillsForRank <= 0) { minFillsForRank = 3; } var offset = (page - 1) * PageSize; var pageRows = await cache.GetPageAsync(sort, period, offset, PageSize, db, minFillsForRank, ct); if (pageRows.Count == 0) { return new Response(PaperLeaderboardMetrics.SortToString(sort), PaperLeaderboardMetrics.PeriodToString(period), page, PageSize, []); } // 전체 지표 산출 후 memberID 로 매핑 (배치 리더보드 규모 소량 가정) var metrics = await PaperLeaderboardMetrics.ComputeAllAsync(db, period, minFillsForRank, ct); var metricByMember = metrics.ToDictionary(c => c.MemberID); var memberIDs = pageRows.Select(c => c.MemberID).ToList(); var members = await db.Member.AsNoTracking() .Where(c => memberIDs.Contains(c.ID)) .Select(c => new { c.ID, c.SID, c.Name, c.Thumb }) .ToListAsync(ct); var memberByID = members.ToDictionary(c => c.ID); var rows = new List(pageRows.Count); foreach (var pr in pageRows) { if (!memberByID.TryGetValue(pr.MemberID, out var m)) { continue; } metricByMember.TryGetValue(pr.MemberID, out var metric); rows.Add(new Row( pr.Rank + 1, m.SID, m.Name, m.Thumb, metric?.ReturnBp ?? 0, metric?.WinRateBp ?? 0, metric?.MddBp ?? 0, metric?.FillCount ?? 0)); } return new Response(PaperLeaderboardMetrics.SortToString(sort), PaperLeaderboardMetrics.PeriodToString(period), page, PageSize, rows); } }