108 lines
5.1 KiB
C#
108 lines
5.1 KiB
C#
using DailyDigestWorker.Configuration; // Для DataSourceUrls, ApiKeys
|
||
using DailyDigestWorker.Models; // Для WeatherData
|
||
using DailyDigestWorker.Models.OpenWeatherMap; // Для DTO классов
|
||
using Microsoft.Extensions.Options; // Для IOptions
|
||
using Newtonsoft.Json; // Для JsonConvert
|
||
|
||
namespace DailyDigestWorker.Services
|
||
{
|
||
public class OpenWeatherMapService : IWeatherService // Реализуем интерфейс
|
||
{
|
||
private readonly IHttpClientFactory _httpClientFactory;
|
||
private readonly ILogger<OpenWeatherMapService> _logger;
|
||
private readonly DataSourceUrls _dataSourceUrls;
|
||
private readonly ApiKeys _apiKeys;
|
||
|
||
// Внедряем зависимости, включая ApiKeys
|
||
public OpenWeatherMapService(
|
||
IHttpClientFactory httpClientFactory,
|
||
IOptions<DataSourceUrls> dataSourceUrlsOptions,
|
||
IOptions<ApiKeys> apiKeysOptions, // Добавляем ApiKeys
|
||
ILogger<OpenWeatherMapService> logger)
|
||
{
|
||
_httpClientFactory = httpClientFactory;
|
||
_logger = logger;
|
||
_dataSourceUrls = dataSourceUrlsOptions.Value;
|
||
_apiKeys = apiKeysOptions.Value; // Сохраняем ApiKeys
|
||
}
|
||
|
||
public async Task<WeatherData?> GetWeatherAsync(CancellationToken cancellationToken)
|
||
{
|
||
// Проверяем наличие ключа API
|
||
if (string.IsNullOrWhiteSpace(_apiKeys.OpenWeatherMap))
|
||
{
|
||
_logger.LogError("Ключ API для OpenWeatherMap не сконфигурирован.");
|
||
return null;
|
||
}
|
||
|
||
// Формируем URL, подставляя API ключ
|
||
string requestUrl = string.Format(_dataSourceUrls.OpenWeatherMap, _apiKeys.OpenWeatherMap);
|
||
_logger.LogInformation("Запрос погоды с OpenWeatherMap: {Url}", requestUrl);
|
||
|
||
var client = _httpClientFactory.CreateClient();
|
||
|
||
try
|
||
{
|
||
HttpResponseMessage response = await client.GetAsync(requestUrl, cancellationToken);
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
_logger.LogError("Ошибка при запросе к API OpenWeatherMap. Статус код: {StatusCode}", response.StatusCode);
|
||
// Попробуем прочитать тело ошибки, если есть
|
||
string errorBody = await response.Content.ReadAsStringAsync(cancellationToken);
|
||
_logger.LogError("Тело ошибки OpenWeatherMap: {ErrorBody}", errorBody);
|
||
return null;
|
||
}
|
||
|
||
string jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken);
|
||
|
||
// Десериализуем JSON в наш DTO
|
||
var weatherResponseDto = JsonConvert.DeserializeObject<WeatherResponseDto>(jsonResponse);
|
||
|
||
// Проверяем основные данные
|
||
if (weatherResponseDto?.Main == null || weatherResponseDto.Weather == null || !weatherResponseDto.Weather.Any())
|
||
{
|
||
_logger.LogWarning("Не удалось десериализовать основные данные из ответа OpenWeatherMap или они пусты.");
|
||
return null;
|
||
}
|
||
|
||
// Извлекаем нужную информацию
|
||
var mainData = weatherResponseDto.Main;
|
||
var descriptionData = weatherResponseDto.Weather.First(); // Берем первое описание
|
||
|
||
_logger.LogInformation("Погода успешно получена: {Description}, {Temperature}°C", descriptionData.Description, mainData.Temp);
|
||
|
||
// Создаем и возвращаем нашу модель WeatherData
|
||
var resultData = new WeatherData
|
||
{
|
||
TemperatureC = mainData.Temp,
|
||
FeelsLikeC = mainData.FeelsLike,
|
||
Description = descriptionData.Description ?? "Нет данных", // Используем ?? для значения по умолчанию
|
||
Icon = descriptionData.Icon ?? ""
|
||
};
|
||
|
||
return resultData;
|
||
}
|
||
catch (HttpRequestException ex)
|
||
{
|
||
_logger.LogError(ex, "Ошибка HTTP при запросе к API OpenWeatherMap.");
|
||
return null;
|
||
}
|
||
catch (JsonException ex)
|
||
{
|
||
_logger.LogError(ex, "Ошибка парсинга JSON ответа OpenWeatherMap.");
|
||
return null;
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
_logger.LogInformation("Запрос погоды был отменен.");
|
||
return null;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "Неожиданная ошибка при получении погоды OpenWeatherMap.");
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
} |