Files
new_lottery_bot/docs/ACCOUNT_BASED_CONFIRMATION.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

11 KiB
Raw Blame History

Подтверждение выигрышей по счетам

🎯 Концепция

Система подтверждения выигрышей работает на уровне счетов, а не пользователей.

Основные принципы:

  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 подтвержден

🔒 Безопасность

Проверка владения счетом

При нажатии кнопки подтверждения система проверяет:

# Получаем владельца КОНКРЕТНОГО счета
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

Каждая запись = один выигрышный счет:

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 данные

# Формат: confirm_win_{winner_id}
callback_data = f"confirm_win_{winner.id}"

# winner.id - уникальный ID записи в таблице winners
# Каждый счет-выигрыш имеет свой winner_id

Проверка при подтверждении

# 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 - Руководство администратора