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.

127 lines
3.3 KiB

1 month ago
<?php
namespace App\Models;
use App\Support\BizDateTime;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class AudienceSession extends Model
{
public const STATUS_OPEN = 'open';
public const STATUS_NOT_STARTED = 'not_started';
public const STATUS_EXPIRED = 'expired';
public const STATUS_FULL = 'full';
protected $fillable = [
'competition_id',
'code',
'name',
1 month ago
'name_en',
1 month ago
'event_date_text',
1 month ago
'event_date_text_en',
1 month ago
'address',
1 month ago
'address_en',
1 month ago
'capacity',
'signup_start_at',
'signup_end_at',
'is_final',
'sort',
'enabled',
];
/**
* @var array<string, string>
*/
protected $casts = [
'capacity' => 'integer',
'signup_start_at' => 'datetime',
'signup_end_at' => 'datetime',
'is_final' => 'boolean',
'sort' => 'integer',
'enabled' => 'boolean',
];
public function competition(): BelongsTo
{
return $this->belongsTo(Competition::class);
}
public function registrations(): HasMany
{
return $this->hasMany(AudienceRegistration::class);
}
public function registeredCount(): int
{
if (array_key_exists('registrations_count', $this->attributes)) {
return (int) $this->attributes['registrations_count'];
}
return (int) $this->registrations()->count();
}
public function remainingCount(): int
{
return max(0, (int) $this->capacity - $this->registeredCount());
}
public function checkedInCount(): int
{
if (array_key_exists('checked_in_count', $this->attributes)) {
return (int) $this->attributes['checked_in_count'];
}
return (int) $this->registrations()->whereNotNull('checked_in_at')->count();
}
public function windowStatus(?CarbonInterface $now = null): string
{
$now = ($now ?? now())->timezone(BizDateTime::TIMEZONE);
$start = $this->signup_start_at?->copy()->timezone(BizDateTime::TIMEZONE);
$end = $this->signup_end_at?->copy()->timezone(BizDateTime::TIMEZONE);
if ($start && $now->lt($start)) {
return self::STATUS_NOT_STARTED;
}
if ($end && $now->gt($end)) {
return self::STATUS_EXPIRED;
}
if ($this->remainingCount() <= 0) {
return self::STATUS_FULL;
}
return self::STATUS_OPEN;
}
public function isAvailable(?CarbonInterface $now = null): bool
{
return $this->enabled && $this->windowStatus($now) === self::STATUS_OPEN;
}
public function statusLabel(?CarbonInterface $now = null): string
{
return match ($this->windowStatus($now)) {
self::STATUS_NOT_STARTED => $this->openFromLabel(),
self::STATUS_EXPIRED => '已截止',
self::STATUS_FULL => '已满额',
default => '报名中',
};
}
public function openFromLabel(): string
{
$start = $this->signup_start_at?->copy()->timezone(BizDateTime::TIMEZONE);
if ($start === null) {
return '即将开放';
}
return $start->format('m.d').' 开放';
}
}