GET /api/v1/developers/projects/{projectId} — Карточка проекта застройщика
Назначение
Экран редактирования проекта в кабинете застройщика. Возвращает полное состояние проекта своей компании: все плоские поля и пять вкладок карточки (Локация, Инфраструктура, Финансирование, Юрданные, Ход строительства), а также признаки полей, управляемых синхронизацией Talent. В отличие от списка проектов, отдаёт карточку целиком. Дом полей — сущность Проект.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | chessboard |
| API area | developers |
| Feature group | projects |
| Статус | draft |
| Документ | docs/06-services/chessboard/api/developers/get-project.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (менеджер застройщика) |
| Auth policy / Permissions | chessboard.projects.manage |
| Scope (RBAC) | организация / группа проектов / проект (projectId должен входить в scope) |
| Tenant isolation | TenantId = CompanyId из JWT; чужой проект недостижим → 404 |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/v1/developers/projects/{projectId} |
| Success status | 200 |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims (tenant_id) | Guid | Да | Required | Tenant isolation |
ProjectId | route | Guid | Да | Required, существующий проект tenant-а | 404 при чужом/отсутствующем |
Модель ответа DeveloperProjectDetail
Все поля — из ../domain/project.md (дом полей), без дельты типов.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | ProjectEntity.Id | — | Id проекта. |
DeveloperId | Guid | Да | DeveloperId | — | Застройщик (= TenantId). |
Status | ProjectStatus | Да | Status | — | Draft/Published/Hidden/Archived. |
Name | string | Да | Name | — | Название. |
Description | string? | Нет | Description | — | Описание. |
Type | ProjectType | Да | Type | — | Тип. |
Class | ProjectClass | Да | Class | — | Класс. |
ConstructionStatus | ConstructionStatus | Да | ConstructionStatus | — | Статус строительства. |
CountryId | short | Да | CountryId | — | Страна — ссылка на справочник handbook (handbook-replica.md). |
RegionId | long? | Нет | RegionId | — | Регион — ссылка на справочник; null — не резолвлен (публикация заблокирована, INV-PRJ-3). |
RegionName | string? | Нет | RegionName | — | Display-имя региона (денормализация; заполнено ⇔ RegionId != null). |
CityId | long? | Нет | CityId | — | Город — ссылка на справочник; null — не резолвлен (публикация заблокирована, INV-PRJ-3). |
CityName | string? | Нет | CityName | — | Display-имя города (заполнено ⇔ CityId != null). |
Stars | short? | Нет | Stars | — | Звёздность (отели). |
HotelierId | Guid? | Нет | HotelierId | — | Отельер (hotelier.md). |
Location | ProjectLocationDocument | Да | Location | — | Вкладка «Локация» (typed-модель project.md). |
Infrastructure | ProjectInfrastructureDocument | Да | Infrastructure | — | Инфраструктура. |
Financing | ProjectFinancingDocument | Да | Financing | — | Финансирование (банки — id, резолв имён через lookup). |
LegalInfo | ProjectLegalInfoDocument | Да | LegalInfo | — | Юрданные (публичны по 214-ФЗ). |
ConstructionProgress | ProjectConstructionProgressDocument | Да | ConstructionProgress | — | Ход строительства. |
PresentationVideoUrl | string? | Нет | PresentationVideoUrl | — | Видео. |
CoverImageFileId | Guid? | Нет | CoverImageFileId | — | Обложка. |
Price | NullableRange<decimal>? | Нет | Project.Price (Price.Min/Price.Max) | — | Диапазон цен свободных объектов недвижимости (денормализация, read-only для клиента). |
CurrencyId | short | Да | CurrencyId | — | Валюта — ссылка на справочник handbook (RUB-only, ADR-0048); display-код/имя — lookup справочника. |
PropertyObjectsTotalCount | int | Да | PropertyObjectsTotalCount | — | Денормализация. |
PropertyObjectsFreeCount | int | Да | PropertyObjectsFreeCount | — | Денормализация. |
PaybackYears | decimal? | Нет | PaybackYears | — | Окупаемость (ручной ввод). |
SyncSource | ChessboardSyncSource | Да | SyncSource | — | Pin/TalentChessboard. |
SyncManagedFields | string[] | Да | вычисляется | — | Имена реплика-полей, закрытых для правки при SyncSource = TalentChessboard (INV-PRJ-9): Name,Type,Class,ConstructionStatus,CountryId,RegionId,CityId,Stars,CoverImageFileId,Location.Address* (display-имена RegionName/CityName следуют за id); при Pin — пустой список. UI дизейблит поля. |
LastSyncedAt | DateTimeOffset? | Нет | LastSyncedAt | — | Последний синк Talent. |
CreatedAt | DateTimeOffset | Да | CreatedAt | — | Создан. |
UpdatedAt | DateTimeOffset | Да | UpdatedAt | — | Изменён. |
Version | long | Да | Version | — | Для optimistic PUT. |
Предусловия
- Пользователь авторизован;
tenant_id= его компания-застройщик. - У актора есть permission
chessboard.projects.manageхотя бы в одном scope компании (иначеFORBIDDEN). - Запрашиваемый проект существует и принадлежит tenant-у актора (включая
Archived— карточка читается для истории); иначе неотличим от отсутствия (404). - Банки в
Financing— id справочника; резолв названий/логотипов — отдельным lookup (references-lookup.md); карточка отдаёт только id.
Алгоритм
Чтение карточки по идентификатору с разграничением доступа. Операция read-only, доменных событий не публикует.
- Контекст и доступ. Актор — менеджер застройщика;
TenantIdиз claims. Требуется permissionchessboard.projects.manage; нет ни в одном scope →FORBIDDEN(403). Просроченный/отозванный к моменту запроса доступ →UNAUTHORIZED(401) /FORBIDDEN(403). - Выборка и разграничение. Проект ищется по
ProjectIdв пределах tenant-а актора:- записи нет или проект чужого tenant-а →
CHESSBOARD_PROJECT_NOT_FOUND(404, существование чужого не раскрывается); - актор с суженным scope «группа проектов/проект»:
ProjectIdвне разрешённого набора — проект недостижим и отдаётся тем жеCHESSBOARD_PROJECT_NOT_FOUND(404), не 403 (симметрично фильтрации выдачи в search-projects.md).FORBIDDEN(403) — только при полном отсутствии permissionchessboard.projects.manage.
- записи нет или проект чужого tenant-а →
- Формирование ответа. Допустим любой статус, включая
Archived(DeletedAt != null) — карточка читается для истории. Все поля берутся из проекта; пять вкладок (Локация/Инфраструктура/Финансирование/ Юрданные/Ход строительства) — typed-модели project.md.SyncManagedFieldsвычисляется: приSyncSource = TalentChessboard— список реплика-полей (INV-PRJ-9, UI дизейблит), приPin— пустой. Маскирование не требуется (свой tenant;LegalInfoпублична по 214-ФЗ).
Read-committed снимок. Денормализации — Price (границы свободных объектов недвижимости) и счётчики
PropertyObjects*Count — могут отставать до фонового пересчёта агрегатов (project-aggregates-recalc,
окно ~40 с); реплика-поля вкладок отражают последний применённый синк Talent (LastSyncedAt).
Acceptance Criteria
- Happy path. Given проект tenant-а актора в scope; When
GET .../projects/{projectId}; Then200с полной карточкой (плоские поля + 5 вкладок),Versionпригоден для optimisticPUT. - Архив читается. Given проект в статусе
Archived; When чтение; Then200(история доступна). - Чужой проект. Given
projectIdдругого tenant-а; When чтение; Then404CHESSBOARD_PROJECT_NOT_FOUND. - Вне scope. Given scope сужен до другой группы проектов; When чтение проекта вне scope; Then
404(не 403). - Нет права. Given нет
chessboard.projects.manage; When чтение; Then403FORBIDDEN. - Отставание денормализации. Given только что изменилась цена объекта недвижимости, пересчёт не завершён;
When чтение карточки; Then
200с прежнимPrice-диапазоном (eventual consistency, не ошибка).
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
CHESSBOARD_PROJECT_NOT_FOUND | 404 | Проект не существует / чужой tenant / ProjectId вне scope-набора | «Проект не найден» | no retry |
FORBIDDEN | 403 | Нет permission chessboard.projects.manage ни в одном scope | «Недостаточно прав» | no retry |
Сквозные ошибки (UNAUTHORIZED 401, ValidationError 400 — ProjectId == Guid.Empty) — по реестру
README.md.
Совместимость и наблюдаемость
- Новые optional поля ответа — additive; изменение вкладок-моделей — по правилам
ContractVersionproject.md; enum-строки (ProjectStatus/ProjectType/ProjectClass/ConstructionStatus/ChessboardSyncSource) — клиентский fallback на unknown обязателен. - Logs:
TenantId,ProjectId,Status,SyncSource(без PII); metrics:chessboard_developers_project_get_total, latency histogram (SLO p99 < 300 мс); traces: OTel (traceparent). Dashboardchessboard-overview; read-only — runbook не требуется.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (3): Chessboard API — реестр операций областей Developers и Admin, GET /api/v1/developers/projects — Поиск/список проектов застройщика, PUT /api/v1/developers/projects/{projectId} — Обновление проекта
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущность: ../domain/project.md (вкладки-модели, INV-PRJ-9); реплика справочников handbook (гео/валюта): ../domain/handbook-replica.md.
- Изменение: update-project.md; список: search-projects.md; реестр: README.md.