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.');