using Application.Abstractions.Messaging; using Application.Abstractions.Data; using Application.Abstractions.Cache; using Application.Abstractions.Crypto; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Crypto.Ticker.GetAll; public sealed class Handler(IAppDbContext db, ICacheService cache) : IQueryHandler { public async Task Handle(Query request, CancellationToken ct) { // Redis에서 Ticker 목록 가져오기 var tickers = await cache.GetAsync>(CacheKeys.CryptoTickers, ct) ?? []; var tickerMap = tickers.ToDictionary(t => t.Market, t => t); // DB에서 코인 정보 가져오기 var coinsQuery = db.Coin .AsNoTracking() .Where(c => c.IsActive && !c.IsDelisted); if (request.FeaturedOnly) { coinsQuery = coinsQuery.Where(c => c.IsFeatured); } var coins = await coinsQuery .OrderByDescending(c => c.IsFeatured) .ThenBy(c => c.DisplayOrder) .ThenBy(c => c.Symbol) .Select(c => new { c.Symbol, c.KorName, c.EngName, c.LogoImage, c.IsFeatured, c.DisplayOrder }) .ToListAsync(ct); var rows = new List(); foreach (var coin in coins) { var market = $"KRW-{coin.Symbol.ToUpper()}"; tickerMap.TryGetValue(market, out var ticker); rows.Add(new Response.Row( coin.Symbol, coin.KorName, coin.EngName, coin.LogoImage, ticker?.TradePrice ?? 0m, ticker?.Change ?? "", ticker?.SignedChangePrice ?? 0m, ticker?.SignedChangeRate ?? 0m, ticker?.AccTradePrice24h ?? 0m, coin.IsFeatured, coin.DisplayOrder )); } return new Response(rows); } }