using Mercados.Infrastructure.DataFetchers; namespace Mercados.Worker { public class DataFetchingService : BackgroundService { private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; // Diccionario para rastrear la 煤ltima vez que se ejecut贸 una tarea diaria. private readonly Dictionary _lastDailyRun = new(); public DataFetchingService(ILogger 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>(); 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>(); 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 } }