Сбор научных публикаций и организация PDF для исследовательской команды
Задача
Исследователи собирали публикации по нескольким темам и наборам поисковых запросов. Для каждой найденной работы приходилось отдельно проверять наличие PDF, скачивать файл и помещать его в нужную папку общей базы материалов. Часть публикаций доступна только как описание; у части PDF-ссылка есть, но возвращает страницу или ошибку. Ручной проход быстро превращается в повторяющуюся работу, в которой легко потерять источник, перепутать тему или повторно загрузить документ.
Нужен был один рабочий экран: запустить поиск по подготовленным запросам, увидеть найденные статьи и состояние их PDF, а затем отправить выбранные материалы в общую структуру. Если автоматический поиск файла не помог, исследователь должен иметь возможность добавить PDF вручную к той же записи.
Роль и объём
Разработал отдельный плагин WordPress: экран исследовательской работы, настройки наборов запросов, интеграции с двумя источниками публикаций и внешним хранилищем материалов, фоновые задачи, таблицу результатов и действия по загрузке файлов. Результаты поиска хранятся в отдельной таблице WordPress, а опубликованные материалы раскладываются по коллекциям и вложенным группам во внешнем сервисе.
Отдельно спроектировал эксплуатационный контур: разграничение доступа, состояния фонового запуска, защита от запоздавших задач, безопасную загрузку внешних файлов и диагностику без записи API-ключей.
Ограничения
- Поиск по нескольким наборам запросов и источникам может занять больше времени одного запроса из браузера. Обход нужно вынести в очередь и показывать его ход на странице.
- Наличие ссылки с расширением
.pdfне гарантирует, что сервер отдаст PDF. Перед учётом успешной загрузки требуется проверить ответ и содержимое файла. - Одна статья может встретиться по разным ключевым словам и в разных источниках. Перед отправкой файла в общую коллекцию нужно показать возможный дубль.
- Внешние поисковые API и хранилище могут отказать независимо друг от друга. Ошибка одного этапа не должна превращаться в ложную отметку «файл отправлен».
Архитектура
Исследователь → экран WordPress
├── Настройки: группы и ключевые слова
├── Запуск поиска и прогресс по группам
└── Результаты: PDF найден / недоступен / отправлен
│
▼
Action Scheduler
├── Идентификатор запуска и контроль актуальности задачи
├── Состояния pending / completed / failed
├── Тайм-аут зависшего запуска и безопасный новый старт
└── Обход групп запросов по очереди
│
▼
Сервисы поиска публикаций
├── Адаптер каталога публикаций
└── Адаптер поисковой выдачи статей
├── Метаданные публикаций
├── Поиск PDF-ссылок
└── Безопасная загрузка и проверка PDF
│
▼
Отдельная таблица результатов MySQL
├── Тема, группа, запрос, источник, заголовок
└── Ссылка и состояние PDF, состояние отправки
│
▼
Выбранные строки / ручной PDF → внешняя база материалов
├── Поиск или создание коллекции и группы
├── Проверка одноимённого файла в группе
└── Отправка файла и обновление статуса строкиПоиск и размещение материалов разделены. Очередь собирает кандидатов и фиксирует результат в таблице; решение, какие PDF включить в общую базу, остаётся за исследователем.
Реализация
Наборы ключевых слов задаются в настройках. При запуске выбранная тема подставляется в шаблоны запросов, создаётся UUID прохода и первая задача Action Scheduler. После обработки группы задача ставит в очередь следующую. Перед изменением прогресса каждый обработчик повторно сверяет идентификатор и состояние запуска. Запоздавшая задача от предыдущего прохода поэтому не может отметить группу в новом исследовании.
Запуск имеет явные состояния pending, completed и failed, время последней активности и стабильный код ошибки. Неудача внешнего поискового API, некорректный JSON, ошибка постановки следующей задачи или исключение обработчика переводят проход в failed. Уже найденные материалы сохраняются и показываются исследователю. Если активность надолго прекратилась, проверка состояния закрывает зависший проход по тайм-ауту и разрешает новый запуск.
На странице прогресс рассчитывается по числу пройденных групп; найденные записи выбираются из таблицы по идентификатору запуска. Хранение результатов отдельно от обычных записей WordPress позволяет сохранить источник, поисковую фразу, исходный ответ API и два независимых статуса: PDF и отправки.
Один адаптер получает статьи из каталога научных публикаций, ищет связанные полнотекстовые материалы и извлекает PDF-ссылку. Второй разбирает поисковую выдачу, в которой PDF может быть указан как отдельный ресурс. Оба передают ссылку общему менеджеру файлов. Для внешнего адреса используется безопасный HTTP-клиент WordPress; перед сохранением проверяются код ответа, MIME-тип, сигнатура %PDF и предел в 50 МБ. Если файл не удалось получить, запись о публикации всё равно остаётся в результатах с соответствующим состоянием.
При отправке выбранной строки плагин находит или создаёт коллекцию по теме исследования и вложенную группу по набору запросов. Перед загрузкой ищет файл с таким же именем именно в этой группе. Если совпадение найдено, интерфейс просит исследователя подтвердить повторную отправку. Массовое действие обрабатывает выбранные строки последовательно; недоступный автоматически PDF можно загрузить вручную. Для ручного файла проверяются ошибка приёма, размер, расширение и MIME-тип. Статус отправки в таблице меняется после успешного ответа внешнего сервиса.
Сбои и диагностика
На рабочем экране видны состояние запуска, прогресс по группам, наличие PDF у каждой записи и факт отправки во внешнее хранилище. При сбое очередь получает состояние failed, интерфейс прекращает опрос, показывает понятное сообщение и сохраняет доступ к уже собранным результатам. Исследователь может устранить причину и начать новый проход без ручного изменения опций WordPress.
Диагностика отправляется в серверный PHP-журнал с категорией подсистемы. Перед записью известные секреты заменяются маркером, параметры авторизации в строках очищаются, а URL внешнего запроса журналируется без query string. Ответ внешнего сервиса целиком в лог не копируется. Так можно отличить транспортную ошибку, некорректный JSON, неподходящий файл и сбой фоновой задачи, не создавая доступные через сайт файлы с ключами интеграций.
При неудачной отправке документа строка не помечается как отправленная. Исследователь может повторить действие или приложить PDF вручную. Успешной группа считается после завершения запросов к источникам; HTTP-ошибка и повреждённый JSON не превращаются в пустой успешный результат.
границы решения
Запуск поиска, опрос состояния, поиск темы и операции с файлами доступны только авторизованному администратору. AJAX-обработчики проверяют nonce и право manage_options. API-ключи находятся в серверной конфигурации и не передаются браузеру.
Плагин не обходит ограничения доступа издателей: если полнотекстовый PDF нельзя получить по прямой публичной ссылке, публикация остаётся в списке, а файл добавляется вручную при наличии законного доступа. Проверка дубля сравнивает имя файла внутри нужной группы и оставляет окончательное решение исследователю. Семантически одинаковые статьи с разными именами требуют отдельной библиографической дедупликации.
Отметка отправки сохраняется после успешного ответа внешнего сервиса. WordPress и внешняя база не участвуют в общей транзакции, поэтому при крайне редком разрыве соединения после принятия файла сверка дубля остаётся необходимой частью повторной попытки.
Результат
Исследователь запускает поиск из одного экрана, получает статьи из нескольких источников с указанием доступности PDF и переносит выбранные документы в понятную структуру общей базы. Долгая работа выполняется в контролируемой очереди, сбой получает явное состояние, частичный результат сохраняется, а зависший проход не блокирует систему навсегда. Неудачная автоматическая загрузка не удаляет найденную публикацию: для неё остаётся ручной путь.
Решение сокращает ручной маршрут от поисковой фразы до разобранной библиотеки и при этом сохраняет контроль исследователя над составом материалов.
Стек
PHP · WordPress · MySQL · Action Scheduler · AJAX · JavaScript · ACF · REST API · PDF
Есть похожая задача?
Расскажите о текущей системе и нужных изменениях — обсудим следующий шаг.
Следующий кейс: Безопасный переход из персональной ссылки в клиентские сервисы