Перейти к содержанию

Входящие сообщения (HL7 FHIR)

[ТЗ] Обмен обязан соответствовать HL7 FHIR — приказ МЗ РБ № 1001 от 08.10.2018. ПО подлежит регистрации в Регистре ИС, ИР и ПО, соответствующего требованиям HL7 FHIR, поэтому конформность проверяема, а не декларативна.

[ОП] Пять типов входящих сообщений. 3H слушает порт; сторона СМП инициирует обмен (ADR-1).

Обзор пяти типов

Файл Тип Когда приходит Ответ 3H Идемпотентность
transport_request.json Запрос на возможность госпитализации До принятия решения о направлении Синхронный содержательный ответ: принять / отказать / принять с условием По message_uid
transport_notification.json Извещение о направлении пациента в это УЗ После решения бригады везти сюда 202 Accepted По message_uid
transport_health_status.json Изменение состояния пациента в пути Многократно во время транспортировки 202 Accepted По message_uid; наблюдения дедуплицируются по (encounter, code, effectiveDateTime)
transport_cancel.json Извещение об отмене транспортировки Пациент не будет доставлен 202 Accepted По message_uid
transport_complete.json Извещение о доставке Машина прибыла, пациент передан 202 Accepted По message_uid
stateDiagram-v2
  [*] --> Запрошено: transport_request
  Запрошено --> Направлено: transport_notification
  [*] --> Направлено: transport_notification<br/>(без предварительного запроса)
  Направлено --> ВПути: transport_health_status
  ВПути --> ВПути: transport_health_status
  ВПути --> Доставлено: transport_complete
  Направлено --> Доставлено: transport_complete
  Направлено --> Отменено: transport_cancel
  ВПути --> Отменено: transport_cancel
  Запрошено --> Отклонено: отказ в госпитализации
  Доставлено --> [*]
  Отменено --> [*]
  Отклонено --> [*]

transport_notification может прийти без transport_request

Бригада не обязана спрашивать о наличии мест — при жизнеугрожающем состоянии пациента везут в ближайшее профильное УЗ. Система обязана принимать transport_notification как первое сообщение по эпизоду. Реализация, требующая предварительного transport_request, будет отвергать самые тяжёлые случаи.

Транспорт и безопасность

[ПР]

Параметр Значение
Протокол HTTPS, TLS 1.2+
Аутентификация mTLS (клиентский сертификат вендора) + IP allowlist
Целостность тела Подпись (HMAC или JWS) — при поддержке вендором
Порт Выделенный, конфигурируемый на инсталляцию УЗ
Content-Type application/fhir+json
Корреляция Заголовок X-Correlation-Id + Bundle.identifier
Таймаут ответа 5 с для transport_request, 2 с для остальных

Требования по криптографической защите — приказ ОАЦ № 66; средства защиты — только сертифицированные по приказу ОАЦ № 77. См. «Безопасность».

Эндпоинты

[ПР] Основной путь — стандартная FHIR-операция; типизированные пути оставлены для отладки и для вендоров, которым проще фиксированный маршрут.

Метод Путь Назначение
POST /fhir/$process-message Основной приём: тип определяется из MessageHeader.event
POST /api/v1/smp/transport-request Явный маршрут — запрос мест
POST /api/v1/smp/transport-notification Явный маршрут — направление
POST /api/v1/smp/transport-health-status Явный маршрут — состояние в пути
POST /api/v1/smp/transport-cancel Явный маршрут — отмена
POST /api/v1/smp/transport-complete Явный маршрут — доставка
GET /fhir/metadata CapabilityStatement — обязателен для FHIR-конформности

CapabilityStatement — не факультатив

GET /fhir/metadata требуется спецификацией FHIR и будет проверяться при регистрации в Регистре HL7 FHIR-совместимого ПО. Реализовать его нужно с самого начала, а не перед подачей документов.


1. transport_request — запрос на возможность госпитализации

Назначение. Бригада или диспетчер спрашивает, может ли УЗ принять пациента с таким профилем.

Минимальный состав: MessageHeader, Patient (пол, возраст), Encounter (повод, приоритет), при наличии — Condition (предварительный диагноз).

Ответ 3H — синхронный, Bundle типа message с MessageHeader.response:

Решение Смысл
accepted Место есть, профиль подходит — везите
rejected Принять не можем; в MessageHeader.response.details — причина
conditional Принять можем с оговоркой (например, только после освобождения места; ожидание N минут)
Ответ (фрагмент)
{
  "resourceType": "Bundle",
  "type": "message",
  "entry": [{
    "resource": {
      "resourceType": "MessageHeader",
      "eventCoding": { "code": "transport-request-response" },
      "response": {
        "identifier": "req-2026-0714-0915-001",
        "code": "ok",
        "details": { "display": "accepted: профиль кардиология, место есть" }
      }
    }
  }]
}

Хранение: таблица bed_requests (группа данных 1). Сохраняются и запрос, и решение, и его обоснование — иначе разбор спорных случаев невозможен.

Решение о госпитализации — не автоматическое

