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

POST /api/v1/platform/referral-rate-rules — Создание ставки реферального вознаграждения (черновик)

Создание ставки реферального вознаграждения в статусе Draft: измерения специфичности — партнёрская ось (PartnerUserId?) и объектная ось-цепочка (DeveloperCompanyId? ⊃ ProjectId? ⊃ LayoutGroupId?), процент от конечной цены сделки и период действия — domain/referral-rate-rule.md.

Назначение

Менеджер ПИН настраивает, какой процент от FinalPrice завершённой сделки получает партнёр, приведший инвестора: персональные ставки, акции по застройщику, временно повышенные ставки, глобальный страховочный уровень. Ставка создаётся черновиком и не участвует в резолюции до активации. Owning-контекст — rewards. Feature group — referral-rates.

Metadata

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

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

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

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/v1/platform/referral-rate-rules
Success status201 (+ Location: /api/v1/platform/referral-rate-rules/{id})
Idempotency headerIdempotency-Key (Guid)
Correlationсквозной trace через заголовок traceparent

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

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

Специфичность ставки (ReferralRateScope: 8 уровней от PartnerDeveloperProjectLayoutGroup до Global) не передаётся — выводится из заполненности партнёрской (PartnerUserId) и объектной (DeveloperCompanyIdProjectIdLayoutGroupId) осей (domain/referral-rate-rule.md).

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

ПолеТипОбязательностьИсточникMaskingОписание
IdGuidДаReferralRateRuleEntity.IdId созданной ставки.
Scopestring (enum ReferralRateScope)Даcomputed из измеренийВыведенный уровень специфичности.
Statusstring (enum ReferralRateRuleStatus)ДаStatusВсегда Draft.
VersionlongДаVersion1 — для последующего ExpectedVersion.

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

  • Предусловия. Актор — Менеджер ПИН с правом rewards.referral-rates.manage (платформенный scope).
  • Постусловия (успех). Появилась ровно одна новая ставка в статусе Draft с Version = 1 и зафиксированным автором; в резолюции ставки при создании реферальных начислений (domain/referral-accrual.md) черновик не участвует.
  • Постусловия (отказ). Ни одна ставка не создана; повтор с тем же Idempotency-Key после успеха возвращает исходный результат и второй ставки не создаёт.

Алгоритм

  1. Контекст и доступ. Операцию выполняет Менеджер ПИН в платформенном scope; требуется право rewards.referral-rates.manage. Повторный вызов с тем же IdempotencyKey возвращает ранее сохранённый результат.
  2. Проверка входа. Percent в (0; 100] (INV-RR-1) — иначе REWARDS_REFERRAL_RATE_PERCENT_OUT_OF_RANGE; вложенность объектных измерений (ProjectId требует DeveloperCompanyId; LayoutGroupId требует ProjectId) (INV-RR-4) — иначе REWARDS_REFERRAL_RATE_SCOPE_HIERARCHY_INVALID; EffectiveTo > EffectiveFrom, если задан — иначе REWARDS_REFERRAL_RATE_PERIOD_INVALID; длина Comment — иначе ValidationError. Существование партнёра/застройщика/проекта/группы планировок синхронно не проверяется (ответственность актора). Непересечение периодов одной комбинации измерений (INV-RR-2) на черновике не проверяется — черновики могут пересекаться, коллизия всплывёт при активации (REWARDS_REFERRAL_RATE_OVERLAP), поэтому будущие акции готовятся заранее.
  3. Создание черновика. Формируется ставка в статусе Draft: PartnerUserId, DeveloperCompanyId, ProjectId, LayoutGroupId, Percent, EffectiveFrom/EffectiveTo, Comment — из тела; фиксируются актор, CreatedAt, Version = 1. Ставка и запись идемпотентности сохраняются в одном согласованном изменении.
  4. Результат. Id, выведенный Scope, Status = Draft, Version = 1201 + Location. Интеграционные события не публикуются (ставка применяется только внутри rewards).

Ошибки и failure modes

КодHTTP statusУсловиеСообщение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«Некорректный период действия»no retry

Общие коды (ValidationError, FORBIDDEN, UNAUTHORIZED) — README.md.

Acceptance-критерии

  • Happy path (персональная ставка по застройщику). Дано: Менеджер ПИН с rewards.referral-rates.manage; PartnerUserId и DeveloperCompanyId заданы (ProjectId, LayoutGroupIdnull), Percent = 1.50, корректный период. Когда: POST /api/v1/platform/referral-rate-rules. Тогда: 201 + Location, Scope = PartnerDeveloper, Status = Draft, Version = 1; резолюция ставок черновик не применяет.
  • Акция на группу планировок проекта. Дано: DeveloperCompanyId, ProjectId, LayoutGroupId заданы, PartnerUserId = null, Percent = 3.00, период акции. Когда: POST. Тогда: 201, Scope = DeveloperProjectLayoutGroup — временно повышенная ставка для всех партнёров по группе.
  • Персональная ставка по конкретному проекту. Дано: PartnerUserId, DeveloperCompanyId, ProjectId заданы, LayoutGroupId = null. Когда: POST. Тогда: 201, Scope = PartnerDeveloperProject.
  • Глобальная страховочная ставка. Дано: все измерения null, EffectiveTo = null. Когда: POST. Тогда: 201, Scope = Global — бессрочный страховочный уровень (заводится первым шагом внедрения).
  • Процент вне диапазона. Дано: Percent = 0 или > 100. Когда: POST. Тогда: 400 REWARDS_REFERRAL_RATE_PERCENT_OUT_OF_RANGE, ставка не создана.
  • Потомок без родителя. Дано: ProjectId задан, DeveloperCompanyId = null (или LayoutGroupId задан при ProjectId = null). Когда: POST. Тогда: 400 REWARDS_REFERRAL_RATE_SCOPE_HIERARCHY_INVALID, ставка не создана (INV-RR-4).
  • Некорректный период. Дано: EffectiveTo <= EffectiveFrom. Когда: POST. Тогда: 400 REWARDS_REFERRAL_RATE_PERIOD_INVALID.
  • Пересечение на черновике допустимо. Дано: активная ставка той же комбинации измерений с пересекающимся периодом. Когда: POST. Тогда: 201 — непересечение (INV-RR-2) проверяется только при активации.
  • Идемпотентный повтор. Дано: ставка уже создана с ключом K. Когда: повторный POST с тем же Idempotency-Key = K. Тогда: тот же Id/Scope/Status/Version, второй ставки нет.
  • Нет права. Дано: актор без rewards.referral-rates.manage. Когда: POST. Тогда: 403 FORBIDDEN.

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

  • Новые уровни специфичности — additive-расширение enum ReferralRateScope (клиент игнорирует unknown при чтении); измерения ставки расширяются только nullable-полями.
  • Logs: ActorUserId, RateRuleId, Scope, PartnerUserId, DeveloperCompanyId, ProjectId, LayoutGroupId; metrics: rewards_referral_rate_create_total{result}; traces: OTel; dashboard rewards-overview.

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

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

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