> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bedolagam.ru/llms.txt
> Use this file to discover all available pages before exploring further.

# База данных

> Полная схема базы данных: модели, связи и миграции

## Обзор

Бот использует PostgreSQL через SQLAlchemy 2.x async ORM. Все модели определены в `app/database/models.py`.

## Миграции (Alembic)

Миграции хранятся в `migrations/alembic/versions/`.

```bash theme={null}
# Применить миграции
make migrate

# Создать новую миграцию
make migration m="add_new_table"

# Отметить текущую БД как актуальную (без DDL)
make migrate-stamp

# История миграций
make migrate-history
```

При запуске бот автоматически выполняет `alembic upgrade head` через `app/database/migrations.py`.

> [!NOTE]
>
> Для существующих production баз после перехода на Alembic выполните `make migrate-stamp` один раз.

## Настройка подключения

```env theme={null}
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/bedolaga
```

Бот поддерживает как PostgreSQL, так и SQLite (для разработки).

## CRUD операции

Каждая сущность имеет свой CRUD-модуль в `app/database/crud/`:

```python theme={null}
from app.database.crud.user import create_user, get_user_by_telegram_id

async with get_session() as session:
    user = await get_user_by_telegram_id(session, telegram_id=123456)
```

## Enum-типы

| Enum | Значения |
| - | - |
| `UserStatus` | `active`, `blocked`, `deleted` |
| `SubscriptionStatus` | `trial`, `active`, `expired`, `disabled`, `pending` |
| `TransactionType` | `deposit`, `withdrawal`, `subscription_payment`, `refund`, `referral_reward`, `poll_reward` |
| `PromoCodeType` | `balance`, `subscription_days`, `trial_subscription`, `promo_group`, `discount` |
| `PaymentMethod` | `telegram_stars`, `tribute`, `yookassa`, `cryptobot`, `heleket`, `mulenpay`, `pal24`, `wata`, `platega`, `cloudpayments`, `freekassa`, `kassa_ai`, `riopay`, `severpay`, `manual`, `balance` |
| `TicketStatus` | `open`, `answered`, `closed`, `pending` |
| `PartnerStatus` | `none`, `pending`, `approved`, `rejected` |
| `WithdrawalRequestStatus` | `pending`, `approved`, `rejected`, `completed`, `cancelled` |
| `GameType` | `quest_buttons`, `lock_hack`, `letter_cipher`, `server_lottery`, `blitz_reaction`, `emoji_guess`, `anagram` |

## Схема по доменам

### Пользователи (3 таблицы)

**`users`** — центральная таблица, к ней привязаны все остальные сущности.

| Группа полей | Ключевые поля |
| - | - |
| Идентификация | `id`, `telegram_id` (UNIQUE, nullable), `auth_type` (`telegram`/`email`) |
| Профиль | `username`, `first_name`, `last_name`, `language`, `status` |
| Баланс | `balance_kopeks`, `has_had_paid_subscription`, `has_made_first_topup` |
| Реферал | `referred_by_id` (FK self), `referral_code` (UNIQUE), `referral_commission_percent` |
| RemnaWave | `remnawave_uuid` (UNIQUE), `trojan_password`, `vless_uuid`, `ss_password` |
| Email-авторизация | `email` (UNIQUE), `email_verified`, `password_hash`, `email_verification_token` |
| OAuth | `google_id`, `yandex_id`, `discord_id`, `vk_id` (все UNIQUE) |
| Промо | `promo_group_id` (FK), `promo_offer_discount_percent`, `auto_promo_group_assigned` |
| Ограничения | `restriction_topup`, `restriction_subscription`, `restriction_reason` |
| Партнер | `partner_status` |

Связи: `subscription` (1:1), `transactions` (1:N), `referrals` (self 1:N), `user_promo_groups` (1:N), `tickets` (1:N), `referral_earnings` (1:N), `discount_offers` (1:N)

**`cabinet_refresh_tokens`** — JWT refresh-токены для Cabinet.

| Поле | Описание |
| - | - |
| `user_id` | FK -> users |
| `token_hash` | SHA-256 хеш (UNIQUE) |
| `expires_at` | Срок действия |
| `revoked_at` | Время отзыва (nullable) |

**`partner_applications`** — заявки на партнерскую программу.

| Поле | Описание |
| - | - |
| `user_id` | FK -> users |
| `company_name`, `website_url`, `telegram_channel` | Данные заявки |
| `status` | PartnerStatus |
| `approved_commission_percent` | Утвержденная комиссия |

### Подписки (5 таблиц)

**`subscriptions`** — VPN-подписка (1:1 с пользователем).

