### **Техническое задание №4: Модуль публикации по расписанию** **Цель:** Реализовать автоматизированный механизм, который будет публиковать согласованный контент в социальные сети в точно запланированное время. Также необходимо предоставить возможность ручной, принудительной публикации для экстренных случаев. --- ## 1\. Интеграция с API социальных сетей Необходимо создать сервисы-клиенты для взаимодействия с API каждого поддерживаемого канала. ### `TelegramApiClient.java` - **Задача:** Инкапсулировать логику отправки сообщений в Telegram. - **Методы:** - `TelegramPostResponse postMessage(String apiToken, String chatId, String text, String imagePath)`: Принимает токен, ID чата и контент, отправляет пост в Telegram. Возвращает объект с `externalId` и `url` опубликованного поста. - **Реализация:** Использовать библиотеку, например, `java-telegram-bot-api`, или прямые HTTP-запросы к Telegram Bot API. _(По аналогии создаются клиенты для других соцсетей, например, `VkApiClient.java`)_ --- ## 2\. Сервисный слой (Business Logic) ### `PublishingService.java` Основной сервис, управляющий процессом публикации. - **Зависимости:** `ContentQueueRepository`, `MessageRepository`, `ChannelRepository`, а также все созданные API-клиенты (`TelegramApiClient` и др.). - **Ключевые методы:** - `void findAndPublishScheduledContent()`: Основной метод, запускаемый по расписанию. - `void publishContentById(UUID contentId)`: Метод для принудительной публикации по ID. #### 2.1. Логика автоматической публикации (`findAndPublishScheduledContent`) Этот метод должен быть аннотирован `@Scheduled` для регулярного запуска (например, каждую минуту). - **Алгоритм работы:** 1. Найти в `ContentQueueRepository` все записи, у которых: - `status` равен `ContentStatus.APPROVED`. - `scheduledAt` меньше или равен текущему времени (`ZonedDateTime.now()`). 2. Для каждой найденной записи (`ContentQueue`): - Получить связанный с ней канал (`Channel`) и его токен (`apiKeyRef`). - Получить текст и ассеты из полей `postDraft` и `assetsRefs`. - Вызвать соответствующий API-клиент (например, `telegramApiClient.postMessage(...)`). - **В случае успеха:** - Создать новую запись в таблице `Message`, сохранив `externalId` и `url` из ответа API. - Обновить статус записи в `ContentQueue` на `PUBLISHED`. - **В случае ошибки:** - Обновить статус записи в `ContentQueue` на `FAILED`. - Записать детальную информацию об ошибке в логи. #### 2.2. Логика ручной публикации (`publishContentById`) - **Алгоритм работы:** 1. Найти запись в `ContentQueue` по `contentId`. Если не найдена — ошибка `ResourceNotFoundException`. 2. Выполнить ту же логику публикации, что и в шаге 2.1, но для одной конкретной записи. 3. Этот метод не зависит от статуса и времени, он должен публиковать пост немедленно. --- ## 3\. API Endpoint (REST Controller) ### `PublishingController.java` Новый контроллер для управления процессом публикации вручную. - **`POST /api/smm/publishing/post/{contentId}`** - **Описание:** Принудительно опубликовать пост из очереди контента по его ID. - **Параметры:** `contentId` (UUID) - ID записи из `ContentQueue`. - **Успешный ответ (200 OK):** ```json { "success": true, "message": "Пост успешно опубликован", "data": { "messageId": "...", // UUID из таблицы Message "externalUrl": "https://t.me/channel/12345" } } ``` - **Ответ при ошибке (404 Not Found):** Если контент с таким `contentId` не найден. - **Ответ при ошибке (502 Bad Gateway):** Если API соцсети вернуло ошибку при публикации. --- ## 4\. Конфигурация планировщика В главном классе приложения или в отдельном конфигурационном классе необходимо включить поддержку планировщика с помощью аннотации `@EnableScheduling`. Метод `findAndPublishScheduledContent` в `PublishingService` должен быть аннотирован: ```java @Scheduled(cron = "0 * * * * *") // Запускать каждую минуту public void findAndPublishScheduledContent() { // ... логика ... } ``` --- ## 5\. Обработка ошибок и повторные попытки - **Ошибки API:** `PublishingService` должен корректно обрабатывать исключения от API-клиентов. - **Повторные попытки (Retry):** Рекомендуется добавить механизм повторных попыток для постов со статусом `FAILED`. Например, можно добавить аннотацию `@Retryable` (из Spring Retry) на метод отправки в API-клиенте или создать отдельный `Scheduled` метод, который будет пытаться повторно опубликовать "упавшие" посты несколько раз с интервалом. --- ## Критерии выполнения - Созданы API-клиенты для взаимодействия как минимум с одной соцсетью (Telegram). - Реализован `PublishingService` с логикой автоматической и ручной публикации. - Метод автоматической публикации успешно запускается по расписанию, находит и публикует согласованный контент. - После успешной публикации в таблице `Message` создается запись, а статус в `ContentQueue` меняется на `PUBLISHED`. - В случае ошибки публикации статус в `ContentQueue` меняется на `FAILED`. - Новый эндпоинт `POST /api/smm/publishing/post/{contentId}` корректно работает и позволяет публиковать посты вручную. - Реализована базовая обработка ошибок от API соцсетей.