diff --git a/.env.example b/.env.example index 98176ad..b8d37bb 100644 --- a/.env.example +++ b/.env.example @@ -75,6 +75,7 @@ AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false +# API REPUVE FEDERAL REPUVE_FED_BASE_URL= REPUVE_FED_USERNAME= REPUVE_FED_PASSWORD= @@ -82,6 +83,14 @@ REPUVE_FED_PASSWORD= # REPUVE ESTATAL REPUVE_EST_URL= +# API REPUVE CONSTANCIAS +REPUVE_CONSTANCIA_BASE_URL= +REPUVE_CONSTANCIA_LOGIN_ENDPOINT= +REPUVE_CONSTANCIA_CONSULTA_ENDPOINT= +REPUVE_CONSTANCIA_EMAIL= +REPUVE_CONSTANCIA_PASSWORD= +REPUVE_CONSTANCIA_TOKEN_TTL=3600 + REVERB_APP_ID= REVERB_APP_KEY= REVERB_APP_SECRET= diff --git a/app/Http/Controllers/Api/VehicleController.php b/app/Http/Controllers/Api/VehicleController.php index ffc1211..b05d891 100644 --- a/app/Http/Controllers/Api/VehicleController.php +++ b/app/Http/Controllers/Api/VehicleController.php @@ -56,7 +56,7 @@ public function consultarVehiculo(Request $request): JsonResponse if (!$vehiculo) { return ApiResponse::NOT_FOUND->response([ 'success' => false, - 'message' => 'No se encontró el vehículo en la base de datos' + 'message' => 'No se encontró el vehículo' ]); } @@ -114,31 +114,6 @@ public function consultarVehiculo(Request $request): JsonResponse ]); } - /** - * Guardar vehículo robado en Redis - */ - private function guardarEnRedis(string $epc, array $datosEstatal, array $datosRobo): void - { - $key = "vehiculo:robado:{$epc}"; - - $datos = [ - 'epc' => $epc, - 'vin' => $datosEstatal['vin'], - 'placa' => $datosEstatal['placa'], - 'fecha_robo' => $datosRobo['fecha_robo'] ?? null, - 'autoridad' => $datosRobo['autoridad'] ?? null, - 'acta' => $datosRobo['acta'] ?? null, - 'denunciante' => $datosRobo['denunciante'] ?? null, - 'fecha_acta' => $datosRobo['fecha_acta'] ?? null, - 'primera_deteccion' => now()->toIso8601String(), - 'ultima_deteccion' => now()->toIso8601String(), - 'detecciones' => 0, - 'origen' => 'CONSULTA_MANUAL' - ]; - - Redis::set($key, json_encode($datos)); - } - /** * Dar de baja (registrar como recuperado) un vehículo por PLACA o VIN */ @@ -400,4 +375,32 @@ public function buscarVehiculoRobado(Request $request) 'vehiculo' => $vehiculoEncontrado['datos'] ]); } + + /** + * Buscar vehículo por tag_id (tag_number) + * GET /api/vehicles/buscar + */ + public function buscarPorTag(Request $request) + { + $validated = $request->validate([ + 'tag_id' => 'required|string', + ]); + + $resultado = $this->vehicleService->consultarVehiculoPorTag( + epc: null, + tagId: $validated['tag_id'] + ); + + if (!$resultado) { + return ApiResponse::NOT_FOUND->response([ + 'success' => false, + 'message' => 'No se encontró el vehículo' + ]); + } + + return ApiResponse::OK->response([ + 'success' => true, + 'vehiculo' => $resultado + ]); + } } diff --git a/app/Services/ConsultaRepuveConstancia.php b/app/Services/ConsultaRepuveConstancia.php new file mode 100644 index 0000000..65868f1 --- /dev/null +++ b/app/Services/ConsultaRepuveConstancia.php @@ -0,0 +1,217 @@ +baseUrl = config('services.vehiculos_externos.base_url'); + $this->loginEndpoint = config('services.vehiculos_externos.login_endpoint'); + $this->vehiculosEndpoint = config('services.vehiculos_externos.vehiculos_endpoint'); + $this->email = config('services.vehiculos_externos.email'); + $this->password = config('services.vehiculos_externos.password'); + $this->tokenTtl = config('services.vehiculos_externos.token_ttl', 3600); + } + + /** + * Consultar vehículo por tag_number + * + */ + public function consultarVehiculoPorTag(?string $tagNumber = null) + { + try { + // solo busca por tag_number + if (empty($tagNumber)) { + Log::warning('ConsultaRepuveConstancia: No se proporcionó tag_number'); + return null; + } + + // Obtener token de autenticación + $token = $this->obtenerToken(); + if (!$token) { + Log::error('ConsultaRepuveConstancia: No se pudo obtener token de autenticación'); + return null; + } + + // Consultar vehículos con filtro tag_number + $url = $this->baseUrl . $this->vehiculosEndpoint; + + Log::info('ConsultaRepuveConstancia: Consultando', [ + 'url' => $url, + 'tag_number' => $tagNumber + ]); + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $token, + 'Accept' => 'application/json', + ])->timeout(30)->get($url, [ + 'tag_number' => $tagNumber + ]); + + if (!$response->successful()) { + Log::error('ConsultaRepuveConstancia: Error en petición HTTP', [ + 'status' => $response->status(), + 'body' => $response->body() + ]); + return null; + } + + $data = $response->json(); + + // Validar estructura de respuesta + if (!isset($data['status']) || $data['status'] !== 'success' || !isset($data['data']['records']['data'])) { + Log::warning('ConsultaRepuveConstancia: Respuesta con estructura inválida', [ + 'response' => $data + ]); + return null; + } + + // recibir solo el vehículo buscado (o vacío) + $vehiculos = $data['data']['records']['data']; + + if (empty($vehiculos)) { + Log::info('ConsultaRepuveConstancia: Vehículo no encontrado', [ + 'tag_number' => $tagNumber + ]); + return null; + } + + $vehiculoEncontrado = $vehiculos[0]; + + // Transformar datos al formato esperado + return $this->transformarDatosVehiculo($vehiculoEncontrado); + + } catch (Exception $e) { + Log::error('ConsultaRepuveConstancia: Error al consultar vehículo', [ + 'tag_number' => $tagNumber, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + return null; + } + } + + /** + * Obtener token JWT + */ + private function obtenerToken() + { + $cacheKey = 'vehiculos_externos_jwt_token'; + + // Intentar obtener token de caché + $token = Cache::get($cacheKey); + if ($token) { + Log::debug('ConsultaRepuveConstancia: Token obtenido de caché'); + return $token; + } + + // Si no está en caché, autenticar + try { + $url = $this->baseUrl . $this->loginEndpoint; + + Log::info('ConsultaRepuveConstancia: Autenticando', [ + 'url' => $url, + 'email' => $this->email + ]); + + $response = Http::timeout(30)->post($url, [ + 'email' => $this->email, + 'password' => $this->password, + ]); + + if (!$response->successful()) { + Log::error('ConsultaRepuveConstancia: Error al autenticar', [ + 'status' => $response->status(), + 'body' => $response->body() + ]); + return null; + } + + $data = $response->json(); + + // Validar estructura de respuesta + if (!isset($data['status']) || $data['status'] !== 'success' || !isset($data['data']['token'])) { + Log::error('ConsultaRepuveConstancia: Respuesta de login inválida', [ + 'response' => $data + ]); + return null; + } + + $token = $data['data']['token']; + + // Guardar en caché (hora por defecto) + Cache::put($cacheKey, $token, $this->tokenTtl); + + Log::info('ConsultaRepuveConstancia: Token obtenido y guardado en caché'); + + return $token; + + } catch (Exception $e) { + Log::error('ConsultaRepuveConstancia: Excepción al obtener token', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + return null; + } + } + + + /** + * Transformar datos del vehículo al formato esperado + */ + private function transformarDatosVehiculo(array $record) + { + $vehiculo = $record['vehicle']; + $tag = $vehiculo['tag'] ?? []; + $owner = $vehiculo['owner'] ?? []; + + return [ + 'tag_number' => $tag['tag_number'] ?? null, + 'folio_tag' => $tag['folio'] ?? null, + 'vin' => $vehiculo['niv'] ?? null, + 'placa' => $vehiculo['placa'] ?? null, + 'marca' => $vehiculo['marca'] ?? null, + 'modelo' => $vehiculo['modelo'] ?? null, + 'linea' => $vehiculo['linea'] ?? null, + 'sublinea' => $vehiculo['sublinea'] ?? null, + 'color' => $vehiculo['color'] ?? null, + 'numero_motor' => $vehiculo['numero_motor'] ?? null, + 'clase_veh' => $vehiculo['clase_veh'] ?? null, + 'tipo_servicio' => $vehiculo['tipo_servicio'] ?? null, + 'rfv' => $vehiculo['rfv'] ?? null, + 'nrpv' => $vehiculo['nrpv'] ?? null, + 'reporte_robo' => $vehiculo['reporte_robo'] ?? false, + 'propietario' => [ + 'id' => $owner['id'] ?? null, + 'nombre_completo' => $owner['full_name'] ?? null, + 'rfc' => $owner['rfc'] ?? null, + 'curp' => $owner['curp'] ?? null, + 'telefono' => $owner['telefono'] ?? null, + 'direccion' => $owner['address'] ?? null, + ], + 'tag_status' => $tag['status']['name'] ?? null, + ]; + } + + /** + * Limpiar token de caché (útil para forzar re-autenticación) + */ + public function limpiarToken() + { + Cache::forget('vehiculos_externos_jwt_token'); + Log::info('ConsultaRepuveConstancia: Token eliminado de caché'); + } +} diff --git a/app/Services/ReporteRoboService.php b/app/Services/ReporteRoboService.php index c27dd3a..c489ff0 100644 --- a/app/Services/ReporteRoboService.php +++ b/app/Services/ReporteRoboService.php @@ -20,7 +20,7 @@ public function __construct() $this->password = config('services.repuve_federal.password'); } - public function consultarPorVin(?string $vin = null, ?string $placa = null): array + public function consultarPorVin(?string $vin = null, ?string $placa = null) { try { if (empty($vin) && empty($placa)) { @@ -115,13 +115,13 @@ public function consultarPorVin(?string $vin = null, ?string $placa = null): arr /** * Parsea la respuesta del servicio de robo */ - private function parseRoboResponse(string $soapResponse, string $valor): array + private function parseRoboResponse(string $soapResponse, string $valor) { - // Extraer contenido del tag + // Extraer contenido preg_match('/(.*?)<\/return>/s', $soapResponse, $matches); if (!isset($matches[1])) { - Log::error('ReporteRoboService: No se encontró tag '); + Log::error('ReporteRoboService:'); return [ 'tiene_reporte' => false, 'datos' => [], @@ -177,7 +177,7 @@ private function parseRoboResponse(string $soapResponse, string $valor): array ]; } - Log::error('ReporteRoboService: Formato no reconocido', ['contenido' => $contenido]); + Log::error('ReporteRoboService:', ['contenido' => $contenido]); return [ 'tiene_reporte' => false, 'datos' => [], diff --git a/app/Services/VehicleService.php b/app/Services/VehicleService.php index 673ae3e..90b6afa 100644 --- a/app/Services/VehicleService.php +++ b/app/Services/VehicleService.php @@ -11,7 +11,8 @@ class VehicleService { public function __construct( private ConsultaEstatalService $consultaEstatal, - private ReporteRoboService $reporteRobo + private ReporteRoboService $reporteRobo, + private ConsultaRepuveConstancia $consultaRepuveCons ) {} public function procesarDeteccion(string $epc, ?int $arcoId = null): array @@ -124,7 +125,7 @@ private function verificarVehiculoRobado(string $epc, ?int $arcoId, array $datos ]; } - private function guardarEnRedis(string $epc, array $datosEstatal, array $datosRobo): void + private function guardarEnRedis(string $epc, array $datosEstatal, array $datosRobo) { $key = "vehiculo:robado:{$epc}"; @@ -145,7 +146,7 @@ private function guardarEnRedis(string $epc, array $datosEstatal, array $datosRo Redis::set($key, json_encode($datos)); } - private function actualizarDeteccionRedis(string $epc, array $datosActuales): void + private function actualizarDeteccionRedis(string $epc, array $datosActuales) { $key = "vehiculo:robado:{$epc}"; @@ -155,7 +156,7 @@ private function actualizarDeteccionRedis(string $epc, array $datosActuales): vo Redis::set($key, json_encode($datosActuales)); } - private function registrarRecuperacion(string $epc, ?int $arcoId, array $datosRedis): void + private function registrarRecuperacion(string $epc, ?int $arcoId, array $datosRedis) { // Guardar en MySQL Vehicle::create([ @@ -190,7 +191,7 @@ public function listarVehiculosRobados(): array return $vehiculos; } - private function registrarDeteccion(string $epc, array $resultado): void + private function registrarDeteccion(string $epc, array $resultado) { if (!$resultado['success'] || !isset($resultado['vehiculo'])) { return; @@ -208,4 +209,41 @@ private function registrarDeteccion(string $epc, array $resultado): void 'fecha_deteccion' => now() ]); } + + /** + * Consultar vehículo por tag_id + */ + public function consultarVehiculoPorTag(?string $epc = null, ?string $tagId = null) + { + try { + // Validar que se proporcionó tag_id + if (empty($tagId)) { + Log::warning('VehicleService: No se proporcionó tag_id'); + return null; + } + $vehiculoExterno = $this->consultaRepuveCons->consultarVehiculoPorTag($tagId); + + if ($vehiculoExterno) { + Log::info('VehicleService: Vehículo encontrado', [ + 'tag_id' => $vehiculoExterno['tag_id'] ?? null, + 'tag_number' => $vehiculoExterno['tag_number'] ?? null, + 'vin' => $vehiculoExterno['vin'] ?? null + ]); + } else { + Log::info('VehicleService: Vehículo no encontrado', [ + 'tag_id' => $tagId + ]); + } + + return $vehiculoExterno; + + } catch (\Exception $e) { + Log::error('VehicleService: Error en consulta', [ + 'tag_id' => $tagId, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + return null; + } + } } diff --git a/config/services.php b/config/services.php index e3339e3..21a55f4 100644 --- a/config/services.php +++ b/config/services.php @@ -47,4 +47,13 @@ 'url' => env('REPUVE_EST_URL'), ], + 'vehiculos_externos' => [ + 'base_url' => env('REPUVE_CONSTANCIA_BASE_URL'), + 'login_endpoint' => env('REPUVE_CONSTANCIA_LOGIN_ENDPOINT'), + 'vehiculos_endpoint' => env('REPUVE_CONSTANCIA_CONSULTA_ENDPOINT'), + 'email' => env('REPUVE_CONSTANCIA_EMAIL'), + 'password' => env('REPUVE_CONSTANCIA_PASSWORD'), + 'token_ttl' => env('REPUVE_CONSTANCIA_TOKEN_TTL'), + ], + ]; diff --git a/routes/api.php b/routes/api.php index d0bf5ce..6f67a39 100644 --- a/routes/api.php +++ b/routes/api.php @@ -27,7 +27,7 @@ Route::get('/vehicles', [VehicleController::class, 'listarRecuperados']); Route::get('/vehicles/detecciones', [VehicleController::class, 'listarDetecciones']); Route::get('/vehicles/robado', [VehicleController::class, 'buscarVehiculoRobado']); - + Route::get('/vehicles/buscar', [VehicleController::class, 'buscarPorTag']); }); /** Rutas públicas */