rows = $rows; $this->id = $id; $this->categoryMap = SongCategory::pluck('id', 'code')->toArray(); } public function handle(): void { Log::warning('匯入啟動', ['model' => "ImportSongChunkJob", 'rows_id' => $this->id]); $ToInsert = []; $songIdMap = []; $artistMap = []; $categoryMap = []; $pMap = [ '\\\\SVR01\\DISK01\\' => 'DISK01\\', '\\\\SVR01\\DISK02\\' => 'DISK02\\', '\\\\SVR01\\DISK03\\' => 'DISK03\\', '\\\\SVR01\\DISK04\\' => 'DISK04\\', '\\\\SVR01\\DISK05\\' => 'DISK05\\', '\\\\SVR01\\DISK06\\' => 'DISK06\\', '\\\\SVR01\\DISK07\\' => 'DISK07\\', '\\\\SVR01\\DISK08\\' => 'DISK08\\', '\\\\SVR01\\DISK09\\' => 'DISK09\\', ]; foreach ($this->rows as $index => $row) { $songId = trim($row['編號'] ?? ''); if (!$songId || Song::where('id', $songId)->exists()) continue; try { $songName = $this->normalizeName($row['歌名'] ?? ''); $simplified = ChineseNameConverter::convertToSimplified($songName); $phoneticAbbr = $row->has('注音') ? trim($row['注音'] ?? '') : ChineseNameConverter::getKTVZhuyinAbbr($simplified); $pinyinAbbr = $row->has('拼音') ? trim($row['拼音'] ?? '') : ChineseNameConverter::getKTVPinyinAbbr($simplified); $strokesAbbr = $row->has('kk3') ? trim($row['kk3'] ?? 0) : (preg_match('/\p{Han}/u', $songName[0] ?? '') ? ChineseStrokesConverter::getStrokes($songName[0]) : 0); $songNumber = $row->has('kk4') ? trim($row['kk4'] ?? 0) : mb_strlen($songName, 'UTF-8'); $disk = $pMap[trim($row['路徑01'] ?? '')] ?? ''; $filename = trim($row['檔名'] ?? ''); $ToInsert[] = [ 'id' => $songId, 'name' => $this->formatText($row['歌名']), 'adddate' => $this->parseExcelDate($row['日期'] ?? null), 'filename' => $disk . $filename, 'language_type' => SongLanguageType::fromLabelOrName($row['語別'] ?? ''), 'db_change' => trim($row['kk2'] ?? 0), 'vocal' => trim($row['kk6'] ?? 0), 'situation' => SongSituation::tryFrom(trim($row['kk7'] ?? '')) ?? SongSituation::Unset, 'copyright01' => trim($row['版權01'] ?? ''), 'copyright02' => trim($row['版權02'] ?? ''), 'note01' => trim($row['版權03'] ?? ''), 'note02' => trim($row['版權04'] ?? ''), 'note03' => trim($row['版權05'] ?? ''), 'note04' => trim($row['版權06'] ?? ''), 'enable' => trim($row['狀態'] ?? 1), 'simplified' => $simplified, 'phonetic_abbr' => $phoneticAbbr, 'pinyin_abbr' => $pinyinAbbr, 'strokes_abbr' => $strokesAbbr, 'song_number' => $songNumber, 'song_counts' => trim($row['點播次數'] ?? 0), ]; foreach (['歌星A', '歌星B'] as $key) { $artistName = $this->normalizeName($row[$key] ?? ''); if ($artistName === '' || ($key === '歌星B' && $artistName === $this->normalizeName($row['歌星A'] ?? ''))) continue; $artistMap[$songId][] = $this->getOrCreateArtistId($artistName); } if (!empty($row['分類'])) { foreach (explode(',', $row['分類']) as $code) { $code = trim($code); if (isset($this->categoryMap[$code])) { $categoryMap[$songId][] = $this->categoryMap[$code]; } } } $songIdMap[] = $songId; } catch (\Throwable $e) { Log::error("Row {$index} failed: {$e->getMessage()}", [ 'row' => $row, 'trace' => $e->getTraceAsString() ]); } } // ✅ 批次插入 collect($ToInsert)->chunk(500)->each(fn($chunk) => Song::insert($chunk->toArray())); // ✅ 一次撈資料並同步 $songs = Song::whereIn('id', $songIdMap)->get()->keyBy('id'); foreach ($songs as $songId => $song) { if (isset($artistMap[$songId])) { $song->artists()->sync($artistMap[$songId]); } if (isset($categoryMap[$songId])) { $song->categories()->sync($categoryMap[$songId]); } SongLibraryCache::syncFromSong($song); } } private function getOrCreateArtistId(string $name): int { if (isset($this->artistCache[$name])) { return $this->artistCache[$name]; } $artist = Artist::firstOrCreate(['name' => $name], ['category' => ArtistCategory::Unset]); return $this->artistCache[$name] = $artist->id; } private function normalizeName(?string $str): string { return strtoupper(mb_convert_kana(trim($str ?? ''), 'as')); } protected function formatText($value) { if (is_numeric($value) && $value < 1 && $value > 0) { return \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value)->format('H:i'); } return trim((string)$value); } private function parseExcelDate($value): ?string { if (is_numeric($value)) { return \Carbon\Carbon::createFromFormat('Y-m-d', '1900-01-01')->addDays((int)$value - 2)->format('Y-m-d'); } try { return \Carbon\Carbon::parse($value)->format('Y-m-d'); } catch (\Exception) { return null; } } }