215 lines
6.9 KiB
PHP
215 lines
6.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Actions\Document\CreateDocumentAction;
|
|
use App\Actions\Document\UploadNewVersionAction;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Admin\StoreDocumentRequest;
|
|
use App\Jobs\ReindexDocumentJob;
|
|
use App\Models\Category;
|
|
use App\Models\Document;
|
|
use App\Models\DocumentVersion;
|
|
use App\Services\KnowledgeBase\AuditService;
|
|
use App\Services\KnowledgeBase\IngestionService;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\View\View;
|
|
use RuntimeException;
|
|
|
|
class DocumentController extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly AuditService $auditService,
|
|
private readonly IngestionService $ingestionService,
|
|
) {}
|
|
|
|
public function index(Request $request): View
|
|
{
|
|
$query = Document::with(['category', 'currentVersion'])
|
|
->withCount('versions');
|
|
|
|
if ($request->filled('category_id')) {
|
|
$query->where('category_id', $request->category_id);
|
|
}
|
|
|
|
if ($request->filled('status')) {
|
|
$query->where('status', $request->status);
|
|
}
|
|
|
|
if ($request->filled('search')) {
|
|
$query->where('title', 'like', '%' . $request->search . '%');
|
|
}
|
|
|
|
$documents = $query->latest()->paginate(15)->withQueryString();
|
|
$categories = Category::active()->ordered()->get();
|
|
|
|
return view('admin.documents.index', compact('documents', 'categories'));
|
|
}
|
|
|
|
public function create(): View
|
|
{
|
|
$categories = Category::active()->ordered()->get();
|
|
return view('admin.documents.create', compact('categories'));
|
|
}
|
|
|
|
public function store(StoreDocumentRequest $request, CreateDocumentAction $action): RedirectResponse
|
|
{
|
|
try {
|
|
$document = $action->execute(
|
|
$request->validated(),
|
|
$request->file('file')
|
|
);
|
|
|
|
return redirect()
|
|
->route('admin.documents.show', $document)
|
|
->with('success', "Dokumen '{$document->title}' berjaya diupload dan sedang diproses.");
|
|
} catch (RuntimeException $e) {
|
|
return back()
|
|
->withInput()
|
|
->with('error', $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function show(Document $document): View
|
|
{
|
|
$document->load([
|
|
'category',
|
|
'versions.uploader',
|
|
'currentVersion.chunks' => fn($q) => $q->limit(20),
|
|
]);
|
|
|
|
return view('admin.documents.show', compact('document'));
|
|
}
|
|
|
|
public function edit(Document $document): View
|
|
{
|
|
$categories = Category::active()->ordered()->get();
|
|
return view('admin.documents.edit', compact('document', 'categories'));
|
|
}
|
|
|
|
public function update(Request $request, Document $document): RedirectResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'title' => ['required', 'string', 'max:255'],
|
|
'description' => ['nullable', 'string', 'max:1000'],
|
|
'category_id' => ['required', 'exists:categories,id'],
|
|
'effective_date' => ['nullable', 'date'],
|
|
'expiry_date' => ['nullable', 'date'],
|
|
'tags' => ['nullable', 'array'],
|
|
'language' => ['nullable', 'in:ms,en'],
|
|
]);
|
|
|
|
$validated['updated_by'] = auth()->id();
|
|
$document->update($validated);
|
|
|
|
return redirect()
|
|
->route('admin.documents.show', $document)
|
|
->with('success', 'Maklumat dokumen berjaya dikemaskini.');
|
|
}
|
|
|
|
/**
|
|
* Upload versi baru untuk dokumen yang sedia ada.
|
|
*/
|
|
public function uploadVersion(
|
|
Request $request,
|
|
Document $document,
|
|
UploadNewVersionAction $action
|
|
): RedirectResponse {
|
|
$request->validate([
|
|
'file' => ['required', 'file', 'mimes:pdf', 'max:' . config('knowledgebase.upload.max_file_size', 20480)],
|
|
'change_notes' => ['nullable', 'string', 'max:500'],
|
|
]);
|
|
|
|
try {
|
|
$action->execute($document, $request->file('file'), $request->only('change_notes'));
|
|
|
|
return redirect()
|
|
->route('admin.documents.show', $document)
|
|
->with('success', 'Versi baru berjaya diupload dan sedang diproses.');
|
|
} catch (RuntimeException $e) {
|
|
return back()->with('error', $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggle status aktif/tidak aktif dokumen.
|
|
* Apabila dinyahaktifkan, chunk dalam Qdrant juga dimatikan.
|
|
*/
|
|
public function toggleStatus(Document $document): RedirectResponse
|
|
{
|
|
$newStatus = !$document->is_active;
|
|
|
|
if ($newStatus) {
|
|
// Aktifkan — versi semasa mesti sudah indexed
|
|
$currentVersion = $document->currentVersion;
|
|
if (!$currentVersion || !$currentVersion->isProcessed()) {
|
|
return back()->with('error',
|
|
'Dokumen tidak boleh diaktifkan kerana pemprosesan belum selesai.'
|
|
);
|
|
}
|
|
|
|
$document->update([
|
|
'is_active' => true,
|
|
'status' => Document::STATUS_ACTIVE,
|
|
]);
|
|
|
|
$this->auditService->documentActivated($document);
|
|
} else {
|
|
// Deactivate — matikan juga dalam Qdrant
|
|
$document->update([
|
|
'is_active' => false,
|
|
'status' => Document::STATUS_INACTIVE,
|
|
]);
|
|
|
|
if ($currentVersion = $document->currentVersion) {
|
|
$this->ingestionService->deactivateVersionInQdrant($currentVersion);
|
|
}
|
|
|
|
$this->auditService->documentDeactivated($document);
|
|
}
|
|
|
|
$status = $newStatus ? 'diaktifkan' : 'dinyahaktifkan';
|
|
|
|
return back()->with('success', "Dokumen '{$document->title}' telah {$status}.");
|
|
}
|
|
|
|
/**
|
|
* Trigger reindex untuk versi tertentu.
|
|
*/
|
|
public function reindex(Document $document): RedirectResponse
|
|
{
|
|
$currentVersion = $document->currentVersion;
|
|
|
|
if (!$currentVersion) {
|
|
return back()->with('error', 'Tiada versi semasa untuk diindeks semula.');
|
|
}
|
|
|
|
ReindexDocumentJob::dispatch($currentVersion->id);
|
|
|
|
$this->auditService->documentReindexed($document, $currentVersion);
|
|
|
|
return back()->with('success', 'Reindeks telah dimulakan. Sila semak semula sebentar lagi.');
|
|
}
|
|
|
|
/**
|
|
* Download PDF asal.
|
|
*/
|
|
public function download(Document $document, DocumentVersion $version)
|
|
{
|
|
abort_if($version->document_id !== $document->id, 404);
|
|
|
|
$disk = config('knowledgebase.upload.storage_disk', 'local');
|
|
|
|
if (!Storage::disk($disk)->exists($version->stored_path)) {
|
|
abort(404, 'Fail tidak dijumpai.');
|
|
}
|
|
|
|
return Storage::disk($disk)->download(
|
|
$version->stored_path,
|
|
$version->original_filename
|
|
);
|
|
}
|
|
}
|