From 1aabe8f88e153e8a3d5dbd793028c2101df96ad5 Mon Sep 17 00:00:00 2001 From: Disledg Date: Sun, 12 Jan 2025 06:30:53 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D0=BD=D1=8B=20=D1=87=D0=B0=D1=81=D1=82=D1=8C=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=87=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B8=20=D1=80=D0=BE=D1=83=D1=82=D0=BE=D0=B2=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20?= =?UTF-8?q?=D1=82=D0=B8=D0=BA=D0=B5=D1=82=D0=B0=D0=BC=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/support_routes.py | 104 ++++++++++++++++++++++++++++------- app/services/db_manager.py | 44 ++++++++++++++- app/services/postgres_rep.py | 82 ++++++++++++++++++++++++++- 3 files changed, 206 insertions(+), 24 deletions(-) diff --git a/app/routes/support_routes.py b/app/routes/support_routes.py index 2f32f75..107d3be 100644 --- a/app/routes/support_routes.py +++ b/app/routes/support_routes.py @@ -4,11 +4,16 @@ from app.services.db_manager import DatabaseManager from instance.configdb import get_database_manager from uuid import UUID from pydantic import BaseModel +from fastapi import Response, status from enum import Enum from typing import List, Literal, Optional from datetime import datetime import logging logger = logging.getLogger(__name__) + +class UpdateTicketStatusRequest(BaseModel): + new_status: str + class TicketStatus(str, Enum): OPEN = "open" PENDING = "pending" @@ -31,7 +36,6 @@ class TicketMessageRequest(BaseModel): message: str class TicketMessageResponse(BaseModel): - id: int ticket_id: int sender: str message: str @@ -43,7 +47,7 @@ def handle_exception(e: Exception, message: str): logger.error(f"{message}: {e}") raise HTTPException(status_code=500, detail=f"{message}: {str(e)}") -@router.post("/support/tickets/{ticket_id}/messages", response_model=TicketMessageResponse, summary="Добавить сообщение") +@router.post("/support/tickets/{ticket_id}/messages", summary="Добавить сообщение") async def add_message( ticket_id: int, request: TicketMessageRequest, @@ -63,10 +67,12 @@ async def add_message( TicketMessageResponse: Данные созданного сообщения. """ try: - message = await database_manager.add_ticket_message(ticket_id=ticket_id, sender=sender, message=request.message) + message = await database_manager.add_message_to_ticket(ticket_id=ticket_id, sender=sender, message=request.message) if not message: raise HTTPException(status_code=404, detail="Тикет не найден или ошибка добавления сообщения") - return message + if message != "OK": + raise HTTPException(status_code=500, detail="Ошибка добавления сообщения") + return Response(status_code=status.HTTP_200_OK) except Exception as e: handle_exception(e,"Ошибка добавления сообщения") @@ -91,13 +97,67 @@ async def get_messages( """ try: messages = await database_manager.get_ticket_messages(ticket_id=ticket_id) - if not messages: - raise HTTPException(status_code=404, detail="Сообщения для тикета не найдены") - return messages + ticket_info = await database_manager.get_ticket(ticket_id) + logger.info(messages) + + result_messages = [] + if messages: + for message in messages: + correct_response = TicketMessageResponse( + ticket_id=ticket_id, + sender=message.sender, + message=message.message, + created_at=message.created_at + ) + result_messages.append(correct_response) + + result_messages.insert(0, + TicketMessageResponse( + ticket_id=ticket_id, + sender="user", + message=ticket_info['message'], + created_at=ticket_info["created_at"] + ) + ) + return result_messages except Exception as e: handle_exception(e,"Ошибка получения сообщения") +@router.get("/support/ticket/{ticket_id}", response_model=TicketResponse, summary="Получить информацию о тикете") +async def get_ticket( + ticket_id: int, + database_manager: DatabaseManager = Depends(get_database_manager) +): + """ + Возвращает информацию о тикете. + + Args: + ticket_id (int): ID тикета, информацию котрого хочет получить пользователь. + database_manager (DatabaseManager): Менеджер базы данных. + + Returns: + TicketResponse: Информация о тикете. + + Raises: + HTTPException: 404, если тикет не найден. + HTTPException: 500, если произошла ошибка на сервере. + """ + try: + # Получаем данные о тикете + ticket = await database_manager.get_ticket(ticket_id=ticket_id) + if not ticket: + raise HTTPException(status_code=404, detail="Тикет не найден") + + # Возвращаем данные через Pydantic-модель + return TicketResponse(**ticket) + except SQLAlchemyError as e: + handle_exception(e, "Ошибка получения тикета") + except Exception as e: + handle_exception(e, "Неизвестная ошибка при получении тикета") + + + @router.post("/support/tickets", response_model=TicketResponse, summary="Создать тикет") async def create_ticket( request: CreateTicketRequest, @@ -148,38 +208,42 @@ async def list_tickets( HTTPException: 500, если произошла ошибка на сервере. """ try: - tickets = await database_manager.list_tickets(user_id=user_id) + tickets = await database_manager.get_active_tickets(user_id=user_id) if not tickets: raise HTTPException(status_code=404, detail="Тикеты не найдены") return tickets except Exception as e: handle_exception(e,"Ошибка получения тикетов") -@router.patch("/support/tickets/{ticket_id}", response_model=TicketResponse, summary="Обновить статус тикета") +@router.patch("/support/ticket/{ticket_id}/status", summary="Обновить статус тикета") async def update_ticket_status( ticket_id: int, - status: TicketStatus, + request: UpdateTicketStatusRequest, database_manager: DatabaseManager = Depends(get_database_manager) ): """ Обновляет статус тикета. Args: - ticket_id (int): ID тикета, статус которого нужно обновить. - status (TicketStatus): Новый статус тикета (open, pending, closed). + ticket_id (int): ID тикета. + request (UpdateTicketStatusRequest): Запрос с новым статусом. database_manager (DatabaseManager): Менеджер базы данных. Returns: - TicketResponse: Обновлённые данные тикета. + dict: Подтверждение обновления статуса. Raises: - HTTPException: 404, если тикет не найден. - HTTPException: 500, если произошла ошибка при обновлении тикета. + HTTPException: Если тикет не найден или произошла ошибка. """ try: - ticket = await database_manager.update_ticket_status(ticket_id=ticket_id, status=status) - if not ticket: - raise HTTPException(status_code=404, detail="Тикет не найден") - return ticket + result = await database_manager.update_ticket_status(ticket_id, request.new_status) + if result != "OK": + return "ERROR" + return "OK" + except ValueError as e: + logger.error(f"Тикет с ID {ticket_id} не найден: {e}") + raise HTTPException(status_code=404, detail="Тикет не найден.") except Exception as e: - handle_exception(e,"Ошибка обновления тикета") + logger.error(f"Ошибка обновления статуса тикета {ticket_id}: {e}") + raise HTTPException(status_code=500, detail="Не удалось обновить статус тикета.") + diff --git a/app/services/db_manager.py b/app/services/db_manager.py index 107401c..a0328be 100644 --- a/app/services/db_manager.py +++ b/app/services/db_manager.py @@ -21,7 +21,11 @@ class DatabaseManager: self.logger = logging.getLogger(__name__) self.mongo_repo = MongoDBRepository() self.postgres_repo = PostgresRepository(session_generator, self.logger) - + async def get_active_tickets(self, user_id: UUID): + """ + Получает активные подписки пользователя + """ + return await self.postgres_repo.list_active_tickets(user_id) async def create_user(self, telegram_id: int): """ Создаёт пользователя. @@ -245,7 +249,11 @@ class DatabaseManager: self.logger.error(f"Ошибка при генерации URI для пользователя {telegram_id}: {e}") return None - + async def get_ticket(self,ticket_id: int): + """ + Ищет тикет по айди + """ + return await self.postgres_repo.get_ticket(ticket_id) async def create_ticket(self, user_id: UUID, subject: str, message: str): """ @@ -253,6 +261,38 @@ class DatabaseManager: """ ticket = SupportTicket(user_id=user_id,subject=subject,message=message) return await self.postgres_repo.add_record(ticket) + + async def add_message_to_ticket(self,ticket_id : int,sender: str,message: str): + """ + Добавляет сообщения к тикету + """ + message = TicketMessage(ticket_id=ticket_id, sender=sender, message=message) + result = await self.postgres_repo.add_record(message) + if result == None: + return "ERROR" + return "OK" + + async def get_ticket_messages(self,ticket_id: int): + """ + Получает сообщения тикета + """ + return await self.postgres_repo.get_ticket_messages(ticket_id) + + async def update_ticket_status(self, ticket_id: int, new_status: str): + """ + Обновляет статус тикета. + + Args: + ticket_id (int): ID тикета, статус которого нужно обновить. + new_status (str): Новый статус тикета. + + Returns: + dict: Словарь с ID тикета и обновлённым статусом. + + Raises: + ValueError: Если тикет не найден. + """ + return await self.postgres_repo.set_new_status(ticket_id,new_status) @staticmethod def generate_string(length): diff --git a/app/services/postgres_rep.py b/app/services/postgres_rep.py index c683c24..0d6cdac 100644 --- a/app/services/postgres_rep.py +++ b/app/services/postgres_rep.py @@ -3,8 +3,8 @@ from uuid import UUID from sqlalchemy.future import select from sqlalchemy.exc import SQLAlchemyError from decimal import Decimal -from sqlalchemy import desc -from instance.model import TicketMessage, User, Subscription, Transaction +from sqlalchemy import asc, desc, update +from instance.model import TicketMessage, User, Subscription, Transaction,SupportTicket class PostgresRepository: @@ -26,6 +26,7 @@ class PostgresRepository: self.logger.error(f"Ошибка при создании пользователя {telegram_id}: {e}") await session.rollback() return None + async def get_active_subscription(self, telegram_id: int): """ Проверяет наличие активной подписки у пользователя. @@ -139,5 +140,82 @@ class PostgresRepository: self.logger.error(f"Ошибка при добавлении записи: {record}: {e}") await session.rollback() return None + + async def list_active_tickets(self, user_id: UUID): + async for session in self.session_generator(): + try: + tickets = await session.execute( + select(SupportTicket) + .where( + SupportTicket.user_id == user_id, + SupportTicket.status.in_([status.upper() for status in ["pending", "open"]]) + ) + ) + result = list(tickets.scalars().all()) + self.logger.info(f"Получены активные тикеты: {result}") + return result + except SQLAlchemyError as e: + self.logger.error(f"Произошла ошибка при поиске активных тикетов: {e}") + return None + async def get_ticket(self, ticket_id): + async for session in self.session_generator(): + try: + ticket = await session.execute( + select(SupportTicket) + .where(SupportTicket.id == ticket_id) + ) + result = ticket.scalars().first() + self.logger.info(f"Получен тикет {ticket_id}.") + if result: + serialized_result = { + "id": result.id, + "user_id": result.user_id, + "subject": result.subject, + "message": result.message, + "status": result.status, + "created_at": result.created_at.isoformat(), + "updated_at": result.updated_at.isoformat(), + } + return serialized_result + except SQLAlchemyError as e: + self.logger.error(f"Произошла ошибка при поиске тикета {ticket_id}.") + return None + + async def get_ticket_messages(self, ticket_id: int): + async for session in self.session_generator(): + try: + # Выполняем запрос для получения сообщений, сортированных по дате + result = await session.execute( + select(TicketMessage) + .where(TicketMessage.ticket_id == ticket_id) + .order_by(asc(TicketMessage.created_at)) + ) + messages = result.scalars().all() + self.logger.info(f"Получены сообщения для тикета {ticket_id}, {messages}") + self.logger.info(messages) + return messages + except SQLAlchemyError as e: + self.logger.error(f"Ошибка при получении сообщений для тикета {ticket_id}: {e}") + return [] + async def set_new_status(self,ticket_id: int, new_status: str): + async for session in self.session_generator(): + try: + # Выполняем обновление тикета + result = await session.execute( + update(SupportTicket) + .where(SupportTicket.id == ticket_id) + .values(status=new_status) + .execution_options(synchronize_session="fetch") + ) + if result.rowcount == 0: + raise ValueError(f"Тикет с ID {ticket_id} не найден.") + + await session.commit() + self.logger.info(f"Статус тикета {ticket_id} обновлён на '{new_status}'.") + return "OK" + except SQLAlchemyError as e: + self.logger.error(f"Ошибка обновления статуса тикета {ticket_id}: {e}") + await session.rollback() + return "ERROR"