GET /api/v1/projects/{id}/lots — Ассортимент проекта/отеля
Назначение
Список объектов конкретного проекта с ценами и статусами — страница /hotels/[id]/assortment и блок
«список всех объектов» полной карточки проекта: фильтры по этажу, комнатности, цене, типу объекта и
доступности; переход в карточку объекта. Часть полной карточки проекта → гейт GATE-FEED (токен
или реф-cookie — README.md); гостю список недоступен (статусы объектов и шахматка
скрыты). Источник — витрина catalog-property-object-card
по ProjectId.
Ассортимент ограничен одним проектом (сотни–тысячи строк) — offset-пагинация вместо keyset; шахматка
(матрица этажей × секций) — контекст chessboard, не этот метод.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | catalog |
| API area | public |
| Feature group | cards |
| Статус | draft |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User или RefGuest (GATE-FEED); Guest → 401 |
| Auth policy / Permissions | чтение — без permission; agentCommission — catalog.commissions.view (MASK-COMMISSION) |
| Scope | платформа |
| Tenant isolation | не применяется (платформенная витрина) |
| Audit | none; фильтры ассортимента — фронт пишет в user-activity |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/v1/projects/{id}/lots |
| Success status | 200 |
| Idempotency | не применяется (safe GET) |
| Correlation | OpenTelemetry trace (traceparent) |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
ProjectId | route {id} | Guid | Да | != Guid.Empty; проект существует и Published | — |
ActorClass | identity middleware | User/RefGuest | Да | GATE-FEED: Guest → 401 | README.md |
BuildingId | query | Guid? | Нет | валидный Guid (фильтр по корпусу) | — |
Floor | query (floorMin/floorMax → Floor.Min/Floor.Max) | NullableRange<short> | Нет | заданные границы >= -5; well-formed Floor.Min <= Floor.Max; null-граница = открытый интервал | — |
Rooms | query (csv) | IReadOnlyList<short>? | Нет | каждый 0..20; максимум 10 значений | — |
Price | query (priceMin/priceMax → Price.Min/Price.Max) | NullableRange<decimal> | Нет | заданные границы > 0; well-formed Price.Min <= Price.Max; null-граница = открытый интервал | — |
TypeName | query | string? | Нет | 1..64 (значение из get-catalog-filters.md) | — |
OnlyAvailable | query | bool | Нет | default false; true → только PropertyObjectStatus = Free | — |
Sort | query | AssortmentSort | Нет | enum Price (default) / Floor / AreaTotal / RoomsCount; иное → ValidationError | — |
Desc | query | bool | Нет | default false (asc) | — |
Page | query | int | Нет | >= 1, default 1 | — |
PageSize | query | int | Нет | 1..100, default 30 | — |
Enum AssortmentSort (локальный для ассортимента; ключ + Id тем же направлением)
| Значение | Ключ сортировки |
|---|---|
Price (default) | Price |
Floor | Floor |
AreaTotal | AreaTotal |
RoomsCount | RoomsCount |
Модель ответа GetProjectPropertyObjectsQueryResult
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
items | IReadOnlyList<AssortmentPropertyObjectModel> | Да | проекция объектов витрины | см. item | Страница объектов проекта. |
page / pageSize | int | Да | echo | — | Текущая страница / размер. |
totalCount | long | Да | счётчик по фильтру | — | Всего объектов под фильтром. |
appliedFilters | AppliedAssortmentFiltersModel | Да | echo запроса | — | Echo для URL-состояния (шаринг). |
AssortmentPropertyObjectModel
Состав = PropertyObjectListItemModel (get-property-objects.md — дом модели, дельта ниже). Актор прошёл
гейт → MASK-PRICE/MASK-STATUS не применяются; MASK-COMMISSION действует.
Поле (дельта к PropertyObjectListItemModel) | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
floor | short | Да | CatalogPropertyObjectCard.Floor | — | Этаж — колонка списка и фильтр. |
floorsTotal | short | Да | FloorsTotal | — | Этажность корпуса. |
areaLiving | decimal? | Нет | AreaLiving | — | Жилая площадь. |
AppliedAssortmentFiltersModel
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
buildingId | Guid? | Нет | echo | — | — |
floor | NullableRange<short> | Нет | echo | — | Применённый диапазон этажей (сериализуется в query как floorMin/floorMax). |
rooms | IReadOnlyList<short>? | Нет | echo | — | — |
price | NullableRange<decimal> | Нет | echo | — | Применённый ценовой диапазон (сериализуется в query как priceMin/priceMax). |
typeName | string? | Нет | echo | — | — |
onlyAvailable | bool | Да | echo | — | — |
sort | string (enum AssortmentSort) | Да | echo (после default-ов) | — | — |
desc | bool | Да | echo (после default-ов) | — | — |
Алгоритм
- Контекст и доступ. Актор по токену (
User; невалидный → 401) либо по реф-cookiereferralUrl(RefGuest); иначе 401 (GATE-FEED — часть полной карточки проекта). ПраваUser-а —catalog.commissions.view(для комиссии). - Проверка входа. Диапазоны/enum по таблице входа, well-formed диапазоны
FloorиPrice,Rooms.Count <= 10,Page >= 1,PageSize ∈ 1..100— иначеValidationError(400). Карточка проекта должна существовать и быть опубликованной (Status = Published); иначеCATALOG_PROJECT_NOT_FOUND(404) — ассортимент скрытого проекта не раскрывается.BuildingId, не принадлежащий проекту, даёт пустую выдачу (не ошибка). - Что читаем. Сначала карточка проекта (guard существования), затем страница объектов проекта:
отбор
Status = Published(INV-1 property-object-card) с фильтрамиBuildingId,Floor,Rooms,Price,TypeName,OnlyAvailable(→ PropertyObjectStatus = Free); сортировка —AssortmentSortId; вместе со страницей — общий счётчик под фильтром.
- Сборка ответа. Маппинг — дом
PropertyObjectListItemModel(get-property-objects.md) + дельтаfloor/floorsTotal/areaLiving;agentCommission— MASK-COMMISSION. - Результат.
GetProjectPropertyObjectsQueryResult→ 200;appliedFilters— URL-состояние. Консистентность — eventual (статусы объектов — лаг консюмераpin.chessboard.property-object-upserted.v1< 1 мин, SLO ../README.md); решающая проверка статуса при бронировании —crm.
Диаграмма
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
| — (auth) | 401 | Guest либо невалидный токен (GATE-FEED) | «Требуется вход или реферальная ссылка» | после входа |
ValidationError | 400 | Нарушение ограничений входа | Список ошибок полей | no retry |
CATALOG_PROJECT_NOT_FOUND | 404 | Проекта нет либо Status ∈ {Hidden, Delisted} | «Проект не найден» | no retry |
Жёсткие фильтры или чужой BuildingId дают 200 с items = [].
Acceptance Criteria
- Happy path. Дано опубликованный проект с объектами недвижимости на этажах 2–10. Когда
Userзапрашивает сfloorMin=2&rooms=1,2&onlyAvailable=true. Тогда200,items— толькоPublishedобъекты сFloor >= 2, комнатностью 1/2 иPropertyObjectStatus = Free;totalCount— под фильтром;appliedFiltersэхо для шаринга URL. - Гейт (гость). Дано
Guest(нет токена и реф-cookie). Когда запрашивает ассортимент. Тогда401(список объектов и статусы гостю не раскрываются — часть полной карточки проекта). - Скрытый проект. Дано проект в
Hidden/Delisted. Когда запрашивают ассортимент. ТогдаCATALOG_PROJECT_NOT_FOUND(404) — ассортимент скрытого проекта не раскрывается. - Чужой
BuildingId. ДаноBuildingIdиз другого проекта. Когда фильтруют. Тогда200сitems = [](жёсткий фильтр без совпадений — не ошибка). - Комиссия по праву. Дано
Userбезcatalog.commissions.view. Когда возвращаются объекты. ТогдаagentCommission = null(MASK-COMMISSION); с правом — заполнена. - Свежесть статуса. Дано объект только что забронирован в
crm. Когда ассортимент читается в окне лага (< 1 мин, событиеpin.chessboard.property-object-upserted.v1). Тогда возможен показ устаревшегоFree; решающую проверку статуса при бронировании выполняетcrmв источнике. - Невалидная сортировка. Дано
sort=Popularity(нет вAssortmentSort). Когда приходит запрос. ТогдаValidationError(400).
Совместимость и наблюдаемость
AssortmentPropertyObjectModelэволюционирует вместе с домомPropertyObjectListItemModel(get-property-objects.md); новые optional-поля — additive.- Logs: projectId, фильтры, размер выдачи; metrics —
catalog_assortment_requests_total{result}, latency histogram; traces/dashboard — общие правила area (README.md).
Обратные ссылки
Автоссылки: где используется этот документ.
- API (4): Catalog Public API — реестр методов и общие правила, GET /api/v1/catalog/filters — Справочники значений фильтров, GET /api/v1/projects — Лента проектов, GET /api/v1/projects/{id} — Карточка проекта: ограниченная / полная
- registries (1): Реестр API-методов PIN
Связанные документы
- Дом item-модели: get-property-objects.md; дом полей: ../domain/catalog-property-object-card.md.
- Карточка проекта: get-project.md; шахматка (матрица):
../../chessboard/(/hotels/[id]/chessboard). - Бронирование выбранного объекта:
../../crm/(booking).