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

GET /api/messenger/chats/{chatId}/conferences — История конференций сделки

Постраничная лента групповых звонков в чате сделки: когда, кем, сколько длилась, велась ли запись. Read-only проекция ConferenceSessionEntity. Дом полей и статусы — conference-session.md.

Назначение

Отдать участнику Deal-чата историю видеовстреч по сделке для панели «Звонки/встречи» (список сессий, длительности, пометка «с записью»). Owning-сервис — messenger, feature group conferences, сценарий «групповой звонок по сделке».

Metadata

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

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

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

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/messenger/chats/{chatId}/conferences
Success status200
Idempotency headerне применяется (безопасный GET)
Correlationсквозной trace через traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ChatIdrouteGuidДа!= Empty; существующий Deal-чат, актор — активный участник
ActorUserIdidentity claimsGuidДаиз JWT; активное членство в ChatId
PagingqueryPagingQueryНетTake 1..100 (default 20), Offset >= 0 (default 0)защита от unbounded
StatusqueryConferenceStatus?Нетenum строкой; unknown → ошибка валидации
RecordedOnlyquerybool?Нетtrue → только конференции с записью
From / ToqueryDateTimeOffset?Нетокно по StartedAt; From <= To

Фильтры комбинируются AND. Сортировка фиксирована StartedAt desc, tiebreak Id desc.

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

Постраничный ответ: элементы страницы + метаданные пагинации. Элемент — компактная проекция конференции без ростера и без дескриптора входа.

ПолеТипОбязательностьИсточникОписание
ItemsConferenceEntry[]Дапроекция ConferenceSessionEntityСтраница истории.
TotalintДаcountВсего по фильтру.
TakeintДаPaging.TakeРазмер страницы.
OffsetintДаPaging.OffsetСмещение.

Вложенная модель ConferenceEntry

ПолеТипОбязательностьИсточникMaskingОписание
ConferenceIdGuidДаConferenceSessionEntity.IdИдентификатор конференции.
ChatIdGuidДа.ChatIdDeal-чат.
MediaTypeCallMediaTypeДа.MediaTypeAudio/Video.
StatusConferenceStatusДа.StatusСтатус сессии.
Titlestring?Нет.TitleНазвание встречи.
StartedByUserIdGuidДа.StartedByUserIdИнициатор.
StartedAtDateTimeOffsetДа.StartedAtМомент старта (ключ сортировки).
EndedAtDateTimeOffset?Нет.EndedAtnull для активной.
DurationSecondsint?Нет.DurationSecondsnull до завершения.
ParticipantCountintДа.PeakParticipantCountПиковое число участников (для карточки истории).
RecordingStatusRecordingStatus?НетCallRecordingEntity.StatusСтатус записи; null — без записи.
RecordingIdGuid?НетCallRecordingEntity.IdИдентификатор записи (если велась).

Алгоритм

  1. Контекст и доступ. Операцию выполняет активный участник Deal-чата (User); ActorUserId — из JWT; требуется право messenger.conferences.use.
  2. Проверка входа. ChatId непустой; актор — активный участник чата (INV-CHAT-4), иначе MSG_CHAT_ACCESS_DENIED. Paging.Take ∈ 1..100, Offset >= 0; Status — известное значение enum (unknown → ошибка валидации); From <= To.
  3. Выборка. Конференции чата ChatId с фильтрами (AND): Status; RecordedOnly → только с привязанной записью; окно StartedAt ∈ [From, To]. Сортировка StartedAt desc, tiebreak Id desc; индекс по ChatId.
  4. Маппинг. Каждая сессия проецируется в ConferenceEntry (без ростера и без Room); ParticipantCount = PeakParticipantCount; RecordingStatus/RecordingId — из привязанной записи (если есть).
  5. Результат. Постраничный ConferenceListModel → HTTP 200; пустая история — 200 с Items = [] (не 404).

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
VALIDATION_FAILED400ChatId = Empty, Take вне 1..100, неизвестный Status, From > To«Проверьте параметры запроса»no retry
MSG_CHAT_ACCESS_DENIED403актор не активный участник Deal-чата«Нет доступа к чату»no retry

Несуществующий/чужой ChatId без членства скрывается как MSG_CHAT_ACCESS_DENIED (не раскрываем наличие чата).

Acceptance Criteria

  • История (happy). Дано: актор — активный участник Deal-чата с несколькими прошедшими конференциями. Когда запрашивает без фильтров. Тогда 200 со страницей StartedAt desc (tiebreak Id desc), Total по чату, каждая запись с DurationSeconds/ParticipantCount/пометкой записи.
  • Фильтр «только с записью». Дано: часть конференций велась с записью. Когда RecordedOnly = true. Тогда в выдаче только конференции с привязанной записью (RecordingId != null).
  • Окно по дате. Дано: From/To заданы. Когда фильтрует. Тогда возвращаются только сессии с StartedAt ∈ [From, To]; при From > To400 VALIDATION_FAILED.
  • Пустая история. Дано: в чате не было конференций. Когда запрашивает. Тогда 200 c Items = [], Total = 0 (не 404).
  • Не участник / чужой чат. Дано: актор не активный участник чата. Когда запрашивает. Тогда 403 MSG_CHAT_ACCESS_DENIED (наличие чужого чата не раскрывается).
  • Некорректная пагинация/enum. Дано: Take вне 1..100 или неизвестный Status. Когда запрашивает. Тогда 400 VALIDATION_FAILED.

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

  • Новые optional поля/фильтры — additive; удаление/переименование/смена типа поля или значения enum — breaking (migration + rollout). Unknown enum во входе — ошибка валидации (не игнор).
  • Logs: conference log listed (ChatId, набор фильтров, Total); metrics: messenger_conference_list_total{result}, latency p95 (< 500 мс, SLO списков); traces — сквозной trace (traceparent); dashboard pin-messenger.

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

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

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