Files
crm.clientright.ru/api_attach_documents.php
Fedor cd90b0d58a feat: Добавлен инструмент генерации документов для AI Ассистента
- Создан API create_document_with_text.php для создания DOCX/XLSX/PPTX с текстом от AI
- Поддержка Markdown форматирования (заголовки, жирный, курсив, списки, код)
- Установлен PHPWord для красивого форматирования документов
- Исправлены пути сохранения (crm2/CRM_Active_Files/... без /crm/ в начале)
- Замена пробелов на подчеркивания в именах папок
- Создана документация для AI и разработчиков
- Добавлены API для работы с шаблонами Nextcloud
2025-11-12 19:46:06 +03:00

252 lines
8.2 KiB
PHP
Raw Permalink 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
/**
* API для привязки документов к проекту/заявке
*
* Использование из n8n:
* POST https://crm.clientright.ru/api_attach_documents.php
*
* Входные данные (JSON массив):
* [
* {
* "contact_id": "320096",
* "project_id": "396868",
* "ticket_id": "396936",
* "filename": "boarding_pass.pdf",
* "file_type": "flight_delay_boarding_or_ticket",
* "file": "/bucket/path/to/file.pdf"
* }
* ]
*/
error_reporting(E_ALL);
ini_set('display_errors', '0');
// Функция для логирования
function log_message($message) {
$timestamp = date('Y-m-d H:i:s');
$line = "[$timestamp] $message\n";
@file_put_contents(__DIR__ . '/logs/api_attach_documents.log', $line, FILE_APPEND | LOCK_EX);
error_log('[api_attach_documents] ' . $message);
}
// Функция для JSON ответа
function json_response($data, $code = 200) {
if (!headers_sent()) {
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
}
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
// CORS preflight
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
json_response(['status' => 'ok']);
}
// Только POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
json_response(['success' => false, 'error' => 'Method not allowed'], 405);
}
log_message('=== START API REQUEST ===');
// Получаем входные данные
$input = file_get_contents('php://input');
log_message('Raw input: ' . substr($input, 0, 500));
$input = ltrim($input, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
log_message('❌ JSON Error: ' . json_last_error_msg());
json_response([
'success' => false,
'error' => 'Invalid JSON: ' . json_last_error_msg()
], 400);
}
// Поддерживаем как массив, так и одиночный объект
$documents_array = is_array($data) && isset($data[0]) ? $data : [$data];
log_message('Processing ' . count($documents_array) . ' document(s)');
// Обрабатываем каждый документ
$processed_documents = [];
$S3_HOST = 'https://s3.twcstorage.ru';
foreach ($documents_array as $idx => $doc) {
$contact_id = $doc['contact_id'] ?? null;
$project_id = $doc['project_id'] ?? null;
$ticket_id = $doc['ticket_id'] ?? null;
// Поддерживаем оба формата: file и file_url
$file_path = $doc['file'] ?? $doc['file_url'] ?? null;
if (!$file_path) {
log_message("❌ Document #{$idx}: missing 'file' or 'file_url'");
continue;
}
// Строим полный S3 URL
if (strpos($file_path, 'http') === 0) {
$file_url = $file_path;
} elseif (strpos($file_path, '/') === 0) {
$file_url = $S3_HOST . $file_path;
} else {
$file_url = $S3_HOST . '/' . $file_path;
}
// Поддерживаем оба формата: filename и file_name
$file_name = $doc['filename'] ?? $doc['file_name'] ?? null;
if (!$file_name) {
log_message("❌ Document #{$idx}: missing 'filename' or 'file_name'");
continue;
}
$file_type = $doc['file_type'] ?? 'Документ';
// Валидация обязательных полей
if (!$contact_id || !$project_id) {
log_message("❌ Document #{$idx}: missing contact_id or project_id");
continue;
}
log_message(" [{$idx}] {$file_name} (type: {$file_type})");
log_message(" Contact: {$contact_id}, Project: {$project_id}, Ticket: " . ($ticket_id ?: 'N/A'));
log_message(" File URL: {$file_url}");
$processed_documents[] = [
'url' => $file_url,
'file_name' => $file_name,
'description' => $file_type,
'projectid' => (int)$project_id,
'ticket_id' => $ticket_id ? (int)$ticket_id : null,
'contactid' => (int)$contact_id,
'pages' => 1
];
}
if (empty($processed_documents)) {
log_message('❌ No valid documents to process');
json_response([
'success' => false,
'error' => 'No valid documents to process'
], 400);
}
log_message('📤 Sending ' . count($processed_documents) . ' documents to upload_documents_to_crm.php');
// Формируем запрос к upload_documents_to_crm.php
$upload_url = 'https://crm.clientright.ru/upload_documents_to_crm.php';
// Берем общие параметры из первого документа
$first_doc = $processed_documents[0];
$payload = json_encode([
'documents' => $processed_documents,
'projectid' => $first_doc['projectid'],
'ticket_id' => $first_doc['ticket_id'],
'user_id' => 1
], JSON_UNESCAPED_UNICODE);
log_message('Payload: ' . substr($payload, 0, 500));
// Отправляем запрос
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $upload_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($payload)
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response === false) {
log_message('❌ CURL error: ' . curl_error($ch));
json_response([
'success' => false,
'error' => 'Internal error: ' . curl_error($ch)
], 500);
}
log_message("Response HTTP code: {$http_code}");
log_message("Response: " . substr($response, 0, 500));
// Парсим ответ
$result = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
log_message('❌ Failed to parse response JSON: ' . json_last_error_msg());
json_response([
'success' => false,
'error' => 'Invalid response from upload service'
], 500);
}
// Проверяем успешность
if ($result && $result['success'] && isset($result['results'])) {
$results_array = $result['results'];
// Формируем ответ
$processed_results = [];
$errors = [];
foreach ($results_array as $idx => $res) {
if ($res['status'] === 'success') {
$crm_result = $res['crm_result'] ?? [];
$processed_results[] = [
'document_id' => $crm_result['document_id'] ?? null,
'document_numeric_id' => $crm_result['document_numeric_id'] ?? null,
'attached_to' => isset($res['ticket_id']) && $res['ticket_id'] ? 'ticket' : 'project',
'attached_to_id' => $res['ticket_id'] ?? $res['projectid'] ?? null,
'file_name' => $res['file_name'] ?? null,
'file_type' => $res['description'] ?? null,
's3_bucket' => $crm_result['s3_bucket'] ?? null,
's3_key' => $crm_result['s3_key'] ?? null,
'file_size' => $crm_result['file_size'] ?? null,
'message' => $crm_result['message'] ?? null
];
log_message(" ✅ [{$idx}] {$res['file_name']}{$crm_result['document_id']}");
} else {
$error_msg = $res['crm_result']['message'] ?? 'Unknown error';
$errors[] = [
'file_name' => $res['file_name'] ?? 'Unknown',
'error' => $error_msg
];
log_message(" ❌ [{$idx}] {$res['file_name']}: {$error_msg}");
}
}
log_message('✅ Success: ' . count($processed_results) . ' documents attached');
json_response([
'success' => true,
'total_processed' => count($results_array),
'successful' => count($processed_results),
'failed' => count($errors),
'results' => $processed_results,
'errors' => !empty($errors) ? $errors : null
]);
} else {
log_message('❌ Upload failed: ' . ($result['error']['message'] ?? 'Unknown error'));
json_response([
'success' => false,
'error' => $result['error']['message'] ?? 'Upload failed'
], 500);
}