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

GET /api/v1/lots/{id} — Карточка объекта недвижимости: ограниченная / полная

Назначение

Карточка объекта /lots/[id]/info: ограниченная для гостя (описание, цена, срок сдачи, общая и жилая площадь, этаж, застройщик) и полная для авторизованного/реферального гостя (+ комнатность, статус, метрики доходности, комиссия агенту). Один endpoint, два режима ответа — режим определяется классом актора (правила маскирования MASK-ANALYTICS/MASK-STATUS — README.md). Дом полей — catalog-property-object-card.

Границы метода

Детальная инвест-аналитика (график роста цены, калькулятор, серии, PDF-отчёт) — не этот метод: полная карточка отдаёт денормализованные метрики и id для запросов в invest-analytics (страница /lots/[id]/analytics).

Metadata

ПолеЗначение
Сервис/контекстcatalog
API areapublic
Feature groupcards
Статусdraft

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

ПроверкаЗначение
Actor typeGuest (ограниченная карточка, без гейта) / RefGuest, User (полная)
Auth policy / Permissionsчтение — без permission; agentCommissioncatalog.commissions.view (MASK-COMMISSION)
Scopeплатформа
Tenant isolationне применяется (платформенная витрина)
Auditnone; просмотр пишет фронт событием в user-activity — каталог просмотры не сохраняет

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/v1/lots/{id}
Success status200
Idempotencyне применяется (safe GET)
CorrelationOpenTelemetry trace (traceparent)

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
IdrouteGuidДавалидный Guid; иначе 404 route-биндинга
ActorClassidentity middlewareGuest/RefGuest/UserДаопределяет режим ограниченная/полная (README.md); невалидный токен → 401классы — README.md

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

Колонка Masking: огр. → null — поле обнуляется в ограниченном режиме (Guest). Источники effective-полей — дом ../domain/catalog-property-object-card.md.

ПолеТипОбязательностьИсточникMaskingОписание
idGuidДаCatalogPropertyObjectCard.IdОбъект недвижимости.
projectIdGuidДаProjectIdПроект (переход «О проекте»).
buildingBuildingRefModelДаBuildingId/BuildingNameКорпус (ref-модель — README.md).
developerDeveloperRefModelДаDeveloperId/DeveloperNameЗастройщик (ref-модель — README.md; виден и гостю).
numberstringДаNumberНомер объекта недвижимости.
titlestringДаTitle (effective)Заголовок («описание» ограниченной карточки).
typeNamestringДаTypeNameТип объекта.
viewstring?НетViewВид из окон.
badgestring?НетBadge (effective)Бейдж.
addressstringДаAddressАдрес.
distanceMinutesint?НетDistanceMinutes (effective)Минуты до ключевой точки.
starsshort?НетStars (effective)Звёздность.
deliveryDatestring (ISO 8601 date)?НетDeliveryDate (effective)Срок сдачи (виден гостю).
readinessPercentshort?НетReadinessPercent (effective)Готовность.
pricedecimalДаPrice (effective)Цена — видна и гостю (принятое расхождение MASK-PRICE, README.md: цена входит в ограниченную карточку).
areaTotaldecimalДаAreaTotalОбщая площадь.
areaLivingdecimal?НетAreaLivingЖилая площадь.
pricePerMeterdecimalДаPricePerMeterЦена за м².
floorshortДаFloorЭтаж (виден гостю).
floorsTotalshortДаFloorsTotalЭтажность корпуса.
ceilingHeightdecimal?НетCeilingHeightВысота потолков, м.
roomsCountshortДаRoomsCountКомнатность.
visualizationFileIdGuid?НетVisualizationFileIdПланировка/визуализация (пресайн — storage/CDN).
propertyObjectStatusstring (enum PropertyObjectAvailabilityStatus)?НетPropertyObjectStatusогр. → null (MASK-STATUS)Free/Reserved/Booked/Sold; управляет кнопкой «Забронировать» (активна при Free; заявка — crm).
paybackPaybackModel?Нетowned Payback (effective)огр. → null (MASK-ANALYTICS)Окупаемость.
reliabilityReliabilityModel?Нетowned Reliability (effective)огр. → nullИндекс надёжности.
growthPercentdecimal?НетGrowthPercentогр. → nullРост цены, %/год.
rentalIncomePerYeardecimal?НетRentalIncomePerYearогр. → nullАрендный доход, ₽/год.
ratingdecimal?НетRating (effective)огр. → nullРейтинг.
agentCommissionAgentCommissionModel?Нетowned AgentCommissionMASK-COMMISSIONКомиссия агенту на странице объекта.
analyticsAvailableboolДаcomputedtrue в полном режиме при AnalyticsSyncedAt != null — фронт показывает вкладку /lots/[id]/analytics (данные — invest-analytics); в ограниченном режиме всегда false.
isLimitedboolДаcomputedtrue — ответ в ограниченном режиме (фронт показывает предложение войти).

