# 设计文档 ## 概述 知识库系统是一个基于 Laravel Filament 3.X 构建的文档管理平台。系统采用 Laravel 的 MVC 架构模式,利用 Filament 的管理面板功能提供直观的中文用户界面。核心功能包括文档上传、分类管理、基于分组的权限控制、文档格式转换和全文搜索。 系统使用 Laravel 的 Eloquent ORM 进行数据库操作,利用 Filament 的资源(Resources)和表单构建器创建管理界面,通过策略(Policies)和作用域(Scopes)实现细粒度的权限控制。文档上传后自动转换为 Markdown 格式,并通过 Meilisearch 搜索引擎实现快速的全文搜索功能。用户可以在线预览 Markdown 渲染内容,也可以下载原始 Word 文档。 ## 架构 ### 技术栈 - **后端框架**: Laravel 10.x/11.x - **管理面板**: Filament 3.X - **数据库**: MySQL 8.0+ / PostgreSQL 13+ - **文件存储**: Laravel Storage (支持本地和云存储) - **认证**: Laravel Breeze/Jetstream + Filament Auth - **搜索引擎**: Meilisearch 1.5+ - **文档转换**: Pandoc 或 PHPWord - **Markdown 渲染**: CommonMark PHP 或 Laravel Markdown - **队列系统**: Redis Queue (用于异步文档转换) ### 架构模式 系统采用分层架构: ``` ┌─────────────────────────────────────┐ │ Filament UI Layer (视图层) │ │ - Resources │ │ - Forms & Tables │ │ - Actions & Filters │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ Application Layer (应用层) │ │ - Controllers │ │ - Policies │ │ - Form Requests │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ Domain Layer (领域层) │ │ - Models │ │ - Services │ │ - Repositories (可选) │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ Infrastructure Layer (基础设施层) │ │ - Database │ │ - File Storage │ │ - Cache │ └─────────────────────────────────────┘ ``` ## 组件和接口 ### 核心模型 #### User (用户模型) ```php class User extends Authenticatable { // 关联关系 public function groups(): BelongsToMany; public function uploadedDocuments(): HasMany; public function downloadLogs(): HasMany; } ``` #### Group (分组模型) ```php class Group extends Model { // 关联关系 public function users(): BelongsToMany; public function documents(): HasMany; } ``` #### Document (文档模型) ```php class Document extends Model { use Searchable; // Laravel Scout trait for Meilisearch // 属性 protected $fillable = [ 'title', 'file_path', 'file_name', 'file_size', 'mime_type', 'type', // 'global' 或 'dedicated' 'group_id', 'uploaded_by', 'description', 'markdown_path', // Markdown 文件路径 'markdown_preview', // Markdown 内容摘要 'conversion_status' // 'pending', 'processing', 'completed', 'failed' ]; // 关联关系 public function group(): BelongsTo; public function uploader(): BelongsTo; public function downloadLogs(): HasMany; // 作用域 public function scopeAccessibleBy(Builder $query, User $user): Builder; public function scopeGlobal(Builder $query): Builder; public function scopeDedicated(Builder $query): Builder; // Meilisearch 配置 public function toSearchableArray(): array; public function shouldBeSearchable(): bool; // 辅助方法 public function getMarkdownContent(): ?string; // 从文件读取完整 Markdown 内容 public function hasMarkdown(): bool; // 检查是否已转换 } ``` #### DownloadLog (下载日志模型) ```php class DownloadLog extends Model { protected $fillable = [ 'document_id', 'user_id', 'downloaded_at', 'ip_address' ]; public function document(): BelongsTo; public function user(): BelongsTo; } ``` ### Filament 资源 #### DocumentResource 负责文档的 CRUD 操作界面: - 列表页:显示文档列表,支持搜索和筛选 - 创建页:上传文档并设置分类 - 编辑页:修改文档信息和分类 - 查看页:查看文档详情和下载 #### GroupResource 负责分组管理界面: - 列表页:显示所有分组 - 创建页:创建新分组 - 编辑页:编辑分组信息和成员 - 关系管理器:管理分组成员和文档 #### UserResource 负责用户管理界面: - 列表页:显示所有用户 - 编辑页:编辑用户信息和分组归属 - 关系管理器:管理用户的分组关系 ### 服务类 #### DocumentService ```php class DocumentService { public function uploadDocument( UploadedFile $file, string $title, string $type, ?int $groupId, int $uploaderId ): Document; public function validateDocumentAccess(Document $document, User $user): bool; public function downloadDocument(Document $document, User $user): StreamedResponse; public function logDownload(Document $document, User $user): void; } ``` #### DocumentConversionService ```php class DocumentConversionService { public function convertToMarkdown(Document $document): string; public function queueConversion(Document $document): void; public function saveMarkdownToFile(Document $document, string $markdown): string; // 返回文件路径 public function updateDocumentMarkdown(Document $document, string $markdownPath): void; public function handleConversionFailure(Document $document, Exception $e): void; public function getMarkdownPreview(string $markdown, int $length = 500): string; } ``` #### DocumentSearchService ```php class DocumentSearchService { public function search(string $query, User $user, array $filters = []): Collection; public function indexDocument(Document $document): void; // 读取 Markdown 文件并索引 public function updateDocumentIndex(Document $document): void; public function removeDocumentFromIndex(Document $document): void; public function filterByUserPermissions(Collection $results, User $user): Collection; public function prepareSearchableData(Document $document): array; // 准备索引数据,包含完整 Markdown 内容 } ``` #### MarkdownRenderService ```php class MarkdownRenderService { public function render(string $markdown): string; public function sanitize(string $html): string; public function extractPreview(string $markdown, int $length = 200): string; } ``` ### 策略类 #### DocumentPolicy ```php class DocumentPolicy { public function viewAny(User $user): bool; public function view(User $user, Document $document): bool; public function create(User $user): bool; public function update(User $user, Document $document): bool; public function delete(User $user, Document $document): bool; public function download(User $user, Document $document): bool; } ``` ## 数据模型 ### 数据库表结构 #### users 表 ```sql - id: bigint (主键) - name: varchar(255) - email: varchar(255) (唯一) - password: varchar(255) - created_at: timestamp - updated_at: timestamp ``` #### groups 表 ```sql - id: bigint (主键) - name: varchar(255) - description: text (可空) - created_at: timestamp - updated_at: timestamp ``` #### group_user 表 (多对多中间表) ```sql - id: bigint (主键) - group_id: bigint (外键) - user_id: bigint (外键) - created_at: timestamp - updated_at: timestamp - 唯一索引: (group_id, user_id) ``` #### documents 表 ```sql - id: bigint (主键) - title: varchar(255) - description: text (可空) - file_path: varchar(500) -- 原始 Word 文档路径 - file_name: varchar(255) - file_size: bigint - mime_type: varchar(100) - type: enum('global', 'dedicated') - group_id: bigint (外键, 可空) - uploaded_by: bigint (外键) - markdown_path: varchar(500) (可空) -- Markdown 文件存储路径 - markdown_preview: text (可空) -- Markdown 内容摘要(前 500 字符),用于快速预览 - conversion_status: enum('pending', 'processing', 'completed', 'failed') -- 转换状态 - conversion_error: text (可空) -- 转换失败时的错误信息 - created_at: timestamp - updated_at: timestamp - 索引: type, group_id, uploaded_by, conversion_status ``` **存储策略说明**: - 原始 Word 文档存储在 `storage/app/private/documents/` 目录 - 转换后的 Markdown 文件存储在 `storage/app/private/markdown/` 目录 - 数据库中只保存文件路径和内容摘要 - Meilisearch 索引包含完整的 Markdown 内容用于搜索 - 这种设计减少数据库体积,同时保持搜索性能 #### download_logs 表 ```sql - id: bigint (主键) - document_id: bigint (外键) - user_id: bigint (外键) - ip_address: varchar(45) - downloaded_at: timestamp - 索引: document_id, user_id, downloaded_at ``` ### 实体关系图 ```mermaid erDiagram User ||--o{ Document : uploads User }o--o{ Group : belongs_to Group ||--o{ Document : owns Document ||--o{ DownloadLog : has User ||--o{ DownloadLog : creates User { bigint id PK string name string email UK string password timestamp created_at timestamp updated_at } Group { bigint id PK string name text description timestamp created_at timestamp updated_at } Document { bigint id PK string title text description string file_path string file_name bigint file_size string mime_type enum type bigint group_id FK bigint uploaded_by FK timestamp created_at timestamp updated_at } DownloadLog { bigint id PK bigint document_id FK bigint user_id FK string ip_address timestamp downloaded_at } ``` ## 正确性属性 *属性是指在系统的所有有效执行中都应该成立的特征或行为——本质上是关于系统应该做什么的正式声明。属性作为人类可读规范和机器可验证正确性保证之间的桥梁。* 基于需求文档中的验收标准,我们识别出以下可测试的正确性属性: ### 属性 1:文件格式验证 *对于任何*上传的文件,系统应该正确识别其是否为有效的 Word 文档格式(.doc 或 .docx),并且只接受有效格式的文件 **验证需求:1.1, 1.4** ### 属性 2:文档存储完整性 *对于任何*成功上传的文档,系统应该将文件存储到指定位置,并在数据库中记录完整的元数据(标题、文件路径、文件大小、MIME 类型、上传者等) **验证需求:1.2** ### 属性 3:上传事务一致性 *对于任何*文档上传操作,如果过程中发生错误,系统应该回滚所有更改,确保数据库记录和文件系统保持一致状态 **验证需求:1.5** ### 属性 4:专用文档必须关联分组 *对于任何*类型为"专用知识库"的文档,系统应该要求并验证其关联了有效的分组 ID **验证需求:2.2** ### 属性 5:全局文档对所有用户可见 *对于任何*全局知识库文档和任何已认证用户,该用户应该能够查看和访问该文档 **验证需求:2.3** ### 属性 6:文档分类持久化 *对于任何*文档,当其分类信息(类型和分组)被保存后,从数据库查询应该返回相同的分类信息 **验证需求:2.4** ### 属性 7:用户文档列表权限过滤 *对于任何*用户,当查询其可访问的文档列表时,返回的结果应该只包含:(1) 所有全局知识库文档,以及 (2) 该用户所属分组的专用知识库文档 **验证需求:3.1, 3.2, 3.3** ### 属性 8:其他分组文档隔离 *对于任何*用户和任何不属于该用户分组的专用知识库文档,该文档不应该出现在用户的可访问文档列表中 **验证需求:3.4** ### 属性 9:有权限文档可下载 *对于任何*用户和该用户有权限访问的文档,系统应该允许用户成功下载该文档 **验证需求:4.1** ### 属性 10:无权限文档访问拒绝 *对于任何*用户和该用户无权限访问的文档(其他分组的专用文档),系统应该拒绝访问请求 **验证需求:4.2** ### 属性 11:下载日志记录 *对于任何*文档下载操作,系统应该在 download_logs 表中创建一条记录,包含文档 ID、用户 ID、下载时间和 IP 地址 **验证需求:4.3** ### 属性 12:文档下载往返一致性 *对于任何*上传的文档,下载后的文件内容应该与原始上传的文件内容完全一致 **验证需求:4.4** ### 属性 13:分组数据持久化 *对于任何*新创建的分组,系统应该在数据库中保存分组名称和描述信息,并且查询应该返回相同的数据 **验证需求:5.1** ### 属性 14:用户分组关联 *对于任何*用户和分组,当用户被分配到该分组时,系统应该在 group_user 表中创建关联记录 **验证需求:5.2** ### 属性 15:分组分配授予权限 *对于任何*用户,当该用户被分配到某个分组后,该用户应该能够访问该分组的所有专用知识库文档 **验证需求:5.3** ### 属性 16:分组移除撤销权限 *对于任何*用户,当该用户从某个分组中移除后,该用户应该无法再访问该分组的专用知识库文档 **验证需求:5.4** ### 属性 17:分组删除级联处理 *对于任何*分组,当该分组被删除时,系统应该正确处理该分组的专用文档(设置为孤立状态或重新分配) **验证需求:5.5** ### 属性 18:关键词搜索匹配 *对于任何*搜索关键词,系统返回的文档列表应该只包含标题或描述中包含该关键词的文档 **验证需求:6.1** ### 属性 19:分类筛选准确性 *对于任何*选定的文档分类(全局或专用),系统返回的文档列表应该只包含该分类的文档 **验证需求:6.2** ### 属性 20:分组筛选准确性 *对于任何*选定的分组,系统返回的文档列表应该只包含属于该分组的专用文档 **验证需求:6.3** ### 属性 21:组合筛选交集 *对于任何*多个筛选条件的组合(如分类 + 分组 + 关键词),系统返回的文档应该同时满足所有筛选条件 **验证需求:6.5** ### 属性 22:权限检查验证用户和分组 *对于任何*文档访问请求,系统应该验证请求用户的身份和该用户的分组归属 **验证需求:7.1** ### 属性 23:数据返回前权限验证 *对于任何*文档查询操作,系统应该在返回数据前执行权限检查,确保用户有权访问 **验证需求:7.2** ### 属性 24:未授权访问日志记录 *对于任何*权限验证失败的访问尝试,系统应该在安全日志中记录该事件 **验证需求:7.3** ### 属性 25:查询结果数据隔离 *对于任何*用户的文档查询,返回的结果集应该只包含该用户有权访问的文档,不包含其他用户的私有数据 **验证需求:7.4** ### 属性 26:错误消息中文化 *对于任何*系统操作产生的错误或成功消息,消息文本应该使用简体中文 **验证需求:8.3** ### 属性 27:日期格式中文化 *对于任何*需要显示的日期时间值,系统应该使用中文日期格式进行格式化 **验证需求:8.5** ### 属性 28:文档上传触发转换 *对于任何*成功上传的 Word 文档,系统应该自动触发 Markdown 转换流程 **验证需求:9.1** ### 属性 29:Markdown 内容持久化 *对于任何*转换完成的文档,系统应该将 Markdown 内容正确存储到文件系统,并在数据库中记录文件路径和内容摘要,读取文件应该返回相同的内容 **验证需求:9.2** ### 属性 30:转换失败不影响文档可用性 *对于任何*转换失败的文档,系统应该记录错误信息,但文档记录应该保持有效状态,用户仍然可以下载原始文件 **验证需求:9.3** ### 属性 31:原始文档保留 *对于任何*转换成功的文档,原始 Word 文件应该仍然存在于文件系统中,并且可以被下载 **验证需求:9.4** ### 属性 32:文档更新重新转换 *对于任何*已存在的文档,当其被更新或重新上传时,系统应该重新执行转换流程,并且新的 Markdown 内容应该替换旧内容 **验证需求:9.5** ### 属性 33:搜索匹配 Markdown 内容 *对于任何*包含特定关键词的文档 Markdown 内容,使用该关键词搜索应该返回该文档(假设用户有权限) **验证需求:10.1** ### 属性 34:搜索覆盖多个字段 *对于任何*关键词,如果该关键词出现在文档的标题、描述或 Markdown 内容中的任何一个字段,搜索应该返回该文档 **验证需求:10.2** ### 属性 35:搜索结果权限过滤 *对于任何*搜索查询和用户,返回的搜索结果应该只包含该用户有权限访问的文档(全局文档和用户分组的专用文档) **验证需求:10.3** ### 属性 36:搜索结果包含必需信息 *对于任何*搜索结果项,应该包含文档标题、内容片段、文档类型和上传时间等信息 **验证需求:10.4** ### 属性 37:有权限文档可预览 *对于任何*用户有权限访问的文档,该用户应该能够获取该文档的 Markdown 渲染后的 HTML 内容 **验证需求:11.1** ### 属性 38:Markdown 渲染正确性 *对于任何*包含标准 Markdown 元素(标题、列表、表格等)的 Markdown 内容,渲染后的 HTML 应该正确表示这些元素 **验证需求:11.2** ### 属性 39:无权限文档预览拒绝 *对于任何*用户无权限访问的文档,该用户尝试预览时应该被拒绝访问 **验证需求:11.3** ### 属性 40:转换完成触发索引 *对于任何*Markdown 转换完成的文档,系统应该自动将该文档索引到 Meilisearch **验证需求:12.1** ### 属性 41:索引数据完整性 *对于任何*索引到 Meilisearch 的文档,索引数据应该包含文档 ID、标题、描述、Markdown 内容、类型和分组 ID **验证需求:12.2** ### 属性 42:文档更新同步索引 *对于任何*已索引的文档,当文档被更新时,Meilisearch 中的索引数据应该同步更新 **验证需求:12.3** ### 属性 43:文档删除移除索引 *对于任何*已索引的文档,当文档被删除时,该文档应该从 Meilisearch 索引中移除 **验证需求:12.4** ### 属性 44:索引失败不影响文档保存 *对于任何*文档,即使 Meilisearch 索引操作失败,文档仍应该成功保存到数据库,并且可以正常使用 **验证需求:12.5** ## 错误处理 ### 文件上传错误 - **无效文件格式**: 返回 422 状态码,提示"文件格式不支持,请上传 Word 文档(.doc 或 .docx)" - **文件过大**: 返回 413 状态码,提示"文件大小超过限制(最大 XX MB)" - **存储失败**: 返回 500 状态码,提示"文件上传失败,请稍后重试",并回滚数据库操作 ### 权限错误 - **未认证访问**: 返回 401 状态码,重定向到登录页面 - **无权限访问**: 返回 403 状态码,提示"您没有权限访问此文档" - **文档不存在**: 返回 404 状态码,提示"文档不存在或已被删除" ### 数据验证错误 - **必填字段缺失**: 返回 422 状态码,提示具体缺失的字段 - **专用文档未指定分组**: 返回 422 状态码,提示"专用知识库文档必须指定所属分组" - **无效的分组 ID**: 返回 422 状态码,提示"指定的分组不存在" ### 数据库错误 - **外键约束失败**: 返回 500 状态码,记录错误日志,提示"操作失败,请联系管理员" - **唯一约束冲突**: 返回 422 状态码,提示"该记录已存在" - **连接超时**: 返回 503 状态码,提示"服务暂时不可用,请稍后重试" ### 文档转换错误 - **转换工具不可用**: 返回 500 状态码,记录错误日志,文档状态设置为 'failed' - **Word 文档损坏**: 返回 422 状态码,提示"文档文件损坏,无法转换" - **转换超时**: 记录错误日志,文档状态设置为 'failed',提示"文档转换超时,请稍后重试" - **Markdown 内容过大**: 记录警告日志,截断内容或使用外部存储 ### 搜索相关错误 - **Meilisearch 服务不可用**: 返回 503 状态码,提示"搜索服务暂时不可用,请稍后重试" - **搜索查询语法错误**: 返回 400 状态码,提示"搜索关键词格式不正确" - **索引操作失败**: 记录错误日志,不影响文档的正常保存和使用 - **搜索超时**: 返回 504 状态码,提示"搜索请求超时,请简化搜索条件" ### 预览相关错误 - **Markdown 内容为空**: 返回 200 状态码,显示提示信息和下载按钮 - **Markdown 渲染失败**: 返回 500 状态码,提示"内容渲染失败,请下载原始文档" - **预览权限不足**: 返回 403 状态码,提示"您没有权限预览此文档" ### 错误处理策略 1. **用户友好的错误消息**: 所有错误消息使用简体中文,避免技术术语 2. **详细的日志记录**: 在服务器端记录完整的错误堆栈和上下文信息 3. **事务回滚**: 对于涉及多个操作的流程,确保失败时完全回滚 4. **安全考虑**: 不在错误消息中暴露敏感信息(如数据库结构、文件路径等) 5. **优雅降级**: 转换或搜索功能失败时,不影响文档的基本上传和下载功能 6. **异步重试**: 对于转换和索引失败的文档,支持后台重试机制 ## 测试策略 ### 单元测试 使用 PHPUnit 进行单元测试,覆盖以下组件: 1. **模型测试** - 测试模型关联关系是否正确定义 - 测试作用域(Scopes)逻辑 - 测试模型验证规则 2. **服务类测试** - 测试 DocumentService 的文档上传逻辑 - 测试权限验证逻辑 - 测试文件存储和检索 3. **策略测试** - 测试 DocumentPolicy 的各种权限判断 - 测试边缘情况(如文档无分组、用户无分组等) 4. **表单请求测试** - 测试验证规则是否正确 - 测试自定义验证逻辑 ### 属性基础测试(Property-Based Testing) 使用 **Pest PHP** 的属性测试功能进行属性基础测试。每个属性测试应该: - 配置为运行至少 **100 次迭代** - 使用注释明确标注对应的设计文档中的正确性属性 - 标注格式:`// Feature: knowledge-base-system, Property X: [属性描述]` - 每个正确性属性对应一个独立的属性测试 属性测试覆盖范围: 1. **文件格式验证属性**(属性 1) 2. **文档存储完整性属性**(属性 2) 3. **事务一致性属性**(属性 3) 4. **权限过滤属性**(属性 7, 8) 5. **往返一致性属性**(属性 12) 6. **权限授予和撤销属性**(属性 15, 16) 7. **搜索和筛选属性**(属性 18-21) 8. **数据隔离属性**(属性 25) 9. **文档转换属性**(属性 28-32) 10. **全文搜索属性**(属性 33-36) 11. **Markdown 预览属性**(属性 37-39) 12. **Meilisearch 索引属性**(属性 40-44) ### 功能测试 使用 Laravel 的功能测试框架测试完整的用户流程: 1. **文档上传流程** - 管理员上传全局文档 - 管理员上传专用文档并指定分组 - 上传无效格式文件被拒绝 2. **权限控制流程** - 用户查看自己分组的专用文档 - 用户无法查看其他分组的专用文档 - 用户可以查看所有全局文档 3. **分组管理流程** - 创建分组并分配用户 - 用户从分组移除后失去权限 - 删除分组后文档状态正确处理 4. **搜索和筛选流程** - 按关键词搜索文档 - 按分类筛选文档 - 组合多个筛选条件 5. **文档转换流程** - 上传 Word 文档后自动触发转换 - 转换完成后 Markdown 内容正确存储 - 转换失败时文档仍然可用 - 更新文档时重新转换 6. **全文搜索流程** - 搜索 Markdown 内容中的关键词 - 搜索结果遵循权限控制 - 搜索结果包含内容片段 - 空搜索关键词的处理 7. **文档预览流程** - 有权限用户可以预览 Markdown 内容 - 无权限用户无法预览 - Markdown 正确渲染为 HTML - 预览页面提供下载按钮 8. **Meilisearch 索引流程** - 文档转换后自动索引 - 文档更新时同步索引 - 文档删除时移除索引 - 索引失败不影响文档保存 ### 测试数据生成 使用 Laravel Factories 生成测试数据: ```php // UserFactory - 生成随机用户 // GroupFactory - 生成随机分组 // DocumentFactory - 生成随机文档(包括全局和专用) // 使用 Faker 生成随机的中文文本 ``` ### 测试覆盖率目标 - 代码覆盖率:≥ 80% - 关键业务逻辑覆盖率:≥ 95% - 所有正确性属性都有对应的属性测试 ### 持续集成 - 在 CI/CD 管道中自动运行所有测试 - 测试失败时阻止代码合并 - 生成测试覆盖率报告 ## 性能考虑 ### 数据库优化 1. **索引策略** - documents 表:在 type, group_id, uploaded_by 字段上创建索引 - download_logs 表:在 document_id, user_id, downloaded_at 上创建索引 - 在 group_user 表的 (group_id, user_id) 上创建唯一复合索引 2. **查询优化** - 使用 Eager Loading 避免 N+1 查询问题 - 对文档列表查询使用分页 - 使用数据库视图或查询作用域简化复杂权限查询 3. **缓存策略** - 缓存用户的分组信息(TTL: 1小时) - 缓存文档元数据(TTL: 30分钟) - 使用 Redis 存储会话和缓存数据 ### 文件存储优化 1. **存储策略** - 使用 Laravel Storage 抽象层,支持本地和云存储切换 - 按日期组织文件目录结构:`documents/YYYY/MM/DD/` 和 `markdown/YYYY/MM/DD/` - 使用 UUID 作为文件名避免冲突 - Markdown 文件单独存储,便于管理和备份 - 数据库中保存 Markdown 摘要(500 字符)用于快速预览 2. **大文件处理** - 配置合理的上传大小限制(建议 50MB) - 使用流式下载避免内存溢出 - 使用队列异步处理文档转换 - 大 Markdown 文件(> 1MB)分块读取和索引 3. **缓存策略** - 缓存已读取的 Markdown 内容(TTL: 1小时) - 缓存渲染后的 HTML(TTL: 30分钟) - 使用 Redis 存储热门文档的 Markdown 内容 ### 并发控制 - 使用数据库事务确保数据一致性 - 对关键操作使用乐观锁或悲观锁 - 使用队列处理耗时操作(如文件处理、日志记录) ## 安全考虑 ### 认证和授权 1. **认证机制** - 使用 Laravel Sanctum 或 Jetstream 提供认证 - 实施会话超时机制 - 支持记住我功能 2. **授权机制** - 使用 Laravel Policy 实现细粒度权限控制 - 在控制器和 Filament 资源中强制执行策略 - 使用 Gate 定义全局权限规则 ### 数据安全 1. **文件安全** - 文件存储在非公开目录 - 通过控制器验证权限后提供文件下载 - 验证文件 MIME 类型,防止恶意文件上传 2. **SQL 注入防护** - 使用 Eloquent ORM 和参数化查询 - 避免使用原始 SQL 查询 - 对用户输入进行验证和清理 3. **XSS 防护** - 使用 Blade 模板引擎自动转义输出 - 对富文本内容使用 HTML Purifier - 设置适当的 Content Security Policy ### 审计日志 1. **操作日志** - 记录所有文档的创建、修改、删除操作 - 记录用户的登录、登出活动 - 记录权限变更操作 2. **安全日志** - 记录所有权限验证失败的尝试 - 记录异常的访问模式 - 定期审查安全日志 ## 部署和配置 ### 环境要求 - PHP 8.1+ - MySQL 8.0+ 或 PostgreSQL 13+ - Redis 6.0+(用于缓存和队列) - Composer 2.x - Node.js 18+ 和 npm(用于前端资源编译) - Meilisearch 1.5+(搜索引擎服务) - Pandoc 2.x+(用于文档转换,可选)或 PHPWord 库 ### Filament 配置 1. **中文化配置** ```php // config/app.php 'locale' => 'zh_CN', 'fallback_locale' => 'zh_CN', // 安装 Filament 中文语言包 composer require filament/filament-zh-cn ``` 2. **面板配置** ```php // app/Providers/Filament/AdminPanelProvider.php public function panel(Panel $panel): Panel { return $panel ->default() ->id('admin') ->path('admin') ->login() ->colors([ 'primary' => Color::Blue, ]) ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources') ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages') ->pages([]) ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets') ->widgets([]) ->middleware([ EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, AuthenticateSession::class, ShareErrorsFromSession::class, VerifyCsrfToken::class, SubstituteBindings::class, DisableBladeIconComponents::class, DispatchServingFilamentEvent::class, ]) ->authMiddleware([ Authenticate::class, ]); } ``` ### 文件存储配置 ```php // config/filesystems.php 'disks' => [ 'documents' => [ 'driver' => 'local', 'root' => storage_path('app/private/documents'), 'visibility' => 'private', ], 'markdown' => [ 'driver' => 'local', 'root' => storage_path('app/private/markdown'), 'visibility' => 'private', ], ], ``` **文件组织结构**: ``` storage/app/private/ ├── documents/ # 原始 Word 文档 │ └── YYYY/MM/DD/ │ └── {uuid}.docx └── markdown/ # 转换后的 Markdown 文件 └── YYYY/MM/DD/ └── {uuid}.md ``` ### 队列配置 ```php // .env QUEUE_CONNECTION=redis // 启动队列工作进程 php artisan queue:work --queue=default,documents ``` ### Meilisearch 配置 ```php // .env MEILISEARCH_HOST=http://127.0.0.1:7700 MEILISEARCH_KEY=your-master-key SCOUT_DRIVER=meilisearch // 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'], ], ], ], // 启动 Meilisearch 服务 meilisearch --master-key="your-master-key" ``` ### 文档转换配置 ```php // .env DOCUMENT_CONVERSION_DRIVER=pandoc // 或 phpword PANDOC_PATH=/usr/local/bin/pandoc CONVERSION_TIMEOUT=300 // 秒 // 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', // 或 'parsedown' 'sanitize' => true, ], ]; ``` ## 扩展性考虑 ### 未来功能扩展 1. **文档版本控制** - 支持文档多版本管理 - 版本比较和回滚功能 - 保留每个版本的 Markdown 内容 2. **增强的文档转换** - 支持更多文档格式(PDF、Excel、PPT) - 保留文档中的图片和格式 - 支持 OCR 识别扫描文档 3. **高级搜索功能** - 支持搜索语法(AND、OR、NOT) - 按日期范围筛选 - 搜索结果高亮显示 - 搜索建议和自动补全 4. **文档标签系统** - 为文档添加多个标签 - 按标签筛选和搜索 - 标签云展示 5. **文档评论和协作** - 用户可以对文档添加评论 - 支持文档收藏和分享 - 文档阅读统计 6. **AI 增强功能** - 文档内容摘要生成 - 智能问答(基于文档内容) - 相关文档推荐 ### 架构扩展性 1. **微服务化** - 文档存储服务可以独立部署 - 使用消息队列解耦服务 2. **多租户支持** - 支持多个组织独立使用系统 - 数据完全隔离 3. **API 接口** - 提供 RESTful API - 支持第三方系统集成 ## 总结 本设计文档详细描述了基于 Laravel Filament 3.X 的知识库系统的架构、组件、数据模型和正确性属性。系统采用分层架构,通过 Eloquent ORM 和 Filament 资源实现数据管理,使用策略和作用域实现细粒度的权限控制。系统集成了文档格式转换和 Meilisearch 全文搜索功能,提供强大的文档管理和检索能力。 关键设计决策包括: 1. 使用枚举类型区分全局和专用知识库 2. 通过多对多关系管理用户和分组 3. 使用查询作用域实现权限过滤 4. 采用属性基础测试验证核心业务逻辑 5. 全面的中文化支持 6. 异步队列处理文档转换,避免阻塞用户操作 7. 使用 Meilisearch 实现快速全文搜索,搜索结果遵循权限控制 8. Markdown 格式存储支持在线预览和内容检索 9. 优雅降级策略,转换或搜索失败不影响核心功能 系统设计充分考虑了安全性、性能和可扩展性,为后续的实施提供了清晰的指导。文档转换和搜索功能的引入大大提升了知识库的可用性和检索效率。