264 lines
11 KiB
PHP
264 lines
11 KiB
PHP
<?php
|
||
function checkAuth(PDO $pdo): ?array {
|
||
if (!isset($_SESSION['user_id'])) return null;
|
||
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
|
||
$stmt->execute([$_SESSION['user_id']]);
|
||
$user = $stmt->fetch();
|
||
return $user ?: null;
|
||
}
|
||
|
||
function redirect(string $url): void {
|
||
header("Location: $url");
|
||
exit;
|
||
}
|
||
|
||
function getUserForms(PDO $pdo, int $userId): array {
|
||
$stmt = $pdo->prepare("SELECT * FROM forms WHERE user_id = ? ORDER BY created_at DESC");
|
||
$stmt->execute([$userId]);
|
||
return $stmt->fetchAll();
|
||
}
|
||
|
||
function createLocalForm(PDO $pdo, ?int $userId, string $type, array $data, string $status = 'pending', ?string $priority = null, ?int $locationId = null): int {
|
||
$jsonData = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||
$stmt = $pdo->prepare("INSERT INTO forms (user_id, type, data, status, priority, location_id) VALUES (?, ?, ?, ?, ?, ?)");
|
||
$stmt->execute([$userId, $type, $jsonData, $status, $priority, $locationId]);
|
||
return (int)$pdo->lastInsertId();
|
||
}
|
||
|
||
function uploadFile(array $file): string|false {
|
||
if ($file['error'] !== UPLOAD_ERR_OK) return false;
|
||
if ($file['size'] > MAX_FILE_SIZE) return false;
|
||
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
|
||
if (!in_array($file['type'], $allowedTypes)) return false;
|
||
|
||
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
|
||
$basename = bin2hex(random_bytes(16)) . '.' . $ext;
|
||
$destination = UPLOAD_DIR . $basename;
|
||
|
||
if (move_uploaded_file($file['tmp_name'], $destination)) {
|
||
return 'uploads/' . $basename;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function syncFormWithGlpi(PDO $pdo, array &$form, GlpiApi $glpi): bool {
|
||
$data = json_decode($form['data'], true);
|
||
$glpiId = $data['glpi_ticket_id'] ?? null;
|
||
if (!$glpiId) return true;
|
||
|
||
$ticket = $glpi->getTicket($glpiId);
|
||
if (!$ticket) {
|
||
$stmt = $pdo->prepare("DELETE FROM forms WHERE id = ?");
|
||
$stmt->execute([$form['id']]);
|
||
return false;
|
||
}
|
||
|
||
$data['glpi_status'] = $ticket['status'] ?? null;
|
||
$data['glpi_priority'] = $ticket['priority'] ?? null;
|
||
$data['glpi_name'] = $ticket['name'] ?? '';
|
||
$newData = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||
|
||
// ----- МАППИНГ СТАТУСОВ GLPI В ДОПУСТИМЫЕ ЗНАЧЕНИЯ -----
|
||
$statusGlpi = strtolower($ticket['status_name'] ?? $ticket['status'] ?? 'unknown');
|
||
|
||
$statusMap = [
|
||
'new' => 'pending',
|
||
'incoming' => 'pending',
|
||
'waiting' => 'pending',
|
||
'processing' => 'processing',
|
||
'assigned' => 'processing',
|
||
'solved' => 'completed',
|
||
'closed' => 'completed',
|
||
'rejected' => 'rejected',
|
||
'pending' => 'pending'
|
||
];
|
||
|
||
$newStatus = $statusMap[$statusGlpi] ?? 'pending';
|
||
// Дополнительная проверка на допустимость
|
||
$allowedStatuses = ['pending', 'processing', 'completed', 'rejected'];
|
||
if (!in_array($newStatus, $allowedStatuses)) {
|
||
$newStatus = 'pending';
|
||
}
|
||
|
||
// ----- МАППИНГ ПРИОРИТЕТОВ (ЧИСЛО -> 'low','medium','high') -----
|
||
$priorityGlpi = $ticket['priority'] ?? 3; // число 1..5
|
||
$priorityMap = [
|
||
1 => 'low',
|
||
2 => 'low',
|
||
3 => 'medium',
|
||
4 => 'high',
|
||
5 => 'high'
|
||
];
|
||
$newPriority = $priorityMap[$priorityGlpi] ?? 'medium';
|
||
|
||
// Обновляем запись в БД
|
||
$stmt = $pdo->prepare("UPDATE forms SET data = ?, status = ?, priority = ? WHERE id = ?");
|
||
$stmt->execute([$newData, $newStatus, $newPriority, $form['id']]);
|
||
|
||
$form['data'] = $newData;
|
||
$form['status'] = $newStatus;
|
||
$form['priority'] = $newPriority;
|
||
return true;
|
||
}
|
||
|
||
/* ========== БАЗА ЗНАНИЙ (исправленная) ========== */
|
||
|
||
// Получение всех статей
|
||
function getAllLocalKnowledgeArticles(PDO $pdo): array {
|
||
return $pdo->query("SELECT * FROM knowledge_base ORDER BY updated_at DESC")->fetchAll();
|
||
}
|
||
|
||
// Поиск с FULLTEXT или LIKE
|
||
function searchLocalKnowledgeFulltext(PDO $pdo, string $query, int $limit = 30): array {
|
||
if (strlen($query) < 3) {
|
||
$stmt = $pdo->prepare("SELECT id, title, SUBSTRING(content, 1, 200) AS preview, updated_at FROM knowledge_base WHERE title LIKE :q1 OR content LIKE :q2 ORDER BY updated_at DESC LIMIT $limit");
|
||
$like = '%' . $query . '%';
|
||
$stmt->execute(['q1' => $like, 'q2' => $like]);
|
||
return $stmt->fetchAll();
|
||
}
|
||
$stmt = $pdo->prepare("SELECT id, title, SUBSTRING(content, 1, 200) AS preview, updated_at, MATCH(title, content) AGAINST(:query IN NATURAL LANGUAGE MODE) AS relevance FROM knowledge_base WHERE MATCH(title, content) AGAINST(:query IN NATURAL LANGUAGE MODE) ORDER BY relevance DESC LIMIT $limit");
|
||
$stmt->execute(['query' => $query]);
|
||
return $stmt->fetchAll();
|
||
}
|
||
|
||
// Инкрементальная синхронизация (с проверкой существования колонки)
|
||
function syncKnowledgeBaseIncremental(PDO $pdo, GlpiApi $glpi, array &$log = []): void {
|
||
$log[] = "=== Инкрементальная синхронизация ===";
|
||
|
||
// Проверяем наличие колонки glpi_updated_at
|
||
try {
|
||
$pdo->query("SELECT glpi_updated_at FROM knowledge_base LIMIT 1");
|
||
} catch (PDOException $e) {
|
||
$log[] = "Ошибка: колонка glpi_updated_at отсутствует. Выполните ALTER TABLE knowledge_base ADD COLUMN glpi_updated_at DATETIME DEFAULT NULL;";
|
||
return;
|
||
}
|
||
|
||
$stmt = $pdo->query("SELECT MAX(glpi_updated_at) as last FROM knowledge_base");
|
||
$lastSync = $stmt->fetch()['last'];
|
||
$since = $lastSync ? $lastSync : '1970-01-01 00:00:00';
|
||
|
||
$glpiItems = $glpi->searchKnowbaseItemsSince($since);
|
||
$log[] = "Найдено изменённых статей в GLPI: " . count($glpiItems);
|
||
|
||
foreach ($glpiItems as $glpiArticle) {
|
||
$glpiId = (int)$glpiArticle['id'];
|
||
$fullArticle = $glpi->getKnowbaseItem($glpiId);
|
||
if (!$fullArticle) continue;
|
||
|
||
$title = $fullArticle['name'];
|
||
$content = $fullArticle['answer'] ?? '';
|
||
$content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||
$content = strip_tags($content, '<p><a><img><ul><li><ol><h1><h2><h3><h4><strong><em><span><div><br>');
|
||
$glpiUpdatedAt = $fullArticle['date_mod'] ?? date('Y-m-d H:i:s');
|
||
|
||
// Получаем документы (улучшенным методом)
|
||
$docs = $glpi->getKnowbaseItemDocuments($glpiId);
|
||
$log[] = "Статья GLPI #$glpiId: найдено документов: " . count($docs);
|
||
$documentsMeta = [];
|
||
foreach ($docs as $doc) {
|
||
$documentsMeta[] = [
|
||
'id' => $doc['id'],
|
||
'name' => $doc['name'] ?? $doc['filename'] ?? "document_{$doc['id']}",
|
||
'mime' => $doc['mime'] ?? '',
|
||
'path' => null // путь будет установлен при первом скачивании
|
||
];
|
||
}
|
||
|
||
$existing = findLocalKnowledgeByGlpiId($pdo, $glpiId);
|
||
if ($existing) {
|
||
updateLocalKnowledgeArticleMeta($pdo, $existing['id'], $title, $content, $documentsMeta, $glpiUpdatedAt);
|
||
$log[] = "Обновлена статья GLPI #$glpiId";
|
||
} else {
|
||
insertLocalKnowledgeArticle($pdo, $glpiId, $title, $content, $documentsMeta, $glpiUpdatedAt);
|
||
$log[] = "Добавлена новая статья GLPI #$glpiId";
|
||
}
|
||
}
|
||
$log[] = "=== Синхронизация завершена ===";
|
||
}
|
||
|
||
function findLocalKnowledgeByGlpiId(PDO $pdo, int $glpiId): ?array {
|
||
$stmt = $pdo->prepare("SELECT * FROM knowledge_base WHERE glpi_id = ?");
|
||
$stmt->execute([$glpiId]);
|
||
return $stmt->fetch() ?: null;
|
||
}
|
||
|
||
function insertLocalKnowledgeArticle(PDO $pdo, int $glpiId, string $title, string $content, array $documents, string $glpiUpdatedAt): int {
|
||
$stmt = $pdo->prepare("INSERT INTO knowledge_base (glpi_id, title, content, documents, glpi_updated_at) VALUES (?, ?, ?, ?, ?)");
|
||
$stmt->execute([$glpiId, $title, $content, json_encode($documents, JSON_UNESCAPED_UNICODE), $glpiUpdatedAt]);
|
||
return (int)$pdo->lastInsertId();
|
||
}
|
||
|
||
function updateLocalKnowledgeArticleMeta(PDO $pdo, int $localId, string $title, string $content, array $documents, string $glpiUpdatedAt): void {
|
||
$stmt = $pdo->prepare("SELECT documents FROM knowledge_base WHERE id = ?");
|
||
$stmt->execute([$localId]);
|
||
$old = $stmt->fetch();
|
||
$oldDocs = json_decode($old['documents'], true);
|
||
// Сохраняем уже скачанные пути
|
||
foreach ($documents as &$newDoc) {
|
||
foreach ($oldDocs as $oldDoc) {
|
||
if ($oldDoc['id'] == $newDoc['id'] && isset($oldDoc['path'])) {
|
||
$newDoc['path'] = $oldDoc['path'];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
$update = $pdo->prepare("UPDATE knowledge_base SET title = ?, content = ?, documents = ?, glpi_updated_at = ?, updated_at = NOW() WHERE id = ?");
|
||
$update->execute([$title, $content, json_encode($documents, JSON_UNESCAPED_UNICODE), $glpiUpdatedAt, $localId]);
|
||
}
|
||
|
||
function getLocalKnowledgeArticle(PDO $pdo, int $id): ?array {
|
||
$stmt = $pdo->prepare("SELECT * FROM knowledge_base WHERE id = ?");
|
||
$stmt->execute([$id]);
|
||
return $stmt->fetch() ?: null;
|
||
}
|
||
|
||
function lazyLoadDocument(PDO $pdo, int $knowledgeId, int $docId, string $docName, string $docMime, GlpiApi $glpi): ?string {
|
||
$safeName = $docId . '_' . sanitizeFileName($docName);
|
||
$cachePath = KB_CACHE_DIR . $safeName;
|
||
if (file_exists($cachePath)) {
|
||
return $cachePath;
|
||
}
|
||
$content = $glpi->getDocumentContent($docId);
|
||
if ($content === null) return null;
|
||
if (file_put_contents($cachePath, $content)) {
|
||
// Обновляем путь в БД
|
||
$stmt = $pdo->prepare("SELECT documents FROM knowledge_base WHERE id = ?");
|
||
$stmt->execute([$knowledgeId]);
|
||
$row = $stmt->fetch();
|
||
$docs = json_decode($row['documents'], true);
|
||
foreach ($docs as &$d) {
|
||
if ($d['id'] == $docId) {
|
||
$d['path'] = $safeName;
|
||
break;
|
||
}
|
||
}
|
||
$update = $pdo->prepare("UPDATE knowledge_base SET documents = ? WHERE id = ?");
|
||
$update->execute([json_encode($docs), $knowledgeId]);
|
||
return $cachePath;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function sanitizeFileName(string $name): string {
|
||
$name = basename($name);
|
||
return preg_replace('/[^a-zA-Z0-9_\-\.]/u', '_', $name);
|
||
}
|
||
|
||
function getDocumentUrl(PDO $pdo, int $knowledgeId, int $docId): ?string {
|
||
$stmt = $pdo->prepare("SELECT documents FROM knowledge_base WHERE id = ?");
|
||
$stmt->execute([$knowledgeId]);
|
||
$row = $stmt->fetch();
|
||
if (!$row) return null;
|
||
$docs = json_decode($row['documents'], true);
|
||
foreach ($docs as $doc) {
|
||
if ($doc['id'] == $docId) {
|
||
if (!empty($doc['path']) && file_exists(KB_CACHE_DIR . $doc['path'])) {
|
||
return 'get_document.php?kid=' . $knowledgeId . '&doc=' . $docId;
|
||
} else {
|
||
// Файл ещё не скачан, но ссылка та же - будет скачан при запросе
|
||
return 'get_document.php?kid=' . $knowledgeId . '&doc=' . $docId;
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
} |