Перейти к основному содержимому

POST /api/messenger/conferences/{conferenceId}/join — Присоединиться к конференции

Активный участник Deal-чата входит в идущую конференцию. Заводится/реактивируется строка ростера (call-participant.md) в состоянии Joined, актору выдаётся свежий дескриптор входа в медиа-комнату. Дом полей и инварианты (INV-CONF-*, INV-CPART-*) — conference-session.md.

Назначение

Позволить участнику чата сделки войти в уже поднятую конференцию (или переподключиться после разрыва). Операция идемпотентна по паре (конференция, пользователь): повторный вход реактивирует ту же строку ростера и выдаёт новый токен доступа, а не создаёт дубль. На выходе — карточка конференции плюс дескриптор входа Room. Owning-сервис — messenger, feature group conferences.

Metadata

ПолеЗначение
Сервис/контекстmessenger
API areaconferences
Feature groupconferences
Статусdraft
Документdocs/06-services/messenger/api/conferences/join-conference.md

Актор и доступ

ПроверкаЗначение
Actor typeUser — активный участник Deal-чата конференции
Auth policy / Permissionspermission messenger.conferences.use
Scope (RBAC)не применяется — доступ по членству в чате
Record-levelактивный ParticipantEntity чата ChatId конференции (INV-CHAT-4)
Tenant isolationне применяется (кросс-tenant); срез — по членству в Deal-чате
Auditupdate ростера: строка участника (Joined, JoinedAt); реактивация при реконнекте

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/messenger/conferences/{conferenceId}/join
Success status200 (OK)
Idempotency headerне применяется — повтор реактивирует ту же строку ростера (INV-CPART-1), выдаётся свежий токен
Correlationсквозной trace через traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ConferenceIdrouteGuidДа!= Empty; существующая конференция в Active (шаги 3–4)
ActorUserIdidentity claimsGuidДаиз JWT; активный участник ChatId конференции

Тела запроса нет.

Модель ответа ConferenceJoinModel

Обёртка «карточка + вход»: карточка конференции (дом полей — start-conference.md, ConferenceModel) и обязательный дескриптор входа в медиа-комнату (MediaJoinModel, дом — messenger-hub.md).

ПолеТипОбязательностьИсточникMaskingОписание
ConferenceConferenceModelДапроекция ConferenceSessionEntityКарточка с ростером; Conference.Room продублирован в Room.
RoomMediaJoinModelДавычисляетсяSensitive: AccessToken/TURN-креды не логируютсяСвежий дескриптор входа (RoomName, AccessToken, SignalingUrl, MediaDomain, IceServers, ExpiresAt).

Предусловия и постусловия

Предусловия. Актор авторизован и имеет messenger.conferences.use; флаг CONFERENCE_CALLS_ENABLED включён. Актор — активный участник Deal-чата конференции (INV-CHAT-4). Конференция существует и в статусе Active (INV-CONF-4).

Постусловия. Строка ростера (ConferenceId, ActorUserId) существует в State = Joined с новым JoinedAt (создана при первом входе либо реактивирована при реконнекте, INV-CPART-1 — дубля нет); LeftAt/LeaveReason сброшены. Пересчитаны ActiveParticipantCount/PeakParticipantCount. Актору выдан эфемерный Room (AccessToken не персистится, INV-CONF-7). В канал chat:{ChatId} отправлен MediaParticipant(Joined). Kafka-события не публикуются. Роль модерации входом не назначается (остаётся Speaker, если ранее не был модератором).

