ImportTechnicalDescriptions.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Console\Commands;
  4. use App\Models\User;
  5. use App\Services\Import\ImportTechnicalDescriptionsService;
  6. use Illuminate\Console\Command;
  7. use JsonException;
  8. class ImportTechnicalDescriptions extends Command
  9. {
  10. protected $signature = 'technical-descriptions:import
  11. {manifest : JSON-файл с данными исходного модуля}
  12. {--images= : Каталог с исходными изображениями}
  13. {--user=1 : ID владельца импортированных файлов}
  14. {--overwrite : Перезаписывать уже заполненные поля}
  15. {--create-missing : Создавать отсутствующие позиции общего каталога}
  16. {--apply-to-variants : Применять данные ко всем позициям с повторяющимся артикулом}
  17. {--dry-run : Только показать результат без изменений}';
  18. protected $description = 'Обогащает общий каталог техническими описаниями из старого модуля';
  19. public function handle(ImportTechnicalDescriptionsService $service): int
  20. {
  21. $manifest = (string) $this->argument('manifest');
  22. if (! is_file($manifest) || ! is_readable($manifest)) {
  23. $this->error("JSON-файл {$manifest} не найден или недоступен для чтения.");
  24. return self::FAILURE;
  25. }
  26. $userId = (int) $this->option('user');
  27. if (! User::query()->whereKey($userId)->exists()) {
  28. $this->error("Пользователь #{$userId} не найден.");
  29. return self::FAILURE;
  30. }
  31. try {
  32. $products = json_decode(
  33. (string) file_get_contents($manifest),
  34. true,
  35. 512,
  36. JSON_THROW_ON_ERROR,
  37. );
  38. } catch (JsonException $exception) {
  39. $this->error('Некорректный JSON: '.$exception->getMessage());
  40. return self::FAILURE;
  41. }
  42. if (! is_array($products) || ! array_is_list($products)) {
  43. $this->error('Корень JSON должен быть массивом позиций.');
  44. return self::FAILURE;
  45. }
  46. $stats = $service->handle(
  47. $products,
  48. $this->option('images') ? (string) $this->option('images') : null,
  49. $userId,
  50. (bool) $this->option('overwrite'),
  51. (bool) $this->option('create-missing'),
  52. (bool) $this->option('apply-to-variants'),
  53. (bool) $this->option('dry-run'),
  54. );
  55. $this->table(['Показатель', 'Количество'], [
  56. ['Источник', $stats['source']],
  57. ['Сопоставлено', $stats['matched']],
  58. ['Обновлено', $stats['updated']],
  59. ['Создано', $stats['created']],
  60. ['Без изменений', $stats['unchanged']],
  61. ['Нет в каталоге', $stats['missing']],
  62. ['Неоднозначный артикул', $stats['ambiguous']],
  63. ['Перенесено изображений', $stats['images_imported']],
  64. ['Не найдено изображений', $stats['images_missing']],
  65. ]);
  66. if ($stats['missing_articles'] !== []) {
  67. $this->warn('Нет в каталоге: '.implode(', ', $stats['missing_articles']));
  68. }
  69. if ($stats['ambiguous_articles'] !== []) {
  70. $this->warn('Неоднозначные артикулы: '.implode(', ', $stats['ambiguous_articles']));
  71. }
  72. if ($this->option('dry-run')) {
  73. $this->info('Dry-run завершён: данные не изменены.');
  74. }
  75. return self::SUCCESS;
  76. }
  77. }