fix(consecutivo): contador atómico con LAST_INSERT_ID — elimina race condition
Reemplaza SELECT MAX() (vulnerable a concurrencia) por UPDATE atómico sobre turnero_consecutivos. Dos recepcionistas simultáneas nunca pueden recibir el mismo número de orden. Sin UNIQUE constraint, sin riesgo en datos históricos. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
9f251d57be
commit
0b39e3f0d0
@@ -1,14 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* GET modules/turnero/api/get_consecutivo.php
|
* GET modules/turnero/api/get_consecutivo.php
|
||||||
* Devuelve el siguiente número de orden del día.
|
* Devuelve el siguiente número de orden del día — atómico, sin race condition.
|
||||||
*
|
*
|
||||||
* Parámetros:
|
* Parámetros:
|
||||||
* tipo string 'F' = Físico/turnero (default) | 'D' = Domicilio
|
* tipo string 'F' = Físico/turnero (default) | 'D' = Domicilio
|
||||||
*
|
*
|
||||||
* Formato resultante:
|
* Formato: F-YYYYMMDD-NNN | D-YYYYMMDD-NNN
|
||||||
* F-YYYYMMDD-NNN para tipo=F
|
*
|
||||||
* D-YYYYMMDD-NNN para tipo=D
|
* Usa turnero_consecutivos con UPDATE ... LAST_INSERT_ID() para garantizar
|
||||||
|
* que dos peticiones simultáneas nunca retornen el mismo número.
|
||||||
*/
|
*/
|
||||||
require_once __DIR__ . '/_helpers.php';
|
require_once __DIR__ . '/_helpers.php';
|
||||||
requireTurnero();
|
requireTurnero();
|
||||||
@@ -18,23 +19,17 @@ $tipo = strtoupper(trim($_GET['tipo'] ?? 'F'));
|
|||||||
if (!in_array($tipo, ['F', 'D'], true)) $tipo = 'F';
|
if (!in_array($tipo, ['F', 'D'], true)) $tipo = 'F';
|
||||||
|
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
$prefix = $tipo . '-' . date('Ymd') . '-';
|
$prefijo = $tipo . '-' . date('Ymd'); // e.g. 'F-20260727'
|
||||||
|
$prefix = $prefijo . '-';
|
||||||
|
|
||||||
if ($tipo === 'D') {
|
// Asegurar que exista la fila del día (INSERT IGNORE no toca si ya existe)
|
||||||
$stmt = $pdo->prepare(
|
$pdo->prepare("INSERT IGNORE INTO turnero_consecutivos (prefijo, ultimo) VALUES (?, 0)")
|
||||||
"SELECT MAX(CAST(SUBSTRING_INDEX(numero_orden, '-', -1) AS UNSIGNED)) AS ultimo
|
->execute([$prefijo]);
|
||||||
FROM lab_domicilios
|
|
||||||
WHERE numero_orden LIKE ?"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$stmt = $pdo->prepare(
|
|
||||||
"SELECT MAX(CAST(SUBSTRING_INDEX(numero_orden, '-', -1) AS UNSIGNED)) AS ultimo
|
|
||||||
FROM turnero_solicitudes
|
|
||||||
WHERE numero_orden LIKE ?"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$stmt->execute([$prefix . '%']);
|
|
||||||
$ultimo = (int)($stmt->fetch(PDO::FETCH_ASSOC)['ultimo'] ?? 0);
|
|
||||||
|
|
||||||
$siguiente = $prefix . str_pad($ultimo + 1, 3, '0', STR_PAD_LEFT);
|
// Incremento atómico: UPDATE toma lock de fila → dos hilos nunca leen el mismo valor
|
||||||
jsonOk(['consecutivo' => $siguiente]);
|
$pdo->prepare("UPDATE turnero_consecutivos SET ultimo = LAST_INSERT_ID(ultimo + 1) WHERE prefijo = ?")
|
||||||
|
->execute([$prefijo]);
|
||||||
|
|
||||||
|
$siguiente = (int)$pdo->query("SELECT LAST_INSERT_ID()")->fetchColumn();
|
||||||
|
|
||||||
|
jsonOk(['consecutivo' => $prefix . str_pad($siguiente, 3, '0', STR_PAD_LEFT)]);
|
||||||
|
|||||||
Reference in New Issue
Block a user