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> $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); } }