| Группа полей | Ключевые поля |
| - | - |
| Основные | `user_id` (UNIQUE FK), `status`, `is_trial`, `start_date`, `end_date` |
| Трафик | `traffic_limit_gb` (0 = безлимит), `traffic_used_gb`, `purchased_traffic_gb`, `traffic_reset_at` |
| Устройства | `device_limit`, `modem_enabled` |
| Серверы | `connected_squads` (JSON), `subscription_url`, `subscription_crypto_link` |
| Автоплатеж | `autopay_enabled`, `autopay_days_before` |
| Тариф | `tariff_id` (FK, SET NULL) |
| Суточный | `is_daily_paused`, `last_daily_charge_at` |

**`subscription_servers`** — привязка подписки к серверам.

| Поле | Описание |
| - | - |
| `subscription_id` | FK -> subscriptions |
| `server_squad_id` | FK -> server\_squads |
| `paid_price_kopeks` | Цена за подключение к серверу |

**`subscription_conversions`** — конверсия триала в платную подписку.

**`subscription_events`** — журнал событий подписки (`event_type`, `amount_kopeks`, `message`, `extra` JSON).

**`traffic_purchases`** — докупки трафика (`traffic_gb`, `expires_at` = дата покупки + 30 дней).

### Платежи и транзакции (12 таблиц)

**`transactions`** — единый финансовый реестр.

| Поле | Описание |
| - | - |
| `user_id` | FK -> users |
| `type` | TransactionType |
| `amount_kopeks` | Сумма в копейках |
| `payment_method` | PaymentMethod |
| `external_id` | ID во внешней системе |
| `is_completed` | Завершена ли |
| `receipt_uuid` | UUID фискального чека NaloGO |

**10 таблиц платежных провайдеров** — по одной на каждый провайдер. Все содержат: `user_id`, `status`, `transaction_id`, сумму и ID во внешней системе. Telegram Stars и Tribute не имеют отдельных таблиц — используют `transactions` напрямую.

| Таблица | Провайдер | Уникальный ID |
| - | - | - |
| `yookassa_payments` | YooKassa | `yookassa_payment_id` |
| `cryptobot_payments` | CryptoBot | `invoice_id` |
| `heleket_payments` | Heleket | `uuid`, `order_id` |
| `mulenpay_payments` | MulenPay | `uuid` |
| `pal24_payments` | PayPalych | `bill_id` |
| `wata_payments` | WATA | `payment_link_id` |
| `platega_payments` | Platega | `platega_transaction_id`, `correlation_id` |
| `cloudpayments_payments` | CloudPayments | `transaction_id_cp`, `invoice_id` |
| `freekassa_payments` | Freekassa | `order_id`, `freekassa_order_id` |
| `kassa_ai_payments` | Kassa AI | `order_id`, `kassa_ai_order_id` |
| `riopay_payments` | RioPay | `riopay_payment_id` |
| `severpay_payments` | SeverPay | `severpay_payment_id` |

**`payment_method_configs`** — настройки отображения платежных методов в Cabinet.

| Поле | Описание |
| - | - |
| `method_id` | ID метода (UNIQUE) |
| `sort_order` | Порядок сортировки |
| `is_enabled` | Включен ли |
| `sub_options` | JSON: `{"card": true, "sbp": false}` |
| `min_amount_kopeks`, `max_amount_kopeks` | Лимиты сумм |
| `user_type_filter` | `all`, `telegram`, `email` |

### Промо-система (8 таблиц + 3 M2M)

**`promo_groups`** — группы со скидками.

| Поле | Описание |
| - | - |
| `name` | Название (UNIQUE) |
| `priority` | Приоритет (выше = важнее) |
| `server_discount_percent`, `traffic_discount_percent`, `device_discount_percent` | Скидки 0-100% |
| `period_discounts` | JSON: `{"30": 10, "90": 15}` |
| `auto_assign_total_spent_kopeks` | Порог автоназначения |
| `is_default` | Группа по умолчанию |

M2M связи: `server_squads`, `tariffs`, `payment_method_configs`

**`user_promo_groups`** — пользователь может состоять в нескольких группах (`user_id` + `promo_group_id` составной PK).

**`promocodes`** — промокоды (5 типов).

| Поле | Описание |
| - | - |
| `code` | Код (UNIQUE) |
| `type` | PromoCodeType |
| `balance_bonus_kopeks`, `subscription_days` | Параметры бонуса |
| `max_uses`, `current_uses` | Лимиты использования |
| `valid_until` | Срок действия |
| `first_purchase_only` | Только для первой покупки |
| `promo_group_id` | FK -> promo\_groups (дополнительная привязка) |

**`promocode_uses`** — журнал активаций (`promocode_id`, `user_id`, `used_at`).

**`discount_offers`** — персональные скидочные предложения.

