# Telegram Shop — Database Schema Единая SQLite-база (`db/shop.db`), используемая **одновременно** ботом (`src/`, better-sqlite3) и админ-панелью (`admin-next/`, Prisma). Источник истины схемы: `admin-next/prisma/schema.prisma`. Миграции бота: `src/migrations/` (001–014, runner: `src/migrations/runner.js`). ## Схема ### users — пользователи Telegram | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | telegram_id | TEXT UNIQUE | ID в Telegram | | username | TEXT? | | | country / city / district | TEXT? | География | | status | INTEGER (0) | 0=active, 2=blocked | | total_balance | REAL (0) | Основной баланс | | bonus_balance | REAL (0) | Бонусный баланс | | language | TEXT ('en') | en / es / de | | language_set | INTEGER (0) | Флаг выбора языка | | notes | TEXT? | Заметки админа (миграция 014) | | created_at | DATETIME | | Связи: `wallets`, `transactions`, `purchases`. ### crypto_wallets — криптокошельки | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | user_id | INTEGER FK → users | | | wallet_type | TEXT | BTC / LTC / ETH / USDT / USDC | | address | TEXT | | | derivation_path | TEXT? | | | mnemonic | TEXT? | Seed-фраза (зашифрована) | | balance | REAL (0) | | | created_at | DATETIME | | Уникальность: `(user_id, wallet_type)` — один кошелёк каждого типа на пользователя. ### transactions — транзакции | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | user_id | INTEGER FK → users | | | wallet_type | TEXT | | | tx_hash | TEXT? | | | amount | REAL | | | created_at | DATETIME | | ### locations — локации | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | country | TEXT | | | city | TEXT | | | district | TEXT ('') | | | is_active | INTEGER (1) | 0/1 | | created_at | DATETIME | | Уникальность: `(country, city, district)`. ### categories — категории | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | location_id | INTEGER FK → locations | | | name | TEXT | | | is_active | INTEGER (1) | | | created_at | DATETIME | | Уникальность: `(location_id, name)`. ### subcategories — подкатегории | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | category_id | INTEGER FK → categories | | | name | TEXT | | | is_active | INTEGER (1) | | | created_at | DATETIME | | Уникальность: `(category_id, name)`. ### products — товары | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | location_id | INTEGER FK → locations | | | category_id | INTEGER FK → categories | | | subcategory_id | INTEGER? FK → subcategories | | | name | TEXT | | | description | TEXT? | | | private_data | TEXT? | Скрытый контент (выдаётся после покупки) | | price | REAL | | | quantity_in_stock | INTEGER (0) | Для mono-товаров = 999999 | | photo_url | TEXT? | Публичное фото | | hidden_photo_url | TEXT? | Скрытое фото | | hidden_coordinates | TEXT? | Скрытые координаты | | hidden_description | TEXT? | Скрытое описание | | is_mono | INTEGER (0) | 1 = неограниченный товар | | created_at | DATETIME | | ### purchases — покупки | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | user_id | INTEGER FK → users | | | product_id | INTEGER FK → products | | | wallet_type | TEXT? | | | tx_hash | TEXT? | | | quantity | INTEGER | | | total_price | REAL | | | purchase_date | DATETIME | | | status | TEXT ('pending') | pending / completed / cancelled | ### commission_payments — комиссионные выплаты | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | total_balance_usd | REAL | | | commission_rate | REAL | | | commission_amount_usd | REAL | | | paid_amount_usd | REAL | | | wallet_count | INTEGER | | | note | TEXT? | | | created_at | DATETIME | | ### audit_log — журнал аудита | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | action | TEXT | `balance_adjust`, `operator_connect`, `seed_phrase_viewed`, `clear_all` и др. | | admin_id | TEXT | Роль или telegram_id | | details | TEXT? | JSON-строка с контекстом | | created_at | DATETIME | | ### chat_sessions — чат-сессии (чатбот) | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | session_id | TEXT UNIQUE | | | telegram_id | TEXT? | | | lead_id | INTEGER? FK → leads | | | messages | TEXT | JSON-массив `{role, content, timestamp}` | | language | TEXT ('en') | | | device / ip / country | TEXT? | | | customer_profile | TEXT? | AI-профиль клиента (JSON) | | is_active | BOOLEAN (true) | | | operator_name | TEXT? | | | auto_reply_disabled | BOOLEAN (false) | | | operator_connected_at | DATETIME? | | | created_at / updated_at | DATETIME | | ### leads — лиды | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | telegram_id | TEXT? UNIQUE | | | name / phone / email / telegram | TEXT? | | | status | TEXT ('new') | new / contacted / qualified / lost / spam | | verification | TEXT ('pending') | | | notes | TEXT? | | | custom_fields | TEXT ('{}') | JSON | | geo_address | TEXT? | | | ai_lead_score | REAL? | | | created_at / updated_at | DATETIME | | Связь: `chatSessions`. ### site_settings — настройки (ключ-значение) | Колонка | Тип | Описание | |---|---|---| | id | INTEGER PK | | | key | TEXT UNIQUE | Например `chatbot_*` | | value | TEXT | | | created_at / updated_at | DATETIME | | ### user_states — состояния пользователей бота | Колонка | Тип | Описание | |---|---|---| | chat_id | TEXT PK | | | state_data | TEXT? | JSON состояния | | updated_at | INTEGER | Unix-время | ## Связи (ER-сводка) ``` users 1──N crypto_wallets users 1──N transactions users 1──N purchases locations 1──N categories 1──N subcategories locations 1──N products categories 1──N products subcategories 1──N products products 1──N purchases leads 1──N chat_sessions leads 1──1 users (по telegram_id, не FK) ``` ## Примечания - **Бот и админка работают с одной БД**: любые изменения мгновенно видны обеим сторонам - `users` и `leads` — **отдельные таблицы**, связываются по `telegram_id` (не внешний ключ) - `mnemonic` хранится зашифрованным (ключ `ENCRYPTION_KEY`) - Каскадное удаление: wallets/transactions/purchases удаляются вместе с пользователем; products — вместе с location/category - Миграции бота нумеруются `NNN_*.js`; Prisma-схема админки должна отражать ту же структуру