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

POST /api/v1/platform/referral-rate-rules/{rateRuleId}/activate — Активация ставки реферального вознаграждения

Перевод ставки Draft → Active: с этого момента ставка участвует в резолюции при создании реферальных начислений (с учётом периода EffectiveFrom/EffectiveTo). При активации проверяется непересечение периодов активных ставок одной комбинации измерений (INV-RR-2) — гарантия детерминированной резолюции — domain/referral-rate-rule.md.

Назначение

Менеджер ПИН вводит ставку в действие. В отличие от условий платформенной комиссии активация не архивирует предыдущие ставки: активные ставки одной комбинации измерений сосуществуют с непересекающимися периодами (текущая + будущая акция). Owning-контекст — rewards. Feature group — referral-rates.

Metadata

ПолеЗначение
Сервис/контекстrewards
API areaplatform
Feature groupreferral-rates
Статусdraft
Документdocs/06-services/rewards/api/platform/activate-referral-rate-rule.md

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

ПроверкаЗначение
Actor typeUser (Менеджер ПИН)
Auth policy / Permissionsrewards.referral-rates.manage
Scope (RBAC)платформа
Tenant isolationвне tenant-модели — платформенная сущность (ADR-0052)
Auditupdate: UpdatedByUserId, Version + 1, доменное событие ReferralRateRuleActivatedEvent

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/v1/platform/referral-rate-rules/{rateRuleId}/activate
Success status200
Idempotency headerне применяется (повтор из ActiveREWARDS_REFERRAL_RATE_INVALID_STATUS_TRANSITION)
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ActorUserIdidentity claims (sub)GuidДаRequired
RateRuleIdroute {rateRuleId}GuidДаRequired, непустой
ExpectedVersionbodylongДа>= 1; сверка с Versionoptimistic concurrency

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

ПолеТипОбязательностьИсточникMaskingОписание
RuleReferralRateRuleModelДаобновлённый агрегатПроекция после активации (Status = Active); модель — list-referral-rate-rules.md.

Предусловия и постусловия

  • Предусловия. Актор — Менеджер ПИН с rewards.referral-rates.manage; ставка существует в статусе Draft; период не истёк; среди активных ставок той же комбинации измерений нет пересечения периодов; клиент передаёт актуальный ExpectedVersion.
  • Постусловия (успех). Ставка в статусе Active, Version увеличен, актор зафиксирован; ставка участвует в резолюции для реферальных начислений, чей asOf = CompletedAt попадает в [EffectiveFrom, EffectiveTo|∞); внутри одной комбинации измерений на любой момент — не более одного активного кандидата (INV-RR-2, детерминированность). Уже созданные начисления не пересчитываются.
  • Постусловия (отказ). Ставка остаётся в исходном статусе, версия не меняется.
  • Граница «активна, но ещё не действует». Ставка с EffectiveFrom в будущем после активации в резолюцию попадает только с наступлением EffectiveFrom — механизм заблаговременной подготовки акций.

Алгоритм

  1. Контекст и доступ. Операцию выполняет Менеджер ПИН в платформенном scope; требуется право rewards.referral-rates.manage.
  2. Проверка входа и предусловий. RateRuleId непустой, ExpectedVersion >= 1. Ставка существует — иначе REWARDS_REFERRAL_RATE_NOT_FOUND. Статус = Draft (единственный допустимый исходный) — иначе REWARDS_REFERRAL_RATE_INVALID_STATUS_TRANSITION. Version совпадает с ExpectedVersion — иначе REWARDS_CONCURRENCY_CONFLICT. Если EffectiveTo задан и уже в прошлом — REWARDS_REFERRAL_RATE_PERIOD_INVALID (нельзя активировать истёкшую ставку).
  3. Непересечение периодов (INV-RR-2). Активные ставки с той же комбинацией значений (PartnerUserId, DeveloperCompanyId, ProjectId, LayoutGroupId), включая null-значения, читаются под блокировкой; если период [EffectiveFrom, EffectiveTo|∞) активируемой ставки пересекается хотя бы с одним из них — отказ REWARDS_REFERRAL_RATE_OVERLAP (id конфликтующих ставок — в metadata ошибки; менеджер либо досрочно завершает действующую ставку через update-referral-rate-rule.md, либо правит период черновика). Пересечение проверяется только внутри одной комбинации измерений: ставки разных уровней специфичности пересекаться могут — конфликт разрешает порядок «специфичное побеждает».
  4. Переход и запись. Ставка переходит Draft → Active; фиксируются актор и новая версия. Переход сохраняется в одном согласованном изменении с проверкой п.3 (гонка двух активаций пересекающихся черновиков одной комбинации — под блокировкой активных ставок комбинации: победитель активируется, проигравший получает REWARDS_REFERRAL_RATE_OVERLAP).
  5. Результат. Проекция активированной ставки (ReferralRateRuleModel, Status = Active) → 200. Интеграционные события не публикуются — ставка применяется только внутри rewards при создании реферального начисления.

