Files
deskhub/DeskHub-Webseite/api/staff/v1/assistant.php
T
2026-10-23 17:22:26 +00:00

172 lines
9.5 KiB
PHP

<?php
declare(strict_types=1);
/**
* api/staff/v1/assistant.php: Verwaltung des digitalen Assistenten für Android- und Windows-App (wie staff/assistant.php).
* Nur Administratoren und Mitarbeiter mit dem Recht "Digitalen Assistenten verwalten".
*
* GET ?section=overview -> Einstellungen, Zustand, Abteilungen, Prioritäten, Synonyme
* GET ?section=answers -> zusätzliche Antworten
* GET ?section=log&result=&period=&q=&page= -> Fragen-Log mit Kennzahlen und häufigen Fragen ohne Antwort
* POST {action: save_settings|save_answer|delete_answer|save_synonyms|clear_log|test, ...}
*/
require_once __DIR__ . '/common.php';
require_once __DIR__ . '/../../../includes/assistant.php';
api_require_method('GET', 'POST');
$staff = api_require_staff();
if ($staff['role'] !== 'admin' && !has_permission($staff, 'manage_assistant')) {
api_error(403, 'FORBIDDEN', 'Für diese Aktion fehlt die erforderliche Berechtigung.');
}
$pdo = get_db();
/** Sind die Tabellen schon da? (sonst: Datenbank aktualisieren) */
function assistant_api_db_ready(PDO $pdo): bool
{
try { $pdo->query('SELECT id FROM assistant_answers LIMIT 1'); return true; } catch (Throwable $e) { return false; }
}
function assistant_api_answer(array $a): array
{
return ['id' => (int) $a['id'], 'title' => $a['title'], 'keywords' => (string) ($a['keywords'] ?? ''), 'answer' => $a['answer'],
'isActive' => (bool) $a['is_active'], 'hits' => (int) $a['hits'], 'updatedAt' => $a['updated_at']];
}
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$section = (string) ($_GET['section'] ?? 'overview');
if ($section === 'overview') {
$texts = assistant_texts();
api_json([
'dbReady' => assistant_api_db_ready($pdo),
'settings' => [
'enabled' => assistant_enabled(),
'mode' => get_setting('assistant_mode', 'offline_only') === 'always' ? 'always' : 'offline_only',
'name' => get_setting('assistant_name', ''),
'greeting' => get_setting('assistant_greeting', ''),
'noAnswer' => get_setting('assistant_no_answer', ''),
'departmentId' => (int) get_setting('assistant_department_id', '0'),
'priorityId' => (int) get_setting('assistant_priority_id', '0'),
'logDays' => assistant_log_days(),
'synonyms' => get_setting('assistant_synonyms', ''),
],
'defaults' => ['name' => $texts['name'], 'greeting' => $texts['greeting'], 'noAnswer' => $texts['no_answer']],
'state' => [
'activeNow' => assistant_active($pdo),
'liveChatEnabled' => get_setting('live_chat_enabled', '0') === '1',
'kbEnabled' => get_setting('kb_enabled', '1') === '1',
'helpdeskOffline' => get_setting('helpdesk_status', 'online') === 'offline',
],
'departments' => array_map(fn($d) => ['id' => (int) $d['id'], 'name' => $d['name']], $pdo->query('SELECT id, name FROM departments WHERE is_active = 1 ORDER BY name')->fetchAll()),
'priorities' => array_map(fn($p) => ['id' => (int) $p['id'], 'name' => $p['name']], $pdo->query('SELECT id, name FROM priorities ORDER BY sort_order')->fetchAll()),
]);
}
if ($section === 'answers') {
try {
$items = array_map('assistant_api_answer', $pdo->query('SELECT * FROM assistant_answers ORDER BY title')->fetchAll());
} catch (Throwable $e) {
api_error(503, 'DB_OUTDATED', 'Die Datenbank ist noch nicht aktuell. Bitte im Adminportal unter System-Update „Datenbank aktualisieren“ klicken.');
}
api_json(['items' => $items]);
}
if ($section === 'log') {
$period = in_array($_GET['period'] ?? '', ['today', '7', '30', 'all'], true) ? (string) $_GET['period'] : '30';
$filter = ['result' => (string) ($_GET['result'] ?? ''), 'period' => $period, 'q' => trim((string) ($_GET['q'] ?? ''))];
try {
$page = assistant_log_list($pdo, $filter, max(1, (int) ($_GET['page'] ?? 1)));
$stats = assistant_log_stats($pdo, $period);
$top = assistant_log_unanswered($pdo, $period);
} catch (Throwable $e) {
api_error(503, 'DB_OUTDATED', 'Das Log ist noch nicht verfügbar. Bitte im Adminportal unter System-Update „Datenbank aktualisieren“ klicken.');
}
api_json([
'filters' => assistant_log_filters(),
'stats' => $stats,
'top' => $top,
'total' => $page['total'], 'pages' => $page['pages'], 'page' => $page['page'],
'items' => array_map(fn(array $r) => [
'id' => (int) $r['id'], 'question' => $r['question'], 'result' => $r['result'], 'matchedTitle' => $r['matched_title'],
'ticketNumber' => $r['ticket_number'], 'helpful' => $r['helpful'] === null ? null : ((int) $r['helpful'] === 1), 'createdAt' => $r['created_at'],
], $page['rows']),
]);
}
api_error(422, 'INVALID_SECTION', 'Unbekannter Bereich.');
}
$body = api_request_body();
$action = (string) ($body['action'] ?? '');
if ($action === 'save_settings') {
$dept = (int) ($body['departmentId'] ?? 0);
$prio = (int) ($body['priorityId'] ?? 0);
if ($dept > 0) { $c = $pdo->prepare('SELECT COUNT(*) FROM departments WHERE id = ? AND is_active = 1'); $c->execute([$dept]); if (!(int) $c->fetchColumn()) $dept = 0; }
if ($prio > 0) { $c = $pdo->prepare('SELECT COUNT(*) FROM priorities WHERE id = ?'); $c->execute([$prio]); if (!(int) $c->fetchColumn()) $prio = 0; }
set_setting('assistant_enabled', !empty($body['enabled']) ? '1' : '0');
set_setting('assistant_mode', ($body['mode'] ?? 'offline_only') === 'always' ? 'always' : 'offline_only');
set_setting('assistant_name', mb_substr(trim(strip_tags((string) ($body['name'] ?? ''))), 0, 60));
set_setting('assistant_greeting', mb_substr(trim(strip_tags((string) ($body['greeting'] ?? ''))), 0, 500));
set_setting('assistant_no_answer', mb_substr(trim(strip_tags((string) ($body['noAnswer'] ?? ''))), 0, 500));
set_setting('assistant_department_id', (string) $dept);
set_setting('assistant_priority_id', (string) $prio);
set_setting('assistant_log_days', (string) max(0, min(3650, (int) ($body['logDays'] ?? 90))));
audit_log((int) $staff['id'], 'Einstellungen des digitalen Assistenten geändert (App)');
api_json(['ok' => true]);
}
if ($action === 'save_synonyms') {
$text = mb_substr(trim(strip_tags((string) ($body['text'] ?? ''))), 0, 5000);
set_setting('assistant_synonyms', $text);
audit_log((int) $staff['id'], 'Gleichbedeutende Wörter des digitalen Assistenten geändert (App)');
api_json(['ok' => true, 'groups' => count(assistant_custom_synonyms($text))]);
}
if ($action === 'save_answer') {
if (!assistant_api_db_ready($pdo)) api_error(503, 'DB_OUTDATED', 'Die Datenbank ist noch nicht aktuell. Bitte „Datenbank aktualisieren“ ausführen.');
$id = (int) ($body['id'] ?? 0);
$title = trim(mb_substr((string) ($body['title'] ?? ''), 0, 255));
$keywords = trim(mb_substr((string) ($body['keywords'] ?? ''), 0, 1000));
$answer = trim(mb_substr((string) ($body['answer'] ?? ''), 0, 4000));
$active = !empty($body['isActive']) ? 1 : 0;
if ($title === '' || $answer === '') api_error(422, 'FIELDS_REQUIRED', 'Bitte eine Frage (Titel) und die Antwort angeben.');
$now = db_now();
if ($id > 0) {
$pdo->prepare('UPDATE assistant_answers SET title=?, keywords=?, answer=?, is_active=?, updated_at=? WHERE id=?')->execute([$title, $keywords ?: null, $answer, $active, $now, $id]);
audit_log((int) $staff['id'], "Antwort des digitalen Assistenten geändert (App): {$title}");
} else {
$pdo->prepare('INSERT INTO assistant_answers (title, keywords, answer, is_active, created_at, updated_at) VALUES (?,?,?,?,?,?)')->execute([$title, $keywords ?: null, $answer, $active, $now, $now]);
$id = (int) $pdo->lastInsertId();
audit_log((int) $staff['id'], "Antwort des digitalen Assistenten angelegt (App): {$title}");
}
$row = $pdo->prepare('SELECT * FROM assistant_answers WHERE id = ?');
$row->execute([$id]);
api_json(['item' => assistant_api_answer($row->fetch())]);
}
if ($action === 'delete_answer') {
$id = (int) ($body['id'] ?? 0);
$t = $pdo->prepare('SELECT title FROM assistant_answers WHERE id = ?');
$t->execute([$id]);
$title = $t->fetchColumn();
if ($title === false) api_error(404, 'NOT_FOUND', 'Die Antwort gibt es nicht.');
$pdo->prepare('DELETE FROM assistant_answers WHERE id = ?')->execute([$id]);
audit_log((int) $staff['id'], "Antwort des digitalen Assistenten gelöscht (App): {$title}");
api_json(['ok' => true]);
}
if ($action === 'clear_log') {
try {
$n = $pdo->exec('DELETE FROM assistant_log');
} catch (Throwable $e) {
api_error(503, 'DB_OUTDATED', 'Das Log ist noch nicht verfügbar. Bitte „Datenbank aktualisieren“ ausführen.');
}
audit_log((int) $staff['id'], 'Fragen-Log des digitalen Assistenten geleert (App)');
api_json(['ok' => true, 'deleted' => (int) $n]);
}
if ($action === 'test') {
$q = trim(mb_substr((string) ($body['question'] ?? ''), 0, 300));
if ($q === '') api_error(422, 'QUESTION_REQUIRED', 'Bitte eine Frage eingeben.');
$r = assistant_search($pdo, $q, false);
api_json(['found' => $r !== null, 'result' => $r ? ['source' => $r['source'], 'title' => $r['title'], 'text' => $r['text'], 'others' => array_map(fn($o) => $o['title'], $r['others'])] : null]);
}
api_error(422, 'INVALID_ACTION', 'Unbekannte Aktion.');