using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Stocks.GetList; internal sealed class Handler(IAppDbContext db) : IQueryHandler { private const ushort MaxPerPage = 100; public async Task Handle(Query request, CancellationToken ct) { var page = request.Page < 1 ? 1 : request.Page; var perPage = request.PerPage is 0 or > MaxPerPage ? (ushort)20 : request.PerPage; var query = db.Stock.AsNoTracking().Where(c => c.IsActive); if (request.Market.HasValue) { query = query.Where(c => c.Market == request.Market.Value); } var q = request.Q?.Trim(); if (!string.IsNullOrEmpty(q)) { query = query.Where(c => c.Name.Contains(q) || c.Code.StartsWith(q)); } var total = await query.CountAsync(ct); // 방향 — 미지정/asc 외에는 내림차순 (숫자열 기본 desc, 기본 정렬도 desc) var desc = !string.Equals(request.Order, "asc", StringComparison.OrdinalIgnoreCase); // 정렬 — 기본 시가총액 내림차순. 거래대금(value)은 최근 거래일 StockDailyPrice 상관 서브쿼리 기준 IOrderedQueryable ordered = request.Sort?.ToLowerInvariant() switch { "name" => desc ? query.OrderByDescending(c => c.Name) : query.OrderBy(c => c.Name), "price" => desc ? query.OrderByDescending(c => c.LastClosePrice) : query.OrderBy(c => c.LastClosePrice), "change" => desc ? query.OrderByDescending(c => c.LastChangeRate) : query.OrderBy(c => c.LastChangeRate), "value" => desc ? query.OrderByDescending(c => db.StockDailyPrice.Where(p => p.StockID == c.ID && p.TradingDate == c.LastTradingDate).Select(p => (long?)p.TradingValue).FirstOrDefault()) : query.OrderBy(c => db.StockDailyPrice.Where(p => p.StockID == c.ID && p.TradingDate == c.LastTradingDate).Select(p => (long?)p.TradingValue).FirstOrDefault()), _ => desc ? query.OrderByDescending(c => c.MarketCap) : query.OrderBy(c => c.MarketCap) }; var list = await ordered .ThenBy(c => c.Code) .Skip((page - 1) * perPage) .Take(perPage) .Select(c => new { c.Code, c.Name, c.Market, c.TradingStatus, c.LastTradingDate, c.LastClosePrice, c.LastChangeRate, c.MarketCap, // 전일 거래량·거래대금 — Stock denorm 에 없어 최근 거래일(LastTradingDate)의 StockDailyPrice 를 상관 서브쿼리로 조회 Volume = db.StockDailyPrice.Where(p => p.StockID == c.ID && p.TradingDate == c.LastTradingDate).Select(p => (long?)p.Volume).FirstOrDefault(), TradingValue = db.StockDailyPrice.Where(p => p.StockID == c.ID && p.TradingDate == c.LastTradingDate).Select(p => (long?)p.TradingValue).FirstOrDefault() }) .ToListAsync(ct); return new Response { Total = total, List = [..list.Select(c => new Response.Row { Code = c.Code, Name = c.Name, Market = c.Market.ToString(), TradingStatus = c.TradingStatus.ToString(), LastTradingDate = c.LastTradingDate, ClosePrice = c.LastClosePrice, ChangeRate = c.LastChangeRate, MarketCap = c.MarketCap, Volume = c.Volume, TradingValue = c.TradingValue })] }; } }