add round for balance and less callback for tariffs

This commit is contained in:
Vladless
2025-05-25 20:34:16 +03:00
parent 64049ce9c2
commit e5d709ff64
4 changed files with 127 additions and 62 deletions
+2 -1
View File
@@ -171,7 +171,8 @@ async def get_total_referral_bonus(
result = await session.execute( result = await session.execute(
text(bonus_query), {"tg_id": referrer_tg_id, "max_levels": max_levels} text(bonus_query), {"tg_id": referrer_tg_id, "max_levels": max_levels}
) )
total_bonus = result.scalar() or 0.0 total_bonus_raw = result.scalar()
total_bonus = round(float(total_bonus_raw or 0), 2)
logger.debug(f"Получена общая сумма бонусов от рефералов: {total_bonus}") logger.debug(f"Получена общая сумма бонусов от рефералов: {total_bonus}")
return total_bonus return total_bonus
+30 -12
View File
@@ -55,11 +55,26 @@ async def count_trial_keys(session: AsyncSession) -> int:
) )
async def get_tariff_distribution(session: AsyncSession) -> list[tuple[int, int]]: async def get_tariff_distribution(
session: AsyncSession, include_unbound: bool = False
) -> tuple[list[tuple[int, int]], list[dict]]:
result = await session.execute( result = await session.execute(
select(Key.tariff_id, func.count(Key.client_id)).group_by(Key.tariff_id) select(Key.tariff_id, func.count(Key.client_id))
.where(Key.tariff_id.isnot(None))
.group_by(Key.tariff_id)
) )
return result.all() tariff_counts = result.all()
if not include_unbound:
return tariff_counts
result = await session.execute(
select(Key.expiry_time)
.where(Key.tariff_id.is_(None))
)
no_tariff_keys = [{"expiry_time": row[0]} for row in result.all()]
return tariff_counts, no_tariff_keys
async def get_tariff_names( async def get_tariff_names(
@@ -79,20 +94,23 @@ async def count_total_referrals(session: AsyncSession) -> int:
async def sum_payments_since(session: AsyncSession, since: date) -> float: async def sum_payments_since(session: AsyncSession, since: date) -> float:
return await session.scalar( result = await session.scalar(
select(func.coalesce(func.sum(Payment.amount), 0)).where( select(func.coalesce(func.sum(Payment.amount), 0))
Payment.created_at >= since .where(Payment.created_at >= since)
)
) )
return round(float(result), 2)
async def sum_payments_between(session: AsyncSession, start: date, end: date) -> float: async def sum_payments_between(session: AsyncSession, start: date, end: date) -> float:
return await session.scalar( result = await session.scalar(
select(func.coalesce(func.sum(Payment.amount), 0)).where( select(func.coalesce(func.sum(Payment.amount), 0))
Payment.created_at >= start, Payment.created_at < end .where(Payment.created_at >= start, Payment.created_at < end)
)
) )
return round(float(result), 2)
async def sum_total_payments(session: AsyncSession) -> float: async def sum_total_payments(session: AsyncSession) -> float:
return await session.scalar(select(func.coalesce(func.sum(Payment.amount), 0))) result = await session.scalar(
select(func.coalesce(func.sum(Payment.amount), 0))
)
return round(float(result), 2)
+26 -5
View File
@@ -5,6 +5,8 @@ from aiogram import F, Router
from aiogram.exceptions import TelegramBadRequest from aiogram.exceptions import TelegramBadRequest
from aiogram.types import CallbackQuery from aiogram.types import CallbackQuery
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from collections import Counter
from bot import bot from bot import bot
from config import ADMIN_ID from config import ADMIN_ID
@@ -67,17 +69,36 @@ async def handle_stats(callback_query: CallbackQuery, session: AsyncSession):
expired_keys = total_keys - active_keys expired_keys = total_keys - active_keys
trial_keys_count = await count_trial_keys(session) trial_keys_count = await count_trial_keys(session)
tariff_counts = await get_tariff_distribution(session) tariff_counts, no_tariff_keys = await get_tariff_distribution(session, include_unbound=True)
tariff_names = await get_tariff_names( tariff_names = await get_tariff_names(session, [tid for tid, _ in tariff_counts])
session, [tid for tid, _ in tariff_counts]
)
tariff_stats_text = "" tariff_stats_text = ""
for tid, count in tariff_counts: for tid, count in tariff_counts:
name = tariff_names.get(tid, f"ID {tid}") name = tariff_names.get(tid, f"ID {tid}")
tariff_stats_text += f"{name}: <b>{count}</b>\n" tariff_stats_text += f"{name}: <b>{count}</b>\n"
duration_buckets = Counter()
now_ts = int(datetime.utcnow().timestamp() * 1000)
for key in no_tariff_keys:
duration_days = round((key["expiry_time"] - now_ts) / (1000 * 60 * 60 * 24))
if 25 <= duration_days <= 35:
bucket = "Без тарифа: 1 мес"
elif 80 <= duration_days <= 100:
bucket = "Без тарифа: 3 мес"
elif 170 <= duration_days <= 200:
bucket = "Без тарифа: 6 мес"
elif 350 <= duration_days <= 380:
bucket = "Без тарифа: 12 мес"
else:
bucket = "Без тарифа: прочее"
duration_buckets[bucket] += 1
for name, count in duration_buckets.items():
tariff_stats_text += f"{name}: <b>{count}</b>\n"
tariff_stats_text = ( tariff_stats_text = (
"└ По тарифам:\n" + tariff_stats_text "└ По тарифам и срокам:\n" + tariff_stats_text
if tariff_stats_text if tariff_stats_text
else "└ Нет данных по тарифам\n" else "└ Нет данных по тарифам\n"
) )
+69 -44
View File
@@ -82,6 +82,11 @@ class UserEditorState(StatesGroup):
selecting_country = State() selecting_country = State()
class RenewTariffState(StatesGroup):
selecting_group = State()
selecting_tariff = State()
class BanUserStates(StatesGroup): class BanUserStates(StatesGroup):
waiting_for_reason = State() waiting_for_reason = State()
waiting_for_ban_duration = State() waiting_for_ban_duration = State()
@@ -554,14 +559,20 @@ async def handle_key_edit(
) )
@router.callback_query( @router.callback_query(F.data == "back:renew", IsAdminFilter())
AdminUserEditorCallback.filter(F.action == "users_back"), IsAdminFilter() async def handle_back_to_key_menu(
)
async def handle_users_back_to_key_edit(
callback_query: CallbackQuery, callback_query: CallbackQuery,
callback_data: AdminUserEditorCallback,
session: AsyncSession, session: AsyncSession,
state: FSMContext,
): ):
data = await state.get_data()
email = data["email"]
tg_id = data["tg_id"]
await state.clear()
callback_data = AdminUserEditorCallback(
action="users_key_edit", data=email, tg_id=tg_id
)
await handle_key_edit( await handle_key_edit(
callback_query=callback_query, callback_query=callback_query,
callback_data=callback_data, callback_data=callback_data,
@@ -577,27 +588,21 @@ async def handle_user_choose_tariff_group(
callback_query: CallbackQuery, callback_query: CallbackQuery,
callback_data: AdminUserEditorCallback, callback_data: AdminUserEditorCallback,
session: AsyncSession, session: AsyncSession,
state: FSMContext,
): ):
email = callback_data.data email = callback_data.data
tg_id = callback_data.tg_id tg_id = callback_data.tg_id
await state.set_state(RenewTariffState.selecting_group)
await state.update_data(email=email, tg_id=tg_id)
result = await session.execute(select(Tariff.group_code).distinct()) result = await session.execute(select(Tariff.group_code).distinct())
groups = [row[0] for row in result.fetchall()] groups = [row[0] for row in result.fetchall()]
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
for group_code in groups: for group_code in groups:
builder.button( builder.button(text=group_code, callback_data=f"group:{group_code}")
text=group_code, builder.button(text="🔙 Назад", callback_data="back:renew")
callback_data=AdminUserEditorCallback(
action="users_renew_group", data=f"{email}|{group_code}", tg_id=tg_id
).pack(),
)
builder.button(
text="🔙 Назад",
callback_data=AdminUserEditorCallback(
action="users_back", data=email, tg_id=tg_id
).pack(),
)
builder.adjust(1) builder.adjust(1)
await callback_query.message.edit_text( await callback_query.message.edit_text(
@@ -606,16 +611,15 @@ async def handle_user_choose_tariff_group(
) )
@router.callback_query( @router.callback_query(F.data.startswith("group:"), IsAdminFilter())
AdminUserEditorCallback.filter(F.action == "users_renew_group"), IsAdminFilter()
)
async def handle_user_choose_tariff( async def handle_user_choose_tariff(
callback_query: CallbackQuery, callback_query: CallbackQuery,
callback_data: AdminUserEditorCallback,
session: AsyncSession, session: AsyncSession,
state: FSMContext,
): ):
email, group_code = callback_data.data.split("|") group_code = callback_query.data.split(":", 1)[1]
tg_id = callback_data.tg_id await state.update_data(group_code=group_code)
await state.set_state(RenewTariffState.selecting_tariff)
result = await session.execute( result = await session.execute(
select(Tariff) select(Tariff)
@@ -631,19 +635,10 @@ async def handle_user_choose_tariff(
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
for tariff in tariffs: for tariff in tariffs:
builder.button( builder.button(
text=f"{tariff.duration_days}д / {int(tariff.price_rub)}", text=f"{tariff.name} {int(tariff.price_rub)}",
callback_data=AdminUserEditorCallback( callback_data=f"confirm:{tariff.id}"
action="users_renew_confirm",
data=f"{email}|{tariff.id}",
tg_id=tg_id,
).pack(),
) )
builder.button( builder.button(text="🔙 Назад", callback_data="back:group")
text="🔙 Назад",
callback_data=AdminUserEditorCallback(
action="users_renew", data=email, tg_id=tg_id
).pack(),
)
builder.adjust(1) builder.adjust(1)
await callback_query.message.edit_text( await callback_query.message.edit_text(
@@ -652,17 +647,16 @@ async def handle_user_choose_tariff(
) )
@router.callback_query( @router.callback_query(F.data.startswith("confirm:"), IsAdminFilter())
AdminUserEditorCallback.filter(F.action == "users_renew_confirm"), IsAdminFilter()
)
async def handle_user_renew_confirm( async def handle_user_renew_confirm(
callback_query: CallbackQuery, callback_query: CallbackQuery,
callback_data: AdminUserEditorCallback,
session: AsyncSession, session: AsyncSession,
state: FSMContext,
): ):
email, tariff_id = callback_data.data.split("|") tariff_id = int(callback_query.data.split(":")[1])
tg_id = callback_data.tg_id data = await state.get_data()
tariff_id = int(tariff_id) email = data["email"]
tg_id = data["tg_id"]
stmt = ( stmt = (
update(Key) update(Key)
@@ -671,11 +665,42 @@ async def handle_user_renew_confirm(
) )
await session.execute(stmt) await session.execute(stmt)
await session.commit() await session.commit()
await state.clear()
callback_data = AdminUserEditorCallback(
action="users_key_edit", data=email, tg_id=tg_id
)
await handle_key_edit(
callback_query=callback_query,
callback_data=callback_data,
session=session,
update=False,
)
@router.callback_query(F.data == "back:group", IsAdminFilter())
async def handle_back_to_group(
callback_query: CallbackQuery,
state: FSMContext,
session: AsyncSession,
):
data = await state.get_data()
result = await session.execute(select(Tariff.group_code).distinct())
groups = [row[0] for row in result.fetchall()]
builder = InlineKeyboardBuilder()
for group_code in groups:
builder.button(text=group_code, callback_data=f"group:{group_code}")
builder.button(text="🔙 Назад", callback_data="back:renew")
builder.adjust(1)
await callback_query.message.edit_text( await callback_query.message.edit_text(
text="✅ Тариф успешно обновлён.", text="📁 <b>Выберите тарифную группу:</b>",
reply_markup=build_key_edit_kb({"tg_id": tg_id}, email), reply_markup=builder.as_markup(),
) )
await state.set_state(RenewTariffState.selecting_group)
@router.callback_query( @router.callback_query(