Files
remnawave-bedolaga-telegram…/app/database/crud/promocode.py
2025-08-31 22:10:21 +03:00

251 lines
6.6 KiB
Python

import logging
from datetime import datetime
from typing import Optional, List
from sqlalchemy import select, and_, func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.database.models import PromoCode, PromoCodeUse, PromoCodeType, User
logger = logging.getLogger(__name__)
async def get_promocode_by_code(db: AsyncSession, code: str) -> Optional[PromoCode]:
result = await db.execute(
select(PromoCode)
.options(selectinload(PromoCode.uses))
.where(PromoCode.code == code.upper())
)
return result.scalar_one_or_none()
async def create_promocode(
db: AsyncSession,
code: str,
type: PromoCodeType,
balance_bonus_kopeks: int = 0,
subscription_days: int = 0,
max_uses: int = 1,
valid_until: Optional[datetime] = None,
created_by: Optional[int] = None
) -> PromoCode:
promocode = PromoCode(
code=code.upper(),
type=type.value,
balance_bonus_kopeks=balance_bonus_kopeks,
subscription_days=subscription_days,
max_uses=max_uses,
valid_until=valid_until,
created_by=created_by
)
db.add(promocode)
await db.commit()
await db.refresh(promocode)
logger.info(f"✅ Создан промокод: {code}")
return promocode
async def use_promocode(
db: AsyncSession,
promocode_id: int,
user_id: int
) -> bool:
try:
promocode = await db.get(PromoCode, promocode_id)
if not promocode:
return False
usage = PromoCodeUse(
promocode_id=promocode_id,
user_id=user_id
)
db.add(usage)
promocode.current_uses += 1
await db.commit()
logger.info(f"✅ Промокод {promocode.code} использован пользователем {user_id}")
return True
except Exception as e:
logger.error(f"Ошибка использования промокода: {e}")
await db.rollback()
return False
async def check_user_promocode_usage(
db: AsyncSession,
user_id: int,
promocode_id: int
) -> bool:
result = await db.execute(
select(PromoCodeUse).where(
and_(
PromoCodeUse.user_id == user_id,
PromoCodeUse.promocode_id == promocode_id
)
)
)
return result.scalar_one_or_none() is not None
async def create_promocode_use(db: AsyncSession, promocode_id: int, user_id: int) -> PromoCodeUse:
promocode_use = PromoCodeUse(
promocode_id=promocode_id,
user_id=user_id,
used_at=datetime.utcnow()
)
db.add(promocode_use)
await db.commit()
await db.refresh(promocode_use)
logger.info(f"📝 Записано использование промокода {promocode_id} пользователем {user_id}")
return promocode_use
async def get_promocode_use_by_user_and_code(
db: AsyncSession,
user_id: int,
promocode_id: int
) -> Optional[PromoCodeUse]:
result = await db.execute(
select(PromoCodeUse).where(
and_(
PromoCodeUse.user_id == user_id,
PromoCodeUse.promocode_id == promocode_id
)
)
)
return result.scalar_one_or_none()
async def get_user_promocodes(db: AsyncSession, user_id: int) -> List[PromoCodeUse]:
result = await db.execute(
select(PromoCodeUse)
.where(PromoCodeUse.user_id == user_id)
.order_by(PromoCodeUse.used_at.desc())
)
return result.scalars().all()
async def get_promocodes_list(
db: AsyncSession,
offset: int = 0,
limit: int = 50,
is_active: Optional[bool] = None
) -> List[PromoCode]:
query = select(PromoCode).options(selectinload(PromoCode.uses))
if is_active is not None:
query = query.where(PromoCode.is_active == is_active)
query = query.order_by(PromoCode.created_at.desc()).offset(offset).limit(limit)
result = await db.execute(query)
return result.scalars().all()
async def get_promocodes_count(
db: AsyncSession,
is_active: Optional[bool] = None
) -> int:
query = select(func.count(PromoCode.id))
if is_active is not None:
query = query.where(PromoCode.is_active == is_active)
result = await db.execute(query)
return result.scalar()
async def update_promocode(
db: AsyncSession,
promocode: PromoCode,
**kwargs
) -> PromoCode:
for field, value in kwargs.items():
if hasattr(promocode, field):
setattr(promocode, field, value)
promocode.updated_at = datetime.utcnow()
await db.commit()
await db.refresh(promocode)
return promocode
async def delete_promocode(db: AsyncSession, promocode: PromoCode) -> bool:
try:
from sqlalchemy import delete as sql_delete
await db.execute(
sql_delete(PromoCodeUse).where(PromoCodeUse.promocode_id == promocode.id)
)
await db.delete(promocode)
await db.commit()
logger.info(f"🗑️ Удален промокод: {promocode.code}")
return True
except Exception as e:
logger.error(f"Ошибка удаления промокода: {e}")
await db.rollback()
return False
async def get_promocode_statistics(db: AsyncSession, promocode_id: int) -> dict:
total_uses_result = await db.execute(
select(func.count(PromoCodeUse.id))
.where(PromoCodeUse.promocode_id == promocode_id)
)
total_uses = total_uses_result.scalar()
today = datetime.utcnow().date()
today_uses_result = await db.execute(
select(func.count(PromoCodeUse.id))
.where(
and_(
PromoCodeUse.promocode_id == promocode_id,
PromoCodeUse.used_at >= today
)
)
)
today_uses = today_uses_result.scalar()
recent_uses_result = await db.execute(
select(PromoCodeUse, User)
.join(User, PromoCodeUse.user_id == User.id)
.where(PromoCodeUse.promocode_id == promocode_id)
.order_by(PromoCodeUse.used_at.desc())
.limit(10)
)
recent_uses_data = recent_uses_result.all()
recent_uses = []
for use, user in recent_uses_data:
use.user_username = user.username
use.user_full_name = user.full_name
use.user_telegram_id = user.telegram_id
recent_uses.append(use)
return {
"total_uses": total_uses,
"today_uses": today_uses,
"recent_uses": recent_uses
}