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:
129
database/factories/DocumentFactory.php
Normal file
129
database/factories/DocumentFactory.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\Group;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Document>
|
||||
*/
|
||||
class DocumentFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Document::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
// 使用中文 Faker 生成器
|
||||
$faker = \Faker\Factory::create('zh_CN');
|
||||
|
||||
// 生成中文文档标题
|
||||
$titles = [
|
||||
'项目管理规范',
|
||||
'技术文档模板',
|
||||
'员工手册',
|
||||
'产品需求文档',
|
||||
'系统设计方案',
|
||||
'测试报告',
|
||||
'会议纪要',
|
||||
'培训资料',
|
||||
'操作指南',
|
||||
'年度总结报告',
|
||||
];
|
||||
|
||||
$title = $faker->randomElement($titles) . ' - ' . $faker->word();
|
||||
$fileName = $faker->word() . '_' . date('Ymd') . '.docx';
|
||||
|
||||
return [
|
||||
'title' => $title,
|
||||
'description' => $faker->paragraph(3),
|
||||
'file_path' => 'documents/' . date('Y/m/d') . '/' . fake()->uuid() . '.docx',
|
||||
'file_name' => $fileName,
|
||||
'file_size' => fake()->numberBetween(10000, 5000000),
|
||||
'mime_type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'type' => fake()->randomElement(['global', 'dedicated']),
|
||||
'group_id' => null,
|
||||
'uploaded_by' => User::factory(),
|
||||
'markdown_path' => null,
|
||||
'markdown_preview' => null,
|
||||
'conversion_status' => 'pending',
|
||||
'conversion_error' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定文档为全局类型
|
||||
*/
|
||||
public function global(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => 'global',
|
||||
'group_id' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定文档为专用类型
|
||||
*/
|
||||
public function dedicated(?int $groupId = null): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => 'dedicated',
|
||||
'group_id' => $groupId ?? Group::factory(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定文档已完成转换
|
||||
*/
|
||||
public function converted(): static
|
||||
{
|
||||
$faker = \Faker\Factory::create('zh_CN');
|
||||
$uuid = fake()->uuid();
|
||||
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'markdown_path' => 'markdown/' . date('Y/m/d') . '/' . $uuid . '.md',
|
||||
'markdown_preview' => $faker->text(500),
|
||||
'conversion_status' => 'completed',
|
||||
'conversion_error' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定文档转换失败
|
||||
*/
|
||||
public function conversionFailed(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'markdown_path' => null,
|
||||
'markdown_preview' => null,
|
||||
'conversion_status' => 'failed',
|
||||
'conversion_error' => 'Failed to convert document: Invalid file format',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定文档正在转换中
|
||||
*/
|
||||
public function converting(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'markdown_path' => null,
|
||||
'markdown_preview' => null,
|
||||
'conversion_status' => 'processing',
|
||||
'conversion_error' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
70
database/factories/DownloadLogFactory.php
Normal file
70
database/factories/DownloadLogFactory.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\DownloadLog;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\DownloadLog>
|
||||
*/
|
||||
class DownloadLogFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = DownloadLog::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'document_id' => Document::factory(),
|
||||
'user_id' => User::factory(),
|
||||
'downloaded_at' => fake()->dateTimeBetween('-1 year', 'now'),
|
||||
'ip_address' => fake()->ipv4(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定下载日志使用特定的文档
|
||||
*/
|
||||
public function forDocument(Document|int $document): static
|
||||
{
|
||||
$documentId = $document instanceof Document ? $document->id : $document;
|
||||
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'document_id' => $documentId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定下载日志使用特定的用户
|
||||
*/
|
||||
public function forUser(User|int $user): static
|
||||
{
|
||||
$userId = $user instanceof User ? $user->id : $user;
|
||||
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'user_id' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定下载日志使用最近的时间
|
||||
*/
|
||||
public function recent(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'downloaded_at' => fake()->dateTimeBetween('-7 days', 'now'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
49
database/factories/GroupFactory.php
Normal file
49
database/factories/GroupFactory.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Group;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Group>
|
||||
*/
|
||||
class GroupFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Group::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
// 使用中文 Faker 生成器
|
||||
$faker = \Faker\Factory::create('zh_CN');
|
||||
|
||||
// 生成中文分组名称(使用公司名或部门名)
|
||||
$groupNames = [
|
||||
'技术部',
|
||||
'市场部',
|
||||
'人力资源部',
|
||||
'财务部',
|
||||
'运营部',
|
||||
'产品部',
|
||||
'设计部',
|
||||
'客服部',
|
||||
'研发中心',
|
||||
'销售部',
|
||||
];
|
||||
|
||||
return [
|
||||
'name' => $faker->randomElement($groupNames) . ' - ' . $faker->company(),
|
||||
'description' => $faker->sentence(10),
|
||||
];
|
||||
}
|
||||
}
|
||||
44
database/factories/UserFactory.php
Normal file
44
database/factories/UserFactory.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user