Files
crm.clientright.ru/crm_extensions/file_storage/migrate_contacts.php
Fedor 9245768987 🚀 CRM Files Migration & Real-time Features
 Features:
- Migrated ALL files to new S3 structure (Projects, Contacts, Accounts, HelpDesk, Invoice, etc.)
- Added Nextcloud folder buttons to ALL modules
- Fixed Nextcloud editor integration
- WebSocket server for real-time updates
- Redis Pub/Sub integration
- File path manager for organized storage
- Redis caching for performance (Functions.php)

📁 New Structure:
Documents/Project/ProjectName_ID/file_docID.ext
Documents/Contacts/FirstName_LastName_ID/file_docID.ext
Documents/Accounts/AccountName_ID/file_docID.ext

🔧 Technical:
- FilePathManager for standardized paths
- S3StorageService integration
- WebSocket server (Node.js + Docker)
- Redis cache for getBasicModuleInfo()
- Predis library for Redis connectivity

📝 Scripts:
- Migration scripts for all modules
- Test pages for WebSocket/SSE/Polling
- Documentation (MIGRATION_*.md, REDIS_*.md)

🎯 Result: 15,000+ files migrated successfully!
2025-10-24 19:59:28 +03:00

272 lines
10 KiB
PHP
Raw 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
/**
* Миграция файлов КОНТАКТОВ
* Переносит файлы из старой структуры в новую: Contacts/имя_ID/файл_docID.pdf
*/
// Включаем отображение ошибок
error_reporting(E_ALL);
ini_set('display_errors', 1);
echo "🚀 МИГРАЦИЯ ФАЙЛОВ КОНТАКТОВ\n";
echo "============================\n\n";
// Подключаем конфигурацию
require_once '/var/www/fastuser/data/www/crm.clientright.ru/config.inc.php';
require_once '/var/www/fastuser/data/www/crm.clientright.ru/vendor/autoload.php';
require_once '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/file_storage/FilePathManager.php';
require_once '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/shared/EnvLoader.php';
// Загружаем переменные окружения
EnvLoader::load('/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/.env');
// Создаем PDO подключение
try {
$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]
);
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";
?>