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

src/live/results.service.ts плюс пакет packages/rating. Всё начисление — одна транзакция, а статус running → finished служит гейтом от второго нажатия.

Не выбывший — место 1, дальше по убыванию времени вылета. Тай-брейки по createdAt и id, чтобы порядок был детерминирован даже при совпадении меток.

N — число чек-инувшихся. Неявки в размер поля не входят, и это заметно влияет на очки: формула зависит от √N.

Куда Что
Result место, очки сезона, xp, skillBefore и skillAfter
PlayerProfile xp, пересчитанный level, новая skill, счётчики игр и побед
AuditLog запись о подтверждении
пуш tournament.results каждому участнику с аккаунтом
WS /live рассылка нового состояния

Формулы — в пакете rating, здесь только момент их применения.

У записи гостя есть guestPlayerId и нет профиля. Такой участник получает только строку Result — начислять некуда. Рейтинг подтянется при клейме аккаунта, см. аутентификацию.

Гейт подтверждения атомарный: конкурентный второй вызов получает 409 RESULTS_ALREADY_CONFIRMED до начисления. Это важнее, чем кажется: без такого порядка два нажатия в зале дали бы двойные очки.

POST /admin/tournaments/{id}/recount, только для finished, только у платформы.

Переиспользует то же начисление, но:

  • удаляет и пересоздаёт Result, сохраняя исходные createdAt;
  • учитывает уже заклеймленных гостей;
  • полностью пересобирает профили всех задетых игроков по хронологии.

Сохранение createdAt обязательно потому, что сила — экспоненциальное сглаживание и зависит от порядка турниров. Пересчёт не последнего турнира без этого сдвинул бы его в конец истории игрока и дал бы дрейф силы.

По той же причине профиль пересобирается, а не правится дельтой.

В AuditLog пишется каждая операция ТД: старт, вылет, отмена вылета, пересадка, подтверждение результатов. Это же источник для ленты событий турнира.