From b9d813955d53624f0c49ad90c3b24a669547f0d5 Mon Sep 17 00:00:00 2001 From: kalugin66 Date: Mon, 16 Mar 2026 22:38:16 +0500 Subject: [PATCH] notifications --- notifications.js | 89 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 21 deletions(-) diff --git a/notifications.js b/notifications.js index 124b7a7..d50eac6 100644 --- a/notifications.js +++ b/notifications.js @@ -3,6 +3,12 @@ const postgresLogger = require('./postgres'); const { getDb } = require('./database'); const emailNotifications = require('./email-notifications'); +/** + * Отправляет уведомления о событиях в задаче согласно новой логике: + * - Если инициатор = автор → уведомления всем исполнителям + * - Если инициатор = исполнитель → уведомление только автору + * - Иначе (например, администратор вне задачи) → уведомления всем, кроме инициатора (старое поведение) + */ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, authorId, comment = '', status = '', userName = '') { try { const db = getDb(); @@ -14,7 +20,7 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a console.log(`📢 Начинаем отправку уведомлений для задачи ${taskId}:`); console.log(` Тип: ${type}, Автор действия: ${authorId}, Название: ${taskTitle}`); - // Получаем заказчика + // 1. Получаем автора задачи (создателя) const creator = await new Promise((resolve, reject) => { db.get(` SELECT t.created_by as user_id, u.name as user_name, u.login as user_login, u.email @@ -27,7 +33,12 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a }); }); - // Получаем исполнителей + if (!creator) { + console.error(`❌ Задача ${taskId} не найдена или у неё нет автора`); + return; + } + + // 2. Получаем всех исполнителей задачи const assignees = await new Promise((resolve, reject) => { db.all(` SELECT ta.user_id, u.name as user_name, u.login as user_login, u.email @@ -40,7 +51,7 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a }); }); - // Получаем информацию об авторе действия + // 3. Получаем информацию об авторе действия (инициаторе) const author = await new Promise((resolve, reject) => { db.get("SELECT name, login, email FROM users WHERE id = ?", [authorId], (err, row) => { if (err) reject(err); @@ -50,7 +61,31 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a const authorName = author ? author.name : 'Система'; const authorLogin = author ? author.login : 'system'; - + + // 4. Определяем получателей уведомления согласно новой логике + let recipients = []; + + // Проверяем, является ли инициатор автором задачи + if (parseInt(authorId) === parseInt(creator.user_id)) { + // Инициатор = автор → уведомления всем исполнителям + recipients = assignees; + console.log(` Инициатор является автором. Получатели: ${assignees.length} исполнителей`); + } + // Проверяем, является ли инициатор одним из исполнителей + else { + const isInitiatorAssignee = assignees.some(a => parseInt(a.user_id) === parseInt(authorId)); + if (isInitiatorAssignee) { + // Инициатор = исполнитель → уведомление только автору + recipients = [creator]; + console.log(` Инициатор является исполнителем. Получатель: автор`); + } else { + // Инициатор не является ни автором, ни исполнителем (например, администратор) + // Отправляем уведомления всем участникам, кроме инициатора (старое поведение) + recipients = [creator, ...assignees].filter(p => parseInt(p.user_id) !== parseInt(authorId)); + console.log(` Инициатор вне задачи. Получатели: ${recipients.length} участников (старое поведение)`); + } + } + // Логируем в PostgreSQL (если настроено) let postgresLogIds = []; if (postgresLogger.initialized) { @@ -62,31 +97,29 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a authorId, authorName, authorLogin, - participants: [...(creator ? [{...creator, role: 'creator'}] : []), ...assignees.map(a => ({...a, role: 'assignee'}))], + recipients, comment, status, userName }); } - // Отправляем email уведомления - const participants = [...(creator ? [creator] : []), ...assignees].filter(p => p.user_id !== authorId); - - for (const participant of participants) { + // 5. Отправляем email уведомления выбранным получателям + for (const recipient of recipients) { const taskData = { taskId, title: taskTitle, description: taskDescription, - due_date: null, // Можно добавить получение срока из БД + due_date: null, // при необходимости можно добавить получение срока из БД author_name: authorName, comment: comment, status: status, - user_name: userName || participant.user_name, - hours_left: type === 'deadline' ? 24 : null // Для уведомлений о дедлайне + user_name: userName || recipient.user_name, + hours_left: type === 'deadline' ? 24 : null }; await emailNotifications.sendTaskNotification( - participant.user_id, + recipient.user_id, taskData, type ); @@ -99,7 +132,9 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a } } -// Обновим функцию для уведомлений о дедлайнах +/** + * Отправляет уведомление о приближающемся дедлайне + */ async function sendDeadlineNotification(assignment, hoursLeft) { try { const taskData = { @@ -118,7 +153,7 @@ async function sendDeadlineNotification(assignment, hoursLeft) { 'deadline' ); - // Отправляем уведомление заказчику + // Отправляем уведомление заказчику (автору) await emailNotifications.sendTaskNotification( assignment.created_by, taskData, @@ -132,6 +167,9 @@ async function sendDeadlineNotification(assignment, hoursLeft) { } } +/** + * Проверяет приближающиеся дедлайны и отправляет уведомления + */ async function checkUpcomingDeadlines() { const now = new Date(); const in48Hours = new Date(now.getTime() + 48 * 60 * 60 * 1000).toISOString(); @@ -179,7 +217,9 @@ async function checkUpcomingDeadlines() { }); } -// Вспомогательные функции для работы с PostgreSQL +/** + * Логирует уведомление в PostgreSQL + */ async function logNotificationToPostgres(data) { try { const { @@ -190,7 +230,7 @@ async function logNotificationToPostgres(data) { authorId, authorName, authorLogin, - participants = [], + recipients = [], comment = '', status = '', userName = '', @@ -217,11 +257,9 @@ async function logNotificationToPostgres(data) { break; } - // Логируем для каждого получателя отдельно - const recipientsToNotify = participants.filter(p => p.user_id !== authorId); const logIds = []; - for (const recipient of recipientsToNotify) { + for (const recipient of recipients) { const logId = await postgresLogger.logNotification({ taskId, taskTitle, @@ -252,6 +290,9 @@ async function logNotificationToPostgres(data) { } } +/** + * Обновляет статус доставки уведомления в PostgreSQL + */ async function updatePostgresLogStatus(logIds, status, errorMessage = null, sentAt = null) { if (!logIds || logIds.length === 0) return; @@ -260,6 +301,9 @@ async function updatePostgresLogStatus(logIds, status, errorMessage = null, sent } } +/** + * Возвращает тему уведомления в зависимости от типа + */ function getNotificationSubject(type, taskTitle) { switch (type) { case 'created': @@ -277,11 +321,14 @@ function getNotificationSubject(type, taskTitle) { } } +/** + * Преобразует внутренний статус в читаемый текст + */ function getStatusText(status) { const statusMap = { 'assigned': 'Назначена', 'in_progress': 'В работе', - 'completed': 'Завершена', + 'completed': 'Выполнена', 'overdue': 'Просрочена', 'rework': 'На доработке' };