feat(documents): дедупликация documents_meta и исправление field_label

- Исправлен 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
This commit is contained in:
Fedor
2025-11-28 18:16:53 +03:00
parent 6c770f0a87
commit 840acca51a
118 changed files with 13379 additions and 218 deletions

203
analyze_deletions.php Normal file
View File

@@ -0,0 +1,203 @@
<?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';
$s3Bucket = $config['s3']['bucket'];
echo "Анализ удалений файлов из S3\n";
echo str_repeat("=", 80) . "\n\n";
try {
$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
]);
// Анализируем паттерны удалений
echo "1. Анализ паттернов удалений по датам...\n";
$deletionsByDate = [];
$deletionsByProject = [];
$totalChecked = 0;
$maxToCheck = 5000; // Ограничиваем для скорости
try {
$isTruncated = true;
$continuationToken = null;
$pageCount = 0;
$maxPages = 10;
while ($isTruncated && $pageCount < $maxPages && $totalChecked < $maxToCheck) {
$params = [
'Bucket' => $s3Bucket,
'Prefix' => 'crm2/CRM_Active_Files/Documents/Project/',
'MaxKeys' => 1000
];
if ($continuationToken) {
$params['ContinuationToken'] = $continuationToken;
}
$versions = $s3Client->listObjectVersions($params);
$pageCount++;
if (isset($versions['DeleteMarkers'])) {
foreach ($versions['DeleteMarkers'] as $marker) {
$totalChecked++;
$key = $marker['Key'];
$deleteDate = isset($marker['LastModified']) ? $marker['LastModified'] : null;
if ($deleteDate) {
$dateKey = substr($deleteDate, 0, 10); // YYYY-MM-DD
if (!isset($deletionsByDate[$dateKey])) {
$deletionsByDate[$dateKey] = 0;
}
$deletionsByDate[$dateKey]++;
}
// Извлекаем ID проекта из пути
if (preg_match('/Project\/([^\/]+)_(\d+)\//', $key, $matches)) {
$projectId = $matches[2];
if (!isset($deletionsByProject[$projectId])) {
$deletionsByProject[$projectId] = 0;
}
$deletionsByProject[$projectId]++;
}
}
}
$isTruncated = isset($versions['IsTruncated']) && $versions['IsTruncated'];
$continuationToken = isset($versions['NextContinuationToken']) ? $versions['NextContinuationToken'] : null;
if (!$isTruncated) {
break;
}
}
echo " Проверено delete markers: $totalChecked\n\n";
// Сортируем по датам
krsort($deletionsByDate);
echo " Удаления по датам (топ 20):\n";
$count = 0;
foreach ($deletionsByDate as $date => $count) {
if ($count > 0) {
echo " $date: $count удалений\n";
if (++$count >= 20) break;
}
}
echo "\n";
// Сортируем проекты по количеству удалений
arsort($deletionsByProject);
echo " Проекты с наибольшим количеством удалений (топ 10):\n";
$count = 0;
foreach ($deletionsByProject as $projectId => $deleteCount) {
echo " Проект $projectId: $deleteCount удалений\n";
if (++$count >= 10) break;
}
echo "\n";
} catch (\Aws\Exception\AwsException $e) {
echo " Ошибка: " . $e->getMessage() . "\n";
}
// Проверяем логи системы
echo "2. Проверка логов на наличие записей об удалениях...\n";
$logFiles = [
'/var/log/nginx/error.log',
'/var/log/apache2/error.log',
'/var/www/fastuser/data/www/crm.clientright.ru/logs/debug.log',
'/var/www/fastuser/data/www/crm.clientright.ru/logs/s3_debug.log',
];
foreach ($logFiles as $logFile) {
if (file_exists($logFile)) {
echo " Проверка: $logFile\n";
$lines = file($logFile);
if ($lines) {
$deleteLines = array_filter($lines, function($line) {
return stripos($line, 'delete') !== false &&
(stripos($line, 's3') !== false || stripos($line, 'file') !== false);
});
if (!empty($deleteLines)) {
echo " Найдено строк с упоминанием удалений: " . count($deleteLines) . "\n";
echo " Последние 5 записей:\n";
foreach (array_slice($deleteLines, -5) as $line) {
echo " " . substr(trim($line), 0, 150) . "\n";
}
} else {
echo " Записей об удалениях не найдено\n";
}
}
echo "\n";
}
}
// Проверяем, есть ли скрипты крона, которые могут удалять файлы
echo "3. Поиск скриптов, которые могут удалять файлы...\n";
$cronFiles = [
'/var/spool/cron/crontabs/root',
'/etc/cron.d/',
'/var/www/fastuser/data/www/crm.clientright.ru/cron/',
];
foreach ($cronFiles as $cronPath) {
if (file_exists($cronPath)) {
echo " Проверка: $cronPath\n";
if (is_dir($cronPath)) {
$files = glob($cronPath . '*');
foreach ($files as $file) {
if (is_file($file)) {
$content = file_get_contents($file);
if (stripos($content, 'delete') !== false || stripos($content, 'remove') !== false) {
echo " Найден файл: $file\n";
echo " Содержит упоминания удаления\n";
}
}
}
} else {
$content = file_get_contents($cronPath);
if (stripos($content, 'delete') !== false || stripos($content, 'remove') !== false) {
echo " Файл содержит упоминания удаления\n";
}
}
}
}
echo "\n";
// Проверяем, может быть это связано с синхронизацией Nextcloud
echo "4. Проверка связи с Nextcloud синхронизацией...\n";
$nextcloudFiles = glob('/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/file_storage/*sync*.php');
if (!empty($nextcloudFiles)) {
echo " Найдено файлов синхронизации: " . count($nextcloudFiles) . "\n";
foreach ($nextcloudFiles as $file) {
$content = file_get_contents($file);
if (stripos($content, 'delete') !== false) {
echo " $file содержит код удаления\n";
}
}
} else {
echo " Файлы синхронизации не найдены\n";
}
echo "\n";
} catch (Exception $e) {
echo "ОШИБКА: " . $e->getMessage() . "\n";
echo "Trace: " . $e->getTraceAsString() . "\n";
}