fix: deduplicar webhooks duplicados con INSERT IGNORE + UNIQUE index en message_id
- conversations.message_id ahora tiene UNIQUE index (aplicado en BD prod) - saveMessage() usa INSERT IGNORE: si el mismo wamid llega dos veces en paralelo solo una llamada procesa el mensaje y llama al bot - La segunda llamada recibe rowCount=0 y hace continue sin pasar al bot - Elimina race condition que enviaba bienvenida + términos simultáneamente
This commit is contained in:
+17
-2
@@ -368,8 +368,14 @@ class WhatsAppWebhook {
|
|||||||
|
|
||||||
// Filtrar campos según columnas existentes para evitar errores en esquemas antiguos
|
// Filtrar campos según columnas existentes para evitar errores en esquemas antiguos
|
||||||
$saveData = $this->filterColumns('conversations', $saveData);
|
$saveData = $this->filterColumns('conversations', $saveData);
|
||||||
// Guardar mensaje y conservar el id de la conversación para posibles encolamientos
|
// Guardar mensaje con INSERT IGNORE para deduplicar webhooks duplicados.
|
||||||
|
// Si el message_id ya existe (segunda llamada paralela), rowCount=0 y saltamos el bot.
|
||||||
$conversationId = $this->saveMessage($saveData);
|
$conversationId = $this->saveMessage($saveData);
|
||||||
|
if (!$conversationId) {
|
||||||
|
// Mensaje ya procesado por una llamada concurrente, ignorar
|
||||||
|
error_log("[webhook] Duplicate webhook ignorado para message_id={$messageId}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Si el media se encoló (no se pudo descargar), actualizar media_queue con el conversation_id
|
// Si el media se encoló (no se pudo descargar), actualizar media_queue con el conversation_id
|
||||||
if (!empty($mediaUrl) && empty($localFile) && in_array($messageType, ['image', 'audio', 'video', 'document', 'sticker'])) {
|
if (!empty($mediaUrl) && empty($localFile) && in_array($messageType, ['image', 'audio', 'video', 'document', 'sticker'])) {
|
||||||
@@ -469,7 +475,16 @@ class WhatsAppWebhook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function saveMessage($messageData) {
|
private function saveMessage($messageData) {
|
||||||
return $this->db->insert('conversations', $messageData);
|
// INSERT IGNORE: si message_id ya existe (webhook duplicado), no inserta y devuelve 0
|
||||||
|
$cols = implode(', ', array_map(fn($c) => "`{$c}`", array_keys($messageData)));
|
||||||
|
$placeholders = implode(', ', array_map(fn($c) => ":{$c}", array_keys($messageData)));
|
||||||
|
$sql = "INSERT IGNORE INTO `conversations` ({$cols}) VALUES ({$placeholders})";
|
||||||
|
$stmt = $this->db->getConnection()->prepare($sql);
|
||||||
|
$stmt->execute($messageData);
|
||||||
|
if ($stmt->rowCount() === 0) {
|
||||||
|
return 0; // duplicado
|
||||||
|
}
|
||||||
|
return $this->db->getConnection()->lastInsertId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user