CaiWuXiTong/app/Console/Commands/ImportPromotionCostRows.php
renshen 0bf1ae67af 导入订单售后与商品推广月度明细 (#578)
新增 2026-06 月度订单售后及商品推广导入能力
2026-10-26 11:09:22 +08:00

141 lines
4.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Console\Commands;
use App\Business\XlsxSheetReader;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Modules\EcommerceFinance\Repositories\PromotionCostRowRepository;
class ImportPromotionCostRows extends Command
{
protected $signature = 'order-data:import-promotion-cost
{file : Excel 文件路径}
{--period= : 数据月份,格式 YYYY-MM}
{--chunk=500 : 每批写入行数}
{--dry-run : 只解析不入库}';
protected $description = '导入聚水潭商品推广费用明细 Excel';
public function handle(XlsxSheetReader $reader): int
{
$path = (string) $this->argument('file');
$period = trim((string) $this->option('period'));
if (! is_file($path)) {
$this->error('文件不存在:' . $path);
return self::FAILURE;
}
if (preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $period) !== 1) {
$this->error('数据月份格式必须是 YYYY-MM:' . ($period !== '' ? $period : '(空)'));
return self::FAILURE;
}
$headers = (array) config('order_promotion_cost.headers', []);
if ($headers === []) {
$this->error('缺少表头映射:config/order_promotion_cost.php');
return self::FAILURE;
}
$this->info('流式读取文件:' . $path);
$chunkSize = max((int) $this->option('chunk'), 100);
$dryRun = (bool) $this->option('dry-run');
$importedAt = now()->toDateTimeString();
$buffer = [];
$total = 0;
$written = 0;
$skipped = 0;
$promotionFee = 0.0;
$shops = [];
try {
foreach ($reader->rows($path, array_keys($headers)) as $row) {
$total++;
$payload = [];
foreach ($headers as $header => $key) {
$value = trim((string) ($row[$header] ?? ''));
if ($value === '') {
continue;
}
$payload[$key] = $key === 'promotion_fee'
? (is_numeric($value) ? (float) $value : 0.0)
: $value;
}
$shop = trim((string) ($payload['shop_name'] ?? ''));
$product = trim((string) ($payload['product_code'] ?? ''));
if ($shop === '' || $product === '') {
$skipped++;
continue;
}
$shops[$shop] = true;
$promotionFee += (float) ($payload['promotion_fee'] ?? 0);
$buffer[] = PromotionCostRowRepository::toRecord($payload, $period, $importedAt);
if (! $dryRun && count($buffer) >= $chunkSize) {
$written += $this->flush($buffer);
$buffer = [];
$this->output->write("\r写入 " . $written . ' 行');
}
if ($dryRun && $buffer !== []) {
$buffer = [];
}
}
} catch (\Throwable $exception) {
if (! $dryRun) {
Cache::forget('order-data.promotion.imported-shop-options:' . $period);
}
$this->error('读取失败:' . $exception->getMessage());
return self::FAILURE;
}
if (! $dryRun && $buffer !== []) {
$written += $this->flush($buffer);
}
if (! $dryRun) {
Cache::forget('order-data.promotion.imported-shop-options:' . $period);
}
$this->newLine(2);
$this->info(sprintf(
'完成:读取 %d 行,写入 %d 行,跳过 %d 行;店铺 %d 个;数据月份 %s;推广费合计 %.2f;峰值内存 %.1f MB',
$total,
$written,
$skipped,
count($shops),
$period,
$promotionFee,
memory_get_peak_usage(true) / 1048576,
));
return self::SUCCESS;
}
/** @param list<array<string,mixed>> $rows */
private function flush(array $rows): int
{
DB::table('ops_promotion_cost_rows')->upsert(
$rows,
['row_key'],
[
'source_period', 'shop_name', 'product_code', 'style_code',
'shop_product_code', 'shop_style_code', 'online_color_spec',
'promotion_fee', 'payload', 'imported_at', 'updated_at',
],
);
return count($rows);
}
}