Corp/docs/in-progress/CHAT_NOTIFICATIONS_ANALYSIS.md
Игорь Надеин 42879e0981 feat(communications): Реализация управления уведомлениями чатов и оптимизация
- Добавлен API endpoint для включения/выключения уведомлений чата
- Реализован frontend для управления уведомлениями через меню чата
- Обновлены сигналы для проверки настроек notifications_enabled
- Стандартизирована логика уведомлений для всех типов чатов
- Оптимизированы запросы к БД (1 запрос вместо N для ChatUserSettings)
- Переведены детальные логи с INFO на DEBUG уровень

Файлы:
- backend/api/v1/communications/views.py (новый endpoint)
- backend/api/v1/urls.py (новый маршрут)
- backend/static/js/components/chatMenuActions.js (полная реализация)
- backend/communications/notification_signals.py (проверка настроек + оптимизация)
- backend/notifications/services.py (оптимизация логирования)
2026-01-08 14:39:40 +03:00

15 KiB
Raw Blame History

Анализ функции "Уведомления" в контекстном меню чатов

Дата проверки: 8 января 2026 г.
Файл: templates/includes/components/chat_menu.html
Элемент: <a data-action="notifications">


🔍 Что найдено

1. HTML элемент (UI)

Местоположение: backend/templates/includes/components/chat_menu.html:22

<a class="dropdown-item" href="#" data-action="notifications">
  <i class="bi-bell me-2"></i>Уведомления
</a>

Используется в:

  • templates/includes/components/chat_card.html (строка 25)
  • templates/communications/chat_list.html (6 раз - для каждого типа чатов)

Статус: ✅ Элемент есть, отображается в меню каждого чата


2. JavaScript обработчик

Файл: backend/static/js/components/chatMenuActions.js

Функция: handleNotificationsAction() (строка 110)

function handleNotificationsAction(chatId, chatName) {
  showDevMessage('Функция настройки уведомлений находится в разработке', 'info');
  
  // TODO: Реализовать API endpoint для управления уведомлениями
  /*
  fetch(`/communications/api/chat/${chatId}/notifications/`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRFToken': getCsrfToken()
    },
    body: JSON.stringify({ enabled: false })
  })
  .then(response => response.json())
  .then(data => {
    if (data.ok) {
      console.log('Notifications updated');
    }
  });
  */
}

Статус: 🔴 ЗАГЛУШКА! Показывает toast уведомление "Функция настройки уведомлений находится в разработке"

Что происходит при клике:

  1. Пользователь кликает на "Уведомления"
  2. Dropdown меню закрывается
  3. Показывается blue toast с иконкой info: "Функция настройки уведомлений находится в разработке"
  4. Никакие API не вызываются
  5. Никакие настройки не меняются

3. API Endpoint

Ожидаемый URL: /api/v1/communications/chats/<chat_id>/notifications/

Статус: ❌ НЕ СУЩЕСТВУЕТ!

Проверено в:

  • ✅ backend/api/v1/urls.py - маршрута нет
  • ✅ backend/api/v1/communications/views.py - функции нет

Существующий похожий endpoint:

# Есть для закрепления (PIN):
path("communications/chats/<int:chat_id>/pin/", pin_chat, name="pin_chat")

# НЕТ для уведомлений:
# path("communications/chats/<int:chat_id>/notifications/", toggle_notifications, name="toggle_notifications")

4. Backend модель

Модель: ChatUserSettings в communications/models.py:304

class ChatUserSettings(models.Model):
    """Персональные настройки пользователя для чата"""
    chat = models.ForeignKey(Chat, on_delete=models.CASCADE, related_name='user_settings')
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    
    # Закрепление
    is_pinned = models.BooleanField(default=False, verbose_name="Закреплен")
    pinned_at = models.DateTimeField(null=True, blank=True, verbose_name="Время закрепления")
    pin_order = models.IntegerField(default=0, verbose_name="Порядок закрепленных")
    
    # Уведомления ✅
    notifications_enabled = models.BooleanField(default=True, verbose_name="Уведомления")
    
    # Кастомное название (если пользователь переименовал)
    custom_name = models.CharField(max_length=255, blank=True, verbose_name="Свое название")
    
    # Скрыть чат
    is_hidden = models.BooleanField(default=False, verbose_name="Скрыт")
    
    class Meta:
        unique_together = [('chat', 'user')]
        ordering = ['-is_pinned', '-pinned_at']

