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

День игры — контракт и фон

День игры трогает три вещи: эндпоинты стойки регистрации, фоновые напоминания и операцию старта, которая фиксирует состав.

Метод и путь Кто может
POST /tournaments/{id}/check-in owner, td
POST /tournaments/{id}/cancel-check-in owner, td
POST /tournaments/{id}/walk-in owner, td
GET /tournaments/{id}/registrations owner, td

Чек-ин возможен только из статуса registered и только пока турнир в registration. Полный контракт модуля — в статье Запись: контракт и переходы.

Принимает телефон и имя. Логика поиска человека:

  1. телефон принадлежит существующему пользователю → запись привязывается к нему;
  2. иначе заводится GuestPlayer, который клеймится при первом входе владельца телефона.

Операция идемпотентна: повтор и гонка возвращают ту же строку. Доступна пока турнир registration или running — то есть это же и поздняя дорегистрация. capacity намеренно не проверяется.

pg-boss на том же PostgreSQL, без Redis; стартует по DI-токену BOSS, в тестах подменён FakeBoss.

Джоба Расписание Что делает
tournament-reminders */5 * * * * пуш за 24 ч до старта и пуш в момент дедлайна отмены
season-rollover 5 0 * * * UTC архивирует истёкший сезон, заводит текущий квартал

Окно поиска равно периоду запуска — пять минут. Дедупликация идёт по журналу Notification: ключ складывается из kind, payload.tournamentId и payload.window, поэтому повторный тик или ручной перезапуск джобы не задваивает напоминание. Гости без аккаунта пропускаются — им некуда слать.

Дедлайн отмены у каждого организатора свой: organizer.cancelDeadlineHours, по умолчанию 3.

Требует минимум двух записей в checked_in. В одной операции:

  • рассадка — случайно и равномерно, SEATS_PER_TABLE = 9, разброс между столами не больше одного игрока;
  • всем, кто остался в registered, ставится no_show и выдаётся страйк;
  • запускается серверный таймер блайндов с первого уровня.

Отсюда важное следствие для отладки: после старта состав уже зафиксирован, и «потерянный» игрок — это почти всегда несделанный чек-ин, а не сбой рассадки.

Живое состояние турнира отдаёт GET /tournaments/{id}/live, подписка — namespace /live через socket.io. Гейт /live требует JWT на handshake: приложение организатора передаёт auth: { token } при подключении. Молчащий сокет чаще всего означает отсутствующий или протухший токен, а не потерянное соединение.