Files
remnawave-bedolaga-telegram…/app/services/user_service.py
Egor 736e4c6cae NEW VERSION
NEW VERSION
2025-08-20 23:57:04 +03:00

333 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
from datetime import datetime, timedelta
from typing import Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from app.database.crud.user import (
get_user_by_id, get_user_by_telegram_id, get_users_list,
get_users_count, get_users_statistics, get_inactive_users,
add_user_balance, subtract_user_balance, update_user, delete_user
)
from app.database.crud.transaction import get_user_transactions_count
from app.database.crud.subscription import get_subscription_by_user_id
from app.database.models import User, UserStatus
from app.config import settings
logger = logging.getLogger(__name__)
class UserService:
async def get_user_profile(
self,
db: AsyncSession,
user_id: int
) -> Optional[Dict[str, Any]]:
try:
user = await get_user_by_id(db, user_id)
if not user:
return None
subscription = await get_subscription_by_user_id(db, user_id)
transactions_count = await get_user_transactions_count(db, user_id)
return {
"user": user,
"subscription": subscription,
"transactions_count": transactions_count,
"is_admin": settings.is_admin(user.telegram_id),
"registration_days": (datetime.utcnow() - user.created_at).days
}
except Exception as e:
logger.error(f"Ошибка получения профиля пользователя {user_id}: {e}")
return None
async def search_users(
self,
db: AsyncSession,
query: str,
page: int = 1,
limit: int = 20
) -> Dict[str, Any]:
try:
offset = (page - 1) * limit
users = await get_users_list(
db, offset=offset, limit=limit, search=query
)
total_count = await get_users_count(db, search=query)
total_pages = (total_count + limit - 1) // limit
return {
"users": users,
"current_page": page,
"total_pages": total_pages,
"total_count": total_count,
"has_next": page < total_pages,
"has_prev": page > 1
}
except Exception as e:
logger.error(f"Ошибка поиска пользователей: {e}")
return {
"users": [],
"current_page": 1,
"total_pages": 1,
"total_count": 0,
"has_next": False,
"has_prev": False
}
async def get_users_page(
self,
db: AsyncSession,
page: int = 1,
limit: int = 20,
status: Optional[UserStatus] = None
) -> Dict[str, Any]:
try:
offset = (page - 1) * limit
users = await get_users_list(
db, offset=offset, limit=limit, status=status
)
total_count = await get_users_count(db, status=status)
total_pages = (total_count + limit - 1) // limit
return {
"users": users,
"current_page": page,
"total_pages": total_pages,
"total_count": total_count,
"has_next": page < total_pages,
"has_prev": page > 1
}
except Exception as e:
logger.error(f"Ошибка получения страницы пользователей: {e}")
return {
"users": [],
"current_page": 1,
"total_pages": 1,
"total_count": 0,
"has_next": False,
"has_prev": False
}
async def update_user_balance(
self,
db: AsyncSession,
user_id: int,
amount_kopeks: int,
description: str,
admin_id: int
) -> bool:
try:
user = await get_user_by_id(db, user_id)
if not user:
return False
if amount_kopeks > 0:
await add_user_balance(db, user, amount_kopeks, description)
logger.info(f"Админ {admin_id} пополнил баланс пользователя {user_id} на {amount_kopeks/100}")
else:
success = await subtract_user_balance(db, user, abs(amount_kopeks), description)
if success:
logger.info(f"Админ {admin_id} списал с баланса пользователя {user_id} {abs(amount_kopeks)/100}")
return success
return True
except Exception as e:
logger.error(f"Ошибка изменения баланса пользователя: {e}")
return False
async def block_user(
self,
db: AsyncSession,
user_id: int,
admin_id: int,
reason: str = "Заблокирован администратором"
) -> bool:
try:
user = await get_user_by_id(db, user_id)
if not user:
return False
await update_user(db, user, status=UserStatus.BLOCKED.value)
logger.info(f"Админ {admin_id} заблокировал пользователя {user_id}: {reason}")
return True
except Exception as e:
logger.error(f"Ошибка блокировки пользователя: {e}")
return False
async def unblock_user(
self,
db: AsyncSession,
user_id: int,
admin_id: int
) -> bool:
try:
user = await get_user_by_id(db, user_id)
if not user:
return False
await update_user(db, user, status=UserStatus.ACTIVE.value)
logger.info(f"Админ {admin_id} разблокировал пользователя {user_id}")
return True
except Exception as e:
logger.error(f"Ошибка разблокировки пользователя: {e}")
return False
async def delete_user_account(
self,
db: AsyncSession,
user_id: int,
admin_id: int
) -> bool:
try:
user = await get_user_by_id(db, user_id)
if not user:
return False
success = await delete_user(db, user)
if success:
logger.info(f"Админ {admin_id} удалил пользователя {user_id}")
return success
except Exception as e:
logger.error(f"Ошибка удаления пользователя: {e}")
return False
async def get_user_statistics(self, db: AsyncSession) -> Dict[str, Any]:
try:
stats = await get_users_statistics(db)
return stats
except Exception as e:
logger.error(f"Ошибка получения статистики пользователей: {e}")
return {
"total_users": 0,
"active_users": 0,
"blocked_users": 0,
"new_today": 0,
"new_week": 0,
"new_month": 0
}
async def cleanup_inactive_users(
self,
db: AsyncSession,
months: int = None
) -> int:
try:
if months is None:
months = settings.INACTIVE_USER_DELETE_MONTHS
inactive_users = await get_inactive_users(db, months)
deleted_count = 0
for user in inactive_users:
success = await delete_user(db, user)
if success:
deleted_count += 1
logger.info(f"Удалено {deleted_count} неактивных пользователей")
return deleted_count
except Exception as e:
logger.error(f"Ошибка очистки неактивных пользователей: {e}")
return 0
async def get_user_activity_summary(
self,
db: AsyncSession,
user_id: int
) -> Dict[str, Any]:
try:
user = await get_user_by_id(db, user_id)
if not user:
return {}
subscription = await get_subscription_by_user_id(db, user_id)
transactions_count = await get_user_transactions_count(db, user_id)
days_since_registration = (datetime.utcnow() - user.created_at).days
days_since_activity = (datetime.utcnow() - user.last_activity).days if user.last_activity else None
return {
"user_id": user.id,
"telegram_id": user.telegram_id,
"username": user.username,
"full_name": user.full_name,
"status": user.status,
"language": user.language,
"balance_kopeks": user.balance_kopeks,
"registration_date": user.created_at,
"last_activity": user.last_activity,
"days_since_registration": days_since_registration,
"days_since_activity": days_since_activity,
"has_subscription": subscription is not None,
"subscription_active": subscription.is_active if subscription else False,
"subscription_trial": subscription.is_trial if subscription else False,
"transactions_count": transactions_count,
"referrer_id": user.referrer_id,
"referral_code": user.referral_code
}
except Exception as e:
logger.error(f"Ошибка получения сводки активности пользователя {user_id}: {e}")
return {}
async def get_users_by_criteria(
self,
db: AsyncSession,
criteria: Dict[str, Any]
) -> List[User]:
try:
status = criteria.get('status')
has_subscription = criteria.get('has_subscription')
is_trial = criteria.get('is_trial')
min_balance = criteria.get('min_balance', 0)
max_balance = criteria.get('max_balance')
days_inactive = criteria.get('days_inactive')
registered_after = criteria.get('registered_after')
registered_before = criteria.get('registered_before')
users = await get_users_list(db, offset=0, limit=10000, status=status)
filtered_users = []
for user in users:
if user.balance_kopeks < min_balance:
continue
if max_balance and user.balance_kopeks > max_balance:
continue
if registered_after and user.created_at < registered_after:
continue
if registered_before and user.created_at > registered_before:
continue
if days_inactive and user.last_activity:
inactive_threshold = datetime.utcnow() - timedelta(days=days_inactive)
if user.last_activity > inactive_threshold:
continue
filtered_users.append(user)
return filtered_users
except Exception as e:
logger.error(f"Ошибка получения пользователей по критериям: {e}")
return []