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

Event Contract: pin.support.arbitrage-resolved (ArbitrageResolvedKafkaEvent)

Версионируемый контракт интеграционного события «арбитражное дело завершено (решение вынесено или эскалировано)». Consumer-документы ссылаются сюда, не переописывая payload. Событие фиксирует терминальный исход ArbitrageCaseEntity (arbitrage-case.md) — вынесение решения (6.3) или эскалацию администратору (6.5) — и служит информированию сторон конфликта (6.4).

Назначение

Даёт notifications факт завершения арбитража, чтобы информировать стороны (заявитель + ответчики) об исходе без синхронного запроса в support (матрица 6.4). Событие несёт исход (Outcome), тип конфликта, признак эскалации и id сторон — ФИО/контакты потребитель резолвит из своей реплики identity. Ручные последствия решения (смена статуса сделки / переназначение фиксации / корректировка комиссии) выполняются в crm/rewards через выданные support platform-scope permissions и этим событием не переносятсяAuditRefs в ArbitrageNoteEntity фиксируют факт (arbitrage-case.md). Эскалация администратору принимается администратором платформы (ADR-0053).

Metadata

ПолеЗначение
Event typepin.support.arbitrage-resolved
Topicpin.support.arbitrage-resolved.v1
Partitions / key3 (низкий объём, ADR-0056) / ключ = CaseId (= AggregateId; per-case порядок)
ContractVersion1
Producersupport: resolve-arbitrage-case (ResolveResolved) и escalate-arbitrage-case (EscalateEscalated) — ../api/support/resolve-arbitrage-case.md, ../api/support/escalate-arbitrage-case.md
Статусdraft

Событие публикуется атомарно с переходом дела в Resolved/Escalated и записью иммутабельного Decision: пока изменение не зафиксировано, событие не отправляется потребителям; после фиксации — доставляется гарантированно (at-least-once). Метаданные события: MessageId (Guid v7, для дедупликации), AggregateId = CaseId (= Kafka key, per-case порядок), AggregateType = "ArbitrageCase".

Payload ArbitrageResolvedKafkaEvent

Дом полей — entity: ArbitrageCaseEntity (раздел «Доменные и интеграционные события», VO ArbitrageDecision, jsonb ArbitrageParties); здесь перечислено только то, что реально в событии. RespondentUserIds — типизированный список id, проекция Respondents.Items.

ПолеТипОбязательностьИсточник (ArbitrageCaseEntity)CompatibilityОписание
CaseIdGuidДаIdImmutableКлюч агрегата (= Kafka key).
NumberlongДаNumberImmutableНомер ARB-NNN для текста уведомления/диплинка.
TenantIdGuidДаTenantIdImmutablePIN CompanyId (единый tenant платформы).
Typestring (enum ArbitrageType)ДаTypeAdditive only (новые значения)Тип конфликта (AgentPriorityConflict/BookingConflict/CommissionDispute/ContentComplaint/Other).
Outcomestring (enum ArbitrageOutcome)ДаDecision.OutcomeAdditive onlyИсход (FavorComplainant/FavorRespondent/SplitResolution/NoViolation/EscalatedToAdmin).
EscalatedboolДаStatus == EscalatedAdditive onlytrue — дело эскалировано администратору (6.5); false — решение вынесено (6.3).
ComplainantUserIdGuidДаComplainantUserIdImmutableЗаявитель — адресат информирования (id, не PII).
RespondentUserIdsIReadOnlyList<Guid>Да (может быть пустым)Respondents.ItemsAdditive onlyОтветчики — адресаты информирования (типизированный список id, <= 50).
RelatedDealIdGuid?НетRelatedDealIdAdditive onlyПривязка к сделке (crm), если есть (контекст уведомления).
DecidedByUserIdGuidДаDecision.DecidedByUserIdImmutableКто вынес решение/эскалировал (супервизор).
DecidedAtDateTimeOffsetДаDecision.DecidedAtImmutableМомент вынесения решения/эскалации.

Enum-значения не дублируются — дом arbitrage-case.md → «Enum ArbitrageType/ArbitrageOutcome». Title, Description, Decision.Rationale, заметки расследования (ArbitrageNoteEntity) и ФИО/телефон сторон в payload не входят: обоснование решения — Internal (читается по API дела с permission support.arbitrage.view), текст уведомления notifications формирует по Outcome/Type, display-подписи — из реплики identity.

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

УсловиеПереход состоянияПубликация
Resolve (супервизор, Decision задан — INV-ARB-2)UnderInvestigation → Resolved (Escalated = false)атомарно со сменой состояния агрегата
Escalate (супервизор, support.arbitrage.decide — INV-ARB-3)UnderInvestigation → Escalated (Outcome = EscalatedToAdmin, Escalated = true)та же

Регистрация дела (6.1), старт расследования (6.2) и добавление заметок Kafka-события не порождают — внешнему потребителю нужен только терминальный исход. Закрытие (→ Closed) события не публикует (пост-финал).

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

Consumer-контекстSide effectsИдемпотентностьЗадержка (SLA)
notificationsИнформирование сторон (заявитель + RespondentUserIds) об исходе (6.4); при Escalated = true — уведомление о передаче администратору; каналы получателей — своя реплика identityдедупликация по MessageId + per-case порядок по ключу< 1 мин

Отображаемые имена сторон консюмер берёт из собственных реплик identity, не из этого события; список получателей вычисляется из ComplainantUserId + RespondentUserIds.

Доставка и восстановление. At-least-once; дедупликация по MessageId (first-seen) — терминальный исход одного дела информируется ровно один раз даже при повторной доставке. Транзиентный сбой — ретраи, исчерпание → DLQ с алертом (без блокировки партиции). Событие терминально и самодостаточно (Outcome/Escalated/Type

  • id сторон); межтопикового порядка с событиями тикета нет (арбитраж — отдельный агрегат/ключ CaseId), см. README.md («Гарантии доставки, порядка и восстановления»).

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

  • ContractVersion = 1; при Version != ContractVersion consumer логирует warning и продолжает обработку. Топик кодирует версию суффиксом .v1 по конвенции.
  • Additive-only для minor: новые optional-поля с безопасным default — без bump. Новые значения enum ArbitrageType/ArbitrageOutcome — additive.
  • BreakingContractVersion = 2 + новый топик pin.support.arbitrage-resolved.v2 + dual-publish + deadline миграции consumers + sunset .v1.
  • Unknown enum (Type/Outcome) — fallback: сохранить строку как есть, warning, не падать (уведомление с нейтральной формулировкой исхода).

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

Событие несёт только идентификаторы, enum-значения, флаг и метку времени — прямого PII нет по построению: ФИО/телефон/email сторон исключены (консюмер резолвит из своей реплики identity); текст обоснования (Rationale, Internal) в payload не выносится.

ПолеКласс данныхПравило
CaseId, Number, TenantId, ComplainantUserId, RespondentUserIds, RelatedDealId, DecidedByUserIdInternalinclude (id/номер, не PII)
Type, Outcome, EscalatedInternalinclude
DecidedAtInternalinclude

Событие НЕ содержит secrets, tokens, ФИО/телефон/email сторон, Rationale/описание дела, заметки расследования, аудит-ссылки, вложения. Доступ к топику — m2m ACL Kafka (notifications).

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

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

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