using DailyDigestWorker.Configuration; // Для DataSourceUrls using DailyDigestWorker.Models; using Microsoft.Extensions.Options; // Для IOptions using Newtonsoft.Json; // Для JsonConvert (или System.Text.Json) using Newtonsoft.Json.Linq; // Для JObject, если парсить вручную namespace DailyDigestWorker.Services { public class CoinGeckoCryptoService : ICryptoService // Реализуем интерфейс { private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly DataSourceUrls _dataSourceUrls; // Внедряем зависимости public CoinGeckoCryptoService( IHttpClientFactory httpClientFactory, IOptions dataSourceUrlsOptions, ILogger logger) { _httpClientFactory = httpClientFactory; _logger = logger; _dataSourceUrls = dataSourceUrlsOptions.Value; // Получаем URL из настроек } public async Task GetCryptoRatesAsync(CancellationToken cancellationToken) { _logger.LogInformation("Запрос курсов криптовалют с API CoinGecko..."); var client = _httpClientFactory.CreateClient(); try { HttpResponseMessage response = await client.GetAsync(_dataSourceUrls.CoinGecko, cancellationToken); if (!response.IsSuccessStatusCode) { _logger.LogError("Ошибка при запросе к API CoinGecko. Статус код: {StatusCode}", response.StatusCode); return null; } // Читаем ответ как строку string jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken); // Десериализуем JSON // CoinGecko API возвращает структуру типа: {"bitcoin": {"usd": 60000, "rub": 5000000}, "ethereum": {...}} // Мы можем десериализовать это прямо в наш тип словаря. var rates = JsonConvert.DeserializeObject>>(jsonResponse); if (rates == null || rates.Count == 0) { _logger.LogWarning("Не удалось десериализовать ответ от CoinGecko или он пуст."); return null; } _logger.LogInformation("Курсы криптовалют успешно получены из CoinGecko."); // Создаем и возвращаем результат var resultData = new CryptoData(); foreach (var cryptoPair in rates) { resultData.CryptoRates.Add(cryptoPair.Key, cryptoPair.Value); } // Логгируем полученные значения для проверки (опционально) foreach (var crypto in resultData.CryptoRates) { foreach (var currencyRate in crypto.Value) { _logger.LogDebug(" - {CryptoId} [{Currency}]: {Rate}", crypto.Key, currencyRate.Key.ToUpper(), currencyRate.Value); } } return resultData; } catch (HttpRequestException ex) { _logger.LogError(ex, "Ошибка HTTP при запросе к API CoinGecko."); return null; } catch (JsonException ex) // Ловим ошибки десериализации JSON { _logger.LogError(ex, "Ошибка парсинга JSON ответа CoinGecko."); return null; } catch (OperationCanceledException) { _logger.LogInformation("Запрос курсов криптовалют был отменен."); return null; } catch (Exception ex) { _logger.LogError(ex, "Неожиданная ошибка при получении курсов криптовалют CoinGecko."); return null; } } } }