arcos-backend/app/Services/VehicleService.php
Juan Felipe Zapata Moreno 1b4eba2c6c laravel reverb
2026-01-10 08:51:36 -06:00

401 lines
14 KiB
PHP

<?php
namespace App\Services;
use App\Events\VehiculoRobadoDetectado;
use App\Models\AlertaRobo;
use App\Models\Detection;
use App\Models\Arco;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Log;
class VehicleService
{
public function __construct(
private ConsultaRepuveConstancia $consultaRepuveCons,
private ReporteRoboService $reporteRoboService // Agregar esta dependencia
) {}
/**
* Procesar detección de vehículo por fast_id
*/
public function procesarDeteccion(string $fastId, int $arcoId, ?string $antena = null): array
{
$key = "vehiculo:robado:{$fastId}";
// Verificar si está en Redis
$enRedis = Redis::get($key);
if ($enRedis) {
// Ya está marcado como robado, verificar si sigue así
$resultado = $this->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
]);
}
}