argument('path'); if (! is_file($path)) { $this->error('文件不存在:' . $path); return self::FAILURE; } try { $rows = $this->readRows($path); } catch (Throwable $e) { $this->error('读取失败:' . $e->getMessage()); return self::FAILURE; } if ($rows === []) { $this->error('没有解析到有效数据(需要表头:平台 / 账单项目 / 总账备注)。'); return self::FAILURE; } $now = now(); DB::transaction(function () use ($rows, $now): void { if ($this->option('replace')) { DB::table('ec_account_ledger_remarks')->delete(); } foreach (array_chunk($rows, 500) as $chunk) { DB::table('ec_account_ledger_remarks')->upsert( array_map(static fn (array $row): array => $row + ['created_at' => $now, 'updated_at' => $now], $chunk), ['platform', 'bill_item'], ['ledger_remark', 'updated_at'], ); } }); $map->forget(); $platforms = []; foreach ($rows as $row) { $platforms[$row['platform']] = ($platforms[$row['platform']] ?? 0) + 1; } ksort($platforms); $this->info(sprintf('已导入 %d 条总账备注映射。', count($rows))); foreach ($platforms as $platform => $count) { $this->line(sprintf(' %s:%d 条', $platform, $count)); } return self::SUCCESS; } /** * @return list */ private function readRows(string $path): array { $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); $readerType = match ($extension) { 'xlsx' => 'Xlsx', 'xls' => 'Xls', 'csv' => 'Csv', default => throw new \InvalidArgumentException('只支持 xlsx / xls / csv 文件。'), }; $reader = IOFactory::createReader($readerType); $reader->setReadDataOnly(true); if ($readerType === 'Csv') { $reader->setInputEncoding('UTF-8'); } $book = $reader->load($path); $sheetOption = trim((string) $this->option('sheet')); if ($sheetOption !== '' && ctype_digit($sheetOption)) { $index = (int) $sheetOption - 1; if ($index < 0 || $index >= $book->getSheetCount()) { throw new \InvalidArgumentException('工作表序号超出范围:' . $sheetOption); } $sheet = $book->getSheet($index); } else { $sheet = $sheetOption === '' ? $book->getActiveSheet() : $book->getSheetByName($sheetOption); if ($sheet === null) { throw new \InvalidArgumentException('找不到工作表:' . $sheetOption); } } $rows = []; $header = null; foreach ($sheet->getRowIterator() as $row) { $cells = []; $cellIterator = $row->getCellIterator(); $cellIterator->setIterateOnlyExistingCells(false); foreach ($cellIterator as $cell) { $cells[] = trim((string) $cell->getValue()); } if ($header === null) { $cells[0] = preg_replace('/^\xEF\xBB\xBF/', '', $cells[0] ?? ''); $header = $cells; continue; } $platform = trim($cells[0] ?? ''); $item = trim($cells[1] ?? ''); $remark = trim($cells[2] ?? ''); if ($platform === '' || $item === '') { continue; } $rows[] = [ 'platform' => mb_substr($platform, 0, 32), 'bill_item' => mb_substr($item, 0, 191), 'ledger_remark' => mb_substr($remark, 0, 191), ]; } return $rows; } }