242 Zeilen
6.8 KiB
JavaScript
242 Zeilen
6.8 KiB
JavaScript
/**
|
|
* TASKMATE - Reminder Service
|
|
* ===========================
|
|
* Service für Erinnerungsbenachrichtigungen und Scheduling
|
|
*/
|
|
|
|
const { getDb } = require('../database');
|
|
const notificationService = require('./notificationService');
|
|
const logger = require('../utils/logger');
|
|
|
|
class ReminderService {
|
|
constructor(io = null) {
|
|
this.io = io;
|
|
this.intervalId = null;
|
|
this.isRunning = false;
|
|
}
|
|
|
|
/**
|
|
* Startet den Reminder-Check-Service
|
|
* Läuft alle 5 Minuten (kann für Produktion auf 1 Stunde erhöht werden)
|
|
*/
|
|
start() {
|
|
if (this.isRunning) {
|
|
logger.warn('Reminder Service ist bereits gestartet');
|
|
return;
|
|
}
|
|
|
|
this.isRunning = true;
|
|
|
|
// Sofort prüfen
|
|
this.checkDueReminders();
|
|
|
|
// Dann alle 5 Minuten (300000 ms)
|
|
// In Produktion könnte das auf 1 Stunde (3600000 ms) erhöht werden
|
|
this.intervalId = setInterval(() => {
|
|
this.checkDueReminders();
|
|
}, 300000); // 5 Minuten
|
|
|
|
logger.info('Reminder Service gestartet - prüft alle 5 Minuten');
|
|
}
|
|
|
|
/**
|
|
* Stoppt den Reminder-Check-Service
|
|
*/
|
|
stop() {
|
|
if (!this.isRunning) {
|
|
return;
|
|
}
|
|
|
|
if (this.intervalId) {
|
|
clearInterval(this.intervalId);
|
|
this.intervalId = null;
|
|
}
|
|
|
|
this.isRunning = false;
|
|
logger.info('Reminder Service gestoppt');
|
|
}
|
|
|
|
/**
|
|
* Prüft fällige Erinnerungen und sendet Benachrichtigungen
|
|
*/
|
|
async checkDueReminders() {
|
|
try {
|
|
const db = getDb();
|
|
const today = new Date().toISOString().split('T')[0];
|
|
|
|
// Finde alle fälligen Benachrichtigungen die noch nicht gesendet wurden
|
|
const dueNotifications = db.prepare(`
|
|
SELECT
|
|
rn.*,
|
|
r.id as reminder_id,
|
|
r.title as reminder_title,
|
|
r.description as reminder_description,
|
|
r.reminder_date,
|
|
r.reminder_time,
|
|
r.color,
|
|
r.project_id,
|
|
r.created_by,
|
|
r.advance_days
|
|
FROM reminder_notifications rn
|
|
JOIN reminders r ON rn.reminder_id = r.id
|
|
WHERE rn.notification_date <= ?
|
|
AND rn.sent = 0
|
|
AND r.is_active = 1
|
|
ORDER BY rn.notification_date ASC, r.reminder_time ASC
|
|
`).all(today);
|
|
|
|
if (dueNotifications.length === 0) {
|
|
logger.debug('Keine fälligen Erinnerungen gefunden');
|
|
return;
|
|
}
|
|
|
|
logger.info(`${dueNotifications.length} fällige Erinnerung(en) gefunden`);
|
|
|
|
// Verarbeite jede fällige Benachrichtigung
|
|
for (const notification of dueNotifications) {
|
|
await this.processReminderNotification(notification);
|
|
}
|
|
|
|
} catch (error) {
|
|
logger.error('Fehler beim Prüfen fälliger Erinnerungen:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verarbeitet eine einzelne fällige Erinnerungs-Benachrichtigung
|
|
*/
|
|
async processReminderNotification(notification) {
|
|
try {
|
|
const db = getDb();
|
|
|
|
// Berechne wie viele Tage im Voraus diese Benachrichtigung ist
|
|
const reminderDate = new Date(notification.reminder_date);
|
|
const notificationDate = new Date(notification.notification_date);
|
|
const daysDiff = Math.ceil((reminderDate - notificationDate) / (1000 * 60 * 60 * 24));
|
|
|
|
const reminder = {
|
|
id: notification.reminder_id,
|
|
title: notification.reminder_title,
|
|
description: notification.reminder_description,
|
|
project_id: notification.project_id,
|
|
created_by: notification.created_by,
|
|
color: notification.color
|
|
};
|
|
|
|
// Erstelle Benachrichtigung
|
|
const createdNotification = notificationService.createReminderNotification(
|
|
reminder,
|
|
daysDiff,
|
|
this.io
|
|
);
|
|
|
|
if (createdNotification) {
|
|
// Markiere als gesendet
|
|
db.prepare(`
|
|
UPDATE reminder_notifications
|
|
SET sent = 1
|
|
WHERE id = ?
|
|
`).run(notification.id);
|
|
|
|
logger.info(`Reminder-Benachrichtigung gesendet: "${notification.reminder_title}" (${daysDiff} Tage vorher)`);
|
|
}
|
|
|
|
} catch (error) {
|
|
logger.error(`Fehler beim Verarbeiten der Reminder-Benachrichtigung ${notification.id}:`, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Erstellt Benachrichtigungstermine für eine neue Erinnerung
|
|
*/
|
|
createNotificationSchedule(reminderId, reminderDate, advanceDays) {
|
|
try {
|
|
const db = getDb();
|
|
const baseDate = new Date(reminderDate);
|
|
|
|
// Lösche alte Termine falls vorhanden
|
|
db.prepare('DELETE FROM reminder_notifications WHERE reminder_id = ?').run(reminderId);
|
|
|
|
// Erstelle neue Termine für jeden advance day
|
|
advanceDays.forEach(days => {
|
|
const notificationDate = new Date(baseDate);
|
|
notificationDate.setDate(notificationDate.getDate() - parseInt(days));
|
|
|
|
const notificationDateStr = notificationDate.toISOString().split('T')[0];
|
|
|
|
// Nur zukünftige Termine erstellen
|
|
const today = new Date().toISOString().split('T')[0];
|
|
if (notificationDateStr >= today) {
|
|
db.prepare(`
|
|
INSERT OR IGNORE INTO reminder_notifications (reminder_id, notification_date)
|
|
VALUES (?, ?)
|
|
`).run(reminderId, notificationDateStr);
|
|
}
|
|
});
|
|
|
|
logger.debug(`Benachrichtigungstermine erstellt für Reminder ${reminderId}`);
|
|
} catch (error) {
|
|
logger.error(`Fehler beim Erstellen der Benachrichtigungstermine für Reminder ${reminderId}:`, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manuelle Prüfung für API-Endpoint
|
|
*/
|
|
async manualCheck() {
|
|
logger.info('Manuelle Reminder-Prüfung ausgelöst');
|
|
return await this.checkDueReminders();
|
|
}
|
|
|
|
/**
|
|
* Statistiken für Debugging
|
|
*/
|
|
getStats() {
|
|
try {
|
|
const db = getDb();
|
|
|
|
const stats = {
|
|
isRunning: this.isRunning,
|
|
activeReminders: db.prepare('SELECT COUNT(*) as count FROM reminders WHERE is_active = 1').get().count,
|
|
pendingNotifications: db.prepare('SELECT COUNT(*) as count FROM reminder_notifications WHERE sent = 0').get().count,
|
|
nextDueDate: db.prepare(`
|
|
SELECT MIN(notification_date) as next_date
|
|
FROM reminder_notifications
|
|
WHERE sent = 0 AND notification_date >= date('now')
|
|
`).get().next_date
|
|
};
|
|
|
|
return stats;
|
|
} catch (error) {
|
|
logger.error('Fehler beim Abrufen der Reminder-Statistiken:', error);
|
|
return { error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Socket.io Instanz setzen/aktualisieren
|
|
*/
|
|
setSocketIO(io) {
|
|
this.io = io;
|
|
logger.debug('Socket.IO Instanz für Reminder Service aktualisiert');
|
|
}
|
|
}
|
|
|
|
// Singleton Export
|
|
let instance = null;
|
|
|
|
module.exports = {
|
|
getInstance(io = null) {
|
|
if (!instance) {
|
|
instance = new ReminderService(io);
|
|
} else if (io) {
|
|
instance.setSocketIO(io);
|
|
}
|
|
return instance;
|
|
},
|
|
|
|
// Für Tests und Debugging
|
|
createInstance(io = null) {
|
|
return new ReminderService(io);
|
|
}
|
|
}; |