import { notificationService, EVENT_TYPES } from "../services/notification.service"; import { eventBus } from "../routes/shared"; /** * Отправляет уведомление о назначении ответственного по задаче. * Централизованная функция — используется из всех endpoint'ов, * где меняется assignedTo (assignees, transitions, crud-write, auto-transitions). * * Возвращает массив userId пользователей, которым было отправлено уведомление. * Отправляет SSE notification events для real-time обновления UI. * * Если у назначенного пользователя нет подписки на task.assigned, * создаёт уведомление принудительно (через targetUserIds), чтобы * назначение задачи никогда не пропадало мимо пользователя. */ export async function notifyTaskAssigned( task: { id: number; title: string; formId: number }, assigneeUserId: number, triggeredBy: number | null, organizationId: number ): Promise { const eventBase = { type: EVENT_TYPES.TASK_ASSIGNED, organizationId, triggeredBy: triggeredBy ?? null, taskId: task.id, formId: task.formId, payload: { taskTitle: task.title, assignedTo: assigneeUserId, authorName: triggeredBy ? undefined : "Система", message: `Вас назначили ответственным по задаче "${task.title}"`, }, timestamp: new Date(), }; // 1. Primary: normal subscriber-based flow let notifiedUserIds = await notificationService.processEvent(eventBase); if (!Array.isArray(notifiedUserIds)) { console.warn(`[notifyTaskAssigned] processEvent returned non-array (${typeof notifiedUserIds}), treating as empty`); notifiedUserIds = []; } // 2. Fallback: if subscription-based flow didn't notify the assignee, // force-create the notification via targetUserIds (bypasses subscription check). // This ensures task assignment is NEVER silently lost. if (!notifiedUserIds.includes(assigneeUserId)) { console.warn( `[notifyTaskAssigned] User ${assigneeUserId} has no subscription for TASK_ASSIGNED. ` + `Forcing notification via targetUserIds (taskId=${task.id}, formId=${task.formId})` ); const forced = await notificationService.processEvent({ ...eventBase, targetUserIds: [assigneeUserId], }); notifiedUserIds = Array.isArray(forced) ? forced : []; } // 3. Send SSE notification events for real-time UI update (badge + inbox count) for (const userId of notifiedUserIds) { console.log(`[SSE] Publishing notification event for user ${userId}, taskId=${task.id}`); eventBus.publishEvent({ type: "notification", data: { type: EVENT_TYPES.TASK_ASSIGNED, taskId: task.id }, organizationId, userId, }); } return notifiedUserIds; }