ImportAreasService.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. <?php
  2. namespace App\Services\Import;
  3. use App\Models\Dictionary\Area;
  4. use App\Models\Dictionary\District;
  5. use Exception;
  6. use Illuminate\Support\Facades\DB;
  7. use Illuminate\Support\Facades\Log;
  8. use PhpOffice\PhpSpreadsheet\IOFactory;
  9. class ImportAreasService
  10. {
  11. private array $logs = [];
  12. private array $districtCache = [];
  13. public function __construct(
  14. private readonly string $filePath,
  15. private readonly int $userId,
  16. ) {}
  17. public function handle(): array
  18. {
  19. try {
  20. $this->log("Начало импорта справочника районов");
  21. // Загружаем кэш округов
  22. $this->loadDistrictCache();
  23. DB::beginTransaction();
  24. try {
  25. $spreadsheet = IOFactory::load($this->filePath);
  26. $sheet = $spreadsheet->getActiveSheet();
  27. $highestRow = $sheet->getHighestRow();
  28. $imported = 0;
  29. $updated = 0;
  30. $skipped = 0;
  31. for ($row = 2; $row <= $highestRow; $row++) {
  32. $id = trim($sheet->getCell('A' . $row)->getValue());
  33. $name = trim($sheet->getCell('B' . $row)->getValue());
  34. $districtShortname = trim($sheet->getCell('C' . $row)->getValue());
  35. $districtId = trim($sheet->getCell('D' . $row)->getValue());
  36. // Пропускаем пустые строки
  37. if (empty($name)) {
  38. $skipped++;
  39. continue;
  40. }
  41. // Определяем округ
  42. $foundDistrictId = null;
  43. // Сначала по ID округа
  44. if (!empty($districtId) && is_numeric($districtId)) {
  45. $foundDistrictId = (int) $districtId;
  46. if (!isset($this->districtCache[$foundDistrictId])) {
  47. $this->log("Строка {$row}: округ с ID {$districtId} не найден");
  48. $foundDistrictId = null;
  49. }
  50. }
  51. // Потом по сокращению
  52. if (!$foundDistrictId && !empty($districtShortname)) {
  53. foreach ($this->districtCache as $dId => $dShortname) {
  54. if (mb_strtolower($dShortname) === mb_strtolower($districtShortname)) {
  55. $foundDistrictId = $dId;
  56. break;
  57. }
  58. }
  59. if (!$foundDistrictId) {
  60. $this->log("Строка {$row}: округ '{$districtShortname}' не найден");
  61. }
  62. }
  63. if (!$foundDistrictId) {
  64. $this->log("Строка {$row}: пропущена (не найден округ)");
  65. $skipped++;
  66. continue;
  67. }
  68. $data = [
  69. 'name' => $name,
  70. 'district_id' => $foundDistrictId,
  71. ];
  72. // Если есть ID, ищем по нему
  73. if (!empty($id) && is_numeric($id)) {
  74. $area = Area::withTrashed()->find((int) $id);
  75. if ($area) {
  76. if ($area->trashed()) {
  77. $area->restore();
  78. }
  79. $area->update($data);
  80. $updated++;
  81. continue;
  82. }
  83. }
  84. // Ищем по названию и округу
  85. $area = Area::withTrashed()
  86. ->where('name', $name)
  87. ->where('district_id', $foundDistrictId)
  88. ->first();
  89. if ($area) {
  90. if ($area->trashed()) {
  91. $area->restore();
  92. }
  93. $area->update($data);
  94. $updated++;
  95. } else {
  96. Area::create($data);
  97. $imported++;
  98. }
  99. }
  100. DB::commit();
  101. $this->log("Импорт районов: создано {$imported}, обновлено {$updated}, пропущено {$skipped}");
  102. return [
  103. 'success' => true,
  104. 'imported' => $imported,
  105. 'updated' => $updated,
  106. 'skipped' => $skipped,
  107. 'logs' => $this->logs,
  108. ];
  109. } catch (Exception $e) {
  110. DB::rollBack();
  111. throw $e;
  112. }
  113. } catch (Exception $e) {
  114. $this->log("ОШИБКА: " . $e->getMessage());
  115. Log::error("Ошибка импорта районов: " . $e->getMessage(), [
  116. 'trace' => $e->getTraceAsString(),
  117. ]);
  118. return [
  119. 'success' => false,
  120. 'error' => $e->getMessage(),
  121. 'logs' => $this->logs,
  122. ];
  123. }
  124. }
  125. private function loadDistrictCache(): void
  126. {
  127. $this->districtCache = District::pluck('shortname', 'id')->toArray();
  128. }
  129. private function log(string $message): void
  130. {
  131. $this->logs[] = '[' . date('H:i:s') . '] ' . $message;
  132. Log::info($message);
  133. }
  134. public function getLogs(): array
  135. {
  136. return $this->logs;
  137. }
  138. }