Переделал модель БД под новую, переделал Репозиторий, переделал сервисы, убрал монгодб, изменил необходимые пакеты, марзбан я добавил, но не настроил. Весь старый бот вроде работает(только в рефералке не уверен)

This commit is contained in:
root
2025-11-24 23:43:40 +03:00
parent f0f3b96005
commit e975bf4774
18 changed files with 637 additions and 1217 deletions

View File

@@ -1,6 +1,5 @@
#from .payment_routes import router as payment_router
from .user_routes import router
from .subscription_routes import router as subscription_router
from .support_routes import router as sup_router
# Экспорт всех маршрутов
__all__ = [ "router", "subscription_router","sup_router"]
__all__ = [ "router", "subscription_router"]

View File

@@ -1,65 +0,0 @@
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from yookassa import Payment
from yookassa.domain.notification import WebhookNotification
from app.services.db_manager import DatabaseManager
router = APIRouter()
class CreatePaymentRequest(BaseModel):
telegram_id: str
amount: float
class PaymentResponse(BaseModel):
payment_url: str
payment_id: str
@router.post("/payment/create", response_model=PaymentResponse)
async def create_payment(request: CreatePaymentRequest):
"""
Создаёт платёж через ЮKassa и возвращает ссылку для оплаты.
"""
try:
# Создание платежа через API ЮKassa
payment = Payment.create({
"amount": {
"value": f"{request.amount:.2f}",
"currency": "RUB"
},
"confirmation": {
"type": "redirect", # Тип подтверждения (redirect или embedded)
"return_url": "https://your-app.com/success" # URL возврата
},
"description": f"Пополнение баланса для пользователя {request.telegram_id}"
})
# Возвращаем ссылку для оплаты и ID платежа
return PaymentResponse(
payment_url=payment.confirmation.confirmation_url,
payment_id=payment.id
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Ошибка создания платежа: {str(e)}")
@router.post("/payment/notification")
async def payment_notification(notification: dict, database_manager: DatabaseManager):
"""
Обрабатывает уведомления от ЮKassa.
"""
try:
# Парсим уведомление
webhook = WebhookNotification(notification)
payment_object = webhook.object
# Проверяем статус платежа
if payment_object["status"] == "succeeded":
# Обновляем баланс пользователя
telegram_id = int(payment_object["description"].split()[-1])
amount = float(payment_object["amount"]["value"])
await database_manager.update_balance(telegram_id, amount)
return {"status": "ok"}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Ошибка обработки уведомления: {str(e)}")

View File

@@ -1,7 +1,7 @@
from typing import List
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from app.services.db_manager import DatabaseManager
from app.services import DatabaseManager
from instance.configdb import get_database_manager
from uuid import UUID
import logging
@@ -14,7 +14,7 @@ router = APIRouter()
class BuySubscriptionRequest(BaseModel):
telegram_id: str
telegram_id: int
plan_id: str
class SubscriptionResponse(BaseModel):
@@ -56,17 +56,17 @@ async def buy_subscription(
# Эндпоинт для получения последней подписки
@router.get("/subscription/{user_id}/last", response_model=SubscriptionResponse)
async def last_subscription(user_id: UUID, database_manager: DatabaseManager = Depends(get_database_manager)):
@router.get("/subscription/{telegram_id}/last", response_model=SubscriptionResponse)
async def last_subscription(telegram_id: int, database_manager: DatabaseManager = Depends(get_database_manager)):
"""
Возвращает последнюю подписку пользователя.
"""
logger.info(f"Получение последней подписки для пользователя: {user_id}")
logger.info(f"Получение последней подписки для пользователя: {telegram_id}")
try:
subscriptions = await database_manager.get_last_subscriptions(user_id=user_id, limit=1)
subscriptions = await database_manager.get_last_subscriptions(telegram_id=telegram_id, limit=1)
if not subscriptions:
logger.warning(f"Подписки для пользователя {user_id} не найдены")
logger.warning(f"Подписки для пользователя {telegram_id} не найдены")
raise HTTPException(status_code=404, detail="No subscriptions found")
sub = subscriptions[0]
@@ -80,7 +80,7 @@ async def last_subscription(user_id: UUID, database_manager: DatabaseManager = D
"updated_at": sub.updated_at.isoformat(),
}
except SQLAlchemyError as e:
logger.error(f"Ошибка базы данных при получении подписки для пользователя {user_id}: {e}")
logger.error(f"Ошибка базы данных при получении подписки для пользователя {telegram_id}: {e}")
raise HTTPException(status_code=500, detail="Database error")
except HTTPException as e:
# Пропускаем HTTPException, чтобы FastAPI обработал её автоматически
@@ -89,18 +89,18 @@ async def last_subscription(user_id: UUID, database_manager: DatabaseManager = D
logger.error(f"Неожиданная ошибка: {e}")
raise HTTPException(status_code=500, detail="Internal Server Error")
@router.get("/subscriptions/{user_id}", response_model=List[SubscriptionResponse])
async def get_subscriptions(user_id: UUID, database_manager: DatabaseManager = Depends(get_database_manager)):
@router.get("/subscriptions/{telegram_id}", response_model=List[SubscriptionResponse])
async def get_subscriptions(telegram_id: int, database_manager: DatabaseManager = Depends(get_database_manager)):
"""
Возвращает список подписок пользователя.
"""
logger.info(f"Получение подписок для пользователя: {user_id}")
logger.info(f"Получение подписок для пользователя: {telegram_id}")
try:
# Получаем подписки без ограничений или с указанным лимитом
subscriptions = await database_manager.last_subscriptions(user_id=str(user_id))
subscriptions = await database_manager.get_last_subscriptions(telegram_id=telegram_id)
if not subscriptions:
logger.warning(f"Подписки для пользователя {user_id} не найдены")
logger.warning(f"Подписки для пользователя {telegram_id} не найдены")
raise HTTPException(status_code=404, detail="No subscriptions found")
# Формируем список подписок для ответа
@@ -116,17 +116,15 @@ async def get_subscriptions(user_id: UUID, database_manager: DatabaseManager = D
for sub in subscriptions
]
except SQLAlchemyError as e:
logger.error(f"Ошибка базы данных при получении подписок для пользователя {user_id}: {e}")
logger.error(f"Ошибка базы данных при получении подписок для пользователя {telegram_id}: {e}")
raise HTTPException(status_code=500, detail="Database error")
except Exception as e:
logger.error(f"Неожиданная ошибка: {e}")
raise HTTPException(status_code=500, detail=str(e))
except HTTPException as e:
# Пропускаем HTTPException, чтобы FastAPI обработал её автоматически
raise e
@router.get("/uri", response_model=dict)
async def get_uri(telegram_id: str, database_manager: DatabaseManager = Depends(get_database_manager)):
async def get_uri(telegram_id: int, database_manager: DatabaseManager = Depends(get_database_manager)):
"""
Возвращает список подписок пользователя.
"""

View File

@@ -1,249 +0,0 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import SQLAlchemyError
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"
CLOSED = "closed"
class CreateTicketRequest(BaseModel):
subject: str
message: str
class TicketResponse(BaseModel):
id: int
user_id: UUID
subject: str
message: str
status: TicketStatus
created_at: datetime
updated_at: datetime
class TicketMessageRequest(BaseModel):
message: str
class TicketMessageResponse(BaseModel):
ticket_id: int
sender: str
message: str
created_at: datetime
router = APIRouter()
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", summary="Добавить сообщение")
async def add_message(
ticket_id: int,
request: TicketMessageRequest,
sender: Literal["user", "support"], # "user" или "support"
database_manager: DatabaseManager = Depends(get_database_manager)
):
"""
Добавляет сообщение в тикет.
Args:
ticket_id (int): ID тикета.
request (TicketMessageRequest): Данные сообщения.
sender (str): Отправитель ("user" или "support").
database_manager (DatabaseManager): Управление базой данных.
Returns:
TicketMessageResponse: Данные созданного сообщения.
"""
try:
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="Тикет не найден или ошибка добавления сообщения")
if message != "OK":
raise HTTPException(status_code=500, detail="Ошибка добавления сообщения")
return Response(status_code=status.HTTP_200_OK)
except Exception as e:
handle_exception(e,"Ошибка добавления сообщения")
@router.get("/support/tickets/{ticket_id}/messages", response_model=List[TicketMessageResponse], summary="Получить сообщения")
async def get_messages(
ticket_id: int,
database_manager: DatabaseManager = Depends(get_database_manager)
):
"""
Возвращает список сообщений в тикете.
Args:
ticket_id (int): ID тикета, для которого нужно получить сообщения.
database_manager (DatabaseManager): Менеджер базы данных.
Returns:
List[TicketMessageResponse]: Список сообщений, связанных с тикетом.
Raises:
HTTPException: 404, если сообщения для тикета не найдены.
HTTPException: 500, если произошла ошибка на сервере.
"""
try:
messages = await database_manager.get_ticket_messages(ticket_id=ticket_id)
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,
user_id: UUID,
database_manager: DatabaseManager = Depends(get_database_manager)
):
"""
Создаёт новый тикет для пользователя.
Args:
request (CreateTicketRequest): Данные для создания тикета (тема и сообщение).
user_id (UUID): Идентификатор пользователя, создающего тикет.
database_manager (DatabaseManager): Менеджер базы данных.
Returns:
TicketResponse: Данные созданного тикета.
Raises:
HTTPException: 500, если произошла ошибка при создании тикета.
"""
try:
ticket = await database_manager.create_ticket(
user_id=user_id,
subject=request.subject,
message=request.message
)
return ticket
except Exception as e:
handle_exception(e,"Ошибка содания тикета")
@router.get("/support/tickets", response_model=List[TicketResponse], summary="Получить список тикетов")
async def list_tickets(
user_id: UUID,
database_manager: DatabaseManager = Depends(get_database_manager)
):
"""
Возвращает список тикетов пользователя.
Args:
user_id (UUID): Идентификатор пользователя, чьи тикеты нужно получить.
database_manager (DatabaseManager): Менеджер базы данных.
Returns:
List[TicketResponse]: Список тикетов пользователя.
Raises:
HTTPException: 404, если тикеты не найдены.
HTTPException: 500, если произошла ошибка на сервере.
"""
try:
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/ticket/{ticket_id}/status", summary="Обновить статус тикета")
async def update_ticket_status(
ticket_id: int,
request: UpdateTicketStatusRequest,
database_manager: DatabaseManager = Depends(get_database_manager)
):
"""
Обновляет статус тикета.
Args:
ticket_id (int): ID тикета.
request (UpdateTicketStatusRequest): Запрос с новым статусом.
database_manager (DatabaseManager): Менеджер базы данных.
Returns:
dict: Подтверждение обновления статуса.
Raises:
HTTPException: Если тикет не найден или произошла ошибка.
"""
try:
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:
logger.error(f"Ошибка обновления статуса тикета {ticket_id}: {e}")
raise HTTPException(status_code=500, detail="Не удалось обновить статус тикета.")

View File

@@ -1,7 +1,7 @@
import sys
from fastapi import APIRouter, Depends, HTTPException
from fastapi.exceptions import HTTPException
from app.services.db_manager import DatabaseManager
from app.services import DatabaseManager
from sqlalchemy.exc import SQLAlchemyError
from instance.configdb import get_database_manager
from pydantic import BaseModel
@@ -20,20 +20,19 @@ router = APIRouter()
# Модели запросов и ответов
class CreateUserRequest(BaseModel):
telegram_id: str
referrer_id: Optional[str] = None
telegram_id: int
invited_by: Optional[int] = None
class UserResponse(BaseModel):
id: UUID
telegram_id: str
telegram_id: int
username: Optional[str]
balance: float
referrer_id: Optional[str]
invited_by: Optional[int] = None
created_at: str
updated_at: str
class AddReferal(BaseModel):
new_user_id: str
invited_id: int
@router.post("/user/create", response_model=UserResponse, summary="Создать пользователя")
async def create_user(
@@ -44,16 +43,15 @@ async def create_user(
Создание пользователя через Telegram ID.
"""
try:
user = await db_manager.create_user(request.telegram_id,request.referrer_id)
if user == "ERROR":
user = await db_manager.create_user(request.telegram_id,request.invited_by)
if user == None:
raise HTTPException(status_code=500, detail="Failed to create user")
return UserResponse(
id=user.id,
telegram_id=user.telegram_id,
username=user.username,
balance=user.balance,
referrer_id=user.referrer_id if user.referrer_id is not None else None,
invited_by=user.invited_by if user.invited_by is not None else None,
created_at=user.created_at.isoformat(),
updated_at=user.updated_at.isoformat()
)
@@ -64,7 +62,7 @@ async def create_user(
@router.get("/user/{telegram_id}", response_model=UserResponse, summary="Получить информацию о пользователе")
async def get_user(
telegram_id: str,
telegram_id: int,
db_manager: DatabaseManager = Depends(get_database_manager)
):
"""
@@ -77,13 +75,12 @@ async def get_user(
logger.warning(f"Пользователь с telegram_id {telegram_id} не найден.")
raise HTTPException(status_code=404, detail="User not found")
print(f"Пользователь найден: ID={user.id}, Username={user.username}")
print(f"Пользователь найден: ID={user.telegram_id}, Username={user.username}")
user_response = UserResponse(
id=user.id,
telegram_id=user.telegram_id,
username=user.username,
balance=user.balance,
referrer_id=user.referrer_id if user.referrer_id is not None else None,
invited_by=user.invited_by if user.invited_by is not None else None,
created_at=user.created_at.isoformat(),
updated_at=user.updated_at.isoformat()
)
@@ -105,7 +102,7 @@ async def get_user(
@router.post("/user/{telegram_id}/balance/{amount}", summary="Обновить баланс")
async def update_balance(
telegram_id: str,
telegram_id: int,
amount: float,
db_manager: DatabaseManager = Depends(get_database_manager)
):
@@ -132,52 +129,55 @@ async def update_balance(
raise HTTPException(status_code=500, detail=str(e))
@router.get("/user/{user_id}/transactions", summary="Последние транзакции пользователя")
@router.get("/user/{telegram_id}/transactions", summary="Последние транзакции пользователя")
async def last_transactions(
user_id: UUID,
telegram_id: int,
db_manager: DatabaseManager = Depends(get_database_manager)
):
"""
Возвращает список последних транзакций пользователя.
"""
logger.info(f"Получен запрос на транзакции для пользователя: {user_id}")
logger.info(f"Получен запрос на транзакции для пользователя: {telegram_id}")
try:
logger.debug(f"Вызов метода get_transaction с user_id={user_id}")
transactions = await db_manager.get_transaction(user_id)
logger.debug(f"Вызов метода get_transaction с user_id={telegram_id}")
transactions = await db_manager.get_transaction(telegram_id)
if transactions == "ERROR":
logger.error(f"Ошибка при получении транзакций для пользователя: {user_id}")
logger.error(f"Ошибка при получении транзакций для пользователя: {telegram_id}")
raise HTTPException(status_code=500, detail="Failed to fetch transactions")
logger.debug(f"Транзакции для {user_id}: {transactions}")
if transactions == None:
response = []
logger.info(f"Формирование ответа для пользователя {telegram_id}: {response}")
return response
logger.debug(f"Транзакции для {telegram_id}: {transactions}")
response = [
{
"id": tx.id,
"amount": tx.amount,
"created_at": tx.created_at.isoformat(),
"transaction_type": tx.transaction_type,
"type": tx.type,
} for tx in transactions
]
logger.info(f"Формирование ответа для пользователя {user_id}: {response}")
logger.info(f"Формирование ответа для пользователя {telegram_id}: {response}")
return response
except HTTPException as http_ex:
logger.warning(f"HTTP ошибка для {user_id}: {http_ex.detail}")
logger.warning(f"HTTP ошибка для {telegram_id}: {http_ex.detail}")
raise http_ex
except SQLAlchemyError as db_ex:
logger.error(f"Ошибка базы данных для {user_id}: {db_ex}")
logger.error(f"Ошибка базы данных для {telegram_id}: {db_ex}")
raise HTTPException(status_code=500, detail="Database error")
except Exception as e:
logger.exception(f"Неожиданная ошибка для {user_id}: {e}")
logger.exception(f"Неожиданная ошибка для {telegram_id}: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.post("/user/{referrer_id}/add_referral", summary="Обновить баланс")
async def add_referal(
referrer_id: str,
referrer_id: int,
request: AddReferal,
db_manager: DatabaseManager = Depends(get_database_manager)
):
@@ -186,12 +186,12 @@ async def add_referal(
"""
logger.info(f"Получен запрос на добавление реферала: telegram_id={referrer_id}")
try:
result = await db_manager.add_referal(referrer_id)
result = await db_manager.add_referal(referrer_id,request.invited_id)
if result == "ERROR":
logger.error(f"Ошибка добавления реферала для {referrer_id} c айди {request.new_user_id}")
logger.error(f"Ошибка добавления реферала для {referrer_id} c айди {request.invited_id}")
raise HTTPException(status_code=500, detail="Failed to update balance")
logger.info(f"Добавлен реферал для {referrer_id} c айди {request.new_user_id}")
logger.info(f"Добавлен реферал для {referrer_id} c айди {request.invited_id}")
return {"message": "Balance updated successfully"}
except HTTPException as http_ex:
logger.warning(f"HTTP ошибка: {http_ex.detail}")