171 lines
6.1 KiB
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;
|
|
}
|
|
}
|