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.

111 lines
4.2 KiB

<?php
namespace App\Http\Controllers\Api\Admin;
use App\Http\Controllers\Controller;
use App\Models\AudienceRegistration;
use App\Models\AudienceSession;
use App\Models\Competition;
use App\Services\AudienceCheckinService;
use App\Support\BizDateTime;
use App\Support\PortalAccess;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
class AudienceCheckinController extends Controller
{
public function store(Request $request, Competition $competition, AudienceCheckinService $service): JsonResponse
{
$data = $request->validate([
'ticket_code' => ['required', 'string', 'max:512'],
'audience_session_id' => ['nullable', 'integer', 'min:1'],
], [], [
'ticket_code' => '入场码',
'audience_session_id' => '场次',
]);
$admin = PortalAccess::actor($request);
$expectedSessionId = isset($data['audience_session_id']) ? (int) $data['audience_session_id'] : null;
if ($expectedSessionId === 0) {
$expectedSessionId = null;
}
$payload = $service->checkInByTicket(
$competition,
(string) $data['ticket_code'],
$admin,
$expectedSessionId,
);
return response()->json($payload);
}
public function storeByRegistration(
Request $request,
Competition $competition,
AudienceRegistration $audience_registration,
AudienceCheckinService $service,
): JsonResponse {
if ((int) $audience_registration->competition_id !== (int) $competition->id) {
abort(404);
}
$admin = PortalAccess::actor($request);
$payload = $service->checkInByRegistration($competition, (int) $audience_registration->id, $admin);
return response()->json($payload);
}
public function stats(Request $request, Competition $competition): JsonResponse
{
$data = $request->validate([
'audience_session_id' => ['sometimes', 'nullable', 'integer', 'min:1'],
]);
$sessionId = (int) ($data['audience_session_id'] ?? 0);
$todayStart = Carbon::now(BizDateTime::TIMEZONE)->startOfDay()->utc();
$todayEnd = Carbon::now(BizDateTime::TIMEZONE)->endOfDay()->utc();
$sessions = $competition->audienceSessions()
->withCount([
'registrations',
'registrations as checked_in_count' => fn ($q) => $q->whereNotNull('checked_in_at'),
'registrations as today_checked_in_count' => fn ($q) => $q
->whereNotNull('checked_in_at')
->whereBetween('checked_in_at', [$todayStart, $todayEnd]),
])
->when($sessionId > 0, fn ($q) => $q->whereKey($sessionId))
->orderBy('sort')
->orderBy('id')
->get()
->map(function (AudienceSession $session) {
$registered = $session->registeredCount();
$checkedIn = $session->checkedInCount();
$todayChecked = (int) ($session->today_checked_in_count ?? 0);
return [
'audience_session_id' => $session->id,
'session_name' => $session->name,
'registered_count' => $registered,
'checked_in_count' => $checkedIn,
'not_checked_in_count' => max(0, $registered - $checkedIn),
'today_checked_in_count' => $todayChecked,
'checkin_rate' => $registered > 0 ? round($checkedIn / $registered, 4) : 0,
];
});
$registered = (int) $sessions->sum('registered_count');
$checkedIn = (int) $sessions->sum('checked_in_count');
$todayChecked = (int) $sessions->sum('today_checked_in_count');
return response()->json([
'registered_count' => $registered,
'checked_in_count' => $checkedIn,
'not_checked_in_count' => max(0, $registered - $checkedIn),
'today_checked_in_count' => $todayChecked,
'checkin_rate' => $registered > 0 ? round($checkedIn / $registered, 4) : 0,
'sessions' => $sessions->values(),
]);
}
}