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

Event Contract: pin.rewards.commission-distribution-adjusted (CommissionDistributionAdjustedKafkaEvent)

Версионируемый контракт интеграционного события «распределение начисления скорректировано после получения денег». Compensating-событие: несёт дельту долей Agency/Agent при per-deal корректировке распределения (adjust-accrual-distribution.md), когда начисление уже в статусе MoneyReceived и счета получателей в wallet кредитованы. Дом полей — entity: CommissionAccrualEntity (строки распределения CommissionDistributionEntity, INV-11).

Назначение

Фиксирует перераспределение пула агентства между строками Agency и Agent после гейта получения денег (ADR-0067): руководитель агентства изменил доли по конкретной сделке, а wallet уже кредитовал счета получателей по старому распределению (событие получения денег, commission-money-received.md). Producer — rewards (операция корректировки распределения); событие публичное, потому что wallet автономно переносит дельту между счетами AgentCommission и AgencyCommission без синхронного запроса в rewards (ADR-0065), выравнивая балансы под новое деление пула. Строка Platform неприкосновенна, gross и пул агентства не меняются — переносится только дельта между агентом и агентством, поэтому Σ балансов получателей остаётся неизменной. Событие публикуется только для корректировок в MoneyReceived; в Confirmed кошелёк ещё не кредитован и событие не нужно.

Metadata

ПолеЗначение
Event typepin.rewards.commission-distribution-adjusted
C# typeCommissionDistributionAdjustedKafkaEvent
Topicpin.rewards.commission-distribution-adjusted.v1
Partitions / key3 / key = AccrualId (строгий порядок корректировок per-начисление; объём — единичные back-office правки, ADR-0056)
ContractVersion1
Producerrewards: per-deal корректировка распределения начисления в статусе MoneyReceived (adjust-accrual-distribution.md) — по одному событию на каждую корректировку
Статусdraft

Метаданные события: идентификатор сообщения (MessageId, ключ дедупликации), время (CreatedAt), Version (= ContractVersion), AggregateId = AccrualId, AggregateType = "CommissionAccrual". Событие публикуется атомарно с применением новых долей и инкрементом доменной версии начисления.

Payload CommissionDistributionAdjustedKafkaEvent

Несёт доли Agency/Agent до и после корректировки — потребитель вычисляет дельту и переносит её между счетами. Строка Platform, CommissionAmount, PlatformFeeAmount в payload не входят (не меняются — INV-11); реквизиты акта, InvestorUserId, цены объекта недвижимости — тоже не входят (см. «Безопасность»).

ПолеТипОбязательностьИсточник (поле агрегата)CompatibilityОписание
AccrualIdGuidДаCommissionAccrualEntity.IdImmutableКлюч агрегата (= Kafka key); часть ключа идемпотентности проводки wallet.
TenantIdGuidДаCommissionAccrualEntity.TenantIdImmutableАгентство-получатель — держатель счёта AgencyCommission (строка Agency).
AgentUserIdGuidДаRecipientUserId строки AgentImmutableАгент-исполнитель сделки — держатель счёта AgentCommission (строка Agent).
OldAgencyAmountMoneyModelДадоля Agency до корректировкиAdditive onlyСумма строки Agency до правки (= кредит, уже проведённый wallet).
NewAgencyAmountMoneyModelДадоля Agency после корректировкиAdditive onlyСумма строки Agency после правки.
OldAgentAmountMoneyModelДадоля Agent до корректировкиAdditive onlyСумма строки Agent до правки (= кредит, уже проведённый wallet).
NewAgentAmountMoneyModelДадоля Agent после корректировкиAdditive onlyСумма строки Agent после правки.
ReasonstringДаReason строки корректировки (Kind = Distribution)Additive onlyОбязательная причина корректировки (INV-11); фиксируется в аудите проводки wallet. 1..1000.
VersionlongДаCommissionAccrualEntity.Version после корректировкиImmutable semanticsДоменная версия начисления после правки — монотонный guard и часть ключа идемпотентности потребителя.

