15 KiB
Documentación Técnica — Arcos Backend
Índice
- Visión General
- Flujo de Detección RFID
- Gestión de Vehículos Robados
- Integraciones REPUVE
- Gestión de Arcos (Lectores RFID)
- Sistema de Alertas
- Registro de Detecciones
- Transmisión en Tiempo Real
- Esquema de Base de Datos
- Comandos y Tareas Programadas
Visión General
Arcos es una API REST construida en Laravel 12 para la detección de vehículos robados mediante lectores RFID ("arcos"). El sistema conecta lectores de hardware en campo con registros federales y estatales de robo vehicular (REPUVE), registra cada detección en base de datos y emite alertas en tiempo real vía WebSocket cuando un vehículo reportado como robado es detectado.
Stack tecnológico relevante
| Componente | Tecnología |
|---|---|
| Framework | Laravel 12 / PHP 8.3 |
| Base de datos principal | MySQL 8.0 |
| Almacenamiento en caliente | Redis |
| Cola de trabajos | Laravel Queue (driver: database) |
| WebSockets | Laravel Reverb |
| Autenticación hardware | Token AES-256-CBC propio |
| Autenticación usuarios | Laravel Passport (OAuth 2.0) |
Flujo de Detección RFID
Este es el flujo central del sistema. Ocurre cada vez que un lector RFID detecta una etiqueta vehicular.
Lector RFID (hardware)
│
▼
POST /api/vehicles/buscar
Authorization: Bearer {arco_token}
{ fast_id, antena?, timestamp? }
│
▼
ArcoTokenMiddleware
├─ Desencripta token del encabezado y lo compara contra arcos.api_token
├─ Verifica que el arco esté activo (activo = true)
└─ Inyecta el objeto Arco en $request->get('arco_autenticado')
│
▼
VehicleController::buscarPorTag()
└─ Despacha ProcesarDeteccionVehiculo al queue
│ (responde 200 OK inmediato al hardware)
│
▼ (proceso asíncrono)
Job: ProcesarDeteccionVehiculo
Reintentos: 3 | Backoff: 10 s | Timeout: 120 s
│
▼
VehicleService::procesarDeteccion($fastId, $arcoId, $antena)
│
├─ ¿fast_id existe en Redis como robado?
│ │
│ ├─ SÍ ──► verificarVehiculoRobado()
│ │ ├─ Incrementa contador en Redis
│ │ ├─ Crea registro en alertas_robos
│ │ ├─ Emite evento WebSocket VehiculoRobadoDetectado
│ │ └─ Registra en detections + daily_detections (estado: ROBADO)
│ │
│ └─ NO ──► consultarNuevoVehiculo()
│ ├─ Consulta ConsultaRepuveConstancia (API REST)
│ │ └─ Si responde → datos del vehículo
│ ├─ Si no encontrado → Consulta ReporteRoboService (SOAP federal)
│ └─ Registra en detections + daily_detections (estado: LIBRE / DESCONOCIDO)
Autenticación del hardware (ArcoTokenMiddleware)
Cada lector RFID tiene un api_token único almacenado encriptado en la tabla arcos. El middleware:
- Extrae el Bearer token del encabezado
Authorization - Itera los arcos activos comparando el token desencriptado con
EncryptionHelper::encryptWithCustomKey() - Rechaza con
401si el token no corresponde a ningún arco - Rechaza con
403si el arco está inactivo (activo = false)
La clave de encriptación proviene de ARCO_TOKEN_ENCRYPTION_KEY en .env.
Gestión de Vehículos Robados
El registro activo de vehículos robados vive en Redis, no en MySQL. Esto permite búsquedas en tiempo O(1) durante cada detección.
Registrar vehículo como robado
POST /api/vehicles/consultar — requiere auth:api
{
"placa": "ABC1234",
"vin": "1G1FB1RX1234567890",
"fecha_robo": "2025-02-01",
"autoridad": "PROCURADURÍA",
"acta": "ACT-2025-001",
"denunciante": "Nombre Denunciante",
"fecha_acta": "2025-02-01"
}
El proceso interno:
- Consulta el
fast_id(tag RFID) del vehículo viaVehicleService::consultarVehiculoPorTag()usando placa o VIN como llave de búsqueda en REPUVE Constancia - Verifica que no esté ya registrado en Redis:
Redis::exists("vehiculo:robado:{fastId}") - Almacena la entrada en Redis con la clave
vehiculo:robado:{fastId}:
[
'fast_id' => string,
'vin' => string,
'placa' => string,
'marca' => string,
'modelo' => string,
'color' => string,
'fecha_robo' => string,
'autoridad' => string,
'acta' => string,
'denunciante' => string,
'fecha_acta' => string,
'primera_deteccion' => ISO8601,
'ultima_deteccion' => ISO8601,
'detecciones' => int // contador incremental
]
Recuperar vehículo
POST /api/vehicles/recuperar — requiere auth:api
- Busca el vehículo en Redis por
placaoviniterando clavesvehiculo:robado:* - Crea un registro permanente en MySQL (
vehicles) con los datos de robo y la fecha de recuperación - Elimina la clave de Redis:
Redis::del("vehiculo:robado:{fastId}")
Consultar estado de un vehículo
GET /api/vehicles/detectar?placa=... o GET /api/vehicles/robado?vin=...
Busca primero en Redis y luego consulta las APIs REPUVE según sea necesario.
Listar vehículos robados activos
GET /api/vehicles/robados?placa=...&vin=...
Lee todas las claves vehiculo:robado:* de Redis. Soporta filtro por placa y vin.
Listar vehículos recuperados
GET /api/vehicles
Lee la tabla vehicles en MySQL (archivo histórico de recuperaciones).
Integraciones REPUVE
El sistema consume tres APIs externas de registros vehiculares. Las credenciales se configuran en .env y se exponen en config/services.php.
1. REPUVE Constancia (ConsultaRepuveConstancia)
API REST con autenticación JWT. Es la fuente principal de datos vehiculares.
Variables de entorno:
REPUVE_CONSTANCIA_BASE_URL
REPUVE_CONSTANCIA_LOGIN_ENDPOINT
REPUVE_CONSTANCIA_CONSULTA_ENDPOINT
REPUVE_CONSTANCIA_EMAIL
REPUVE_CONSTANCIA_PASSWORD
REPUVE_CONSTANCIA_TOKEN_TTL # TTL del token en caché (segundos)
Flujo de autenticación: El servicio obtiene un JWT via email/contraseña y lo almacena en caché por TOKEN_TTL segundos. Se renueva automáticamente al expirar.
Búsqueda de vehículo — consultarVehiculoPorTag():
Intenta en orden: tag_number → placa → vin
Respuesta normalizada:
[
'tag_number' => string, // fast_id / folio RFID
'folio_tag' => string,
'vin' => string,
'placa' => string,
'marca' => string,
'modelo' => string,
'linea' => string,
'sublinea' => string,
'color' => string,
'numero_motor' => string,
'clase_veh' => string,
'tipo_servicio' => string,
'reporte_robo' => bool, // indicador de robo en REPUVE
'propietario' => [
'id', 'nombre_completo', 'rfc', 'curp', 'telefono', 'direccion'
],
'tag_status' => string // ACTIVO / INACTIVO
]
2. REPUVE Federal — Reporte de Robo (ReporteRoboService)
Servicio SOAP sobre cURL. Consulta el padrón federal de vehículos robados.
Variables de entorno:
REPUVE_FED_BASE_URL
REPUVE_FED_USERNAME
REPUVE_FED_PASSWORD
Método consultarRobado($vin, $placa)
Protocolo SOAP; el cuerpo de la solicitud:
<wsdl:doConsRepRobo>
<arg0>{username}</arg0>
<arg1>{password}</arg1>
<arg2>{vin}|{placa}|||||</arg2>
</wsdl:doConsRepRobo>
Respuesta parseada desde <return>: OK:indicador|fecha|placa|vin|autoridad|acta|denunciante|fecha_acta
[
'tiene_reporte' => bool, // true si indicador == '1'
'datos' => [
'indicador' => '1', // 1 = robado, 0 = no robado
'fecha_robo' => 'YYYY-MM-DD',
'placa' => string,
'vin' => string,
'autoridad' => string,
'acta' => string,
'denunciante' => string,
'fecha_acta' => 'YYYY-MM-DD',
]
]
Método consultarVehiculo($vin, $placa)
Consulta el padrón nacional para obtener datos del vehículo (sin estado de robo).
3. Padrón Estatal (ConsultaEstatalService)
Servicio SOAP que responde en JSON. Consulta el registro estatal de vehículos.
Variable de entorno: REPUVE_EST_URL
Jerarquía de consulta durante una detección
fast_id detectado
│
├─ 1° Redis (robados en caliente) → respuesta inmediata
├─ 2° REPUVE Constancia (datos + robo) → API REST / JWT
└─ 3° REPUVE Federal (backup) → SOAP
Gestión de Arcos (Lectores RFID)
Un arco representa un lector RFID físico instalado en campo.
Modelo Arco
Campos relevantes de la tabla arcos:
| Campo | Tipo | Descripción |
|---|---|---|
nombre |
string | Nombre descriptivo del arco |
ip_address |
string (unique) | IP del lector en la red |
ubicacion |
string | Descripción de la ubicación física |
activo |
boolean | Habilita/deshabilita el arco |
antena_1..4 |
string | Etiquetas para cada antena |
api_token |
string | Token encriptado AES-256-CBC |
Generación de token: Al crear un arco se genera automáticamente un token de 64 caracteres aleatorios, encriptado con EncryptionHelper::encryptWithCustomKey() antes de persistir en BD.
Endpoints
| Método | Ruta | Acción |
|---|---|---|
GET |
/api/arcos |
Listar arcos (filtros: activo, ip) |
POST |
/api/arcos |
Crear arco |
GET |
/api/arcos/{id} |
Detalle de un arco |
PUT/PATCH |
/api/arcos/{id} |
Actualizar arco |
DELETE |
/api/arcos/{id} |
Eliminar arco |
PATCH |
/api/arcos/{id}/toggle-estado |
Activar / desactivar |
GET |
/api/arcos/{id}/detecciones/dia |
Detecciones del día para ese arco |
Sistema de Alertas
Una alerta se genera automáticamente cuando el job de detección identifica que el fast_id detectado corresponde a un vehículo registrado como robado en Redis.
Generación (VehicleService::crearAlertaRobo())
- Inserta registro en
alertas_robos - Emite evento WebSocket
VehiculoRobadoDetectadoal canalalertas-robos
Confirmar alerta
PUT /api/alertas/{id}/confirmar — requiere auth:api
- Si ya fue confirmada (
visto = true), devuelve400con los datos del usuario que la confirmó - Si no, actualiza:
visto = true,usuario_id = auth()->id(),fecha_confirmacion = now()
Endpoints de consulta
| Endpoint | Descripción |
|---|---|
GET /api/alertas/pendientes |
Sin confirmar, ordenadas por más recientes |
GET /api/alertas |
Todas, filtrable por visto, arco_id, placa, vin, rango de fechas |
GET /api/alertas/{id} |
Detalle de una alerta |
Registro de Detecciones
Cada detección se persiste en dos tablas con propósitos distintos.
detections — Historial completo
Registro individual e inmutable de cada evento RFID.
| Campo | Descripción |
|---|---|
arco_id |
FK al arco que detectó |
fast_id |
Tag RFID leído |
vin, placa, marca, modelo, color |
Datos del vehículo al momento de detección |
antena |
Número de antena del arco |
fecha_deteccion |
Timestamp exacto |
daily_detections — Resumen diario
Incluye información adicional de estado, útil para dashboards y reportes.
| Campo | Descripción |
|---|---|
arco_nombre |
Nombre del arco (desnormalizado) |
estado |
LIBRE | ROBADO | DESCONOCIDO |
tiene_reporte_robo |
Boolean — si REPUVE reporta robo |
fecha_dia |
Columna virtual: DATE(fecha_deteccion) |
Índices: arco_id, fecha_dia, [arco_id + fecha_dia], fast_id
Endpoints de consulta
| Endpoint | Descripción |
|---|---|
GET /api/vehicles/detecciones |
Historial paginado (filtros: placa, vin) |
GET /api/vehicles/detecciones/dia?fecha=YYYY-MM-DD |
Resumen diario (ventana de últimos 10 s) |
GET /api/arcos/{id}/detecciones/dia?fecha=YYYY-MM-DD |
Resumen diario por arco |
Transmisión en Tiempo Real
Servidor: Laravel Reverb (WebSocket)
Cuando se detecta un vehículo robado, se emite el evento VehiculoRobadoDetectado:
- Canal:
alertas-robos(público) - Nombre en frontend:
vehiculo.robado.detectado - Payload:
{
"alerta_id": 42,
"fast_id": "1234567890",
"vin": "1G1FB1RX1234567890",
"placa": "ABC1234",
"marca": "Chevrolet",
"modelo": "Cruze",
"color": "BLANCO",
"arco_id": 3,
"arco_nombre": "Arco Reforma Norte",
"antena": "1",
"fecha_deteccion": "2025-02-01T14:30:00Z",
"mensaje": "🚨 VEHÍCULO ROBADO DETECTADO: ABC1234 en Arco Reforma Norte"
}
El frontend conecta vía Laravel Echo + Pusher.js.
Esquema de Base de Datos
arcos
id, nombre, ip_address (UNIQUE), ubicacion, descripcion,
activo BOOLEAN DEFAULT true,
antena_1, antena_2, antena_3, antena_4,
api_token,
created_at, updated_at
detections
id, arco_id (FK arcos), fast_id, vin, placa,
marca, modelo, color, antena, fecha_deteccion,
created_at, updated_at
daily_detections
id, arco_id (FK arcos), arco_nombre, antena,
fast_id, vin, placa, marca, modelo, color,
estado ENUM('LIBRE','ROBADO','DESCONOCIDO'),
tiene_reporte_robo BOOLEAN,
fecha_deteccion, fecha_dia (virtual: DATE(fecha_deteccion)),
created_at, updated_at
vehicles — Archivo de recuperaciones
id, fast_id, vin, placa,
fecha_robo, acta_robo, denunciante, fecha_acta,
fecha_recuperacion,
datos_robo_original JSON,
created_at, updated_at
alertas_robos
id, fast_id, vin, placa, marca, modelo, color,
arco_id (FK arcos), arco_nombre, antena,
fecha_deteccion,
visto BOOLEAN DEFAULT false,
usuario_id (FK users, NULLABLE),
fecha_confirmacion NULLABLE,
created_at, updated_at
Comandos y Tareas Programadas
php artisan detecciones:limpiar {--dias=1}
Elimina registros de daily_detections con más de N días de antigüedad (default: 1 día). Se puede ejecutar manualmente o agregar al scheduler.
php artisan detecciones:limpiar # elimina detecciones de más de 1 día
php artisan detecciones:limpiar --dias=7 # elimina detecciones de más de 7 días
Servicios en producción (PM2)
composer run services:start # inicia queue worker, scheduler y Reverb
composer run services:stop # detiene todos los servicios
composer run services:status # muestra estado de cada proceso PM2
Procesos individuales:
php artisan queue:work # procesa detecciones encoladas
php artisan schedule:work # ejecuta tareas programadas
php artisan reverb:start # servidor WebSocket