--- title: Асинхронное сообщение GetScheduleMessage (Backend) --- # Сценарий 2.3: `GetScheduleMessage` и `GetScheduleMessageHandler` ## Бизнес-цель Получение расписания спроектировано через **Symfony Messenger**, чтобы: - отделить HTTP-слой от интеграции с MIS и работы с кешем; - унифицировать вызовы (тот же message может диспатчиться из других мест); - заложить возможность смены транспорта с `sync` на очередь без переписывания контроллера. По факту конфигурации в `config/packages/messenger.yaml` маршрут для `App\Message\GetScheduleMessage` указывает на транспорт **`sync://`**, то есть обработка **синхронная в том же PHP-процессе**, а не отложенная очередь. ## Точки входа | Тип | Где создаётся сообщение | Класс | | --- | --- | --- | | HTTP | `GET /specialist/schedule` → `SpecialistService::getSchedule` | `App\Message\GetScheduleMessage` | | Messenger | обработчик | `App\MessageHandler\GetScheduleMessageHandler` | CLI для этого сообщения отдельно не зарегистрирован в коде репозитория. ## Пошаговый алгоритм 1. `SpecialistService::getSchedule` создаёт `new GetScheduleMessage($dto->toQueryString(), $dto->onlineMode)` и диспатчит через `MessageBusInterface`. 2. `GetScheduleMessageHandler::__invoke`: - стартует `PerformanceTrackerService`; - **кеш-hit**: `ScheduleCacheService::getCachedSchedule` — при успехе возвращает данные + `_meta.source = cached`; - **кеш-miss**: `InfoclinicaClientService::getSchedule($queryString, $isOnlineMode)` (второй аргумент — в коде хендлера; фактическая сигнатура клиента может отличаться — см. раздел Edge cases); - при успешном ответе MIS — `ScheduleCacheService::saveSchedule` и ответ с `_meta.source = api`; - при `HttpExceptionInterface` — `ScheduleErrorHandlerService::handleHttpException` возвращает массив диагностики; - при прочих исключениях — `handleGeneralException`. ## Mermaid ```mermaid sequenceDiagram participant SC as SpecialistController participant SS as SpecialistService participant BUS as MessageBus participant H as GetScheduleMessageHandler participant CACHE as ScheduleCacheService participant MIS as InfoclinicaClientService participant ERR as ScheduleErrorHandlerService SC->>SS: getSchedule(ScheduleDto) SS->>BUS: dispatch(GetScheduleMessage) BUS->>H: __invoke H->>CACHE: getCachedSchedule alt hit CACHE-->>H: данные слотов else miss H->>MIS: getSchedule (HTTP) alt MIS OK MIS-->>H: JSON нормализован H->>CACHE: saveSchedule else HTTP error MIS-->>ERR: HttpException ERR-->>H: error payload end end H-->>SS: результат массива SS-->>SC: JsonResponse ``` ## Внешние зависимости | Система | Роль | | --- | --- | | PostgreSQL (`Schedule`) | кеш слотов | | Инфоклиника | `GET /api/reservation/intervals?{query}` (см. `InfoclinicaClientService`) | | Логирование | канал `infoclinica-cache`, `infoclinica-error` (через `withName`) | ## Обработка ошибок и edge cases - **Ошибка HTTP MIS** — не исключение наружу; возвращается массив с `status_code`, `response` и др. Контроллер отдаёт его как JSON `200` с этим телом (поведение «мягкой ошибки» — важно для фронта). - **Рассинхрон сигнатур**: в `GetScheduleMessageHandler` вызов `getSchedule` с двумя аргументами должен соответствовать PHP-интерфейсу клиента; при несоответствии будет `ArgumentCountError` на старте (сигнал провести рефакторинг интерфейса `InfoclinicaClientServiceInterface` и реализации). - **sync-транспорт**: нет повторного выполнения из failed queue для этого message в штатной конфигурации (в отличие от реальной async-очереди). ## Ссылки на классы - `apps/backend/src/Message/GetScheduleMessage.php` - `apps/backend/src/MessageHandler/GetScheduleMessageHandler.php` - `apps/backend/src/Service/Specialist/SpecialistService.php` - `apps/backend/config/packages/messenger.yaml` (`routing` для `GetScheduleMessage`) Дополнительно: кеш в БД — [schedule-cache.md](./schedule-cache.md).