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\Http\Controllers\Controller;
use App\Models\Arco; use App\Models\Arco;
use App\Services\VehicleService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Notsoweb\ApiResponse\Enums\ApiResponse; use Notsoweb\ApiResponse\Enums\ApiResponse;
@ -12,6 +13,9 @@
*/ */
class ArcoController extends Controller class ArcoController extends Controller
{ {
public function __construct(
private VehicleService $vehicleService
) {}
/** /**
* Listar todos los arcos * Listar todos los arcos
* GET /api/arcos * GET /api/arcos
@ -159,4 +163,60 @@ public function toggleEstado(int $id)
'arco' => $arco '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( $resultado = $this->vehicleService->procesarDeteccion(
$validated['fast_id'], $validated['fast_id'],
$arco->ip_address, $arco->ip_address,
$validated['timestamp'] ?? null $validated['antena'] ?? null
); );
if (!$resultado['success']) { if (!$resultado['success']) {

View File

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

View File

@ -17,7 +17,7 @@ public function __construct(
/** /**
* Procesar detección de vehículo por fast_id * 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}"; $key = "vehiculo:robado:{$fastId}";
@ -31,13 +31,13 @@ public function procesarDeteccion(string $fastId, string $arcoIp): array
if ($enRedis) { if ($enRedis) {
// Ya está marcado como robado, verificar si sigue así // Ya está marcado como robado, verificar si sigue así
$resultado = $this->verificarVehiculoRobado($fastId, json_decode($enRedis, true)); $resultado = $this->verificarVehiculoRobado($fastId, json_decode($enRedis, true));
$this->registrarDeteccion($fastId, $resultado, $arcoId); $this->registrarDeteccion($fastId, $resultado, $arcoId, $antena);
return $resultado; return $resultado;
} }
// No está en Redis, consultar servicios // No está en Redis, consultar servicios
$resultado = $this->consultarNuevoVehiculo($fastId); $resultado = $this->consultarNuevoVehiculo($fastId);
$this->registrarDeteccion($fastId, $resultado, $arcoId); $this->registrarDeteccion($fastId, $resultado, $arcoId, $antena);
return $resultado; 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) * 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) * 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'])) { if (!$resultado['success'] || !isset($resultado['vehiculo'])) {
return; return;
@ -224,6 +240,7 @@ private function registrarDeteccion(string $fastId, array $resultado, ?int $arco
// Registrar en MySQL // Registrar en MySQL
Detection::create([ Detection::create([
'arco_id' => $arcoId, 'arco_id' => $arcoId,
'antena' => $antena,
'fast_id' => $fastId, 'fast_id' => $fastId,
'vin' => $vehiculo['vin'] ?? null, 'vin' => $vehiculo['vin'] ?? null,
'placa' => $vehiculo['placa'] ?? 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) // 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} * Formato de key: deteccion:dia:YYYY-MM-DD:{fastId}:{timestamp}
* Expira automáticamente a las 23:59:59 del día * 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'); $fecha = now()->format('Y-m-d');
$timestamp = now()->timestamp; $timestamp = now()->timestamp;
@ -256,6 +273,7 @@ private function registrarDeteccionDelDia(string $fastId, array $vehiculo, ?int
'modelo' => $vehiculo['modelo'] ?? null, 'modelo' => $vehiculo['modelo'] ?? null,
'color' => $vehiculo['color'] ?? null, 'color' => $vehiculo['color'] ?? null,
'arco_id' => $arcoId, 'arco_id' => $arcoId,
'antena' => $antena,
'estado' => $resultado['estado'] ?? 'LIBRE', 'estado' => $resultado['estado'] ?? 'LIBRE',
'tiene_reporte_robo' => $resultado['tiene_reporte_robo'] ?? false, 'tiene_reporte_robo' => $resultado['tiene_reporte_robo'] ?? false,
'fecha_deteccion' => now()->toIso8601String(), '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 * Seeder Producción
* *
* @author Moisés Cortés C. <moises.cortes@notsoweb.com> * @author Moisés Cortés C. <moises.cortes@notsoweb.com>
* *
* @version 1.0.0 * @version 1.0.0
*/ */
class DatabaseSeeder extends Seeder class DatabaseSeeder extends Seeder
@ -22,5 +22,6 @@ public function run(): void
$this->call(RoleSeeder::class); $this->call(RoleSeeder::class);
$this->call(UserSeeder::class); $this->call(UserSeeder::class);
$this->call(SettingSeeder::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(RoleSeeder::class);
$this->call(UserSeeder::class); $this->call(UserSeeder::class);
$this->call(SettingSeeder::class); $this->call(SettingSeeder::class);
$this->call(ArcoSeeder::class);
} }
} }

View File

@ -9,9 +9,9 @@
/** /**
* Usuarios predeterminados del sistema * Usuarios predeterminados del sistema
* *
* @author Moisés Cortés C. <moises.cortes@notsoweb.com> * @author Moisés Cortés C. <moises.cortes@notsoweb.com>
* *
* @version 1.0.0 * @version 1.0.0
*/ */
class UserSeeder extends Seeder class UserSeeder extends Seeder
@ -21,34 +21,25 @@ class UserSeeder extends Seeder
*/ */
public function run(): void public function run(): void
{ {
$developer = UserSecureSupport::create('developer@notsoweb.com'); $developer = UserSecureSupport::create('developer@golsystems.com');
User::create([ User::create([
'name' => 'Developer', 'name' => 'admin',
'paternal' => 'Notsoweb', 'paternal' => 'golsystems',
'maternal' => 'Software', 'maternal' => 'desarrollo',
'email' => $developer->email, 'email' => $developer->email,
'password' => $developer->hash, 'password' => $developer->hash,
])->assignRole(__('developer')); ])->assignRole(__('developer'));
$admin = UserSecureSupport::create('admin@notsoweb.com'); $admin = UserSecureSupport::create('admin@golsystems.com');
User::create([ User::create([
'name' => 'Admin', 'name' => 'Admin',
'paternal' => 'Notsoweb', 'paternal' => 'Golsystems',
'maternal' => 'Software', 'maternal' => 'Desarrollo',
'email' => $admin->email, 'email' => $admin->email,
'password' => $admin->hash, 'password' => $admin->hash,
])->assignRole(__('admin')); ])->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 // Rutas de Arcos RFID
Route::resource('/arcos', ArcoController::class); Route::resource('/arcos', ArcoController::class);
Route::patch('/arcos/{id}/toggle-estado', [ArcoController::class, 'toggleEstado']); Route::patch('/arcos/{id}/toggle-estado', [ArcoController::class, 'toggleEstado']);
Route::get('/arcos/{id}/detecciones/dia', [ArcoController::class, 'deteccionesDelDia']);
}); });
/** Rutas públicas */ /** Rutas públicas */