notifications

This commit is contained in:
2026-03-16 22:38:16 +05:00
parent 76075da0ad
commit b9d813955d

View File

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