Corp/backend/notifications/services.py
lizerk d5b4ae9f43 refactor: Полный переход на новую архитектуру уведомлений v2.0
BREAKING CHANGES:
- Удалена старая система (6 моделей → 2 модели)
- models_new.py → models.py (Notification, UserChannelPreferences)
- signals_new.py → signals.py (notify.send API)
- admin_new.py → admin.py

Новая архитектура:
- Все отправки через Celery (полностью асинхронно)
- Модульная структура tasks/ (base, email, push, websocket)
- Каждый канал - отдельный класс задачи наследующий BaseNotificationTask
- Rate limiting, retry, приоритеты из коробки

Обратная совместимость:
- Создан services.py facade для старого API
- NotificationService.create_notification() работает через notify.send()
- Старый код продолжает работать без изменений

Удалено:
- Старые models.py, signals.py, admin.py, tasks.py (→ .old)
- telegram_bot.py, telegram_models.py (Telegram отключен)
- test_views.py, views.py, urls.py (устаревшие)

Создано:
- tasks/ модуль с классовой архитектурой (как senders/)
- tasks/base.py - BaseNotificationTask
- tasks/email.py - EmailNotificationTask + DigestEmailTask
- tasks/push.py - PushNotificationTask
- tasks/websocket.py - WebSocketNotificationTask

Обновлено:
- apps.py - загружает signals, channels, tasks
- channels.py - все отправки через Celery задачи
- Все импорты models_new → models
2026-03-09 16:36:14 +03:00

200 lines
7.2 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.

"""
Слой обратной совместимости для старого API уведомлений.
DEPRECATED: Этот модуль предоставляет facade для старого кода.
Новый код должен использовать напрямую:
- from notifications.signals import notify
- notify.send(sender=user, recipient=other, verb='liked', ...)
"""
import logging
from typing import Optional, Dict, Any
from django.contrib.auth import get_user_model
logger = logging.getLogger(__name__)
User = get_user_model()
class NotificationService:
"""
Facade для обратной совместимости со старым API.
Все методы теперь работают через новую систему (notify.send()).
"""
@staticmethod
def create_notification(
recipient: User,
notification_type_code: str,
title: str,
message: str,
content_object=None,
action_url: str = '',
action_text: str = 'Посмотреть',
metadata: Optional[Dict[str, Any]] = None,
send_immediately: bool = True,
):
"""
Создает уведомление (старый API → новая система).
DEPRECATED: Используйте notify.send() напрямую.
Args:
recipient: Получатель уведомления
notification_type_code: Код типа (mapping в verb)
title: Заголовок (не используется в новой системе)
message: Описание уведомления
content_object: Объект-источник (→ action_object)
action_url: URL для действия
action_text: Текст кнопки (не используется)
metadata: Дополнительные данные
send_immediately: Игнорируется (всегда через Celery)
Returns:
Notification объект (новая модель)
"""
from .signals import notify
# Маппинг старых типов на новые verbs
type_to_verb = {
'message': 'messaged',
'message_new': 'messaged',
'document': 'shared',
'document_new': 'shared',
'request': 'created',
'request_status_changed': 'updated',
'request_comment_added': 'commented',
'feed_post': 'posted',
'feed_comment': 'commented',
'feed_like': 'liked',
'calendar_event': 'scheduled',
'procurement': 'created',
'procurement_approved': 'approved',
'procurement_rejected': 'rejected',
}
verb = type_to_verb.get(notification_type_code, 'notified')
# Создаем через новую систему
notification = notify.send(
sender=None, # System notification
recipient=recipient,
verb=verb,
action_object=content_object,
description=message,
action_url=action_url,
)
# notify.send() возвращает список [(signal, [notifications])]
if notification and len(notification) > 0:
return notification[0][1][0] if notification[0][1] else None
return None
@staticmethod
def create_notification_async(
recipient: User,
notification_type_code: str,
title: str,
message: str,
content_object=None,
action_url: str = '',
action_text: str = 'Посмотреть',
metadata: Optional[Dict[str, Any]] = None,
):
"""
Асинхронное создание уведомления.
DEPRECATED: Новая система всегда асинхронна через Celery.
Этот метод теперь идентичен create_notification().
"""
return NotificationService.create_notification(
recipient=recipient,
notification_type_code=notification_type_code,
title=title,
message=message,
content_object=content_object,
action_url=action_url,
action_text=action_text,
metadata=metadata,
send_immediately=True,
)
@staticmethod
def mark_as_read(notification_id: int, user: User) -> bool:
"""
Отмечает уведомление как прочитанное.
Args:
notification_id: ID уведомления
user: Пользователь (проверка владения)
Returns:
True если успешно, False иначе
"""
try:
from .models import Notification
notification = Notification.objects.get(
id=notification_id,
recipient=user
)
notification.mark_as_read()
return True
except Notification.DoesNotExist:
logger.warning(f"Notification {notification_id} not found for user {user.id}")
return False
@staticmethod
def mark_all_as_read(user: User, category: Optional[str] = None) -> int:
"""
Отмечает все уведомления как прочитанные.
Args:
user: Пользователь
category: Категория (не используется в новой системе)
Returns:
Количество отмеченных уведомлений
"""
from .models import Notification
return Notification.objects.filter(
recipient=user,
unread=True
).mark_all_as_read()
@staticmethod
def get_user_settings(user: User, setting_type: Optional[str] = None):
"""
Получает настройки уведомлений пользователя.
DEPRECATED: Используйте UserChannelPreferences напрямую.
Args:
user: Пользователь
setting_type: Тип настройки (игнорируется)
Returns:
UserChannelPreferences объект
"""
try:
return user.channel_preferences
except Exception:
from .models import UserChannelPreferences
return UserChannelPreferences.objects.create(user=user)
@staticmethod
def send_websocket_notification(user_id: int, notification_data: Optional[dict] = None):
"""
Отправка WebSocket уведомления.
DEPRECATED: WebSocket уведомления отправляются автоматически
через channels.py при создании Notification.
"""
logger.warning(
"NotificationService.send_websocket_notification() is deprecated. "
"WebSocket notifications are sent automatically via channels.py"
)
return True