Files
new_lottery_bot/migrations/versions/001_init_tables.py
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

97 lines
4.3 KiB
Python

"""Init tables
Revision ID: init
Revises:
Create Date: 2025-11-16 12:15:00.000000
"""
from alembic import op
import sqlalchemy as sa
from datetime import datetime, timezone
# revision identifiers, used by Alembic.
revision = 'init'
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
# Users table
op.create_table('users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('telegram_id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=255), nullable=True),
sa.Column('first_name', sa.String(length=255), nullable=True),
sa.Column('last_name', sa.String(length=255), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True),
nullable=True, default=lambda: datetime.now(timezone.utc)),
sa.Column('is_admin', sa.Boolean(), nullable=True, default=False),
sa.Column('account_number', sa.String(length=20), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index('ix_users_telegram_id', 'users', ['telegram_id'], unique=True)
op.create_index('ix_users_account_number', 'users', ['account_number'], unique=False)
# Lotteries table
op.create_table('lotteries',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('title', sa.String(length=500), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True),
nullable=True, default=lambda: datetime.now(timezone.utc)),
sa.Column('start_date', sa.DateTime(timezone=True), nullable=True),
sa.Column('end_date', sa.DateTime(timezone=True), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=True, default=True),
sa.Column('is_completed', sa.Boolean(), nullable=True, default=False),
sa.Column('prizes', sa.JSON(), nullable=True),
sa.Column('creator_id', sa.Integer(), nullable=False),
sa.Column('manual_winners', sa.JSON(), nullable=True, default=dict),
sa.Column('draw_results', sa.JSON(), nullable=True),
sa.Column('winner_display_type', sa.String(length=20), nullable=True, default='username'),
sa.ForeignKeyConstraint(['creator_id'], ['users.id']),
sa.PrimaryKeyConstraint('id')
)
# Participations table
op.create_table('participations',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('lottery_id', sa.Integer(), nullable=False),
sa.Column('account_number', sa.String(length=20), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True),
nullable=True, default=lambda: datetime.now(timezone.utc)),
sa.ForeignKeyConstraint(['lottery_id'], ['lotteries.id']),
sa.ForeignKeyConstraint(['user_id'], ['users.id']),
sa.PrimaryKeyConstraint('id')
)
op.create_index('ix_participations_account_number', 'participations', ['account_number'], unique=False)
# Winners table
op.create_table('winners',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('lottery_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('account_number', sa.String(length=20), nullable=True),
sa.Column('place', sa.Integer(), nullable=False),
sa.Column('prize', sa.String(length=500), nullable=True),
sa.Column('is_manual', sa.Boolean(), nullable=True, default=False),
sa.Column('created_at', sa.DateTime(timezone=True),
nullable=True, default=lambda: datetime.now(timezone.utc)),
sa.ForeignKeyConstraint(['lottery_id'], ['lotteries.id']),
sa.ForeignKeyConstraint(['user_id'], ['users.id']),
sa.PrimaryKeyConstraint('id')
)
op.create_index('ix_winners_account_number', 'winners', ['account_number'], unique=False)
def downgrade() -> None:
op.drop_index('ix_winners_account_number', table_name='winners')
op.drop_table('winners')
op.drop_index('ix_participations_account_number', table_name='participations')
op.drop_table('participations')
op.drop_table('lotteries')
op.drop_index('ix_users_account_number', table_name='users')
op.drop_index('ix_users_telegram_id', table_name='users')
op.drop_table('users')