|
|
@@ -0,0 +1,159 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace Longyi\Core\Services;
|
|
|
+
|
|
|
+use Illuminate\Http\UploadedFile;
|
|
|
+use Illuminate\Support\Facades\Log;
|
|
|
+use Illuminate\Support\Facades\Storage;
|
|
|
+use Longyi\Core\Imports\ProductBasicInfoSpreadsheet;
|
|
|
+use Longyi\Core\Jobs\ImportProductBasicInfoJob;
|
|
|
+use Longyi\Core\Models\AdminNotification;
|
|
|
+use Longyi\Core\Models\ProductSyncImport;
|
|
|
+use Maatwebsite\Excel\Facades\Excel;
|
|
|
+use Webkul\User\Models\Admin;
|
|
|
+
|
|
|
+class ProductSyncImportService
|
|
|
+{
|
|
|
+ public const DISK = 'local';
|
|
|
+
|
|
|
+ public function __construct(protected ProductBasicInfoSyncService $syncService) {}
|
|
|
+
|
|
|
+ public function queue(UploadedFile $file, ?Admin $admin, string $locale): ProductSyncImport
|
|
|
+ {
|
|
|
+ $path = $file->store('product-sync', self::DISK);
|
|
|
+
|
|
|
+ $import = ProductSyncImport::query()->create([
|
|
|
+ 'admin_id' => $admin?->id,
|
|
|
+ 'original_filename' => $file->getClientOriginalName(),
|
|
|
+ 'disk_path' => $path,
|
|
|
+ 'status' => ProductSyncImport::STATUS_PENDING,
|
|
|
+ 'locale' => $locale,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ ImportProductBasicInfoJob::dispatch($import->id, $locale);
|
|
|
+
|
|
|
+ return $import;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function process(ProductSyncImport $import, bool $reindex = true): ProductSyncImport
|
|
|
+ {
|
|
|
+ if ($import->status !== ProductSyncImport::STATUS_PENDING) {
|
|
|
+ return $import;
|
|
|
+ }
|
|
|
+
|
|
|
+ $import->update([
|
|
|
+ 'status' => ProductSyncImport::STATUS_PROCESSING,
|
|
|
+ 'started_at' => now(),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (! Storage::disk(self::DISK)->exists($import->disk_path)) {
|
|
|
+ throw new ProductBasicInfoSyncException(trans('longyi::app.product-sync.errors.empty-file'));
|
|
|
+ }
|
|
|
+
|
|
|
+ $sheets = Excel::toArray(
|
|
|
+ new ProductBasicInfoSpreadsheet,
|
|
|
+ $import->disk_path,
|
|
|
+ self::DISK
|
|
|
+ );
|
|
|
+
|
|
|
+ $result = $this->syncService->syncFromTable($sheets[0] ?? [], $reindex);
|
|
|
+
|
|
|
+ $import->update([
|
|
|
+ 'status' => ProductSyncImport::STATUS_COMPLETED,
|
|
|
+ 'updated_count' => $result->updated,
|
|
|
+ 'failed_count' => $result->failed,
|
|
|
+ 'errors' => $result->errors,
|
|
|
+ 'message' => null,
|
|
|
+ 'finished_at' => now(),
|
|
|
+ ]);
|
|
|
+ } catch (ProductBasicInfoSyncException $e) {
|
|
|
+ $this->markFailed($import, $e->getMessage());
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Log::error('Product sync import failed.', [
|
|
|
+ 'import_id' => $import->id,
|
|
|
+ 'message' => $e->getMessage(),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $this->markFailed($import, $e->getMessage());
|
|
|
+ } finally {
|
|
|
+ $this->deleteStoredFile($import);
|
|
|
+ }
|
|
|
+
|
|
|
+ $import->refresh();
|
|
|
+
|
|
|
+ $this->notify($import);
|
|
|
+
|
|
|
+ return $import;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function markFailedAndNotify(ProductSyncImport $import, string $message): void
|
|
|
+ {
|
|
|
+ if ($import->isFinished()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->markFailed($import, $message);
|
|
|
+ $this->deleteStoredFile($import);
|
|
|
+ $this->notify($import->fresh());
|
|
|
+ }
|
|
|
+
|
|
|
+ public function notify(ProductSyncImport $import): void
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ AdminNotification::query()->create([
|
|
|
+ 'type' => AdminNotification::TYPE_PRODUCT_SYNC,
|
|
|
+ 'read' => 0,
|
|
|
+ 'order_id' => null,
|
|
|
+ 'title' => $import->summaryMessage(),
|
|
|
+ 'description' => $import->original_filename,
|
|
|
+ 'route' => 'admin.catalog.products.sync.index',
|
|
|
+ 'admin_id' => $import->admin_id,
|
|
|
+ ]);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Log::error('Product sync import notification failed.', [
|
|
|
+ 'import_id' => $import->id,
|
|
|
+ 'message' => $e->getMessage(),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return list<ProductSyncImport>
|
|
|
+ */
|
|
|
+ public function recentForAdmin(int $adminId, int $limit = 10): array
|
|
|
+ {
|
|
|
+ return ProductSyncImport::query()
|
|
|
+ ->where('admin_id', $adminId)
|
|
|
+ ->latest('id')
|
|
|
+ ->limit($limit)
|
|
|
+ ->get()
|
|
|
+ ->all();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function markFailed(ProductSyncImport $import, string $message): void
|
|
|
+ {
|
|
|
+ $import->update([
|
|
|
+ 'status' => ProductSyncImport::STATUS_FAILED,
|
|
|
+ 'message' => $message,
|
|
|
+ 'finished_at' => now(),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function deleteStoredFile(ProductSyncImport $import): void
|
|
|
+ {
|
|
|
+ if ($import->disk_path === '' || $import->disk_path === null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Storage::disk(self::DISK)->delete($import->disk_path);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Log::warning('Product sync import file could not be deleted.', [
|
|
|
+ 'import_id' => $import->id,
|
|
|
+ 'path' => $import->disk_path,
|
|
|
+ 'message' => $e->getMessage(),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|