""" Сервис для работы с участием счетов в розыгрышах (без привязки к пользователям) """ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, delete, func from models import Lottery, Participation, Winner from account_utils import validate_account_number, format_account_number, parse_accounts_from_message, search_accounts_by_pattern from typing import List, Optional, Dict, Any class AccountParticipationService: """Сервис для работы с участием счетов в розыгрышах""" @staticmethod async def add_account_to_lottery( session: AsyncSession, lottery_id: int, account_number: str ) -> Dict[str, Any]: """ Добавить счет в розыгрыш Returns: Dict с ключами: success, message, account_number """ # Валидируем и форматируем formatted_account = format_account_number(account_number) if not formatted_account: return { "success": False, "message": f"Неверный формат счета: {account_number}", "account_number": account_number } # Проверяем существование розыгрыша lottery = await session.get(Lottery, lottery_id) if not lottery: return { "success": False, "message": f"Розыгрыш #{lottery_id} не найден", "account_number": formatted_account } # Проверяем, не участвует ли уже этот счет existing = await session.execute( select(Participation).where( Participation.lottery_id == lottery_id, Participation.account_number == formatted_account ) ) if existing.scalar_one_or_none(): return { "success": False, "message": f"Счет {formatted_account} уже участвует в розыгрыше", "account_number": formatted_account } # Добавляем участие participation = Participation( lottery_id=lottery_id, account_number=formatted_account, user_id=None # Без привязки к пользователю ) session.add(participation) await session.commit() return { "success": True, "message": f"Счет {formatted_account} добавлен в розыгрыш", "account_number": formatted_account } @staticmethod async def add_accounts_bulk( session: AsyncSession, lottery_id: int, account_numbers: List[str] ) -> Dict[str, Any]: """ Массовое добавление счетов в розыгрыш """ results = { "added": 0, "skipped": 0, "errors": [], "details": [], "added_accounts": [], "skipped_accounts": [] } for account in account_numbers: result = await AccountParticipationService.add_account_to_lottery( session, lottery_id, account ) if result["success"]: results["added"] += 1 results["added_accounts"].append(result["account_number"]) results["details"].append(f"✅ {result['account_number']}") else: results["skipped"] += 1 results["skipped_accounts"].append(account) results["errors"].append(result["message"]) results["details"].append(f"❌ {result['message']}") return results @staticmethod async def remove_account_from_lottery( session: AsyncSession, lottery_id: int, account_number: str ) -> Dict[str, Any]: """Удалить счет из розыгрыша""" formatted_account = format_account_number(account_number) if not formatted_account: return { "success": False, "message": f"Неверный формат счета: {account_number}" } participation = await session.execute( select(Participation).where( Participation.lottery_id == lottery_id, Participation.account_number == formatted_account ) ) participation = participation.scalar_one_or_none() if not participation: return { "success": False, "message": f"Счет {formatted_account} не участвует в розыгрыше" } await session.delete(participation) await session.commit() return { "success": True, "message": f"Счет {formatted_account} удален из розыгрыша" } @staticmethod async def get_lottery_accounts( session: AsyncSession, lottery_id: int, limit: Optional[int] = None, offset: int = 0 ) -> List[str]: """Получить все счета, участвующие в розыгрыше""" query = select(Participation.account_number).where( Participation.lottery_id == lottery_id, Participation.account_number.isnot(None) ).order_by(Participation.created_at.desc()) if limit: query = query.offset(offset).limit(limit) result = await session.execute(query) return [account for account in result.scalars().all() if account] @staticmethod async def get_accounts_count(session: AsyncSession, lottery_id: int) -> int: """Получить количество счетов в розыгрыше""" result = await session.scalar( select(func.count(Participation.id)).where( Participation.lottery_id == lottery_id, Participation.account_number.isnot(None) ) ) return result or 0 @staticmethod async def search_accounts_in_lottery( session: AsyncSession, lottery_id: int, pattern: str, limit: int = 20 ) -> List[str]: """ Поиск счетов в розыгрыше по частичному совпадению Args: lottery_id: ID розыгрыша pattern: Паттерн поиска (например "11-22" или "33") limit: Максимальное количество результатов """ # Получаем все счета розыгрыша all_accounts = await AccountParticipationService.get_lottery_accounts( session, lottery_id ) # Ищем совпадения return search_accounts_by_pattern(pattern, all_accounts)[:limit] @staticmethod async def set_account_as_winner( session: AsyncSession, lottery_id: int, account_number: str, place: int, prize: Optional[str] = None ) -> Dict[str, Any]: """ Установить счет как победителя на указанное место """ formatted_account = format_account_number(account_number) if not formatted_account: return { "success": False, "message": f"Неверный формат счета: {account_number}" } # Проверяем, участвует ли счет в розыгрыше participation = await session.execute( select(Participation).where( Participation.lottery_id == lottery_id, Participation.account_number == formatted_account ) ) if not participation.scalar_one_or_none(): return { "success": False, "message": f"Счет {formatted_account} не участвует в розыгрыше" } # Проверяем, не занято ли уже это место existing_winner = await session.execute( select(Winner).where( Winner.lottery_id == lottery_id, Winner.place == place ) ) existing_winner = existing_winner.scalar_one_or_none() if existing_winner: # Обновляем существующего победителя existing_winner.account_number = formatted_account existing_winner.user_id = None existing_winner.is_manual = True if prize: existing_winner.prize = prize else: # Создаем нового победителя winner = Winner( lottery_id=lottery_id, account_number=formatted_account, user_id=None, place=place, prize=prize, is_manual=True ) session.add(winner) await session.commit() return { "success": True, "message": f"Счет {formatted_account} установлен победителем на место {place}", "account_number": formatted_account, "place": place } @staticmethod async def get_lottery_winners_accounts( session: AsyncSession, lottery_id: int ) -> List[Dict[str, Any]]: """Получить всех победителей розыгрыша (счета)""" result = await session.execute( select(Winner).where( Winner.lottery_id == lottery_id, Winner.account_number.isnot(None) ).order_by(Winner.place) ) winners = result.scalars().all() return [ { "place": w.place, "account_number": w.account_number, "prize": w.prize, "is_manual": w.is_manual } for w in winners ]