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.
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
|
||||
## Текущее состояние
|
||||
|
||||
Собран и проверен full-stack MVP: Go API с PostgreSQL, Discord OAuth и серверными сессиями, React-интерфейс, календарь и RSVP, административные сценарии, preference-aware балансировка 1/2/2, капитаны, Bo3-драфты и результаты, single-elimination сетка, SSE и RU/EN локализация. Добавлены Docker Compose для локальной разработки и Coolify, миграции, OpenAPI-контракт и CI.
|
||||
Собран полный вертикальный production-пайплайн скрима: закрытие регистрации → versioned балансировка → ручная правка и подтверждение составов 1/2/2 → капитаны → атомарный старт Bo3 или турнира → жеребьёвка → баны карт → баны героев → результаты и завершение. Состояние события, roster draft и серии сохраняется в PostgreSQL с optimistic locking, аудитом и глобальной SSE-синхронизацией без ручного обновления страницы.
|
||||
|
||||
Admin UI содержит пошаговый workflow и roster editor с same-role swap, резервом и запуском. Live, spectator и bracket используют реальные API-команды и realtime refresh; demo fixtures остались только локальным showcase.
|
||||
|
||||
## Подтверждённые требования
|
||||
|
||||
@@ -10,10 +12,11 @@
|
||||
- backend на Go с Clean Architecture;
|
||||
- современный frontend на фреймворке;
|
||||
- Discord OAuth и самостоятельное заполнение игроком рейтингов Tank, Damage и Support;
|
||||
- выбор игроком предпочитаемых ролей и до трёх желаемых союзников;
|
||||
- выбор игроком предпочитаемых ролей, до трёх желаемых союзников и до трёх avoid-игроков;
|
||||
- переключение всего интерфейса между RU и EN;
|
||||
- календарь запланированных миксов со статусами Going / Maybe / NotGoing;
|
||||
- административная панель управления событиями, игроками и регистрациями;
|
||||
- Admin может отменить событие с сохранением его в календаре или полностью удалить событие и связанные данные;
|
||||
- возможность Admin отметить участие за другого игрока;
|
||||
- автоматическая балансировка полных команд 1 Tank / 2 Damage / 2 Support;
|
||||
- резерв для участников, не вошедших в полные команды;
|
||||
@@ -26,13 +29,15 @@
|
||||
- production-развёртывание единым Docker Compose resource в Coolify;
|
||||
- PostgreSQL в том же Compose-стеке с persistent volume;
|
||||
- мобильный и десктопный интерфейс.
|
||||
- карточки отменённых событий отображаются красными, завершённых — зелёными, активных — розовыми с меткой LIVE.
|
||||
|
||||
## Исходный регламент
|
||||
|
||||
- основной Bo3: Control → Hybrid/Escort → Control;
|
||||
- альтернативный Bo3: Control → Push → Escort;
|
||||
- первый бан карт определяется жеребьёвкой;
|
||||
- карты банятся по очереди до одной оставшейся;
|
||||
- первая карта определяется банами: первый бан задаёт жеребьёвка, затем команды банят по очереди до одной оставшейся;
|
||||
- вторую и последующие карты напрямую выбирает команда, проигравшая предыдущую карту;
|
||||
- после ничьей следующую карту выбирает команда, проигравшая начальный жребий;
|
||||
- тайбрейкер Control не повторяет уже сыгранную Control-карту;
|
||||
- перед каждой картой команды делают по два бана героев в порядке A → B → A → B;
|
||||
- два бана одной команды должны относиться к разным ролям;
|
||||
@@ -53,12 +58,17 @@
|
||||
- игрок входит через Discord;
|
||||
- игрок самостоятельно указывает Overwatch-ранг от Bronze 5 до Champion 1 для каждой роли;
|
||||
- балансировщик автоматически назначает роли и собирает команды 1/2/2;
|
||||
- предпочтения ролей и союзников учитываются как мягкие штрафы после требований баланса;
|
||||
- предпочтения ролей, союзников и avoid-списки учитываются как мягкие штрафы после требований баланса;
|
||||
- участники сверх полного состава остаются в резерве;
|
||||
- первая турнирная сетка — single elimination;
|
||||
- игрок отмечается на микс через календарь;
|
||||
- Admin может изменить RSVP за игрока, а действие сохраняет автора;
|
||||
- капитан назначается отдельно для каждой команды и должен входить в её состав;
|
||||
- Admin закрывает регистрацию и фиксирует список участников перед балансировкой;
|
||||
- до подтверждения составов Admin может менять игроков одной роли местами и заменять игрока резервным;
|
||||
- подтверждённые составы и капитаны являются обязательным условием запуска скрима;
|
||||
- запуск скрима создаёт серверную серию и переводит событие в последовательный конечный автомат жеребьёвки, банов и результатов;
|
||||
- после запуска обычное редактирование составов блокируется; аварийная замена выполняется Admin отдельно и фиксируется в аудите;
|
||||
- исход карты задаётся как победа одной из команд или ничья;
|
||||
- сервер автоматически пересчитывает счёт Bo3 и победителя серии;
|
||||
- frontend, API и PostgreSQL разворачиваются одним Compose-стеком;
|
||||
@@ -66,6 +76,6 @@
|
||||
|
||||
## Ближайший следующий шаг
|
||||
|
||||
Развернуть тестовый экземпляр через Coolify, настроить Discord OAuth и выполнить приёмочное тестирование полного сценария на реальных аккаунтах и PostgreSQL.
|
||||
Проверить миграцию `006_event_workflow.sql` и полный сценарий на staging с реальной PostgreSQL (`TEST_DATABASE_URL`), затем провести первый пробный микс через production UI.
|
||||
|
||||
Открытое продуктовое решение, которое не блокирует каркас: как выбирается Hybrid/Escort для второй карты — заранее организатором, случайно или отдельным действием команд.
|
||||
Публичная вкладка FAQ описывает регистрацию, балансировку, полномочия капитанов, выбор карт, баны героев и подсчёт Bo3.
|
||||
|
||||
@@ -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 1–40 только для внутренних вычислений. Жёсткие ограничения обеспечивают состав 1/2/2 и уникальность игроков. Мягкие штрафы учитывают общий и ролевой разброс рейтинга, назначение вне предпочитаемой роли и разделение желаемых союзников. После построения нескольких начальных вариантов выполняется локальный поиск обменами игроков одной роли.
|
||||
В MVP — детерминированный оптимизатор с весовой функцией качества и ограниченным поиском вариантов. Overwatch-ранги Bronze 5 → Champion 1 отображаются в ordinal 1–40 только для внутренних вычислений. Жёсткие ограничения обеспечивают состав 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 и держит соединение открытым.
|
||||
|
||||
### Капитаны
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
Игрок связан с Discord-аккаунтом и имеет отображаемое имя и собственный Overwatch-ранг для каждой роли: Tank, Damage, Support. Лестница содержит 40 значений от Bronze 5 до Champion 1: Bronze, Silver, Gold, Platinum, Diamond, Master, Grandmaster, Champion, каждый с дивизионами 5 → 1. Для вычислений значения преобразуются в ordinal 1–40.
|
||||
|
||||
Игрок редактирует ранги самостоятельно; Admin может видеть время последнего изменения. Игрок выбирает любое число предпочитаемых ролей и до трёх желаемых союзников. Предпочтения являются мягкими пожеланиями, а не гарантией состава.
|
||||
Игрок редактирует ранги самостоятельно; Admin может видеть время последнего изменения. Игрок выбирает любое число предпочитаемых ролей, до трёх желаемых союзников и до трёх игроков для avoid. Предпочтения и avoid являются мягкими пожеланиями, а не гарантией состава; один игрок не может одновременно находиться в обоих списках.
|
||||
|
||||
### Account
|
||||
|
||||
@@ -20,6 +20,21 @@
|
||||
|
||||
Отметка игрока в календаре со статусом Going, Maybe или NotGoing. Игрок меняет собственный статус, а Admin может изменить его за игрока. Регистрация хранит автора и время последнего изменения, чтобы интерфейс явно показывал административное действие. В момент закрытия регистрации Admin подтверждает список участников; только Going-игроки по умолчанию попадают в балансировку.
|
||||
|
||||
### Жизненный цикл события
|
||||
|
||||
Событие проходит серверно контролируемые состояния:
|
||||
|
||||
1. `RegistrationOpen` — игроки меняют RSVP, Admin добавляет гостей;
|
||||
2. `RegistrationClosed` — список участников зафиксирован, обычные RSVP заблокированы;
|
||||
3. `Balancing` — создаются варианты команд и резерв;
|
||||
4. `RostersDraft` — Admin выбирает вариант, меняет игроков одной роли местами и заменяет их резервными;
|
||||
5. `RostersConfirmed` — составы 1/2/2 и капитаны проверены и заблокированы;
|
||||
6. `Live` — создана серия, разрешены только действия текущего шага жеребьёвки, банов или результата;
|
||||
7. `Completed` — серия или турнир завершены;
|
||||
8. `Cancelled` — Admin отменил событие; оно остаётся в календаре, но игровые действия заблокированы.
|
||||
|
||||
Переходы выполняются отдельными командами API с проверкой ожидаемой версии. Нельзя начать скрим без минимум двух полных команд, валидного состава 1/2/2 и капитана в каждой команде. После `RostersConfirmed` обычная перестановка запрещена. Аварийная замена во время `Live` доступна только Admin, должна сохранять ролевой состав и записывается в аудит. Полное удаление события является отдельной Admin-командой и каскадно удаляет его RSVP, составы, roster draft, серии, турнир и event-scoped аудит.
|
||||
|
||||
### Team
|
||||
|
||||
Состав игроков, назначенный Admin капитан, цвет/название и рассчитанные показатели силы. Капитан должен входить в текущий состав команды; переназначение фиксируется в аудите.
|
||||
@@ -50,7 +65,7 @@
|
||||
|
||||
### MapDraft
|
||||
|
||||
Пошаговое исключение карт из пула выбранного режима до одной оставшейся карты. Для тайбрейкера ранее сыгранная Control-карта исключается.
|
||||
Для первой карты — пошаговое исключение карт из пула до одной оставшейся. Вторую и последующие карты напрямую выбирает команда, проигравшая предыдущую карту; после ничьей право выбора получает команда, проигравшая начальный жребий. Ранее сыгранные карты исключаются.
|
||||
|
||||
### HeroDraft
|
||||
|
||||
@@ -72,7 +87,7 @@
|
||||
|
||||
Альтернативная последовательность: Control → Push → Escort.
|
||||
|
||||
Первый бан определяется жеребьёвкой. Затем команды по очереди исключают карты внутри пула режима, пока не останется одна.
|
||||
Первый бан первой карты определяется жеребьёвкой. Затем команды по очереди исключают карты внутри пула режима, пока не останется одна. После завершения карты FSM переходит в `MapPick`: проигравшая команда выбирает следующую доступную карту, после чего начинается hero draft.
|
||||
|
||||
### Герои
|
||||
|
||||
@@ -89,6 +104,7 @@
|
||||
- разницу средних рейтингов по ролям;
|
||||
- корректность состава 1 Tank / 2 Damage / 2 Support;
|
||||
- назначение игроков на предпочитаемые роли;
|
||||
- разведение по разным командам игроков из avoid-списков;
|
||||
- попадание выбранных игроком союзников в одну команду;
|
||||
- запрет повторения составов при регулярных играх;
|
||||
- заранее заданные пары или ограничения «вместе/раздельно».
|
||||
@@ -97,6 +113,8 @@
|
||||
|
||||
Жёсткие ограничения 1/2/2 и уникальности игроков всегда важнее предпочтений. Разница рейтингов имеет больший вес, чем пожелания. В MVP используется детерминированная эвристика: несколько начальных назначений, взвешенная целевая функция и локальные обмены игроков одной роли. Архитектура допускает замену на CP-SAT/MILP solver.
|
||||
|
||||
Ручная корректировка не изменяет исходные варианты балансировщика: выбранный вариант копируется в редактируемый roster draft. Обмен допускается только между слотами одинаковой роли. Замена из резерва назначает игрока в конкретный ролевой слот и пересчитывает показатели обеих команд и резерва.
|
||||
|
||||
## Доменные события
|
||||
|
||||
- EventCreated;
|
||||
|
||||
@@ -8,7 +8,7 @@ Mixmaker — веб-приложение для организации люби
|
||||
|
||||
- регистрация участников и оценка их силы по ролям;
|
||||
- вход через Discord и самостоятельный выбор ранга Bronze 5 → Champion 1 для каждой роли;
|
||||
- выбор предпочитаемых ролей и до трёх желаемых союзников;
|
||||
- выбор предпочитаемых ролей, до трёх желаемых союзников и до трёх игроков, которых желательно избегать;
|
||||
- переключение интерфейса между русским и английским языком;
|
||||
- календарь запланированных миксов и отметки об участии;
|
||||
- административная панель для управления событиями, участниками и их отметками;
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
# Дорожная карта
|
||||
|
||||
Статус на 19 июля 2026: технический MVP этапов 0–4 реализован. Следующий контрольный рубеж — развёртывание в Coolify и приёмочное тестирование с реальными Discord-аккаунтами; улучшения этапа 5 остаются будущей работой.
|
||||
Статус на 19 июля 2026: полный versioned production-пайплайн между событием, составами, live-серией и турнирной сеткой реализован. Backend, admin roster editor, live/spectator UI, SSE и проверки контракта связаны в единый сценарий.
|
||||
|
||||
## Завершённый контрольный рубеж — полный пайплайн скрима
|
||||
|
||||
- [x] закрыть регистрацию и зафиксировать Going-участников;
|
||||
- [x] сгенерировать и выбрать вариант баланса;
|
||||
- [x] дать Admin обменивать игроков одинаковой роли и заменять их резервными;
|
||||
- [x] пересчитывать показатели после каждой ручной правки;
|
||||
- [x] подтвердить составы 1/2/2 и капитанов;
|
||||
- [x] запускать одиночную Bo3-серию для двух команд или single-elimination турнир для нескольких;
|
||||
- [x] связать жеребьёвку, map draft, hero draft и результаты карт единым versioned FSM;
|
||||
- [x] блокировать обычное изменение составов после старта;
|
||||
- [x] разрешить аудируемую аварийную замену Admin;
|
||||
- [x] синхронизировать admin/live/spectator экраны через SSE;
|
||||
- [x] покрыть переходы и конфликты версий unit-, integration- и UI-тестами.
|
||||
|
||||
## Этап 0 — уточнение продукта
|
||||
|
||||
@@ -29,7 +43,7 @@
|
||||
## Этап 2 — игроки и балансировка
|
||||
|
||||
- самостоятельное заполнение профиля и рейтингов по ролям;
|
||||
- выбор предпочитаемых ролей и до трёх желаемых союзников;
|
||||
- выбор предпочитаемых ролей, до трёх желаемых союзников и до трёх avoid-игроков;
|
||||
- календарь будущих миксов;
|
||||
- статусы участия Going / Maybe / NotGoing;
|
||||
- админку для событий, игроков и регистраций;
|
||||
@@ -38,7 +52,7 @@
|
||||
- назначение доступных ролей;
|
||||
- генерация нескольких вариантов команд;
|
||||
- оценка качества баланса;
|
||||
- мягкий учёт ролевых и командных предпочтений в целевой функции;
|
||||
- мягкий учёт ролевых, командных и avoid-предпочтений в целевой функции;
|
||||
- ручная перестановка игроков с пересчётом показателей.
|
||||
- назначение и переназначение капитана каждой команды.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user