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

GET /api/v1/developers/projects/{projectId}/property-objects — Список объектов недвижимости проекта

Назначение

Экран «Объекты» страницы проекта в кабинете застройщика: список объектов недвижимости проекта своей компании с пагинацией, фильтрами (здание / этажи / секции / комнатность / статус / цена) и поиском по номеру, а также сортировками (натуральный порядок шахматки / цена). Менеджер видит все свои объекты — включая Hidden и (по флагу) soft-deleted, в отличие от публичного списка public/property-objects/list-property-objects.md. Дом полей — сущность Объект недвижимости.

Metadata

ПолеЗначение
Сервис/контекстchessboard
API areadevelopers
Feature grouppropertyObjects
Статусdraft
Документdocs/06-services/chessboard/api/developers/search-property-objects.md

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

ПроверкаЗначение
Actor typeUser (менеджер застройщика)
Auth policy / Permissionschessboard.property-objects.manage (../../identity/domain/permission.md)
Scope (RBAC)организация / группа проектов / проект (projectId в scope)
Tenant isolationTenantId = CompanyId (= DeveloperId) из JWT; выборка ограничивается tenant-ом актора; чужой проект → 404

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/v1/developers/projects/{projectId}/property-objects
Success status200
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
TenantIdidentity claims (tenant_id)GuidДаRequiredTenant isolation
ProjectIdrouteGuidДа!= Guid.Empty; существующий проект tenant-а404 при чужом/несуществующем
PagequeryintНет (default 1)>= 1
PageSizequeryintНет (default 50)1..200
BuildingIdqueryGuid?Нетздание этого проекта; null — все здания проекта404 при чужом здании
Floorsquery (csv)short[]?Неткаждый -5..200; ≤ 50 элементов, без дублей
Sectionsquery (csv)short[]?Неткаждый 1..50; ≤ 50 элементов, без дублей
Roomsquery (csv)short[]?Неткаждый 0..20 (0 = студия); ≤ 20 элементов, без дублей
Statusesquery (csv)PropertyObjectStatus[]?Нетзначения enum Free/Reserved/Booked/Sold/Hidden (reference-data.md); пусто = все
Pricequery (Price.Min/Price.Max)NullableRange<decimal>?НетPrice.Min >= 0, Price.Max >= 0, при обоих — Price.Min <= Price.Max (фильтр по PropertyObject.Price)
Searchquerystring?Нет<= 32 симв.; поиск по номеру lower(number) like lower(@Search%)
SortByqueryPropertyObjectSortBy (string)Нет (default FloorSectionNumber)enum ниже
IncludeDeletedqueryboolНет (default false)false → фильтр DeletedAt == null

Enum PropertyObjectSortBy (поле сортировки)

ЗначениеКолонки
FloorSectionNumber (default)floor asc, section asc nulls first, lower(number) asc
PriceAscprice asc
PriceDescprice desc

Тай-брейк любой сортировки — Id asc (стабильная пагинация).

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

Постраничный контейнер: Items, Page, PageSize, TotalCount (не сырой массив).

ПолеТипОбязательностьИсточникMaskingОписание
ItemsIReadOnlyList<DeveloperPropertyObjectListItem>Да (может быть пустым)проекция PropertyObjectEntityСтраница объектов недвижимости.
PageintДаecho запросаТекущая страница.
PageSizeintДаecho запросаРазмер страницы.
TotalCountlongДаподсчёт под фильтромВсего записей под фильтром.

Модель DeveloperPropertyObjectListItem (проекция property-object.md)

ПолеТипОбязательностьИсточникMaskingОписание
IdGuidДаPropertyObject.IdId объекта недвижимости (переход в карточку).
BuildingIdGuidДаPropertyObject.BuildingIdЗдание (литер/корпус).
NumberstringДаPropertyObject.NumberНомер объекта.
FloorshortДаPropertyObject.FloorЭтаж (ось Y шахматки).
Sectionshort?НетPropertyObject.SectionСекция/подъезд (ось X).
RoomsshortДаPropertyObject.RoomsКомнатность (0 = студия).
TotalAreadecimalДаPropertyObject.TotalAreaОбщая площадь, м².
LivingAreadecimal?НетPropertyObject.LivingAreaЖилая площадь, м².
PricedecimalДаPropertyObject.PriceТекущая цена.
CurrencyIdshortДаPropertyObject.CurrencyIdВалюта — ссылка на справочник handbook (= Project.CurrencyId, INV-PRJ-6).
PricePerMeterdecimalДаPropertyObject.PricePerMeterЦена за м² (денормализация).
StatusPropertyObjectStatus (string)ДаPropertyObject.StatusFree/Reserved/Booked/Sold/Hidden (reference-data.md).
ReservedUntilDateTimeOffset?НетPropertyObject.ReservedUntilТаймер резерва (⇔ Status = Reserved).
BookedUntilDateTimeOffset?НетPropertyObject.BookedUntilТаймер брони (⇔ Status = Booked).
LayoutIdGuid?НетPropertyObject.LayoutIdПланировка (layout.md).
WindowViewsIReadOnlyList<WindowViewKind> (string)Да (может быть пустым)PropertyObject.WindowViews.ViewsВиды из окон (reference-data.md).
SyncSourceChessboardSyncSourceДаPropertyObject.SyncSourcePin/TalentChessboard (UI блокирует реплика-поля).
VersionlongДаPropertyObject.VersionДоменная версия (для последующих PUT, update-property-object.md).

