feat: implement system-wide activity logging with database persistence, repository patterns, and admin controller access.
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\ActivityLog;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ActivityLogger
|
||||
{
|
||||
private array $hiddenFields = [
|
||||
'password',
|
||||
'remember_token',
|
||||
'plain_password',
|
||||
'token',
|
||||
'secret',
|
||||
'api_key',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
public function log(
|
||||
string $event,
|
||||
string $logName,
|
||||
string $description,
|
||||
?Model $subject = null,
|
||||
array $oldValues = [],
|
||||
array $newValues = [],
|
||||
): void {
|
||||
try {
|
||||
[$causerType, $causerId, $causerName] = $this->resolveCauser();
|
||||
|
||||
ActivityLog::create([
|
||||
'log_name' => $logName,
|
||||
'event' => $event,
|
||||
'description' => $description,
|
||||
'subject_type' => $subject ? get_class($subject) : null,
|
||||
'subject_id' => $subject?->getKey(),
|
||||
'causer_type' => $causerType,
|
||||
'causer_id' => $causerId,
|
||||
'causer_name' => $causerName,
|
||||
'old_values' => $oldValues ?: null,
|
||||
'new_values' => $newValues ?: null,
|
||||
'ip_address' => request()->getClientIp(),
|
||||
'user_agent' => request()->userAgent(),
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('[ActivityLogger] Failed to write log', [
|
||||
'error' => $e->getMessage(),
|
||||
'event' => $event,
|
||||
'log_name' => $logName,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function logCreated(Model $model, string $logName): void
|
||||
{
|
||||
$causer = $this->getCauserName();
|
||||
$id = $model->getKey();
|
||||
|
||||
$this->log(
|
||||
event: 'created',
|
||||
logName: $logName,
|
||||
description: "$causer tạo mới $logName" . ($id ? " #$id" : ''),
|
||||
subject: $model,
|
||||
newValues: $this->filterValues($model->getAttributes()),
|
||||
);
|
||||
}
|
||||
|
||||
public function logUpdated(Model $model, string $logName): void
|
||||
{
|
||||
$dirty = $model->getDirty();
|
||||
if (empty($dirty)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dirty = $this->filterValues($dirty);
|
||||
$original = $this->filterValues(array_intersect_key($model->getOriginal(), $dirty));
|
||||
|
||||
if (empty($dirty)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$causer = $this->getCauserName();
|
||||
$id = $model->getKey();
|
||||
$fields = implode(', ', array_keys($dirty));
|
||||
|
||||
$this->log(
|
||||
event: 'updated',
|
||||
logName: $logName,
|
||||
description: "$causer cập nhật $logName" . ($id ? " #$id" : '') . " [$fields]",
|
||||
subject: $model,
|
||||
oldValues: $original,
|
||||
newValues: $dirty,
|
||||
);
|
||||
}
|
||||
|
||||
public function logDeleted(Model $model, string $logName): void
|
||||
{
|
||||
$causer = $this->getCauserName();
|
||||
$id = $model->getOriginal($model->getKeyName()) ?? $model->getKey();
|
||||
|
||||
$this->log(
|
||||
event: 'deleted',
|
||||
logName: $logName,
|
||||
description: "$causer xóa $logName" . ($id ? " #$id" : ''),
|
||||
subject: $model,
|
||||
);
|
||||
}
|
||||
|
||||
public function logRestored(Model $model, string $logName): void
|
||||
{
|
||||
$causer = $this->getCauserName();
|
||||
$id = $model->getKey();
|
||||
|
||||
$this->log(
|
||||
event: 'restored',
|
||||
logName: $logName,
|
||||
description: "$causer khôi phục $logName" . ($id ? " #$id" : ''),
|
||||
subject: $model,
|
||||
);
|
||||
}
|
||||
|
||||
public function logAuth(string $event, string $causerType, $causerId, string $causerName): void
|
||||
{
|
||||
$action = match ($event) {
|
||||
'login' => 'đăng nhập',
|
||||
'logout' => 'đăng xuất',
|
||||
'register' => 'đăng ký tài khoản',
|
||||
default => $event,
|
||||
};
|
||||
|
||||
$this->log(
|
||||
event: $event,
|
||||
logName: 'auth',
|
||||
description: "[$causerType] $causerName $action",
|
||||
subject: null,
|
||||
oldValues: [],
|
||||
newValues: ['causer_type' => $causerType, 'causer_id' => $causerId, 'causer_name' => $causerName],
|
||||
);
|
||||
}
|
||||
|
||||
private function resolveCauser(): array
|
||||
{
|
||||
if ($admin = auth()->guard('admin')->user()) {
|
||||
return ['admin', $admin->id, $admin->username ?? $admin->email];
|
||||
}
|
||||
|
||||
if ($customer = auth()->guard('client')->user()) {
|
||||
return ['customer', $customer->id, $customer->email ?? $customer->name];
|
||||
}
|
||||
|
||||
return ['system', null, 'System'];
|
||||
}
|
||||
|
||||
public function getCauserName(): string
|
||||
{
|
||||
[, , $name] = $this->resolveCauser();
|
||||
return $name;
|
||||
}
|
||||
|
||||
private function filterValues(array $values): array
|
||||
{
|
||||
return array_diff_key($values, array_flip($this->hiddenFields));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user