| Поле | Описание |
| - | - |
| `user_id` | FK -> users |
| `discount_percent` | Процент скидки |
| `expires_at` | Срок действия |
| `is_active`, `claimed_at` | Статус |
| `effect_type` | Тип эффекта |
| `extra_data` | JSON с доп. параметрами |

**`promo_offer_templates`** — шаблоны предложений (тип, текст сообщения, кнопка, часы действия, серверы для test\_access).

**`subscription_temporary_access`** — временный доступ к серверам через промо-предложения.

**`promo_offer_logs`** — журнал действий с предложениями (`claimed`, `consumed`, `disabled`).

### Реферальная система (3 таблицы)

**`referral_earnings`** — начисления реферальных комиссий.

| Поле | Описание |
| - | - |
| `user_id` | FK -> users (реферер) |
| `referral_id` | FK -> users (приглашенный) |
| `amount_kopeks` | Сумма комиссии |
| `reason` | Причина начисления |
| `campaign_id` | FK -> advertising\_campaigns (nullable) |

**`withdrawal_requests`** — заявки на вывод средств.

| Поле | Описание |
| - | - |
| `user_id` | FK -> users |
| `amount_kopeks` | Сумма |
| `status` | WithdrawalRequestStatus |
| `risk_score` | 0-100 (антифрод) |
| `risk_analysis` | JSON с деталями анализа |

**`sent_notifications`** — трекинг отправленных уведомлений (предотвращает дублирование).

### Поддержка (5 таблиц)

**`tickets`** — тикеты поддержки (`status`, `priority`, `user_reply_block_permanent`, `user_reply_block_until`).

**`ticket_messages`** — сообщения в тикетах (текст, медиа-вложения с `media_file_id`).

**`ticket_notifications`** — уведомления для Cabinet (`notification_type`: new\_ticket, admin\_reply, user\_reply).

**`support_audit_logs`** — журнал действий модераторов (`action`, `actor_user_id`, `target_user_id`, `details` JSON).

**`broadcast_history`** — история рассылок.

| Поле | Описание |
| - | - |
| `target_type` | Сегмент аудитории |
| `channel` | `telegram`, `email`, `both` |
| `total_count`, `sent_count`, `failed_count`, `blocked_count` | Статистика доставки |
| `status` | Статус рассылки |

### Конкурсы (6 таблиц)

**`referral_contests`** — реферальные конкурсы (`title`, `contest_type`, `start_at`/`end_at`, `daily_summary_times`).

**`referral_contest_events`** — события конкурса (UNIQUE: `contest_id` + `referral_id`).

**`referral_contest_virtual_participants`** — виртуальные участники для буста лидерборда.

**`contest_templates`** — шаблоны ежедневных игр (7 типов).

| Поле | Описание |
| - | - |
| `slug` | Уникальный идентификатор игры |
| `prize_type` | `days`, `balance`, `custom` |
| `schedule_times` | Расписание (CSV "HH:MM") |
| `payload` | JSON с конфигурацией игры |

**`contest_rounds`** — раунды игр (`status`: active/finished, `winners_count`).

**`contest_attempts`** — попытки участия (UNIQUE: `round_id` + `user_id`).

### Кампании (2 таблицы)

**`advertising_campaigns`** — рекламные кампании с deep-link.

| Поле | Описание |
| - | - |
| `start_parameter` | Deep-link параметр (UNIQUE) |
| `bonus_type` | `balance`, `subscription`, `tariff`, `none` |
| `tariff_id` | FK -> tariffs (для тарифных бонусов) |
| `partner_user_id` | FK -> users (привязка к партнеру) |

**`advertising_campaign_registrations`** — регистрации через кампании (UNIQUE: `campaign_id` + `user_id`).

### Опросы (5 таблиц)

Цепочка: `polls` -> `poll_questions` -> `poll_options` -> `poll_answers`.

**`polls`** — опросы (`title`, `reward_enabled`, `reward_amount_kopeks`).

**`poll_responses`** — участие пользователя в опросе (UNIQUE: `poll_id` + `user_id`, `reward_given`).

### Колесо фортуны (3 таблицы)

**`wheel_configs`** — конфигурация колеса (`rtp_percent`, `spin_cost_stars`, `daily_spin_limit`).

**`wheel_prizes`** — призы (`prize_type`: subscription\_days/balance\_bonus/traffic\_gb/promocode/nothing, `manual_probability`).

**`wheel_spins`** — история вращений (`user_id`, `prize_id`, `payment_type`, `is_applied`).

### Серверы и тарифы (3 таблицы)

