Files
laralog/app/Blog/Services/AttachmentImporter.php

128 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Blog\Services;
use App\Models\Post;
use App\Support\MediaDisk;
use Illuminate\Support\Str;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class AttachmentImporter
{
/**
* 将一条 sablog 附件记录写入媒体库。
*
* @param array{attachmentid:int,articleid:int,dateline:int,filename:string,filetype:string,filesize:int,downloads:int,filepath:string,thumb_filepath:string,thumb_width:int,thumb_height:int,isimage:int} $row
*/
public function create(int $postId, array $row, ?string $attachmentsDir, bool $syncNow): Media
{
$legacyId = (int) ($row['attachmentid'] ?? 0);
// 幂等:同一老附件已成功导入过则直接复用
$existing = Media::query()
->where('collection_name', 'attachments')
->where('custom_properties->legacy_attachmentid', $legacyId)
->latest('id')
->first();
if ($existing && ! $existing->getCustomProperty('pending_sync')) {
return $existing;
}
$post = Post::find($postId);
// 游离附件(articleid=0 或对应文章不存在)统一挂到 id=0 占位文章上
if (! $post) {
$post = new Post(['id' => 0]);
}
$properties = [
'downloads' => (int) ($row['downloads'] ?? 0),
'isimage' => (bool) ($row['isimage'] ?? false),
'legacy_attachmentid' => (int) ($row['attachmentid'] ?? 0),
'legacy_filepath' => $row['filepath'] ?? null,
'legacy_thumb' => $row['thumb_filepath'] ?? null,
'legacy_articleid' => (int) ($row['articleid'] ?? 0),
'pending_sync' => true,
];
$source = $this->locateSource($row['filepath'] ?? null, $attachmentsDir);
if ($source && $syncNow) {
try {
$media = $post->addMedia($source)
->preservingOriginal()
->withCustomProperties($properties)
->usingFileName(basename($row['filename']) ?: basename($source))
->toMediaCollection('attachments', MediaDisk::name());
$media->setCustomProperty('pending_sync', false);
$media->save();
return $media;
} catch (\Throwable $e) {
// addMedia 可能已插入记录并保存文件,仅缩略图生成失败:找回该记录标记完成
$existing = Media::query()
->where('collection_name', 'attachments')
->where('custom_properties->legacy_attachmentid', (int) ($row['attachmentid'] ?? 0))
->latest('id')
->first();
if ($existing) {
$existing->setCustomProperty('pending_sync', false);
$existing->save();
return $existing;
}
// 文件损坏无法入库时降级为 pending,不中断整批导入
\Illuminate\Support\Facades\Log::warning('附件导入失败,已降级为待同步', [
'attachment' => $row['attachmentid'] ?? null,
'file' => $source,
'error' => $e->getMessage(),
]);
}
}
return $this->createPendingRecord($post->id, $row, $properties);
}
private function locateSource(?string $filepath, ?string $attachmentsDir): ?string
{
if (! $attachmentsDir || ! $filepath) {
return null;
}
$candidate = rtrim($attachmentsDir, '/').'/'.ltrim($filepath, '/');
return is_file($candidate) ? $candidate : null;
}
private function createPendingRecord(int $postId, array $row, array $properties): Media
{
$disk = MediaDisk::name();
$media = new Media;
$media->model_type = Post::class;
$media->model_id = $postId;
$media->uuid = (string) Str::uuid();
$media->collection_name = 'attachments';
$media->name = pathinfo($row['filename'] ?? 'attachment', PATHINFO_FILENAME) ?: 'attachment';
$media->file_name = $row['filename'] ?: basename($row['filepath'] ?? 'attachment.bin');
$media->mime_type = $row['filetype'] ?: null;
$media->size = (int) ($row['filesize'] ?? 0);
$media->disk = $disk;
$media->conversions_disk = $disk;
$media->manipulations = [];
$media->custom_properties = $properties;
$media->generated_conversions = [];
$media->responsive_images = [];
$media->save();
return $media;
}
}