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

853 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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