feat: Система автоматического подтверждения выигрышей с поддержкой множественных счетов
Some checks reported errors
continuous-integration/drone/push Build encountered an error
Some checks reported errors
continuous-integration/drone/push Build encountered an error
Основные изменения: ✨ Новые функции: - Система регистрации пользователей с множественными счетами - Автоматическое подтверждение выигрышей через inline-кнопки - Механизм переигровки для неподтвержденных выигрышей (24 часа) - Подтверждение на уровне счетов (каждый счет подтверждается отдельно) - Скрипт полной очистки базы данных 🔧 Технические улучшения: - Исправлена ошибка MissingGreenlet при lazy loading (добавлен joinedload/selectinload) - Добавлено поле claimed_at для отслеживания времени подтверждения - Пакетное добавление счетов с выбором розыгрыша - Проверка владения конкретным счетом при подтверждении 📚 Документация: - docs/AUTO_CONFIRM_SYSTEM.md - Полная документация системы подтверждения - docs/ACCOUNT_BASED_CONFIRMATION.md - Подтверждение на уровне счетов - docs/REGISTRATION_SYSTEM.md - Система регистрации - docs/ADMIN_COMMANDS.md - Команды администратора - docs/CLEAR_DATABASE.md - Очистка БД - docs/QUICK_GUIDE.md - Быстрое начало - docs/UPDATE_LOG.md - Журнал обновлений 🗄️ База данных: - Миграция 003: Таблицы accounts, winner_verifications - Миграция 004: Поле claimed_at в таблице winners - Скрипт scripts/clear_database.py для полной очистки 🎮 Новые команды: Админские: - /check_unclaimed <lottery_id> - Проверка неподтвержденных выигрышей - /redraw <lottery_id> - Повторный розыгрыш - /add_accounts - Пакетное добавление счетов - /list_accounts <telegram_id> - Список счетов пользователя Пользовательские: - /register - Регистрация с вводом данных - /my_account - Просмотр своих счетов - Callback confirm_win_{id} - Подтверждение выигрыша 🛠️ Makefile: - make clear-db - Очистка всех данных из БД (с подтверждением) 🔒 Безопасность: - Проверка владения счетом при подтверждении - Защита от подтверждения чужих счетов - Независимое подтверждение каждого выигрышного счета 📊 Логика работы: 1. Пользователь регистрируется и добавляет счета 2. Счета участвуют в розыгрыше 3. Победители получают уведомление с кнопкой подтверждения 4. Каждый счет подтверждается отдельно (24 часа на подтверждение) 5. Неподтвержденные выигрыши переигрываются через /redraw
This commit is contained in:
227
scripts/README_CLEAR_DB.md
Normal file
227
scripts/README_CLEAR_DB.md
Normal file
@@ -0,0 +1,227 @@
|
||||
# 🗑️ Скрипт очистки базы данных
|
||||
|
||||
## Описание
|
||||
|
||||
`clear_database.py` - скрипт для полной очистки всех данных из базы данных бота.
|
||||
|
||||
## ⚠️ ВНИМАНИЕ!
|
||||
|
||||
**Этот скрипт удаляет ВСЕ данные без возможности восстановления:**
|
||||
- Всех пользователей
|
||||
- Все розыгрыши (активные и завершенные)
|
||||
- Все счета участников
|
||||
- Все записи об участии
|
||||
- Всех победителей
|
||||
- Все записи верификации
|
||||
|
||||
## Использование
|
||||
|
||||
### Через Makefile (рекомендуется)
|
||||
|
||||
```bash
|
||||
make clear-db
|
||||
```
|
||||
|
||||
Система попросит подтверждение:
|
||||
```
|
||||
⚠️ ВНИМАНИЕ! Это удалит ВСЕ данные из базы данных!
|
||||
- Все пользователи
|
||||
- Все розыгрыши
|
||||
- Все счета
|
||||
- Все участия
|
||||
- Всех победителей
|
||||
|
||||
Вы уверены? Введите 'yes' для подтверждения:
|
||||
```
|
||||
|
||||
### Напрямую через Python
|
||||
|
||||
```bash
|
||||
cd /home/trevor/new_lottery_bot
|
||||
source .venv/bin/activate
|
||||
python scripts/clear_database.py
|
||||
```
|
||||
|
||||
## Что делает скрипт
|
||||
|
||||
1. **Подключается к базе данных** используя `DATABASE_URL` из `.env`
|
||||
|
||||
2. **Удаляет данные в правильном порядке:**
|
||||
- `winner_verifications` - записи верификации победителей
|
||||
- `winners` - записи о победителях
|
||||
- `participations` - записи об участии в розыгрышах
|
||||
- `accounts` - счета участников
|
||||
- `lotteries` - розыгрыши
|
||||
- `users` - пользователи
|
||||
|
||||
3. **Сбрасывает счетчики ID (sequences):**
|
||||
- `users_id_seq` → 1
|
||||
- `lotteries_id_seq` → 1
|
||||
- `accounts_id_seq` → 1
|
||||
- `participations_id_seq` → 1
|
||||
- `winners_id_seq` → 1
|
||||
- `winner_verifications_id_seq` → 1
|
||||
|
||||
4. **Фиксирует транзакцию** (COMMIT)
|
||||
|
||||
5. **Закрывает соединение**
|
||||
|
||||
## Вывод скрипта
|
||||
|
||||
Пример успешного выполнения:
|
||||
|
||||
```
|
||||
INFO:__main__:🔌 Подключение к базе данных...
|
||||
|
||||
INFO:__main__:🗑️ Удаление данных из таблиц...
|
||||
INFO:__main__:------------------------------------------------------------
|
||||
INFO:__main__:✅ winner_verifications - удалено 0 строк
|
||||
INFO:__main__:✅ winners - удалено 2 строк
|
||||
INFO:__main__:✅ participations - удалено 1 строк
|
||||
INFO:__main__:✅ accounts - удалено 2 строк
|
||||
INFO:__main__:✅ lotteries - удалено 1 строк
|
||||
INFO:__main__:✅ users - удалено 2 строк
|
||||
INFO:__main__:------------------------------------------------------------
|
||||
|
||||
INFO:__main__:🔄 Сброс последовательностей...
|
||||
INFO:__main__:------------------------------------------------------------
|
||||
INFO:__main__:✅ users_id_seq
|
||||
INFO:__main__:✅ lotteries_id_seq
|
||||
INFO:__main__:✅ accounts_id_seq
|
||||
INFO:__main__:✅ participations_id_seq
|
||||
INFO:__main__:✅ winners_id_seq
|
||||
INFO:__main__:✅ winner_verifications_id_seq
|
||||
INFO:__main__:------------------------------------------------------------
|
||||
|
||||
INFO:__main__:============================================================
|
||||
INFO:__main__:🎉 ВСЕ ДАННЫЕ УСПЕШНО УДАЛЕНЫ ИЗ БАЗЫ ДАННЫХ!
|
||||
INFO:__main__:============================================================
|
||||
|
||||
INFO:__main__:Следующие ID начнутся с 1:
|
||||
INFO:__main__: - users.id
|
||||
INFO:__main__: - lotteries.id
|
||||
INFO:__main__: - accounts.id
|
||||
INFO:__main__: - participations.id
|
||||
INFO:__main__: - winners.id
|
||||
INFO:__main__: - winner_verifications.id
|
||||
|
||||
INFO:__main__:✅ Соединение с БД закрыто
|
||||
```
|
||||
|
||||
## Когда использовать
|
||||
|
||||
### ✅ Безопасные сценарии:
|
||||
|
||||
- **Разработка/тестирование**: Очистка тестовых данных
|
||||
- **Staging окружение**: Подготовка к тестированию
|
||||
- **Миграция**: Перед полным переносом данных
|
||||
- **Сброс демо**: Возврат к чистому состоянию
|
||||
|
||||
### ❌ НЕ использовать:
|
||||
|
||||
- **Production база**: НИКОГДА не запускайте на рабочей БД
|
||||
- **Активные розыгрыши**: Если есть незавершенные розыгрыши
|
||||
- **Без бэкапа**: Всегда делайте резервную копию перед очисткой
|
||||
|
||||
## Безопасность
|
||||
|
||||
### Файл читает DATABASE_URL из .env:
|
||||
|
||||
```env
|
||||
DATABASE_URL=postgresql+asyncpg://user:password@host:port/database
|
||||
```
|
||||
|
||||
### Проверка перед запуском:
|
||||
|
||||
```bash
|
||||
# Проверьте что вы на правильной базе
|
||||
grep DATABASE_URL .env
|
||||
```
|
||||
|
||||
### Создание бэкапа (ОБЯЗАТЕЛЬНО для production):
|
||||
|
||||
```bash
|
||||
# PostgreSQL
|
||||
pg_dump -h localhost -U bot_user -d bot_db > backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
|
||||
# Восстановление
|
||||
psql -h localhost -U bot_user -d bot_db < backup_20251116_140000.sql
|
||||
```
|
||||
|
||||
## Альтернативы
|
||||
|
||||
### Мягкая очистка (сохранение структуры):
|
||||
|
||||
```sql
|
||||
-- Очистить только данные розыгрышей
|
||||
DELETE FROM winners;
|
||||
DELETE FROM participations;
|
||||
DELETE FROM lotteries;
|
||||
```
|
||||
|
||||
### Выборочная очистка:
|
||||
|
||||
```sql
|
||||
-- Удалить только тестовые данные
|
||||
DELETE FROM users WHERE club_card_number LIKE 'TEST%';
|
||||
```
|
||||
|
||||
## Требования
|
||||
|
||||
- Python 3.12+
|
||||
- Установленные зависимости (`pip install -r requirements.txt`)
|
||||
- Файл `.env` с корректным `DATABASE_URL`
|
||||
- Права на изменение данных в БД
|
||||
|
||||
## Технические детали
|
||||
|
||||
### Используемые библиотеки:
|
||||
|
||||
```python
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
```
|
||||
|
||||
### Асинхронное выполнение:
|
||||
|
||||
```python
|
||||
async with engine.begin() as conn:
|
||||
await conn.execute(text('DELETE FROM table_name'))
|
||||
```
|
||||
|
||||
### Обработка транзакций:
|
||||
|
||||
- `engine.begin()` - автоматический COMMIT при успехе
|
||||
- `engine.dispose()` - закрытие всех соединений
|
||||
|
||||
## Отладка
|
||||
|
||||
### Проблемы с подключением:
|
||||
|
||||
```bash
|
||||
# Проверка доступности БД
|
||||
psql -h 192.168.0.102 -U bot_user -d bot_db -c "SELECT 1"
|
||||
```
|
||||
|
||||
### Проверка прав:
|
||||
|
||||
```sql
|
||||
-- В psql
|
||||
SELECT has_database_privilege('bot_user', 'bot_db', 'CONNECT');
|
||||
SELECT has_table_privilege('bot_user', 'users', 'DELETE');
|
||||
```
|
||||
|
||||
### Логи:
|
||||
|
||||
Скрипт использует стандартный logging с уровнем INFO. Для отладки:
|
||||
|
||||
```python
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
```
|
||||
|
||||
## См. также
|
||||
|
||||
- `docs/ADMIN_GUIDE.md` - Руководство администратора
|
||||
- `scripts/setup_postgres.sh` - Настройка PostgreSQL
|
||||
- `migrations/` - Миграции базы данных
|
||||
- `Makefile` - Все доступные команды
|
||||
97
scripts/clear_database.py
Normal file
97
scripts/clear_database.py
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Скрипт для полной очистки базы данных
|
||||
Удаляет все данные из всех таблиц и сбрасывает счетчики ID
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def clear_database():
|
||||
"""Очистка всех таблиц и сброс последовательностей"""
|
||||
|
||||
# Читаем DATABASE_URL из .env
|
||||
with open('.env', 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('DATABASE_URL='):
|
||||
DATABASE_URL = line.split('=', 1)[1].strip().strip('"').strip("'")
|
||||
break
|
||||
|
||||
logger.info('🔌 Подключение к базе данных...')
|
||||
engine = create_async_engine(DATABASE_URL)
|
||||
|
||||
try:
|
||||
async with engine.begin() as conn:
|
||||
# Отключаем ограничения внешних ключей
|
||||
await conn.execute(text('SET CONSTRAINTS ALL DEFERRED'))
|
||||
|
||||
# Очищаем таблицы в правильном порядке (от зависимых к независимым)
|
||||
tables = [
|
||||
'winner_verifications',
|
||||
'winners',
|
||||
'participations',
|
||||
'accounts',
|
||||
'lotteries',
|
||||
'users'
|
||||
]
|
||||
|
||||
logger.info('')
|
||||
logger.info('🗑️ Удаление данных из таблиц...')
|
||||
logger.info('-' * 60)
|
||||
|
||||
for table in tables:
|
||||
result = await conn.execute(text(f'DELETE FROM {table}'))
|
||||
logger.info(f'✅ {table:25} - удалено {result.rowcount:5} строк')
|
||||
|
||||
logger.info('-' * 60)
|
||||
|
||||
# Сбрасываем последовательности (auto-increment)
|
||||
sequences = [
|
||||
'users_id_seq',
|
||||
'lotteries_id_seq',
|
||||
'accounts_id_seq',
|
||||
'participations_id_seq',
|
||||
'winners_id_seq',
|
||||
'winner_verifications_id_seq'
|
||||
]
|
||||
|
||||
logger.info('')
|
||||
logger.info('🔄 Сброс последовательностей...')
|
||||
logger.info('-' * 60)
|
||||
|
||||
for seq in sequences:
|
||||
try:
|
||||
await conn.execute(text(f'ALTER SEQUENCE {seq} RESTART WITH 1'))
|
||||
logger.info(f'✅ {seq}')
|
||||
except Exception as e:
|
||||
logger.warning(f'⚠️ {seq}: {e}')
|
||||
|
||||
logger.info('-' * 60)
|
||||
|
||||
logger.info('')
|
||||
logger.info('=' * 60)
|
||||
logger.info('🎉 ВСЕ ДАННЫЕ УСПЕШНО УДАЛЕНЫ ИЗ БАЗЫ ДАННЫХ!')
|
||||
logger.info('=' * 60)
|
||||
logger.info('')
|
||||
logger.info('Следующие ID начнутся с 1:')
|
||||
logger.info(' - users.id')
|
||||
logger.info(' - lotteries.id')
|
||||
logger.info(' - accounts.id')
|
||||
logger.info(' - participations.id')
|
||||
logger.info(' - winners.id')
|
||||
logger.info(' - winner_verifications.id')
|
||||
logger.info('')
|
||||
|
||||
finally:
|
||||
await engine.dispose()
|
||||
logger.info('✅ Соединение с БД закрыто')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(clear_database())
|
||||
Reference in New Issue
Block a user