using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Microsoft.EntityFrameworkCore; using SharedKernel.Results; namespace Application.Features.Api.Stocks.GetElsRiskBoard; internal sealed class Handler(IAppDbContext db) : IQueryHandler> { public async Task> Handle(Query request, CancellationToken ct) { var query = db.ElsUnredeemedScale.AsNoTracking(); var bassetCd = request.BassetCd?.Trim(); if (!string.IsNullOrEmpty(bassetCd)) { query = query.Where(c => c.StndBassetCd == bassetCd); } var secnTpcd = request.SecnTpcd?.Trim(); if (!string.IsNullOrEmpty(secnTpcd)) { query = query.Where(c => c.SecnTpcd == secnTpcd); } if (request.From.HasValue) { query = query.Where(c => c.StdDt >= request.From.Value); } if (request.To.HasValue) { query = query.Where(c => c.StdDt <= request.To.Value); } // 기초자산코드 × 종목구분별로 그룹화하고, 각 그룹 안에서 기준일 오름차순 시계열을 만든다. var rows = await query .OrderBy(c => c.StndBassetCd).ThenBy(c => c.SecnTpcd).ThenBy(c => c.StdDt) .Select(c => new { c.StndBassetCd, c.SecnTpcd, c.BassetContent, c.StdDt, c.UnredSecncnt, c.UnredRema }) .ToListAsync(ct); var series = rows .GroupBy(c => new { c.StndBassetCd, c.SecnTpcd }) .Select(g => new Response.Series { BassetCd = g.Key.StndBassetCd, SecnTpcd = g.Key.SecnTpcd, BassetContent = g.Select(c => c.BassetContent).FirstOrDefault(c => c != null), Points = g.Select(c => new Response.Point { StdDt = c.StdDt, UnredeemedCount = c.UnredSecncnt, UnredeemedRemaining = c.UnredRema }).ToList() }) .ToList(); return new Response { Assets = series }; } }