**`tariffs`** — тарифные планы (см. [Управление тарифами](https://docs.bedolagam.ru/bot/tariffs.md)).

**`server_squads`** — серверные группы.

| Поле | Описание |
| - | - |
| `squad_uuid` | UUID из RemnaWave (UNIQUE) |
| `display_name`, `original_name` | Названия |
| `country_code` | Код страны |
| `is_available`, `is_trial_eligible` | Доступность |
| `price_kopeks` | Цена подключения |
| `max_users`, `current_users` | Лимиты |

**`squads`** — устаревшая таблица серверов (заменена на `server_squads`).

### Новости (3 таблицы)

**`news_articles`** — статьи новостей.

| Группа полей | Ключевые поля |
| - | - |
| Основные | `id`, `title`, `slug` (UNIQUE), `content` (HTML), `excerpt` |
| Категория | `category` (legacy string), `category_color`, `category_id` (FK → news\_categories, SET NULL) |
| Тег | `tag` (legacy string), `tag_id` (FK → news\_tags, SET NULL) |
| Медиа | `featured_image_url` (http/https) |
| Публикация | `is_published`, `is_featured` (единственная), `published_at` |
| Метаданные | `read_time_minutes` (1-60), `views_count`, `created_by` (FK → users) |

Индексы: slug (unique), composite `is_published + published_at DESC`, composite `is_published + category`, `created_at`, FK-индексы.

**`news_categories`** — управляемые категории.

| Поле | Описание |
| - | - |
| `name` | Название (unique, case-insensitive) |
| `color` | HEX-цвет (`#00e5a0` по умолчанию) |

**`news_tags`** — управляемые теги.

| Поле | Описание |
| - | - |
| `name` | Название (unique, case-insensitive) |
| `color` | HEX-цвет (`#94a3b8` по умолчанию) |

### Системные и контентные (12 таблиц)

| Таблица | Описание |
| - | - |
| `system_settings` | Key-value хранилище рантайм-настроек |
| `service_rules` | Правила сервиса (по языкам) |
| `privacy_policies` | Политика конфиденциальности (по языкам) |
| `public_offers` | Публичная оферта (по языкам) |
| `faq_settings` | Настройки FAQ (по языкам) |
| `faq_pages` | Страницы FAQ |
| `user_messages` | Пользовательские сообщения от админа |
| `welcome_texts` | Приветственные тексты |
| `pinned_messages` | Закрепленные сообщения (текст, медиа) |
| `main_menu_buttons` | Кастомные кнопки главного меню |
| `menu_layout_history` | История изменений конфигурации меню |
| `monitoring_logs` | Журнал мониторинга |

### Вебхуки (2 таблицы)

**`webhooks`** — настройки вебхуков (`url`, `secret`, `event_type`, `failure_count`, `success_count`).

**`webhook_deliveries`** — история доставок (`status`: pending/success/failed, `attempt_number`, `next_retry_at`).

### Web API и Cabinet (2 таблицы)

**`web_api_tokens`** — API-ключи (`token_hash` UNIQUE, `token_prefix`, `expires_at`, `is_active`).

**`button_click_logs`** — журнал кликов по кнопкам меню (аналитика).

## Сводка по доменам

| Домен | Таблиц | Основные сущности |
| - | - | - |
| Пользователи | 3 | users, cabinet\_refresh\_tokens, partner\_applications |
| Подписки | 5 | subscriptions, subscription\_servers, subscription\_conversions, subscription\_events, traffic\_purchases |
| Платежи | 12 | transactions + 10 провайдеров + payment\_method\_configs |
| Промо | 8 + 3 M2M | promo\_groups, user\_promo\_groups, promocodes, discount\_offers, promo\_offer\_templates |
| Реферал | 3 | referral\_earnings, withdrawal\_requests, sent\_notifications |
| Поддержка | 5 | tickets, ticket\_messages, ticket\_notifications, support\_audit\_logs, broadcast\_history |
| Конкурсы | 6 | contest\_templates, contest\_rounds, contest\_attempts, referral\_contests |
| Кампании | 2 | advertising\_campaigns, advertising\_campaign\_registrations |
| Опросы | 5 | polls, poll\_questions, poll\_options, poll\_responses, poll\_answers |
| Колесо фортуны | 3 | wheel\_configs, wheel\_prizes, wheel\_spins |
| Серверы и тарифы | 3 | tariffs, server\_squads, squads |
| Системные | 12 | system\_settings, service\_rules, privacy\_policies, faq\_pages, main\_menu\_buttons, menu\_layout\_history и др. |
| Вебхуки | 2 | webhooks, webhook\_deliveries |
| Новости | 3 | news\_articles, news\_categories, news\_tags |
| Web API | 2 | web\_api\_tokens, button\_click\_logs |
| **Итого** | **\~69 + 3 M2M** | |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.