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,227 @@
"""
Утилиты для контекстного логирования
"""
from typing import Any, Optional, Dict, Union
from aiogram.types import Message, CallbackQuery, User
from services.infrastructure.logger import get_logger
class LoggingContext:
"""Контекст для логирования с дополнительной информацией"""
def __init__(self, module_name: str):
self.logger = get_logger(module_name)
self.context_data = {}
def add_context(self, key: str, value: Any) -> 'LoggingContext':
"""Добавить данные в контекст"""
self.context_data[key] = value
return self
def log_info(self, message: str, **kwargs):
"""Логирование с контекстом"""
context_str = self._format_context()
full_message = f"{message}{context_str}"
self.logger.info(full_message, **kwargs)
def log_warning(self, message: str, **kwargs):
"""Логирование предупреждения с контекстом"""
context_str = self._format_context()
full_message = f"{message}{context_str}"
self.logger.warning(full_message, **kwargs)
def log_error(self, message: str, **kwargs):
"""Логирование ошибки с контекстом"""
context_str = self._format_context()
full_message = f"{message}{context_str}"
self.logger.error(full_message, **kwargs)
def _format_context(self) -> str:
"""Форматирование контекстных данных"""
if not self.context_data:
return ""
context_parts = [f"{k}={v}" for k, v in self.context_data.items()]
return f" | {', '.join(context_parts)}"
def get_logging_context(module_name: str) -> LoggingContext:
"""Получить контекст логирования для модуля"""
return LoggingContext(module_name)
def log_user_action(
logger,
action: str,
user: Union[User, Message, CallbackQuery, int],
additional_info: Optional[Dict[str, Any]] = None
):
"""
Логирование действий пользователя
Args:
logger: Логгер
action: Действие пользователя
user: Объект пользователя, сообщение, callback или user_id
additional_info: Дополнительная информация
"""
user_id = _extract_user_id(user)
user_info = _extract_user_info(user)
context_parts = [f"user_id={user_id}"]
if user_info:
context_parts.append(f"user_info={user_info}")
if additional_info:
for key, value in additional_info.items():
context_parts.append(f"{key}={value}")
context_str = f" | {', '.join(context_parts)}" if context_parts else ""
logger.info(f"👤 {action}{context_str}")
def log_business_operation(
logger,
operation: str,
entity_type: str,
entity_id: Optional[Union[int, str]] = None,
additional_info: Optional[Dict[str, Any]] = None
):
"""
Логирование бизнес-операций
Args:
logger: Логгер
operation: Операция (create, update, delete, etc.)
entity_type: Тип сущности (question, user, etc.)
entity_id: ID сущности
additional_info: Дополнительная информация
"""
context_parts = [f"operation={operation}", f"entity_type={entity_type}"]
if entity_id is not None:
context_parts.append(f"entity_id={entity_id}")
if additional_info:
for key, value in additional_info.items():
context_parts.append(f"{key}={value}")
context_str = f" | {', '.join(context_parts)}"
logger.info(f"📊 Бизнес-операция: {operation} {entity_type}{context_str}")
def log_fsm_event(
logger,
event: str,
state: Optional[str] = None,
user_id: Optional[int] = None,
additional_info: Optional[Dict[str, Any]] = None
):
"""
Логирование FSM событий
Args:
logger: Логгер
event: Событие FSM
state: Текущее состояние
user_id: ID пользователя
additional_info: Дополнительная информация
"""
context_parts = [f"event={event}"]
if state:
context_parts.append(f"state={state}")
if user_id:
context_parts.append(f"user_id={user_id}")
if additional_info:
for key, value in additional_info.items():
context_parts.append(f"{key}={value}")
context_str = f" | {', '.join(context_parts)}"
logger.info(f"🔄 FSM: {event}{context_str}")
def log_performance(
logger,
operation: str,
duration: float,
additional_info: Optional[Dict[str, Any]] = None
):
"""
Логирование производительности
Args:
logger: Логгер
operation: Операция
duration: Время выполнения в секундах
additional_info: Дополнительная информация
"""
context_parts = [f"duration={duration:.3f}s"]
if additional_info:
for key, value in additional_info.items():
context_parts.append(f"{key}={value}")
context_str = f" | {', '.join(context_parts)}"
logger.info(f"⏱️ Производительность: {operation}{context_str}")
def _extract_user_id(user: Union[User, Message, CallbackQuery, int]) -> int:
"""Извлечение user_id из различных объектов"""
if isinstance(user, int):
return user
elif isinstance(user, User):
return user.id
elif isinstance(user, (Message, CallbackQuery)):
return user.from_user.id
else:
return 0
def _extract_user_info(user: Union[User, Message, CallbackQuery, int]) -> Optional[str]:
"""Извлечение информации о пользователе"""
if isinstance(user, int):
return None
elif isinstance(user, User):
return f"{user.first_name or ''} {user.last_name or ''}".strip() or user.username or "Unknown"
elif isinstance(user, (Message, CallbackQuery)):
user_obj = user.from_user
return f"{user_obj.first_name or ''} {user_obj.last_name or ''}".strip() or user_obj.username or "Unknown"
else:
return None
# Удобные функции для быстрого логирования
def log_question_created(logger, question_id: int, from_user_id: int, to_user_id: int):
"""Логирование создания вопроса"""
log_business_operation(
logger, "create", "question", question_id,
{"from_user_id": from_user_id, "to_user_id": to_user_id}
)
def log_question_answered(logger, question_id: int, user_id: int):
"""Логирование ответа на вопрос"""
log_business_operation(
logger, "answer", "question", question_id,
{"user_id": user_id}
)
def log_user_created(logger, user_id: int, username: Optional[str] = None):
"""Логирование создания пользователя"""
additional_info = {"username": username} if username else None
log_business_operation(
logger, "create", "user", user_id, additional_info
)
def log_user_blocked(logger, user_id: int, reason: Optional[str] = None):
"""Логирование блокировки пользователя"""
additional_info = {"reason": reason} if reason else None
log_business_operation(
logger, "block", "user", user_id, additional_info
)