177 lines
6.0 KiB
Python
177 lines
6.0 KiB
Python
"""
|
||
QR service for backend API integration
|
||
"""
|
||
|
||
import asyncio
|
||
import logging
|
||
from typing import Dict, List, Optional, Any
|
||
|
||
import aiohttp
|
||
|
||
from config import config
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class QRService:
|
||
"""Service for QR code operations via backend API"""
|
||
|
||
def __init__(self):
|
||
self.api_url = config.backend_api_url
|
||
self.session: Optional[aiohttp.ClientSession] = None
|
||
|
||
async def __aenter__(self):
|
||
"""Async context manager entry"""
|
||
self.session = aiohttp.ClientSession()
|
||
return self
|
||
|
||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||
"""Async context manager exit"""
|
||
if self.session:
|
||
await self.session.close()
|
||
|
||
async def generate_qr_codes(self, qr_type: str, value: str, count: int) -> List[str]:
|
||
"""
|
||
Generate QR codes via backend API
|
||
|
||
Args:
|
||
qr_type: Type of QR code ('reward', 'quiz', 'shop')
|
||
value: Value for the QR code
|
||
count: Number of QR codes to generate
|
||
|
||
Returns:
|
||
List of generated tokens
|
||
"""
|
||
if not self.session:
|
||
raise RuntimeError("QRService must be used as async context manager")
|
||
|
||
url = f"{self.api_url}/api/admin/qrcodes"
|
||
|
||
payload = {
|
||
"type": qr_type,
|
||
"value": value,
|
||
"count": count
|
||
}
|
||
|
||
try:
|
||
async with self.session.post(url, json=payload) as response:
|
||
if response.status == 200:
|
||
data = await response.json()
|
||
if data.get("success"):
|
||
return data.get("data", {}).get("tokens", [])
|
||
else:
|
||
logger.error(f"Backend API error: {data.get('message')}")
|
||
raise Exception(data.get("message", "Unknown error"))
|
||
else:
|
||
error_text = await response.text()
|
||
logger.error(f"Backend API request failed: {response.status} - {error_text}")
|
||
raise Exception(f"API request failed with status {response.status}")
|
||
|
||
except aiohttp.ClientError as e:
|
||
logger.error(f"Network error: {e}")
|
||
raise Exception(f"Network error: {e}")
|
||
|
||
async def validate_qr_token(self, token: str) -> Dict[str, Any]:
|
||
"""
|
||
Validate QR token via backend API
|
||
|
||
Args:
|
||
token: QR token to validate
|
||
|
||
Returns:
|
||
Validation result from backend
|
||
"""
|
||
if not self.session:
|
||
raise RuntimeError("QRService must be used as async context manager")
|
||
|
||
url = f"{self.api_url}/api/qr/validate"
|
||
|
||
payload = {
|
||
"payload": token
|
||
}
|
||
|
||
try:
|
||
async with self.session.post(url, json=payload) as response:
|
||
if response.status == 200:
|
||
data = await response.json()
|
||
if data.get("success"):
|
||
return data.get("data", {})
|
||
else:
|
||
logger.error(f"Validation failed: {data.get('message')}")
|
||
raise Exception(data.get("message", "Validation failed"))
|
||
else:
|
||
error_text = await response.text()
|
||
logger.error(f"Validation request failed: {response.status} - {error_text}")
|
||
raise Exception(f"Validation failed with status {response.status}")
|
||
|
||
except aiohttp.ClientError as e:
|
||
logger.error(f"Network error during validation: {e}")
|
||
raise Exception(f"Network error: {e}")
|
||
|
||
def format_qr_examples(self, tokens: List[str], qr_type: str, description: str) -> str:
|
||
"""
|
||
Format QR code examples for display
|
||
|
||
Args:
|
||
tokens: List of QR tokens
|
||
qr_type: Type of QR codes
|
||
description: Description of the QR codes
|
||
|
||
Returns:
|
||
Formatted message with QR examples
|
||
"""
|
||
message = f"🎫 {description}\n\n"
|
||
message += "📱 QR-коды содержат следующие токены:\n\n"
|
||
|
||
for i, token in enumerate(tokens, 1):
|
||
message += f"{i}. `{token}`\n"
|
||
|
||
message += f"\n⚡ Всего сгенерировано: {len(tokens)} шт."
|
||
message += f"\n📅 Срок действия: 30 дней"
|
||
message += f"\n🔒 Одноразовое использование"
|
||
|
||
return message
|
||
|
||
def validate_qr_request(self, qr_type: str, value: str, count: int) -> tuple[bool, str]:
|
||
"""
|
||
Validate QR generation request parameters
|
||
|
||
Args:
|
||
qr_type: Type of QR code
|
||
value: Value for the QR code
|
||
count: Number of QR codes to generate
|
||
|
||
Returns:
|
||
Tuple of (is_valid, error_message)
|
||
"""
|
||
if not qr_type:
|
||
return False, "Тип QR-кода не может быть пустым"
|
||
|
||
if not value:
|
||
return False, "Значение QR-кода не может быть пустым"
|
||
|
||
if count <= 0 or count > 100:
|
||
return False, "Количество должно быть от 1 до 100"
|
||
|
||
valid_types = ["reward", "quiz", "shop"]
|
||
if qr_type not in valid_types:
|
||
return False, f"Недопустимый тип. Допустимые: {', '.join(valid_types)}"
|
||
|
||
# Additional validation based on type
|
||
if qr_type == "reward":
|
||
try:
|
||
amount = int(value)
|
||
if amount <= 0 or amount > 1000:
|
||
return False, "Сумма награды должна быть от 1 до 1000"
|
||
except ValueError:
|
||
return False, "Сумма награды должна быть числом"
|
||
|
||
elif qr_type == "quiz":
|
||
try:
|
||
quiz_id = int(value)
|
||
if quiz_id <= 0:
|
||
return False, "ID викторины должен быть положительным числом"
|
||
except ValueError:
|
||
return False, "ID викторины должен быть числом"
|
||
|
||
return True, "" |