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

UC-SUP-004. Ручное вмешательство через permissions

Назначение

Описывает ручное вмешательство Службы заботы в чужие сервисы (матрица 3.x): смена статуса/переназначение сделки в crm, блокировка пользователя / правка роли в identity, корректировка комиссии в rewards. Ключ границы: вмешательство выполняется в owning-сервисе через выданные support platform-scope permissions; сами permissions живут и проверяются в owning-сервисе, support их не определяет и не дублирует. Support лишь фиксирует факт/ссылку заметкой арбитражного дела (ArbitrageNoteEntity.AuditRefs) — чужие таблицы не пишет. Связан с контекстами support (+ crm/identity/rewards); агрегат support — ArbitrageCaseEntity. Покрывает README решение №2.

Metadata

ПолеЗначение
IDUC-SUP-004
Контекстsupport (вмешательство — crm/identity/rewards)
Типосновной (+ альтернативные потоки A1–A3)
Статусdraft

Актор

ПолеЗначение
РольСлужбаЗаботы — Оператор (с выданными интервенционными правами) / Супервизор (финансы/блокировка, 3.6/3.8)
Permissions / scopeвмешательство: crm.deals.manage, identity.users.block, identity.role-assignments.manage, rewards.accruals.manageplatform-scope, назначены support-роли, проверяются в owning-сервисе; фиксация — support.arbitrage.manage (ADR-0052)
Tenantвмешательство — кросс-tenant через platform-scope (owning-сервис резолвит TenantId цели); support-заметка — TenantId = PIN CompanyId
Record-levelв owning-сервисе — по его правилам; в support — по AssigneeUserId дела

Предусловие

  • Идёт разбор (UC-SUP-003) или обращение, требующее ручной правки в чужом сервисе.
  • Оператору назначены нужные интервенционные permissions в platform-scope (SEED-каталог owning-сервисов, не support).
  • Целевой ресурс (сделка/пользователь/начисление) существует в owning-сервисе.
  • Для фиксации факта существует арбитражное дело в статусе Registered/UnderInvestigation (иначе заметка запрещена, INV-ARB-5).

Гарантируется после сценария: в owning-сервисе применена ручная правка (его аудит несёт actor = support-оператор, событие — из его outbox); в support добавлена append-only заметка с типизированными AuditRefs, ArbitrageCaseEntity.Version++; support не писал чужие таблицы и не публиковал интеграционных событий по правке.

Триггер

ПолеЗначение
ИсточникUI button / API call
Триггерящий элементКарточка дела/тикета Службы заботы со сквозными действиями по чужим сервисам

Основной Поток

  1. Оператор в карточке дела определяет необходимое вмешательство (например, конфликт бронирований требует отката статуса сделки).
  2. Вмешательство в crm. Оператор → POST /api/pin-manager/crm/deals/{id}/override-status (crm override-deal-status); авторизация crm.deals.manage проверяется в crm (platform-scope), не в support.
  3. crm применяет override статуса сделки, пишет таймлайн и публикует pin.crm.deal-stage-changed.v1 из своего outbox. Support синхронного ответа ждёт как обычный API-клиент.
  4. Фиксация факта в support. Оператор → POST /api/support/arbitrage-cases/{id}/notes (add-arbitrage-note) с AuditRefs = [{ Service = "crm", EntityType = "Deal", EntityId, ChangeAt, Note }].
  5. Добавляется append-only заметка с AuditRefs (INV-ARB-5); поднимается доменное событие ArbitrageNoteAdded; support не пишет в таблицы crm/identity/rewards.
  6. Фиксируется только заметка арбитражного дела support; интеграционного Kafka-события support здесь не публикует (событие о правке — на стороне owning-сервиса).
  7. Аудит: факт вмешательства виден в таймлайне owning-сервиса (actor-stamping = support-оператор) и в AuditRefs дела; централизованный аудит-лог только читается.
  8. Фронт обновляет карточку дела и (при сквозном просмотре) карточку сделки; стороны узнают об изменении через события owning-сервиса (notifications).

Альтернативные Потоки

A1. Блокировка/разблокировка пользователя в identity

  1. Супервизор → POST /api/v1/companies/my/users/{userId}/block (identity block-company-user) в platform-scope (identity.users.block); identity публикует pin.identity.users.v1.
  2. Support фиксирует AuditRefs = [{ Service = "identity", EntityType = "User", EntityId = userId }] заметкой дела.

Возвращается к основному потоку на шаг 4.

