Files
docs/apps/backend-scenarios/schedule-cache.md
T

114 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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).