*/ public function previewByTicket( Competition $competition, string $ticketCode, ?int $expectedSessionId = null, ): array { $row = $this->findByTicket($competition, $ticketCode, lock: false); return $this->inspectTicket($row, $expectedSessionId); } /** * @return array */ public function checkInByTicket( Competition $competition, string $ticketCode, AdminUser|CompetitionAdmin $admin, ?int $expectedSessionId = null, ): array { return DB::transaction(function () use ($competition, $ticketCode, $admin, $expectedSessionId) { $row = $this->findByTicket($competition, $ticketCode, lock: true); return $this->applyCheckIn($row, $admin, $expectedSessionId); }); } /** * @return array */ public function checkInByRegistration( Competition $competition, int $registrationId, AdminUser|CompetitionAdmin $admin, ): array { return DB::transaction(function () use ($competition, $registrationId, $admin) { /** @var AudienceRegistration|null $row */ $row = AudienceRegistration::query() ->where('competition_id', $competition->id) ->whereKey($registrationId) ->lockForUpdate() ->first(); if ($row === null) { throw ValidationException::withMessages([ 'id' => ['报名记录不存在'], ]); } return $this->applyCheckIn($row, $admin, null); }); } public function normalizeTicketCode(string $raw): string { $raw = trim($raw); if ($raw === '') { return ''; } if (preg_match('#/t/([A-Za-z0-9_-]+)#', $raw, $matches) === 1) { return $matches[1]; } return preg_replace('/\s+/', '', $raw) ?? ''; } private function findByTicket(Competition $competition, string $ticketCode, bool $lock): AudienceRegistration { $normalized = $this->normalizeTicketCode($ticketCode); if ($normalized === '') { throw ValidationException::withMessages([ 'ticket_code' => ['无效入场码'], ]); } $query = AudienceRegistration::query() ->where('competition_id', $competition->id) ->where('ticket_code', $normalized); if ($lock) { $query->lockForUpdate(); } /** @var AudienceRegistration|null $row */ $row = $query->first(); if ($row === null) { throw ValidationException::withMessages([ 'ticket_code' => ['无效入场码'], ]); } return $row; } /** * @return array */ private function inspectTicket(AudienceRegistration $row, ?int $expectedSessionId): array { $row->loadMissing('session'); $session = $row->session; if ($session === null) { throw ValidationException::withMessages([ 'ticket_code' => ['无效入场码'], ]); } if (! $row->isApproved()) { throw ValidationException::withMessages([ 'ticket_code' => ['该报名尚未审核通过,暂不可核销'], ]); } if ($expectedSessionId !== null && (int) $row->audience_session_id !== $expectedSessionId) { throw ValidationException::withMessages([ 'ticket_code' => ['非本场次入场码'], ]); } if (! $session->enabled) { throw ValidationException::withMessages([ 'ticket_code' => ['该场次已停用,暂不可核销'], ]); } return [ 'already_checked_in' => $row->checked_in_at !== null, 'id' => $row->id, 'ticket_code' => $row->ticket_code, 'name' => $row->name, 'phone' => $row->phone, 'company' => $row->company, 'job' => $row->job, 'purpose' => $row->purpose, 'audience_session_id' => (int) $row->audience_session_id, 'session_name' => $session->name, 'event_date_text' => $session->event_date_text, 'address' => $session->address, 'checked_in_at' => BizDateTime::toIso8601($row->checked_in_at), ]; } /** * @return array */ private function applyCheckIn(AudienceRegistration $row, AdminUser|CompetitionAdmin $admin, ?int $expectedSessionId): array { $payload = $this->inspectTicket($row, $expectedSessionId); if ($payload['already_checked_in']) { return $payload; } $row->checked_in_at = now(); if ($admin instanceof CompetitionAdmin) { $row->checked_in_by_competition_admin_id = $admin->id; $row->checked_in_by_admin_id = null; } else { $row->checked_in_by_admin_id = $admin->id; $row->checked_in_by_competition_admin_id = null; } $row->save(); $payload['already_checked_in'] = false; $payload['checked_in_at'] = BizDateTime::toIso8601($row->checked_in_at); return $payload; } }