Алгоритм

  1. Контекст и доступ. Актор по токену (User; невалидный → 401), затем по реф-cookie referralUrl (RefGuest), иначе Guestбез отказа (ограниченный режим). Для User — права catalog.commissions.view.
  2. Проверка входа. Id != Guid.Empty — иначе ValidationError (400). Карточка объекта должна существовать и быть опубликованной (Status = Published); если карточки нет либо она в статусе Hidden/DelistedCATALOG_PROPERTY_OBJECT_NOT_FOUND (404) — скрытые карточки неотличимы от несуществующих (не раскрываем факт скрытия).
  3. Что читаем. Единственная карточка объекта по id (все данные денормализованы; owned VO — часть той же строки). Запросов в chessboard/invest-analytics/rewards нет.
  4. Режим и маскирование. isLimited = (ActorClass == Guest). Маппинг поле-в-поле — таблица PropertyObjectCardModel; в ограниченном режиме поля с пометкой «огр. → null» обнуляются (MASK-STATUS + MASK-ANALYTICS), analyticsAvailable = false; в полном — analyticsAvailable = (AnalyticsSyncedAt != null). agentCommission — по MASK-COMMISSION независимо от режима.
  5. Результат. PropertyObjectCardModel → 200 (оба режима). Консистентность — eventual (лаг консюмеров источников; критичную проверку статуса перед бронью выполняет crm в источнике — ../README.md, «Известные ограничения»).

Диаграмма

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
— (auth)401Передан невалидный/просроченный токенбез тела схемы (фронт очищает сессию)re-login
ValidationError400Id = Guid.EmptyСписок ошибок полейno retry
CATALOG_PROPERTY_OBJECT_NOT_FOUND404Карточки нет либо Status ∈ {Hidden, Delisted}«Объект не найден»no retry

Acceptance Criteria

  • Полная карточка. Дано User/RefGuest и опубликованный объект. Когда запрашивает /api/v1/lots/{id}. Тогда 200 с propertyObjectStatus, метриками доходности (payback/reliability/growthPercent/rentalIncomePerYear/rating), analyticsAvailable = (AnalyticsSyncedAt != null), isLimited = false.
  • Ограниченная карточка (гость). Дано Guest. Когда запрашивает. Тогда 200, но поля с пометкой «огр. → null» обнулены (MASK-STATUS + MASK-ANALYTICS), analyticsAvailable = false, isLimited = true; price, floor, deliveryDate и застройщик остаются видны.
  • Комиссия по праву. Дано User без catalog.commissions.view. Когда возвращается карточка. Тогда agentCommission = null независимо от режима.
  • Скрытый/несуществующий объект. Дано карточка в Hidden/Delisted либо отсутствует. Когда запрашивают. Тогда CATALOG_PROPERTY_OBJECT_NOT_FOUND (404) — скрытое неотличимо от несуществующего.
  • Кнопка брони. Дано propertyObjectStatus = Free в полном режиме. Когда фронт рендерит карточку. Тогда кнопка «Забронировать» активна; решающую проверку статуса перед заявкой выполняет crm (витрина eventual, лаг консюмера).
  • Невалидный токен. Дано просроченный JWT. Когда приходит запрос. Тогда 401 (фронт очищает сессию), в отличие от гостя без токена — тот получает ограниченный 200.

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

  • Расширение маскируемого набора — additive; раскрытие ранее маскированного поля гостю — breaking (пересмотр MASK-* в README.md).
  • Logs: id, класс актора, режим (limited/full), результат; metrics — catalog_property_object_card_requests_total{actor_class,result}, latency histogram, доля limited-ответов; traces/dashboard — общие правила area (README.md).

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

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

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