user(); return response()->json([ 'id' => $user->id, 'mobile' => $user->mobile, 'name' => $user->name, ]); } public function sessions(Request $request): JsonResponse { $competition = $this->publishedCompetition($request); /** @var User $user */ $user = $request->user(); $registeredSessionIds = AudienceRegistration::query() ->where('user_id', $user->id) ->where('competition_id', $competition->id) ->pluck('audience_session_id') ->map(fn ($id) => (int) $id) ->all(); $sessions = $competition->audienceSessions() ->withCount('registrations') ->where('enabled', true) ->get() ->map(fn (AudienceSession $session) => $this->sessionPayload( $session, in_array((int) $session->id, $registeredSessionIds, true) )); return response()->json(['data' => $sessions]); } public function storeRegistration(Request $request, AudienceRegistrationService $service): JsonResponse { $competition = $this->publishedCompetition($request); /** @var User $user */ $user = $request->user(); $data = $request->validate([ 'audience_session_id' => ['required', 'integer', 'min:1'], 'name' => ['required', 'string', 'max:64'], 'company' => ['nullable', 'string', 'max:128'], 'job' => ['nullable', 'string', 'max:128'], 'purpose' => ['required', 'string', Rule::in(AudienceRegistration::PURPOSES)], ], [], [ 'name' => '个人姓名', 'company' => '所属单位', 'job' => '个人职务', 'purpose' => '观赛目的', 'audience_session_id' => '赛事场次', ]); $mobile = trim((string) $user->mobile); if (! preg_match('/^1[3-9]\d{9}$/', $mobile)) { throw ValidationException::withMessages([ 'phone' => ['登录手机号无效,请重新登录'], ]); } $data['phone'] = $mobile; $registration = $service->register($user, $competition, $data); $registration->load('session'); return response()->json($this->registrationPayload($registration), 201); } public function registrations(Request $request): JsonResponse { $competition = $this->publishedCompetition($request); /** @var User $user */ $user = $request->user(); $rows = AudienceRegistration::query() ->with('session') ->where('user_id', $user->id) ->where('competition_id', $competition->id) ->orderByDesc('registered_at') ->orderByDesc('id') ->get() ->map(fn (AudienceRegistration $row) => $this->registrationPayload($row)); return response()->json(['data' => $rows]); } /** * @return array */ private function sessionPayload(AudienceSession $session, bool $isRegistered): array { $status = $session->windowStatus(); $statusLabel = $isRegistered ? '已报名' : $session->statusLabel(); return [ 'id' => $session->id, 'code' => $session->code, 'name' => $session->name, 'event_date_text' => $session->event_date_text, 'address' => $session->address, 'capacity' => (int) $session->capacity, 'registered_count' => $session->registeredCount(), 'remaining' => $session->remainingCount(), 'is_final' => (bool) $session->is_final, 'signup_start_at' => BizDateTime::toIso8601($session->signup_start_at), 'signup_end_at' => BizDateTime::toIso8601($session->signup_end_at), 'status' => $status, 'status_label' => $statusLabel, 'is_registered' => $isRegistered, 'is_available' => ! $isRegistered && $session->isAvailable(), ]; } /** * @return array */ private function registrationPayload(AudienceRegistration $row): array { $session = $row->session; return [ 'id' => $row->id, 'audience_session_id' => $row->audience_session_id, 'session_name' => $session?->name, 'event_date_text' => $session?->event_date_text, 'address' => $session?->address, 'name' => $row->name, 'phone' => $row->phone, 'company' => $row->company, 'job' => $row->job, 'purpose' => $row->purpose, 'ticket_code' => $row->ticket_code, 'registered_at' => BizDateTime::toIso8601($row->registered_at), 'checked_in_at' => BizDateTime::toIso8601($row->checked_in_at), ]; } private function publishedCompetition(Request $request): Competition { $data = $request->validate([ 'competition_slug' => ['required', 'string', 'max:64'], ]); $competition = Competition::query() ->where('slug', $data['competition_slug']) ->where('published', true) ->first(); if ($competition === null) { throw ValidationException::withMessages([ 'competition_slug' => ['赛事不存在或未发布'], ]); } return $competition; } }