feat(audit): activity_log attribution — user_id/ip/ua for all 4 DealController events
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -398,10 +398,12 @@ class DealController extends Controller
|
||||
$deal->comment = $validated['comment'];
|
||||
ActivityLog::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'user_id' => null,
|
||||
'user_id' => request()->user()?->id,
|
||||
'deal_id' => $deal->id,
|
||||
'event' => 'deal.commented',
|
||||
'context' => ['text' => $validated['comment'] ?? ''],
|
||||
'ip_address' => request()->ip(),
|
||||
'user_agent' => request()->userAgent(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -411,10 +413,12 @@ class DealController extends Controller
|
||||
$deal->assigned_at = $validated['manager_id'] !== null ? now() : null;
|
||||
ActivityLog::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'user_id' => null,
|
||||
'user_id' => request()->user()?->id,
|
||||
'deal_id' => $deal->id,
|
||||
'event' => ActivityLog::EVENT_DEAL_ASSIGNED,
|
||||
'context' => ['from' => $previousManager, 'to' => $validated['manager_id']],
|
||||
'ip_address' => request()->ip(),
|
||||
'user_agent' => request()->userAgent(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -423,10 +427,12 @@ class DealController extends Controller
|
||||
$deal->status = $validated['status'];
|
||||
ActivityLog::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'user_id' => null,
|
||||
'user_id' => request()->user()?->id,
|
||||
'deal_id' => $deal->id,
|
||||
'event' => ActivityLog::EVENT_DEAL_STATUS_CHANGED,
|
||||
'context' => ['from' => $previousStatus, 'to' => $validated['status'], 'source' => 'manual'],
|
||||
'ip_address' => request()->ip(),
|
||||
'user_agent' => request()->userAgent(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -534,10 +540,12 @@ class DealController extends Controller
|
||||
|
||||
ActivityLog::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'user_id' => null, // на prod — request()->user()->id
|
||||
'user_id' => request()->user()?->id,
|
||||
'deal_id' => $deal->id,
|
||||
'event' => ActivityLog::EVENT_DEAL_CREATED,
|
||||
'context' => ['source' => 'manual'],
|
||||
'ip_address' => request()->ip(),
|
||||
'user_agent' => request()->userAgent(),
|
||||
]);
|
||||
|
||||
return $deal;
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\Deal;
|
||||
use App\Models\Project;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
uses(DatabaseTransactions::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->tenant = Tenant::factory()->create([
|
||||
'balance_leads' => 100,
|
||||
]);
|
||||
$this->user = User::factory()->for($this->tenant)->create();
|
||||
$this->actingAs($this->user);
|
||||
|
||||
DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
|
||||
$this->project = Project::factory()->for($this->tenant)->create();
|
||||
});
|
||||
|
||||
test('ActivityLog deal.created содержит user_id, ip_address, user_agent актора', function () {
|
||||
$r = $this->withServerVariables(['REMOTE_ADDR' => '10.1.2.3', 'HTTP_USER_AGENT' => 'TestBrowser/1.0'])
|
||||
->postJson('/api/deals', [
|
||||
'project_name' => 'Тест Attribution',
|
||||
'phone' => '+7 (999) 000-11-22',
|
||||
]);
|
||||
|
||||
$r->assertStatus(201);
|
||||
|
||||
DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
|
||||
$row = ActivityLog::where('deal_id', $r->json('deal.id'))
|
||||
->where('event', ActivityLog::EVENT_DEAL_CREATED)
|
||||
->first();
|
||||
|
||||
expect($row)->not->toBeNull();
|
||||
expect($row->user_id)->toBe($this->user->id);
|
||||
expect($row->ip_address)->toBe('10.1.2.3');
|
||||
expect($row->user_agent)->toBe('TestBrowser/1.0');
|
||||
});
|
||||
|
||||
test('ActivityLog deal.commented содержит user_id, ip_address, user_agent актора', function () {
|
||||
$deal = Deal::factory()->for($this->tenant)->for($this->project)->create(['comment' => 'old']);
|
||||
|
||||
$r = $this->withServerVariables(['REMOTE_ADDR' => '10.1.2.4', 'HTTP_USER_AGENT' => 'TestBrowser/2.0'])
|
||||
->patchJson('/api/deals/'.$deal->id, [
|
||||
'comment' => 'Новый комментарий',
|
||||
]);
|
||||
|
||||
$r->assertStatus(200);
|
||||
|
||||
DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
|
||||
$row = ActivityLog::where('deal_id', $deal->id)
|
||||
->where('event', 'deal.commented')
|
||||
->first();
|
||||
|
||||
expect($row)->not->toBeNull();
|
||||
expect($row->user_id)->toBe($this->user->id);
|
||||
expect($row->ip_address)->toBe('10.1.2.4');
|
||||
expect($row->user_agent)->toBe('TestBrowser/2.0');
|
||||
});
|
||||
|
||||
test('ActivityLog deal.assigned содержит user_id, ip_address, user_agent актора', function () {
|
||||
$manager = User::factory()->for($this->tenant)->create(['is_active' => true]);
|
||||
$deal = Deal::factory()->for($this->tenant)->for($this->project)->create([
|
||||
'manager_id' => null,
|
||||
'assigned_at' => null,
|
||||
]);
|
||||
|
||||
$r = $this->withServerVariables(['REMOTE_ADDR' => '10.1.2.5', 'HTTP_USER_AGENT' => 'TestBrowser/3.0'])
|
||||
->patchJson('/api/deals/'.$deal->id, [
|
||||
'manager_id' => $manager->id,
|
||||
]);
|
||||
|
||||
$r->assertStatus(200);
|
||||
|
||||
DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
|
||||
$row = ActivityLog::where('deal_id', $deal->id)
|
||||
->where('event', ActivityLog::EVENT_DEAL_ASSIGNED)
|
||||
->first();
|
||||
|
||||
expect($row)->not->toBeNull();
|
||||
expect($row->user_id)->toBe($this->user->id);
|
||||
expect($row->ip_address)->toBe('10.1.2.5');
|
||||
expect($row->user_agent)->toBe('TestBrowser/3.0');
|
||||
});
|
||||
|
||||
test('ActivityLog deal.status_changed содержит user_id, ip_address, user_agent актора', function () {
|
||||
$deal = Deal::factory()->for($this->tenant)->for($this->project)->create(['status' => 'new']);
|
||||
|
||||
$r = $this->withServerVariables(['REMOTE_ADDR' => '10.1.2.6', 'HTTP_USER_AGENT' => 'TestBrowser/4.0'])
|
||||
->patchJson('/api/deals/'.$deal->id, [
|
||||
'status' => 'won',
|
||||
]);
|
||||
|
||||
$r->assertStatus(200);
|
||||
|
||||
DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
|
||||
$row = ActivityLog::where('deal_id', $deal->id)
|
||||
->where('event', ActivityLog::EVENT_DEAL_STATUS_CHANGED)
|
||||
->first();
|
||||
|
||||
expect($row)->not->toBeNull();
|
||||
expect($row->user_id)->toBe($this->user->id);
|
||||
expect($row->ip_address)->toBe('10.1.2.6');
|
||||
expect($row->user_agent)->toBe('TestBrowser/4.0');
|
||||
});
|
||||
Reference in New Issue
Block a user