feat: Система автоматического подтверждения выигрышей с поддержкой множественных счетов
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:
2025-11-16 14:01:30 +09:00
parent 31c4c5382a
commit 505d26f0e9
21 changed files with 4217 additions and 68 deletions

View File

@@ -0,0 +1,279 @@
# Подтверждение выигрышей по счетам
## 🎯 Концепция
Система подтверждения выигрышей работает **на уровне счетов**, а не пользователей.
### Основные принципы:
1. **Один пользователь = Много счетов**
- У клиента может быть несколько счетов (11-22-33..., 44-55-66..., и т.д.)
- Каждый счет участвует в розыгрыше независимо
2. **Один счет = Один выигрыш = Одно подтверждение**
- Если счет `11-22-33-44-55-66-77` выиграл 1 место - требуется подтверждение
- Если счет `44-55-66-77-88-99-00` выиграл 3 место - требуется отдельное подтверждение
- Даже если оба счета принадлежат одному клиенту
3. **Независимое подтверждение**
- Каждый выигрышный счет подтверждается отдельной кнопкой
- Подтверждение одного счета не влияет на другие
- У каждого счета свой 24-часовой лимит
## 📱 Как это работает для пользователя
### Сценарий: У клиента 3 счета, выиграли 2
**Счета клиента:**
- `11-22-33-44-55-66-77` ✅ Выиграл 1 место (iPhone 15)
- `22-33-44-55-66-77-88`Не выиграл
- `33-44-55-66-77-88-99` ✅ Выиграл 3 место (AirPods Pro)
**Клиент получает 2 сообщения:**
### Сообщение 1:
```
🎉 Поздравляем! Ваш счет выиграл!
🎯 Розыгрыш: Новогодний розыгрыш
🏆 Место: 1
🎁 Приз: iPhone 15
💳 Выигрышный счет: 11-22-33-44-55-66-77
У вас есть 24 часа для подтверждения!
Нажмите кнопку ниже, чтобы подтвердить получение приза по этому счету.
Если вы не подтвердите в течение 24 часов, приз будет разыгран заново.
Если у вас несколько выигрышных счетов, подтвердите каждый из них отдельно.
[✅ Подтвердить счет 11-22-33-44-55-66-77]
[📞 Связаться с администратором]
```
### Сообщение 2:
```
🎉 Поздравляем! Ваш счет выиграл!
🎯 Розыгрыш: Новогодний розыгрыш
🏆 Место: 3
🎁 Приз: AirPods Pro
💳 Выигрышный счет: 33-44-55-66-77-88-99
У вас есть 24 часа для подтверждения!
[✅ Подтвердить счет 33-44-55-66-77-88-99]
[📞 Связаться с администратором]
```
### Действия клиента:
1. **Нажимает первую кнопку** → Счет `11-22-33-44-55-66-77` подтвержден ✅
2. **Нажимает вторую кнопку** → Счет `33-44-55-66-77-88-99` подтвержден ✅
## 🔒 Безопасность
### Проверка владения счетом
При нажатии кнопки подтверждения система проверяет:
```python
# Получаем владельца КОНКРЕТНОГО счета
owner = await AccountService.get_account_owner(session, winner.account_number)
# Проверяем что текущий пользователь - владелец ЭТОГО счета
if not owner or owner.telegram_id != callback.from_user.id:
await callback.answer(
f"❌ Счет {winner.account_number} вам не принадлежит",
show_alert=True
)
return
```
### Что НЕ может сделать пользователь:
❌ Подтвердить чужой счет
❌ Подтвердить счет, который ему не принадлежит
❌ Подтвердить один счет дважды
### Что может сделать пользователь:
✅ Подтвердить только свои счета
✅ Подтвердить каждый свой выигрышный счет отдельно
✅ Видеть номер счета на каждой кнопке
## 🎊 После подтверждения
### Сообщение пользователю:
```
✅ Выигрыш успешно подтвержден!
🎯 Розыгрыш: Новогодний розыгрыш
🏆 Место: 1
🎁 Приз: iPhone 15
💳 Счет: 11-22-33-44-55-66-77
🎊 Поздравляем! Администратор свяжется с вами
для передачи приза в ближайшее время.
Спасибо за участие!
```
### Уведомление администратору:
```
✅ Победитель подтвердил получение приза!
🎯 Розыгрыш: Новогодний розыгрыш
🏆 Место: 1
🎁 Приз: iPhone 15
💳 Подтвержденный счет: 11-22-33-44-55-66-77
👤 Владелец: Иван Петров (@ivan)
🎫 Клубная карта: 2223
📱 Телефон: +7 900 123-45-67
```
## 📊 База данных
### Таблица `winners`
Каждая запись = один выигрышный счет:
```sql
id | lottery_id | account_number | place | prize | is_claimed | claimed_at
---|------------|-----------------------|-------|------------|------------|------------------
1 | 5 | 11-22-33-44-55-66-77 | 1 | iPhone 15 | TRUE | 2025-11-16 14:00
2 | 5 | 33-44-55-66-77-88-99 | 3 | AirPods | TRUE | 2025-11-16 14:05
3 | 5 | 55-66-77-88-99-00-11 | 2 | MacBook | FALSE | NULL
```
### Ключевые поля:
- `account_number` - конкретный выигрышный счет
- `is_claimed` - подтвержден ли ЭТОТ счет
- `claimed_at` - когда ЭТОТ счет был подтвержден
## 🔄 Повторный розыгрыш
Если счет не подтвержден в течение 24 часов:
1. **Админ проверяет**: `/check_unclaimed 5`
```
⚠️ Неподтвержденные выигрыши:
🏆 2 место - MacBook
💳 55-66-77-88-99-00-11
⏰ Прошло: 26 часов
```
2. **Админ переигрывает**: `/redraw 5`
- Удаляется Winner с `account_number = 55-66-77-88-99-00-11`
- Выбирается новый случайный счет
- Новому владельцу счета отправляется уведомление
- Новый владелец получает свои 24 часа
## 💡 Преимущества подхода
### ✅ Для пользователей:
1. **Понятность** - видят конкретный номер счета на кнопке
2. **Контроль** - могут подтверждать счета независимо
3. **Гибкость** - один счет подтвердил, другой нет (если забыл)
### ✅ Для администраторов:
1. **Точность** - знают какой именно счет подтвержден
2. **Прозрачность** - видят все действия по каждому счету
3. **Справедливость** - переиграть можно конкретный неподтвержденный счет
### ✅ Для системы:
1. **Масштабируемость** - неограниченное количество счетов у одного пользователя
2. **Независимость** - каждый счет живет своей жизнью
3. **Целостность** - нет конфликтов между разными выигрышами
## 🔍 Примеры использования
### Пример 1: Один пользователь, два выигрыша
```
Клиент: Иван Петров (КК: 2223)
Счета:
- 11-22-33-44-55-66-77 → Выиграл 1 место ✅ Подтвержден
- 22-33-44-55-66-77-88 → Выиграл 3 место ✅ Подтвержден
Результат: Иван получит 2 приза
```
### Пример 2: Частичное подтверждение
```
Клиент: Мария Сидорова (КК: 3334)
Счета:
- 33-44-55-66-77-88-99 → Выиграл 2 место ✅ Подтвержден
- 44-55-66-77-88-99-00 → Выиграл 4 место ❌ Не подтвержден (> 24ч)
Результат:
- Мария получит приз за 2 место
- 4 место будет переиграно
```
### Пример 3: Полная неявка
```
Клиент: Петр Иванов (КК: 5556)
Счета:
- 55-66-77-88-99-00-11 → Выиграл 1 место ❌ Не подтвержден
- 66-77-88-99-00-11-22 → Выиграл 2 место ❌ Не подтвержден
Результат: Оба места будут переиграны отдельно
```
## 📝 Технические детали
### Callback данные
```python
# Формат: confirm_win_{winner_id}
callback_data = f"confirm_win_{winner.id}"
# winner.id - уникальный ID записи в таблице winners
# Каждый счет-выигрыш имеет свой winner_id
```
### Проверка при подтверждении
```python
# 1. Получаем winner по ID
winner = await session.get(Winner, winner_id)
# 2. Проверяем что счет принадлежит пользователю
owner = await AccountService.get_account_owner(session, winner.account_number)
if owner.telegram_id != current_user_id:
return "Не ваш счет"
# 3. Подтверждаем ЭТОТ счет
winner.is_claimed = True
winner.claimed_at = datetime.now(timezone.utc)
await session.commit()
```
## 🎓 Выводы
Подход "счет = выигрыш" обеспечивает:
- 🎯 **Точность** - подтверждается конкретный счет, а не абстрактный выигрыш
- 🔒 **Безопасность** - только владелец счета может подтвердить
- 📊 **Масштабируемость** - неограниченное количество счетов и выигрышей
- 👥 **Справедливость** - каждый счет обрабатывается независимо
- 💡 **Прозрачность** - всегда понятно какой именно счет подтверждается
---
## 📞 См. также
- `AUTO_CONFIRM_SYSTEM.md` - Полная документация системы подтверждения
- `REGISTRATION_SYSTEM.md` - Система регистрации счетов
- `ADMIN_GUIDE.md` - Руководство администратора

