Kontakt-Modul
Dieser Commit ist enthalten in:
committet von
Server Deploy
Ursprung
623bbdf5dd
Commit
7d67557be4
@ -47,6 +47,10 @@ const NOTIFICATION_TYPES = {
|
||||
title: (data) => 'Genehmigung erforderlich',
|
||||
message: (data) => `Neue Genehmigung: "${data.proposalTitle}"`
|
||||
},
|
||||
'reminder:due': {
|
||||
title: (data) => 'Erinnerung',
|
||||
message: (data) => `${data.reminderTitle} - ${data.daysAdvance === '0' ? 'Heute' : `in ${data.daysAdvance} Tag${data.daysAdvance > 1 ? 'en' : ''}`}`
|
||||
},
|
||||
'approval:granted': {
|
||||
title: (data) => 'Genehmigung erteilt',
|
||||
message: (data) => `"${data.proposalTitle}" wurde genehmigt`
|
||||
@ -284,6 +288,24 @@ const notificationService = {
|
||||
if (result) results.push(result);
|
||||
});
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reminder-Benachrichtigung erstellen
|
||||
*/
|
||||
createReminderNotification(reminder, daysAdvance, io) {
|
||||
return this.create(
|
||||
reminder.created_by,
|
||||
'reminder:due',
|
||||
{
|
||||
reminderTitle: reminder.title,
|
||||
daysAdvance: daysAdvance.toString(),
|
||||
projectId: reminder.project_id,
|
||||
reminderId: reminder.id
|
||||
},
|
||||
io,
|
||||
false
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
242
backend/services/reminderService.js
Normale Datei
242
backend/services/reminderService.js
Normale Datei
@ -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);
|
||||
}
|
||||
};
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren