mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-30 08:40:23 +00:00
131 lines
5.0 KiB
Python
131 lines
5.0 KiB
Python
import logging
|
||
from datetime import datetime, timedelta
|
||
from typing import Optional, List
|
||
from sqlalchemy import select, func
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from app.database.models import SubscriptionConversion, User
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
async def create_subscription_conversion(
|
||
db: AsyncSession,
|
||
user_id: int,
|
||
trial_duration_days: int,
|
||
payment_method: str,
|
||
first_payment_amount_kopeks: int,
|
||
first_paid_period_days: int
|
||
) -> SubscriptionConversion:
|
||
|
||
conversion = SubscriptionConversion(
|
||
user_id=user_id,
|
||
converted_at=datetime.utcnow(),
|
||
trial_duration_days=trial_duration_days,
|
||
payment_method=payment_method,
|
||
first_payment_amount_kopeks=first_payment_amount_kopeks,
|
||
first_paid_period_days=first_paid_period_days
|
||
)
|
||
|
||
db.add(conversion)
|
||
await db.commit()
|
||
await db.refresh(conversion)
|
||
|
||
logger.info(f"✅ Создана запись о конверсии для пользователя {user_id}: {trial_duration_days} дн. → {first_paid_period_days} дн. за {first_payment_amount_kopeks/100}₽")
|
||
|
||
return conversion
|
||
|
||
|
||
async def get_conversion_by_user_id(
|
||
db: AsyncSession,
|
||
user_id: int
|
||
) -> Optional[SubscriptionConversion]:
|
||
|
||
result = await db.execute(
|
||
select(SubscriptionConversion)
|
||
.where(SubscriptionConversion.user_id == user_id)
|
||
.order_by(SubscriptionConversion.converted_at.desc())
|
||
.limit(1)
|
||
)
|
||
|
||
return result.scalar_one_or_none()
|
||
|
||
|
||
async def get_conversion_statistics(db: AsyncSession) -> dict:
|
||
from app.database.models import Subscription
|
||
|
||
# Получаем количество записей о конверсиях в таблице
|
||
total_conversions_result = await db.execute(
|
||
select(func.count(SubscriptionConversion.id))
|
||
)
|
||
total_conversions = total_conversions_result.scalar() or 0
|
||
|
||
# Подсчитываем пользователей с платными подписками
|
||
users_with_paid_result = await db.execute(
|
||
select(func.count(User.id))
|
||
.where(User.has_had_paid_subscription == True)
|
||
)
|
||
users_with_paid = users_with_paid_result.scalar() or 0
|
||
|
||
# Подсчитываем всех пользователей с подписками (использовавших триал)
|
||
# Считаем что все новые пользователи начинают с триала
|
||
total_users_with_subscriptions_result = await db.execute(
|
||
select(func.count(func.distinct(Subscription.user_id)))
|
||
)
|
||
total_users_with_subscriptions = total_users_with_subscriptions_result.scalar() or 0
|
||
|
||
# Расчёт конверсии: (оплатившие) / (всего с подписками) * 100
|
||
# Это показывает какой % пользователей, получивших подписку, в итоге оплатили
|
||
if total_users_with_subscriptions > 0:
|
||
conversion_rate = round((users_with_paid / total_users_with_subscriptions) * 100, 1)
|
||
else:
|
||
conversion_rate = 0.0
|
||
|
||
avg_trial_duration_result = await db.execute(
|
||
select(func.avg(SubscriptionConversion.trial_duration_days))
|
||
)
|
||
avg_trial_duration = avg_trial_duration_result.scalar() or 0
|
||
|
||
avg_first_payment_result = await db.execute(
|
||
select(func.avg(SubscriptionConversion.first_payment_amount_kopeks))
|
||
)
|
||
avg_first_payment = avg_first_payment_result.scalar() or 0
|
||
|
||
month_ago = datetime.utcnow() - timedelta(days=30)
|
||
month_conversions_result = await db.execute(
|
||
select(func.count(SubscriptionConversion.id))
|
||
.where(SubscriptionConversion.converted_at >= month_ago)
|
||
)
|
||
month_conversions = month_conversions_result.scalar() or 0
|
||
|
||
logger.info(f"📊 Статистика конверсий:")
|
||
logger.info(f" Всего пользователей с подписками: {total_users_with_subscriptions}")
|
||
logger.info(f" Оплативших подписку: {users_with_paid}")
|
||
logger.info(f" Рассчитанная конверсия: {conversion_rate}%")
|
||
|
||
return {
|
||
"total_conversions": total_conversions,
|
||
"conversion_rate": conversion_rate,
|
||
"users_with_trial": total_users_with_subscriptions,
|
||
"converted_users": users_with_paid,
|
||
"avg_trial_duration_days": round(avg_trial_duration, 1),
|
||
"avg_first_payment_rubles": round((avg_first_payment or 0) / 100, 2),
|
||
"month_conversions": month_conversions
|
||
}
|
||
|
||
|
||
async def get_users_had_trial_count(db: AsyncSession) -> int:
|
||
|
||
conversions_count_result = await db.execute(
|
||
select(func.count(func.distinct(SubscriptionConversion.user_id)))
|
||
)
|
||
conversions_count = conversions_count_result.scalar()
|
||
|
||
paid_users_result = await db.execute(
|
||
select(func.count(User.id))
|
||
.where(User.has_had_paid_subscription == True)
|
||
)
|
||
paid_users_count = paid_users_result.scalar()
|
||
|
||
return max(conversions_count, paid_users_count)
|