# app/services/user_service.py import logging import re from typing import Dict, List, Optional, Tuple, Any from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import joinedload from app import db from app.extensions.bot_send import bot_send_message from app.models import Users # Предполагаем, что app.models/__init__.py экспортирует Users from app.extensions.audit_logger import AuditLogger logger = logging.getLogger(__name__) auditlog = AuditLogger(db.session) def get_users(page: int, per_page: int) -> Dict[str, Any]: logger.debug(f"Получение пользователей: page={page}, per_page={per_page}") users_query = Users.query.options(joinedload(Users.subscriptions)) users_paginated = users_query.paginate(page=page, per_page=per_page, error_out=False) users_list: List[Dict[str, Any]] = [] for user in users_paginated.items: user_data: Dict[str, Any] = { 'chat_id': user.chat_id, 'telegram_id': user.telegram_id, 'email': user.user_email, 'subscriptions': [], 'disaster_only': "Все уведомления", 'status': "Активен" if not user.is_blocked else "Заблокирован", 'blocked': user.is_blocked } if user.subscriptions: for subscription in user.subscriptions: if subscription.active and not subscription.skip: user_data['subscriptions'].append(subscription.region_id) if subscription.disaster_only: user_data['disaster_only'] = "Только критические уведомления" users_list.append(user_data) logger.debug(f"Получено пользователей: {len(users_list)} элементов") return { 'users': users_list, 'total_users': users_paginated.total, 'total_pages': users_paginated.pages, 'current_page': users_paginated.page, 'per_page': users_paginated.per_page } def get_user(chat_id: int) -> Optional[Dict[str, Any]]: logger.debug(f"Получение пользователя: chat_id={chat_id}") user: Optional[Users] = Users.query.filter_by(chat_id=chat_id).first() if user: user_data: Dict[str, Any] = { 'chat_id': user.chat_id, 'telegram_id': user.telegram_id, 'email': user.user_email, 'blocked': user.is_blocked } logger.debug(f"Пользователь найден: chat_id={chat_id}") return user_data else: logger.warning(f"Пользователь не найден: chat_id={chat_id}") return None def toggle_block_user(chat_id: int, actor_user: Any) -> Tuple[Dict[str, Any], int]: logger.debug(f"Переключение блокировки пользователя: chat_id={chat_id}") user: Optional[Users] = Users.query.filter_by(chat_id=chat_id).first() if not user: error_msg = "Пользователь не найден" auditlog.users(action_type="toggle_block", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, error=error_msg) logger.warning(f"{error_msg}: chat_id={chat_id}") return {'status': 'error', 'message': error_msg}, 404 try: user.is_blocked = not user.is_blocked db.session.commit() status_text = "заблокирован" if user.is_blocked else "разблокирован" logger.info(f"Пользователь {chat_id} {status_text}") action_type = "block" if user.is_blocked else "unblock" auditlog.users( action_type=action_type, actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, email=user.user_email, telegram_id=user.telegram_id, ) return {'status': 'updated', 'new_status': user.is_blocked}, 200 except Exception as e: db.session.rollback() error_msg = str(e) logger.error(f"Ошибка при переключении блокировки пользователя {chat_id}: {error_msg}") auditlog.users(action_type="toggle_block", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, error=error_msg) return {'status': 'error', 'message': error_msg}, 500 def delete_user(chat_id: int, actor_user: Any) -> Tuple[Dict[str, Any], int]: logger.info(f"Удаление пользователя: chat_id={chat_id}") user: Optional[Users] = Users.query.filter_by(chat_id=chat_id).first() if not user: error_msg = "Пользователь не найден" auditlog.users(action_type="delete", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, error=error_msg) logger.warning(f"{error_msg}: chat_id={chat_id}") return {'status': 'error', 'message': error_msg}, 404 try: db.session.delete(user) db.session.commit() logger.info(f"Пользователь удален: chat_id={chat_id}") auditlog.users( action_type="delete", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, email=user.user_email, telegram_id=user.telegram_id, ) return {'status': 'deleted', 'message': 'Пользователь удален'}, 200 except Exception as e: db.session.rollback() error_msg = str(e) logger.error(f"Ошибка при удалении пользователя {chat_id}: {error_msg}") auditlog.users(action_type="delete", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, error=error_msg) return {'status': 'error', 'message': error_msg}, 500 def add_user(user_data: Dict[str, Any], actor_user: Any) -> Tuple[Dict[str, str], int]: logger.info(f"Добавление пользователя: {user_data}") chat_id = None telegram_id = user_data.get('telegram_id') user_email = user_data.get('user_email') try: try: chat_id = int(user_data.get('chat_id')) except (ValueError, TypeError): error_msg = 'Chat ID должен быть числом' auditlog.users(action_type="add", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, telegram_id=telegram_id, email=user_email, error=error_msg) logger.warning(error_msg) return {'error': error_msg}, 400 if not telegram_id or not re.match(r'^@.*$', telegram_id): error_msg = "Telegram ID должен начинаться с символа @" auditlog.users(action_type="add", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, telegram_id=telegram_id, email=user_email, error=error_msg) logger.warning(error_msg) return {'error': error_msg}, 400 if not user_email or not re.match(r'.*@rtmis.ru$', user_email): error_msg = "Email должен содержать домен @rtmis.ru" auditlog.users(action_type="add", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, telegram_id=telegram_id, email=user_email, error=error_msg) logger.warning(error_msg) return {'error': error_msg}, 400 existing_user = Users.query.filter( (Users.user_email == user_email) | (Users.telegram_id == telegram_id) | (Users.chat_id == chat_id) ).first() if existing_user: error_msg = 'Пользователь с таким Chat ID, Telegram ID или Email уже существует.' auditlog.users(action_type="add", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, telegram_id=telegram_id, email=user_email, error=error_msg) logger.warning(error_msg) return {'error': error_msg}, 409 new_user: Users = Users( chat_id=chat_id, telegram_id=telegram_id, user_email=user_email, is_blocked=user_data.get('is_blocked', False) ) db.session.add(new_user) db.session.commit() logger.info(f"Пользователь добавлен успешно: {new_user.user_email}") auditlog.users(action_type="add", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, telegram_id=telegram_id, email=user_email) try: bot_send_message(chat_id,"Регистрация пройдена успешно. нажмите продолжить что бы начать пользоваться ботом!") except Exception as e: logger.info(f"Ошибка при отправке сообщения {chat_id}: {e}") return {'message': 'Пользователь добавлен успешно'}, 201 except IntegrityError as e: db.session.rollback() error_msg = 'Ошибка уникальности данных' logger.error(f"Ошибка уникальности при добавлении пользователя: {e}") auditlog.users(action_type="add", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, telegram_id=telegram_id, email=user_email, error=error_msg) return {'error': error_msg}, 409 except Exception as e: db.session.rollback() error_msg = f'Ошибка при добавлении пользователя: {type(e).__name__}: {e}' logger.error(error_msg) auditlog.users(action_type="add", actor_display_name=actor_user.display_name, ldap_user_id=actor_user.id, affected_chat_id=chat_id, telegram_id=telegram_id, email=user_email, error=error_msg) return {'error': 'Ошибка при добавлении пользователя'}, 500 def search_users(telegram_id: Optional[str] = None, email: Optional[str] = None) -> List[Dict[str, Any]]: logger.debug(f"Поиск пользователей: telegram_id={telegram_id}, email={email}") query = db.session.query(Users) if telegram_id: query = query.filter(Users.telegram_id.ilike(f"%{telegram_id}%")) if email: query = query.filter(Users.user_email.ilike(f"%{email}%")) users: List[Users] = query.all() users_list: List[Dict[str, Any]] = [] for user in users: # Используем названия полей модели напрямую user_data: Dict[str, Any] = { 'chat_id': user.chat_id, 'telegram_id': user.telegram_id, 'email': user.user_email, 'blocked': user.is_blocked } users_list.append(user_data) logger.debug(f"Найдено пользователей: {len(users_list)}") return users_list