lizerk 7e410ebff8 refactor(feed): реструктуризация notifications в модульную архитектуру
Проблема:
- Монолитный notification_signals.py (~200 строк) смешивал разные concerns
- Сложно поддерживать и понимать логику уведомлений
- Отсутствовала интеграция с API для уведомлений о лайках

Решение:
Разделен на модульную структуру feed/notifications/:
- config.py - NotificationVerbs, MessageTemplates, ActionURLs, truncate_text
- handlers.py - notify_new_post(), notify_comment(), notify_post_reaction(), get_post_recipients()
- signals.py - Django signal receivers (@receiver)
- __init__.py - публичный API модуля

Изменения:
- Создан: feed/notifications/ модуль с 4 файлами
- Переименован: notification_signals.py → notification_signals.py.old
- Изменен: apps.py - обновлен импорт на feed.notifications
- Изменен: api/v1/feed/views.py - добавлен вызов notify_post_reaction() в метод like()

Преимущества:
- ✅ Единообразие с requests_app и procurement
- ✅ Четкое разделение ответственности (SRP)
- ✅ Легче тестировать отдельные компоненты
- ✅ Публичный API через __init__.py
- ✅ Уведомления о лайках теперь работают
- ✅ Централизованные шаблоны сообщений

Верификация:
- python manage.py check - успешно
2026-03-10 13:05:58 +03:00

163 lines
5.7 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.

"""
Бизнес-логика отправки уведомлений для модуля Feed.
Функции:
- notify_new_post - уведомление о новой публикации
- notify_comment - уведомление о комментарии
- notify_post_reaction - уведомление о реакции (лайке)
- get_post_recipients - определение получателей уведомлений
"""
from django.contrib.auth import get_user_model
from notifications.signals import notify
from ..constants import TYPE_COMPANY, TYPE_DEPARTMENT, TYPE_EMPLOYEE
from .config import (
NotificationVerbs,
MessageTemplates,
ActionURLs,
truncate_text,
)
Employee = get_user_model()
def notify_new_post(post):
"""
Отправляет уведомления о новой публикации.
Args:
post: Экземпляр модели Post
"""
recipients = get_post_recipients(post)
# Исключаем автора
recipients = [r for r in recipients if r.id != post.author.id]
# Определяем заголовок в зависимости от типа публикации
if post.type == TYPE_COMPANY:
title = MessageTemplates.new_post_company()
elif post.type == TYPE_DEPARTMENT and post.department:
title = MessageTemplates.new_post_department(post.department.name)
else:
author_name = post.author.get_full_name() or post.author.username
title = MessageTemplates.new_post_employee(author_name)
# Отправляем уведомление каждому получателю
for recipient in recipients:
notify.send(
sender=post.author,
recipient=recipient,
verb=NotificationVerbs.NEW_POST,
action_object=post,
description=post.title,
action_url=ActionURLs.FEED_HOME,
data={
'title': title,
'post_id': post.id,
'post_type': post.type,
'post_title': post.title,
'author_id': post.author.id,
'department_id': (
post.department.id if post.department else None
),
}
)
def notify_comment(comment):
"""
Отправляет уведомление автору публикации о новом комментарии.
Args:
comment: Экземпляр модели Comment
"""
post = comment.post
comment_author = comment.author
# Уведомляем автора публикации (если комментарий не от него)
if post.author.id == comment_author.id:
return
author_name = comment_author.get_full_name() or comment_author.username
comment_text = truncate_text(comment.text, 80)
notify.send(
sender=comment_author,
recipient=post.author,
verb=NotificationVerbs.POST_COMMENT,
action_object=post,
description=MessageTemplates.comment(author_name, comment_text),
action_url=ActionURLs.FEED_HOME,
data={
'title': MessageTemplates.comment_title(),
'post_id': post.id,
'post_title': post.title,
'comment_id': comment.id,
'author_id': comment_author.id,
}
)
def notify_post_reaction(post, user):
"""
Создает уведомление о реакции (лайке) на публикацию.
Вызывается вручную из view, так как лайки не имеют отдельной модели.
Args:
post: Публикация, которую лайкнули
user: Пользователь, который поставил лайк
"""
# Уведомляем автора (если лайк не от него)
if post.author.id == user.id:
return
user_name = user.get_full_name() or user.username
notify.send(
sender=user,
recipient=post.author,
verb=NotificationVerbs.POST_REACTION,
action_object=post,
description=MessageTemplates.reaction(user_name, post.title),
action_url=ActionURLs.FEED_HOME,
data={
'title': MessageTemplates.reaction_title(),
'post_id': post.id,
'post_title': post.title,
'user_id': user.id,
'reaction_type': 'like',
}
)
def get_post_recipients(post):
"""
Возвращает список получателей уведомлений для публикации.
Args:
post: Экземпляр модели Post
Returns:
Список пользователей (Employee), которые должны получить уведомление:
- TYPE_COMPANY - все активные сотрудники
- TYPE_DEPARTMENT - сотрудники отдела
- TYPE_EMPLOYEE - подписчики (пока не реализовано)
"""
if post.type == TYPE_COMPANY:
# Новость компании - всем активным сотрудникам
return list(Employee.objects.filter(is_active=True))
elif post.type == TYPE_DEPARTMENT and post.department:
# Новость отдела - сотрудникам отдела
return list(post.department.employees.filter(is_active=True))
elif post.type == TYPE_EMPLOYEE:
# Личная публикация - подписчикам
# TODO: реализовать систему подписок
# Пока возвращаем пустой список
return []
return []