Files
crm.clientright.ru/crm_extensions/file_storage/migrate_helpdesk.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

229 lines
9.0 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
/**
* Миграция файлов тикетов (HelpDesk) в новую структуру
* Перемещает файлы из Documents/documentID/ в Documents/HelpDesk/ticketNo_ticketID/
*/
// Подключаем необходимые файлы
require_once '/var/www/fastuser/data/www/crm.clientright.ru/config.inc.php';
require_once '/var/www/fastuser/data/www/crm.clientright.ru/include/database/PearDatabase.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/file_storage/S3Client.php';
// Загружаем переменные окружения
$envFile = '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos($line, '=') !== false && strpos($line, '#') !== 0) {
list($key, $value) = explode('=', $line, 2);
$_ENV[trim($key)] = trim($value);
}
}
}
// Подключаем Composer autoloader для AWS SDK
require_once '/var/www/fastuser/data/www/crm.clientright.ru/vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
echo "🚀 Начинаем миграцию файлов тикетов (HelpDesk)...\n\n";
// Устанавливаем кодировку UTF-8
mb_internal_encoding('UTF-8');
try {
// Инициализируем S3 клиент
$s3Client = new S3Client([
'version' => '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);
}