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 detecciones del día desde MySQL */ public function listarDeteccionesDelDia(?string $fecha = null, ?int $arcoId = null, int $segundos = 10): array { $fecha = $fecha ?? now()->format('Y-m-d'); // Calcular timestamp hace N segundos $tiempoLimite = now()->subSeconds($segundos); $query = DailyDetection::delDia($fecha) ->where('fecha_deteccion', '>=', $tiempoLimite); // Filtrar por arco si se especifica if ($arcoId !== null) { $query->porArco($arcoId); } $detecciones = $query ->orderBy('fecha_deteccion', 'desc') ->get() ->map(function ($deteccion) { return [ 'id' => $deteccion->id, 'fast_id' => $deteccion->fast_id, 'vin' => $deteccion->vin, 'placa' => $deteccion->placa, 'marca' => $deteccion->marca, 'modelo' => $deteccion->modelo, 'color' => $deteccion->color, 'arco_id' => $deteccion->arco_id, 'arco_nombre' => $deteccion->arco_nombre, 'antena' => $deteccion->antena, 'estado' => $deteccion->estado, 'tiene_reporte_robo' => $deteccion->tiene_reporte_robo, 'fecha_deteccion' => $deteccion->fecha_deteccion->toIso8601String(), ]; }) ->toArray(); return $detecciones; } /** * Listar detecciones del día de un arco específico */ public function listarDeteccionesDelDiaPorArco(int $arcoId, ?string $fecha = null): array { return $this->listarDeteccionesDelDia($fecha, $arcoId); } /** * 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 ]; } /** * 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 MySQL para el día actual */ private function registrarDeteccionDelDia(string $fastId, array $vehiculo, ?int $arcoId, array $resultado, ?string $antena = null) { // Obtener nombre del arco $arcoNombre = $arcoId ? Arco::find($arcoId)?->nombre : null; // Crear la nueva detección $nuevaDeteccion = DailyDetection::create([ '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(), ]); Log::info('Detección del día registrada en MySQL', [ 'id' => $nuevaDeteccion->id, 'fast_id' => $fastId, 'arco_id' => $arcoId, 'estado' => $nuevaDeteccion->estado ]); } /** * 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 ]); } }