Уведомления
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 и т.д.) для глубоких ссылок из центра уведомлений - Ответы на комментарии уведомляют автора родительского комментария (не только создателя/исполнителя задачи)
- Количество непрочитанных отображается в виде значка в заголовке