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 ApiResponse::INTERNAL_ERROR->response([ 'success' => false, 'message' => 'Datos inválidos', 'errors' => $validator->errors() ]); } $placa = $request->placa; $vin = $request->vin; // Buscar vehículo en vehicle_fakes $vehiculo = VehicleFake::when($vin, function ($query) use ($vin) { return $query->where('vin', $vin); }) ->when($placa, function ($query) use ($placa) { return $query->orWhere('placa', $placa); }) ->first(); if (!$vehiculo) { return ApiResponse::NOT_FOUND->response([ 'success' => false, 'message' => 'No se encontró el vehículo' ]); } // Generar EPC $epc = 'E280117000000' . $vehiculo->vin; // Verificar si ya está en Redis $key = "vehiculo:robado:{$placa}-{$vin}"; if (Redis::exists($key)) { return ApiResponse::BAD_REQUEST->response([ 'success' => false, 'message' => 'El vehículo ya está registrado como robado' ]); } // Guardar en Redis como robado $datos = [ 'epc' => $epc, 'folio_tag' => $vehiculo->folio_tag, 'tag_number' => $vehiculo->tag_number, 'vin' => $vehiculo->vin, 'placa' => $vehiculo->placa, 'marca' => $vehiculo->marca, 'modelo' => $vehiculo->modelo, 'color' => $vehiculo->color, 'fecha_robo' => now()->format('Y-m-d'), 'acta' => 'MANUAL-' . now()->format('Ymd-His'), 'denunciante' => 'SISTEMA', 'fecha_acta' => now()->format('Y-m-d'), 'primera_deteccion' => now()->toIso8601String(), 'ultima_deteccion' => now()->toIso8601String(), ]; Redis::set($key, json_encode($datos)); Log::warning('Vehículo dado de alta como robado (MANUAL)', [ 'epc' => $epc, 'vin' => $vehiculo->vin, 'placa' => $vehiculo->placa ]); return ApiResponse::OK->response([ 'success' => true, 'message' => 'Vehículo registrado como robado exitosamente', 'vehiculo' => [ 'epc' => $epc, 'folio_tag' => $vehiculo->folio_tag, 'tag_number' => $vehiculo->tag_number, 'vin' => $vehiculo->vin, 'placa' => $vehiculo->placa, 'marca' => $vehiculo->marca, 'modelo' => $vehiculo->modelo, 'color' => $vehiculo->color ] ]); } /** * 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 ApiResponse::BAD_REQUEST->response([ 'success' => false, 'message' => 'Datos inválidos', 'errors' => $validator->errors() ]); } 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 ApiResponse::NOT_FOUND->response([ 'success' => false, 'message' => 'El vehículo no se encuentra en la lista de robados activos' ]); } $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'] ?? null, 'placa' => $datosRedis['placa'] ?? null, 'fecha_robo' => $datosRedis['fecha_robo'] ?? null, 'acta_robo' => $datosRedis['acta'] ?? null, 'denunciante' => $datosRedis['denunciante'] ?? null, '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 ApiResponse::OK->response([ '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, '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 ApiResponse::INTERNAL_ERROR->response([ 'success' => false, 'message' => 'Error al registrar vehículo como recuperado', 'error' => $e->getMessage() ]); } } /** * 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(Request $request) { try { $vehiculos = $this->vehicleService->listarVehiculosRobados(); $collection = collect($vehiculos); $perPage = $request->input('per_page', config('app.pagination', 15)); $page = $request->input('page', 1); $paginatedVehiculos = new \Illuminate\Pagination\LengthAwarePaginator( $collection->forPage($page, $perPage), $collection->count(), $perPage, $page, ['path' => $request->url(), 'query' => $request->query()] ); return ApiResponse::OK->response([ 'success' => true, 'total' => $collection->count(), 'vehiculos' => $paginatedVehiculos ]); } catch (\Exception $e) { return ApiResponse::BAD_REQUEST->response([ 'success' => false, 'message' => 'Error al listar vehículos robados', 'error' => $e->getMessage() ]); } } public function listarRecuperados() { $vehiculos = Vehicle::orderBy('fecha_recuperacion', 'desc') ->paginate(config('app.pagination')); return ApiResponse::OK->response([ 'success' => true, 'vehiculos' => $vehiculos ]); } public function listarDetecciones() { $detecciones = Detection::orderBy('fecha_deteccion', 'desc') ->paginate(config('app.pagination')); return ApiResponse::OK->response([ 'success' => true, 'detecciones' => $detecciones ]); } public function buscarVehiculo(Request $request) { $validated = $request->validate([ 'placa' => 'required_without:vin|string|nullable', 'vin' => 'required_without:placa|string|nullable', ]); $placa = $validated['placa'] ?? null; $vin = $validated['vin'] ?? null; $vehiculo = VehicleFake::when($vin, function ($query) use ($vin) { return $query->where('vin', $vin); }) ->when($placa, function ($query) use ($placa) { return $query->orWhere('placa', $placa); }) ->first(); if (!$vehiculo) { return ApiResponse::NOT_FOUND->response([ 'success' => false, 'message' => 'No se encontró el vehículo en la base de datos' ]); } // Buscar detecciones del vehículo $detecciones = Detection::when($vin, function ($query) use ($vin) { return $query->where('vin', $vin); }) ->when($placa, function ($query) use ($placa) { return $query->orWhere('placa', $placa); }) ->orderBy('fecha_deteccion', 'desc') ->paginate(config('app.pagination')); return ApiResponse::OK->response([ 'success' => true, 'vehiculo' => [ 'folio_tag' => $vehiculo->folio_tag, 'tag_number' => $vehiculo->tag_number, 'placa' => $vehiculo->placa, 'vin' => $vehiculo->vin, 'marca' => $vehiculo->marca, 'modelo' => $vehiculo->modelo, 'color' => $vehiculo->color, ], 'total_detecciones' => $detecciones->total(), 'detecciones' => $detecciones ]); } public function buscarVehiculoRobado(Request $request) { $validated = $request->validate([ 'placa' => 'required_without:vin|string|nullable', 'vin' => 'required_without:placa|string|nullable', ]); $placa = $validated['placa'] ?? null; $vin = $validated['vin'] ?? null; // Buscar en Redis $vehiculoEncontrado = $this->buscarEnRedis($vin, $placa); if (!$vehiculoEncontrado) { return ApiResponse::NOT_FOUND->response([ 'success' => false, 'message' => 'El vehículo no está registrado como robado' ]); } return ApiResponse::OK->response([ 'success' => true, 'message' => 'Vehículo encontrado en lista de robados', 'vehiculo' => $vehiculoEncontrado['datos'] ]); } /** * Procesar detección de tag RFID * POST /api/vehicles/buscar */ public function buscarPorTag(Request $request) { $validated = $request->validate([ 'fast_id' => 'required|string', 'epc' => 'nullable|string', 'arco_ip' => 'nullable|ip', 'timestamp' => 'nullable|date' ]); // Si no se proporciona EPC, usar el FastID como identificador $epc = $validated['epc'] ?? $validated['fast_id']; $resultado = $this->vehicleService->procesarDeteccion( $epc, $validated['fast_id'], $validated['arco_ip'], $validated['timestamp'] ?? null ); if (!$resultado['success']) { return ApiResponse::NOT_FOUND->response([ 'success' => false, 'message' => $resultado['message'] ?? 'No se encontró el vehículo' ]); } return ApiResponse::OK->response($resultado); } }