PDO::ERRMODE_EXCEPTION] ); echo "✅ PDO подключен\n"; } catch (Exception $e) { die("❌ Ошибка PDO: " . $e->getMessage() . "\n"); } // S3 конфигурация $s3Config = [ 'version' => 'latest', 'region' => 'ru-1', 'endpoint' => 'https://s3.twcstorage.ru', 'bucket' => 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c', 'use_path_style_endpoint' => true, 'key' => EnvLoader::getRequired('S3_ACCESS_KEY'), 'secret' => EnvLoader::getRequired('S3_SECRET_KEY') ]; try { echo "🔧 Создаем S3 клиент...\n"; $s3 = new Aws\S3\S3Client($s3Config); echo "✅ S3 подключен\n"; } catch (Exception $e) { die("❌ Ошибка S3: " . $e->getMessage() . "\n"); } echo "🔧 Создаем FilePathManager...\n"; $pathMgr = new FilePathManager(); echo "✅ FilePathManager создан\n"; // Получаем контакты с файлами в старой структуре echo "\n📁 ПОИСК КОНТАКТОВ С ФАЙЛАМИ:\n"; echo "=============================\n"; $sql = "SELECT DISTINCT sr.crmid as contactid, CONCAT(c.firstname, ' ', c.lastname) as contact_name, COUNT(n.notesid) as file_count FROM vtiger_senotesrel sr INNER JOIN vtiger_notes n ON sr.notesid = n.notesid INNER JOIN vtiger_crmentity ce ON sr.crmid = ce.crmid INNER JOIN vtiger_contactdetails c ON sr.crmid = c.contactid WHERE ce.setype = 'Contacts' AND n.filelocationtype = 'E' AND n.s3_key IS NOT NULL AND n.s3_key NOT LIKE '%/Contacts/%' GROUP BY sr.crmid, c.firstname, c.lastname ORDER BY file_count DESC, contact_name LIMIT 50"; $result = $pdo->query($sql); $contacts = []; while ($row = $result->fetch(PDO::FETCH_ASSOC)) { $contacts[] = $row; echo "• {$row['contact_name']} (ID: {$row['contactid']}): {$row['file_count']} файлов\n"; } echo "\n📈 ПОКАЗАНО: " . count($contacts) . " контактов (топ 50 по количеству файлов)\n"; // Подсчитываем общее количество файлов для миграции $sql = "SELECT COUNT(*) as total_files, COUNT(DISTINCT sr.crmid) as total_contacts FROM vtiger_senotesrel sr INNER JOIN vtiger_notes n ON sr.notesid = n.notesid INNER JOIN vtiger_crmentity ce ON sr.crmid = ce.crmid WHERE ce.setype = 'Contacts' AND n.filelocationtype = 'E' AND n.s3_key IS NOT NULL AND n.s3_key NOT LIKE '%/Contacts/%'"; $stmt = $pdo->prepare($sql); $stmt->execute(); $stats = $stmt->fetch(PDO::FETCH_ASSOC); echo "📁 ВСЕГО КОНТАКТОВ: {$stats['total_contacts']}\n"; echo "📄 ВСЕГО ФАЙЛОВ: {$stats['total_files']}\n"; // Спрашиваем пользователя echo "\n❓ ВОПРОС:\n"; echo "===========\n"; echo "Мигрировать файлы контактов ({$stats['total_files']} файлов от {$stats['total_contacts']} контактов)? (y/n): "; $handle = fopen("php://stdin", "r"); $line = fgets($handle); fclose($handle); if (trim(strtolower($line)) !== 'y') { echo "❌ Миграция отменена\n"; exit; } // Получаем ВСЕ контакты с файлами echo "\n🔄 Загружаем полный список контактов...\n"; $sql = "SELECT DISTINCT sr.crmid as contactid, CONCAT(c.firstname, ' ', c.lastname) as contact_name FROM vtiger_senotesrel sr INNER JOIN vtiger_notes n ON sr.notesid = n.notesid INNER JOIN vtiger_crmentity ce ON sr.crmid = ce.crmid INNER JOIN vtiger_contactdetails c ON sr.crmid = c.contactid WHERE ce.setype = 'Contacts' AND n.filelocationtype = 'E' AND n.s3_key IS NOT NULL AND n.s3_key NOT LIKE '%/Contacts/%' ORDER BY contact_name"; $result = $pdo->query($sql); $allContacts = []; while ($row = $result->fetch(PDO::FETCH_ASSOC)) { $allContacts[] = $row; } echo "✅ Загружено: " . count($allContacts) . " контактов\n"; // Начинаем миграцию echo "\n🚀 НАЧИНАЕМ МИГРАЦИЮ КОНТАКТОВ:\n"; echo "===============================\n"; $migratedContacts = 0; $migratedFiles = 0; $errors = 0; foreach ($allContacts as $contact) { $contactId = $contact['contactid']; $contactName = $contact['contact_name']; echo "\n👤 Контакт: $contactName (ID: $contactId)\n"; // Получаем все файлы контакта которые еще не мигрированы $sql = "SELECT n.notesid, n.title, n.filename, n.s3_key, n.s3_bucket FROM vtiger_notes n INNER JOIN vtiger_senotesrel sr ON n.notesid = sr.notesid WHERE sr.crmid = ? AND n.filelocationtype = 'E' AND n.s3_key IS NOT NULL AND n.s3_key NOT LIKE '%/Contacts/%'"; $stmt = $pdo->prepare($sql); $stmt->execute([$contactId]); $files = []; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $files[] = $row; } echo " 📄 Файлов для миграции: " . count($files) . "\n"; $contactMigratedFiles = 0; $contactErrors = 0; foreach ($files as $file) { $documentId = $file['notesid']; $fileName = $file['filename']; $oldS3Key = $file['s3_key']; $title = $file['title']; // Генерируем новый путь для Contacts $newFilePath = $pathMgr->getFilePath('Contacts', $contactId, $documentId, $fileName, $title, $contactName); $newS3Key = $newFilePath; // Проверяем, нужно ли мигрировать if ($oldS3Key === $newS3Key) { $contactMigratedFiles++; continue; } echo " 🔄 Мигрируем: $title\n"; try { // Проверяем существование старого файла $oldUrl = "https://s3.twcstorage.ru/{$s3Config['bucket']}/{$oldS3Key}"; $headers = @get_headers($oldUrl); if (!$headers || strpos($headers[0], '200') === false) { echo " ⚠️ Файл не найден в S3\n"; $contactErrors++; continue; } // Скачиваем файл $fileContent = file_get_contents($oldUrl); if ($fileContent === false) { echo " ❌ Не удалось скачать файл\n"; $contactErrors++; continue; } // Загружаем в новое место $uploadResult = $s3->putObject([ 'Bucket' => $s3Config['bucket'], 'Key' => $newS3Key, 'Body' => $fileContent, 'ContentType' => mime_content_type('data://text/plain;base64,' . base64_encode($fileContent)) ]); // Обновляем БД (и s3_key и filename с полным URL) $newFileUrl = "https://s3.twcstorage.ru/{$s3Config['bucket']}/{$newS3Key}"; $updateSql = "UPDATE vtiger_notes SET s3_key = ?, filename = ? WHERE notesid = ?"; $updateStmt = $pdo->prepare($updateSql); $updateStmt->execute([$newS3Key, $newFileUrl, $documentId]); // Удаляем старый файл try { $s3->deleteObject([ 'Bucket' => $s3Config['bucket'], 'Key' => $oldS3Key ]); } catch (Exception $e) { // Не критичная ошибка } echo " ✅ Файл мигрирован успешно\n"; $contactMigratedFiles++; } catch (Exception $e) { echo " ❌ Ошибка миграции: " . $e->getMessage() . "\n"; $contactErrors++; } } echo " 📊 Результат контакта: $contactMigratedFiles файлов мигрировано, $contactErrors ошибок\n"; $migratedContacts++; $migratedFiles += $contactMigratedFiles; $errors += $contactErrors; } // Итоговая статистика echo "\n🎉 МИГРАЦИЯ КОНТАКТОВ ЗАВЕРШЕНА!\n"; echo "================================\n"; echo "👤 Контактов обработано: $migratedContacts\n"; echo "📄 Файлов мигрировано: $migratedFiles\n"; echo "❌ Ошибок: $errors\n"; if ($migratedFiles + $errors > 0) { echo "✅ Успешность: " . round(($migratedFiles / ($migratedFiles + $errors)) * 100, 2) . "%\n"; } echo "\n🚀 Все файлы контактов мигрированы в новую структуру Contacts/имя_ID/файл_docID!\n"; ?>