add: endpoint detecciones

This commit is contained in:
Juan Felipe Zapata Moreno 2026-01-08 14:48:10 -06:00
parent 098e8c487d
commit 56a599603d
10 changed files with 165 additions and 27 deletions

View File

@ -4,6 +4,7 @@
use App\Http\Controllers\Controller;
use App\Models\Arco;
use App\Services\VehicleService;
use Illuminate\Http\Request;
use Notsoweb\ApiResponse\Enums\ApiResponse;
@ -12,6 +13,9 @@
*/
class ArcoController extends Controller
{
public function __construct(
private VehicleService $vehicleService
) {}
/**
* Listar todos los arcos
* GET /api/arcos
@ -159,4 +163,60 @@ public function toggleEstado(int $id)
'arco' => $arco
]);
}
/**
* Listar detecciones del día de un arco específico
* GET /api/arcos/{id}/detecciones/dia
*/
public function deteccionesDelDia(Request $request, int $id)
{
$arco = Arco::find($id);
if (!$arco) {
return ApiResponse::NOT_FOUND->response([
'success' => false,
'message' => 'Arco no encontrado'
]);
}
$fecha = $request->input('fecha'); // Formato: Y-m-d (opcional)
if ($fecha && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha)) {
return ApiResponse::BAD_REQUEST->response([
'success' => false,
'message' => 'Formato de fecha inválido. Use YYYY-MM-DD'
]);
}
try {
$detecciones = $this->vehicleService->listarDeteccionesDelDiaPorArco($id, $fecha);
// Filtrar solo VIN, placa y antena
$deteccionesFiltradas = array_map(function($deteccion) {
return [
'vin' => $deteccion['vin'] ?? null,
'placa' => $deteccion['placa'] ?? null,
'antena' => $deteccion['antena'] ?? null,
];
}, $detecciones);
return ApiResponse::OK->response([
'success' => true,
'arco' => [
'id' => $arco->id,
'nombre' => $arco->nombre,
'ubicacion' => $arco->ubicacion,
],
'fecha' => $fecha ?? now()->format('Y-m-d'),
'total' => count($deteccionesFiltradas),
'detecciones' => $deteccionesFiltradas
]);
} catch (\Exception $e) {
return ApiResponse::INTERNAL_ERROR->response([
'success' => false,
'message' => 'Error al obtener detecciones del arco',
'error' => $e->getMessage()
]);
}
}
}

View File

