[ 'path' => 'templates/courier-payables/freight-difference-import-template.xlsx', 'ascii_name' => 'freight_difference_import_template.xlsx', 'download_name' => '运费差导入模板.xlsx', ], 'other-surcharge-summary' => [ 'path' => 'templates/courier-payables/other-surcharge-import-template.xlsx', 'ascii_name' => 'other_surcharge_import_template.xlsx', 'download_name' => '其他附加费导入模板.xlsx', ], 'return-fee' => [ 'path' => 'templates/courier-payables/return-fee-import-template.xlsx', 'ascii_name' => 'return_fee_import_template.xlsx', 'download_name' => '退件费导入模板.xlsx', ], ]; public function __construct( private readonly CourierPayableSummaryStore $store, private readonly CourierPayableSummaryFilter $filter, ) { $this->middleware('auth'); $this->middleware('company.identify'); } public function import(Request $request, string $summaryPage): RedirectResponse { $this->authorizePage($summaryPage); $this->store->title($summaryPage); $request->validate([ 'import' => ['required', 'file', 'mimes:xlsx,xls,csv', 'max:10240'], ]); try { $result = $this->store->import( $request->file('import'), (int) (company_id() ?: 1), $summaryPage, user_id() ? (int) user_id() : null, ); } catch (RuntimeException $exception) { report($exception); return $this->redirectToPage($summaryPage)->with('error', '汇总数据保存失败,请稍后重试。'); } return $this->redirectToPage($summaryPage)->with( 'success', "导入完成:{$result['rows']} 条数据,{$result['columns']} 个字段。", ); } public function template(string $summaryPage): BinaryFileResponse { $this->authorizePage($summaryPage); $template = self::IMPORT_TEMPLATES[$summaryPage] ?? null; abort_unless(is_array($template), 404); $path = resource_path($template['path']); abort_unless(is_file($path), 404); $response = response()->download($path, $template['ascii_name'], [ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'Cache-Control' => 'private, no-store', ]); return $response->setContentDisposition( 'attachment', $template['download_name'], $template['ascii_name'], ); } public function export(Request $request, string $summaryPage): StreamedResponse|RedirectResponse { $this->authorizePage($summaryPage); $title = $this->store->title($summaryPage); try { $snapshot = $this->store->snapshot((int) (company_id() ?: 1), $summaryPage); } catch (RuntimeException) { return $this->redirectToPage($summaryPage)->with('error', '汇总数据暂时无法读取,请重新导入后再试。'); } if ($snapshot === null) { return $this->redirectToPage($summaryPage)->with('error', '当前没有可导出的汇总数据。'); } $filters = $this->filter->fromRequest($request); $headers = $snapshot['headers']; $rows = $this->filter->apply(collect($snapshot['rows']), $headers, $filters)->values(); $dateColumn = $this->filter->dateColumnIndex($headers); return response()->streamDownload(function () use ($headers, $rows, $dateColumn): void { $csv = fopen('php://output', 'w'); if ($csv === false) { throw new RuntimeException('无法创建汇总数据导出流。'); } fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF)); fputcsv($csv, array_map([$this, 'csvText'], $headers)); foreach ($rows as $row) { fputcsv($csv, array_map( fn (mixed $value, int $column): string => $this->csvText($value, $column === $dateColumn), $row, array_keys($row), )); } fclose($csv); }, $title . '_' . now()->format('Ymd_His') . '.csv', [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Cache-Control' => 'private, no-store', ]); } private function redirectToPage(string $summaryPage): RedirectResponse { return redirect()->route('business.pages.show', [ 'section' => 'courier-payables', 'page' => $summaryPage, ]); } private function authorizePage(string $summaryPage): void { $section = collect(SidebarPermissions::navigationFor(user())) ->firstWhere('slug', 'courier-payables'); $pages = collect($section['pages'] ?? [])->flatMap(static fn (array $page): array => [ $page, ...($page['children'] ?? []), ]); abort_unless($pages->contains('slug', $summaryPage), 403, '当前账号没有该页面权限。'); } private function csvText(mixed $value, bool $preserveYearMonth = false): string { $value = (string) $value; if ($preserveYearMonth && preg_match('/^\d{4}-(?:0[1-9]|1[0-2])$/', $value) === 1) { return '="' . $value . '"'; } if (preg_match('/^[+-]?(?:(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?|\.\d+)%?$/u', $value) === 1) { return $value; } return preg_match('/^[=+\-@\t\r]/u', $value) === 1 ? "\t" . $value : $value; } }