verificarVehiculoRobado($fastId, json_decode($enRedis, true), $arcoId, $antena); $this->registrarDeteccion($fastId, $resultado, $arcoId, $antena); return $resultado; } // No está en Redis, consultar servicios $resultado = $this->consultarNuevoVehiculo($fastId); $this->registrarDeteccion($fastId, $resultado, $arcoId, $antena); return $resultado; } /** * Consultar vehículo por tag_id, placa o VIN */ public function consultarVehiculoPorTag(string $fastId) { try { //Intentar con REPUVE Constancia primero $vehiculoExterno = $this->consultaRepuveCons->consultarVehiculoPorTag($fastId); if ($vehiculoExterno) { Log::info('VehicleService: Vehículo encontrado en REPUVE Constancia', [ 'fast_id' => $fastId, 'tag_number' => $vehiculoExterno['tag_number'] ?? null, 'placa' => $vehiculoExterno['placa'] ?? null, 'vin' => $vehiculoExterno['vin'] ?? null ]); return $vehiculoExterno; } // Si no se encuentra, intentar con REPUVE Nacional Log::info('VehicleService: Intentando consulta en REPUVE Nacional', [ 'fast_id' => $fastId ]); $resultadoNacional = $this->reporteRoboService->consultarVehiculo($fastId); if ($resultadoNacional['success'] && !empty($resultadoNacional['datos'])) { Log::info('VehicleService: Vehículo encontrado en REPUVE Nacional', [ 'fast_id' => $fastId, 'datos' => $resultadoNacional['datos'] ]); // Adaptar formato de REPUVE Nacional al formato esperado $datos = $resultadoNacional['datos']; return [ 'tag_number' => $fastId, 'vin' => $datos['vin'] ?? null, 'placa' => $datos['placa'] ?? null, 'marca' => $datos['marca'] ?? null, 'modelo' => $datos['modelo'] ?? null, 'color' => $datos['color'] ?? null, 'origen' => 'REPUVE_NACIONAL' ]; } Log::info('VehicleService: Vehículo no encontrado en ningún servicio', [ 'fast_id' => $fastId ]); return null; } catch (\Exception $e) { Log::error('VehicleService: Error en consulta', [ 'fast_id' => $fastId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return null; } } /** * Listar todos los vehículos robados activos en Redis */ public function listarVehiculosRobados(): array { $keys = Redis::keys('vehiculo:robado:*'); $vehiculos = []; foreach ($keys as $key) { $datos = Redis::get($key); if ($datos) { $vehiculos[] = json_decode($datos, true); } } return $vehiculos; } /** * Listar todas las detecciones del día desde Redis */ public function listarDeteccionesDelDia(?string $fecha = null): array { $fecha = $fecha ?? now()->format('Y-m-d'); $pattern = "deteccion:dia:{$fecha}:*"; $keys = Redis::keys($pattern); $detecciones = []; foreach ($keys as $key) { $datos = Redis::get($key); if ($datos) { $detecciones[] = json_decode($datos, true); } } // Ordenar por fecha de detección (más reciente primero) usort($detecciones, function ($a, $b) { return strcmp($b['fecha_deteccion'], $a['fecha_deteccion']); }); return $detecciones; } /** * Obtener estadísticas de detecciones del día */ public function obtenerEstadisticasDelDia(?string $fecha = null): array { $detecciones = $this->listarDeteccionesDelDia($fecha); $total = count($detecciones); $robados = collect($detecciones)->where('tiene_reporte_robo', true)->count(); $libres = collect($detecciones)->where('tiene_reporte_robo', false)->count(); return [ 'fecha' => $fecha ?? now()->format('Y-m-d'), 'total_detecciones' => $total, 'vehiculos_robados' => $robados, 'vehiculos_libres' => $libres, 'detecciones' => $detecciones ]; } /** * Listar detecciones del día de un arco específico */ public function listarDeteccionesDelDiaPorArco(int $arcoId, ?string $fecha = null): array { $todasDetecciones = $this->listarDeteccionesDelDia($fecha); // Filtrar solo las detecciones de este arco $deteccionesArco = collect($todasDetecciones) ->where('arco_id', $arcoId) ->values() ->all(); return $deteccionesArco; } /** * Consultar nuevo vehículo (no está en Redis de robados) */ private function consultarNuevoVehiculo(string $fastId): array { $datosVehiculo = $this->consultaRepuveCons->consultarVehiculoPorTag($fastId); if ($datosVehiculo && isset($datosVehiculo['vin'])) { Log::info('Vehículo encontrado en RepuveConstancia - LIBRE', [ 'fast_id' => $fastId, 'vin' => $datosVehiculo['vin'], 'placa' => $datosVehiculo['placa'] ?? null, 'fuente' => 'RepuveConstancia' ]); return [ 'success' => true, 'tiene_reporte_robo' => false, 'estado' => 'LIBRE', 'vehiculo' => [ 'vin' => $datosVehiculo['vin'] ?? null, 'placa' => $datosVehiculo['placa'] ?? null, 'marca' => $datosVehiculo['marca'] ?? null, 'modelo' => $datosVehiculo['modelo'] ?? null, 'color' => $datosVehiculo['color'] ?? null, ], 'fuente' => 'RepuveConstancia' ]; } $resultadoNacional = $this->reporteRoboService->consultarVehiculo($fastId, null); if ($resultadoNacional['success'] && !empty($resultadoNacional['datos'])) { $datos = $resultadoNacional['datos']; Log::info('Vehículo encontrado en RepuveNacional - LIBRE', [ 'fast_id' => $fastId, 'vin' => $datos['vin'] ?? null, 'placa' => $datos['placa'] ?? null, 'fuente' => 'RepuveNacional' ]); return [ 'success' => true, 'tiene_reporte_robo' => false, 'estado' => 'LIBRE', 'vehiculo' => [ 'vin' => $datos['vin'] ?? null, 'placa' => $datos['placa'] ?? null, 'marca' => $datos['marca'] ?? null, 'modelo' => $datos['modelo'] ?? null, 'color' => $datos['color'] ?? null, ], 'fuente' => 'RepuveNacional' ]; } return [ 'success' => false, 'message' => 'No se encontró información del vehículo en ningún servicio', 'fuente' => 'ninguno' ]; } /** * Verificar vehículo robado (si ya está en Redis) */ private function verificarVehiculoRobado(string $fastId, array $datosRedis, ?int $arcoId = null, ?string $antena = null): array { // Actualizar contador de detecciones $this->actualizarDeteccionRedis($fastId, $datosRedis); Log::warning('¡VEHÍCULO ROBADO DETECTADO!', [ 'fast_id' => $fastId, 'vin' => $datosRedis['vin'] ?? null, 'placa' => $datosRedis['placa'] ?? null ]); // Crear alerta en la base de datos $this->crearAlertaRobo($fastId, $datosRedis, $arcoId, $antena); return [ 'success' => true, 'tiene_reporte_robo' => true, 'estado' => 'ROBADO', 'accion' => 'ACTUALIZADO_EN_REDIS', 'vehiculo' => $datosRedis ]; } /** * Actualizar contador de detecciones en Redis */ private function actualizarDeteccionRedis(string $fastId, array $datosActuales) { $key = "vehiculo:robado:{$fastId}"; $datosActuales['ultima_deteccion'] = now()->toIso8601String(); $datosActuales['detecciones'] = ($datosActuales['detecciones'] ?? 0) + 1; Redis::set($key, json_encode($datosActuales)); } /** * Registrar detección en MySQL y Redis (detecciones del día) */ private function registrarDeteccion(string $fastId, array $resultado, ?int $arcoId = null, ?string $antena = null) { // Datos mínimos que siempre tenemos $datosMinimos = [ 'arco_id' => $arcoId, 'antena' => $antena, 'fast_id' => $fastId, 'fecha_deteccion' => now() ]; // Si encontramos el vehículo, agregamos datos completos if ($resultado['success'] && isset($resultado['vehiculo'])) { $vehiculo = $resultado['vehiculo']; Detection::create(array_merge($datosMinimos, [ 'vin' => $vehiculo['vin'] ?? null, 'placa' => $vehiculo['placa'] ?? null, 'marca' => $vehiculo['marca'] ?? null, 'modelo' => $vehiculo['modelo'] ?? null, 'color' => $vehiculo['color'] ?? null, ])); // También registrar en Redis (detecciones del día) $this->registrarDeteccionDelDia($fastId, $vehiculo, $arcoId, $resultado, $antena); } else { // No encontrado: guardar solo datos mínimos Detection::create(array_merge($datosMinimos, [ 'vin' => null, 'placa' => null, 'marca' => null, 'modelo' => null, 'color' => null, ])); // Registrar en Redis con datos incompletos $this->registrarDeteccionDelDia($fastId, [], $arcoId, [ 'estado' => 'DESCONOCIDO', 'tiene_reporte_robo' => false ], $antena); } } /** * Registrar detección en Redis para el día actual * Formato de key: deteccion:dia:YYYY-MM-DD:{fastId}:{timestamp} * Expira automáticamente a las 23:59:59 del día */ private function registrarDeteccionDelDia(string $fastId, array $vehiculo, ?int $arcoId, array $resultado, ?string $antena = null) { $fecha = now()->format('Y-m-d'); $timestamp = now()->timestamp; $key = "deteccion:dia:{$fecha}:{$fastId}:{$timestamp}"; // Obtener nombre del arco $arcoNombre = $arcoId ? Arco::find($arcoId)?->nombre : null; $datosDeteccion = [ 'fast_id' => $fastId, 'vin' => $vehiculo['vin'] ?? null, 'placa' => $vehiculo['placa'] ?? null, 'marca' => $vehiculo['marca'] ?? null, 'modelo' => $vehiculo['modelo'] ?? null, 'color' => $vehiculo['color'] ?? null, 'arco_id' => $arcoId, 'arco_nombre' => $arcoNombre, 'antena' => $antena, 'estado' => $resultado['estado'] ?? 'LIBRE', 'tiene_reporte_robo' => $resultado['tiene_reporte_robo'] ?? false, 'fecha_deteccion' => now()->toIso8601String(), ]; // Guardar en Redis Redis::set($key, json_encode($datosDeteccion)); // Calcular segundos hasta el final del día (23:59:59) $finDelDia = now()->endOfDay(); $segundosHastaFinDelDia = $finDelDia->timestamp - now()->timestamp; // Establecer expiración automática al final del día Redis::expire($key, $segundosHastaFinDelDia); } /** * Crear alerta de vehículo robado y disparar evento */ private function crearAlertaRobo(string $fastId, array $datosVehiculo, ?int $arcoId, ?string $antena): void { // Obtener nombre del arco $arcoNombre = $arcoId ? Arco::find($arcoId)?->nombre : 'Desconocido'; // Crear alerta en la base de datos $alerta = AlertaRobo::create([ 'fast_id' => $fastId, 'vin' => $datosVehiculo['vin'] ?? null, 'placa' => $datosVehiculo['placa'] ?? null, 'marca' => $datosVehiculo['marca'] ?? null, 'modelo' => $datosVehiculo['modelo'] ?? null, 'color' => $datosVehiculo['color'] ?? null, 'arco_id' => $arcoId, 'arco_nombre' => $arcoNombre, 'antena' => $antena, 'fecha_deteccion' => now(), 'visto' => false, ]); // Disparar evento para Laravel Reverb (WebSocket) event(new VehiculoRobadoDetectado($alerta)); Log::info('Alerta de vehículo robado creada y evento disparado', [ 'alerta_id' => $alerta->id, 'fast_id' => $fastId, 'placa' => $alerta->placa, 'arco' => $arcoNombre ]); } }