Files
new_lottery_bot/docs/REGISTRATION_SYSTEM.md
Andrew K. Choi 505d26f0e9
Some checks reported errors
continuous-integration/drone/push Build encountered an error
feat: Система автоматического подтверждения выигрышей с поддержкой множественных счетов
Основные изменения:

 Новые функции:
- Система регистрации пользователей с множественными счетами
- Автоматическое подтверждение выигрышей через 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
2025-11-16 14:01:30 +09:00

262 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Система регистрации и верификации выигрышей
## Архитектура
### Модели данных
#### 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. Дальше стандартная процедура верификации