POST /api/v1/platform/commission-terms/{termsId}/activate — Активация условий платформенной комиссии
Перевод условий Draft → Active: с этого момента условия участвуют в резолюции удержания при создании
начислений (с учётом периода EffectiveFrom/EffectiveTo, резолюция по asOf = CompletedAt сделки). При
активации проверяется непересечение периодов активных условий одной специфичности (INV-PT-2) — гарантия
детерминированной резолюции — domain/platform-commission-terms.md.
Назначение
Менеджер ПИН вводит условия удержания в действие. Активация не архивирует предыдущие условия:
активные условия одной специфичности сосуществуют с непересекающимися периодами (текущие + будущая акция).
Плановая смена условий — это досрочное завершение текущих через EffectiveTo
(update-platform-terms.md) и активация новых на смежный период.
Owning-контекст — rewards. Feature group — platform-terms.
| Поле | Значение |
|---|
| Сервис/контекст | rewards |
| API area | platform |
| Feature group | platform-terms |
| Статус | draft |
| Документ | docs/06-services/rewards/api/platform/activate-platform-terms.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User (Менеджер ПИН) |
| Auth policy / Permissions | rewards.platform-terms.manage |
| Scope (RBAC) | платформа |
| Tenant isolation | вне tenant-модели — платформенная сущность (ADR-0052) |
| Audit | update: UpdatedByUserId, Version + 1, доменное событие PlatformTermsActivatedEvent |
HTTP-контракт
| Поле | Значение |
|---|
| Method | POST |
| Route | /api/v1/platform/commission-terms/{termsId}/activate |
| Success status | 200 |
| Idempotency header | не применяется (повтор из Active → REWARDS_PLATFORM_TERMS_INVALID_STATUS_TRANSITION) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
ActorUserId | identity claims (sub) | Guid | Да | Required | — |
TermsId | route {termsId} | Guid | Да | Required, непустой | — |
ExpectedVersion | body | long | Да | >= 1; сверка с Version | optimistic concurrency |
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
Terms | PlatformCommissionTermsModel | Да | обновлённый агрегат | — | Проекция после активации (Status = Active); модель — list-platform-terms.md. |
Предусловия и постусловия
- Предусловия. Актор — Менеджер ПИН с
rewards.platform-terms.manage; условия существуют в статусе
Draft; период не истёк; среди активных условий той же специфичности нет пересечения периодов; клиент
передаёт актуальный ExpectedVersion.
- Постусловия (успех). Условия в статусе
Active, Version увеличен, актор зафиксирован; условия
участвуют в резолюции удержания для начислений, чей asOf = CompletedAt попадает в
[EffectiveFrom, EffectiveTo|∞); внутри специфичности на любой момент — не более одного активного
кандидата (INV-PT-2, детерминированность). Уже созданные начисления не пересчитываются — в них
зафиксирован снапшот (PlatformRetentionPercentagePoints, AppliedPlatformTermsId —
domain/commission-accrual.md).
- Постусловия (отказ). Условия остаются в исходном статусе, версия не меняется.
- Граница «активны, но ещё не действуют». Условия с
EffectiveFrom в будущем после активации в
резолюцию попадают только с наступлением EffectiveFrom — механизм заблаговременной подготовки акций.
Алгоритм
- Контекст и доступ. Операцию выполняет Менеджер ПИН в платформенном scope; требуется право
rewards.platform-terms.manage.
- Проверка входа и предусловий.
TermsId непустой, ExpectedVersion >= 1. Условия существуют —
иначе REWARDS_PLATFORM_TERMS_NOT_FOUND. Статус = Draft (единственный допустимый исходный) — иначе
REWARDS_PLATFORM_TERMS_INVALID_STATUS_TRANSITION. Version совпадает с ExpectedVersion — иначе
REWARDS_CONCURRENCY_CONFLICT. Если EffectiveTo задан и уже в прошлом —
REWARDS_PLATFORM_TERMS_PERIOD_INVALID (нельзя активировать истёкшие условия).
- Непересечение периодов (INV-PT-2). Активные условия с тем же набором заполненных измерений
(
AgencyCompanyId, DeveloperCompanyId, ProjectId, LayoutGroupId), включая null-значения,
читаются под блокировкой; если период [EffectiveFrom, EffectiveTo|∞) активируемых условий
пересекается хотя бы с одним из них — отказ REWARDS_PLATFORM_TERMS_OVERLAP (id конфликтующих условий
— в metadata ошибки; менеджер либо досрочно завершает действующие через
update-platform-terms.md, либо правит период черновика). Пересечение
проверяется только внутри одной комбинации измерений: условия разных уровней специфичности
пересекаться могут — конфликт разрешает порядок «специфичное побеждает».
- Переход и запись. Условия переходят
Draft → Active; фиксируются актор и новая версия. Переход
сохраняется в одном согласованном изменении с проверкой п.3 (гонка двух активаций пересекающихся
черновиков одной комбинации — под блокировкой активных условий комбинации: победитель активируется,
проигравший получает REWARDS_PLATFORM_TERMS_OVERLAP).
- Результат. Проекция активированных условий (
PlatformCommissionTermsModel, Status = Active) →
200. Интеграционные события не публикуются — условия применяются только внутри rewards при
создании начисления.
Диаграмма
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
REWARDS_PLATFORM_TERMS_NOT_FOUND | 404 | Условия не существуют | «Условия не найдены» | no retry |
REWARDS_PLATFORM_TERMS_INVALID_STATUS_TRANSITION | 409 | Status != Draft (уже Active / Archived) | «Активировать можно только черновик» | no retry |
REWARDS_PLATFORM_TERMS_OVERLAP | 409 | Период пересекается с активными условиями той же специфичности (INV-PT-2) | «Период пересекается с действующими условиями» + id конфликтующих в metadata | no retry (сначала завершить/поправить конфликтующие) |
REWARDS_PLATFORM_TERMS_PERIOD_INVALID | 400 | EffectiveTo уже в прошлом | «Период действия уже истёк» | no retry |
REWARDS_CONCURRENCY_CONFLICT | 409 | ExpectedVersion устарел | «Условия изменены другим пользователем» | retry после refresh |
Общие коды — README.md.
Acceptance-критерии
- Happy path. Дано: черновик условий уровня
AgencyProject, период не пересекается с активными
условиями той же комбинации измерений, ExpectedVersion актуален. Когда: POST .../activate. Тогда:
200, Status = Active, Version + 1; начисления с CompletedAt внутри периода применяют эти
условия.
- Первая активация комбинации. Дано: активных условий этой комбинации измерений нет. Когда:
POST .../activate. Тогда: 200; до этого момента резолюция комбинации падала на менее специфичный уровень /
fallback 0 п.п.
- Пересечение периодов. Дано: активные условия той же комбинации с периодом
[t0, ∞); черновик с
EffectiveFrom = t0 + 30дней. Когда: POST .../activate. Тогда: 409
REWARDS_PLATFORM_TERMS_OVERLAP с id конфликтующих условий; после досрочного завершения действующих
(EffectiveTo = t0 + 30дней) повторная активация проходит — периоды смыкаются без пересечения.
- Разные специфичности пересекаются свободно. Дано: активные
Global-условия на [t0, ∞); черновик
AgencyLayoutGroup с периодом внутри. Когда: POST .../activate. Тогда: 200 — уровни разные,
резолюция выбирает более специфичный (INV-PT-2 действует внутри комбинации измерений).
- Будущая акция. Дано: черновик с
EffectiveFrom в будущем, пересечений нет. Когда: POST .../activate. Тогда: 200, Status = Active; в резолюцию условия попадут с наступлением
EffectiveFrom.
- Истёкший период. Дано:
EffectiveTo уже в прошлом. Когда: POST .../activate. Тогда: 400
REWARDS_PLATFORM_TERMS_PERIOD_INVALID.
- Не черновик. Дано: условия уже
Active или Archived. Когда: POST .../activate. Тогда: 409
REWARDS_PLATFORM_TERMS_INVALID_STATUS_TRANSITION.
- Существующие начисления не пересчитываются. Дано: начисление со снапшотом прежних условий. Когда:
активация новых условий той же комбинации на смежный период. Тогда: снапшот начисления
(
PlatformRetentionPercentagePoints, AppliedPlatformTermsId) не меняется.
- Гонка двух активаций. Дано: два черновика одной комбинации измерений с пересекающимися периодами.
Когда: одновременный
POST .../activate обоих. Тогда: один — 200, другой — 409
REWARDS_PLATFORM_TERMS_OVERLAP (проверка под блокировкой активных условий комбинации).
- Конкурентная версия. Дано:
ExpectedVersion устарел. Когда: POST. Тогда: 409
REWARDS_CONCURRENCY_CONFLICT.
Совместимость и наблюдаемость
- Проверка INV-PT-2 — app-level под блокировкой (записей единицы, ADR-0056), db-индекса непересечения нет
— блокировка активных условий комбинации обязательна.
- Guard INV-PT-4 (
RetentionPercentagePoints < CommissionPercent применённого правила застройщика)
проверяется не при активации, а при создании начисления — ставки застройщиков различны и заранее
неизвестны; нарушение уводит событие сделки в DLQ
(REWARDS_PLATFORM_RETENTION_EXCEEDS_RULE, domain/platform-commission-terms.md).
- Logs:
ActorUserId, TermsId, Scope, период; metrics:
rewards_platform_terms_activate_total{result}; traces: OTel; dashboard rewards-overview.
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы