--- title: Расписание врача и кеш слотов (Backend) --- # Сценарий 2.2: Расписание и «кеш» слотов ## Бизнес-цель Пациенту показывают **свободные интервалы** записи к врачу в конкретном филиале и режиме (очный / онлайн). Чтобы не дёргать MIS на каждый запрос, backend хранит **недавно полученное расписание** и отдаёт его при повторных обращениях с теми же параметрами. **Важно для архитектуры:** несмотря на название сервиса, **данные кеша живут в PostgreSQL** (таблица сущности `Schedule`), а не в Redis. Redis в этом сценарии **не используется** (см. `ScheduleCacheService`). ## Точки входа | Тип | Метод + URL | Класс | | --- | --- | --- | | HTTP | `GET /specialist/schedule?...` | `SpecialistController::specialistSchedule` | | CLI | `app:schedule:clear-cache` | `ClearScheduleCacheCommand` (очистка старых строк) | Фактический вызов MIS выполняется внутри `GetScheduleMessageHandler` (см. [schedule-messenger.md](./schedule-messenger.md)). ## Параметры запроса расписания Используется `App\Dto\ScheduleDto`: - `st`, `en` — границы времени (целые); - `dcode` — код врача; - `filial` — филиал (в query строка строится как `filialId`); - `onlineMode` — признак онлайн-расписания. `ScheduleDto::toQueryString()` формирует строку для поиска в кеше и запроса к MIS (HTTP query). ## Пошаговый алгоритм HTTP 1. Контроллер наполняет DTO из query, валидирует. 2. `SpecialistService::getSchedule($dto)` диспатчит `GetScheduleMessage` (см. отдельную статью). 3. Хендлер сначала зовёт `ScheduleCacheService::getCachedSchedule($queryString, $isOnlineMode)`. ## Логика `ScheduleCacheService` ### TTL («как долго живёт кеш») Константа **`CACHE_TTL_MINUTES = 5`**. `getCachedSchedule`: - вычисляет порог `createdAt >= now - 5 minutes`; - `ScheduleRepository::findByQueryModeAndTime($queryString, $isOnlineMode, $createdAfter)`; - если строк нет — `null` (промах кеша). То есть **инвалидация по времени**: записи старше 5 минут не считаются валидными для ответа. ### Запись и перезапись `saveSchedule` перед вставкой новых слотов вызывает **`removeByQueryStringAndMode`**: удаляет все записи кеша с тем же `queryString` и `onlineMode`. Это **жёсткое обновление** среза расписания под ключ запроса. Каждый слот — строка `Schedule` с полями врача, отделения, даты, интервала, `queryString`, `onlineMode`, `createdAt` и др. ### Ошибки чтения из БД В `getCachedSchedule` перехват `Exception` → лог `Error reading from cache` → возврат `null` (как промах кеша, дальше пойдут в MIS). ## Инвалидация / уборка | Механизм | Описание | | --- | --- | | По TTL при чтении | старше 5 минут не отдаются | | `saveSchedule` | удаление предыдущих строк с тем же ключом перед insert | | `app:schedule:clear-cache --hours=N` | массовое удаление по `createdAt < now - N hours` через `clearOldCache` | | Опция `--stats` | статистика по таблице без удаления | ## `ScheduleErrorHandlerService` Не часть кеша; вызывается из хендлера при ошибках HTTP-клиента или непойманных исключений: логирование и возврат массива с `status_code`, телом ответа MIS, длительностью и т.д. (см. [schedule-messenger.md](./schedule-messenger.md)). ## Mermaid ```mermaid flowchart TD A[GET /specialist/schedule] --> B[ScheduleDto + validate] B --> C[SpecialistService.getSchedule] C --> H[GetScheduleMessageHandler] H --> D{Есть свежие Schedule строки?} D -->|да ≤5 мин| R[reconstructFromDatabase → ответ cached] D -->|нет| E[HTTP MIS intervals] E --> S[saveSchedule: delete old + insert Schedule rows] S --> R2[ответ api + _meta] ``` ## Внешние зависимости | Система | Роль | | --- | --- | | PostgreSQL | хранение «кеша» `Schedule` | | Инфоклиника (MIS) | источник расписания при промахе | | Redis | **не используется** в этом сценарии по текущему коду | ## Обработка ошибок и edge cases - **БД недоступна при чтении кеша** — лог, ответ пойдёт в MIS; если и MIS недоступна — ошибка из хендлера. - **Пустой ответ MIS** — `normalize` / пустые массивы зависят от клиента (см. `InfoclinicaClientService`). - **Несогласованность `onlineMode` в DTO**: в контроллере в поле может попадать `0|1` из query — при строгой типизации возможны проблемы валидации (наблюдение для джуна при отладке). ## Ссылки на классы - `apps/backend/src/Controller/SpecialistController.php` (`specialistSchedule`) - `apps/backend/src/Dto/ScheduleDto.php` - `apps/backend/src/Service/ScheduleCache/ScheduleCacheService.php` - `apps/backend/src/Repository/ScheduleRepository.php` - `apps/backend/src/Command/ClearScheduleCacheCommand.php` - `apps/backend/src/Service/ErrorHandler/ScheduleErrorHandlerService.php` Связанный сценарий Messenger: [schedule-messenger.md](./schedule-messenger.md).