Проблема: - Монолитный notification_signals.py (~520 строк) смешивал разные concerns - Сложная логика с множественными сигналами и обработчиками в одном файле - Логирование и бизнес-логика переплетены с signal receivers Решение: Разделен на модульную структуру documents/notifications/: - config.py - NotificationVerbs, MessageTemplates, ActionURLs, get_uploader_name(), get_bulk_threshold() - handlers.py - notify_all_employees(), notify_specific_users(), notify_department_employees(), notify_all_acknowledged(), notify_document_comment(), notify_comment_reply() - signals.py - Django signal receivers (@receiver) для Document, DocumentAcknowledgement, DocumentComment - __init__.py - публичный API модуля Изменения: - Создан: documents/notifications/ модуль с 4 файлами - Переименован: notification_signals.py → notification_signals.py.old - Изменен: apps.py - обновлен импорт на documents.notifications Логика уведомлений: 1. Новый документ → все сотрудники/отделы/конкретные получатели 2. Все ознакомились → уведомление загрузившему 3. Комментарий → автор документа 4. Ответ на комментарий → автор родительского комментария Преимущества: - ✅ Единообразие с requests_app, procurement, feed, communications - ✅ Четкое разделение ответственности (SRP) - ✅ Централизованное логирование с контекстом - ✅ Поддержка bulk режима для массовых рассылок - ✅ Легче тестировать компоненты - ✅ Публичный API для переиспользования Верификация: - python manage.py check - успешно
115 lines
4.1 KiB
Python
115 lines
4.1 KiB
Python
"""
|
||
Конфигурация уведомлений для модуля Documents.
|
||
|
||
Содержит:
|
||
- NotificationVerbs - глаголы для уведомлений
|
||
- MessageTemplates - шаблоны сообщений
|
||
- ActionURLs - URL-адреса действий
|
||
"""
|
||
|
||
from django.conf import settings
|
||
|
||
|
||
class NotificationVerbs:
|
||
"""Глаголы (verbs) для уведомлений documents."""
|
||
|
||
DOCUMENT_READY = 'document_ready'
|
||
DOCUMENT_SIGNED_ALL = 'document_signed_all'
|
||
DOCUMENT_COMMENT = 'document_comment'
|
||
DOCUMENT_COMMENT_REPLY = 'document_comment_reply'
|
||
|
||
|
||
class MessageTemplates:
|
||
"""Шаблоны сообщений для уведомлений documents."""
|
||
|
||
@staticmethod
|
||
def document_ready(uploader_name: str, document_title: str) -> str:
|
||
"""Шаблон для нового документа на ознакомление."""
|
||
return (
|
||
f'{uploader_name} загрузил документ "{document_title}". '
|
||
f'Требуется ознакомление.'
|
||
)
|
||
|
||
@staticmethod
|
||
def document_ready_title() -> str:
|
||
"""Заголовок для нового документа."""
|
||
return 'Новый документ на ознакомление'
|
||
|
||
@staticmethod
|
||
def all_acknowledged(document_title: str) -> str:
|
||
"""Шаблон для завершения ознакомления всеми."""
|
||
return f'Все сотрудники ознакомились с документом "{document_title}"'
|
||
|
||
@staticmethod
|
||
def all_acknowledged_title() -> str:
|
||
"""Заголовок для завершения ознакомления."""
|
||
return 'Все ознакомились с документом'
|
||
|
||
@staticmethod
|
||
def comment(author_name: str, document_title: str) -> str:
|
||
"""Шаблон для нового комментария к документу."""
|
||
return (
|
||
f'{author_name} оставил комментарий '
|
||
f'к вашему документу "{document_title}"'
|
||
)
|
||
|
||
@staticmethod
|
||
def comment_title() -> str:
|
||
"""Заголовок для комментария к документу."""
|
||
return 'Новый комментарий к документу'
|
||
|
||
@staticmethod
|
||
def comment_reply(author_name: str, document_title: str) -> str:
|
||
"""Шаблон для ответа на комментарий."""
|
||
return (
|
||
f'{author_name} ответил на ваш '
|
||
f'комментарий к документу "{document_title}"'
|
||
)
|
||
|
||
@staticmethod
|
||
def comment_reply_title() -> str:
|
||
"""Заголовок для ответа на комментарий."""
|
||
return 'Ответ на ваш комментарий'
|
||
|
||
|
||
class ActionURLs:
|
||
"""URL-адреса для действий с документами."""
|
||
|
||
DOCUMENTS = '/documents'
|
||
|
||
@staticmethod
|
||
def document_detail(document_id: int) -> str:
|
||
"""
|
||
Возвращает URL-адрес документа.
|
||
|
||
TODO: реализовать прямые ссылки на конкретные документы
|
||
"""
|
||
return f'/documents?id={document_id}'
|
||
|
||
|
||
# ===== Константы =====
|
||
|
||
def get_bulk_threshold() -> int:
|
||
"""
|
||
Возвращает порог для определения массовой рассылки.
|
||
|
||
Returns:
|
||
int: Количество получателей, начиная с которого используется bulk режим
|
||
"""
|
||
return getattr(settings, 'NOTIFICATION_BULK_THRESHOLD', 10)
|
||
|
||
|
||
def get_uploader_name(uploaded_by) -> str:
|
||
"""
|
||
Возвращает имя загрузившего документ пользователя.
|
||
|
||
Args:
|
||
uploaded_by: Объект Employee или None
|
||
|
||
Returns:
|
||
str: Полное имя или 'Администратор'
|
||
"""
|
||
if uploaded_by:
|
||
return uploaded_by.get_full_name() or uploaded_by.username
|
||
return 'Администратор'
|