Files
KnowledgeBase/app/Services/DocumentPreviewService.php

91 lines
2.8 KiB
PHP

<?php
namespace App\Services;
use App\Models\Document;
class DocumentPreviewService
{
/**
* 将文档的 Markdown 内容转换为 HTML 用于预览
* 统一用于 Filament 后台内联预览和独立预览页面
*
* @throws \Exception
*/
public function convertToHtml(Document $document): string
{
return $this->convertMarkdownToHtml($document);
}
/**
* 将 Markdown 转换为 HTML
*
* @throws \Exception
*/
public function convertMarkdownToHtml(Document $document): string
{
$markdownContent = $document->getMarkdownContent();
if (empty($markdownContent)) {
throw new \Exception('Markdown 内容为空');
}
app(DocumentConversionService::class)->ensureMarkdownMediaAssets($document);
$markdownContent = $this->stripPreviewFrontMatter($markdownContent);
$markdownContent = $this->rewriteMarkdownMediaPaths($document, $markdownContent);
$renderService = app(MarkdownRenderService::class);
return $renderService->render($markdownContent);
}
protected function stripPreviewFrontMatter(string $markdownContent): string
{
if (!preg_match('/\A---\R(?P<frontmatter>.*?\R)---\R*/s', $markdownContent, $matches)) {
return $markdownContent;
}
$frontMatter = $matches['frontmatter'] ?? '';
if (!preg_match('/^(author|source_file):/m', $frontMatter)) {
return $markdownContent;
}
return (string) preg_replace('/\A---\R.*?\R---\R*/s', '', $markdownContent, 1);
}
protected function rewriteMarkdownMediaPaths(Document $document, string $markdownContent): string
{
$documentDir = dirname($document->markdown_path);
return (string) preg_replace_callback(
'/!\[(?<alt>[^\]]*)]\((?<path>(?:\.\/)?media\/[^)]+)\)/',
function (array $matches) use ($documentDir): string {
$relativePath = trim($matches['path'] ?? '');
$relativePath = preg_replace('/^\.?\//', '', $relativePath) ?? $relativePath;
$relativePath = ltrim(str_replace('\\', '/', $relativePath), '/');
$segments = array_filter(
explode('/', $documentDir . '/' . $relativePath),
fn (string $segment): bool => $segment !== ''
);
$url = '/markdown-media/' . implode('/', array_map('rawurlencode', $segments));
return sprintf('![%s](%s)', $matches['alt'] ?? '', $url);
},
$markdownContent
);
}
/**
* 检查文档是否可以预览
*/
public function canPreview(Document $document): bool
{
return $document->conversion_status === 'completed'
&& !empty($document->markdown_path);
}
}