Запись — контракт и переходы
Модуль registrations в poker-api: запись, отмена, чек-ин, walk-in, страйки.
Все эндпоинты — за JwtAuthGuard.
Где код: repos/poker-api/apps/server/src/registrations/.
Контракт
Заголовок раздела «Контракт»| Метод и путь | Кто может | Что делает |
|---|---|---|
POST /tournaments/{id}/register |
игрок за себя | запись; отдаёт { status: 'registered' | 'waitlist' } |
POST /tournaments/{id}/cancel-registration |
игрок за себя | отмена; после дедлайна с занятого места — страйк |
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 |
список с именем, ником, аватаром и телефоном |
GET /tournaments/{id}/strikes |
owner, td |
страйки турнира |
POST /strikes/{id}/forgive |
owner, td |
простить страйк |
Пути даны без префикса /api/v1.
Коды ошибок при записи
Заголовок раздела «Коды ошибок при записи»| Код | Когда |
|---|---|
409 REGISTRATION_CLOSED |
статус турнира не registration |
403 REGISTRATION_BANNED |
у профиля игрока banUntil в будущем |
409 ALREADY_REGISTERED |
живая запись уже есть |
404 NOT_FOUND |
турнира или записи нет |
409 INVALID_STATUS |
операция не подходит текущему статусу записи |
Как работает внутри
Заголовок раздела «Как работает внутри»Место и запись — одна транзакция под блокировкой строки турнира.
SELECT … FOR UPDATE по tournaments сериализует конкурентные записи, поэтому
подсчёт занятых мест точен и capacity не переполняется. Занятыми считаются
registered и checked_in.
await tx.$queryRaw`SELECT id FROM tournaments WHERE id = ${tournamentId}::uuid FOR UPDATE`const takenSeats = await tx.registration.count({ where: { tournamentId, status: { in: ['registered', 'checked_in'] } },})const seat = takenSeats < tournament.capacity ? 'registered' : 'waitlist'Повторная запись после отмены переиспользует ту же строку и заново
отсчитывает позицию в очереди: createdAt = now. Уникальный индекс по
(tournamentId, userId) не даёт завести вторую строку; гонка на создании
ловится по P2002 и превращается в 409 ALREADY_REGISTERED.
Освободившееся место сразу поднимает первого из листа ожидания
guarded-write’ом, и пуш waitlist.promoted уходит только тем, кто реально
поднялся, — не всей очереди.
Отмена чек-ина смотрит на seatedFromWaitlist. Если человека сажали из
очереди, он возвращается в waitlist, иначе в registered. Без этого флага
человек из листа ожидания получил бы при старте неявку и страйк за турнир, куда
его не звали.
Страйки и бан — под блокировкой строки профиля. strikesToBan (2)
непрощённых страйков за strikeWindowDays (30) дают
banUntil = now + banDays (14). Прощение пересчитывает бан и снимает его, если
активных страйков стало меньше порога.
Ограничения и грабли
Заголовок раздела «Ограничения и грабли»- Walk-in намеренно не проверяет
capacity. Это решение ТД, а не баг; но значит, число записей может превысить заявленное число мест. - Walk-in идемпотентен: повтор и гонка возвращают ту же строку. Если телефон
принадлежит существующему пользователю, запись привязывается к нему, иначе
заводится
GuestPlayer— он клеймится при первом входе владельца телефона. lateRegLevelне работает. Поле в шаблоне блайндов есть, но нигде не проверяется: walk-in доступен на любом уровне, пока турнирrunning.- Пуш подписчикам
followee.registered— best-effort. Сбой отправки не роняет запись, и это осознанно. - Отмена турнира гасит живые регистрации без страйков.
Что рядом
Заголовок раздела «Что рядом»- Запись и лист ожидания — те же правила словами продукта.
- День игры: чек-ин, walk-in, санкции — что происходит дальше.
- poker-api — как запустить сервис локально.