documents($actor)->where('id', $id)->lockForUpdate()->first(); abort_unless($document && $document->status !== 'deleted' && $document->deleted_at === null, 404); abort_unless(in_array($document->malware_scan_status, ['pending', 'inconclusive', 'failed'], true), 422, 'Só é possível repetir uma análise pendente, inconclusiva ou falhada. Para substituir um ficheiro, carregue uma nova versão.'); DB::afterCommit(function () use ($actor, $id, $job, $document, &$outcome): void { $prepared = false; try { $outcome = app(DocumentScanDelivery::class)->dispatch($job, function () use ($actor, $id, $job, $document, &$prepared): bool { $ready = DB::transaction(function () use ($actor, $id, $job, $document): bool { $currentActor = $actor->fresh(); if (! $currentActor) { return false; } $access = app(DocumentManagementAccess::class); if (! $access->full($currentActor) && ! $access->limited($currentActor) && ! $access->editorial($currentActor)) { return false; } $current = $access->documents($currentActor)->where('id', $id)->lockForUpdate()->first(); if (! $current || $current->status === 'deleted' || $current->deleted_at !== null || $current->sha256 !== $document->sha256 || $current->malware_scan_attempts !== $document->malware_scan_attempts || $current->malware_scan_status !== $document->malware_scan_status || $current->updated_at !== $document->updated_at) { return false; } DB::table('documents')->where('id', $id)->update(['status' => 'quarantined', 'malware_scan_status' => 'pending', 'malware_failure_reason' => null, 'updated_at' => now()]); $access->audit($currentActor, 'scan_retry_requested', ['document_id' => $id, 'reference' => $job->runIdentifier(), 'previous_scan_status' => $current->malware_scan_status]); return true; }); $prepared = $ready; return $ready; }); } catch (Throwable) { $outcome = 'unconfirmed'; try { if ($prepared) { DB::transaction(function () use ($id, $document): void { $current = DB::table('documents')->where('id', $id)->lockForUpdate()->first(); if ($current && $current->deleted_at === null && $current->status !== 'deleted' && $current->sha256 === $document->sha256 && $current->malware_scan_attempts === $document->malware_scan_attempts && $current->malware_scan_status === 'pending') { DB::table('documents')->where('id', $id)->update(['status' => 'quarantined', 'malware_scan_status' => 'failed', 'malware_failure_reason' => 'queue_dispatch_failed', 'updated_at' => now()]); } }); } } catch (Throwable) { // An uncertain write remains private and needs reconciliation. } try { Log::warning('Document scan retry dispatch requires verification.', ['reference' => $job->runIdentifier(), 'error_code' => 'scan_dispatch_unconfirmed']); } catch (Throwable) { } } }); }); return ['outcome' => $outcome, 'reference' => $job->runIdentifier()]; } }