0cd1047b10
Run Tests / test (pull_request) Successful in 30s
repo 內 35/38 份既有 spec 都是舊版歸檔流程留下的原始 delta 內容(`## ADDED Requirements`,缺標題與 Purpose),openspec CLI 現版本嚴格驗證 (--strict)會報錯或警告。逐一補上: - 缺標題/Purpose 的 30 份:加上 `# <name> Specification` 標題 + 依內容 撰寫的 Purpose 段落,`## ADDED Requirements` 改回 `## Requirements` - compose-cloud-baseline、scheduler-container:已有標題與 Requirements,只補 Purpose 包裝 - provider-verification:已有標題與 Purpose,只需把 `## ADDED Requirements` 改回 `## Requirements` - admin-user-management、notification-email:各有一條 requirement 缺 SHALL/MUST 關鍵字(純敘述句或表格),補上規範用語,內容不變 `openspec validate --specs --strict`:38/38 全過(原本 4/38)。純文件 補齊,未變更任何 requirement 的實質內容或行為描述。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011LpcY9b7y9x4fusHBTXciQ
6.9 KiB
6.9 KiB
booking-lifecycle Specification
Purpose
定義預約系統的七狀態機、建立/確認/拒絕/取消流程、防超賣的名額驗證、Scheduler 自動過期與完成,以及狀態轉換觸發的通知。
Requirements
Requirement: Member 送出預約
Member SHALL 能選擇一個開放時段送出預約,系統記錄價格快照。pending 狀態不佔用時段名額。
Scenario: 成功建立預約
- WHEN 已登入 Member 送出
POST /api/member/bookings,指定schedule_id與participants(≥1) - THEN 系統建立 Booking,status 為
pending,total_price快照為diving_offer.price × participants,回傳 201
Scenario: 時段已滿無法預約
- WHEN 指定時段 status 為
full或cancelled - THEN 系統回傳 422,告知時段不可用
Scenario: 超過剩餘名額(API 層快速驗證)
- WHEN
participants大於時段當前剩餘名額(max_participants - current_participants),在進入 DB transaction 前 - THEN 系統回傳 422,告知人數超過上限,不進入 lockForUpdate 流程
Scenario: 超過剩餘名額(DB 層二次驗證)
- WHEN API 層通過但 lockForUpdate 後重新計算剩餘名額仍不足(race condition 情境)
- THEN 系統 rollback transaction,回傳 422,告知名額不足
Scenario: 不可重複預約同一時段
- WHEN Member 對同一
schedule_id已有pending或confirmed狀態的 Booking - THEN 系統回傳 422,告知已有預約(取消後可重新預約)
Requirement: 預約狀態機
系統 SHALL 維護七個合法狀態,且只允許以下轉換:
pending→confirmed(Provider 確認)pending→rejected(Provider 拒絕)pending→member_cancelled(Member 取消)pending→expired(Scheduler 超時)confirmed→completed(Scheduler 課程後自動)confirmed→member_cancelled(Member 取消)confirmed→provider_cancelled(Provider 取消)
各狀態對應訊息頻道語義:
confirmed:presence-booking.{id}頻道開放,可讀寫訊息completed:頻道關閉,訊息歷史封存唯讀- 其餘狀態(
pending、rejected、expired、member_cancelled、provider_cancelled):無訊息頻道
Scenario: 非法狀態轉換被拒絕
- WHEN 任何角色嘗試執行上述以外的狀態轉換
- THEN 系統回傳 422,說明當前狀態不允許此操作
Scenario: confirmed 轉 completed 時封存訊息頻道
- WHEN Scheduler 將
confirmed預約轉為completed - THEN 對應
presence-booking.{id}頻道不再授權新連線;現有訊息歷史保留,後續 POST 訊息回傳 403
Requirement: Provider 確認或拒絕預約
Provider SHALL 能對自己課程的 pending 預約執行確認或拒絕。確認時才真正佔用時段名額。
Scenario: 確認預約
- WHEN Provider 送出
PUT /api/provider/bookings/{id}/confirm - THEN Booking status 改為
confirmed,時段current_participants增加對應人數
Scenario: 拒絕預約
- WHEN Provider 送出
PUT /api/provider/bookings/{id}/reject - THEN Booking status 改為
rejected,current_participants不變(pending 未佔位)
Scenario: 只能操作自己課程的預約
- WHEN Provider 嘗試操作不屬於自己課程的 Booking
- THEN 系統回傳 403 Forbidden
Requirement: Provider 取消已確認預約
Provider SHALL 能取消 confirmed 狀態的預約(例如天氣因素)。
Scenario: Provider 取消確認中預約
- WHEN Provider 送出
PUT /api/provider/bookings/{id}/cancel - THEN Booking status 改為
provider_cancelled,時段名額釋放
Requirement: Member 取消預約
Member SHALL 能取消自己的 pending 或 confirmed 預約,但須在課程開始前 24 小時之前提出。
Scenario: 取消 pending 預約(期限內)
- WHEN Member 送出
DELETE /api/member/bookings/{id},Booking status 為pending,且當前時間早於scheduled_date + start_time - 24h - THEN Booking status 改為
member_cancelled,current_participants不變
Scenario: 取消 confirmed 預約(期限內)
- WHEN Member 送出
DELETE /api/member/bookings/{id},Booking status 為confirmed,且當前時間早於scheduled_date + start_time - 24h - THEN Booking status 改為
member_cancelled,時段名額釋放
Scenario: 課程開始前 24h 內不可取消
- WHEN Member 送出
DELETE /api/member/bookings/{id},但當前時間距scheduled_date + start_time不足 24 小時 - THEN 系統回傳 422,告知「距課程開始不足 24 小時,無法取消,請聯繫教練」;Booking 狀態不變
Scenario: 不可取消已終態預約
- WHEN Booking status 為
completed、rejected、expired、provider_cancelled - THEN 系統回傳 422,告知無法取消
Requirement: 系統自動過期 pending 預約
Scheduler SHALL 每小時掃描 pending 超過 48 小時的 Booking 並標記為 expired。
Scenario: 過期觸發
- WHEN Booking status 為
pending且created_at早於 48 小時前 - THEN Scheduler 將 status 改為
expired,current_participants不變(pending 未佔位)
Requirement: 系統自動完成 confirmed 預約
Scheduler SHALL 每日掃描課程日期已過的 confirmed Booking 並標記為 completed。
Scenario: 自動完成
- WHEN Booking status 為
confirmed,對應course_schedule.scheduled_date早於今天 - THEN Scheduler 將 status 改為
completed
Requirement: Member 查看自己的預約列表
Member SHALL 能查詢自己所有預約的列表及詳情,含課程連結與完整課程資訊。
Scenario: 取得預約列表
- WHEN 已登入 Member 送出
GET /api/member/bookings - THEN 系統回傳該 Member 所有 Booking,含 offer_id、課程名稱、地點、時段日期、狀態、金額
Scenario: 取得單一預約詳情
- WHEN 已登入 Member 送出
GET /api/member/bookings/{id} - THEN 系統回傳該 Booking 詳情;若非本人預約則回傳 403
Requirement: 預約狀態轉換觸發通知
預約七狀態機(pending / confirmed / completed / rejected / expired / member_cancelled / provider_cancelled)的每個轉換點,系統 SHALL 在狀態成功更新後觸發對應通知(詳見 notification-triggers spec)。通知觸發 MUST 在主業務 transaction commit 之後執行,且以 try/catch 包裹,不影響主業務結果。
Scenario: 狀態轉換後通知觸發
- WHEN
BookingService中任一狀態轉換方法成功執行 - THEN 對應的 Notification class 被觸發,不論通知是否成功主業務均正常回傳
Scenario: 通知失敗不影響主業務
- WHEN notify 呼叫拋出例外
- THEN 預約狀態已正確儲存,HTTP response 成功回傳,錯誤記錄至 Laravel log