Files
CFDivePlatform/openspec/specs/course-scheduling/spec.md
T
a620906209 975b56ca54 feat:實作預約系統 — 時段管理、預約生命週期與前端整合
後端:
- 新增 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>
2026-05-12 00:24:51 +08:00

71 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### Requirement: Provider 建立開課時段
Provider SHALL 能為自己擁有的 DivingOffer 建立開課時段,指定日期、開始時間、人數上限。
#### Scenario: 成功建立時段
- **WHEN** Provider 送出 `POST /api/provider/schedules`,包含合法的 `diving_offer_id``scheduled_date`(未來日期)、`start_time``max_participants`(≥1
- **THEN** 系統建立 CourseSchedulestatus 為 `open`,回傳 201 與新時段資料
#### Scenario: 不可為他人課程建立時段
- **WHEN** Provider 送出的 `diving_offer_id` 屬於其他 Provider
- **THEN** 系統回傳 403 Forbidden
#### Scenario: 日期不可為過去
- **WHEN** `scheduled_date` 早於今天
- **THEN** 系統回傳 422,錯誤訊息指出日期無效
### Requirement: Provider 管理既有時段
Provider SHALL 能更新或取消自己的開課時段。
#### Scenario: 更新時段資訊
- **WHEN** Provider 送出 `PUT /api/provider/schedules/{id}`,修改 `start_time``max_participants`
- **THEN** 系統更新時段資料,回傳更新後內容
#### Scenario: 取消時段
- **WHEN** Provider 送出 `DELETE /api/provider/schedules/{id}`
- **THEN** 系統將時段 status 改為 `cancelled`,不實體刪除;cascade 處理所有相關 Booking(詳見下方 Requirement
#### Scenario: 不可修改他人時段
- **WHEN** Provider 嘗試修改不屬於自己的時段
- **THEN** 系統回傳 403 Forbidden
### Requirement: 取消時段的 Booking Cascade 處理
Provider 取消時段時,系統 SHALL 在同一 DB transaction 內處理該時段下所有活躍 Booking,並明確定義各狀態的 cascade 規則。
#### Scenario: pending Booking cascade 為 provider_cancelled
- **WHEN** Provider 取消時段,時段下存在 status 為 `pending` 的 Booking
- **THEN** 這些 Booking status 全部改為 `provider_cancelled`
#### Scenario: confirmed Booking cascade 為 provider_cancelled
- **WHEN** Provider 取消時段,時段下存在 status 為 `confirmed` 的 Booking
- **THEN** 這些 Booking status 全部改為 `provider_cancelled``current_participants` 不需調整(時段已取消)
#### Scenario: 終態 Booking 不受 cascade 影響
- **WHEN** Provider 取消時段,時段下存在 status 為 `completed``rejected``expired``member_cancelled``provider_cancelled` 的 Booking
- **THEN** 這些 Booking status 維持不變
#### Scenario: cascade 在同一 transaction 內完成
- **WHEN** Provider 取消時段
- **THEN** 時段狀態更新與所有 Booking cascade 更新在同一 DB transaction 內完成;任一失敗則全部 rollbackAPI 回傳 500
### Requirement: 時段人數自動管理
系統 SHALL 在預約確認時自動累計 `current_participants`,並於額滿時將時段 status 改為 `full``current_participants` 只計算 confirmed 人數,pending 不佔位。
#### Scenario: 預約確認後人數更新
- **WHEN** Provider 確認一筆 Bookingconfirmed),booking 的 `participants` 為 N
- **THEN** `course_schedules.current_participants` 增加 N;若達到 `max_participants` 則 status 改為 `full`
#### Scenario: 預約取消後人數釋放
- **WHEN** 一筆 `confirmed` 狀態的 Booking 被取消(member_cancelled 或 provider_cancelled
- **THEN** `current_participants` 減少對應人數;若原本為 `full` 則 status 改回 `open`
### Requirement: Member 查詢可用時段
Member SHALL 能查詢指定課程的可用開課時段列表。
#### Scenario: 取得開放時段
- **WHEN** 任何人(含未登入)送出 `GET /api/diving-offers/{id}/schedules`
- **THEN** 系統回傳該課程 status 為 `open`、日期未過的時段列表(含剩餘名額 `remaining_spots`),依日期升冪排序
#### Scenario: 已滿時段不顯示
- **WHEN** 時段 status 為 `full`
- **THEN** 不包含在上述列表中