fix: [attach] 短代码跨文章全局解析 + 缺失残留清理;损坏附件导入降级不中断;media 导入幂等;sablog:import 支持 sqlite 老库 + 5 个导入自动化测试

This commit is contained in:
ak
2026-08-11 19:16:16 +08:00
parent e735967218
commit 34ea046906
92 changed files with 340 additions and 12 deletions
+44 -8
View File
@@ -16,6 +16,19 @@ class AttachmentImporter
*/
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 占位文章上
@@ -36,16 +49,39 @@ class AttachmentImporter
$source = $this->locateSource($row['filepath'] ?? null, $attachmentsDir);
if ($source && $syncNow) {
$media = $post->addMedia($source)
->preservingOriginal()
->withCustomProperties($properties)
->usingFileName(basename($row['filename']) ?: basename($source))
->toMediaCollection('attachments', MediaDisk::name());
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();
$media->setCustomProperty('pending_sync', false);
$media->save();
return $media;
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);
+14 -3
View File
@@ -3,10 +3,12 @@
namespace App\Blog\Services;
use App\Models\Post;
use Illuminate\Support\Facades\Cache;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
use League\CommonMark\MarkdownConverter;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class PostContentRenderer
{
@@ -63,9 +65,16 @@ class PostContentRenderer
return $html;
}
$mediaItems = $post->getMedia('attachments');
$byLegacyId = $mediaItems->keyBy(fn ($m) => (int) $m->getCustomProperty('legacy_attachmentid'));
$ordered = $mediaItems->values();
// 短代码引用的是 sablog 全局附件 id,可能属于其他文章,需要全局查
$byLegacyId = Cache::remember('attach.legacy_ids', now()->addHour(), function () {
return Media::query()
->where('collection_name', 'attachments')
->get()
->keyBy(fn ($m) => (int) $m->getCustomProperty('legacy_attachmentid'));
});
// 当前文章媒体用于索引回退
$ordered = $post->getMedia('attachments')->values();
foreach ($matches as $match) {
$full = $match[0];
@@ -80,6 +89,8 @@ class PostContentRenderer
}
if (! $media) {
// 找不到媒体时移除残留短代码,避免把 [attach=999] 当正文显示
$html = str_replace($full, '', $html);
continue;
}