Предусловия

  • Пользователь авторизован; tenant_id = его компания-застройщик (= DeveloperId).
  • У актора есть permission chessboard.property-objects.manage в scope, покрывающем projectId.
  • Проект-родитель существует и принадлежит tenant-у актора; при заданном BuildingId — здание того же проекта; иначе — 404.

Алгоритм

Операция read-only, доменных событий не публикует.

  1. Контекст и доступ. Операцию выполняет менеджер застройщика; tenant — его компания; требуется право chessboard.property-objects.manage в scope Organization; при сужении «группа проектов/проект» — ProjectId должен входить в разрешённый набор (иначе проект недостижим → 404). Нет permission ни в одном scope → FORBIDDEN (403); просроченный/отозванный доступ → UNAUTHORIZED (401) / FORBIDDEN (403).
  2. Проверка входа. Диапазоны Page/PageSize, парсинг csv-enum-ов и Floors/Sections/Rooms (диапазоны, дубли, лимиты), Price.Min <= Price.Max, длина Search — иначе ValidationError (400). Проект-родитель существует в пределах tenant-а и в scope — иначе CHESSBOARD_PROJECT_NOT_FOUND (404). При заданном BuildingId — здание принадлежит этому проекту, иначе CHESSBOARD_BUILDING_NOT_FOUND (404). Допустим любой статус проекта.
  3. Выборка. Страница проекций собирается по фильтрам: ProjectId + tenant-фильтр; при заданном BuildingId — по зданию; Floors/Sections/Rooms/Statuses — совпадение по множествам; Price — диапазон по Price; Search — префикс номера; DeletedAt == null при !IncludeDeleted. Сортировка — по SortBy (таблица enum), тай-брейк Id asc. Считается TotalCount под тем же фильтром. Пустой результат и Page за пределами последней страницы — валидны: 200, Items = [], фактический TotalCount.
  4. Результат. Возвращаются Items, Page, PageSize, TotalCount; HTTP 200. Маскирование не требуется — менеджер видит полные данные своего tenant-а. Freshness: read-committed снимок; PricePerMeter денормализована, ReservedUntil/BookedUntil/Status отражают последнее применённое событие CRM (истёкший таймер при ещё Reserved/Booked — норма до страховочного job property-object-hold-expiration).

Acceptance Criteria

  • Happy path. Given проект с объектами недвижимости; When GET .../projects/{projectId}/property-objects; Then 200, страница в порядке шахматки (FloorSectionNumber), включая Hidden.
  • Фильтр по зданию и статусу. Given BuildingId=<id>&Statuses=Free; When запрос; Then только свободные объекты этого здания.
  • Диапазон цены. Given Price.Min/Price.Max; When запрос; Then только объекты в диапазоне; при Price.Min > Price.Max400 ValidationError.
  • Чужое здание. Given BuildingId вне проекта/tenant-а; When запрос; Then 404 CHESSBOARD_BUILDING_NOT_FOUND.
  • Чужой/вне-scope проект. Given projectId чужого tenant-а или вне scope; When запрос; Then 404 CHESSBOARD_PROJECT_NOT_FOUND.
  • Нет права. Given нет chessboard.property-objects.manage; When запрос; Then 403 FORBIDDEN.

Диаграмма

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
ValidationError400Неверные paging/enum/диапазоны/дубли/длина SearchСписок ошибок полейno retry
FORBIDDEN403Нет chessboard.property-objects.manage в scope«Недостаточно прав»no retry
CHESSBOARD_PROJECT_NOT_FOUND404Проект не найден / чужой tenant«Проект не найден»no retry
CHESSBOARD_BUILDING_NOT_FOUND404BuildingId не принадлежит проекту tenant-а«Здание не найдено»no retry

Сквозные ошибки области (UNAUTHORIZED 401) — в реестре README.md, здесь не повторяются.

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

  • Новые optional фильтры/поля ответа — additive; изменение семантики сортировок или default-порядка — breaking (rollout). Enum-строки — клиентский fallback на unknown обязателен.
  • Logs: TenantId, ProjectId, BuildingId, имена применённых фильтров (без значений), TotalCount; metrics: chessboard_developers_property_objects_search_total, latency p99 < 300 мс (SLO README); traces: OTel (traceparent).

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

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

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