Feat(holidays): Implement database-backed holiday detection system
- 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`."
			
			
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
using Mercados.Core.Entities;
 | 
			
		||||
using Mercados.Infrastructure.Persistence.Repositories;
 | 
			
		||||
using Mercados.Infrastructure.Services;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace Mercados.Api.Controllers
 | 
			
		||||
@@ -11,6 +12,7 @@ namespace Mercados.Api.Controllers
 | 
			
		||||
        private readonly ICotizacionBolsaRepository _bolsaRepo;
 | 
			
		||||
        private readonly ICotizacionGranoRepository _granoRepo;
 | 
			
		||||
        private readonly ICotizacionGanadoRepository _ganadoRepo;
 | 
			
		||||
        private readonly IHolidayService _holidayService;
 | 
			
		||||
        private readonly ILogger<MercadosController> _logger;
 | 
			
		||||
 | 
			
		||||
        // Inyectamos TODOS los repositorios que necesita el controlador.
 | 
			
		||||
@@ -18,11 +20,13 @@ namespace Mercados.Api.Controllers
 | 
			
		||||
            ICotizacionBolsaRepository bolsaRepo,
 | 
			
		||||
            ICotizacionGranoRepository granoRepo,
 | 
			
		||||
            ICotizacionGanadoRepository ganadoRepo,
 | 
			
		||||
            IHolidayService holidayService,
 | 
			
		||||
            ILogger<MercadosController> logger)
 | 
			
		||||
        {
 | 
			
		||||
            _bolsaRepo = bolsaRepo;
 | 
			
		||||
            _granoRepo = granoRepo;
 | 
			
		||||
            _ganadoRepo = ganadoRepo;
 | 
			
		||||
            _holidayService = holidayService;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -147,5 +151,30 @@ namespace Mercados.Api.Controllers
 | 
			
		||||
                return StatusCode(500, "Ocurrió un error interno en el servidor.");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpGet("es-feriado/{mercado}")]
 | 
			
		||||
        [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
        public async Task<IActionResult> IsMarketHoliday(string mercado)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // Usamos la fecha actual en la zona horaria de Argentina
 | 
			
		||||
                TimeZoneInfo argentinaTimeZone;
 | 
			
		||||
                try { argentinaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Argentina/Buenos_Aires"); }
 | 
			
		||||
                catch { argentinaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Argentina Standard Time"); }
 | 
			
		||||
 | 
			
		||||
                var todayInArgentina = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, argentinaTimeZone);
 | 
			
		||||
 | 
			
		||||
                var esFeriado = await _holidayService.IsMarketHolidayAsync(mercado.ToUpper(), todayInArgentina);
 | 
			
		||||
                return Ok(esFeriado);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogError(ex, "Error al comprobar si es feriado para el mercado {Mercado}.", mercado);
 | 
			
		||||
                // Si hay un error, devolvemos 'false' para no bloquear la UI innecesariamente.
 | 
			
		||||
                return Ok(false);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,6 +5,7 @@ using Mercados.Infrastructure.Persistence;
 | 
			
		||||
using Mercados.Infrastructure.Persistence.Repositories;
 | 
			
		||||
using Mercados.Api.Utils;
 | 
			
		||||
using Microsoft.AspNetCore.HttpOverrides;
 | 
			
		||||
using Mercados.Infrastructure.Services;
 | 
			
		||||
 | 
			
		||||
var builder = WebApplication.CreateBuilder(args);
 | 
			
		||||
 | 
			
		||||
@@ -32,6 +33,10 @@ builder.Services.AddScoped<ICotizacionGanadoRepository, CotizacionGanadoReposito
 | 
			
		||||
builder.Services.AddScoped<ICotizacionGranoRepository, CotizacionGranoRepository>();
 | 
			
		||||
builder.Services.AddScoped<ICotizacionBolsaRepository, CotizacionBolsaRepository>();
 | 
			
		||||
builder.Services.AddScoped<IFuenteDatoRepository, FuenteDatoRepository>();
 | 
			
		||||
builder.Services.AddScoped<IMercadoFeriadoRepository, MercadoFeriadoRepository>();
 | 
			
		||||
builder.Services.AddMemoryCache();
 | 
			
		||||
builder.Services.AddScoped<IMercadoFeriadoRepository, MercadoFeriadoRepository>();
 | 
			
		||||
builder.Services.AddScoped<IHolidayService, FinnhubHolidayService>();
 | 
			
		||||
 | 
			
		||||
// Configuración de FluentMigrator (perfecto)
 | 
			
		||||
builder.Services
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user