Статус: ✅ Поле notifications_enabled существует!


5. Использование в сигналах

Файл: backend/communications/notification_signals.py

Проблема: ❌ Поле notifications_enabled НЕ ПРОВЕРЯЕТСЯ!

@receiver(post_save, sender=Message)
def create_message_notifications(sender, instance, created, **kwargs):
    """Создает уведомления при создании нового сообщения"""
    if not created or instance.is_system or instance.is_deleted:
        return
    
    chat = instance.chat
    author = instance.author
    content = instance.content
    
    # Получаем всех участников чата кроме автора
    if chat.type in ['announcement', 'channel', 'department', 'global']:
        participants = chat.get_participants.exclude(id=author.id)
    else:
        participants = chat.participants.exclude(id=author.id)
    
    # ❌ ПРОБЛЕМА: Не проверяется ChatUserSettings.notifications_enabled!
    # Уведомления отправляются ВСЕМ участникам независимо от их настроек
    
    for recipient in recipients_for_new_message:
        NotificationService.create_notification(
            recipient=recipient,
            notification_type_code='chat_new_message',
            title=f'Новое сообщение от {author.get_full_name() or author.username}',
            message=truncate_message(content, 100),
            # ...
        )

Должно быть:

for recipient in recipients_for_new_message:
    # Проверяем настройки уведомлений пользователя для этого чата
    chat_settings = ChatUserSettings.objects.filter(
        chat=chat,
        user=recipient
    ).first()
    
    # Пропускаем, если уведомления отключены
    if chat_settings and not chat_settings.notifications_enabled:
        continue
    
    NotificationService.create_notification(...)

📊 Сводная таблица статусов

Компонент Статус Описание
HTML элемент ✅ Есть Кнопка "Уведомления" в меню чата
JavaScript обработчик 🔴 Заглушка Показывает toast "в разработке"
API endpoint ❌ Нет /communications/chats/<id>/notifications/ не существует
Backend функция ❌ Нет toggle_chat_notifications() не реализована
Модель ChatUserSettings ✅ Есть Поле notifications_enabled существует
Проверка в сигналах ❌ Нет Настройки не проверяются при отправке
Маршрут в urls.py ❌ Нет Не зарегистрирован

❌ Вердикт: ПОЛНОСТЬЮ НЕ РАБОТАЕТ

Что реально происходит:

  1. При клике на "Уведомления":

    • ✅ Меню закрывается
    • ✅ Показывается toast "Функция в разработке"
    • ❌ API не вызывается
    • ❌ Настройки не меняются
    • ❌ Ничего не сохраняется
  2. При отправке сообщения в чат:

    • ✅ Сигнал срабатывает
    • ✅ Уведомления создаются
    • ❌ Настройка notifications_enabled ИГНОРИРУЕТСЯ
    • ❌ Уведомления приходят ВСЕМ независимо от настроек
  3. При попытке отключить уведомления:

    • ❌ Функция не реализована
    • ❌ Изменить настройки невозможно (нет UI)
    • ❌ База данных не обновляется

🔧 Что нужно реализовать

Шаг 1: Создать API endpoint

Файл: backend/api/v1/communications/views.py

Добавить функцию:

@login_required
@require_POST
def toggle_chat_notifications(request, chat_id):
    """Включение/выключение уведомлений для чата"""
    import json
    
    chat = get_object_or_404(Chat, pk=chat_id)
    
    # Проверка доступа к чату
    if not user_can_access_chat(chat, request.user):
        return JsonResponse(
            {'ok': False, 'error': 'Access denied'}, 
            status=403
        )
    
    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse(
            {'ok': False, 'error': 'Invalid JSON'}, 
            status=400
        )
    
    enabled = data.get('enabled', True)
    
    settings, created = ChatUserSettings.objects.get_or_create(
        chat=chat,
        user=request.user
    )
    
    settings.notifications_enabled = enabled
    settings.save()
    
    return JsonResponse({
        'ok': True, 
        'enabled': enabled,
        'message': f'Уведомления {"включены" if enabled else "отключены"}'
    })

