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

Investor-Owner API — реестр операций и общие контрактные модели

Индекс HTTP-операций сервиса investor-owner (портфель инвестора, страница /profile/my-lots — «Мои объекты недвижимости») и общий дом контрактных моделей ответа портфеля (MoneyModel, PropertyObjectSnapshotModel, CurrentValuationModel) и правила деградации текущей оценки. Файлы операций описывают только свою дельту и ссылаются сюда.

Metadata

ПолеЗначение
Сервис/контекстinvestor-owner
Статусdraft
Owning teamPIN Investor Experience
Область действияPublic API area (портфель инвестора, self-only); admin-операции — отдельные документы
AssumptionsТекущая оценка объекта недвижимости — internal-API invest-analytics (см. «Текущая оценка» ниже, assumption до фиксации контракта); история цен — public API invest-analytics по PropertyObjectId
Зависимости../domain/investor-property.md, ../domain/owner-mapping.md, invest-analytics (текущая оценка + история цен)

Реестр операций (public, self-only)

#ОперацияHTTPRouteFeature groupPermissionДокумент
1getMyPropertiesGET/api/v1/portfolio/propertiesportfolioinvestor-owner.portfolio.view-selfpublic/get-my-properties.md
2getMyPropertyGET/api/v1/portfolio/properties/{propertyId}portfolioinvestor-owner.portfolio.view-selfpublic/get-my-property.md
3getMyPortfolioSummaryGET/api/v1/portfolio/summaryportfolioinvestor-owner.portfolio.view-selfpublic/get-my-portfolio-summary.md

Admin-операции (Служба заботы: очередь конфликтов связи, ручное разрешение, компенсирующие исправления портфеля) — карта в ../README.md, документы api/admin/: admin/list-owner-mapping-conflicts.md, admin/resolve-owner-mapping-conflict.md, admin/archive-investor-property.md, admin/create-investor-property.md (permissions investor-owner.mappings.manage / investor-owner.portfolio.manage). Internal-API и callbacks отсутствуют — межсервисное взаимодействие контекста только через Kafka (см. ../README.md).

Общие правила доступа (все public-операции)

ПравилоЗначение
Actor typeUser (инвестор, Bearer JWT; InvestorUserId = claim sub)
Permissioninvestor-owner.portfolio.view-self (каталог — ../../identity/domain/permission.md)
Tenant isolationне применяется: данные инвестора вне tenant (ADR-0052)
Record-levelобязательный фильтр InvestorUserId = actor; условие владельца дублируется явно в запросе — defence-in-depth
Enumerationчужая/несуществующая запись портфеля неразличимы: единый INVESTOR_OWNER_PROPERTY_NOT_FOUND (404)
Maskingсуммы (цены, доход) — финансовые данные владельца: в ответ отдаются без маскирования только владельцу; в логи суммы не пишутся (только PropertyId/PropertyObjectId/количества)
Auditread-sensitive: чтение своего портфеля — без audit-записи; платформенный просмотр чужого портфеля (investor-owner.portfolio.view) — только admin API с deep audit (ADR-0053), в этом реестре не описывается

Общие контрактные модели портфеля

MoneyModel (проекция VO Money — дом полей ../domain/investor-property.md)

ПолеТипОбязательностьОграниченияОписание
amountdecimalДаnumeric(18,2); отрицательные значения допустимы только для профитовСумма.
currencyIdshortДассылка на справочник валют handbook (../../handbook/domain/currency.md); v1 — всегда RUB (ADR-0048)Валюта — типизированная ссылка.

Display-код валюты в MoneyModel не передаётся: единая валюта записи (INV-5) отдаётся один раз — currencyIsoCode в сводке (public/get-my-portfolio-summary.md, денормализованный CurrencyIsoCode записи); полный display-набор валюты — публичный lookup справочника (../../handbook/api/public/lookup-references.md).

PropertyObjectSnapshotModel (проекция VO PropertyObjectSnapshot — дом полей ../domain/investor-property.md)

Пары id + name проекта и корпуса свёрнуты во вложенные ref-модели (strict-typed — strict-typed-documentation-rules.md), а не в плоские поля *Name.

