user(); abort_unless($user?->isSuperAdmin(), 403, '仅超级管理员可下载导入模板'); $spreadsheet = $this->venueImportService->buildTemplateSpreadsheet(); $writer = new Xlsx($spreadsheet); return response()->streamDownload(function () use ($writer) { $writer->save('php://output'); }, '场馆导入模板.xlsx', [ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ]); } public function preview(Request $request): JsonResponse { abort_unless($request->user()?->isSuperAdmin(), 403, '仅超级管理员可导入场馆'); $request->validate([ 'file' => ['required', 'file', 'mimes:xlsx,xls', 'max:10240'], ]); $path = $request->file('file')->getRealPath(); if ($path === false) { return response()->json(['message' => '无法读取上传文件'], 422); } $result = $this->venueImportService->previewFromPath($path); return response()->json($result); } public function confirm(Request $request): JsonResponse { $user = $request->user(); abort_unless($user?->isSuperAdmin(), 403, '仅超级管理员可导入场馆'); $data = $request->validate([ 'rows' => ['required', 'array', 'min:1'], 'rows.*' => ['required', 'array'], ]); $created = []; $errors = []; DB::beginTransaction(); try { foreach ($data['rows'] as $index => $row) { $errs = $this->venueImportService->validatePayload($row); if ($errs !== []) { $errors[] = ['index' => $index, 'errors' => $errs]; continue; } $clean = $this->venueImportService->stripInternalKeys($row); $created[] = $this->venueImportService->createVenueFromPayload($clean, $user)->toArray(); } if ($errors !== []) { DB::rollBack(); return response()->json([ 'message' => '部分数据校验失败,未写入任何场馆', 'failures' => $errors, ], 422); } DB::commit(); } catch (\Throwable $e) { DB::rollBack(); throw $e; } return response()->json([ 'created' => $created, 'count' => count($created), ], 201); } }