uchill/backend/apps/chat/services.py

112 lines
4.1 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.

"""
Сервисы для системы чата.
Централизованная логика создания и управления чатами.
"""
from django.db import transaction
from django.db.models import Q
from .models import Chat, ChatParticipant
class ChatService:
"""
Сервис для работы с чатами.
Обеспечивает атомарность операций и предотвращает создание дубликатов.
"""
@staticmethod
def get_or_create_direct_chat(user1, user2, created_by=None):
"""
Получить или создать личный чат между двумя пользователями.
Использует блокировку для предотвращения race condition
при одновременных запросах на создание чата.
Args:
user1: Первый пользователь (User)
user2: Второй пользователь (User)
created_by: Кто создает чат (по умолчанию user1)
Returns:
tuple: (chat, created) - объект чата и флаг создания
"""
if user1.id == user2.id:
raise ValueError("Нельзя создать чат с самим собой")
# Нормализуем порядок пользователей для консистентного поиска
users = sorted([user1, user2], key=lambda u: u.id)
with transaction.atomic():
# Ищем существующий чат между пользователями
# Используем select_for_update для блокировки найденных записей
existing_chat = Chat.objects.select_for_update().filter(
chat_type='direct',
participants__user=users[0]
).filter(
participants__user=users[1]
).distinct().first()
if existing_chat:
return existing_chat, False
# Чата нет - создаем новый
creator = created_by or users[0]
chat = Chat.objects.create(
chat_type='direct',
created_by=creator
)
# Определяем роли участников
# Создатель становится админом
ChatParticipant.objects.create(
chat=chat,
user=users[0],
role='admin' if users[0] == creator else 'member'
)
ChatParticipant.objects.create(
chat=chat,
user=users[1],
role='admin' if users[1] == creator else 'member'
)
return chat, True
@staticmethod
def get_direct_chat(user1, user2):
"""
Получить существующий личный чат между двумя пользователями.
Args:
user1: Первый пользователь
user2: Второй пользователь
Returns:
Chat или None
"""
return Chat.objects.filter(
chat_type='direct',
participants__user=user1
).filter(
participants__user=user2
).distinct().first()
@staticmethod
def ensure_participant(chat, user, role='member'):
"""
Убедиться что пользователь является участником чата.
Если нет - добавить его.
Args:
chat: Чат
user: Пользователь
role: Роль (по умолчанию 'member')
Returns:
tuple: (participant, created)
"""
return ChatParticipant.objects.get_or_create(
chat=chat,
user=user,
defaults={'role': role}
)