mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 11:50:27 +00:00
312 lines
9.9 KiB
Python
312 lines
9.9 KiB
Python
"""CRUD операции для связи пользователей с промогруппами (Many-to-Many)."""
|
||
import logging
|
||
from datetime import datetime
|
||
from typing import List, Optional
|
||
|
||
from sqlalchemy import select, and_, desc
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from sqlalchemy.orm import selectinload
|
||
|
||
from app.database.models import UserPromoGroup, PromoGroup, User
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
async def _sync_user_primary_promo_group(
|
||
db: AsyncSession,
|
||
user_id: int,
|
||
) -> None:
|
||
"""Синхронизирует колонку users.promo_group_id с primary промогруппой."""
|
||
|
||
try:
|
||
result = await db.execute(
|
||
select(UserPromoGroup.promo_group_id)
|
||
.join(PromoGroup, UserPromoGroup.promo_group_id == PromoGroup.id)
|
||
.where(UserPromoGroup.user_id == user_id)
|
||
.order_by(desc(PromoGroup.priority), PromoGroup.id)
|
||
)
|
||
|
||
first = result.first()
|
||
new_primary_id = first[0] if first else None
|
||
|
||
user = await db.get(User, user_id)
|
||
if not user:
|
||
return
|
||
|
||
if user.promo_group_id != new_primary_id:
|
||
user.promo_group_id = new_primary_id
|
||
user.updated_at = datetime.utcnow()
|
||
|
||
except Exception as error:
|
||
logger.error(
|
||
"Ошибка синхронизации primary промогруппы пользователя %s: %s",
|
||
user_id,
|
||
error,
|
||
)
|
||
|
||
|
||
async def sync_user_primary_promo_group(
|
||
db: AsyncSession,
|
||
user_id: int,
|
||
) -> None:
|
||
"""Публичная обертка для синхронизации primary промогруппы пользователя."""
|
||
|
||
await _sync_user_primary_promo_group(db, user_id)
|
||
|
||
|
||
async def add_user_to_promo_group(
|
||
db: AsyncSession,
|
||
user_id: int,
|
||
promo_group_id: int,
|
||
assigned_by: str = "admin"
|
||
) -> Optional[UserPromoGroup]:
|
||
"""
|
||
Добавляет пользователю промогруппу.
|
||
|
||
Args:
|
||
db: Сессия БД
|
||
user_id: ID пользователя
|
||
promo_group_id: ID промогруппы
|
||
assigned_by: Кто назначил ('admin', 'system', 'auto', 'promocode')
|
||
|
||
Returns:
|
||
UserPromoGroup или None если уже существует
|
||
"""
|
||
try:
|
||
# Проверяем существование связи
|
||
existing = await has_user_promo_group(db, user_id, promo_group_id)
|
||
if existing:
|
||
logger.info(f"Пользователь {user_id} уже имеет промогруппу {promo_group_id}")
|
||
return None
|
||
|
||
# Создаем новую связь
|
||
user_promo_group = UserPromoGroup(
|
||
user_id=user_id,
|
||
promo_group_id=promo_group_id,
|
||
assigned_by=assigned_by,
|
||
)
|
||
db.add(user_promo_group)
|
||
await db.flush()
|
||
|
||
await _sync_user_primary_promo_group(db, user_id)
|
||
|
||
await db.commit()
|
||
await db.refresh(user_promo_group)
|
||
|
||
logger.info(f"Пользователю {user_id} добавлена промогруппа {promo_group_id} ({assigned_by})")
|
||
return user_promo_group
|
||
|
||
except Exception as error:
|
||
logger.error(f"Ошибка добавления промогруппы пользователю: {error}")
|
||
await db.rollback()
|
||
return None
|
||
|
||
|
||
async def remove_user_from_promo_group(
|
||
db: AsyncSession,
|
||
user_id: int,
|
||
promo_group_id: int
|
||
) -> bool:
|
||
"""
|
||
Удаляет промогруппу у пользователя.
|
||
|
||
Args:
|
||
db: Сессия БД
|
||
user_id: ID пользователя
|
||
promo_group_id: ID промогруппы
|
||
|
||
Returns:
|
||
True если удалено, False если связи не было
|
||
"""
|
||
try:
|
||
result = await db.execute(
|
||
select(UserPromoGroup).where(
|
||
and_(
|
||
UserPromoGroup.user_id == user_id,
|
||
UserPromoGroup.promo_group_id == promo_group_id
|
||
)
|
||
)
|
||
)
|
||
user_promo_group = result.scalar_one_or_none()
|
||
|
||
if not user_promo_group:
|
||
logger.warning(f"Связь пользователя {user_id} с промогруппой {promo_group_id} не найдена")
|
||
return False
|
||
|
||
await db.delete(user_promo_group)
|
||
await db.flush()
|
||
|
||
await _sync_user_primary_promo_group(db, user_id)
|
||
|
||
await db.commit()
|
||
|
||
logger.info(f"У пользователя {user_id} удалена промогруппа {promo_group_id}")
|
||
return True
|
||
|
||
except Exception as error:
|
||
logger.error(f"Ошибка удаления промогруппы у пользователя: {error}")
|
||
await db.rollback()
|
||
return False
|
||
|
||
|
||
async def get_user_promo_groups(
|
||
db: AsyncSession,
|
||
user_id: int
|
||
) -> List[UserPromoGroup]:
|
||
"""
|
||
Получает все промогруппы пользователя, отсортированные по приоритету.
|
||
|
||
Args:
|
||
db: Сессия БД
|
||
user_id: ID пользователя
|
||
|
||
Returns:
|
||
Список UserPromoGroup с загруженными PromoGroup, отсортированный по приоритету DESC
|
||
"""
|
||
try:
|
||
result = await db.execute(
|
||
select(UserPromoGroup)
|
||
.options(selectinload(UserPromoGroup.promo_group))
|
||
.where(UserPromoGroup.user_id == user_id)
|
||
.join(PromoGroup, UserPromoGroup.promo_group_id == PromoGroup.id)
|
||
.order_by(desc(PromoGroup.priority), PromoGroup.id)
|
||
)
|
||
return list(result.scalars().all())
|
||
|
||
except Exception as error:
|
||
logger.error(f"Ошибка получения промогрупп пользователя {user_id}: {error}")
|
||
return []
|
||
|
||
|
||
async def get_primary_user_promo_group(
|
||
db: AsyncSession,
|
||
user_id: int
|
||
) -> Optional[PromoGroup]:
|
||
"""
|
||
Получает промогруппу пользователя с максимальным приоритетом.
|
||
|
||
Args:
|
||
db: Сессия БД
|
||
user_id: ID пользователя
|
||
|
||
Returns:
|
||
PromoGroup с максимальным приоритетом или None
|
||
"""
|
||
try:
|
||
user_promo_groups = await get_user_promo_groups(db, user_id)
|
||
|
||
if not user_promo_groups:
|
||
return None
|
||
|
||
# Первая в списке имеет максимальный приоритет (список уже отсортирован)
|
||
return user_promo_groups[0].promo_group if user_promo_groups[0].promo_group else None
|
||
|
||
except Exception as error:
|
||
logger.error(f"Ошибка получения primary промогруппы пользователя {user_id}: {error}")
|
||
return None
|
||
|
||
|
||
async def has_user_promo_group(
|
||
db: AsyncSession,
|
||
user_id: int,
|
||
promo_group_id: int
|
||
) -> bool:
|
||
"""
|
||
Проверяет наличие промогруппы у пользователя.
|
||
|
||
Args:
|
||
db: Сессия БД
|
||
user_id: ID пользователя
|
||
promo_group_id: ID промогруппы
|
||
|
||
Returns:
|
||
True если пользователь уже имеет эту промогруппу
|
||
"""
|
||
try:
|
||
result = await db.execute(
|
||
select(UserPromoGroup).where(
|
||
and_(
|
||
UserPromoGroup.user_id == user_id,
|
||
UserPromoGroup.promo_group_id == promo_group_id
|
||
)
|
||
)
|
||
)
|
||
return result.scalar_one_or_none() is not None
|
||
|
||
except Exception as error:
|
||
logger.error(f"Ошибка проверки промогруппы пользователя: {error}")
|
||
return False
|
||
|
||
|
||
async def count_user_promo_groups(
|
||
db: AsyncSession,
|
||
user_id: int
|
||
) -> int:
|
||
"""
|
||
Подсчитывает количество промогрупп у пользователя.
|
||
|
||
Args:
|
||
db: Сессия БД
|
||
user_id: ID пользователя
|
||
|
||
Returns:
|
||
Количество промогрупп
|
||
"""
|
||
try:
|
||
result = await db.execute(
|
||
select(UserPromoGroup).where(UserPromoGroup.user_id == user_id)
|
||
)
|
||
return len(list(result.scalars().all()))
|
||
|
||
except Exception as error:
|
||
logger.error(f"Ошибка подсчета промогрупп пользователя: {error}")
|
||
return 0
|
||
|
||
|
||
async def replace_user_promo_groups(
|
||
db: AsyncSession,
|
||
user_id: int,
|
||
promo_group_ids: List[int],
|
||
assigned_by: str = "admin"
|
||
) -> bool:
|
||
"""
|
||
Заменяет все промогруппы пользователя на новый список.
|
||
|
||
Args:
|
||
db: Сессия БД
|
||
user_id: ID пользователя
|
||
promo_group_ids: Список ID промогрупп
|
||
assigned_by: Кто назначил
|
||
|
||
Returns:
|
||
True если успешно
|
||
"""
|
||
try:
|
||
# Удаляем все текущие промогруппы
|
||
await db.execute(
|
||
select(UserPromoGroup).where(UserPromoGroup.user_id == user_id)
|
||
)
|
||
result = await db.execute(
|
||
select(UserPromoGroup).where(UserPromoGroup.user_id == user_id)
|
||
)
|
||
for upg in result.scalars().all():
|
||
await db.delete(upg)
|
||
|
||
# Добавляем новые
|
||
for promo_group_id in promo_group_ids:
|
||
user_promo_group = UserPromoGroup(
|
||
user_id=user_id,
|
||
promo_group_id=promo_group_id,
|
||
assigned_by=assigned_by
|
||
)
|
||
db.add(user_promo_group)
|
||
|
||
await db.commit()
|
||
logger.info(f"Промогруппы пользователя {user_id} заменены на {promo_group_ids}")
|
||
return True
|
||
|
||
except Exception as error:
|
||
logger.error(f"Ошибка замены промогрупп пользователя: {error}")
|
||
await db.rollback()
|
||
return False
|