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

GET /api/v1/internal/selections/{id} — Подборка для карточки клиента/сделки (m2m crm)

Синхронное internal-чтение подборки по сохранённому у crm SelectionId для карточки клиента (лид/сделка, страница /crm/clients/[id]/collections). В отличие от публичного открытия по коду, ответ адресован доверенному сервису: включает владельца (OwnerUserId) и способ создания (Source), а soft-deleted подборку не прячет под 404 — резолвит её и сообщает статус удаления, чтобы CRM показал «подборка недоступна/удалена» (INV-SEL-009 действует только на публичное чтение).

Назначение

crm хранит у себя SelectionId подборки, которую агент отправил клиенту в лиде/сделке. Открывая карточку клиента, CRM вызывает этот метод и получает подборку владельца-агента для отображения блока «подборка от агента X»: название, описание, состав, публичную ссылку и — ключевое — признак удаления. Право агента на подборку в лиде обеспечивает сам crm (доверенный вызыватель по сохранённому id), поэтому record-level фильтр по OwnerUserId здесь не применяется. Display-данные объектов недвижимости CRM/фронт добирает батчем в catalog — метод отдаёт только состав. Дом полей — ../domain/selection.md.

Metadata

ПолеЗначение
Сервис/контекстselections
API areainternal
Feature groupselections
Статусdraft
Документdocs/06-services/selections/api/internal/get-selection.md

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

ПроверкаЗначение
Actor typeExternalSystem — m2m-клиент crm (client-credentials)
Auth policy / Permissionsauthorization policy InternalCrmOnly (m2m-клиент = crm); пользовательского пути к операции нет
Scope (RBAC)не применяется; право агента на подборку в лиде проверяет crm до вызова (доверенный сервис читает по сохранённому у себя SelectionId)
Record-levelне применяется — фильтр по OwnerUserId снят намеренно: crm читает чужую (владельца-агента) подборку по доверенному id
Tenant isolationне применяется — вне tenant (ADR-0052 п.2: подборка личная, владелец — инвестор или агент)
Auditread; PII нет; OwnerUserId/IsDeleted в логах операции

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/v1/internal/selections/{id:guid}
Success status200 (в т.ч. для soft-deleted подборки — с IsDeleted = true)
Idempotency headerне применяется (безопасный read)
Correlationсквозной trace через заголовок traceparent, из crm

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
SelectionIdroute ({id})GuidДа!= Guid.Emptyдоверенный m2m; record-level фильтр не применяется

Body отсутствует.

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

ПолеТипОбязательностьИсточникMaskingОписание
SelectionIdGuidДаSelection.IdEcho сохранённого у crm id.
OwnerUserIdGuidДаSelection.OwnerUserIdотдаётся (trusted)Владелец-агент — для подписи «подборка от агента X» в карточке клиента.
TitlestringДаSelection.TitleНазвание.
Descriptionstring?НетSelection.DescriptionОписание.
PublicCodestringДаSelection.PublicCodeПубличный код для ссылки /collection/{publicCode} из карточки клиента.
SourceSelectionSource (enum строкой)ДаSelection.SourceManual / Favorites — как создана (аудит для CRM).
IsDeletedboolДаcomputed: Selection.DeletedAt != nulltrue → CRM показывает «подборка недоступна/удалена».
DeletedAtDateTimeOffset?НетSelection.DeletedAtМомент soft delete (только при IsDeleted == true).
CreatedAtDateTimeOffsetДаSelection.CreatedAtСоздание.
UpdatedAtDateTimeOffsetДаSelection.UpdatedAtПоследняя правка.
ItemsIReadOnlyList<InternalSelectionItemModel>Да (может быть пуст)Selection.Items order by SortOrderСостав в порядке 0..N-1.

InternalSelectionItemModel

ПолеТипОбязательностьИсточникMaskingОписание
PropertyObjectIdGuidДаSelectionItem.PropertyObjectIdКлюч для батча display-данных в catalog (недоступный объект недвижимости пометит CRM/фронт).
SortOrderintДаSelectionItem.SortOrderПозиция 0..N-1 (INV-SEL-006).
Commentstring?НетSelectionItem.CommentКомментарий владельца к объекту недвижимости.

