- Исправлен N8N_CODE_PROCESS_UPLOADED_FILES_FIXED.js: использовать uploads_field_labels[0] вместо [grp] - Создан SQL_CLAIMSAVE_FIXED_NEW_FLOW_DEDUP.sql с дедупликацией documents_meta - Создан SQL_CLEANUP_DOCUMENTS_META_DUPLICATES.sql для очистки существующих дубликатов - Создан полный уникальный индекс idx_document_texts_hash_unique на document_texts(file_hash) - Добавлен SESSION_LOG_2025-11-28_documents_dedup.md с описанием всех изменений Fixes: - field_label теперь корректно отображает 'Переписка' вместо 'group-2' - documents_meta не накапливает дубликаты при повторных сохранениях - ON CONFLICT (file_hash) теперь работает для document_texts
356 lines
15 KiB
PHP
356 lines
15 KiB
PHP
<?php
|
||
error_reporting(E_ALL);
|
||
ini_set('display_errors', 1);
|
||
|
||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/vendor/autoload.php';
|
||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/config.inc.php';
|
||
|
||
$config = require '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/file_storage/config.php';
|
||
$docId = 386869;
|
||
$s3Bucket = $config['s3']['bucket'];
|
||
|
||
echo "ПОЛНЫЙ поиск всех файлов, связанных с документом $docId\n";
|
||
echo str_repeat("=", 80) . "\n\n";
|
||
|
||
try {
|
||
// Получаем информацию о документе из БД
|
||
$pdo = new PDO(
|
||
"mysql:host={$dbconfig['db_server']};port=3306;dbname={$dbconfig['db_name']};charset=utf8",
|
||
$dbconfig['db_username'],
|
||
$dbconfig['db_password'],
|
||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
|
||
);
|
||
|
||
$stmt = $pdo->prepare('SELECT notesid, title, s3_key, filename, filelocationtype FROM vtiger_notes WHERE notesid = ?');
|
||
$stmt->execute([$docId]);
|
||
$doc = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (!$doc) {
|
||
die("Документ $docId не найден в БД\n");
|
||
}
|
||
|
||
echo "Информация о документе:\n";
|
||
echo " ID: {$doc['notesid']}\n";
|
||
echo " Название: {$doc['title']}\n";
|
||
echo " S3 Key (ожидаемый): " . ($doc['s3_key'] ?: 'не указан') . "\n";
|
||
echo " Filename: " . substr($doc['filename'], 0, 150) . "\n";
|
||
echo " Тип хранения: " . ($doc['filelocationtype'] ?: 'не указан') . "\n\n";
|
||
|
||
// Инициализация S3 клиента
|
||
$s3Client = new \Aws\S3\S3Client([
|
||
'version' => 'latest',
|
||
'region' => $config['s3']['region'],
|
||
'endpoint' => $config['s3']['endpoint'],
|
||
'use_path_style_endpoint' => true,
|
||
'credentials' => [
|
||
'key' => $config['s3']['key'],
|
||
'secret' => $config['s3']['secret'],
|
||
],
|
||
'suppress_php_deprecation_warning' => true
|
||
]);
|
||
|
||
$foundFiles = [];
|
||
|
||
// 1. Проверяем ожидаемый путь
|
||
echo "1. Проверка ожидаемого пути в S3...\n";
|
||
if (!empty($doc['s3_key'])) {
|
||
try {
|
||
$result = $s3Client->headObject([
|
||
'Bucket' => $s3Bucket,
|
||
'Key' => $doc['s3_key']
|
||
]);
|
||
echo " ✅ Файл найден по ожидаемому пути!\n";
|
||
echo " Путь: {$doc['s3_key']}\n";
|
||
echo " Размер: " . number_format($result['ContentLength'] / 1024, 2) . " KB\n";
|
||
$foundFiles[] = [
|
||
'key' => $doc['s3_key'],
|
||
'size' => $result['ContentLength'],
|
||
'source' => 'ожидаемый путь'
|
||
];
|
||
} catch (\Aws\Exception\AwsException $e) {
|
||
echo " ❌ Файл не найден по ожидаемому пути: " . $e->getAwsErrorCode() . "\n";
|
||
}
|
||
}
|
||
echo "\n";
|
||
|
||
// 2. Поиск по точному ID документа
|
||
echo "2. Поиск файлов с точным ID $docId...\n";
|
||
$searchPatterns = [
|
||
(string)$docId,
|
||
(string)($docId - 1),
|
||
(string)($docId + 1),
|
||
(string)($docId - 2),
|
||
(string)($docId + 2),
|
||
];
|
||
|
||
$totalChecked = 0;
|
||
$maxFilesToCheck = 50000;
|
||
|
||
foreach ($searchPatterns as $pattern) {
|
||
echo " Поиск по паттерну: $pattern\n";
|
||
|
||
try {
|
||
$isTruncated = true;
|
||
$continuationToken = null;
|
||
$pageCount = 0;
|
||
$maxPages = 50;
|
||
|
||
while ($isTruncated && $pageCount < $maxPages && $totalChecked < $maxFilesToCheck) {
|
||
$params = [
|
||
'Bucket' => $s3Bucket,
|
||
'MaxKeys' => 1000
|
||
];
|
||
|
||
if ($continuationToken) {
|
||
$params['ContinuationToken'] = $continuationToken;
|
||
}
|
||
|
||
$objects = $s3Client->listObjectsV2($params);
|
||
$pageCount++;
|
||
$totalChecked += isset($objects['Contents']) ? count($objects['Contents']) : 0;
|
||
|
||
if (isset($objects['Contents'])) {
|
||
foreach ($objects['Contents'] as $object) {
|
||
$key = $object['Key'];
|
||
|
||
// Ищем файлы, содержащие паттерн
|
||
if (strpos($key, $pattern) !== false) {
|
||
// Проверяем, не добавили ли уже этот файл
|
||
$alreadyAdded = false;
|
||
foreach ($foundFiles as $found) {
|
||
if ($found['key'] === $key) {
|
||
$alreadyAdded = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!$alreadyAdded) {
|
||
try {
|
||
$headResult = $s3Client->headObject([
|
||
'Bucket' => $s3Bucket,
|
||
'Key' => $key
|
||
]);
|
||
|
||
echo " ✅ НАЙДЕН: $key\n";
|
||
echo " Размер: " . number_format($headResult['ContentLength'] / 1024, 2) . " KB\n";
|
||
echo " Дата: " . ($headResult['LastModified'] ?? 'не указана') . "\n";
|
||
|
||
$foundFiles[] = [
|
||
'key' => $key,
|
||
'size' => $headResult['ContentLength'],
|
||
'source' => "поиск по ID $pattern",
|
||
'date' => $headResult['LastModified'] ?? null
|
||
];
|
||
} catch (\Aws\Exception\AwsException $e) {
|
||
// Пропускаем недоступные файлы
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
$isTruncated = isset($objects['IsTruncated']) && $objects['IsTruncated'];
|
||
$continuationToken = isset($objects['NextContinuationToken']) ? $objects['NextContinuationToken'] : null;
|
||
|
||
if (!$isTruncated) {
|
||
break;
|
||
}
|
||
}
|
||
} catch (\Aws\Exception\AwsException $e) {
|
||
echo " Ошибка при поиске: " . $e->getMessage() . "\n";
|
||
}
|
||
}
|
||
echo " Проверено файлов: $totalChecked\n\n";
|
||
|
||
// 3. Поиск по ключевым словам из названия документа
|
||
echo "3. Поиск по ключевым словам из названия...\n";
|
||
$title = mb_strtolower($doc['title']);
|
||
$keywords = [];
|
||
|
||
// Извлекаем ключевые слова
|
||
if (strpos($title, 'исковое') !== false) $keywords[] = 'iskovoe';
|
||
if (strpos($title, 'заявление') !== false) $keywords[] = 'zayavlenie';
|
||
if (strpos($title, 'марсель') !== false) $keywords[] = 'marse';
|
||
if (strpos($title, 'гафиев') !== false) $keywords[] = 'gafiev';
|
||
if (strpos($title, 'gafiev') !== false) $keywords[] = 'gafiev';
|
||
|
||
echo " Ключевые слова: " . implode(', ', $keywords) . "\n";
|
||
|
||
if (!empty($keywords)) {
|
||
// Ищем в temp/ папках
|
||
try {
|
||
$objects = $s3Client->listObjectsV2([
|
||
'Bucket' => $s3Bucket,
|
||
'Prefix' => 'temp/',
|
||
'MaxKeys' => 10000
|
||
]);
|
||
|
||
if (isset($objects['Contents'])) {
|
||
foreach ($objects['Contents'] as $object) {
|
||
$key = $object['Key'];
|
||
$keyLower = mb_strtolower($key);
|
||
|
||
// Проверяем совпадение по ключевым словам
|
||
$matches = 0;
|
||
foreach ($keywords as $keyword) {
|
||
if (strpos($keyLower, $keyword) !== false) {
|
||
$matches++;
|
||
}
|
||
}
|
||
|
||
// Если совпало хотя бы 2 ключевых слова
|
||
if ($matches >= 2) {
|
||
$alreadyAdded = false;
|
||
foreach ($foundFiles as $found) {
|
||
if ($found['key'] === $key) {
|
||
$alreadyAdded = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!$alreadyAdded) {
|
||
try {
|
||
$headResult = $s3Client->headObject([
|
||
'Bucket' => $s3Bucket,
|
||
'Key' => $key
|
||
]);
|
||
|
||
echo " ✅ НАЙДЕН по ключевым словам: $key\n";
|
||
echo " Размер: " . number_format($headResult['ContentLength'] / 1024, 2) . " KB\n";
|
||
|
||
$foundFiles[] = [
|
||
'key' => $key,
|
||
'size' => $headResult['ContentLength'],
|
||
'source' => 'поиск по ключевым словам',
|
||
'date' => $headResult['LastModified'] ?? null
|
||
];
|
||
} catch (\Aws\Exception\AwsException $e) {
|
||
// Пропускаем
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (\Aws\Exception\AwsException $e) {
|
||
echo " Ошибка при поиске: " . $e->getMessage() . "\n";
|
||
}
|
||
}
|
||
echo "\n";
|
||
|
||
// 4. Поиск в папке проекта (если известен projectId)
|
||
echo "4. Поиск в папке проекта...\n";
|
||
$stmt = $pdo->prepare('SELECT crmid FROM vtiger_senotesrel WHERE notesid = ? LIMIT 1');
|
||
$stmt->execute([$docId]);
|
||
$projectRel = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if ($projectRel) {
|
||
$projectId = $projectRel['crmid'];
|
||
echo " Проект ID: $projectId\n";
|
||
|
||
$projectPrefixes = [
|
||
"crm2/CRM_Active_Files/Documents/Project/",
|
||
"temp/$projectId/",
|
||
];
|
||
|
||
foreach ($projectPrefixes as $prefix) {
|
||
try {
|
||
$objects = $s3Client->listObjectsV2([
|
||
'Bucket' => $s3Bucket,
|
||
'Prefix' => $prefix,
|
||
'MaxKeys' => 1000
|
||
]);
|
||
|
||
if (isset($objects['Contents'])) {
|
||
foreach ($objects['Contents'] as $object) {
|
||
$key = $object['Key'];
|
||
|
||
// Ищем файлы с ID документа
|
||
if (strpos($key, (string)$docId) !== false ||
|
||
strpos($key, (string)($docId - 1)) !== false ||
|
||
strpos($key, (string)($docId + 1)) !== false) {
|
||
|
||
$alreadyAdded = false;
|
||
foreach ($foundFiles as $found) {
|
||
if ($found['key'] === $key) {
|
||
$alreadyAdded = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!$alreadyAdded) {
|
||
try {
|
||
$headResult = $s3Client->headObject([
|
||
'Bucket' => $s3Bucket,
|
||
'Key' => $key
|
||
]);
|
||
|
||
echo " ✅ НАЙДЕН в папке проекта: $key\n";
|
||
echo " Размер: " . number_format($headResult['ContentLength'] / 1024, 2) . " KB\n";
|
||
|
||
$foundFiles[] = [
|
||
'key' => $key,
|
||
'size' => $headResult['ContentLength'],
|
||
'source' => "папка проекта $projectId",
|
||
'date' => $headResult['LastModified'] ?? null
|
||
];
|
||
} catch (\Aws\Exception\AwsException $e) {
|
||
// Пропускаем
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (\Aws\Exception\AwsException $e) {
|
||
// Пропускаем ошибки
|
||
}
|
||
}
|
||
}
|
||
echo "\n";
|
||
|
||
// Итоги
|
||
echo str_repeat("=", 80) . "\n";
|
||
echo "ИТОГОВЫЕ РЕЗУЛЬТАТЫ:\n";
|
||
echo " Всего найдено файлов: " . count($foundFiles) . "\n\n";
|
||
|
||
if (!empty($foundFiles)) {
|
||
echo "НАЙДЕННЫЕ ФАЙЛЫ:\n";
|
||
foreach ($foundFiles as $i => $file) {
|
||
echo " " . ($i + 1) . ". {$file['key']}\n";
|
||
echo " Размер: " . number_format($file['size'] / 1024, 2) . " KB\n";
|
||
echo " Источник: {$file['source']}\n";
|
||
if ($file['date']) {
|
||
echo " Дата: {$file['date']}\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
// Определяем наиболее вероятный файл
|
||
echo "💡 РЕКОМЕНДАЦИЯ:\n";
|
||
$bestMatch = null;
|
||
foreach ($foundFiles as $file) {
|
||
if ($file['source'] === 'ожидаемый путь') {
|
||
$bestMatch = $file;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!$bestMatch && !empty($foundFiles)) {
|
||
// Берем первый найденный файл
|
||
$bestMatch = $foundFiles[0];
|
||
}
|
||
|
||
if ($bestMatch) {
|
||
echo " Наиболее вероятный файл: {$bestMatch['key']}\n";
|
||
echo " Этот файл можно использовать для обновления s3_key в БД\n";
|
||
}
|
||
} else {
|
||
echo " ❌ Файлы не найдены в S3\n";
|
||
echo " Возможно, файл находится в Nextcloud или локальном хранилище\n";
|
||
}
|
||
|
||
} catch (Exception $e) {
|
||
echo "ОШИБКА: " . $e->getMessage() . "\n";
|
||
echo "Trace: " . $e->getTraceAsString() . "\n";
|
||
}
|
||
|