9a2aea038a
- 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
198 lines
6.8 KiB
Python
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,
|
|
)
|