mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-16 17:10:31 +00:00
Add advertising campaign stats to admin user info
This commit is contained in:
@@ -9,6 +9,12 @@ from sqlalchemy.orm import selectinload
|
||||
from app.database.models import (
|
||||
AdvertisingCampaign,
|
||||
AdvertisingCampaignRegistration,
|
||||
Subscription,
|
||||
SubscriptionConversion,
|
||||
SubscriptionStatus,
|
||||
Transaction,
|
||||
TransactionType,
|
||||
User,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -157,6 +163,19 @@ async def delete_campaign(db: AsyncSession, campaign: AdvertisingCampaign) -> bo
|
||||
return True
|
||||
|
||||
|
||||
async def get_campaign_registration_by_user(
|
||||
db: AsyncSession,
|
||||
user_id: int,
|
||||
) -> Optional[AdvertisingCampaignRegistration]:
|
||||
result = await db.execute(
|
||||
select(AdvertisingCampaignRegistration)
|
||||
.options(selectinload(AdvertisingCampaignRegistration.campaign))
|
||||
.where(AdvertisingCampaignRegistration.user_id == user_id)
|
||||
.limit(1)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def record_campaign_registration(
|
||||
db: AsyncSession,
|
||||
*,
|
||||
@@ -197,6 +216,11 @@ async def get_campaign_statistics(
|
||||
db: AsyncSession,
|
||||
campaign_id: int,
|
||||
) -> Dict[str, Optional[int]]:
|
||||
registrations_query = select(AdvertisingCampaignRegistration.user_id).where(
|
||||
AdvertisingCampaignRegistration.campaign_id == campaign_id
|
||||
)
|
||||
registrations_subquery = registrations_query.subquery()
|
||||
|
||||
result = await db.execute(
|
||||
select(
|
||||
func.count(AdvertisingCampaignRegistration.id),
|
||||
@@ -217,11 +241,80 @@ async def get_campaign_statistics(
|
||||
)
|
||||
)
|
||||
|
||||
deposits_result = await db.execute(
|
||||
select(func.coalesce(func.sum(Transaction.amount_kopeks), 0)).where(
|
||||
Transaction.user_id.in_(select(registrations_subquery.c.user_id)),
|
||||
Transaction.type == TransactionType.DEPOSIT.value,
|
||||
Transaction.is_completed.is_(True),
|
||||
)
|
||||
)
|
||||
total_revenue = deposits_result.scalar() or 0
|
||||
|
||||
trials_result = await db.execute(
|
||||
select(func.count(func.distinct(Subscription.user_id))).where(
|
||||
Subscription.user_id.in_(select(registrations_subquery.c.user_id)),
|
||||
Subscription.is_trial.is_(True),
|
||||
)
|
||||
)
|
||||
trial_users_count = trials_result.scalar() or 0
|
||||
|
||||
active_trials_result = await db.execute(
|
||||
select(func.count(func.distinct(Subscription.user_id))).where(
|
||||
Subscription.user_id.in_(select(registrations_subquery.c.user_id)),
|
||||
Subscription.is_trial.is_(True),
|
||||
Subscription.status == SubscriptionStatus.ACTIVE.value,
|
||||
)
|
||||
)
|
||||
active_trials_count = active_trials_result.scalar() or 0
|
||||
|
||||
conversions_result = await db.execute(
|
||||
select(func.count(func.distinct(SubscriptionConversion.user_id))).where(
|
||||
SubscriptionConversion.user_id.in_(select(registrations_subquery.c.user_id))
|
||||
)
|
||||
)
|
||||
conversion_count = conversions_result.scalar() or 0
|
||||
|
||||
paid_users_result = await db.execute(
|
||||
select(func.count(User.id)).where(
|
||||
User.id.in_(select(registrations_subquery.c.user_id)),
|
||||
User.has_had_paid_subscription.is_(True),
|
||||
)
|
||||
)
|
||||
paid_users_count = paid_users_result.scalar() or 0
|
||||
|
||||
avg_first_payment_result = await db.execute(
|
||||
select(func.coalesce(func.avg(SubscriptionConversion.first_payment_amount_kopeks), 0)).where(
|
||||
SubscriptionConversion.user_id.in_(select(registrations_subquery.c.user_id))
|
||||
)
|
||||
)
|
||||
avg_first_payment = int(avg_first_payment_result.scalar() or 0)
|
||||
|
||||
conversion_rate = 0.0
|
||||
if count:
|
||||
conversion_rate = round((paid_users_count / count) * 100, 1)
|
||||
|
||||
trial_conversion_rate = 0.0
|
||||
if trial_users_count:
|
||||
trial_conversion_rate = round((conversion_count / trial_users_count) * 100, 1)
|
||||
|
||||
avg_revenue_per_user = 0
|
||||
if count:
|
||||
avg_revenue_per_user = int(total_revenue / count)
|
||||
|
||||
return {
|
||||
"registrations": count or 0,
|
||||
"balance_issued": total_balance or 0,
|
||||
"subscription_issued": subscription_count_result.scalar() or 0,
|
||||
"last_registration": last_registration,
|
||||
"total_revenue_kopeks": total_revenue,
|
||||
"trial_users_count": trial_users_count,
|
||||
"active_trials_count": active_trials_count,
|
||||
"conversion_count": conversion_count,
|
||||
"paid_users_count": paid_users_count,
|
||||
"conversion_rate": conversion_rate,
|
||||
"trial_conversion_rate": trial_conversion_rate,
|
||||
"avg_revenue_per_user_kopeks": avg_revenue_per_user,
|
||||
"avg_first_payment_kopeks": avg_first_payment,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -333,6 +333,35 @@ async def show_campaign_detail(
|
||||
f"• Выдано баланса: <b>{texts.format_price(stats['balance_issued'])}</b>"
|
||||
)
|
||||
text.append(f"• Выдано подписок: <b>{stats['subscription_issued']}</b>")
|
||||
text.append(
|
||||
f"• Доход: <b>{texts.format_price(stats['total_revenue_kopeks'])}</b>"
|
||||
)
|
||||
text.append(
|
||||
"• Получили триал: "
|
||||
f"<b>{stats['trial_users_count']}</b>"
|
||||
f" (активно: {stats['active_trials_count']})"
|
||||
)
|
||||
text.append(
|
||||
"• Конверсий в оплату: "
|
||||
f"<b>{stats['conversion_count']}</b>"
|
||||
f" / пользователей с оплатой: {stats['paid_users_count']}"
|
||||
)
|
||||
text.append(
|
||||
"• Конверсия в оплату: "
|
||||
f"<b>{stats['conversion_rate']:.1f}%</b>"
|
||||
)
|
||||
text.append(
|
||||
"• Конверсия триала: "
|
||||
f"<b>{stats['trial_conversion_rate']:.1f}%</b>"
|
||||
)
|
||||
text.append(
|
||||
"• Средний доход на пользователя: "
|
||||
f"<b>{texts.format_price(stats['avg_revenue_per_user_kopeks'])}</b>"
|
||||
)
|
||||
text.append(
|
||||
"• Средний первый платеж: "
|
||||
f"<b>{texts.format_price(stats['avg_first_payment_kopeks'])}</b>"
|
||||
)
|
||||
if stats["last_registration"]:
|
||||
text.append(
|
||||
f"• Последняя: {stats['last_registration'].strftime('%d.%m.%Y %H:%M')}"
|
||||
|
||||
@@ -8,7 +8,11 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.config import settings
|
||||
from app.states import AdminStates
|
||||
from app.database.models import User, UserStatus, Subscription, SubscriptionStatus, TransactionType
|
||||
from app.database.crud.user import get_user_by_id
|
||||
from app.database.crud.user import get_user_by_id
|
||||
from app.database.crud.campaign import (
|
||||
get_campaign_registration_by_user,
|
||||
get_campaign_statistics,
|
||||
)
|
||||
from app.keyboards.admin import (
|
||||
get_admin_users_keyboard, get_user_management_keyboard,
|
||||
get_admin_pagination_keyboard, get_confirmation_keyboard,
|
||||
@@ -1214,6 +1218,10 @@ async def show_user_statistics(
|
||||
subscription = profile["subscription"]
|
||||
|
||||
referral_stats = await get_detailed_referral_stats(db, user.id)
|
||||
campaign_registration = await get_campaign_registration_by_user(db, user.id)
|
||||
campaign_stats = None
|
||||
if campaign_registration:
|
||||
campaign_stats = await get_campaign_statistics(db, campaign_registration.campaign_id)
|
||||
|
||||
text = f"📊 <b>Статистика пользователя</b>\n\n"
|
||||
text += f"👤 {user.full_name} (ID: <code>{user.telegram_id}</code>)\n\n"
|
||||
@@ -1236,17 +1244,80 @@ async def show_user_statistics(
|
||||
text += f"• Отсутствует\n"
|
||||
|
||||
text += f"\n<b>Реферальная программа:</b>\n"
|
||||
|
||||
|
||||
if user.referred_by_id:
|
||||
referrer = await get_user_by_id(db, user.referred_by_id)
|
||||
if referrer:
|
||||
text += f"• Пришел по реферальной ссылке от <b>{referrer.full_name}</b>\n"
|
||||
else:
|
||||
text += f"• Пришел по реферальной ссылке (реферер не найден)\n"
|
||||
text += "• Пришел по реферальной ссылке (реферер не найден)\n"
|
||||
if campaign_registration and campaign_registration.campaign:
|
||||
text += (
|
||||
"• Дополнительно зарегистрирован через кампанию "
|
||||
f"<b>{campaign_registration.campaign.name}</b>\n"
|
||||
)
|
||||
elif campaign_registration and campaign_registration.campaign:
|
||||
text += (
|
||||
"• Регистрация через рекламную кампанию "
|
||||
f"<b>{campaign_registration.campaign.name}</b>\n"
|
||||
)
|
||||
if campaign_registration.created_at:
|
||||
text += (
|
||||
"• Дата регистрации по кампании: "
|
||||
f"{campaign_registration.created_at.strftime('%d.%m.%Y %H:%M')}\n"
|
||||
)
|
||||
else:
|
||||
text += f"• Прямая регистрация\n"
|
||||
|
||||
text += "• Прямая регистрация\n"
|
||||
|
||||
text += f"• Реферальный код: <code>{user.referral_code}</code>\n\n"
|
||||
|
||||
if campaign_registration and campaign_registration.campaign and campaign_stats:
|
||||
text += "<b>Рекламная кампания:</b>\n"
|
||||
text += (
|
||||
"• Название: "
|
||||
f"<b>{campaign_registration.campaign.name}</b>"
|
||||
)
|
||||
if campaign_registration.campaign.start_parameter:
|
||||
text += (
|
||||
" (параметр: "
|
||||
f"<code>{campaign_registration.campaign.start_parameter}</code>)"
|
||||
)
|
||||
text += "\n"
|
||||
text += (
|
||||
"• Всего регистраций: "
|
||||
f"{campaign_stats['registrations']}\n"
|
||||
)
|
||||
text += (
|
||||
"• Суммарный доход: "
|
||||
f"{settings.format_price(campaign_stats['total_revenue_kopeks'])}\n"
|
||||
)
|
||||
text += (
|
||||
"• Получили триал: "
|
||||
f"{campaign_stats['trial_users_count']}"
|
||||
f" (активно: {campaign_stats['active_trials_count']})\n"
|
||||
)
|
||||
text += (
|
||||
"• Конверсий в оплату: "
|
||||
f"{campaign_stats['conversion_count']}"
|
||||
f" (оплативших пользователей: {campaign_stats['paid_users_count']})\n"
|
||||
)
|
||||
text += (
|
||||
"• Конверсия в оплату: "
|
||||
f"{campaign_stats['conversion_rate']:.1f}%\n"
|
||||
)
|
||||
text += (
|
||||
"• Конверсия триала: "
|
||||
f"{campaign_stats['trial_conversion_rate']:.1f}%\n"
|
||||
)
|
||||
text += (
|
||||
"• Средний доход на пользователя: "
|
||||
f"{settings.format_price(campaign_stats['avg_revenue_per_user_kopeks'])}\n"
|
||||
)
|
||||
text += (
|
||||
"• Средний первый платеж: "
|
||||
f"{settings.format_price(campaign_stats['avg_first_payment_kopeks'])}\n"
|
||||
)
|
||||
text += "\n"
|
||||
|
||||
if referral_stats['invited_count'] > 0:
|
||||
text += f"<b>Доходы от рефералов:</b>\n"
|
||||
|
||||
Reference in New Issue
Block a user