# Доменная модель

Источник: https://docs.novapoker.ru/dev/domain-model/
Дорожка: разработка
Сверено с кодом: Mon Aug 24
Код: repos/poker-api/apps/server/prisma/schema.prisma

---

Схема — `repos/poker-api/apps/server/prisma/schema.prisma`: 30 моделей, 476 строк,
23 миграции на 2026-08-24.

## Люди и профили

| Модель | Смысл |
|---|---|
| `User` | аккаунт: телефон (уникален), имя, ник, аватар, город, `adminRole?`, `status` (`active`/`banned`) |
| `PlayerProfile` | 1:1 с `User`: `xp`, `level`, `skill`, `gamesPlayed`, `wins`, `banUntil?` |
| `DealerProfile` | витрина дилера: фото, био, стаж, `gamesDealt` |
| `GuestPlayer` | walk-in без аккаунта: телефон и имя, `claimedByUserId?` |
| `OtpCode`, `RefreshToken` | материал аутентификации: хеши, TTL, счётчики |

`PlayerProfile` держит производные величины (уровень, силу, число игр). Они
пересчитываются, а не правятся поверх, — см. [рейтинг](https://docs.novapoker.ru/dev/rating.md).

## Клубы

| Модель | Смысл |
|---|---|
| `Organizer` | клуб: имя, статус, `cancelDeadlineHours` (по умолчанию 3) |
| `OrganizerMember` | членство `(organizerId, userId)` с ролью `owner \| td \| dealer` |
| `DealerInvite` | приглашение дилера: телефон и одноразовый код с TTL |
| `Location` | зал: имя, город, адрес, таймзона, координаты, описание, фото |
| `BlindTemplate` | структура блайндов: `levels` (JSON), `startingStack`, `lateRegLevel` |

У `BlindTemplate` значение `organizerId = null` означает **платформенный шаблон**,
доступный всем клубам.

## Игра

| Модель | Смысл |
|---|---|
| `Season` | квартал: `startsOn`/`endsOn`, статус `current \| archived` |
| `Tournament` | организатор, зал, сезон, шаблон, тип (`mtt \| sng`), старт, вместимость, статус, призы, чат, баннер и серверные поля часов: `currentLevelIdx`, `levelStartedAt`, `pausedAt`, `chipsInPlay` |
| `TournamentTranslation` | заголовок и подпись по локали (`ru`/`en`) |
| `TournamentDealer` | назначение дилера на турнир |
| `Registration` | запись игрока или гостя: статус, источник `app \| walk_in`, `eliminatedAt?` |
| `TableSeat` | посадка: `(tournamentId, tableNo, seatNo)` уникальна, `active` гасится при вылете |
| `Result` | итог: место, очки сезона, xp, сила до и после; `(tournamentId, place)` уникальна |
| `NoShowStrike` | страйк за неявку или позднюю отмену, прощаемый |

**Часы турнира живут на сервере, а не в клиенте.** `currentLevelIdx`,
`levelStartedAt` и `pausedAt` в `Tournament` — источник истины; клиент считает
оставшееся время сам, но от серверных значений.

Уникальность `(tournamentId, place)` в `Result` — та причина, по которой
перестановка мест делается перестановкой отметок между игроками, а не
переписыванием номеров подряд.

## Платформа и контент

| Модель | Смысл |
|---|---|
| `City` + `CityTranslation` | справочник городов (ru/en), `slug` — стабильный ключ сида |
| `LevelTier` + `LevelTierTranslation` | тиры уровней: `minLevel`, цвет, картинка, названия |
| `PromoSlide` + `PromoSlideTranslation` | слайдер главного экрана: картинка, цель (`tournament \| url`), окно показа, порядок |
| `RatingConfigRow` | единственная строка с конфигом рейтинга и санкций |
| `Follow`, `PushToken`, `Notification`, `AuditLog` | подписки, токены устройств, журнал уведомлений, аудит |

## Сид

`prisma db seed`, идемпотентен. Кладёт:

- платформенный шаблон блайндов «Стандарт 20 мин» — 13 отрезков, стек 20 000,
  перерыв 15 минут после шестого уровня;
- 10 городов, от Москвы до Владивостока;
- 6 тиров уровней: `fish` с уровня 1, `grinder` 3, `regular` 6, `shark` 10,
  `pro` 15, `legend` 25.

## Что рядом

- [Жизненный цикл турнира](https://docs.novapoker.ru/product/tournament-lifecycle.md) — те же сущности в движении.
- [Рейтинг: пакет формул](https://docs.novapoker.ru/dev/rating.md) — что пишется в `Result` и `PlayerProfile`.
- [poker-api](https://docs.novapoker.ru/dev/poker-api.md) — как накатить миграции локально.
