Corp/backend/requests_app/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

228 lines
8.8 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 для автоматической генерации уведомлений в модуле Requests.
Обрабатывает события:
- Новая заявка (для ответственных/руководителей)
- Одобрение заявки
- Отклонение заявки
- Комментарий к заявке
- Изменение статуса заявки
"""
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from .models import Request, RequestComment
from notifications.services import NotificationService
Employee = get_user_model()
@receiver(post_save, sender=Request)
def create_request_notifications(sender, instance, created, **kwargs):
"""
Создает уведомления при создании или изменении заявки.
Обрабатывает:
1. Новая заявка - уведомление руководителю/ответственным
2. Изменение статуса - уведомление автору
"""
request_obj = instance
if created and request_obj.status != 'draft':
# Новая заявка - уведомляем ответственных
notify_new_request(request_obj)
elif not created:
# Проверяем изменение статуса
# Получаем предыдущее состояние из БД
try:
old_request = Request.objects.get(pk=request_obj.pk)
if hasattr(request_obj, '_old_status'):
old_status = request_obj._old_status
new_status = request_obj.status
if old_status != new_status:
notify_status_change(request_obj, old_status, new_status)
except Request.DoesNotExist:
pass
@receiver(pre_save, sender=Request)
def track_status_change(sender, instance, **kwargs):
"""
Сохраняем старый статус перед обновлением для отслеживания изменений.
"""
if instance.pk:
try:
old_instance = Request.objects.get(pk=instance.pk)
instance._old_status = old_instance.status
except Request.DoesNotExist:
instance._old_status = None
@receiver(post_save, sender=RequestComment)
def create_comment_notification(sender, instance, created, **kwargs):
"""
Создает уведомление при добавлении комментария к заявке.
"""
if not created:
return
comment = instance
request_obj = comment.request
author = comment.author
# Уведомляем автора заявки (если комментарий не от него)
if request_obj.employee.id != author.id:
NotificationService.create_notification(
recipient=request_obj.employee,
notification_type_code='request_comment',
title='Новый комментарий к вашей заявке',
message=(
f'{author.get_full_name() or author.username} '
f'прокомментировал заявку: {comment.text[:100]}'
),
content_object=request_obj,
action_url=f'/requests/{request_obj.id}/',
metadata={
'request_id': request_obj.id,
'request_type': request_obj.type,
'comment_id': comment.id,
'author_id': author.id,
}
)
# Также уведомляем согласующего (если есть и это не он)
if (request_obj.approver and
request_obj.approver.id != author.id and
request_obj.approver.id != request_obj.employee.id):
NotificationService.create_notification(
recipient=request_obj.approver,
notification_type_code='request_comment',
title='Комментарий к заявке',
message=(
f'{author.get_full_name() or author.username} '
f'прокомментировал заявку: {comment.text[:100]}'
),
content_object=request_obj,
action_url=f'/requests/{request_obj.id}/',
metadata={
'request_id': request_obj.id,
'request_type': request_obj.type,
'comment_id': comment.id,
'author_id': author.id,
}
)
# ===== Вспомогательные функции =====
def notify_new_request(request_obj):
"""
Отправляет уведомление о новой заявке ответственным лицам.
"""
# Определяем получателей в зависимости от типа заявки
recipients = []
# 1. Руководитель отдела сотрудника
if request_obj.department and request_obj.department.head:
head = request_obj.department.head
if head.id != request_obj.employee.id:
recipients.append(head)
# 2. Если есть назначенный согласующий
if request_obj.approver:
if request_obj.approver not in recipients:
recipients.append(request_obj.approver)
# 3. Пользователи с правом обрабатывать заявки
users_with_permission = Employee.objects.filter(
user_permissions__codename='can_process_requests'
).exclude(id=request_obj.employee.id)
for user in users_with_permission:
if user not in recipients:
recipients.append(user)
# Отправляем уведомления
for recipient in recipients:
NotificationService.create_notification(
recipient=recipient,
notification_type_code='request_new',
title=f'Новая заявка от {request_obj.employee.get_full_name()}',
message=(
f'Тип: {request_obj.get_type_display()}. '
f'{request_obj.comment[:100] if request_obj.comment else ""}'
),
content_object=request_obj,
action_url=f'/requests/{request_obj.id}/',
metadata={
'request_id': request_obj.id,
'request_type': request_obj.type,
'employee_id': request_obj.employee.id,
'department_id': (
request_obj.department.id
if request_obj.department else None
),
}
)
def notify_status_change(request_obj, old_status, new_status):
"""
Отправляет уведомление при изменении статуса заявки.
"""
# Уведомляем автора заявки
recipient = request_obj.employee
# Определяем тип уведомления и сообщение
if new_status == 'approved':
notification_type = 'request_approved'
title = 'Ваша заявка одобрена'
approver_name = (
request_obj.approver.get_full_name()
if request_obj.approver else 'Руководитель'
)
message = (
f'Заявка "{request_obj.get_type_display()}" '
f'одобрена пользователем {approver_name}'
)
elif new_status == 'rejected':
notification_type = 'request_rejected'
title = 'Ваша заявка отклонена'
approver_name = (
request_obj.approver.get_full_name()
if request_obj.approver else 'Руководитель'
)
message = (
f'Заявка "{request_obj.get_type_display()}" '
f'отклонена пользователем {approver_name}'
)
else:
# Общее уведомление об изменении статуса
notification_type = 'request_status_changed'
title = 'Статус заявки изменен'
message = (
f'Статус заявки "{request_obj.get_type_display()}" '
f'изменен: {old_status} → {new_status}'
)
NotificationService.create_notification(
recipient=recipient,
notification_type_code=notification_type,
title=title,
message=message,
content_object=request_obj,
action_url=f'/requests/{request_obj.id}/',
metadata={
'request_id': request_obj.id,
'request_type': request_obj.type,
'old_status': old_status,
'new_status': new_status,
'approver_id': (
request_obj.approver.id if request_obj.approver else None
),
}
)