Appearance
Инициализация портфолио
Общий процесс
Портфолио работает только на клиенте (обёрнуто в <client-only>). При загрузке страницы выполняется следующая последовательность:
mermaid
sequenceDiagram
participant B as Браузер
participant P as PortfolioPage
participant MX as Mixin transactions
participant V as ValidationTransaction
participant LS as localStorage
participant Store as Vuex Store
participant API as Backend API
B->>P: Монтирование компонента (mounted)
P->>MX: setTransactionsFromStorage()
MX->>LS: getItem("transactions")
LS-->>MX: JSON-строка кластера
MX->>MX: JSON.parse() → defaultCluster если null
MX->>MX: insertTransactions(кластер)
MX->>V: hasCluster() — объект существует и непустой?
V-->>MX: true / false (reject при false)
MX->>V: isValidCluster() — каждое значение — массив?
V-->>MX: true / false (toast ошибки при false)
loop Для каждого coinId
MX->>MX: validateTransactionsList(transactions)
loop Для каждой транзакции
MX->>V: isValid(validator, transactionData, currenciesKeys)
Note over V: 1. isValidTransactionType(tt)<br/>2. getRequiredKeys(tt)<br/>3. hasRequiredKeys(data, keys)<br/>4. getRules(tt) → verify каждого поля
end
V-->>MX: Массив валидных транзакций
end
alt Есть невалидные транзакции
MX->>MX: toast ImportErrorToast
MX->>MX: needSaveUpdates = true
end
MX->>MX: setSpecialCoinsAssets()
Note over MX: Добавляет pirate/cosa<br/>если отсутствуют и нет онбординга
MX->>Store: setTransactions(validData)
MX->>LS: updateTransactionsStorage()
MX->>MX: fetchAssetsData()
loop Для каждого актива
MX->>MX: addCoinToFetch(coinId)
Note over MX: + все парные монеты из транзакций
end
MX->>API: fetchAllFilteredCoins()
API-->>MX: Данные монет (или notFoundCoins)
loop Ненайденные монеты
MX->>MX: removeAssetHandler(coinId)
end
MX->>Store: getAssetsData()
loop Для каждого coinId
Store->>Store: TransactionAssets.getAssetData()
Note over Store: Итерация транзакций,<br/>расчёт avg, holdings, profit
end
Store-->>P: Данные готовы
P->>P: Рендер UIДетали шагов
1. Чтение из localStorage
javascript
getTransactions() {
if (process.server) return this.defaultCluster;
const localTransactions = localStorage.getItem('transactions');
return JSON.parse(localTransactions) ?? this.defaultCluster;
}defaultCluster содержит пустые массивы для специальных монет (pirate, cosa).
2. Валидация кластера
Двухуровневая проверка:
hasCluster()— проверка что объект существует и имеет ключиisValidCluster()— проверка что каждое значение является массивом
3. Валидация транзакций
Для каждой транзакции вызывается ValidationTransaction.isValid():
- Проверка типа транзакции (
tt∈ {1, 2, 3, 4, 5}) - Получение списка обязательных полей для данного типа
- Проверка наличия всех обязательных полей
- Получение правил валидации для каждого поля
- Проверка значения каждого поля через
vee-validate
Невалидные транзакции фильтруются, показывается toast-уведомление.
4. Специальные монеты
Если пользователь ещё не прошёл онбординг (getStorageOnboarding() === false), в кластер добавляются pirate и cosa с пустыми массивами транзакций.
5. Загрузка данных с бекенда
Для каждой монеты в кластере и каждой парной монеты из транзакций выполняется запрос к API. Монеты, которые не были найдены на бекенде, удаляются из портфолио.
6. Расчёт ассетов
После загрузки рыночных данных запускается getAssetsData(), который для каждого coinId вызывает TransactionAssets.getAssetData() — итеративный расчёт avg, holdings, profit.
7. Миграция marketPrice
Асинхронный процесс marketPriceMigration() проверяет все транзакции типа Transfer и Mining на наличие поля marketPrice. Если отсутствует — делает запрос к API за историческими данными и дописывает поле.