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

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

Полная правка ставки только в статусе Draft; для ставки в статусе Active допускается изменение только EffectiveTo — досрочное завершение действия сокращением периода (INV-RR-3) — 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/update-referral-rate-rule.md

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

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

HTTP-контракт

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

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ActorUserIdidentity claims (sub)GuidДаRequired
RateRuleIdroute {rateRuleId}GuidДаRequired, непустой
ExpectedVersionbodylongДа>= 1; сверка с Versionoptimistic concurrency
PartnerUserIdbodyGuid?Нетnull — любой партнёр; логическая ссылка на пользователя-партнёра в identity (синхронно не проверяется)
DeveloperCompanyIdbodyGuid?Нетnull — любой застройщик; логическая ссылка на компанию Type = Developer (синхронно не проверяется)
ProjectIdbodyGuid?Нетnull — любой проект застройщика; логическая ссылка на проект каталога; требует заданного DeveloperCompanyId (INV-RR-4) — иначе REWARDS_REFERRAL_RATE_SCOPE_HIERARCHY_INVALID. Меняется только в Draft
LayoutGroupIdbodyGuid?Нетnull — любая группа планировок проекта; логическая ссылка на группу планировок каталога; требует заданного ProjectId (INV-RR-4) — иначе REWARDS_REFERRAL_RATE_SCOPE_HIERARCHY_INVALID. Меняется только в Draft
PercentbodydecimalДа0 < x <= 100, scale 2 (INV-RR-1)
EffectiveFrombodyDateTimeOffsetДаRequired
EffectiveTobodyDateTimeOffset?Нет> EffectiveFrom, если задан; для Active — единственное изменяемое поле: только сокращение периода (новое значение >= UtcNow и раньше текущего EffectiveTo либо текущий EffectiveTo = null) — сокращение пересечений не создаёт (INV-RR-3)
Commentbodystring?Нет<= 2000

PUT — полная замена: клиент присылает все поля (форма заполнена текущими значениями из list-referral-rate-rules.md).

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

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

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

  • Предусловия. Актор — Менеджер ПИН с rewards.referral-rates.manage; ставка существует в статусе Draft (полная правка) или Active (только досрочное завершение); клиент передаёт актуальный ExpectedVersion.
  • Постусловия (успех, Draft). Все поля черновика заменены значениями из тела, Scope пересчитан из измерений; статус остаётся Draft, Version увеличен; резолюция ставок не затронута.
  • Постусловия (успех, Active). Изменён только EffectiveTo (период сокращён); Version увеличен; с нового EffectiveTo ставка перестаёт применяться к новым начислениям — резолюция уходит на менее специфичный уровень или следующую по периоду ставку. Уже созданные начисления не пересчитываются — в них зафиксирован снапшот ставки (AppliedRateSnapshot).
  • Постусловия (отказ). Ставка без изменений.

