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

Event Contract: pin.crm.document-requested (DocumentRequestedEvent)

Версионируемый контракт интеграционного события «запрошен документ сделки». Consumer-документы ссылаются сюда, не переописывая payload. Дом полей — entity: DealDocumentEntity; контекст сделки — entity: DealEntity.

Назначение

Фиксирует запрос документа у участника сделки: менеджер застройщика или система создаёт DealDocumentEntity в статусе Requested (файла ещё нет). Producer — crm, операция «запрос документа» (карточка целевого действия «загрузите документы» в чате сделки, US-INV-16; запрос документов для формирования договора ДДУ/ДКП). Публичное: messenger рисует карточку целевого действия адресату без синхронного запроса в crm; notifications шлёт уведомление «требуется загрузка». Прямая загрузка документа без предварительного запроса (переход [*] → Uploaded) это событие не публикует — её фиксирует document-status-changed.

Metadata

ПолеЗначение
Event typepin.crm.document-requested
Topicpin.crm.document-requested.v1
Partitions / key3 (низкообъёмная сущность, ADR-0056) / key = AggregateId = DocumentId
ContractVersion1
Producercrm: запрос документа (менеджер застройщика / система) — вкладка «Документы» карточки сделки
Статусdraft
Атомарность публикации

Событие публикуется атомарно с созданием DealDocumentEntity: пока запись не зафиксирована, событие не отправляется; после фиксации — доставляется гарантированно (at-least-once), ключ = DocumentId.

Payload DocumentRequestedEvent

Дом полей — entity: DealDocumentEntity (раздел «Поля» и «Доменные и интеграционные события»); здесь перечислено только то, что реально в событии.

ПолеТипОбязательностьИсточник (поле агрегата)CompatibilityОписание
DocumentIdGuidДаDealDocumentEntity.IdImmutableКлюч агрегата (= AggregateId = Kafka key).
DealIdGuidДаDealDocumentEntity.DealIdImmutableСделка-контекст (deal.md).
TenantIdGuidДаDealDocumentEntity.TenantIdImmutableDeveloperCompanyId застройщика сделки (ADR-0052).
DocumentTypestring (enum DealDocumentType)ДаDealDocumentEntity.TypeAdditive only (новые значения)Тип документа: ClientPassport / SignedBookingContract / BookingPaymentProof / ContractDraftDocument / SignedSaleContract / PaymentProof / Other и др. (полный перечень — deal-document.md).
TitlestringДаDealDocumentEntity.TitleAdditive onlyОтображаемое название запрошенного документа («Скан паспорта, стр. 1»).
RequestCommentstring?НетDealDocumentEntity.RequestCommentAdditive onlyТекст карточки «загрузите документы» — что именно нужно (US-INV-16), ≤ 1000.
RequestedByUserIdGuid?НетDealDocumentEntity.RequestedByUserIdAdditive onlyКто запросил (менеджер застройщика); null — системный/автоматический запрос.
TargetParticipantUserIdGuid?Нетактивный DealMemberEntity.UserId (Role = Investor)Additive onlyКому адресован запрос — активный инвестор сделки (INV-DEAL-3); null — общий запрос без явного адресата (карточка видна всем участникам).
RequestedAtDateTimeOffsetДаDealDocumentEntity.CreatedAtImmutableМомент запроса (создания записи в статусе Requested).
DueAtDateTimeOffset?НетDealDocumentEntity.DueAtAdditive onlyСрок предоставления (индикация «просрочено» в карточке/таймлайне).
Что не входит в payload

FileId, FileName, ContentType, SizeBytes, RejectReason, ReviewedByUserId в payload не входят: на момент запроса файла нет (статус Requested), поля появляются позже и фиксируются событием document-status-changed. RequisiteSetId не публикуется (внутренняя ссылка на profile).

Когда публикуется

УсловиеПереход состоянияПубликация
Менеджер застройщика или система создаёт DealDocumentEntity со статусом Requested у сделки в нетерминальном статусе (US-INV-16; запрос для ДДУ/ДКП)[*] → Requested (stateDiagram deal-document.md)атомарно со сменой состояния агрегата
Гварды и соседнее событие

Гварды инвариантов запроса — INV-DOC-5 (сделка не терминальна) и record-level scope участника; см. deal-document.md. Прямая загрузка ([*] → Uploaded) и смена статуса (Uploaded/Accepted/Rejected) публикуют другое событие — document-status-changed.

Потребители (consumers)

Сверено с README («Events И Consumers») и deal-document.md.