A2. Правка ролей/прав в identity

  1. Оператор → отзыв/выдача роли (revoke-role-assignment / grant-role-assignment) с identity.role-assignments.manage (platform-scope).
  2. Изменения прав применяет identity; support фиксирует ссылку в AuditRefs.

Возвращается к основному потоку на шаг 4.

A3. Корректировка комиссии в rewards

  1. По решению арбитража (UC-SUP-003) — ручное начисление → rewards create-manual-accrual (rewards.accruals.manage); rewards публикует pin.rewards.commission-accrual-created.v1.
  2. Support фиксирует AuditRefs = [{ Service = "rewards", EntityType = "Accrual", EntityId }].

Возвращается к основному потоку на шаг 4.

A4. Owning-guard отклоняет правку

  1. Правка нарушает бизнес-инвариант owning-сервиса: override терминальной сделки → CRM_DEAL_ALREADY_TERMINAL (409); повторная блокировка → IDENTITY_USER_ALREADY_BLOCKED (409). Правки не происходит; support заметку не пишет (фиксировать нечего).

Завершается ошибкой owning-сервиса; оператор корректирует намерение.

A5. Owning-сервис недоступен

  1. crm/identity/rewards недоступен → CRM_UNAVAILABLE / IDENTITY_UNAVAILABLE (503). Правка не применена, support-заметка не создаётся (нет подтверждённого факта). Оператор повторяет позже; идемпотентность самой правки обеспечивает owning-сервис.

Завершается ошибкой доступности; повтор позже.

A6. Нет интервенционного права в owning-сервисе

  1. У support-оператора нет назначенного platform-scope permission (crm.deals.manage и т.п.) → FORBIDDEN (403) из owning-сервиса (не из support). Правки нет; заметка не создаётся.

Завершается отказом доступа owning-сервиса; запросить назначение права.

A7. Фиксация после решения дела

  1. Дело, к которому привязывается заметка, уже Resolved/Escalated/ClosedSUPPORT_ARBITRAGE_NOTE_FORBIDDEN (409, INV-ARB-5). Правка в owning-сервисе могла примениться, но факт фиксируется в аудите owning-сервиса; в support заметку добавить нельзя (дело закрыто для записи).

Завершается ошибкой; факт остаётся в аудите owning-сервиса.

Постусловие

  • В owning-сервисе применена ручная правка (статус сделки/блок пользователя/роль/начисление); его аудит несёт actor = support-оператор.
  • В support добавлена append-only ArbitrageNoteEntity с типизированными AuditRefs; ArbitrageCaseEntity.Version++.
  • Support чужие таблицы не мутировал и интеграционных событий по правке не публиковал (события — из owning-сервисов).

Возможные Ошибки

КодHTTPУсловиеПоведение клиента
FORBIDDEN403нет platform-scope permission в owning-сервисе (crm.deals.manage и т.п.)Скрыть действие вмешательства
CRM_DEAL_ALREADY_TERMINAL409override терминальной сделки (owning-guard)Показать «сделка завершена»
IDENTITY_USER_ALREADY_BLOCKED409повторная блокировка (owning-guard)Показать текущий статус
SUPPORT_ARBITRAGE_NOT_FOUND404дело для фиксации не найденоОбновить реестр дел
SUPPORT_ARBITRAGE_NOTE_FORBIDDEN409заметка после решения (INV-ARB-5)Заметки только для чтения
IDENTITY_UNAVAILABLE / CRM_UNAVAILABLE503owning-сервис недоступенПовторить позже

Доменные События

СобытиеКогда поднимаетсяSubscribers
ArbitrageNoteAddedArbitrageCase.AddNote (фиксация факта)таймлайн дела (интеграционного события support нет)
DealStatusChangedcrm)Deal.OverrideStatus в owning-сервисесборщик DealStageChangedEvent (crm)
UserBlockedidentity)блокировка в owning-сервисесборщик UserBlockedKafkaEvent (identity)

Kafka Интеграционные События

Support по факту вмешательства не публикует событий (граница: правка — в owning-сервисе). События исходят из owning-сервисов:

СобытиеTopicOwning-сервисConsumers
DealStageChangedEventpin.crm.deal-stage-changed.v1crm (шаг 3)messenger, notifications
UserBlockedKafkaEventpin.identity.users.v1identity (A1)подписчики users (в т.ч. support реплики)
CommissionAccrualCreatedEventpin.rewards.commission-accrual-created.v1rewards (A3)notifications

