230 lines
7.0 KiB
PHP
230 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services\KnowledgeBase;
|
|
|
|
use App\Models\AuditLog;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Request;
|
|
|
|
/**
|
|
* AuditService
|
|
*
|
|
* Simpan audit trail untuk semua tindakan penting dalam sistem.
|
|
* Append-only — tiada delete atau update audit log.
|
|
*/
|
|
class AuditService
|
|
{
|
|
/**
|
|
* Log satu event.
|
|
*
|
|
* @param string $event Nama event (e.g. 'document.uploaded')
|
|
* @param mixed $model Model yang terlibat (optional)
|
|
* @param array $oldValues Data sebelum perubahan
|
|
* @param array $newValues Data selepas perubahan
|
|
* @param ?string $description Huraian untuk manusia
|
|
*/
|
|
public function log(
|
|
string $event,
|
|
mixed $model = null,
|
|
array $oldValues = [],
|
|
array $newValues = [],
|
|
?string $description = null
|
|
): AuditLog {
|
|
return AuditLog::create([
|
|
'user_id' => Auth::id(),
|
|
'event' => $event,
|
|
'auditable_type' => $model ? get_class($model) : null,
|
|
'auditable_id' => $model?->getKey(),
|
|
'old_values' => empty($oldValues) ? null : $oldValues,
|
|
'new_values' => empty($newValues) ? null : $newValues,
|
|
'description' => $description,
|
|
'ip_address' => Request::ip(),
|
|
'user_agent' => Request::userAgent(),
|
|
]);
|
|
}
|
|
|
|
// Shortcut methods untuk event biasa
|
|
|
|
public function documentUploaded($document, $version): void
|
|
{
|
|
$this->log(
|
|
'document.uploaded',
|
|
$document,
|
|
[],
|
|
[
|
|
'document_id' => $document->id,
|
|
'version_number' => $version->version_number,
|
|
'filename' => $version->original_filename,
|
|
],
|
|
"Dokumen '{$document->title}' versi {$version->version_number} diupload."
|
|
);
|
|
}
|
|
|
|
public function documentActivated($document): void
|
|
{
|
|
$this->log(
|
|
'document.activated',
|
|
$document,
|
|
['is_active' => false],
|
|
['is_active' => true],
|
|
"Dokumen '{$document->title}' diaktifkan."
|
|
);
|
|
}
|
|
|
|
public function documentDeactivated($document): void
|
|
{
|
|
$this->log(
|
|
'document.deactivated',
|
|
$document,
|
|
['is_active' => true],
|
|
['is_active' => false],
|
|
"Dokumen '{$document->title}' dinyahaktifkan."
|
|
);
|
|
}
|
|
|
|
public function documentReindexed($document, $version): void
|
|
{
|
|
$this->log(
|
|
'document.reindexed',
|
|
$version,
|
|
[],
|
|
['document_id' => $document->id, 'version_id' => $version->id],
|
|
"Dokumen '{$document->title}' versi {$version->version_number} diindeks semula."
|
|
);
|
|
}
|
|
|
|
public function knowledgeItemCreated($item): void
|
|
{
|
|
$this->log(
|
|
'knowledge_item.created',
|
|
$item,
|
|
[],
|
|
['title' => $item->title, 'type' => $item->item_type],
|
|
"Knowledge item '{$item->title}' ({$item->item_type}) dicipta."
|
|
);
|
|
}
|
|
|
|
public function knowledgeItemUpdated($item, array $oldValues): void
|
|
{
|
|
$this->log(
|
|
'knowledge_item.updated',
|
|
$item,
|
|
$oldValues,
|
|
$item->getAttributes(),
|
|
"Knowledge item '{$item->title}' dikemaskini."
|
|
);
|
|
}
|
|
|
|
public function knowledgeItemDeactivated($item): void
|
|
{
|
|
$this->log(
|
|
'knowledge_item.deactivated',
|
|
$item,
|
|
['is_active' => true],
|
|
['is_active' => false],
|
|
"Knowledge item '{$item->title}' dinyahaktifkan."
|
|
);
|
|
}
|
|
|
|
public function faqConvertedFromFeedback($feedback, $knowledgeItem): void
|
|
{
|
|
$this->log(
|
|
'faq.converted_from_feedback',
|
|
$knowledgeItem,
|
|
[],
|
|
['feedback_id' => $feedback->id, 'knowledge_item_id' => $knowledgeItem->id],
|
|
"FAQ baru '{$knowledgeItem->title}' dicipta dari feedback chat."
|
|
);
|
|
}
|
|
|
|
public function categoryCreated($category): void
|
|
{
|
|
$this->log(
|
|
'category.created',
|
|
$category,
|
|
[],
|
|
['name' => $category->name, 'slug' => $category->slug],
|
|
"Kategori '{$category->name}' dicipta."
|
|
);
|
|
}
|
|
|
|
public function systemReindexStarted(string $scope): void
|
|
{
|
|
$this->log(
|
|
'system.reindex_started',
|
|
null,
|
|
[],
|
|
['scope' => $scope],
|
|
"Reindeks sistem dimulakan untuk: {$scope}"
|
|
);
|
|
}
|
|
|
|
// =========================================================================
|
|
// CHUNK REVIEW & EDITING EVENTS
|
|
// =========================================================================
|
|
|
|
public function chunkFinalTextEdited($chunk, ?string $oldText, string $newText): void
|
|
{
|
|
$this->log(
|
|
'chunk.final_text_edited',
|
|
$chunk,
|
|
['final_text' => mb_substr($oldText ?? '[content asal]', 0, 200)],
|
|
['final_text' => mb_substr($newText, 0, 200)],
|
|
"final_text chunk #{$chunk->chunk_index} (ID: {$chunk->id}) diedit. Reindex diantrikan."
|
|
);
|
|
}
|
|
|
|
public function chunkExcluded($chunk, string $oldStatus): void
|
|
{
|
|
$this->log(
|
|
'chunk.excluded',
|
|
$chunk,
|
|
['chunk_status' => $oldStatus, 'is_active' => true],
|
|
['chunk_status' => 'excluded', 'is_active' => false],
|
|
"Chunk #{$chunk->chunk_index} (ID: {$chunk->id}) dikecualikan dari indexing."
|
|
);
|
|
}
|
|
|
|
public function chunkIncluded($chunk, string $oldStatus): void
|
|
{
|
|
$this->log(
|
|
'chunk.included',
|
|
$chunk,
|
|
['chunk_status' => $oldStatus, 'is_active' => false],
|
|
['chunk_status' => $chunk->chunk_status, 'is_active' => true],
|
|
"Chunk #{$chunk->chunk_index} (ID: {$chunk->id}) dikembalikan ke indexing."
|
|
);
|
|
}
|
|
|
|
public function chunkReindexTriggered($chunk): void
|
|
{
|
|
$this->log(
|
|
'chunk.reindex_triggered',
|
|
$chunk,
|
|
[],
|
|
['chunk_status' => 'needs_reindex'],
|
|
"Reindex manual dicetuskan untuk chunk #{$chunk->chunk_index} (ID: {$chunk->id})."
|
|
);
|
|
}
|
|
|
|
public function chunkSplit($parentChunk, array $children, string $splitGroupId): void
|
|
{
|
|
$childIds = array_map(fn($c) => $c->id, $children);
|
|
|
|
$this->log(
|
|
'chunk.split',
|
|
$parentChunk,
|
|
['chunk_status' => 'indexed', 'is_active' => true],
|
|
[
|
|
'chunk_status' => 'superseded',
|
|
'is_active' => false,
|
|
'split_group_id' => $splitGroupId,
|
|
'child_count' => count($children),
|
|
'child_chunk_ids' => $childIds,
|
|
],
|
|
"Chunk #{$parentChunk->chunk_index} (ID: {$parentChunk->id}) di-split kepada "
|
|
. count($children) . " chunk baharu. Split group: {$splitGroupId}"
|
|
);
|
|
}
|
|
}
|