using CredentialManager.Services; using Data_Coupler.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Data_Coupler.BackgroundServices; /// /// Servizio di background per l'esecuzione automatica delle schedulazioni /// public class ScheduleExecutorService : BackgroundService { private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; private readonly TimeSpan _checkInterval = TimeSpan.FromMinutes(1); // Controlla ogni minuto public ScheduleExecutorService(IServiceProvider serviceProvider, ILogger logger) { _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("ScheduleExecutorService avviato. Controllo schedulazioni ogni {Interval} minuti.", _checkInterval.TotalMinutes); while (!stoppingToken.IsCancellationRequested) { try { await CheckAndExecuteSchedules(); await Task.Delay(_checkInterval, stoppingToken); } catch (OperationCanceledException) { _logger.LogInformation("ScheduleExecutorService arrestato."); break; } catch (Exception ex) { _logger.LogError(ex, "Errore nel controllo schedulazioni"); await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); // Attendi 5 minuti prima di riprovare } } } private async Task CheckAndExecuteSchedules() { using var scope = _serviceProvider.CreateScope(); var scheduleService = scope.ServiceProvider.GetRequiredService(); var dataTransferService = scope.ServiceProvider.GetRequiredService(); try { var pendingSchedules = await scheduleService.GetPendingExecutionsAsync(); if (pendingSchedules.Any()) { _logger.LogInformation("Trovate {Count} schedulazioni in attesa di esecuzione", pendingSchedules.Count); } foreach (var schedule in pendingSchedules) { try { await ExecuteSchedule(schedule, scheduleService, dataTransferService); } catch (Exception ex) { _logger.LogError(ex, "Errore nell'esecuzione della schedulazione {ScheduleName} (ID: {ScheduleId})", schedule.Name, schedule.Id); // Aggiorna lo status dell'esecuzione fallita await scheduleService.UpdateExecutionStatusAsync(schedule.Id, "failed", $"Errore nell'esecuzione: {ex.Message}"); } } } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero delle schedulazioni in attesa"); } } private async Task ExecuteSchedule(CredentialManager.Models.ProfileSchedule schedule, IProfileScheduleService scheduleService, IDataTransferService dataTransferService) { _logger.LogInformation("Esecuzione schedulazione {ScheduleName} (ID: {ScheduleId}) iniziata", schedule.Name, schedule.Id); // Aggiorna lo status a "running" await scheduleService.UpdateExecutionStatusAsync(schedule.Id, "running", "Esecuzione automatica avviata"); try { if (schedule.Profile == null) { throw new InvalidOperationException("Profilo associato alla schedulazione non trovato"); } // Esegui il trasferimento dati var result = await dataTransferService.ExecuteProfileAsync(schedule.Profile); // Aggiorna lo status con il risultato var status = result.IsSuccess ? "success" : "failed"; var message = result.IsSuccess ? $"Esecuzione completata con successo in {result.Duration.TotalSeconds:F1} secondi. " + $"{result.RecordsProcessed} record elaborati." : $"Esecuzione fallita: {result.ErrorMessage}"; await scheduleService.UpdateExecutionStatusAsync(schedule.Id, status, message, result.RecordsProcessed); if (result.IsSuccess) { _logger.LogInformation("Schedulazione {ScheduleName} completata con successo. " + "Record processati: {RecordsProcessed}, Durata: {Duration}s", schedule.Name, result.RecordsProcessed, result.Duration.TotalSeconds); } else { _logger.LogWarning("Schedulazione {ScheduleName} fallita: {ErrorMessage}", schedule.Name, result.ErrorMessage); } } catch (Exception ex) { _logger.LogError(ex, "Errore durante l'esecuzione della schedulazione {ScheduleName}", schedule.Name); await scheduleService.UpdateExecutionStatusAsync(schedule.Id, "failed", $"Errore nell'esecuzione: {ex.Message}"); throw; // Re-throw per permettere la gestione a livello superiore } } public override async Task StopAsync(CancellationToken stoppingToken) { _logger.LogInformation("ScheduleExecutorService in fase di arresto..."); await base.StopAsync(stoppingToken); } }