# 通知文檔 ## 通知類型 | 類型 | 編號 | |------------|----| | [訂單已配對司機](#訂單已配對司機) | 0 | | [司機已接單](#司機已接單) | 1 | | [配對訂單已被乘客取消](#配對訂單已被乘客取消) | 2 | | [訂單已完成](#訂單已完成) | 3 | | [配對訂單超時](#配對訂單超時) | 4 | | [訂單已被運營人員取消](#訂單已被運營人員取消) | 5 | ## 通知資料欄位意義 ### 訂單已配對司機 | key | 意義 | 類型 | |-------------------|---------|----------------| | no_type | 通知類型 | string | | no_type_id | 通知編號 | int | | driver_name | 司機名稱 | string or null | | passenger_name | 乘客名稱 | string | | order_id | 訂單 id | string | | pick_up_location | 上車地點 | string or null | | pick_up_lat | 上車緯度 | float or null | | pick_up_lng | 上車經度 | float or null | | pick_up_remark | 上車備注 | string or null | | drop_off_location | 下車地點 | float or null | | drop_off_lat | 下車緯度 | float or null | | drop_off_lng | 下車經度 | string or null | | payment_method | 付款方式 | string or null | | accepted_charge | 可接受收費標準 | string | | passenger_count | 乘客人數 | int | | remark | 備注 | string or null | | add_money | 加錢 | float | | tips | 小費 | float | ### 司機已接單 | key | 意義 | 類型 | |-------------------|---------|----------------| | no_type | 通知類型 | string | | no_type_id | 通知編號 | int | | driver_name | 司機名稱 | string or null | | passenger_name | 乘客名稱 | string | | order_id | 訂單 id | string | | pick_up_location | 上車地點 | string or null | | pick_up_lat | 上車緯度 | float or null | | pick_up_lng | 上車經度 | float or null | | pick_up_remark | 上車備注 | string or null | | drop_off_location | 下車地點 | float or null | | drop_off_lat | 下車緯度 | float or null | | drop_off_lng | 下車經度 | string or null | | payment_method | 付款方式 | string or null | | accepted_charge | 可接受收費標準 | string | | passenger_count | 乘客人數 | int | | remark | 備注 | string or null | | add_money | 加錢 | float | | tips | 小費 | float | ### 配對訂單已被乘客取消 | key | 意義 | 類型 | |-------------------|---------|----------------| | no_type | 通知類型 | string | | no_type_id | 通知編號 | int | | driver_name | 司機名稱 | string or null | | passenger_name | 乘客名稱 | string | | order_id | 訂單 id | string | | pick_up_location | 上車地點 | string or null | | pick_up_lat | 上車緯度 | float or null | | pick_up_lng | 上車經度 | float or null | | pick_up_remark | 上車備注 | string or null | | drop_off_location | 下車地點 | float or null | | drop_off_lat | 下車緯度 | float or null | | drop_off_lng | 下車經度 | string or null | | payment_method | 付款方式 | string or null | | accepted_charge | 可接受收費標準 | string | | passenger_count | 乘客人數 | int | | remark | 備注 | string or null | | add_money | 加錢 | float | | tips | 小費 | float | ### 訂單已完成 | key | 意義 | 類型 | |-------------------|---------|----------------| | no_type | 通知類型 | string | | no_type_id | 通知編號 | int | | driver_name | 司機名稱 | string or null | | passenger_name | 乘客名稱 | string | | order_id | 訂單 id | string | | pick_up_location | 上車地點 | string or null | | pick_up_lat | 上車緯度 | float or null | | pick_up_lng | 上車經度 | float or null | | pick_up_remark | 上車備注 | string or null | | drop_off_location | 下車地點 | float or null | | drop_off_lat | 下車緯度 | float or null | | drop_off_lng | 下車經度 | string or null | | payment_method | 付款方式 | string or null | | accepted_charge | 可接受收費標準 | string | | passenger_count | 乘客人數 | int | | remark | 備注 | string or null | | add_money | 加錢 | float | | tips | 小費 | float | ### 配對訂單超時 | key | 意義 | 類型 | |-------------------|---------|----------------| | no_type | 通知類型 | string | | no_type_id | 通知編號 | int | | driver_name | 司機名稱 | string or null | | passenger_name | 乘客名稱 | string | | order_id | 訂單 id | string | | pick_up_location | 上車地點 | string or null | | pick_up_lat | 上車緯度 | float or null | | pick_up_lng | 上車經度 | float or null | | pick_up_remark | 上車備注 | string or null | | drop_off_location | 下車地點 | float or null | | drop_off_lat | 下車緯度 | float or null | | drop_off_lng | 下車經度 | string or null | | payment_method | 付款方式 | string or null | | accepted_charge | 可接受收費標準 | string | | passenger_count | 乘客人數 | int | | remark | 備注 | string or null | | add_money | 加錢 | float | | tips | 小費 | float | ### 訂單已被運營人員取消 | key | 意義 | 類型 | |-------------------|---------|----------------| | no_type | 通知類型 | string | | no_type_id | 通知編號 | int | | driver_name | 司機名稱 | string or null | | passenger_name | 乘客名稱 | string | | order_id | 訂單 id | string | | pick_up_location | 上車地點 | string or null | | pick_up_lat | 上車緯度 | float or null | | pick_up_lng | 上車經度 | float or null | | pick_up_remark | 上車備注 | string or null | | drop_off_location | 下車地點 | float or null | | drop_off_lat | 下車緯度 | float or null | | drop_off_lng | 下車經度 | string or null | | payment_method | 付款方式 | string or null | | accepted_charge | 可接受收費標準 | string | | passenger_count | 乘客人數 | int | | remark | 備注 | string or null | | add_money | 加錢 | float | | tips | 小費 | float | # 訂單聊天室整合指南 本文檔針對第三方系統或前端團隊,說明如何串接訂單聊天室的 REST API 與即時訊息(WebSocket / Broadcast)。範例皆以 Bearer Token 驗證為主;亦可使用同源的 session cookie(視服務端部署而定)。 ## 概覽 - REST API:用於發送訊息與取得歷史訊息。呼叫後伺服器會將訊息持久化並觸發即時 broadcast。 - 即時訊息:伺服器會 broadcast 到 private channel(頻道格式:`order.{order_id}`),已授權的使用者可以訂閱並收到新訊息事件。 ------------------------------------------------------------------------ ## 認證(Authentication) - REST API:在 HTTP header 中帶入 `Authorization: Bearer <TOKEN>`(或採用同源 session cookie)。 - 頻道授權(private channel):前端在建立 WebSocket 或 Echo 時,會由 client 向 `/broadcasting/auth` 發出授權請求,此請求也需要攜帶相同的驗證(Authorization header 或 cookie)。 範例 header: ``` Authorization: Bearer <YOUR_TOKEN> Accept: application/json Content-Type: application/json ``` ------------------------------------------------------------------------ ## REST API - 端點說明 Base URL 範例:`{BASE_URL}`(請替換成貴方環境) ### 1) 發送訊息 - 方法:POST - 路徑:`/api/order/chatroom/sent` - 必要 Header:`Authorization: Bearer <TOKEN>`、`Accept: application/json` - Body (JSON): ```json { "order_id": "ORD12345", "message": "我到了,請下樓。" } ``` - 行為:伺服器會驗證 `order_id` 與當前使用者是否有權存取該訂單聊天室(例如乘客或司機),若合法則: 1. 觸發即時事件(broadcast 到 private channel `order.{order_id}`) 2. 將訊息寫入資料庫(持久化) - 成功回應 (HTTP 200): ```json { "success": true, "message": "訊息已成功發送", "data": [] } ``` - 常見錯誤: - 401 Unauthorized:未帶 token 或 token 無效 - 422 Unprocessable Entity:參數驗證錯誤(例如 `order_id` 不存在、`message` 違反長度限制) - 403 Forbidden:使用者無權存取該訂單聊天室 - 500 Internal Server Error:伺服器錯誤 範例 curl: ```bash curl -X POST "{BASE_URL}/api/order/chatroom/sent" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"order_id":"ORD12345","message":"我到達集合地點"}' ``` JavaScript fetch 範例: ```javascript await fetch(`${BASE_URL}/api/order/chatroom/sent`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer ' + token, }, body: JSON.stringify({ order_id: 'ORD12345', message: '我到了' }) }); ``` ### 2) 取得聊天室歷史 - 方法:GET - 路徑:`/api/order/chatroom/history` - 參數(Query):`order_id`(必填) - 必要 Header:`Authorization: Bearer <TOKEN>` - 成功回應 (HTTP 200): ```json { "success": true, "message": "取得聊天室歷史資料成功", "data": [ { "id": 123, "order_id": "ORD12345", "user_id": 45, "message": "您好", "role": 0, "created_at": "2025-09-23T15:30:00Z", "user": { "id":45, "name":"乘客A" } } ] } ``` 範例 curl: ```bash curl -G "{BASE_URL}/api/order/chatroom/history" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "order_id=ORD12345" ``` 備註:回傳的 `data` 為訊息陣列(時間排序依服務端實作可能為新->舊或舊->新),請在介面上處理排序與分頁(若必要)。 ------------------------------------------------------------------------ ## 即時訊息(WebSocket / Broadcast)整合 ### 頻道與事件 - **頻道名稱(Private Channel)**:`order.{order_id}`(例如 `order.ORD12345`) - **事件名稱**:`OrderMessageSentEvent`(依廣播實作,實際前端收到的 event 名稱可能為短名或包含 namespace,但使用 `OrderMessageSentEvent` 應可接收) - **事件載荷(payload)**:伺服器會送出 JSON,通常包含下列欄位(請以實際收到的 payload 為準): ```json { "order": { "order_id": "ORD12345", "id": 987 }, "sender": { "id": 45, "name": "乘客A" }, "role": 0, "message": "我在路口,請下樓" } ``` 說明:`role` 通常為數字(0 表示乘客、1 表示司機)。`sender` 是發送者(簡化的 user 資料)。 ### 訂閱流程(以 Laravel Echo + Pusher/laravel-websockets 為例) 1. 建立 Echo 並在 auth headers 中帶入 Bearer token(或使用同源 cookie): ```javascript import Echo from 'laravel-echo'; import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: '<PUSHER_KEY>', wsHost: window.location.hostname, wsPort: 6001, forceTLS: false, disableStats: true, authEndpoint: '/broadcasting/auth', auth: { headers: { Authorization: 'Bearer ' + token, Accept: 'application/json', } } }); ``` 2. 訂閱 private channel 並監聽事件: ```javascript const orderId = 'ORD12345'; Echo.private(`order.${orderId}`) .listen('OrderMessageSentEvent', (e) => { // e.message, e.sender, e.role, e.order console.log('收到聊天室訊息', e); }); ``` 若使用 socket.io 或其他 broadcaster,初始化方式會不同,但重點是: - 必須訂閱 private channel `order.{order_id}` - 訂閱前會向 `/broadcasting/auth` 要求授權,請確保此 request 帶入正確的認證資訊 ### 授權失敗處理 - 若 `/broadcasting/auth` 回傳 401/403,表示 client 未通過授權,無法訂閱 private channel。請確認: - token 是否正確 - token 是否有對應使用者且該使用者為該訂單的乘客或司機(或為被允許的 matched 身分) 範例授權失敗 response(HTTP 401/403): ```json { "message": "Unauthenticated." } ``` 或 ```json { "message": "This action is unauthorized." } ``` ### 事件接收注意事項 - 伺服器 broadcast 的 payload 可能包含模型的部分欄位,請在接收端依需要映射或過濾。 - 建議實做「樂觀 UI」:先在 client 端顯示暫存訊息(pending),待 POST API 回傳成功或收到 broadcast 回覆後將狀態調整為已送達。 - 不要只依賴 socket 訊息來持久化訊息(例如如果 client 直接把訊息送到 socket 而非 POST API,可能會無法持久化或繞過後端驗證)。 ------------------------------------------------------------------------ ## 錯誤類型與範例處理 - 422 Validation Error 範例(Laravel 典型格式): ```json { "message": "The given data was invalid.", "errors": { "order_id": ["使用者無權限存取此訂單聊天室。"], "message": ["The message field is required."] } } ``` - 401/403:請提示使用者重新登入或聯繫後端以取得存取權限。