Dieser Commit ist enthalten in:
hendrik_gebhardt@gmx.de
2026-01-06 21:49:26 +00:00
committet von Server Deploy
Ursprung 623bbdf5dd
Commit 7d67557be4
34 geänderte Dateien mit 21416 neuen und 2367 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,242 @@
/**
* 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);
}
};