Init Commit
This commit is contained in:
155
Backend/GestorFacturas.API/Workers/CronogramaWorker.cs
Normal file
155
Backend/GestorFacturas.API/Workers/CronogramaWorker.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using GestorFacturas.API.Data;
|
||||
using GestorFacturas.API.Models;
|
||||
using GestorFacturas.API.Services.Interfaces;
|
||||
|
||||
namespace GestorFacturas.API.Workers;
|
||||
|
||||
/// <summary>
|
||||
/// Worker Service que ejecuta el procesamiento de facturas de forma programada
|
||||
/// </summary>
|
||||
public class CronogramaWorker : BackgroundService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<CronogramaWorker> _logger;
|
||||
// Eliminamos el Timer antiguo
|
||||
|
||||
public CronogramaWorker(
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<CronogramaWorker> logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.LogInformation("CronogramaWorker iniciado correctamente.");
|
||||
|
||||
// PeriodicTimer espera a que termine la ejecución antes de contar el siguiente intervalo.
|
||||
// Iniciamos con un tick de 1 minuto para chequear.
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
|
||||
|
||||
try
|
||||
{
|
||||
// Bucle infinito mientras el servicio esté activo
|
||||
while (await timer.WaitForNextTickAsync(stoppingToken))
|
||||
{
|
||||
await VerificarYEjecutar(stoppingToken);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("CronogramaWorker detenido.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error fatal en el ciclo del CronogramaWorker");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task VerificarYEjecutar(CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
var config = await context.Configuraciones.FirstOrDefaultAsync(c => c.Id == 1, stoppingToken);
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
_logger.LogWarning("No se encontró configuración del sistema");
|
||||
return;
|
||||
}
|
||||
|
||||
// Verificar si el servicio está activo
|
||||
if (!config.EnEjecucion)
|
||||
{
|
||||
// Solo loguear en nivel Debug para no saturar los logs
|
||||
_logger.LogDebug("Servicio en estado detenido");
|
||||
return;
|
||||
}
|
||||
|
||||
// Determinar si toca ejecutar según la periodicidad
|
||||
if (!DebeEjecutar(config))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("¡Es momento de ejecutar el proceso de facturas!");
|
||||
|
||||
// Ejecutar el proceso
|
||||
var procesador = scope.ServiceProvider.GetRequiredService<IProcesadorFacturasService>();
|
||||
|
||||
// Calcular fecha desde basándonos en la última ejecución o periodicidad
|
||||
DateTime fechaDesde = CalcularFechaDesde(config);
|
||||
|
||||
await procesador.EjecutarProcesoAsync(fechaDesde);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error en CronogramaWorker al verificar y ejecutar");
|
||||
}
|
||||
}
|
||||
|
||||
private bool DebeEjecutar(Configuracion config)
|
||||
{
|
||||
if (config.UltimaEjecucion == null) return true;
|
||||
|
||||
var ahora = DateTime.Now;
|
||||
var ultimaEjecucion = config.UltimaEjecucion.Value;
|
||||
|
||||
if (!TimeSpan.TryParse(config.HoraEjecucion, out TimeSpan horaConfigurada))
|
||||
{
|
||||
horaConfigurada = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
switch (config.Periodicidad.ToUpper())
|
||||
{
|
||||
case "MINUTOS":
|
||||
var minutosTranscurridos = (ahora - ultimaEjecucion).TotalMinutes;
|
||||
return minutosTranscurridos >= config.ValorPeriodicidad;
|
||||
|
||||
case "DIAS":
|
||||
case "DÍAS":
|
||||
var diasTranscurridos = (ahora.Date - ultimaEjecucion.Date).Days;
|
||||
if (diasTranscurridos < config.ValorPeriodicidad) return false;
|
||||
|
||||
var horaActual = ahora.TimeOfDay;
|
||||
var yaEjecutadoHoy = ultimaEjecucion.Date == ahora.Date;
|
||||
|
||||
// Ejecuta si pasó la hora configurada Y no se ha ejecutado hoy
|
||||
return horaActual >= horaConfigurada && !yaEjecutadoHoy;
|
||||
|
||||
case "MESES":
|
||||
var mesesTranscurridos = ((ahora.Year - ultimaEjecucion.Year) * 12) + ahora.Month - ultimaEjecucion.Month;
|
||||
if (mesesTranscurridos < config.ValorPeriodicidad) return false;
|
||||
|
||||
return ahora.TimeOfDay >= horaConfigurada && !(ultimaEjecucion.Date == ahora.Date);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime CalcularFechaDesde(Configuracion config)
|
||||
{
|
||||
// Buffer de seguridad de 10 días
|
||||
int diasBuffer = 10;
|
||||
|
||||
if (config.UltimaEjecucion != null)
|
||||
{
|
||||
return config.UltimaEjecucion.Value.Date.AddDays(-diasBuffer);
|
||||
}
|
||||
|
||||
// Si es la primera vez, usa la periodicidad + buffer
|
||||
return config.Periodicidad.ToUpper() switch
|
||||
{
|
||||
"MINUTOS" => DateTime.Now.AddMinutes(-config.ValorPeriodicidad).AddDays(-diasBuffer),
|
||||
"DIAS" or "DÍAS" => DateTime.Now.AddDays(-config.ValorPeriodicidad - diasBuffer),
|
||||
"MESES" => DateTime.Now.AddMonths(-config.ValorPeriodicidad).AddDays(-diasBuffer),
|
||||
_ => DateTime.Today.AddDays(-diasBuffer)
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user