ajax()) { return datagrid(MemberDataGrid::class)->process(); } return view('member::admin.index'); } /** * Plus 会员统计:新增 / 过期。 */ public function statistics(Request $request): JsonResponse { $request->validate([ 'start' => ['nullable', 'date'], 'end' => ['nullable', 'date'], ]); $end = $request->date('end') ?? now(); $start = $request->date('start') ?? $end->copy()->subDays(29); return new JsonResponse($this->statisticsService->get($start, $end)); } /** * 批量导入 Plus 会员(邮箱 + 过期天数)。 */ public function import(Request $request): RedirectResponse { $request->validate([ 'import_file' => [ 'required', 'file', 'extensions:xlsx,xls,csv', 'mimetypes:text/csv,text/plain,application/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'max:10240', ], ]); try { $sheets = Excel::toArray(new MemberPlusSpreadsheet, $request->file('import_file')); $result = $this->importService->import($sheets[0] ?? []); } catch (MemberPlusImportException $e) { return $this->redirectToIndex('error', $e->getMessage()); } catch (\Throwable $e) { return $this->redirectToIndex('error', trans('member::app.member.import.errors.generic', [ 'message' => $this->sanitizeMessage($e->getMessage()), ])); } return redirect() ->route('admin.member.index') ->with('member_import_result', $result) ->with( $result['failed'] > 0 ? 'warning' : 'success', trans('member::app.member.import.summary', [ 'updated' => $result['updated'], 'failed' => $result['failed'], 'skipped' => $result['skipped'], ]) ); } /** * 返回列表页并带上提示信息。 */ protected function redirectToIndex(string $type, string $message): RedirectResponse { return redirect() ->route('admin.member.index') ->with($type, $this->sanitizeMessage($message)); } /** * 去掉会破坏前端提示脚本的引号与换行。 */ protected function sanitizeMessage(string $message): string { return trim(str_replace(['"', "\r", "\n"], ["'", ' ', ' '], $message)); } }