140 lines
4.8 KiB
PHP
140 lines
4.8 KiB
PHP
<?php
|
||
|
||
namespace App\Console\Commands;
|
||
|
||
use Illuminate\Console\Command;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Modules\EcommerceFinance\Services\AccountLedgerRemarkMap;
|
||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||
use Throwable;
|
||
|
||
/**
|
||
* 导入「平台 | 账单项目 | 总账备注」映射表,供店铺收款账户账单的「总账备注」列使用。
|
||
*/
|
||
class ImportAccountLedgerRemarks extends Command
|
||
{
|
||
protected $signature = 'account-ledger-remarks:import {path : xlsx/csv 文件} {--sheet=1 : 工作表序号(从 1 开始)或名称} {--replace : 先清空再导入(默认按平台+账单项目覆盖)}';
|
||
|
||
protected $description = '导入账单项目对应的总账备注映射表';
|
||
|
||
public function handle(AccountLedgerRemarkMap $map): int
|
||
{
|
||
$path = (string) $this->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<array{platform:string,bill_item:string,ledger_remark:string}>
|
||
*/
|
||
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;
|
||
}
|
||
}
|