daily_digest/Services/CbrCurrencyService.cs
2025-04-12 00:27:03 +07:00

132 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using DailyDigestWorker.Configuration; // Для DataSourceUrls
using DailyDigestWorker.Models;
using Microsoft.Extensions.Options; // Для IOptions
using System.Globalization; // Для CultureInfo и NumberStyles
using System.Xml.Linq; // Для работы с XML
namespace DailyDigestWorker.Services
{
public class CbrCurrencyService : ICurrencyService // Реализуем интерфейс
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<CbrCurrencyService> _logger;
private readonly DataSourceUrls _dataSourceUrls;
// Внедряем зависимости
public CbrCurrencyService(
IHttpClientFactory httpClientFactory,
IOptions<DataSourceUrls> dataSourceUrlsOptions,
ILogger<CbrCurrencyService> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
_dataSourceUrls = dataSourceUrlsOptions.Value; // Получаем URL из настроек
}
public async Task<CurrencyData?> GetCurrencyRatesAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Запрос курсов валют с API ЦБ РФ...");
var client = _httpClientFactory.CreateClient(); // Создаем HttpClient
try
{
// Выполняем GET-запрос к URL из конфигурации
HttpResponseMessage response = await client.GetAsync(_dataSourceUrls.Cbr, cancellationToken);
// Проверяем, успешен ли запрос
if (!response.IsSuccessStatusCode)
{
_logger.LogError("Ошибка при запросе к API ЦБ РФ. Статус код: {StatusCode}", response.StatusCode);
return null; // Возвращаем null при ошибке HTTP
}
// Читаем ответ как поток (для работы с XML)
using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
// Загружаем XML из потока
XDocument xmlDoc = await XDocument.LoadAsync(responseStream, LoadOptions.None, cancellationToken);
// Ищем нужные валюты (USD и EUR)
var usdElement = xmlDoc.Root?.Elements("Valute")
.FirstOrDefault(v => v.Attribute("ID")?.Value == "R01235"); // ID для USD
var eurElement = xmlDoc.Root?.Elements("Valute")
.FirstOrDefault(v => v.Attribute("ID")?.Value == "R01239"); // ID для EUR
if (usdElement == null || eurElement == null)
{
_logger.LogError("Не удалось найти элементы USD или EUR в XML ответе ЦБ РФ.");
return null;
}
// Получаем дату курсов из атрибута Date корневого элемента
DateTime coursesDate = DateTime.Today; // Значение по умолчанию
var dateAttribute = xmlDoc.Root?.Attribute("Date")?.Value;
if (!string.IsNullOrEmpty(dateAttribute) && DateTime.TryParseExact(dateAttribute, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedDate))
{
coursesDate = parsedDate;
}
else
{
_logger.LogWarning("Не удалось распарсить дату курсов из XML ЦБ РФ. Используется текущая дата.");
}
// Извлекаем значения курсов (в виде строк)
var usdValueStr = usdElement.Element("Value")?.Value;
var eurValueStr = eurElement.Element("Value")?.Value;
if (usdValueStr == null || eurValueStr == null)
{
_logger.LogError("Не удалось найти значения Value для USD или EUR в XML.");
return null;
}
// Парсим строки в decimal, учитывая русскую культуру (запятая как разделитель)
var culture = CultureInfo.GetCultureInfo("ru-RU");
if (decimal.TryParse(usdValueStr, NumberStyles.Any, culture, out decimal usdRate) &&
decimal.TryParse(eurValueStr, NumberStyles.Any, culture, out decimal eurRate))
{
_logger.LogInformation("Курсы валют успешно получены: USD={UsdRate}, EUR={EurRate} на {Date}", usdRate, eurRate, coursesDate.ToString("dd.MM.yyyy"));
// Сначала создаем объект CurrencyData
var resultData = new CurrencyData
{
Date = coursesDate
};
// Затем добавляем элементы в его словарь Rates
resultData.Rates.Add("USD", usdRate);
resultData.Rates.Add("EUR", eurRate);
// Возвращаем готовый объект
return resultData;
}
else
{
_logger.LogError("Не удалось преобразовать строковые значения курсов в decimal.");
return null;
}
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Ошибка HTTP при запросе к API ЦБ РФ.");
return null;
}
catch (System.Xml.XmlException ex)
{
_logger.LogError(ex, "Ошибка парсинга XML ответа ЦБ РФ.");
return null;
}
catch (OperationCanceledException) // Ловим отмену операции
{
_logger.LogInformation("Запрос курсов валют был отменен.");
return null; // Возвращаем null, если операция отменена
}
catch (Exception ex) // Ловим любые другие неожиданные ошибки
{
_logger.LogError(ex, "Неожиданная ошибка при получении курсов валют ЦБ РФ.");
return null;
}
}
}
}