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