Refactor referral system to support multi-level bonuses and enhance logging. Update database interactions for referral handling and statistics retrieval. Change key display format in notifications and admin user editor for better readability.

This commit is contained in:
Zakhar Izmaylov
2024-11-24 19:38:59 +03:00
parent 18ca2b275c
commit 7893be6ef8
4 changed files with 114 additions and 38 deletions
+110 -34
View File
@@ -3,7 +3,7 @@ from typing import Any
import asyncpg
from config import BONUS_PERCENT, DATABASE_URL
from config import REFERRAL_BONUS_PERCENTAGES, DATABASE_URL
from logger import logger
@@ -638,69 +638,91 @@ async def add_referral(referred_tg_id: int, referrer_tg_id: int, session: Any):
async def handle_referral_on_balance_update(tg_id: int, amount: float):
"""
Обработка реферальной системы при обновлении баланса.
Обработка многоуровневой реферальной системы при обновлении баланса пользователя.
Эта функция проверяет, есть ли у пользователя реферер,
и если есть, начисляет бонус за пополнение баланса.
Метод анализирует цепочку рефералов для указанного пользователя и начисляет
бонусы рефереерам на разных уровнях согласно настроенным процентам.
Args:
tg_id (int): Telegram ID пользователя
tg_id (int): Идентификатор Telegram пользователя, пополнившего баланс
amount (float): Сумма пополнения баланса
Returns:
None
Raises:
Exception: В случае ошибки при работе с базой данных
"""
conn = None
try:
conn = await asyncpg.connect(DATABASE_URL)
logger.info(f"Установлено подключение к базе данных для обработки реферала пользователя {tg_id}")
logger.info(f"Начало обработки реферальной системы для пользователя {tg_id}")
referral = await conn.fetchrow(
"""
SELECT referrer_tg_id FROM referrals WHERE referred_tg_id = $1
""",
tg_id,
)
# Максимальное количество уровней рефералов
MAX_REFERRAL_LEVELS = len(REFERRAL_BONUS_PERCENTAGES.keys())
if referral:
referrer_tg_id = referral["referrer_tg_id"]
logger.info(f"Найден реферер {referrer_tg_id} для пользователя {tg_id}")
# Текущий уровень для отслеживания
current_tg_id = tg_id
referral_chain = []
bonus = amount * BONUS_PERCENT
# Собираем цепочку рефералов
for level in range(1, MAX_REFERRAL_LEVELS + 1):
# Получаем информацию о рефере текущего уровня
referral = await conn.fetchrow(
"""
SELECT referrer_tg_id
FROM referrals
WHERE referred_tg_id = $1
""",
current_tg_id,
)
# Если реферер не найден, прекращаем поиск
if not referral:
break
referrer_tg_id = referral['referrer_tg_id']
referral_chain.append({
'tg_id': referrer_tg_id,
'level': level
})
# Переходим к следующему уровню
current_tg_id = referrer_tg_id
# Начисляем бонусы по цепочке рефералов
for referral in referral_chain:
referrer_tg_id = referral['tg_id']
level = referral['level']
# Расчет бонуса для текущего уровня
bonus_percent = REFERRAL_BONUS_PERCENTAGES.get(level, 0)
bonus = amount * bonus_percent
bonus = max(bonus, 0) # Гарантируем неотрицательный бонус
logger.info(f"Начисление бонуса {bonus} рублей рефереру {referrer_tg_id}")
await update_balance(referrer_tg_id, bonus)
await conn.execute(
"""
UPDATE referrals SET reward_issued = TRUE
WHERE referrer_tg_id = $1 AND referred_tg_id = $2
""",
referrer_tg_id,
tg_id,
)
logger.info(f"Обновлен статус реферала для {referrer_tg_id}")
if bonus > 0:
logger.info(f"Начисление бонуса {bonus} рублей рефереру {referrer_tg_id} на уровне {level}")
# Обновляем баланс реферера
await update_balance(referrer_tg_id, bonus)
except Exception as e:
logger.error(f"Ошибка при обработке реферала для пользователя {tg_id}: {e}")
logger.error(f"Ошибка при обработке многоуровневой реферальной системы для {tg_id}: {e}")
finally:
if conn:
await conn.close()
logger.info("Закрытие подключения к базе данных")
async def get_referral_stats(referrer_tg_id: int):
"""
Получение статистики рефералов для указанного пользователя.
Получение подробной статистики рефералов для указанного пользователя.
Args:
referrer_tg_id (int): Telegram ID пользователя, для которого запрашивается статистика рефералов.
Returns:
dict: Словарь со статистикой рефералов, содержащий:
dict: Словарь с детальной статистикой рефералов, содержащий:
- total_referrals (int): Общее количество рефералов
- active_referrals (int): Количество активных рефералов (с начисленным бонусом)
- referrals_by_level (dict): Количество рефералов по каждому уровню
- total_referral_bonus (float): Общая сумма бонусов от рефералов
Raises:
Exception: В случае ошибки при подключении к базе данных или выполнении запроса
@@ -712,6 +734,7 @@ async def get_referral_stats(referrer_tg_id: int):
f"Установлено подключение к базе данных для получения статистики рефералов пользователя {referrer_tg_id}"
)
# Общее количество рефералов
total_referrals = await conn.fetchval(
"""
SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1
@@ -720,6 +743,7 @@ async def get_referral_stats(referrer_tg_id: int):
)
logger.debug(f"Получено общее количество рефералов: {total_referrals}")
# Активные рефералы
active_referrals = await conn.fetchval(
"""
SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1 AND reward_issued = TRUE
@@ -728,9 +752,61 @@ async def get_referral_stats(referrer_tg_id: int):
)
logger.debug(f"Получено количество активных рефералов: {active_referrals}")
# Рефералы по уровням
referrals_by_level_records = await conn.fetch(
"""
WITH RECURSIVE referral_levels AS (
SELECT referred_tg_id, referrer_tg_id, 1 AS level
FROM referrals
WHERE referrer_tg_id = $1
UNION
SELECT r.referred_tg_id, r.referrer_tg_id, rl.level + 1
FROM referrals r
JOIN referral_levels rl ON r.referrer_tg_id = rl.referred_tg_id
WHERE rl.level < 5
)
SELECT level,
COUNT(*) AS level_count,
COUNT(CASE WHEN reward_issued = TRUE THEN 1 END) AS active_level_count
FROM referral_levels rl
JOIN referrals r ON rl.referred_tg_id = r.referred_tg_id
GROUP BY level
ORDER BY level
""",
referrer_tg_id,
)
# Преобразование результатов в словарь
referrals_by_level = {
record['level']: {
'total': record['level_count'],
'active': record['active_level_count']
} for record in referrals_by_level_records
}
logger.debug(f"Получена статистика рефералов по уровням: {referrals_by_level}")
# Общая сумма бонусов от рефералов
total_referral_bonus = await conn.fetchval(
"""
SELECT COALESCE(SUM(amount), 0)
FROM payments
WHERE tg_id IN (
SELECT referred_tg_id
FROM referrals
WHERE referrer_tg_id = $1
) AND status = 'success'
""",
referrer_tg_id,
)
logger.debug(f"Получена общая сумма бонусов от рефералов: {total_referral_bonus}")
return {
"total_referrals": total_referrals,
"active_referrals": active_referrals,
"referrals_by_level": referrals_by_level,
"total_referral_bonus": total_referral_bonus,
}
except Exception as e:
+2 -2
View File
@@ -243,7 +243,7 @@ async def process_key_edit(callback_query: CallbackQuery, session: Any):
return
response_message = (
f"🔑 Ключ: <pre>{key_details['key']}</pre>\n"
f"🔑 Ключ: <code>{key_details['key']}</code>\n"
f"⏰ Дата истечения: <b>{key_details['expiry_date']}</b>\n"
f"💰 Баланс пользователя: <b>{key_details['balance']}</b>\n"
f"🌐 Сервер: <b>{key_details['server_name']}</b>"
@@ -295,7 +295,7 @@ async def handle_key_name_input(message: types.Message, state: FSMContext, sessi
return
response_message = (
f"🔑 Ключ: <pre>{key_details['key']}</pre>\n"
f"🔑 Ключ: <code>{key_details['key']}</code>\n"
f"⏰ Дата истечения: <b>{key_details['expiry_date']}</b>\n"
f"💰 Баланс пользователя: <b>{key_details['balance']}</b>\n"
f"🌐 Сервер: <b>{key_details['server_name']}</b>"
+1 -1
View File
@@ -8,7 +8,7 @@ from py3xui import AsyncApi
from client import delete_client
from config import ADMIN_PASSWORD, ADMIN_USERNAME, CLUSTERS, DATABASE_URL, TOTAL_GB, TRIAL_TIME
from database import delete_key, get_balance, update_balance, update_key_expiry
from database import delete_key, get_balance, update_balance, update_key_expiry#,add_notification,check_notification_time
from handlers.keys.key_utils import renew_key_in_cluster
from handlers.texts import KEY_EXPIRY_10H, KEY_EXPIRY_24H, KEY_RENEWED, RENEWAL_PLANS
from logger import logger
+1 -1
View File
@@ -75,7 +75,7 @@ async def handle_connect_vpn(callback_query: CallbackQuery, session: Any):
key_message = (
f"🔑 <b>Ваш персональный ключ доступа:</b>\n"
f"<pre>{trial_key_info['key']}</pre>\n\n"
f"<code>{trial_key_info['key']}</code>\n\n"
f"📋 <b>Быстрая инструкция по подключению:</b>\n{INSTRUCTIONS_TRIAL}"
)