190 lines
5.6 KiB
PHP
190 lines
5.6 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\AuditLog;
|
|
use App\Models\Document;
|
|
use App\Models\DocumentCategory;
|
|
use App\Models\User;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class ImportDocuments extends Command
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'documents:import
|
|
{path : Path to directory containing documents and manifest.json}
|
|
{--user-id=1 : User ID to attribute uploads to}
|
|
{--dry-run : Preview import without making changes}';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'Bulk import documents from a directory with manifest.json';
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*/
|
|
public function handle()
|
|
{
|
|
$path = $this->argument('path');
|
|
$userId = $this->option('user-id');
|
|
$dryRun = $this->option('dry-run');
|
|
|
|
// Validate path
|
|
if (!File::isDirectory($path)) {
|
|
$this->error("Directory not found: {$path}");
|
|
return 1;
|
|
}
|
|
|
|
// Check for manifest.json
|
|
$manifestPath = $path . '/manifest.json';
|
|
if (!File::exists($manifestPath)) {
|
|
$this->error("manifest.json not found in {$path}");
|
|
$this->info("Expected format:");
|
|
$this->line($this->getManifestExample());
|
|
return 1;
|
|
}
|
|
|
|
// Load manifest
|
|
$manifest = json_decode(File::get($manifestPath), true);
|
|
if (!$manifest || !isset($manifest['documents'])) {
|
|
$this->error("Invalid manifest.json format");
|
|
return 1;
|
|
}
|
|
|
|
// Validate user
|
|
$user = User::find($userId);
|
|
if (!$user) {
|
|
$this->error("User not found: {$userId}");
|
|
return 1;
|
|
}
|
|
|
|
$this->info("Importing documents from: {$path}");
|
|
$this->info("Attributed to: {$user->name}");
|
|
if ($dryRun) {
|
|
$this->warn("DRY RUN - No changes will be made");
|
|
}
|
|
$this->newLine();
|
|
|
|
$successCount = 0;
|
|
$errorCount = 0;
|
|
|
|
foreach ($manifest['documents'] as $item) {
|
|
try {
|
|
$this->processDocument($path, $item, $user, $dryRun);
|
|
$successCount++;
|
|
} catch (\Exception $e) {
|
|
$this->error("Error processing {$item['file']}: {$e->getMessage()}");
|
|
$errorCount++;
|
|
}
|
|
}
|
|
|
|
$this->newLine();
|
|
$this->info("Import complete!");
|
|
$this->info("Success: {$successCount}");
|
|
if ($errorCount > 0) {
|
|
$this->error("Errors: {$errorCount}");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
protected function processDocument(string $basePath, array $item, User $user, bool $dryRun): void
|
|
{
|
|
$filePath = $basePath . '/' . $item['file'];
|
|
|
|
// Validate file exists
|
|
if (!File::exists($filePath)) {
|
|
throw new \Exception("File not found: {$filePath}");
|
|
}
|
|
|
|
// Find or create category
|
|
$category = DocumentCategory::where('slug', $item['category'])->first();
|
|
if (!$category) {
|
|
throw new \Exception("Category not found: {$item['category']}");
|
|
}
|
|
|
|
$this->line("Processing: {$item['title']}");
|
|
$this->line(" Category: {$category->name}");
|
|
$this->line(" File: {$item['file']}");
|
|
|
|
if ($dryRun) {
|
|
$this->line(" [DRY RUN] Would create document");
|
|
return;
|
|
}
|
|
|
|
// Copy file to storage
|
|
$fileInfo = pathinfo($filePath);
|
|
$storagePath = 'documents/' . uniqid() . '.' . $fileInfo['extension'];
|
|
Storage::disk('private')->put($storagePath, File::get($filePath));
|
|
|
|
// Create document
|
|
$document = Document::create([
|
|
'document_category_id' => $category->id,
|
|
'title' => $item['title'],
|
|
'document_number' => $item['document_number'] ?? null,
|
|
'description' => $item['description'] ?? null,
|
|
'access_level' => $item['access_level'] ?? $category->default_access_level,
|
|
'status' => 'active',
|
|
'created_by' => $user->id,
|
|
'updated_by' => $user->id,
|
|
]);
|
|
|
|
// Add first version
|
|
$document->addVersion(
|
|
filePath: $storagePath,
|
|
originalFilename: $fileInfo['basename'],
|
|
mimeType: File::mimeType($filePath),
|
|
fileSize: File::size($filePath),
|
|
uploadedBy: $user,
|
|
versionNotes: $item['version_notes'] ?? 'Initial import'
|
|
);
|
|
|
|
AuditLog::create([
|
|
'user_id' => $user->id,
|
|
'action' => 'document.imported',
|
|
'auditable_type' => Document::class,
|
|
'auditable_id' => $document->id,
|
|
'old_values' => null,
|
|
'new_values' => ['title' => $item['title']],
|
|
'ip_address' => '127.0.0.1',
|
|
'user_agent' => 'CLI Import',
|
|
]);
|
|
|
|
$this->info(" ✓ Created document ID: {$document->id}");
|
|
}
|
|
|
|
protected function getManifestExample(): string
|
|
{
|
|
return <<<'JSON'
|
|
{
|
|
"documents": [
|
|
{
|
|
"file": "bylaws.pdf",
|
|
"title": "協會章程",
|
|
"category": "association-bylaws",
|
|
"document_number": "2024-001",
|
|
"description": "協會章程修正版",
|
|
"access_level": "members",
|
|
"version_notes": "Initial import"
|
|
},
|
|
{
|
|
"file": "meeting-2024-01.pdf",
|
|
"title": "2024年1月會議記錄",
|
|
"category": "meeting-minutes",
|
|
"access_level": "members"
|
|
}
|
|
]
|
|
}
|
|
JSON;
|
|
}
|
|
}
|