Files
crm.clientright.ru/ParseAndCreateEvent.php
Fedor 01c4fe80b5 chore: snapshot current working tree changes
Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
2026-03-26 14:19:01 +03:00

530 lines
25 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
/**
* Обёртка для парсинга ответа от parscourt.php и создания события
*
* Принимает те же параметры что и parscourt.php
* Вызывает parscourt.php, получает JSON с last_event
* И создаёт событие через CreateCourtEvent.php
*
* Этот скрипт можно дёргать из workflow вместо parscourt.php
*/
// Устанавливаем рабочую директорию
chdir(__DIR__);
// Логирование
function log_wrapper($level, $message) {
$log_file = 'logs/parse_and_create_event.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);
}
try {
$scriptStartTime = microtime(true);
log_wrapper('INFO', '=== НАЧАЛО ОБРАБОТКИ ===');
log_wrapper('DEBUG', "Память на старте: " . round(memory_get_usage() / 1024 / 1024, 2) . " МБ");
// Получаем параметры (из POST, GET или argv)
$params = array_merge($_GET, $_POST);
// Если параметров нет, пробуем argv (для вызова через CLI)
if (empty($params) && !empty($argv)) {
for ($i = 1; $i < count($argv); $i++) {
if (strpos($argv[$i], '=') !== false) {
list($key, $value) = explode('=', $argv[$i], 2);
$params[$key] = $value;
}
}
}
log_wrapper('DEBUG', "Параметры: " . json_encode($params, JSON_UNESCAPED_UNICODE));
// Проверяем обязательные параметры
if (empty($params['project_id'])) {
// Если это прямой доступ через браузер без параметров, показываем информативную страницу
if (empty($params) && !empty($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'curl') === false) {
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ParseAndCreateEvent - Тестирование</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-top: 0;
}
.info {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 15px;
margin: 20px 0;
border-radius: 4px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 600;
color: #555;
}
input[type="text"], input[type="number"], textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
textarea {
min-height: 80px;
font-family: monospace;
}
.required {
color: #f44336;
}
button {
background: #2196f3;
color: white;
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
margin-right: 10px;
}
button:hover {
background: #1976d2;
}
.result {
margin-top: 20px;
padding: 15px;
border-radius: 4px;
display: none;
}
.result.success {
background: #e8f5e9;
border-left: 4px solid #4caf50;
display: block;
}
.result.error {
background: #ffebee;
border-left: 4px solid #f44336;
display: block;
}
pre {
background: #f5f5f5;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
font-size: 12px;
}
.help-text {
font-size: 12px;
color: #666;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1>🔍 ParseAndCreateEvent - Тестирование</h1>
<div class="info">
<strong>Назначение:</strong> Этот скрипт парсит ответ от parscourt.php и создаёт событие через CreateCourtEvent_v2.php.<br>
<strong>Использование:</strong> Обычно вызывается из n8n workflow или через API с параметрами.
</div>
<form id="testForm" method="POST">
<div class="form-group">
<label for="project_id">Project ID <span class="required">*</span></label>
<input type="number" id="project_id" name="project_id" required placeholder="Например: 12345">
<div class="help-text">Обязательный параметр. ID проекта в CRM.</div>
</div>
<div class="form-group">
<label for="case_number">Номер дела</label>
<input type="text" id="case_number" name="case_number" placeholder="02-15800/2025">
<div class="help-text">По умолчанию: 02-15800/2025</div>
</div>
<div class="form-group">
<label for="status">Статус</label>
<input type="text" id="status" name="status" placeholder="">
</div>
<div class="form-group">
<label for="link1">Ссылка 1</label>
<input type="text" id="link1" name="link1" placeholder="">
</div>
<div class="form-group">
<label for="link2">Ссылка 2</label>
<input type="text" id="link2" name="link2" placeholder="">
</div>
<div class="form-group">
<label for="link3">Ссылка 3</label>
<input type="text" id="link3" name="link3" placeholder="">
</div>
<div class="form-group">
<label for="uid">UID</label>
<input type="text" id="uid" name="uid" placeholder="">
</div>
<div class="form-group">
<label>
<input type="checkbox" name="use_new_parser" value="true" checked> Использовать новый парсер
</label>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="skip_duplicate_check" value="true"> Пропустить проверку дубликатов
</label>
</div>
<button type="submit">🚀 Запустить парсинг и создание события</button>
<button type="button" onclick="window.location.reload()">🔄 Сбросить</button>
</form>
<div id="result" class="result"></div>
</div>
<script>
document.getElementById('testForm').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
const resultDiv = document.getElementById('result');
resultDiv.className = 'result';
resultDiv.innerHTML = '<p>⏳ Обработка...</p>';
resultDiv.style.display = 'block';
try {
const response = await fetch(window.location.href, {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
resultDiv.className = 'result success';
resultDiv.innerHTML = '<h3>✅ Успешно!</h3><pre>' + JSON.stringify(data, null, 2) + '</pre>';
} else {
resultDiv.className = 'result error';
resultDiv.innerHTML = '<h3>❌ Ошибка</h3><pre>' + JSON.stringify(data, null, 2) + '</pre>';
}
} catch (error) {
resultDiv.className = 'result error';
resultDiv.innerHTML = '<h3>❌ Ошибка запроса</h3><pre>' + error.message + '</pre>';
}
});
</script>
</body>
</html>
<?php
exit(0);
}
// Для API вызовов (curl, n8n и т.д.) возвращаем JSON ошибку
throw new Exception('Параметр project_id обязателен');
}
$projectId = $params['project_id'];
// Формируем параметры для parscourt.php
// ВАЖНО: Проверяем не только на null, но и на пустые строки
// parscourt.php требует: status, link (из link1/link2/link3), case_number
$status = !empty($params['status']) ? $params['status'] : '';
$link1 = !empty($params['link1']) ? $params['link1'] : '';
$link2 = !empty($params['link2']) ? $params['link2'] : '';
$link3 = !empty($params['link3']) ? $params['link3'] : '';
$case_number = !empty($params['case_number']) ? $params['case_number'] : '02-15800/2025'; // Дефолтный номер дела для тестирования
$uid = !empty($params['uid']) ? $params['uid'] : '';
// Проверяем обязательные параметры для parscourt.php
$link = $link1 ?: ($link2 ?: $link3);
if (empty($status) || empty($link) || empty($case_number)) {
$missing = [];
if (empty($status)) $missing[] = 'status';
if (empty($link)) $missing[] = 'link1/link2/link3 (хотя бы одна ссылка)';
if (empty($case_number)) $missing[] = 'case_number';
log_wrapper('ERROR', 'Отсутствуют обязательные параметры для parscourt.php: ' . implode(', ', $missing));
throw new Exception('Отсутствуют обязательные параметры для парсинга: ' . implode(', ', $missing));
}
$parscourtParams = [
'project_id' => $projectId,
'status' => $status,
'link1' => $link1,
'link2' => $link2,
'link3' => $link3,
'case_number' => $case_number,
'uid' => $uid,
'use_new_parser' => !empty($params['use_new_parser']) ? $params['use_new_parser'] : 'true',
'skip_duplicate_check' => !empty($params['skip_duplicate_check']) ? $params['skip_duplicate_check'] : 'false'
];
log_wrapper('INFO', "Вызываем parscourt.php для проекта $projectId");
// Формируем URL для вызова parscourt.php
// ВСЕГДА используем прямой доступ к Apache через HTTP (порт 81) для внутренних вызовов
// Это полностью обходит проблему с SSL сертификатом
$domain = $_SERVER['HTTP_HOST'] ?? 'crm.clientright.ru';
// Для внутренних вызовов всегда используем HTTP на порту 81
// Это работает быстрее и обходит все проблемы с SSL
$curlUrl = 'http://127.0.0.1:81/parscourt.php';
log_wrapper('DEBUG', "Используем внутренний вызов: $curlUrl");
log_wrapper('DEBUG', "Домен: $domain");
log_wrapper('DEBUG', "Параметры для parscourt.php: " . json_encode($parscourtParams, JSON_UNESCAPED_UNICODE));
$postData = http_build_query($parscourtParams);
log_wrapper('DEBUG', "POST данные (http_build_query): $postData");
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $curlUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 180); // Увеличен таймаут до 3 минут
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); // Таймаут на установку соединения
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
// ВАЖНО: Отключаем проверку SSL на всякий случай (если вдруг используется HTTPS)
// Это гарантирует, что SSL ошибки не возникнут
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
// Устанавливаем заголовок Host для правильной работы с виртуальными хостами Apache
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Host: ' . $domain]);
log_wrapper('DEBUG', "Отправляем cURL запрос к parscourt.php...");
$startTime = microtime(true);
$output = curl_exec($ch);
$execTime = round(microtime(true) - $startTime, 2);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
$curlErrno = curl_errno($ch);
$curlInfo = curl_getinfo($ch);
curl_close($ch);
log_wrapper('DEBUG', "cURL выполнен за {$execTime} сек, HTTP код: $httpCode");
if ($curlErrno !== 0) {
log_wrapper('ERROR', "cURL ошибка #{$curlErrno}: {$curlError}");
log_wrapper('DEBUG', "cURL info: " . json_encode($curlInfo, JSON_UNESCAPED_UNICODE));
throw new Exception("Ошибка cURL при вызове parscourt.php: [{$curlErrno}] {$curlError}");
}
if ($httpCode !== 200) {
log_wrapper('ERROR', "Неожиданный HTTP код: $httpCode");
log_wrapper('DEBUG', "Первые 500 символов ответа: " . substr($output, 0, 500));
throw new Exception("Ошибка вызова parscourt.php: HTTP $httpCode");
}
$outputSize = strlen($output);
log_wrapper('DEBUG', "Получен ответ от parscourt.php (размер: {$outputSize} байт)");
log_wrapper('DEBUG', "Ответ от parscourt.php: $output");
// Парсим JSON ответ
$parscourtResponse = json_decode($output, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$jsonError = json_last_error_msg();
log_wrapper('ERROR', "Ошибка декодирования JSON: {$jsonError}");
log_wrapper('DEBUG', "Первые 1000 символов проблемного ответа: " . substr($output, 0, 1000));
throw new Exception('Ошибка декодирования JSON от parscourt.php: ' . $jsonError);
}
log_wrapper('DEBUG', "Распарсенный ответ: " . json_encode($parscourtResponse, JSON_UNESCAPED_UNICODE));
// Проверяем наличие last_event и что он не пустой
if (empty($parscourtResponse['last_event']) ||
!isset($parscourtResponse['last_event']['Наименование']) ||
empty($parscourtResponse['last_event']['Наименование'])) {
log_wrapper('WARNING', 'Нет данных о событиях (last_event пустой или без названия)');
$response = [
'success' => true,
'message' => 'Парсинг выполнен, но нет новых событий',
'event_created' => false
];
header('Content-Type: application/json; charset=utf-8');
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
exit(0);
}
$lastEvent = $parscourtResponse['last_event'];
// Извлекаем данные события (пробуем оба варианта ключей)
$eventName = $lastEvent['Наименование'] ?? $lastEvent['name'] ?? 'Судебное заседание';
$eventDate = $lastEvent['Дата'] ?? $lastEvent['date'] ?? '';
$eventTime = $lastEvent['Время'] ?? $lastEvent['time'] ?? '';
$location = $lastEvent['Место'] ?? $lastEvent['location'] ?? '';
$result = $lastEvent['Результат'] ?? $lastEvent['result'] ?? '';
$basis = $lastEvent['Основание'] ?? $lastEvent['basis'] ?? '';
$note = $lastEvent['Примечание'] ?? $lastEvent['note'] ?? '';
$publicationDate = $lastEvent['Дата размещения'] ?? $lastEvent['publication_date'] ?? '';
log_wrapper('DEBUG', "Извлеченные данные: eventName='$eventName', eventDate='$eventDate', eventTime='$eventTime'");
log_wrapper('INFO', "Событие извлечено: $eventName ($eventDate $eventTime)");
// Проверяем что дата не пустая
if (empty($eventDate)) {
log_wrapper('WARNING', 'Дата события пустая, пропускаем создание');
$response = [
'success' => true,
'message' => 'Событие не создано: дата отсутствует',
'event_created' => false
];
header('Content-Type: application/json; charset=utf-8');
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
exit(0);
}
// Формируем данные для CreateCourtEvent.php
log_wrapper('DEBUG', "Перед формированием данных: eventName='$eventName', result='$result'");
$eventData = [
'project_id' => $projectId,
'event_name' => $eventName,
'event_date' => $eventDate,
'event_time' => $eventTime,
'location' => $location,
'result' => $result,
'basis' => $basis,
'note' => $note,
'publication_date' => $publicationDate
];
log_wrapper('INFO', "Создаём событие через CreateCourtEvent_v2.php");
log_wrapper('DEBUG', "Данные события: " . json_encode($eventData, JSON_UNESCAPED_UNICODE));
// Вызываем CreateCourtEvent_v2.php через CLI
$createEventCommand = 'php ' . __DIR__ . '/CreateCourtEvent_v2.php';
$eventDataJson = json_encode($eventData, JSON_UNESCAPED_UNICODE);
// Передаём данные через временный файл
$tempFile = tempnam(sys_get_temp_dir(), 'event_data_');
file_put_contents($tempFile, $eventDataJson);
log_wrapper('DEBUG', "Временный файл создан: $tempFile");
log_wrapper('DEBUG', "Запускаем команду: cat $tempFile | $createEventCommand");
$startTimeEvent = microtime(true);
$createEventOutput = shell_exec('cat ' . escapeshellarg($tempFile) . ' | ' . $createEventCommand . ' 2>&1');
$execTimeEvent = round(microtime(true) - $startTimeEvent, 2);
log_wrapper('DEBUG', "CreateCourtEvent_v2.php выполнен за {$execTimeEvent} сек");
// Удаляем временный файл
unlink($tempFile);
log_wrapper('DEBUG', "Временный файл удалён");
log_wrapper('DEBUG', "Ответ от CreateCourtEvent_v2.php (длина: " . strlen($createEventOutput) . " байт): $createEventOutput");
// Фильтруем PHP Notice из ответа
$cleanOutput = preg_replace('/^PHP Notice:.*$/m', '', $createEventOutput);
$cleanOutput = trim($cleanOutput);
log_wrapper('DEBUG', "Очищенный ответ (длина: " . strlen($cleanOutput) . " байт)");
$createEventResponse = json_decode($cleanOutput, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$jsonError = json_last_error_msg();
log_wrapper('ERROR', "Ошибка декодирования JSON от CreateCourtEvent_v2.php: {$jsonError}");
log_wrapper('DEBUG', "Проблемный ответ: $cleanOutput");
throw new Exception('Ошибка декодирования JSON от CreateCourtEvent_v2.php: ' . $jsonError);
}
if (empty($createEventResponse['success'])) {
$errorMsg = $createEventResponse['error'] ?? 'Неизвестная ошибка';
log_wrapper('ERROR', "CreateCourtEvent_v2.php вернул ошибку: $errorMsg");
throw new Exception('Ошибка создания события: ' . $errorMsg);
}
log_wrapper('SUCCESS', "Событие создано: " . $createEventResponse['event_id']);
// Формируем финальный ответ
$response = [
'success' => true,
'message' => 'Парсинг выполнен и событие создано',
'event_created' => true,
'event_id' => $createEventResponse['event_id'],
'event_name' => $eventName,
'event_date' => $eventDate,
'event_time' => $eventTime,
'project_id' => $projectId
];
$totalTime = round(microtime(true) - $scriptStartTime, 2);
$memoryUsed = round(memory_get_usage() / 1024 / 1024, 2);
$memoryPeak = round(memory_get_peak_usage() / 1024 / 1024, 2);
log_wrapper('SUCCESS', "=== ОБРАБОТКА ЗАВЕРШЕНА УСПЕШНО ===");
log_wrapper('INFO', "Общее время выполнения: {$totalTime} сек");
log_wrapper('DEBUG', "Память использовано: {$memoryUsed} МБ (пик: {$memoryPeak} МБ)");
header('Content-Type: application/json; charset=utf-8');
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
exit(0);
} catch (Exception $e) {
$error_message = $e->getMessage();
$totalTime = isset($scriptStartTime) ? round(microtime(true) - $scriptStartTime, 2) : 0;
$memoryUsed = round(memory_get_usage() / 1024 / 1024, 2);
log_wrapper('ERROR', "=== ОШИБКА ОБРАБОТКИ ===");
log_wrapper('ERROR', "Сообщение: $error_message");
log_wrapper('ERROR', "Файл: " . $e->getFile() . " (строка " . $e->getLine() . ")");
log_wrapper('ERROR', "Стек вызовов:");
log_wrapper('ERROR', $e->getTraceAsString());
log_wrapper('INFO', "Время до ошибки: {$totalTime} сек");
log_wrapper('DEBUG', "Память использовано: {$memoryUsed} МБ");
log_wrapper('ERROR', '=== КОНЕЦ ЛОГА ОШИБКИ ===');
$response = [
'success' => false,
'error' => $error_message,
'timestamp' => date('Y-m-d H:i:s'),
'execution_time' => $totalTime
];
header('Content-Type: application/json; charset=utf-8');
http_response_code(500);
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
exit(1);
}
?>