""" Сервис для управления вопросами """ from datetime import datetime from typing import List, Optional, Tuple from aiogram import Bot from models.question import Question, QuestionStatus 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_question_created, log_question_answered logger = get_logger(__name__) class QuestionService: """Сервис для управления вопросами""" def __init__(self, database: DatabaseService, utils: UtilsService): self.database = database self.utils = utils self.metrics = get_metrics_service() @log_business_event("create_question", log_params=True, log_result=True) async def create_question(self, from_user_id: int, to_user_id: int, message_text: str) -> Question: """ Создание нового вопроса Args: from_user_id: ID автора вопроса to_user_id: ID получателя вопроса message_text: Текст вопроса Returns: Созданный объект вопроса """ try: question = Question( from_user_id=from_user_id, to_user_id=to_user_id, message_text=message_text.strip(), status=QuestionStatus.PENDING, created_at=datetime.now(), is_anonymous=True ) question = await self.database.create_question(question) self.metrics.increment_questions("created") return question except Exception as e: logger.error(f"Ошибка при создании вопроса от {from_user_id} к {to_user_id}: {e}") raise @log_function_call(log_params=True, log_result=True) async def get_question(self, question_id: int) -> Optional[Question]: """ Получение вопроса по ID Args: question_id: ID вопроса Returns: Объект вопроса или None """ try: return await self.database.get_question(question_id) except Exception as e: logger.error(f"Ошибка при получении вопроса {question_id}: {e}") return None @log_function_call(log_params=True, log_result=True) async def get_user_questions(self, user_id: int, limit: int = 50, offset: int = 0) -> List[Question]: """ Получение вопросов пользователя Args: user_id: ID пользователя limit: Лимит вопросов offset: Смещение Returns: Список вопросов """ try: return await self.database.get_user_questions(user_id, limit=limit, offset=offset) except Exception as e: logger.error(f"Ошибка при получении вопросов пользователя {user_id}: {e}") return [] @log_business_event("answer_question", log_params=True, log_result=True) async def answer_question(self, question_id: int, answer_text: str) -> Optional[Question]: """ Ответ на вопрос Args: question_id: ID вопроса answer_text: Текст ответа Returns: Обновленный объект вопроса или None """ try: question = await self.database.get_question(question_id) if not question: return None question.mark_as_answered(answer_text.strip()) question.answered_at = datetime.now() question = await self.database.update_question(question) self.metrics.increment_answers("sent") return question except Exception as e: logger.error(f"Ошибка при ответе на вопрос {question_id}: {e}") return None @log_business_event("reject_question", log_params=True, log_result=True) async def reject_question(self, question_id: int) -> Optional[Question]: """ Отклонение вопроса Args: question_id: ID вопроса Returns: Обновленный объект вопроса или None """ try: question = await self.database.get_question(question_id) if not question: return None question.mark_as_rejected() question.answered_at = datetime.now() question = await self.database.update_question(question) self.metrics.increment_questions("rejected") return question except Exception as e: logger.error(f"Ошибка при отклонении вопроса {question_id}: {e}") return None @log_business_event("delete_question", log_params=True, log_result=True) async def delete_question(self, question_id: int) -> Optional[Question]: """ Удаление вопроса Args: question_id: ID вопроса Returns: Обновленный объект вопроса или None """ try: question = await self.database.get_question(question_id) if not question: return None question.mark_as_deleted() question.answered_at = datetime.now() question = await self.database.update_question(question) self.metrics.increment_questions("deleted") return question except Exception as e: logger.error(f"Ошибка при удалении вопроса {question_id}: {e}") return None @log_business_event("edit_answer", log_params=True, log_result=True) async def edit_answer(self, question_id: int, new_answer_text: str) -> Optional[Question]: """ Редактирование ответа на вопрос Args: question_id: ID вопроса new_answer_text: Новый текст ответа Returns: Обновленный объект вопроса или None """ try: question = await self.database.get_question(question_id) if not question: return None question.answer_text = new_answer_text.strip() question.answered_at = datetime.now() question = await self.database.update_question(question) self.metrics.increment_answers("edited") return question except Exception as e: logger.error(f"Ошибка при редактировании ответа на вопрос {question_id}: {e}") return None @log_function_call(log_params=True, log_result=True) def validate_question_text(self, text: str, max_length: int = 1000) -> Tuple[bool, str]: """ Валидация текста вопроса Args: text: Текст вопроса max_length: Максимальная длина Returns: Кортеж (валидность, сообщение об ошибке) """ return self.utils.is_valid_question_text(text, max_length) @log_function_call(log_params=True, log_result=True) def validate_answer_text(self, text: str, max_length: int = 2000) -> Tuple[bool, str]: """ Валидация текста ответа Args: text: Текст ответа max_length: Максимальная длина Returns: Кортеж (валидность, сообщение об ошибке) """ return self.utils.is_valid_answer_text(text, max_length) @log_function_call(log_params=True, log_result=True) async def send_answer_to_author(self, bot: Bot, question: Question, answer_text: str) -> bool: """ Отправка ответа автору вопроса Args: bot: Экземпляр бота question: Объект вопроса answer_text: Текст ответа Returns: True если успешно отправлено """ try: await self.utils.send_answer_to_author(bot, question, answer_text) self.metrics.increment_answers("delivered") return True except Exception as e: logger.error(f"Ошибка при отправке ответа автору вопроса {question.id}: {e}") self.metrics.increment_answers("delivery_failed") return False @log_function_call(log_params=True, log_result=True) def format_question_info(self, question: Question, show_answer: bool = False) -> str: """ Форматирование информации о вопросе Args: question: Объект вопроса show_answer: Показывать ли ответ Returns: Отформатированная строка """ return self.utils.format_question_info(question, show_answer) @log_function_call(log_params=True, log_result=True) def get_question_preview(self, question: Question, max_length: int = 50) -> str: """ Получение превью вопроса Args: question: Объект вопроса max_length: Максимальная длина превью Returns: Превью вопроса """ return question.get_question_preview(max_length)