You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
4.4 KiB

<?php
namespace App\Services;
use App\Models\AdminUser;
use App\Models\AudienceRegistration;
use App\Models\Competition;
use App\Models\CompetitionAdmin;
use App\Support\BizDateTime;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class AudienceCheckinService
{
/**
* @return array<string, mixed>
*/
public function checkInByTicket(
Competition $competition,
string $ticketCode,
AdminUser|CompetitionAdmin $admin,
?int $expectedSessionId = null,
): array {
$normalized = $this->normalizeTicketCode($ticketCode);
if ($normalized === '') {
throw ValidationException::withMessages([
'ticket_code' => ['无效入场码'],
]);
}
return DB::transaction(function () use ($competition, $normalized, $admin, $expectedSessionId) {
/** @var AudienceRegistration|null $row */
$row = AudienceRegistration::query()
->where('competition_id', $competition->id)
->where('ticket_code', $normalized)
->lockForUpdate()
->first();
if ($row === null) {
throw ValidationException::withMessages([
'ticket_code' => ['无效入场码'],
]);
}
return $this->applyCheckIn($row, $admin, $expectedSessionId);
});
}
/**
* @return array<string, mixed>
*/
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) ?? '';
}
/**
* @return array<string, mixed>
*/
private function applyCheckIn(AudienceRegistration $row, AdminUser|CompetitionAdmin $admin, ?int $expectedSessionId): array
{
$row->load('session');
$session = $row->session;
if ($session === null) {
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' => ['该场次已停用,暂不可核销'],
]);
}
$already = $row->checked_in_at !== null;
if (! $already) {
$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();
}
return [
'already_checked_in' => $already,
'id' => $row->id,
'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,
'checked_in_at' => BizDateTime::toIso8601($row->checked_in_at),
];
}
}