Skip to content

Уведомления

Planner использует уведомления в реальном времени, доставляемые через WebSocket (Socket.IO). Модуль NotificationsModule@Global(), поэтому любой сервис может внедрить NotificationsService напрямую.

Архитектура

  • Пространство имён: /notifications
  • Аутентификация: На основе токена через handshake.auth.token с использованием JwtService
  • Комнаты: Пользователи присоединяются к комнате user:<id> при подключении
  • Доставка: Отправляется как событие notification:new в комнату пользователя

Типы уведомлений

ТипТриггерДанные
task.assignedЗадача назначена пользователю{ taskId, projectId }
task.status_changedСтатус задачи обновлён{ taskId, projectId, oldStatus, newStatus }
task.comment_addedНовый комментарий или ответ{ taskId, projectId, commentId }
team.member.addedПользователь добавлен в команду{ teamId, teamSlug }
org.member.addedПользователь добавлен в организацию{ orgId }
project.member.addedПользователь добавлен в проект{ projectId }

API Endpoints

МетодПутьОписание
GET/api/notificationsСписок уведомлений пользователя
GET/api/notifications/unread-countКоличество непрочитанных
PATCH/api/notifications/:id/readОтметить как прочитанное
PATCH/api/notifications/read-allОтметить все как прочитанные
DELETE/api/notifications/:idУдалить уведомление

Prisma модель

prisma
model Notification {
  id        String   @id @default(uuid())
  userId    String
  type      String
  title     String
  body      String?
  data      Json?    // Произвольные данные события для глубоких ссылок
  read      Boolean  @default(false)
  createdAt DateTime @default(now())

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId, read])
  @@index([userId, createdAt])
}

Фронтенд

Store уведомлений

typescript
const store = useNotificationsStore()
const { notifications, unreadCount, loading } = storeToRefs(store)

await store.fetchNotifications()
await store.fetchUnreadCount()
await store.markAsRead(id)
await store.markAllAsRead()
await store.deleteNotification(id)

Socket слушатель

Фронтенд подключается к пространству имён /notifications после аутентификации и слушает события notification:new:

typescript
socket.on('notification:new', (notification) => {
  notifications.value.unshift(notification)
  unreadCount.value++
})

Ключевые детали реализации

  • NotificationsService.create() — fire-and-forget (не ожидается) — не замедляет основную операцию
  • JSON-поле data хранит структурированный payload (taskId, projectId и т.д.) для глубоких ссылок из центра уведомлений
  • Ответы на комментарии уведомляют автора родительского комментария (не только создателя/исполнителя задачи)
  • Количество непрочитанных отображается в виде значка в заголовке