Files
KnowledgeBase/docs/API_REFERENCE.md
Knowledge Base System acf549c43c feat: 初始化知识库系统项目
- 实现基于 Laravel 11 和 Filament 3.X 的文档管理系统
- 添加用户认证和分组管理功能
- 实现文档上传、分类和权限控制
- 集成 Word 文档自动转换为 Markdown
- 集成 Meilisearch 全文搜索引擎
- 实现文档在线预览功能
- 添加安全日志和审计功能
- 完整的简体中文界面
- 包含完整的项目文档和部署指南

技术栈:
- Laravel 11.x
- Filament 3.X
- Meilisearch 1.5+
- Pandoc 文档转换
- Redis 队列系统
- Pest PHP 测试框架
2025-12-05 14:44:44 +08:00

15 KiB
Raw Blame History

API 参考文档

本文档描述知识库系统的核心服务类和方法。

目录

  1. DocumentService
  2. DocumentConversionService
  3. DocumentSearchService
  4. MarkdownRenderService
  5. SecurityLogger

DocumentService

文档管理服务,处理文档上传、下载和权限验证。

位置: app/Services/DocumentService.php

方法

uploadDocument()

上传并保存文档。

public function uploadDocument(
    UploadedFile $file,
    string $title,
    string $type,
    ?int $groupId,
    int $uploaderId,
    ?string $description = null
): Document