318
docs/ADMIN_COMMANDS.md Normal file
View File

@@ -0,0 +1,318 @@
# Админские команды - Руководство
## 🎯 Управление счетами
### `/add_account` - Добавить счет пользователю
Привязывает счет к зарегистрированному пользователю по клубной карте.
**Формат:**
```
/add_account <club_card> <account_number>
```
**Пример:**
```
/add_account 2223 11-22-33-44-55-66-77
```
**Что происходит:**
- Система проверяет существование пользователя с указанной клубной картой
- Создает новую запись счета в таблице `accounts`
- Отправляет уведомление владельцу о добавлении счета
- Счет становится активным и может участвовать в розыгрышах
**Возможные ошибки:**
- "Пользователь с клубной картой X не найден" - пользователь не зарегистрирован
- "Счет уже существует" - этот номер счета уже привязан к другому пользователю
---
### `/remove_account` - Деактивировать счет
Делает счет неактивным (не удаляет из БД).
**Формат:**
```
/remove_account <account_number>
```
**Пример:**
```
/remove_account 11-22-33-44-55-66-77
```
**Что происходит:**
- Устанавливает флаг `is_active = False`
- Счет остается в БД, но не может участвовать в новых розыгрышах
- История участия сохраняется
---
## 🏆 Управление выигрышами
### `/verify_winner` - Подтвердить выигрыш
Подтверждает выигрыш по коду верификации пользователя.
**Формат:**
```
/verify_winner <verification_code> <lottery_id>
```
**Пример:**
```
/verify_winner AB12CD34 1
```
**Процесс верификации:**
1. Пользователь сообщает администратору свой код верификации
2. Администратор проверяет, что пользователь является победителем указанного розыгрыша
3. Система устанавливает флаг `is_claimed = True` для выигрыша
4. Отправляется уведомление победителю о подтверждении
**Что происходит:**
- Поиск пользователя по коду верификации
- Проверка наличия выигрыша в указанном розыгрыше
- Установка флага `is_claimed = True`
- Отправка подтверждающего сообщения победителю
**Возможные ошибки:**
- "Розыгрыш не найден" - неверный lottery_id
- "Выигрыш не найден" - неверный код или пользователь не победитель
- "Выигрыш уже был подтвержден" - повторная попытка подтверждения
---
### `/winner_status` - Статус победителей
Показывает всех победителей розыгрыша и их статус подтверждения.
**Формат:**
```
/winner_status <lottery_id>
```
**Пример:**
```
/winner_status 1
```
**Отображаемая информация:**
- 🏆 Место и приз
- 👤 Имя и клубная карта победителя
- 💳 Номер счета (если участвовал через счет)
- ✅ Статус подтверждения (подтвержден / ожидает)
- 📨 Статус уведомления (отправлено / нет)
**Статусы:**
- ✅ - Выигрыш подтвержден (`is_claimed = True`)
- ⏳ - Ожидает подтверждения (`is_claimed = False`)
- 📨 - Уведомление отправлено (`is_notified = True`)
- 📭 - Уведомление не отправлено (`is_notified = False`)
---
## 👤 Информация о пользователе
### `/user_info` - Информация о пользователе
Показывает полную информацию о пользователе по клубной карте.
**Формат:**
```
/user_info <club_card>
```
**Пример:**
```
/user_info 2223
```
**Отображаемая информация:**
- 🎫 Клубная карта
- 👤 Имя и Telegram username
- 📞 Телефон (если указан)
- 🔑 Код верификации
- 📅 Дата регистрации
- 💳 Список всех счетов (активные и неактивные)
- 🏆 История выигрышей
---
## 🔄 Автоматические уведомления
При проведении розыгрыша (`/conduct` через кнопку) система автоматически:
1. **Определяет владельцев счетов-победителей**
- Ищет запись в таблице `accounts`
- Находит владельца через `owner_id`
2. **Создает токен верификации**
- Генерирует уникальный токен для каждого выигрыша
- Устанавливает срок действия 24 часа
- Сохраняет в таблице `winner_verifications`
3. **Отправляет уведомление победителю**
```
🎉 Поздравляем! Ваш счет выиграл!
🎯 Розыгрыш: Название розыгрыша
🏆 Место: 1
🎁 Приз: Приз первого места
💳 Счет: 11-22-33-44-55-66-77
🔑 Ваш код верификации: AB12CD34
Для получения приза свяжитесь с администратором
и предоставьте этот код.
```
4. **Устанавливает флаг уведомления**
- `is_notified = True` в таблице `winners`
---
## 🔐 Система верификации
### Как это работает:
1. **При регистрации пользователя:**
- Генерируется уникальный 8-символьный код (например, `AB12CD34`)
- Сохраняется в `users.verification_code`
- Пользователь может посмотреть свой код через `/my_code`
2. **При выигрыше:**
- Владельцу счета отправляется сообщение с его кодом верификации
- Создается токен в таблице `winner_verifications`
3. **При подтверждении выигрыша:**
- Администратор запрашивает код у победителя
- Вводит команду `/verify_winner AB12CD34 1`
- Система проверяет соответствие кода и наличие выигрыша
- Устанавливает `is_claimed = True`
### Преимущества:
- 🔒 **Безопасность**: Только владелец знает свой код
- ✅ **Проверка**: Невозможно подтвердить чужой выигрыш
- 📊 **Отслеживание**: Полная история подтверждений
- ⏱️ **Контроль**: Токены имеют срок действия
---
## 📊 Типичные сценарии
### Сценарий 1: Новый пользователь с двумя счетами
```
1. Пользователь: /start → "Зарегистрироваться"
2. Пользователь: Вводит клубную карту "2223"
3. Пользователь: Вводит телефон или пропускает
4. Система: Показывает код верификации "AB12CD34"
5. Админ: /add_account 2223 11-22-33-44-55-66-77
6. Админ: /add_account 2223 88-99-00-11-22-33-44
7. Пользователь: /my_accounts
→ Видит оба счета
```
### Сценарий 2: Проведение розыгрыша и подтверждение
```
1. Админ: Создает розыгрыш через интерфейс
2. Счета участвуют автоматически
3. Админ: Нажимает "Провести розыгрыш"
4. Система: Автоматически отправляет уведомления:
"🎉 Поздравляем! Ваш счет выиграл!
💳 Счет: 11-22-33-44-55-66-77
🔑 Ваш код верификации: AB12CD34"
5. Победитель: Связывается с админом, называет код "AB12CD34"
6. Админ: /verify_winner AB12CD34 1
7. Система: Подтверждает выигрыш, отправляет уведомление победителю
```
### Сценарий 3: Проверка статуса всех победителей
```
1. Админ: /winner_status 1
Результат:
🏆 Победители розыгрыша 'Новогодний розыгрыш':
✅ 1 место - Главный приз
👤 Иван (КК: 2223)
💳 11-22-33-44-55-66-77
✅ Подтвержден
⏳ 2 место - Второй приз
👤 Петр (КК: 3334)
💳 22-33-44-55-66-77-88
⏳ Ожидает подтверждения
```
---
## 🛠️ Техническая информация
### Таблицы базы данных:
**accounts** - Счета пользователей
- `account_number` - Номер счета (формат XX-XX-XX-XX-XX-XX-XX)
- `owner_id` - ID владельца (FK → users.id)
- `is_active` - Активен ли счет
- `created_at` - Дата создания
**winner_verifications** - Токены верификации выигрышей
- `winner_id` - ID выигрыша (FK → winners.id)
- `verification_token` - Уникальный токен
- `is_verified` - Подтвержден ли
- `verified_at` - Время подтверждения
- `expires_at` - Срок действия (24 часа)
**winners** - Победители (расширенная)
- Добавлены поля: `is_notified`, `is_claimed`
**users** - Пользователи (расширенная)
- Добавлены поля: `club_card_number`, `phone`, `is_registered`, `verification_code`
---
## 📝 Полезные команды для пользователей
- `/start` - Главное меню / регистрация
- `/my_code` - Показать свой код верификации
- `/my_accounts` - Список моих счетов
---
## ⚠️ Важные замечания
1. **Код верификации** - строго конфиденциальная информация
2. **Один счет** = один владелец (нельзя передать счет другому)
3. **Деактивация счета** - не удаляет историю участия
4. **Токены верификации** - действуют 24 часа
5. **Уведомления** - отправляются автоматически только если пользователь зарегистрирован
---
## 🆘 Решение проблем
**Пользователь не получил уведомление о выигрыше:**
- Проверить `/winner_status <lottery_id>` - должна быть отметка 📨
- Возможно, пользователь заблокировал бота
- Проверить telegram_id пользователя через `/user_info`
**Не получается подтвердить выигрыш:**
- Проверить код верификации: `/user_info <club_card>`
- Убедиться что lottery_id верный
- Проверить что выигрыш еще не подтвержден
**Счет не добавляется:**
- Убедиться что пользователь зарегистрирован
- Проверить формат номера счета (7 пар цифр через дефис)
- Проверить что счет уникален (не добавлен другому пользователю)

