Implement user-specific question numbering and update database schema. Added triggers for automatic question numbering and adjustments upon deletion. Enhanced CRUD operations to manage user_question_number effectively.

This commit is contained in:
2025-09-06 18:35:12 +03:00
parent 50be010026
commit 596a2fa813
111 changed files with 16847 additions and 65 deletions

View File

@@ -0,0 +1,208 @@
"""
Сервис для управления пользователями
"""
from datetime import datetime
from typing import Optional, Tuple
from aiogram.types import User as TelegramUser
from models.user import User
from services.infrastructure.database import DatabaseService
from services.utils import UtilsService
from services.infrastructure.logger import get_logger
from services.infrastructure.metrics import get_metrics_service
from services.infrastructure.logging_decorators import log_function_call, log_business_event
from services.infrastructure.logging_utils import log_user_created, log_user_blocked
logger = get_logger(__name__)
class UserService:
"""Сервис для управления пользователями"""
def __init__(self, database: DatabaseService, utils: UtilsService):
self.database = database
self.utils = utils
self.metrics = get_metrics_service()
@log_business_event("create_or_update_user", log_params=True, log_result=True)
async def create_or_update_user(self, telegram_user: TelegramUser, chat_id: int) -> User:
"""
Создание или обновление пользователя
Args:
telegram_user: Объект пользователя из Telegram
chat_id: ID чата
Returns:
Объект пользователя
"""
try:
# Проверяем, существует ли пользователь
existing_user = await self.database.get_user(telegram_user.id)
if existing_user:
# Обновляем существующего пользователя
logger.info(f"👤 Обновление существующего пользователя {telegram_user.id}")
self.metrics.increment_users("updated")
return await self._update_existing_user(existing_user, telegram_user, chat_id)
else:
# Создаем нового пользователя
logger.info(f"👤 Создание нового пользователя {telegram_user.id}")
self.metrics.increment_users("created")
return await self._create_new_user(telegram_user, chat_id)
except Exception as e:
logger.error(f"Ошибка при создании/обновлении пользователя {telegram_user.id}: {e}")
raise
@log_function_call(log_params=True, log_result=True)
async def _update_existing_user(self, existing_user: User, telegram_user: TelegramUser, chat_id: int) -> User:
"""Обновление существующего пользователя"""
existing_user.username = telegram_user.username
existing_user.first_name = telegram_user.first_name or "Пользователь"
existing_user.last_name = telegram_user.last_name
existing_user.chat_id = chat_id
existing_user.update_timestamp()
return await self.database.update_user(existing_user)
@log_function_call(log_params=True, log_result=True)
async def _create_new_user(self, telegram_user: TelegramUser, chat_id: int) -> User:
"""Создание нового пользователя"""
user = User(
telegram_id=telegram_user.id,
username=telegram_user.username,
first_name=telegram_user.first_name or "Пользователь",
last_name=telegram_user.last_name,
chat_id=chat_id,
profile_link=self.utils.generate_anonymous_id(),
is_active=True,
created_at=datetime.now(),
updated_at=datetime.now()
)
return await self.database.create_user(user)
@log_function_call(log_params=True, log_result=True)
async def get_user_by_profile_link(self, profile_link: str) -> Optional[User]:
"""
Получение пользователя по ссылке профиля
Args:
profile_link: Ссылка профиля
Returns:
Объект пользователя или None
"""
try:
return await self.database.get_user_by_profile_link(profile_link)
except Exception as e:
logger.error(f"Ошибка при получении пользователя по ссылке {profile_link}: {e}")
return None
@log_function_call(log_params=True, log_result=True)
async def get_user_by_telegram_id(self, telegram_id: int) -> Optional[User]:
"""
Получение пользователя по Telegram ID
Args:
telegram_id: ID пользователя в Telegram
Returns:
Объект пользователя или None
"""
try:
return await self.database.get_user(telegram_id)
except Exception as e:
logger.error(f"Ошибка при получении пользователя {telegram_id}: {e}")
return None
@log_function_call(log_params=True, log_result=True)
def generate_referral_link(self, bot_username: str, user: User) -> str:
"""
Генерация реферальной ссылки для пользователя
Args:
bot_username: Имя бота
user: Объект пользователя
Returns:
Реферальная ссылка
"""
return self.utils.generate_referral_link(bot_username, user.profile_link)
@log_function_call(log_params=True, log_result=True)
async def is_user_blocked(self, blocker_id: int, blocked_id: int) -> bool:
"""
Проверка, заблокирован ли пользователь
Args:
blocker_id: ID пользователя, который блокирует
blocked_id: ID пользователя, которого блокируют
Returns:
True если заблокирован, False иначе
"""
try:
return await self.database.is_user_blocked(blocker_id, blocked_id)
except Exception as e:
logger.error(f"Ошибка при проверке блокировки {blocker_id} -> {blocked_id}: {e}")
return False
@log_business_event("block_user", log_params=True, log_result=True)
async def block_user(self, blocker_id: int, blocked_id: int) -> bool:
"""
Блокировка пользователя
Args:
blocker_id: ID пользователя, который блокирует
blocked_id: ID пользователя, которого блокируют
Returns:
True если успешно заблокирован
"""
try:
await self.database.block_user(blocker_id, blocked_id)
logger.info(f"Пользователь {blocked_id} заблокирован пользователем {blocker_id}")
return True
except Exception as e:
logger.error(f"Ошибка при блокировке пользователя {blocked_id}: {e}")
return False
@log_business_event("unblock_user", log_params=True, log_result=True)
async def unblock_user(self, blocker_id: int, blocked_id: int) -> bool:
"""
Разблокировка пользователя
Args:
blocker_id: ID пользователя, который разблокирует
blocked_id: ID пользователя, которого разблокируют
Returns:
True если успешно разблокирован
"""
try:
result = await self.database.unblock_user(blocker_id, blocked_id)
if result:
logger.info(f"Пользователь {blocked_id} разблокирован пользователем {blocker_id}")
return result
except Exception as e:
logger.error(f"Ошибка при разблокировке пользователя {blocked_id}: {e}")
return False
@log_function_call(log_params=True, log_result=True)
async def get_blocked_users(self, user_id: int) -> list:
"""
Получение списка заблокированных пользователей
Args:
user_id: ID пользователя
Returns:
Список ID заблокированных пользователей
"""
try:
return await self.database.user_blocks.get_blocked_users(user_id)
except Exception as e:
logger.error(f"Ошибка при получении заблокированных пользователей для {user_id}: {e}")
return []