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
3.9 KiB
111 lines
3.9 KiB
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\AudienceRegistration;
|
|
use App\Models\AudienceSession;
|
|
use App\Models\Competition;
|
|
use App\Models\User;
|
|
use App\Services\Sms\AudienceRegistrationSmsNotifier;
|
|
use App\Support\BizDateTime;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class AudienceRegistrationService
|
|
{
|
|
public function __construct(private readonly AudienceRegistrationSmsNotifier $smsNotifier)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @param array{audience_session_id: int, name: string, company?: string|null, job?: string|null, purpose: string} $data
|
|
*/
|
|
public function register(User $user, Competition $competition, array $data): AudienceRegistration
|
|
{
|
|
$registration = DB::transaction(function () use ($user, $competition, $data) {
|
|
/** @var AudienceSession|null $session */
|
|
$session = AudienceSession::query()
|
|
->where('competition_id', $competition->id)
|
|
->whereKey($data['audience_session_id'])
|
|
->lockForUpdate()
|
|
->first();
|
|
|
|
if ($session === null) {
|
|
throw ValidationException::withMessages([
|
|
'audience_session_id' => ['场次不存在'],
|
|
]);
|
|
}
|
|
|
|
if (! $session->enabled) {
|
|
throw ValidationException::withMessages([
|
|
'audience_session_id' => ['该场次暂未开放报名'],
|
|
]);
|
|
}
|
|
|
|
$now = now()->timezone(BizDateTime::TIMEZONE);
|
|
$status = $session->windowStatus($now);
|
|
if ($status === AudienceSession::STATUS_NOT_STARTED) {
|
|
throw ValidationException::withMessages([
|
|
'audience_session_id' => ['该场次尚未开始报名'],
|
|
]);
|
|
}
|
|
if ($status === AudienceSession::STATUS_EXPIRED) {
|
|
throw ValidationException::withMessages([
|
|
'audience_session_id' => ['该场次报名已截止'],
|
|
]);
|
|
}
|
|
|
|
$already = AudienceRegistration::query()
|
|
->where('audience_session_id', $session->id)
|
|
->where('user_id', $user->id)
|
|
->exists();
|
|
if ($already) {
|
|
throw ValidationException::withMessages([
|
|
'audience_session_id' => ['您已报名该场次'],
|
|
]);
|
|
}
|
|
|
|
$count = AudienceRegistration::query()
|
|
->where('audience_session_id', $session->id)
|
|
->count();
|
|
if ($count >= (int) $session->capacity) {
|
|
throw ValidationException::withMessages([
|
|
'audience_session_id' => ['该场次已满额'],
|
|
]);
|
|
}
|
|
|
|
return AudienceRegistration::query()->create([
|
|
'competition_id' => $competition->id,
|
|
'audience_session_id' => $session->id,
|
|
'user_id' => $user->id,
|
|
'name' => $data['name'],
|
|
'phone' => trim((string) $user->mobile),
|
|
'company' => $this->nullableText($data['company'] ?? null),
|
|
'job' => $this->nullableText($data['job'] ?? null),
|
|
'purpose' => $data['purpose'],
|
|
'answers_json' => is_array($data['answers_json'] ?? null) ? $data['answers_json'] : null,
|
|
'registered_at' => now(),
|
|
]);
|
|
});
|
|
|
|
try {
|
|
$this->smsNotifier->notify($registration);
|
|
} catch (\Throwable $e) {
|
|
Log::error('sms.audience_registration unexpected_error', [
|
|
'registration_id' => $registration->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
|
|
return $registration;
|
|
}
|
|
|
|
private function nullableText(mixed $value): ?string
|
|
{
|
|
$s = trim((string) ($value ?? ''));
|
|
|
|
return $s === '' ? null : $s;
|
|
}
|
|
}
|