Переделаны часть обработчиков и роутов для работы с тикетами.
This commit is contained in:
@@ -4,11 +4,16 @@ from app.services.db_manager import DatabaseManager
|
|||||||
from instance.configdb import get_database_manager
|
from instance.configdb import get_database_manager
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from fastapi import Response, status
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List, Literal, Optional
|
from typing import List, Literal, Optional
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class UpdateTicketStatusRequest(BaseModel):
|
||||||
|
new_status: str
|
||||||
|
|
||||||
class TicketStatus(str, Enum):
|
class TicketStatus(str, Enum):
|
||||||
OPEN = "open"
|
OPEN = "open"
|
||||||
PENDING = "pending"
|
PENDING = "pending"
|
||||||
@@ -31,7 +36,6 @@ class TicketMessageRequest(BaseModel):
|
|||||||
message: str
|
message: str
|
||||||
|
|
||||||
class TicketMessageResponse(BaseModel):
|
class TicketMessageResponse(BaseModel):
|
||||||
id: int
|
|
||||||
ticket_id: int
|
ticket_id: int
|
||||||
sender: str
|
sender: str
|
||||||
message: str
|
message: str
|
||||||
@@ -43,7 +47,7 @@ def handle_exception(e: Exception, message: str):
|
|||||||
logger.error(f"{message}: {e}")
|
logger.error(f"{message}: {e}")
|
||||||
raise HTTPException(status_code=500, detail=f"{message}: {str(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(
|
async def add_message(
|
||||||
ticket_id: int,
|
ticket_id: int,
|
||||||
request: TicketMessageRequest,
|
request: TicketMessageRequest,
|
||||||
@@ -63,10 +67,12 @@ async def add_message(
|
|||||||
TicketMessageResponse: Данные созданного сообщения.
|
TicketMessageResponse: Данные созданного сообщения.
|
||||||
"""
|
"""
|
||||||
try:
|
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:
|
if not message:
|
||||||
raise HTTPException(status_code=404, detail="Тикет не найден или ошибка добавления сообщения")
|
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:
|
except Exception as e:
|
||||||
handle_exception(e,"Ошибка добавления сообщения")
|
handle_exception(e,"Ошибка добавления сообщения")
|
||||||
|
|
||||||
@@ -91,13 +97,67 @@ async def get_messages(
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
messages = await database_manager.get_ticket_messages(ticket_id=ticket_id)
|
messages = await database_manager.get_ticket_messages(ticket_id=ticket_id)
|
||||||
if not messages:
|
ticket_info = await database_manager.get_ticket(ticket_id)
|
||||||
raise HTTPException(status_code=404, detail="Сообщения для тикета не найдены")
|
logger.info(messages)
|
||||||
return 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:
|
except Exception as e:
|
||||||
handle_exception(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="Создать тикет")
|
@router.post("/support/tickets", response_model=TicketResponse, summary="Создать тикет")
|
||||||
async def create_ticket(
|
async def create_ticket(
|
||||||
request: CreateTicketRequest,
|
request: CreateTicketRequest,
|
||||||
@@ -148,38 +208,42 @@ async def list_tickets(
|
|||||||
HTTPException: 500, если произошла ошибка на сервере.
|
HTTPException: 500, если произошла ошибка на сервере.
|
||||||
"""
|
"""
|
||||||
try:
|
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:
|
if not tickets:
|
||||||
raise HTTPException(status_code=404, detail="Тикеты не найдены")
|
raise HTTPException(status_code=404, detail="Тикеты не найдены")
|
||||||
return tickets
|
return tickets
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
handle_exception(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(
|
async def update_ticket_status(
|
||||||
ticket_id: int,
|
ticket_id: int,
|
||||||
status: TicketStatus,
|
request: UpdateTicketStatusRequest,
|
||||||
database_manager: DatabaseManager = Depends(get_database_manager)
|
database_manager: DatabaseManager = Depends(get_database_manager)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Обновляет статус тикета.
|
Обновляет статус тикета.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ticket_id (int): ID тикета, статус которого нужно обновить.
|
ticket_id (int): ID тикета.
|
||||||
status (TicketStatus): Новый статус тикета (open, pending, closed).
|
request (UpdateTicketStatusRequest): Запрос с новым статусом.
|
||||||
database_manager (DatabaseManager): Менеджер базы данных.
|
database_manager (DatabaseManager): Менеджер базы данных.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TicketResponse: Обновлённые данные тикета.
|
dict: Подтверждение обновления статуса.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
HTTPException: 404, если тикет не найден.
|
HTTPException: Если тикет не найден или произошла ошибка.
|
||||||
HTTPException: 500, если произошла ошибка при обновлении тикета.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
ticket = await database_manager.update_ticket_status(ticket_id=ticket_id, status=status)
|
result = await database_manager.update_ticket_status(ticket_id, request.new_status)
|
||||||
if not ticket:
|
if result != "OK":
|
||||||
raise HTTPException(status_code=404, detail="Тикет не найден")
|
return "ERROR"
|
||||||
return ticket
|
return "OK"
|
||||||
|
except ValueError as e:
|
||||||
|
logger.error(f"Тикет с ID {ticket_id} не найден: {e}")
|
||||||
|
raise HTTPException(status_code=404, detail="Тикет не найден.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
handle_exception(e,"Ошибка обновления тикета")
|
logger.error(f"Ошибка обновления статуса тикета {ticket_id}: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail="Не удалось обновить статус тикета.")
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ class DatabaseManager:
|
|||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.mongo_repo = MongoDBRepository()
|
self.mongo_repo = MongoDBRepository()
|
||||||
self.postgres_repo = PostgresRepository(session_generator, self.logger)
|
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):
|
async def create_user(self, telegram_id: int):
|
||||||
"""
|
"""
|
||||||
Создаёт пользователя.
|
Создаёт пользователя.
|
||||||
@@ -245,7 +249,11 @@ class DatabaseManager:
|
|||||||
self.logger.error(f"Ошибка при генерации URI для пользователя {telegram_id}: {e}")
|
self.logger.error(f"Ошибка при генерации URI для пользователя {telegram_id}: {e}")
|
||||||
return None
|
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):
|
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)
|
ticket = SupportTicket(user_id=user_id,subject=subject,message=message)
|
||||||
return await self.postgres_repo.add_record(ticket)
|
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
|
@staticmethod
|
||||||
def generate_string(length):
|
def generate_string(length):
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ from uuid import UUID
|
|||||||
from sqlalchemy.future import select
|
from sqlalchemy.future import select
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import asc, desc, update
|
||||||
from instance.model import TicketMessage, User, Subscription, Transaction
|
from instance.model import TicketMessage, User, Subscription, Transaction,SupportTicket
|
||||||
|
|
||||||
|
|
||||||
class PostgresRepository:
|
class PostgresRepository:
|
||||||
@@ -26,6 +26,7 @@ class PostgresRepository:
|
|||||||
self.logger.error(f"Ошибка при создании пользователя {telegram_id}: {e}")
|
self.logger.error(f"Ошибка при создании пользователя {telegram_id}: {e}")
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_active_subscription(self, telegram_id: int):
|
async def get_active_subscription(self, telegram_id: int):
|
||||||
"""
|
"""
|
||||||
Проверяет наличие активной подписки у пользователя.
|
Проверяет наличие активной подписки у пользователя.
|
||||||
@@ -139,5 +140,82 @@ class PostgresRepository:
|
|||||||
self.logger.error(f"Ошибка при добавлении записи: {record}: {e}")
|
self.logger.error(f"Ошибка при добавлении записи: {record}: {e}")
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
return None
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user