- Adds a new `MercadosFeriados` table to the database to persist market holidays.
- Implements `HolidayDataFetcher` to update holidays weekly from Finnhub API.
- Implements `IHolidayService` with in-memory caching to check for holidays efficiently.
- Worker service now skips fetcher execution on market holidays.
- Adds a new API endpoint `/api/mercados/es-feriado/{mercado}`.
- Integrates a non-blocking holiday alert into the `BolsaLocalWidget`."
		
	
		
			
				
	
	
		
			84 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			84 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Microsoft.Extensions.Configuration;
 | |
| using Microsoft.Extensions.Logging;
 | |
| using System.Net.Http.Json;
 | |
| using Mercados.Core.Entities;
 | |
| using Mercados.Infrastructure.Persistence.Repositories;
 | |
| using System.Text.Json.Serialization;
 | |
| 
 | |
| namespace Mercados.Infrastructure.DataFetchers
 | |
| {
 | |
|     // Añadimos las clases DTO necesarias para deserializar la respuesta de Finnhub
 | |
|     public class MarketHolidayResponse
 | |
|     {
 | |
|         [JsonPropertyName("data")]
 | |
|         public List<MarketHoliday>? Data { get; set; }
 | |
|     }
 | |
|     public class MarketHoliday
 | |
|     {
 | |
|         [JsonPropertyName("at")]
 | |
|         public string? At { get; set; }
 | |
| 
 | |
|         [JsonIgnore]
 | |
|         public DateOnly Date => DateOnly.FromDateTime(DateTime.Parse(At!));
 | |
|     }
 | |
| 
 | |
|     public class HolidayDataFetcher : IDataFetcher
 | |
|     {
 | |
|         public string SourceName => "Holidays";
 | |
|         private readonly string[] _marketCodes = { "US", "BA" };
 | |
| 
 | |
|         private readonly IHttpClientFactory _httpClientFactory;
 | |
|         private readonly IMercadoFeriadoRepository _feriadoRepository;
 | |
|         private readonly IConfiguration _configuration;
 | |
|         private readonly ILogger<HolidayDataFetcher> _logger;
 | |
| 
 | |
|         public HolidayDataFetcher(
 | |
|             IHttpClientFactory httpClientFactory,
 | |
|             IMercadoFeriadoRepository feriadoRepository,
 | |
|             IConfiguration configuration,
 | |
|             ILogger<HolidayDataFetcher> logger)
 | |
|         {
 | |
|             _httpClientFactory = httpClientFactory;
 | |
|             _feriadoRepository = feriadoRepository;
 | |
|             _configuration = configuration;
 | |
|             _logger = logger;
 | |
|         }
 | |
| 
 | |
|         public async Task<(bool Success, string Message)> FetchDataAsync()
 | |
|         {
 | |
|             _logger.LogInformation("Iniciando actualización de feriados desde Finnhub.");
 | |
|             var apiKey = _configuration["ApiKeys:Finnhub"];
 | |
|             if (string.IsNullOrEmpty(apiKey)) return (false, "API Key de Finnhub no configurada.");
 | |
| 
 | |
|             var client = _httpClientFactory.CreateClient("FinnhubDataFetcher");
 | |
| 
 | |
|             foreach (var marketCode in _marketCodes)
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     var apiUrl = $"https://finnhub.io/api/v1/stock/market-holiday?exchange={marketCode}&token={apiKey}";
 | |
|                     // Ahora la deserialización funcionará porque la clase existe
 | |
|                     var response = await client.GetFromJsonAsync<MarketHolidayResponse>(apiUrl);
 | |
| 
 | |
|                     if (response?.Data != null)
 | |
|                     {
 | |
|                         var nuevosFeriados = response.Data.Select(h => new MercadoFeriado
 | |
|                         {
 | |
|                             CodigoMercado = marketCode,
 | |
|                             Fecha = h.Date.ToDateTime(TimeOnly.MinValue),
 | |
|                             Nombre = "Feriado Bursátil"
 | |
|                         }).ToList();
 | |
| 
 | |
|                         await _feriadoRepository.ReemplazarFeriadosPorMercadoAsync(marketCode, nuevosFeriados);
 | |
|                         _logger.LogInformation("Feriados para {MarketCode} actualizados exitosamente: {Count} registros.", marketCode, nuevosFeriados.Count);
 | |
|                     }
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     _logger.LogError(ex, "Falló la obtención de feriados para {MarketCode}.", marketCode);
 | |
|                 }
 | |
|             }
 | |
|             return (true, "Actualización de feriados completada.");
 | |
|         }
 | |
|     }
 | |
| } |