298 lines
11 KiB
JavaScript
298 lines
11 KiB
JavaScript
// notifications.js
|
||
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();
|
||
if (!db) {
|
||
console.log('❌ База данных не доступна для отправки уведомлений');
|
||
return;
|
||
}
|
||
|
||
console.log(`📢 Начинаем отправку уведомлений для задачи ${taskId}:`);
|
||
console.log(` Тип: ${type}, Автор действия: ${authorId}, Название: ${taskTitle}`);
|
||
|
||
// Получаем заказчика
|
||
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
|
||
FROM tasks t
|
||
LEFT JOIN users u ON t.created_by = u.id
|
||
WHERE t.id = ?
|
||
`, [taskId], (err, row) => {
|
||
if (err) reject(err);
|
||
else resolve(row);
|
||
});
|
||
});
|
||
|
||
// Получаем исполнителей
|
||
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
|
||
FROM task_assignments ta
|
||
LEFT JOIN users u ON ta.user_id = u.id
|
||
WHERE ta.task_id = ?
|
||
`, [taskId], (err, rows) => {
|
||
if (err) reject(err);
|
||
else resolve(rows || []);
|
||
});
|
||
});
|
||
|
||
// Получаем информацию об авторе действия
|
||
const author = await new Promise((resolve, reject) => {
|
||
db.get("SELECT name, login, email FROM users WHERE id = ?", [authorId], (err, row) => {
|
||
if (err) reject(err);
|
||
else resolve(row);
|
||
});
|
||
});
|
||
|
||
const authorName = author ? author.name : 'Система';
|
||
const authorLogin = author ? author.login : 'system';
|
||
|
||
// Логируем в PostgreSQL (если настроено)
|
||
let postgresLogIds = [];
|
||
if (postgresLogger.initialized) {
|
||
postgresLogIds = await logNotificationToPostgres({
|
||
type,
|
||
taskId,
|
||
taskTitle,
|
||
taskDescription,
|
||
authorId,
|
||
authorName,
|
||
authorLogin,
|
||
participants: [...(creator ? [{...creator, role: 'creator'}] : []), ...assignees.map(a => ({...a, role: 'assignee'}))],
|
||
comment,
|
||
status,
|
||
userName
|
||
});
|
||
}
|
||
|
||
// Отправляем email уведомления
|
||
const participants = [...(creator ? [creator] : []), ...assignees].filter(p => p.user_id !== authorId);
|
||
|
||
for (const participant of participants) {
|
||
const taskData = {
|
||
taskId,
|
||
title: taskTitle,
|
||
description: taskDescription,
|
||
due_date: null, // Можно добавить получение срока из БД
|
||
author_name: authorName,
|
||
comment: comment,
|
||
status: status,
|
||
user_name: userName || participant.user_name,
|
||
hours_left: type === 'deadline' ? 24 : null // Для уведомлений о дедлайне
|
||
};
|
||
|
||
await emailNotifications.sendTaskNotification(
|
||
participant.user_id,
|
||
taskData,
|
||
type
|
||
);
|
||
}
|
||
|
||
console.log(`✅ Уведомления отправлены для задачи ${taskId}`);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Общая ошибка при обработке уведомлений:', error);
|
||
}
|
||
}
|
||
|
||
// Обновим функцию для уведомлений о дедлайнах
|
||
async function sendDeadlineNotification(assignment, hoursLeft) {
|
||
try {
|
||
const taskData = {
|
||
taskId: assignment.task_id,
|
||
title: assignment.title,
|
||
description: assignment.description || '',
|
||
due_date: assignment.due_date,
|
||
author_name: assignment.creator_name,
|
||
hours_left: hoursLeft
|
||
};
|
||
|
||
// Отправляем уведомление исполнителю
|
||
await emailNotifications.sendTaskNotification(
|
||
assignment.user_id,
|
||
taskData,
|
||
'deadline'
|
||
);
|
||
|
||
// Отправляем уведомление заказчику
|
||
await emailNotifications.sendTaskNotification(
|
||
assignment.created_by,
|
||
taskData,
|
||
'deadline'
|
||
);
|
||
|
||
console.log(`✅ Email уведомление о сроке (${hoursLeft}ч) отправлено для задачи ${assignment.task_id}`);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Ошибка отправки email уведомления о сроке:', error);
|
||
}
|
||
}
|
||
|
||
async function checkUpcomingDeadlines() {
|
||
const now = new Date();
|
||
const in48Hours = new Date(now.getTime() + 48 * 60 * 60 * 1000).toISOString();
|
||
const in24Hours = new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString();
|
||
const nowISO = now.toISOString();
|
||
|
||
const query = `
|
||
SELECT DISTINCT ta.*, t.title, t.description, t.created_by, u.name as user_name, u.email as user_email,
|
||
creator.name as creator_name, creator.email as creator_email
|
||
FROM task_assignments ta
|
||
JOIN tasks t ON ta.task_id = t.id
|
||
JOIN users u ON ta.user_id = u.id
|
||
JOIN users creator ON t.created_by = creator.id
|
||
WHERE ta.due_date IS NOT NULL
|
||
AND ta.due_date > ?
|
||
AND ta.due_date <= ?
|
||
AND ta.status NOT IN ('completed', 'overdue')
|
||
AND t.status = 'active'
|
||
AND t.closed_at IS NULL
|
||
`;
|
||
|
||
const db = getDb();
|
||
if (!db) {
|
||
console.error('❌ База данных не доступна для проверки сроков');
|
||
return;
|
||
}
|
||
|
||
db.all(query, [nowISO, in48Hours], async (err, assignments) => {
|
||
if (err) {
|
||
console.error('❌ Ошибка при проверке сроков задач:', err);
|
||
return;
|
||
}
|
||
|
||
for (const assignment of assignments) {
|
||
const dueDate = new Date(assignment.due_date);
|
||
const timeLeft = dueDate.getTime() - now.getTime();
|
||
const hoursLeft = Math.floor(timeLeft / (60 * 60 * 1000));
|
||
|
||
if (hoursLeft <= 48 && hoursLeft > 24) {
|
||
await sendDeadlineNotification(assignment, 48);
|
||
} else if (hoursLeft <= 24) {
|
||
await sendDeadlineNotification(assignment, 24);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// Вспомогательные функции для работы с PostgreSQL
|
||
async function logNotificationToPostgres(data) {
|
||
try {
|
||
const {
|
||
type,
|
||
taskId,
|
||
taskTitle,
|
||
taskDescription,
|
||
authorId,
|
||
authorName,
|
||
authorLogin,
|
||
participants = [],
|
||
comment = '',
|
||
status = '',
|
||
userName = '',
|
||
error = ''
|
||
} = data;
|
||
|
||
// Создаем сообщение
|
||
let messageContent = '';
|
||
switch (type) {
|
||
case 'created':
|
||
messageContent = `Создана новая задача: ${taskTitle}`;
|
||
break;
|
||
case 'updated':
|
||
messageContent = `Обновлена задача: ${taskTitle}`;
|
||
break;
|
||
case 'rework':
|
||
messageContent = `Задача возвращена на доработку: ${taskTitle}. Комментарий: ${comment}`;
|
||
break;
|
||
case 'closed':
|
||
messageContent = `Задача закрыта: ${taskTitle}`;
|
||
break;
|
||
case 'status_changed':
|
||
messageContent = `Изменен статус задачи: ${taskTitle}. Новый статус: ${getStatusText(status)}`;
|
||
break;
|
||
}
|
||
|
||
// Логируем для каждого получателя отдельно
|
||
const recipientsToNotify = participants.filter(p => p.user_id !== authorId);
|
||
const logIds = [];
|
||
|
||
for (const recipient of recipientsToNotify) {
|
||
const logId = await postgresLogger.logNotification({
|
||
taskId,
|
||
taskTitle,
|
||
taskDescription,
|
||
notificationType: type,
|
||
authorId,
|
||
authorName,
|
||
authorLogin,
|
||
recipientId: recipient.user_id,
|
||
recipientName: recipient.user_name,
|
||
recipientLogin: recipient.user_login,
|
||
messageContent: `${messageContent}\n\nЗадача: ${taskTitle}\nОписание: ${taskDescription || 'Без описания'}\nАвтор: ${authorName}`,
|
||
messageSubject: getNotificationSubject(type, taskTitle),
|
||
deliveryMethods: ['email', 'telegram', 'vk'],
|
||
comments: error ? `Ошибка: ${error}` : comment
|
||
});
|
||
|
||
if (logId) {
|
||
logIds.push(logId);
|
||
}
|
||
}
|
||
|
||
return logIds;
|
||
|
||
} catch (error) {
|
||
console.error('❌ Ошибка логирования в PostgreSQL:', error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
async function updatePostgresLogStatus(logIds, status, errorMessage = null, sentAt = null) {
|
||
if (!logIds || logIds.length === 0) return;
|
||
|
||
for (const logId of logIds) {
|
||
await postgresLogger.updateNotificationStatus(logId, status, errorMessage, sentAt);
|
||
}
|
||
}
|
||
|
||
function getNotificationSubject(type, taskTitle) {
|
||
switch (type) {
|
||
case 'created':
|
||
return `Новая задача: ${taskTitle}`;
|
||
case 'updated':
|
||
return `Обновлена задача: ${taskTitle}`;
|
||
case 'rework':
|
||
return `Задача возвращена на доработку: ${taskTitle}`;
|
||
case 'closed':
|
||
return `Задача закрыта: ${taskTitle}`;
|
||
case 'status_changed':
|
||
return `Изменен статус задачи: ${taskTitle}`;
|
||
default:
|
||
return `Уведомление по задаче: ${taskTitle}`;
|
||
}
|
||
}
|
||
|
||
function getStatusText(status) {
|
||
const statusMap = {
|
||
'assigned': 'Назначена',
|
||
'in_progress': 'В работе',
|
||
'completed': 'Завершена',
|
||
'overdue': 'Просрочена',
|
||
'rework': 'На доработке'
|
||
};
|
||
return statusMap[status] || status;
|
||
}
|
||
|
||
// Экспортируем функции
|
||
module.exports = {
|
||
sendTaskNotifications,
|
||
checkUpcomingDeadlines,
|
||
sendDeadlineNotification,
|
||
getStatusText,
|
||
emailNotifications // Экспортируем для доступа к методам
|
||
}; |