Today we’ll take our audit system to the next level by grouping logs by user role and department, then asking GPT to summarize team activity clearly — useful for manager-level reporting or compliance review.
🧩 Step 1: Ensure roles and departments are accessible
Assuming each User
has:
roles()
relationship (many-to-many or Spatie roles)department()
relationship
In User.php
:
public function roles()
{
return $this->belongsToMany(Role::class);
}
public function department()
{
return $this->belongsTo(Department::class);
}
🧠 Step 2: Update GPT helper for role/department grouping
Extend AuditSummaryHelper.php
:
public static function summarizeByRole(array $logs): string
{
$grouped = collect($logs)->groupBy(function ($log) {
$user = $log->causer;
$role = optional($user)->roles->pluck('name')->join(', ') ?? 'Unknown';
$dept = optional($user)->department->name ?? 'No Dept';
return "$role / $dept";
});
$output = $grouped->map(function ($group, $key) {
$lines = $group->map(fn($log) =>
"- {$log->created_at->format('Y-m-d H:i')} | " .
(optional($log->causer)->name ?? 'System') . " | {$log->description}"
)->implode("\n");
return "## $key\n$lines";
})->implode("\n\n");
$prompt = <<<PROMPT
You are a Laravel audit analyst. Summarize the following user activity logs by team (role and department). Provide a clear summary per group.
$output
Team Summary:
PROMPT;
$response = OpenAI::chat()->create([
'model' => 'gpt-4o',
'messages' => [
['role' => 'user', 'content' => $prompt],
],
'max_tokens' => 500,
]);
return $response->choices[0]->message->content ?? 'No team summary generated.';
}
🔁 Step 3: Add route and controller method
In web.php
:
Route::post('/audit-logs/roles-summary', [\App\Http\Controllers\AuditLogController::class, 'rolesSummary'])->name('audit.logs.roles.summary');
In AuditLogController.php
:
public function rolesSummary(Request $request)
{
$logs = \Spatie\Activitylog\Models\Activity::with('causer.roles', 'causer.department')->latest()->take(50)->get();
$summary = \App\Helpers\AuditSummaryHelper::summarizeByRole($logs);
return back()->with('role_summary', $summary);
}
📄 Step 4: Add button and output to view
In index.blade.php
:
<form method="POST" action="{{ route('audit.logs.roles.summary') }}" class="mb-4 inline-block">
@csrf
<button class="bg-indigo-500 text-white px-3 py-1 rounded">Summarize by Role/Department</button>
</form>
@if(session('role_summary'))
<div class="bg-indigo-100 text-indigo-900 p-3 rounded mb-4 whitespace-pre-wrap">
<strong>GPT Role/Department Summary:</strong><br>
{!! nl2br(e(session('role_summary'))) !!}
</div>
@endif
✅ Example Output
GPT Role/Department Summary:
Admin / SOC
- Admin created 4 vehicle renewals and edited 2 payment plans.
Finance Manager / Finance
- Finance Manager deleted 3 expired customer plans and added 1 discount.
✅ Tomorrow (Day 6), we’ll automate weekly/monthly audit reports with GPT-generated summaries and email them directly to stakeholders.