Files
Investidor-V2/app/Services/OpportunityScanDispatch.php
T

51 lines
2.3 KiB
PHP

<?php
namespace App\Services;
use App\Jobs\ScanDocumentForMalware;
use App\Services\Malware\DocumentScanDelivery;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Throwable;
/** A saved resource remains saved when its post-commit scan dispatch cannot be confirmed. */
final class OpportunityScanDispatch
{
/** @param array<string,mixed>|null $context */
public function dispatch(string $documentId, ?array $context): void
{
$job = new ScanDocumentForMalware($documentId, $context);
$dispatch = function () use ($documentId, $context, $job): void {
try {
// Existing uniqueness, retries and scanner are retained.
app(DocumentScanDelivery::class)->dispatch($job);
} catch (Throwable) {
try {
DB::transaction(function () use ($documentId): void {
$document = DB::table('documents')->where('id', $documentId)->lockForUpdate()->first();
if ($document && $document->deleted_at === null && $document->status !== 'deleted' && $document->malware_scan_status === 'pending') {
DB::table('documents')->where('id', $documentId)->update(['status' => 'quarantined', 'malware_scan_status' => 'failed', 'malware_failure_reason' => 'queue_dispatch_failed', 'updated_at' => now()]);
}
});
} catch (Throwable) {
try {
Log::warning('Document scan dispatch state requires verification.', ['reference' => $job->runIdentifier(), 'error_code' => 'scan_dispatch_unconfirmed']);
} catch (Throwable) {
}
}
if ($context !== null) {
app(OpportunityTechnicalIncidents::class)->recordScan($context, $job->runIdentifier(), 'scan_dispatch_unconfirmed');
}
}
};
try {
DB::afterCommit($dispatch);
} catch (Throwable) {
// Registration itself failed: never escape after a successful resource commit.
if ($context !== null) {
app(OpportunityTechnicalIncidents::class)->recordScan($context, $job->runIdentifier(), 'scan_dispatch_unconfirmed');
}
}
}
}