POST /api/v1/agency/agent-boost-campaigns/{campaignId}/activate — Активация boost-кампании
Перевод boost-кампании Draft → Active. С этого момента кампания участвует в резолюции удержания при
подтверждении начислений (Confirm — confirm-commission-accrual.md):
на asOf = CompletedAt в пределах периода кампании и для агентов из её набора применяется повышенная доля
BoostRule вместо базового правила политики (шаг 3 «Алгоритма применения политики»,
domain/agency-commission-policy.md). В отличие от политики,
активная кампания предыдущую не архивирует — у агентства может действовать несколько кампаний
одновременно при условии, что их периоды не пересекаются по общим агентам (INV-9 NoBoostOverlapPerAgent).
Назначение
Руководитель/администратор агентства вводит подготовленный черновик кампанию в действие. Owning-контекст —
rewards. Feature group — agent-boost (операция op59 реестра README.md).
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | rewards |
| API area | agency |
| Feature group | agent-boost |
| Статус | draft |
| Документ | docs/06-services/rewards/api/agency/activate-agent-boost-campaign.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (руководитель/администратор агентства) |
| Auth policy / Permissions | rewards.commission-policy.manage |
| Scope (RBAC) | организация: компания Type ∈ {Agency, Broker, Pin} |
| Record-level | не применяется (tenant-интерсептор ограничивает выборку своим агентством) |
| Tenant isolation | TenantId = CompanyId агентства из claims; чужая кампания → 404 |
| Audit | update: UpdatedByUserId, Version + 1; доменное событие AgentBoostCampaignActivatedEvent |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | POST |
| Route | /api/v1/agency/agent-boost-campaigns/{campaignId}/activate |
| Success status | 200 |
| Idempotency header | не применяется (повтор из Active → REWARDS_AGENT_BOOST_INVALID_STATUS_TRANSITION) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims (tenant_id) | Guid | Да | Required | Tenant isolation |
ActorUserId | identity claims (sub) | Guid | Да | Required | — |
CampaignId | route {campaignId} | Guid | Да | Required, непустой | — |
ExpectedVersion | body | long | Да | >= 1; сверка с Version агрегата | optimistic concurrency |
Модель ответа AgentBoostCampaignModel
Полная проекция под-агрегата AgentBoostCampaignEntity — та же модель, что возвращают create/list; дом
модели (поля Id, Name, Description, Status, BoostRule, AgentUserIds, EffectiveFrom,
EffectiveTo, CreatedAt, UpdatedAt, Version) — create-agent-boost-campaign.md;
дом полей под-агрегата — domain/agency-commission-policy.md. Здесь
— только дельта значений после активации (остальные поля наследуются от черновика без изменений):
| Поле | Значение после операции | Источник |
|---|---|---|
Status | Active (было Draft) | переход шага 4 |
Version | Version + 1 — новая база ExpectedVersion для последующих мутаций | version-stamping |
Masking BoostRule/AgentUserIds: полное правило и список агентов — только ролям с
rewards.commission-policy.{view,manage} (агенту отдаётся лишь применённый к нему boost в его проекциях — Masking-правила
README.md).
Алгоритм
- Контекст и доступ. Операцию выполняет руководитель/администратор агентства; scope — организация
агентства (
TenantId); требуется правоrewards.commission-policy.manage. Выборка ограничена своим агентством. - Проверка входа и предусловий.
CampaignIdнепустой,ExpectedVersion >= 1. Кампания существует в пределах своего агентства — иначеREWARDS_AGENT_BOOST_NOT_FOUND. Статус =Draft(единственный допустимый исходный) — иначеREWARDS_AGENT_BOOST_INVALID_STATUS_TRANSITION.Versionсовпадает сExpectedVersion— иначеREWARDS_CONCURRENCY_CONFLICT. - Проверка агентов и непересечения периодов.
- Все
Targets.AgentUserIdsдолжны существовать и быть активными в реплике состава пользователей агентства (INV-11, зависимостьidentity) — недостающие ids возвращаются вREWARDS_AGENT_BOOST_TARGETS_INVALID(перечислением в metadata ошибки). - Активные кампании агентства, пересекающиеся по хотя бы одному общему агенту, читаются под блокировкой
на запись; если для общего агента период
[EffectiveFrom, EffectiveTo|∞)активируемой кампании пересекается с периодом уже активной — отказREWARDS_AGENCY_POLICY_OVERLAP(INV-9): на любойasOfк агенту применима не более одной кампании (детерминизм резолюции, шаг 3 «Алгоритма применения политики»).
- Все
- Переход. Кампания переходит
Draft → Active(вActiveеёBoostRule,AgentUserIdsиEffectiveFromиммутабельны — меняется толькоEffectiveToв сторону сокращения, INV-10), проставляется актор, новая версия. Базовая политика и другие кампании не затрагиваются. Гонка параллельных активаций пересекающихся по агентам кампаний сериализуется блокировкой и возвращаетREWARDS_AGENCY_POLICY_OVERLAPпроигравшему (retry после пересмотра периодов/набора). - События. Интеграционные Kafka-события не публикуются — boost применяется исключительно внутри
rewards(приConfirm); фиксируется только доменный фактAgentBoostCampaignActivatedEvent. - Результат. Проекция активированной кампании (
AgentBoostCampaignModel,Status = Active, новыйVersion) →200.
Диаграмма
Ошибки и failure modes
Общие коды (ValidationError, FORBIDDEN, UNAUTHORIZED, REWARDS_CONCURRENCY_CONFLICT) —
README.md.
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
REWARDS_AGENT_BOOST_NOT_FOUND | 404 | Кампания не существует / чужой tenant | «Кампания не найдена» | no retry |
REWARDS_AGENT_BOOST_INVALID_STATUS_TRANSITION | 409 | Status != Draft | «Активировать можно только черновик кампании» | no retry |
REWARDS_AGENT_BOOST_TARGETS_INVALID | 400 | AgentUserIds содержит несуществующего/неактивного пользователя агентства (INV-11) | «Набор кампании указывает недоступного сотрудника» + ids в metadata | retry после синка identity-реплики |
REWARDS_AGENCY_POLICY_OVERLAP | 409 | Период активируемой кампании пересекается с периодом другой активной кампании по общему агенту (INV-9) | «Периоды кампаний по общему агенту пересекаются» | retry после пересмотра периода/набора |
REWARDS_CONCURRENCY_CONFLICT | 409 | ExpectedVersion/xmin конфликт | «Кампания изменена — обновите данные» | retry после refresh |
Acceptance criteria
- AC-1 (активация). Given черновик кампании (
Draft,Version = N), все агенты активны, период не пересекается с другими активными кампаниями по общим агентам; When руководитель активирует его сExpectedVersion = N; Then кампания переходитDraft → Active,Version = N+1; ответ200. Постусловие: последующиеConfirmначислений сasOfв периоде и агентом из набора применяютBoostRule. - AC-2 (несколько непересекающихся кампаний). Given уже действует кампания
B0на агентаXв январе; активируетсяB1на того жеXв марте (периоды не пересекаются); When активацияB1; Then200— обеActive, INV-9 соблюдён (на любойasOfкXприменима не более одной). - AC-3 (пересечение периодов по агенту). Given активная кампания
B0на агентаXв январе–марте; активируетсяB1, содержащаяX, с пересекающимся периодом; When активация; ThenREWARDS_AGENCY_POLICY_OVERLAP(409, INV-9) — повтор после сдвига периода или изъятияXиз набора. - AC-4 (агент недоступен). Given черновик с
AgentUserIds, где хотя бы один отсутствует/неактивен в реплике состава агентства (INV-11); When активация; ThenREWARDS_AGENT_BOOST_TARGETS_INVALID(400) со списком проблемных id в metadata; после синхронизации реплики — повтор. - AC-5 (недопустимый статус). Given кампания уже
ActiveилиArchived; When активация; ThenREWARDS_AGENT_BOOST_INVALID_STATUS_TRANSITION(409) — активировать можно толькоDraft. - AC-6 (устаревшая версия / чужой tenant). Given
ExpectedVersionне совпал —REWARDS_CONCURRENCY_CONFLICT(409); кампания другого агентства —REWARDS_AGENT_BOOST_NOT_FOUND(404, без раскрытия). - AC-7 (гонка активации и подтверждения начисления). Given одновременно идут активация кампании и
подтверждение начисления (
Confirm), считающее распределение; When обе операции борются за блокировку; Then они сериализуются: еслиConfirmуспел прочитать состояние до активации — boost не применён (AppliedBoostCampaignId = null); если после — применён. Промежуточного состояния с двумя пересекающимися кампаниями по агенту не наблюдается (INV-9); уже подтверждённые начисления не пересчитываются.
Совместимость и наблюдаемость
- Kafka-контракт не затрагивается (событий нет). Если появится потребитель изменений boost — добавить
публикацию через additive-процедуру событийного реестра (дом domain-дока, общий топик слоёв
pin.rewards.commission-rules.v1). - Logs:
TenantId,CampaignId,AgentsCount,EffectiveFrom/EffectiveTo(ставки не логируются); metrics:rewards_agent_boost_activate_total{result},rewards_agent_boost_overlap_total(гонка INV-9); traces: OTel; dashboardrewards-overview. - НФТ (ADR-0056): переход одного агрегата + сверка десятков
AgentUserIdsпо локальной реплике identity + проверка непересечения по единицам активных кампаний (app-level, ADR-0056); целевая латентность p95 < 400 мс; частота — редкая (заведение кампаний — единичные операции).
Обратные ссылки
Автоссылки: где используется этот документ.
- API (4): GET /api/v1/agency/agent-boost-campaigns — Список boost-кампаний агентов, POST /api/v1/agency/agent-boost-campaigns — Создание boost-кампании агентов (черновик), POST /api/v1/agency/agent-boost-campaigns/{campaignId}/archive — Архивирование boost-кампании, Rewards API — реестр операций и общие контрактные модели
- Use Cases (2): UC-RWD-015. Настройка индивидуальных условий вознаграждения по области и сроку (акции и boost), US-реестр: Владелец агентства (US-OWN-AG)
- registries (1): Реестр API-методов PIN
Связанные документы
- Под-агрегат и инварианты: domain/agency-commission-policy.md (INV-8..12, stateDiagram boost, «Алгоритм применения политики», шаг 3).
- Точка применения: confirm-commission-accrual.md (резолюция
удержания при
Confirm). - Жизненный цикл: create-agent-boost-campaign.md, archive-agent-boost-campaign.md, list-agent-boost-campaigns.md.