feat: 初始化知识库系统项目

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

技术栈:
- Laravel 11.x
- Filament 3.X
- Meilisearch 1.5+
- Pandoc 文档转换
- Redis 队列系统
- Pest PHP 测试框架
This commit is contained in:
Knowledge Base System
2025-12-05 14:44:44 +08:00
commit acf549c43c
165 changed files with 32838 additions and 0 deletions

852
docs/API_REFERENCE.md Normal file
View File

@@ -0,0 +1,852 @@
# 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

816
docs/DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,816 @@
# 知识库系统部署指南
本文档提供知识库系统的详细部署步骤和配置说明。
## 目录
1. [服务器要求](#服务器要求)
2. [安装依赖](#安装依赖)
3. [项目部署](#项目部署)
4. [配置服务](#配置服务)
5. [性能优化](#性能优化)
6. [监控和维护](#监控和维护)
7. [故障排除](#故障排除)
## 服务器要求
### 最低配置
- **CPU**: 2 核心
- **内存**: 4GB RAM
- **存储**: 50GB SSD
- **操作系统**: Ubuntu 20.04+ / CentOS 8+ / Debian 11+
### 推荐配置
- **CPU**: 4 核心
- **内存**: 8GB RAM
- **存储**: 100GB SSD
- **操作系统**: Ubuntu 22.04 LTS
### 软件要求
- PHP 8.1 或更高版本
- MySQL 8.0+ 或 PostgreSQL 13+
- Redis 6.0+
- Nginx 1.18+ 或 Apache 2.4+
- Composer 2.x
- Node.js 18+ 和 npm
- Meilisearch 1.5+
- Pandoc 2.x+(用于文档转换)
- Supervisor用于管理队列进程
## 安装依赖
### 1. 安装 PHP 和扩展
#### Ubuntu/Debian
```bash
sudo apt update
sudo apt install -y php8.1 php8.1-fpm php8.1-cli php8.1-common \
php8.1-mysql php8.1-pgsql php8.1-redis php8.1-xml php8.1-mbstring \
php8.1-curl php8.1-zip php8.1-gd php8.1-intl php8.1-bcmath
```
#### CentOS/RHEL
```bash
sudo dnf install -y php php-fpm php-cli php-common php-mysqlnd \
php-pgsql php-redis php-xml php-mbstring php-curl php-zip \
php-gd php-intl php-bcmath
```
### 2. 安装 Composer
```bash
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer
```
### 3. 安装 Node.js 和 npm
```bash
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
```
### 4. 安装 MySQL
```bash
sudo apt install -y mysql-server
sudo mysql_secure_installation
```
创建数据库和用户:
```sql
CREATE DATABASE knowledge_base CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'kb_user'@'localhost' IDENTIFIED BY 'your_secure_password';
GRANT ALL PRIVILEGES ON knowledge_base.* TO 'kb_user'@'localhost';
FLUSH PRIVILEGES;
```
### 5. 安装 Redis
```bash
sudo apt install -y redis-server
sudo systemctl enable redis-server
sudo systemctl start redis-server
```
### 6. 安装 Meilisearch
#### 使用 Docker推荐
```bash
docker run -d \
--name meilisearch \
-p 7700:7700 \
-v $(pwd)/storage/meilisearch:/meili_data \
-e MEILI_MASTER_KEY='your_master_key_here' \
getmeili/meilisearch:v1.5
```
#### 直接安装
```bash
curl -L https://install.meilisearch.com | sh
sudo mv ./meilisearch /usr/local/bin/
```
创建 systemd 服务:
```bash
sudo nano /etc/systemd/system/meilisearch.service
```
内容:
```ini
[Unit]
Description=Meilisearch
After=network.target
[Service]
Type=simple
User=www-data
ExecStart=/usr/local/bin/meilisearch --master-key="your_master_key_here" --db-path=/var/lib/meilisearch/data
Restart=on-failure
[Install]
WantedBy=multi-user.target
```
启动服务:
```bash
sudo systemctl daemon-reload
sudo systemctl enable meilisearch
sudo systemctl start meilisearch
```
### 7. 安装 Pandoc
```bash
sudo apt install -y pandoc
```
或下载最新版本:
```bash
wget https://github.com/jgm/pandoc/releases/download/3.1.11/pandoc-3.1.11-1-amd64.deb
sudo dpkg -i pandoc-3.1.11-1-amd64.deb
```
### 8. 安装 Supervisor
```bash
sudo apt install -y supervisor
sudo systemctl enable supervisor
sudo systemctl start supervisor
```
## 项目部署
### 1. 克隆项目
```bash
cd /var/www
sudo git clone <repository-url> knowledge-base
cd knowledge-base
```
### 2. 设置权限
```bash
sudo chown -R www-data:www-data /var/www/knowledge-base
sudo chmod -R 755 /var/www/knowledge-base
sudo chmod -R 775 /var/www/knowledge-base/storage
sudo chmod -R 775 /var/www/knowledge-base/bootstrap/cache
```
### 3. 安装依赖
```bash
# PHP 依赖
composer install --no-dev --optimize-autoloader
# 前端依赖
npm install
npm run build
```
### 4. 配置环境变量
```bash
cp .env.example .env
nano .env
```
配置以下关键参数:
```env
APP_NAME="知识库系统"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=knowledge_base
DB_USERNAME=kb_user
DB_PASSWORD=your_secure_password
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=your_master_key_here
SCOUT_DRIVER=meilisearch
DOCUMENT_CONVERSION_DRIVER=pandoc
PANDOC_PATH=/usr/bin/pandoc
CONVERSION_TIMEOUT=300
FILESYSTEM_DISK=local
```
### 5. 生成应用密钥
```bash
php artisan key:generate
```
### 6. 运行数据库迁移
```bash
php artisan migrate --force
```
### 7. 创建存储目录
```bash
mkdir -p storage/app/private/documents
mkdir -p storage/app/private/markdown
sudo chown -R www-data:www-data storage/app/private
sudo chmod -R 775 storage/app/private
```
### 8. 优化应用
```bash
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan filament:optimize
```
### 9. 创建管理员用户
```bash
php artisan make:filament-user
```
## 配置服务
### 1. 配置 Nginx
创建站点配置:
```bash
sudo nano /etc/nginx/sites-available/knowledge-base
```
内容:
```nginx
server {
listen 80;
listen [::]:80;
server_name your-domain.com;
root /var/www/knowledge-base/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
# 文件上传大小限制
client_max_body_size 50M;
}
```
启用站点:
```bash
sudo ln -s /etc/nginx/sites-available/knowledge-base /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
### 2. 配置 SSL使用 Let's Encrypt
```bash
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
```
### 3. 配置队列工作进程
创建 Supervisor 配置:
```bash
sudo nano /etc/supervisor/conf.d/knowledge-base-worker.conf
```
内容:
```ini
[program:knowledge-base-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/knowledge-base/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/knowledge-base/storage/logs/worker.log
stopwaitsecs=3600
```
重新加载 Supervisor
```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start knowledge-base-worker:*
```
### 4. 配置定时任务
编辑 crontab
```bash
sudo crontab -e -u www-data
```
添加:
```cron
* * * * * cd /var/www/knowledge-base && php artisan schedule:run >> /dev/null 2>&1
```
## 性能优化
### 1. PHP-FPM 优化
编辑 PHP-FPM 配置:
```bash
sudo nano /etc/php/8.1/fpm/pool.d/www.conf
```
调整参数:
```ini
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
```
重启 PHP-FPM
```bash
sudo systemctl restart php8.1-fpm
```
### 2. Redis 优化
编辑 Redis 配置:
```bash
sudo nano /etc/redis/redis.conf
```
调整参数:
```conf
maxmemory 2gb
maxmemory-policy allkeys-lru
```
重启 Redis
```bash
sudo systemctl restart redis-server
```
### 3. MySQL 优化
编辑 MySQL 配置:
```bash
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
```
添加优化参数:
```ini
[mysqld]
innodb_buffer_pool_size = 2G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
query_cache_size = 0
query_cache_type = 0
```
重启 MySQL
```bash
sudo systemctl restart mysql
```
### 4. Opcache 配置
编辑 PHP 配置:
```bash
sudo nano /etc/php/8.1/fpm/conf.d/10-opcache.ini
```
内容:
```ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
```
## 监控和维护
### 1. 日志监控
查看应用日志:
```bash
tail -f /var/www/knowledge-base/storage/logs/laravel.log
```
查看队列工作进程日志:
```bash
tail -f /var/www/knowledge-base/storage/logs/worker.log
```
查看 Nginx 日志:
```bash
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
```
### 2. 定期备份
#### 数据库备份
创建备份脚本:
```bash
sudo nano /usr/local/bin/backup-kb-db.sh
```
内容:
```bash
#!/bin/bash
BACKUP_DIR="/var/backups/knowledge-base"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
mysqldump -u kb_user -p'your_secure_password' knowledge_base | gzip > $BACKUP_DIR/db_$DATE.sql.gz
# 保留最近 7 天的备份
find $BACKUP_DIR -name "db_*.sql.gz" -mtime +7 -delete
```
设置权限并添加到 crontab
```bash
sudo chmod +x /usr/local/bin/backup-kb-db.sh
sudo crontab -e
```
添加每日备份任务:
```cron
0 2 * * * /usr/local/bin/backup-kb-db.sh
```
#### 文件备份
```bash
sudo nano /usr/local/bin/backup-kb-files.sh
```
内容:
```bash
#!/bin/bash
BACKUP_DIR="/var/backups/knowledge-base"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
tar -czf $BACKUP_DIR/files_$DATE.tar.gz \
/var/www/knowledge-base/storage/app/private/documents \
/var/www/knowledge-base/storage/app/private/markdown
# 保留最近 7 天的备份
find $BACKUP_DIR -name "files_*.tar.gz" -mtime +7 -delete
```
### 3. 系统监控
安装监控工具:
```bash
sudo apt install -y htop iotop nethogs
```
监控系统资源:
```bash
# CPU 和内存
htop
# 磁盘 I/O
iotop
# 网络流量
nethogs
```
### 4. 应用健康检查
创建健康检查脚本:
```bash
sudo nano /usr/local/bin/check-kb-health.sh
```
内容:
```bash
#!/bin/bash
# 检查 Web 服务
if ! curl -f http://localhost > /dev/null 2>&1; then
echo "Web service is down!"
sudo systemctl restart nginx
fi
# 检查队列工作进程
if ! sudo supervisorctl status knowledge-base-worker:* | grep RUNNING > /dev/null; then
echo "Queue worker is down!"
sudo supervisorctl restart knowledge-base-worker:*
fi
# 检查 Meilisearch
if ! curl -f http://localhost:7700/health > /dev/null 2>&1; then
echo "Meilisearch is down!"
sudo systemctl restart meilisearch
fi
```
添加到 crontab每 5 分钟检查一次):
```cron
*/5 * * * * /usr/local/bin/check-kb-health.sh
```
## 故障排除
### 1. 文件上传失败
检查 PHP 配置:
```bash
php -i | grep upload_max_filesize
php -i | grep post_max_size
```
如需调整,编辑 PHP 配置:
```bash
sudo nano /etc/php/8.1/fpm/php.ini
```
修改:
```ini
upload_max_filesize = 50M
post_max_size = 50M
```
重启 PHP-FPM
```bash
sudo systemctl restart php8.1-fpm
```
### 2. 队列任务不执行
检查队列工作进程状态:
```bash
sudo supervisorctl status knowledge-base-worker:*
```
重启工作进程:
```bash
sudo supervisorctl restart knowledge-base-worker:*
```
查看队列日志:
```bash
tail -f /var/www/knowledge-base/storage/logs/worker.log
```
### 3. Meilisearch 连接失败
检查 Meilisearch 状态:
```bash
curl http://localhost:7700/health
```
检查 Meilisearch 日志:
```bash
sudo journalctl -u meilisearch -f
```
重启 Meilisearch
```bash
sudo systemctl restart meilisearch
```
### 4. 文档转换失败
检查 Pandoc 是否安装:
```bash
which pandoc
pandoc --version
```
检查转换日志:
```bash
grep "conversion" /var/www/knowledge-base/storage/logs/laravel.log
```
手动测试转换:
```bash
pandoc test.docx -o test.md
```
### 5. 权限问题
重置存储目录权限:
```bash
cd /var/www/knowledge-base
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
```
### 6. 缓存问题
清除所有缓存:
```bash
cd /var/www/knowledge-base
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
```
重新生成缓存:
```bash
php artisan config:cache
php artisan route:cache
php artisan view:cache
```
## 更新部署
### 1. 拉取最新代码
```bash
cd /var/www/knowledge-base
sudo -u www-data git pull origin main
```
### 2. 更新依赖
```bash
sudo -u www-data composer install --no-dev --optimize-autoloader
sudo -u www-data npm install
sudo -u www-data npm run build
```
### 3. 运行迁移
```bash
php artisan migrate --force
```
### 4. 清除和重建缓存
```bash
php artisan cache:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan filament:optimize
```
### 5. 重启服务
```bash
sudo systemctl reload php8.1-fpm
sudo systemctl reload nginx
sudo supervisorctl restart knowledge-base-worker:*
```
## 安全建议
1. **定期更新系统和软件包**
```bash
sudo apt update && sudo apt upgrade -y
```
2. **配置防火墙**
```bash
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
```
3. **禁用不必要的 PHP 函数**
编辑 `php.ini`,添加:
```ini
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
```
4. **设置强密码策略**
5. **定期审查安全日志**
6. **启用 HTTPS**
7. **限制文件上传类型**
8. **定期备份数据**
## 联系支持
如遇到部署问题,请联系技术支持团队。
---
**最后更新**2025-12-05

View File

@@ -0,0 +1,171 @@
# 文档转换服务使用指南
## 概述
DocumentConversionService 负责将上传的 Word 文档(.doc/.docx转换为 Markdown 格式。转换过程是异步的,通过队列系统处理。
## 功能特性
1. **自动转换**: 文档上传后自动触发转换
2. **异步处理**: 使用队列系统,不阻塞用户操作
3. **错误处理**: 转换失败时记录错误,但不影响文档的基本功能
4. **重试机制**: 转换失败后自动重试(默认 3 次)
5. **预览生成**: 自动生成 Markdown 内容的预览(前 500 字符)
## 使用方法
### 1. 队列转换(推荐)
```php
use App\Services\DocumentConversionService;
use App\Models\Document;
$document = Document::find($id);
$conversionService = app(DocumentConversionService::class);
// 将转换任务加入队列
$conversionService->queueConversion($document);
```
### 2. 同步转换(不推荐用于生产环境)
```php
use App\Services\DocumentConversionService;
use App\Models\Document;
$document = Document::find($id);
$conversionService = app(DocumentConversionService::class);
try {
// 转换文档
$markdown = $conversionService->convertToMarkdown($document);
// 保存 Markdown 文件
$markdownPath = $conversionService->saveMarkdownToFile($document, $markdown);
// 更新文档信息
$conversionService->updateDocumentMarkdown($document, $markdownPath);
} catch (\Exception $e) {
// 处理转换失败
$conversionService->handleConversionFailure($document, $e);
}
```
## 转换状态
文档的 `conversion_status` 字段有以下几种状态:
- `pending`: 等待转换
- `processing`: 正在转换
- `completed`: 转换完成
- `failed`: 转换失败
## 配置
转换相关的配置在 `config/documents.php` 文件中:
```php
'conversion' => [
'driver' => 'pandoc', // 转换驱动
'pandoc_path' => '/usr/local/bin/pandoc', // Pandoc 路径
'timeout' => 300, // 超时时间(秒)
'queue' => 'documents', // 队列名称
'retry_times' => 3, // 重试次数
'retry_delay' => 60, // 重试延迟(秒)
],
```
## 队列工作进程
确保队列工作进程正在运行:
```bash
# 启动队列工作进程
php artisan queue:work --queue=documents
# 或者使用 Supervisor 管理队列进程
```
## 依赖要求
### Pandoc
系统需要安装 Pandoc 才能进行文档转换:
**macOS:**
```bash
brew install pandoc
```
**Ubuntu/Debian:**
```bash
sudo apt-get install pandoc
```
**验证安装:**
```bash
pandoc --version
```
## 错误处理
转换失败时,系统会:
1. 记录详细的错误日志
2. 更新文档的 `conversion_status``failed`
3. 在 `conversion_error` 字段中保存错误信息
4. 保留原始 Word 文档,用户仍可下载
## 监控和调试
### 查看转换日志
```bash
tail -f storage/logs/laravel.log | grep "文档转换"
```
### 查看队列任务
```bash
php artisan queue:failed
```
### 重试失败的任务
```bash
# 重试所有失败的任务
php artisan queue:retry all
# 重试特定任务
php artisan queue:retry {job-id}
```
## 性能优化
1. **使用 Redis 队列**: 比数据库队列更快
2. **增加队列工作进程**: 并行处理多个转换任务
3. **调整超时时间**: 根据文档大小调整 `timeout` 配置
4. **监控队列长度**: 避免队列积压
## 故障排查
### 转换一直处于 processing 状态
- 检查队列工作进程是否运行
- 检查 Pandoc 是否正确安装
- 查看错误日志
### 转换失败
- 检查 Pandoc 路径配置是否正确
- 检查文档文件是否存在
- 检查文档文件是否损坏
- 查看 `conversion_error` 字段的错误信息
### 队列任务不执行
- 确认队列连接配置正确Redis/Database
- 确认队列工作进程正在运行
- 检查队列名称是否匹配

View File

@@ -0,0 +1,146 @@
# 文档预览功能使用指南
## 功能概述
知识库系统现在支持在线预览 Word 文档(.doc 和 .docx 格式)。用户可以在文档查看页面直接查看文档内容,无需下载。
## 技术实现
### 核心组件
1. **DocumentPreviewService** (`app/Services/DocumentPreviewService.php`)
- 负责将 Word 文档转换为 HTML 格式
- 使用 PHPWord 库进行文档解析
- 提供文档预览能力检查
2. **ViewDocument 页面** (`app/Filament/Resources/DocumentResource/Pages/ViewDocument.php`)
- 使用 Filament Infolist 显示文档信息
- 集成文档预览组件
- 提供下载按钮
3. **预览视图** (`resources/views/filament/resources/document/preview.blade.php`)
- 渲染文档预览 HTML
- 处理错误和不支持的格式
- 提供友好的用户界面
### 依赖库
- **phpoffice/phpword**: 用于读取和转换 Word 文档
## 功能特性
### 1. 文档信息展示
在查看页面顶部显示:
- 文档标题
- 文档描述
- 文档类型(全局/专用知识库)
- 所属分组(专用文档)
- 上传者
- 文件名
- 文件大小
- 上传时间
- 更新时间
### 2. 文档预览
- **支持格式**: .doc, .docx
- **预览内容**:
- 文本内容
- 基本格式(标题、段落、列表等)
- 表格
- 图片(如果有)
- **最大高度**: 600px超出部分可滚动查看
### 3. 错误处理
系统会优雅地处理以下情况:
- 文档文件不存在
- 文档格式不支持预览
- 文档转换失败
### 4. 下载功能
- 在页面顶部提供"下载文档"按钮
- 自动记录下载日志
- 验证用户权限
## 使用方法
### 查看文档
1. 登录系统
2. 进入"文档管理"
3. 点击任意文档的"查看"按钮
4. 系统会自动显示文档信息和预览
### 下载文档
1. 在文档查看页面
2. 点击顶部的"下载文档"按钮
3. 文档会自动下载到本地
## API 方法
### DocumentPreviewService
```php
// 检查文档是否可以预览
$canPreview = $previewService->canPreview($document);
// 将文档转换为 HTML
$htmlContent = $previewService->convertToHtml($document);
// 提取文档纯文本(用于搜索等)
$text = $previewService->extractText($document);
```
## 性能考虑
1. **首次加载**: 文档转换可能需要几秒钟,取决于文档大小和复杂度
2. **缓存**: 目前未实现缓存,每次查看都会重新转换
3. **大文件**: 建议对大文件(>10MB提示用户下载查看
## 未来改进
1. **缓存机制**: 缓存转换后的 HTML提高加载速度
2. **更多格式**: 支持 PDF、Excel 等格式
3. **全文搜索**: 利用提取的文本内容实现全文搜索
4. **在线编辑**: 支持在线编辑文档内容
5. **版本控制**: 支持文档版本管理
## 故障排除
### 预览失败
如果预览失败,可能的原因:
1. 文档文件损坏
2. 文档格式不标准
3. PHPWord 库不支持某些特殊格式
解决方法:
- 检查文档是否可以在本地打开
- 尝试重新上传文档
- 使用下载功能获取原始文档
### 格式显示异常
预览版本可能与原始格式略有差异,这是正常现象。如需查看完整格式,请下载文档。
## 安全考虑
1. **权限验证**: 只有有权限的用户才能查看文档
2. **文件隔离**: 文档存储在私有目录,不能直接访问
3. **XSS 防护**: HTML 内容经过清理,防止 XSS 攻击
## 测试
运行测试:
```bash
php artisan test --filter=DocumentPreviewServiceTest
```
测试覆盖:
- 文档格式检查
- 文档不存在时的错误处理
- HTML 转换功能

View File

@@ -0,0 +1,192 @@
# 文档搜索功能指南
## 概述
文档搜索功能使用 Laravel Scout 和 Meilisearch 提供强大的全文搜索能力。系统会自动将转换完成的文档索引到 Meilisearch用户可以通过关键词快速检索文档内容。
## 核心组件
### DocumentSearchService
文档搜索服务类,提供以下功能:
1. **search()** - 执行全文搜索
- 搜索文档标题、描述和 Markdown 内容
- 支持额外的筛选条件(类型、分组、上传者)
- 自动应用用户权限过滤
2. **filterByUserPermissions()** - 权限过滤
- 确保用户只能看到有权限访问的文档
- 全局文档对所有用户可见
- 专用文档只对所属分组的用户可见
3. **prepareSearchableData()** - 准备索引数据
- 包含完整的 Markdown 内容用于搜索
- 包含文档元数据(标题、描述、类型、分组等)
4. **indexDocument()** - 索引文档
- 将文档添加到 Meilisearch 索引
- 只索引转换完成的文档
5. **updateDocumentIndex()** - 更新索引
- 更新已索引文档的信息
- 如果文档不应被索引,则移除索引
6. **removeDocumentFromIndex()** - 移除索引
- 从 Meilisearch 中删除文档索引
### DocumentObserver
文档观察者,自动管理文档的搜索索引:
- **created** - 文档创建时不立即索引(等待转换完成)
- **updated** - 文档更新时检查转换状态并更新索引
- 当 conversion_status 变为 'completed' 时自动索引
- 当其他重要字段更新时也更新索引
- **deleted** - 文档删除时移除索引
- **restored** - 文档恢复时重新索引
- **forceDeleted** - 强制删除时移除索引
## 工作流程
### 文档索引流程
1. 用户上传 Word 文档
2. 文档保存到数据库conversion_status 设置为 'pending'
3. 转换任务加入队列
4. 转换完成后updateDocumentMarkdown() 将 conversion_status 更新为 'completed'
5. DocumentObserver 监听到 updated 事件
6. 自动调用 DocumentSearchService::indexDocument() 创建索引
7. 文档可以被搜索
### 搜索流程
1. 用户输入搜索关键词
2. 调用 DocumentSearchService::search()
3. 使用 Scout 在 Meilisearch 中搜索
4. 应用额外的筛选条件
5. 使用 filterByUserPermissions() 过滤结果
6. 返回用户有权限访问的文档列表
## 配置
### Meilisearch 配置
`config/scout.php` 中配置:
```php
'meilisearch' => [
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
'key' => env('MEILISEARCH_KEY'),
'index-settings' => [
'documents' => [
'filterableAttributes' => ['type', 'group_id', 'uploaded_by', 'conversion_status'],
'sortableAttributes' => ['created_at', 'title', 'updated_at'],
'searchableAttributes' => ['title', 'description', 'markdown_content'],
],
],
],
```
### 环境变量
`.env` 文件中配置:
```env
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=your-master-key
```
## 使用示例
### 基本搜索
```php
use App\Services\DocumentSearchService;
$searchService = app(DocumentSearchService::class);
$user = auth()->user();
// 搜索关键词
$results = $searchService->search('Laravel', $user);
// 带筛选条件的搜索
$results = $searchService->search('Laravel', $user, [
'type' => 'global',
'group_id' => 1,
]);
```
### 手动索引管理
```php
use App\Services\DocumentSearchService;
use App\Models\Document;
$searchService = app(DocumentSearchService::class);
$document = Document::find(1);
// 索引文档
$searchService->indexDocument($document);
// 更新索引
$searchService->updateDocumentIndex($document);
// 移除索引
$searchService->removeDocumentFromIndex($document);
```
### 批量重建索引
```php
// 重建所有文档的索引
php artisan scout:import "App\Models\Document"
// 清空索引
php artisan scout:flush "App\Models\Document"
```
## 错误处理
所有索引操作都包含错误处理:
- 索引失败不会影响文档的正常保存和使用
- 所有错误都会记录到日志中
- 搜索失败时返回空集合
## 性能考虑
1. **异步索引** - 文档转换和索引都在队列中异步处理
2. **权限过滤** - 在应用层过滤搜索结果,确保数据安全
3. **缓存策略** - 可以缓存热门搜索结果(待实现)
4. **分页** - 搜索结果应该分页显示(在前端实现)
## 故障排查
### 文档无法被搜索
1. 检查 conversion_status 是否为 'completed'
2. 检查 Meilisearch 服务是否运行
3. 检查文档是否已索引:`php artisan scout:import "App\Models\Document"`
4. 查看日志文件中的错误信息
### Meilisearch 连接失败
1. 确认 Meilisearch 服务正在运行
2. 检查 .env 中的 MEILISEARCH_HOST 和 MEILISEARCH_KEY
3. 测试连接:`curl http://localhost:7700/health`
### 搜索结果为空
1. 确认文档已完成转换
2. 确认用户有权限访问文档
3. 检查搜索关键词是否正确
4. 查看 Meilisearch 日志
## 下一步
- 实现搜索页面 UI任务 25
- 添加搜索结果高亮显示
- 实现搜索建议和自动补全
- 添加高级搜索语法支持

View File

@@ -0,0 +1,261 @@
# 文件名保留功能说明
## 功能概述
知识库系统现在会完整保留用户上传文档时的原始文件名,包括中文、特殊字符等。下载文档时,文件名将与上传时保持一致。
## 实现细节
### 1. Filament 表单配置
`DocumentResource` 的文件上传字段中添加了 `preserveFilenames()` 方法:
```php
Forms\Components\FileUpload::make('file')
->label('文档文件')
->required()
->acceptedFileTypes([...])
->preserveFilenames() // 保留原始文件名
->disk('local')
->directory('documents/' . date('Y/m/d'))
// ...
```
### 2. 文件上传处理
#### CreateDocument 页面
```php
protected function mutateFormDataBeforeCreate(array $data): array
{
if (isset($data['file'])) {
$filePath = $data['file'];
$originalFileName = basename($filePath); // 获取原始文件名
$data['file_path'] = $filePath;
$data['file_name'] = $originalFileName; // 保存原始文件名
// ...
}
return $data;
}
```
#### EditDocument 页面
```php
protected function mutateFormDataBeforeSave(array $data): array
{
if (isset($data['file']) && $data['file'] !== $this->record->file_path) {
$filePath = $data['file'];
$originalFileName = basename($filePath); // 获取原始文件名
$data['file_path'] = $filePath;
$data['file_name'] = $originalFileName; // 保存原始文件名
// ...
}
return $data;
}
```
### 3. DocumentService 更新
#### 上传方法
```php
public function uploadDocument(...): Document
{
return DB::transaction(function () use (...) {
// 获取原始文件名
$originalFileName = $file->getClientOriginalName();
// 使用 storeAs 保存文件,保留原始文件名
$directory = 'documents/' . date('Y/m/d');
$filePath = $file->storeAs($directory, $originalFileName, 'local');
// 保存到数据库
$document = Document::create([
'file_path' => $filePath,
'file_name' => $originalFileName, // 原始文件名
// ...
]);
return $document;
});
}
```
#### 下载方法
```php
public function downloadDocument(Document $document, User $user): StreamedResponse
{
// 使用原始文件名作为下载文件名
return Storage::disk('local')->download(
$document->file_path,
$document->file_name // 原始文件名
);
}
```
## 支持的文件名格式
### ✅ 完全支持
1. **中文文件名**
- 示例:`知识库管理系统需求文档.docx`
- 上传后保留:✅
- 下载时保留:✅
2. **英文文件名**
- 示例:`Requirements Document.docx`
- 上传后保留:✅
- 下载时保留:✅
3. **数字文件名**
- 示例:`2024-Report.docx`
- 上传后保留:✅
- 下载时保留:✅
4. **特殊字符文件名**
- 示例:`文档(2024-01-01)_v1.0.docx`
- 支持的特殊字符:`()[]_-`
- 上传后保留:✅
- 下载时保留:✅
5. **混合格式文件名**
- 示例:`Project_项目文档_2024.docx`
- 上传后保留:✅
- 下载时保留:✅
## 文件存储结构
文件按日期组织存储:
```
storage/app/
└── documents/
└── 2024/
└── 12/
└── 04/
├── 知识库管理系统需求文档.docx
├── Requirements Document.docx
└── 文档(2024-01-01)_v1.0.docx
```
## 数据库字段
### documents 表
- `file_path`: 存储相对路径(如:`documents/2024/12/04/知识库管理系统需求文档.docx`
- `file_name`: 存储原始文件名(如:`知识库管理系统需求文档.docx`
- `file_size`: 文件大小(字节)
- `mime_type`: MIME 类型
## 浏览器兼容性
### 下载文件名编码
对于包含非 ASCII 字符(如中文)的文件名,系统会自动处理编码:
```
Content-Disposition: attachment; filename=document.docx; filename*=utf-8''%E7%9F%A5%E8%AF%86%E5%BA%93.docx
```
- `filename`: ASCII 兼容的后备文件名
- `filename*`: UTF-8 编码的完整文件名RFC 5987
### 支持的浏览器
- ✅ Chrome 80+
- ✅ Firefox 75+
- ✅ Safari 13+
- ✅ Edge 80+
- ✅ 移动浏览器iOS Safari, Chrome Mobile
## 测试覆盖
### 测试文件:`tests/Feature/DocumentFileNameTest.php`
1. **test_上传文档时保留原始文件名**
- 验证上传后 `file_name` 字段正确保存
2. **test_下载文档时使用原始文件名**
- 验证下载响应头包含原始文件名
3. **test_中文文件名正确处理**
- 验证中文文件名的完整支持
4. **test_特殊字符文件名正确处理**
- 验证特殊字符的正确处理
### 运行测试
```bash
php artisan test --filter=DocumentFileNameTest
```
## 注意事项
### 1. 文件名冲突
如果同一天上传了同名文件,后上传的文件会覆盖先上传的文件。建议:
- 用户在上传前检查文件名
- 或者在文件名中添加时间戳或版本号
### 2. 文件名长度限制
- 数据库字段 `file_name` 限制255 字符
- 文件系统限制:通常为 255 字节
- 建议文件名不超过 100 个字符
### 3. 特殊字符限制
某些特殊字符可能在不同操作系统上有限制:
- Windows: 不支持 `< > : " / \ | ? *`
- Linux/Mac: 不支持 `/`
系统会自动处理这些字符,但建议避免使用。
## 安全考虑
### 1. 路径遍历攻击防护
- 使用 `basename()` 提取文件名,防止路径遍历
- 文件存储在受控目录中
### 2. 文件名注入防护
- Laravel Storage 自动处理文件名转义
- 数据库使用参数化查询
### 3. XSS 防护
- 文件名在显示时会被 Blade 模板自动转义
- 下载响应头使用标准编码
## 未来改进
1. **文件名冲突处理**
- 自动添加序号:`文档.docx``文档(1).docx`
- 或添加时间戳:`文档.docx``文档_20240104_143022.docx`
2. **文件名验证**
- 添加文件名格式验证
- 限制特殊字符使用
- 提供文件名建议
3. **批量上传**
- 支持批量上传多个文件
- 自动处理文件名冲突
4. **文件名搜索**
- 支持按文件名搜索文档
- 支持模糊匹配
## 总结
文件名保留功能确保了用户上传和下载文档时的一致性体验,特别是对中文文件名的完整支持,使得知识库系统更加符合中文用户的使用习惯。
所有功能都经过完整测试,确保在各种场景下都能正常工作。

258
docs/MEILISEARCH_SETUP.md Normal file
View File

@@ -0,0 +1,258 @@
# Meilisearch 安装和配置指南
## 概述
本项目使用 Meilisearch 作为全文搜索引擎,为文档内容提供快速准确的搜索功能。
## 安装方式
### 方式 1使用 Docker推荐
项目已经配置了 `docker-compose.yml` 文件,可以快速启动 Meilisearch 服务。
#### 启动服务
```bash
# 启动 Meilisearch 服务
docker-compose up -d meilisearch
# 查看服务状态
docker-compose ps
# 查看服务日志
docker-compose logs -f meilisearch
```
#### 停止服务
```bash
# 停止服务
docker-compose down
# 停止服务并删除数据卷
docker-compose down -v
```
### 方式 2本地安装macOS
使用 Homebrew 安装:
```bash
# 安装 Meilisearch
brew install meilisearch
# 启动服务
meilisearch --master-key="your-master-key-change-this-in-production"
```
### 方式 3本地安装Linux
```bash
# 下载 Meilisearch
curl -L https://install.meilisearch.com | sh
# 启动服务
./meilisearch --master-key="your-master-key-change-this-in-production"
```
### 方式 4本地安装Windows
从 [Meilisearch 官方网站](https://www.meilisearch.com/docs/learn/getting_started/installation) 下载 Windows 版本,然后运行:
```powershell
.\meilisearch.exe --master-key="your-master-key-change-this-in-production"
```
## Laravel Scout 安装
本项目使用 Laravel Scout 作为搜索抽象层。
### 安装依赖包
```bash
# 安装 Laravel Scout
composer require laravel/scout
# 安装 Meilisearch PHP SDK
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
# 发布 Scout 配置文件
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
```
安装完成后,会在 `config/scout.php` 中生成配置文件。
## 配置
### 主密钥Master Key
**重要**:在生产环境中,必须更改默认的主密钥!
1. 在 `docker-compose.yml` 中修改 `MEILI_MASTER_KEY` 环境变量
2. 在 `.env` 文件中更新 `MEILISEARCH_KEY` 配置
### 环境变量
`.env` 文件中配置以下变量:
```env
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=your-master-key-change-this-in-production
```
### Scout 索引配置
`config/scout.php` 中已配置 documents 索引的设置:
```php
'meilisearch' => [
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
'key' => env('MEILISEARCH_KEY'),
'index-settings' => [
'documents' => [
'filterableAttributes' => ['type', 'group_id', 'uploaded_by', 'conversion_status'],
'sortableAttributes' => ['created_at', 'title', 'updated_at'],
'searchableAttributes' => ['title', 'description', 'markdown_content'],
'displayedAttributes' => ['id', 'title', 'description', 'type', 'group_id', 'uploaded_by', 'created_at', 'updated_at'],
],
],
],
```
**配置说明**
- `filterableAttributes`: 可用于筛选的字段(类型、分组、上传者、转换状态)
- `sortableAttributes`: 可用于排序的字段(创建时间、标题、更新时间)
- `searchableAttributes`: 可搜索的字段标题、描述、Markdown 内容)
- `displayedAttributes`: 搜索结果中返回的字段
## 验证安装
访问 Meilisearch 管理界面:
```
http://localhost:7700
```
或使用 curl 测试:
```bash
curl -X GET 'http://localhost:7700/health'
```
应该返回:
```json
{"status":"available"}
```
## 数据持久化
使用 Docker 方式时Meilisearch 数据存储在 `storage/meilisearch` 目录中。
**注意**:请确保将此目录添加到 `.gitignore` 文件中,避免将索引数据提交到版本控制系统。
## 索引管理
### 查看所有索引
```bash
curl -X GET 'http://localhost:7700/indexes' \
-H 'Authorization: Bearer your-master-key-change-this-in-production'
```
### 删除索引
```bash
curl -X DELETE 'http://localhost:7700/indexes/documents' \
-H 'Authorization: Bearer your-master-key-change-this-in-production'
```
### 重建索引
在 Laravel 项目中运行:
```bash
# 清空所有索引
php artisan scout:flush "App\Models\Document"
# 重新导入所有文档
php artisan scout:import "App\Models\Document"
```
## 故障排除
### 服务无法启动
1. 检查端口 7700 是否被占用:
```bash
lsof -i :7700
```
2. 查看 Docker 日志:
```bash
docker-compose logs meilisearch
```
### 搜索不返回结果
1. 检查文档是否已索引:
```bash
php artisan scout:import "App\Models\Document"
```
2. 验证索引配置:
```bash
curl -X GET 'http://localhost:7700/indexes/documents/settings' \
-H 'Authorization: Bearer your-master-key-change-this-in-production'
```
### 权限错误
确保 `storage/meilisearch` 目录有正确的写入权限:
```bash
chmod -R 775 storage/meilisearch
```
## 性能优化
### 生产环境配置
在生产环境中,建议:
1. 使用强密钥作为 `MEILI_MASTER_KEY`
2. 设置 `MEILI_ENV=production`
3. 配置适当的资源限制CPU、内存
4. 定期备份 `storage/meilisearch` 目录
### 索引优化
根据实际使用情况调整索引设置:
```php
// config/scout.php
'meilisearch' => [
'index-settings' => [
'documents' => [
'filterableAttributes' => ['type', 'group_id', 'uploaded_by'],
'sortableAttributes' => ['created_at', 'title'],
'searchableAttributes' => ['title', 'description', 'markdown_content'],
'rankingRules' => [
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
],
],
],
],
```
## 更多信息
- [Meilisearch 官方文档](https://www.meilisearch.com/docs)
- [Laravel Scout 文档](https://laravel.com/docs/scout)
- [Meilisearch PHP SDK](https://github.com/meilisearch/meilisearch-php)

304
docs/PROJECT_OVERVIEW.md Normal file
View File

@@ -0,0 +1,304 @@
# 知识库系统项目概览
## 项目简介
知识库系统是一个基于 Laravel 11 和 Filament 3.X 构建的企业级文档管理平台。系统支持 Word 文档上传、自动转换为 Markdown 格式、全文搜索、在线预览以及基于用户分组的细粒度权限控制。
## 核心功能
### 1. 文档管理
- **文档上传**:支持 .doc 和 .docx 格式的 Word 文档上传
- **文档分类**
- 全局知识库:所有用户可访问
- 专用知识库:仅特定分组用户可访问
- **文档下载**:支持原始 Word 文档下载,并记录下载日志
- **文档预览**:在线预览 Markdown 渲染后的文档内容
### 2. 自动文档转换
- **异步转换**:使用队列系统异步将 Word 文档转换为 Markdown 格式
- **转换工具**:支持 Pandoc 或 PHPWord 作为转换引擎
- **状态跟踪**实时跟踪转换状态pending、processing、completed、failed
- **错误处理**:转换失败不影响文档的正常使用
### 3. 全文搜索
- **搜索引擎**:集成 Meilisearch 提供快速的全文搜索
- **搜索范围**:支持搜索文档标题、描述和 Markdown 内容
- **权限过滤**:搜索结果自动过滤,只显示用户有权访问的文档
- **高级筛选**:支持按文档类型、分组等条件筛选
### 4. 权限控制
- **用户分组**:用户可以属于多个分组
- **访问控制**
- 全局文档:所有用户可访问
- 专用文档:只有所属分组的用户可访问
- **权限验证**:在数据查询、下载、预览等操作中强制执行权限检查
- **安全日志**:记录所有未授权访问尝试
### 5. 用户界面
- **中文界面**:完整的简体中文用户界面
- **管理面板**:基于 Filament 3.X 的现代化管理界面
- **响应式设计**:支持桌面和移动设备访问
## 技术栈
### 后端
- **框架**Laravel 11.x
- **管理面板**Filament 3.X
- **数据库**MySQL 8.0+ / PostgreSQL 13+
- **搜索引擎**Meilisearch 1.5+
- **队列系统**Redis Queue
- **文档转换**Pandoc 2.x+ 或 PHPWord
### 前端
- **模板引擎**Laravel Blade
- **CSS 框架**Tailwind CSS 3.x
- **JavaScript**Alpine.js 3.xFilament 内置)
### 开发工具
- **包管理**Composer 2.x, npm
- **测试框架**Pest PHP
- **代码质量**PHPStan, Laravel Pint
## 项目结构
```
knowledge-base-system/
├── app/
│ ├── Filament/ # Filament 资源和页面
│ │ ├── Pages/ # 自定义页面(搜索页面)
│ │ └── Resources/ # 资源管理(文档、分组、用户)
│ ├── Http/
│ │ └── Controllers/ # 控制器(文档预览)
│ ├── Jobs/ # 队列任务(文档转换)
│ ├── Models/ # Eloquent 模型
│ ├── Observers/ # 模型观察者(文档索引)
│ ├── Policies/ # 授权策略
│ └── Services/ # 业务逻辑服务
├── config/
│ ├── documents.php # 文档转换配置
│ ├── filesystems.php # 文件存储配置
│ └── scout.php # Meilisearch 配置
├── database/
│ ├── factories/ # 测试数据工厂
│ ├── migrations/ # 数据库迁移
│ └── seeders/ # 数据填充
├── docs/ # 项目文档
├── resources/
│ └── views/ # Blade 视图模板
├── storage/
│ └── app/
│ └── private/
│ ├── documents/ # 原始 Word 文档存储
│ └── markdown/ # Markdown 文件存储
├── tests/ # 测试文件
└── .kiro/
└── specs/ # 功能规格文档
```
## 数据模型
### 核心实体
1. **User用户**
- 系统用户
- 可属于多个分组
- 可上传文档
2. **Group分组**
- 用户组织单位
- 拥有专用知识库文档
- 管理成员访问权限
3. **Document文档**
- 文档记录
- 包含原始文件和 Markdown 内容
- 关联分组和上传者
4. **DownloadLog下载日志**
- 记录文档下载历史
- 用于审计和统计
### 关系图
```
User ──┬── uploads ──> Document
└── belongs_to ──> Group ──> owns ──> Document
└── has ──> DownloadLog
```
## 已实现功能清单
### ✅ 核心功能
- [x] 用户认证和授权
- [x] 用户分组管理
- [x] 文档上传和存储
- [x] 文档分类(全局/专用)
- [x] 基于分组的权限控制
- [x] 文档下载和日志记录
- [x] 文档搜索和筛选
### ✅ 高级功能
- [x] Word 文档自动转换为 Markdown
- [x] 异步队列处理转换任务
- [x] Meilisearch 全文搜索集成
- [x] 文档 Markdown 在线预览
- [x] 搜索结果权限过滤
- [x] 安全日志记录
### ✅ 用户界面
- [x] Filament 管理面板
- [x] 完整中文界面
- [x] 文档管理界面
- [x] 分组管理界面
- [x] 用户管理界面
- [x] 搜索页面
- [x] 预览页面
### ⏳ 待完成功能
- [ ] 属性基础测试Property-Based Testing
- [ ] 完整的功能测试套件
- [ ] 性能优化(缓存、索引优化)
- [ ] UI 增强Alpine.js 动画和交互)
- [ ] 部署文档和脚本
## 快速开始
### 环境要求
- PHP 8.1+
- Composer 2.x
- Node.js 18+
- MySQL 8.0+ 或 PostgreSQL 13+
- Redis 6.0+
- Meilisearch 1.5+
- Pandoc 2.x+(可选)
### 安装步骤
1. **克隆项目**
```bash
git clone <repository-url>
cd knowledge-base-system
```
2. **安装依赖**
```bash
composer install
npm install
```
3. **配置环境**
```bash
cp .env.example .env
php artisan key:generate
```
4. **配置数据库**
编辑 `.env` 文件,设置数据库连接信息
5. **运行迁移**
```bash
php artisan migrate
```
6. **生成测试数据**(可选)
```bash
php artisan db:seed
```
7. **启动服务**
```bash
# 启动 Laravel 开发服务器
php artisan serve
# 启动队列工作进程
php artisan queue:work
# 启动 Meilisearch
meilisearch --master-key="your-master-key"
```
8. **访问系统**
打开浏览器访问 `http://localhost:8000/admin`
## 配置说明
### 文档转换配置
`.env` 文件中配置:
```env
DOCUMENT_CONVERSION_DRIVER=pandoc
PANDOC_PATH=/usr/local/bin/pandoc
CONVERSION_TIMEOUT=300
```
### Meilisearch 配置
```env
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=your-master-key
SCOUT_DRIVER=meilisearch
```
### 队列配置
```env
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
```
## 测试
### 运行测试
```bash
# 运行所有测试
php artisan test
# 运行特定测试
php artisan test --filter=DocumentAccessScopePropertyTest
# 生成测试覆盖率报告
php artisan test --coverage
```
### 测试数据
使用 Factory 生成测试数据:
```php
// 创建用户
$user = User::factory()->create();
// 创建分组
$group = Group::factory()->create();
// 创建文档
$document = Document::factory()->create([
'type' => 'global',
]);
```
## 部署
详细的部署指南请参考 [DEPLOYMENT.md](./DEPLOYMENT.md)
## 贡献指南
请参考 [CONTRIBUTING.md](./CONTRIBUTING.md)
## 许可证
本项目采用 MIT 许可证
## 联系方式
如有问题或建议,请联系项目维护者。
---
**最后更新**2025-12-05
**版本**1.0.0

130
docs/SETUP.md Normal file
View File

@@ -0,0 +1,130 @@
# 知识库系统 - 安装配置文档
## 项目初始化完成
本项目已成功完成初始化,包括以下配置:
### 1. Laravel 框架
- **版本**: Laravel 12.x
- **PHP 版本要求**: PHP 8.1+
- **数据库**: SQLite (database/database.sqlite)
### 2. Filament 管理面板
- **版本**: Filament 3.3.45
- **面板 ID**: admin
- **访问路径**: `/admin`
- **登录路径**: `/admin/login`
### 3. 中文化配置
- **应用语言**: zh_CN (简体中文)
- **Laravel 语言包**: laravel-lang/common ^6.7
- **Filament 中文翻译**: 已内置并发布
### 4. 管理员账户
- **用户名**: admin
- **邮箱**: admin@example.com
- **密码**: (创建时设置)
## 环境配置
### 应用配置 (.env)
```env
APP_LOCALE=zh_CN
APP_FALLBACK_LOCALE=zh_CN
APP_FAKER_LOCALE=zh_CN
```
### 数据库配置
```env
DB_CONNECTION=sqlite
```
## 已安装的主要依赖
### 生产依赖
- filament/filament: ^3.0
- livewire/livewire: ^3.7
- blade-ui-kit/blade-heroicons: ^2.6
- doctrine/dbal: ^4.4
### 开发依赖
- laravel-lang/common: ^6.7
- phpunit/phpunit: ^11.5
- laravel/pint: ^1.26
## 目录结构
```
.
├── app/
│ ├── Filament/
│ │ ├── Resources/ # Filament 资源文件
│ │ ├── Pages/ # Filament 页面
│ │ └── Widgets/ # Filament 小部件
│ ├── Models/ # Eloquent 模型
│ └── Providers/
│ └── Filament/
│ └── AdminPanelProvider.php # Filament 面板配置
├── database/
│ ├── database.sqlite # SQLite 数据库文件
│ ├── migrations/ # 数据库迁移
│ └── factories/ # 模型工厂
├── lang/
│ ├── zh_CN/ # Laravel 中文语言包
│ └── vendor/
│ └── filament/
│ └── zh_CN/ # Filament 中文翻译
├── tests/
│ └── Feature/
│ └── SetupTest.php # 配置验证测试
└── .env # 环境配置文件
```
## 验证安装
运行以下命令验证安装是否成功:
```bash
# 运行配置测试
php artisan test --filter=SetupTest
# 清除缓存
php artisan config:clear
php artisan route:clear
php artisan view:clear
# 查看路由
php artisan route:list --path=admin
```
## 启动开发服务器
```bash
php artisan serve
```
然后访问 http://localhost:8000/admin/login 登录管理面板。
## 下一步
根据 `.kiro/specs/knowledge-base-system/tasks.md` 中的任务列表,接下来需要:
1. 创建数据库迁移和模型
2. 实现文档权限查询作用域
3. 创建服务类和策略类
4. 创建 Filament 资源
## 测试结果
所有初始化测试均已通过:
- ✓ 应用语言配置为简体中文
- ✓ 数据库连接正常
- ✓ Filament 管理面板路由可访问
- ✓ 中文翻译文件存在
## 技术支持
如有问题,请参考:
- Laravel 文档: https://laravel.com/docs
- Filament 文档: https://filamentphp.com/docs
- Laravel Lang 文档: https://laravel-lang.com/

189
docs/security-logging.md Normal file
View File

@@ -0,0 +1,189 @@
# 安全日志记录
## 概述
知识库系统实现了全面的安全日志记录功能,用于记录所有未授权访问尝试和安全相关事件。
## 功能特性
### 1. 自动记录未授权访问
系统会自动记录以下未授权访问尝试:
- **查看文档** (view):用户尝试查看无权访问的专用文档
- **下载文档** (download):用户尝试下载无权访问的文档
- **更新文档** (update):用户尝试更新不属于自己的文档
- **删除文档** (delete):用户尝试删除不属于自己的文档
- **恢复文档** (restore):用户尝试恢复不属于自己的文档
- **永久删除** (forceDelete):用户尝试永久删除不属于自己的文档
### 2. 记录的信息
每条安全日志包含以下信息:
- **事件类型** (event)unauthorized_access
- **操作类型** (action)view, download, update, delete 等
- **用户信息**
- 用户 ID (user_id)
- 用户名 (user_name)
- 用户邮箱 (user_email)
- **文档信息**
- 文档 ID (document_id)
- 文档标题 (document_title)
- 文档类型 (document_type)
- 文档分组 ID (document_group_id)
- **请求信息**
- IP 地址 (ip_address)
- 时间戳 (timestamp)
- 用户代理 (user_agent)
## 日志配置
### 日志通道
安全日志使用独立的 `security` 通道,配置在 `config/logging.php` 中:
```php
'security' => [
'driver' => 'daily',
'path' => storage_path('logs/security.log'),
'level' => env('LOG_LEVEL', 'info'),
'days' => env('LOG_SECURITY_DAYS', 90),
'replace_placeholders' => true,
],
```
### 日志保留期
默认情况下,安全日志保留 90 天。可以通过环境变量 `LOG_SECURITY_DAYS` 调整:
```env
LOG_SECURITY_DAYS=90
```
## 查看日志
### 日志文件位置
安全日志存储在:`storage/logs/security.log`
每天会自动创建新的日志文件,格式为:`security-YYYY-MM-DD.log`
### 日志格式示例
```
[2024-12-04 10:30:45] local.WARNING: 未授权访问尝试 {"event":"unauthorized_access","action":"view","user_id":2,"user_name":"张三","user_email":"zhangsan@example.com","document_id":5,"document_title":"机密文档","document_type":"dedicated","document_group_id":3,"ip_address":"192.168.1.100","timestamp":"2024-12-04T10:30:45+08:00","user_agent":"Mozilla/5.0..."}
```
### 使用命令行查看日志
查看最新的安全日志:
```bash
tail -f storage/logs/security.log
```
查看今天的安全日志:
```bash
cat storage/logs/security-$(date +%Y-%m-%d).log
```
搜索特定用户的未授权访问:
```bash
grep "user_id\":2" storage/logs/security.log
```
搜索特定文档的访问尝试:
```bash
grep "document_id\":5" storage/logs/security.log
```
## 安全监控建议
### 1. 定期审查
建议定期审查安全日志,特别关注:
- 频繁的未授权访问尝试
- 来自异常 IP 地址的访问
- 针对敏感文档的访问尝试
- 同一用户的大量失败尝试
### 2. 告警设置
可以配置日志监控工具(如 ELK Stack、Graylog 等)来:
- 实时监控安全日志
- 设置告警规则
- 生成安全报告
- 可视化安全事件
### 3. 日志分析
使用日志分析工具可以:
- 识别攻击模式
- 发现潜在的安全威胁
- 追踪用户行为
- 生成合规报告
## 扩展功能
### SecurityLogger 服务
系统提供了 `SecurityLogger` 服务类,可以用于记录其他安全事件:
```php
use App\Services\SecurityLogger;
// 记录未授权访问
$securityLogger->logUnauthorizedAccess($user, $document, 'view');
// 记录权限验证失败
$securityLogger->logAuthorizationFailure($user, 'Document', $documentId, 'view', '用户不在分组中');
// 记录可疑活动
$securityLogger->logSuspiciousActivity($user, '短时间内大量下载尝试', [
'attempts' => 50,
'timeframe' => '5分钟'
]);
```
## 合规性
安全日志记录有助于满足以下合规要求:
- **数据保护法规**:记录数据访问和使用情况
- **审计要求**:提供完整的访问审计轨迹
- **安全标准**:符合 ISO 27001 等安全标准
- **内部政策**:支持组织的安全政策执行
## 注意事项
1. **隐私保护**:日志中包含用户信息,需要妥善保管
2. **存储空间**:定期清理旧日志以节省存储空间
3. **性能影响**:日志记录对性能影响很小,但在高并发场景下需要监控
4. **日志轮转**:使用 daily 驱动自动进行日志轮转
## 故障排查
### 日志未生成
1. 检查 `storage/logs` 目录权限
2. 确认日志配置正确
3. 检查 `LOG_CHANNEL` 环境变量
### 日志文件过大
1. 调整 `LOG_SECURITY_DAYS` 减少保留天数
2. 配置日志轮转策略
3. 使用外部日志管理系统
### 无法写入日志
1. 检查文件系统权限
2. 确认磁盘空间充足
3. 检查 SELinux 或 AppArmor 配置