@ -397,7 +397,7 @@ public function buscarPorTag(Request $request)
$resultado = $this->vehicleService->procesarDeteccion(
$validated['fast_id'],
$arco->ip_address,
$validated['timestamp'] ?? null
$validated['antena'] ?? null
);
if (!$resultado['success']) {

View File

@ -18,6 +18,7 @@ class Detection extends Model
protected $fillable = [
'arco_id',
'antena',
'fast_id',
'vin',
'placa',

View File

@ -17,7 +17,7 @@ public function __construct(
/**
* Procesar detección de vehículo por fast_id
*/
public function procesarDeteccion(string $fastId, string $arcoIp): array
public function procesarDeteccion(string $fastId, string $arcoIp, ?string $antena = null): array
{
$key = "vehiculo:robado:{$fastId}";
@ -31,13 +31,13 @@ public function procesarDeteccion(string $fastId, string $arcoIp): array
if ($enRedis) {
// Ya está marcado como robado, verificar si sigue así
$resultado = $this->verificarVehiculoRobado($fastId, json_decode($enRedis, true));
$this->registrarDeteccion($fastId, $resultado, $arcoId);
$this->registrarDeteccion($fastId, $resultado, $arcoId, $antena);
return $resultado;
}
// No está en Redis, consultar servicios
$resultado = $this->consultarNuevoVehiculo($fastId);
$this->registrarDeteccion($fastId, $resultado, $arcoId);
$this->registrarDeteccion($fastId, $resultado, $arcoId, $antena);
return $resultado;
}
@ -141,6 +141,22 @@ public function obtenerEstadisticasDelDia(?string $fecha = null): array
];
}
/**
* 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)
*/
@ -213,7 +229,7 @@ private function actualizarDeteccionRedis(string $fastId, array $datosActuales)
/**
* Registrar detección en MySQL y Redis (detecciones del día)
*/
private function registrarDeteccion(string $fastId, array $resultado, ?int $arcoId = null)
private function registrarDeteccion(string $fastId, array $resultado, ?int $arcoId = null, ?string $antena = null)
{
if (!$resultado['success'] || !isset($resultado['vehiculo'])) {
return;
@ -224,6 +240,7 @@ private function registrarDeteccion(string $fastId, array $resultado, ?int $arco
// Registrar en MySQL
Detection::create([
'arco_id' => $arcoId,
'antena' => $antena,
'fast_id' => $fastId,
'vin' => $vehiculo['vin'] ?? null,
'placa' => $vehiculo['placa'] ?? null,
@ -234,7 +251,7 @@ private function registrarDeteccion(string $fastId, array $resultado, ?int $arco
]);
// Registrar en Redis (detecciones del día)
$this->registrarDeteccionDelDia($fastId, $vehiculo, $arcoId, $resultado);
$this->registrarDeteccionDelDia($fastId, $vehiculo, $arcoId, $resultado, $antena);
}
/**
@ -242,7 +259,7 @@ private function registrarDeteccion(string $fastId, array $resultado, ?int $arco
* 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)
private function registrarDeteccionDelDia(string $fastId, array $vehiculo, ?int $arcoId, array $resultado, ?string $antena = null)
{
$fecha = now()->format('Y-m-d');
$timestamp = now()->timestamp;
@ -256,6 +273,7 @@ private function registrarDeteccionDelDia(string $fastId, array $vehiculo, ?int
'modelo' => $vehiculo['modelo'] ?? null,
'color' => $vehiculo['color'] ?? null,
'arco_id' => $arcoId,
'antena' => $antena,
'estado' => $resultado['estado'] ?? 'LIBRE',
'tiene_reporte_robo' => $resultado['tiene_reporte_robo'] ?? false,
'fecha_deteccion' => now()->toIso8601String(),

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('detections', function (Blueprint $table) {
$table->string('antena')->nullable()->after('arco_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('detections', function (Blueprint $table) {
$table->dropColumn('antena');
});
}
};

View File

@ -0,0 +1,37 @@
<?php namespace Database\Seeders;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use App\Models\Arco;
use Illuminate\Database\Seeder;
use Notsoweb\LaravelCore\Supports\UserSecureSupport;
/**
* Descripción
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @version 1.0.0
*/
class ArcoSeeder extends Seeder
{
/**
* Ejecutar sembrado de base de datos
*/
public function run(): void
{
$arco = Arco::create([
'nombre' => 'Arco',
'ip_address' => '10.30.204.125',
'ubicacion' => 'Ubicación del arco',
'antena_1' => 'FRONTERA - VILLAHERMOSA',
'antena_2' => 'VILLAHERMOSA - FRONTERA',
]);
// Obtener el token desencriptado
$plainToken = $arco->obtenerTokenDesencriptado();
$this->command->getOutput()->writeln("\n Token del arco => {$plainToken}\n");
}
}

View File

@ -7,9 +7,9 @@
/**
* Seeder Producción
*
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
*
* @version 1.0.0
*/
class DatabaseSeeder extends Seeder
@ -22,5 +22,6 @@ public function run(): void
$this->call(RoleSeeder::class);
$this->call(UserSeeder::class);
$this->call(SettingSeeder::class);
$this->call(ArcoSeeder::class);
}
}

View File

@ -22,5 +22,6 @@ public function run(): void
$this->call(RoleSeeder::class);
$this->call(UserSeeder::class);
$this->call(SettingSeeder::class);
$this->call(ArcoSeeder::class);
}
}

View File

@ -9,9 +9,9 @@
/**
* Usuarios predeterminados del sistema
*
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
*
* @version 1.0.0
*/
class UserSeeder extends Seeder
@ -21,34 +21,25 @@ class UserSeeder extends Seeder
*/
public function run(): void
{
$developer = UserSecureSupport::create('developer@notsoweb.com');
$developer = UserSecureSupport::create('developer@golsystems.com');
User::create([
'name' => 'Developer',
'paternal' => 'Notsoweb',
'maternal' => 'Software',
'name' => 'admin',
'paternal' => 'golsystems',
'maternal' => 'desarrollo',
'email' => $developer->email,
'password' => $developer->hash,
])->assignRole(__('developer'));
$admin = UserSecureSupport::create('admin@notsoweb.com');
$admin = UserSecureSupport::create('admin@golsystems.com');
User::create([
'name' => 'Admin',
'paternal' => 'Notsoweb',
'maternal' => 'Software',
'paternal' => 'Golsystems',
'maternal' => 'Desarrollo',
'email' => $admin->email,
'password' => $admin->hash,
])->assignRole(__('admin'));
$demo = UserSecureSupport::create('demo@notsoweb.com');
User::create([
'name' => 'Demo',
'paternal' => 'Notsoweb',
'maternal' => 'Software',
'email' => $demo->email,
'password' => $demo->hash,
]);
}
}

View File

@ -39,6 +39,7 @@
// Rutas de Arcos RFID
Route::resource('/arcos', ArcoController::class);
Route::patch('/arcos/{id}/toggle-estado', [ArcoController::class, 'toggleEstado']);
Route::get('/arcos/{id}/detecciones/dia', [ArcoController::class, 'deteccionesDelDia']);
});
/** Rutas públicas */