Files
CFDivePlatform/openspec/changes/realtime-booking-chat/specs/user-presence/spec.md
T
a620906209 ab24f210a6 feat: 即時預約聊天室(realtime booking chat)
## 新增功能
- 會員與教練在 confirmed 預約期間可互傳文字與圖片訊息
- Presence Channel 顯示對方在線狀態(即時加入/離線)
- 已讀回執:對方讀取訊息後即時更新
- 預約完成(completed)後訊息封存唯讀
- 圖片上傳使用 intervention/image 處理(移除 EXIF、限制尺寸、強制重新編碼)

## 通知系統
- 收到新訊息時 Bell Icon 即時更新(NotificationCreated via private channel)
- 預約列表卡片顯示未讀角標(GET /api/bookings/messages/unread-counts 批次查詢)
- 瀏覽器分頁在背景時推送 Web Notification

## 基礎建設
- 引入 Laravel Reverb 作為自架 WebSocket 伺服器
- docker-compose 新增 reverb service(連接 proxy_net,供 NPM 代理)
- 前端安裝 laravel-echo + pusher-js,初始化 Echo plugin
- Dockerfile 補 GD JPEG/WebP/FreeType 支援

## 清理
- 移除 test_broadcast.php 與 resources/js/echo.js(Blade 殘留)
- 移除 /ping、/testpost 測試路由
- channels.php 改用 class-based 授權語法,移除 debug log
- BookingChat.vue otherType 提取為 computed 消除重複
- docker-entrypoint.sh 健康檢查改用 env var 取代硬編碼密碼

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 03:59:14 +08:00

54 lines
2.7 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.
## ADDED Requirements
### Requirement: 訂閱預約 Presence Channel
已認證的 Member 與 Provider SHALL 能透過 Laravel Echo 訂閱 `presence-booking.{booking_id}` 頻道,訂閱時系統 SHALL 驗證使用者確為該預約的參與方。
#### Scenario: 合法參與方成功訂閱
- **WHEN** 已認證使用者帶有效 Bearer token 連線至 `wss://ws.hank-space.com`,並訂閱 `presence-booking.{id}`
- **THEN** `broadcasting/auth` 端點回傳授權成功,使用者加入頻道,頻道廣播 `joining` event(含 `user_id``user_type`
#### Scenario: 非參與方訂閱被拒絕
- **WHEN** 非該預約參與方嘗試訂閱頻道
- **THEN** `broadcasting/auth` 回傳 403,連線不建立
#### Scenario: 預約非 confirmed 狀態時頻道不授權
- **WHEN** 預約 status 不為 `confirmed`,任何使用者嘗試訂閱
- **THEN** `broadcasting/auth` 回傳 403
### Requirement: 在線狀態感知
訂閱 Presence Channel 後,系統 SHALL 回傳目前在線成員清單。任一方加入或離開時 SHALL 廣播對應事件。
#### Scenario: 取得目前在線清單
- **WHEN** 使用者成功加入 `presence-booking.{id}` 頻道
- **THEN** Echo `.here()` callback 收到目前在線的使用者清單(含 `user_id``user_type``name`
#### Scenario: 對方加入頻道
- **WHEN** 對方(Member 或 Provider)開啟訊息視窗並成功訂閱頻道
- **THEN** 已在頻道中的使用者收到 `.joining()` event,可顯示「對方已上線」
#### Scenario: 對方離開頻道
- **WHEN** 對方關閉訊息視窗或斷線
- **THEN** 仍在頻道中的使用者收到 `.leaving()` event,可顯示「對方已離線」
### Requirement: 已讀回執顯示
前端 SHALL 依據 `MessageRead` event 更新訊息的已讀狀態,顯示「已讀」標記。
#### Scenario: 己方訊息被對方讀取後顯示已讀
- **WHEN** 頻道收到 `MessageRead` event`reader_type` 為對方,`last_read_message_id` >= 某訊息 id
- **THEN** 前端將該訊息及之前的己方訊息顯示「已讀」標記
#### Scenario: 對方不在頻道時訊息顯示未讀
- **WHEN** 對方未訂閱頻道(離線)
- **THEN** 新發送的訊息 `read_at` 為 null,顯示「未讀」狀態
### Requirement: 未讀訊息計數
系統 SHALL 在預約列表頁顯示每個預約的未讀訊息數量角標。
#### Scenario: 有未讀訊息時顯示角標
- **WHEN** Member 或 Provider 進入預約列表,某預約有 `read_at IS NULL``sender_type` 為對方的訊息
- **THEN** 對應預約卡片顯示未讀數量角標(數字或紅點)
#### Scenario: 進入訊息視窗後角標清除
- **WHEN** 使用者進入該預約的訊息視窗,送出 `POST /api/bookings/{id}/messages/read`
- **THEN** 未讀角標消失