03f8caf3e9
後端 - 新增 6 個 Notification class(預約建立/確認/拒絕/取消/完成、收到評價),database + mail 雙 channel - 新增 NotificationController(list / unread-count / markRead / markAllRead / destroy) - 整合通知觸發至 MemberBookingController、ProviderBookingController、CompleteFinishedBookings、ReviewController - 新增 notifications / jobs / failed_jobs migration - Docker Compose 加入 queue-worker、mailpit service - DivingOffer 補上 provider() 關聯 前端 - 新增 notificationStore(Polling 30s/60s 自適應 + Page Visibility API) - 新增 NotificationBell(未讀 Badge)、NotificationDrawer(側邊通知中心) - main.js:auth store init 前置於 router.use(),修正 beforeEach guard 時序問題 - notificationAxios:依路徑動態選擇 member/coach token - NotificationDrawer:改用 new URL().pathname 提取 action_url 路徑 OpenSpec - 歸檔 notification-system change - 同步 notification-core / notification-email / notification-triggers specs 至主規格 - 更新 booking-lifecycle / review-lifecycle spec(補充通知觸發 requirement) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
7.8 KiB
7.8 KiB
0. 前置設定
- 0.1 [後端]
config/app.php加入'frontend_url' => env('FRONTEND_URL', 'http://localhost:5173') - 0.2 [後端]
.env.example補上FRONTEND_URL=http://localhost:5173
1. 基礎設施:資料庫與 Queue
- 1.1 [後端] 執行
php artisan notifications:table產生 notifications migration,確認 schema 欄位正確 - 1.2 [後端] 執行
php artisan queue:table產生 jobs / failed_jobs migration(若尚未存在) - 1.3 [後端] 執行
php artisan migrate建立兩張資料表(需 Docker 啟動後執行) - 1.4 [後端] 在
.env設定QUEUE_CONNECTION=database(已存在) - 1.5 [後端]
docker-compose.yml新增queue-workerservice(php artisan queue:work --sleep=3 --tries=3) - 1.6 [後端]
docker-compose.yml新增mailpitservice(image:axllent/mailpit,port 1025/8025),.env設定MAIL_HOST=mailpit MAIL_PORT=1025
2. Notification Classes(後端)
- 2.1 [後端] 建立
app/Notifications/BookingCreatedNotification.php,via()回傳['database', 'mail'],實作toArray()與toMail() - 2.2 [後端] 建立
app/Notifications/BookingConfirmedNotification.php(via: database + mail) - 2.3 [後端] 建立
app/Notifications/BookingRejectedNotification.php(via: database + mail) - 2.4 [後端] 建立
app/Notifications/BookingCancelledNotification.php(via: database + mail,含cancelledBy參數) - 2.5 [後端] 建立
app/Notifications/BookingCompletedNotification.php(via: database + mail) - 2.6 [後端] 建立
app/Notifications/ReviewReceivedNotification.php(via: database 僅站內) - 2.7 [後端] 所有 Notification class 的
toArray()回傳統一結構:{ type, title, body, action_url, related_id, related_type }
3. Email Markdown 模板
- 3.1 [後端] 建立
resources/views/emails/notifications/booking-created.blade.php(Markdown)(改用 toMail() 內聯實作) - 3.2 [後端] 建立
resources/views/emails/notifications/booking-confirmed.blade.php - 3.3 [後端] 建立
resources/views/emails/notifications/booking-rejected.blade.php - 3.4 [後端] 建立
resources/views/emails/notifications/booking-cancelled.blade.php - 3.5 [後端] 建立
resources/views/emails/notifications/booking-completed.blade.php - 3.6 [後端] 確認所有模板包含:平台名稱、通知標題、正文、CTA 按鈕(action_url)、底部免責聲明
4. Notification API(後端)
- 4.1 [後端] 建立
app/Http/Controllers/Api/NotificationController.php,實作index()、unreadCount()、markRead()、markAllRead()、destroy() - 4.2 [後端]
routes/api.php新增路由群組(Sanctum middleware) - 4.3 [後端]
index()分頁 20 筆,依created_atDESC,response 含unread_count與meta - 4.4 [後端]
markRead()/destroy()驗證通知屬於當前使用者(findOrFail 在 user->notifications() 作用域內自動限制)
5. 業務觸發整合(後端,無 Service 層,直接插入 Controller)
- 5.1 [後端]
app/Models/DivingOffer.php補上provider()關聯 - 5.2 [後端] 確認
app/Models/User.php已使用Notifiabletrait(已存在) - 5.3 [後端]
MemberBookingController::store():notifyBookingCreatedNotification - 5.4 [後端]
ProviderBookingController::confirm():notifyBookingConfirmedNotification - 5.5 [後端]
ProviderBookingController::reject():notifyBookingRejectedNotification - 5.6 [後端]
MemberBookingController::cancel():notifyBookingCancelledNotification(cancelledBy: 'member') - 5.7 [後端]
ProviderBookingController::cancel():notifyBookingCancelledNotification(cancelledBy: 'provider') - 5.8 [後端]
ProviderBookingController::complete():notifyBookingCompletedNotification - 5.9 [後端]
CompleteFinishedBookings::handle():改為 get()+loop,逐筆 notify - 5.10 [後端]
ReviewController::store():notifyReviewReceivedNotification
6. 前端 Pinia Store
- 6.1 [前端] 建立
frontend/src/stores/notifications.js,含 state:{ unreadCount, notifications, isOpen } - 6.2 [前端]
notificationStore.startPolling():登入後立即 fetch 一次,未讀 > 0 每 30 秒、= 0 每 60 秒;count 改變時 clearInterval 重啟新間隔 - 6.3 [前端] Page Visibility API 整合:
visibilitychange = hidden暫停 interval;= visible立即 fetch 並重啟 - 6.4 [前端]
notificationStore.stopPolling():登出時 clearInterval + removeEventListener('visibilitychange') - 6.5 [前端]
notificationStore.fetchNotifications():呼叫GET /api/notifications,更新notifications與unreadCount - 6.6 [前端]
notificationStore.markRead(id)/markAllRead()/remove(id)actions(markRead 採 Optimistic update)
7. 前端通知元件
- 7.1 [前端] 建立
frontend/src/components/NotificationBell.vue:Bell Icon + 未讀 Badge(紅色,count > 0 才顯示) - 7.2 [前端] 建立
frontend/src/components/NotificationDrawer.vue:側邊 Drawer,列出通知列表,每項顯示 title / body(截 80 字)/ 相對時間 / 已讀狀態 - 7.3 [前端] Drawer 頂部加「全部標為已讀」按鈕,點擊後呼叫
markAllRead() - 7.4 [前端] 點擊通知項目:呼叫
markRead(id)後router.push(action_url) - 7.5 [前端] 點擊通知項目右側刪除 Icon:呼叫
remove(id)
8. 整合至 NavBar
- 8.1 [前端]
frontend/src/components/NavBar.vue(Member):加入<NotificationBell /> - 8.2 [前端]
frontend/src/components/CoachNavBar.vue(Coach):加入 Bell Icon - 8.3 [前端]
frontend/src/App.vue:加入<NotificationDrawer /> - 8.4 [前端]
frontend/src/stores/auth.js:setAuth/init 呼叫 startPolling,logout 呼叫 stopPolling - 8.5 [前端]
frontend/src/stores/coachAuth.js:同上整合 polling 生命週期
9. 手動驗證
- 9.1 [整合測試] 啟動 Docker Compose(含 queue-worker + mailpit),確認所有 service 正常
- 9.2 [整合測試] Member 建立預約 → Provider 站內通知出現 + Mailpit 收到信
- 9.3 [整合測試] Provider 確認預約 → Member 站內通知出現 + Email
- 9.4 [整合測試] Member 提交評價 → Provider 站內通知出現(無 Email)
- 9.5 [整合測試] Bell Icon 未讀 Badge 顯示正確數量,全部標已讀後 Badge 消失
- 9.6 [整合測試] 點擊通知項目 → 標記已讀 → 跳轉 action_url
- 9.7 [整合測試] Mailpit Web UI(
http://localhost:8025)確認 Email 格式與 CTA 連結正確
10. 整合測試中發現的 Bug 修正
- 10.1 [前端]
main.js:將三個 auth store 的init()移至app.use(router)之前執行,修正beforeEachguard 在 store 初始化前跑導致 protected route 被誤踢的問題 - 10.2 [後端]
BookingConfirmedNotification/BookingRejectedNotification/BookingCancelledNotification:action_url移除/{booking.id}尾綴,改為{FRONTEND_URL}/my-bookings(前端路由無/my-bookings/:id詳情頁) - 10.3 [資料庫] 修正已存在的歷史通知中錯誤的
action_url(UPDATE notifications SET data = JSON_SET(...)) - 10.4 [後端] 建立
failed_jobs資料表(php artisan queue:failed-table && php artisan migrate),修正 queue job 失敗時無法寫入錯誤記錄的問題 - 10.5 [前端]
notificationAxios.js:依window.location.pathname動態選擇 token(/coach開頭優先coach_token,其餘優先token),修正雙 token 環境下通知 API 用錯帳號的問題 - 10.6 [前端]
NotificationDrawer.vue:clickItem()改用new URL(action_url).pathname提取路徑,取代原本replace(window.location.origin, '')的不穩定做法