$pendingImages */ public function __construct( protected array $pendingImages, protected string $imageDir, protected ?int $companyId = null, protected ?int $userId = null ) {} public function handle(ProductCatalogService $catalogService): void { $success = 0; $missing = []; try { foreach ($this->pendingImages as $item) { $product = Product::withoutGlobalScopes()->find($item['product_id']); if (!$product) { $missing[] = $item; continue; } $path = rtrim($this->imageDir, '/') . '/' . basename($item['filename']); if (!is_file($path) || getimagesize($path) === false) { $missing[] = $item; continue; } try { $oldImage = $product->image_url; $file = new UploadedFile($path, basename($path), null, null, true); $storedPath = $this->storeAsWebp($file, 'products', 80, 320, 320); $product->update(['image_url' => Storage::url($storedPath)]); if ($oldImage) { Storage::disk('public')->delete(str_replace('/storage/', '', $oldImage)); } $success++; } catch (\Throwable $e) { $missing[] = $item; } } // 圖片已更新,重建目錄快取讓機台拿到新圖 if ($this->companyId) { $catalogService->rebuildCache($this->companyId); } SystemOperationLog::create([ 'company_id' => $this->companyId, 'user_id' => $this->userId, 'module' => 'product', 'action' => 'update', 'target_type' => Product::class, 'note' => 'product_import_images_processed', 'new_values' => [ 'image_success' => $success, 'image_missing' => count($missing), 'missing_detail' => $missing, ], ]); } finally { // 無論成敗,清掉暫存圖片資料夾 if (is_dir($this->imageDir)) { File::deleteDirectory($this->imageDir); } } } }