import cron from 'node-cron'; import * as notificationService from '../services/notification.service'; /** * Cron job that runs daily at 1:00 AM to detect meters with negative flow * and create notifications for responsible users */ export function scheduleNegativeFlowDetection(): void { // Schedule: Every day at 1:00 AM // Cron format: minute hour day-of-month month day-of-week // '0 1 * * *' = At 01:00 (1:00 AM) every day cron.schedule('0 1 * * *', async () => { console.log('🔍 [Cron] Starting negative flow detection job...'); const startTime = Date.now(); try { // Get all meters with negative flow values const negativeFlowMeters = await notificationService.getMetersWithNegativeFlow(); if (negativeFlowMeters.length === 0) { console.log('✅ [Cron] No meters with negative flow found'); return; } console.log(`⚠️ [Cron] Found ${negativeFlowMeters.length} meter(s) with negative flow`); let notificationsCreated = 0; let errors = 0; // Group meters by project to avoid duplicate notifications const metersByProject = new Map(); for (const meter of negativeFlowMeters) { const projectId = meter.project_id; if (!metersByProject.has(projectId)) { metersByProject.set(projectId, []); } metersByProject.get(projectId)!.push(meter); } // Create notifications for each project's users for (const [projectId, meters] of metersByProject.entries()) { try { // Get users responsible for this project const userIds = await notificationService.getUsersForProject(projectId); if (userIds.length === 0) { console.log(`⚠️ [Cron] No users found for project ${projectId}`); continue; } // Create notification for each meter for each user for (const meter of meters) { const title = 'Negative Flow Alert'; const message = `${meter.name} (${meter.serial_number}) has negative flow of ${meter.last_reading_value} units`; for (const userId of userIds) { try { await notificationService.create({ user_id: userId, meter_id: meter.id, notification_type: 'NEGATIVE_FLOW', title, message, meter_serial_number: meter.serial_number, flow_value: meter.last_reading_value, }); notificationsCreated++; } catch (error) { console.error(`❌ [Cron] Error creating notification for user ${userId}, meter ${meter.id}:`, error); errors++; } } } } catch (error) { console.error(`❌ [Cron] Error processing project ${projectId}:`, error); errors++; } } const duration = Date.now() - startTime; console.log( `✅ [Cron] Negative flow detection completed in ${duration}ms:`, `${notificationsCreated} notification(s) created,`, `${errors} error(s)` ); // Clean up old read notifications (optional maintenance task) try { const deletedCount = await notificationService.deleteOldReadNotifications(); if (deletedCount > 0) { console.log(`🗑️ [Cron] Cleaned up ${deletedCount} old read notification(s)`); } } catch (error) { console.error('❌ [Cron] Error cleaning up old notifications:', error); } } catch (error) { console.error('❌ [Cron] Fatal error in negative flow detection job:', error); } }); console.log('⏰ [Cron] Negative flow detection job scheduled (daily at 1:00 AM)'); } /** * Manual trigger for testing the negative flow detection * Can be called directly for testing purposes */ export async function triggerNegativeFlowDetection(): Promise { console.log('🔍 [Manual] Starting negative flow detection...'); const startTime = Date.now(); try { const negativeFlowMeters = await notificationService.getMetersWithNegativeFlow(); if (negativeFlowMeters.length === 0) { console.log('✅ [Manual] No meters with negative flow found'); return; } console.log(`⚠️ [Manual] Found ${negativeFlowMeters.length} meter(s) with negative flow`); let notificationsCreated = 0; // Group meters by project const metersByProject = new Map(); for (const meter of negativeFlowMeters) { const projectId = meter.project_id; if (!metersByProject.has(projectId)) { metersByProject.set(projectId, []); } metersByProject.get(projectId)!.push(meter); } // Create notifications for (const [projectId, meters] of metersByProject.entries()) { const userIds = await notificationService.getUsersForProject(projectId); for (const meter of meters) { const title = 'Negative Flow Alert'; const message = `${meter.name} (${meter.serial_number}) has negative flow of ${meter.last_reading_value} units`; for (const userId of userIds) { await notificationService.create({ user_id: userId, meter_id: meter.id, notification_type: 'NEGATIVE_FLOW', title, message, meter_serial_number: meter.serial_number, flow_value: meter.last_reading_value, }); notificationsCreated++; } } } const duration = Date.now() - startTime; console.log(`✅ [Manual] Created ${notificationsCreated} notification(s) in ${duration}ms`); } catch (error) { console.error('❌ [Manual] Error in negative flow detection:', error); throw error; } }