Алгоритм

  1. Контекст и доступ. Доверенный m2m crm (client-credentials); вызыватель не crm403 FORBIDDEN. Record-level фильтр по OwnerUserId не накладывается — crm читает чужую (владельца-агента) подборку по доверенному id.
  2. Проверка входа и предусловий. SelectionId непустой, иначе VALIDATION_ERROR.
  3. Что читаем. Подборка по id с элементами — без условия DeletedAt == null (в отличие от публичного чтения: CRM обязан различать «удалена» и «нет записи»). Строки нет вообще → SELECTIONS_NOT_FOUND.
  4. Бизнес-правила и маппинг. Маппинг поле-в-поле: OwnerUserId, Source, PublicCode отдаются (адресат доверенный); IsDeleted = подборка soft-deleted; DeletedAt — момент удаления; Items — элементы в порядке SortOrder (PropertyObjectId, SortOrder, Comment). Существование объектов недвижимости не проверяется (INV-SEL-008) — недоступные пометит батч catalog на стороне CRM/фронта.
  5. Результат. Модель InternalSelectionResult200 — в том числе для soft-deleted подборки (с IsDeleted = true); показать «недоступна/удалена» решает сам CRM.

Диаграмма

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
VALIDATION_ERROR400SelectionId == Guid.Empty«Некорректный идентификатор»no retry
SELECTIONS_NOT_FOUND404Строки нет вообще (никогда не существовала) — soft-deleted не сюда, она резолвится с IsDeleted = true«Подборка не найдена»no retry
FORBIDDEN403Вызыватель не m2m crm (policy InternalCrmOnly)no retry

Кросс-сервисный контракт (selectionscrm)

  • selections ожидает от crm: валидный m2m client-credentials токен (policy InternalCrmOnly); SelectionId, ранее полученный crm из события selection-shared (key = SelectionId) или из отправки в сделку. crm — доверенный вызыватель: право агента на подборку в лиде проверено на стороне crm до вызова, поэтому record-level по OwnerUserId здесь снят.
  • selections предоставляет crm: подборку владельца-агента с OwnerUserId/Source/PublicCode (адресат доверенный) и признаком удаления IsDeleted/DeletedAt. Soft-deleted подборка резолвится (не прячется под 404) — crm обязан различать «удалена» и «нет записи». Состав отдаётся без display-данных объектов недвижимости — их CRM/фронт добирает батчем в catalog.
  • Поведение при устаревании: SelectionId у crm вечно разрешим (soft delete + FK restrict от snapshot-ов не дают строке исчезнуть); crm не кэширует состав — при каждом открытии карточки читает актуальную версию, поэтому переименование/правка состава видны сразу.

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

  • Предусловия: вызыватель — доверенный m2m crm; SelectionId непустой.
  • Постусловия: состояние не меняется (безопасное чтение); при существующей строке возвращается подборка (в т.ч. soft-deleted — с IsDeleted = true); OwnerUserId отдаётся (trusted-адресат); Items в порядке SortOrder 0..N-1.

Acceptance Criteria

  • Given активная подборка, When crm вызывает GET /internal/selections/{id}, Then 200 с OwnerUserId, Source, PublicCode, IsDeleted = false и составом в порядке SortOrder.
  • Given soft-deleted подборка, When crm читает по сохранённому id, Then 200 с IsDeleted = true и заполненным DeletedAt — CRM показывает «подборка недоступна/удалена» (не 404).
  • Given SelectionId, которого никогда не было, When GET, Then 404 SELECTIONS_NOT_FOUND (в отличие от soft-deleted).
  • Given вызыватель не crm (другой m2m или пользовательский токен), When GET, Then 403 FORBIDDEN (policy InternalCrmOnly).
  • Given SelectionId == Guid.Empty, When GET, Then 400 VALIDATION_ERROR.
  • Given подборка с недоступным объектом недвижимости в составе, When GET, Then элемент отдаётся как есть (существование не проверяется, INV-SEL-008) — недоступность пометит батч catalog на стороне CRM.
  • Given сетевой сбой/5xx, When crm повторяет GET, Then повтор безопасен (read-only, без побочных эффектов).

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

  • Новые optional поля ответа — additive; удаление/переименование/смена типа — breaking (migration + rollout, согласование с crm). Source/IsDeleted — семантика для доверенного адресата, в публичный ответ (get-selection-by-code.md) не просачиваются.
  • Logs: SelectionId, IsDeleted (без PII); metrics: selections_internal_read_total{is_deleted}; traces: сквозной span из crm. Dashboard — selections-overview; alert — рост 5xx internal-области (деградация карточек клиента в CRM, ../README.md).

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

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

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

  • Сущность: ../domain/selection.md (INV-SEL-002/006/008/009; soft delete сохраняет разрешимость ссылок CRM).
  • Публичный аналог (прячет удалённую под 404, без OwnerUserId/Source): get-selection-by-code.md.
  • Поведение CRM после удаления подборки: delete-selection.md.
  • Карта API области: README.md; будущий потребитель — docs/06-services/crm/README.md.