delete test
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Servicio de Gestión de Estados de Conversación
|
||||
* Controla el flujo y contexto de las conversaciones
|
||||
*/
|
||||
|
||||
class ConversationStateService
|
||||
{
|
||||
private $db;
|
||||
|
||||
// Estados posibles
|
||||
const STATE_INITIAL = 'initial';
|
||||
const STATE_MAIN_MENU = 'main_menu';
|
||||
const STATE_SUBMENU_EXAMS = 'submenu_exams';
|
||||
const STATE_REQUESTING_HOME_SERVICE = 'requesting_home_service';
|
||||
const STATE_AWAITING_ORDER_PHOTO = 'awaiting_order_photo';
|
||||
const STATE_AWAITING_PATIENT_DATA = 'awaiting_patient_data';
|
||||
const STATE_AWAITING_ID_PHOTO = 'awaiting_id_photo';
|
||||
const STATE_VALIDATING_DATA = 'validating_data';
|
||||
const STATE_QUOTATION = 'quotation';
|
||||
const STATE_AWAITING_CONFIRMATION = 'awaiting_confirmation';
|
||||
const STATE_SCHEDULED = 'scheduled';
|
||||
const STATE_REQUESTING_RESULTS_HELP = 'requesting_results_help';
|
||||
const STATE_WITH_ADVISOR = 'with_advisor';
|
||||
const STATE_COMPLETED = 'completed';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->db = Database::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene el estado actual de un usuario
|
||||
* @param string $phoneNumber
|
||||
* @return array|null
|
||||
*/
|
||||
public function getState(string $phoneNumber): ?array
|
||||
{
|
||||
$stmt = $this->db->prepare("
|
||||
SELECT * FROM user_states
|
||||
WHERE phone_number = ?
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$phoneNumber]);
|
||||
$state = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($state && $state['state_data']) {
|
||||
$state['state_data'] = json_decode($state['state_data'], true);
|
||||
}
|
||||
|
||||
return $state ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establece un nuevo estado para el usuario
|
||||
* @param string $phoneNumber
|
||||
* @param string $state
|
||||
* @param array $data Datos adicionales del estado
|
||||
* @return bool
|
||||
*/
|
||||
public function setState(string $phoneNumber, string $state, array $data = []): bool
|
||||
{
|
||||
$stmt = $this->db->prepare("
|
||||
INSERT INTO user_states (phone_number, state, state_data, created_at, updated_at)
|
||||
VALUES (?, ?, ?, NOW(), NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
state = VALUES(state),
|
||||
state_data = VALUES(state_data),
|
||||
updated_at = NOW()
|
||||
");
|
||||
|
||||
$stateDataJson = !empty($data) ? json_encode($data, JSON_UNESCAPED_UNICODE) : null;
|
||||
|
||||
return $stmt->execute([$phoneNumber, $state, $stateDataJson]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualiza los datos del estado actual sin cambiar el estado
|
||||
* @param string $phoneNumber
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function updateStateData(string $phoneNumber, array $data): bool
|
||||
{
|
||||
$currentState = $this->getState($phoneNumber);
|
||||
|
||||
if (!$currentState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$existingData = $currentState['state_data'] ?? [];
|
||||
$mergedData = array_merge($existingData, $data);
|
||||
|
||||
$stmt = $this->db->prepare("
|
||||
UPDATE user_states
|
||||
SET state_data = ?, updated_at = NOW()
|
||||
WHERE phone_number = ?
|
||||
");
|
||||
|
||||
return $stmt->execute([
|
||||
json_encode($mergedData, JSON_UNESCAPED_UNICODE),
|
||||
$phoneNumber
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene datos específicos del estado
|
||||
* @param string $phoneNumber
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getStateData(string $phoneNumber, string $key)
|
||||
{
|
||||
$state = $this->getState($phoneNumber);
|
||||
|
||||
if (!$state || !isset($state['state_data'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $state['state_data'][$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia el estado del usuario
|
||||
* @param string $phoneNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function clearState(string $phoneNumber): bool
|
||||
{
|
||||
$stmt = $this->db->prepare("
|
||||
DELETE FROM user_states
|
||||
WHERE phone_number = ?
|
||||
");
|
||||
|
||||
return $stmt->execute([$phoneNumber]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica si el usuario está en un flujo específico
|
||||
* @param string $phoneNumber
|
||||
* @param string $state
|
||||
* @return bool
|
||||
*/
|
||||
public function isInState(string $phoneNumber, string $state): bool
|
||||
{
|
||||
$currentState = $this->getState($phoneNumber);
|
||||
return $currentState && $currentState['state'] === $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene el contador de intentos
|
||||
* @param string $phoneNumber
|
||||
* @return int
|
||||
*/
|
||||
public function getAttemptCount(string $phoneNumber): int
|
||||
{
|
||||
return (int)($this->getStateData($phoneNumber, 'attempt_count') ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Incrementa el contador de intentos
|
||||
* @param string $phoneNumber
|
||||
* @return int Nuevo número de intentos
|
||||
*/
|
||||
public function incrementAttempts(string $phoneNumber): int
|
||||
{
|
||||
$attempts = $this->getAttemptCount($phoneNumber) + 1;
|
||||
$this->updateStateData($phoneNumber, ['attempt_count' => $attempts]);
|
||||
return $attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resetea el contador de intentos
|
||||
* @param string $phoneNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function resetAttempts(string $phoneNumber): bool
|
||||
{
|
||||
return $this->updateStateData($phoneNumber, ['attempt_count' => 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guarda datos temporales del agendamiento
|
||||
* @param string $phoneNumber
|
||||
* @param array $appointmentData
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAppointmentData(string $phoneNumber, array $appointmentData): bool
|
||||
{
|
||||
return $this->updateStateData($phoneNumber, [
|
||||
'appointment' => $appointmentData,
|
||||
'appointment_started_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene datos del agendamiento en curso
|
||||
* @param string $phoneNumber
|
||||
* @return array|null
|
||||
*/
|
||||
public function getAppointmentData(string $phoneNumber): ?array
|
||||
{
|
||||
return $this->getStateData($phoneNumber, 'appointment');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica si el estado ha expirado (más de 30 minutos de inactividad)
|
||||
* @param string $phoneNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function isStateExpired(string $phoneNumber): bool
|
||||
{
|
||||
$state = $this->getState($phoneNumber);
|
||||
|
||||
if (!$state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$updatedAt = strtotime($state['updated_at']);
|
||||
$now = time();
|
||||
$diffMinutes = ($now - $updatedAt) / 60;
|
||||
|
||||
// Expirar después de 30 minutos
|
||||
return $diffMinutes > 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia estados expirados de todos los usuarios
|
||||
* @return int Número de estados eliminados
|
||||
*/
|
||||
public function cleanExpiredStates(): int
|
||||
{
|
||||
$stmt = $this->db->prepare("
|
||||
DELETE FROM user_states
|
||||
WHERE updated_at < DATE_SUB(NOW(), INTERVAL 30 MINUTE)
|
||||
AND state NOT IN (?, ?)
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
self::STATE_SCHEDULED,
|
||||
self::STATE_WITH_ADVISOR
|
||||
]);
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene el menú actual del usuario
|
||||
* @param string $phoneNumber
|
||||
* @return int|null ID del menú
|
||||
*/
|
||||
public function getCurrentMenuId(string $phoneNumber): ?int
|
||||
{
|
||||
return $this->getStateData($phoneNumber, 'current_menu_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establece el menú actual
|
||||
* @param string $phoneNumber
|
||||
* @param int $menuId
|
||||
* @return bool
|
||||
*/
|
||||
public function setCurrentMenu(string $phoneNumber, int $menuId): bool
|
||||
{
|
||||
return $this->updateStateData($phoneNumber, ['current_menu_id' => $menuId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marca la conversación para transferir a asesor
|
||||
* @param string $phoneNumber
|
||||
* @param string $reason
|
||||
* @return bool
|
||||
*/
|
||||
public function markForTransfer(string $phoneNumber, string $reason = ''): bool
|
||||
{
|
||||
return $this->setState($phoneNumber, self::STATE_WITH_ADVISOR, [
|
||||
'transfer_reason' => $reason,
|
||||
'transferred_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica si el usuario está esperando asesor
|
||||
* @param string $phoneNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function isWaitingForAdvisor(string $phoneNumber): bool
|
||||
{
|
||||
return $this->isInState($phoneNumber, self::STATE_WITH_ADVISOR);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user