$detail */ public function download( array $detail, string $supplier, string $month, string $reconciler, ): StreamedResponse { $safeSupplier = preg_replace('/[\\\\\\/:*?"<>|]+/u', '_', $supplier) ?: '厂家'; $period = $month !== '' ? preg_replace('/[^0-9-]+/u', '', $month) : '全部月份'; $filename = $safeSupplier . '_货款对账比对_' . $period . '_' . now()->format('Ymd_His') . '.csv'; $summary = (array) ($detail['summary'] ?? []); $rows = (array) ($detail['rows'] ?? []); return response()->streamDownload(function () use ($detail, $rows, $summary, $supplier, $month, $reconciler): void { $csv = fopen('php://output', 'w'); if ($csv === false) { throw new RuntimeException('无法创建导出文件。'); } fwrite($csv, "\xEF\xBB\xBF"); $this->write($csv, ['供应商', $supplier, '月份', $month !== '' ? $month : '全部月份', '对账人', $reconciler !== '' ? $reconciler : '全部']); $this->write($csv, [ '我方数据行数', (int) ($detail['mineRows'] ?? 0), '厂家数据行数', (int) ($detail['theirsRows'] ?? 0), '比对结果条数', count($rows), ]); $this->write($csv, [ '双方一致', (int) ($summary['match'] ?? 0), '厂家多', (int) ($summary['supplierExtra'] ?? 0), '数量/金额不一致', (int) ($summary['mismatch'] ?? 0), '厂家少', (int) ($summary['supplierMissing'] ?? 0), ]); $this->write($csv, [ '我方金额合计', $this->money($summary['mineAmount'] ?? 0), '厂家金额合计', $this->money($summary['theirsAmount'] ?? 0), '厂家多算金额', $this->money($summary['supplierOverAmount'] ?? 0), '按口径结算合计', $this->money($summary['settleAmount'] ?? 0), '差异金额', $this->money($summary['differenceAmount'] ?? 0), ]); $this->write($csv, []); $this->write($csv, self::HEADERS); foreach ($rows as $row) { $this->write($csv, [ $row['purchaseNo'] ?? '', $row['date'] ?? '', $row['barcode'] ?? '', $row['supplierBarcode'] ?? '', $row['type'] ?? '', $this->number($row['mineQty'] ?? 0), $this->money($row['mineUnitPrice'] ?? 0), $this->money($row['mineAmount'] ?? 0), $this->number($row['theirsQty'] ?? 0), $this->money($row['theirsUnitPrice'] ?? 0), $this->money($row['theirsAmount'] ?? 0), $this->quantityDifference($row['qtyDifference'] ?? 0), $this->amountDifference($row['amountDifference'] ?? 0), $row['note'] ?? '', $this->money($row['settleAmount'] ?? 0), ]); } fclose($csv); }, $filename, [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Cache-Control' => 'private, no-store', ]); } /** @param resource $csv @param list $values */ private function write($csv, array $values): void { fputcsv($csv, array_map(fn (mixed $value): string => $this->csvCell($value), $values)); } private function quantityDifference(mixed $value): string { $difference = (float) $value; if (abs($difference) < 0.005) { return '一致'; } return '厂家' . ($difference > 0 ? '多 ' : '少 ') . $this->number(abs($difference)); } private function amountDifference(mixed $value): string { $difference = (float) $value; if (abs($difference) < 0.005) { return '一致'; } return '厂家' . ($difference > 0 ? '多 ¥' : '少 ¥') . $this->money(abs($difference)); } private function number(mixed $value): string { return rtrim(rtrim(number_format((float) $value, 2, '.', ''), '0'), '.'); } private function money(mixed $value): string { return number_format((float) $value, 2, '.', ''); } private function csvCell(mixed $value): string { $text = trim((string) $value); if (is_numeric(str_replace(',', '', $text))) { return $text; } return preg_match('/^[=+\-@\t\r]/u', $text) === 1 ? "\t" . $text : $text; } }