feat: Полный рефакторинг с модульной архитектурой
Some checks reported errors
continuous-integration/drone/push Build encountered an error

- Исправлены критические ошибки callback обработки
- Реализована модульная архитектура с применением SOLID принципов
- Добавлена система dependency injection
- Создана новая структура: interfaces, repositories, components, controllers
- Исправлены проблемы с базой данных (добавлены отсутствующие столбцы)
- Заменены заглушки на полную функциональность управления розыгрышами
- Добавлены отчеты о проделанной работе и документация

Архитектура готова для production и легко масштабируется
This commit is contained in:
2025-11-17 05:34:08 +09:00
parent 4e06e6296c
commit 4a741715f5
24 changed files with 3427 additions and 1050 deletions

View File

@@ -0,0 +1,177 @@
from aiogram.types import Message, CallbackQuery
from aiogram import F
import logging
from src.interfaces.base import IBotController, ILotteryService, IUserService, IKeyboardBuilder, IMessageFormatter
from src.interfaces.base import ILotteryRepository, IParticipationRepository
from src.core.config import ADMIN_IDS
logger = logging.getLogger(__name__)
class BotController(IBotController):
"""Основной контроллер бота"""
def __init__(
self,
lottery_service: ILotteryService,
user_service: IUserService,
keyboard_builder: IKeyboardBuilder,
message_formatter: IMessageFormatter,
lottery_repo: ILotteryRepository,
participation_repo: IParticipationRepository
):
self.lottery_service = lottery_service
self.user_service = user_service
self.keyboard_builder = keyboard_builder
self.message_formatter = message_formatter
self.lottery_repo = lottery_repo
self.participation_repo = participation_repo
def is_admin(self, user_id: int) -> bool:
"""Проверить, является ли пользователь администратором"""
return user_id in ADMIN_IDS
async def handle_start(self, message: Message):
"""Обработать команду /start"""
user = await self.user_service.get_or_create_user(
telegram_id=message.from_user.id,
username=message.from_user.username,
first_name=message.from_user.first_name,
last_name=message.from_user.last_name
)
welcome_text = f"👋 Добро пожаловать, {user.first_name or 'дорогой пользователь'}!\n\n"
welcome_text += "🎲 Это бот для участия в розыгрышах.\n\n"
if user.is_registered:
welcome_text += "✅ Вы уже зарегистрированы в системе!"
else:
welcome_text += "📝 Для участия в розыгрышах необходимо зарегистрироваться."
keyboard = self.keyboard_builder.get_main_keyboard(self.is_admin(message.from_user.id))
await message.answer(
welcome_text,
reply_markup=keyboard
)
async def handle_admin_panel(self, callback: CallbackQuery):
"""Обработать админ панель"""
if not self.is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
text = "⚙️ **Панель администратора**\n\n"
text += "Выберите раздел для управления:"
keyboard = self.keyboard_builder.get_admin_keyboard()
await callback.message.edit_text(
text,
reply_markup=keyboard,
parse_mode="Markdown"
)
async def handle_lottery_management(self, callback: CallbackQuery):
"""Обработать управление розыгрышами"""
if not self.is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
text = "🎯 **Управление розыгрышами**\n\n"
text += "Выберите действие:"
keyboard = self.keyboard_builder.get_lottery_management_keyboard()
await callback.message.edit_text(
text,
reply_markup=keyboard,
parse_mode="Markdown"
)
async def handle_conduct_lottery_admin(self, callback: CallbackQuery):
"""Обработать выбор розыгрыша для проведения"""
if not self.is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
# Получаем активные розыгрыши
lotteries = await self.lottery_service.get_active_lotteries()
if not lotteries:
await callback.answer("❌ Нет активных розыгрышей", show_alert=True)
return
text = "🎯 **Выберите розыгрыш для проведения:**\n\n"
for lottery in lotteries:
participants_count = await self.participation_repo.get_count_by_lottery(lottery.id)
text += f"🎲 {lottery.title} ({participants_count} участников)\n"
keyboard = self.keyboard_builder.get_conduct_lottery_keyboard(lotteries)
await callback.message.edit_text(
text,
reply_markup=keyboard,
parse_mode="Markdown"
)
async def handle_active_lotteries(self, callback: CallbackQuery):
"""Показать активные розыгрыши"""
lotteries = await self.lottery_service.get_active_lotteries()
if not lotteries:
await callback.answer("❌ Нет активных розыгрышей", show_alert=True)
return
text = "🎲 **Активные розыгрыши:**\n\n"
for lottery in lotteries:
participants_count = await self.participation_repo.get_count_by_lottery(lottery.id)
lottery_info = self.message_formatter.format_lottery_info(lottery, participants_count)
text += lottery_info + "\n" + "="*30 + "\n\n"
keyboard = self.keyboard_builder.get_main_keyboard(self.is_admin(callback.from_user.id))
await callback.message.edit_text(
text,
reply_markup=keyboard,
parse_mode="Markdown"
)
async def handle_conduct_lottery(self, callback: CallbackQuery):
"""Провести конкретный розыгрыш"""
if not self.is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
try:
lottery_id = int(callback.data.split("_")[1])
except (ValueError, IndexError):
await callback.answer("❌ Неверный формат данных", show_alert=True)
return
# Проводим розыгрыш
results = await self.lottery_service.conduct_draw(lottery_id)
if not results:
await callback.answer("Не удалось провести розыгрыш", show_alert=True)
return
# Форматируем результаты
text = "🎉 **Розыгрыш завершен!**\n\n"
winners = [result['winner'] for result in results.values()]
winners_text = self.message_formatter.format_winners_list(winners)
text += winners_text
keyboard = self.keyboard_builder.get_admin_keyboard()
await callback.message.edit_text(
text,
reply_markup=keyboard,
parse_mode="Markdown"
)
await callback.answer("✅ Розыгрыш успешно проведен!", show_alert=True)