GET /api/agent/crm/leads/{leadId} — Карточка лида
Карточка пути клиента для агента (страница лида /crm/leads/[leadId], UC-1..3): «шапка хотелок» инвестора,
клиент, срок жизни лида и списки порождённых фиксаций и сделок. Read-only проекция агрегата LeadEntity
(lead.md). Лента событий лида на карточке не разворачивается — она грузится отдельным
пагинированным методом list-lead-events.md, чтобы карточка оставалась лёгкой.
Назначение
Отдать агенту сводную карточку одного лида: агрегированные «хотелки» (LeadWishes), реквизиты клиента,
ExpiresAt/ExtensionsCount (сколько осталось жить и сколько раз продлевали) и компактные списки
фиксаций/сделок, порождённых из лида, — для навигации в дочерние процессы.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | agent |
| Feature group | leads |
| Статус | draft |
| Документ | docs/06-services/crm/api/agent/get-lead.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Агент; руководитель агентства — весь TenantId; Менеджер ПИН — platform-scope) |
| Auth policy / Permissions | crm.leads.view |
| Scope (RBAC) | организация (агентство): TenantId = AgencyCompanyId |
| Record-level | агент — активный участник (Executor: ExecutorAgentUserId = actor); руководитель агентства — любой лид своего TenantId; несоответствие маскируется под CRM_LEAD_NOT_FOUND (без enumeration) |
| Tenant isolation | TenantId в каждом запросе; soft-deleted — глобальный фильтр |
| Audit | read (не sensitive-read: PII клиента доступна владеющему агенту штатно) |
Рядовой агент видит только свои лиды (ExecutorAgentUserId = actor); руководитель агентства — любой лид
своего TenantId; Менеджер ПИН — сквозной доступ (platform-scope). Любое несоответствие (чужой лид, другой
TenantId, soft-deleted) маскируется под CRM_LEAD_NOT_FOUND — без enumeration.
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/agent/crm/leads/{leadId} |
| Success status | 200 |
| Idempotency header | не применяется (безопасный GET) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims | Guid | Да | Required | Tenant isolation |
ActorUserId | identity claims | Guid | Да | Required | Record-level (агент/руководитель) |
LeadId | route | Guid | Да | != Guid.Empty; существующий лид | — |
Модель ответа LeadCardModel
Проекция LeadEntity (дом полей — lead.md); ниже — только форма карточки. Списки
Fixations/Deals — ограниченные компактные ряды (у лида немного дочерних процессов); глубокий
пагинированный просмотр — отдельные agent-списки фиксаций/сделок. Лента событий — отдельный метод
list-lead-events.md.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
LeadId | Guid | Да | Lead.Id | — | Идентификатор лида. |
Status | LeadStatus (enum→string) | Да | Lead.Status | — | Active/Expired/Archived (lead.md). |
Source | LeadSource (enum→string) | Да | Lead.Source | — | Как появился лид. |
Client | LeadClientModel | Да | ClientEntity (Lead.ClientId) | см. ниже | Реквизиты клиента (шапка). |
Wishes | LeadWishesModel | Да | Lead.Wishes (owned VO) | — | «Шапка хотелок»; все поля nullable. |
ExpiresAt | DateTimeOffset | Да | Lead.ExpiresAt | — | Срок жизни лида. |
ExtensionsCount | int | Да | Lead.ExtensionsCount | — | Сколько раз продлевали. |
LastExtendedAt | DateTimeOffset? | Нет | Lead.LastExtendedAt | — | Последнее продление. |
ExpiredAt | DateTimeOffset? | Нет | Lead.ExpiredAt | — | Факт перевода в Expired (worker). |
LastEventAt | DateTimeOffset? | Нет | Lead.LastEventAt | — | Последнее событие ленты (для перехода в list-lead-events). |
CreatedAt | DateTimeOffset | Да | Lead.CreatedAt | — | Создание лида. |
Fixations | IReadOnlyList<LeadFixationRefModel> | Да | crm.fixation_requests (LeadId) | — | Порождённые фиксации (может быть пустым). |
Deals | IReadOnlyList<LeadDealRefModel> | Да | crm.deals (LeadId) | — | Порождённые сделки (может быть пустым). |
LeadClientModel — реквизиты клиента
Проекция ClientEntity (дом — client.md); только шапка карточки.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
ClientId | Guid | Да | Client.Id | — | Карточка клиента (/crm/clients/[clientId]). |
FullName | FullName (owned VO) | Да | Client.Name | — | ФИО; контракт VO — client.md. |
PhoneNumber | string | Да | Client.PhoneNumber | PII: в логах маска +7***NNNN | Телефон клиента (E.164). |
InvestorUserId | Guid? | Нет | Client.InvestorUserId | — | null — клиент не зарегистрирован (lazy). |
Status | ClientStatus (enum→string) | Да | Client.Status | — | Active/Archived. |
LeadWishesModel — «шапка хотелок»
Форма 1:1 с owned-VO LeadWishes (дом полей/ограничений — lead.md): Budget
(NullableRange<decimal>), BudgetCurrencyId (short?, справочник валют handbook), CityId (long?,
справочник городов handbook) + CityName (string?, денормализованное display-имя, снятое при записи
хотелок), Urgency (LeadUrgency?→string), PurchaseGoal (PurchaseGoal?→string),
PreferredPaymentMethod (PaymentMethod?→string), Comment (string?). Enum-ы сериализуются строкой;
display-код валюты клиент получает из lookup справочника
(lookup-references.md).
LeadFixationRefModel — компактный ряд фиксации
Проекция FixationRequestEntity (дом — fixation-request.md).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
FixationRequestId | Guid | Да | FixationRequest.Id | Заявка на фиксацию. |
DeveloperCompanyId | Guid | Да | FixationRequest.TenantId | Застройщик-адресат. |
Status | FixationStatus (enum→string) | Да | FixationRequest.Status | Состояние заявки. |
Uniqueness | FixationUniqueness (enum→string) | Да | FixationRequest.Uniqueness | Итог дубль-проверки. |
DealId | Guid? | Нет | FixationRequest.DealId | Сделка, если одобрено. |
CreatedAt | DateTimeOffset | Да | FixationRequest.CreatedAt | Подача. |
DecidedAt | DateTimeOffset? | Нет | FixationRequest.DecidedAt | Решение застройщика. |
LeadDealRefModel — компактный ряд сделки
Проекция DealEntity (дом — deal.md).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
DealId | Guid | Да | Deal.Id | Сделка. |
Title | string | Да | Deal.Title | Заголовок карточки канбана. |
Status | DealStatus (enum→string) | Да | Deal.Status | Статус процесса. |
StepGroup | PipelineStepGroup (enum→string) | Да | Deal.StepGroup | Системная группа текущего этапа. |
PropertyObjectId | Guid | Да | Deal.PropertyObjectId | Объект (chessboard). |
Price | Money (owned VO) | Да | Deal.Price | Стоимость объекта (контракт VO — deal.md). |
DeveloperCompanyId | Guid | Да | Deal.TenantId | Застройщик. |
UpdatedAt | DateTimeOffset | Да | Deal.UpdatedAt | Последнее обновление. |
Алгоритм
- Контекст и доступ. Операцию выполняет агент (permission
crm.leads.view) в рамкахTenantId. - Проверка входа и предусловий.
- формат:
LeadId != Guid.Empty— иначе ошибка валидации (400); - лид существует в
TenantId(чужой tenant и soft-deleted — как «не найдено») — иначеCRM_LEAD_NOT_FOUND; - record-level: рядовой агент видит только свои лиды (
ExecutorAgentUserId = actor), иначеCRM_LEAD_NOT_FOUND(без enumeration; руководитель агентства/Менеджер ПИН — проходят); - состояние: карточка доступна в любом
Status(Active/Expired/Archived).
- формат:
- Что читаем. Лид, его клиента-родителя (шапка), компактные списки порождённых фиксаций (по
CreatedAtdesc) и сделок (поUpdatedAtdesc). Лента событий не грузится (отдельный методlist-lead-events). - Маппинг. Поле-в-поле
Lead → LeadCardModel,Lead.Wishes → LeadWishesModel(enum-ы строкой),Client → LeadClientModel(PhoneNumberотдаётся владеющему агенту, в логи — маской),Fixations/Deals— из проекций шага 3. - Результат. Карточка лида → HTTP
200. Лид не найден/не виден →CRM_LEAD_NOT_FOUND→404 ProblemDetails. Freshness — на момент запроса; допустима микро-рассинхронизация между списками (карточка — сводка, не финансовая консистентность).
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Интеграционные события | none (read-only) |
| Внешние вызовы | none (объект/проект в Deal* — снапшоты в crm; синхронного вызова chessboard карта не делает) |
| Проекции / поиск | none |
| Уведомления | none |
Ошибки и failure modes
Главный гейт — маскировка: чужой лид, лид другого TenantId и soft-deleted лид неотличимы снаружи и все
отдаются как CRM_LEAD_NOT_FOUND (без enumeration).
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | LeadId = Guid.Empty | «Некорректный идентификатор лида» | no retry |
CRM_LEAD_NOT_FOUND | 404 | лид не существует / другой tenant / soft-deleted / не виден по record-level | «Лид не найден» | no retry |
Совместимость и наблюдаемость
- Новые optional поля карточки — additive; удаление/переименование/смена типа поля или enum — breaking (migration + rollout клиентов). Неизвестные enum-значения клиент трактует как «прочее», не падает.
- Logs:
lead-card requested(LeadId,TenantId, actor; телефон клиента — маской); metrics:crm_lead_card_requests_total{result}, latency p95 (< 500 мс, SLO чтений README); traces — сквозной trace черезtraceparent; dashboardpin-crm.
Acceptance Criteria
- Happy path. Given агент-владелец и существующий лид своего
TenantId; WhenGET; Then200сLeadCardModel: «шапка хотелок», реквизиты клиента,ExpiresAt/ExtensionsCount, компактные списки фиксаций и сделок; телефон — владельцу, в логах маска. - Руководитель. Given руководитель агентства и лид своего
TenantIdдругого агента; WhenGET; Then200. - Чужой лид. Given рядовой агент и чужой лид (
ExecutorAgentUserId != actor) того жеTenantId; WhenGET; Then404 CRM_LEAD_NOT_FOUND(без enumeration). - Другой tenant. Given лид другого
TenantIdили soft-deleted; WhenGET; Then404 CRM_LEAD_NOT_FOUND. - Валидация. Given
LeadId = Guid.Empty; WhenGET; Then400 VALIDATION_FAILED. - Закрытый лид. Given лид в статусе
Expired/Archived; WhenGET; Then200(карточка доступна в любом статусе). - Без процессов. Given лид без порождённых фиксаций/сделок; When
GET; ThenFixations = []иDeals = [].
Обратные ссылки
Автоссылки: где используется этот документ.
- API (3): GET /api/investor/crm/leads/{leadId} — Карточка лида (инвестор), GET /api/pin-manager/crm/leads/{leadId} — Карточка любого лида (Менеджер ПИН), POST /api/agent/crm/leads/{leadId}/property-object-context — Контекст инвестора по объектам недвижимости
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: lead.md (дом полей,
LeadWishes), client.md (FullName), fixation-request.md, deal.md (Money). - Лента событий лида: list-lead-events.md.
- Смежный flow: create-booking-request.md (порождение лида и события ленты).
- Карта областей и события: README контекста.