using Application.Abstractions.Messaging; using Application.Abstractions.Data; using Application.Abstractions.Cache; using Application.Abstractions.Crypto; using Domain.Entities.Crypto; using Microsoft.EntityFrameworkCore; namespace Application.Features.Admin.Crypto.List.Sync; public sealed class Handler(IAppDbContext db, IUpbitClient upbit, ICacheService cache) : ICommandHandler { public async Task Handle(Command request, CancellationToken ct) { var allMarkets = await upbit.GetMarketsAsync(ct); if (allMarkets.Count == 0) { throw new InvalidOperationException("Upbit API에서 마켓 데이터를 가져오지 못했습니다."); } var dbCoins = await db.Coin.Include(c => c.CoinMarket).ToListAsync(ct); var dbCoinMap = dbCoins.ToDictionary(c => c.Symbol, c => c, StringComparer.OrdinalIgnoreCase); var upbitSymbols = new HashSet(StringComparer.OrdinalIgnoreCase); int created = 0; int updated = 0; int delisted = 0; // 심볼별 첫 마켓에서 Coin 생성/갱신 foreach (var market in allMarkets) { var symbol = market.Market.Split('-')[1]; if (!upbitSymbols.Add(symbol)) { continue; } if (dbCoinMap.TryGetValue(symbol, out var existing)) { existing.SyncUpdate(market.KoreanName, market.EnglishName, market.MarketEvent.Warning); updated++; } else { var coin = Coin.SyncFromUpbit(symbol, market.KoreanName, market.EnglishName, market.MarketEvent.Warning); db.Coin.Add(coin); dbCoinMap[symbol] = coin; created++; } } // SaveChanges로 신규 코인 ID 확정 await db.SaveChangesAsync(ct); // 거래쌍 동기화 (CoinMarket) foreach (var market in allMarkets) { var symbol = market.Market.Split('-')[1]; if (!dbCoinMap.TryGetValue(symbol, out var coin)) { continue; } if (!coin.CoinMarket.Any(m => m.Market.Equals(market.Market, StringComparison.OrdinalIgnoreCase))) { db.CoinMarket.Add(new CoinMarket { CoinID = coin.ID, Market = market.Market.ToUpper() }); } } // 상폐 처리 foreach (var coin in dbCoins) { if (!upbitSymbols.Contains(coin.Symbol) && !coin.IsDelisted) { coin.MarkDelisted(); delisted++; } } await db.SaveChangesAsync(ct); await cache.RemoveByPrefixAsync("crypto:", ct); return new Response(created, updated, delisted); } }