ПолеТипОбязательностьИсточникОписание
titlestringДаPropertyObjectSnapshot.TitleНазвание объекта недвижимости на момент покупки.
numberstring?НетPropertyObjectSnapshot.NumberНомер объекта недвижимости.
projectProjectRefModel?НетPropertyObjectSnapshot.ProjectПроект (ЖК/отель) — ref-модель ниже; null, если объект недвижимости не сопоставлен с шахматкой PIN.
buildingBuildingRefModel?НетPropertyObjectSnapshot.BuildingЗдание/литер — ref-модель ниже; null при отсутствии сопоставления.
areaTotaldecimal?НетPropertyObjectSnapshot.AreaTotalПлощадь, м².
floorint?НетPropertyObjectSnapshot.FloorЭтаж.
snapshotAtstring (ISO 8601)ДаPropertyObjectSnapshot.SnapshotAtМомент фиксации снапшота (иммутабелен — «что купили»).

ProjectRefModel (проект — ref-модель)

ПолеТипОбязательностьОписание
projectIdGuidДаProjectId (снапшот на момент покупки; совпадает с денормализованным projectId записи).
projectNamestringДаProjectName (название проекта на момент покупки).

BuildingRefModel (корпус — ref-модель)

ПолеТипОбязательностьОписание
buildingIdGuidДаBuildingId (снапшот на момент покупки).
buildingNamestringДаBuildingName (здание/литер).

CurrentValuationModel (read-проекция текущей оценки; в БД investor-owner не хранится)

ПолеТипОбязательностьИсточникОписание
availableboolДаcomputedtrue, если текущая цена получена от invest-analytics (см. правило деградации ниже).
currentPriceMoneyModel?Нет (обязателен при available = true)invest-analytics (NewPrice последнего факта цены объекта недвижимости)Текущая полная цена объекта недвижимости.
pricedAtstring (ISO 8601)?Нетinvest-analytics (EventTime последнего факта)Момент последнего изменения цены.
unrealizedProfitMoneyModel?Нетcomputed: currentPrice.amount − PurchasePrice.Amount; только при available = true, Status = Active и совпадении валют оценки и записи (currentPrice.currencyId = PurchasePrice.CurrencyId)Нереализованный доход (может быть отрицательным). Не хранится (см. ../domain/investor-property.md).

Текущая оценка: источник и правило деградации

  • Источник — internal-API invest-analytics getLotsCurrentPrices (батч по PropertyObjectId[], последняя цена из ленты lot_price_history_facts; ../../invest-analytics/api/get-lots-current-prices.md, assumption до фиксации контракта; валюта цены — CurrencyId справочника handbook: события ленты цен chessboard несут CurrencyId). Операционный source of truth текущей цены — chessboard (../../invest-analytics/domain/price-history-fact.md); для портфеля достаточно аналитической ленты (freshness ≤ 5 мин).
  • Оценка запрашивается только для записей Status = Active c ProjectId != null (объект сопоставлен с шахматкой PIN). Для ProjectId = null (несопоставленный объект Talent Owners) и для Sold оценка не запрашивается: available = false.
  • Вызов — best-effort: timeout 500 мс, retry отсутствует (простой GET на горячем пути списка). Любой сбой/таймаут/отсутствие цены по объекту недвижимости → available = false, currentPrice = null, операция завершается 200 (портфель работает без оценки — failure mode из ../README.md). Метрика деградации: investor_owner_current_valuation_degraded_total.
  • История изменения цен объекта в этом API не отдаётся: карточка возвращает признак priceHistoryAvailable, фронт запрашивает график напрямую у public API invest-analytics по PropertyObjectId (../../invest-analytics/api/public/get-property-object-price-history.md).

Согласованность и свежесть (операции чтения)

  • Записи портфеля (InvestorPropertyEntity) — сильная согласованность внутри сервиса: после появления объекта (consumer deal-completed / синк Talent Owners) или admin-правки чтение видит результат немедленно (read-your-writes). Порядок появления и лаги источников — SLO ../README.md (объект после deal-completed < 1 мин; лаг синка Talent Owners < 15 мин).
  • Текущая оценка (CurrentValuationModel) — eventual: свежесть аналитической ленты цен invest-analytics ≤ 5 мин; при недоступности/таймауте источника — деградация до available = false без ошибки операции. Реализованные показатели (RealizedProfit и суммы по Sold) от оценки не зависят и всегда полны.
  • Промежуточные состояния, видимые пользователю: пока связь Собственник ↔ Инвестор не установлена (PendingLink / Conflict), объекты этого собственника ещё не появляются в портфеле; после перехода в Linked и обработки владений они появляются в течение окна лага синка.

Совместимость (все операции)

  • Новые optional поля — additive; удаление/переименование/смена типа — breaking (migration + синхронный rollout фронта /profile/my-lots).
  • Enum-значения (OwnershipStatus, PropertySource) сериализуются строками; клиент обязан игнорировать неизвестные значения (unknown enum handling: показывать запись без бейджа статуса, не падать).

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