Fix: Conteo y Clasificación de Eventos y Docker Compose Puerto

This commit is contained in:
2025-12-15 11:40:37 -03:00
parent 5ddef72f06
commit 6ab3c22362
2 changed files with 52 additions and 39 deletions

View File

@@ -37,6 +37,12 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
public async Task EjecutarProcesoAsync(DateTime fechaDesde)
{
// Contadores inicializados AL PRINCIPIO
int copiadas = 0;
int omitidas = 0;
int errores = 0;
// No declarar 'int procesadas = 0' porque es redundante con copiadas/omitidas
var config = await _context.Configuraciones.FirstOrDefaultAsync(c => c.Id == 1);
if (config == null)
{
@@ -44,7 +50,6 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
return;
}
// Actualizamos la fecha de ejecución
config.UltimaEjecucion = DateTime.Now;
await _context.SaveChangesAsync();
@@ -64,20 +69,27 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
await RegistrarEventoAsync($"Se encontraron {facturas.Count} facturas para procesar", TipoEvento.Info);
int procesadas = 0;
int errores = 0;
List<FacturaParaProcesar> pendientes = new();
List<string> detallesErroresParaMail = new();
// Lista para rastrear las entidades de Evento y actualizar su flag 'Enviado' luego
List<Evento> eventosDeErrorParaActualizar = new();
// --- 1. Primer intento ---
foreach (var factura in facturas)
{
bool exito = await ProcesarFacturaAsync(factura, config);
if (exito) procesadas++;
else pendientes.Add(factura);
var resultado = await ProcesarFacturaAsync(factura, config);
switch (resultado)
{
case ResultadoProceso.Copiado:
copiadas++;
break;
case ResultadoProceso.Omitido:
omitidas++;
break;
case ResultadoProceso.Error:
pendientes.Add(factura);
break;
}
}
// --- 2. Sistema de Reintentos ---
@@ -93,11 +105,14 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
foreach (var factura in pendientes)
{
bool exito = await ProcesarFacturaAsync(factura, config);
if (exito)
var resultado = await ProcesarFacturaAsync(factura, config);
if (resultado != ResultadoProceso.Error)
{
procesadas++;
_logger.LogInformation("Archivo encontrado en reintento {intento}: {archivo}", intento, factura.NombreArchivoOrigen);
if (resultado == ResultadoProceso.Copiado) copiadas++;
if (resultado == ResultadoProceso.Omitido) omitidas++;
_logger.LogInformation("Archivo recuperado en reintento {intento}: {archivo}", intento, factura.NombreArchivoOrigen);
}
else
{
@@ -110,7 +125,7 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
// --- 3. Registro de Errores Finales ---
if (pendientes.Count > 0)
{
errores = pendientes.Count;
errores = pendientes.Count; // Aquí usamos la variable ya declarada arriba
foreach (var factura in pendientes)
{
string msgError = $"El archivo NO EXISTE después de {MAX_REINTENTOS} intentos: {factura.NombreArchivoOrigen}";
@@ -124,8 +139,6 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
};
_context.Eventos.Add(eventoError);
// Guardamos referencias
eventosDeErrorParaActualizar.Add(eventoError);
detallesErroresParaMail.Add(factura.NombreArchivoOrigen);
}
@@ -151,40 +164,31 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
catch { }
// --- 6. Evento Final ---
var mensajeFinal = $"Proceso finalizado. Procesadas: {procesadas}, Errores: {errores}";
var mensajeFinal = $"Proceso finalizado. Nuevas: {copiadas}, Verificadas: {omitidas}, Errores: {errores}";
await RegistrarEventoAsync(mensajeFinal, errores > 0 ? TipoEvento.Warning : TipoEvento.Info);
// --- 7. Envío de Mail Inteligente (Solo 1 vez por archivo) ---
// --- 7. Envío de Mail Inteligente ---
if (errores > 0 && config.AvisoMail && !string.IsNullOrEmpty(config.SMTPDestinatario))
{
// 1. Buscamos en la historia de la DB si estos archivos ya fueron reportados previamente.
// Buscamos en TODOS los logs disponibles (que suelen ser los últimos 30 días según la limpieza).
// Filtramos por Tipo Error y Enviado=true.
var historialErroresEnviados = await _context.Eventos
.Where(e => e.Tipo == TipoEvento.Error.ToString() && e.Enviado == true)
.Select(e => e.Mensaje)
.ToListAsync();
// 2. Filtramos la lista actual:
// Solo queremos los archivos que NO aparezcan en ningún mensaje del historial.
var archivosNuevosParaNotificar = detallesErroresParaMail.Where(archivoFallido =>
{
// El mensaje en BD es: "El archivo NO EXISTE...: nombre_archivo.pdf"
// Chequeamos si el nombre del archivo está contenido en algún mensaje viejo.
bool yaFueNotificado = historialErroresEnviados.Any(msgHistorico => msgHistorico.Contains(archivoFallido));
return !yaFueNotificado;
}).ToList();
// 3. Decidir si enviar mail
bool mailEnviado = false;
if (archivosNuevosParaNotificar.Count > 0)
{
// Si hay archivos NUEVOS, enviamos mail SOLO con esos.
// Nota: Pasamos 'errores' (total técnico) y 'archivosNuevosParaNotificar' (detalle visual)
// Pasamos 'copiadas' en lugar de 'procesadas' para el mail, o la suma de ambas si prefieres
mailEnviado = await EnviarNotificacionErroresAsync(
config.SMTPDestinatario,
procesadas,
copiadas + omitidas,
errores,
archivosNuevosParaNotificar
);
@@ -196,14 +200,10 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
}
else
{
_logger.LogInformation("Se omitió el envío de correo: Los {count} errores ya fueron notificados anteriormente.", errores);
// Simulamos que se "envió" (se gestionó) para marcar los flags en BD
_logger.LogInformation("Se omitió el envío de correo por errores repetidos.");
mailEnviado = true;
}
// 4. Actualizar flag en BD (CRÍTICO)
// Si gestionamos la notificación correctamente (ya sea enviándola o detectando que ya estaba enviada),
// marcamos los eventos actuales como Enviado=true para que pasen al historial y no se vuelvan a procesar.
if (mailEnviado && eventosDeErrorParaActualizar.Count > 0)
{
foreach (var evento in eventosDeErrorParaActualizar)
@@ -296,7 +296,7 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
return facturas;
}
private async Task<bool> ProcesarFacturaAsync(FacturaParaProcesar factura, Configuracion config)
private async Task<ResultadoProceso> ProcesarFacturaAsync(FacturaParaProcesar factura, Configuracion config)
{
try
{
@@ -306,7 +306,8 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
string rutaOrigen = Path.Combine(rutaBaseOrigen, factura.NombreArchivoOrigen);
string carpetaDestinoFinal = Path.Combine(rutaBaseDestino, factura.CarpetaDestino);
if (!File.Exists(rutaOrigen)) return false;
// Si no existe origen, es un error (para reintento)
if (!File.Exists(rutaOrigen)) return ResultadoProceso.Error;
if (!Directory.Exists(carpetaDestinoFinal))
{
@@ -319,16 +320,21 @@ public class ProcesadorFacturasService : IProcesadorFacturasService
if (infoDestino.Exists)
{
if (infoDestino.Length == infoOrigen.Length) return true;
// Si ya existe y es igual, lo OMITIMOS
if (infoDestino.Length == infoOrigen.Length)
{
return ResultadoProceso.Omitido;
}
}
// Si llegamos acá, copiamos
File.Copy(rutaOrigen, rutaDestinoCompleta, overwrite: true);
return true;
return ResultadoProceso.Copiado;
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Fallo al copiar {archivo}", factura.NombreArchivoOrigen);
return false;
return ResultadoProceso.Error;
}
}
@@ -481,3 +487,10 @@ public class FacturaParaProcesar
public string NombreArchivoDestino { get; set; } = string.Empty;
public string CarpetaDestino { get; set; } = string.Empty;
}
public enum ResultadoProceso
{
Copiado, // Era nuevo y se copió
Omitido, // Ya existía y estaba bien
Error // No se encontró o falló
}