121 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			121 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | 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 | ||
|  |     } | ||
|  | } |