跳轉到

即時報價權限(realtime_quote)Integration Guide — Frontend

Backend branch: feat/realtime-quote-tier-gate → cortex/dev 生效條件:live PostgreSQL 先套 migration 168_realtime_quote_tier_feature.sql,再部署 API(順序見 §6)

Consumers:

  • 會員端 App / Web:使用即時報價、盤中 K、首頁大盤即時、持股即時估值的畫面
  • Cortex Management:等級權限頁(/api/admin/membership-tiers)

1. 欄位改名對照表(Breaking)

無欄位改名。Breaking 的地方是權限(§3)與後台等級權限 PUT 的必填欄位(§2)。

2. 新欄位

Endpoint 欄位 型別 必填 語意
GET /api/admin/membership-tiers items[].features.realtime_quote boolean 套完 migration 168 後每個 tier 都有 該 tier 是否可使用 7 個即時端點
PUT /api/admin/membership-tiers/{tier_name}/permissions features.realtime_quote boolean(strict,不接受字串或數字) 是,缺少回 422 同上

migration 168 的初始值:admin = true;free、advance、premium(以及其他沒有此 key 的 tier)= false。

Cortex Management 的 PUT 若是「把 GET 拿到的完整 features 原樣送回」(member-management.md 的既定做法),不需改動即相容;若前端寫死 4 個 key 組 payload,存任何 tier 的權限都會 422,必須補上 realtime_quote,並在權限矩陣加一個 Boolean 開關。

Consumer type(TypeScript):

type TierFeatures = {
  ai_analysis: boolean;
  portfolio_export: boolean;
  realtime_quote: boolean; // 新增,必填
  max_watchlists: number; // -1 = 無限制
  max_watchlist_items: number; // -1 = 無限制
};

3. 行為改變(Status code)

受影響的 7 個端點:

Endpoint 類型
GET /api/stocks/{ticker}/realtime-quote REST
GET /api/stocks/{ticker}/intraday-ohlc REST
GET /api/market/realtime-quote REST
GET /api/market/intraday-ohlc REST
GET /api/stocks/{ticker}/realtime-quote/stream SSE
GET /api/market/realtime-quote/stream SSE
GET /api/portfolio/realtime-quotes/stream SSE
情境 修前 修後
admin 會員(members.is_admin = true) 200 200(不變)
非 admin,所屬 tier 的 realtime_quote 為 false 或缺 200 403 FEATURE_REQUIRED
非 admin,所屬 tier 的 realtime_quote 被後台改為 true — 200
無權限 + 不存在的 ticker 404 403(權限檢查先於 ticker)
無權限 + 非法 interval(intraday-ohlc) 422 403(權限檢查先於 query 驗證)
無權限 + tickers 內容錯誤(空值、空項目、超過 100 個、格式錯、非台股、未收錄) 404 / 422 403
未帶 tickers 參數(portfolio stream) 422 422(不變,FastAPI 參數驗證先於權限檢查)
未帶 Authorization header 422 422(不變)
token 無效/過期 401 401(不變)
帳號停權 403 MEMBER_SUSPENDED 403 MEMBER_SUSPENDED(不變)

403 body(與既有 error envelope 相同):

{
  "error": {
    "code": "FEATURE_REQUIRED",
    "message": "This membership does not include realtime_quote",
    "details": {}
  }
}

SSE 端點在連線時就回 HTTP 403,不會開始送事件。

4. SSE 事件語意

事件型別不變(quote / quotes / session_closed / token_expired / unauthorized)。unauthorized 多了觸發條件:

  • 串流中被取消 admin,或所屬 tier 的 realtime_quote 被改為 false → 下一輪輪詢送 unauthorized 並結束
  • 伺服器端讀不到 admin 權限設定(設定錯誤,503 類)也會落在 unauthorized

前端處理:unauthorized 不要自動重連(既有規則),但不可一律當成登出——應改顯示日資料,必要時重新打一次 REST 端點,用 401 / 403 FEATURE_REQUIRED 分辨原因。

5. 前端怎麼判斷能不能用即時功能

以端點實際回應為準:收到 403 FEATURE_REQUIRED 就隱藏或降級即時 UI,改用日資料。

不要用 /api/members/me 或 /api/auth/me 的 tier.features.realtime_quote 判斷:這兩個端點回的是會員所屬 tier 的設定,不含 admin 的保留權限,所以 admin 在那裡會看到 false,但實際可以使用。已開 follow-up #1924 追蹤讓 /me 回傳有效權限,完成前請依 403 判斷。

建議流程:進入含即時資訊的頁面時先打一次對應 REST 端點(例如 /api/market/realtime-quote);200 才開 SSE,403 FEATURE_REQUIRED 就整頁停用即時功能、本次 session 不再重試。

6. 部署順序與遷移 checklist

  • [ ] 後端:live PostgreSQL 套 migration 168(SIT 由 deploy-sit 的 pending-migration runner 在重建容器前自動套)
  • [ ] 後端:部署 API(本機 cortex-api 為 --reload,主 repo pull 後生效;不需重啟 scheduler)
  • [ ] 會員端:即時報價、盤中 K、首頁大盤即時、持股即時估值處理 403 FEATURE_REQUIRED(降級日資料,不顯示錯誤彈窗、不重試)
  • [ ] 會員端:SSE unauthorized 不視為登出
  • [ ] 會員端:不用 /me 的 features.realtime_quote 決定是否顯示即時 UI
  • [ ] Cortex Management:權限頁 PUT 帶上 realtime_quote(原樣回送 GET 的 features),權限矩陣加 Boolean 開關
  • [ ] 驗收:admin 帳號 7 個端點正常;非 admin 帳號 REST 回 403、SSE 連線回 403

順序風險:程式先上而 migration 未套 → 所有人(含 admin)即時端點 403;migration 先套而程式未上 → 舊程式的權限頁 PUT 因未知 key 回 422。兩步應緊接著做。

7. 測試來源

  • 單元測試:investment-agent/backend/tests/test_realtime_quote_tier_gate.py
  • 真實 PostgreSQL:investment-agent/backend/tests/integration/test_realtime_quote_api_integration.py(非 admin 對 7 端點 403、admin 對 4 個 REST 200、串流中取消 admin 送 unauthorized、自建 tier 開關控制非 admin 存取)、tests/integration/test_portfolio_realtime_quote_pg.py(持股串流:無權限先於 tickers 驗證回 403、開啟的 tier 可串流、串流中 tier 被關閉送 unauthorized)、tests/integration/test_admin_memberships_realtime_quote_pg.py(admin 以權限 API 開啟 tier 後非 admin 立即可用;PUT 缺 realtime_quote 回 422 且不寫入)、tests/integration/test_migration_168_realtime_quote_feature_pg.py