Corp/backend/feed/notification_signals.py
Игорь Надеин ddf22c47da fix: автоматическое обновление JWT токенов
- ApiClient теперь обрабатывает 401 и 403 ошибки для обновления токена
- Добавлен JWTRefreshMiddleware для проактивного обновления токенов за 5 минут до истечения
- Добавлена настройка JWT_REFRESH_THRESHOLD_MINUTES (по умолчанию 5)
- Исправлена проблема с периодическими 403 ошибками после истечения access token
2025-11-20 18:52:29 +03:00

183 lines
6.5 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.

"""
Signals для автоматической генерации уведомлений в модуле Feed.
Обрабатывает события:
- Новая публикация (для подписчиков/отдела/компании)
- Комментарий к публикации
- Реакция (лайк) на публикацию - через отдельный механизм
"""
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from .models import Post, Comment
from .constants import TYPE_COMPANY, TYPE_DEPARTMENT, TYPE_EMPLOYEE
from notifications.services import NotificationService
Employee = get_user_model()
@receiver(post_save, sender=Post)
def create_post_notification(sender, instance, created, **kwargs):
"""
Создает уведомления при создании новой публикации.
Уведомления отправляются в зависимости от типа:
- TYPE_COMPANY - всем сотрудникам
- TYPE_DEPARTMENT - сотрудникам отдела
- TYPE_EMPLOYEE - подписчикам автора (если реализовано)
"""
if not created:
return
post = instance
notify_new_post(post)
@receiver(post_save, sender=Comment)
def create_comment_notification(sender, instance, created, **kwargs):
"""
Создает уведомления при добавлении комментария к публикации.
"""
if not created:
return
comment = instance
post = comment.post
comment_author = comment.author
# Уведомляем автора публикации (если комментарий не от него)
if post.author.id != comment_author.id:
NotificationService.create_notification(
recipient=post.author,
notification_type_code='feed_post_comment',
title='Новый комментарий к вашей публикации',
message=(
f'{comment_author.get_full_name() or comment_author.username}'
f' прокомментировал: {truncate_text(comment.text, 80)}'
),
content_object=post,
action_url=f'/feed/post/{post.id}/',
metadata={
'post_id': post.id,
'post_title': post.title,
'comment_id': comment.id,
'author_id': comment_author.id,
}
)
# ===== Вспомогательные функции =====
def notify_new_post(post):
"""
Отправляет уведомления о новой публикации.
"""
recipients = get_post_recipients(post)
# Исключаем автора
recipients = [r for r in recipients if r.id != post.author.id]
# Определяем контекст публикации
if post.type == TYPE_COMPANY:
context = 'компании'
elif post.type == TYPE_DEPARTMENT and post.department:
context = f'отдела {post.department.name}'
else:
context = (
f'от {post.author.get_full_name() or post.author.username}'
)
for recipient in recipients:
NotificationService.create_notification(
recipient=recipient,
notification_type_code='feed_new_post',
title=f'Новая публикация {context}',
message=f'{post.title}',
content_object=post,
action_url=f'/feed/post/{post.id}/',
metadata={
'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 get_post_recipients(post):
"""
Возвращает список получателей уведомлений для публикации.
- 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 []
def truncate_text(text, max_length=100):
"""
Обрезает текст до указанной длины.
"""
if not text:
return ''
text = text.strip()
if len(text) <= max_length:
return text
return text[:max_length - 3] + '...'
# ===== Функция для уведомлений о лайках =====
# Используется во views при добавлении лайка
def notify_post_reaction(post, user):
"""
Создает уведомление о реакции (лайке) на публикацию.
Вызывается вручную из view, так как лайки не имеют отдельной модели.
Args:
post: Публикация, которую лайкнули
user: Пользователь, который поставил лайк
"""
# Уведомляем автора (если лайк не от него)
if post.author.id != user.id:
NotificationService.create_notification(
recipient=post.author,
notification_type_code='feed_post_reaction',
title='Ваша публикация понравилась',
message=(
f'{user.get_full_name() or user.username} '
f'понравилась публикация "{post.title}"'
),
content_object=post,
action_url=f'/feed/post/{post.id}/',
metadata={
'post_id': post.id,
'post_title': post.title,
'user_id': user.id,
'reaction_type': 'like',
}
)