114 lines
6.3 KiB
Markdown
114 lines
6.3 KiB
Markdown
---
|
||
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).
|