Files
remnawave-bedolaga-telegram…/app/cabinet/routes/referral.py
T
c0mrade 9a2aea038a chore: add uv package manager and ruff linter configuration
- Add pyproject.toml with uv and ruff configuration
- Pin Python version to 3.13 via .python-version
- Add Makefile commands: lint, format, fix
- Apply ruff formatting to entire codebase
- Remove unused imports (base64 in yookassa/simple_subscription)
- Update .gitignore for new config files
2026-01-24 17:45:27 +03:00

198 lines
6.8 KiB
Python

"""Referral program routes for cabinet."""
import logging
import math
from fastapi import APIRouter, Depends, Query
from sqlalchemy import desc, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.config import settings
from app.database.models import ReferralEarning, User
from ..dependencies import get_cabinet_db, get_current_cabinet_user
from ..schemas.referral import (
ReferralEarningResponse,
ReferralEarningsListResponse,
ReferralInfoResponse,
ReferralItemResponse,
ReferralListResponse,
ReferralTermsResponse,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix='/referral', tags=['Cabinet Referral'])
@router.get('', response_model=ReferralInfoResponse)
async def get_referral_info(
user: User = Depends(get_current_cabinet_user),
db: AsyncSession = Depends(get_cabinet_db),
):
"""Get referral program info for current user."""
# Get total referrals count
total_query = select(func.count()).select_from(User).where(User.referred_by_id == user.id)
total_result = await db.execute(total_query)
total_referrals = total_result.scalar() or 0
# Get active referrals (with subscription)
active_query = (
select(func.count())
.select_from(User)
.where(User.referred_by_id == user.id)
.where(User.has_had_paid_subscription == True)
)
active_result = await db.execute(active_query)
active_referrals = active_result.scalar() or 0
# Get total earnings
earnings_query = select(func.coalesce(func.sum(ReferralEarning.amount_kopeks), 0)).where(
ReferralEarning.user_id == user.id
)
earnings_result = await db.execute(earnings_query)
total_earnings = earnings_result.scalar() or 0
# Get user's commission percent
commission_percent = user.referral_commission_percent
if commission_percent is None:
commission_percent = settings.REFERRAL_COMMISSION_PERCENT
# Build referral link
bot_username = settings.get_bot_username() or 'bot'
referral_link = f'https://t.me/{bot_username}?start={user.referral_code}'
return ReferralInfoResponse(
referral_code=user.referral_code or '',
referral_link=referral_link,
total_referrals=total_referrals,
active_referrals=active_referrals,
total_earnings_kopeks=total_earnings,
total_earnings_rubles=total_earnings / 100,
commission_percent=commission_percent,
)
@router.get('/list', response_model=ReferralListResponse)
async def get_referral_list(
page: int = Query(1, ge=1, description='Page number'),
per_page: int = Query(20, ge=1, le=100, description='Items per page'),
user: User = Depends(get_current_cabinet_user),
db: AsyncSession = Depends(get_cabinet_db),
):
"""Get list of invited users."""
# Base query with eager loading of subscription relationship
query = select(User).options(selectinload(User.subscription)).where(User.referred_by_id == user.id)
# Get total count
count_query = select(func.count()).select_from(User).where(User.referred_by_id == user.id)
total_result = await db.execute(count_query)
total = total_result.scalar() or 0
# Paginate
offset = (page - 1) * per_page
query = query.order_by(desc(User.created_at)).offset(offset).limit(per_page)
result = await db.execute(query)
referrals = result.scalars().all()
items = [
ReferralItemResponse(
id=r.id,
username=r.username,
first_name=r.first_name,
created_at=r.created_at,
has_subscription=r.subscription is not None,
has_paid=r.has_had_paid_subscription,
)
for r in referrals
]
pages = math.ceil(total / per_page) if total > 0 else 1
return ReferralListResponse(
items=items,
total=total,
page=page,
per_page=per_page,
pages=pages,
)
@router.get('/earnings', response_model=ReferralEarningsListResponse)
async def get_referral_earnings(
page: int = Query(1, ge=1, description='Page number'),
per_page: int = Query(20, ge=1, le=100, description='Items per page'),
user: User = Depends(get_current_cabinet_user),
db: AsyncSession = Depends(get_cabinet_db),
):
"""Get referral earnings history."""
# Base query
query = select(ReferralEarning).where(ReferralEarning.user_id == user.id)
# Get total count and sum
count_query = select(func.count()).select_from(ReferralEarning).where(ReferralEarning.user_id == user.id)
total_result = await db.execute(count_query)
total = total_result.scalar() or 0
sum_query = select(func.coalesce(func.sum(ReferralEarning.amount_kopeks), 0)).where(
ReferralEarning.user_id == user.id
)
sum_result = await db.execute(sum_query)
total_amount = sum_result.scalar() or 0
# Paginate
offset = (page - 1) * per_page
query = query.order_by(desc(ReferralEarning.created_at)).offset(offset).limit(per_page)
result = await db.execute(query)
earnings = result.scalars().all()
items = []
for e in earnings:
# Get referral user info
referral_query = select(User).where(User.id == e.referral_id)
referral_result = await db.execute(referral_query)
referral_user = referral_result.scalar_one_or_none()
items.append(
ReferralEarningResponse(
id=e.id,
amount_kopeks=e.amount_kopeks,
amount_rubles=e.amount_kopeks / 100,
reason=e.reason or 'Referral commission',
referral_username=referral_user.username if referral_user else None,
referral_first_name=referral_user.first_name if referral_user else None,
created_at=e.created_at,
)
)
pages = math.ceil(total / per_page) if total > 0 else 1
return ReferralEarningsListResponse(
items=items,
total=total,
total_amount_kopeks=total_amount,
total_amount_rubles=total_amount / 100,
page=page,
per_page=per_page,
pages=pages,
)
@router.get('/terms', response_model=ReferralTermsResponse)
async def get_referral_terms():
"""Get referral program terms."""
return ReferralTermsResponse(
is_enabled=settings.is_referral_program_enabled(),
commission_percent=settings.REFERRAL_COMMISSION_PERCENT,
minimum_topup_kopeks=settings.REFERRAL_MINIMUM_TOPUP_KOPEKS,
minimum_topup_rubles=settings.REFERRAL_MINIMUM_TOPUP_KOPEKS / 100,
first_topup_bonus_kopeks=settings.REFERRAL_FIRST_TOPUP_BONUS_KOPEKS,
first_topup_bonus_rubles=settings.REFERRAL_FIRST_TOPUP_BONUS_KOPEKS / 100,
inviter_bonus_kopeks=settings.REFERRAL_INVITER_BONUS_KOPEKS,
inviter_bonus_rubles=settings.REFERRAL_INVITER_BONUS_KOPEKS / 100,
)