// backend/src/Titulares.Api/Workers/ProcesoScrapingWorker.cs using Microsoft.AspNetCore.SignalR; using Titulares.Api.Data; using Titulares.Api.Hubs; using Titulares.Api.Services; using Microsoft.Extensions.Options; using Titulares.Api.Models; namespace Titulares.Api.Workers; public class ProcesoScrapingWorker : BackgroundService { private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; private readonly IOptionsMonitor _configuracion; public ProcesoScrapingWorker(ILogger logger, IServiceProvider serviceProvider, IOptionsMonitor configuracion) { _logger = logger; _serviceProvider = serviceProvider; _configuracion = configuracion; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { var configActual = _configuracion.CurrentValue; _logger.LogInformation("Iniciando ciclo de scraping con cantidad: {cantidad}", configActual.CantidadTitularesAScrapear); try { // Creamos un 'scope' para obtener instancias 'scoped' de nuestros servicios. // Es la práctica correcta en servicios de larga duración. using (var scope = _serviceProvider.CreateScope()) { var repositorio = scope.ServiceProvider.GetRequiredService(); var scrapingService = scope.ServiceProvider.GetRequiredService(); var hubContext = scope.ServiceProvider.GetRequiredService>(); var csvService = scope.ServiceProvider.GetRequiredService(); // Obtener estos valores desde la configuración int cantidadAObtener = configActual.CantidadTitularesAScrapear; int limiteTotalEnDb = configActual.LimiteTotalEnDb; // 1. Obtener los últimos titulares de la web var articulosScrapeados = await scrapingService.ObtenerUltimosTitulares(cantidadAObtener); if (articulosScrapeados.Any()) { await repositorio.SincronizarDesdeScraping(articulosScrapeados, limiteTotalEnDb); _logger.LogInformation("Sincronización con la base de datos completada."); var titularesActualizados = await repositorio.ObtenerTodosAsync(); await hubContext.Clients.All.SendAsync("TitularesActualizados", titularesActualizados, stoppingToken); _logger.LogInformation("Notificación enviada a los clientes."); await csvService.GenerarCsvAsync(titularesActualizados); } else { _logger.LogWarning("No se encontraron artículos en el scraping."); } } } catch (Exception ex) { _logger.LogError(ex, "Ocurrió un error durante el proceso de scraping."); } var intervaloEnMinutos = configActual.IntervaloMinutos; _logger.LogInformation("Proceso en espera por {minutos} minutos.", intervaloEnMinutos); await Task.Delay(TimeSpan.FromMinutes(intervaloEnMinutos), stoppingToken); } } }