notifications
This commit is contained in:
@@ -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': 'На доработке'
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user