Appearance
BBSport-New API 架構與協議知識庫
最後更新:2026-04-09
說明
本文件描述 API 層的架構、協議、BaseResponse 結構、Adapter/Decision 機制等技術細節。 端點快速查詢請參考 api-index.md。
API 層架構概覽
BBSport 的網路請求透過 STApiManager 框架管理,採用 Decision(決策鏈)模式處理回應。整個 API 層分為以下命名空間,對應不同的後端服務:
| 命名空間 | 說明 | 主要用途 |
|---|---|---|
STAPI | 平台自家 API | 用戶、充值、提款、聊天、任務、GameJump(平台登入) |
FBSportAPI | 極速體育(FB) | FB 賽事列表、投注計算、提前結算 |
UPSportAPI | UP/BB 體育(雷速) | UP/BB 賽事列表、投注計算、提前結算 |
DBSportAPI | DB體育(OB體育) | 賽事資料、賠率、冠軍賽 |
LeisuSportAPI | 雷速體育 | 賽事詳情統計資料 |
彩票 | 彩票模組(Moya) | 彩票大廳、投注、開獎、追號 |
HttpService | Legacy 請求(OC 橋接) | 提款提交等歷史接口 |
Request / Response 基礎結構
STAPI BaseResponse
swift
struct BaseResponse<T: Codable>: STAutoCodable, STBaseResponseProtocol {
var code: Int // 回應狀態碼(1 = 成功)
var message: String // 回應訊息
var data: T? // 實際資料
}FBSportAPI BaseResponse
swift
struct BaseResponse<T: Codable>: STAutoCodable {
var success: Bool?
var code: Int
var data: T?
}DBSportAPI BaseResponse(DB體育)
swift
struct BaseResponse<T: Codable>: STAutoCodable {
var code: String // "0000000" 或 "1" 代表成功
var ts: Double // timestamp
var data: T?
}LeisuSportAPI BaseResponse(雷速體育)
swift
struct BaseResponse<T: Codable>: STAutoCodable {
var errno: Int? // 錯誤碼(部分 API 使用)
var code: Int? // 狀態碼(部分 API 使用)
var errmsg: String?
var message: String?
var data: T
}API 請求通用 Header(必讀)
所有 API 請求都必須帶以下 Header,缺少任何一個會被 server 拒絕(403 訪問受限)。
通用 Header(所有請求必帶)
| Header | 值 | 說明 |
|---|---|---|
device-id | iOSTemp-<UUID> 或設備真實 ID | 設備唯一標識,由 STGlobalTools.getDeviceId() 產生 |
os-type | 3 | 固定值(0=PC, 1=H5, 2=Android, 3=iOS) |
timestamp | 毫秒時間戳 | int(time.time() * 1000),如 1712693000000 |
version | App 版本號 | 如 1.2.3,來自 CFBundleShortVersionString |
sign | MD5 簽名 | 見下方簽名規則 |
app-type | 0 或 1 | 0=現金網(BBGame), 1=體育(FBSport/DBSport/UPSport) |
pid | bb | 固定值,平台標識 |
User-Agent | {bundleId}/{version} (iPhone; iOS {ver}; Scale/{scale}) | 如 com.seektop.bbsport/1.2.3 (iPhone; iOS 15.5; Scale/3.0) |
認證 Header(登入後才需要)
| Header | 值 | 說明 |
|---|---|---|
token | JWT Token 字串 | 登入成功後從 response 取得,存於 UserDefaults["KEY_TOKEN"] |
uid | 使用者 ID | 如 186342,存於 UserDefaults["KEY_UID"] |
登入前的請求不帶
token和uid,其他通用 Header 仍然需要。
簽名(sign)計算規則
步驟 1: 組成簽名字典
{
"device-id": "iOSTemp-test-abcd1234",
"os-type": "3",
"timestamp": "1712693000000",
"version": "1.2.3"
}
步驟 2: key 按字母排序,拼成 URL query 格式
"device-id=iOSTemp-test-abcd1234&os-type=3×tamp=1712693000000&version=1.2.3"
步驟 3: 末尾追加 "global"
"device-id=iOSTemp-test-abcd1234&os-type=3×tamp=1712693000000&version=1.2.3global"
步驟 4: MD5 雜湊
sign = MD5("device-id=...global")FBSport 特殊認證
FBSport API 使用 Authorization header(非 token),值來自 GameStatusManager.shared.token(by: .fbSport)。
cURL 測試範例
bash
#!/bin/bash
# === 通用變數 ===
DEVICE_ID="iOSTemp-test-abcd1234"
OS_TYPE="3"
VERSION="1.2.3"
TIMESTAMP=$(python3 -c "import time; print(int(time.time()*1000))")
APP_TYPE="1"
PID="bb"
BASE_URL="http://m.bbtstxqm7.com" # 測試環境
# === 計算簽名 ===
SIGN_STR="device-id=${DEVICE_ID}&os-type=${OS_TYPE}×tamp=${TIMESTAMP}&version=${VERSION}global"
SIGN=$(echo -n "${SIGN_STR}" | md5) # macOS 用 md5,Linux 用 md5sum
# === 通用 Header ===
HEADERS=(
-H "device-id: ${DEVICE_ID}"
-H "os-type: ${OS_TYPE}"
-H "timestamp: ${TIMESTAMP}"
-H "version: ${VERSION}"
-H "sign: ${SIGN}"
-H "app-type: ${APP_TYPE}"
-H "pid: ${PID}"
-H "User-Agent: com.seektop.bbsport/${VERSION} (iPhone; iOS 15.5; Scale/3.0)"
)
# === 登入(不需要 token)===
curl -X POST "${BASE_URL}/api/forehead/user/login/username/v2" \
-H "Content-Type: application/x-www-form-urlencoded" \
"${HEADERS[@]}" \
-d "username=帳號&password=密碼"
# === 登入後請求(帶 token)===
TOKEN="登入回應中的 token"
UID="登入回應中的 id"
curl -X POST "${BASE_URL}/api/forehead/user/login/heartbeat" \
-H "Content-Type: application/x-www-form-urlencoded" \
"${HEADERS[@]}" \
-H "token: ${TOKEN}" \
-H "uid: ${UID}"環境域名
| 環境 | 域名 | 說明 |
|---|---|---|
| 測試 (Test) | http://m.bbtstxqm7.com | 測試環境,驗證碼會直接回傳 |
| Dev | http://m.bbgamedev.com | 開發環境(需內網/VPN) |
| UAT | https://m.bbuat2021.com | UAT 環境 |
| 正式 | https://m.bbtyv16.com | 生產環境 |
| 客服 Test | http://api.bbkefutest.com | 客服 SDK 測試環境 |
| 客服 UAT | https://api.uatbbkf.com | 客服 SDK UAT 環境 |
| 客服正式 | https://api.bbkefu88.com | 客服 SDK 生產環境 |
Request Adapters(請求攔截器)
| Adapter | 說明 | 設置 Header |
|---|---|---|
STRequestSignAdapter | 注入簽名相關欄位 | device-id, os-type, timestamp, version, sign, pid |
STRequestTokenAdapter | 注入 Token 與 UID | token, uid(從 UserDefaults 讀取) |
STRequestDeviceInfoAdapter | 注入設備資訊 | app-type |
STRequestCachePolicyAdapter | 設定快取策略 | - |
FBSportRequestAuthorizationAdapter | FB體育授權 | Authorization |
DBSportLangueAdapter | DB體育語言 | - |
DBSportRequestIDAdapter | DB體育請求 ID | - |
Adapter 執行順序:SignAdapter → TokenAdapter → DeviceInfoAdapter → CachePolicyAdapter
認證機制
- Token 存放於
UserDefaults.standard["KEY_TOKEN"],在請求 header 加入token - UID 存放於
UserDefaults.standard["KEY_UID"],在請求 header 加入uid - app-type 由
STGlobalTools.getAppType()取得,加入 headerapp-type - Token 無獨立 refresh 機制,靠心跳(每 60 秒)保活
- 各體育平台 Token 透過
STAPI.GameJumpRequest取得,失效後最多重試 5 次
回應處理 Decision 鏈
Request
└→ Adapters(附加 Header)
└→ 發送 HTTP 請求
└→ Decision 鏈
├→ STBadResponseStatusCodeDecision(HTTP 狀態碼驗證)
├→ {平台}ParseJSONStringDecision(JSON 字串解析)
├→ {平台}ParseResultDecision(業務碼判斷)
└→ STPostLogDecision(日誌記錄)各平台成功碼:
- STAPI:
code == 1 - FBSportAPI:
success == true或code為成功碼 - DBSportAPI:
code == "0000000"或code == "1"
FBSportAPI 投注結果狀態碼(FBBetResultType)
| 值 | 名稱 | 說明 |
|---|---|---|
| 0 | created | 待確認 |
| 1 | confirming | 確認中 |
| 2 | refused | 已拒絕 |
| 3 | canceled | 已取消 |
| 4 | confirmed | 已確認 |
| 5 | settled | 已結算 |
FB 與 UP 端點關係
FB 極速體育與 UP/BB 體育的 API 路徑完全相同(如 v1/match/getList、v1/order/bet/singlePass 等),差異僅在:
- Request struct 分屬
FBSportAPI/UPSportAPI - 各自走不同的 domain 和 auth adapter
- Response Model 共用
STEventModel
DB 體育使用完全不同的 API 路徑體系(yewu11/yewu12/yewu13 前綴),且不支援預約投注和預約提前結算。
彩票模組網路層
彩票模組使用獨立的 Moya 網路層(MLLotteryPlatformRequestInfo),baseURL 由 MLLTJumpManager.shared.baseUrl 動態設定。所有端點路徑以 wps/api/ 開頭。詳見 api-index.md 彩票段落。
相關檔案
APILayer(API 基礎架構)
| 類型 | 檔案路徑 |
|---|---|
| API Manager 擴展 | BBSport/API/APILayer/STApiManager/STApiManager+Extension.swift |
| Protocol(BaseResponse) | BBSport/API/APILayer/STApiManager/Protocol/STBaseResponseProtocol.swift |
| Protocol(解析跳脫) | BBSport/API/APILayer/STApiManager/Protocol/STParseResponseEscapable.swift |
| Adapter(Token) | BBSport/API/APILayer/STApiManager/Adapters/STRequestTokenAdapter.swift |
| Adapter(設備資訊) | BBSport/API/APILayer/STApiManager/Adapters/STRequestDeviceInfoAdapter.swift |
| Adapter(簽名) | BBSport/API/APILayer/STApiManager/Adapters/STRequestSignAdapter.swift |
| Adapter(快取策略) | BBSport/API/APILayer/STApiManager/Adapters/STRequestCachePolicyAdapter.swift |
| Decision(HTTP 狀態碼) | BBSport/API/APILayer/STApiManager/Decision/STBadResponseStatusCodeDecision.swift |
| Decision(日誌) | BBSport/API/APILayer/STApiManager/Decision/STPostLogDecision.swift |
| Model(空回應) | BBSport/API/APILayer/Model/STEmptyResponse.swift |
STAPI Request 基礎架構
| 類型 | 檔案路徑 |
|---|---|
| STAPI 命名空間定義 | BBSport/API/STAPI/STAPI.swift |
| STAPI Host 設定 | BBSport/API/STAPI/STAPI+Host.swift |
| STAPI Request 基礎 | BBSport/API/STAPI/STAPIRequest/STAPIRequest.swift |
| Decision(錯誤處理) | BBSport/API/STAPI/STAPIRequest/Decision/STAPIErrorDecision.swift |
| Decision(解析訊息) | BBSport/API/STAPI/STAPIRequest/Decision/STAPIParseMessageDecision.swift |
| Decision(解析結果) | BBSport/API/STAPI/STAPIRequest/Decision/STAPIParseResultDecision.swift |
| Decision(解析結果-無 Code) | BBSport/API/STAPI/STAPIRequest/Decision/STAPIParseResultWithoutCodeDecision.swift |
| Decision(JSON 字串解析) | BBSport/API/STAPI/STAPIRequest/Decision/STParseJSONStringDecision.swift |
| Decision(成功 Toast) | BBSport/API/STAPI/STAPIRequest/Decision/STSuccessToastDecision.swift |
| API(系統設定圖片) | BBSport/API/STAPI/STAPI+SystemConfigImagesRequest.swift |
| API(系統檔案 URL) | BBSport/API/STAPI/STAPI+SystemFileURLRequest.swift |
LeisuSportAPI(雷速體育數據 API)
| 類型 | 檔案路徑 |
|---|---|
| 命名空間定義 | BBSport/API/LeisuSportAPI/LeisuSportAPI.swift |
| Request 基礎 | BBSport/API/LeisuSportAPI/LeisuSportAPIRequest/LeisuSportAPIRequest.swift |
| Decision(JSON 解析) | BBSport/API/LeisuSportAPI/LeisuSportAPIRequest/Decision/LeisuSportParseJSONStringDecision.swift |
| Decision(結果解析) | BBSport/API/LeisuSportAPI/LeisuSportAPIRequest/Decision/LeisuSportParseResultDecision.swift |
| API(賽事分析) | BBSport/API/LeisuSportAPI/LeisuSportAPI+AnalyzeRequest.swift |
| API(足球情報) | BBSport/API/LeisuSportAPI/Football/LeisuSportAPI+FootballIntelligenceRequest.swift |
| API(足球陣容) | BBSport/API/LeisuSportAPI/Football/LeisuSportAPI+FootballLineupRequest.swift |
| API(足球賽事資訊) | BBSport/API/LeisuSportAPI/Football/LeisuSportAPI+FootballMatchInfoRequest.swift |
| API(足球文字直播) | BBSport/API/LeisuSportAPI/Football/LeisuSportAPI+FootballTextLiveRequest.swift |
| API(籃球情報) | BBSport/API/LeisuSportAPI/Basketball/LeisuSportAPI+BasketballIntelligenceRequest.swift |
| API(籃球陣容) | BBSport/API/LeisuSportAPI/Basketball/LeisuSportAPI+BasketballLineupRequest.swift |
| API(籃球技術統計) | BBSport/API/LeisuSportAPI/Basketball/LeisuSportAPI+BasketballTeahnicRequest.swift |
| API(籃球文字直播) | BBSport/API/LeisuSportAPI/Basketball/LeisuSportAPI+BasketballTextLiveRequest.swift |