Messenger Service
Назначение
messenger — собственный мини-мессенджер платформы PIN (ADR-0055). Владеет
личными чатами 1:1, групповыми чатами CRM-процессов (чат лида — до сделки; чат сделки),
типизированными сообщениями (текст / вложение /
событие-карточка), контактами (заявка → подтверждение), чёрным списком, поиском пользователей по телефону,
read-receipts / непрочитанными, а также аудио/видео-звонками, конференциями и демонстрацией экрана поверх
внешнего media-стека (SFU, ADR-0062) с policy-driven записью звонков в file storage. Хранение — PostgreSQL
(агрегаты Chat / Message / Participant / ChatEvent / Contact / Blacklist / CallSession / ConferenceSession /
CallParticipant / CallRecording); медиа идёт клиент ↔ SFU (бэкенд минтит токен доступа и принимает
вебхуки, не в медиа-плоскости); доставка в реальном времени — SignalR-hub (persist-then-push: outbox → hub).
Greenfield. Платформа реализует собственный REST + SignalR-контракт мессенджера с нуля (ADR-0055).
Metadata
| Поле | Значение |
|---|---|
| Статус | draft |
| Owning team | PIN Platform (Comms squad) |
| Domain | Коммуникации / чаты |
| Data classification | Mixed (PII: тексты сообщений, ФИО/телефоны контактов; Sensitive: IP/UA отправителя, скрытая история правок) |
| Runtime criticality | high (realtime UX; чаты сделок — часть воронки денег, но не source of truth сделки) |
Ownership
| Artifact | Owner/Rule |
|---|---|
| PostgreSQL schema | messenger |
| API areas | chats / messages / contacts / blacklist / calls / conferences / recordings / internal (вебхуки media-стека) / admin (арбитраж Службы заботы) + SignalR-hub /hubs/messenger |
| Owned entities | ChatEntity(+ParticipantEntity), MessageEntity (TPH: TextMessage/AttachmentMessage/EventCardMessage; +MessageEditEntity, +EventCardRecipientEntity), ChatEventEntity, ContactEntity, BlacklistEntity, CallSessionEntity, ConferenceSessionEntity(+CallParticipantEntity), CallRecordingEntity |
| Replica entities | MessengerUserEntity (реплика identity: ФИО/телефон для поиска и display контактов) |
| Produced events | pin.messenger.deal-chat-status-changed.v1, pin.messenger.message-posted.v1, pin.messenger.contact-request-created.v1, pin.messenger.contact-request-accepted.v1, pin.messenger.conference-ended.v1, pin.messenger.call-recording-available.v1 |
| Consumed events | pin.crm.deal-created.v1, pin.crm.deal-stage-changed.v1, pin.crm.deal-members-changed.v1, pin.crm.lead-participants-changed.v1, pin.crm.document-requested.v1, pin.crm.booking-paid.v1, pin.crm.booking-expired.v1, pin.identity.users.v1 |
| Search indexes | Elasticsearch-проекция messenger-contacts (ADR-0059; search/contacts.md) — FTS по ФИО/телефону контактов; source of truth — реплика MessengerUserEntity в PostgreSQL; PostgreSQL-FTS (tsvector/GIN) не используется |
| Analytics facts | нет (ClickHouse не используется; simplicity-first ADR-0056; dev-stats — отдельный контекст) |
Ответственность И Границы
- Делает: личные чаты 1:1 (открытие по пользователю / из поиска; авто-создание при первом сообщении);
групповые чаты CRM-процессов — лида (
ChatKind.Lead, до сделки) и сделки (ChatKind.Deal) — с авто-созданием и синхронизацией участников по событиямcrm(единыйParticipantRole, состав — производный отcrm, пользовательских ручек управления составом нет); типизированные сообщения (текст; вложение = ссылка наFileIdв storage, загрузка вне форм; событие-карточка с per-participant видимостью и per-participant текстами); авто-карточки целевых действий («Загрузите документы для бронирования/ДДУ/ДКП», «Прикрепите чек») и информационные («Договор бронирования оплачен», «Отправлено в Росреестр», «Бронь истекла — подтвердите/продлите»); правка/удаление сообщения ≤ 1 мин + хранимая (не показываемая) история правок; захват IP / User-Agent отправителя; read-receipts и счётчики непрочитанных; контакты (глобальный поиск по телефону с маской фамилии → заявка → подтверждение/отклонение → полный профиль обеим сторонам); чёрный список (запрет писать и звонить); 1:1-звонки (лог + приглашение с комнатой/токеном через hub) и групповые конференции в чатах сделок (динамический ростер, модератор, демонстрация экрана) поверх внешнего SFU (ADR-0062); policy-driven запись звонков с выгрузкой в file storage и историей; realtime-доставка через SignalR (persist-then-push). - Не делает: не владеет жизненным циклом сделки/участников (владелец —
crm; messenger — консюмер событий, возвращает готовность чата событием); не хранит бинарные файлы, включая записи звонков (file storage/CDN, толькоFileId+ метаданные); не в медиа-плоскости — медиа (звук/видео/экран) идёт клиент ↔ SFU, бэкенд минтит токен доступа и принимает вебхуки, SDP/ICE/токены не персистятся; не рассылает push/email напрямую (доставка —notificationsкак консюмер наших событий; SignalR — только in-app realtime); не источник справочника пользователей (реплика изidentity). - Source of truth: Chat, Participant, Message (+edit history), ChatEvent, Contact, Blacklist, CallSession, ConferenceSession (+CallParticipant), CallRecording (метаданные; бинарь — file storage).
- Реплики/проекции:
MessengerUserEntity(ФИО/телефон изpin.identity.user-upserted, монотонный version-guard); snapshot названия/участников/проекта сделки — из событийcrmпри провизии/синке чата.
Зависимости
| Зависимость | Тип | Зачем Нужна | Failure mode |
|---|---|---|---|
PostgreSQL (messenger) | db | все агрегаты, source of truth реплики/search-проекции контактов, outbox | недоступность = недоступность сервиса |
| Elasticsearch (search projection) | search | FTS контактов по ФИО/телефону (messenger-contacts, ADR-0059) — read/search-проекция, не source of truth | деградация: поиск контактов недоступен/отстаёт; листинг контактов без search работает (PostgreSQL); rebuild-джоб восстанавливает |
| Kafka | queue | входящие события crm/identity, исходящие (outbox-first) | деградация: события копятся в outbox; чаты сделок отстают (ChatStatus = Provisioning/Failed) |
identity | service/events | JWT (actor/permissions), реплика ФИО/телефона (user-upserted) для контактов/поиска | недоступность = 401/503 на командах; поиск/контакты отстают (eventual) |
crm | producer событий для нас | жизненный цикл чата сделки: создание, синк участников, карточки | чат сделки отстаёт; carousel карточек задержан (eventual) |
| storage/CDN | external | пресайн-ссылки вложений (FileId), скачивание | отправка/просмотр вложений недоступны; текст работает |
file storage (CALL_RECORDINGS) | service | выгрузка и хранение записей звонков (S3), пресайн скачивания; ссылка по FileId | запись не выгружается (recording-finalize-retry повторяет); чат/звонок работают |
| media-стек: SFU (JVB) + jicofo + prosody | external (ADR-0062) | медиа-плоскость звонков/конференций, комнаты, JWT-авторизация; вебхуки admission/presence | звонки/конференции недоступны (CALLS_MEDIA_ENABLED/CONFERENCE_CALLS_ENABLED off = graceful 503); чат/лог работают |
| coturn (TURN/STUN) | external | обход NAT для клиент ↔ SFU | часть клиентов не соединяется по медиа; сигналинг/лог работают |
| jibri (рекордер) | external | серверная запись конференции → finalize → ingest в messenger → file storage | запись не ведётся/не финализируется; звонок работает без записи |
notifications | consumer наших событий | offline push/email о новых сообщениях, заявках в контакты, готовности записи | уведомления вне приложения отстают (in-app realtime работает) |
| Redis (backplane) | cache | SignalR backplane при multi-replica (ADR-0043/SDK) — опционально (на целевой нагрузке 1–10k активных один инстанс без Redis, ADR-0055/0056) | без backplane при multi-replica часть push теряется → клиент перезапрашивает REST при reconnect |
Проекты
src/services/messenger/
Pin.Messenger.Api/ # области chats/messages/contacts/blacklist/calls/conferences/recordings/internal/admin + SignalR-hub /hubs/messenger
Pin.Messenger.Application/ # сценарии/операции чатов, сообщений, контактов, звонков/конференций/записей (доступ по членству, не по tenant); RecordingPolicy; минтинг токена media-комнаты
Pin.Messenger.Domain/ # агрегаты, CommunicationPolicy, RecordingPolicy, инварианты, доменные события
Pin.Messenger.Infrastructure/ # хранение (PostgreSQL, TPH Message/ChatEvent), outbox, realtime-публикация (SignalR), storage/file-storage presign+upload-адаптеры, минтинг media-JWT, поиск-проекция контактов (Elasticsearch)
Pin.Messenger.Background/ # Kafka-консюмеры (crm/identity), воркеры (chat-provision-retry, call-timeout-reaper, conference-room-reaper, recording-finalize-retry, contact-search-rebuild), realtime-dispatch (outbox → hub)
Pin.Messenger.Migrator/ # миграции + SEED каталога типов карточек (EventCardType → i18n-ключи текстов целевых/инфо карточек)
Pin.Messenger.Contracts/ # контракты интеграционных событий pin.messenger.*; SignalR message-контракты (RealtimeMessage)
Основные Сущности
Детальные документы — domain/<entity>.md по entity-template,
по одному файлу на агрегат (создаются следующим слоем ТЗ).
| Entity | Назначение | Source Of Truth | Документ |
|---|---|---|---|
ChatEntity (+ParticipantEntity) | Чат: личный 1:1 (DirectKey), лида (LeadId) или сделки (DealId); участники, read-state, denorm last-message | messenger | domain/chat.md, domain/participant.md |
MessageEntity (TPH) | Сообщение: Text/Attachment/EventCard; edit ≤ 1 мин + скрытая история; IP/UA; per-participant видимость/тексты карточек | messenger | domain/message.md |
ChatEventEntity | Системная лента чата: создан/переименован/участник добавлен/удалён/закрыт (append-only, без read-state) | messenger | domain/chat-event.md |
ContactEntity | Контакт-связь заявка → подтверждение (разрешение звонков + раскрытие профиля) | messenger | domain/contact.md |
BlacklistEntity | Чёрный список (запрет писать/звонить) | messenger | domain/blacklist.md |
CallSessionEntity | 1:1-звонок: лог + медиа поверх SFU (SessionKind = Direct) | messenger | domain/call-session.md |
ConferenceSessionEntity (+CallParticipantEntity) | Групповой звонок в чате сделки: ростер, модератор, демонстрация экрана | messenger | domain/conference-session.md, domain/call-participant.md |
CallRecordingEntity | Запись звонка/конференции (policy auto-record → file storage FileId) | messenger | domain/call-recording.md |
MessengerUserEntity | Реплика identity (ФИО/телефон) для поиска и display контактов | identity (реплика) | domain/messenger-user.md |
ER-обзор
API
Карта областей (детальные документы — api/<method>.md по
api-method-template, по одному файлу на операцию; создаются
следующим слоем ТЗ):
- chats (
/api/messenger/chats):list-chats(список чатов + unread + last-message-preview),get-chat(карточка + участники + read-state),open-direct-chat(создать/открыть личный 1:1, идемпотентно по паре),mark-chat-read(отметить прочитанным доSeq). - messages (
/api/messenger/chats/{chatId}/messages):list-messages(история, cursor поSeq, per-participant фильтр карточек),send-text-message,send-attachment-message(FileId+ caption),edit-message(PATCH≤ 1 мин),delete-message(DELETE≤ 1 мин, soft). - contacts (
/api/messenger/contacts):list-contacts,search-users-by-phone(Имя Отчество + первая буква Фамилии),list-contact-requests,send-contact-request,accept-contact-request,decline-contact-request,remove-contact. - blacklist (
/api/messenger/blacklist):list-blacklist,block-user,unblock-user. - calls (
/api/messenger/calls, 1:1 медиа поверх SFU):start-call,answer-call,decline-call,end-call,list-calls(история / пропущенные),media-token(обновление токена комнаты). - conferences (
/api/messenger/conferencesи/chats/{chatId}/conferences):start-conference,join-conference,leave-conference,end-conference,get-conference,list-conferences,conference-media-token; управление составом —invite-participant(адресный ring),decline-conference,remove-participant(модератор) — групповые звонки в чатах сделок. - recordings (
/api/messenger/recordingsи.../{callId|conferenceId}/recordings):list-call-recordings,list-conference-recordings,get-recording,get-recording-download(пресайн file storage),stop-recording(модератор). - internal (
/api/messenger/internal, вебхуки media-стека, ExternalSystem + подпись):jitsi-reservation(admission комнаты),jitsi-events(presence участников/записи),jibri-recording-ingest(приём байтов записи → file storage). - hub:
MessengerHub(/hubs/messenger) — контракт SignalR (server → client push: сообщения, звонки, конференции, запись, экран + client → server ephemeral typing/join-chat). - admin (
/api/messenger/admin, Служба заботы):get-chat-transcript(read-only арбитраж, включая историю правок).
Events И Consumers
| Type | Name | Topic | Назначение | Link |
|---|---|---|---|---|
| Produced | DealChatStatusChangedEvent | pin.messenger.deal-chat-status-changed.v1 | готовность чата сделки → проекция Deal.ChatStatus в crm (Provisioning/Ready/Failed) | events/deal-chat-status-changed.md |
| Produced | MessagePostedEvent | pin.messenger.message-posted.v1 | notifications: offline push/email адресатам (без тела — non-PII) | events/message-posted.md |
| Produced | ContactRequestCreatedEvent | pin.messenger.contact-request-created.v1 | notifications: уведомить адресата о заявке в контакты | events/contact-request-created.md |
| Produced | ContactRequestAcceptedEvent | pin.messenger.contact-request-accepted.v1 | notifications: уведомить инициатора о подтверждении | events/contact-request-accepted.md |
| Produced | ConferenceEndedEvent | pin.messenger.conference-ended.v1 | crm (карточка встречи в таймлайн сделки), notifications, dev-stats | events/conference-ended.md |
| Produced | CallRecordingAvailableEvent | pin.messenger.call-recording-available.v1 | crm (прикрепить запись к сделке), notifications (запись готова) | events/call-recording-available.md |
| Consumed | pin.crm.deal-created.v1 | crm | создать чат сделки, добавить участников, вернуть готовность | consumers/deal-created.md |
| Consumed | pin.crm.deal-stage-changed.v1 | crm | карточка «этап изменён»; на терминальный ToStatus — финальная инфо-карточка + закрытие чата | consumers/deal-stage-changed.md |
| Consumed | pin.crm.deal-members-changed.v1 | crm | идемпотентная set-реконсиляция участников чата сделки (ActiveMembers) | consumers/deal-members-changed.md |
| Consumed | pin.crm.lead-participants-changed.v1 | crm | идемпотентная set-реконсиляция участников чата лида ChatKind.Lead (ActiveParticipants) | consumers/lead-participants.md |
| Consumed | pin.crm.document-requested.v1 | crm | карточка целевого действия «Загрузите документы» (Pending, адресат — инвестор) | consumers/document-requested.md |
| Consumed | pin.crm.booking-paid.v1 | crm | инфо-карточка «Договор бронирования оплачен» + сумма | consumers/booking-paid.md |
| Consumed | pin.crm.booking-expired.v1 | crm | карточка «Бронь истекла — подтвердите/продлите» | consumers/booking-expired.md |
| Consumed | pin.identity.users.v1 | identity | реплика ФИО/телефона (MessengerUserEntity) + реиндексация search-документа контактов (Elasticsearch) | consumers/identity-user-upserted.md |
Все produced-события публикуются через транзакционный outbox (несут ContractVersion, версия — суффикс
топика .vN); все консюмеры идемпотентны по идентификатору сообщения + бизнес-ключу. Источники
consumed-событий: crm, identity.
Кросс-сервисные контракты (ожидания и гарантии)
Что messenger ожидает от соседей (входящие события):
- От
crm(жизненный цикл чата сделки): ключ партиционирования =DealId(per-deal порядок), доставка at-least-once,deal-createdпредшествуетdeal-stage-changed/deal-members-changed/booking-*той же сделки. Снапшотdeal-createdнесётDealId, developer-company (DealTenantId), проект, состав участников (Members), стадию — этого достаточно для провизии чата без синхронных дозапросов. При лаге событий чат сделки отстаёт (ChatStatus = Provisioning), не теряется. - От
crm(состав чата лида): ключ =LeadId(per-lead порядок), доставка at-least-once;lead-participants-changedнесёт полный активный составActiveParticipants+ дельту и монотоннуюVersion—messengerприводит ростер чата лида (ChatKind.Lead) к присланному составу set-реконсиляцией (consumers/lead-participants.md). Провизия Lead-чата — по факту создания лида (аналогdeal-createdдля сделки, соседний слой ТЗ); до провизии синк состава ретраится (MSG_CHAT_NOT_PROVISIONED). - От
identity(реплика пользователей): топикpin.identity.users.v1, ключ =UserId(per-user порядок), монотонная доменнаяVersion(version-guard у потребителя — старое событие игнорируется), at-least-once. Несёт ФИО/телефон — источник display-имени и поиска контактов. При отсутствии реплики (первое событие ещё не доехало) read-модели отдают безопасный placeholder, строку не отбрасывают.
Что messenger предоставляет (исходящие события): каждое produced-событие идёт через транзакционный outbox,
несёт ContractVersion (сейчас 1) и партиционируется бизнес-ключом ради порядка: deal-chat-status-changed
и conference-ended — по DealId; message-posted — по ChatId; call-recording-available — по SessionId;
contact-request-* — по адресату. Совместимость additive-only (новые optional-поля — без bump; breaking —
новый ContractVersion/.vN + dual-publish). Потребители обязаны быть идемпотентны (событие at-least-once).
Окна eventual-consistency и промежуточные состояния для пользователя: провизия чата сделки
deal-created → Ready — целевое окно < 5 мин (ChatStatus: Provisioning → Ready, при исчерпании ретраев —
Failed + re-emit воркером chat-provision-retry); пользователь в промежутке видит «чат готовится».
Рассинхрон реплики пользователя (лаг user-upserted) — секунды/минуты: контакт/участник показывается с
placeholder-подписью до догона. Search-проекция контактов (Elasticsearch) отстаёт от source of truth на
секунды; листинг контактов без поиска работает по PostgreSQL немедленно.
Runtime И Эксплуатация
| Область | Требование |
|---|---|
| Health checks | /health/live, /health/ready (PostgreSQL + Kafka + Elasticsearch (search контактов, non-fatal degrade) + file storage + Redis-backplane если включён; media-стек/SFU — non-fatal degrade, звонки за флагом), /health/startup (миграции применены) |
| SLO/SLA | persist команды сообщения p95 < 300 мс; realtime push (persist → hub) p95 < 1 с; списки/история p95 < 500 мс; провизия чата сделки (deal-created → Ready) < 5 мин; вход в звонок/конференцию (join) p95 < 3 с; успешность записи ≥ 99%; hub KeepAlive 15 с / ClientTimeout 30 с / MaximumReceiveMessageSize 32 КБ |
| Background jobs | chat-provision-retry (цикл 1 мин; ChatStatus = Failed → повтор + re-emit), call-timeout-reaper (цикл 15 с; Ringing без ответа > 45 с → Missed), conference-room-reaper (цикл 30 с; пустая/зависшая конференция → Ended/Failed), recording-finalize-retry (цикл 60 с; застрявшая выгрузка записи → сверка file storage / Failed), realtime-dispatch (hosted service: outbox → hub relay, persist-then-push), contact-search-rebuild (bootstrap/восстановление индекса messenger-contacts, ADR-0059) |
| Dashboard | Grafana pin-messenger (создаётся при реализации); отдельные панели JVB/jibri/coturn (эксплуатация media-стека — calls/jitsi-deployment.md) |
| Alerts | лаг консюмеров > 5 мин; outbox backlog > 1000; провизия чата Failed > 10/ч; всплеск ошибок realtime-dispatch; hub disconnect rate; запись Failed/застряла > 30 мин; насыщение рекордеров jibri |
| Runbooks | docs/06-services/messenger/runbooks/ (будущий путь по конвенции); эксплуатация media-стека — calls/ |
| Feature flags | REALTIME_BACKPLANE_ENABLED (Redis backplane для multi-replica); CALLS_MEDIA_ENABLED (медиа 1:1-звонков поверх SFU; заменяет исторический CALLS_SIGNALING_ENABLED); CONFERENCE_CALLS_ENABLED (групповые конференции); RECORDINGS_ENABLED (запись звонков; авто-старт — RecordingPolicy) |
Безопасность
- Actor types: Инвестор, Агент, Руководитель агентства, Менеджер застройщика, Менеджер ПИН (все
User), Служба заботы (read-only арбитраж), система (консюмеры / hub / воркеры). - Permission model: Scoped-RBAC + record-level (permission); permissions
messenger.chats.view/.write,messenger.messages.edit,messenger.contacts.manage/.search,messenger.blacklist.manage,messenger.calls.use,messenger.conferences.use,messenger.recordings.view/.manage,messenger.admin.view(арбитраж, platform-scope). Вебхуки media-стека (internal) — actorExternalSystemс подписью (HMAC/mTLS), fail-closed по имени комнаты. - Tenant isolation (ADR-0052): чаты messenger НЕ используют строковую single-tenant изоляцию (личные
чаты кросс-стороны: инвестор ↔ агент, инвестор ↔ инвестор; инвестор — вне tenant). Доступ = record-level
по членству (активный
ParticipantEntity). Чат сделки несётDealTenantId = DeveloperCompanyIdтолько для корреляции и scope арбитража Службы заботы. - Sensitive/PII: тексты сообщений (PII); ФИО/телефон контакта (PII; в поиске фамилия маскируется первой буквой до подтверждения контакта); IP/UA отправителя (Sensitive, только internal — наружу и в события/логи не отдаются); скрытая история правок (не отдаётся ни одной read-моделью, кроме admin-арбитража). События не несут тела сообщений/PII (только id).
- Audit: версия на агрегатах; бизнес-история — append-only
ChatEvent+MessageEdit; блокировки/контакты — статусные переходы.
Известные Ограничения И Assumptions
- Simplicity-first (ADR-0056): PostgreSQL — source of truth; таймеры/reaper — polling-воркеры без Redis TTL; целевая нагрузка ≤ 1 млн пользователей, 1–10k одновременно активных → один инстанс, backplane опционален. Поиск контактов — Elasticsearch-проекция (ADR-0059 supersedes FTS-пункт ADR-0056), а не PostgreSQL-FTS.
- Media-стек звонков (SFU/TURN/рекордер) — self-hosted Jitsi (ADR-0062): JVB=SFU, coturn=TURN, jibri=запись; бэкенд минтит токен доступа и принимает вебхуки, медиа идёт клиент ↔ SFU (не в медиа-плоскости). Запись — policy-driven, выгрузка в file storage. Инфраструктура/интеграция — calls/.
- Запись хранится в file storage по
FileId(бинарь не в messenger); транскрипция/конспект — будущая фаза (шов черезcall-recording-available, calls/transcription-roadmap.md). - Правка/удаление жёстко ≤ 60 с (
EditableUntil), затем immutable; история правок хранится, но не показывается (кроме admin-арбитража). - Realtime — не source of truth: при reconnect клиент перезапрашивает REST.
Принятые решения контекста
| # | Вопрос | Решение |
|---|---|---|
| 1 | Транспорт realtime | SignalR, persist-then-push (outbox → hub); Redis backplane — опция (ADR-0055/0043) |
| 2 | Оркестрация чат ↔ сделка | модуль CRM + события; messenger — консюмер; готовность возвращает deal-chat-status-changed |
| 3 | Модель сообщений | EF TPH: TextMessage/AttachmentMessage/EventCardMessage; событие-карточка — per-participant видимость + per-participant тексты (child EventCardRecipient), typed jsonb payload |
| 4 | Кто кому может писать/звонить | CommunicationPolicy: писать можно не-контакту, запрет только по чёрному списку (любая сторона); звонить и видеть полный профиль — только подтверждённый контакт |
| 5 | Доступ к чату | по членству (активный Participant), не по tenant; чат сделки хранит DealTenantId для арбитража |
| 6 | Непрочитанные / read-receipts | монотонный per-chat Seq; Participant.LastReadSeq; unread = Chat.LastMessageSeq − LastReadSeq |
| 8 | Вложения | только FileId + метаданные (storage/CDN); бинарь не хранится; загрузка вне форм |
| 9 | Звонки | 1:1 + групповые конференции поверх self-hosted Jitsi (JVB SFU, ADR-0062); бэкенд минтит токен и room-authority, медиа клиент ↔ SFU; SDP/ICE/токены не персистятся (CallSessionEntity/ConferenceSessionEntity) |
| 10 | Запись звонков | policy-driven auto-record (RecordingPolicy); jibri → ingest в messenger → file storage (FileId); история + будущая транскрипция; crm/notifications через call-recording-available |
Связанные Документы
- Домены:
domain/(10 документов выше); события:events/, консюмеры:consumers/, API:api/(calls/conferences/recordings/internal/…), use-cases:use-cases/, background:background/, search:search/contacts.md(Elasticsearch-проекция контактов). - Интеграция media-стека (Jitsi): calls/README.md — деплой, JWT, комнаты, нативный клиент, пайплайн записи, роадмап транскрипции, инвентарь правок Jitsi.
- Обзор: technical-assignment.md, service-boundaries.md, glossary.md, prototype-mapping.md.
- Источники событий: crm (deal-created / stage-changed / members-changed / lead-participants-changed / document-requested / booking-paid / booking-expired), identity/events/user-upserted.md.
- ADR: 0055 свой мини-мессенджер, 0062 медиа-стек и запись на Jitsi, 0052 tenancy, 0056 simplicity-first, 0043 SignalR backplane, 0032 retention, 0059 Elasticsearch-проекции, 0071 единая модель участников CRM, 0072 каскад увольнения агента.