Fix fecha totalizados nivel provincia
This commit is contained in:
		| @@ -184,59 +184,119 @@ public class CriticalDataWorker : BackgroundService | ||||
|   } | ||||
|  | ||||
|   /// <summary> | ||||
|   /// Obtiene y actualiza el resumen de votos a nivel provincial. | ||||
|   /// Esta versión mejorada utiliza una transacción para garantizar la consistencia de los datos. | ||||
|   /// Obtiene y actualiza el resumen de votos y el estado del recuento a nivel provincial. | ||||
|   /// Esta versión actualizada guarda tanto los votos por agrupación (en ResumenesVotos) | ||||
|   /// como el estado general del recuento, incluyendo la fecha de totalización (en EstadosRecuentosGenerales), | ||||
|   /// asegurando que toda la operación sea atómica mediante una transacción de base de datos. | ||||
|   /// </summary> | ||||
|   /// <param name="authToken">El token de autenticación válido para la sesión.</param> | ||||
|   /// <param name="stoppingToken">El token de cancelación para detener la operación.</param> | ||||
|   private async Task SondearResumenProvincialAsync(string authToken, CancellationToken stoppingToken) | ||||
|   { | ||||
|     try | ||||
|     { | ||||
|       // Creamos un scope de DbContext para esta operación. | ||||
|       using var scope = _serviceProvider.CreateScope(); | ||||
|       var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); | ||||
|  | ||||
|       var provincia = await dbContext.AmbitosGeograficos.AsNoTracking().FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken); | ||||
|       if (provincia == null) return; | ||||
|       // Obtenemos el registro de la Provincia (NivelId 10). | ||||
|       var provincia = await dbContext.AmbitosGeograficos | ||||
|           .AsNoTracking() | ||||
|           .FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken); | ||||
|  | ||||
|       var resumen = await _apiService.GetResumenAsync(authToken, provincia.DistritoId!); | ||||
|  | ||||
|       // --- CAMBIO CLAVE: Lógica de actualización robusta --- | ||||
|       // Solo procedemos si la respuesta de la API es válida Y contiene datos de votos positivos. | ||||
|       if (resumen?.ValoresTotalizadosPositivos is { Count: > 0 } nuevosVotos) | ||||
|       // Si no encontramos el ámbito de la provincia, no podemos continuar. | ||||
|       if (provincia == null) | ||||
|       { | ||||
|         // Usamos una transacción explícita para asegurar que la operación sea atómica: | ||||
|         // O se completa todo (borrado e inserción), o no se hace nada. | ||||
|         _logger.LogWarning("No se encontró el ámbito 'Provincia' (NivelId 10) para el sondeo de resumen."); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       // Llamamos a la API para obtener el resumen de datos provincial. | ||||
|       var resumenDto = await _apiService.GetResumenAsync(authToken, provincia.DistritoId!); | ||||
|  | ||||
|       // Solo procedemos si la API devolvió una respuesta válida y no nula. | ||||
|       if (resumenDto != null) | ||||
|       { | ||||
|         // Iniciamos una transacción explícita. Esto garantiza que todas las operaciones de base de datos | ||||
|         // dentro de este bloque (el DELETE, los INSERTs y los UPDATEs) se completen con éxito, | ||||
|         // o si algo falla, se reviertan todas, manteniendo la consistencia de los datos. | ||||
|         await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken); | ||||
|  | ||||
|         // 1. Borramos los datos viejos. | ||||
|         await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResumenesVotos", stoppingToken); | ||||
|         // --- 1. ACTUALIZAR LA TABLA 'ResumenesVotos' --- | ||||
|  | ||||
|         // 2. Insertamos los nuevos datos. | ||||
|         foreach (var voto in nuevosVotos) | ||||
|         // Verificamos si la respuesta contiene una lista de votos positivos. | ||||
|         if (resumenDto.ValoresTotalizadosPositivos is { Count: > 0 } nuevosVotos) | ||||
|         { | ||||
|           dbContext.ResumenesVotos.Add(new ResumenVoto | ||||
|           // Estrategia "Borrar y Reemplazar": vaciamos la tabla antes de insertar los nuevos datos. | ||||
|           await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResumenesVotos", stoppingToken); | ||||
|  | ||||
|           // Añadimos cada nuevo registro de voto al DbContext. | ||||
|           foreach (var voto in nuevosVotos) | ||||
|           { | ||||
|             AmbitoGeograficoId = provincia.Id, | ||||
|             AgrupacionPoliticaId = voto.IdAgrupacion, | ||||
|             Votos = voto.Votos, | ||||
|             VotosPorcentaje = voto.VotosPorcentaje | ||||
|           }); | ||||
|             dbContext.ResumenesVotos.Add(new ResumenVoto | ||||
|             { | ||||
|               AmbitoGeograficoId = provincia.Id, | ||||
|               AgrupacionPoliticaId = voto.IdAgrupacion, | ||||
|               Votos = voto.Votos, | ||||
|               VotosPorcentaje = voto.VotosPorcentaje | ||||
|             }); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         // 3. Guardamos los cambios y confirmamos la transacción. | ||||
|         // --- 2. ACTUALIZAR LA TABLA 'EstadosRecuentosGenerales' --- | ||||
|  | ||||
|         // El endpoint de Resumen no especifica una categoría, por lo que aplicamos sus datos de estado de recuento | ||||
|         // a todas las categorías que tenemos en nuestra base de datos. | ||||
|         var todasLasCategorias = await dbContext.CategoriasElectorales.AsNoTracking().ToListAsync(stoppingToken); | ||||
|         foreach (var categoria in todasLasCategorias) | ||||
|         { | ||||
|           // Buscamos el registro existente usando la clave primaria compuesta. | ||||
|           var registroDb = await dbContext.EstadosRecuentosGenerales.FindAsync(new object[] { provincia.Id, categoria.Id }, stoppingToken); | ||||
|  | ||||
|           // Si no existe, lo creamos. | ||||
|           if (registroDb == null) | ||||
|           { | ||||
|             registroDb = new EstadoRecuentoGeneral { AmbitoGeograficoId = provincia.Id, CategoriaId = categoria.Id }; | ||||
|             dbContext.EstadosRecuentosGenerales.Add(registroDb); | ||||
|           } | ||||
|  | ||||
|           // Parseamos la fecha de forma segura para evitar errores con cadenas vacías o nulas. | ||||
|           if (DateTime.TryParse(resumenDto.FechaTotalizacion, out var parsedDate)) | ||||
|           { | ||||
|             registroDb.FechaTotalizacion = parsedDate.ToUniversalTime(); | ||||
|           } | ||||
|  | ||||
|           // Mapeamos el resto de los datos del estado del recuento. | ||||
|           registroDb.MesasEsperadas = resumenDto.EstadoRecuento.MesasEsperadas; | ||||
|           registroDb.MesasTotalizadas = resumenDto.EstadoRecuento.MesasTotalizadas; | ||||
|           registroDb.MesasTotalizadasPorcentaje = resumenDto.EstadoRecuento.MesasTotalizadasPorcentaje; | ||||
|           registroDb.CantidadElectores = resumenDto.EstadoRecuento.CantidadElectores; | ||||
|           registroDb.CantidadVotantes = resumenDto.EstadoRecuento.CantidadVotantes; | ||||
|           registroDb.ParticipacionPorcentaje = resumenDto.EstadoRecuento.ParticipacionPorcentaje; | ||||
|         } | ||||
|  | ||||
|         // 3. CONFIRMAR Y GUARDAR | ||||
|         // Guardamos todos los cambios preparados (DELETEs, INSERTs, UPDATEs) en la base de datos. | ||||
|         await dbContext.SaveChangesAsync(stoppingToken); | ||||
|         // Confirmamos la transacción para hacer los cambios permanentes. | ||||
|         await transaction.CommitAsync(stoppingToken); | ||||
|  | ||||
|         _logger.LogInformation("Sondeo de Resumen Provincial completado. La tabla ha sido actualizada."); | ||||
|         _logger.LogInformation("Sondeo de Resumen Provincial completado. Las tablas han sido actualizadas."); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         // Si la API no devuelve datos de votos, no hacemos NADA en la base de datos. | ||||
|         _logger.LogInformation("Sondeo de Resumen Provincial completado. No se recibieron datos nuevos, la tabla no fue modificada."); | ||||
|         // Si la API no devolvió datos (ej. devuelve null), no hacemos nada en la BD. | ||||
|         _logger.LogInformation("Sondeo de Resumen Provincial completado. No se recibieron datos nuevos."); | ||||
|       } | ||||
|     } | ||||
|     catch (OperationCanceledException) | ||||
|     { | ||||
|       _logger.LogInformation("Sondeo de resumen provincial cancelado."); | ||||
|     } | ||||
|     catch (Exception ex) | ||||
|     { | ||||
|       _logger.LogError(ex, "Ocurrió un error en el sondeo de Resumen Provincial."); | ||||
|       // Capturamos cualquier otro error inesperado para que el worker no se detenga. | ||||
|       _logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Resumen Provincial."); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user