Алгоритм

  1. Контекст и доступ. Операцию выполняет Менеджер ПИН в платформенном scope; требуется право rewards.referral-rates.manage.
  2. Проверка входа и предусловий. Percent (INV-RR-1) — иначе REWARDS_REFERRAL_RATE_PERCENT_OUT_OF_RANGE; для черновика — вложенность объектных измерений (ProjectId требует DeveloperCompanyId; LayoutGroupId требует ProjectId, INV-RR-4) — иначе REWARDS_REFERRAL_RATE_SCOPE_HIERARCHY_INVALID; согласованность периода — иначе REWARDS_REFERRAL_RATE_PERIOD_INVALID; длина Comment — иначе ValidationError. Ставка существует — иначе REWARDS_REFERRAL_RATE_NOT_FOUND. Статус ∈ {Draft, Active}; Archived не редактируется → REWARDS_REFERRAL_RATE_ACTIVE_IMMUTABLE. Version совпадает с ExpectedVersion — иначе REWARDS_CONCURRENCY_CONFLICT.
  3. Правка и переход.
    • Черновик (Status = Draft) — заменяются все поля из тела (полная замена PUT, включая ProjectId/LayoutGroupId); Scope пересчитывается из измерений; непересечение периодов (INV-RR-2) здесь не проверяется — коллизия всплывёт при активации (activate-referral-rate-rule.md); фиксируются актор и новая версия;
    • действующая ставка (Status = Active) — разрешено изменить только EffectiveTo и только сокращением: новое значение >= UtcNow (нельзя завершить задним числом — резолюция уже созданных начислений воспроизводима) и раньше текущего конца периода (либо текущий бессрочный). Расширение периода или изменение любого другого поля → REWARDS_REFERRAL_RATE_ACTIVE_IMMUTABLE (INV-RR-3); некорректное новое значение EffectiveToREWARDS_REFERRAL_RATE_PERIOD_INVALID. Сокращение периода пересечений не создаёт — повторная проверка INV-RR-2 не нужна.
  4. Результат. Полная проекция после правки (ReferralRateRuleModel, включая новый Version) → 200. Интеграционные события не публикуются.

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
REWARDS_REFERRAL_RATE_NOT_FOUND404Ставка не существует«Ставка не найдена»no retry
REWARDS_REFERRAL_RATE_ACTIVE_IMMUTABLE409Правка полей кроме EffectiveTo у Active; расширение периода Active; правка Archived (INV-RR-3)«Действующую ставку можно только досрочно завершить — создайте новую»no retry
REWARDS_REFERRAL_RATE_PERCENT_OUT_OF_RANGE400Percent вне (0; 100] (INV-RR-1)«Процент вне допустимого диапазона»no retry
REWARDS_REFERRAL_RATE_SCOPE_HIERARCHY_INVALID400Черновик: задан потомок без родителя (ProjectId без DeveloperCompanyId либо LayoutGroupId без ProjectId) (INV-RR-4)«Проект требует застройщика, группа планировок — проекта»no retry
REWARDS_REFERRAL_RATE_PERIOD_INVALID400EffectiveTo <= EffectiveFrom; для Active — новый EffectiveTo < UtcNow«Некорректный период действия»no retry
REWARDS_CONCURRENCY_CONFLICT409ExpectedVersion устарел«Ставка изменена другим пользователем»retry после refresh

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

Acceptance-критерии

  • Правка черновика. Дано: ставка в Draft (Version = N). Когда: PUT с валидными полями и ExpectedVersion = N. Тогда: 200, все поля заменены, Scope пересчитан, Status = Draft, Version = N + 1.
  • Углубление объектной оси черновика. Дано: черновик Scope = PartnerDeveloper (ProjectId, LayoutGroupId = null). Когда: PUT, где заданы ProjectId и LayoutGroupId (родители сохранены), остальное валидно. Тогда: 200, Scope = PartnerDeveloperProjectLayoutGroup.
  • Потомок без родителя в черновике. Дано: черновик. Когда: PUT, где задан LayoutGroupId при ProjectId = null (или ProjectId при DeveloperCompanyId = null). Тогда: 400 REWARDS_REFERRAL_RATE_SCOPE_HIERARCHY_INVALID (INV-RR-4), ставка не изменена.
  • Досрочное завершение действующей. Дано: ставка Active с EffectiveTo = T (или бессрочная). Когда: PUT, где изменён только EffectiveTo на T' ∈ [UtcNow; T), остальные поля равны текущим. Тогда: 200, EffectiveTo = T', Version + 1; новые начисления после T' эту ставку не применяют; созданные начисления не пересчитываются (снапшот).
  • Иммутабельность действующей. Дано: ставка Active. Когда: PUT с изменённым Percent (или измерениями, или EffectiveFrom). Тогда: 409 REWARDS_REFERRAL_RATE_ACTIVE_IMMUTABLE, ставка не изменена.
  • Расширение периода действующей. Дано: ставка Active с EffectiveTo = T. Когда: PUT с EffectiveTo > T. Тогда: 409 REWARDS_REFERRAL_RATE_ACTIVE_IMMUTABLE (только сокращение; продление — новая ставка с последующим периодом).
  • Завершение задним числом. Дано: ставка Active. Когда: PUT с EffectiveTo < UtcNow. Тогда: 400 REWARDS_REFERRAL_RATE_PERIOD_INVALID.
  • Правка архива. Дано: ставка Archived. Когда: PUT. Тогда: 409 REWARDS_REFERRAL_RATE_ACTIVE_IMMUTABLE.
  • Конкурентная версия. Дано: ExpectedVersion устарел. Когда: PUT. Тогда: 409 REWARDS_CONCURRENCY_CONFLICT; клиент перечитывает ставку и повторяет.

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

  • Контракт PUT полный: добавление нового optional поля требует одновременного обновления формы (иначе PUT затрёт значение).
  • Logs: ActorUserId, RateRuleId, ветка (draft-edit/close-period); metrics: rewards_referral_rate_update_total{result,branch}; traces: OTel; dashboard rewards-overview.

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

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

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