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
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(),
|
|
]);
|
|
}
|
|
}
|