[?] Кто принимает решение — система по числу свободных коек или оператор вручную — операционными требованиями не определено. Проектное предположение: система предлагает решение по данным о занятости, оператор подтверждает; при отсутствии реакции в течение таймаута отдаётся conditional. Требует подтверждения заказчика — см. R-6.


2. transport_notification — извещение о направлении

Назначение. Пациента везут в это УЗ. Центральное сообщение: с него начинается работа приёмного отделения.

Состав: MessageHeader, Patient, Encounter, Device (машина СМП), Practitioner (бригада), набор Observation (состояние на момент начала транспортировки), Condition, AllergyIntolerance, при наличии — RelatedPerson.

Покрывает большую часть набора данных СМП — пп. 1–28, 34, 40.

Действия 3H:

  1. Регистрация направления → transport_notifications (группа данных 2).
  2. Создание/сопоставление пациента в каталоге.
  3. Сохранение исходного состояния как первого набора наблюдений.
  4. Появление карточки на центральном пульте и в списке направляющихся (FR-T-10).
  5. Запуск опроса координат по номеру машины (FR-T-6).
  6. Расчёт рекомендации по цвету — как подсказка оператору, не как решение.

Ответ: 202 Accepted.


3. transport_health_status — изменение состояния в пути

Назначение. Пул сообщений с обновлением жизненных показателей во время транспортировки (FR-T-5).

Состав: MessageHeader, ссылка на Encounter, набор Observation с effectiveDateTime.

Особенности обработки:

  • Приходит многократно; объём заранее неизвестен — это главный драйвер роста БД.
  • Наблюдения не перезаписывают предыдущие: хранится вся история, потому что важна динамика (ухудшение при стабильных абсолютных значениях — сигнал).
  • Дедупликация по тройке (encounter, code, effectiveDateTime) — повторная доставка того же измерения не создаёт дубль.
  • Каждое обновление пересчитывает рекомендацию цвета и, при ухудшении, подсвечивает карточку на пульте.

Хранение: vital_observations (группа данных 3), партиционирование по effective_at.

Ухудшение в пути обязано быть заметным

Смысл этого сообщения — дать приёмнику увидеть, что пациент, выехавший жёлтым, стал красным. Если интерфейс показывает только последнее значение без указания на изменение, канал бесполезен. Требование: карточка на пульте отображает тренд и явно сигнализирует о переходе через пороги правил. См. «Логику ТРИАЖ».


4. transport_cancel — отмена транспортировки

Назначение. Пациент не будет доставлен: отказ от госпитализации, смерть на месте, перенаправление в другое УЗ, безрезультатный выезд.

Состав: MessageHeader, ссылка на Encounter, Encounter.status = cancelled, причина; при отказе — Consent; при смерти — Patient.deceased[x].

Действия 3H:

  1. Перевод направления в состояние «отменено» с сохранением причины.
  2. Снятие карточки с борда направляющихся.
  3. Остановка опроса координат.
  4. Если бригада встречи уже собиралась — уведомление МИС об отмене.
  5. Освобождение зарезервированного места, если резервирование выполнялось.

Пункт 4 легко упустить

Отмена после ручного запроса на сбор бригады (FR-T-11) означает, что собранная бригада специалистов ждёт пациента, которого не будет. Уведомление об отмене обязано доходить до МИС, иначе система создаёт ровно ту проблему, которую призвана решать. Отдельная задача в эпике E5.


5. transport_complete — извещение о доставке

Назначение. Машина прибыла, пациент передан приёмному отделению (FR-T-13).

Состав: MessageHeader, Encounter.period.end, финальные Observation (состояние после транспортировки), Encounter.participant (кто передал и кто принял), километраж.

Действия 3H:

  1. Отсечка времени доставки — фиксация момента; это основа КПЭ «среднее время ожидания».
  2. Перевод направления в «доставлено» → deliveries (группа данных 5).
  3. Создание обращения (appeals) или связывание с уже созданным.
  4. Постановка в очередь на сортировку → triage_queue (группа данных 6).
  5. Инициация заполнения документов.

Доставка ≠ регистрация в приёмнике

Сообщение от вендора фиксирует факт прибытия по данным СМП. Фактическая передача пациента подтверждается в приёмном отделении действием медработника. Оба момента фиксируются раздельно: расхождение между ними — управленчески значимая метрика (сколько машина ждала приёма). См. «Жизненный цикл».

Обработка ошибок

[ПР] Ответы вендору при неуспехе:

Код Ситуация Тело
400 Невалидный FHIR OperationOutcome с указанием проблемного элемента
401 / 403 Не пройдена аутентификация mTLS, IP вне allowlist OperationOutcome
409 Конфликт состояния (например, complete по отменённому эпизоду) OperationOutcome
422 FHIR валиден, но бизнес-правила нарушены OperationOutcome
429 Превышен лимит частоты Retry-After
503 Внутренняя недоступность Retry-After; вендор обязан повторить

Никогда не возвращать 200 на непринятое сообщение

Если сообщение не сохранено, ответ обязан быть ошибкой — иначе вендор считает доставку успешной и не повторит, а данные о едущем пациенте потеряны безвозвратно. Правило: 2xx возвращается только после успешной записи в БД, а не после постановки в очередь в памяти.

Пример полного Bundle — в «Примере Bundle».