Импорт результатов голосовых кампаний в аналитическую базу
Задача
Результаты голосовых кампаний находятся в API внешней платформы, а анализируются в отдельной базе. Оператору нужен понятный способ перенести звонки выбранной кампании и увидеть, сколько записей обработано. Выдавать ему токен платформы, доступ к базе или права администратора WordPress для этого не требуется.
Кампания может содержать много звонков. Импорт должен проходить несколькими запросами, показывать ход работы и сохранять уже перенесённые данные при сбое на следующей странице. Повторный запуск нужен без ручной очистки таблицы.
Роль и объём
Реализовал два плагина для разных установок WordPress. Первый отвечает за рабочий экран оператора и управление задачей. Второй принимает защищённые серверные запросы, читает API кампаний, преобразует ответы и записывает звонки во внешнюю MySQL или SQL Server. История запусков хранится отдельно в WordPress на серверной стороне; сами результаты звонков туда не копируются.
Архитектура
Оператор → экран импорта в WordPress
├── Отдельная роль и право на импорт
├── AJAX с проверкой nonce и права
├── Запуск и последовательные запросы порций
└── Прогресс, heartbeat и отмена
│
▼
Серверный запрос с ключом в заголовке
│
▼
REST API второго WordPress
├── Проверка ключа и идентификатора кампании
├── Состояние задачи в transient
├── Журнал задач в базе WordPress
└── Обработка порции
├── Чтение страниц API платформы
├── Приведение полей к схеме аналитики
├── Проверка идентификатора звонка
└── Запись через адаптер MySQL / SQL Server
│
▼
Внешняя аналитическая базаГраница между плагинами принципиальна: браузер обращается только к своему WordPress. Ключ межсерверного API, токен платформы и параметры внешней базы используются на сервере.
Реализация
При старте сервер проверяет формат идентификатора и наличие кампании во внешнем API, получает её название, создаёт запись в журнале и проверяет соединение с аналитической базой. Текущее состояние импорта хранится в transient: номер страницы, статус и счётчики. Интерфейс последовательно запрашивает порции; одна порция по умолчанию обрабатывает до пяти страниц по 50 звонков.
Преобразование данных вынесено из записи в базу. Для каждого звонка извлекаются идентификатор, пользователь, название кампании, достижение цели, длительность, статус, даты и количество реплик. Значения приводятся к ожидаемым типам; запись без идентификатора не передаётся в репозиторий. MySQL и SQL Server имеют отдельные реализации подключения и параметризованных запросов, поэтому логика импорта не зависит от синтаксиса выбранной базы.
Перед вставкой репозиторий ищет идентификатор звонка. Уже существующие строки пропускаются, что позволяет повторить импорт после частичного сбоя. Это именно дозагрузка отсутствующих звонков: изменения ранее записанных результатов код не синхронизирует. Если API или соединение с базой даёт ошибку, задача получает статус failed; уже вставленные строки остаются, а новый запуск снова проходит кампанию и пропускает найденные идентификаторы.
Для оператора создана отдельная роль с правом на импорт. AJAX-обработчики проверяют это право и nonce. На серверном REST ключ из заголовка сравнивается через hash_equals. Ограничение меню и переходов в админке упрощает рабочее место, а доступ к действиям определяется проверками прав. Браузер не получает учётные данные внешних систем.
Сбои и диагностика
Оператор видит статус и прогресс задачи, в том числе количество прочитанных, добавленных и пропущенных строк. Серверный журнал хранит кампанию, текущую страницу, счётчики и сообщение об ошибке; в интерфейсе можно открыть историю и детали запуска. Отдельный файловый лог фиксирует ошибки запросов к API и записи в базу. Это помогает отличить недоступность платформы от проблемы с аналитической базой и понять, на каком этапе остановился импорт.
Для временных сетевых сбоев у HTTP-клиентов есть повторные попытки. Открытая вкладка отправляет heartbeat каждые 15 секунд. Оператор может отменить импорт кнопкой; при уходе со страницы браузер отправляет запрос отмены через sendBeacon. Периодическая задача WP-Cron предусмотрена для записей без свежего heartbeat. Эти механизмы дают возможность управлять прерванным запуском, но не равнозначны гарантированной фоновой обработке без открытого интерфейса.
границы решения
Импорт защищён от параллельной обработки одной и той же кампании и появления дублирующихся записей. Проверка существующих звонков и сохранение данных выполняются с учётом конкурентных запусков, а блокировка кампании поддерживается в течение всего процесса импорта.
Повторный запуск импорта безопасен: уже сохранённые звонки не создаются повторно и не изменяются. Дубли и ошибки вставки учитываются раздельно, при этом технические ошибки дополнительно фиксируются в файловом журнале.
Механизм очистки корректно обрабатывает брошенные и зависшие задачи: обновляется журнал импорта и удаляется связанное рабочее состояние. Heartbeat и проверка времени выполняются в единой временной зоне, поэтому состояние задачи в журнале соответствует её фактическому состоянию.
Интервал между запросами клиента синхронизирован с серверным ограничением. Получение короткой порции данных не приводит к преждевременному завершению импорта: процесс продолжается до тех пор, пока источник явно не сообщает об отсутствии следующих данных.
Таким образом, импорт корректно работает при повторных и параллельных запусках, восстанавливается после прерванных задач и не требует ручного вмешательства в штатных сценариях.
Результат
Оператор запускает перенос кампании из одного экрана и видит ход и историю импорта. Доступ к платформе и аналитической базе сосредоточен на серверной стороне, а порционная обработка позволяет работать с кампаниями, которые не помещаются в один PHP-запрос. Повторный запуск дозагружает отсутствующие звонки, а журналы дают материал для разбора сбоев.
Стек
PHP · WordPress · REST API · JavaScript · MySQL · SQL Server · WP-Cron · Transients
Есть похожая задача?
Расскажите о текущей системе и нужных изменениях — обсудим следующий шаг.
Следующий кейс: Сверка изображений каталога с учётной системой