Consumer-контекстSide effectsИдемпотентностьЗадержка (SLA)
messengerКарточка целевого действия «загрузите документы» в чат сделки (DealId → чат сделки, US-INV-16); адресат — TargetParticipantUserIdMessageId (де-дуп карточки)< 1 мин
notificationsПуш/СМС адресату «требуется загрузка документа» (по TargetParticipantUserId)MessageId< 5 мин

Оба консюмера идемпотентны по MessageId (повторная доставка не создаёт вторую карточку/уведомление).

Доставка, порядок и обработка сбоев

  • Гарантия публикации. Событие уходит только после фиксации создания DealDocumentEntity в статусе Requested; при откате не публикуется. Доставка — at-least-once, ключ DocumentId.
  • Порядок. События одного документа упорядочены ключом DocumentId; document-requested предшествует последующим document-status-changed того же DocumentId.
  • Идемпотентность потребителя. messenger/notifications дедуплицируют по MessageId (повторная доставка не создаёт вторую карточку/уведомление).
  • Retry. Транзиентный сбой потребителя → повтор обработки с backoff; событие доступно до успешной обработки.
  • DLQ. Неустранимый сбой после исчерпания попыток → dead-letter c алертом; проигрывание безопасно (идемпотентно по MessageId). При неизвестном DocumentType карточка нейтральная (fail-safe), в DLQ не уходит.

Версионирование и совместимость

  • ContractVersion = 1; при несовпадении версии контракта потребитель логирует предупреждение и продолжает обработку. Топик кодирует версию суффиксом .v1 по конвенции.
  • Additive-only для minor: новые поля — optional с безопасным default, без bump (DueAt, RequestComment, TargetParticipantUserId уже nullable).
  • Breaking (удаление/переименование/смена типа/смысла) — ContractVersion = 2 + топик pin.crm.document-requested.v2 + dual-publish на период миграции + deadline миграции consumers + sunset .v1.
  • Unknown enum DocumentType — consumer сохраняет строку как есть и рендерит нейтральную карточку «запрошен документ» + warning (fail-safe), не роняет обработку.

Безопасность

ПолеКласс данныхПравило
DocumentId, DealId, TenantId, RequestedAt, DueAtInternalinclude
DocumentTypeInternalinclude
Title, RequestCommentInternalinclude — свободный текст менеджера; producer гарантирует отсутствие реквизитов/секретов/полных PII
RequestedByUserId, TargetParticipantUserIdInternalinclude (Guid-идентификаторы участников)
PII и маскирование

Событие не содержит ФИО, телефонов, номеров эскроу/счетов, FileId/бинарных вложений, secrets и токенов — только Guid-идентификаторы участников. ФИО/телефон адресата consumer резолвит из собственной реплики identity (user-upserted); при рендере карточки/логировании ФИО/телефон маскируются на стороне consumer (+7***1234, инициалы), в логах producer/consumer — только DocumentId/DealId.

Наблюдаемость

  • Логи (без PII): факт публикации document-requested с DocumentId/DealId/DocumentType/ RequestedAt (идентификаторы, не PII); Title/RequestComment (свободный текст) в логах не печатать целиком; ФИО/телефон адресата в логах не фигурируют (в payload их нет).
  • Метрики: число запросов документов по топику в разрезе DocumentType; сквозная задержка до messenger (< 1 мин — карточка «загрузите документы») и notifications (< 5 мин); глубина DLQ (порог алерта > 0); доля повторных доставок.
  • Доступ: топик — m2m ACL Kafka (messenger/notifications); FileId/бинарь и RequisiteSetId в событие не входят; ФИО/телефон адресата consumer резолвит из своей реплики и маскирует в логах.

Acceptance Criteria

  • Given сделка в нетерминальном статусе (guard INV-DOC-5), When менеджер застройщика или система запрашивает документ, Then создаётся DealDocumentEntity в статусе Requested, ровно одно document-requested публикуется с DocumentType, Title и (если задан) TargetParticipantUserId.
  • Given прямая загрузка документа без предварительного запроса ([*] → Uploaded), When файл загружен, Then document-requested не публикуется — фиксирует document-status-changed.
  • Given повторная доставка события, When messenger обрабатывает, Then карточка целевого действия создаётся один раз (дедуп по MessageId).
  • Given запрос без явного адресата, When событие обрабатывается, Then TargetParticipantUserId = null и карточка видна всем участникам сделки.
  • Given неизвестное значение DocumentType, When потребитель обрабатывает, Then нейтральная карточка «запрошен документ» + warning (без падения).
  • Given breaking-изменение payload, When новый контракт, Then pin.crm.document-requested.v2 + dual-publish + sunset .v1.

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

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

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