Files
Mercados-Web/src/Mercados.Worker/DataFetchingService.cs

121 lines
4.8 KiB
C#
Raw Normal View History

using Mercados.Infrastructure.DataFetchers;
namespace Mercados.Worker
{
public class DataFetchingService : BackgroundService
{
private readonly ILogger<DataFetchingService> _logger;
private readonly IServiceProvider _serviceProvider;
// Diccionario para rastrear la última vez que se ejecutó una tarea diaria.
private readonly Dictionary<string, DateTime> _lastDailyRun = new();
public DataFetchingService(ILogger<DataFetchingService> logger, IServiceProvider serviceProvider)
{
_logger = logger;
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("🚀 Servicio de Fetching iniciado a las: {time}", DateTimeOffset.Now);
// Ejecutamos una vez al inicio para tener datos frescos inmediatamente.
await RunAllFetchersAsync();
// Usamos un PeriodicTimer que "despierta" cada minuto para revisar si hay tareas pendientes.
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
while (await timer.WaitForNextTickAsync(stoppingToken))
{
await RunScheduledTasksAsync();
}
}
private async Task RunScheduledTasksAsync()
{
// --- Lógica de Planificación ---
var now = DateTime.Now;
// Tarea 1: Mercado Agroganadero (todos los días a las 11:00)
if (now.Hour == 11 && now.Minute == 0 && HasNotRunToday("MercadoAgroganadero"))
{
await RunFetcherByNameAsync("MercadoAgroganadero");
_lastDailyRun["MercadoAgroganadero"] = now.Date;
}
// Tarea 2: Granos BCR (todos los días a las 11:30)
if (now.Hour == 11 && now.Minute == 30 && HasNotRunToday("BCR"))
{
await RunFetcherByNameAsync("BCR");
_lastDailyRun["BCR"] = now.Date;
}
// Tarea 3: Mercados de Bolsa (cada 10 minutos si el mercado está abierto)
if (IsMarketOpen(now) && now.Minute % 10 == 0)
{
_logger.LogInformation("Mercados abiertos. Ejecutando fetchers de bolsa.");
await RunFetcherByNameAsync("Finnhub");
await RunFetcherByNameAsync("YahooFinance");
}
}
// Esta función crea un "scope" para ejecutar un fetcher específico.
// Esto es crucial para que la inyección de dependencias funcione correctamente.
private async Task RunFetcherByNameAsync(string sourceName)
{
_logger.LogInformation("Intentando ejecutar fetcher: {sourceName}", sourceName);
using var scope = _serviceProvider.CreateScope();
var fetchers = scope.ServiceProvider.GetRequiredService<IEnumerable<IDataFetcher>>();
var fetcher = fetchers.FirstOrDefault(f => f.SourceName.Equals(sourceName, StringComparison.OrdinalIgnoreCase));
if (fetcher != null)
{
var (success, message) = await fetcher.FetchDataAsync();
if (!success)
{
_logger.LogError("Falló la ejecución del fetcher {sourceName}: {message}", sourceName, message);
}
}
else
{
_logger.LogWarning("No se encontró un fetcher con el nombre: {sourceName}", sourceName);
}
}
// Función de ayuda para ejecutar todos los fetchers (usada al inicio).
private async Task RunAllFetchersAsync()
{
_logger.LogInformation("Ejecutando todos los fetchers al iniciar...");
using var scope = _serviceProvider.CreateScope();
var fetchers = scope.ServiceProvider.GetRequiredService<IEnumerable<IDataFetcher>>();
foreach (var fetcher in fetchers)
{
await RunFetcherByNameAsync(fetcher.SourceName);
}
}
#region Funciones de Ayuda para la Planificación
private bool HasNotRunToday(string taskName)
{
return !_lastDailyRun.ContainsKey(taskName) || _lastDailyRun[taskName].Date < DateTime.Now.Date;
}
private bool IsMarketOpen(DateTime now)
{
// Lunes a Viernes (1 a 5, Domingo es 0)
if (now.DayOfWeek == DayOfWeek.Saturday || now.DayOfWeek == DayOfWeek.Sunday)
return false;
// Horario de mercado de 11:00 a 17:15 (hora de Argentina)
var marketOpen = new TimeSpan(11, 0, 0);
var marketClose = new TimeSpan(17, 15, 0);
return now.TimeOfDay >= marketOpen && now.TimeOfDay <= marketClose;
}
#endregion
}
}