GET /api/v1/developers/buildings/{buildingId} — Карточка здания
Назначение
Экран редактирования литера/корпуса в кабинете застройщика. Возвращает полное состояние здания своей компании: все поля карточки и признаки полей, управляемых синхронизацией Talent. Дом полей — сущность Здание.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | chessboard |
| API area | developers |
| Feature group | buildings |
| Статус | draft |
| Документ | docs/06-services/chessboard/api/developers/get-building.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (менеджер застройщика) |
| Auth policy / Permissions | chessboard.buildings.manage |
| Scope (RBAC) | организация / группа проектов / проект (building.ProjectId в scope) |
| Tenant isolation | TenantId = CompanyId из JWT; чужое здание → 404 |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/v1/developers/buildings/{buildingId} |
| Success status | 200 |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims (tenant_id) | Guid | Да | Required | Tenant isolation |
BuildingId | route | Guid | Да | существующее здание tenant-а | 404 при чужом |
Модель ответа DeveloperBuildingDetail
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | BuildingEntity.Id | — | Id здания. |
ProjectId | Guid | Да | ProjectId | — | Проект-родитель. |
Status | BuildingStatus | Да | Status | — | Draft/Published/Hidden. |
Name | string | Да | Name | — | Название литера. |
Code | string? | Нет | Code | — | Технический код (реплика-поле). |
FloorsTotal | short | Да | FloorsTotal | — | Этажность. |
SectionsCount | short? | Нет | SectionsCount | — | Секции. |
DeliveryYear | short? | Нет | DeliveryYear | — | Год сдачи. |
DeliveryQuarter | short? | Нет | DeliveryQuarter | — | Квартал сдачи. |
DeliveredAt | DateOnly? | Нет | DeliveredAt | — | Фактический ввод в эксплуатацию. |
ConstructionStatus | ConstructionStatus | Да | ConstructionStatus | — | Статус строительства здания. |
ReadinessPercent | decimal | Да | ReadinessPercent | — | Готовность. |
CeilingHeightMeters | decimal? | Нет | CeilingHeightMeters | — | Типовая высота потолков. |
PropertyObjectsTotalCount | int | Да | PropertyObjectsTotalCount | — | Денормализация. |
PropertyObjectsFreeCount | int | Да | PropertyObjectsFreeCount | — | Денормализация. |
SyncSource | ChessboardSyncSource | Да | SyncSource | — | Pin/TalentChessboard. |
SyncManagedFields | string[] | Да | вычисляется | — | При TalentChessboard (INV-BLD-7): Name,Code,FloorsTotal,SectionsCount,DeliveryYear,DeliveryQuarter,ConstructionStatus,ReadinessPercent; иначе пусто. |
LastSyncedAt | DateTimeOffset? | Нет | LastSyncedAt | — | Последний синк. |
CreatedAt | DateTimeOffset | Да | CreatedAt | — | Создано. |
UpdatedAt | DateTimeOffset | Да | UpdatedAt | — | Изменено. |
Version | long | Да | Version | — | Для optimistic PUT. |
Предусловия
- Пользователь авторизован (валидный JWT
identity);tenant_id= его компания-застройщик. - У актора есть permission
chessboard.buildings.manageхотя бы в одном scope компании (иначе —FORBIDDENдо чтения). - Запрашиваемое здание существует в БД и принадлежит tenant-у актора; иначе неотличимо от отсутствия (404).
- Денормализованные счётчики (
PropertyObjectsTotalCount/PropertyObjectsFreeCount) отражают последний фоновый пересчёт; при незавершённом пересчёте — читаются устаревшие (eventual consistency, ниже).
Алгоритм
Чтение по идентификатору с разграничением доступа. Отдельного изменения состояния нет — операция read-only, доменных событий не публикует.
- Контекст и доступ. Актор — менеджер застройщика;
TenantIdиз claims. Требуется permissionchessboard.buildings.manage; если его нет ни в одном scope —FORBIDDEN(403). Право проверяется до чтения; отозванное/просроченное к моменту запроса право (revoked mid-session) даётUNAUTHORIZED(401, истёкший JWT) либоFORBIDDEN(403, снятый permission). - Выборка и разграничение. Здание ищется по
BuildingIdсреди не удалённых записей tenant-а актора:- записи нет,
DeletedAt != null(soft-deleted) или здание чужого tenant-а →CHESSBOARD_BUILDING_NOT_FOUND(404, единый код — существование чужой/удалённой записи не раскрывается); - актор с суженным scope «группа проектов/проект»:
building.ProjectIdвне разрешённого набора — здание считается недостижимым и отдаётся тем жеCHESSBOARD_BUILDING_NOT_FOUND(404), не 403 (не раскрываем, что здание существует за пределами scope; ср. фильтрацию выдачи в search-buildings.md).FORBIDDEN(403) — только когда у актора нет самого permissionchessboard.buildings.manage.
- записи нет,
- Формирование ответа. Допустим любой статус здания (
Draft/Published/Hidden) — карточка редактирования показывает корпус на любой стадии. Все поля берутся из здания;SyncManagedFieldsвычисляется поSyncSource(INV-BLD-7): приTalentChessboard— фиксированный список реплика-полей (UI дизейблит их для правки), приPin— пустой список. Маскирование не требуется (свой tenant, PII нет).
Read-committed снимок. Реплика-поля (Name/Code/FloorsTotal/SectionsCount/Delivery*/
ConstructionStatus/ReadinessPercent) отражают последний применённый синк Talent (LastSyncedAt); между
приходом события синка и его применением может наблюдаться устаревшее значение. Счётчики объектов
недвижимости могут отставать до фонового пересчёта агрегатов (окно ~40 с, project-aggregates-recalc).
Acceptance Criteria
- Happy path. Given здание tenant-а актора в scope; When
GET .../buildings/{buildingId}; Then200с полной карточкой,Versionпригоден для последующего optimisticPUT,SyncManagedFieldsсоответствуетSyncSource. - Любой статус. Given здание в статусе
Draft(илиHidden); When чтение; Then200(статус не ограничивает чтение в кабинете). - Чужой/удалённый. Given
buildingIdдругого tenant-а или soft-deleted; When чтение; Then404CHESSBOARD_BUILDING_NOT_FOUNDбез раскрытия существования. - Вне scope. Given у актора scope сужен до другой группы проектов; When чтение здания вне scope; Then
404CHESSBOARD_BUILDING_NOT_FOUND(не 403). - Нет права. Given у актора нет
chessboard.buildings.manage; When чтение; Then403FORBIDDEN. - Sync-managed поля. Given
SyncSource = TalentChessboard; When чтение; ThenSyncManagedFieldsсодержит реплика-поля (INV-BLD-7), UI блокирует их редактирование. - Отставание денормализации. Given только что изменился объект недвижимости, фоновый пересчёт агрегатов
не завершён; When чтение карточки; Then
200с прежнимиPropertyObjectsTotalCount/PropertyObjectsFreeCount(eventual consistency, не ошибка).
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
CHESSBOARD_BUILDING_NOT_FOUND | 404 | Нет здания / чужой tenant / soft-deleted / ProjectId вне scope-набора | «Здание не найдено» | no retry |
FORBIDDEN | 403 | Нет permission chessboard.buildings.manage ни в одном scope | «Недостаточно прав» | no retry |
Сквозные ошибки области (UNAUTHORIZED 401 — нет/просрочен JWT; ValidationError 400 — BuildingId == Guid.Empty)
— по реестру README.md, здесь не повторяются.
Совместимость и наблюдаемость
- Новые optional поля ответа — additive; изменение состава/семантики
SyncManagedFields— согласуется с инвариантом INV-BLD-7 (building.md); enum-строки (BuildingStatus,ConstructionStatus,ChessboardSyncSource) — клиентский fallback на unknown обязателен. - Logs:
TenantId,BuildingId,Status,SyncSource(без PII); metrics:chessboard_developers_building_get_total, latency histogram (SLO p99 < 300 мс); traces: OTel (traceparent). Dashboardchessboard-overview. Read-only — runbook не требуется (деградация = 404 при недостижимом здании).
Обратные ссылки
Автоссылки: где используется этот документ.
- API (2): Chessboard API — реестр операций областей Developers и Admin, GET /api/v1/developers/projects/{projectId}/buildings — Список зданий проекта
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущность: ../domain/building.md (INV-BLD-7).
- Изменение: update-building.md; список: search-buildings.md; реестр: README.md.