From f648ea39eb13c411b649aa4ac678254ac54eb138 Mon Sep 17 00:00:00 2001 From: Juan Felipe Zapata Moreno Date: Fri, 19 Dec 2025 15:06:03 -0600 Subject: [PATCH] =?UTF-8?q?ADD:=20Implementa=20servicios=20para=20consulta?= =?UTF-8?q?=20y=20registro=20de=20veh=C3=ADculos=20robados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Api/VehicleController.php | 308 ++++++++++++++++++ app/Models/Vehicle.php | 31 ++ app/Services/ConsultaEstatalService.php | 133 ++++++++ app/Services/ReporteRoboService.php | 187 +++++++++++ app/Services/VehicleService.php | 187 +++++++++++ config/services.php | 12 + ...025_12_19_122433_create_vehicles_table.php | 46 +++ docker-compose.yml | 28 ++ routes/api.php | 11 +- 9 files changed, 940 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/Api/VehicleController.php create mode 100644 app/Models/Vehicle.php create mode 100644 app/Services/ConsultaEstatalService.php create mode 100644 app/Services/ReporteRoboService.php create mode 100644 app/Services/VehicleService.php create mode 100644 database/migrations/2025_12_19_122433_create_vehicles_table.php diff --git a/app/Http/Controllers/Api/VehicleController.php b/app/Http/Controllers/Api/VehicleController.php new file mode 100644 index 0000000..74a1472 --- /dev/null +++ b/app/Http/Controllers/Api/VehicleController.php @@ -0,0 +1,308 @@ +all(), [ + 'placa' => 'required_without:vin|string|nullable', + 'vin' => 'required_without:placa|string|nullable', + ], [ + 'placa.required_without' => 'Debe proporcionar al menos placa o VIN', + 'vin.required_without' => 'Debe proporcionar al menos placa o VIN', + ]); + + if ($validator->fails()) { + return response()->json([ + 'success' => false, + 'message' => 'Datos inválidos', + 'errors' => $validator->errors() + ], 422); + } + + try { + $placa = $request->placa; + $vin = $request->vin; + + // Consultar padrón estatal (obtener datos completos del vehículo) + Log::info('Consultando padrón estatal', ['placa' => $placa, 'vin' => $vin]); + + // Si viene VIN, usar VIN, si no usar placa + $criterio = $vin ?: $placa; + $datosEstatal = $this->consultaEstatal->consultarPorEpc($criterio); + + if (!$datosEstatal || (!$datosEstatal['vin'] && !$datosEstatal['placa'])) { + return response()->json([ + 'success' => false, + 'message' => 'No se encontró información del vehículo en el padrón estatal' + ], 404); + } + + // Obtener VIN y Placa completos + $vinCompleto = $datosEstatal['vin'] ?: $vin; + $placaCompleta = $datosEstatal['placa'] ?: $placa; + + Log::info('Datos del padrón estatal obtenidos', [ + 'vin' => $vinCompleto, + 'placa' => $placaCompleta + ]); + + // Consultar REPUVE + $reporteRobo = $this->reporteRobo->consultarPorVin($vinCompleto, $placaCompleta); + + $tieneReporte = $reporteRobo['tiene_reporte'] ?? false; + + // Si tiene reporte de robo → Guardar en Redis + if ($tieneReporte) { + $epc = 'MANUAL_' . ($vinCompleto ?: $placaCompleta); + + $this->guardarEnRedis($epc, $datosEstatal, $reporteRobo['datos']); + + Log::warning('¡VEHÍCULO CON REPORTE DE ROBO REGISTRADO!', [ + 'vin' => $vinCompleto, + 'placa' => $placaCompleta, + 'autoridad' => $reporteRobo['datos']['autoridad'] ?? null + ]); + + return response()->json([ + 'success' => true, + 'tiene_reporte_robo' => true, + 'message' => '¡ALERTA! El vehículo tiene reporte de robo', + 'vehiculo' => [ + 'vin' => $vinCompleto, + 'placa' => $placaCompleta, + ], + 'reporte_robo' => $reporteRobo['datos'] + ]); + } + + // No tiene reporte de robo + Log::info('Vehículo consultado sin reporte de robo', [ + 'vin' => $vinCompleto, + 'placa' => $placaCompleta + ]); + + return response()->json([ + 'success' => true, + 'tiene_reporte_robo' => false, + 'message' => 'Vehículo sin reporte de robo', + 'vehiculo' => [ + 'vin' => $vinCompleto, + 'placa' => $placaCompleta, + ] + ]); + + } catch (\Exception $e) { + Log::error('Error al consultar vehículo', [ + 'placa' => $request->placa, + 'vin' => $request->vin, + 'error' => $e->getMessage() + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Error al consultar vehículo', + 'error' => $e->getMessage() + ], 500); + } + } + + /** + * 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 + */ + public function recuperarVehiculo(Request $request): JsonResponse + { + $validator = Validator::make($request->all(), [ + 'placa' => 'required_without:vin|string|nullable', + 'vin' => 'required_without:placa|string|nullable', + ], [ + 'placa.required_without' => 'Debe proporcionar al menos placa o VIN', + 'vin.required_without' => 'Debe proporcionar al menos placa o VIN', + ]); + + if ($validator->fails()) { + return response()->json([ + 'success' => false, + 'message' => 'Datos inválidos', + 'errors' => $validator->errors() + ], 422); + } + + try { + $placa = $request->placa; + $vin = $request->vin; + + Log::info('Buscando vehículo robado para recuperar', ['placa' => $placa, 'vin' => $vin]); + + // 1. Buscar en Redis (vehículos robados activos) + $vehiculoEncontrado = $this->buscarEnRedis($vin, $placa); + + if (!$vehiculoEncontrado) { + return response()->json([ + 'success' => false, + 'message' => 'El vehículo no se encuentra en la lista de robados activos' + ], 404); + } + + $datosRedis = $vehiculoEncontrado['datos']; + $epc = $vehiculoEncontrado['epc']; + + Log::info('Vehículo encontrado en Redis', [ + 'epc' => $epc, + 'vin' => $datosRedis['vin'], + 'placa' => $datosRedis['placa'] + ]); + + // 2. Guardar en MySQL como recuperado + $vehiculoRecuperado = Vehicle::create([ + 'epc' => $epc, + 'vin' => $datosRedis['vin'], + 'placa' => $datosRedis['placa'], + 'fecha_robo' => $datosRedis['fecha_robo'], + 'autoridad_robo' => $datosRedis['autoridad'], + 'acta_robo' => $datosRedis['acta'], + 'denunciante' => $datosRedis['denunciante'], + 'fecha_acta' => $datosRedis['fecha_acta'] ?? null, + 'fecha_recuperacion' => now(), + 'datos_robo_original' => $datosRedis + ]); + + // 3. Eliminar de Redis + Redis::del("vehiculo:robado:{$epc}"); + + Log::info('Vehículo registrado como recuperado', [ + 'id' => $vehiculoRecuperado->id, + 'vin' => $vehiculoRecuperado->vin, + 'placa' => $vehiculoRecuperado->placa + ]); + + return response()->json([ + 'success' => true, + 'message' => 'Vehículo registrado como recuperado exitosamente', + 'vehiculo' => [ + 'id' => $vehiculoRecuperado->id, + 'epc' => $vehiculoRecuperado->epc, + 'vin' => $vehiculoRecuperado->vin, + 'placa' => $vehiculoRecuperado->placa, + 'fecha_robo' => $vehiculoRecuperado->fecha_robo, + 'fecha_recuperacion' => $vehiculoRecuperado->fecha_recuperacion, + 'autoridad_robo' => $vehiculoRecuperado->autoridad_robo, + 'acta_robo' => $vehiculoRecuperado->acta_robo, + ] + ]); + + } catch (\Exception $e) { + Log::error('Error al recuperar vehículo', [ + 'placa' => $request->placa, + 'vin' => $request->vin, + 'error' => $e->getMessage() + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Error al registrar vehículo como recuperado', + 'error' => $e->getMessage() + ], 500); + } + } + + /** + * Buscar vehículo en Redis por VIN o Placa + */ + private function buscarEnRedis(?string $vin, ?string $placa): ?array + { + $keys = Redis::keys('vehiculo:robado:*'); + + foreach ($keys as $key) { + $datos = Redis::get($key); + if (!$datos) { + continue; + } + + $vehiculo = json_decode($datos, true); + + // Buscar por VIN o Placa + if (($vin && $vehiculo['vin'] === $vin) || ($placa && $vehiculo['placa'] === $placa)) { + // Extraer el EPC del key + $epc = str_replace('vehiculo:robado:', '', $key); + + return [ + 'epc' => $epc, + 'datos' => $vehiculo + ]; + } + } + + return null; + } + + /** + * Listar todos los vehículos robados activos + * GET /api/vehicles/robados + */ + public function listarRobados(): JsonResponse + { + try { + $vehiculos = $this->vehicleService->listarVehiculosRobados(); + + return response()->json([ + 'success' => true, + 'total' => count($vehiculos), + 'vehiculos' => $vehiculos + ]); + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Error al listar vehículos robados', + 'error' => $e->getMessage() + ], 500); + } + } + +} diff --git a/app/Models/Vehicle.php b/app/Models/Vehicle.php new file mode 100644 index 0000000..e3627ee --- /dev/null +++ b/app/Models/Vehicle.php @@ -0,0 +1,31 @@ + 'date', + 'fecha_acta' => 'date', + 'fecha_recuperacion' => 'datetime', + 'datos_robo_original' => 'array', + ]; +} diff --git a/app/Services/ConsultaEstatalService.php b/app/Services/ConsultaEstatalService.php new file mode 100644 index 0000000..c2a43db --- /dev/null +++ b/app/Services/ConsultaEstatalService.php @@ -0,0 +1,133 @@ +soapUrl = config('services.padron_estatal.url'); + } + + /** + * Consulta vehículo + */ + public function consultarPorEpc(string $epc): ?array + { + try { + $datos = $this->consultarPadron('niv', $epc); + + return [ + 'placa' => $datos['placa'] ?? null, + 'vin' => $datos['niv'] ?? null, + ]; + + } catch (Exception $e) { + Log::error("Error consultando padrón por EPC: {$epc} - " . $e->getMessage()); + return null; + } + } + + /** + * Consulta el padrón vehicular estatal vía SOAP + */ + private function consultarPadron(string $tipo, string $valor): array + { + // Construir el Data en formato JSON + $data = json_encode([ + 'tipo' => $tipo, + 'valor' => $valor + ]); + + // Construir el cuerpo SOAP + $soapBody = << + + + + {$data} + + + + XML; + + // Configurar cURL + $ch = curl_init($this->soapUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $soapBody); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: text/xml; charset=utf-8', + 'SOAPAction: ""', + 'Content-Length: ' . strlen($soapBody) + ]); + + try { + // Ejecutar la petición + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + + if ($error) { + throw new Exception("Error en la petición: {$error}"); + } + + if ($httpCode !== 200) { + throw new Exception("Error HTTP {$httpCode}"); + } + + // Parsear la respuesta + return $this->parsearRespuesta($response); + + } finally { + unset($ch); + } + } + + /** + * Parsea la respuesta y extrae los datos del vehículo + */ + private function parsearRespuesta(string $soapResponse): array + { + // Extraer el contenido del tag + preg_match('/(.*?)<\/result>/s', $soapResponse, $matches); + + if (!isset($matches[1])) { + throw new Exception("No se pudo extraer el resultado del padrón estatal"); + } + + $jsonContent = trim($matches[1]); + + // Decodificar el JSON + $result = json_decode($jsonContent, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception("Error al decodificar JSON: " . json_last_error_msg()); + } + + if (!isset($result[0])) { + throw new Exception("Formato de respuesta inesperado"); + } + + $data = $result[0]; + + // Verificar si hay error + if ($data['error'] !== 0) { + throw new Exception("Error en consulta: código {$data['error']}"); + } + + // Verificar si hay datos + if (!isset($data['datos'][0])) { + throw new Exception("No se encontraron datos del vehículo"); + } + + return $data['datos'][0]; + } +} diff --git a/app/Services/ReporteRoboService.php b/app/Services/ReporteRoboService.php new file mode 100644 index 0000000..c27dd3a --- /dev/null +++ b/app/Services/ReporteRoboService.php @@ -0,0 +1,187 @@ +baseUrl = config('services.repuve_federal.base_url'); + $this->roboEndpoint = config('services.repuve_federal.robo_endpoint'); + $this->username = config('services.repuve_federal.username'); + $this->password = config('services.repuve_federal.password'); + } + + public function consultarPorVin(?string $vin = null, ?string $placa = null): array + { + try { + if (empty($vin) && empty($placa)) { + Log::warning('ReporteRoboService: No se proporcionó VIN ni PLACA'); + return [ + 'tiene_reporte' => false, + 'datos' => [], + 'error' => 'Debe proporcionar al menos VIN o PLACA' + ]; + } + + $url = $this->baseUrl . $this->roboEndpoint; + + // Construir arg2 según los parámetros + if (!empty($vin) && !empty($placa)) { + $arg2 = $vin . '|' . $placa . str_repeat('|', 5); + } elseif (!empty($vin)) { + $arg2 = $vin . str_repeat('|', 7); + } else { + $arg2 = '||' . $placa . str_repeat('|', 5); + } + + Log::info('ReporteRoboService: Consultando REPUVE', [ + 'vin' => $vin, + 'placa' => $placa + ]); + + $soapBody = << + + + + {$this->username} + {$this->password} + {$arg2} + + + + XML; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $soapBody); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: text/xml; charset=utf-8', + 'SOAPAction: "doConsRepRobo"', + 'Content-Length: ' . strlen($soapBody), + ]); + + try { + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + + if ($error) { + Log::error('ReporteRoboService: Error de conexión', ['error' => $error]); + return [ + 'tiene_reporte' => false, + 'datos' => [], + 'error' => 'Error de conexión con REPUVE' + ]; + } + + if ($httpCode !== 200) { + Log::error('ReporteRoboService: HTTP error', ['http_code' => $httpCode]); + return [ + 'tiene_reporte' => false, + 'datos' => [], + 'error' => "Error HTTP {$httpCode}" + ]; + } + + return $this->parseRoboResponse($response, $vin ?? $placa); + + } finally { + unset($ch); + } + + } catch (Exception $e) { + Log::error('ReporteRoboService: Excepción', ['message' => $e->getMessage()]); + return [ + 'tiene_reporte' => false, + 'datos' => [], + 'error' => $e->getMessage() + ]; + } + } + + /** + * Parsea la respuesta del servicio de robo + */ + private function parseRoboResponse(string $soapResponse, string $valor): array + { + // Extraer contenido del tag + preg_match('/(.*?)<\/return>/s', $soapResponse, $matches); + + if (!isset($matches[1])) { + Log::error('ReporteRoboService: No se encontró tag '); + return [ + 'tiene_reporte' => false, + 'datos' => [], + 'error' => 'Respuesta SOAP inválida' + ]; + } + + $contenido = trim($matches[1]); + + // Verificar si hay error (ERR: o ERROR:) + if (preg_match('/(ERR|ERROR|err|error):(-?\d+)/i', $contenido, $errorMatch)) { + $errorCode = $errorMatch[2]; + Log::warning('ReporteRoboService: Error del servicio', ['error_code' => $errorCode]); + + return [ + 'tiene_reporte' => false, + 'datos' => ['error_code' => $errorCode], + 'error' => "Error REPUVE código {$errorCode}" + ]; + } + + // Si empieza con OK:, parsear los datos + if (str_starts_with($contenido, 'OK:')) { + $datos = str_replace('OK:', '', $contenido); + $valores = explode('|', $datos); + + // 1 = robado, 0 = no robado + $indicador = $valores[0] ?? '0'; + $tieneReporte = ($indicador === '1'); + + $datosRobo = [ + 'indicador' => $indicador, + 'fecha_robo' => $valores[1] ?? null, + 'placa' => $valores[2] ?? null, + 'vin' => $valores[3] ?? null, + 'autoridad' => $valores[4] ?? null, + 'acta' => $valores[5] ?? null, + 'denunciante' => $valores[6] ?? null, + 'fecha_acta' => $valores[7] ?? null, + ]; + + if ($tieneReporte) { + Log::warning('ReporteRoboService: ¡VEHÍCULO ROBADO DETECTADO!', [ + 'vin' => $datosRobo['vin'], + 'placa' => $datosRobo['placa'], + 'autoridad' => $datosRobo['autoridad'] + ]); + } + + return [ + 'tiene_reporte' => $tieneReporte, + 'datos' => $datosRobo + ]; + } + + Log::error('ReporteRoboService: Formato no reconocido', ['contenido' => $contenido]); + return [ + 'tiene_reporte' => false, + 'datos' => [], + 'error' => 'Formato de respuesta no reconocido' + ]; + } +} diff --git a/app/Services/VehicleService.php b/app/Services/VehicleService.php new file mode 100644 index 0000000..f99b131 --- /dev/null +++ b/app/Services/VehicleService.php @@ -0,0 +1,187 @@ +verificarVehiculoRobado($epc, $arcoId, json_decode($enRedis, true)); + } + + // No está en Redis, consultar servicios + return $this->consultarNuevoVehiculo($epc, $arcoId); + } + + private function consultarNuevoVehiculo(string $epc, ?int $arcoId): array + { + // Consultar padrón estatal + $datosEstatal = $this->consultaEstatal->consultarPorEpc($epc); + + if (!$datosEstatal || !$datosEstatal['vin']) { + return [ + 'success' => false, + 'message' => 'No se encontró información del vehículo' + ]; + } + + // Consultar REPUVE + $reporteRobo = $this->reporteRobo->consultarPorVin( + $datosEstatal['vin'], + $datosEstatal['placa'] + ); + + if ($reporteRobo['tiene_reporte']) { + // Está robado → Guardar en Redis + $this->guardarEnRedis($epc, $datosEstatal, $reporteRobo['datos']); + + Log::warning('¡VEHÍCULO ROBADO DETECTADO!', [ + 'epc' => $epc, + 'vin' => $datosEstatal['vin'], + 'placa' => $datosEstatal['placa'] + ]); + + return [ + 'success' => true, + 'tiene_reporte_robo' => true, + 'estado' => 'ROBADO', + 'accion' => 'GUARDADO_EN_REDIS', + 'vehiculo' => array_merge($datosEstatal, $reporteRobo['datos']) + ]; + } + + // No está robado, no hacer nada + return [ + 'success' => true, + 'tiene_reporte_robo' => false, + 'estado' => 'LIBRE', + 'vehiculo' => $datosEstatal + ]; + } + + private function verificarVehiculoRobado(string $epc, ?int $arcoId, array $datosRedis): array + { + // Consultar REPUVE para verificar estado actual + $reporteRobo = $this->reporteRobo->consultarPorVin( + $datosRedis['vin'], + $datosRedis['placa'] + ); + + if (!$reporteRobo['tiene_reporte']) { + // No tiene reporte robo + $this->registrarRecuperacion($epc, $arcoId, $datosRedis); + + Log::info('¡VEHÍCULO RECUPERADO!', [ + 'epc' => $epc, + 'vin' => $datosRedis['vin'], + 'placa' => $datosRedis['placa'] + ]); + + return [ + 'success' => true, + 'tiene_reporte_robo' => false, + 'estado' => 'RECUPERADO', + 'accion' => 'GUARDADO_EN_MYSQL_Y_ELIMINADO_DE_REDIS', + 'vehiculo' => $datosRedis + ]; + } + + // Sigue robado → Actualizar contador en Redis + $this->actualizarDeteccionRedis($epc, $datosRedis); + + Log::warning('Vehículo robado detectado nuevamente', [ + 'epc' => $epc, + 'detecciones' => $datosRedis['detecciones'] + 1 + ]); + + return [ + 'success' => true, + 'tiene_reporte_robo' => true, + 'estado' => 'ROBADO', + 'accion' => 'ACTUALIZADO_EN_REDIS', + 'vehiculo' => $datosRedis + ]; + } + + 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' => 1 + ]; + + Redis::set($key, json_encode($datos)); + } + + private function actualizarDeteccionRedis(string $epc, array $datosActuales): void + { + $key = "vehiculo:robado:{$epc}"; + + $datosActuales['ultima_deteccion'] = now()->toIso8601String(); + $datosActuales['detecciones'] = ($datosActuales['detecciones'] ?? 0) + 1; + + Redis::set($key, json_encode($datosActuales)); + } + + private function registrarRecuperacion(string $epc, ?int $arcoId, array $datosRedis): void + { + // Guardar en MySQL + Vehicle::create([ + 'epc' => $epc, + 'vin' => $datosRedis['vin'], + 'placa' => $datosRedis['placa'], + 'fecha_robo' => $datosRedis['fecha_robo'], + 'autoridad_robo' => $datosRedis['autoridad'], + 'acta_robo' => $datosRedis['acta'], + 'denunciante' => $datosRedis['denunciante'], + 'fecha_recuperacion' => now(), + 'fecha_acta' => $datosRedis['fecha_acta'] ?? null, + 'datos_robo_original' => $datosRedis + ]); + + // Eliminar de Redis + Redis::del("vehiculo:robado:{$epc}"); + } + + 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; + } +} diff --git a/config/services.php b/config/services.php index 27a3617..e3339e3 100644 --- a/config/services.php +++ b/config/services.php @@ -35,4 +35,16 @@ ], ], + 'repuve_federal' => [ + 'base_url' => env('REPUVE_FED_BASE_URL'), + 'robo_endpoint' => '/jaxws-consultarpv/ConsultaRpv', + 'inscripcion_endpoint' => '/jaxrpc-inscripcion/Inscripcion?WSDLs=', + 'username' => env('REPUVE_FED_USERNAME'), + 'password' => env('REPUVE_FED_PASSWORD'), + ], + + 'padron_estatal' => [ + 'url' => env('REPUVE_EST_URL'), + ], + ]; diff --git a/database/migrations/2025_12_19_122433_create_vehicles_table.php b/database/migrations/2025_12_19_122433_create_vehicles_table.php new file mode 100644 index 0000000..e5b4dbe --- /dev/null +++ b/database/migrations/2025_12_19_122433_create_vehicles_table.php @@ -0,0 +1,46 @@ +id(); + + // Identificadores del vehículo + $table->string('epc')->unique(); + $table->string('vin')->nullable(); + $table->string('placa')->nullable(); + + // Datos del reporte de robo + $table->date('fecha_robo')->nullable(); + $table->string('autoridad_robo')->nullable(); + $table->string('acta_robo')->nullable(); + $table->string('denunciante')->nullable(); + $table->date('fecha_acta')->nullable(); + + // Datos de recuperación + $table->timestamp('fecha_recuperacion')->nullable(); + + // Datos completos del robo original (JSON) - Para auditoría + $table->json('datos_robo_original')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('vehicles'); + } +}; diff --git a/docker-compose.yml b/docker-compose.yml index 8531c36..0a37e73 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,11 +66,39 @@ services: - arcos-network mem_limit: 400m + redis: + image: redis:alpine + ports: + - "${REDIS_PORT}:6379" + volumes: + - redis_data:/data + networks: + - arcos-network + mem_limit: 256m + healthcheck: + test: ["CMD", "redis-cli", "ping"] + timeout: 5s + retries: 5 + + redis-commander: + image: rediscommander/redis-commander:latest + environment: + - ${REDIS_HOST}:redis:6379 + ports: + - "${REDIS_COMMANDER_PORT}:8081" + networks: + - arcos-network + mem_limit: 256m + depends_on: + - redis + volumes: nginx_data: driver: local mysql_data: driver: local + redis_data: + driver: local networks: arcos-network: diff --git a/routes/api.php b/routes/api.php index b752e50..791c945 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,23 +1,28 @@ group(function() { - // Tus rutas protegidas + + Route::post('/vehicles/consultar', [VehicleController::class, 'consultarVehiculo']); + Route::post('/vehicles/recuperar', [VehicleController::class, 'recuperarVehiculo']); + Route::get('/vehicles/robados', [VehicleController::class, 'listarRobados']); }); /** Rutas públicas */