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:
1
database/.gitignore
vendored
Normal file
1
database/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
49
database/migrations/0001_01_01_000000_create_users_table.php
Normal file
49
database/migrations/0001_01_01_000000_create_users_table.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
35
database/migrations/0001_01_01_000001_create_cache_table.php
Normal file
35
database/migrations/0001_01_01_000001_create_cache_table.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
57
database/migrations/0001_01_01_000002_create_jobs_table.php
Normal file
57
database/migrations/0001_01_01_000002_create_jobs_table.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
$table->unsignedTinyInteger('attempts');
|
||||
$table->unsignedInteger('reserved_at')->nullable();
|
||||
$table->unsignedInteger('available_at');
|
||||
$table->unsignedInteger('created_at');
|
||||
});
|
||||
|
||||
Schema::create('job_batches', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->string('name');
|
||||
$table->integer('total_jobs');
|
||||
$table->integer('pending_jobs');
|
||||
$table->integer('failed_jobs');
|
||||
$table->longText('failed_job_ids');
|
||||
$table->mediumText('options')->nullable();
|
||||
$table->integer('cancelled_at')->nullable();
|
||||
$table->integer('created_at');
|
||||
$table->integer('finished_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->text('connection');
|
||||
$table->text('queue');
|
||||
$table->longText('payload');
|
||||
$table->longText('exception');
|
||||
$table->timestamp('failed_at')->useCurrent();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
Schema::dropIfExists('job_batches');
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('groups', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('groups');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('group_user', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('group_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
|
||||
// 添加唯一索引,确保同一用户不会重复加入同一分组
|
||||
$table->unique(['group_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('group_user');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('documents', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->text('description')->nullable();
|
||||
$table->string('file_path', 500);
|
||||
$table->string('file_name');
|
||||
$table->bigInteger('file_size');
|
||||
$table->string('mime_type', 100);
|
||||
$table->enum('type', ['global', 'dedicated']);
|
||||
$table->foreignId('group_id')->nullable()->constrained()->onDelete('set null');
|
||||
$table->foreignId('uploaded_by')->constrained('users')->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
|
||||
// 添加索引以优化查询性能
|
||||
$table->index('type');
|
||||
$table->index('group_id');
|
||||
$table->index('uploaded_by');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('documents');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('download_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('document_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('ip_address', 45);
|
||||
$table->timestamp('downloaded_at');
|
||||
|
||||
// 添加索引以优化查询性能
|
||||
$table->index('document_id');
|
||||
$table->index('user_id');
|
||||
$table->index('downloaded_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('download_logs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('documents', function (Blueprint $table) {
|
||||
// 添加 Markdown 文件路径字段
|
||||
$table->string('markdown_path', 500)->nullable()->after('description');
|
||||
|
||||
// 添加 Markdown 内容摘要字段(用于快速预览)
|
||||
$table->text('markdown_preview')->nullable()->after('markdown_path');
|
||||
|
||||
// 添加转换状态字段
|
||||
$table->enum('conversion_status', ['pending', 'processing', 'completed', 'failed'])
|
||||
->default('pending')
|
||||
->after('markdown_preview');
|
||||
|
||||
// 添加转换错误信息字段
|
||||
$table->text('conversion_error')->nullable()->after('conversion_status');
|
||||
|
||||
// 添加索引以优化查询性能
|
||||
$table->index('conversion_status');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('documents', function (Blueprint $table) {
|
||||
// 删除索引
|
||||
$table->dropIndex(['conversion_status']);
|
||||
|
||||
// 删除字段
|
||||
$table->dropColumn([
|
||||
'markdown_path',
|
||||
'markdown_preview',
|
||||
'conversion_status',
|
||||
'conversion_error'
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
218
database/seeders/DatabaseSeeder.php
Normal file
218
database/seeders/DatabaseSeeder.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\Group;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
use WithoutModelEvents;
|
||||
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->command->info('开始生成演示数据...');
|
||||
|
||||
// 1. 创建管理员用户
|
||||
$this->command->info('创建管理员用户...');
|
||||
$admin = User::factory()->create([
|
||||
'name' => '系统管理员',
|
||||
'email' => 'admin@example.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
|
||||
// 2. 创建普通用户
|
||||
$this->command->info('创建普通用户...');
|
||||
$user1 = User::factory()->create([
|
||||
'name' => '张三',
|
||||
'email' => 'zhangsan@example.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
|
||||
$user2 = User::factory()->create([
|
||||
'name' => '李四',
|
||||
'email' => 'lisi@example.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
|
||||
$user3 = User::factory()->create([
|
||||
'name' => '王五',
|
||||
'email' => 'wangwu@example.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
|
||||
$user4 = User::factory()->create([
|
||||
'name' => '赵六',
|
||||
'email' => 'zhaoliu@example.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
|
||||
// 3. 创建分组
|
||||
$this->command->info('创建分组...');
|
||||
$techGroup = Group::factory()->create([
|
||||
'name' => '技术部',
|
||||
'description' => '负责公司技术研发和系统维护工作',
|
||||
]);
|
||||
|
||||
$marketGroup = Group::factory()->create([
|
||||
'name' => '市场部',
|
||||
'description' => '负责市场推广和品牌建设工作',
|
||||
]);
|
||||
|
||||
$hrGroup = Group::factory()->create([
|
||||
'name' => '人力资源部',
|
||||
'description' => '负责人力资源管理和员工关系维护',
|
||||
]);
|
||||
|
||||
// 4. 建立用户和分组的关联关系
|
||||
$this->command->info('建立用户和分组的关联关系...');
|
||||
// 管理员属于所有分组
|
||||
$admin->groups()->attach([$techGroup->id, $marketGroup->id, $hrGroup->id]);
|
||||
|
||||
// 张三和李四属于技术部
|
||||
$user1->groups()->attach($techGroup->id);
|
||||
$user2->groups()->attach($techGroup->id);
|
||||
|
||||
// 王五属于市场部
|
||||
$user3->groups()->attach($marketGroup->id);
|
||||
|
||||
// 赵六属于人力资源部
|
||||
$user4->groups()->attach($hrGroup->id);
|
||||
|
||||
// 5. 创建全局文档
|
||||
$this->command->info('创建全局文档...');
|
||||
Document::factory()->global()->create([
|
||||
'title' => '公司员工手册',
|
||||
'description' => '包含公司规章制度、员工福利、考勤制度等重要信息',
|
||||
'file_name' => '员工手册_2024.docx',
|
||||
'uploaded_by' => $admin->id,
|
||||
]);
|
||||
|
||||
Document::factory()->global()->create([
|
||||
'title' => '办公室使用规范',
|
||||
'description' => '办公室设施使用规范和注意事项',
|
||||
'file_name' => '办公室规范.docx',
|
||||
'uploaded_by' => $admin->id,
|
||||
]);
|
||||
|
||||
Document::factory()->global()->create([
|
||||
'title' => '公司年度总结报告',
|
||||
'description' => '2024年度公司发展总结和2025年规划',
|
||||
'file_name' => '年度总结_2024.docx',
|
||||
'uploaded_by' => $admin->id,
|
||||
]);
|
||||
|
||||
Document::factory()->global()->create([
|
||||
'title' => '安全管理制度',
|
||||
'description' => '公司信息安全和物理安全管理制度',
|
||||
'file_name' => '安全管理制度.docx',
|
||||
'uploaded_by' => $admin->id,
|
||||
]);
|
||||
|
||||
// 6. 创建技术部专用文档
|
||||
$this->command->info('创建技术部专用文档...');
|
||||
Document::factory()->dedicated($techGroup->id)->create([
|
||||
'title' => '系统架构设计文档',
|
||||
'description' => '公司核心系统的架构设计和技术选型说明',
|
||||
'file_name' => '系统架构设计.docx',
|
||||
'uploaded_by' => $user1->id,
|
||||
]);
|
||||
|
||||
Document::factory()->dedicated($techGroup->id)->create([
|
||||
'title' => '代码规范指南',
|
||||
'description' => '团队代码编写规范和最佳实践',
|
||||
'file_name' => '代码规范.docx',
|
||||
'uploaded_by' => $admin->id,
|
||||
]);
|
||||
|
||||
Document::factory()->dedicated($techGroup->id)->create([
|
||||
'title' => '数据库设计文档',
|
||||
'description' => '数据库表结构设计和关系说明',
|
||||
'file_name' => '数据库设计.docx',
|
||||
'uploaded_by' => $user2->id,
|
||||
]);
|
||||
|
||||
Document::factory()->dedicated($techGroup->id)->create([
|
||||
'title' => 'API 接口文档',
|
||||
'description' => '系统对外提供的 API 接口说明和使用示例',
|
||||
'file_name' => 'API接口文档.docx',
|
||||
'uploaded_by' => $user1->id,
|
||||
]);
|
||||
|
||||
// 7. 创建市场部专用文档
|
||||
$this->command->info('创建市场部专用文档...');
|
||||
Document::factory()->dedicated($marketGroup->id)->create([
|
||||
'title' => '市场推广方案',
|
||||
'description' => '2025年第一季度市场推广计划和预算',
|
||||
'file_name' => '市场推广方案_Q1.docx',
|
||||
'uploaded_by' => $user3->id,
|
||||
]);
|
||||
|
||||
Document::factory()->dedicated($marketGroup->id)->create([
|
||||
'title' => '品牌宣传策略',
|
||||
'description' => '品牌定位和宣传渠道策略',
|
||||
'file_name' => '品牌宣传策略.docx',
|
||||
'uploaded_by' => $admin->id,
|
||||
]);
|
||||
|
||||
Document::factory()->dedicated($marketGroup->id)->create([
|
||||
'title' => '客户调研报告',
|
||||
'description' => '目标客户群体调研分析报告',
|
||||
'file_name' => '客户调研报告.docx',
|
||||
'uploaded_by' => $user3->id,
|
||||
]);
|
||||
|
||||
// 8. 创建人力资源部专用文档
|
||||
$this->command->info('创建人力资源部专用文档...');
|
||||
Document::factory()->dedicated($hrGroup->id)->create([
|
||||
'title' => '招聘流程指南',
|
||||
'description' => '公司招聘流程和面试评估标准',
|
||||
'file_name' => '招聘流程指南.docx',
|
||||
'uploaded_by' => $user4->id,
|
||||
]);
|
||||
|
||||
Document::factory()->dedicated($hrGroup->id)->create([
|
||||
'title' => '员工培训计划',
|
||||
'description' => '2025年员工培训计划和课程安排',
|
||||
'file_name' => '员工培训计划_2025.docx',
|
||||
'uploaded_by' => $admin->id,
|
||||
]);
|
||||
|
||||
Document::factory()->dedicated($hrGroup->id)->create([
|
||||
'title' => '薪酬福利制度',
|
||||
'description' => '公司薪酬结构和福利政策说明',
|
||||
'file_name' => '薪酬福利制度.docx',
|
||||
'uploaded_by' => $user4->id,
|
||||
]);
|
||||
|
||||
Document::factory()->dedicated($hrGroup->id)->create([
|
||||
'title' => '绩效考核标准',
|
||||
'description' => '员工绩效考核指标和评估流程',
|
||||
'file_name' => '绩效考核标准.docx',
|
||||
'uploaded_by' => $admin->id,
|
||||
]);
|
||||
|
||||
$this->command->info('演示数据生成完成!');
|
||||
$this->command->newLine();
|
||||
$this->command->info('=== 生成的数据摘要 ===');
|
||||
$this->command->info('用户数量: ' . User::count());
|
||||
$this->command->info('分组数量: ' . Group::count());
|
||||
$this->command->info('文档数量: ' . Document::count());
|
||||
$this->command->info(' - 全局文档: ' . Document::global()->count());
|
||||
$this->command->info(' - 专用文档: ' . Document::dedicated()->count());
|
||||
$this->command->newLine();
|
||||
$this->command->info('=== 测试账号信息 ===');
|
||||
$this->command->info('管理员: admin@example.com / password');
|
||||
$this->command->info('张三(技术部): zhangsan@example.com / password');
|
||||
$this->command->info('李四(技术部): lisi@example.com / password');
|
||||
$this->command->info('王五(市场部): wangwu@example.com / password');
|
||||
$this->command->info('赵六(人力资源部): zhaoliu@example.com / password');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user