'latest', 'region' => 'ru-1', 'endpoint' => 'https://s3.twcstorage.ru', 'credentials' => [ 'key' => $_ENV['S3_ACCESS_KEY'], 'secret' => $_ENV['S3_SECRET_KEY'], ], 'use_path_style_endpoint' => true, ]); echo "✅ S3 клиент инициализирован\n"; // Подключаемся к базе данных $pdo = new PDO("mysql:host={$dbconfig['db_server']};dbname={$dbconfig['db_name']};charset=utf8", $dbconfig['db_username'], $dbconfig['db_password']); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo "✅ Подключение к БД установлено\n\n"; // Находим все файлы тикетов в старой структуре $sql = " SELECT n.notesid, n.title, n.filename, n.s3_key, t.ticketid, t.ticket_no, t.title as ticket_title FROM vtiger_notes n INNER JOIN vtiger_senotesrel sr ON n.notesid = sr.notesid INNER JOIN vtiger_troubletickets t ON sr.crmid = t.ticketid WHERE n.filelocationtype = 'E' AND n.s3_key IS NOT NULL AND n.s3_key LIKE '%/Documents/%' AND n.s3_key NOT LIKE '%/Project/%' AND n.s3_key NOT LIKE '%/Contacts/%' AND n.s3_key NOT LIKE '%/Accounts/%' AND n.s3_key NOT LIKE '%/HelpDesk/%' ORDER BY t.ticketid, n.notesid "; $stmt = $pdo->prepare($sql); $stmt->execute(); $files = $stmt->fetchAll(PDO::FETCH_ASSOC); echo "📊 Найдено файлов тикетов для миграции: " . count($files) . "\n\n"; if (empty($files)) { echo "✅ Все файлы тикетов уже мигрированы!\n"; exit(0); } $migratedCount = 0; $errorCount = 0; $currentTicketId = null; $ticketCount = 0; $bucket = $_ENV['S3_BUCKET']; foreach ($files as $file) { $notesId = $file['notesid']; $title = $file['title']; $oldS3Key = $file['s3_key']; $ticketId = $file['ticketid']; $ticketNo = $file['ticket_no']; $ticketTitle = $file['ticket_title']; // Считаем тикеты if ($currentTicketId !== $ticketId) { $currentTicketId = $ticketId; $ticketCount++; } echo "🎫 Тикет: {$ticketNo} - {$ticketTitle} (ID: {$ticketId})\n"; echo " 📄 Файл: {$title} (ID: {$notesId})\n"; echo " 🔄 Старый путь: {$oldS3Key}\n"; try { // Простая нормализация имени тикета $normalizedTicketNo = preg_replace('/[^a-zA-Z0-9\-_]/u', '_', $ticketNo); $normalizedTicketNo = preg_replace('/_+/', '_', $normalizedTicketNo); $normalizedTicketNo = trim($normalizedTicketNo, '_'); if (empty($normalizedTicketNo)) { $normalizedTicketNo = "ticket_{$ticketId}"; } // Простая нормализация имени файла $normalizedTitle = preg_replace('/[^a-zA-Zа-яА-Я0-9\s\-_\.]/u', '', $title); $normalizedTitle = preg_replace('/\s+/', '_', trim($normalizedTitle)); $normalizedTitle = preg_replace('/_+/', '_', $normalizedTitle); $normalizedTitle = trim($normalizedTitle, '_'); if (empty($normalizedTitle)) { $normalizedTitle = "file_{$notesId}"; } // Получаем расширение файла $extension = pathinfo($normalizedTitle, PATHINFO_EXTENSION); if (empty($extension)) { $extension = 'pdf'; } // Формируем новый путь $newS3Key = "crm2/CRM_Active_Files/Documents/HelpDesk/{$normalizedTicketNo}_{$ticketId}/{$normalizedTitle}_{$notesId}.{$extension}"; echo " ✅ Новый путь: {$newS3Key}\n"; // Проверяем существование файла в S3 $oldS3Key = ltrim($oldS3Key, '/'); try { $s3Client->headObject([ 'Bucket' => $bucket, 'Key' => $oldS3Key ]); echo " ✅ Файл найден в S3\n"; // Копируем файл в новое место $s3Client->copyObject([ 'Bucket' => $bucket, 'CopySource' => $bucket . '/' . $oldS3Key, 'Key' => $newS3Key ]); echo " ✅ Файл скопирован в новое место\n"; // Проверяем что новый файл существует $s3Client->headObject([ 'Bucket' => $bucket, 'Key' => $newS3Key ]); echo " ✅ Новый файл проверен\n"; // Удаляем старый файл $s3Client->deleteObject([ 'Bucket' => $bucket, 'Key' => $oldS3Key ]); echo " ✅ Старый файл удален\n"; // Обновляем записи в БД $newFilename = 'https://s3.twcstorage.ru/' . $bucket . '/' . $newS3Key; $updateSql = " UPDATE vtiger_notes SET s3_key = ?, filename = ? WHERE notesid = ? "; $updateStmt = $pdo->prepare($updateSql); $updateStmt->execute([$newS3Key, $newFilename, $notesId]); echo " ✅ Записи в БД обновлены\n"; $migratedCount++; } catch (AwsException $e) { if ($e->getAwsErrorCode() === 'NotFound') { echo " ❌ Файл не найден в S3: {$oldS3Key}\n"; } else { echo " ❌ Ошибка S3: " . $e->getMessage() . "\n"; } $errorCount++; } } catch (Exception $e) { echo " ❌ Ошибка: " . $e->getMessage() . "\n"; $errorCount++; } echo "\n"; } echo "🎉 МИГРАЦИЯ ЗАВЕРШЕНА!\n"; echo "📊 Статистика:\n"; echo " • Тикетов обработано: {$ticketCount}\n"; echo " • Файлов мигрировано: {$migratedCount}\n"; echo " • Ошибок: {$errorCount}\n"; echo " • Всего файлов: " . count($files) . "\n"; if ($errorCount > 0) { echo "\n⚠️ Некоторые файлы не удалось мигрировать. Возможные причины:\n"; echo " • Файлы отсутствуют в S3\n"; echo " • Проблемы с правами доступа\n"; echo " • Ошибки сети\n"; } } catch (Exception $e) { echo "❌ КРИТИЧЕСКАЯ ОШИБКА: " . $e->getMessage() . "\n"; echo "Стек вызовов:\n" . $e->getTraceAsString() . "\n"; exit(1); }