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 = []; $artistMap = []; $categoryMap = []; foreach ($this->rows as $index => $row) { $songId = trim($row['編號'] ?? ''); if (!$songId || Song::where('id', $songId)->exists()) { continue; } try { // 字元處理 $songName=trim($row['歌名'] ?? ''); $simplified=ChineseNameConverter::convertToSimplified($songName);// 繁體轉簡體 if (!$row->has('注音')) { $phoneticAbbr = ChineseNameConverter::getKTVZhuyinAbbr($simplified);// 注音符號 } else { $phoneticAbbr = trim($row['注音'] ?? ''); } if (!$row->has('拼音')) { $pinyinAbbr = ChineseNameConverter::getKTVPinyinAbbr($simplified);// 拼音首字母 } else { $pinyinAbbr = trim($row['拼音'] ?? ''); } if (!$row->has('kk3')) {//歌名第一個字筆畫 $chars = preg_split('//u', $songName, -1, PREG_SPLIT_NO_EMPTY); $firstChar = $chars[0] ?? null; $strokesAbbr=( $firstChar && preg_match('/\p{Han}/u', $firstChar) )? ChineseStrokesConverter::getStrokes($firstChar) : 0; } else { $strokesAbbr=trim($row['kk3'] ?? 0); } if (!$row->has('kk4')) {//歌名字數 $songNumber = mb_strlen($songName, 'UTF-8'); } else { $songNumber=trim($row['kk4'] ?? 0); } // 準備 song 資料 $ToInsert[] = [ 'id' => $songId, 'name' => $this->formatText($row['歌名']), 'adddate' => $this->parseExcelDate($row['日期'] ?? null), 'filename' => trim($row['檔名'] ?? ''), 'language_type' => SongLanguageType::tryFrom(trim($row['語別'] ?? '')) ?? SongLanguageType::Unset, '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), ]; // 處理關聯 - 歌手 $artistIds = []; foreach (['歌星A', '歌星B'] as $key) { $artistName = trim($row[$key] ?? ''); if ($artistName === '') continue; // 若是歌星B,且與歌星A相同,則跳過 if ($key === '歌星B' && $artistName === trim($row['歌星A'] ?? '')) continue; $artistMap[$songId][] = $this->getOrCreateArtistId($artistName); } // 分類處理(多個用 , 分隔) if (!empty($row['分類'])) { $categoryIds = []; $codes = explode(',', $row['分類']); foreach ($codes as $code) { $code = trim($code); if (isset($this->categoryMap[$code])) { $categoryMap[$songId][] = $this->categoryMap[$code]; } } } } catch (\Throwable $e) { \Log::error("Row {$index} failed: {$e->getMessage()}", [ 'row' => $row, 'trace' => $e->getTraceAsString() ]); } } // 寫入資料庫 Song::insert($ToInsert); // 同步關聯(建議可用事件或批次處理) foreach ($artistMap as $songId => $artistIds) { $song = Song::find($songId); if ($song) { $song->artists()->sync($artistIds); } } foreach ($categoryMap as $songId => $categoryIds) { $song = Song::find($songId); if ($song) { $song->categories()->sync($categoryIds); } } } 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; } protected function formatText($value) { if (is_numeric($value) && $value < 1 && $value > 0) { // 嘗試判斷為時間類型的小數,轉為時間字串 $time = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value); return $time->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 $e) { return null; } } }