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;
|
||
}
|
||
}
|
||
}
|
||
} |