Шаг 2: Добавить маршрут

Файл: backend/api/v1/urls.py

После строки с pin_chat добавить:

path(
    "communications/chats/<int:chat_id>/notifications/",
    toggle_chat_notifications,
    name="toggle_chat_notifications"
),

Шаг 3: Обновить JavaScript

Файл: backend/static/js/components/chatMenuActions.js

Заменить функцию handleNotificationsAction:

async function handleNotificationsAction(chatId, chatName) {
  try {
    // Получаем текущее состояние (можно хранить в data-атрибуте)
    const chatRow = document.querySelector(`[data-chat-id="${chatId}"]`);
    const currentlyEnabled = chatRow?.dataset.notificationsEnabled !== 'false';
    
    // Переключаем состояние
    const newState = !currentlyEnabled;
    
    const response = await fetch(`/api/v1/communications/chats/${chatId}/notifications/`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRFToken': getCsrfToken()
      },
      body: JSON.stringify({ enabled: newState })
    });
    
    const data = await response.json();
    
    if (data.ok) {
      // Обновляем data-атрибут
      if (chatRow) {
        chatRow.dataset.notificationsEnabled = newState.toString();
      }
      
      // Показываем toast с результатом
      showSuccessMessage(data.message || `Уведомления ${newState ? 'включены' : 'отключены'}`);
      
      // Можно добавить визуальный индикатор (иконка колокольчика с перечеркиванием)
      updateNotificationsIcon(chatRow, newState);
    } else {
      showErrorMessage(data.error || 'Ошибка при изменении настроек');
    }
  } catch (error) {
    console.error('Notifications toggle error:', error);
    showErrorMessage('Ошибка при изменении настроек уведомлений');
  }
}

Шаг 4: Обновить сигналы

Файл: backend/communications/notification_signals.py

В функции create_message_notifications после получения списка участников добавить проверку:

# Фильтруем получателей с учетом их настроек уведомлений
filtered_recipients = []
for recipient in recipients_for_new_message:
    # Проверяем настройки уведомлений для этого чата
    chat_settings = ChatUserSettings.objects.filter(
        chat=chat,
        user=recipient
    ).first()
    
    # Если настройки не найдены - по умолчанию уведомления включены
    # Если найдены - проверяем флаг
    if not chat_settings or chat_settings.notifications_enabled:
        filtered_recipients.append(recipient)

# Отправляем уведомления только отфильтрованным получателям
for recipient in filtered_recipients:
    NotificationService.create_notification(...)

Шаг 5: Добавить индикатор в UI (опционально)

В templates/includes/components/chat_card.html добавить визуальный индикатор:

<div class="chat-row" 
     data-chat-id="{{ chat.pk }}"
     data-notifications-enabled="{% if chat_settings.notifications_enabled %}true{% else %}false{% endif %}">
  
  {# ... существующий код ... #}
  
  {# Индикатор отключенных уведомлений #}
  {% if not chat_settings.notifications_enabled %}
    <i class="bi-bell-slash text-muted" title="Уведомления отключены"></i>
  {% endif %}
</div>

📝 Приоритет реализации

Оценка работы: 2-3 часа

Сложность: 🟡 Средняя

Приоритет: 🟡 Средний (не критично, но желательно)

Зависимости: Нет

Блокирует: Ничего


✅ Чек-лист реализации

  • Создать функцию toggle_chat_notifications() в views.py
  • Добавить маршрут в urls.py
  • Импортировать функцию в начале urls.py
  • Обновить JavaScript в chatMenuActions.js
  • Добавить проверку в notification_signals.py
  • Протестировать включение/выключение уведомлений
  • Убедиться, что уведомления не приходят при отключенных настройках
  • Добавить индикатор в UI (опционально)
  • Создать миграцию если изменяли модель (не требуется)

🎯 Результат после реализации

После реализации пользователи смогут:

  1. ✅ Кликнуть на "Уведомления" в меню чата
  2. ✅ Увидеть toggle для включения/выключения
  3. ✅ Сохранить свой выбор
  4. ✅ Перестать получать уведомления от выбранного чата
  5. ✅ Увидеть визуальный индикатор (иконка колокольчика)

Уведомления будут приходить только от тех чатов, где они включены!