Skip to content

Инициализация портфолио

Общий процесс

Портфолио работает только на клиенте (обёрнуто в <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. Валидация кластера

Двухуровневая проверка:

  1. hasCluster() — проверка что объект существует и имеет ключи
  2. isValidCluster() — проверка что каждое значение является массивом

3. Валидация транзакций

Для каждой транзакции вызывается ValidationTransaction.isValid():

  1. Проверка типа транзакции (tt ∈ {1, 2, 3, 4, 5})
  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 за историческими данными и дописывает поле.