First commit
This commit is contained in:
229
app/Services/KnowledgeBase/AuditService.php
Normal file
229
app/Services/KnowledgeBase/AuditService.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?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}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user