975b56ca54
後端: - 新增 course_schedules / bookings migration(含索引) - BookingStatus / ScheduleStatus PHP BackedEnum - CourseSchedule / Booking Model(七狀態機 VALID_TRANSITIONS) - ScheduleController、ProviderBookingController、MemberBookingController - 雙層名額驗證(API 層快速失敗 + DB lockForUpdate 防超賣) - 24h 取消截止、pending 不佔位設計 - ExpirePendingBookings(每小時)/ CompleteFinishedBookings(每日)Scheduler - Docker cron 配置、CACHE_STORE 改為 file 修正 502 前端: - 課程詳情頁加入時段選擇與預約流程 - 我的預約頁(展開式卡片、狀態說明、連結課程詳情) - Coach 時段管理(上午/下午時間選擇器、新課程引導) - Coach 預約管理(依課程分組、待確認徽章) - Navbar 新增「我的預約」與「時段/預約管理」入口 - 密碼格式提示與即時比對 OpenSpec: - booking-system change 歸檔至 archive/2026-05-12-booking-system - 新增 specs/course-scheduling 與 specs/booking-lifecycle 主規格 Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
55 lines
1.2 KiB
PHP
55 lines
1.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Enums\ScheduleStatus;
|
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
class CourseSchedule extends Model
|
|
{
|
|
protected $fillable = [
|
|
'diving_offer_id',
|
|
'provider_id',
|
|
'scheduled_date',
|
|
'start_time',
|
|
'max_participants',
|
|
'current_participants',
|
|
'status',
|
|
];
|
|
|
|
protected $casts = [
|
|
'scheduled_date' => 'date',
|
|
'max_participants' => 'integer',
|
|
'current_participants' => 'integer',
|
|
'status' => ScheduleStatus::class,
|
|
];
|
|
|
|
public function divingOffer()
|
|
{
|
|
return $this->belongsTo(DivingOffer::class);
|
|
}
|
|
|
|
public function provider()
|
|
{
|
|
return $this->belongsTo(User::class, 'provider_id');
|
|
}
|
|
|
|
public function bookings()
|
|
{
|
|
return $this->hasMany(Booking::class, 'schedule_id');
|
|
}
|
|
|
|
public function remainingSpots(): int
|
|
{
|
|
return max(0, $this->max_participants - $this->current_participants);
|
|
}
|
|
|
|
protected function startTime(): Attribute
|
|
{
|
|
return Attribute::make(
|
|
get: fn($value) => $value ? substr($value, 0, 5) : $value,
|
|
);
|
|
}
|
|
}
|