'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, ]); $pdo = new PDO("mysql:host={$dbconfig['db_server']};dbname={$dbconfig['db_name']};charset=utf8mb4", $dbconfig['db_username'], $dbconfig['db_password']); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->exec("SET NAMES utf8mb4"); echo "✅ Подключения установлены\n\n"; // Находим ВСЕ файлы проектов в старой структуре (без фильтра по статусу!) $sql = " SELECT n.notesid, n.title, n.s3_key, n.filename, p.projectid, p.projectname, p.projectstatus FROM vtiger_notes n INNER JOIN vtiger_senotesrel sr ON n.notesid = sr.notesid INNER JOIN vtiger_project p ON sr.crmid = p.projectid WHERE n.filelocationtype = 'E' AND n.s3_key IS NOT NULL AND n.s3_key NOT LIKE '%/Project/%' ORDER BY p.projectid, 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); } $bucket = $_ENV['S3_BUCKET']; $migratedCount = 0; $errorCount = 0; $currentProjectId = null; $projectCount = 0; foreach ($files as $file) { $notesId = $file['notesid']; $title = $file['title']; $currentS3Key = $file['s3_key']; $projectId = $file['projectid']; $projectName = $file['projectname']; $projectStatus = $file['projectstatus']; // Считаем проекты if ($currentProjectId !== $projectId) { $currentProjectId = $projectId; $projectCount++; // Выводим прогресс каждые 10 проектов if ($projectCount % 10 == 0) { echo "\n📊 Обработано проектов: {$projectCount}\n\n"; } } // Компактный вывод if ($migratedCount % 50 == 0 && $migratedCount > 0) { echo "📊 Мигрировано файлов: {$migratedCount}, ошибок: {$errorCount}\n"; } try { // Правильная нормализация имени проекта (СОХРАНЯЕМ КИРИЛЛИЦУ!) $normalizedName = preg_replace('/[\/\\:*?"<>|№]/u', '_', $projectName); $normalizedName = preg_replace('/\s+/', '_', trim($normalizedName)); $normalizedName = preg_replace('/_+/', '_', $normalizedName); $normalizedName = trim($normalizedName, '_'); if (empty($normalizedName)) { $normalizedName = "project_{$projectId}"; } // Правильная нормализация имени файла (СОХРАНЯЕМ КИРИЛЛИЦУ!) $normalizedTitle = preg_replace('/[\/\\:*?"<>|№]/u', '_', $title); $normalizedTitle = preg_replace('/\s+/', '_', trim($normalizedTitle)); $normalizedTitle = preg_replace('/_+/', '_', $normalizedTitle); $normalizedTitle = trim($normalizedTitle, '_'); if (empty($normalizedTitle)) { $normalizedTitle = "file_{$notesId}"; } // Получаем расширение файла из РЕАЛЬНОГО s3_key $extension = pathinfo($currentS3Key, PATHINFO_EXTENSION); if (empty($extension)) { $extension = 'pdf'; } // Формируем новый путь $targetS3Key = "crm2/CRM_Active_Files/Documents/Project/{$normalizedName}_{$projectId}/{$normalizedTitle}_{$notesId}.{$extension}"; // Проверяем существование текущего файла в S3 $currentS3Key = ltrim($currentS3Key, '/'); try { $s3Client->headObject([ 'Bucket' => $bucket, 'Key' => $currentS3Key ]); // Копируем файл в новое место $s3Client->copyObject([ 'Bucket' => $bucket, 'CopySource' => $bucket . '/' . $currentS3Key, 'Key' => $targetS3Key ]); // Проверяем что новый файл существует $s3Client->headObject([ 'Bucket' => $bucket, 'Key' => $targetS3Key ]); // Удаляем старый файл $s3Client->deleteObject([ 'Bucket' => $bucket, 'Key' => $currentS3Key ]); // Обновляем записи в БД $newFilename = 'https://s3.twcstorage.ru/' . $bucket . '/' . $targetS3Key; $updateSql = "UPDATE vtiger_notes SET s3_key = ?, filename = ? WHERE notesid = ?"; $updateStmt = $pdo->prepare($updateSql); $updateStmt->execute([$targetS3Key, $newFilename, $notesId]); $migratedCount++; } catch (AwsException $e) { if ($e->getAwsErrorCode() === 'NotFound') { // Файл не найден в S3 - пропускаем молча } else { echo "❌ S3 ошибка для файла {$notesId}: " . $e->getMessage() . "\n"; } $errorCount++; } } catch (Exception $e) { echo "❌ Ошибка для файла {$notesId}: " . $e->getMessage() . "\n"; $errorCount++; } } echo "\n\n🎉 МИГРАЦИЯ ЗАВЕРШЕНА!\n"; echo "📊 Статистика:\n"; echo " • Проектов обработано: {$projectCount}\n"; echo " • Файлов мигрировано: {$migratedCount}\n"; echo " • Ошибок: {$errorCount}\n"; echo " • Всего файлов: " . count($files) . "\n"; if ($errorCount > 0) { echo "\n⚠️ Ошибки: файлы отсутствуют в S3 или проблемы с доступом\n"; } } catch (Exception $e) { echo "❌ КРИТИЧЕСКАЯ ОШИБКА: " . $e->getMessage() . "\n"; exit(1); }