This commit is contained in:
F4ilji
2026-03-21 15:40:18 +05:00
parent 56d9a0207c
commit b1c4f7b054
9 changed files with 1135 additions and 784 deletions
@@ -4,27 +4,21 @@ namespace App\Containers\Dashboard\Actions;
use App\Containers\Article\Models\Category;
use App\Containers\Article\Models\Post;
use App\Containers\Dashboard\Tasks\CallAiServiceForFileSelectionTask;
use App\Containers\Dashboard\Tasks\CallAiServiceTask;
use App\Containers\Dashboard\Tasks\CreatePostFromAiDataTask;
use App\Containers\Dashboard\Tasks\ExtractTextFromDocumentTask;
use App\Containers\Dashboard\Tasks\ExtractTextFragmentTask;
use App\Containers\Dashboard\Tasks\FindMainNewsFileTask;
use App\Containers\Dashboard\Tasks\UnpackArchiveTask;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class ProcessMixedFilesAction
{
private array $extractPathsToCleanup = [];
public function __construct(
private readonly FindMainNewsFileTask $findMainNewsFileTask,
private readonly ExtractTextFromDocumentTask $extractTextFromDocumentTask,
private readonly CallAiServiceTask $callAiServiceTask,
private readonly CreatePostFromAiDataTask $createPostFromAiDataTask,
private readonly UnpackArchiveTask $unpackArchiveTask,
) {}
/**
@@ -40,25 +34,6 @@ class ProcessMixedFilesAction
'files' => $files->map(fn($f) => $f->getClientOriginalName())->toArray(),
]);
// Проверяем, есть ли архивы и нужно ли их распаковывать
$files = $this->processArchives($files);
Log::info('[ProcessMixedFilesAction] Файлы после обработки архивов', [
'files_count' => $files->count(),
'files' => $files->map(fn($f) => $f->getClientOriginalName())->toArray(),
]);
// После распаковки проверяем, что есть DOC/DOCX файл
$hasDocOrDocx = $files->contains(function ($file) {
$ext = strtolower($file->getClientOriginalExtension());
return in_array($ext, ['doc', 'docx']);
});
if (!$hasDocOrDocx) {
Log::error('[ProcessMixedFilesAction] После распаковки не найден DOC/DOCX файл');
throw new \RuntimeException('В архиве не найден DOC или DOCX файл для извлечения текста новости.');
}
// Находим основной файл с текстом новости
$mainFile = $this->findMainNewsFileTask->run($files);
@@ -120,9 +95,6 @@ class ProcessMixedFilesAction
// Создаём пост
$post = $this->createPostFromAiDataTask->run($newsData, $documentPath, $mediaPaths, $attachedFiles);
// Очищаем временные директории после успешной обработки
$this->cleanupExtractPaths();
// Возвращаем данные для отображения
return $this->prepareResponse($post, $newsData);
}
@@ -147,81 +119,13 @@ class ProcessMixedFilesAction
];
}
/**
* Обрабатывает архивы: распаковывает, если загружены только архивы
*/
private function processArchives(Collection $files): Collection
{
$archiveExtensions = ['zip'];
// Убедимся, что директория для распаковки существует
$tempPath = storage_path('app/temp');
if (!file_exists($tempPath)) {
mkdir($tempPath, 0755, true);
}
// Разделяем файлы на архивы и остальные
$archives = $files->filter(fn($file) =>
in_array(strtolower($file->getClientOriginalExtension()), $archiveExtensions)
);
$nonArchives = $files->filter(fn($file) =>
!in_array(strtolower($file->getClientOriginalExtension()), $archiveExtensions)
);
// Если есть только архивы (нет других файлов) — распаковываем
if ($archives->isNotEmpty() && $nonArchives->isEmpty()) {
Log::info('[ProcessMixedFilesAction] Обнаружены только архивы, начинаем распаковку', [
'archives_count' => $archives->count(),
]);
$unpackedFiles = collect();
$extractPaths = [];
foreach ($archives as $archive) {
try {
$result = $this->unpackArchiveTask->run($archive);
$extractedFiles = $result['files'];
$extractPaths[] = $result['extract_path'];
$unpackedFiles = $unpackedFiles->merge($extractedFiles);
Log::info('[ProcessMixedFilesAction] Архив распакован', [
'archive' => $archive->getClientOriginalName(),
'extracted_count' => $extractedFiles->count(),
]);
} catch (\Exception $e) {
Log::error('[ProcessMixedFilesAction] Ошибка при распаковке архива', [
'archive' => $archive->getClientOriginalName(),
'error' => $e->getMessage(),
]);
throw $e;
}
}
// Сохраняем пути для очистки после обработки
$this->extractPathsToCleanup = $extractPaths;
return $unpackedFiles;
}
// Если есть не-архивные файлы — возвращаем все файлы как есть
// (архивы будут обработаны как обычные медиа-файлы)
Log::info('[ProcessMixedFilesAction] Обнаружены смешанные файлы, архивы не распаковываем', [
'archives_count' => $archives->count(),
'non_archives_count' => $nonArchives->count(),
]);
return $files;
}
/**
* Обрабатывает прикреплённые файлы (для добавления в контент)
*/
private function processAttachedFiles(Collection $files, UploadedFile $mainFile): array
{
$attachedFiles = [];
$fileExtensions = ['doc', 'docx', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'rar'];
$fileExtensions = ['doc', 'docx', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx'];
foreach ($files as $file) {
// Пропускаем основной файл
@@ -285,49 +189,4 @@ class ProcessMixedFilesAction
return round($bytes, 2) . ' ' . $units[$pow];
}
/**
* Очищает временные директории после распаковки
*/
private function cleanupExtractPaths(): void
{
foreach ($this->extractPathsToCleanup as $path) {
if (file_exists($path)) {
$this->cleanupDirectory($path);
}
}
$this->extractPathsToCleanup = [];
}
/**
* Очищает директорию рекурсивно
*/
private function cleanupDirectory(string $path): void
{
if (!file_exists($path) || !is_dir($path)) {
return;
}
try {
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $file) {
if ($file->isFile()) {
@unlink($file->getPathname());
} elseif ($file->isDir()) {
@rmdir($file->getPathname());
}
}
@rmdir($path);
} catch (\Exception $e) {
Log::warning('[ProcessMixedFilesAction] Не удалось очистить временную директорию', [
'error' => $e->getMessage(),
'path' => $path,
]);
}
}
}