Files
crm.clientright.ru/crm_extensions/simple_project_updater_v2.php
Fedor ac7467f0b4 Major CRM updates: AI Assistant, Court Status API, S3 integration improvements, and extensive file storage system
- Added comprehensive AI Assistant system (aiassist/ directory):
  * Vector search and embedding capabilities
  * Typebot proxy integration
  * Elastic search functionality
  * Message classification and chat history
  * MCP proxy for external integrations

- Implemented Court Status API (GetCourtStatus.php):
  * Real-time court document status checking
  * Integration with external court systems
  * Comprehensive error handling and logging

- Enhanced S3 integration:
  * Improved file backup system with metadata
  * Batch processing capabilities
  * Enhanced error logging and recovery
  * Copy operations with URL fixing

- Added Telegram contact creation API
- Improved error logging across all modules
- Enhanced callback system for AI responses
- Extensive backup file storage with timestamps
- Updated documentation and README files

- File storage improvements:
  * Thousands of backup files with proper metadata
  * Fix operations for broken file references
  * Project-specific backup and recovery systems
  * Comprehensive file integrity checking

Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
2025-10-16 11:17:21 +03:00

459 lines
22 KiB
PHP
Raw 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
// simple_project_updater_v2.php
// Правильная версия: использует CRM API вместо прямых SQL запросов
// Устанавливаем рабочую директорию
chdir(__DIR__ . '/..');
require_once 'config.inc.php';
require_once 'include/utils/utils.php';
require_once 'includes/Loader.php';
vimport('includes.runtime.Globals');
require_once 'include/database/PearDatabase.php';
require_once 'modules/Users/Users.php';
require_once 'include/Webservices/Utils.php';
require_once 'include/Webservices/Create.php';
require_once 'include/Webservices/Login.php';
require_once 'include/Webservices/AuthToken.php';
require_once 'include/Webservices/AddRelated.php';
require_once 'data/CRMEntity.php';
require_once 'modules/Vtiger/CRMEntity.php';
require_once 'crm_extensions/file_storage/S3Client.php';
$adb = PearDatabase::getInstance();
// Логирование
function log_message($level, $message) {
$log_file = __DIR__ . '/logs/project_update.log';
$timestamp = date('Y-m-d H:i:s');
$log_entry = "{$timestamp} - {$level}: {$message}\n";
file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX);
}
// Функция для создания документа через внутренние функции CRM
function createDocumentViaAPI($documentData) {
global $adb, $current_user;
// Создаем пользователя для CRM
if (!isset($current_user) || !$current_user) {
$current_user = new Users();
$current_user->retrieveCurrentUserInfoFromFile(8); // Фёдор Коробков
}
// Создаем документ через прямые SQL запросы (как в simple_project_updater.php)
log_message('DEBUG', "Создаем документ через прямые SQL запросы");
global $mysqli;
// Получаем следующий ID
log_message('DEBUG', "Получаем следующий ID для документа");
$result = $mysqli->query("SELECT MAX(crmid) as max_id FROM vtiger_crmentity");
$row = $result->fetch_assoc();
$next_id = ($row['max_id'] ?? 0) + 1;
log_message('DEBUG', "Следующий ID: $next_id");
$created_time = date('Y-m-d H:i:s');
// Создаем запись в vtiger_crmentity
log_message('DEBUG', "Создаем запись в vtiger_crmentity");
$sql = "INSERT INTO vtiger_crmentity (crmid, smcreatorid, smownerid, modifiedby, setype, description, createdtime, modifiedtime, presence, deleted, label, source) VALUES ($next_id, 8, 8, 8, 'Documents', '" . $mysqli->real_escape_string($documentData['notecontent']) . "', '$created_time', '$created_time', 1, 0, '" . $mysqli->real_escape_string($documentData['notes_title']) . "', 'WEBSERVICE')";
log_message('DEBUG', "SQL: $sql");
$result = $mysqli->query($sql);
if (!$result) {
throw new Exception('Ошибка создания записи в vtiger_crmentity: ' . $mysqli->error);
}
log_message('DEBUG', "Запись в vtiger_crmentity создана");
// Создаем запись в vtiger_notes
log_message('DEBUG', "Создаем запись в vtiger_notes");
$note_no = ОК_' . $next_id;
// Формируем S3 URL для filename
$s3_bucket = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
$s3_key = 'crm2/CRM_Active_Files/Documents/' . $next_id . '/' . $documentData['filename'];
$s3_url = "https://s3.twcstorage.ru/$s3_bucket/$s3_key";
$sql = "INSERT INTO vtiger_notes (notesid, note_no, title, filename, notecontent, folderid, filetype, filelocationtype, filedownloadcount, filestatus, filesize, fileversion, tags, its4you_company, s3_bucket, s3_key, s3_etag, nc_path) VALUES ($next_id, '$note_no', '" . $mysqli->real_escape_string($documentData['notes_title']) . "', '" . $mysqli->real_escape_string(strip_tags(trim($s3_url))) . "', '" . $mysqli->real_escape_string($documentData['notecontent']) . "', 1, '" . $mysqli->real_escape_string($documentData['filetype']) . "', '" . $mysqli->real_escape_string($documentData['filelocationtype']) . "', 0, '" . $mysqli->real_escape_string($documentData['filestatus']) . "', " . intval($documentData['filesize']) . ", '" . $mysqli->real_escape_string($documentData['fileversion']) . "', '', '', '', '', '', '')";
log_message('DEBUG', "SQL vtiger_notes: $sql");
$result = $mysqli->query($sql);
if (!$result) {
throw new Exception('Ошибка создания записи в vtiger_notes: ' . $mysqli->error);
}
log_message('DEBUG', "Запись в vtiger_notes создана");
// Обновляем последовательность
$mysqli->query("UPDATE vtiger_crmentity_seq SET id = $next_id");
log_message('DEBUG', "Документ создан с ID: $next_id");
return [
'id' => '15x' . $next_id,
'notes_title' => $documentData['notes_title'],
'createdtime' => $created_time,
'modifiedtime' => $created_time
];
}
// Функция для привязки документа к проекту через прямые SQL запросы
function linkDocumentToProject($projectId, $documentId) {
global $mysqli;
log_message('DEBUG', "Привязываем документ $documentId к проекту $projectId");
// Создаем связь в vtiger_senotesrel
$stmt = $mysqli->prepare("INSERT INTO vtiger_senotesrel (crmid, notesid) VALUES (?, ?)");
$stmt->bind_param('ii', $projectId, $documentId);
$stmt->execute();
$stmt->close();
log_message('DEBUG', "Связь создана между проектом $projectId и документом $documentId");
return true;
}
try {
// Читаем JSON из stdin
$input = file_get_contents('php://stdin');
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Ошибка парсинга JSON: ' . json_last_error_msg());
}
// Проверяем структуру данных - может быть массив или объект
if (is_array($data) && isset($data[0])) {
// Если это массив, берем первый элемент
$item = $data[0];
} elseif (is_array($data) && isset($data['content'])) {
// Если это объект, используем его напрямую
$item = $data;
} else {
throw new Exception('Неверная структура данных: ожидается объект с полем content или массив объектов');
}
// Проверяем наличие обязательных полей
if (!$item || !isset($item['content']) || !is_array($item['content'])) {
throw new Exception('Неверная структура данных: отсутствует content');
}
$content = $item['content'];
$file_url = trim($item['file'] ?? '');
$file_name = $item['file_name'] ?? '';
if (empty($file_url) || empty($file_name)) {
throw new Exception('Отсутствует URL файла или имя файла');
}
// Инициализируем пользователя CRM
$current_user = new Users();
$current_user->retrieveCurrentUserInfoFromFile(8); // Фёдор Коробков
log_message('INFO', "Инициализирован пользователь CRM: " . $current_user->user_name);
// Подключаемся к базе данных для поиска проекта
$mysqli = new mysqli('localhost', 'ci20465_72new', 'EcY979Rn', 'ci20465_72new');
if ($mysqli->connect_error) {
throw new Exception('Не удалось подключиться к базе данных: ' . $mysqli->connect_error);
}
$mysqli->set_charset('utf8');
// Проверяем кодировку соединения
$charset_result = $mysqli->query("SELECT @@character_set_connection, @@collation_connection");
if ($charset_result) {
$charset_row = $charset_result->fetch_row();
log_message('DEBUG', "Кодировка БД: " . $charset_row[0] . ", collation: " . $charset_row[1]);
}
$plaintiff_fio = $content['plaintiff_fio'] ?? '';
$case_number = $content['case_number'] ?? '';
$uid = $content['uid'] ?? '';
log_message('DEBUG', "Поиск проекта по данным: plaintiff_fio='$plaintiff_fio', case_number='$case_number', uid='$uid'");
$project_id = null;
// Улучшенный поиск по комбинации критериев с нестрогим соответствием
$search_criteria = [];
$search_params = [];
$param_types = '';
// ФИО с учетом мужского/женского рода
if ($plaintiff_fio) {
$surname = explode(' ', $plaintiff_fio)[0];
$surname_male = $surname; // Соколов
$surname_female = $surname . 'а'; // Соколова
$search_criteria[] = "(p.projectname LIKE ? OR e.description LIKE ? OR p.projectname LIKE ? OR e.description LIKE ?)";
$search_params[] = "%" . $mysqli->real_escape_string($surname_male) . "%";
$search_params[] = "%" . $mysqli->real_escape_string($surname_male) . "%";
$search_params[] = "%" . $mysqli->real_escape_string($surname_female) . "%";
$search_params[] = "%" . $mysqli->real_escape_string($surname_female) . "%";
$param_types .= 'ssss';
log_message('DEBUG', "Поиск по ФИО: '$surname_male' и '$surname_female'");
}
// Суд
if (!empty($content['court'])) {
$court = $content['court'];
$search_criteria[] = "(e.description LIKE ? OR cf.cf_1499 LIKE ? OR cf.cf_2278 LIKE ?)";
$search_params[] = "%" . $mysqli->real_escape_string($court) . "%";
$search_params[] = "%" . $mysqli->real_escape_string($court) . "%";
$search_params[] = "%" . $mysqli->real_escape_string($court) . "%";
$param_types .= 'sss';
log_message('DEBUG', "Поиск по суду: '$court'");
}
// Номер дела
if ($case_number) {
$search_criteria[] = "(p.project_no = ? OR e.description LIKE ?)";
$search_params[] = $case_number;
$search_params[] = "%" . $mysqli->real_escape_string($case_number) . "%";
$param_types .= 'ss';
log_message('DEBUG', "Поиск по номеру дела: '$case_number'");
}
// УИД
if ($uid) {
$search_criteria[] = "(e.description LIKE ?)";
$search_params[] = "%" . $mysqli->real_escape_string($uid) . "%";
$param_types .= 's';
log_message('DEBUG', "Поиск по УИД: '$uid'");
}
// Выполняем поиск если есть критерии
if (!empty($search_criteria)) {
// Сначала пробуем строгий поиск (все критерии)
$where_clause = implode(' AND ', $search_criteria);
$sql = "
SELECT p.projectid, p.projectname, p.project_no, e.description
FROM vtiger_project p
JOIN vtiger_crmentity e ON e.crmid = p.projectid AND e.deleted = 0
LEFT JOIN vtiger_projectcf cf ON cf.projectid = p.projectid
WHERE $where_clause
ORDER BY
CASE
WHEN p.projectname LIKE '%" . $mysqli->real_escape_string($surname ?? '') . "%' THEN 1
WHEN e.description LIKE '%" . $mysqli->real_escape_string($surname ?? '') . "%' THEN 2
ELSE 3
END,
p.projectid ASC
LIMIT 1
";
log_message('DEBUG', "Строгий поиск SQL: $sql");
log_message('DEBUG', "Параметры: " . json_encode($search_params));
$stmt = $mysqli->prepare($sql);
if ($stmt) {
if (!empty($search_params)) {
$stmt->bind_param($param_types, ...$search_params);
}
$stmt->execute();
$result = $stmt->get_result();
if ($row = $result->fetch_assoc()) {
$project_id = $row['projectid'];
log_message('SUCCESS', "Найден проект (строгий поиск): ID=$project_id, название='{$row['projectname']}'");
}
$stmt->close();
} else {
log_message('ERROR', "Ошибка подготовки SQL: " . $mysqli->error);
}
// Если строгий поиск не дал результатов, пробуем поиск только по ФИО
if (!$project_id && !empty($surname)) {
$sql = "
SELECT p.projectid, p.projectname, p.project_no, e.description
FROM vtiger_project p
JOIN vtiger_crmentity e ON e.crmid = p.projectid AND e.deleted = 0
LEFT JOIN vtiger_projectcf cf ON cf.projectid = p.projectid
WHERE (p.projectname LIKE ? OR e.description LIKE ? OR p.projectname LIKE ? OR e.description LIKE ?)
ORDER BY
CASE
WHEN p.projectname LIKE '%" . $mysqli->real_escape_string($surname) . "%' THEN 1
WHEN e.description LIKE '%" . $mysqli->real_escape_string($surname) . "%' THEN 2
ELSE 3
END,
p.projectid ASC
LIMIT 1
";
log_message('DEBUG', "Поиск только по ФИО SQL: $sql");
$stmt = $mysqli->prepare($sql);
if ($stmt) {
$surname_male = "%" . $mysqli->real_escape_string($surname) . "%";
$surname_female = "%" . $mysqli->real_escape_string($surname . 'а') . "%";
log_message('DEBUG', "Параметры поиска по ФИО: '$surname_male', '$surname_female'");
log_message('DEBUG', "Длина параметров: " . strlen($surname_male) . ", " . strlen($surname_female));
log_message('DEBUG', "Кодировка параметров: " . mb_detect_encoding($surname_male) . ", " . mb_detect_encoding($surname_female));
$stmt->bind_param('ssss', $surname_male, $surname_male, $surname_female, $surname_female);
$stmt->execute();
$result = $stmt->get_result();
log_message('DEBUG', "Количество найденных записей: " . $result->num_rows);
if ($row = $result->fetch_assoc()) {
$project_id = $row['projectid'];
log_message('SUCCESS', "Найден проект (поиск по ФИО): ID=$project_id, название='{$row['projectname']}'");
} else {
log_message('DEBUG', "Проект не найден по ФИО");
}
$stmt->close();
} else {
log_message('ERROR', "Ошибка подготовки SQL для поиска по ФИО: " . $mysqli->error);
}
}
}
if (!$project_id) {
throw new Exception('Проект не найден по указанным данным');
}
log_message('INFO', "Найден проект ID: $project_id");
// Формируем название документа
$document_title = "СУДЕБНЫЙ ДОКУМЕНТ";
if (!empty($content['document_title'])) {
$document_title = $content['document_title'];
}
if (!empty($content['case_number'])) {
$document_title .= " по делу " . $content['case_number'];
}
if (!empty($content['plaintiff_fio'])) {
$document_title .= " " . $content['plaintiff_fio'];
}
$document_title .= " " . date('d.m.Y');
// Создаём документ через CRM API
$documentData = [
'notes_title' => $document_title,
'filename' => $file_name,
'assigned_user_id' => '19x8', // Фёдор Коробков
'notecontent' => "Документ создан автоматически из судебного документа.\n\n" .
"Номер дела: " . ($content['case_number'] ?? 'не указан') . "\n" .
"Суд: " . ($content['court'] ?? 'не указан') . "\n" .
"Истец: " . ($content['plaintiff_fio'] ?? 'не указан') . "\n" .
"УИД: " . ($content['uid'] ?? 'не указан') . "\n" .
"Дата создания: " . date('d.m.Y H:i:s'),
'filetype' => 'application/pdf',
'filesize' => '0', // Будет обновлено после загрузки
'filelocationtype' => 'E', // External URL
'fileversion' => '1.0',
'filestatus' => '1', // Active
'folderid' => '22x1', // Default папка
];
log_message('INFO', "Создаём документ: $document_title");
$documentResult = createDocumentViaAPI($documentData);
$documentWsId = $documentResult['id'];
list(, $documentNumericId) = explode('x', $documentWsId, 2);
log_message('SUCCESS', "Документ создан: $documentWsId (numeric: $documentNumericId)");
// S3 метаданные уже установлены при создании записи
log_message('SUCCESS', "S3 метаданные установлены для документа $documentNumericId");
// Загружаем файл в S3
if (!empty($file_url) && !empty($file_name)) {
log_message('DEBUG', "Начинаем загрузку файла в S3: $file_url");
// Скачиваем файл
$file_content = file_get_contents($file_url);
if ($file_content === false) {
log_message('ERROR', "Не удалось скачать файл: $file_url");
} else {
log_message('DEBUG', "Файл скачан, размер: " . strlen($file_content) . " байт");
// Генерируем ETag для S3
$s3_etag = '"' . md5($file_content) . '"';
// Формируем S3 ключ
$s3_bucket = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
$s3_key = 'crm2/CRM_Active_Files/Documents/' . $documentNumericId . '/' . $file_name;
// Загружаем файл в S3 через S3Client
try {
log_message('DEBUG', "Загружаем конфигурацию S3");
// Загружаем конфигурацию S3
$config = require __DIR__ . '/file_storage/config.php';
log_message('DEBUG', "Конфигурация S3 загружена");
$s3Client = new S3Client($config['s3']);
log_message('DEBUG', "S3Client создан");
// Создаем временный файл
$temp_file = tempnam(sys_get_temp_dir(), 'crm_doc_');
file_put_contents($temp_file, $file_content);
// Загружаем файл в S3
log_message('DEBUG', "Загружаем файл в S3 с ключом: $s3_key");
$result = $s3Client->uploadFile($temp_file, $s3_key, [
'ContentType' => 'application/pdf'
]);
// Удаляем временный файл
unlink($temp_file);
log_message('SUCCESS', "Файл успешно загружен в S3");
// Обновляем ETag, размер файла и S3 метаданные в БД
$stmt = $mysqli->prepare("
UPDATE vtiger_notes
SET s3_etag = ?, filesize = ?, s3_bucket = ?, s3_key = ?
WHERE notesid = ?
");
$file_size = strlen($file_content);
$stmt->bind_param('sissi', $s3_etag, $file_size, $s3_bucket, $s3_key, $documentNumericId);
$stmt->execute();
$stmt->close();
log_message('SUCCESS', "S3 ETag и размер файла обновлены");
} catch (Exception $e) {
log_message('ERROR', "Ошибка загрузки в S3: " . $e->getMessage());
}
}
}
// Привязываем документ к проекту
$linkSuccess = linkDocumentToProject($project_id, (int)$documentNumericId);
if ($linkSuccess) {
log_message('SUCCESS', "Документ успешно привязан к проекту");
} else {
log_message('ERROR', "Ошибка привязки документа к проекту");
}
$mysqli->close();
// Формируем результат
$result = [
'success' => true,
'project_id' => $project_id,
'plaintiff_fio' => explode(' ', $plaintiff_fio)[0],
'case_number' => $case_number,
'uid' => $uid,
'document_id' => $documentNumericId,
'document_ws_id' => $documentWsId,
's3_url' => $s3_url,
'message' => 'Документ успешно создан через CRM API и добавлен к проекту'
];
log_message('SUCCESS', "Обработка завершена успешно: " . json_encode($result, JSON_UNESCAPED_UNICODE));
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
return 0;
} catch (Exception $e) {
$error_message = $e->getMessage();
log_message('ERROR', "Ошибка: $error_message");
echo json_encode([
'success' => false,
'error' => $error_message,
'timestamp' => date('Y-m-d H:i:s')
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
return 1;
}
?>