Record the approved proposal, delta specifications, design and 27 verified implementation tasks. Keep the change active; archiving and main-spec synchronization are deferred.
17 KiB
MODIFIED Requirements
Requirement: Получение Portfolio выбранного Brokerage Account
Система SHALL предоставлять авторизованному User GET /api/v1/brokerage-accounts/:accountId/portfolio. Она MUST возвращать нормализованный полный Portfolio Snapshot только для открытого поддерживаемого Brokerage Account, доступного через текущий T-Bank connection User. Ответ MUST иметь envelope { data, meta } по контракту tbank-data-sync; сохранённый полный Snapshot в data MUST содержать lastSuccessfulAt в RFC 3339 UTC без миллисекунд, boolean isStale, totalsByCurrency, positions и cashBalances.
Portfolio Snapshot является результатом наблюдения Portfolio на конкретный момент. Система MUST сохранять последний полный Snapshot постоянно, без накопления исторических снимков стоимости. Это MUST NOT подтверждать доступность Brokerage Account вне текущего T-Bank connection. Проверка локальной сессии, текущего подключения и сохранённого состава доступных счетов MUST выполняться без синхронного запроса списка счетов T-Bank. При отсутствии сохранённого Snapshot ответ MUST иметь 200, data: null, meta.availability: missing и состояние запрошенной загрузки; это MUST NOT обозначать пустой Portfolio.
Scenario: Получение текущего Portfolio
- WHEN User запрашивает Portfolio доступного Brokerage Account, а полный Snapshot текущего подключения успешно сохранён и ещё свеж
- THEN система отвечает
200с полным нормализованным Portfolio Snapshot вdataбез ожидания T-Bank сisStale: falseи временем успешного получения финансового снимка
Scenario: Недоступный Brokerage Account
- WHEN для текущего подключения подтверждено, что
accountIdне принадлежит User, закрыт, неподдерживаем или больше не доступен - THEN система отвечает
404без Portfolio Snapshot
Scenario: Сохранённый Portfolio читается после перезапуска
- WHEN backend перезапущен, текущий connection не изменился и полный Snapshot уже сохранён
- THEN запрос возвращает его из постоянного хранилища без ожидания T-Bank и отдельно сообщает свежесть и состояние фонового обновления
Requirement: Свежесть и fallback полного Portfolio Snapshot
Система MUST постоянно сохранять последний полный успешный Portfolio Snapshot отдельно для User, Brokerage Account и текущего T-Bank connection. Обычный запрос MUST возвращать сохранённый Snapshot без ожидания T-Bank; при возрасте не менее одной минуты он MUST запросить фоновое обновление. Временная ошибка MUST NOT удалять Snapshot или ограничивать его выдачу прежним пределом пяти минут. lastSuccessfulAt MUST сохранять время успешного получения финансового снимка и совпадать с соответствующим временем в meta; isStale MUST отражать meta.freshness: stale, а не состояние клиентского кэша. Ручное действие «Обновить» MUST обходить проверку давности текущего Portfolio и его показанных зависимостей. Неуспешное ручное обновление MUST сохранять исходное lastSuccessfulAt и MUST NOT само по себе устанавливать isStale: true, пока минутный срок свежести сохранённого финансового снимка не истёк; ошибка MUST отображаться отдельно через meta.sync.
Metadata инструмента MUST NOT влиять на успешность или свежесть Portfolio Snapshot. Сохранённые metadata MUST использоваться без ожидания их обновления, а отсутствующие сведения MUST допускать существующий fallback идентификатора. Новые доступные metadata MUST дополнять представление, не меняя времени финансового снимка. Общие данные инструментов MUST следовать суточной свежести tbank-data-sync, без сохранения персональных позиций в общих данных.
После успешной замены или удаления T-Bank connection прежние Snapshot и поздние результаты его запросов MUST NOT обслуживать новое подключение. Известный отказ token текущего подключения MUST блокировать личное чтение с существующим INVALID_TBANK_TOKEN; он MUST NOT маскироваться сохранённым Snapshot.
Scenario: Свежий Snapshot
- WHEN User повторно запрашивает тот же Portfolio ранее чем через одну минуту после успешного получения
- THEN система возвращает свежий Snapshot без нового обращения к T-Bank
Scenario: Медленный lookup инструмента
- WHEN metadata инструмента ещё отсутствует и его lookup не завершился
- THEN система возвращает сохранённый финансовый Snapshot без ожидания lookup с доступным fallback, а после успешного lookup последующее чтение использует полученное name
Scenario: Fallback после временной upstream-ошибки
- WHEN обновление устаревшего Snapshot неуспешно из-за временной недоступности T-Bank, а последний полный успешный Snapshot получен более пяти минут назад
- THEN система отвечает
200этим Snapshot сisStale: true, исходнымlastSuccessfulAtи ошибкой обновления вmeta.sync, без повторной синхронной попытки
Scenario: Ручное обновление ещё свежего Snapshot неуспешно
- WHEN User принудительно обновляет Snapshot, полученный менее минуты назад, и попытка завершается временной ошибкой до истечения его TTL
- THEN наблюдение возвращает прежний Snapshot с
isStale: false, исходнымlastSuccessfulAtи отдельной ошибкой синхронизации; ошибка не продлевает TTL и не делает данные устаревшими раньше срока
Scenario: Недействительный token не маскируется fallback-данными
- WHEN фоновое обновление подтвердило отклонение token текущего подключения как неаутентифицированного или без прав, после чего запрошен Portfolio
- THEN система не возвращает fallback Snapshot и отвечает
400Problem Details с codeINVALID_TBANK_TOKEN
Requirement: Состояния и ошибки Portfolio на клиенте
Клиент SHALL загружать Portfolio при открытии защищённого маршрута
/accounts/:accountId. Он MUST использовать успешный ответ в течение
30 секунд, без automatic retry неуспешного запроса, периодической синхронизации или refetch при возврате фокуса. Наблюдение за состоянием уже запрошенной работы MUST следовать tbank-data-sync и MUST NOT запускать новую работу.
При загрузке без ранее полученного успешного ответа либо при data: null
с состоянием загрузки для этого же Brokerage Account клиент MUST показывать структурное skeleton-состояние ожидаемого
содержимого, включая сводку, без вымышленных сумм. Область загрузки MUST
иметь доступное обозначение занятости; skeleton MUST NOT быть интерактивным
или получать клавиатурный фокус.
Истечение срока свежести MUST NOT само по себе скрывать уже доступный
Client Cached Snapshot. Во время фонового обновления и после его временной ошибки
клиент MUST сохранять ранее полученные данные того же Portfolio, включая
валютные итоги и исходное lastSuccessfulAt. При обновлении клиент MUST
показывать компактную неблокирующую индикацию без замены данных skeleton.
При ошибке обновления он MUST показывать локализованное неблокирующее
предупреждение о последних полученных данных и предоставлять действие
повторной попытки. Сохранённые данные MUST NOT считаться новым успешным
снимком или подтверждением текущей доступности Brokerage Account.
Клиент MUST показать штатное empty-состояние только при meta.availability: complete, непустом data и пустых positions
и cashBalances, без нулевых валютных карточек. Для показываемого состава
Portfolio клиент MUST сохранять время успешного получения снимка
lastSuccessfulAt, форматируемое в UTC. При isStale: true клиент MUST
показывать Snapshot, его исходное время и нейтральное предупреждение
об устаревших данных. Ошибка синхронизации MUST иметь отдельное безопасное объяснение. На экране MUST быть доступно действие «Обновить», обновляющее текущий экран и его зависимости независимо от давности. Server-side stale и ошибка фонового
обновления Client Cached Snapshot MUST оставаться раздельными состояниями:
ошибка клиента MUST NOT менять или подменять isStale, а оба предупреждения
и действие повтора клиентского обновления MUST оставаться доступными при
одновременном наличии этих состояний.
При ответе 404 для этого же Portfolio клиент MUST показать состояние недоступного счёта
с действием возврата к списку счетов без действия повтора. При TBANK_NOT_CONNECTED или INVALID_TBANK_TOKEN клиент MUST скрыть личные данные даже при наличии Snapshot. При отсутствии Snapshot и TBANK_UNAVAILABLE
или иной ошибке клиент MUST показать безопасное error-состояние с доступными
для ошибки действиями; действие повтора разрешено только для временной
и неизвестной ошибки.
Scenario: Показ устаревшего Portfolio
- WHEN клиент получает успешный ответ с
isStale: true - THEN клиент показывает позиции, остатки, валютные итоги, время последнего успешного снимка, нейтральное предупреждение об устаревании и доступное действие «Обновить»
Scenario: Недоступный счёт на клиенте
- WHEN клиент получает
404для текущего Portfolio - THEN клиент не показывает Portfolio и предлагает вернуться к списку Brokerage Account без действия повтора
Scenario: Первичная загрузка Portfolio
- WHEN клиент ожидает Portfolio этого Brokerage Account без ранее
полученного успешного ответа либо получает
data: nullс активной загрузкой - THEN он показывает структурный skeleton, включая место сводки, с доступным обозначением загрузки, без вымышленных или нулевых сумм и без фокусируемых placeholders
Scenario: Обновление после истечения срока свежести
- WHEN срок свежести показанного Portfolio истёк и началось фоновое обновление данных того же Brokerage Account
- THEN клиент сохраняет прежние валютные итоги и время их успешного получения, показывает неблокирующую индикацию обновления и не заменяет доступный состав и сводку skeleton-состоянием
Scenario: Ошибка обновления после успешного ответа
- WHEN клиент уже показывает Client Cached Snapshot Portfolio, а обновление этих же данных завершается временной ошибкой
- THEN клиент сохраняет данные, суммы и время прежнего снимка,
показывает неблокирующее предупреждение и действие повтора;
ошибка не делает сохранённый снимок свежим и не изменяет
isStale
Scenario: Ошибка клиентского обновления при server-side stale
- WHEN клиент показывает Portfolio с
isStale: true, а фоновое обновление этого же Portfolio завершается временной ошибкой - THEN прежние суммы и время остаются видимыми одновременно с нейтральным server-side предупреждением, отдельным предупреждением об ошибке клиентского обновления и его действием повтора
Scenario: Повторное обновление успешно
- WHEN после ошибки фонового обновления User запускает повторную попытку и получает успешный Portfolio Snapshot
- THEN до ответа прежние суммы и время остаются видимыми, затем
клиент показывает значения и
lastSuccessfulAtуспешного ответа, убирает предупреждение об ошибке клиентского обновления и показывает server-side предупреждение только в соответствии с новымisStale
Scenario: Штатный пустой Portfolio на клиенте
- WHEN успешно полученный ответ имеет
meta.availability: completeи Portfolio вdataсодержит пустыеpositionsиcashBalances - THEN клиент показывает существующее empty-состояние без валютных карточек с вымышленными нулевыми итогами