feat(audit): ProjectService writes tenant_operations_log on create/update/delete/bulk
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -9,12 +9,17 @@ use App\Jobs\SyncSupplierProjectJob;
|
||||
use App\Models\Project;
|
||||
use App\Models\SupplierProject;
|
||||
use App\Models\Tenant;
|
||||
use App\Services\Audit\OperationsLogger;
|
||||
use App\Services\Supplier\SupplierProjectGrouping;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ProjectService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly OperationsLogger $ops = new OperationsLogger(),
|
||||
) {}
|
||||
|
||||
public function update(Project $project, array $data): Project
|
||||
{
|
||||
// Immutable fields — silently drop (don't 422)
|
||||
@@ -63,6 +68,9 @@ class ProjectService
|
||||
|| array_key_exists('sms_keyword', $data);
|
||||
$oldAgnostic = $identifierFieldsTouched ? SupplierProjectGrouping::buildUniqueKeyAgnostic($project) : null;
|
||||
|
||||
// Snapshot before mutation for audit diff
|
||||
$before = array_intersect_key($project->toArray(), $data);
|
||||
|
||||
$project->update($data);
|
||||
|
||||
if ($oldAgnostic !== null) {
|
||||
@@ -72,6 +80,18 @@ class ProjectService
|
||||
}
|
||||
}
|
||||
|
||||
$this->ops->record(
|
||||
tenantId: $project->tenant_id,
|
||||
userId: auth()->id(),
|
||||
entityType: 'project',
|
||||
entityId: $project->id,
|
||||
event: 'project.updated',
|
||||
payloadBefore: $before,
|
||||
payloadAfter: $data,
|
||||
ip: request()->ip(),
|
||||
userAgent: request()->userAgent(),
|
||||
);
|
||||
|
||||
if ($needsResync) {
|
||||
SyncSupplierProjectJob::dispatch($project->id);
|
||||
}
|
||||
@@ -142,8 +162,25 @@ class ProjectService
|
||||
->pluck('supplier_project_id')
|
||||
->all();
|
||||
|
||||
// Snapshot ДО удаления — после delete() модель недоступна.
|
||||
$tenantId = $project->tenant_id;
|
||||
$projectId = $project->id;
|
||||
$snapshot = $project->toArray();
|
||||
|
||||
$project->delete(); // hard delete (Project без SoftDeletes); cascade чистит pivot + служебные.
|
||||
|
||||
$this->ops->record(
|
||||
tenantId: $tenantId,
|
||||
userId: auth()->id(),
|
||||
entityType: 'project',
|
||||
entityId: $projectId,
|
||||
event: 'project.deleted',
|
||||
payloadBefore: $snapshot,
|
||||
payloadAfter: null,
|
||||
ip: request()->ip(),
|
||||
userAgent: request()->userAgent(),
|
||||
);
|
||||
|
||||
if ($supplierProjectIds !== []) {
|
||||
DeleteSupplierProjectJob::dispatch(array_map('intval', $supplierProjectIds));
|
||||
}
|
||||
@@ -190,7 +227,7 @@ class ProjectService
|
||||
|
||||
$query = Project::where('tenant_id', $tenantId)->whereIn('id', $ids);
|
||||
|
||||
return match ($action) {
|
||||
$result = match ($action) {
|
||||
'pause' => $this->bulkPauseResume($query, false),
|
||||
'resume' => $this->bulkPauseResume($query, true),
|
||||
'delete' => $this->bulkDelete($query),
|
||||
@@ -198,6 +235,20 @@ class ProjectService
|
||||
'update_days' => $this->bulkUpdateDays($query, $payload),
|
||||
'update_limit' => $this->bulkUpdateLimit($query, $payload),
|
||||
};
|
||||
|
||||
$this->ops->record(
|
||||
tenantId: $tenantId,
|
||||
userId: auth()->id(),
|
||||
entityType: 'project',
|
||||
entityId: null,
|
||||
event: 'project.bulk_' . $action,
|
||||
payloadBefore: null,
|
||||
payloadAfter: array_merge(['ids' => $ids], $result),
|
||||
ip: request()->ip(),
|
||||
userAgent: request()->userAgent(),
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,6 +463,18 @@ class ProjectService
|
||||
|
||||
$project = Project::create($data);
|
||||
|
||||
$this->ops->record(
|
||||
tenantId: $project->tenant_id,
|
||||
userId: auth()->id(),
|
||||
entityType: 'project',
|
||||
entityId: $project->id,
|
||||
event: 'project.created',
|
||||
payloadBefore: null,
|
||||
payloadAfter: $project->toArray(),
|
||||
ip: request()->ip(),
|
||||
userAgent: request()->userAgent(),
|
||||
);
|
||||
|
||||
SyncSupplierProjectJob::dispatch($project->id);
|
||||
|
||||
return $project->fresh();
|
||||
|
||||
Reference in New Issue
Block a user