filters(); return $this->csv( PlantingReimbursementPageData::TITLE . '-' . $filters['month'], $data->exportColumns(), static fn () => $data->rows($filters), ); } public function exportLoss(PlantingLossPageData $data) { $filters = $data->filters(); $suffix = match ($filters['result']) { PlantingLossCalculator::RESULT_EXEMPT => '-不扣款拍A发B', PlantingLossCalculator::RESULT_FREE_GOODS => '-不扣款虚拟条码挂钩', PlantingLossCalculator::RESULT_UNMATCHED => '-未匹配发货', 'all' => '-全部', default => '-扣款', }; // 发运营的拍A发B 名单直接按「种菜报销」的字段导出。 if (request()->query('format') === 'reimbursement') { return $this->csv( '拍A发B发运营-' . $filters['month'], PlantingReimbursementPageData::COLUMNS, static fn () => $data->rows($filters), ); } return $this->csv( PlantingLossPageData::TITLE . '-' . $filters['month'] . $suffix, $data->exportColumns(), static fn () => $data->rows($filters), ); } /** @param array $columns @param callable():iterable> $rows */ private function csv(string $name, array $columns, callable $rows) { $file = tmpfile(); abort_if($file === false, 500, '无法创建导出文件。'); try { fwrite($file, "\xEF\xBB\xBF"); fputcsv($file, array_values($columns)); foreach ($rows() as $row) { $values = []; foreach (array_keys($columns) as $key) { $values[] = $row[$key] ?? ''; } fputcsv($file, array_map(static function (mixed $value): string { $value = (string) $value; // 长数字单号/账号保留文本,避免 Excel 截断;同时阻止公式执行。 return preg_match('/^[=+\-@\t\r\n]|^\d{12,}$/u', $value) ? "'" . $value : $value; }, $values)); } rewind($file); } catch (\Throwable $exception) { fclose($file); report($exception); abort(503, '聚工单镜像或发货数据未就绪,未生成不完整的导出文件,请稍后重试。'); } return response()->streamDownload(static function () use ($file): void { fpassthru($file); fclose($file); }, $name . '-' . now()->format('YmdHis') . '.csv', ['Content-Type' => 'text/csv; charset=UTF-8']); } }