Инвариант дельты (следствие INV-11, пул сохраняется): (NewAgencyAmount − OldAgencyAmount) + (NewAgentAmount − OldAgentAmount) = 0 — сколько прибавлено одной стороне, столько снято у другой; строка Platform и Σ балансов получателей не меняются.

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

Проекция VO Money (commission-accrual.md, раздел «Value objects»); валюта всех сумм payload едина и равна валюте начисления (INV-6).

ПолеТипОбязательностьОграниченияОписание
ValuedecimalДа>= 0, scale 2Сумма.
CurrencyIdshortДассылка на валюту справочника; RUB-only (ADR-0048)Типизированная ссылка на валюту.
CurrencySnapshotCurrencySnapshotДа../../handbook/domain/snapshots.mdDisplay-снапшот валюты на момент публикации.

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

УсловиеПереход состоянияСогласованность
Руководитель агентства скорректировал распределение начисления в статусе MoneyReceived (доли Agency/Agent изменены, adjust-accrual-distribution.md)статус не меняется (MoneyReceived); Version + 1Новые доли, строка корректировки (Kind = Distribution) и публикация — в одном согласованном изменении

Корректировка в статусе Confirmed (кошелёк ещё не кредитован) событие не публикует. Каждая корректировка в MoneyReceived даёт ровно одно событие с новым Version; на один AccrualId их может быть несколько (последовательные правки), различаются по Version.

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

ПотребительЭффект (событие → действие)ИдемпотентностьЗадержка (SLA)
walletАвто-проводка Adjustment: перенос дельты между холдерскими счетами AgentCommission (AgentUserId) и AgencyCommission (TenantId) — списание у стороны, чья доля уменьшилась, зачисление стороне, чья доля выросла. Consumer — ../../wallet/consumers/rewards-commission-distribution-adjusted.mdMessageId + бизнес-ключ (Adjustment, AccrualId + Version) — повтор события не удваивает перенос< 5 мин

Гарантии доставки и порядка

  • Доставка — at-least-once; wallet дедуплицирует по MessageId и бизнес-ключу (Adjustment, AccrualId + Version) — повторная доставка не создаёт второй перенос.
  • Порядок. Key топика = AccrualId — последовательные корректировки одного начисления приходят в порядке возрастания Version; перестановки между разными начислениями безразличны.
  • Между топиками порядок не гарантирован. Событие получения денег (commission-money-received.md, первичный кредит) и это событие (перенос дельты) — разные топики; корректировка возможна только после MoneyReceived, то есть первичный кредит бизнес-логически предшествует. Если перенос всё же обгонит кредит, недостаток баланса ловится guard-ом wallet (INV-WLT-3) → DLQ + алерт + reconciliation, а не молчаливой потерей.
  • Неизвестные поля у потребителя игнорируются (additive-политика ниже).

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

  • Версия — один ContractVersion = 1; consumer при несовпадении версии логирует warning и продолжает (upcasting нет).
  • Additive-only для minor: новые optional-поля с безопасным default — без bump.
  • Breaking (удаление/переименование/смена типа/смысла) — ContractVersion = 2 + новый топик pin.rewards.commission-distribution-adjusted.v2 + dual-publish на период миграции + deadline миграции consumers + sunset старого топика.
  • Deprecation — по общему sunset-правилу событийного реестра.

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

ПолеКласс данныхПравило
AccrualId, TenantId, AgentUserId, VersionInternalinclude (адресация переноса)
OldAgencyAmount, NewAgencyAmount, OldAgentAmount, NewAgentAmountSensitive (суммы долей)include (внутренний топик, m2m ACL Kafka — wallet); в логи потребителей не пишутся
ReasonInternalinclude (аудит проводки); текст причины в логи не пишется
Реквизиты акта, InvestorUserId, CommissionAmount, PlatformFeeAmount, строка Platform, цены объекта недвижимостиSensitive/Internalexclude — к переносу дельты не относятся

Событие НЕ содержит secrets, PII (ФИО/телефоны), платёжные реквизиты, unbounded-вложения.

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

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

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