439
docs/AUTO_CONFIRM_SYSTEM.md Normal file
View File

@@ -0,0 +1,439 @@
# Система автоматического подтверждения и повторного розыгрыша
## 🎯 Обзор системы
Реализована полная система автоматического подтверждения выигрышей с возможностью повторного розыгрыша для неподтвержденных призов.
---
## 🔄 Как это работает
### 1. Победитель получает уведомление
После проведения розыгрыша победителю автоматически отправляется сообщение с **интерактивной кнопкой**:
```
🎉 Поздравляем! Ваш счет выиграл!
🎯 Розыгрыш: Новогодний розыгрыш
🏆 Место: 1
🎁 Приз: Главный приз
💳 Счет: 11-22-33-44-55-66-77
У вас есть 24 часа для подтверждения!
Нажмите кнопку ниже, чтобы подтвердить получение приза.
Если вы не подтвердите в течение 24 часов, приз будет разыгран заново.
[✅ Подтвердить получение приза]
[📞 Связаться с администратором]
```
### 2. Победитель подтверждает выигрыш
Пользователь нажимает кнопку "✅ Подтвердить получение приза":
**Что происходит:**
- В БД устанавливается `is_claimed = True`
- Сохраняется время подтверждения `claimed_at`
- Победитель видит подтверждение:
```
✅ Выигрыш успешно подтвержден!
🎯 Розыгрыш: Новогодний розыгрыш
🏆 Место: 1
🎁 Приз: Главный приз
🎊 Поздравляем! Администратор свяжется с вами
для передачи приза в ближайшее время.
Спасибо за участие!
```
**Администраторы получают уведомление:**
```
✅ Победитель подтвердил получение приза!
🎯 Розыгрыш: Новогодний розыгрыш
🏆 Место: 1
🎁 Приз: Главный приз
👤 Победитель: Иван (@ivan)
🎫 Клубная карта: 2223
📱 Телефон: +7 900 123-45-67
💳 Счет: 11-22-33-44-55-66-77
```
### 3. Если победитель не подтверждает (24 часа)
Администратор проверяет неподтвержденные выигрыши:
```
/check_unclaimed 1
```
**Ответ бота:**
```
⚠️ Неподтвержденные выигрыши в розыгрыше 'Новогодний розыгрыш':
🏆 1 место - Главный приз
👤 Иван (КК: 2223)
💳 11-22-33-44-55-66-77
⏰ Прошло: 26 часов
🏆 3 место - Третий приз
👤 Петр (КК: 3334)
💳 22-33-44-55-66-77-88
⏰ Прошло: 30 часов
📊 Всего неподтвержденных: 2
Используйте /redraw 1 для повторного розыгрыша
```
### 4. Повторный розыгрыш
Администратор запускает переигровку:
```
/redraw 1
```
**Что происходит:**
1. **Система находит неподтвержденные выигрыши** (старше 24 часов)
2. **Получает пул участников** (исключая текущих победителей)
3. **Случайно выбирает новых победителей** для каждого неподтвержденного места
4. **Удаляет старых победителей** из БД
5. **Создает новых победителей**
6. **Отправляет уведомления** новым победителям (с кнопкой подтверждения)
**Результат для администратора:**
```
🔄 Повторный розыгрыш завершен!
🎯 Розыгрыш: Новогодний розыгрыш
📊 Переиграно мест: 2
🏆 1 место - Главный приз
❌ Было: 11-22-33-44-55-66-77
✅ Стало: 99-88-77-66-55-44-33
🏆 3 место - Третий приз
❌ Было: 22-33-44-55-66-77-88
✅ Стало: 12-34-56-78-90-12-34
📨 Новым победителям отправлены уведомления
```
**Новые победители получают** то же уведомление с кнопкой подтверждения и 24-часовым лимитом.
---
## 📋 Админские команды
### `/check_unclaimed <lottery_id>`
Проверить неподтвержденные выигрыши старше 24 часов.
**Пример:**
```
/check_unclaimed 1
```
**Показывает:**
- Список всех неподтвержденных выигрышей
- Информацию о победителях
- Сколько времени прошло с момента уведомления
### `/redraw <lottery_id>`
Переиграть розыгрыш для неподтвержденных выигрышей.
**Пример:**
```
/redraw 1
```
**Требования:**
- Должны быть неподтвержденные выигрыши старше 24 часов
- Должны быть доступные участники (не победители)
---
## 🗄️ Изменения в базе данных
### Таблица `winners`
Добавлено новое поле:
- `claimed_at` (TIMESTAMP) - время подтверждения выигрыша победителем
### Миграция
Файл: `migrations/versions/004_add_claimed_at.py`
Применить:
```bash
alembic upgrade head
```
---
## 🔧 Технические детали
### Обработчик подтверждения
**Файл:** `main.py`
**Callback:** `confirm_win_{winner_id}`
**Функция:** `confirm_winner_response()`
**Логика:**
1. Проверяет существование выигрыша
2. Проверяет, что подтверждает владелец
3. Устанавливает `is_claimed = True` и `claimed_at = now()`
4. Обновляет сообщение победителя
5. Уведомляет всех администраторов
### Система уведомлений
**Функция:** `notify_winners_async()`
**Изменения:**
- Добавлена кнопка "✅ Подтвердить получение приза"
- Добавлено предупреждение о 24-часовом лимите
- Добавлена кнопка "📞 Связаться с администратором"
### Повторный розыгрыш
**Файл:** `src/handlers/redraw_handlers.py`
**Команды:**
- `check_unclaimed_winners()` - проверка
- `redraw_lottery()` - переигровка
**Алгоритм:**
1. Получает всех победителей розыгрыша
2. Фильтрует неподтвержденных (is_claimed=False, is_notified=True, >24ч)
3. Получает пул участников (исключая победителей)
4. Для каждого неподтвержденного:
- Выбирает случайного участника
- Удаляет старого победителя
- Создает нового победителя
- Отправляет уведомление
---
## 📊 Состояния выигрыша
### Таймлайн жизни выигрыша:
```
1. Розыгрыш проведен
├─ is_notified: False
├─ is_claimed: False
└─ claimed_at: NULL
2. Уведомление отправлено
├─ is_notified: True
├─ is_claimed: False
└─ claimed_at: NULL
3А. Победитель подтвердил (успех)
├─ is_notified: True
├─ is_claimed: True
└─ claimed_at: 2025-11-16 13:00:00
3Б. Прошло 24 часа (неудача)
├─ is_notified: True
├─ is_claimed: False
├─ claimed_at: NULL
└─ ⏰ time_passed > 24h → переигровка
4. После переигровки (новый победитель)
├─ Старый победитель удален
├─ Создан новый победитель
├─ is_notified: True (после отправки)
├─ is_claimed: False
└─ claimed_at: NULL → снова 24 часа
```
---
## ⚠️ Важные моменты
### Безопасность
1. **Проверка владельца**: Только владелец счета может подтвердить выигрыш
2. **Повторное подтверждение**: Если выигрыш уже подтвержден, показывается соответствующее сообщение
3. **Права администратора**: Только админы могут запускать `/redraw`
### Ограничения
1. **24-часовой лимит**: Жестко закодирован, но легко изменить в коде
2. **Пул участников**: Если все участники уже победители, переигровка невозможна
3. **Уникальность**: Один счет не может выиграть дважды в одном розыгрыше
### Отказоустойчивость
1. **Уведомления**: Если не удается отправить - продолжает работу
2. **Транзакции**: Все изменения в БД атомарны
3. **Логирование**: Все действия записываются в лог
---
## 🎯 Типичные сценарии
### Сценарий 1: Все подтвердили
```
1. Розыгрыш → 3 победителя
2. Все 3 нажали кнопку подтверждения
3. Админ: /check_unclaimed 1
→ "✅ Все победители подтвердили выигрыш"
4. Приз передается всем победителям
```
### Сценарий 2: Один не подтвердил
```
1. Розыгрыш → 3 победителя
2. 2 подтвердили, 1 игнорирует
3. Через 25 часов админ: /check_unclaimed 1
→ "⚠️ 1 неподтвержденный выигрыш"
4. Админ: /redraw 1
5. Система переигрывает 1 место
6. Новый победитель получает уведомление
7. У нового победителя снова 24 часа
```
### Сценарий 3: Множественная переигровка
```
1. Розыгрыш → 5 победителей
2. 3 подтвердили, 2 игнорируют
3. Через 25 часов: /redraw 1
4. 2 новых победителя выбраны
5. Один из новых тоже игнорирует
6. Через 25 часов: /redraw 1 снова
7. Выбран еще один новый победитель
```
---
## 📈 Статистика и мониторинг
### Проверка статуса
```
/winner_status 1
```
Покажет:
- ✅ Подтвержденные выигрыши (с is_claimed=True)
- ⏳ Ожидающие подтверждения (с is_claimed=False)
- 📨 Статус уведомления (is_notified)
### SQL запросы для админа
**Найти все неподтвержденные старше 24 часов:**
```sql
SELECT w.*, l.title
FROM winners w
JOIN lotteries l ON w.lottery_id = l.id
WHERE w.is_notified = TRUE
AND w.is_claimed = FALSE
AND w.created_at < NOW() - INTERVAL '24 hours';
```
**Статистика по подтверждениям:**
```sql
SELECT
lottery_id,
COUNT(*) as total_winners,
SUM(CASE WHEN is_claimed THEN 1 ELSE 0 END) as confirmed,
SUM(CASE WHEN NOT is_claimed THEN 1 ELSE 0 END) as unconfirmed
FROM winners
WHERE is_notified = TRUE
GROUP BY lottery_id;
```
---
## 🚀 Использование
### Для администратора
1. **После розыгрыша**: Ничего не делать - система отправит уведомления
2. **Через 24-30 часов**: Проверить `/check_unclaimed <id>`
3. **Если есть неподтвержденные**: Запустить `/redraw <id>`
4. **Повторить** при необходимости
### Для победителя
1. **Получить уведомление** с кнопкой
2. **Нажать "✅ Подтвердить"**
3. **Дождаться связи с админом**
4. **Получить приз**
---
## 💡 Рекомендации
### Оптимальные настройки
- **Лимит подтверждения**: 24 часа (можно увеличить до 48ч)
- **Частота проверки**: 1-2 раза в день
- **Уведомления**: Включить push-уведомления в боте
### Коммуникация с пользователями
После розыгрыша отправьте общее сообщение:
```
🎉 Розыгрыш завершен!
Если вы выиграли, вам придет сообщение с кнопкой подтверждения.
У вас будет 24 часа чтобы подтвердить выигрыш!
Если не подтвердите - приз будет переигран.
```
---
## 🔮 Возможные улучшения
1. **Напоминания**: Отправка напоминания за 2 часа до истечения срока
2. **Гибкий лимит**: Разные лимиты для разных типов призов
3. **История**: Логирование всех переигровок
4. **Статистика**: Процент подтверждений, среднее время подтверждения
5. **Автоматизация**: Cron-задача для автоматической переигровки
---
## ✅ Преимущества системы
- 🤖 **Полная автоматизация**: Минимум ручной работы для админа
- ⏱️ **Справедливость**: Четкий дедлайн для всех
- 🔄 **Эффективность**: Призы не "зависают" у неактивных победителей
- 📊 **Прозрачность**: Полная история всех действий
- 🛡️ **Безопасность**: Только владелец может подтвердить
- 💬 **UX**: Простая кнопка вместо сложной верификации
---
## 📞 Поддержка
Если что-то пошло не так:
1. Проверьте логи бота
2. Проверьте состояние БД (claimed_at, is_notified, is_claimed)
3. Используйте `/winner_status <id>` для диагностики
4. При критических ошибках - используйте `/verify_winner` для ручного подтверждения

