markRunning($this->importId); $stateService->appendLog($this->importId, 'Worker avviato. Inizio elaborazione dei PDF.'); $territoriMap = []; foreach (Territorio::withTrashed()->get() as $territorio) { $territoriMap[$this->normalizeTerritoryNumber($territorio->numero)] = $territorio; } $actor = User::find($this->actorId); $seenNumbers = []; try { foreach ($this->files as $file) { $originalName = $file['original_name'] ?? 'file-sconosciuto.pdf'; $stateService->increment($this->importId, 'processed'); $territoryMatch = $this->resolveTerritoryFromFilename($originalName, $territoriMap); if ($territoryMatch === null) { $stateService->increment($this->importId, 'skipped'); $stateService->appendLog($this->importId, '[SKIP] ' . $originalName . ' - nessun numero territorio riconosciuto nel nome file.'); $stateService->addIssue($this->importId, [ 'file' => $originalName, 'type' => 'no-match', 'message' => 'Nessun numero territorio riconosciuto nel nome file.', ]); continue; } if (($territoryMatch['type'] ?? 'single') === 'ambiguous') { $stateService->increment($this->importId, 'skipped'); $stateService->appendLog($this->importId, '[SKIP] ' . $originalName . ' - nome ambiguo, possibili territori: ' . implode(', ', $territoryMatch['matched_numbers']) . '.'); $stateService->addIssue($this->importId, [ 'file' => $originalName, 'type' => 'ambiguous', 'message' => 'Nome ambiguo.', 'matched_numbers' => $territoryMatch['matched_numbers'], ]); continue; } $normalizedNumber = $territoryMatch['normalized_number']; $matchedNumber = $territoryMatch['matched_number']; $territorio = $territoryMatch['territorio']; if (isset($seenNumbers[$normalizedNumber])) { $stateService->increment($this->importId, 'skipped'); $stateService->appendLog($this->importId, '[SKIP] ' . $originalName . ' - territorio ' . $matchedNumber . ' presente piu volte nella stessa importazione.'); $stateService->addIssue($this->importId, [ 'file' => $originalName, 'type' => 'duplicate-in-batch', 'message' => 'Territorio presente piu volte nella stessa importazione.', 'matched_numbers' => [$matchedNumber], ]); continue; } $seenNumbers[$normalizedNumber] = true; $sourcePath = Storage::disk('local')->path($file['stored_path']); if (! is_file($sourcePath)) { $stateService->increment($this->importId, 'errors'); $stateService->appendLog($this->importId, '[ERR] ' . $originalName . ' - file temporaneo non trovato.'); $stateService->addIssue($this->importId, [ 'file' => $originalName, 'type' => 'missing-temp-file', 'message' => 'File temporaneo non trovato.', 'matched_numbers' => [$matchedNumber], ]); continue; } try { if ($territorio->pdf_path) { Storage::disk('public')->delete($territorio->pdf_path); } if ($territorio->thumbnail_path) { $thumbnailService->delete($territorio->thumbnail_path); } $storedFilename = Str::slug('territorio-' . $territorio->numero) . '.pdf'; $publicPath = 'territori-pdf/' . $storedFilename; Storage::disk('public')->put($publicPath, file_get_contents($sourcePath)); $thumbnailPath = $thumbnailService->generate($publicPath); $territorio->update([ 'pdf_path' => $publicPath, 'thumbnail_path' => $thumbnailPath, ]); if ($actor) { activity()->causedBy($actor) ->performedOn($territorio) ->withProperties([ 'numero' => $territorio->numero, 'pdf' => $originalName, 'bulk_import' => true, ]) ->log('bulk_uploaded_pdf'); } $stateService->increment($this->importId, 'updated'); $stateService->appendLog( $this->importId, '[OK] ' . $originalName . ' - aggiornato territorio ' . $territorio->numero . ($thumbnailPath ? ' con thumbnail generata.' : ' ma la thumbnail non e stata generata.') ); } catch (\Throwable $exception) { $stateService->increment($this->importId, 'errors'); $stateService->appendLog($this->importId, '[ERR] ' . $originalName . ' - ' . $exception->getMessage()); $stateService->addIssue($this->importId, [ 'file' => $originalName, 'type' => 'processing-error', 'message' => $exception->getMessage(), 'matched_numbers' => [$matchedNumber], ]); } } $stateService->appendLog($this->importId, 'Import completato.'); $stateService->markCompleted($this->importId); } catch (\Throwable $exception) { $stateService->increment($this->importId, 'errors'); $stateService->appendLog($this->importId, '[ERR] Errore fatale del job: ' . $exception->getMessage()); $stateService->markFailed($this->importId); throw $exception; } finally { Storage::disk('local')->deleteDirectory('bulk-territori-imports/' . $this->importId); } } protected function resolveTerritoryFromFilename(string $filename, array $territoriMap): ?array { if ($territoriMap === []) { return null; } $basename = pathinfo($filename, PATHINFO_FILENAME); $normalizedBasename = mb_strtoupper($basename); $normalizedBasename = preg_replace('/[^A-Z0-9]+/u', ' ', $normalizedBasename); $normalizedBasename = trim(preg_replace('/\s+/', ' ', $normalizedBasename)); $territoryKeys = array_keys($territoriMap); usort($territoryKeys, function (string $left, string $right) { $lengthComparison = mb_strlen($right) <=> mb_strlen($left); if ($lengthComparison !== 0) { return $lengthComparison; } return strnatcasecmp($left, $right); }); $matches = []; foreach ($territoryKeys as $normalizedNumber) { if (! $this->filenameContainsTerritoryNumber($normalizedBasename, $normalizedNumber)) { continue; } $matches[] = [ 'normalized_number' => $normalizedNumber, 'matched_number' => $territoriMap[$normalizedNumber]->numero, 'territorio' => $territoriMap[$normalizedNumber], ]; } if ($matches === []) { return null; } if (count($matches) > 1) { return [ 'type' => 'ambiguous', 'matched_numbers' => array_values(array_map(fn(array $match) => $match['matched_number'], $matches)), ]; } return $matches[0]; } protected function filenameContainsTerritoryNumber(string $normalizedBasename, string $normalizedNumber): bool { $escapedNumber = preg_quote($normalizedNumber, '/'); if (preg_match('/^\d+$/', $normalizedNumber)) { $escapedNumber = '0*' . preg_quote(ltrim($normalizedNumber, '0') ?: '0', '/'); } return (bool) preg_match('/(^| )' . $escapedNumber . '(?= |$)/', $normalizedBasename); } protected function normalizeTerritoryNumber(string $number): string { $normalized = preg_replace('/\s+/', ' ', trim(mb_strtoupper($number))); if ($normalized !== '' && preg_match('/^\d+$/', $normalized)) { return ltrim($normalized, '0') ?: '0'; } return $normalized; } }