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.

179 lines
6.0 KiB

1 month ago
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\AudienceRegistration;
use App\Models\AudienceSession;
use App\Models\Competition;
use App\Models\User;
use App\Services\AudienceRegistrationService;
use App\Support\BizDateTime;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class AudienceController extends Controller
{
public function me(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->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<string, mixed>
*/
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<string, mixed>
*/
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;
}
}