Диаграмма

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
REWARDS_REFERRAL_RATE_NOT_FOUND404Ставка не существует«Ставка не найдена»no retry
REWARDS_REFERRAL_RATE_INVALID_STATUS_TRANSITION409Status != Draft (уже Active / Archived)«Активировать можно только черновик»no retry
REWARDS_REFERRAL_RATE_OVERLAP409Период пересекается с активной ставкой той же комбинации измерений (INV-RR-2)«Период пересекается с действующей ставкой» + id конфликтующих ставок в metadatano retry (сначала завершить/поправить конфликтующую)
REWARDS_REFERRAL_RATE_PERIOD_INVALID400EffectiveTo уже в прошлом«Период действия уже истёк»no retry
REWARDS_CONCURRENCY_CONFLICT409ExpectedVersion устарел«Ставка изменена другим пользователем»retry после refresh

Общие коды — README.md.

Acceptance-критерии

  • Happy path. Дано: черновик ставки Partner-специфичности, период не пересекается с активными ставками того же партнёра (без застройщика), ExpectedVersion актуален. Когда: POST .../activate. Тогда: 200, Status = Active, Version + 1; начисления с CompletedAt внутри периода применяют эту ставку.
  • Пересечение периодов. Дано: активная ставка той же комбинации измерений с периодом [t0, ∞); черновик с EffectiveFrom = t0 + 30дней. Когда: POST .../activate. Тогда: 409 REWARDS_REFERRAL_RATE_OVERLAP с id конфликтующей ставки; после досрочного завершения действующей (EffectiveTo = t0 + 30дней) повторная активация проходит — периоды смыкаются без пересечения.
  • Разные специфичности пересекаются свободно. Дано: активная DeveloperProject-ставка на [t0, ∞); черновик DeveloperProjectLayoutGroup (тот же застройщик и проект, но задана группа планировок) с периодом внутри. Когда: POST .../activate. Тогда: 200 — комбинации измерений разные, резолюция выбирает более специфичный уровень (INV-RR-2 действует внутри одной комбинации).
  • Будущая акция. Дано: черновик с EffectiveFrom в будущем, пересечений нет. Когда: POST .../activate. Тогда: 200, Status = Active; в резолюцию ставка попадёт с наступлением EffectiveFrom.
  • Истёкший период. Дано: EffectiveTo уже в прошлом. Когда: POST .../activate. Тогда: 400 REWARDS_REFERRAL_RATE_PERIOD_INVALID.
  • Не черновик. Дано: ставка уже Active или Archived. Когда: POST .../activate. Тогда: 409 REWARDS_REFERRAL_RATE_INVALID_STATUS_TRANSITION.
  • Гонка двух активаций. Дано: два черновика одной комбинации измерений с пересекающимися периодами. Когда: одновременный POST .../activate обоих. Тогда: один — 200, другой — 409 REWARDS_REFERRAL_RATE_OVERLAP (проверка под блокировкой активных ставок комбинации).
  • Конкурентная версия. Дано: ExpectedVersion устарел. Когда: POST. Тогда: 409 REWARDS_CONCURRENCY_CONFLICT.

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

  • Проверка INV-RR-2 — app-level под блокировкой (ставок единицы–десятки, ADR-0056), db-индекса непересечения нет — блокировка активных ставок комбинации измерений обязательна.
  • Logs: ActorUserId, RateRuleId, Scope, период; metrics: rewards_referral_rate_activate_total{result}; traces: OTel; dashboard rewards-overview.

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

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

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