72
docs/CLEAR_DATABASE.md Normal file
View File

@@ -0,0 +1,72 @@
# Очистка базы данных - Краткое руководство
## 🎯 Быстрый старт
```bash
make clear-db
```
Система попросит подтверждение, введите `yes`.
## ✅ Результат
База данных будет полностью очищена:
- ✅ Удалены все пользователи
- ✅ Удалены все розыгрыши
- ✅ Удалены все счета
- ✅ Удалены все участия
- ✅ Удалены все победители
- ✅ Сброшены счетчики ID (начнутся с 1)
## 📊 Статистика очистки
Скрипт покажет сколько строк удалено из каждой таблицы:
```
✅ winner_verifications - удалено 0 строк
✅ winners - удалено 2 строк
✅ participations - удалено 1 строк
✅ accounts - удалено 2 строк
✅ lotteries - удалено 1 строк
✅ users - удалено 2 строк
```
## ⚠️ ВНИМАНИЕ
**Данные удаляются БЕЗ ВОЗМОЖНОСТИ ВОССТАНОВЛЕНИЯ!**
Перед запуском на production всегда делайте бэкап:
```bash
pg_dump -h 192.168.0.102 -U bot_user -d bot_db > backup.sql
```
## 🔧 Альтернативные способы
### Через Python напрямую
```bash
source .venv/bin/activate
python scripts/clear_database.py
```
### Через SQL (только данные, без сброса ID)
```sql
DELETE FROM winner_verifications;
DELETE FROM winners;
DELETE FROM participations;
DELETE FROM accounts;
DELETE FROM lotteries;
DELETE FROM users;
```
## 📖 Подробная документация
См. `scripts/README_CLEAR_DB.md` для полной документации.
## 🆘 Восстановление из бэкапа
```bash
psql -h 192.168.0.102 -U bot_user -d bot_db < backup.sql
```

