Conventional commits / commits (pull_request) Successful in 12s
Specs security / security (pull_request) Successful in 7s
Specs verify / verify (pull_request) Successful in 11s
Specs security / security (push) Successful in 7s
Specs verify / verify (push) Successful in 9s
Record the approved proposal, delta specifications, design and 27 verified implementation tasks. Keep the change active; archiving and main-spec synchronization are deferred.
314 lines
33 KiB
Markdown
314 lines
33 KiB
Markdown
## Purpose
|
|
|
|
Определяет постоянное хранение данных T-Bank, локальное чтение и синхронизацию
|
|
по запросу с проверяемыми полнотой, свежестью и изоляцией подключения.
|
|
|
|
## ADDED Requirements
|
|
|
|
### Requirement: Все экраны читают сохранённые данные без ожидания T-Bank
|
|
|
|
Система SHALL постоянно сохранять уже загруженные счета, последний полный
|
|
портфель, исполненные операции, данные инструментов, текущие котировки,
|
|
дневные свечи и календари выплат. Доходы, личные позиции и прогнозные выплаты
|
|
SHALL рассчитываться по сохранённым источникам. Все соответствующие экраны,
|
|
включая режим «Все счета», поиск, сведения о компании и зависимости котировок,
|
|
MUST читать доступные данные без ожидания ответа T-Bank. Перезапуск backend
|
|
MUST NOT требовать повторной загрузки сохранённых полных интервалов.
|
|
|
|
#### Scenario: Брокер медленно отвечает при заполненном хранилище
|
|
- **WHEN** User открывает любой экран с сохранёнными данными текущего подключения, а T-Bank задерживает ответ
|
|
- **THEN** обычный серверный ответ содержит сохранённые данные и их состояние, не ожидая T-Bank, включая проверку доступных счетов
|
|
|
|
#### Scenario: Перезапуск сохраняет историю
|
|
- **WHEN** backend перезапущен и User запрашивает ранее полностью загруженный прошлый период
|
|
- **THEN** система возвращает его из постоянного хранилища без повторного получения операций или завершённых свечей этого периода
|
|
|
|
#### Scenario: Данные ещё не загружены
|
|
- **WHEN** User впервые запрашивает отсутствующие данные
|
|
- **THEN** ответ различает отсутствие загруженного результата и подтверждённый пустой результат, сообщает о запрошенной загрузке и не ожидает её завершения
|
|
|
|
### Requirement: Обновление запускается только запросом с учётом свежести
|
|
|
|
Система MUST запускать синхронизацию только запросом экрана либо ручным
|
|
обновлением. Обычный запрос SHALL планировать только отсутствующие зависимости,
|
|
пропуски и данные с истекшим сроком свежести: портфель и текущие цены —
|
|
60 секунд; операции текущего месяца и доступные счета — 300 секунд;
|
|
справочник инструментов, включая сведения о компании, и календари выплат —
|
|
24 часа; незавершённая дневная свеча — 60 секунд. Свежесть MUST отсчитываться
|
|
от последнего успешного получения соответствующего источника, а не чтения
|
|
ответа или расчёта производного представления. Полнота и свежесть MUST
|
|
оставаться независимыми признаками. Периодические запуски MUST NOT выполняться.
|
|
Для текущего изменяемого периода полнота SHALL относиться к состоянию на
|
|
последний успешный `asOf`: рост `to=now` внутри TTL не является новым
|
|
пропуском, требующим RPC. Настоящие пропуски до `asOf` и незавершённый хвост
|
|
при переходе месяца MUST NOT маскироваться этим правилом.
|
|
|
|
#### Scenario: Данные ещё свежие
|
|
- **WHEN** экран повторно запрошен до истечения сроков всех его полных зависимостей
|
|
- **THEN** запрос не создаёт новых обращений к T-Bank
|
|
|
|
#### Scenario: Текущий период запрошен через секунду
|
|
- **WHEN** текущий месяц полностью загружен на `asOf` и повторный запрос через секунду имеет более поздний `to=now`
|
|
- **THEN** система возвращает сохранённый результат с исходным `asOf` без нового RPC до истечения пяти минут, не утверждая наблюдение новых операций после `asOf`
|
|
|
|
#### Scenario: Истекла только свежесть портфеля
|
|
- **WHEN** портфель получен 60 секунд назад, а полные счета и metadata ещё свежие
|
|
- **THEN** обычный запрос инициирует обновление портфеля и только иных необходимых устаревших зависимостей, сохраняя прежний ответ доступным
|
|
|
|
#### Scenario: Пользователь не обращается к системе
|
|
- **WHEN** запрошенная работа завершилась и сроки свежести истекают без новых запросов
|
|
- **THEN** никакая синхронизация не запускается, в том числе при запуске backend
|
|
|
|
### Requirement: Ручное обновление ограничено текущим экраном
|
|
|
|
Действие «Обновить» SHALL обходить давность данных и запрашивать обновление
|
|
экрана с его зависимостями: текущего счёта либо доступных счетов в режиме
|
|
«Все счета», применённого периода и сравнений доходов, либо инструмента
|
|
с показанными свечами, выплатами и личной историей. Сервер MUST определять
|
|
разрешённые зависимости сам, сохраняя проверку входа и доступа. Обновление
|
|
MUST NOT перечитывать всю накопленную историю других периодов и инструментов.
|
|
Подтверждение приёма работы MUST NOT означать успешное обновление данных.
|
|
|
|
#### Scenario: Обновление старого месяца
|
|
- **WHEN** User нажимает «Обновить» на журнале за полностью загруженный прошлый месяц
|
|
- **THEN** перечитывается показанный период счёта и необходимые зависимости без принудительного перечитывания остальных полных месяцев
|
|
|
|
#### Scenario: Обновление доходов
|
|
- **WHEN** User обновляет аналитику за выбранный период
|
|
- **THEN** обновление охватывает выбранный и требуемые периоды сравнения, а результаты разных периодов не подменяют друг друга
|
|
|
|
#### Scenario: Обновление инструмента
|
|
- **WHEN** User обновляет инструмент с выбранным диапазоном графика и показанной личной историей
|
|
- **THEN** обновляются зависимости этого инструмента, диапазон свечей и показанный период личной истории разрешённых счетов без обновления всего каталога
|
|
|
|
### Requirement: Первая загрузка приоритизирует экран и продолжает историю
|
|
|
|
Система SHALL сначала загружать данные нужного экрану периода, включая
|
|
периоды сравнения доходов, затем всю доступную историю запрошенного счёта.
|
|
Для режима «Все счета» это применяется к каждому доступному счёту.
|
|
В рамках уже запрошенной работы SHALL заранее загружаться доступная рыночная
|
|
история бумаг текущего портфеля; для остальных инструментов — при первом
|
|
открытии. Новый запрос нужного экрану пропуска MUST получать приоритет перед
|
|
ещё не начатой догрузкой старой истории. Начатая работа MAY завершиться
|
|
после ухода со страницы. Недоказанная нижняя граница доступной истории
|
|
MUST NOT выдаваться за подтверждение её полной загрузки.
|
|
|
|
#### Scenario: Сначала сравнения доходов
|
|
- **WHEN** история счёта отсутствует и User открывает доходы за месяц
|
|
- **THEN** выбранный месяц и доступные обязательные периоды сравнения загружаются перед остальной историей; состояние старой истории не задерживает уже полный результат экрана
|
|
|
|
#### Scenario: Уход со страницы
|
|
- **WHEN** User уходит после начала догрузки истории
|
|
- **THEN** запрошенная работа может закончиться и сохранить результат, но не создаёт расписание последующих обновлений
|
|
|
|
#### Scenario: Инструмент отсутствует в портфеле
|
|
- **WHEN** User впервые открывает такой инструмент
|
|
- **THEN** система запрашивает и сохраняет его историю, не предзагружая весь каталог
|
|
|
|
### Requirement: Полнота подтверждается успешным покрытием интервалов
|
|
|
|
Система MUST считать историю счёта полной за период только после успешного
|
|
получения всех доступных исполненных операций всего этого интервала на момент
|
|
сверки. Полный период MAY быть пустым. Фильтр категорий, инструмента, размер
|
|
страницы, отсутствие записей, максимальная дата операции и наличие курсора
|
|
MUST NOT служить доказательством полноты. Производное представление «Все
|
|
счета» MUST учитывать покрытие каждого входящего счёта. Полные прошлые месяцы
|
|
SHALL сохраняться без автоматической повторной сверки; пропуски SHALL
|
|
догружаться при запросе. Смена месяца MUST NOT автоматически закрывать
|
|
незагруженный хвост прошлого месяца. Существующая семантика входных дат
|
|
журнала и доходов MUST сохраняться без пропусков и двойного учёта на границах.
|
|
|
|
#### Scenario: Пустой полный интервал
|
|
- **WHEN** все страницы интервала успешно получены и операций нет
|
|
- **THEN** период обозначается полным и пустым; последующее обычное чтение прошлого месяца не обращается к брокеру за этим интервалом
|
|
|
|
#### Scenario: Сбой промежуточной страницы
|
|
- **WHEN** одна из нескольких страниц интервала недоступна
|
|
- **THEN** интервал остаётся неполным, сохранённый результат не удаляется, частичные записи не выдаются за полный период
|
|
|
|
#### Scenario: Граница месяца
|
|
- **WHEN** последний успешный запрос прошлого месяца закончился до его конца
|
|
- **THEN** первый подходящий запрос нового месяца догружает недостающий хвост; смена даты сама не подтверждает полноту прошлого месяца
|
|
|
|
#### Scenario: Неполон один из счетов
|
|
- **WHEN** все счета, кроме одного, полностью покрывают период общего дохода
|
|
- **THEN** общий период не считается полным, а отсутствующий вклад счёта не заменяется нулём
|
|
|
|
### Requirement: Сверка операций сохраняет идентичность и прежний результат
|
|
|
|
Система SHALL использовать стабильный ID брокерской операции в границах
|
|
пользователя, подключения и счёта как принятое допущение проекта, а не гарантию
|
|
API. Повторное получение ID MUST обновлять поля той же операции без дубля
|
|
и двойного финансового учёта. Ручная сверка MUST согласовывать состав и поля
|
|
исполненных операций показанного интервала: отсутствующие в полном новом
|
|
ответе записи могут быть удалены только вместе с успешным завершением всех
|
|
страниц этого интервала. Частичный либо ошибочный новый ответ MUST NOT
|
|
изменять ранее опубликованный полный финансовый результат и время его
|
|
успешной сверки. Автоматическое обнаружение изменений полной старой истории
|
|
и смены ID не требуется.
|
|
|
|
#### Scenario: Поля операции исправлены
|
|
- **WHEN** успешная повторная сверка получает прежний ID с другой суммой или статусом, меняющим вхождение в исполненную историю
|
|
- **THEN** итоговый набор отражает новый полный ответ без дублирования ID, а доходы пересчитываются из согласованного набора
|
|
|
|
#### Scenario: Операция исчезла при ручной сверке
|
|
- **WHEN** все страницы перечитанного интервала успешно завершились и прежняя операция отсутствует
|
|
- **THEN** операция исключается из опубликованного набора этого интервала и производных финансовых результатов
|
|
|
|
#### Scenario: Перезагрузка полного периода оборвалась
|
|
- **WHEN** после нескольких страниц ручной сверки возникает ошибка
|
|
- **THEN** прежний полный набор, его суммы, полнота и время успешной сверки остаются доступными вместе с ошибкой обновления
|
|
|
|
#### Scenario: Сбой во втором месяце перечитываемого квартала
|
|
- **WHEN** ручная сверка полного квартала получила первый месяц, но оборвалась на странице второго
|
|
- **THEN** весь прежний результат показанного квартала, включая режим «Все счета», остаётся опубликованным; успешный новый первый месяц не смешивается с прежними остальными месяцами
|
|
|
|
### Requirement: Прогресс переживает перезапуск и одновременные запросы
|
|
|
|
Система MUST сохранять завершённые интервалы, незавершённую работу и её
|
|
прогресс. После перезапуска следующий подходящий запрос экрана или ручное
|
|
обновление SHALL возобновлять запрошенную работу, повторяя незавершённый
|
|
интервал безопасно и без повторной загрузки полных старых интервалов.
|
|
Одновременные запросы одной границы данных SHALL объединять одинаковую
|
|
работу, а пересекающиеся интервалы SHALL использовать общую загрузку
|
|
пересечения. Ошибка MUST оставлять возможность следующего явного запроса,
|
|
не создавая бесконечный цикл повторов. Поздняя публикация устаревшей попытки
|
|
MUST NOT перезаписывать результат новой попытки.
|
|
|
|
#### Scenario: Двадцать одновременных запросов
|
|
- **WHEN** двадцать запросов требуют один отсутствующий интервал одного счёта
|
|
- **THEN** выполняется один проход страниц этого интервала с разрешёнными транспортными повторами, а все запросы видят общее состояние работы
|
|
|
|
#### Scenario: Перезапуск между страницами
|
|
- **WHEN** процесс остановлен после сохранения прогресса, но до полного завершения интервала
|
|
- **THEN** до следующего запроса работа не возобновляется; после запроса интервал перечитывается без дублей и без ложного признака полноты
|
|
|
|
#### Scenario: Ошибка завершила попытку
|
|
- **WHEN** запрошенная синхронизация исчерпала допустимые транспортные повторы
|
|
- **THEN** она показывает безопасную ошибку и прекращает попытки до следующего запроса; наблюдение её состояния не запускает её заново
|
|
|
|
### Requirement: Общие данные инструментов отделены от личных
|
|
|
|
Система SHALL совместно использовать неперсональные metadata, котировки,
|
|
свечи и календари одного инструмента между пользователями. Общие записи
|
|
MUST NOT содержать аккаунты, личные позиции, остатки, операции, оценки выплат
|
|
пользователя, token либо персональные ошибки. Личные данные и вычисления
|
|
MUST оставаться ограничены пользователем, доступным счётом и текущим
|
|
подключением. Успешный общий результат MAY переиспользоваться между счетами
|
|
без повторного получения того же источника.
|
|
|
|
#### Scenario: Два пользователя открывают инструмент
|
|
- **WHEN** оба имеют право пользоваться интеграцией и общие данные инструмента уже загружены
|
|
- **THEN** они используют одни общие данные, но видят только собственные позиции, операции и оценки выплат
|
|
|
|
#### Scenario: Ошибка token одного пользователя
|
|
- **WHEN** источник отклоняет token одного пользователя при загрузке общего инструмента
|
|
- **THEN** личная ошибка не становится состоянием инструмента для другого пользователя и не удаляет ранее сохранённые общие данные
|
|
|
|
### Requirement: Локальная проверка доступа сохраняет границу подключения
|
|
|
|
Каждое чтение и действие синхронизации MUST проверять сессию, текущее
|
|
подключение и локально подтверждённую принадлежность счёта этому подключению.
|
|
Отсутствие ранее подтверждённого списка SHALL означать загрузку доступа,
|
|
а не доступ к произвольному accountId или доказанный пустой список.
|
|
Устаревший подтверждённый список MAY использоваться до фоновой сверки;
|
|
успешная сверка MUST отзывать доступ к исчезнувшим, закрытым или неподдержанным
|
|
счетам. Известный отказ token MUST сохранять существующую ошибку
|
|
`INVALID_TBANK_TOKEN`, отсутствие подключения — `TBANK_NOT_CONNECTED`,
|
|
недоступный счёт — `404`. Хранение не вводит архив без подключения.
|
|
Успешная замена или удаление подключения MUST блокировать выдачу прежних
|
|
личных данных и публикацию любых поздних результатов прежней работы.
|
|
Неуспешная замена MUST сохранять действующую границу.
|
|
|
|
#### Scenario: Подтверждённый счёт проверяется локально
|
|
- **WHEN** User читает заполненные данные доступного счёта текущего подключения
|
|
- **THEN** проверка принадлежности не обращается синхронно к T-Bank и не использует список другого пользователя или подключения
|
|
|
|
#### Scenario: Прямой переход до загрузки счетов
|
|
- **WHEN** список текущего подключения ещё неизвестен и User запрашивает конкретный accountId
|
|
- **THEN** система сообщает о загрузке проверки доступа, не выдаёт личных данных этого ID и загружает доступные счета
|
|
|
|
#### Scenario: Доступ к счёту отозван
|
|
- **WHEN** успешная фоновая сверка больше не подтверждает доступ к ранее видимому счёту
|
|
- **THEN** последующие запросы этого счёта возвращают `404`, а клиент прекращает показывать его как доступный
|
|
|
|
#### Scenario: Поздний результат прежнего подключения
|
|
- **WHEN** старый запрос заканчивается после успешной замены или удаления подключения
|
|
- **THEN** его данные, прогресс, ошибки и признаки свежести не публикуются и не становятся результатом нового подключения
|
|
|
|
### Requirement: Завершённость свечей подтверждается источником
|
|
|
|
Система SHALL сохранять дневные свечи по инструменту и времени источника.
|
|
Завершённые свечи MUST NOT перечитываться обычным запросом при полном
|
|
покрытии диапазона. Незавершённая свеча SHALL обновляться при запросе после
|
|
60 секунд до явного подтверждения завершённости источником, даже если её
|
|
календарный день прошёл. Успешное пустое окно MAY подтверждать отсутствие
|
|
свечей только в завершённом диапазоне и MUST NOT создавать синтетические
|
|
свечи. Открытое окно текущего дня, в том числе пока пустое, SHALL оставаться
|
|
изменяемым с минутным сроком свежести. Ручное обновление SHALL
|
|
перечитывать показанный диапазон, сохраняя прежние данные при ошибке.
|
|
|
|
#### Scenario: Наступил новый день
|
|
- **WHEN** сохранённая свеча предыдущего дня не подтверждена как завершённая источником
|
|
- **THEN** новый день не делает её окончательной, и подходящий запрос продолжает обновлять её с минутным сроком свежести
|
|
|
|
#### Scenario: Выходной без свечей
|
|
- **WHEN** источник успешно подтвердил пустое окно без торговых свечей
|
|
- **THEN** окно сохраняется как проверенное без выдуманных цен и повторной обычной загрузки завершённого диапазона
|
|
|
|
#### Scenario: До начала торгов свеча отсутствует
|
|
- **WHEN** запрос текущего дня вернул пустое окно, а после начала торгов User снова открывает график по истечении минуты
|
|
- **THEN** система запрашивает изменяемое окно и сохраняет появившуюся свечу; утренний ответ не закрепляет окончательное отсутствие свечи
|
|
|
|
### Requirement: Интерфейс различает свежесть полноту и состояние работы
|
|
|
|
На всех экранах T-Bank клиент MUST показывать время последнего успешного
|
|
получения, свежесть, загрузку и безопасную ошибку обновления применимых
|
|
данных. Полнота MUST относиться к показанному периоду и его зависимостям;
|
|
общий прогресс истории счёта MUST NOT подменять полноту экрана. Отсутствующие
|
|
данные SHALL иметь состояние первичной загрузки; доступные данные SHALL
|
|
оставаться видимыми во время обновления. Неполные totals/comparisons MUST
|
|
NOT показываться как окончательные или как ноль; неполный пустой журнал
|
|
MUST NOT сообщать, что операций нет. Наблюдение запрошенной работы MUST
|
|
прекращаться после завершения либо ухода и MUST NOT запускать синхронизацию.
|
|
Известные ошибки доступа MUST NOT маскироваться сохранённым результатом.
|
|
|
|
#### Scenario: Недавний период готов раньше всей истории
|
|
- **WHEN** показанный период и его зависимости полны, но старая история ещё загружается
|
|
- **THEN** пользователь видит готовый результат периода и отдельное состояние догрузки истории
|
|
|
|
#### Scenario: Полнота изменилась без новых записей
|
|
- **WHEN** источник подтвердил пустой период экрана, а остальная история ещё загружается
|
|
- **THEN** наблюдающий клиент получает изменение полноты и показывает подтверждённый пустой результат, не ожидая конца всей истории
|
|
|
|
#### Scenario: Ответы наблюдения завершились в обратном порядке
|
|
- **WHEN** поздний ответ чтения содержит прежние данные или доступ, а клиент уже принял более новое состояние этого ресурса либо отзыв доступа
|
|
- **THEN** прежний ответ не откатывает показанные данные и не восстанавливает недоступный личный результат
|
|
|
|
#### Scenario: Источник временно недоступен
|
|
- **WHEN** обновление сохранённого полного результата завершается временной ошибкой
|
|
- **THEN** прежние данные и время сохраняются, ошибка видима и доступно ручное повторное обновление; ошибка не обновляет время успешного получения и не считается успешной сверкой
|
|
|
|
#### Scenario: Неполное сравнение дохода
|
|
- **WHEN** выбранный период готов, а необходимый период сравнения ещё не загружен
|
|
- **THEN** сравнение обозначается как незавершённое без нулевой базы или окончательной разницы
|
|
|
|
### Requirement: Быстродействие и сокращение запросов проверяются измерениями
|
|
|
|
Для обычных чтений заполненной БД система SHALL иметь целевой бюджет
|
|
серверного ответа до 500 мс в зафиксированном профиле нагрузки. Выполнение
|
|
цели MUST подтверждаться измерениями, а не предполагаться из наличия БД.
|
|
Свежие полные зависимости MUST давать ноль новых RPC T-Bank; двадцать
|
|
одновременных запросов одного устаревшего набора MUST создавать не более
|
|
одной логической загрузки каждой зависимости. Полные старые операции и
|
|
завершённые свечи MUST давать ноль повторных RPC для своих интервалов при
|
|
обычном чтении, включая чтение после перезапуска.
|
|
|
|
#### Scenario: Измеряется заполненное хранилище
|
|
- **WHEN** проверяется согласованный набор обычных запросов при задержанном или недоступном T-Bank
|
|
- **THEN** фиксируются распределение серверных задержек, число RPC и параметры нагрузки; цель 500 мс не объявляется достигнутой без этих результатов
|
|
|
|
#### Scenario: Повторные открытия не перечитывают историю
|
|
- **WHEN** один и тот же полный прошлый период читается повторно без ручного обновления
|
|
- **THEN** сохранённые данные выдаются без RPC этого интервала, независимо от числа страниц клиентской выдачи
|