Add event management features to API, including event cancellation, deletion, registration closure, and workflow balance generation. Introduce new roster management endpoints and enhance participant handling with avoided player preferences. Update database schema and service logic to support these changes.
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / compose (push) Has been cancelled

This commit is contained in:
2026-07-19 02:10:02 +03:00
parent 59c0bdb345
commit 4b889fb8a0
30 changed files with 2766 additions and 149 deletions

View File

@@ -64,7 +64,7 @@ frontend/src/
## Границы модулей
- Players: профили, рейтинги, предпочтения ролей и желаемых союзников;
- Players: профили, рейтинги, предпочтения ролей, желаемых союзников и avoid-списки;
- Auth: Discord OAuth, сессии и роли доступа;
- Admin: управление игроками, регистрациями, событиями и капитанами;
- Events: календарь, регистрации участников и состояние игрового вечера;
@@ -78,13 +78,21 @@ frontend/src/
### Балансировщик
В MVP — детерминированный оптимизатор с весовой функцией качества и ограниченным поиском вариантов. Overwatch-ранги Bronze 5 → Champion 1 отображаются в ordinal 140 только для внутренних вычислений. Жёсткие ограничения обеспечивают состав 1/2/2 и уникальность игроков. Мягкие штрафы учитывают общий и ролевой разброс рейтинга, назначение вне предпочитаемой роли и разделение желаемых союзников. После построения нескольких начальных вариантов выполняется локальный поиск обменами игроков одной роли.
В MVP — детерминированный оптимизатор с весовой функцией качества и ограниченным поиском вариантов. Overwatch-ранги Bronze 5 → Champion 1 отображаются в ordinal 140 только для внутренних вычислений. Жёсткие ограничения обеспечивают состав 1/2/2 и уникальность игроков. Мягкие штрафы учитывают общий и ролевой разброс рейтинга, назначение вне предпочитаемой роли, разделение желаемых союзников и попадание avoid-пары в одну команду. Avoid имеет больший мягкий вес, чем пожелание играть вместе, но не нарушает требования баланса. После построения нескольких начальных вариантов выполняется локальный поиск обменами игроков одной роли.
Алгоритм располагается в доменном слое и не зависит от БД. При росте сообщества его можно заменить на CP-SAT/MILP solver без изменения API сценария.
### Оркестрация события и серии
Application-слой управляет единым versioned workflow события. Состояние события, выбранные команды, резерв и активная серия сохраняются транзакционно. Команды `close-registration`, `select-balance`, `edit-rosters`, `confirm-rosters` и `start-scrim` проверяют текущее состояние и `expectedVersion`, поэтому повторные клики и конкурирующие вкладки не создают две серии.
Ручное редактирование работает с серверным roster draft. Backend разрешает обмен только между одинаковыми ролевыми слотами и замену слота игроком из резерва, после чего пересчитывает средние рейтинги и метрики. Подтверждение требует полного состава 1/2/2, уникальных игроков и капитана внутри каждой команды.
`start-scrim` атомарно блокирует составы, создаёт одну или несколько Bo3-серий и первый draft state. Для двух команд создаётся одиночная серия; для четырёх и более подтверждённых команд создаётся single-elimination турнир. ID активной серии возвращается frontend, который переходит на live-экран.
### Драфт как конечный автомат
Жеребьёвка и баны моделируются состояниями и допустимыми командами. Сервер является источником истины и отклоняет действие, если оно нарушает порядок или регламент.
Жеребьёвка и баны моделируются состояниями и допустимыми командами. После жеребьёвки победитель получает первый бан для первой карты, а проигравший — первый бан героев. Первая карта определяется map draft. После результата второй и последующих карт FSM переходит в `MapPick`, где проигравшая команда напрямую выбирает карту из следующего пула; затем запускается hero draft. При ничьей право выбора получает проигравший начальную жеребьёвку. Сервер является источником истины и отклоняет действие, если оно нарушает порядок, версию или регламент.
### История действий
@@ -98,7 +106,11 @@ frontend/src/
### Календарь и участие
События хранятся в UTC и отображаются в локальном часовом поясе пользователя. REST API отдаёт будущие события и статусы Going/Maybe/NotGoing. Изменения регистрации публикуются через realtime-канал, чтобы Admin сразу видел актуальный список. При изменении статуса за другого игрока API сохраняет actor ID и источник `admin`, а UI показывает, кем сделана отметка.
События хранятся в UTC и отображаются в локальном часовом поясе пользователя. REST API отдаёт будущие и прошедшие события и статусы Going/Maybe/NotGoing. Изменения регистрации публикуются через realtime-канал, чтобы Admin сразу видел актуальный список. При изменении статуса за другого игрока API сохраняет actor ID и источник `admin`, а UI показывает, кем сделана отметка.
### Realtime-синхронизация
Один глобальный authenticated SSE-канал подписывается на wildcard topic и адресно инвалидирует TanStack Query-кэш для событий, RSVP, составов, серий, турниров и игроков. Поэтому новые регистрации, участники, жеребьёвка, баны, результаты и сетка появляются без перезагрузки. WebSocket не требуется: команды остаются обычными HTTP mutations, а серверные изменения передаются клиентам однонаправленно через SSE. Nginx отключает buffering и держит соединение открытым.
### Капитаны