Files
crm.clientright.ru/analyze_deletion_patterns.php
Fedor 840acca51a 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
2025-11-28 18:16:53 +03:00

237 lines
9.8 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
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 "Детальный анализ паттернов удалений\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";
$deletionsByHour = [];
$deletionsByDay = [];
$batchDeletions = []; // Массовые удаления (много файлов за короткое время)
$totalChecked = 0;
$maxToCheck = 10000;
try {
$isTruncated = true;
$continuationToken = null;
$pageCount = 0;
$maxPages = 20;
$currentBatch = [];
$lastDeleteTime = null;
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++;
$deleteDate = isset($marker['LastModified']) ? $marker['LastModified'] : null;
if ($deleteDate) {
$dateTime = new DateTime($deleteDate);
$hour = $dateTime->format('H');
$day = $dateTime->format('Y-m-d');
if (!isset($deletionsByHour[$hour])) {
$deletionsByHour[$hour] = 0;
}
$deletionsByHour[$hour]++;
if (!isset($deletionsByDay[$day])) {
$deletionsByDay[$day] = 0;
}
$deletionsByDay[$day]++;
// Определяем массовые удаления (более 10 файлов за минуту)
$deleteTimestamp = strtotime($deleteDate);
if ($lastDeleteTime && abs($deleteTimestamp - $lastDeleteTime) < 60) {
$currentBatch[] = $marker;
} else {
if (count($currentBatch) > 10) {
$batchDeletions[] = [
'count' => count($currentBatch),
'time' => date('Y-m-d H:i:s', $lastDeleteTime),
'files' => array_slice($currentBatch, 0, 5) // Первые 5 для примера
];
}
$currentBatch = [$marker];
}
$lastDeleteTime = $deleteTimestamp;
}
}
}
$isTruncated = isset($versions['IsTruncated']) && $versions['IsTruncated'];
$continuationToken = isset($versions['NextContinuationToken']) ? $versions['NextContinuationToken'] : null;
if (!$isTruncated) {
break;
}
}
// Проверяем последний батч
if (count($currentBatch) > 10) {
$batchDeletions[] = [
'count' => count($currentBatch),
'time' => $lastDeleteTime ? date('Y-m-d H:i:s', $lastDeleteTime) : 'неизвестно',
'files' => array_slice($currentBatch, 0, 5)
];
}
echo " Проверено delete markers: $totalChecked\n\n";
echo " Удаления по часам суток:\n";
ksort($deletionsByHour);
foreach ($deletionsByHour as $hour => $count) {
if ($count > 0) {
echo " {$hour}:00 - " . ($hour + 1) . ":00: $count удалений\n";
}
}
echo "\n";
echo " Удаления по дням (топ 15):\n";
arsort($deletionsByDay);
$count = 0;
foreach ($deletionsByDay as $day => $deleteCount) {
echo " $day: $deleteCount удалений\n";
if (++$count >= 15) break;
}
echo "\n";
if (!empty($batchDeletions)) {
echo " Массовые удаления (более 10 файлов за минуту): " . count($batchDeletions) . "\n";
foreach (array_slice($batchDeletions, 0, 5) as $batch) {
echo " Время: {$batch['time']}, удалено файлов: {$batch['count']}\n";
}
echo "\n";
}
} catch (\Aws\Exception\AwsException $e) {
echo " Ошибка: " . $e->getMessage() . "\n";
}
// Проверяем, может быть это связано с удалением документов в CRM
echo "2. Проверка связи с удалением документов в CRM...\n";
$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 DATE(e.modifiedtime) as delete_date, COUNT(*) as count
FROM vtiger_crmentity e
INNER JOIN vtiger_notes n ON n.notesid = e.crmid
WHERE e.deleted = 1
AND n.filelocationtype = "E"
AND e.modifiedtime >= DATE_SUB(NOW(), INTERVAL 60 DAY)
GROUP BY DATE(e.modifiedtime)
ORDER BY delete_date DESC
LIMIT 20
');
$stmt->execute();
$deletedDocs = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($deletedDocs)) {
echo " Удаленные документы в CRM (за последние 60 дней):\n";
foreach ($deletedDocs as $doc) {
echo " {$doc['delete_date']}: {$doc['count']} документов\n";
}
echo "\n";
} else {
echo " Удаленных документов не найдено\n\n";
}
// Сравниваем даты удалений в S3 и CRM
echo "3. Сравнение дат удалений в S3 и CRM...\n";
if (!empty($deletionsByDay) && !empty($deletedDocs)) {
echo " Сравнение:\n";
foreach ($deletedDocs as $doc) {
$crmDate = $doc['delete_date'];
$s3Count = $deletionsByDay[$crmDate] ?? 0;
$crmCount = $doc['count'];
if ($s3Count > 0) {
echo " $crmDate:\n";
echo " Удалено в CRM: $crmCount документов\n";
echo " Delete markers в S3: $s3Count\n";
if ($s3Count > $crmCount * 2) {
echo " ⚠️ В S3 удалено значительно больше файлов!\n";
} elseif (abs($s3Count - $crmCount) <= 10) {
echo " ✅ Количество примерно совпадает\n";
}
echo "\n";
}
}
}
echo str_repeat("=", 80) . "\n";
echo "ВЫВОДЫ:\n\n";
// Определяем наиболее вероятную причину
$maxHour = array_search(max($deletionsByHour), $deletionsByHour);
$maxDay = array_search(max($deletionsByDay), $deletionsByDay);
$maxDayCount = max($deletionsByDay);
echo "1. Пик удалений:\n";
echo " - Время: {$maxHour}:00\n";
echo " - День: $maxDay ($maxDayCount удалений)\n\n";
if ($maxHour >= 6 && $maxHour <= 8) {
echo "2. 💡 ВЕРОЯТНАЯ ПРИЧИНА: Автоматическая задача (cron job)\n";
echo " Удаления происходят рано утром (6-8 утра) - типичное время для cron\n\n";
}
if (!empty($batchDeletions)) {
echo "3. 💡 ВЕРОЯТНАЯ ПРИЧИНА: Массовое удаление (скрипт или автоматизация)\n";
echo " Найдено " . count($batchDeletions) . " случаев массового удаления\n\n";
}
echo "4. 💡 РЕКОМЕНДАЦИЯ: Проверить:\n";
echo " - DeleteOrphanedItems в Nextcloud (запускается ежедневно)\n";
echo " - Cron задачи, которые могут удалять файлы\n";
echo " - Логи Nextcloud на предмет массовых удалений\n";
} catch (Exception $e) {
echo "ОШИБКА: " . $e->getMessage() . "\n";
echo "Trace: " . $e->getTraceAsString() . "\n";
}