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

POST /api/v1/agency/agent-boost-campaigns/{campaignId}/activate — Активация boost-кампании

Перевод boost-кампании Draft → Active. С этого момента кампания участвует в резолюции удержания при подтверждении начислений (Confirmconfirm-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 areaagency
Feature groupagent-boost
Статусdraft
Документdocs/06-services/rewards/api/agency/activate-agent-boost-campaign.md

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

ПроверкаЗначение
Actor typeUser (руководитель/администратор агентства)
Auth policy / Permissionsrewards.commission-policy.manage
Scope (RBAC)организация: компания Type ∈ {Agency, Broker, Pin}
Record-levelне применяется (tenant-интерсептор ограничивает выборку своим агентством)
Tenant isolationTenantId = CompanyId агентства из claims; чужая кампания → 404
Auditupdate: UpdatedByUserId, Version + 1; доменное событие AgentBoostCampaignActivatedEvent

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/v1/agency/agent-boost-campaigns/{campaignId}/activate
Success status200
Idempotency headerне применяется (повтор из ActiveREWARDS_AGENT_BOOST_INVALID_STATUS_TRANSITION)
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
TenantIdidentity claims (tenant_id)GuidДаRequiredTenant isolation
ActorUserIdidentity claims (sub)GuidДаRequired
CampaignIdroute {campaignId}GuidДаRequired, непустой
ExpectedVersionbodylongДа>= 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. Здесь — только дельта значений после активации (остальные поля наследуются от черновика без изменений):

ПолеЗначение после операцииИсточник
StatusActive (было Draft)переход шага 4
VersionVersion + 1 — новая база ExpectedVersion для последующих мутацийversion-stamping

Masking BoostRule/AgentUserIds: полное правило и список агентов — только ролям с rewards.commission-policy.{view,manage} (агенту отдаётся лишь применённый к нему boost в его проекциях — Masking-правила README.md).

Алгоритм

  1. Контекст и доступ. Операцию выполняет руководитель/администратор агентства; scope — организация агентства (TenantId); требуется право rewards.commission-policy.manage. Выборка ограничена своим агентством.
  2. Проверка входа и предусловий. CampaignId непустой, ExpectedVersion >= 1. Кампания существует в пределах своего агентства — иначе REWARDS_AGENT_BOOST_NOT_FOUND. Статус = Draft (единственный допустимый исходный) — иначе REWARDS_AGENT_BOOST_INVALID_STATUS_TRANSITION. Version совпадает с ExpectedVersion — иначе REWARDS_CONCURRENCY_CONFLICT.
  3. Проверка агентов и непересечения периодов.
    • Все Targets.AgentUserIds должны существовать и быть активными в реплике состава пользователей агентства (INV-11, зависимость identity) — недостающие ids возвращаются в REWARDS_AGENT_BOOST_TARGETS_INVALID (перечислением в metadata ошибки).
    • Активные кампании агентства, пересекающиеся по хотя бы одному общему агенту, читаются под блокировкой на запись; если для общего агента период [EffectiveFrom, EffectiveTo|∞) активируемой кампании пересекается с периодом уже активной — отказ REWARDS_AGENCY_POLICY_OVERLAP (INV-9): на любой asOf к агенту применима не более одной кампании (детерминизм резолюции, шаг 3 «Алгоритма применения политики»).
  4. Переход. Кампания переходит Draft → ActiveActive её BoostRule, AgentUserIds и EffectiveFrom иммутабельны — меняется только EffectiveTo в сторону сокращения, INV-10), проставляется актор, новая версия. Базовая политика и другие кампании не затрагиваются. Гонка параллельных активаций пересекающихся по агентам кампаний сериализуется блокировкой и возвращает REWARDS_AGENCY_POLICY_OVERLAP проигравшему (retry после пересмотра периодов/набора).
  5. События. Интеграционные Kafka-события не публикуются — boost применяется исключительно внутри rewards (при Confirm); фиксируется только доменный факт AgentBoostCampaignActivatedEvent.
  6. Результат. Проекция активированной кампании (AgentBoostCampaignModel, Status = Active, новый Version) → 200.

Диаграмма

Ошибки и failure modes

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

КодHTTP statusУсловиеСообщениеRetry
REWARDS_AGENT_BOOST_NOT_FOUND404Кампания не существует / чужой tenant«Кампания не найдена»no retry
REWARDS_AGENT_BOOST_INVALID_STATUS_TRANSITION409Status != Draft«Активировать можно только черновик кампании»no retry
REWARDS_AGENT_BOOST_TARGETS_INVALID400AgentUserIds содержит несуществующего/неактивного пользователя агентства (INV-11)«Набор кампании указывает недоступного сотрудника» + ids в metadataretry после синка identity-реплики
REWARDS_AGENCY_POLICY_OVERLAP409Период активируемой кампании пересекается с периодом другой активной кампании по общему агенту (INV-9)«Периоды кампаний по общему агенту пересекаются»retry после пересмотра периода/набора
REWARDS_CONCURRENCY_CONFLICT409ExpectedVersion/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; Then 200 — обе Active, INV-9 соблюдён (на любой asOf к X применима не более одной).
  • AC-3 (пересечение периодов по агенту). Given активная кампания B0 на агента X в январе–марте; активируется B1, содержащая X, с пересекающимся периодом; When активация; Then REWARDS_AGENCY_POLICY_OVERLAP (409, INV-9) — повтор после сдвига периода или изъятия X из набора.
  • AC-4 (агент недоступен). Given черновик с AgentUserIds, где хотя бы один отсутствует/неактивен в реплике состава агентства (INV-11); When активация; Then REWARDS_AGENT_BOOST_TARGETS_INVALID (400) со списком проблемных id в metadata; после синхронизации реплики — повтор.
  • AC-5 (недопустимый статус). Given кампания уже Active или Archived; When активация; Then REWARDS_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; dashboard rewards-overview.
  • НФТ (ADR-0056): переход одного агрегата + сверка десятков AgentUserIds по локальной реплике identity + проверка непересечения по единицам активных кампаний (app-level, ADR-0056); целевая латентность p95 < 400 мс; частота — редкая (заведение кампаний — единичные операции).

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

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

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