--- title: Синхронизация врачей и отзывов (Backend) --- # Сценарий 4.1: Синхронизация врачей и отзывов (Infoclinica + Bitrix) ## Бизнес-цель Актуализировать справочники backend из внешних систем: - **Список врачей из Инфоклиники** загружается и складывается в PostgreSQL (сущность **`Idoctor`** — staging/интеграционная модель). - **Нормализация признаков `Specialist`** из Bitrix-related данных (команда **`bitrix-update-doctors`** — очистка `dcodes`). - **Отзывы** подтягиваются **из MySQL Bitrix** через `BitrixService` и сохраняются как `Review`, связанные с `Specialist`. ## Точки входа | Тип | Имя команды Symfony | Класс | | --- | --- | --- | | CLI | `upload:doctors` | `UploadDoctorsCommand` | | CLI | `bitrix-update-doctors` | `BitrixUpdateDoctorsCommand` | | CLI | `bitrix-update-reviews` | `BitrixUpdateReviewsCommand` | Все три предназначены для **cron** или ручного запуска в контейнере `php84`. ## Сценарий A — `upload:doctors` (Инфоклиника → `Idoctor`) ### Алгоритм 1. Читает активные отделения из PostgreSQL (`Department`, опция `--department` для одного `did`). 2. Для каждого отделения в цикле вызывает HTTP **через клиент Инфоклиники**: `GET /specialists/doctors?departments={did}&onlineMode={0|1}&firstrow=&lastrow=` чанками (`CHUNK_SIZE` 300). 3. Для каждого врача определяется ключ `"{dcode}_{departmentId}_{onlineMode}"`, подгружаются существующие `Idoctor` тем же ключом. 4. `updateDoctorEntity` обновляет поля (`dcode`, `name`, `department`, `filial`, `nearestDate`, `onlineMode`), `persist`, пакетный `flush` каждые `BATCH_SIZE` (150). 5. Между отделениями — `sleep(1)`; между чанками — `usleep(200000)`. ### «Конфликты» Явного SQL `ON CONFLICT` нет: используется **ORM upsert-паттерн** — найти сущность по составному ключу в PHP или создать `new Idoctor()`, затем `persist`. ## Сценарий B — `bitrix-update-doctors` (PostgreSQL `Specialist`) ### Алгоритм 1. Загружает **все** `Specialist` из БД. 2. Нормализует строку `dcodes`: для каждого врача фильтрует коды длиной ≥ 7, отбрасывает `'0'`, пустые наборы превращает в `null`. 3. `flush` один раз в конце. 4. Обращение к `BitrixService` для `kodoper` **закомментировано** в текущей версии файла. **Это не загрузка врачей из Bitrix**, а офлайн-очистка данных в уже существующей таблице `specialist`. ## Сценарий C — `bitrix-update-reviews` (MySQL Bitrix → PostgreSQL `Review`) ### Алгоритм 1. Постранично обходит `Specialist` батчами по 5 записей. 2. Для каждого врача `BitrixService::getReviews($specialist->getId())`: - читает связанные элементы инфоблоков в **MySQL** (`doctrine.dbal.mysql_connection`); - для каждого отзыва известен `REVIEW_ID`. 3. В PostgreSQL ищется `Review` с тем же `externalId`; если нет — `new Review()` + `setExternalId`. 4. Поля текста, рейтинга, автора, даты, активности заполняются из структуры Bitrix (включая «распаковку» сериализованных полей в `getReviews`). 5. Неактивные или без текста — пропуск. 6. `$specialist->addReview($review)`, `flush`; при ошибке драйвера — логирование проблемных UTF-8 последовательностей. ### «Конфликты» Снова **без `ON CONFLICT`**: идемпотентность за счёт поиска по `externalId` перед вставкой. ## Mermaid ```mermaid flowchart LR subgraph MIS["Инфоклиника"] API["GET /specialists/doctors"] end subgraph PG["PostgreSQL"] ID["Idoctor"] SP["Specialist"] RV["Review"] end subgraph BX["Bitrix MySQL"] IB["Инфоблоки отзывов"] end UC["upload:doctors"] --> API API --> ID BD["bitrix-update-doctors"] --> SP BR["bitrix-update-reviews"] --> IB BR --> RV RV --> SP ``` Узлы `UC` / `BD` / `BR` — это команды `upload:doctors`, `bitrix-update-doctors`, `bitrix-update-reviews`. ## Внешние зависимости | Система | Сценарий | | --- | --- | | Инфоклиника HTTP | `upload:doctors` | | PostgreSQL | все три команды | | Bitrix MySQL | `bitrix-update-reviews` (и потенциально расширения `BitrixService`) | ## Обработка ошибок и edge cases - **Сетевые ошибки загрузки врачей** — warning в консоли, переход к следующему чанку/отделению. - **Проблемные отзывы** — `DriverException` логируется с дампом полей. - **`BitrixService` зависит от `regionId` в некоторых методах** — убедитесь, что выставление региона покрыто в вашем окружении при расширении команды. ## Ссылки на классы - `apps/backend/src/Command/UploadDoctorsCommand.php` - `apps/backend/src/Command/BitrixUpdateDoctorsCommand.php` - `apps/backend/src/Command/BitrixUpdateReviewsCommand.php` - `apps/backend/src/Service/Bitrix/BitrixService.php` - `apps/backend/src/Entity/Idoctor.php`, `Specialist.php`, `Review.php` См. [backend-ddd.md](../backend-ddd.md) и [data-model.md](../../data-model.md).