152
docs/QUICK_GUIDE.md Normal file
View File

@@ -0,0 +1,152 @@
# Быстрая шпаргалка - Команды администратора
## 🚀 Основные команды
### Добавление счетов
**Один счет (быстро):**
```
/add_account 2223 11-22-33-44-55-66-77
```
**Несколько счетов (пакетом):**
```
/add_account
2223 11-22-33-44-55-66-77
2223 88-99-00-11-22-33-44
3334 12-34-56-78-90-12-34
```
### Управление счетами
```
/remove_account 11-22-33-44-55-66-77 # Деактивировать
/user_info 2223 # Информация о пользователе
```
### Работа с розыгрышами
```
/winner_status 1 # Статус победителей
/verify_winner AB12CD34 1 # Подтвердить выигрыш
```
---
## 📋 Типичные сценарии
### Новый клиент (полный цикл)
1. Клиент регистрируется: `/start` → "Зарегистрироваться"
2. Админ добавляет счета:
```
/add_account 2223 11-22-33-44-55-66-77
```
3. Выбрать розыгрыш из списка
4. Готово!
### Массовое добавление (10+ счетов)
```
/add_account
2223 11-22-33-44-55-66-77
2223 22-33-44-55-66-77-88
3334 33-44-55-66-77-88-99
3334 44-55-66-77-88-99-00
5556 55-66-77-88-99-00-11
```
→ Выбрать розыгрыш → Готово!
### Проведение розыгрыша
1. В боте: "Розыгрыши" → Выбрать → "Провести розыгрыш"
2. Победители получат уведомления автоматически
3. Проверить: `/winner_status 1`
### Подтверждение победителя
1. Победитель сообщает код: `AB12CD34`
2. Админ: `/verify_winner AB12CD34 1`
3. Система подтверждает и уведомляет победителя
---
## 💡 Полезные советы
- **Формат счета:** `XX-XX-XX-XX-XX-XX-XX` (7 пар цифр)
- **Клубная карта:** Любые цифры (например: 2223, 5556)
- **Код верификации:** Генерируется автоматически при регистрации
- **Отмена операции:** `/cancel` во время ввода данных
---
## ⚠️ Частые ошибки
| Ошибка | Причина | Решение |
|--------|---------|---------|
| "Пользователь не найден" | Клубная карта не зарегистрирована | Попросить зарегистрироваться через /start |
| "Счет уже существует" | Номер занят другим пользователем | Проверить номер или деактивировать старый |
| "Неверный формат" | Формат счета неправильный | Использовать XX-XX-XX-XX-XX-XX-XX |
| Нет розыгрышей | Нет активных розыгрышей | Создать розыгрыш через админ-панель |
---
## 🔍 Проверка данных
### Проверить пользователя:
```
/user_info 2223
```
Увидите:
- Все счета пользователя
- Код верификации
- Историю выигрышей
### Проверить победителей:
```
/winner_status 1
```
Увидите:
- Список всех победителей
- Статус подтверждения (✅/⏳)
- Информацию о владельцах
---
## 📞 Пользовательские команды
Клиенты используют:
- `/start` - Главное меню / регистрация
- `/my_code` - Показать код верификации
- `/my_accounts` - Список счетов
- "Мой счет" (через меню) - Подробная информация
---
## 🎯 Быстрый старт (за 1 минуту)
```bash
# 1. Клиент регистрируется
Клиент → /start → "Зарегистрироваться" → Вводит данные
# 2. Админ добавляет счета
/add_account
2223 11-22-33-44-55-66-77
2223 88-99-00-11-22-33-44
# 3. Выбрать розыгрыш
Нажать "🎯 Новогодний розыгрыш"
# 4. Провести розыгрыш
"Розыгрыши" → Выбрать → "Провести"
# 5. Подтвердить победителя
/verify_winner AB12CD34 1
✅ Готово!
```

