Fix bancas y telegramas
This commit is contained in:
@@ -83,13 +83,21 @@ public class ElectoralApiService : IElectoralApiService
|
|||||||
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<RepartoBancasDto>() : null;
|
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<RepartoBancasDto>() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId)
|
public async Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null)
|
||||||
{
|
{
|
||||||
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
|
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
|
||||||
var requestUri = $"/api/resultados/getTelegramasTotalizados?distritoId={distritoId}&seccionId={seccionId}";
|
var requestUri = $"/api/resultados/getTelegramasTotalizados?distritoId={distritoId}&seccionId={seccionId}";
|
||||||
|
|
||||||
|
// Añadimos el parámetro categoriaId a la URL SÓLO si se proporciona un valor.
|
||||||
|
if (categoriaId.HasValue)
|
||||||
|
{
|
||||||
|
requestUri += $"&categoriaId={categoriaId.Value}";
|
||||||
|
}
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
||||||
request.Headers.Add("Authorization", $"Bearer {authToken}");
|
request.Headers.Add("Authorization", $"Bearer {authToken}");
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
|
// Si la respuesta es 400, devolvemos null para que el worker sepa que falló.
|
||||||
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<string[]>>() : null;
|
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<string[]>>() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public interface IElectoralApiService
|
|||||||
Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId);
|
Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId);
|
||||||
Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string? municipioId, int categoriaId);
|
Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string? municipioId, int categoriaId);
|
||||||
Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId, int categoriaId);
|
Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId, int categoriaId);
|
||||||
Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId);
|
Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null);
|
||||||
Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId);
|
Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId);
|
||||||
Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId);
|
Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId);
|
||||||
Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId);
|
Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId);
|
||||||
|
|||||||
@@ -466,96 +466,83 @@ public class Worker : BackgroundService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// PASO 1: Preparar el DbContext
|
|
||||||
// Creamos un scope para obtener una instancia fresca de DbContext para esta operación.
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
|
|
||||||
// PASO 2: Obtener los Partidos/Municipios (NivelId = 30)
|
// Obtenemos todos los partidos (NivelId=30) y todas las categorías para iterar
|
||||||
// --- CORRECCIÓN CLAVE ---
|
|
||||||
// La API requiere un 'seccionId' para listar telegramas, y el manual lo define como "Id del partido".
|
|
||||||
// La respuesta de getCatalogo nos confirmó que los registros de Partidos/Municipios usan NivelId = 30.
|
|
||||||
var partidos = await dbContext.AmbitosGeograficos
|
var partidos = await dbContext.AmbitosGeograficos
|
||||||
.AsNoTracking() // Optimización: Solo necesitamos leer estos datos.
|
.AsNoTracking()
|
||||||
.Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null)
|
.Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null)
|
||||||
.ToListAsync(stoppingToken);
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
// Si la sincronización inicial no cargó ningún partido, no podemos continuar.
|
var categorias = await dbContext.CategoriasElectorales
|
||||||
if (!partidos.Any())
|
.AsNoTracking()
|
||||||
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
|
if (!partidos.Any() || !categorias.Any())
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No se encontraron Partidos (NivelId 30) en la BD para sondear telegramas.");
|
_logger.LogWarning("No se encontraron partidos o categorías en la BD para sondear telegramas.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Iniciando sondeo de Telegramas nuevos para {count} partidos...", partidos.Count);
|
_logger.LogInformation("Iniciando sondeo de Telegramas nuevos para {partidosCount} partidos y {categoriasCount} categorías...", partidos.Count, categorias.Count);
|
||||||
|
|
||||||
// PASO 3: Iterar sobre cada Partido para buscar telegramas.
|
|
||||||
foreach (var partido in partidos)
|
foreach (var partido in partidos)
|
||||||
{
|
{
|
||||||
if (stoppingToken.IsCancellationRequested) break; // Salida limpia si la aplicación se detiene.
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
|
||||||
// 3.1 - OBTENER LA LISTA COMPLETA DE IDs DE LA API PARA EL PARTIDO ACTUAL
|
foreach (var categoria in categorias)
|
||||||
var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(authToken, partido.DistritoId!, partido.SeccionId!);
|
{
|
||||||
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
|
||||||
// Solo procesamos si la API devolvió una lista con al menos un telegrama.
|
// Llamamos al servicio pasando la categoriaId.
|
||||||
|
// Si mañana la API ya no lo necesita, simplemente lo ignorará.
|
||||||
|
// Si lo vuelve a necesitar en el futuro, nuestro código ya está preparado.
|
||||||
|
var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(authToken, partido.DistritoId!, partido.SeccionId!, categoria.Id);
|
||||||
|
|
||||||
|
// El resto de la lógica es la misma: si la respuesta es válida y contiene datos...
|
||||||
if (listaTelegramasApi is { Count: > 0 })
|
if (listaTelegramasApi is { Count: > 0 })
|
||||||
{
|
{
|
||||||
// La API devuelve una lista de arrays de string (ej. [ ["id1"], ["id2"] ]).
|
|
||||||
// Usamos .Select(t => t[0]) para extraer solo el ID de cada sub-array.
|
|
||||||
var idsDeApi = listaTelegramasApi.Select(t => t[0]).Distinct().ToList();
|
var idsDeApi = listaTelegramasApi.Select(t => t[0]).Distinct().ToList();
|
||||||
|
|
||||||
// 3.2 - COMPARAR CON LA BASE DE DATOS LOCAL PARA ENCONTRAR LOS NUEVOS
|
|
||||||
// Esta es una consulta muy eficiente: le pedimos a la BD que nos devuelva solo los IDs que
|
|
||||||
// ya existen de la lista que nos acaba de dar la API.
|
|
||||||
var idsYaEnDb = await dbContext.Telegramas
|
var idsYaEnDb = await dbContext.Telegramas
|
||||||
.Where(t => idsDeApi.Contains(t.Id))
|
.Where(t => idsDeApi.Contains(t.Id))
|
||||||
.Select(t => t.Id)
|
.Select(t => t.Id)
|
||||||
.ToListAsync(stoppingToken);
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
// Comparamos las dos listas para obtener una tercera: la de los IDs que están en la API pero no en nuestra BD.
|
|
||||||
var nuevosTelegramasIds = idsDeApi.Except(idsYaEnDb).ToList();
|
var nuevosTelegramasIds = idsDeApi.Except(idsYaEnDb).ToList();
|
||||||
|
|
||||||
// Si no hay telegramas nuevos para este partido, simplemente continuamos con el siguiente.
|
|
||||||
if (!nuevosTelegramasIds.Any())
|
if (!nuevosTelegramasIds.Any())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Se encontraron {count} telegramas nuevos en el partido '{nombrePartido}' ({seccionId}). Descargando...", nuevosTelegramasIds.Count, partido.Nombre, partido.SeccionId);
|
_logger.LogInformation("Se encontraron {count} telegramas nuevos en el partido '{nombre}' para la categoría '{cat}'. Descargando...", nuevosTelegramasIds.Count, partido.Nombre, categoria.Nombre);
|
||||||
|
|
||||||
// 3.3 - DESCARGAR Y GUARDAR CADA NUEVO TELEGRAMA
|
|
||||||
foreach (var mesaId in nuevosTelegramasIds)
|
foreach (var mesaId in nuevosTelegramasIds)
|
||||||
{
|
{
|
||||||
if (stoppingToken.IsCancellationRequested) break;
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
|
||||||
// Descargamos el contenido completo del telegrama (imagen base64 y metadatos).
|
|
||||||
var telegramaFile = await _apiService.GetTelegramaFileAsync(authToken, mesaId);
|
var telegramaFile = await _apiService.GetTelegramaFileAsync(authToken, mesaId);
|
||||||
if (telegramaFile != null)
|
if (telegramaFile != null)
|
||||||
{
|
{
|
||||||
var nuevoTelegrama = new Telegrama
|
var nuevoTelegrama = new Telegrama
|
||||||
{
|
{
|
||||||
Id = telegramaFile.NombreArchivo,
|
Id = telegramaFile.NombreArchivo,
|
||||||
AmbitoGeograficoId = partido.Id, // Lo asociamos al ID del partido.
|
AmbitoGeograficoId = partido.Id,
|
||||||
ContenidoBase64 = telegramaFile.Imagen,
|
ContenidoBase64 = telegramaFile.Imagen,
|
||||||
FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(),
|
FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(),
|
||||||
FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime()
|
FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime()
|
||||||
};
|
};
|
||||||
// Añadimos la nueva entidad a la sesión de EF Core para su inserción.
|
|
||||||
await dbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken);
|
await dbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guardamos los cambios en la base de datos después de procesar todos los telegramas nuevos de UN partido.
|
|
||||||
// Esto es más eficiente que guardar en cada iteración del bucle interno.
|
|
||||||
await dbContext.SaveChangesAsync(stoppingToken);
|
await dbContext.SaveChangesAsync(stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_logger.LogInformation("Sondeo de Telegramas completado.");
|
_logger.LogInformation("Sondeo de Telegramas completado.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Capturamos cualquier error para que no detenga el worker y lo registramos para depuración.
|
|
||||||
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Telegramas.");
|
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Telegramas.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user