feat: migrate to VitePress from monorepo docs, add test-contour section

This commit is contained in:
sova-bootstrap
2026-05-28 12:29:31 +03:00
parent e90dfe1bd4
commit e3e438df68
76 changed files with 11998 additions and 60 deletions
@@ -0,0 +1,90 @@
---
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).