Кросс-сервисные ожидания и согласованность

Ожидает от соседей (вход) — граница ответственности

  • Owning-сервис проверяет право и инвариант. Авторизация интервенционного permission (crm.deals.manage, identity.users.block, identity.role-assignments.manage, rewards.accruals.manage) выполняется в owning-сервисе (platform-scope), не в support. Owning-guards (CRM_DEAL_ALREADY_TERMINAL, IDENTITY_USER_ALREADY_BLOCKED) — тоже на стороне соседа. support вызывает как обычный синхронный API-клиент и ждёт ответа.
  • Owning-сервис — source of truth правки и её события. crm/identity/rewards применяют изменение, ставят actor = support-оператор в своём аудите (actor-stamping) и публикуют своё интеграционное событие из своего outbox. support этих таблиц не пишет и permissions не дублирует.

Предоставляет соседям (выход)

  • По факту вмешательства support событий НЕ публикует (граница: правка — в owning-сервисе). Единственный эффект в support — append-only ArbitrageNoteEntity с типизированными AuditRefs (Service/EntityType/ EntityId/ChangeAt?/Note?) и доменное ArbitrageNoteAdded (внутренний таймлайн, интеграционного события нет).

Последовательность и окно согласованности

  • Порядок (важно): сначала синхронная правка в owning-сервисе (шаг 2–3), затем фиксация факта заметкой в support (шаг 4–6). Если правка не подтверждена (guard/недоступность/403) — заметку не пишем (A4–A6): инвариант «фиксируем только состоявшийся факт».
  • Событие owning-сервиса (deal-stage-changed/users/commission-accrual-created) доходит до своих потребителей (messenger/notifications/реплики) асинхронно, окно < 1 мин — именно оно уведомляет стороны об изменении, не support. В промежутке карточка дела в support уже несёт AuditRef, а стороны узна́ют об изменении по мере доставки события соседа.

Идемпотентность и конкурентность

  • Идемпотентность самой правки — на стороне owning-сервиса (его guards/версии). Повтор support-заметки на тот же AuditRef допустим (append-only фиксирует попытки), но семантически дубль-ссылку арбитр не добавляет.
  • Заметка после решения дела запрещена (INV-ARB-5, A7); гонка правки в support — оптимистичная блокировка на ArbitrageCaseEntity (проигравший перечитывает).

Acceptance Criteria

#Given (дано)When (когда)Then (тогда)
AC1support-оператор без crm.deals.manageвызывает override статуса сделки в crmFORBIDDEN (403) из crm, не из support; правки нет
AC2Правка применена в owning-сервисевмешательство завершеноsupport не пишет в таблицы crm/identity/rewards (только своя заметка)
AC3Правка состоялась, дело UnderInvestigationоператор фиксирует фактappend-only заметка с типизированными AuditRefs (Service/EntityType/EntityId)
AC4Вмешательство завершенопроверяется outbox supportsupport не публикует Kafka-событие по правке; событие исходит из owning-сервиса
AC5Правка в crm/identityпроверяется таймлайн owning-сервисаactor = support-оператор (actor-stamping)
AC6Дело Resolved/Escalated/Closedоператор добавляет заметкуSUPPORT_ARBITRAGE_NOTE_FORBIDDEN (INV-ARB-5)
AC7Owning-сервис недоступен или guard отклонил (терминальная сделка/уже заблокирован)оператор вмешиваетсяCRM_UNAVAILABLE/IDENTITY_UNAVAILABLE (503) или CRM_DEAL_ALREADY_TERMINAL/IDENTITY_USER_ALREADY_BLOCKED (409); support-заметка не создаётся

Наблюдаемость и НФТ

  • Латентность: синхронный вызов owning-сервиса + фиксация заметки — p95 < 1 с (зависит от соседа); таймаут/недоступность соседа → 503 без записи заметки.
  • Логи (без PII): support-intervention-recorded (caseId, service, entityType, entityId, actor) — только ссылка/факт, без тел правок; правку логирует owning-сервис.
  • Метрики: число вмешательств по типам (crm/identity/rewards), доля отказов owning-guards/403/503, задержка «правка → фиксация заметки».
  • Доступ/безопасность: интервенционные permissions живут и проверяются в owning-сервисах (platform-scope), support их не определяет; фиксация — support.arbitrage.manage; каждый факт прослеживается через actor-stamping owning-сервиса + AuditRefs дела (двойной след).

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

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

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