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

117
src/components/services.py Normal file
View File

@@ -0,0 +1,117 @@
from typing import List, Dict, Any, Optional
import random
from datetime import datetime, timezone
from src.interfaces.base import ILotteryService, IUserService
from src.interfaces.base import ILotteryRepository, IUserRepository, IParticipationRepository, IWinnerRepository
from src.core.models import Lottery, User
class LotteryServiceImpl(ILotteryService):
"""Реализация сервиса розыгрышей"""
def __init__(
self,
lottery_repo: ILotteryRepository,
participation_repo: IParticipationRepository,
winner_repo: IWinnerRepository,
user_repo: IUserRepository
):
self.lottery_repo = lottery_repo
self.participation_repo = participation_repo
self.winner_repo = winner_repo
self.user_repo = user_repo
async def create_lottery(self, title: str, description: str, prizes: List[str], creator_id: int) -> Lottery:
"""Создать новый розыгрыш"""
return await self.lottery_repo.create(
title=title,
description=description,
prizes=prizes,
creator_id=creator_id,
is_active=True,
is_completed=False,
created_at=datetime.now(timezone.utc)
)
async def conduct_draw(self, lottery_id: int) -> Dict[str, Any]:
"""Провести розыгрыш"""
lottery = await self.lottery_repo.get_by_id(lottery_id)
if not lottery or lottery.is_completed:
return {}
# Получаем участников
participations = await self.participation_repo.get_by_lottery(lottery_id)
if not participations:
return {}
# Проводим розыгрыш
random.shuffle(participations)
results = {}
num_prizes = len(lottery.prizes) if lottery.prizes else 3
winners = participations[:num_prizes]
for i, participation in enumerate(winners):
place = i + 1
prize = lottery.prizes[i] if lottery.prizes and i < len(lottery.prizes) else f"Приз {place}"
# Создаем запись о победителе
winner = await self.winner_repo.create(
lottery_id=lottery_id,
user_id=participation.user_id,
account_number=participation.account_number,
place=place,
prize=prize,
is_manual=False
)
results[str(place)] = {
'winner': winner,
'user': participation.user,
'prize': prize
}
# Помечаем розыгрыш как завершенный
lottery.is_completed = True
lottery.draw_results = {str(k): v['prize'] for k, v in results.items()}
await self.lottery_repo.update(lottery)
return results
async def get_active_lotteries(self) -> List[Lottery]:
"""Получить активные розыгрыши"""
return await self.lottery_repo.get_active()
class UserServiceImpl(IUserService):
"""Реализация сервиса пользователей"""
def __init__(self, user_repo: IUserRepository):
self.user_repo = user_repo
async def get_or_create_user(self, telegram_id: int, **kwargs) -> User:
"""Получить или создать пользователя"""
user = await self.user_repo.get_by_telegram_id(telegram_id)
if not user:
user_data = {
'telegram_id': telegram_id,
'created_at': datetime.now(timezone.utc),
**kwargs
}
user = await self.user_repo.create(**user_data)
return user
async def register_user(self, telegram_id: int, phone: str, club_card_number: str) -> bool:
"""Зарегистрировать пользователя"""
user = await self.user_repo.get_by_telegram_id(telegram_id)
if not user:
return False
user.phone = phone
user.club_card_number = club_card_number
user.is_registered = True
user.generate_verification_code()
await self.user_repo.update(user)
return True