---
title: Онлайн-консультация в личном кабинете
---

# Онлайн-консультация (cabinet)

> Как устроена запись на **онлайн-приём** в `apps/cabinet`: маршруты, MIS/Widget API, оплата и видеоконференция. Связано с [расписанием врачей](./doctor-schedule-sync.md).

---

## TL;DR

| Этап | Где | Внешний ресурс |
|------|-----|----------------|
| Список онлайн-врачей | `GET /online-specialists` (нужен login) | PostgreSQL `location_view` (`online_mode=true`) |
| Слоты | `GET /api/interval?onlineMode=1` | `MIS` → `widget.sovamed.ru` |
| Запись | `webSDK.scheduleRecReserve({ onlineType: 1 })` | Infoclinica SDK в браузере |
| Оплата / видео | `/case-history` | `webSDK.loadPaymentView`, `openConference` |

Cron `upload:doctors 1` (backend) обновляет **ближайшие даты** для онлайн-локаций, не сами слоты.

---

## 1. Маршруты

| URL | Auth | Назначение |
|-----|------|------------|
| `/online-specialists` | `ROLE_USER` | Список врачей с онлайн-локациями |
| `/specialists` | публичный | Очный приём (`onlineMode=0`) |
| `/api/interval` | публичный | Прокси расписания в MIS |
| `/case-history` | `ROLE_USER` | Записи, оплата, «Онлайн приём», возврат |
| `/refund` | `ROLE_USER` | Форма возврата |

Код: [`SpecialistController.php`](../../apps/cabinet/src/Controller/SpecialistController.php), [`PublicAPIController.php`](../../apps/cabinet/src/Controller/PublicAPIController.php), [`SecurityController.php`](../../apps/cabinet/src/Controller/SecurityController.php).

---

## 2. Поток данных

```mermaid
sequenceDiagram
    participant User as Пользователь ЛК
    participant Cab as cabinet
    participant MIS as widget.sovamed.ru
    participant SDK as webSDK браузер

    User->>Cab: GET /online-specialists
    Cab->>Cab: location_view online_mode=1
    User->>Cab: GET /api/interval onlineMode=1
    Cab->>MIS: /api/reservation/schedule + /intervals
    MIS-->>Cab: intervalsData
    User->>SDK: scheduleRecReserve onlineType=1
    User->>Cab: GET /case-history
    Cab->>SDK: records, payment, conference
```

---

## 3. Отличия online vs offline

| | Offline | Online |
|---|---------|--------|
| Список | `/specialists` | `/online-specialists` + login |
| `onlineMode` в API | `0` | `1` |
| Анонимная запись | да (`/api/anonymous-reserve`) | **нет** |
| Согласия | PD | PD + оферта + ИДС |
| После записи | `#doctor-success` | `#online` + предупреждение об оплате 5 мин |
| Оплата / видео | — | `/case-history` |

---

## 4. Типичные причины поломки

### 4.1. Пустой список `/online-specialists`

- Нет строк в `location_view` с `online_mode = true` (cron `upload:doctors 1` + `update_location()`).
- Врач не привязан к MIS (`dcode`, `department`, `filial`).

**Проверка:** SQL `SELECT * FROM location_view WHERE online_mode = true;`

### 4.2. Нет слотов на карточке

- MIS недоступен (`MIS` env, `widget.sovamed.ru`).
- Неверный `filial`/`department` для онлайн-режима.
- `GET /api/interval?onlineMode=1` возвращает 5xx.

**Проверка:** `curl "https://widget.sovamed.ru/specialists/departments"` и `/api/interval` с prod.

### 4.3. Запись создаётся как очная

- Баг нормализации `onlineMode` в JS/PHP (исправлено: `OnlineMode`, `onlineMode.js`).
- В `scheduleRecReserve` уходит `onlineType: 0` вместо `1`.

### 4.4. Фильтр на онлайн-странице уводит на `/specialists`

- Форма поиска post'ила на `specialist_index` (исправлено → `specialist_online_index`).

### 4.5. Оплата / видео не работает

- Не загружен SDK (`loader.js` → `widget.sovamed.ru/.../sdk.build.min.js`).
- Запись не оплачена в течение 5 мин (MIS отменяет).
- `webSDK.openConference` — вне окна времени приёма.

### 4.6. Авторизация внутри iframe (`#iframeProtocol`) — не должна появляться

**Симптом:** пользователь уже в ЛК, но в модальном окне «Онлайн приём» или «Оплата» внутри iframe просят войти снова.

**Причина:** две независимые сессии:

| Сессия | Где хранится | За что отвечает |
|--------|--------------|-----------------|
| Symfony (`ROLE_USER`) | cookie `cabinet.sovamed.ru` | `/case-history`, `/online-specialists` |
| MIS / webSDK | cookie `widget.sovamed.ru` + скрытый iframe `/sdk` | `openConference`, `loadPaymentView`, `scheduleRecReserve` |

Symfony может быть активна, а MIS — нет (истёк timeout, другой браузер, блокировка third-party cookies).

**Где iframe:**

| Действие | Метод SDK | Элемент |
|----------|-----------|---------|
| «Онлайн приём» | `openConference()` | `#iframeProtocol` — URL видеоконференции |
| Вход через Госуслуги | `loadLoginView()` | `#iframeProtocol` — **ожидаемо** показывает login |
| Оплата | `loadPaymentView()` | виджет ЮKassa в `#popup-body` (не login-iframe) |

**Исправление в коде (cabinet):**

- `assets/components/misSession.js` — проверка `webSDK.data.user.authenticated` / `isLoggedIn()` перед оплатой и конференцией.
- `caseHistory_controller.js` — `openConference` **без** `container` (DOM-элемент ломал SDK `eval(container)` и пропускал Guest URL); iframe переносится в popup через `mountConferenceInPopup()`.
- При протухшей MIS-сессии — popup «Войти снова» → `/logout`, а не форма login внутри iframe.

**Проверка в браузере (prod):**

```javascript
// на /case-history, до клика «Онлайн приём»
window.webSDK?.data?.user?.authenticated   // должно быть true
```

```bash
# Symfony-сессия (smoke-скрипт)
curl -b cookies.txt https://cabinet.sovamed.ru/api/userInfo
# → {"data":"<uid>"}  — это НЕ гарантия MIS-сессии
```

**Smoke:**

```bash
make local-online-smoke
# шаги 9–10: UI-маркеры case-history, /api/userInfo, доступность MIS SDK
```

### 4.7. MIS / Infoclinica

Env: `MIS=https://widget.sovamed.ru` в [`apps/cabinet/.env`](../../apps/cabinet/.env).

Публичный DNS: CNAME `production.infoclinica.ru` → `217.74.42.159` (не путать с внутренними IP вроде `10.34.23.239`).

---

## 5. Тесты

### Shell smoke (Docker local)

```bash
make local-up
make local-online-smoke
```

Скрипт: [`scripts/online-consultation-smoke.sh`](../../scripts/online-consultation-smoke.sh)

### Pytest e2e

```bash
./scripts/run-online-consultation-pytest.sh
```

Каталог: [`tests/e2e/online_consultation/`](../../tests/e2e/online_consultation/)

### PHPUnit (cabinet)

```bash
make cabinet-test
# или
docker exec php82-local php bin/phpunit tests/Unit tests/Controller
```

- `tests/Unit/Support/OnlineModeTest.php` — нормализация `0/1/true/false`
- `tests/Controller/OnlineSpecialistsControllerTest.php` — auth на `/online-specialists`

---

## 6. Чеклист диагностики на prod

1. Пользователь залогинен? `/online-specialists` без login → redirect.
2. Есть онлайн-локации в БД? `location_view.online_mode = true`.
3. `docker exec php84 php bin/console upload:doctors 1` — без ошибок?
4. `/api/interval?...&onlineMode=1` — HTTP 200 и `intervalsData`?
5. В браузере загружен `webSDK`? Console → нет 404 на `sdk.build.min.js`.
6. `/case-history` — запись с `onlineType`, статус оплаты?
7. **До «Онлайн приём»:** `window.webSDK.data.user.authenticated === true`? Если `false` — MIS-сессия протухла (см. §4.6).
8. Iframe `#iframeProtocol`: `src` — URL конференции, а не `/login`?

---

## 7. Ключевые файлы

| Файл | Роль |
|------|------|
| `src/Controller/SpecialistController.php` | `/online-specialists` |
| `src/Controller/PublicAPIController.php` | `/api/interval` |
| `src/Repository/SpecialistViewRepository.php` | фильтр `onlineMode` |
| `src/Support/OnlineMode.php` | нормализация флага |
| `assets/components/record.js` | запись, `onlineType` |
| `assets/components/misSession.js` | проверка MIS-сессии, mount iframe конференции |
| `assets/controllers/caseHistory_controller.js` | оплата, конференция |
| `assets/components/loader.js` | загрузка SDK |
| `templates/specialist/_item.html.twig` | карточка врача |
