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

UC-CHS-004. Просмотр шахматки проекта: гость vs инвестор (разница полей)

Назначение

Read-only сценарий просмотра шахматки проекта (матрица «этажи × секции» с цветовой индикацией статусов) и различие проекций для неавторизованного гостя и авторизованного инвестора/агента: гостю точные цены, статусы объектов и сама шахматка не отдаются. Сценарий относится к контексту chessboard.

Metadata

ПолеЗначение
IDUC-CHS-004
Контекстchessboard (см. docs/06-services/chessboard/)
Типосновной
Статусdraft

Актор

ПолеЗначение
РольГость (ограниченная проекция) / Инвестор / Агент (полная проекция)
Permissions / scopePermission не требуется: public area. Разница проекций — по факту аутентификации (валидный access-JWT identity). Agents area — те же полные чтения
TenantАктор вне tenant (инвестор/гость — TenantId = null, ADR-0052); публичное чтение отдаёт только Published-срезы
Record-levelНе применяется

Предусловие

  • Проект Published, его застройщик Published (иначе public-проекции фильтруют — INV-DEV-6), здание Published.
  • Объекты недвижимости заведены (UC-CHS-002 / UC-CHS-005); Hidden-объекты недвижимости публично не отдаются.

Триггер

ПолеЗначение
ИсточникПереход в интерфейсе / вызов API
Триггерящий элементКарточка проекта → вкладка «Шахматка» (/hotels/[id]/chessboard)

Основной Поток

  1. Инвестор (авторизован) открывает вкладку «Шахматка» карточки проекта; фронт вызывает GET /public/projects/{projectId}/chessboard?buildingId={buildingId}. Актор берётся из access-JWT, если передан (гость — анонимный).
  2. Проверяется вход: projectId/buildingId — Guid; состояние — Project.Status = Published и Developer.Status = Published (иначе 404 — скрытые срезы неотличимы от несуществующих); связь — building.ProjectId == projectId.
  3. Читаются данные под проекцию (без загрузки агрегатов): проект (Status, DeveloperId, Name, CurrencyId) → застройщик (Status) → корпуса проекта Status = Published (список для переключателя, сортировка по Name) → объекты недвижимости выбранного здания (DeletedAt == null, для гостя и public Status != Hidden, сортировка Floor desc, Section, Number) — один range-scan по индексу ix_property_objects_building_floor (building_id, floor, section).
  4. Строится read-модель шахматки: объекты недвижимости группируются по Floor (ось Y, от Building.FloorsTotal вниз) и Section (ось X). Маппинг ячейки: PropertyObjectId, Number, Rooms, TotalArea, Status → цвет (Free — зелёный, Reserved/Booked — жёлтый, Sold — красный, Hidden — серый, только для застройщика в developers area — публично отсутствует), Price + PricePerMeter (tooltip), таймеры ReservedUntil/BookedUntil (обратный отсчёт в tooltip для авторизованных).
  5. Результат — ChessboardModel: ProjectId, Buildings[] {Id, Name, FloorsTotal, SectionsCount}, SelectedBuildingId, Floors[] {Floor, Cells[] {PropertyObjectId, Number, Section, Rooms, TotalArea, Status, StatusColor, Price, PricePerMeter, ReservedUntil?, BookedUntil?}}, Legend[] (маппинг статус→цвет), CurrencyId + CurrencySnapshot (display-имя/знак валюты — snapshots.md). Freshness: read-committed снимок; таймеры клиент считает от серверного Now в ответе.
  6. Фронт рисует интерактивную матрицу: hover — tooltip (номер, цена), клик по ячейке — переход в карточку объекта (GET /public/property-objects/{id} — полная для авторизованного). Переключение корпуса/этажа — повторный запрос с другим buildingId (пагинация по этажам для больших зданий — параметры floorFrom/floorTo, виртуализация).

Альтернативные Потоки

A1. Гость запрашивает шахматку

  1. Запрос без валидного JWT → 401 c кодом CHESSBOARD_AUTH_REQUIRED: шахматка, точные цены и статусы объектов неавторизованным скрываются.
  2. Фронт показывает ограниченную карточку проекта (GET /public/projects/{id} — вкладки Информация/Локация/Финансирование/Юрданные/Ход строительства/Видео/Карта, диапазон цен проекта Price (Min–Max) без цен объектов недвижимости) и CTA входа. После авторизации (identity, UC-IDN-001) — возврат на шаг 1 основного потока.

A2. Гость смотрит ограниченную карточку объекта

  1. GET /public/property-objects/{id} без JWT → ограниченная проекция: описание, срок сдачи (Building.DeliveryYear/Quarter), общая/жилая площадь, этаж, застройщик — без точной цены, статуса, таймеров, инвест-аналитики. Авторизованному — полная (сама аналитика/график — invest-analytics и catalog, не шахматка).

A3. Проект скрыт/архивирован или застройщик Hidden

  1. Project.Status != Published либо Developer.Status != Published (INV-DEV-6) → 404 (утечка существования скрытых срезов недопустима). Building.Status = Hidden — корпус отсутствует в списке Buildings[]; прямой запрос по нему → 404.

A4. В здании нет доступных к показу объектов недвижимости

  1. Список объектов пуст → 200 c пустыми Floors[]; фронт показывает «объекты появятся позже» (не ошибка).