261
docs/REGISTRATION_SYSTEM.md Normal file
View File

@@ -0,0 +1,261 @@
# Система регистрации и верификации выигрышей
## Архитектура
### Модели данных
#### 1. User (Расширенная)
- `club_card_number` - номер клубной карты (уникальный идентификатор клиента)
- `phone` - телефон для связи
- `is_registered` - прошел ли полную регистрацию
- `verification_code` - секретный код для подтверждения выигрыша (генерируется автоматически)
#### 2. Account (Новая)
- `account_number` - номер счета в формате XX-XX-XX-XX-XX-XX-XX
- `owner_id` - владелец счета (связь с User через club_card_number)
- `is_active` - активен ли счет
#### 3. WinnerVerification (Новая)
- `winner_id` - связь с Winner
- `verification_token` - токен для подтверждения выигрыша
- `is_verified` - подтвержден ли выигрыш
- `expires_at` - срок действия токена (24 часа)
## Процессы
### 1. Регистрация пользователя
**Инициатор:** Обычный пользователь через бота
**Шаги:**
1. Пользователь отправляет `/start`
2. Бот проверяет `is_registered`
3. Если `False` - запрашивает:
- Номер клубной карты
- Телефон (опционально)
4. Генерируется `verification_code` (8-символьный уникальный код)
5. Код показывается пользователю: "Ваш код верификации: **AB12CD34**. Сохраните его!"
6. `is_registered = True`
### 2. Создание счета администратором
**Инициатор:** Администратор
**Формат команды:**
```
/add_account 2223 11-22-33-44-55-66-77
```
**Процесс:**
1. Админ отправляет команду с номером КК и счетом
2. Бот находит пользователя по `club_card_number = 2223`
3. Создается запись в таблице `accounts`:
```python
Account(
account_number="11-22-33-44-55-66-77",
owner_id=user.id,
is_active=True
)
```
4. Пользователю отправляется уведомление:
```
К вашему профилю добавлен счет:
11-22-33-44-55-66-77
```
### 3. Проведение розыгрыша с уведомлением победителей
**Процесс:**
1. Администратор проводит розыгрыш
2. Выбирается случайный счет (например, `11-22-33-44-55-66-77`)
3. Система ищет владельца счета:
```python
account = Account.query.filter_by(account_number="11-22-33-44-55-66-77").first()
user = account.owner if account else None
```
4. Создается запись `Winner` и `WinnerVerification`:
```python
winner = Winner(
lottery_id=lottery.id,
user_id=user.id if user else None,
account_number="11-22-33-44-55-66-77",
place=1,
prize="iPhone 15"
)
verification = WinnerVerification(
winner_id=winner.id,
verification_token=generate_token(),
expires_at=now + 24hours
)
```
5. Если `user` найден - отправляется личное сообщение:
```
🎉 ПОЗДРАВЛЯЕМ!
Вы выиграли 1 место в розыгрыше "Новогодний розыгрыш"!
🏆 Приз: iPhone 15
📋 Ваш выигрышный счет: 11-22-33-44-55-66-77
✅ Для подтверждения выигрыша:
1. Свяжитесь с администратором @admin_username
2. Сообщите ваш код верификации: AB12CD34
3. Администратор подтвердит ваш выигрыш в системе
⏰ Срок подтверждения: 24 часа
```
### 4. Верификация выигрыша администратором
**Сценарий:**
1. Победитель связывается с админом в личных сообщениях
2. Сообщает код верификации: `AB12CD34`
3. Админ проверяет код в боте:
```
/verify_winner AB12CD34 1
```
где `1` - ID розыгрыша
4. Система:
- Находит пользователя по `verification_code = "AB12CD34"`
- Проверяет, что у него есть выигрыш в розыгрыше #1
- Помечает `winner.is_claimed = True`
- Обновляет `verification.is_verified = True`
5. Победителю отправляется:
```
✅ Ваш выигрыш подтвержден!
Администратор свяжется с вами для получения приза.
```
## Дополнительные механизмы безопасности
### 1. Двухфакторная верификация (опционально)
Можно добавить отправку одноразового кода на телефон победителя.
### 2. Ограничение по времени
Токены верификации действуют 24 часа. После истечения срока требуется повторная генерация.
### 3. Логирование
Все действия с выигрышами логируются:
- Создание выигрыша
- Отправка уведомления
- Подтверждение администратором
- Получение приза
## API для админа
### Команды бота
#### `/add_account <club_card> <account_number>`
Добавить счет к профилю клиента
```
/add_account 2223 11-22-33-44-55-66-77
```
#### `/remove_account <account_number>`
Деактивировать счет
```
/remove_account 11-22-33-44-55-66-77
```
#### `/verify_winner <verification_code> <lottery_id>`
Подтвердить выигрыш победителя
```
/verify_winner AB12CD34 1
```
#### `/winner_status <lottery_id>`
Показать статус всех победителей розыгрыша
```
/winner_status 1
Результаты:
1 место - @username (КК: 2223) ✅ Подтвержден
2 место - Счет: 33-44-55-66-77-88-99 ⏳ Ожидает подтверждения
3 место - @user2 (КК: 4445) ❌ Не подтвержден (истек срок)
```
## Аналитика и отчеты
### Запросы для аналитики
```sql
-- Победители по клубным картам
SELECT
u.club_card_number,
u.first_name,
COUNT(w.id) as total_wins,
SUM(CASE WHEN w.is_claimed THEN 1 ELSE 0 END) as claimed_wins
FROM users u
JOIN winners w ON w.user_id = u.id
GROUP BY u.id;
-- Счета с наибольшим количеством участий
SELECT
a.account_number,
u.club_card_number,
COUNT(p.id) as participation_count
FROM accounts a
JOIN users u ON u.id = a.owner_id
JOIN participations p ON p.account_id = a.id
GROUP BY a.id, u.id
ORDER BY participation_count DESC;
-- Невостребованные выигрыши
SELECT
l.title,
w.place,
w.prize,
w.account_number,
u.club_card_number
FROM winners w
JOIN lotteries l ON l.id = w.lottery_id
LEFT JOIN users u ON u.id = w.user_id
WHERE w.is_claimed = false
AND w.created_at < NOW() - INTERVAL '24 hours';
```
## Миграция существующих данных
Если у вас уже есть данные в старой схеме:
```python
# Миграция старых account_number из users в таблицу accounts
async def migrate_accounts():
users = await session.execute(select(User).where(User.account_number != None))
for user in users.scalars():
account = Account(
account_number=user.account_number,
owner_id=user.id,
is_active=True
)
session.add(account)
user.account_number = None # Очищаем старое поле
await session.commit()
```
## Тестирование
### Сценарий 1: Полный цикл выигрыша
1. Регистрация пользователя (КК: 2223)
2. Админ добавляет счет: `/add_account 2223 11-22-33-44-55-66-77`
3. Счет участвует в розыгрыше
4. Счет выигрывает 1 место
5. Пользователю приходит уведомление с кодом
6. Пользователь связывается с админом, сообщает код
7. Админ подтверждает: `/verify_winner AB12CD34 1`
8. Выигрыш получен ✅
### Сценарий 2: Выигрыш незарегистрированного счета
1. Админ добавляет счет без владельца
2. Счет участвует и выигрывает
3. Бот показывает в публичном объявлении: "Счет 11-22-33-44-55-66-77 выиграл!"
4. Владелец счета регистрируется в боте
5. Админ привязывает счет к владельцу
6. Дальше стандартная процедура верификации