参数:

  • $file - 上传的文件对象
  • $title - 文档标题
  • $type - 文档类型('global' 或 'dedicated'
  • $groupId - 分组 ID专用文档必填
  • $uploaderId - 上传者用户 ID
  • $description - 文档描述(可选)

返回: Document - 创建的文档模型实例

异常:

  • ValidationException - 文件格式无效或专用文档未指定分组
  • Exception - 文件存储失败

示例:

use App\Services\DocumentService;
use Illuminate\Http\UploadedFile;

$service = app(DocumentService::class);

$document = $service->uploadDocument(
    file: $request->file('document'),
    title: '技术文档',
    type: 'global',
    groupId: null,
    uploaderId: auth()->id(),
    description: '系统架构说明'
);

validateDocumentAccess()

验证用户是否有权访问文档。

public function validateDocumentAccess(Document $document, User $user): bool

参数:

  • $document - 文档实例
  • $user - 用户实例

返回: bool - 是否有权访问

示例:

$hasAccess = $service->validateDocumentAccess($document, auth()->user());

if (!$hasAccess) {
    abort(403, '您没有权限访问此文档');
}

downloadDocument()

下载文档并记录日志。

public function downloadDocument(Document $document, User $user): StreamedResponse

参数:

  • $document - 文档实例
  • $user - 用户实例

返回: StreamedResponse - 文件流式响应

异常:

  • AuthorizationException - 用户无权下载
  • FileNotFoundException - 文件不存在

示例:

return $service->downloadDocument($document, auth()->user());

logDownload()

记录文档下载日志。

public function logDownload(Document $document, User $user): void

参数:

  • $document - 文档实例
  • $user - 用户实例

示例:

$service->logDownload($document, auth()->user());

DocumentConversionService

文档格式转换服务,将 Word 文档转换为 Markdown。

位置: app/Services/DocumentConversionService.php

方法

convertToMarkdown()

将 Word 文档转换为 Markdown 格式。

public function convertToMarkdown(Document $document): string

参数:

  • $document - 文档实例

返回: string - Markdown 内容

异常:

  • ConversionException - 转换失败

示例:

use App/Services/DocumentConversionService;

$service = app(DocumentConversionService::class);
$markdown = $service->convertToMarkdown($document);

queueConversion()

将文档转换任务加入队列。

public function queueConversion(Document $document): void

参数:

  • $document - 文档实例

示例:

$service->queueConversion($document);

saveMarkdownToFile()

保存 Markdown 内容到文件。

public function saveMarkdownToFile(Document $document, string $markdown): string

参数:

  • $document - 文档实例
  • $markdown - Markdown 内容

返回: string - 文件存储路径

示例:

$path = $service->saveMarkdownToFile($document, $markdown);

updateDocumentMarkdown()

更新文档的 Markdown 相关字段。

public function updateDocumentMarkdown(Document $document, string $markdownPath): void

参数:

  • $document - 文档实例
  • $markdownPath - Markdown 文件路径

示例:

$service->updateDocumentMarkdown($document, $markdownPath);

handleConversionFailure()

处理转换失败的情况。

public function handleConversionFailure(Document $document, Exception $e): void

参数:

  • $document - 文档实例
  • $e - 异常对象

示例:

try {
    $markdown = $service->convertToMarkdown($document);
} catch (Exception $e) {
    $service->handleConversionFailure($document, $e);
}

getMarkdownPreview()

获取 Markdown 内容摘要。

public function getMarkdownPreview(string $markdown, int $length = 500): string

参数:

  • $markdown - Markdown 内容
  • $length - 摘要长度(默认 500 字符)

返回: string - Markdown 摘要

示例:

$preview = $service->getMarkdownPreview($markdown, 200);

DocumentSearchService

文档搜索服务,提供全文搜索和权限过滤。

位置: app/Services/DocumentSearchService.php

方法

搜索文档。

public function search(string $query, User $user, array $filters = []): Collection

参数:

  • $query - 搜索关键词
  • $user - 用户实例
  • $filters - 筛选条件数组(可选)
    • type: 文档类型('global' 或 'dedicated'
    • group_id: 分组 ID

返回: Collection - 搜索结果集合

示例:

use App\Services\DocumentSearchService;

$service = app(DocumentSearchService::class);

$results = $service->search(
    query: '技术文档',
    user: auth()->user(),
    filters: [
        'type' => 'global',
    ]
);

indexDocument()

将文档索引到 Meilisearch。

public function indexDocument(Document $document): void

参数:

  • $document - 文档实例

示例:

$service->indexDocument($document);

updateDocumentIndex()

更新文档索引。

public function updateDocumentIndex(Document $document): void

参数:

  • $document - 文档实例

示例:

$service->updateDocumentIndex($document);

removeDocumentFromIndex()

从索引中删除文档。

public function removeDocumentFromIndex(Document $document): void

参数:

  • $document - 文档实例

示例:

$service->removeDocumentFromIndex($document);

filterByUserPermissions()

根据用户权限过滤搜索结果。

public function filterByUserPermissions(Collection $results, User $user): Collection

参数:

  • $results - 搜索结果集合
  • $user - 用户实例

返回: Collection - 过滤后的结果集合

示例:

$filteredResults = $service->filterByUserPermissions($results, auth()->user());

prepareSearchableData()

准备文档的可搜索数据。

public function prepareSearchableData(Document $document): array

参数:

  • $document - 文档实例

返回: array - 可搜索数据数组

示例:

$searchableData = $service->prepareSearchableData($document);

MarkdownRenderService

Markdown 渲染服务,将 Markdown 转换为 HTML。

位置: app/Services/MarkdownRenderService.php

方法

render()

将 Markdown 渲染为 HTML。

public function render(string $markdown): string

参数:

  • $markdown - Markdown 内容

返回: string - 渲染后的 HTML

示例:

use App\Services\MarkdownRenderService;

$service = app(MarkdownRenderService::class);
$html = $service->render($markdown);

sanitize()

清理 HTML 内容,防止 XSS 攻击。

public function sanitize(string $html): string

参数:

  • $html - HTML 内容

返回: string - 清理后的 HTML

示例:

$safeHtml = $service->sanitize($html);

extractPreview()

从 Markdown 中提取摘要。

public function extractPreview(string $markdown, int $length = 200): string

参数:

  • $markdown - Markdown 内容
  • $length - 摘要长度(默认 200 字符)

返回: string - 摘要文本

示例:

$preview = $service->extractPreview($markdown, 150);

SecurityLogger

安全日志记录服务。

位置: app/Services/SecurityLogger.php

方法

logUnauthorizedAccess()

记录未授权访问尝试。

public function logUnauthorizedAccess(
    User $user,
    Document $document,
    string $action
): void

参数:

  • $user - 用户实例
  • $document - 文档实例
  • $action - 尝试的操作(如 'view', 'download'

示例:

use App\Services\SecurityLogger;

$logger = app(SecurityLogger::class);
$logger->logUnauthorizedAccess(
    user: auth()->user(),
    document: $document,
    action: 'download'
);

logDocumentAccess()

记录文档访问。

public function logDocumentAccess(
    User $user,
    Document $document,
    string $action
): void

参数:

  • $user - 用户实例
  • $document - 文档实例
  • $action - 操作类型

示例:

$logger->logDocumentAccess(
    user: auth()->user(),
    document: $document,
    action: 'view'
);

模型查询作用域

Document 模型

accessibleBy()

获取用户可访问的文档。

Document::accessibleBy($user)->get();

参数:

  • $user - 用户实例

返回: 查询构建器

示例:

// 获取用户可访问的所有文档
$documents = Document::accessibleBy(auth()->user())->get();

// 结合其他查询条件
$recentDocuments = Document::accessibleBy(auth()->user())
    ->where('created_at', '>=', now()->subDays(7))
    ->orderBy('created_at', 'desc')
    ->get();

global()

获取全局文档。

Document::global()->get();

返回: 查询构建器

示例:

$globalDocuments = Document::global()->get();

dedicated()

获取专用文档。

Document::dedicated()->get();

返回: 查询构建器

示例:

$dedicatedDocuments = Document::dedicated()
    ->where('group_id', $groupId)
    ->get();

策略方法

DocumentPolicy

view()

检查用户是否可以查看文档。

Gate::allows('view', $document);

参数:

  • $document - 文档实例

返回: bool

示例:

if (Gate::allows('view', $document)) {
    // 用户可以查看文档
}

// 或使用 authorize 方法
$this->authorize('view', $document);

download()

检查用户是否可以下载文档。

Gate::allows('download', $document);

参数:

  • $document - 文档实例

返回: bool

示例:

if (Gate::denies('download', $document)) {
    abort(403, '您没有权限下载此文档');
}

队列任务

ConvertDocumentToMarkdown

文档转换队列任务。

位置: app/Jobs/ConvertDocumentToMarkdown.php

分发任务:

use App\Jobs\ConvertDocumentToMarkdown;

ConvertDocumentToMarkdown::dispatch($document);

延迟分发:

ConvertDocumentToMarkdown::dispatch($document)
    ->delay(now()->addMinutes(5));

指定队列:

ConvertDocumentToMarkdown::dispatch($document)
    ->onQueue('documents');

事件和监听器

文档事件

系统会在文档生命周期的关键点触发事件:

文档创建后

// 自动触发转换和索引
Document::created(function ($document) {
    // 队列转换任务
    // 索引到 Meilisearch
});

文档更新后

Document::updated(function ($document) {
    // 如果文件变更,重新转换
    // 更新 Meilisearch 索引
});

文档删除后

Document::deleted(function ($document) {
    // 删除文件
    // 从 Meilisearch 移除索引
});

配置选项

文档转换配置

文件: config/documents.php

return [
    'conversion' => [
        'driver' => env('DOCUMENT_CONVERSION_DRIVER', 'pandoc'),
        'pandoc_path' => env('PANDOC_PATH', '/usr/bin/pandoc'),
        'timeout' => env('CONVERSION_TIMEOUT', 300),
        'queue' => 'documents',
    ],
    'markdown' => [
        'renderer' => 'commonmark',
        'sanitize' => true,
    ],
];

Scout 配置

文件: config/scout.php

'meilisearch' => [
    'host' => env('MEILISEARCH_HOST', 'http://127.0.0.1:7700'),
    'key' => env('MEILISEARCH_KEY'),
    'index-settings' => [
        'documents' => [
            'filterableAttributes' => ['type', 'group_id', 'uploaded_by'],
            'sortableAttributes' => ['created_at', 'title'],
            'searchableAttributes' => ['title', 'description', 'markdown_content'],
        ],
    ],
],

错误处理

常见异常

ValidationException

文件格式验证失败或必填字段缺失。

try {
    $document = $service->uploadDocument(...);
} catch (ValidationException $e) {
    return back()->withErrors($e->errors());
}

AuthorizationException

用户无权执行操作。

try {
    $this->authorize('download', $document);
} catch (AuthorizationException $e) {
    abort(403, '您没有权限下载此文档');
}

FileNotFoundException

文件不存在。

try {
    return $service->downloadDocument($document, $user);
} catch (FileNotFoundException $e) {
    abort(404, '文件不存在');
}

ConversionException

文档转换失败。

try {
    $markdown = $service->convertToMarkdown($document);
} catch (ConversionException $e) {
    Log::error('文档转换失败', [
        'document_id' => $document->id,
        'error' => $e->getMessage(),
    ]);
}

最佳实践

1. 使用依赖注入

class DocumentController extends Controller
{
    public function __construct(
        private DocumentService $documentService,
        private MarkdownRenderService $markdownService
    ) {}
    
    public function preview(Document $document)
    {
        $this->authorize('view', $document);
        
        $markdown = $document->getMarkdownContent();
        $html = $this->markdownService->render($markdown);
        
        return view('documents.preview', compact('html', 'document'));
    }
}

2. 使用事务处理

DB::transaction(function () use ($file, $data) {
    $document = $this->documentService->uploadDocument(
        file: $file,
        title: $data['title'],
        type: $data['type'],
        groupId: $data['group_id'] ?? null,
        uploaderId: auth()->id()
    );
    
    // 其他相关操作
});

3. 使用查询作用域

// 好的做法
$documents = Document::accessibleBy(auth()->user())
    ->where('type', 'global')
    ->latest()
    ->paginate(20);

// 避免手动实现权限过滤

4. 异步处理耗时操作

// 文档转换应该异步处理
ConvertDocumentToMarkdown::dispatch($document);

// 而不是同步执行
// $service->convertToMarkdown($document); // 避免

最后更新2025-12-05