A5. Просмотр шахматки застройщиком (developers area)

  1. Менеджер застройщика смотрит ту же матрицу через GET /developers/projects/{id}/chessboard — без public-фильтров: видит Hidden-объекты недвижимости (серым), Draft/Hidden-корпуса, HoldDealId активных холдов. Permission chessboard.property-objects.manage, ограничение своим tenant-ом обычное (без bypass).

Межсервисный контракт, согласованность и наблюдаемость

Что читается и чего шахматка ожидает от соседей. Матрица строится только из локального состояния шахматки — source of truth статуса, таймеров и цены объектов недвижимости; синхронных вызовов соседних сервисов при отрисовке нет. Актуальность локального состояния обеспечивают фоновые потоки: статусы/таймеры — consumer CRM (UC-CHS-006, топики pin.crm.booking-*/deal-*); цены — UC-CHS-003/UC-CHS-005; паспортные поля и продажи вне PIN — синк Talent (UC-CHS-005).

Окно согласованности (что видит инвестор). Между событием CRM (например, резерв объекта недвижимости) и его применением consumer-ом UC-CHS-006 есть короткое окно, в котором матрица ещё показывает прежний статус (ячейка зелёная Free, хотя заявка на бронирование уже создана). После применения события повторный запрос вернёт жёлтую ячейку с ReservedUntil. Это ожидаемая eventual consistency, а не рассинхрон: шахматка всегда отдаёт свой последний закоммиченный (read-committed) статус.

Деградация зависимостей. Инвест-аналитика и график роста цены (invest-analytics/catalog) — вне границ шахматки; их недоступность не ломает саму матрицу (деградация: аналитический блок карточки скрыт, статусы/цены/таймеры объектов недвижимости отдаются как обычно).

Наблюдаемость / НФТ. Публичное чтение без записи и без PII; целевая латентность p95 — десятки мс на проект средней величины (один range-scan по ix_property_objects_building_floor); большие здания — пагинация по этажам (floorFrom/floorTo) + виртуализация. Public API под rate-limit (превышение → 429 с backoff); короткое кеширование матрицы допустимо, инвалидация — по статус/ценовым событиям объектов недвижимости здания. Разница проекций гость/инвестор — критичная точка приватности: контракт-тесты фиксируют, что неавторизованному не отдаются шахматка, точные цены, статусы и таймеры.

Постусловие

  • Состояние системы не изменилось (read-only); мутаций, событий, транзакций нет.
  • Инвестор видит актуальную (read-committed) матрицу со статусами/ценами/таймерами; гость — только ограниченную карточку без шахматки.
  • Факт просмотра фиксирует контекст user-activity (вне границ шахматки).

Возможные Ошибки

КодHTTPУсловиеПоведение клиента
CHESSBOARD_AUTH_REQUIRED401Запрос шахматки/полной карточки без JWT (A1)Показать CTA входа + ограниченную версию
CHESSBOARD_PROJECT_NOT_FOUND404Проект не существует / не Published / застройщик Hidden (A3)Страница 404
CHESSBOARD_BUILDING_NOT_FOUND404buildingId не существует / не Published / чужого проектаСброс на первый корпус проекта
— (validation)400Невалидные Guid / floorFrom > floorToИсправить параметры
— (rate limit)429Превышение лимита public APIRetry с backoff

Доменные События

Не поднимаются — read-only сценарий (просмотры трекает user-activity: события property-object-view-ended/session-ended по ADR-0056; шахматка событий просмотра не пишет).

Kafka Интеграционные События

Не публикуются. Потребляемая связь: актуальность статусов/цен в матрице обеспечивают UC-CHS-006 (consumer статусов CRM pin.crm.booking-*/pin.crm.deal-* — маппинг «событие → переход» в ../consumers/crm-property-object-status.md) и UC-CHS-003/005 (цены) — шахматка всегда читает своё локальное состояние-source-of-truth.

Acceptance Criteria

#КритерийПроверка
AC1Гость: 401 на шахматку; ограниченные карточки проекта/объекта без точных цен и статусовcontract-тест проекций гостя
AC2Инвестор: полная матрица со статусами, ценами, ценой/м², таймерамиcontract-тест авторизованной проекции
AC3Hidden-объекты недвижимости и Hidden/Draft-корпуса отсутствуют в public-ответе, но видны застройщику (A5)тест трёх областей (public/agents/developers)
AC4Скрытый проект/застройщик → 404 без утечки существования (A3)тест INV-DEV-6
AC5Ячейки сгруппированы Floor desc × Section; цветовая легенда соответствует статусамsnapshot-тест read-модели
AC6После CRM-события резерва (UC-CHS-006) повторный запрос показывает жёлтую ячейку с ReservedUntile2e-тест цепочки
AC7Пагинация по этажам (floorFrom/floorTo) возвращает согласованные срезытест пагинации
AC8Недоступность invest-analytics/catalog не ломает матрицу: аналитический блок скрыт, статусы/цены/таймеры отдаютсятест деградации зависимостей
AC9До применения CRM-события матрица показывает прежний статус, после — обновлённый (окно eventual consistency)тест окна согласованности

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

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

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