CaiWuXiTong/app/Http/Controllers/Business/CourierPayableSummaries.php

171 lines
6.1 KiB
PHP

<?php
namespace App\Http\Controllers\Business;
use App\Business\CourierPayableSummaryFilter;
use App\Business\CourierPayableSummaryStore;
use App\Utilities\SidebarPermissions;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use RuntimeException;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
class CourierPayableSummaries extends Controller
{
private const IMPORT_TEMPLATES = [
'freight-difference' => [
'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;
}
}