++ Prima versione del designer piantine + Skill grafica
This commit is contained in:
198
app/Services/Piantine/PiantinaPdfService.php
Normal file
198
app/Services/Piantine/PiantinaPdfService.php
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Piantine;
|
||||
|
||||
use App\Models\Territorio;
|
||||
use App\Models\TerritorioMappa;
|
||||
use App\Models\TerritorioPdfVersione;
|
||||
use App\Services\TerritorioThumbnailService;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use RuntimeException;
|
||||
use setasign\Fpdi\Fpdi;
|
||||
|
||||
/**
|
||||
* Compone il PDF del territorio sul modulo ufficiale S-12 e lo sostituisce
|
||||
* nel gestionale conservando la versione precedente.
|
||||
*
|
||||
* Pagina 1: il modulo S-12 originale (resources/pdf/S-12_I.pdf) importato
|
||||
* com'è, con Località, numero e piantina sovrapposti negli spazi previsti.
|
||||
* Pagina 2: i confini scritti (campo `confini` del territorio).
|
||||
*/
|
||||
class PiantinaPdfService
|
||||
{
|
||||
public function __construct(
|
||||
private PiantineArea $area,
|
||||
private TerritorioThumbnailService $thumbnails,
|
||||
) {
|
||||
}
|
||||
|
||||
/** Il PDF completo, come stringa binaria. */
|
||||
public function genera(Territorio $territorio, TerritorioMappa $mappa): string
|
||||
{
|
||||
if (! $mappa->image_path || ! Storage::disk('public')->exists($mappa->image_path)) {
|
||||
throw new RuntimeException('Prima di generare il PDF va generata l\'immagine della piantina.');
|
||||
}
|
||||
|
||||
$immagine = Storage::disk('public')->path($mappa->image_path);
|
||||
$dimensioni = getimagesize($immagine);
|
||||
if ($dimensioni === false) {
|
||||
throw new RuntimeException('L\'immagine della piantina non è leggibile: rigenerala.');
|
||||
}
|
||||
[$imgPxW, $imgPxH] = $dimensioni;
|
||||
|
||||
$pdf = new Fpdi();
|
||||
$pdf->setSourceFile(resource_path('pdf/S-12_I.pdf'));
|
||||
$template = $pdf->importPage(1);
|
||||
$misure = $pdf->getTemplateSize($template);
|
||||
|
||||
$w = $misure['width'];
|
||||
$h = $misure['height'];
|
||||
|
||||
$pdf->AddPage($misure['orientation'], [$w, $h]);
|
||||
$pdf->useTemplate($template);
|
||||
|
||||
// Intestazione: i valori si appoggiano sulle righe punteggiate del
|
||||
// modulo. Posizioni in proporzione alla pagina, tarate sull'S-12.
|
||||
$pdf->SetFont('Helvetica', 'B', 10);
|
||||
$pdf->SetTextColor(0, 0, 0);
|
||||
|
||||
// Tarato sull'S-12 reale: i valori si appoggiano alle righe
|
||||
// punteggiate senza coprire le etichette "Località" e "Terr. N.".
|
||||
$headerY = $h * 0.112;
|
||||
$pdf->SetXY($w * 0.155, $headerY);
|
||||
$pdf->Cell($w * 0.48, 5, $this->latin($this->area->localita()), 0, 0, 'L');
|
||||
|
||||
$pdf->SetXY($w * 0.875, $headerY);
|
||||
$pdf->Cell($w * 0.10, 5, $this->latin($territorio->numero), 0, 0, 'C');
|
||||
|
||||
// Piantina nello spazio centrale, adattata mantenendo le proporzioni:
|
||||
// sotto l'intestazione, sopra la dicitura "(Incollate qui sopra...)".
|
||||
$boxX = $w * 0.045;
|
||||
$boxY = $h * 0.175;
|
||||
$boxW = $w * 0.91;
|
||||
$boxH = $h * 0.565;
|
||||
|
||||
$scala = min($boxW / $imgPxW, $boxH / $imgPxH);
|
||||
$imgW = $imgPxW * $scala;
|
||||
$imgH = $imgPxH * $scala;
|
||||
$pdf->Image(
|
||||
$immagine,
|
||||
$boxX + ($boxW - $imgW) / 2,
|
||||
$boxY + ($boxH - $imgH) / 2,
|
||||
$imgW,
|
||||
$imgH,
|
||||
'PNG'
|
||||
);
|
||||
|
||||
// Pagina 2: i confini scritti.
|
||||
$pdf->AddPage($misure['orientation'], [$w, $h]);
|
||||
$pdf->SetFont('Helvetica', 'B', 13);
|
||||
$pdf->SetXY($w * 0.045, $h * 0.06);
|
||||
$pdf->Cell($w * 0.91, 7, $this->latin('Confini del territorio N. ' . $territorio->numero), 0, 2, 'C');
|
||||
|
||||
$pdf->SetFont('Helvetica', '', 11);
|
||||
$pdf->SetXY($w * 0.045, $h * 0.16);
|
||||
$confini = trim((string) $territorio->confini);
|
||||
$pdf->MultiCell(
|
||||
$w * 0.91,
|
||||
6,
|
||||
$this->latin($confini !== '' ? $confini : 'Confini non ancora descritti.')
|
||||
);
|
||||
|
||||
return $pdf->Output('S');
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera il PDF e lo rende quello ufficiale del territorio, archiviando
|
||||
* l'eventuale PDF precedente nello storico versioni.
|
||||
*/
|
||||
public function sostituisci(Territorio $territorio, TerritorioMappa $mappa, string $origine = 'piantine'): void
|
||||
{
|
||||
$binario = $this->genera($territorio, $mappa);
|
||||
$disk = Storage::disk('public');
|
||||
|
||||
$this->archiviaCorrente($territorio, $origine);
|
||||
|
||||
$nuovoPath = 'territori-pdf/piantina-' . $territorio->id . '-' . now()->format('Ymd-His') . '.pdf';
|
||||
$disk->put($nuovoPath, $binario);
|
||||
|
||||
$territorio->update([
|
||||
'pdf_path' => $nuovoPath,
|
||||
'thumbnail_path' => $this->thumbnails->generate($nuovoPath),
|
||||
]);
|
||||
|
||||
activity()
|
||||
->causedBy(auth()->user())
|
||||
->performedOn($territorio)
|
||||
->log('piantina_pdf_sostituito');
|
||||
}
|
||||
|
||||
/**
|
||||
* Rimette in vigore una versione archiviata; il PDF attuale finisce a sua
|
||||
* volta nello storico, quindi il ripristino è sempre reversibile.
|
||||
*/
|
||||
public function ripristina(TerritorioPdfVersione $versione): void
|
||||
{
|
||||
$disk = Storage::disk('public');
|
||||
$territorio = $versione->territorio;
|
||||
|
||||
if (! $territorio) {
|
||||
throw new RuntimeException('La versione non appartiene più a nessun territorio.');
|
||||
}
|
||||
|
||||
if (! $disk->exists($versione->pdf_path)) {
|
||||
throw new RuntimeException('Il file di questa versione non esiste più sul disco.');
|
||||
}
|
||||
|
||||
$this->archiviaCorrente($territorio, 'ripristino');
|
||||
|
||||
$nuovoPath = 'territori-pdf/ripristino-' . $territorio->id . '-' . now()->format('Ymd-His') . '.pdf';
|
||||
$disk->move($versione->pdf_path, $nuovoPath);
|
||||
|
||||
$territorio->update([
|
||||
'pdf_path' => $nuovoPath,
|
||||
'thumbnail_path' => $this->thumbnails->generate($nuovoPath),
|
||||
]);
|
||||
|
||||
$versione->delete();
|
||||
|
||||
activity()
|
||||
->causedBy(auth()->user())
|
||||
->performedOn($territorio)
|
||||
->log('piantina_pdf_ripristinato');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sposta il PDF attuale del territorio (se esiste) nello storico.
|
||||
*/
|
||||
private function archiviaCorrente(Territorio $territorio, string $origine): void
|
||||
{
|
||||
$disk = Storage::disk('public');
|
||||
|
||||
if (! $territorio->pdf_path || ! $disk->exists($territorio->pdf_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$versionePath = 'territori-pdf-versioni/territorio-' . $territorio->id
|
||||
. '-' . now()->format('Ymd-His') . '-' . substr(uniqid(), -5) . '.pdf';
|
||||
|
||||
$disk->makeDirectory('territori-pdf-versioni');
|
||||
$disk->move($territorio->pdf_path, $versionePath);
|
||||
|
||||
TerritorioPdfVersione::create([
|
||||
'territorio_id' => $territorio->id,
|
||||
'pdf_path' => $versionePath,
|
||||
'thumbnail_path' => $territorio->thumbnail_path,
|
||||
'origine' => $origine,
|
||||
'sostituito_il' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
/** FPDF ragiona in windows-1252: gli accenti UTF-8 vanno convertiti. */
|
||||
private function latin(string $testo): string
|
||||
{
|
||||
$convertito = @iconv('UTF-8', 'windows-1252//TRANSLIT', $testo);
|
||||
|
||||
return $convertito !== false ? $convertito : $testo;
|
||||
}
|
||||
}
|
||||
52
app/Services/Piantine/PiantinaRenderer.php
Normal file
52
app/Services/Piantine/PiantinaRenderer.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Piantine;
|
||||
|
||||
use App\Models\TerritorioMappa;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Client del container `map-renderer`: gli passa la geometria di un
|
||||
* territorio, riceve il PNG ad alta definizione e lo archivia sul disco
|
||||
* pubblico accanto a PDF e miniature.
|
||||
*/
|
||||
class PiantinaRenderer
|
||||
{
|
||||
public function render(TerritorioMappa $mappa): string
|
||||
{
|
||||
$geometry = $mappa->geometria();
|
||||
|
||||
if ($geometry === null) {
|
||||
throw new RuntimeException('La geometria salvata per questo territorio non è leggibile: ridisegna il poligono.');
|
||||
}
|
||||
|
||||
$response = Http::timeout((int) config('piantine.renderer_timeout'))
|
||||
->post(rtrim((string) config('piantine.renderer_url'), '/') . '/render', [
|
||||
'geometry' => $geometry,
|
||||
'marginRatio' => (float) config('piantine.margin_ratio'),
|
||||
'maxSide' => (int) config('piantine.max_side'),
|
||||
]);
|
||||
|
||||
if (! $response->successful()) {
|
||||
$dettaglio = $response->json('error') ?? ('HTTP ' . $response->status());
|
||||
throw new RuntimeException('Il renderer delle mappe ha risposto con un errore: ' . $dettaglio);
|
||||
}
|
||||
|
||||
$png = $response->body();
|
||||
if ($png === '' || ! str_starts_with($png, "\x89PNG")) {
|
||||
throw new RuntimeException('Il renderer delle mappe non ha restituito un PNG valido.');
|
||||
}
|
||||
|
||||
$path = 'territori-mappe/territorio-' . $mappa->territorio_id . '.png';
|
||||
Storage::disk('public')->put($path, $png);
|
||||
|
||||
$mappa->update([
|
||||
'image_path' => $path,
|
||||
'rendered_at' => now(),
|
||||
]);
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
189
app/Services/Piantine/PiantineArea.php
Normal file
189
app/Services/Piantine/PiantineArea.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Piantine;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* L'area della città su cui si disegnano i territori.
|
||||
*
|
||||
* Vive su file (storage/app/piantine/), non su tabelle: c'è una sola area
|
||||
* attiva, caricare un KML nuovo la sostituisce e i poligoni già disegnati
|
||||
* restano al loro posto. Stando dentro storage/, entra nei backup completi
|
||||
* senza bisogno di altro.
|
||||
*/
|
||||
class PiantineArea
|
||||
{
|
||||
private const DIR = 'piantine';
|
||||
private const FILE_KML = 'piantine/area.kml';
|
||||
private const FILE_GEOJSON = 'piantine/area.geojson';
|
||||
private const FILE_CONFIG = 'piantine/config.json';
|
||||
|
||||
/**
|
||||
* Registra un nuovo KML come area attiva.
|
||||
*
|
||||
* @return int Numero di geometrie riconosciute nel file.
|
||||
*/
|
||||
public function salvaKml(string $contenuto, string $nomeOriginale): int
|
||||
{
|
||||
$geojson = $this->kmlInGeoJson($contenuto);
|
||||
$quante = count($geojson['features']);
|
||||
|
||||
if ($quante === 0) {
|
||||
throw new RuntimeException(
|
||||
'Nel file KML non è stato trovato nessun poligono o linea: '
|
||||
. 'serve almeno un perimetro che delimiti l\'area della città.'
|
||||
);
|
||||
}
|
||||
|
||||
Storage::disk('local')->makeDirectory(self::DIR);
|
||||
Storage::disk('local')->put(self::FILE_KML, $contenuto);
|
||||
Storage::disk('local')->put(self::FILE_GEOJSON, json_encode($geojson, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$config = $this->config();
|
||||
$config['kml_nome'] = $nomeOriginale;
|
||||
$config['aggiornato_il'] = now()->toDateTimeString();
|
||||
$this->salvaConfig($config);
|
||||
|
||||
return $quante;
|
||||
}
|
||||
|
||||
/** GeoJSON FeatureCollection dell'area attiva, o null se mai caricata. */
|
||||
public function area(): ?array
|
||||
{
|
||||
if (! Storage::disk('local')->exists(self::FILE_GEOJSON)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = json_decode(Storage::disk('local')->get(self::FILE_GEOJSON), true);
|
||||
|
||||
return is_array($decoded) ? $decoded : null;
|
||||
}
|
||||
|
||||
public function config(): array
|
||||
{
|
||||
if (! Storage::disk('local')->exists(self::FILE_CONFIG)) {
|
||||
return ['localita' => null, 'kml_nome' => null, 'aggiornato_il' => null];
|
||||
}
|
||||
|
||||
$decoded = json_decode(Storage::disk('local')->get(self::FILE_CONFIG), true);
|
||||
|
||||
return is_array($decoded)
|
||||
? $decoded + ['localita' => null, 'kml_nome' => null, 'aggiornato_il' => null]
|
||||
: ['localita' => null, 'kml_nome' => null, 'aggiornato_il' => null];
|
||||
}
|
||||
|
||||
/** La "Località" stampata sull'intestazione del modulo S-12. */
|
||||
public function localita(): string
|
||||
{
|
||||
$config = $this->config();
|
||||
|
||||
if (! empty($config['localita'])) {
|
||||
return $config['localita'];
|
||||
}
|
||||
|
||||
return (string) \App\Models\Setting::getValue('congregazione_nome', '');
|
||||
}
|
||||
|
||||
public function impostaLocalita(?string $localita): void
|
||||
{
|
||||
$config = $this->config();
|
||||
$config['localita'] = $localita !== null && trim($localita) !== '' ? trim($localita) : null;
|
||||
$this->salvaConfig($config);
|
||||
}
|
||||
|
||||
private function salvaConfig(array $config): void
|
||||
{
|
||||
Storage::disk('local')->makeDirectory(self::DIR);
|
||||
Storage::disk('local')->put(self::FILE_CONFIG, json_encode($config, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
// ─── KML → GeoJSON ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Estrae poligoni e linee dai Placemark di un KML (anche annidati in
|
||||
* Document/Folder e dentro MultiGeometry) e li restituisce come
|
||||
* FeatureCollection GeoJSON.
|
||||
*
|
||||
* Le xpath usano local-name(): i prefissi registrati con
|
||||
* registerXPathNamespace non si ereditano sui nodi restituiti da xpath(),
|
||||
* e i KML in circolazione dichiarano il namespace in almeno tre modi
|
||||
* diversi (OGC, Google legacy, nessuno). Cosi' funzionano tutti.
|
||||
*/
|
||||
public function kmlInGeoJson(string $kml): array
|
||||
{
|
||||
$precedente = libxml_use_internal_errors(true);
|
||||
$xml = simplexml_load_string($kml);
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors($precedente);
|
||||
|
||||
if ($xml === false) {
|
||||
throw new RuntimeException('Il file non è un XML/KML leggibile.');
|
||||
}
|
||||
|
||||
$placemarks = $xml->xpath("//*[local-name()='Placemark']") ?: [];
|
||||
|
||||
$features = [];
|
||||
foreach ($placemarks as $placemark) {
|
||||
$nodiNome = $placemark->xpath("./*[local-name()='name']") ?: [];
|
||||
$nome = trim((string) ($nodiNome[0] ?? ''));
|
||||
|
||||
foreach ($this->geometrieDelPlacemark($placemark) as $geometry) {
|
||||
$features[] = [
|
||||
'type' => 'Feature',
|
||||
'properties' => ['name' => $nome],
|
||||
'geometry' => $geometry,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return ['type' => 'FeatureCollection', 'features' => $features];
|
||||
}
|
||||
|
||||
private function geometrieDelPlacemark(\SimpleXMLElement $placemark): array
|
||||
{
|
||||
$geometrie = [];
|
||||
|
||||
foreach ($placemark->xpath(".//*[local-name()='Polygon']") ?: [] as $polygon) {
|
||||
$anelli = [];
|
||||
foreach ($polygon->xpath(".//*[local-name()='LinearRing']/*[local-name()='coordinates']") ?: [] as $coordinates) {
|
||||
$anello = $this->coordinateKml((string) $coordinates);
|
||||
if (count($anello) >= 3) {
|
||||
$anelli[] = $anello;
|
||||
}
|
||||
}
|
||||
if ($anelli !== []) {
|
||||
$geometrie[] = ['type' => 'Polygon', 'coordinates' => $anelli];
|
||||
}
|
||||
}
|
||||
|
||||
// Le LineString servono ai KML che tracciano il perimetro come linea
|
||||
// invece che come poligono chiuso.
|
||||
foreach ($placemark->xpath(".//*[local-name()='LineString']/*[local-name()='coordinates']") ?: [] as $coordinates) {
|
||||
$punti = $this->coordinateKml((string) $coordinates);
|
||||
if (count($punti) >= 2) {
|
||||
$geometrie[] = ['type' => 'LineString', 'coordinates' => $punti];
|
||||
}
|
||||
}
|
||||
|
||||
return $geometrie;
|
||||
}
|
||||
|
||||
/** "lng,lat[,alt] lng,lat[,alt] ..." → [[lng, lat], ...] */
|
||||
private function coordinateKml(string $testo): array
|
||||
{
|
||||
$punti = [];
|
||||
foreach (preg_split('/\s+/', trim($testo)) ?: [] as $tripla) {
|
||||
if ($tripla === '') {
|
||||
continue;
|
||||
}
|
||||
$parti = explode(',', $tripla);
|
||||
if (count($parti) >= 2 && is_numeric($parti[0]) && is_numeric($parti[1])) {
|
||||
$punti[] = [(float) $parti[0], (float) $parti[1]];
|
||||
}
|
||||
}
|
||||
|
||||
return $punti;
|
||||
}
|
||||
}
|
||||
152
app/Services/Piantine/PiantineOperationState.php
Normal file
152
app/Services/Piantine/PiantineOperationState.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Piantine;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/**
|
||||
* Stato della rigenerazione massiva delle piantine.
|
||||
*
|
||||
* Stessa impostazione di BackupOperationState e TerritorioPdfImportState:
|
||||
* la pagina segue il lavoro in coda interrogando questo stato, la barra si
|
||||
* muove su unità di lavoro completate davvero (territori renderizzati).
|
||||
* Un'operazione alla volta: due rigenerazioni parallele martellerebbero il
|
||||
* renderer e il tile server senza alcun beneficio.
|
||||
*/
|
||||
class PiantineOperationState
|
||||
{
|
||||
protected int $ttlSeconds = 86400;
|
||||
|
||||
private const MAX_LOG = 60;
|
||||
private const CHIAVE_CORRENTE = 'piantine-op:corrente';
|
||||
private const TTL_CORRENTE = 3600;
|
||||
|
||||
public function initialize(string $id, int $total, bool $anchePdf): array
|
||||
{
|
||||
$state = [
|
||||
'id' => $id,
|
||||
'status' => 'queued',
|
||||
'anche_pdf' => $anchePdf,
|
||||
'total' => $total,
|
||||
'processed' => 0,
|
||||
'falliti' => 0,
|
||||
'logs' => [],
|
||||
'error' => null,
|
||||
'started_at' => null,
|
||||
'finished_at' => null,
|
||||
];
|
||||
|
||||
$this->put($id, $state);
|
||||
Cache::put(self::CHIAVE_CORRENTE, $id, self::TTL_CORRENTE);
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
public function corrente(): ?string
|
||||
{
|
||||
$id = Cache::get(self::CHIAVE_CORRENTE);
|
||||
|
||||
if (! is_string($id) || $id === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stato = $this->get($id);
|
||||
|
||||
if ($stato === null || in_array($stato['status'] ?? '', ['completed', 'failed'], true)) {
|
||||
Cache::forget(self::CHIAVE_CORRENTE);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function get(string $id): ?array
|
||||
{
|
||||
return Cache::get($this->key($id));
|
||||
}
|
||||
|
||||
public function put(string $id, array $state): void
|
||||
{
|
||||
Cache::put($this->key($id), $state, $this->ttlSeconds);
|
||||
}
|
||||
|
||||
public function update(string $id, callable $callback): void
|
||||
{
|
||||
$state = $this->get($id);
|
||||
|
||||
if (! $state) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->put($id, $callback($state) ?? $state);
|
||||
}
|
||||
|
||||
public function log(string $id, string $message): void
|
||||
{
|
||||
$this->update($id, function (array $state) use ($message) {
|
||||
$state['logs'][] = '[' . now()->format('H:i:s') . '] ' . $message;
|
||||
$state['logs'] = array_slice($state['logs'], -self::MAX_LOG);
|
||||
|
||||
return $state;
|
||||
});
|
||||
}
|
||||
|
||||
public function avanza(string $id, int $processed, int $falliti): void
|
||||
{
|
||||
$this->update($id, function (array $state) use ($processed, $falliti) {
|
||||
$state['processed'] = $processed;
|
||||
$state['falliti'] = $falliti;
|
||||
|
||||
return $state;
|
||||
});
|
||||
}
|
||||
|
||||
public function markRunning(string $id): void
|
||||
{
|
||||
$this->update($id, function (array $state) {
|
||||
$state['status'] = 'running';
|
||||
$state['started_at'] ??= now()->toDateTimeString();
|
||||
|
||||
return $state;
|
||||
});
|
||||
}
|
||||
|
||||
public function markCompleted(string $id): void
|
||||
{
|
||||
$this->update($id, function (array $state) {
|
||||
$state['status'] = 'completed';
|
||||
$state['finished_at'] = now()->toDateTimeString();
|
||||
|
||||
return $state;
|
||||
});
|
||||
|
||||
$this->liberaCorrente($id);
|
||||
}
|
||||
|
||||
public function markFailed(string $id, string $error): void
|
||||
{
|
||||
$this->update($id, function (array $state) use ($error) {
|
||||
$state['status'] = 'failed';
|
||||
$state['error'] = $error;
|
||||
$state['finished_at'] = now()->toDateTimeString();
|
||||
|
||||
return $state;
|
||||
});
|
||||
|
||||
$this->log($id, 'ERRORE: ' . $error);
|
||||
$this->liberaCorrente($id);
|
||||
}
|
||||
|
||||
private function liberaCorrente(string $id): void
|
||||
{
|
||||
if (Cache::get(self::CHIAVE_CORRENTE) === $id) {
|
||||
Cache::forget(self::CHIAVE_CORRENTE);
|
||||
}
|
||||
}
|
||||
|
||||
protected function key(string $id): string
|
||||
{
|
||||
return 'piantine-op:' . $id;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user