# API 参考文档 本文档描述知识库系统的核心服务类和方法。 ## 目录 1. [DocumentService](#documentservice) 2. [DocumentConversionService](#documentconversionservice) 3. [DocumentSearchService](#documentsearchservice) 4. [MarkdownRenderService](#markdownrenderservice) 5. [SecurityLogger](#securitylogger) --- ## DocumentService 文档管理服务,处理文档上传、下载和权限验证。 **位置**: `app/Services/DocumentService.php` ### 方法 #### uploadDocument() 上传并保存文档。 ```php 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` - 文件存储失败 **示例**: ```php 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() 验证用户是否有权访问文档。 ```php public function validateDocumentAccess(Document $document, User $user): bool ``` **参数**: - `$document` - 文档实例 - `$user` - 用户实例 **返回**: `bool` - 是否有权访问 **示例**: ```php $hasAccess = $service->validateDocumentAccess($document, auth()->user()); if (!$hasAccess) { abort(403, '您没有权限访问此文档'); } ``` #### downloadDocument() 下载文档并记录日志。 ```php public function downloadDocument(Document $document, User $user): StreamedResponse ``` **参数**: - `$document` - 文档实例 - `$user` - 用户实例 **返回**: `StreamedResponse` - 文件流式响应 **异常**: - `AuthorizationException` - 用户无权下载 - `FileNotFoundException` - 文件不存在 **示例**: ```php return $service->downloadDocument($document, auth()->user()); ``` #### logDownload() 记录文档下载日志。 ```php public function logDownload(Document $document, User $user): void ``` **参数**: - `$document` - 文档实例 - `$user` - 用户实例 **示例**: ```php $service->logDownload($document, auth()->user()); ``` --- ## DocumentConversionService 文档格式转换服务,将 Word 文档转换为 Markdown。 **位置**: `app/Services/DocumentConversionService.php` ### 方法 #### convertToMarkdown() 将 Word 文档转换为 Markdown 格式。 ```php public function convertToMarkdown(Document $document): string ``` **参数**: - `$document` - 文档实例 **返回**: `string` - Markdown 内容 **异常**: - `ConversionException` - 转换失败 **示例**: ```php use App/Services/DocumentConversionService; $service = app(DocumentConversionService::class); $markdown = $service->convertToMarkdown($document); ``` #### queueConversion() 将文档转换任务加入队列。 ```php public function queueConversion(Document $document): void ``` **参数**: - `$document` - 文档实例 **示例**: ```php $service->queueConversion($document); ``` #### saveMarkdownToFile() 保存 Markdown 内容到文件。 ```php public function saveMarkdownToFile(Document $document, string $markdown): string ``` **参数**: - `$document` - 文档实例 - `$markdown` - Markdown 内容 **返回**: `string` - 文件存储路径 **示例**: ```php $path = $service->saveMarkdownToFile($document, $markdown); ``` #### updateDocumentMarkdown() 更新文档的 Markdown 相关字段。 ```php public function updateDocumentMarkdown(Document $document, string $markdownPath): void ``` **参数**: - `$document` - 文档实例 - `$markdownPath` - Markdown 文件路径 **示例**: ```php $service->updateDocumentMarkdown($document, $markdownPath); ``` #### handleConversionFailure() 处理转换失败的情况。 ```php public function handleConversionFailure(Document $document, Exception $e): void ``` **参数**: - `$document` - 文档实例 - `$e` - 异常对象 **示例**: ```php try { $markdown = $service->convertToMarkdown($document); } catch (Exception $e) { $service->handleConversionFailure($document, $e); } ``` #### getMarkdownPreview() 获取 Markdown 内容摘要。 ```php public function getMarkdownPreview(string $markdown, int $length = 500): string ``` **参数**: - `$markdown` - Markdown 内容 - `$length` - 摘要长度(默认 500 字符) **返回**: `string` - Markdown 摘要 **示例**: ```php $preview = $service->getMarkdownPreview($markdown, 200); ``` --- ## DocumentSearchService 文档搜索服务,提供全文搜索和权限过滤。 **位置**: `app/Services/DocumentSearchService.php` ### 方法 #### search() 搜索文档。 ```php public function search(string $query, User $user, array $filters = []): Collection ``` **参数**: - `$query` - 搜索关键词 - `$user` - 用户实例 - `$filters` - 筛选条件数组(可选) - `type`: 文档类型('global' 或 'dedicated') - `group_id`: 分组 ID **返回**: `Collection` - 搜索结果集合 **示例**: ```php use App\Services\DocumentSearchService; $service = app(DocumentSearchService::class); $results = $service->search( query: '技术文档', user: auth()->user(), filters: [ 'type' => 'global', ] ); ``` #### indexDocument() 将文档索引到 Meilisearch。 ```php public function indexDocument(Document $document): void ``` **参数**: - `$document` - 文档实例 **示例**: ```php $service->indexDocument($document); ``` #### updateDocumentIndex() 更新文档索引。 ```php public function updateDocumentIndex(Document $document): void ``` **参数**: - `$document` - 文档实例 **示例**: ```php $service->updateDocumentIndex($document); ``` #### removeDocumentFromIndex() 从索引中删除文档。 ```php public function removeDocumentFromIndex(Document $document): void ``` **参数**: - `$document` - 文档实例 **示例**: ```php $service->removeDocumentFromIndex($document); ``` #### filterByUserPermissions() 根据用户权限过滤搜索结果。 ```php public function filterByUserPermissions(Collection $results, User $user): Collection ``` **参数**: - `$results` - 搜索结果集合 - `$user` - 用户实例 **返回**: `Collection` - 过滤后的结果集合 **示例**: ```php $filteredResults = $service->filterByUserPermissions($results, auth()->user()); ``` #### prepareSearchableData() 准备文档的可搜索数据。 ```php public function prepareSearchableData(Document $document): array ``` **参数**: - `$document` - 文档实例 **返回**: `array` - 可搜索数据数组 **示例**: ```php $searchableData = $service->prepareSearchableData($document); ``` --- ## MarkdownRenderService Markdown 渲染服务,将 Markdown 转换为 HTML。 **位置**: `app/Services/MarkdownRenderService.php` ### 方法 #### render() 将 Markdown 渲染为 HTML。 ```php public function render(string $markdown): string ``` **参数**: - `$markdown` - Markdown 内容 **返回**: `string` - 渲染后的 HTML **示例**: ```php use App\Services\MarkdownRenderService; $service = app(MarkdownRenderService::class); $html = $service->render($markdown); ``` #### sanitize() 清理 HTML 内容,防止 XSS 攻击。 ```php public function sanitize(string $html): string ``` **参数**: - `$html` - HTML 内容 **返回**: `string` - 清理后的 HTML **示例**: ```php $safeHtml = $service->sanitize($html); ``` #### extractPreview() 从 Markdown 中提取摘要。 ```php public function extractPreview(string $markdown, int $length = 200): string ``` **参数**: - `$markdown` - Markdown 内容 - `$length` - 摘要长度(默认 200 字符) **返回**: `string` - 摘要文本 **示例**: ```php $preview = $service->extractPreview($markdown, 150); ``` --- ## SecurityLogger 安全日志记录服务。 **位置**: `app/Services/SecurityLogger.php` ### 方法 #### logUnauthorizedAccess() 记录未授权访问尝试。 ```php public function logUnauthorizedAccess( User $user, Document $document, string $action ): void ``` **参数**: - `$user` - 用户实例 - `$document` - 文档实例 - `$action` - 尝试的操作(如 'view', 'download') **示例**: ```php use App\Services\SecurityLogger; $logger = app(SecurityLogger::class); $logger->logUnauthorizedAccess( user: auth()->user(), document: $document, action: 'download' ); ``` #### logDocumentAccess() 记录文档访问。 ```php public function logDocumentAccess( User $user, Document $document, string $action ): void ``` **参数**: - `$user` - 用户实例 - `$document` - 文档实例 - `$action` - 操作类型 **示例**: ```php $logger->logDocumentAccess( user: auth()->user(), document: $document, action: 'view' ); ``` --- ## 模型查询作用域 ### Document 模型 #### accessibleBy() 获取用户可访问的文档。 ```php Document::accessibleBy($user)->get(); ``` **参数**: - `$user` - 用户实例 **返回**: 查询构建器 **示例**: ```php // 获取用户可访问的所有文档 $documents = Document::accessibleBy(auth()->user())->get(); // 结合其他查询条件 $recentDocuments = Document::accessibleBy(auth()->user()) ->where('created_at', '>=', now()->subDays(7)) ->orderBy('created_at', 'desc') ->get(); ``` #### global() 获取全局文档。 ```php Document::global()->get(); ``` **返回**: 查询构建器 **示例**: ```php $globalDocuments = Document::global()->get(); ``` #### dedicated() 获取专用文档。 ```php Document::dedicated()->get(); ``` **返回**: 查询构建器 **示例**: ```php $dedicatedDocuments = Document::dedicated() ->where('group_id', $groupId) ->get(); ``` --- ## 策略方法 ### DocumentPolicy #### view() 检查用户是否可以查看文档。 ```php Gate::allows('view', $document); ``` **参数**: - `$document` - 文档实例 **返回**: `bool` **示例**: ```php if (Gate::allows('view', $document)) { // 用户可以查看文档 } // 或使用 authorize 方法 $this->authorize('view', $document); ``` #### download() 检查用户是否可以下载文档。 ```php Gate::allows('download', $document); ``` **参数**: - `$document` - 文档实例 **返回**: `bool` **示例**: ```php if (Gate::denies('download', $document)) { abort(403, '您没有权限下载此文档'); } ``` --- ## 队列任务 ### ConvertDocumentToMarkdown 文档转换队列任务。 **位置**: `app/Jobs/ConvertDocumentToMarkdown.php` **分发任务**: ```php use App\Jobs\ConvertDocumentToMarkdown; ConvertDocumentToMarkdown::dispatch($document); ``` **延迟分发**: ```php ConvertDocumentToMarkdown::dispatch($document) ->delay(now()->addMinutes(5)); ``` **指定队列**: ```php ConvertDocumentToMarkdown::dispatch($document) ->onQueue('documents'); ``` --- ## 事件和监听器 ### 文档事件 系统会在文档生命周期的关键点触发事件: #### 文档创建后 ```php // 自动触发转换和索引 Document::created(function ($document) { // 队列转换任务 // 索引到 Meilisearch }); ``` #### 文档更新后 ```php Document::updated(function ($document) { // 如果文件变更,重新转换 // 更新 Meilisearch 索引 }); ``` #### 文档删除后 ```php Document::deleted(function ($document) { // 删除文件 // 从 Meilisearch 移除索引 }); ``` --- ## 配置选项 ### 文档转换配置 **文件**: `config/documents.php` ```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` ```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 文件格式验证失败或必填字段缺失。 ```php try { $document = $service->uploadDocument(...); } catch (ValidationException $e) { return back()->withErrors($e->errors()); } ``` #### AuthorizationException 用户无权执行操作。 ```php try { $this->authorize('download', $document); } catch (AuthorizationException $e) { abort(403, '您没有权限下载此文档'); } ``` #### FileNotFoundException 文件不存在。 ```php try { return $service->downloadDocument($document, $user); } catch (FileNotFoundException $e) { abort(404, '文件不存在'); } ``` #### ConversionException 文档转换失败。 ```php try { $markdown = $service->convertToMarkdown($document); } catch (ConversionException $e) { Log::error('文档转换失败', [ 'document_id' => $document->id, 'error' => $e->getMessage(), ]); } ``` --- ## 最佳实践 ### 1. 使用依赖注入 ```php 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. 使用事务处理 ```php 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. 使用查询作用域 ```php // 好的做法 $documents = Document::accessibleBy(auth()->user()) ->where('type', 'global') ->latest() ->paginate(20); // 避免手动实现权限过滤 ``` ### 4. 异步处理耗时操作 ```php // 文档转换应该异步处理 ConvertDocumentToMarkdown::dispatch($document); // 而不是同步执行 // $service->convertToMarkdown($document); // 避免 ``` --- **最后更新**:2025-12-05