Notifications
This commit is contained in:
170
water-api/src/jobs/negativeFlowDetection.ts
Normal file
170
water-api/src/jobs/negativeFlowDetection.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
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<string, typeof negativeFlowMeters>();
|
||||
|
||||
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<void> {
|
||||
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<string, typeof negativeFlowMeters>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user