🚀 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!
This commit is contained in:
@@ -7,20 +7,26 @@
|
||||
* - Загрузка файлов в S3 с retry механизмом
|
||||
* - Получение presigned URLs для скачивания
|
||||
* - Логирование ошибок
|
||||
* - Поддержка универсальной структуры путей через FilePathManager
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @date 2025-09-20
|
||||
* @date 2025-10-22 (updated)
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../crm_extensions/file_storage/S3Client.php';
|
||||
require_once __DIR__ . '/../../crm_extensions/file_storage/FilePathManager.php';
|
||||
|
||||
class S3StorageService {
|
||||
private $s3Client;
|
||||
private $bucket;
|
||||
private $prefix;
|
||||
private $config;
|
||||
private $filePathManager;
|
||||
|
||||
public function __construct() {
|
||||
// Инициализируем FilePathManager
|
||||
$this->filePathManager = new FilePathManager();
|
||||
|
||||
// Загружаем конфигурацию S3
|
||||
try {
|
||||
file_put_contents('logs/debug.log', '[' . date('Y-m-d H:i:s') . '] S3StorageService: Loading config...' . PHP_EOL, FILE_APPEND);
|
||||
@@ -43,17 +49,34 @@ class S3StorageService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузка файла в S3 с retry механизмом
|
||||
* Загрузка файла в S3 с поддержкой универсальной структуры
|
||||
*
|
||||
* @param string $tmpFile Временный файл на сервере
|
||||
* @param int $notesId ID записи документа
|
||||
* @param string $filename Имя файла
|
||||
* @param int $maxRetries Максимальное количество попыток
|
||||
* @param array $context Контекст: ['module' => string, 'recordId' => int, 'recordName' => string, 'documentTitle' => string]
|
||||
* @return array Результат загрузки
|
||||
* @throws Exception При неудачной загрузке
|
||||
*/
|
||||
public function put($tmpFile, $notesId, $filename, $maxRetries = 3) {
|
||||
$key = $this->prefix . '/' . $notesId . '/' . $filename;
|
||||
public function put($tmpFile, $notesId, $filename, $maxRetries = 3, $context = []) {
|
||||
// Определяем путь к файлу
|
||||
if (!empty($context['module']) && !empty($context['recordId'])) {
|
||||
// Новая универсальная структура
|
||||
$module = $context['module'];
|
||||
$recordId = $context['recordId'];
|
||||
$recordName = $context['recordName'] ?? null;
|
||||
$documentTitle = $context['documentTitle'] ?? null;
|
||||
|
||||
$key = $this->filePathManager->getFilePath($module, $recordId, $notesId, $filename, $documentTitle, $recordName);
|
||||
|
||||
$this->logInfo("Using universal path structure: module=$module, recordId=$recordId");
|
||||
} else {
|
||||
// Старая структура (обратная совместимость)
|
||||
$key = $this->prefix . '/' . $notesId . '/' . $filename;
|
||||
|
||||
$this->logInfo("Using legacy path structure");
|
||||
}
|
||||
|
||||
$this->logInfo("Starting S3 upload: key=$key, file=$tmpFile");
|
||||
|
||||
@@ -75,7 +98,9 @@ class S3StorageService {
|
||||
'Metadata' => [
|
||||
'notesId' => (string)$notesId,
|
||||
'originalName' => (string)$filename,
|
||||
'uploadedAt' => (string)date('Y-m-d H:i:s')
|
||||
'uploadedAt' => (string)date('Y-m-d H:i:s'),
|
||||
'module' => !empty($context['module']) ? (string)$context['module'] : '',
|
||||
'recordId' => !empty($context['recordId']) ? (string)$context['recordId'] : ''
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -262,7 +287,6 @@ class S3StorageService {
|
||||
public function getPrefix() {
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение имени bucket'а
|
||||
*
|
||||
@@ -271,5 +295,14 @@ class S3StorageService {
|
||||
public function getBucket() {
|
||||
return $this->bucket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение экземпляра FilePathManager
|
||||
*
|
||||
* @return FilePathManager
|
||||
*/
|
||||
public function getFilePathManager() {
|
||||
return $this->filePathManager;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user