Files
CFDivePlatform/openspec/specs/notification-triggers/spec.md
T
a620906209 0cd1047b10
Run Tests / test (pull_request) Successful in 30s
docs(openspec): 補齊所有 spec 缺少的 Purpose section 與規格標頭
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
2026-08-03 04:11:06 +08:00

5.9 KiB
Raw Blame History

notification-triggers Specification

Purpose

定義預約與評價各事件(建立、確認、拒絕、取消、完成、收到評價)觸發站內與 Email 通知的時機與收件人,且通知失敗不影響主業務。

Requirements

Requirement: 預約建立觸發通知

系統 SHALL 在預約成功建立(status = pending)時,通知課程所屬 Provider(站內 + Email)。觸發點在 MemberBookingController::store() 的 DB transaction commit 之後。

Scenario: Member 建立預約

  • WHEN MemberBookingController::store() 成功建立預約並回傳 201
  • THEN 取得 $booking->schedule->divingOffer->providerProvider),呼叫 $provider->notify(new BookingCreatedNotification($booking)),以 try/catch 包裹

Requirement: 預約確認觸發通知

系統 SHALL 在 Provider 確認預約(status pendingconfirmed)時,通知 Member(站內 + Email)。觸發點在 ProviderBookingController::confirm() 的 DB transaction commit 之後。

Scenario: Provider 確認預約

  • WHEN ProviderBookingController::confirm() 執行,狀態更新為 confirmed
  • THEN 取得 $booking->member,呼叫 $member->notify(new BookingConfirmedNotification($booking))

Requirement: 預約拒絕觸發通知

系統 SHALL 在 Provider 拒絕預約(status → rejected)時,通知 Member(站內 + Email)。觸發點在 ProviderBookingController::reject()$booking->update() 之後。

Scenario: Provider 拒絕預約

  • WHEN ProviderBookingController::reject() 執行
  • THEN 取得 $booking->member,呼叫 $member->notify(new BookingRejectedNotification($booking))

Requirement: BookingCancelledNotification 文案區分

BookingCancelledNotification SHALL 依建構子參數 cancelledBy: 'member' | 'provider' 產生不同文案:

cancelledBy 通知對象 title body
'member' Provider 學員取消了預約 學員已取消《{課程名稱}》的預約(時段:{日期})
'provider' Member 教練取消了你的預約 教練已取消你的《{課程名稱}》預約(時段:{日期}),如有疑問請聯繫教練

toArray()action_url

  • cancelledBy: 'member'{FRONTEND_URL}/coach/bookings
  • cancelledBy: 'provider'{FRONTEND_URL}/my-bookings/{booking.id}

Scenario: 文案依角色區分

  • WHEN new BookingCancelledNotification($booking, cancelledBy: 'member')toArray() 被呼叫
  • THEN title 為「學員取消了預約」,action_url 指向 /coach/bookings

Scenario: Provider 取消文案

  • WHEN new BookingCancelledNotification($booking, cancelledBy: 'provider')toArray() 被呼叫
  • THEN title 為「教練取消了你的預約」,action_url 指向 /my-bookings/{id}

Requirement: 預約取消觸發通知(Member 發起)

系統 SHALL 在 Member 取消預約(status → member_cancelled)時,通知 Provider(站內 + Email)。觸發點在 MemberBookingController::cancel() 的 DB transaction commit 之後。

Scenario: Member 取消預約

  • WHEN MemberBookingController::cancel() 執行,$booking->update(['status' => BookingStatus::MemberCancelled])
  • THEN 取得 $booking->schedule->divingOffer->providerProvider),呼叫 $provider->notify(new BookingCancelledNotification($booking, cancelledBy: 'member'))

Requirement: 預約取消觸發通知(Provider 發起)

系統 SHALL 在 Provider 取消預約(status → provider_cancelled)時,通知 Member(站內 + Email)。觸發點在 ProviderBookingController::cancel() 的 DB transaction commit 之後。

Scenario: Provider 取消預約

  • WHEN ProviderBookingController::cancel() 執行,$booking->update(['status' => BookingStatus::ProviderCancelled])
  • THEN 取得 $booking->member,呼叫 $member->notify(new BookingCancelledNotification($booking, cancelledBy: 'provider'))

Requirement: 預約完成觸發通知

系統 SHALL 在預約標記為完成(status → completed)時,通知 Member 可前往評價(站內 + Email)。觸發點包含:ProviderBookingController::complete()(手動)與 CompleteFinishedBookings Command(排程自動完成)。

Scenario: 手動完成

  • WHEN ProviderBookingController::complete() 執行
  • THEN 取得 $booking->member,呼叫 $member->notify(new BookingCompletedNotification($booking))

Scenario: 排程自動完成(含 N+1 防護)

  • WHEN CompleteFinishedBookings::handle() 執行
  • THEN 使用 ->with(['member', 'schedule.divingOffer'])->get() 取得 booking 集合(禁止 bulk ->update()),loop 內逐筆 $booking->update(status: Completed) + try/catch notify;單筆 notify 失敗不中斷整個批次

Requirement: 收到評價觸發通知

系統 SHALL 在 Member 成功提交評價後,通知被評價課程的 Provider(僅站內通知,無 Email)。觸發點在 ReviewController::store() 的 DB transaction commit 之後。

取得 Provider 的方式:DivingOffer::with('provider')->findOrFail($offerId)->providerDivingOffer belongsTo User)。

Scenario: Member 提交評價

  • WHEN ReviewController::store() 的 DB transaction 成功,$review 建立完成
  • THEN 取得 $offer->providerProvider),呼叫 $provider->notify(new ReviewReceivedNotification($review))(僅 ['database']

Requirement: 通知觸發為原子操作,不影響主業務

所有 notify 呼叫 SHALL 以 try/catch (\Throwable $e) 包裹,若失敗僅寫入 Laravel log,不得造成主業務回傳錯誤或 rollback。

Scenario: notify 失敗不影響主業務

  • WHEN $user->notify(...) 拋出任何例外
  • THEN 預約/評價主業務資料已正確儲存,HTTP response 正常回傳,\Log::error(...) 記錄錯誤