Files
2026-11-04 07:56:07 +00:00

87 lines
3.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// Direkter Web-Zugriff (Browser/URL) wird geblockt. Ausnahmen: echter CLI-Cronjob
// sowie Einbindung per require_once aus dem authentifizierten Backend (staff/settings.php),
// das vor dem require_once die Konstante DESKHUB_CRON_RUNNER definiert.
if (php_sapi_name() !== 'cli' && !defined('DESKHUB_CRON_RUNNER')) { http_response_code(403); exit('Kein Web-Zugriff.'); }
/**
* cron/check_sla.php
*
* Prüft alle offenen Tickets auf überschrittene SLA-Fristen (Erstreaktion
* und Lösung) und markiert sie als überfällig (is_overdue). Optional kann
* bei neu erkannter Überfälligkeit eine Eskalations-E-Mail an die
* zuständige Abteilung gesendet werden.
*
* Einrichtung: Per Cronjob alle 15 Minuten aufrufen (Crontab-Beispiel siehe README.md):
* /usr/bin/php /pfad/zu/deskhub/cron/check_sla.php >> /pfad/zu/deskhub/cron/check_sla.log 2>&1
*
* Kann außerdem manuell aus dem Backend (Einstellungen → Cron-Jobs) ausgeführt werden.
*/
require_once __DIR__ . '/../includes/bootstrap.php';
/**
* Führt die SLA-Prüfung aus. Kann vom Cron und manuell aus settings.php aufgerufen werden.
*
* @return array{overdueTotal: int, newlyOverdue: int, log: string[]}
*/
function run_check_sla(PDO $pdo, bool $verbose = false): array
{
$now = db_now();
$stmt = $pdo->prepare(
"SELECT t.*, d.email AS department_email, d.name AS department_name FROM tickets t
JOIN departments d ON d.id = t.department_id
WHERE t.status NOT IN ('resolved','closed')
AND t.sla_paused_at IS NULL
AND t.resolution_due_at IS NOT NULL
AND t.resolution_due_at < ?"
);
$stmt->execute([$now]);
$overdue = $stmt->fetchAll();
$newlyOverdue = 0;
$log = [];
foreach ($overdue as $t) {
if (!$t['is_overdue']) {
$pdo->prepare('UPDATE tickets SET is_overdue = 1, updated_at = updated_at WHERE id = ?')->execute([$t['id']]);
ticket_sla_log_overdue($pdo, (int) $t['id'], 'resolution', (string) $t['resolution_due_at']);
log_activity($pdo, $t['id'], null, 'SLA überschritten – Ticket als überfällig markiert');
// Nur die Betroffenen bekommen eine Mail (Bearbeiter bzw. Abteilung), nicht die Postfach-Adresse der Abteilung und nicht alle Admins
notify_overdue_ticket_staff($t);
fcm_notify_overdue($t); // Push auf die Android-App
$newlyOverdue++;
if ($verbose) {
$log[] = "[ok] Ticket #{$t['ticket_number']} als überfällig markiert";
}
}
}
$responseStmt = $pdo->prepare("SELECT id, first_response_due_at FROM tickets
WHERE first_responded_at IS NULL AND first_response_due_at IS NOT NULL AND first_response_due_at < ?
AND sla_paused_at IS NULL AND status NOT IN ('resolved','closed')");
$responseStmt->execute([$now]);
foreach ($responseStmt->fetchAll() as $ticket) ticket_sla_log_overdue($pdo, (int) $ticket['id'], 'first_response', (string) $ticket['first_response_due_at']);
// Tickets, die nicht mehr überfällig sind (z.B. weil sie gelöst wurden), zurücksetzen
$pdo->exec("UPDATE tickets SET is_overdue = 0, updated_at = updated_at WHERE status IN ('resolved','closed') AND is_overdue = 1");
// Erinnerung an Bearbeiter: Ticket wartet seit mehreren Arbeitsstunden auf Antwort, bevor die Frist reißt
$waitReminders = wait_reminders_run($pdo, $now);
foreach ($waitReminders['log'] as $line) $log[] = $line;
set_setting('cron_check_sla_last_run', $now);
$log[] = "[" . date('Y-m-d H:i:s') . "] SLA-Prüfung abgeschlossen. " . count($overdue) . " überfällige Tickets insgesamt, davon {$newlyOverdue} neu erkannt.";
return ['overdueTotal' => count($overdue), 'newlyOverdue' => $newlyOverdue, 'waitReminders' => $waitReminders['sent'], 'log' => $log];
}
if (php_sapi_name() === 'cli') {
$result = run_check_sla(get_db(), true);
foreach ($result['log'] as $line) {
echo $line . "\n";
}
}