mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-19 19:32:10 +00:00
fix Реализация корзины пользователя! запоминает настройки подписки после пополнения баланса
This commit is contained in:
@@ -79,7 +79,8 @@ class Settings(BaseSettings):
|
||||
DATABASE_MODE: str = "auto"
|
||||
|
||||
REDIS_URL: str = "redis://localhost:6379/0"
|
||||
|
||||
CART_TTL_SECONDS: int = 3600 # Время жизни корзины пользователя в Redis (1 час)
|
||||
|
||||
REMNAWAVE_API_URL: Optional[str] = None
|
||||
REMNAWAVE_API_KEY: Optional[str] = None
|
||||
REMNAWAVE_SECRET_KEY: Optional[str] = None
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import timedelta
|
||||
|
||||
import redis.asyncio as redis
|
||||
|
||||
@@ -9,104 +8,136 @@ from app.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserCartService:
|
||||
"""
|
||||
Сервис для работы с корзиной пользователя через Redis
|
||||
Сервис для работы с корзиной пользователя через Redis.
|
||||
|
||||
Использует ленивую инициализацию Redis-клиента для graceful fallback
|
||||
при недоступности Redis.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.redis_client = None
|
||||
self._setup_redis()
|
||||
|
||||
def _setup_redis(self):
|
||||
"""Инициализация Redis клиента"""
|
||||
self._redis_client: Optional[redis.Redis] = None
|
||||
self._initialized: bool = False
|
||||
|
||||
def _get_redis_client(self) -> Optional[redis.Redis]:
|
||||
"""Ленивая инициализация Redis клиента."""
|
||||
if self._initialized:
|
||||
return self._redis_client
|
||||
|
||||
try:
|
||||
self.redis_client = redis.from_url(settings.REDIS_URL)
|
||||
self._redis_client = redis.from_url(settings.REDIS_URL)
|
||||
self._initialized = True
|
||||
logger.debug("Redis клиент для корзины инициализирован")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка подключения к Redis: {e}")
|
||||
raise
|
||||
|
||||
async def save_user_cart(self, user_id: int, cart_data: Dict[str, Any], ttl: int = 3600) -> bool:
|
||||
logger.warning(f"Не удалось подключиться к Redis для корзины: {e}")
|
||||
self._redis_client = None
|
||||
self._initialized = True
|
||||
|
||||
return self._redis_client
|
||||
|
||||
async def save_user_cart(
|
||||
self, user_id: int, cart_data: Dict[str, Any], ttl: Optional[int] = None
|
||||
) -> bool:
|
||||
"""
|
||||
Сохранить корзину пользователя в Redis
|
||||
|
||||
Сохранить корзину пользователя в Redis.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя
|
||||
cart_data: Данные корзины (параметры подписки)
|
||||
ttl: Время жизни ключа в секундах (по умолчанию 1 час)
|
||||
|
||||
ttl: Время жизни ключа в секундах (по умолчанию из settings.CART_TTL_SECONDS)
|
||||
|
||||
Returns:
|
||||
bool: Успешность сохранения
|
||||
"""
|
||||
client = self._get_redis_client()
|
||||
if client is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
key = f"user_cart:{user_id}"
|
||||
json_data = json.dumps(cart_data, ensure_ascii=False)
|
||||
await self.redis_client.setex(key, ttl, json_data)
|
||||
logger.info(f"Корзина пользователя {user_id} сохранена в Redis")
|
||||
effective_ttl = ttl if ttl is not None else settings.CART_TTL_SECONDS
|
||||
await client.setex(key, effective_ttl, json_data)
|
||||
logger.debug(f"Корзина пользователя {user_id} сохранена в Redis")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка сохранения корзины пользователя {user_id}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def get_user_cart(self, user_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Получить корзину пользователя из Redis
|
||||
|
||||
Получить корзину пользователя из Redis.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя
|
||||
|
||||
|
||||
Returns:
|
||||
dict: Данные корзины или None
|
||||
"""
|
||||
client = self._get_redis_client()
|
||||
if client is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
key = f"user_cart:{user_id}"
|
||||
json_data = await self.redis_client.get(key)
|
||||
json_data = await client.get(key)
|
||||
if json_data:
|
||||
cart_data = json.loads(json_data)
|
||||
logger.info(f"Корзина пользователя {user_id} загружена из Redis")
|
||||
logger.debug(f"Корзина пользователя {user_id} загружена из Redis")
|
||||
return cart_data
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка получения корзины пользователя {user_id}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def delete_user_cart(self, user_id: int) -> bool:
|
||||
"""
|
||||
Удалить корзину пользователя из Redis
|
||||
|
||||
Удалить корзину пользователя из Redis.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя
|
||||
|
||||
|
||||
Returns:
|
||||
bool: Успешность удаления
|
||||
"""
|
||||
client = self._get_redis_client()
|
||||
if client is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
key = f"user_cart:{user_id}"
|
||||
result = await self.redis_client.delete(key)
|
||||
result = await client.delete(key)
|
||||
if result:
|
||||
logger.info(f"Корзина пользователя {user_id} удалена из Redis")
|
||||
logger.debug(f"Корзина пользователя {user_id} удалена из Redis")
|
||||
return bool(result)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка удаления корзины пользователя {user_id}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def has_user_cart(self, user_id: int) -> bool:
|
||||
"""
|
||||
Проверить наличие корзины у пользователя
|
||||
|
||||
Проверить наличие корзины у пользователя.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя
|
||||
|
||||
|
||||
Returns:
|
||||
bool: Наличие корзины
|
||||
"""
|
||||
client = self._get_redis_client()
|
||||
if client is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
key = f"user_cart:{user_id}"
|
||||
exists = await self.redis_client.exists(key)
|
||||
exists = await client.exists(key)
|
||||
return bool(exists)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка проверки наличия корзины пользователя {user_id}: {e}")
|
||||
return False
|
||||
|
||||
# Глобальный экземпляр сервиса
|
||||
user_cart_service = UserCartService()
|
||||
|
||||
# Глобальный экземпляр сервиса (инициализация Redis отложена)
|
||||
user_cart_service = UserCartService()
|
||||
|
||||
@@ -31,7 +31,8 @@ def mock_redis():
|
||||
@pytest.fixture
|
||||
def user_cart_service(mock_redis):
|
||||
service = UserCartService()
|
||||
service.redis_client = mock_redis
|
||||
service._redis_client = mock_redis
|
||||
service._initialized = True
|
||||
return service
|
||||
|
||||
async def test_save_user_cart(user_cart_service, mock_redis):
|
||||
|
||||
Reference in New Issue
Block a user