arcos-backend/app/Services/VehicleService.php

404 lines
14 KiB
PHP

<?php
namespace App\Services;
use App\Events\VehiculoRobadoDetectado;
use App\Models\AlertaRobo;
use App\Models\Detection;
use App\Models\DailyDetection;
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 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
]);
}
}