GET /api/v1/public/projects/{projectId} — Карточка проекта (ограниченная/полная)
Назначение
Карточка проекта (ЖК/апарт-отеля) для публичного каталога: вкладки «Информация / Локация /
Финансирование / Юридическая информация / Ход строительства / Видео / Карта». Один endpoint обслуживает обе
версии карточки: ограниченную для неавторизованных (скрываются точные цены, счётчики статусов и доступ к
шахматке) и полную для авторизованных (инвестор/агент). Зеркалируется в agents area
(GET /api/v1/agents/projects/{projectId}) той же операцией с обязательной авторизацией.
Запись просмотра в историю — ответственность клиента/user-activity (событие просмотра), не этого метода.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | chessboard |
| API area | public (зеркало: agents) |
| Feature group | projects |
| Статус | draft |
| Owner | PIN Platform Core |
| Документ | docs/06-services/chessboard/api/public/projects/get-project.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | Anonymous (ограниченная проекция) / User — инвестор, агент, менеджер застройщика (полная проекция) |
| Auth policy / Permissions | none — public endpoint; полнота проекции определяется признаком авторизации |
| Scope (RBAC) | не применяется (платформенное чтение) |
| Record-level | не применяется |
| Tenant isolation | Контролируемый bypass tenant-фильтра только для среза Status = Published и Developer.Status = Published (ADR-0052) |
| Audit | none (read; просмотры трекает user-activity) |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/v1/public/projects/{projectId} |
| Success status | 200 |
| Idempotency header | не применяется (idempotent by design — GET) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
ProjectId | route | Guid | Да | Required, != Guid.Empty | — |
IsAuthenticated | identity claims | bool | Да (вычисляется) | из контекста авторизации; аноним = false | определяет masking |
ActorUserId | identity claims | Guid? | Нет | null для анонима | не логируется в теле ответа |
Модель ответа ProjectCardResponse
Проекция ProjectEntity + вложенные summary-модели. Masking-колонка:
правило для анонима (авторизованный получает значение всегда).
| Поле | Тип | Обязательность | Источник | Masking (аноним) | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | Project.Id | — | Идентификатор проекта. |
Name | string | Да | Project.Name | — | Название ЖК/отеля. |
Description | string? | Нет | Project.Description | — | Вкладка «Информация». |
Type | ProjectType (string) | Да | Project.Type | — | Тип (reference-data.md). |
Class | ProjectClass (string) | Да | Project.Class | — | Класс. |
ConstructionStatus | ConstructionStatus (string) | Да | Project.ConstructionStatus | — | Статус строительства. |
CountryId | short | Да | Project.CountryId | — | Ссылка на страну справочника (handbook-replica.md); display-имя — публичный lookup handbook. |
RegionId | long | Да | Project.RegionId | — | Ссылка на регион справочника (у Published-проекта резолвлена, INV-PRJ-3). |
RegionName | string | Да | Project.RegionName (денормализация) | — | Display-имя региона. |
CityId | long | Да | Project.CityId | — | Ссылка на город справочника (INV-PRJ-3). |
CityName | string | Да | Project.CityName (денормализация) | — | Display-имя города. |
Stars | short? | Нет | Project.Stars | — | Звёздность отеля. |
Location | ProjectLocationDocument | Да | Project.Location (типизированный jsonb-документ) | — | Вкладки «Локация»/«Карта» (модель — дом project.md). |
Infrastructure | ProjectInfrastructureDocument | Да | Project.Infrastructure | — | Инфраструктура. |
Financing | ProjectFinancingCardModel | Да | Project.Financing + резолв банков | — | Вкладка «Финансирование» (id банков заменены на summary — модель ниже). |
LegalInfo | ProjectLegalInfoDocument | Да | Project.LegalInfo | — (публично по 214-ФЗ) | Вкладка «Юридическая информация», декларации. |
ConstructionProgress | ProjectConstructionProgressDocument | Да | Project.ConstructionProgress | — | Вкладки «Ход строительства»/«Видео». |
PresentationVideoUrl | string? | Нет | Project.PresentationVideoUrl | — | Главное видео. |
CoverImageFileId | Guid? | Нет | Project.CoverImageFileId | — | Обложка (пресайн — storage/CDN). |
Price | NullableRange<decimal>? | Нет | Project.Price (Price.Min/Price.Max) | null (точные цены скрыты) | Диапазон цен свободных объектов недвижимости. |
CurrencyId | short | Да | Project.CurrencyId | — | Ссылка на валюту справочника (RUB-only, ADR-0048); display-код/знак — публичный lookup handbook. |
PropertyObjectsTotalCount | int | Да | Project.PropertyObjectsTotalCount | — | Всего объектов недвижимости (карточка ленты). |
PropertyObjectsFreeCount | int? | Нет | Project.PropertyObjectsFreeCount | null (производная статусов объектов) | Свободных объектов недвижимости. |
PaybackYears | decimal? | Нет | Project.PaybackYears | — | Окупаемость (детальная аналитика — invest-analytics). |
Developer | DeveloperSummaryModel | Да | загруженный DeveloperEntity | — | Карточка застройщика (ниже); ссылка «переход на девелопера». |
Hotelier | HotelierSummaryModel? | Нет | загруженный HotelierEntity | — | Оператор апарт-отеля. |
Buildings | IReadOnlyList<BuildingSummaryModel> | Да (может быть пустым) | Filter по зданиям | — | Published-здания проекта (ниже). |
IsLimited | bool | Да | вычисляется: !IsAuthenticated | — | true — отдана ограниченная проекция; фронт показывает CTA «войдите». |
IsChessboardAvailable | bool | Да | вычисляется: IsAuthenticated | — | Доступ к шахматке (аноним — скрыта). |
CreatedAt | DateTimeOffset | Да | Project.CreatedAt | — | Дата добавления. |
Version | long | Да | Project.Version | — | Версия агрегата (client-side cache validation). |
Вложенная модель DeveloperSummaryModel (проекция developer.md)
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | Developer.Id | — | = CompanyId identity. |
Name | string | Да | Developer.Name | — | Название. |
LogoFileId | Guid? | Нет | Developer.LogoFileId | — | Лого. |
FoundedYear | short? | Нет | Developer.FoundedYear | — | Год основания. |
ReliabilityValue | decimal? | Нет | Developer.Highlights.ReliabilityValue | — | Надёжность (прототип reliability.value). |
ReliabilityMax | decimal | Да | Developer.Highlights.ReliabilityMax | — | Шкала (прототип reliability.max). |
ObjectsDeliveredCount | int? | Нет | Developer.Highlights.ObjectsDeliveredCount | — | Сдано объектов. |
ObjectsInProgressCount | int? | Нет | Developer.Highlights.ObjectsInProgressCount | — | Строится. |
Вложенная модель HotelierSummaryModel (проекция hotelier.md)
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | Hotelier.Id | — | Идентификатор. |
Name | string | Да | Hotelier.Name | — | Название отельера. |
LogoFileId | Guid? | Нет | Hotelier.LogoFileId | — | Лого. |
Вложенная модель BuildingSummaryModel (проекция building.md)
| Поле | Тип | Обязательность | Источник | Masking (аноним) | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | Building.Id | — | Идентификатор здания. |
Name | string | Да | Building.Name | — | «Литер 3», «Корпус Б». |
FloorsTotal | short | Да | Building.FloorsTotal | — | Этажность. |
SectionsCount | short? | Нет | Building.SectionsCount | — | Секций. |
DeliveryYear | short? | Нет | Building.DeliveryYear | — | Год сдачи. |
DeliveryQuarter | short? | Нет | Building.DeliveryQuarter | — | Квартал сдачи. |
ConstructionStatus | ConstructionStatus (string) | Да | Building.ConstructionStatus | — | Статус строительства здания. |
ReadinessPercent | decimal | Да | Building.ReadinessPercent | — | Готовность. |
PropertyObjectsTotalCount | int | Да | Building.PropertyObjectsTotalCount | — | Всего объектов недвижимости. |
PropertyObjectsFreeCount | int? | Нет | Building.PropertyObjectsFreeCount | null | Свободных объектов недвижимости. |
Вложенная модель ProjectFinancingCardModel
Дельта к ProjectFinancingDocument (project.md): id банков резолвятся в
summary для отображения без второго запроса.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
SalesModel | SalesModel (string) | Да | Financing.SalesModel | — | ДДУ/ДКП/Mixed. |
EscrowBanks | IReadOnlyList<BankSummaryModel> | Да (может быть пустым) | резолв банков по Financing.EscrowBankIds | — | Эскроу-банки. |
ProjectFinancingBank | BankSummaryModel? | Нет | резолв банка по Financing.ProjectFinancingBankId | — | Банк проектного финансирования. |
MortgageAvailable | bool | Да | Financing.MortgageAvailable | — | Ипотека доступна. |
MortgageBanks | IReadOnlyList<BankSummaryModel> | Да (может быть пустым) | резолв банков по Financing.MortgageBankIds | — | Ипотечные банки. |
InstallmentAvailable | bool | Да | Financing.InstallmentAvailable | — | Рассрочка есть (программы — crm). |
Вложенная модель BankSummaryModel (проекция bank.md)
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | Bank.Id | — | Идентификатор. |
Name | string | Да | Bank.Name | — | Название банка. |
LogoFileId | Guid? | Нет | Bank.LogoFileId | — | Лого. |
Предусловия и постусловия
Предусловия:
- Вызов анонимом (ограниченная проекция) или авторизованным пользователем (полная); признак авторизации определяет masking.
- Проект видим только по публичному срезу:
Project.Status = PublishedиDeveloper.Status = Published(INV-DEV-6). Иначе —404(единый код без раскрытия существования Draft/Hidden).
Постусловия (read — состояние не меняется):
- Возвращена карточка с summary застройщика, отельера, банков и published-зданий. Для анонима:
Price = null,PropertyObjectsFreeCount = null(проект и каждое здание),IsLimited = true,IsChessboardAvailable = false; для авторизованного значения отдаются как есть. - Архивные/отсутствующие банки или отельер молча выпадают из проекций — карточка не падает.
Price и счётчики объектов недвижимости — денормализация с лагом debounce-джоба
project-aggregates-recalc; просмотры в историю пишет user-activity, не этот метод.
Алгоритм
Собрать карточку проекта с summary-моделями застройщика, отельера, банков и зданий; полнота полей зависит от авторизации.
- Контекст и доступ. Вызывает аноним (ограниченная проекция) или авторизованный пользователь (полная); признак авторизации определяет masking. Чтение — только по публичному срезу (Published-иерархия).
- Проверка входа.
ProjectIdзадан; иначе —VALIDATION_ERROR. Существование и публичность — шаг 3 (единый404: отсутствие и непубличность неотличимы снаружи, без утечки существования Draft/Hidden). - Что читаем (родитель до дочерних):
- Проект по id со статусом
Publishedи не удалённый; не найден →CHESSBOARD_PROJECT_NOT_FOUND. - Застройщик проекта; если его статус не
Published— проект публично невидим (INV-DEV-6 HiddenCascades) →CHESSBOARD_PROJECT_NOT_FOUND(тот же код — причину не раскрываем). - Отельер проекта (если задан); архивный отельер даёт
null— карточка не падает. - Банки финансирования (эскроу, проектного финансирования, ипотечные) по id из
Financing; отсутствующие или архивные id молча выпадают из проекций. - Published-здания проекта (сортировка по
Nameasc).
- Проект по id со статусом
- Сборка ответа и masking. Поля — по таблицам моделей: плоские значения проекта, типизированные
jsonb-документы,
Financing→ProjectFinancingCardModel(id банков заменены на summary),developer→DeveloperSummaryModel(в т.ч.Highlights.Reliability*),buildings→ списокBuildingSummaryModel. При анониме:Price = null,PropertyObjectsFreeCount = null(проект и каждое здание),IsLimited = true,IsChessboardAvailable = false; для авторизованного значения отдаются как есть,IsLimited = false,IsChessboardAvailable = true. - Результат. Карточка проекта, статус
200; не найдено/непублично →404; нарушение проверки входа →400. Просмотры в историю пишетuser-activity, не этот метод.
Диаграмма
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_ERROR | 400 | ProjectId == Guid.Empty | «Некорректный идентификатор проекта» | no retry |
CHESSBOARD_PROJECT_NOT_FOUND | 404 | Проект не существует / не Published / удалён / застройщик не Published | «Проект не найден» (без раскрытия причины) | no retry |
INTERNAL_ERROR | 500 | Инфраструктурный сбой (PostgreSQL недоступен) | generic | retry (client backoff) |
Совместимость и наблюдаемость
Новые optional поля ответа — additive (без breaking); удаление/переименование/смена типа поля или правила masking в сторону раскрытия — breaking (новая версия route).
- Enum-поля сериализуются строками; клиент обязан иметь fallback на unknown-значения (reference-data.md).
- Logs:
projectId, actor type (anonymous/user), latency; без PII. Metrics:chessboard_public_project_requests_total{authenticated}, histogram latency (SLO p99 < 300 мс — README). Traces: spanchessboard.get-public-projectсо сквозной корреляцией. - Dashboard/runbook: chessboard-overview (README «Runtime И Эксплуатация»).
Acceptance Criteria
- AC1 (полная карточка). Дано: авторизованный, published-проект published-застройщика. Когда:
GET. Тогда:200,Price/PropertyObjectsFreeCountзаполнены,IsLimited = false,IsChessboardAvailable = true. - AC2 (masking для анонима). Дано: тот же проект, вызывает аноним. Когда:
GET. Тогда:200,Price = null, всеPropertyObjectsFreeCount = null(проект и здания),IsLimited = true,IsChessboardAvailable = false. - AC3 (каскад скрытия застройщика). Дано: застройщик не
Published(INV-DEV-6). Когда:GET. Тогда:CHESSBOARD_PROJECT_NOT_FOUND(404) — тот же код, причина не раскрывается. - AC4 (непубличный/несуществующий проект). Дано: проект
Draft/Hidden/удалён/не существует. Когда:GET. Тогда:CHESSBOARD_PROJECT_NOT_FOUND(404). - AC5 (архивные ссылки не ломают карточку). Дано: отельер архивен или банк из
Financingотсутствует. Когда:GET. Тогда:200,Hotelier = null/ банк выпал из списка, без ошибки. - AC6 (валидация). Дано:
ProjectId = Guid.Empty. Когда:GET. Тогда:VALIDATION_ERROR(400).
Обратные ссылки
Автоссылки: где используется этот документ.
- API (5): GET /api/v1/public/projects — Лента published-проектов (поиск + фильтры + сортировки), GET /api/v1/public/projects/{projectId}/buildings/{buildingId} — Карточка здания (литера/корпуса), GET /api/v1/public/projects/{projectId}/chessboard — Шахматка проекта (матрица этажи × секции), GET /api/v1/public/property-objects — Список объектов недвижимости проекта/здания с фильтрами, GET /api/v1/public/property-objects/{propertyObjectId} — Карточка объекта/объекта недвижимости (ограниченная/полная)
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: project.md, developer.md,
building.md, bank.md,
hotelier.md, reference-data.md;
справочные ссылки
CountryId/RegionId/CityId/CurrencyId— handbook-replica.md. - Соседние операции: get-building.md, get-chessboard.md, list-property-objects.md.
- Витрина/ранжирование ленты —
docs/06-services/catalog/(лента каталога — catalog; карточка структуры — этот контекст).