424
docs/UPDATE_LOG.md Normal file
View File

@@ -0,0 +1,424 @@
# Обновления системы - 16.11.2025
## ✅ Исправленные ошибки
### 1. Ошибка "User object has no attribute 'account_number'"
**Проблема:** В старой системе поле `account_number` было в таблице `users`, но после миграции оно перенесено в отдельную таблицу `accounts`.
**Решение:**
- Обновлена функция `show_my_account()` в `main.py`
- Теперь использует `AccountService.get_user_accounts()` для получения всех счетов пользователя
- Показывает список всех счетов с их статусом (активен/неактивен)
**Новый функционал в "Мой счет":**
```
💳 Ваши счета
🎫 Клубная карта: 2223
🔑 Код верификации: AB12CD34
Счета (2):
1. 11-22-33-44-55-66-77
✅ Активен
2. 88-99-00-11-22-33-44
✅ Активен
```
---
## 🆕 Новый функционал
### 1. Улучшенная команда `/add_account`
#### Режим 1: Быстрое добавление одного счета
```
/add_account 2223 11-22-33-44-55-66-77
```
#### Режим 2: Интерактивное добавление (один или несколько)
```
/add_account
```
Затем отправьте данные:
**Один счет:**
```
2223 11-22-33-44-55-66-77
```
**Несколько счетов (пакетное добавление):**
```
2223 11-22-33-44-55-66-77
2223 88-99-00-11-22-33-44
3334 12-34-56-78-90-12-34
5556 99-88-77-66-55-44-33
```
**Формат:** `клубная_карта номер_счета` (через пробел, каждый счет с новой строки)
---
### 2. Автоматическое добавление счетов в розыгрыш
После успешного добавления счетов система **автоматически предлагает** добавить их в активный розыгрыш:
```
✅ Счет успешно добавлен!
🎫 Клубная карта: 2223
💳 Счет: 11-22-33-44-55-66-77
👤 Владелец: Иван
📨 Владельцу отправлено уведомление
Добавить счета в розыгрыш?
Выберите розыгрыш из списка:
[🎯 Новогодний розыгрыш]
[🎯 Летний розыгрыш]
[❌ Пропустить]
```
**Процесс:**
1. Администратор добавляет счета командой `/add_account`
2. Система показывает список активных розыгрышей
3. Администратор выбирает розыгрыш или пропускает
4. Счета автоматически добавляются в выбранный розыгрыш
**Преимущества:**
- ✅ Экономия времени - не нужно вручную добавлять каждый счет
- ✅ Меньше ошибок - система проверяет дубликаты
- ✅ Удобство - всё в одном процессе
- ✅ Гибкость - можно пропустить добавление
---
### 3. Пакетное добавление счетов
**Сценарий использования:**
У вас есть список новых клиентов с их счетами. Вместо добавления каждого по отдельности:
```
/add_account
2223 11-22-33-44-55-66-77
2223 88-99-00-11-22-33-44
3334 12-34-56-78-90-12-34
3334 99-88-77-66-55-44-33
5556 11-11-11-11-11-11-11
```
**Результат:**
```
📊 Результаты добавления счетов
✅ Успешно добавлено: 5
• 2223 → 11-22-33-44-55-66-77
👤 Иван
• 2223 → 88-99-00-11-22-33-44
👤 Иван
• 3334 → 12-34-56-78-90-12-34
👤 Петр
• 3334 → 99-88-77-66-55-44-33
👤 Петр
• 5556 → 11-11-11-11-11-11-11
👤 Мария
Добавить счета в розыгрыш?
```
**Обработка ошибок:**
Если какие-то счета невалидны, система покажет:
```
❌ Ошибки: 2
• Строка 3 (9999 123-456): Пользователь с клубной картой 9999 не найден
• Строка 5 (2223 11-22-33): Неверный формат номера счета
```
Остальные счета будут добавлены успешно.
---
## 🔄 Обновленный процесс работы
### Полный цикл: Регистрация → Счета → Розыгрыш
**Шаг 1: Клиент регистрируется**
```
Клиент: /start
Клиент: Нажимает "Зарегистрироваться"
Клиент: Вводит клубную карту "2223"
Клиент: Вводит телефон или пропускает
Система: Показывает код верификации "AB12CD34"
```
**Шаг 2: Администратор добавляет счета**
```
Админ: /add_account
2223 11-22-33-44-55-66-77
2223 88-99-00-11-22-33-44
Система: Добавляет оба счета
Система: Отправляет уведомления владельцу
Система: Предлагает добавить в розыгрыш
```
**Шаг 3: Администратор выбирает розыгрыш**
```
Админ: Нажимает "🎯 Новогодний розыгрыш"
Система: Добавляет оба счета в розыгрыш
Система: Показывает результат:
✅ Добавлено счетов: 2
```
**Шаг 4: Клиент проверяет свои счета**
```
Клиент: /start → "Мой счет"
Видит:
💳 Ваши счета
🎫 Клубная карта: 2223
🔑 Код верификации: AB12CD34
Счета (2):
1. 11-22-33-44-55-66-77 ✅ Активен
2. 88-99-00-11-22-33-44 ✅ Активен
```
**Шаг 5: Проведение розыгрыша**
```
Админ: Нажимает "Провести розыгрыш"
Система: Выбирает победителей
Система: Находит владельцев счетов-победителей
Система: Отправляет уведомления с кодами верификации
```
**Шаг 6: Подтверждение выигрыша**
```
Победитель: Связывается с админом, называет код "AB12CD34"
Админ: /verify_winner AB12CD34 1
Система: Подтверждает выигрыш
Система: Отправляет уведомление победителю
```
---
## 📝 Примеры использования
### Пример 1: Добавление счетов для одного пользователя
```
/add_account
2223 11-22-33-44-55-66-77
2223 22-33-44-55-66-77-88
2223 33-44-55-66-77-88-99
```
Результат: Все 3 счета добавлены пользователю с клубной картой 2223
### Пример 2: Добавление счетов для разных пользователей
```
/add_account
2223 11-22-33-44-55-66-77
3334 22-33-44-55-66-77-88
5556 33-44-55-66-77-88-99
7778 44-55-66-77-88-99-00
```
Результат: Каждый счет добавлен соответствующему пользователю
### Пример 3: Быстрое добавление с немедленным участием
```
/add_account 2223 11-22-33-44-55-66-77
[Выбрать розыгрыш]
[Нажать "🎯 Новогодний розыгрыш"]
```
Результат: Счет добавлен и сразу участвует в розыгрыше
---
## 🛠️ Технические детали
### Новая FSM структура в admin_account_handlers.py
```python
class AddAccountStates(StatesGroup):
waiting_for_data = State() # Ожидание данных счетов
choosing_lottery = State() # Выбор розыгрыша
```
### Новые callback handlers
- `add_to_lottery_{lottery_id}` - Добавить счета в розыгрыш
- `skip_lottery_add` - Пропустить добавление в розыгрыш
### Обновленные функции
1. **add_account_command()** - Точка входа, поддерживает оба режима
2. **process_single_account()** - Обработка одного счета из команды
3. **process_accounts_data()** - Обработка пакета счетов
4. **show_lottery_selection()** - Показать выбор розыгрыша
5. **add_accounts_to_lottery()** - Добавить счета в выбранный розыгрыш
### Проверки безопасности
- ✅ Проверка существования пользователя по клубной карте
- ✅ Проверка уникальности номера счета
- ✅ Проверка формата номера (XX-XX-XX-XX-XX-XX-XX)
- ✅ Проверка дубликатов при добавлении в розыгрыш
- ✅ Проверка прав администратора
---
## 📊 Сравнение: До и После
### Старый процесс (до обновления):
```
1. /add_account 2223 11-22-33-44-55-66-77
2. [Ждем подтверждения]
3. Вручную находим розыгрыш
4. Вручную добавляем счет в розыгрыш
5. Повторяем для каждого счета
```
**Время на 10 счетов:** ~5-7 минут
### Новый процесс (после обновления):
```
1. /add_account
2. Вставляем 10 строк с данными
3. Выбираем розыгрыш из списка
4. Готово!
```
**Время на 10 счетов:** ~30 секунд
**Экономия времени: 90%!**
---
## 🔍 Отладка и решение проблем
### Проблема: Не показываются розыгрыши после добавления счетов
**Причина:** Нет активных розыгрышей
**Решение:** Создайте активный розыгрыш через админ-панель
### Проблема: Ошибка "Пользователь не найден"
**Причина:** Клубная карта не зарегистрирована
**Решение:**
1. Попросите пользователя зарегистрироваться через /start
2. Или проверьте правильность клубной карты через /user_info
### Проблема: "Счет уже существует"
**Причина:** Этот номер счета уже привязан к другому пользователю
**Решение:**
1. Проверьте номер счета
2. Если ошибка - деактивируйте старый: /remove_account <номер>
3. Добавьте заново
### Проблема: Счет не добавился в розыгрыш
**Причина:** Счет уже участвует в этом розыгрыше
**Решение:** Это нормально, система предотвращает дубликаты
---
## 📚 Команды для администратора (обновленный список)
### Управление счетами:
- `/add_account` - Добавить счета (интерактивно или пакетом)
- `/add_account <club_card> <account>` - Быстрое добавление одного счета
- `/remove_account <account>` - Деактивировать счет
- `/user_info <club_card>` - Информация о пользователе
### Управление розыгрышами:
- Создание через интерфейс (кнопка "Создать розыгрыш")
- Проведение через интерфейс (кнопка "Провести розыгрыш")
- `/winner_status <lottery_id>` - Статус победителей
### Верификация:
- `/verify_winner <code> <lottery_id>` - Подтвердить выигрыш
---
## 🎯 Рекомендации по использованию
### Для массового добавления счетов:
1. **Подготовьте данные в текстовом файле**
```
2223 11-22-33-44-55-66-77
2223 88-99-00-11-22-33-44
3334 12-34-56-78-90-12-34
...
```
2. **Скопируйте и вставьте в бота**
```
/add_account
[Вставить все строки]
```
3. **Выберите розыгрыш**
4. **Проверьте результаты**
### Для одиночных счетов:
Используйте быстрый режим:
```
/add_account 2223 11-22-33-44-55-66-77
```
### Для новых пользователей:
1. Сначала попросите зарегистрироваться
2. Затем добавьте счета
3. Счета автоматически будут доступны в розыгрышах
---
## ✅ Чек-лист для администратора
При добавлении новых пользователей:
- [ ] Пользователь прошел регистрацию (/start → Зарегистрироваться)
- [ ] Пользователь получил код верификации
- [ ] Добавлены все счета через /add_account
- [ ] Счета добавлены в активный розыгрыш
- [ ] Пользователю отправлены уведомления
- [ ] Проверено через /user_info <club_card>
При проведении розыгрыша:
- [ ] Все счета добавлены и активны
- [ ] Розыгрыш настроен (призы, описание)
- [ ] Нажата кнопка "Провести розыгрыш"
- [ ] Победители получили уведомления
- [ ] Проверен статус через /winner_status <id>
При подтверждении выигрышей:
- [ ] Победитель сообщил код верификации
- [ ] Код проверен командой /verify_winner
- [ ] Победитель получил подтверждение
- [ ] Приз передан победителю