97 lines
4.4 KiB
C#
97 lines
4.4 KiB
C#
![]() |
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<CoinGeckoCryptoService> _logger;
|
|||
|
private readonly DataSourceUrls _dataSourceUrls;
|
|||
|
|
|||
|
// Внедряем зависимости
|
|||
|
public CoinGeckoCryptoService(
|
|||
|
IHttpClientFactory httpClientFactory,
|
|||
|
IOptions<DataSourceUrls> dataSourceUrlsOptions,
|
|||
|
ILogger<CoinGeckoCryptoService> logger)
|
|||
|
{
|
|||
|
_httpClientFactory = httpClientFactory;
|
|||
|
_logger = logger;
|
|||
|
_dataSourceUrls = dataSourceUrlsOptions.Value; // Получаем URL из настроек
|
|||
|
}
|
|||
|
|
|||
|
public async Task<CryptoData?> 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<Dictionary<string, Dictionary<string, decimal>>>(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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|