Алгоритм

  1. Контекст и доступ. Операцию выполняет участник Deal-чата (User); ActorUserId — из JWT; требуется право messenger.conferences.use. Конференции включены флагом CONFERENCE_CALLS_ENABLED, иначе — MSG_CONFERENCES_DISABLED.
  2. Проверка входа. ConferenceId непустой; иначе ошибка валидации.
  3. Загрузка сессии и доступ. Конференция по ConferenceId; нет → MSG_CONFERENCE_NOT_FOUND. Актор — активный участник чата ChatId конференции (INV-CHAT-4), иначе MSG_CHAT_ACCESS_DENIED.
  4. Состояние. Конференция должна быть Active (INV-CONF-4); Provisioning/Ended/FailedMSG_CONFERENCE_NOT_ACTIVEProvisioning комната ещё не подтверждена, в терминале — вход невозможен).
  5. Вход в ростер. Строка (ConferenceId, ActorUserId) создаётся или реактивируется (INV-CPART-1): State = Joined, новый JoinedAt, LeftAt/LeaveReason сбрасываются; орг-роль Role — снимок роли актора в чате. Пересчитывается ActiveParticipantCount/PeakParticipantCount. Роль модерации не назначается (остаётся Speaker, если ранее не был модератором).
  6. Приглашение/присутствие. В том же изменении фиксируется намерение push MediaParticipant(Joined) в chat:{ChatId} (persist-then-push). Дескриптор входа Room выдаётся в ответе; AccessToken короткоживущий, в БД не хранится (INV-CONF-7).
  7. Результат. ConferenceJoinModel (карточка + Room) → HTTP 200 OK.

Диаграмма

Побочные эффекты

ТипДетали
Integration eventsnone
Realtime push (hub)MediaParticipant(Joined)chat:{ChatId}
Токены доступаRoom.AccessToken/TURN-креды короткоживущие, в БД/realtime_outbox не хранятся, не логируются
Cache / projections / searchпересчёт ActiveParticipantCount/PeakParticipantCount; ростер — partial ix_call_participants_active
Notificationsнет

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
VALIDATION_FAILED400ConferenceId = Empty«Проверьте параметры запроса»no retry
MSG_CONFERENCE_NOT_FOUND404конференции нет«Конференция не найдена»no retry
MSG_CHAT_ACCESS_DENIED403актор не активный участник Deal-чата«Нет доступа к конференции»no retry
MSG_CONFERENCE_NOT_ACTIVE409статус не Active (Provisioning/Ended/Failed)«Конференция ещё не готова или уже завершена»no retry (перечитать)
MSG_CONFERENCES_DISABLED503CONFERENCE_CALLS_ENABLED = false«Конференции временно недоступны»retry позже

Acceptance Criteria

  • Первый вход (happy). Дано: актор — активный участник Deal-чата, конференция Active, актор ещё не в ростере. Когда присоединяется. Тогда 200 с ConferenceJoinModel, заведена строка ростера Joined, ActiveParticipantCount увеличен, выдан Room; участники получают MediaParticipant(Joined).
  • Реконнект (идемпотентность). Дано: у актора есть строка ростера в Left после разрыва. Когда присоединяется снова. Тогда та же строка реактивируется (Left → Joined, новый JoinedAt, INV-CPART-1), дубля не создаётся, выдан свежий Room.
  • Повторный join активного участника. Дано: актор уже Joined. Когда присоединяется повторно. Тогда 200 со свежим токеном, ActiveParticipantCount не задваивается.
  • Конференция ещё не готова / завершена. Дано: статус Provisioning, Ended или Failed. Когда актор присоединяется. Тогда 409 MSG_CONFERENCE_NOT_ACTIVEProvisioning комната не подтверждена; в терминале вход невозможен).
  • Не участник чата. Дано: актор не активный участник Deal-чата. Когда присоединяется. Тогда 403 MSG_CHAT_ACCESS_DENIED.
  • Нет конференции. Дано: ConferenceId не существует. Тогда 404 MSG_CONFERENCE_NOT_FOUND.
  • Флаг выключен. Дано: CONFERENCE_CALLS_ENABLED = false. Тогда 503 MSG_CONFERENCES_DISABLED.

Совместимость и наблюдаемость

  • Новые optional поля ответа — additive; смена маршрута/семантики статуса — breaking. Kafka-контракта нет.
  • Logs: conference join requested/joined/not-active (ConferenceId, ActorUserId; токены — никогда в логи); metrics: messenger_conference_join_total{result}, latency p95; traces — сквозной trace (traceparent); dashboard pin-messenger.

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы