Files
ntspi-app/app/Containers/Dashboard/Tasks/CreatePostFromAiDataTask.php
T
2026-03-20 21:47:02 +05:00

130 lines
4.3 KiB
PHP

<?php
namespace App\Containers\Dashboard\Tasks;
use App\Containers\Article\Enums\PostStatus;
use App\Containers\Article\Models\Post;
use App\Services\Filament\Domain\Posts\PostDataProcessor;
use Illuminate\Support\Str;
class CreatePostFromAiDataTask
{
/**
* Создаёт пост из данных, полученных от AI
*
* @param array $newsData Данные от AI (title, body, authors, tags, category_id)
* @param string|null $documentPath Путь к основному документу
* @param array $mediaPaths Пути к медиафайлам
* @param array $attachedFiles Прикреплённые файлы (для блока files)
*/
public function run(array $newsData, ?string $documentPath, array $mediaPaths, array $attachedFiles): Post
{
// Формируем контент в формате редактора
$content = $this->buildContent($newsData, $attachedFiles);
// Обрабатываем изображения
['preview' => $previewPath, 'images' => $imagesPaths] = $this->processImages($mediaPaths);
// Подготавливаем данные для создания поста
$postData = $this->preparePostData($newsData, $content, $previewPath, $imagesPaths);
// Обрабатываем через PostDataProcessor (как в админке)
$processedData = (new PostDataProcessor())->processCreate($postData);
// Создаём пост
$post = Post::create($processedData);
// Сохраняем теги
$this->syncTags($post, $newsData);
return $post;
}
/**
* Формирует контент в формате редактора
*/
private function buildContent(array $newsData, array $attachedFiles): array
{
$content = [
[
'type' => 'paragraph',
'data' => [
'seo_active' => true,
'content' => $newsData['body'] ?? '',
],
],
];
if (!empty($attachedFiles)) {
$content[] = [
'type' => 'files',
'data' => [
'file' => $attachedFiles,
],
];
}
return $content;
}
/**
* Обрабатывает изображения: все изображения идут в gallery, первое — preview
*/
private function processImages(array $mediaPaths): array
{
$imagesPaths = [];
$imageExtensions = ['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'svg', 'tiff'];
foreach ($mediaPaths as $path) {
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (in_array($extension, $imageExtensions)) {
$imagesPaths[] = $path;
}
}
return [
'preview' => !empty($imagesPaths) ? reset($imagesPaths) : null,
'images' => $imagesPaths,
];
}
/**
* Подготавливает данные для создания поста
*/
private function preparePostData(array $newsData, array $content, ?string $previewPath, array $imagesPaths): array
{
return [
'title' => $newsData['title'] ?? 'Без названия',
'slug' => Str::slug($newsData['title'] ?? '') . '-' . time(),
'content' => $content,
'authors' => $newsData['authors'] ?? [],
'category_id' => $newsData['category_id'] ?? null,
'status' => PostStatus::VERIFICATION,
'user_id' => auth()->id(),
'preview' => $previewPath,
'images' => !empty($imagesPaths) ? $imagesPaths : null,
'publish_setting' => [
'publish_after' => false,
'publish_at' => null,
],
'publication' => [],
];
}
/**
* Сохраняет теги поста
*/
private function syncTags(Post $post, array $newsData): void
{
if (!empty($newsData['tags'])) {
$tagNames = array_map(
fn($tag) => str_replace('#', '', $tag),
$newsData['tags']
);
$post->syncTags($tagNames);
}
}
}