Skip to content

C9 Platform — API 完整文件

本文件涵蓋 C9 娛樂城後端 (c9-be) 所有 API 端點的完整說明,供前端工程師、產品經理、UI 設計師參考。

Base URL: http://localhost:8080/apiSwagger UI: http://localhost:8080/api/docs最後更新: 2026-03-01


目錄

  1. 系統概述
  2. 統一規範
  3. Auth 認證模組
  4. Common 公用模組
  5. App Health Check
  6. Game 遊戲模組
  7. VIP 會員模組
  8. Affiliate 代理推廣模組
  9. Deposit 存款模組
  10. Withdrawal 提領模組
  11. Wallet 錢包模組
  12. Vendor 金流商模組
  13. Promo 活動模組
  14. Inbox 站內信模組
  15. SiteConfig 站點配置模組
  16. BetRecord 投注紀錄模組
  17. Ranking 排行榜模組
  18. Mission 任務模組
  19. LiveSports 即時賽事模組
  20. Admin 後台管理模組
  21. S2S 伺服器回調

系統概述

技術棧

  • 框架: NestJS v11 (TypeScript 5.7 strict)
  • 資料庫: MySQL (utf8mb4, 時區 +08:00)
  • ORM: TypeORM 0.3.28
  • 快取: Redis (@keyv/redis)
  • 認證: JWT + Passport(前台)/ AdminJWT(後台)
  • API 文件: Swagger UI(/api/docs

多站點 (Multi-Tenant) 架構

C9 採用白牌架構,單一後端服務支援多個站點(品牌)。每個站點透過 siteCode 區分(例如 C9A1)。

  • 前台請求: 透過 site-name HTTP header 識別站點
  • 後台請求: 透過 x-site-code HTTP header 或 query parameter 識別站點
  • 資料隔離: 大部分資料表都有 siteCode 欄位,確保各站點資料獨立

統一規範

回應格式

所有 API 回應統一使用以下 JSON 格式:

成功回應

json
{
  "code": 200,
  "message": "ok",
  "result": { ... },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/login"
}

業務錯誤回應

HTTP 狀態碼仍為 200,但 code 不等於 200:

json
{
  "code": 1001,
  "message": "帳號或密碼錯誤",
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/login"
}

未授權回應

HTTP 狀態碼 401:

json
{
  "code": 401,
  "message": "Unauthorized"
}

認證方式

角色方式Header有效期
前台用戶JWT Bearer TokenAuthorization: Bearer <token>7 天
後台管理員AdminJWTAuthorization: Bearer <admin-token>依設定

通用 Headers

Header必填說明
Authorization需認證的端點Bearer <JWT Token>
locales語系(zh-TW / en-US / zh-CN / th-TH / vi-VN),預設 zh-TW
site-name前台請求站點名稱,用於識別白牌站點
x-site-code後台請求站點代碼,用於多站點篩選

金額精度

用途型別精度說明
金額decimal(18,6)小數 6 位所有金額統一使用 USD
匯率decimal(18,10)小數 10 位台幣/各幣種 -> USD 轉換用
百分比decimal(5,2)小數 2 位返水比例等
倍率decimal(10,2)小數 2 位遊戲倍率

語系與幣別對照

語系代碼語系名稱對應幣別
zh-TW繁體中文TWD (新台幣)
en-USEnglishUSD (美元)
zh-CN简体中文CNY (人民幣)
th-THภาษาไทยTHB (泰銖)
vi-VNTiếng ViệtVND (越南盾)

錯誤碼查表

前端不硬寫錯誤訊息,統一透過 GET /api/common/enums 取得 ERROR_CODES 物件,再依 pathcode 查表顯示對應語系的錯誤文字。

javascript
// 前端錯誤處理範例
const errorCodes = enumStore.ERROR_CODES;
const path = '/api/auth/login';
const code = 2001;
const message = errorCodes[path]?.[code]; // "帳號或密碼錯誤"

Auth 認證模組

路由前綴: /api/authSwagger 標籤: Auth端點數量: 21 個 功能概述: 用戶註冊、登入(帳密/Google/Telegram)、登出、個人資料管理、Email/手機驗證、Google Authenticator 2FA、密碼管理、頭像切換、三方帳號綁定/解綁

認證模式說明

Auth 模組支援三種登入方式,每種方式都會回傳 JWT Token:

  1. 帳號密碼登入 — 傳統帳號 + 密碼登入
  2. Google OAuth 2.0 登入 — 透過 Authorization Code + PKCE 流程
  3. Telegram Login Widget 登入 — 透過 Telegram 官方 Widget 取得資料 + HMAC 驗簽

三方登入(Google / Telegram)的特殊行為:

  • 新用戶: 自動建立帳號(account 格式為 google_{sub}tg_{telegramId})、分配預設頭像、分配金流群組、發送歡迎站內信
  • 已存在用戶: 直接登入,若 Google/Telegram 欄位為空則自動補綁
  • 密碼欄位: 三方登入用戶的 password 為空字串,可透過 POST /auth/set-password 首次設定

Token 機制

  • JWT payload 包含 { sub: userId, account, tokenVersion }
  • tokenVersion 每次登入遞增 +1,用於使舊 Token 失效
  • 登出時 tokenVersion 也會 +1,確保 Token 即時失效
  • Redis 快取 tokenVersion(cache key: cache:auth:tv:{userId}),加速驗證

裝置指紋

  • 登入和註冊 DTO 支援選填的 device 欄位
  • 前端應使用 FingerprintJS 產生唯一裝置指紋
  • 後端優先使用客戶端傳入的 device,若無則 fallback 至 User-Agent
  • 裝置指紋記錄於 auth-user-login-log 表,供後台風控查詢

3.1 POST /api/auth/register — 用戶註冊

功能說明

用戶註冊帳號。註冊成功後,系統自動執行以下操作:

  1. 帳號唯一性檢查: 同一站點 (siteCode) 內帳號不可重複
  2. 推廣碼驗證 (選填): 若提供 refCode,驗證對應代理是否存在
  3. 密碼加密: 使用 bcryptjs 加密(saltRounds = 10)
  4. 站點歸屬: 根據 site-name header 判定用戶歸屬的站點
  5. 語系設定: 根據 locales header 設定用戶預設語系
  6. 預設頭像指派: 自動指派吉祥物列表中的第一個頭像
  7. 金流群組分配: 根據語系對應的幣別,自動分配啟用中的金流群組
  8. 代理綁定 (選填): 若提供推廣碼,執行代理上下線綁定(best-effort,不阻擋註冊流程)
  9. 歡迎站內信: 發送多語系歡迎站內信(受信件設定控制,best-effort)
  10. JWT Token 簽發: 回傳 Token 供前端即時登入

認證需求

無需認證(公開端點)

Request

Headers:

Header必填說明
locales語系代碼,影響用戶預設語系與金流群組分配。預設 zh-TW
site-name站點名稱,決定用戶所屬站點。未傳則使用環境變數 SITE_CODE(預設 C9

Body (JSON):

欄位型別必填驗證規則說明
accountstring@IsString()帳號,同站點內必須唯一
passwordstring@IsString(), @MinLength(6)密碼,至少 6 字元
namestring@IsString()暱稱/顯示名稱
refCodestring@IsString()推廣碼(代理推廣碼),填入後綁定上線代理
devicestring@IsString()裝置指紋,由前端 FingerprintJS 產生

Body 範例:

json
{
  "account": "user001",
  "password": "pass1234",
  "name": "John",
  "refCode": "AGT123",
  "device": "fp_abc123xyz"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsImFjY291bnQiOiJ1c2VyMDAxIiwidG9rZW5WZXJzaW9uIjowLCJpYXQiOjE3MDk5MDg4MDB9...",
    "user": {
      "id": 1,
      "name": "John"
    },
    "vendorGroupId": 1
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/register"
}
欄位型別說明
result.tokenstringJWT Token(7 天有效),前端儲存後作為後續請求的 Bearer Token
result.user.idnumber用戶 ID
result.user.namestring用戶暱稱
result.vendorGroupIdnumber | null自動分配的金流群組 ID,null 表示無匹配的金流群組

錯誤碼

code訊息說明
2001帳號已存在該帳號在同站點內已被註冊
2002推廣碼不存在提供的 refCode 找不到對應的代理帳號

業務邏輯說明

  • 帳號唯一性是基於 (siteCode, account) 的複合唯一索引,不同站點可以有相同帳號
  • 金流群組分配邏輯:語系 -> 幣別 -> 尋找啟用中且幣別匹配的金流群組(取最小 ID)
  • 推廣碼綁定為 best-effort,即使綁定失敗也不影響註冊流程
  • 歡迎站內信受後台「信件設定」中的 welcomeRegistration 開關控制
  • tokenVersion 初始值為 0

前端注意事項

  • 註冊成功後直接取得 Token,無需再呼叫登入 API
  • 應將 token 存入 localStorage 或 cookie,並設定到 HTTP client 的 Authorization header
  • vendorGroupId 回傳 null 時,可提示用戶聯繫客服或先跳過金流設定
  • 密碼長度限制前端應同步驗證(至少 6 字元)

UI 設計提示

  • 註冊表單欄位:帳號、密碼、確認密碼、暱稱、推廣碼(選填,可預填 URL 參數)
  • 推廣碼輸入框可設計為「有推薦人?輸入推廣碼」的可展開區域
  • 密碼欄位建議加入顯示/隱藏切換按鈕
  • 帳號欄位可加入即時查重(debounce 500ms,呼叫另外的接口或等待提交後顯示錯誤)

3.2 POST /api/auth/login — 用戶登入

功能說明

帳號密碼登入。登入成功後,系統在同一個資料庫交易 (Transaction) 內執行以下操作:

  1. 帳號查找: 查詢用戶是否存在
  2. 密碼比對: 使用 bcrypt 比較密碼雜湊
  3. Token 版本遞增: tokenVersion + 1,使所有舊 Token 失效(實現「新登入踢舊裝置」效果)
  4. 舊登入紀錄更新: 將上一筆 LOGIN 紀錄改為 LOGOUT
  5. 新登入紀錄寫入: 記錄 IP、裝置指紋、登入時間
  6. Redis 快取清除: 刪除 cache:auth:tv:{userId},確保新 tokenVersion 生效
  7. JWT Token 簽發: 回傳新 Token

登入失敗(帳號不存在或密碼錯誤)也會記錄到 auth-user-login-log 表,action 為 LOGIN_FAIL,方便後台風控分析。

認證需求

無需認證(公開端點)

Request

Body (JSON):

欄位型別必填驗證規則說明
accountstring@IsString()帳號
passwordstring@IsString(), @MinLength(6)密碼,至少 6 字元
devicestring@IsString()裝置指紋,由前端 FingerprintJS 產生

Body 範例:

json
{
  "account": "user001",
  "password": "pass1234",
  "device": "fp_abc123xyz"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
      "id": 1,
      "account": "user001",
      "name": "John",
      "email": null,
      "mobile": null,
      "telegram": null,
      "google": null,
      "vipLevel": "1",
      "vipProgress": "0",
      "totalEffectiveBet": "0.000000",
      "balance": "0.000000",
      "frozenBalance": "0.000000",
      "locale": "zh-TW",
      "avatar": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-dragon.png",
      "vendorGroupId": 1,
      "agentCode": null,
      "level1AgentId": null,
      "googleAuthEnabled": 0,
      "lastActivityAt": null,
      "siteCode": "C9",
      "createdAt": "2026-02-22T00:00:00.000Z"
    }
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/login"
}
欄位型別說明
result.tokenstringJWT Token(7 天有效)
result.user.idnumber用戶 ID
result.user.accountstring帳號
result.user.namestring暱稱
result.user.emailstring | nullEmail(已驗證才有值)
result.user.mobilestring | null手機號碼 E.164 格式(已驗證才有值)
result.user.telegramstring | nullTelegram ID(已綁定才有值)
result.user.googlestring | nullGoogle sub ID(已綁定才有值)
result.user.vipLevelstringVIP 等級("1" 起始)
result.user.vipProgressstring | nullVIP 升級進度
result.user.totalEffectiveBetstring累計有效投注流水 USD(decimal 回傳為字串)
result.user.balancestringUSD 餘額(decimal(18,6) 回傳為字串,如 "100.000000"
result.user.frozenBalancestring凍結中金額(提領審核中)
result.user.localestring語系偏好
result.user.avatarstring | null頭像 URL
result.user.vendorGroupIdnumber | null金流群組 ID
result.user.agentCodestring | null代理推廣碼(null 表示非代理)
result.user.level1AgentIdnumber | null直屬上線代理 ID
result.user.googleAuthEnablednumberGoogle Authenticator 是否啟用(0=停用, 1=啟用)
result.user.lastActivityAtstring | null最後活動時間
result.user.siteCodestring所屬站點代碼
result.user.createdAtstring帳號建立時間

注意: password 欄位已在 Service 層移除,不會回傳

錯誤碼

code訊息說明
2001帳號或密碼錯誤帳號不存在,或密碼不正確。為安全考量不區分「帳號不存在」和「密碼錯誤」

業務邏輯說明

  • 登入成功後,所有舊裝置的 Token 會因 tokenVersion 遞增而失效(單點登入效果)
  • 登入失敗會記錄到 auth-user-login-log,供後台「登入失敗紀錄」頁面查詢
  • 帳號不存在時的失敗紀錄 userIdnullremark 記錄為 ACCOUNT_NOT_FOUND:{account}
  • 密碼錯誤時的失敗紀錄 userId 為實際用戶 ID,remark 記錄為 WRONG_PASSWORD
  • 整個登入流程(tokenVersion 遞增 + 登入紀錄寫入)在同一個 DB transaction 中執行,確保原子性

前端注意事項

  • 登入成功後將 Token 存入 localStorage 或 cookie
  • 建議在應用啟動時檢查 Token 是否過期(可透過 JWT decode 檢查 exp)
  • 收到 401 回應時清除 Token 並導向登入頁
  • balancefrozenBalance 為字串型別(decimal),顯示時需格式化
  • googleAuthEnabled 為數字 0 或 1,不是布林值

UI 設計提示

  • 登入表單:帳號、密碼、登入按鈕
  • 支援「記住帳號」功能(localStorage 存帳號,非密碼)
  • 錯誤提示統一顯示「帳號或密碼錯誤」,不透露帳號是否存在
  • 下方提供「Google 登入」、「Telegram 登入」按鈕
  • 新用戶引導至註冊頁面的連結

3.3 GET /api/auth/user-detail — 取得用戶資料

功能說明

取得當前登入用戶的完整個人資料。可選擇是否同時載入最近 20 筆登入紀錄。

此端點回傳的用戶資料包含一個額外欄位 hasPassword(布林值),表示用戶是否已設定密碼。三方登入用戶(Google/Telegram)初始未設定密碼,hasPasswordfalse

若用戶未設定頭像,系統會自動回傳第一隻吉祥物的 URL 作為預設頭像。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Headers:

Header必填說明
AuthorizationBearer <JWT Token>

Query Parameters:

參數型別必填說明
relatedstring帶入 "loginLogs" 可一併取得最近 20 筆登入紀錄

Request 範例:

GET /api/auth/user-detail?related=loginLogs
Authorization: Bearer eyJhbGci...

Response

成功回應 (code: 200) — 不含登入紀錄:

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "account": "user001",
    "name": "John",
    "email": "user@example.com",
    "mobile": "+886912345678",
    "telegram": null,
    "google": "google_sub_id_123",
    "vipLevel": "3",
    "vipProgress": "45",
    "totalEffectiveBet": "15000.500000",
    "balance": "1250.000000",
    "frozenBalance": "100.000000",
    "locale": "zh-TW",
    "avatar": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-dragon.png",
    "vendorGroupId": 1,
    "agentCode": "AGT001",
    "level1AgentId": null,
    "googleAuthEnabled": 1,
    "lastActivityAt": "2026-02-28T15:30:00.000Z",
    "siteCode": "C9",
    "createdAt": "2026-01-15T08:00:00.000Z",
    "hasPassword": true
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/user-detail"
}

成功回應 (code: 200) — 含登入紀錄 (?related=loginLogs):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "account": "user001",
    "name": "John",
    "email": "user@example.com",
    "hasPassword": true,
    "loginLogs": [
      {
        "id": 42,
        "userId": 1,
        "device": "fp_abc123xyz",
        "ip": "203.69.123.45",
        "lastUse": "2026-03-01T10:00:00.000Z",
        "action": "LOGIN",
        "remark": null
      },
      {
        "id": 41,
        "userId": 1,
        "device": "Mozilla/5.0 ...",
        "ip": "203.69.123.45",
        "lastUse": "2026-02-28T08:00:00.000Z",
        "action": "LOGOUT",
        "remark": null
      }
    ]
  }
}
欄位型別說明
result.hasPasswordboolean是否已設定密碼。三方登入用戶初始為 false
result.loginLogsArray(僅 ?related=loginLogs 時回傳)最近 20 筆登入紀錄
result.loginLogs[].actionstringLOGIN / LOGOUT / LOGIN_FAIL / DEL / UNCAPTURED
result.loginLogs[].devicestring裝置指紋或 User-Agent
result.loginLogs[].ipstring登入 IP 位址
result.loginLogs[].lastUsestring紀錄時間

前端注意事項

  • 此端點是取得用戶完整資料的主要方式,建議在應用啟動後立即呼叫
  • hasPassword: false 時,應在個人資料頁面提示用戶「設定密碼」(引導至 POST /auth/set-password
  • hasPassword: true 時,顯示「修改密碼」功能(引導至 POST /auth/edit-password
  • 登入紀錄可用於「安全中心」頁面顯示最近的登入裝置與 IP
  • googleAuthEnabled: 1 時,個人資料頁應顯示「2FA 已啟用」狀態

UI 設計提示

  • 個人資料頁面分區建議:
    • 基本資訊(頭像、暱稱、帳號)
    • 聯絡資訊(Email、手機 — 含驗證狀態指示器)
    • 三方帳號(Google、Telegram — 含綁定/解綁按鈕)
    • 安全設定(密碼、Google Authenticator)
    • 登入紀錄(最近的登入裝置/IP/時間)

3.4 GET /api/auth/country-codes — 取得國碼列表

功能說明

取得全球所有國家/地區的電話國碼列表,用於手機驗證時選擇國碼。回傳結果依據當前語系排序(透過 Intl.DisplayNames API 取得國家名稱本地化)。

結果會快取在 Redis 中 24 小時(cache key: cache:auth:country-codes:{lang})。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Headers:

Header必填說明
AuthorizationBearer <JWT Token>

Cookies:

Cookie說明
i18n_redirected語系 cookie,決定國家名稱的顯示語言,預設 zh-TW

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": [
    { "country": "TW", "callingCode": "886", "name": "台灣" },
    { "country": "US", "callingCode": "1", "name": "美國" },
    { "country": "JP", "callingCode": "81", "name": "日本" },
    { "country": "KR", "callingCode": "82", "name": "韓國" },
    { "country": "TH", "callingCode": "66", "name": "泰國" },
    { "country": "VN", "callingCode": "84", "name": "越南" }
  ],
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/country-codes"
}
欄位型別說明
result[].countrystringISO 3166-1 alpha-2 國家代碼(如 TWUS
result[].callingCodestring電話國碼(如 8861
result[].namestring國家名稱(依語系本地化)

前端注意事項

  • 列表較長(200+ 國家),建議使用可搜尋的下拉選單(Select with search)
  • 可快取結果在前端,避免重複請求
  • 預設顯示 886 - 台灣(或依用戶語系選擇對應國家)

UI 設計提示

  • 國碼選擇器:國旗 icon + 國碼 + 國家名稱(如 TW +886 台灣
  • 搜尋框支援用國碼數字或國家名稱搜尋
  • 常用國碼(台灣、中國、美國)可置頂顯示

3.5 POST /api/auth/send-verify-email — 發送驗證信

功能說明

向指定的 Email 地址發送 6 位數驗證碼。用於用戶綁定 Email 前的驗證流程。

驗證碼產生規則:

  • 6 位數字(000000 ~ 999999)
  • 排除全相同數字(如 111111、222222)
  • 排除連續遞增/遞減數字(如 123456、654321)
  • 排除黑名單中的碼(如 000000)

驗證碼儲存在 auth-user.emailVerifyCode 欄位中,無過期時間限制(由前端控制重發間隔)。

郵件服務使用 Resend API(設定來源優先順序:環境變數 > 站點設定 serviceProviders.resend)。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填驗證規則說明
emailstring@IsEmail()要驗證的 Email 地址
subjectstring@IsString()信件主旨,預設 "C9邀請您驗證信箱"

Body 範例:

json
{
  "email": "user@example.com",
  "subject": "C9邀請您驗證信箱"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": "email_id_xxx_from_resend"
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/send-verify-email"
}

錯誤碼

code訊息說明
2001此信箱已被其他帳號使用該 Email 已經被其他帳號綁定

前端注意事項

  • 發送成功後應啟動倒數計時器(建議 60 秒),避免用戶頻繁重發
  • Email 格式驗證建議在前端先做一層
  • 發送按鈕在倒數期間應 disabled 並顯示剩餘秒數

UI 設計提示

  • Email 輸入框 + 「發送驗證碼」按鈕
  • 發送後按鈕變為「重新發送 (59s)」灰色不可點擊狀態
  • 驗證碼輸入框(6 格分離輸入,自動跳轉焦點)

3.6 POST /api/auth/check-verify-email — 驗證 Email

功能說明

驗證用戶輸入的 6 位 Email 驗證碼。驗證成功後,將 Email 綁定到當前用戶。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填說明
codestring6 位驗證碼
emailstring驗證的 Email 地址(必須與發送時一致)

Body 範例:

json
{
  "code": "123456",
  "email": "user@example.com"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": null,
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/check-verify-email"
}

錯誤碼

code訊息說明
2001查無驗證資訊用戶未先呼叫 send-verify-email,或 emailVerifyCode 為空
2002驗證碼錯誤輸入的驗證碼與系統記錄不符

業務邏輯說明

  • 驗證成功後,email 欄位會被寫入 auth-user
  • emailVerifyCode 欄位不會被清除(重複驗證同一碼仍會通過)
  • Email 在同站點內有唯一索引,若 Email 已被佔用,會在 send-verify-email 階段就攔截

前端注意事項

  • 驗證成功後應重新呼叫 GET /auth/user-detail 更新本地用戶資料
  • 可加入重試上限(例如 5 次錯誤後鎖定一段時間)

3.7 POST /api/auth/send-verify-mobile — 發送手機驗證簡訊

功能說明

發送 SMS 驗證碼到指定手機號碼。支援兩種模式:

  1. 正式環境: 透過 Twilio Verify Service 發送簡訊
  2. 開發環境: 直接生成 6 位驗證碼並 console.log 輸出,不實際發送簡訊

手機號碼格式處理:

  • country.label 提取國碼(格式為 "886-台灣" -> "886"
  • 去除手機號碼前導 0
  • 組合為 E.164 格式(如 +886912345678

Twilio 設定來源優先順序:環境變數 > 站點設定 serviceProviders.twilio

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填驗證規則說明
mobilestring@IsString(), @Length(3, 40)手機號碼(不含國碼)
countryobject@ValidateNested()國碼物件
country.labelstring@IsString()格式為 "886-台灣"
country.iconstring@IsString()國旗 icon(如 "cif:tw"

Body 範例:

json
{
  "mobile": "912345678",
  "country": {
    "label": "886-台灣",
    "icon": "cif:tw"
  }
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "sent": true
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/send-verify-mobile"
}

錯誤碼

code訊息說明
2001此手機號碼已被其他帳號使用該 E.164 格式手機號碼已被其他帳號綁定
2002簡訊發送失敗Twilio API 呼叫失敗

業務邏輯說明

  • 開發環境下,驗證碼儲存格式為 "{e164}|{code}"(如 "+886912345678|485723"),會在 console 輸出
  • 正式環境下,mobileVerifyCode 只儲存 E.164 號碼,驗證碼由 Twilio 管理
  • 同一手機號碼可以被同一用戶重複發送(只檢查其他用戶的佔用)

前端注意事項

  • 手機號碼前導 0 會被後端自動去除,前端可保留也可不保留
  • 國碼選擇建議使用 GET /auth/country-codes 回傳的列表
  • 發送成功後顯示倒數計時(建議 60 秒)

3.8 POST /api/auth/check-verify-mobile — 驗證手機 OTP

功能說明

驗證手機 SMS 驗證碼。驗證成功後將手機號碼(E.164 格式)綁定到當前用戶。

開發環境下,直接比對 DB 中儲存的驗證碼。正式環境下,透過 Twilio Verify Service 驗證。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填說明
mobilestring手機號碼
codestring6 位驗證碼

Body 範例:

json
{
  "mobile": "912345678",
  "code": "485723"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": null,
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/check-verify-mobile"
}

錯誤碼

code訊息說明
2001查無驗證資訊用戶未先呼叫 send-verify-mobile,或 mobileVerifyCode 為空
2002驗證碼錯誤或已過期驗證碼不正確或已過期(Twilio 預設 10 分鐘過期)

業務邏輯說明

  • 驗證成功後,mobile 欄位更新為 E.164 格式號碼,mobileVerifyCode 清空
  • 手機號碼在同站點內有唯一索引

前端注意事項

  • 驗證成功後重新呼叫 GET /auth/user-detail 更新資料
  • 可在 UI 上顯示「手機已驗證」狀態標籤

3.9 POST /api/auth/generate-google-auth — 產生 Google Authenticator 設定

功能說明

為當前用戶產生 Google Authenticator (TOTP) 的 secret 和 QR Code。此端點不會直接啟用 2FA,用戶需要在掃描 QR Code 後,使用 POST /auth/enable-google-auth 輸入驗證碼來啟用。

每次呼叫會產生新的 secret 並覆蓋舊的,同時將 googleAuthEnabled 重設為 0。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

無需 Body

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "secret": "JBSWY3DPEHPK3PXP",
    "qrCode": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..."
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/generate-google-auth"
}
欄位型別說明
result.secretstringBase32 編碼的 TOTP secret,用戶可手動輸入到 Authenticator App
result.qrCodestringQR Code 的 base64 PNG 圖片(data:image/png;base64,...

前端注意事項

  • QR Code 可直接作為 <img> 標籤的 src 屬性使用
  • 同時提供 secret 文字,供無法掃碼的用戶手動複製輸入
  • 此步驟只是「準備」,尚未啟用 2FA

UI 設計提示

  • 啟用 2FA 對話框三步驟流程:
    1. 顯示 QR Code + secret 文字(提示用戶使用 Google Authenticator 掃描)
    2. 輸入 6 位驗證碼確認(呼叫 enable-google-auth
    3. 成功提示

3.10 POST /api/auth/enable-google-auth — 啟用 Google Authenticator

功能說明

輸入 Google Authenticator 產生的 6 位 TOTP 驗證碼,驗證成功後正式啟用 2FA。

TOTP 驗證使用 speakeasy 庫,允許 1 個時間視窗的偏移(window = 1,即前後 30 秒)。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填說明
codestringGoogle Authenticator 6 位 TOTP 驗證碼

Body 範例:

json
{
  "code": "123456"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "affected": 1
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/enable-google-auth"
}

錯誤碼

code訊息說明
2001查無驗證資訊用戶未先呼叫 generate-google-auth,或 googleAuthSecret 為空
20026 位數密碼輸入錯誤TOTP 驗證碼不正確

前端注意事項

  • 啟用成功後更新本地用戶狀態 googleAuthEnabled = 1
  • 建議在成功後提示用戶「請妥善保管備份碼」

3.11 POST /api/auth/edit-password — 修改密碼

功能說明

修改已登入用戶的密碼。需提供原密碼進行身份驗證,以及新密碼和確認密碼。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填說明
passwordstring當前密碼
newPasswordstring新密碼
confirmPasswordstring確認新密碼(必須與 newPassword 相同)

Body 範例:

json
{
  "password": "oldPass123",
  "newPassword": "newPass456",
  "confirmPassword": "newPass456"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "affected": 1
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/edit-password"
}

錯誤碼

code訊息說明
2002密碼驗證失敗當前密碼不正確,或新密碼與確認密碼不一致

注意: 錯誤碼 2002 同時用於「原密碼錯誤」和「新密碼與確認密碼不一致」兩種情況。前端建議先在客戶端檢查兩次密碼是否一致,若一致但仍返回 2002 則為原密碼錯誤。

前端注意事項

  • 前端應先驗證 newPassword === confirmPassword,不一致時本地提示
  • 密碼修改成功後不會使 Token 失效,用戶可繼續使用
  • 建議密碼欄位都加上顯示/隱藏切換按鈕

3.12 POST /api/auth/set-password — 首次設定密碼(三方登入用戶)

功能說明

僅限未設定密碼的三方登入用戶(Google / Telegram 註冊)使用。允許用戶首次設定帳號密碼。

判斷條件:用戶的 password 欄位為空字串 '' 時才能使用此端點。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填驗證規則說明
newPasswordstring@IsString(), @MinLength(6)新密碼,至少 6 字元
confirmPasswordstring@IsString(), @MinLength(6)確認密碼

Body 範例:

json
{
  "newPassword": "pass1234",
  "confirmPassword": "pass1234"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": null,
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/set-password"
}

錯誤碼

code訊息說明
2002密碼不一致newPasswordconfirmPassword 不相同
2003已設定過密碼用戶已經有密碼(非三方登入用戶,或已設定過)

前端注意事項

  • 僅在 user-detail 回傳 hasPassword: false 時顯示此功能入口
  • 設定成功後,用戶可以使用帳號 + 密碼方式登入
  • 建議在個人資料頁面以醒目提示「尚未設定密碼,建議設定以提升帳號安全性」

UI 設計提示

  • 可在個人資料的「安全設定」區域放置黃色警告卡片
  • 點擊後開啟彈窗:新密碼 + 確認密碼 + 確認按鈕

3.13 GET /api/auth/login-config — 取得登入設定

功能說明

取得三方登入的設定資訊,包含:

  1. Google OAuth 2.0: 回傳完整的授權 URL(含 PKCE code_challenge、state 防偽造)
  2. Telegram Login: 回傳 Bot 資訊(botUsername、botId)

Google OAuth 流程使用 PKCE(Proof Key for Code Exchange)安全機制:

  • 產生隨機 codeVerifier (64 bytes)
  • 計算 codeChallenge = SHA256(codeVerifier) 的 base64url
  • codeVerifier 加密儲存在 state 參數中(HMAC-SHA256 簽名)
  • State 有效期為 10 分鐘

認證需求

無需認證(公開端點)

Request

無需參數

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "google": "https://accounts.google.com/o/oauth2/v2/auth?client_id=xxx.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fc9-ec.zeabur.app%2Fredirect%2Fgoogle&response_type=code&scope=openid+email+profile&state=eyJ2IjoxLCJuIjoiLi4uIn0.abc123&code_challenge=xxx&code_challenge_method=S256&access_type=offline&prompt=consent",
    "telegram": {
      "botUsername": "C9GameBot",
      "botId": "1234567890"
    }
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/login-config"
}
欄位型別說明
result.googlestringGoogle OAuth2 完整授權 URL,前端直接 window.location.href = url 即可跳轉
result.telegramobject | nullTelegram Bot 資訊。null 表示未設定
result.telegram.botUsernamestringTelegram Bot 使用者名稱,用於 Login Widget
result.telegram.botIdstringTelegram Bot ID

錯誤碼

code訊息說明
2001尚未設置 GOOGLE_CLIENT_IDGoogle OAuth 未設定,無法產生授權 URL

業務邏輯說明

  • Google OAuth redirect URI 格式:{domain}/redirect/google(後端自動補上路徑)
  • State 參數結構:{v: 1, n: nonce, cv: codeVerifier, ra: redirectAfter, iat: timestamp}
  • State 用 HMAC-SHA256 簽名防偽造,key 為 JWT_SECRET
  • 若 Google 或 Telegram 未設定,對應欄位回傳 null,前端應隱藏對應的登入按鈕

前端注意事項

  • Google 登入流程:
    1. 呼叫此端點取得 Google URL
    2. window.location.href = result.google 跳轉 Google 授權頁
    3. 用戶授權後,Google 重導向至 /redirect/google?code=xxx&state=xxx
    4. 前端從 URL 取得 codestate
    5. 呼叫 POST /auth/login-google 傳入 codestate
  • Telegram 登入:使用 Telegram Login Widget,設定 bot-usernameresult.telegram.botUsername

3.14 POST /api/auth/login-google — Google 登入

功能說明

使用 Google OAuth2 Authorization Code 完成登入或註冊。完整流程:

  1. State 驗簽: 驗證 state 的 HMAC 簽名,防止 CSRF 攻擊
  2. State 解析: 解碼 base64url,取出 codeVerifieriat
  3. 過期檢查: State 發出後超過 10 分鐘則拒絕
  4. Token 交換: 使用 code + codeVerifier 向 Google 交換 access_token 和 id_token
  5. ID Token 驗證: 使用 Google Auth Library 驗證 id_token 的真實性
  6. 用戶處理:
    • 用 Google email 查找現有用戶
    • 若存在:遞增 tokenVersion 登入,若未綁定 Google sub 則自動補綁
    • 若不存在:建立新帳號(account: google_{sub})、分配頭像/金流群組、處理推廣碼綁定

認證需求

無需認證(公開端點)

Request

Headers:

Header必填說明
locales語系(新用戶註冊時使用),預設 zh-TW
site-name站點名稱(新用戶註冊時使用)

Body (JSON):

欄位型別必填說明
codestringGoogle OAuth2 authorization code(由 Google 回調帶回)
statestringOAuth2 state(由 login-config 產生,含 PKCE code_verifier)
refCodestring推廣碼(僅新用戶註冊時使用)

Body 範例:

json
{
  "code": "4/0AQSTgQHhB7...",
  "state": "eyJ2IjoxLCJuIjoiLi4uIn0.abc123def456",
  "refCode": "AGT123"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
      "id": 1,
      "account": "google_123456789",
      "name": "John Doe",
      "email": "john@gmail.com",
      "google": "123456789",
      "vipLevel": "1",
      "balance": "0.000000"
    },
    "google": {
      "sub": "123456789",
      "email": "john@gmail.com",
      "name": "John Doe",
      "picture": "https://lh3.googleusercontent.com/a/...",
      "email_verified": true,
      "iss": "https://accounts.google.com",
      "aud": "xxx.apps.googleusercontent.com"
    },
    "isNewUser": true
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/login-google"
}
欄位型別說明
result.tokenstringJWT Token
result.userobject用戶完整資料
result.googleobjectGoogle Token 資訊 + ID Token Payload
result.isNewUserboolean是否為新註冊用戶(前端可據此顯示歡迎引導)

錯誤碼

code訊息說明
2001尚未設置 GOOGLE_CLIENT_IDGoogle OAuth 未設定
2002GOOGLE_CLIENT_ID 驗證失敗State 參數格式不正確
2003Google Payload 加密失敗State 簽名驗證不通過(可能被竄改)
2004Google Payload 解析失敗State 的 base64url 解碼失敗
2005本次操作時效已過期, 請重新登入State 超過 10 分鐘有效期
2006Google Code 驗證錯誤State 中缺少 codeVerifier
2007Google Api 響應過程錯誤Google Token 交換 API 回傳錯誤
2008Google Ticket 解析失敗id_token 驗證失敗(無效的 token)
2009Google Ticket Payload 解析失敗id_token payload 中缺少 sub 欄位

前端注意事項

  • isNewUser: true 時,可顯示歡迎引導頁面或提示設定密碼
  • Google 登入新用戶的 password 為空字串,hasPasswordfalse
  • 登入成功後的 Token 處理方式與一般登入相同

3.15 POST /api/auth/login-telegram — Telegram 登入

功能說明

使用 Telegram Login Widget 回傳的資料完成登入或註冊。驗證流程:

  1. Bot Token 檢查: 確認已設定 Telegram Bot Token
  2. HMAC 驗簽: 使用 SHA256(botToken) 作為 key,對 data-check-string 做 HMAC-SHA256,比對 hash 是否一致
  3. 時效驗證: auth_date 必須在 5 分鐘內
  4. 用戶處理:
    • 用 Telegram ID 查找現有用戶
    • 若存在:遞增 tokenVersion 登入
    • 若不存在:建立新帳號(account: tg_{telegramId}

認證需求

無需認證(公開端點)

Request

Body (JSON):

欄位型別必填說明
idnumberTelegram user ID
first_namestringTelegram first name
last_namestringTelegram last name
usernamestringTelegram username
photo_urlstringTelegram 頭像 URL
auth_datenumberUnix timestamp(認證時間)
hashstringHMAC-SHA256 驗證雜湊
refCodestring推廣碼(僅新用戶註冊時使用)

Body 範例:

json
{
  "id": 123456789,
  "first_name": "John",
  "last_name": "Doe",
  "username": "johndoe",
  "photo_url": "https://t.me/i/userpic/320/...",
  "auth_date": 1708862400,
  "hash": "a1b2c3d4e5f6...",
  "refCode": "AGT123"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
      "id": 1,
      "account": "tg_123456789",
      "name": "John Doe",
      "telegram": "123456789"
    },
    "isNewUser": true
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/login-telegram"
}

錯誤碼

code訊息說明
2001尚未設置 TELEGRAM_BOT_TOKENTelegram Bot 未設定
2002Telegram 驗證失敗HMAC hash 驗證不通過
2003本次操作時效已過期, 請重新登入auth_date 超過 5 分鐘

前端注意事項

  • Telegram Login Widget 會自動帶回以上所有欄位,前端直接傳送即可
  • hash 由 Telegram 產生,前端不需自行計算
  • 5 分鐘過期限制代表用戶在 Telegram 授權後,必須在 5 分鐘內完成後端驗證

3.16 POST /api/auth/logout — 登出

功能說明

登出當前用戶。執行以下操作:

  1. 語系更新: 根據當前 locales header 更新用戶語系偏好
  2. 金流群組重新分配: 根據新語系對應的幣別,重新匹配啟用中的金流群組
  3. Token 失效: tokenVersion + 1,使所有現有 Token 失效
  4. Redis 快取清除: 刪除 cache:auth:tv:{userId}

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Headers:

Header必填說明
AuthorizationBearer <JWT Token>
locales語系代碼(決定登出後用戶的語系偏好與金流群組)

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "locale": "zh-TW",
    "vendorGroupId": 1
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/logout"
}
欄位型別說明
result.localestring更新後的語系偏好
result.vendorGroupIdnumber | null重新分配的金流群組 ID

業務邏輯說明

  • 登出後 tokenVersion 遞增,即使 Token 尚未過期也無法再使用
  • 語系更新和金流群組重新分配是為了確保下次登入時的正確性

前端注意事項

  • 登出成功後清除本地儲存的 Token 和用戶資料
  • 重導向至登入頁面
  • 即使 API 呼叫失敗(如網路問題),前端也應清除本地狀態並導向登入頁

3.17 PATCH /api/auth/locale — 更新語系偏好

功能說明

更新當前用戶的語系偏好。僅接受系統支援的 5 種語系。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填說明
localestring語系代碼:zh-TW / en-US / zh-CN / th-TH / vi-VN

Body 範例:

json
{
  "locale": "en-US"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "locale": "en-US"
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/locale"
}

錯誤碼

code訊息說明
2001不支援的語系傳入的 locale 不在支援列表中

前端注意事項

  • 前端切換語系時同步呼叫此 API 更新後端紀錄
  • 僅更新偏好,不影響 Token 或金流群組

3.18 GET /api/auth/mascots — 取得吉祥物頭像列表

功能說明

取得所有可選的吉祥物頭像列表。目前共 10 隻吉祥物,頭像圖片儲存在 Cloudflare R2 上。

吉祥物列表由 SiteConfigService.getMascots() 提供,可透過後台管理設定。

認證需求

無需認證(公開端點)

Request

無需參數

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": [
    { "id": "c9-dragon", "label": "翡翠龍", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-dragon.png" },
    { "id": "c9-phoenix", "label": "鳳凰", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-phoenix.png" },
    { "id": "c9-angel", "label": "天使", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-angel.png" },
    { "id": "c9-wizard", "label": "法師", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-wizard.png" },
    { "id": "c9-knight", "label": "騎士", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-knight.png" },
    { "id": "c9-mermaid", "label": "人魚", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-mermaid.png" },
    { "id": "c9-tiger", "label": "猛虎", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-tiger.png" },
    { "id": "c9-fox", "label": "靈狐", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-fox.png" },
    { "id": "c9-owl", "label": "貓頭鷹", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-owl.png" },
    { "id": "c9-wolf", "label": "銀狼", "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-wolf.png" }
  ],
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/mascots"
}
欄位型別說明
result[].idstring吉祥物 ID(用於 PATCH /auth/avatar
result[].labelstring吉祥物名稱
result[].urlstring頭像圖片 URL(R2 CDN)

前端注意事項

  • 可在註冊完成後或個人資料頁提供頭像選擇功能
  • 圖片 URL 指向 R2 CDN,可直接用於 <img> 標籤

UI 設計提示

  • 頭像選擇器:3x4 或 5x2 的網格排列
  • 當前選中的頭像加上邊框高亮
  • 點擊後呼叫 PATCH /auth/avatar 即時更換

3.19 PATCH /api/auth/avatar — 切換用戶頭像

功能說明

切換當前用戶的頭像為指定的吉祥物。系統會驗證 mascotId 是否存在於吉祥物列表中。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填說明
mascotIdstring吉祥物 ID(從 GET /auth/mascots 取得)

可用的 mascotId 列表:

mascotId名稱
c9-dragon翡翠龍
c9-phoenix鳳凰
c9-angel天使
c9-wizard法師
c9-knight騎士
c9-mermaid人魚
c9-tiger猛虎
c9-fox靈狐
c9-owl貓頭鷹
c9-wolf銀狼

Body 範例:

json
{
  "mascotId": "c9-dragon"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "avatar": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-dragon.png"
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/avatar"
}

錯誤碼

code訊息說明
2001無效的頭像 ID傳入的 mascotId 不存在於吉祥物列表中

前端注意事項

  • 更換成功後,前端即時更新顯示的頭像(使用回傳的 avatar URL)
  • 不需要重新呼叫 user-detail API

3.20 POST /api/auth/bind-google — 綁定 Google 帳號

功能說明

將 Google 帳號綁定到已登入用戶。流程與 login-google 相同的 OAuth 驗證流程,但不建立新用戶,而是更新現有用戶的 google 欄位。

額外功能:若用戶尚未設定 Email,且 Google 帳號有 Email,會一併寫入。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON):

欄位型別必填說明
codestringGoogle OAuth2 authorization code
statestringOAuth2 state(含 PKCE code_verifier)

Body 範例:

json
{
  "code": "4/0AQSTgQ...",
  "state": "eyJ2IjoxLCJuIjoiLi4uIn0.abc123"
}

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "google": "google_sub_id_123",
    "email": "john@gmail.com",
    "name": "John Doe"
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/bind-google"
}

錯誤碼

code訊息說明
2001 ~ 2009(同 login-google)Google OAuth 驗證過程中的各種錯誤
2010此 Google 帳號已被其他用戶綁定該 Google sub 已經綁定在其他帳號上
2011此 Email 已被其他用戶使用Google 帳號的 Email 已被其他帳號佔用

前端注意事項

  • 綁定流程和登入流程共用同一個 Google OAuth 授權 URL(由 login-config 取得)
  • 差別在於前端收到 Google callback 後,呼叫 bind-google 而非 login-google
  • 綁定成功後刷新用戶資料,顯示「Google 已綁定」狀態

3.21 POST /api/auth/bind-telegram — 綁定 Telegram 帳號

功能說明

將 Telegram 帳號綁定到已登入用戶。驗證邏輯與 login-telegram 相同(HMAC 驗簽 + 5 分鐘過期檢查)。

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

Body (JSON): (與 login-telegram 相同)

欄位型別必填說明
idnumberTelegram user ID
first_namestringTelegram first name
last_namestringTelegram last name
usernamestringTelegram username
photo_urlstringTelegram 頭像 URL
auth_datenumberUnix timestamp
hashstringHMAC-SHA256 驗證雜湊

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "telegram": "123456789"
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/bind-telegram"
}

錯誤碼

code訊息說明
2001尚未設置 TELEGRAM_BOT_TOKENTelegram Bot 未設定
2002Telegram 驗證失敗HMAC hash 驗證不通過
2003本次操作時效已過期, 請重新登入auth_date 超過 5 分鐘
2010此 Telegram 帳號已被其他用戶綁定該 Telegram ID 已綁定在其他帳號上

3.22 POST /api/auth/unbind-google — 解除綁定 Google

功能說明

解除當前用戶的 Google 帳號綁定。將 google 欄位設為 null

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

無需 Body

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "affected": 1
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/unbind-google"
}

前端注意事項

  • 解綁前建議彈出確認對話框
  • 若用戶僅透過 Google 登入(hasPassword: false),解綁後將無法登入。建議先引導設定密碼

3.23 POST /api/auth/unbind-telegram — 解除綁定 Telegram

功能說明

解除當前用戶的 Telegram 帳號綁定。將 telegram 欄位設為 null

認證需求

需要 JWT Token (@UseGuards(JwtAuthGuard))

Request

無需 Body

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "affected": 1
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/auth/unbind-telegram"
}

前端注意事項

  • 解綁前建議彈出確認對話框
  • 同理,若用戶僅透過 Telegram 登入且未設定密碼,解綁後將無法登入

Auth 模組端點摘要表

#方法路徑認證說明
1POST/api/auth/register--用戶註冊
2POST/api/auth/login--帳號密碼登入
3GET/api/auth/user-detailJWT取得用戶資料(可含登入紀錄)
4GET/api/auth/country-codesJWT取得國碼列表
5POST/api/auth/send-verify-emailJWT發送 Email 驗證碼
6POST/api/auth/check-verify-emailJWT驗證 Email 驗證碼
7POST/api/auth/send-verify-mobileJWT發送手機 SMS 驗證碼
8POST/api/auth/check-verify-mobileJWT驗證手機 OTP
9POST/api/auth/generate-google-authJWT產生 Google Authenticator QR Code
10POST/api/auth/enable-google-authJWT啟用 Google Authenticator 2FA
11POST/api/auth/edit-passwordJWT修改密碼
12POST/api/auth/set-passwordJWT首次設定密碼(三方登入用戶)
13GET/api/auth/login-config--取得三方登入設定(Google URL + Telegram Bot)
14POST/api/auth/login-google--Google OAuth 登入/註冊
15POST/api/auth/login-telegram--Telegram 登入/註冊
16POST/api/auth/logoutJWT登出(Token 失效 + 語系更新)
17PATCH/api/auth/localeJWT更新語系偏好
18GET/api/auth/mascots--取得吉祥物頭像列表
19PATCH/api/auth/avatarJWT切換用戶頭像
20POST/api/auth/bind-googleJWT綁定 Google 帳號
21POST/api/auth/bind-telegramJWT綁定 Telegram 帳號
22POST/api/auth/unbind-googleJWT解除綁定 Google
23POST/api/auth/unbind-telegramJWT解除綁定 Telegram

Common 公用模組

路由前綴: /api/commonSwagger 標籤: Common端點數量: 1 個 功能概述: 提供全系統共用的列舉值和錯誤碼對照表,是前端錯誤處理機制的核心依賴

模組初始化機制

CommonService 實作了 OnModuleInit 介面,在模組啟動時執行以下初始化流程:

  1. 掃描 i18n 目錄: 遞迴讀取 i18n/zh-TW/ 下所有 JSON 檔案
  2. 收集數字 key: 找出所有數字格式的 key(這些就是錯誤碼)
  3. 建構 CodeMap: 產生 { "authError.register": [2001, 2002], "authError.login": [2001], ... } 的結構
  4. API 路徑映射: 透過 I18N_PATH_MAP 將 i18n prefix 轉換為 API path

這個機制讓新增錯誤碼只需要在 i18n JSON 中加入即可,不需要手動維護錯誤碼清單。


4.1 GET /api/common/enums — 取得共用列舉與錯誤碼

功能說明

回傳系統所有共用列舉值和依語系翻譯的錯誤碼對照表。這是前端錯誤處理機制的核心 API。

錯誤碼查表機制:

前端收到業務錯誤回應時(code !== 200),不顯示後端回傳的 message(可能因快取或其他原因不一致),而是透過 ERROR_CODES[path][code] 查表取得當前語系的錯誤文字。

結果會快取在 Redis 中 1 小時(cache key: cache:common:enums:{lang})。

認證需求

無需認證(公開端點)

Request

Headers:

Header必填說明
locales語系代碼,決定 ERROR_CODES 的語言。預設 zh-TW

Request 範例:

GET /api/common/enums
locales: zh-TW

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "AUTH_ENUM": {
      "RELATED": {
        "LOGIN_LOG": "LOGIN_LOG"
      },
      "LOGIN_LOG": {
        "ACTION": {
          "LOGIN": "登入",
          "LOGOUT": "登出",
          "LOGIN_FAIL": "登入失敗",
          "DEL": "移除",
          "UNCAPTURED": "未補獲"
        }
      }
    },
    "ERROR_CODES": {
      "/api/auth/register": {
        "2001": "帳號已存在",
        "2002": "推廣碼不存在"
      },
      "/api/auth/login": {
        "2001": "帳號或密碼錯誤"
      },
      "/api/auth/send-verify-email": {
        "2001": "此信箱已被其他帳號使用"
      },
      "/api/auth/check-verify-email": {
        "2001": "查無驗證資訊",
        "2002": "驗證碼錯誤"
      },
      "/api/auth/enable-google-auth": {
        "2001": "查無驗證資訊",
        "2002": "6 位數密碼輸入錯誤"
      },
      "/api/auth/edit-password": {
        "2002": "密碼驗證失敗"
      },
      "/api/auth/login-config": {
        "2001": "尚未設置 GOOGLE_CLIENT_ID"
      },
      "/api/auth/login-google": {
        "2001": "尚未設置 GOOGLE_CLIENT_ID",
        "2002": "GOOGLE_CLIENT_ID 驗證失敗",
        "2003": "Google Payload 加密失敗",
        "2004": "Google Payload 解析失敗",
        "2005": "本次操作時效已過期, 請重新登入",
        "2006": "Google Code 驗證錯誤",
        "2007": "Google Api 響應過程錯誤",
        "2008": "Google Ticket 解析失敗",
        "2009": "Google Ticket Payload 解析失敗"
      },
      "/api/auth/locale": {
        "2001": "不支援的語系"
      },
      "/api/wallet/bank-card/add": {
        "2001": "銀行卡已存在"
      },
      "/api/deposit": {
        "2003": "不支援的金流通道類型"
      },
      "/api/game/launch": {
        "5010": "您已被限制進入此遊戲"
      }
    }
  },
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api/common/enums"
}

回傳結構詳解

AUTH_ENUM — 認證相關列舉值

路徑說明
AUTH_ENUM.RELATED.LOGIN_LOG"LOGIN_LOG"用於 GET /auth/user-detail?related=loginLogs 的常數
AUTH_ENUM.LOGIN_LOG.ACTION.LOGIN"登入"登入紀錄的 action 顯示名稱
AUTH_ENUM.LOGIN_LOG.ACTION.LOGOUT"登出"登出紀錄
AUTH_ENUM.LOGIN_LOG.ACTION.LOGIN_FAIL"登入失敗"登入失敗紀錄
AUTH_ENUM.LOGIN_LOG.ACTION.DEL"移除"手動移除紀錄
AUTH_ENUM.LOGIN_LOG.ACTION.UNCAPTURED"未補獲"未捕獲的異常紀錄

ERROR_CODES — 錯誤碼對照表

結構為 Record<apiPath, Record<errorCode, translatedMessage>>

  • Key (apiPath): API 端點路徑(含 /api 前綴),如 /api/auth/login
  • Value: 該端點所有可能的錯誤碼和對應的翻譯文字

路徑支援 :id 萬用匹配,例如 /api/wallet/bank-card/:id 可匹配 /api/wallet/bank-card/123

業務邏輯說明

  • 錯誤碼的翻譯由 nestjs-i18n 庫處理,根據 locales header 回傳對應語系
  • 快取 1 小時,語系切換時會使用不同的 cache key,不會影響
  • I18N_PATH_MAP 支援一對多映射:同一組錯誤碼可以映射到多個 API 路徑

I18N_PATH_MAP 完整映射表

i18n PrefixAPI Path
authError.register/api/auth/register
authError.login/api/auth/login
authError.sendVerifyEmail/api/auth/send-verify-email
authError.checkVerifyEmail/api/auth/check-verify-email
authError.enableGoogleAuth/api/auth/enable-google-auth
authError.editPassword/api/auth/edit-password
authError.loginConfig/api/auth/login-config
authError.loginGoogle/api/auth/login-google
authError.updateLocale/api/auth/locale
walletError.bankCard.add/api/wallet/bank-card/add
walletError.bankCard.delete/api/wallet/bank-card/:id
walletError.creditCard.add/api/wallet/credit-card/add
walletError.creditCard.delete/api/wallet/credit-card/:id
walletError.cryptoAddress.add/api/wallet/crypto-address/add
walletError.cryptoAddress.delete/api/wallet/crypto-address/:id
vendorError.channels/api/vendor/channels
vendorError.wantong/api/vendor/wantong/add-atm, /api/vendor/wantong/add-card
vendorError.usdt/api/deposit
depositError/api/deposit
depositError.exchangeRate/api/deposit/exchange-rate
promoError/api/promo/:id, /api/admin/promos/create, /api/admin/promos/:id
promoError.claim/api/promo/:id/claim
vipError.levels/api/vip/levels, /api/vip/levels/:id
vipError.rebates/api/vip/rebates, /api/vip/rebates/:id
vipError.users.hold/api/vip/users/:userId/hold
gameError.launch/api/game/launch
gameError.demo/api/game/demo
gameError.simulate/api/game/simulate
gameError.list/api/game/list
betRecordError.details/api/bet-record/:orderId/details
affiliateError.*(代理相關多個路徑)
inboxError/api/inbox/:id/read, /api/inbox/admin/:id
siteConfigError/api/site-config, /api/site-config/admin/*
withdrawalError.*(提領相關多個路徑)
adminError.*(後台管理相關多個路徑)

前端注意事項

  • 應用啟動時呼叫一次此 API,將 ERROR_CODES 存入全域狀態管理(Zustand enumStore
  • 語系切換時重新呼叫,因為錯誤碼文字會隨語系變化
  • 錯誤處理流程:
    1. API 回傳 code !== 200
    2. enumStore.ERROR_CODES 查找 [apiPath][code]
    3. 找到則顯示該翻譯文字
    4. 找不到則 fallback 使用 API 回傳的 message
  • :id 路徑匹配:前端需將實際路徑(如 /api/wallet/bank-card/123)轉換為模板路徑(如 /api/wallet/bank-card/:id)再查表

前端錯誤處理程式碼範例

typescript
// IMS 前端 (c9-ims) useHttp 三層錯誤碼映射
function resolveErrorMessage(path: string, code: number, override?: string | Record<number, string>) {
  // 第一層:Store 查表
  const errorCodes = enumStore.getState().errorCodes;
  const storeMessage = errorCodes?.[path]?.[code];

  // 第二層:呼叫端覆寫
  if (typeof override === 'string') return override;
  if (typeof override === 'object' && override[code]) return override[code];

  // 第三層:Store 查表結果
  if (storeMessage) return storeMessage;

  // Fallback
  return '操作失敗';
}

App Health Check

路由前綴: /apiSwagger 標籤: App端點數量: 1 個 功能概述: 系統健康檢查端點,用於確認後端服務是否正常運行


5.1 GET /api — 健康檢查

功能說明

最基本的健康檢查端點,用於確認後端服務是否正常啟動和運行。回傳固定字串 "Hello World!"

此端點通常用於:

  • 負載均衡器的健康檢查探針
  • 部署後的存活性驗證
  • 前端啟動時的後端連通性檢查
  • 監控系統的心跳偵測

認證需求

無需認證(公開端點)

Request

無需參數

Request 範例:

GET /api

Response

成功回應 (code: 200):

json
{
  "code": 200,
  "message": "ok",
  "result": "Hello World!",
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/api"
}

注意: result 為純字串 "Hello World!",不是物件。此回應經過 SuccessResponseInterceptor 包裝為統一格式。

前端注意事項

  • 可用於應用啟動時的「後端連線檢查」
  • 若呼叫失敗,表示後端服務未啟動或網路不通,應顯示「服務維護中」頁面
  • 回應時間可用於粗略估計網路延遲

適用場景

場景說明
前端啟動檢查確認 API Server 可達,再進入主應用
Kubernetes Liveness ProbeGET /api 回傳 200 表示容器健康
CI/CD 部署驗證部署後呼叫此端點確認服務正常
監控告警定期 ping 此端點,異常時觸發告警

附錄

Auth 用戶資料表結構 (auth-user)

欄位型別Nullable預設值索引說明
idint (PK)autoPK用戶 ID
accountvarchar(50)-UNIQUE(siteCode, account)帳號
passwordvarchar(255)--bcrypt 密碼雜湊(三方登入為空字串)
namevarchar(50)--暱稱
emailvarchar(50)nullUNIQUE(siteCode, email)已驗證的 Email
emailVerifyCodevarchar(6)null-Email 驗證碼暫存
mobilevarchar(30)nullUNIQUE(siteCode, mobile)E.164 格式手機號碼
mobileVerifyCodevarchar(40)null-手機驗證中的 E.164 號碼
telegramvarchar(50)nullUNIQUE(siteCode, telegram)Telegram user ID
googlevarchar(50)nullUNIQUE(siteCode, google)Google sub ID
vipLevelvarchar(4)'1'-VIP 等級
vipProgressvarchar(3)'0'-VIP 升級進度
totalEffectiveBetdecimal(18,6)0-累計有效投注流水 USD
relegationMissCounttinyint(1)0-連續未達保級月數
vipHoldtinyint(1)0-VIP 保級鎖定(1=鎖定)
googleAuthSecretvarchar(32)null-TOTP secret (base32)
googleAuthEnabledtinyint(1)0-Google Authenticator 啟用(0/1)
tokenVersionint0-Token 版本號(遞增使舊 Token 失效)
balancedecimal(18,6)0-USD 餘額
frozenBalancedecimal(18,6)0-凍結中金額(提領審核中)
withdrawalVerifyCodevarchar(6)null-提領驗證碼
localevarchar(10)'zh-TW'-語系偏好
avatarvarchar(255)null-頭像 URL
vendorGroupIdintnullINDEX金流群組 ID
agentCodevarchar(20)nullUNIQUE(siteCode, agentCode)代理推廣碼
level1AgentIdintnullINDEX一級代理 ID
level2AgentIdintnull-二級代理 ID
level3AgentIdintnull-三級代理 ID
agentTourCompletedAtdatetimenull-代理導覽完成時間
agentTourDismissedAtdatetimenull-代理導覽跳過時間
lastActivityAtdatetimenullINDEX最後活動時間
siteCodevarchar(30)'C9'INDEX所屬站點代碼
createdAtdatetimeCURRENT_TIMESTAMP-建立時間

登入紀錄表結構 (auth-user-login-log)

欄位型別Nullable說明
idint (PK)紀錄 ID
userIdint用戶 ID(登入失敗且帳號不存在時為 null)
devicevarchar裝置指紋或 User-Agent
ipvarchar登入 IP 位址
lastUsedatetime紀錄時間
actionvarcharLOGIN / LOGOUT / LOGIN_FAIL / DEL / UNCAPTURED
remarkvarchar備註(如 ACCOUNT_NOT_FOUND:user001WRONG_PASSWORD

全域 Middleware Pipeline

後端請求處理流水線順序:

HTTP Request
  → cookieParser()                      # 解析 Cookie
  → compression()                       # Gzip 壓縮
  → CORS (origin: true, credentials)    # 跨域設定
  → Global Prefix: /api                 # 路由前綴
  → ValidationPipe (whitelist, transform) # DTO 驗證(去除未知欄位、自動轉型)
  → Controller                          # 路由匹配
  → Guards (JWT / Admin / Permissions)  # 認證與授權檢查
  → Interceptors                        # 攔截器
  → Service                             # 業務邏輯
  → SuccessResponseInterceptor          # 成功回應包裝 { code, message, result }
  → AllExceptionsFilter                 # 錯誤回應處理
  → HTTP Response

C9 Platform API 文件 — Part 2

涵蓋 Game 遊戲模組、VIP 會員模組、BetRecord 投注紀錄模組、Ranking 排行榜模組、Mission 任務模組、LiveSports 即時賽事模組


遊戲類型對照表(全模組通用)

數字代碼typeKey中文名稱
1sports體育
2slot電子(老虎機)
3live真人
4lottery彩票
5chess棋牌
8esports電競
9crypto加密貨幣
10fish捕魚

Game 遊戲模組

路由前綴:/api/game

遊戲模組負責遊戲商管理、遊戲啟動、試玩、遊戲分類設定,以及後台的遊戲供應商 CRUD、模板帶入、跨站複製等功能。遊戲採「轉帳錢包」模式,玩家進入遊戲後由遊戲商透過 S2S 回調進行扣款/派彩。每次投注結算後會自動觸發 VIP 等級重算、活動打碼量累計、任務投注進度更新。

相關資料表

資料表說明
game-provider遊戲供應商(含 siteCode,每站獨立配置)
game-type-config遊戲分類設定(含 siteCode,每站獨立配置)
game-transaction遊戲交易紀錄(扣款/派彩)
game-play-log玩家遊玩紀錄(UPSERT,記錄最近遊玩的遊戲)
bet-order投注訂單
bet-detail投注明細(小注單)

GET /api/game/provider — 取得遊戲商列表

功能說明

取得目前啟用的遊戲供應商列表。此端點為公開端點,不需要登入即可呼叫。前台首頁、遊戲大廳頁面載入時應呼叫此 API 取得可用的遊戲商清單,根據 gameType 參數可篩選特定類型的遊戲商。回傳結果會經 Redis 快取(1 小時 TTL),減少資料庫查詢壓力。

認證需求:無(公開端點)

Request

類型名稱型別必填說明範例
QuerygameTypestring遊戲類型篩選"2"

gameType 可選值:1(體育)、2(電子)、3(真人)、4(彩票)、5(棋牌)、8(電競)、9(加密貨幣)、10(捕魚)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "gameCode": "slot-rsg",
      "providerCode": "rsg",
      "gameType": 2,
      "gameTypeLabel": "slot",
      "areaBlock": false,
      "maintain": false,
      "enable": true,
      "createdAt": "2026-02-22T00:00:00.000Z"
    }
  ],
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/game/provider"
}

回傳欄位說明

欄位型別說明
idnumber遊戲供應商 ID
gameCodestring遊戲代碼(全站唯一,用於啟動遊戲)
providerCodestring對接的遊戲商 API 代碼(rsg / betsolutions
gameTypenumber遊戲類型代碼
gameTypeLabelstring遊戲類型標籤(sports/slot/live 等)
areaBlockboolean是否地區封鎖
maintainboolean是否維護中
enableboolean是否啟用
createdAtstring建立時間 (ISO 8601)

業務邏輯說明

  • 僅回傳 enable = true 的遊戲商(前台端點自動過濾)
  • 快取 key 格式:cache:game:providerscache:game:providers:{gameType},TTL 1 小時
  • 遊戲商以 providerCode 區分實際對接的遊戲商 API(目前支援 rsgbetsolutions

前端注意事項 / UI 設計提示

  • 前台遊戲大廳應依 gameType 分類顯示遊戲商
  • maintain = true 的遊戲商應顯示維護中提示,禁止進入
  • areaBlock = true 的遊戲商根據用戶地區判斷是否顯示

POST /api/game/launch — 啟動遊戲(取得遊戲 URL)

功能說明

已登入用戶透過此端點啟動遊戲,後端會根據 gameCode 查詢對應的遊戲供應商設定,自動轉換成遊戲商 API 所需的參數格式,產生含有認證 token 的遊戲 URL 回傳給前端。前端收到 URL 後應以 iframe 或新視窗開啟。啟動前會檢查風控黑名單,若用戶被封鎖則回傳錯誤碼 5010。啟動成功後會非同步記錄玩家遊玩紀錄(UPSERT game-play-log)。

認證需求:需要登入(JwtAuthGuard,前台用戶 JWT Bearer Token)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer TokenBearer eyJhbG...
Headerlocalesstring語系(預設 zh-TW"zh-TW" / "en-US"
Bodydevicestring裝置類型"mobile" / "desktop"
BodygameCodestring遊戲商代碼(對應 game-provider.gameCode"slot-betsolutions"
BodyproductIdnumber遊戲商內的遊戲 ID1

Body 範例

json
{
  "device": "mobile",
  "gameCode": "slot-betsolutions",
  "productId": 1
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "url": "https://game-server.example.com/play?token=xxx"
  },
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/game/launch"
}

錯誤碼

錯誤碼說明
5010用戶已被列入遊戲黑名單,禁止進入遊戲
401未授權(未登入或 Token 過期)

業務邏輯說明

  • 啟動前呼叫 AdminRiskService.isUserBlockedFromGame() 檢查風控黑名單
  • 黑名單支援三層封鎖:全封鎖(gameType=null, productId=null)、類型封鎖(gameType='slot')、特定遊戲封鎖(gameType='slot', productId=123
  • 語系由 locales header 決定(zh-TW / en-US),用於遊戲內介面語言
  • 啟動成功後非同步 UPSERT game-play-log,記錄玩家最近遊玩的遊戲(.catch(() => {}) 不阻塞主流程)
  • BetSolutions 遊戲商的 gameType 會透過 BS_PRODUCT_MAP 映射:2(slot) → ProductId 29(crypto) → ProductId 3

前端注意事項 / UI 設計提示

  • 收到 URL 後建議以 iframe 或新視窗開啟,避免離開當前頁面
  • 手機版建議全螢幕 iframe;桌機版可用彈窗或新分頁
  • 若收到 5010 錯誤碼,應顯示「您已被限制進入此遊戲」提示
  • device 參數影響遊戲商回傳的 URL(手機版/桌機版介面不同)

POST /api/game/simulate — 模擬遊戲一輪(模擬遊戲商回調)

功能說明

前端按下按鈕觸發一輪模擬遊戲。此端點內部模擬遊戲商 S2S callback 的完整流程:先扣款(debit)、再隨機開獎(RTP 97%)、最後派彩(credit)。玩家餘額會即時變動,交易紀錄會寫入 game-transactionbet-order / bet-detail。此端點主要用於開發測試和 Demo 展示,讓前端無需真正連接遊戲商即可模擬完整的投注流程。投注結算後會自動觸發 VIP 重算、活動打碼量、任務進度更新。

認證需求:需要登入(JwtAuthGuard,前台用戶 JWT Bearer Token)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer TokenBearer eyJhbG...
BodygameCodestring遊戲商代碼"slot-betsolutions"
BodyproductIdnumber遊戲 ID1
BodybetAmountnumber投注金額 (USD,範圍 0.01 ~ 10000)10

Body 範例

json
{
  "gameCode": "slot-betsolutions",
  "productId": 1,
  "betAmount": 10
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "roundId": "SIM_1708888888888_abc123",
    "betAmount": 10,
    "multiplier": 2,
    "winAmount": 20,
    "profit": 10,
    "result": "medium",
    "balanceBefore": 200,
    "balanceAfter": 210
  },
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/game/simulate"
}

回傳欄位說明

欄位型別說明
roundIdstring模擬回合 ID(格式:SIM_{timestamp}_{random}
betAmountnumber投注金額 (USD)
multipliernumber開獎倍率
winAmountnumber贏得金額 (USD)
profitnumber淨損益 (USD,可為負數)
resultstring結果標籤(lose/small/medium/good/big/huge/mega
balanceBeforenumber投注前餘額
balanceAfternumber投注後餘額

業務邏輯說明

  • RTP(Return to Player)設定為 97%,賠率表如下:
機率倍率標籤
60%0xlose(全輸)
22%0.5xsmall
10%2xmedium
5%4xgood
2%8xbig
0.8%20xhuge
0.2%70xmega(大獎)
  • 模擬成功後自動觸發以下連鎖操作:
    1. VipService.recalculateUserVip() — VIP 等級重算(只升不降)
    2. PromoService.updatePromoTurnover() — 優惠打碼量累計
    3. MissionService.updateBetProgress() — 任務投注進度
  • 模擬成功後非同步記錄 game-play-log

前端注意事項 / UI 設計提示

  • 此端點主要用於 Demo 展示,正式環境遊戲由遊戲商回調處理
  • 前端可根據 result 標籤播放不同的動畫效果
  • 投注後應刷新用戶餘額顯示(可用 balanceAfter 即時更新)
  • 建議加入投注金額輸入驗證(0.01 ~ 10000)

POST /api/game/demo — 試玩遊戲(免登入 Demo)

功能說明

不需要登入即可取得遊戲 Demo URL,讓訪客可以體驗遊戲。目前僅支援 BetSolutions 的 slot 類遊戲。前端可在遊戲列表中提供「試玩」按鈕,讓未註冊的用戶也能體驗遊戲樂趣,提升轉化率。

認證需求:無(公開端點)

Request

類型名稱型別必填說明範例
Headerlocalesstring語系(預設 zh-TW"en-US"
Bodydevicestring裝置類型"mobile" / "desktop"
BodygameCodestring遊戲商代碼"slot-betsolutions"
BodyproductIdnumber遊戲 ID1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "url": "https://auth-staging.betsolutions.com/auth/auth?..."
  },
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/game/demo"
}

業務邏輯說明

  • 僅支援 BetSolutions slot 類遊戲的 Demo 模式
  • Demo 遊戲使用虛擬籌碼,不影響真實餘額
  • 不需要風控黑名單檢查(無登入用戶)
  • 不記錄遊玩紀錄

前端注意事項 / UI 設計提示

  • 在遊戲列表每個遊戲卡片上提供「試玩」和「正式遊戲」兩個按鈕
  • 試玩按鈕不需要判斷登入狀態
  • 開啟方式同 /game/launch(iframe 或新視窗)

GET /api/game/recent — 取得玩家最近遊玩的遊戲

功能說明

查詢已登入玩家最近遊玩過的遊戲列表。資料來源為 game-play-log 表,每次遊戲啟動或模擬成功後會自動 UPSERT 該表。前端可在遊戲大廳顯示「最近遊玩」區塊,讓玩家快速回到之前玩過的遊戲。

認證需求:需要登入(JwtAuthGuard,前台用戶 JWT Bearer Token)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer TokenBearer eyJhbG...
Querylimitstring回傳筆數(預設 10,最多 20)"10"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "gameCode": "slot-rsg",
      "productId": "112",
      "lastPlayedAt": "2026-02-28T10:30:00.000Z"
    },
    {
      "gameCode": "slot-betsolutions",
      "productId": "5",
      "lastPlayedAt": "2026-02-27T15:20:00.000Z"
    }
  ],
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/game/recent"
}

回傳欄位說明

欄位型別說明
gameCodestring遊戲商代碼
productIdstring遊戲商內的遊戲 ID
lastPlayedAtstring最後遊玩時間 (ISO 8601)

業務邏輯說明

  • limit 參數範圍限制:最小 1,最大 20,預設 10
  • 資料來源為 game-play-log 表,按 lastPlayedAt 降序排列
  • 每次遊戲啟動/模擬成功時自動 UPSERT(以 userId + gameCode + productId 為唯一鍵)

前端注意事項 / UI 設計提示

  • 在遊戲大廳顯示「最近遊玩」橫向滾動列表
  • 需搭配 /game/provider 的資料來顯示遊戲名稱、圖片等詳細資訊
  • 未登入狀態不應顯示此區塊

GET /api/game/type-configs — 取得遊戲分類設定(公開)

功能說明

取得遊戲分類設定列表,供前台側邊欄或遊戲大廳的分類導航使用。此端點為公開端點,不需要登入。回傳的分類設定包含多語系名稱、圖標、排序等資訊,前端可據此渲染遊戲分類選單。

認證需求:無(公開端點)

Request:無參數

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "gameType": 2,
      "siteCode": "C9",
      "typeKey": "slot",
      "label": { "zh-TW": "電子遊戲", "en-US": "Slots" },
      "icon": "mdi:slot-machine",
      "sortOrder": 1,
      "enabled": true,
      "createdAt": "2026-02-22T00:00:00.000Z",
      "updatedAt": "2026-02-22T00:00:00.000Z"
    }
  ],
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/game/type-configs"
}

回傳欄位說明

欄位型別說明
idnumber分類設定 ID
gameTypenumber遊戲類型代碼(1/2/3/4/5/8/9/10)
siteCodestring所屬站點代碼
typeKeystring類型 key(sports/slot/live/lottery/chess/esports/crypto/fish
labelobject多語系顯示名稱
iconstring圖標名稱(Iconify 格式)
sortOrdernumber排序權重(越小越前)
enabledboolean是否啟用

前端注意事項 / UI 設計提示

  • 前台側邊欄遊戲分類應根據 enabled 過濾,按 sortOrder 排序
  • 使用 label 物件中對應語系的值作為顯示文字
  • icon 為 Iconify 格式,可使用 Iconify 元件渲染

GET /api/game/admin/providers — [Admin] 取得遊戲供應商列表

功能說明

後台管理員取得遊戲供應商列表。與前台 /game/provider 不同,此端點回傳完整欄位(含 name 多語系、siteCodesortOrder),且支援依站點篩選。搭配 @AdminSiteCode() 裝飾器,自動讀取 x-site-code header 或 siteCode query param。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
Headerx-site-codestring站點代碼(由 API Client 自動注入)"C9"
QuerygameTypestring遊戲類型篩選"2"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "gameCode": "slot-rsg",
      "providerCode": "rsg",
      "gameType": 2,
      "name": { "zh-TW": "RSG電子", "en-US": "RSG Slot" },
      "areaBlock": false,
      "maintain": false,
      "enable": true,
      "siteCode": "C9",
      "sortOrder": 1
    }
  ],
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/game/admin/providers"
}

業務邏輯說明

  • siteCode 篩選邏輯:Header 選「全部站點」時不帶 x-site-code,前端手動傳 siteCode query param;選獨立站點時 API Client 自動帶 x-site-code header
  • name 欄位為 JSON 格式的多語系名稱,後台編輯時應支援多語系輸入

前端注意事項 / UI 設計提示

  • 後台遊戲供應商管理頁面應使用 SiteTabs 多站點切換
  • 列表支援新增、編輯、刪除操作
  • 支援「同預設站點」一鍵複製和「帶入模板」功能

POST /api/game/admin/providers — [Admin] 新增遊戲供應商

功能說明

後台管理員新增遊戲供應商。每個站點的 gameCode 必須唯一(game-provider 表有 siteCode + gameCode 的唯一約束)。新增時需指定遊戲代碼、供應商代碼、遊戲類型等基本資訊。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
BodygameCodestring遊戲代碼(站內唯一)"slot-rsg"
BodyproviderCodestring供應商代碼"rsg"
BodygameTypenumber遊戲類型2
Bodynameobject多語系名稱{ "zh-TW": "RSG電子", "en-US": "RSG Slot" }
BodysiteCodestring站點代碼"C9"
Bodyenableboolean啟用狀態true
BodysortOrdernumber排序1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 5,
    "gameCode": "slot-rsg",
    "providerCode": "rsg",
    "gameType": 2,
    "name": { "zh-TW": "RSG電子", "en-US": "RSG Slot" },
    "siteCode": "C9",
    "enable": true,
    "sortOrder": 1,
    "createdAt": "2026-03-01T10:00:00.000Z"
  }
}

PATCH /api/game/admin/providers/:id — [Admin] 更新遊戲供應商

功能說明

更新指定 ID 的遊戲供應商資訊。可部分更新(僅傳需要修改的欄位)。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
URL Paramidnumber遊戲供應商 ID1
Body(同新增,所有欄位皆為選填)

DELETE /api/game/admin/providers/:id — [Admin] 刪除遊戲供應商

功能說明

刪除指定 ID 的遊戲供應商。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
URL Paramidnumber遊戲供應商 ID1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

GET /api/game/admin/type-configs — [Admin] 取得遊戲分類設定列表

功能說明

後台管理員取得遊戲分類設定列表,支援依站點篩選。搭配 @AdminSiteCode() 裝飾器自動讀取站點代碼。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
Headerx-site-codestring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "gameType": 2,
      "siteCode": "C9",
      "typeKey": "slot",
      "label": { "zh-TW": "電子遊戲", "en-US": "Slots" },
      "icon": "mdi:slot-machine",
      "sortOrder": 1,
      "enabled": true,
      "createdAt": "2026-02-22T00:00:00.000Z",
      "updatedAt": "2026-02-22T00:00:00.000Z"
    }
  ]
}

POST /api/game/admin/type-configs — [Admin] 新增遊戲分類設定

功能說明

新增遊戲分類設定。每個站點的 gameType 必須唯一(game-type-config 表有 siteCode + gameType 的唯一約束)。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
BodygameTypenumber遊戲類型2
Bodynameobject多語系名稱{ "zh-TW": "電子遊戲", "en-US": "Slots" }
BodysiteCodestring站點代碼"C9"
Bodyenableboolean啟用狀態true
BodysortOrdernumber排序1

PATCH /api/game/admin/type-configs/:id — [Admin] 更新遊戲分類設定

功能說明

更新指定 ID 的遊戲分類設定。可部分更新。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
URL Paramidnumber遊戲分類設定 ID1

DELETE /api/game/admin/type-configs/:id — [Admin] 刪除遊戲分類設定

功能說明

刪除指定 ID 的遊戲分類設定。

認證需求:需要後台管理員登入(AdminJwtAuthGuard


GET /api/game/admin/preview-template — [Admin] 預覽遊戲模板資料

功能說明

回傳預設遊戲模板的 typeConfigsproviders 資料,僅供預覽,不寫入資料庫。後台管理員可在「帶入模板」功能中先預覽模板內容,確認無誤後再執行帶入。前端可提供模板預覽對話框,讓管理員在確認前檢查並編輯模板資料。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request:無參數(僅需 Admin Bearer Token)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "typeConfigs": [
      { "gameType": 1, "typeKey": "sports", "label": { "zh-TW": "體育", "en-US": "Sports" }, "icon": "mdi:basketball", "sortOrder": 1 }
    ],
    "providers": [
      { "gameCode": "slot-rsg", "providerCode": "rsg", "gameType": 2, "name": { "zh-TW": "RSG電子", "en-US": "RSG Slot" }, "sortOrder": 1 }
    ]
  }
}

POST /api/game/admin/load-template — [Admin] 帶入遊戲預設模板

功能說明

將遊戲模板資料帶入指定站點。可傳入自訂資料(經管理員在預覽對話框中編輯後),不傳則使用預設模板。寫入時依 x-site-code header 決定目標站點,null 則寫入全站(預設站點代碼)。操作在 transaction 內執行,先刪除目標站點現有資料再插入模板資料,確保原子性。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
Headerx-site-codestring目標站點代碼"C9"
BodytypeConfigsarray自訂分類設定(不傳用預設)[...]
Bodyprovidersarray自訂供應商設定(不傳用預設)[...]

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "typeConfigs": 8, "providers": 12 }
}

業務邏輯說明

  • Transaction 內先刪後插,確保原子性
  • 僅影響指定站點的資料,不會影響其他站點
  • 帶入後自動清除相關 Redis 快取

POST /api/game/admin/copy-site-data — [Admin] 同預設站點(跨站複製)

功能說明

將來源站點的遊戲資料複製到目標站點。支援複製遊戲供應商(providers)或遊戲分類設定(typeConfigs)。操作在 transaction 內執行,先刪除目標站點現有資料再從來源站點複製。這是後台「同預設站點」按鈕的核心 API。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
BodysourceSiteCodestring來源站點代碼"C9"
BodytargetSiteCodestring目標站點代碼"SITE_A"
Bodytypestring複製類型"providers""typeConfigs"

Body 範例

json
{
  "sourceSiteCode": "C9",
  "targetSiteCode": "SITE_A",
  "type": "providers"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "copied": 12 }
}

業務邏輯說明

  • Transaction 內先刪後插,確保原子性
  • 複製時會將 siteCode 替換為目標站點代碼
  • 複製完成後自動清除目標站點的 Redis 快取

前端注意事項 / UI 設計提示

  • 後台 SiteTabs 的非預設站 tab 會出現「同預設站點」按鈕
  • 點擊後應彈出確認對話框,顯示來源和目標站點代碼
  • 複製成功後自動刷新當前列表

VIP 會員模組

路由前綴:/api/vip

VIP 模組負責會員等級管理、返水(Rebate)規則配置、反水結算、月度保級檢查等功能。系統採每站獨立配置,每個站點可自行設定 VIP 等級數量和返水規則。投注累積自動升級(只升不降),每日 00:05 自動結算反水,每月 1 號 01:00 進行保級檢查。

相關資料表

資料表說明
vip-levelVIP 等級配置(含 siteCode,每站獨立)
vip-rebate返水規則(unique key: siteCode + level + gameType)
vip-rebate-log反水結算紀錄

自動排程

排程時間說明
每日反水結算每日 00:05結算昨日各用戶的反水金額並發放到餘額
月度保級檢查每月 1 號 01:00檢查 VIP 2+ 用戶是否達到保級門檻

GET /api/vip/levels — 取得 VIP 等級列表

功能說明

取得指定站點的 VIP 等級列表。此端點為公開端點,前台 VIP 頁面、個人中心、以及任何需要顯示 VIP 等級資訊的頁面都應呼叫此 API。回傳結果會經 Redis 快取(1 小時 TTL)。站點代碼來源優先順序:siteCode query param > site-name header > 環境變數 SITE_CODE。多語系名稱會依當前語系自動解析。

認證需求:無(公開端點)

Request

類型名稱型別必填說明範例
Headersite-namestring站點名稱(前台自動帶入)"C9"
QuerysiteCodestring站點代碼(優先於 header)"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "level": 1,
      "name": "Bronze I",
      "tier": "bronze",
      "minChip": "0.000000",
      "relegationChip": "0.000000",
      "sortOrder": 1,
      "enabled": 1,
      "siteCode": "C9",
      "createdAt": "2026-02-22T00:00:00.000Z",
      "updatedAt": "2026-02-22T00:00:00.000Z"
    }
  ],
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/vip/levels"
}

回傳欄位說明

欄位型別說明
idnumberVIP 等級 ID
levelnumberVIP 等級編號
namestring等級名稱(已依語系解析,非原始 JSON)
tierstring階級(bronze/gold/platinum/diamond
minChipstring升級所需最低累計籌碼 (USD, decimal(18,6))
relegationChipstring保級所需月度投注額 (USD, decimal(18,6))
sortOrdernumber排序權重
enablednumber是否啟用(0/1)
siteCodestring所屬站點代碼

業務邏輯說明

  • 僅回傳 enabled = 1 的等級
  • sortOrder 升序排列
  • 快取 key:cache:vip:levels:{siteCode},TTL 1 小時
  • name 欄位原始值為 JSON({"zh-TW":"青銅 I","en-US":"Bronze I"}),由 resolveText() 自動解析為當前語系文字

前端注意事項 / UI 設計提示

  • VIP 等級數量為動態的(不限 15 級),前端不應硬編碼等級數量
  • 前台 VIP 相關下拉選項(如篩選 VIP 等級)均應使用此 API 動態取得
  • 可用 tier 欄位決定對應的色彩主題(bronze=銅色、gold=金色、platinum=白金、diamond=鑽石)

GET /api/vip/rebates — 取得返水規則

功能說明

取得指定站點的 VIP 返水規則列表。每筆規則定義了特定 VIP 等級在特定遊戲類型下的返水比例。前台 VIP 頁面可用此資料展示各等級的返水比例表格。

認證需求:無(公開端點)

Request

類型名稱型別必填說明範例
Headersite-namestring站點名稱"C9"
QuerysiteCodestring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "level": 1,
      "gameType": "live",
      "rebateRate": "0.50",
      "siteCode": "C9",
      "createdAt": "2026-02-22T00:00:00.000Z",
      "updatedAt": "2026-02-22T00:00:00.000Z"
    },
    {
      "id": 2,
      "level": 1,
      "gameType": "slot",
      "rebateRate": "0.50",
      "siteCode": "C9"
    }
  ]
}

回傳欄位說明

欄位型別說明
levelnumberVIP 等級
gameTypestring遊戲類型標籤(sports/slot/live/lottery/chess/esports/crypto/fish
rebateRatestring返水比例 (%, decimal(5,2)),例如 "0.50" 代表 0.5%

業務邏輯說明

  • 快取 key:cache:vip:rebates:{siteCode},TTL 1 小時
  • 返水比例為百分比,0.50 = 0.5%
  • 預設模板為 15 級 x 8 遊戲類型 = 120 筆規則

前端注意事項 / UI 設計提示

  • 前台 VIP 頁面可用矩陣表格展示:行=VIP 等級、列=遊戲類型、格子=返水比例
  • 返水比例單位為 %,顯示時加上百分號

GET /api/vip/status — 取得用戶 VIP 狀態

功能說明

取得當前登入用戶的完整 VIP 狀態資訊,包含當前等級、累積有效投注、升級進度、本月有效投注、返水比例、保級資訊、以及全部等級列表。此端點是前台個人中心 VIP 資訊的核心 API,前端可據此渲染 VIP 等級卡片、進度條、返水表格等 UI 元件。

認證需求:需要登入(JwtAuthGuard,前台用戶 JWT Bearer Token)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer TokenBearer eyJhbG...
Headersite-namestring站點名稱"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "level": 1,
    "name": "Bronze I",
    "tier": "bronze",
    "totalEffectiveBet": "1200.000000",
    "currentChip": "0.000000",
    "nextLevelMinChip": "3600.000000",
    "progress": "0.333333",
    "relegationChip": "0.000000",
    "monthlyEffective": "450.000000",
    "relegationMissCount": 0,
    "vipHold": 0,
    "rebates": [
      { "gameType": "sports", "rebateRate": "0.50" },
      { "gameType": "slot", "rebateRate": "0.50" },
      { "gameType": "live", "rebateRate": "0.50" },
      { "gameType": "lottery", "rebateRate": "0.50" },
      { "gameType": "chess", "rebateRate": "0.50" },
      { "gameType": "esports", "rebateRate": "0.30" },
      { "gameType": "crypto", "rebateRate": "0.50" },
      { "gameType": "fish", "rebateRate": "0.50" }
    ],
    "allLevels": [
      {
        "level": 1,
        "name": "Bronze I",
        "tier": "bronze",
        "minChip": "0.000000",
        "relegationChip": "0.000000"
      },
      {
        "level": 2,
        "name": "Bronze II",
        "tier": "bronze",
        "minChip": "3600.000000",
        "relegationChip": "200.000000"
      }
    ]
  }
}

回傳欄位說明

欄位型別說明
levelnumber當前 VIP 等級
namestring等級名稱(已依語系解析)
tierstring階級(bronze/gold/platinum/diamond
totalEffectiveBetstring歷史累計有效投注 (USD)
currentChipstring當前等級所需最低籌碼
nextLevelMinChipstring | null下一等級所需最低籌碼(已滿級時為 null)
progressstring升級進度(0~1 之間的小數,如 0.333333 = 33.33%)
relegationChipstring當前等級保級所需月度投注額
monthlyEffectivestring本月已累計的有效投注(即時查詢)
relegationMissCountnumber連續未達保級門檻的月數(0=正常、1=警告中)
vipHoldnumber保級鎖定狀態(0=未鎖定、1=已鎖定)
rebatesarray當前等級的返水比例列表
allLevelsarray全部 VIP 等級列表(用於進度條渲染)

業務邏輯說明

  • monthlyEffective 為即時查詢(非快取),確保資料即時性
  • progress 計算方式:totalEffectiveBet / nextLevelMinChip,最大為 1
  • 已滿級時 nextLevelMinChip = nullprogress = 1
  • relegationMissCount 每月保級檢查時更新:達標重置為 0,未達標 +1,連續 2 月未達則降級

前端注意事項 / UI 設計提示

  • 使用 progress 渲染升級進度條
  • relegationMissCount = 1 時應顯示保級警告提示
  • vipHold = 1 時顯示「保級鎖定」標章(VIP 5+ 才能鎖定)
  • allLevels 渲染完整等級路線圖,高亮當前等級
  • rebates 陣列渲染為返水比例表格

POST /api/vip/settlement/daily-rebate — [Admin] 手動觸發每日反水結算

功能說明

手動觸發每日反水結算作業(通常由 Cron 排程於每日 00:05 自動執行)。結算昨日各用戶在各遊戲類型的有效投注,依 VIP 等級對應的返水比例計算反水金額,直接發放到用戶餘額,同時寫入 vip-rebate-log 結算紀錄。此端點供後台管理員在特殊情況下(如結算異常、補結算)手動觸發。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明
HeaderAuthorizationstringAdmin Bearer Token

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "usersProcessed": 25,
    "totalRebate": "3.120000"
  }
}

回傳欄位說明

欄位型別說明
usersProcessednumber處理的用戶數量
totalRebatestring發放的反水總額 (USD)

業務邏輯說明

  • 結算昨日(UTC+8 00:00~23:59:59)的投注資料
  • 計算公式:反水金額 = 有效投注 x (返水比例 / 100)
  • 反水金額使用 truncateUsd() 無條件捨去至 6 位小數
  • 結算流程:
    1. 查詢昨日各用戶各遊戲類型的有效投注合計
    2. 批次載入所有相關用戶的 VIP 等級
    3. 查詢所有返水規則建立 map(key: siteCode-level-gameType
    4. 逐用戶計算反水金額,發放到餘額
    5. 寫入 vip-rebate-log 紀錄

POST /api/vip/settlement/monthly-relegation — [Admin] 手動觸發月度保級檢查

功能說明

手動觸發月度保級檢查(通常由 Cron 排程於每月 1 號 01:00 自動執行)。檢查所有 VIP 2 級以上用戶上月的有效投注是否達到保級門檻。連續第 1 月未達標僅發出警告(relegationMissCount +1),連續第 2 月未達標則降 1 級。VIP 5 級以上且已鎖定保級(vipHold = 1)的用戶會跳過檢查。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明
HeaderAuthorizationstringAdmin Bearer Token

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "checked": 10,
    "warned": 2,
    "demoted": 1
  }
}

回傳欄位說明

欄位型別說明
checkednumber檢查的用戶數量(VIP 2+ 用戶)
warnednumber發出警告的用戶數量(第 1 月未達標)
demotednumber降級的用戶數量(連續 2 月未達標)

業務邏輯說明

  • 僅檢查 VIP 等級 >= 2 的用戶(VIP 1 級為最低等級,不需保級)
  • vipHold = 1 且 VIP >= 5 的用戶跳過保級檢查
  • 保級規則:
    • 上月有效投注 >= relegationChip → 達標,relegationMissCount 重置為 0
    • 第 1 月未達標 → relegationMissCount = 1(警告)
    • 連續第 2 月未達標 → 降 1 級,relegationMissCount 重置為 0
  • 降級後最低只能降到 VIP 1 級

PATCH /api/vip/users/:userId/hold — [Admin] 設定 VIP 保級鎖定

功能說明

手動鎖定或解鎖指定用戶的 VIP 保級狀態。僅 VIP 5 級以上的用戶可以設定保級鎖定。鎖定後月度保級檢查會跳過該用戶,不會因未達投注門檻而降級。此功能供後台管理員對 VIP 高等級用戶進行特殊維護。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
URL ParamuserIdnumber用戶 ID1
Bodyholdnumber鎖定狀態(0=解鎖、1=鎖定)1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "userId": 1,
    "vipHold": 1
  }
}

錯誤碼

錯誤碼說明
2001用戶不存在
2002VIP 等級未達 5,不可設定保級鎖定

POST /api/vip/levels — [Admin] 建立 VIP 等級

功能說明

建立新的 VIP 等級。每個站點的 VIP 等級編號必須唯一。搭配 @AdminSiteCode() 裝飾器,自動將新等級歸屬到指定站點。建立成功後自動清除該站點的 VIP 等級快取。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringAdmin Bearer TokenBearer eyJhbG...
Headerx-site-codestring站點代碼"C9"
BodylevelnumberVIP 等級編號(>= 1)1
Bodynameobject多語系名稱{ "zh-TW": "青銅 I", "en-US": "Bronze I" }
Bodytierstring階級"bronze" / "gold" / "platinum" / "diamond"
BodyminChipnumber升級所需最低累計籌碼 (USD)3600
BodyrelegationChipnumber保級所需月度投注額 (USD)200
BodysortOrdernumber排序權重(預設 = level)1
Bodyenablednumber是否啟用(0/1,預設 1)1

錯誤碼

錯誤碼說明
2003此 VIP 等級已存在(同站點同 level 重複)

PATCH /api/vip/levels/:id — [Admin] 更新 VIP 等級

功能說明

更新指定 ID 的 VIP 等級。可部分更新(使用 PartialType)。更新成功後自動清除該站點的 VIP 等級快取。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
URL ParamidnumberVIP 等級 ID1
Body(同建立,所有欄位皆為選填)

錯誤碼

錯誤碼說明
2001查無此 VIP 等級

DELETE /api/vip/levels/:id — [Admin] 刪除 VIP 等級

功能說明:刪除指定 ID 的 VIP 等級。刪除成功後自動清除該站點的 VIP 等級快取。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

錯誤碼

錯誤碼說明
2001查無此 VIP 等級

POST /api/vip/rebates — [Admin] 建立返水規則

功能說明

建立新的返水規則。每個站點的 level + gameType 組合必須唯一。搭配 @AdminSiteCode() 裝飾器自動歸屬站點。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
Headerx-site-codestring站點代碼"C9"
BodylevelnumberVIP 等級1
BodygameTypestring遊戲類型"slot"
BodyrebateRatenumber返水比例 (%)0.5

gameType 可選值:sportsslotlivelotterychessesportscryptofish

錯誤碼

錯誤碼說明
2004此等級的遊戲類型返水規則已存在

PATCH /api/vip/rebates/:id — [Admin] 更新返水規則

認證需求:需要後台管理員登入(AdminJwtAuthGuard

錯誤碼

錯誤碼說明
2002查無此返水規則

DELETE /api/vip/rebates/:id — [Admin] 刪除返水規則

認證需求:需要後台管理員登入(AdminJwtAuthGuard

錯誤碼

錯誤碼說明
2002查無此返水規則

POST /api/vip/rebates/bulk — [Admin] 批次更新返水規則

功能說明

批次新增或更新(upsert)返水規則。依 siteCode + level + gameType 判斷是新增還是更新。此端點適用於後台返水設定頁面的「批次儲存」操作,一次送出所有等級 x 遊戲類型的返水規則。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明範例
Headerx-site-codestring站點代碼"C9"
Bodyitemsarray返水規則陣列見下方

Body 範例

json
{
  "items": [
    { "level": 1, "gameType": "sports", "rebateRate": 0.2 },
    { "level": 1, "gameType": "slot", "rebateRate": 0.5 },
    { "level": 1, "gameType": "live", "rebateRate": 0.5 },
    { "level": 2, "gameType": "sports", "rebateRate": 0.25 },
    { "level": 2, "gameType": "slot", "rebateRate": 0.55 }
  ]
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "affected": 5
  }
}

業務邏輯說明

  • 使用 TypeORM upsert() 方法,以 ['siteCode', 'level', 'gameType'] 為衝突鍵
  • 已存在的規則會更新 rebateRate,不存在的會新增
  • 操作完成後自動清除該站點的返水規則快取

GET /api/vip/preview-template — [Admin] 預覽 VIP 模板資料

功能說明

回傳預設 VIP 模板資料,包含 15 個等級和 120 筆返水規則(15 級 x 8 遊戲類型),僅供預覽,不寫入資料庫。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "levels": [
      { "level": 1, "name": { "zh-TW": "青銅 I", "en-US": "Bronze I" }, "tier": "bronze", "minChip": "0.000000", "relegationChip": "0.000000", "sortOrder": 1 },
      { "level": 2, "name": { "zh-TW": "青銅 II", "en-US": "Bronze II" }, "tier": "bronze", "minChip": "3600.000000", "relegationChip": "200.000000", "sortOrder": 2 }
    ],
    "rebates": [
      { "level": 1, "gameType": "sports", "rebateRate": "0.20" },
      { "level": 1, "gameType": "slot", "rebateRate": "0.50" }
    ]
  }
}

業務邏輯說明

  • 預設模板包含 4 個階級:bronze(1~6)、gold(7~9)、platinum(10~12)、diamond(13~15)
  • 返水比例隨等級遞增,從 VIP 1 的 0.2%~0.5% 到 VIP 15 的 0.8%~1.5%

POST /api/vip/load-template — [Admin] 帶入 VIP 模板

功能說明

將 VIP 模板資料帶入指定站點。在 transaction 內先刪除目標站點的現有 VIP 等級和返水規則,再插入模板資料。可傳入管理員編輯後的自訂資料,不傳則使用預設模板。

認證需求:需要後台管理員登入(AdminJwtAuthGuard

Request

類型名稱型別必填說明
Headerx-site-codestring目標站點代碼
Bodylevelsarray自訂等級資料(不傳用預設)
Bodyrebatesarray自訂返水規則(不傳用預設)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "levels": 15,
    "rebates": 120
  }
}

BetRecord 投注紀錄模組

路由前綴:/api/bet-record

投注紀錄模組提供前台用戶查詢自己的投注歷史,包含訂單列表(含匯總統計)和小注單明細。投注資料由遊戲模組的 S2S 回調或模擬投注自動寫入。

相關資料表

資料表說明
bet-order投注訂單(主單)
bet-detail投注明細(小注單,關聯到 bet-order)

GET /api/bet-record — 投注紀錄列表

功能說明

查詢當前登入用戶的投注紀錄列表,支援分頁、狀態篩選、遊戲類型篩選、日期範圍篩選。除了分頁的訂單列表外,還會同時回傳匯總統計(總投注次數、投注金額、有效投注、輸贏),匯總統計僅計算有效注單(status = 'valid')。前台投注紀錄頁面的核心 API。

認證需求:需要登入(JwtAuthGuard,前台用戶 JWT Bearer Token)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer TokenBearer eyJhbG...
Querypagestring頁碼(預設 1)"1"
QuerypageSizestring每頁筆數(預設 10,最大 50)"10"
Querystatusstring注單狀態篩選"valid" / "invalid" / "cancelled"
QuerygameTypestring遊戲類型篩選"2"
QuerystartDatestring開始日期 (YYYY-MM-DD)"2026-01-01"
QueryendDatestring結束日期 (YYYY-MM-DD)"2026-02-28"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "totalBetCount": 79,
    "betAmount": "13.800000",
    "betEffective": "13.800000",
    "winLose": "-1.220000",
    "items": [
      {
        "id": 1,
        "gameType": "2",
        "gamePlatform": "rsg",
        "gameNumber": "N1s50_n1s5076021_349009404986",
        "totalBetCount": 13,
        "betAmount": "0.400000",
        "betEffective": "0.400000",
        "winLose": "5.950000",
        "betDatetime": "2026-02-11 20:39:00",
        "gameName": "RSG電子"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 6,
      "totalPages": 1
    }
  },
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/bet-record"
}

回傳欄位說明(頂層匯總)

欄位型別說明
totalBetCountnumber有效注單的總投注次數合計
betAmountstring有效注單的投注金額合計 (USD)
betEffectivestring有效注單的有效投注合計 (USD)
winLosestring有效注單的淨輸贏合計 (USD,負數=虧損)

回傳欄位說明(items 陣列)

欄位型別說明
idnumber訂單 ID
gameTypestring遊戲類型代碼(數字字串)
gamePlatformstring遊戲平台代碼
gameNumberstring遊戲商方注單編號
totalBetCountnumber該訂單內的投注次數
betAmountstring投注金額 (USD, decimal(18,6))
betEffectivestring有效投注 (USD)
winLosestring淨輸贏 (USD)
betDatetimestring投注時間
gameNamestring遊戲名稱

業務邏輯說明

  • 匯總統計(頂層 4 個欄位)僅計算 status = 'valid' 的注單,且受 gameType 和日期範圍篩選影響
  • 列表查詢支援所有狀態篩選(valid/invalid/cancelled
  • 分頁使用 parsePagination() 工具函數,pageSize 最大限制 50
  • 日期範圍使用 applyDateRange() 工具函數
  • 結果按 betDatetime 降序排列(最新的在前)

前端注意事項 / UI 設計提示

  • 頁面頂部顯示匯總統計卡片(總投注次數、投注金額、有效投注、輸贏)
  • winLose 為負數時以紅色顯示,正數以綠色顯示
  • 提供 statusgameType、日期範圍三個篩選條件
  • 每筆訂單可點擊展開查看小注單明細(呼叫 GET /:orderId/details
  • 金額顯示保留 2~6 位小數(依設計需求截斷)

GET /api/bet-record/:orderId/details — 訂單小注單明細

功能說明

根據訂單 ID 取得該訂單內的所有小注單(bet-detail)明細。前端在投注紀錄列表中點擊某筆訂單後呼叫此 API 取得詳細的每輪投注資訊。系統會驗證該訂單是否屬於當前登入用戶,不屬於則回傳錯誤碼 2001。

認證需求:需要登入(JwtAuthGuard,前台用戶 JWT Bearer Token)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer TokenBearer eyJhbG...
URL ParamorderIdnumber訂單 ID1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "roundNo": 1,
      "betAmount": "0.020000",
      "winLose": "0.500000",
      "createdAt": "2026-02-11T12:39:01.000Z"
    },
    {
      "id": 2,
      "roundNo": 2,
      "betAmount": "0.040000",
      "winLose": "-0.040000",
      "createdAt": "2026-02-11T12:39:05.000Z"
    }
  ],
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/bet-record/1/details"
}

回傳欄位說明

欄位型別說明
idnumber小注單 ID
roundNonumber回合序號
betAmountstring該回合投注金額 (USD)
winLosestring該回合淨輸贏 (USD)
createdAtstring回合時間 (ISO 8601)

錯誤碼

錯誤碼說明
2001訂單不存在或不屬於該用戶

業務邏輯說明

  • 安全檢查:同時查詢 orderIduserId,確保用戶只能查看自己的訂單
  • 小注單按 roundNo 升序排列
  • 僅回傳 idroundNobetAmountwinLosecreatedAt 五個欄位

前端注意事項 / UI 設計提示

  • 在投注紀錄列表中,點擊訂單行可展開 accordion 或彈出 dialog 顯示小注單
  • 每筆小注單顯示回合序號、投注金額、輸贏
  • winLose 以顏色區分正負值

Ranking 排行榜模組

路由前綴:/api/ranking

排行榜模組提供前台的投注排行和累積提領排行功能。支援即時、每日、每週、每月、累積五種類型。排行榜資料同時包含真實數據和假資料填充(當真實數據不足時)。匿名用戶的名稱會以翻譯後的「隱身」文字替代。

相關資料表

資料表說明
rank-list排行榜資料(含投注金額、倍率、支付金額、匿名狀態)

GET /api/ranking — 取得排行榜

功能說明

根據排行榜類型回傳對應的排行資料。支援 5 種類型:realtime(即時投注)、daily(每日)、weekly(每週)、monthly(每月)、total(累積提領)。前 4 種類型回傳投注明細格式(含遊戲名稱、投注金額、倍率、支付金額),total 類型回傳累積提領格式(含玩家帳號、累積支付總額)。此端點為公開端點,供前台首頁或排行榜頁面使用。資料不足時會用假資料填充至指定筆數。

認證需求:無(公開端點)

Request

類型名稱型別必填說明範例
Querytypestring排行榜類型(預設 realtime"realtime"
Querylimitstring筆數(預設 20,最大 50)"20"

type 可選值:

說明排序方式時間範圍
realtime即時投注時間降序(最新在前)不限(最近 30 分鐘的假資料填充)
daily每日排行支付金額降序今天 00:00 ~ 現在
weekly每週排行支付金額降序最近 7 天
monthly每月排行支付金額降序最近 30 天
total累積提領累積支付降序全部時間

Response 範例(realtime / daily / weekly / monthly)

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "rank": 1,
      "id": 1,
      "gameName": "VIP Blackjack 7",
      "playerName": "隐身",
      "time": "2026-02-22T14:27:00.000Z",
      "betAmount": "1875.000000",
      "multiplier": "1.20",
      "payout": "2250.000000",
      "isAnonymous": true
    },
    {
      "rank": 2,
      "id": 3,
      "gameName": "Sweet Bonanza",
      "playerName": "ja***s88",
      "time": "2026-02-22T14:25:00.000Z",
      "betAmount": "500.000000",
      "multiplier": "3.50",
      "payout": "1750.000000",
      "isAnonymous": false
    }
  ]
}

Response 範例(total 累積提領)

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "rank": 1,
      "playerAccount": "ja***s88",
      "totalPayout": "125000.000000"
    },
    {
      "rank": 2,
      "playerAccount": "lu***y07",
      "totalPayout": "98000.000000"
    }
  ]
}

回傳欄位說明(投注類型:realtime / daily / weekly / monthly)

欄位型別說明
ranknumber排名(從 1 開始)
idnumber排行紀錄 ID(假資料為負數)
gameNamestring遊戲名稱
playerNamestring玩家名稱(匿名時顯示翻譯後的「隱身」)
timestring投注時間 (ISO 8601)
betAmountstring投注金額 (USD)
multiplierstring倍率
payoutstring支付金額 (USD)
isAnonymousboolean是否匿名

回傳欄位說明(累積類型:total)

欄位型別說明
ranknumber排名
playerAccountstring玩家帳號
totalPayoutstring累積支付總額 (USD)

業務邏輯說明

  • realtime 類型不做快取,每次請求都即時查詢
  • 其他類型快取 30 秒(key 格式:cache:ranking:{type}:{limit}:{lang}
  • 真實資料不足時用假資料填充至 limit 筆,假資料特徵:
    • id 為負數(-(index + 1)
    • 使用 SeededRandom(確定性偽隨機數)產生,同一 cache 週期內結果穩定
    • 假資料的投注金額分布:60% 在 $50~$500、25% 在 $500~$2000、10% 在 $2000~$5000、5% 在 $5000~$10000
    • 假資料的倍率分布:15% 為 0(全輸)、50% 在 0.3x~2x、20% 在 2x~5x、10% 在 5x~10x、5% 在 10x~50x(大獎)
  • 匿名用戶(isAnonymous = true)的 playerName 使用 i18n 翻譯的「隱身」文字
  • total 類型按 userId 分組,加總 payout 後排序

前端注意事項 / UI 設計提示

  • 排行榜頁面提供 Tab 切換 5 種類型
  • realtime 類型可加入自動刷新機制(每 10~30 秒)
  • 匿名用戶可用特殊圖示或遮罩效果表示
  • 排名前 3 名可使用特殊樣式(金/銀/銅牌)
  • id < 0 的紀錄為假資料填充,前端不需要特別處理(對用戶透明)
  • total 類型的 UI 格式與投注類型不同(只有帳號和累積支付)

Mission 任務模組

路由前綴:/api/mission

任務模組提供每日/每週/每月的存款任務和投注任務。用戶達成任務門檻後可領取獎勵,獎勵會直接發放到餘額。部分任務要求完成打碼量(turnover),未完成打碼量的獎勵處於「pending」狀態。投注結算後和存款確認後會自動更新對應的任務進度。

相關資料表

資料表說明
mission任務定義(category/periodType/tier/門檻/獎勵)
mission-progress任務進度(userId/periodType/periodKey/累計存款/累計投注)
mission-claim任務領取紀錄(含打碼量追蹤)

任務類別與週期

類別 (category)說明
deposit存款任務 — 累計存款金額達門檻
bet投注任務 — 累計有效投注達門檻
週期 (periodType)說明periodKey 格式
daily每日任務2026-03-01
weekly每週任務2026-W09 (ISO week)
monthly每月任務2026-03

GET /api/mission — 取得任務列表(含進度與領取狀態)

功能說明

取得所有啟用中的任務列表。若用戶已登入,會同時回傳每個任務的當前進度、是否已領取、是否可領取、VIP 等級是否符合。若未登入(使用 OptionalJwtAuthGuard),仍會回傳任務列表但進度相關欄位為預設值。前台任務頁面的核心 API,載入頁面時即應呼叫。任務列表會經 Redis 快取(1 小時 TTL),但進度資料為即時查詢。

認證需求:可選登入(OptionalJwtAuthGuard,未登入也能呼叫但無進度資料)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer Token(可選)Bearer eyJhbG...

Response 範例(已登入)

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "category": "deposit",
      "periodType": "daily",
      "tier": 1,
      "threshold": "10.000000",
      "rewardAmount": "0.300000",
      "vipRequired": 0,
      "turnoverMultiplier": "3.00",
      "enabled": 1,
      "siteCode": "C9",
      "currentProgress": "50.000000",
      "isClaimed": false,
      "isClaimable": true,
      "meetsVip": true
    },
    {
      "id": 2,
      "category": "bet",
      "periodType": "daily",
      "tier": 1,
      "threshold": "100.000000",
      "rewardAmount": "1.000000",
      "vipRequired": 3,
      "turnoverMultiplier": "5.00",
      "enabled": 1,
      "siteCode": "C9",
      "currentProgress": "75.000000",
      "isClaimed": false,
      "isClaimable": false,
      "meetsVip": false
    }
  ]
}

回傳欄位說明

欄位型別說明
idnumber任務 ID
categorystring任務類別(deposit / bet
periodTypestring週期類型(daily / weekly / monthly
tiernumber階級(1~5,同類同週期的不同門檻等級)
thresholdstring門檻金額 (USD)
rewardAmountstring獎勵金額 (USD)
vipRequirednumberVIP 最低等級要求(0=無要求)
turnoverMultiplierstring打碼量倍數(領取後需完成 獎勵 x 倍數 的投注)
currentProgressstring | null當前累計進度 (USD)。未登入時為 null
isClaimedboolean本期是否已領取
isClaimableboolean是否可領取(!isClaimed && 進度 >= 門檻 && VIP 等級達標
meetsVipbooleanVIP 等級是否符合要求

業務邏輯說明

  • 任務列表(mission 表)使用 Redis 快取,key: cache:mission:list,TTL 1 小時
  • 進度查詢為即時資料(不快取),批次查詢 mission-progressmission-claim
  • isClaimable 的判斷條件:!isClaimed && currentProgress >= threshold && meetsVip
  • 未登入時:currentProgress = nullisClaimed = falseisClaimable = falsemeetsVip = true
  • 任務按 category ASC, periodType ASC, tier ASC 排序

前端注意事項 / UI 設計提示

  • 任務頁面可分 Tab 展示:存款任務 / 投注任務
  • 每個任務卡片顯示:門檻、獎勵、進度條(currentProgress / threshold
  • isClaimable = true 時高亮顯示「領取」按鈕
  • isClaimed = true 時顯示「已領取」標記
  • meetsVip = false 時顯示「需 VIP x 級」提示
  • 未登入時顯示「登入後查看進度」提示

GET /api/mission/claims — 取得任務領取紀錄

功能說明

查詢當前登入用戶的任務領取紀錄,支援分頁、打碼量狀態篩選(pending=未完成打碼 / completed=已完成)、日期範圍篩選。回傳結果包含任務類別、週期、獎勵金額、打碼量進度等資訊。前台用於「我的任務獎勵紀錄」頁面。

認證需求:需要登入(JwtAuthGuard,前台用戶 JWT Bearer Token)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer TokenBearer eyJhbG...
Querypagestring頁碼(預設 1)"1"
QuerypageSizestring每頁筆數(預設 10,最大 50)"10"
Querytabstring篩選:pending=未完成打碼、completed=已完成"pending"
QuerystartDatestring開始日期 (YYYY-MM-DD)"2026-01-01"
QueryendDatestring結束日期 (YYYY-MM-DD)"2026-12-31"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "missionId": 3,
        "category": "deposit",
        "periodType": "daily",
        "tier": 1,
        "periodKey": "2026-02-28",
        "rewardAmount": "0.300000",
        "requiredTurnover": "0.900000",
        "completedTurnover": "0.500000",
        "turnoverCompleted": 0,
        "claimedAt": "2026-02-28T08:30:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 5,
      "totalPages": 1
    }
  }
}

回傳欄位說明

欄位型別說明
idnumber領取紀錄 ID
missionIdnumber任務定義 ID
categorystring任務類別
periodTypestring週期類型
tiernumber階級
periodKeystring領取時的週期 key
rewardAmountstring實際發放金額 (USD)
requiredTurnoverstring需完成的打碼量 (USD)
completedTurnoverstring已完成的打碼量 (USD)
turnoverCompletednumber打碼量是否已完成(0/1)
claimedAtstring領取時間 (ISO 8601)

業務邏輯說明

  • tab 篩選:pendingturnoverCompleted = 0completedturnoverCompleted = 1
  • 打碼量進度:領取獎勵後需完成 rewardAmount x turnoverMultiplier 的投注額
  • 投注結算後會自動呼叫 MissionService.updateMissionTurnover() 更新打碼量進度
  • 結果按 claimedAt 降序排列

前端注意事項 / UI 設計提示

  • 提供 Tab 切換:全部 / 未完成打碼 / 已完成
  • 每筆紀錄顯示打碼量進度條(completedTurnover / requiredTurnover
  • turnoverCompleted = 0 且有 requiredTurnover > 0 時顯示打碼量進度

POST /api/mission/:id/claim — 領取任務獎勵

功能說明

領取指定任務的獎勵。系統會依序檢查:任務是否存在且啟用、本期是否已領取、VIP 等級是否達標、進度是否達到門檻。所有檢查通過後,獎勵金額直接加到用戶餘額,同時寫入領取紀錄(含打碼量要求)。回傳新餘額讓前端即時更新顯示。

認證需求:需要登入(JwtAuthGuard,前台用戶 JWT Bearer Token)

Request

類型名稱型別必填說明範例
HeaderAuthorizationstringBearer TokenBearer eyJhbG...
URL Paramidnumber任務 ID1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "rewardAmount": "0.300000",
    "requiredTurnover": "0.900000",
    "newBalance": "150.300000"
  },
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/mission/1/claim"
}

回傳欄位說明

欄位型別說明
rewardAmountstring發放的獎勵金額 (USD)
requiredTurnoverstring需完成的打碼量 (USD)。若 turnoverMultiplier = 0 則為 "0.000000"
newBalancestring領取後的最新餘額 (USD)

錯誤碼

錯誤碼說明
3001任務不存在或未啟用
3002本期已領取過此任務獎勵
3003VIP 等級未達任務要求
3004累計進度未達任務門檻

業務邏輯說明

  • 領取流程:
    1. 查詢任務定義(enabled = 1
    2. 檢查本期是否已領取(mission-claim 表,unique: siteCode + missionId + userId + periodKey
    3. 檢查 VIP 等級(vipRequired > 0 時才檢查)
    4. 檢查進度(deposit 類看 depositTotalbet 類看 betTotal
    5. 發放獎勵:balance += rewardAmount
    6. 計算打碼量:requiredTurnover = rewardAmount x turnoverMultiplier
    7. 寫入 mission-claim 紀錄
  • turnoverMultiplier = 0,則無打碼量要求,turnoverCompleted 直接設為 1
  • periodKeyperiodType 決定:daily2026-03-01weekly2026-W09monthly2026-03

前端注意事項 / UI 設計提示

  • 領取成功後:
    1. 更新頁面上的餘額顯示(使用 newBalance
    2. 刷新任務列表(將該任務標記為已領取)
    3. requiredTurnover > 0,顯示打碼量提示
  • 領取失敗時根據錯誤碼顯示對應的錯誤訊息
  • 「領取」按鈕應在點擊後立即禁用,避免重複提交

LiveSports 即時賽事模組

路由前綴:/api/live-sports

即時賽事模組透過 API-Football 第三方服務取得進行中或即將開始的足球賽事資訊,供前台首頁的賽事 Banner 使用。資料每 30 分鐘由 Cron 排程自動更新至 Redis 快取。支援多語系的狀態標籤和賽事類型名稱。

資料來源

  • API-Football (v3.football.api-sports.io):提供即時足球賽事、聯賽、球隊、賠率等資料
  • 快取 TTL:35 分鐘(略長於 30 分鐘的更新週期,避免快取提前過期)
  • API Key 配置:環境變數 LIVE_SPORTS_API_KEY 或站點設定 gameProviders.liveSports.apiKey

自動排程

排程時間說明
賽事快取更新每 30 分鐘拉取 live 賽事 + 今日未開始賽事,快取至 Redis

GET /api/live-sports — 取得即時體育賽事 Banner

功能說明

取得進行中或即將開始的足球賽事列表,供前台首頁橫向滾動 Banner 使用。資料每 30 分鐘自動更新。回傳內容包含賽事基本資訊、聯賽資訊、主客隊資訊(含比分和隊徽 URL)、賠率,以及依據語系翻譯的狀態標籤。此端點為公開端點,不需要登入。

認證需求:無(公開端點)

Request

類型名稱型別必填說明範例
Headerlocalesstring語系(影響狀態標籤翻譯)"zh-TW" / "en-US"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "fixtureId": 1234567,
      "kickoffAt": "2026-02-24T20:00:00+00:00",
      "sportLabel": "足球",
      "isLive": false,
      "status": {
        "short": "NS",
        "long": "Not Started",
        "label": "尚未開始",
        "elapsed": null
      },
      "league": {
        "id": 2,
        "name": "UEFA Champions League",
        "country": "World",
        "logo": "https://media.api-sports.io/football/leagues/2.png",
        "round": "Quarter-finals"
      },
      "home": {
        "id": 530,
        "name": "Atletico Madrid",
        "logo": "https://media.api-sports.io/football/teams/530.png",
        "score": null
      },
      "away": {
        "id": 569,
        "name": "Club Brugge",
        "logo": "https://media.api-sports.io/football/teams/569.png",
        "score": null
      },
      "odds": {
        "home": "1.43",
        "draw": "5.20",
        "away": "6.80",
        "extraCount": 5
      }
    },
    {
      "fixtureId": 1234568,
      "kickoffAt": "2026-02-24T18:00:00+00:00",
      "sportLabel": "足球",
      "isLive": true,
      "status": {
        "short": "1H",
        "long": "First Half",
        "label": "上半場",
        "elapsed": 35
      },
      "league": {
        "id": 39,
        "name": "Premier League",
        "country": "England",
        "logo": "https://media.api-sports.io/football/leagues/39.png",
        "round": "Regular Season - 25"
      },
      "home": {
        "id": 33,
        "name": "Manchester United",
        "logo": "https://media.api-sports.io/football/teams/33.png",
        "score": 1
      },
      "away": {
        "id": 40,
        "name": "Liverpool",
        "logo": "https://media.api-sports.io/football/teams/40.png",
        "score": 2
      },
      "odds": {
        "home": "2.10",
        "draw": "3.40",
        "away": "3.20",
        "extraCount": 5
      }
    }
  ]
}

回傳欄位說明

欄位型別說明
fixtureIdnumber賽事 ID(API-Football)
kickoffAtstring開球時間 (ISO 8601)
sportLabelstring運動類型標籤(依語系翻譯,如「足球」/「Football」)
isLiveboolean是否為進行中的賽事
status.shortstring狀態簡碼(NS/1H/2H/HT/ET/BT/P/FT 等)
status.longstring狀態全名(英文)
status.labelstring狀態標籤(依語系翻譯)
status.elapsednumber | null已進行分鐘數(未開始或已結束時為 null)
league.idnumber聯賽 ID
league.namestring聯賽名稱
league.countrystring國家
league.logostring聯賽 Logo URL
league.roundstring | null輪次資訊
home.idnumber主隊 ID
home.namestring主隊名稱
home.logostring主隊隊徽 URL
home.scorenumber | null主隊比分(未開始時為 null)
away.idnumber客隊 ID
away.namestring客隊名稱
away.logostring客隊隊徽 URL
away.scorenumber | null客隊比分(未開始時為 null)
odds.homestring | null主勝賠率
odds.drawstring | null平手賠率
odds.awaystring | null客勝賠率
odds.extraCountnumber額外投注選項數量

「進行中」狀態碼

狀態碼說明
1H上半場 (First Half)
2H下半場 (Second Half)
HT中場休息 (Half Time)
ET加時賽 (Extra Time)
BT加時賽中場 (Break Time)
P點球大戰 (Penalty)

業務邏輯說明

  • 資料來源為 Redis 快取,由 Cron 排程每 30 分鐘更新
  • 更新流程:
    1. 拉取 live 賽事(/fixtures?live=all
    2. 拉取今日未開始賽事(/fixtures?date={today}&status=NS
    3. 去重合併,最多取 20 場
    4. 為前 5 場賽事拉取賠率(/odds?fixture={id}&bookmaker=1&bet=1
    5. 建構語言無關的快取資料存入 Redis
  • 排序規則:進行中的賽事排在前面,然後按開球時間升序
  • API 配額管理:剩餘配額 < 10 時跳過賠率查詢
  • Cold start:快取為空時會同步觸發一次更新
  • 狀態標籤(status.label)使用 i18n 翻譯(i18n key: liveSports.status.{short}

前端注意事項 / UI 設計提示

  • 首頁設計為橫向滾動 Banner 卡片
  • 每張卡片顯示:聯賽 Logo + 名稱、主客隊 Logo + 名稱 + 比分、狀態標籤、賠率
  • isLive = true 的賽事加上閃爍的「LIVE」標章
  • score = null 時顯示 -0
  • odds = null 時隱藏賠率區塊
  • elapsed 不為 null 時顯示進行分鐘數(如 35'
  • 建議每 30 秒 ~ 1 分鐘自動刷新一次(配合後端快取週期)

模組間關聯圖

投注結算(Game S2S 回調 / 模擬投注)

  ├──→ VipService.recalculateUserVip()    → VIP 等級重算(只升不降)
  │     └──→ Alliance VIP 里程碑獎勵檢查(若升級且有代理)

  ├──→ PromoService.updatePromoTurnover() → 優惠打碼量累計

  ├──→ MissionService.updateBetProgress() → 任務投注進度更新

  └──→ MissionService.updateMissionTurnover() → 任務領取紀錄打碼量更新

存款確認
  └──→ MissionService.updateDepositProgress() → 任務存款進度更新

遊戲啟動/模擬成功
  └──→ GameService.recordGamePlay()       → 遊玩紀錄 UPSERT (game-play-log)

附錄:統一回應格式

所有端點回應均遵循以下格式:

成功

json
{
  "code": 200,
  "message": "ok",
  "result": "...",
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/..."
}

業務錯誤

json
{
  "code": 2001,
  "message": "帳號或密碼錯誤",
  "timestamp": "2026-03-01T10:00:00.000Z",
  "path": "/api/..."
}

未授權

json
{
  "code": 401,
  "message": "Unauthorized"
}

Affiliate 代理推廣模組

代理推廣系統採三層代理結構(上線 → 下線,最多 3 層),佣金依據下線投注淨損失 (net loss) 按比例結算。佣金結算分為週結(每週一 03:00)與日結(每日 03:30),結算後進入待審核狀態,需後台人工審核後才入帳到代理餘額。代理提款支援三階段流程:pending → approved → completed。

所有金額使用 USD decimal(18,6) 精度,截斷規則為無條件捨去 (Math.floor(value * 1e6) / 1e6)。

下線帳號在代理端點中會透過 maskAccount() 遮蔽中間字元以保護隱私(例如 us***01)。


公開端點 (Public — 無需認證)


1. POST /api/affiliate/track-click

功能說明

記錄推廣連結的點擊事件。當用戶透過帶有 refCode 的推廣連結進入前台時,前端呼叫此端點記錄點擊資訊,用於後續追蹤轉換率。支援聯盟推廣碼(alliance-referral-code 表)與主代理碼(auth-user.agentCode)兩種查找方式。

認證需求:無

Request

欄位位置型別必填說明
refCodebodystring推廣碼(如 AGT123 或自訂推廣碼)
referrerbodystring來源頁面 URL
json
{
  "refCode": "AGT123",
  "referrer": "https://google.com"
}

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "tracked": true
  }
}

錯誤碼

錯誤碼說明
2001推廣碼不存在(查詢 alliance-referral-codeauth-user.agentCode 均無結果)

業務邏輯

  • 透過 AllianceService.resolveRefCode() 解析推廣碼,先查 alliance-referral-code 表,再查 auth-user.agentCode 欄位
  • 記錄到 affiliate-click 表,含 IP、User-Agent、referrer
  • 點擊紀錄用於代理儀表板的點擊統計及轉換率追蹤
  • 用戶註冊時會透過 bindAgentOnRegister() 將點擊標記為已轉換(converted = 1

2. GET /api/affiliate/alliance-info

功能說明

取得聯盟計劃的公開資訊,包含佣金比例表、代理階層列表、VIP 里程碑獎勵。此端點供前台推廣展示頁使用,讓潛在代理了解佣金制度。回傳結果會快取 1 小時(Redis)。

認證需求:無

Request

無參數。

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "commissionRates": [
      {
        "agentTier": "bronze",
        "agentLevel": 1,
        "gameType": "slot",
        "commissionRate": "0.28"
      },
      {
        "agentTier": "bronze",
        "agentLevel": 1,
        "gameType": null,
        "commissionRate": "0.25"
      }
    ],
    "agentTiers": [
      {
        "tierCode": "bronze",
        "tierName": "Bronze Agent",
        "minTotalEarned": "0",
        "minActiveMembers": 0
      },
      {
        "tierCode": "silver",
        "tierName": "Silver Agent",
        "minTotalEarned": "500",
        "minActiveMembers": 5
      },
      {
        "tierCode": "gold",
        "tierName": "Gold Agent",
        "minTotalEarned": "5000",
        "minActiveMembers": 20
      },
      {
        "tierCode": "platinum",
        "tierName": "Platinum Agent",
        "minTotalEarned": "50000",
        "minActiveMembers": 50
      }
    ],
    "vipMilestones": [
      {
        "vipLevel": 3,
        "bonusAmount": "5",
        "description": "下線達 VIP 3 獎勵"
      },
      {
        "vipLevel": 5,
        "bonusAmount": "20",
        "description": "下線達 VIP 5 獎勵"
      }
    ]
  }
}

業務邏輯

  • 快取 key:cache:alliance:public-info,TTL 1 小時
  • gameTypenull 時代表該階層的預設佣金比例(萬用 wildcard)
  • commissionRate 為百分比值(如 0.25 表示 0.25%)
  • 佣金比例按 agentTier → agentLevel → gameType 排序
  • 任何後台 CRUD 操作(新增/更新/刪除佣金比例、階層、里程碑)都會清除此快取

代理端點 (Agent — 需前台用戶登入)

以下端點皆使用 JwtAuthGuard,需在 Request Header 中帶入 Authorization: Bearer <token>


3. POST /api/affiliate/apply

功能說明

前台用戶申請成為代理。可自訂推廣碼或由系統自動產生。申請成功後會初始化佣金餘額記錄(affiliate-balance 表),並取得專屬推廣碼。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
agentCodebodystring自訂推廣碼(3-20 字元),不填則自動產生
json
{
  "agentCode": "MYCHANNEL"
}

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "agentCode": "MYCHANNEL"
  }
}

錯誤碼

錯誤碼說明
2001您已是代理(agentCode 欄位已有值)
2002推廣碼已被使用(自訂的推廣碼重複)

業務邏輯

  • 自動產生的推廣碼格式為 AGT + 6 位隨機英數字(排除易混淆字元 0OIL1
  • 若自動產生的推廣碼碰撞,會重新產生一次
  • 成功後自動建立 affiliate-balance 記錄(初始餘額為 0)

4. GET /api/affiliate/tour-status

功能說明

取得代理導覽的顯示狀態。用於判斷前台是否需要顯示代理推廣導覽彈窗,引導用戶成為代理。已是代理的用戶不會顯示導覽。跳過導覽後,根據站點配置的間隔時間(預設 7 天)後再次顯示。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "shouldShow": true
  }
}

業務邏輯

  • 已是代理(agentCode 不為空) → shouldShow: false
  • 從未跳過導覽(agentTourDismissedAt 為空) → shouldShow: true
  • 站點 agentTourEnabled 設為 0 → shouldShow: false
  • 已跳過但超過間隔時間(agentTourIntervalSec,預設 604800 秒 = 7 天) → shouldShow: true

5. POST /api/affiliate/apply-from-tour

功能說明

從導覽頁面一鍵成為代理並領取 5 USDT 獎勵金。此端點僅限導覽流程觸發,成功後自動產生推廣碼並將 5 USDT 加入用戶主帳戶餘額。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

無 body 參數。

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "agentCode": "AGTXK5N7P",
    "bonusAmount": "5.000000",
    "newBalance": "105.000000"
  }
}

錯誤碼

錯誤碼說明
2001您已是代理

業務邏輯

  • 自動產生推廣碼(格式同 apply 端點)
  • 記錄 agentTourCompletedAt 時間戳
  • 初始化 affiliate-balance 記錄
  • 發放 5 USDT 至用戶主帳戶 balance(原子 SQL 操作 balance + 5.000000
  • 回傳獎勵金額與更新後的帳戶餘額

6. POST /api/affiliate/tour-dismiss

功能說明

跳過代理導覽。記錄跳過時間,在站點設定的間隔時間(預設 7 天)後再次顯示導覽。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

無 body 參數。

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "dismissed": true
  }
}

業務邏輯

  • 更新 agentTourDismissedAt 為當前時間
  • 下次呼叫 GET /tour-status 時會根據間隔時間判斷是否再次顯示

7. GET /api/affiliate/dashboard

功能說明

取得代理儀表板數據總覽。包含代理推廣碼、佣金餘額、三層下線人數統計、本月佣金累計、最近 7 天的點擊/轉換統計。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "agentCode": "AGTXK5N7P",
    "balance": {
      "available": "120.000000",
      "frozen": "30.000000",
      "totalEarned": "500.000000",
      "totalWithdrawn": "350.000000"
    },
    "downline": {
      "level1": 15,
      "level2": 8,
      "level3": 2,
      "total": 25
    },
    "monthCommission": "45.230000",
    "clickStats": [
      {
        "date": "2026-02-28",
        "clicks": "12",
        "conversions": "3"
      },
      {
        "date": "2026-02-27",
        "clicks": "8",
        "conversions": "1"
      }
    ]
  }
}

錯誤碼

錯誤碼說明
2001您不是代理身份(agentCode 為空)

業務邏輯

  • balance.available:可提領佣金餘額
  • balance.frozen:已申請提款但尚未完成的凍結金額
  • balance.totalEarned:歷史累計佣金收入
  • balance.totalWithdrawn:歷史累計已提領金額
  • downline.level1:直屬下線人數
  • downline.level2:二層下線人數(下線的下線)
  • downline.level3:三層下線人數
  • monthCommission:當月累計佣金(從月初到當前)
  • clickStats:最近 7 天每日點擊數與轉換數,按日期倒序排列
  • 下線人數使用單一查詢 + CASE WHEN 統計三層,避免 N+1

功能說明

取得代理的專屬推廣連結。包含推廣碼與完整的註冊連結 URL,用於代理分享推廣使用。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "refCode": "AGTXK5N7P",
    "link": "https://example.com/register?refCode=AGTXK5N7P"
  }
}

錯誤碼

錯誤碼說明
2001您不是代理身份

業務邏輯

  • 前端 URL 優先取自環境變數 FRONTEND_URL
  • 若未設定,fallback 從 site-config.domains 取第一個 hostname 並拼接 https://
  • 推廣連結格式:{frontendUrl}/register?refCode={agentCode}

9. GET /api/affiliate/downline

功能說明

取得代理的下線會員列表,支援依層級(1/2/3)篩選與分頁。下線帳號會以遮蔽方式顯示以保護隱私。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
pagequerynumber頁碼(預設 1)
pageSizequerynumber每頁筆數(預設 10,最大 50)
levelquerynumber層級篩選:1(直屬)、2(二層)、3(三層),預設 1

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 42,
        "account": "us***01",
        "name": "王小明",
        "vipLevel": 5,
        "createdAt": "2026-01-15T08:30:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 25,
      "totalPages": 3
    }
  }
}

錯誤碼

錯誤碼說明
2001您不是代理身份

業務邏輯

  • level=1 查詢 level1AgentId = agentId(直屬下線)
  • level=2 查詢 level2AgentId = agentId(二層下線)
  • level=3 查詢 level3AgentId = agentId(三層下線)
  • 帳號遮蔽規則:長度 <=4 時顯示首字 + ***;否則顯示前 2 字 + *** + 後 2 字
  • 結果按 createdAt 倒序排列

10. GET /api/affiliate/click-stats

功能說明

取得推廣連結的每日點擊與轉換統計。可透過日期範圍篩選特定期間的數據。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
startDatequerystring開始日期 YYYY-MM-DD(如 2026-02-17
endDatequerystring結束日期 YYYY-MM-DD(如 2026-02-23

Response

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "date": "2026-02-23",
      "clicks": "15",
      "conversions": "4"
    },
    {
      "date": "2026-02-22",
      "clicks": "10",
      "conversions": "2"
    }
  ]
}

業務邏輯

  • 按日期分組統計點擊數(clicks)與轉換數(conversions
  • 轉換數 = 透過該推廣碼註冊的用戶數
  • 結果按日期倒序排列
  • 若不帶日期篩選,回傳所有歷史統計

11. GET /api/affiliate/commissions

功能說明

查詢代理的佣金明細列表。每筆佣金記錄對應一筆有效投注的佣金分成,包含投注層級、遊戲類型、佣金比例與金額。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
pagequerynumber頁碼(預設 1)
pageSizequerynumber每頁筆數(預設 10,最大 50)
weekStartquerystring篩選週開始日 YYYY-MM-DD

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "agentId": 5,
        "memberId": 42,
        "betOrderId": 1001,
        "agentLevel": 1,
        "gameType": "slot",
        "netLoss": "50.000000",
        "commissionRate": "0.28",
        "commissionAmount": "0.140000",
        "weekStart": "2026-02-17",
        "weekEnd": "2026-02-23",
        "settlementId": 10,
        "createdAt": "2026-02-24T03:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 150,
      "totalPages": 15
    }
  }
}

業務邏輯

  • agentLevel:佣金來源層級(1=直屬下線、2=二層下線、3=三層下線)
  • netLoss:該筆投注的淨損失金額(USD),佣金僅從淨損失中提取
  • commissionRate:適用的佣金比例(百分比)
  • commissionAmount:實際佣金金額 = netLoss * commissionRate / 100(USD 截斷)
  • weekStart / weekEnd:佣金所屬結算週期
  • 結果按 createdAt 倒序排列

12. GET /api/affiliate/settlements

功能說明

查詢代理的結算紀錄列表。每筆結算記錄代表一個週期(週/日)的佣金彙總,包含三層佣金分項、活躍下線數、總淨損失。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
pagequerynumber頁碼(預設 1)
pageSizequerynumber每頁筆數(預設 10,最大 50)
statusquerystring篩選狀態:pending / approved / rejected

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 10,
        "agentId": 5,
        "weekStart": "2026-02-17",
        "weekEnd": "2026-02-23",
        "activeMemberCount": 8,
        "totalNetLoss": "1200.000000",
        "level1Commission": "3.360000",
        "level2Commission": "1.200000",
        "level3Commission": "0.500000",
        "totalCommission": "5.060000",
        "gameTypeBreakdown": {
          "slot": "2.100000",
          "live": "1.500000",
          "sports": "1.460000"
        },
        "periodType": "weekly",
        "status": "pending",
        "reviewedBy": null,
        "reviewedAt": null,
        "createdAt": "2026-02-24T03:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 12,
      "totalPages": 2
    }
  }
}

業務邏輯

  • status 狀態流轉:pendingapproved(佣金入帳到 available) 或 rejected
  • periodTypeweekly(週結)或 daily(日結)
  • gameTypeBreakdown:按遊戲類型拆分的佣金明細
  • level1Commission / level2Commission / level3Commission:三層佣金分項
  • 結算具備冪等性,同一期間不會重複結算
  • 結果按 weekStart 倒序排列

13. GET /api/affiliate/settlements/:id

功能說明

取得單筆結算的詳情,包含結算摘要與該期間的所有佣金明細列表。用於代理查看特定結算週期的完整資訊。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
idpathnumber結算紀錄 ID

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "settlement": {
      "id": 10,
      "agentId": 5,
      "weekStart": "2026-02-17",
      "weekEnd": "2026-02-23",
      "activeMemberCount": 8,
      "totalNetLoss": "1200.000000",
      "level1Commission": "3.360000",
      "level2Commission": "1.200000",
      "level3Commission": "0.500000",
      "totalCommission": "5.060000",
      "gameTypeBreakdown": {
        "slot": "2.100000",
        "live": "1.500000",
        "sports": "1.460000"
      },
      "periodType": "weekly",
      "status": "approved",
      "reviewedBy": "admin@c9.com",
      "reviewedAt": "2026-02-24T10:30:00.000Z",
      "createdAt": "2026-02-24T03:00:00.000Z"
    },
    "commissions": [
      {
        "id": 1,
        "agentId": 5,
        "memberId": 42,
        "betOrderId": 1001,
        "agentLevel": 1,
        "gameType": "slot",
        "netLoss": "50.000000",
        "commissionRate": "0.28",
        "commissionAmount": "0.140000",
        "weekStart": "2026-02-17",
        "weekEnd": "2026-02-23",
        "settlementId": 10,
        "createdAt": "2026-02-24T03:00:00.000Z"
      }
    ]
  }
}

錯誤碼

錯誤碼說明
2001結算紀錄不存在(或不屬於當前代理)

業務邏輯

  • 查詢條件同時比對 settlementIdagentId,確保代理只能查看自己的結算
  • commissionscreatedAt 倒序排列

14. GET /api/affiliate/balance

功能說明

取得代理的佣金餘額資訊。包含可用餘額、凍結餘額、歷史累計收入與歷史累計提領。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "available": "120.000000",
    "frozen": "30.000000",
    "totalEarned": "500.000000",
    "totalWithdrawn": "350.000000"
  }
}

錯誤碼

錯誤碼說明
2001您不是代理身份

業務邏輯

  • available:可提領佣金餘額(結算審核通過後入帳)
  • frozen:已申請提款但尚未完成的凍結金額
  • totalEarned:歷史累計佣金收入(含 VIP 里程碑獎勵)
  • totalWithdrawn:歷史累計已完成提領的金額
  • 餘額關係:available + frozen + totalWithdrawn = totalEarned + VIP 里程碑獎勵

15. GET /api/affiliate/withdrawals

功能說明

查詢代理的提款申請紀錄列表,支援分頁。顯示每筆提款的金額、方式、狀態及審核資訊。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
pagequerynumber頁碼(預設 1)
pageSizequerynumber每頁筆數(預設 10,最大 50)

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "agentId": 5,
        "amount": "100.000000",
        "method": "bank",
        "bankCardId": 3,
        "cryptoAddressId": null,
        "status": "pending",
        "reviewedBy": null,
        "reviewedAt": null,
        "rejectReason": null,
        "completedAt": null,
        "createdAt": "2026-02-28T10:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 5,
      "totalPages": 1
    }
  }
}

業務邏輯

  • 狀態流轉:pendingapprovedcompleted,或 pendingrejected
  • 結果按 createdAt 倒序排列

16. POST /api/affiliate/withdrawals/request

功能說明

代理發起佣金提款申請。提款金額會從可用餘額 (available) 凍結到凍結餘額 (frozen),等待後台審核。支援銀行轉帳與加密貨幣兩種提款方式。每 24 小時僅允許一筆進行中的提款申請。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
amountbodystring提款金額 (USD),如 "100.000000"
methodbodystring提款方式:bankcrypto
bankCardIdbodynumber銀行卡 ID(method=bank 時必填)
cryptoAddressIdbodynumber加密錢包 ID(method=crypto 時必填)
json
{
  "amount": "100.000000",
  "method": "bank",
  "bankCardId": 3
}

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "agentId": 5,
    "amount": "100.000000",
    "method": "bank",
    "bankCardId": 3,
    "cryptoAddressId": null,
    "status": "pending",
    "createdAt": "2026-02-28T10:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001提款金額須大於零
2002提款方式無效(銀行卡/加密錢包不存在或未啟用)
2003冷卻中(24 小時內已有 pending 或 approved 提款申請)
2004餘額不足(可用餘額 available 小於提款金額)
2005非代理身份

業務邏輯

  • 提款金額經過 USD 截斷處理(truncateUsd
  • 凍結操作為原子 SQL:available - amountfrozen + amount,同時檢查 available >= amount
  • 銀行卡/加密錢包必須屬於當前用戶且狀態為啟用(status = 1
  • 24 小時冷卻:查詢最近 24 小時內是否有 pendingapproved 狀態的提款
  • 提款被拒時,凍結金額會退回到可用餘額

聯盟代理端點 (Alliance Agent — 需前台用戶登入)


17. GET /api/affiliate/referral-codes

功能說明

取得當前代理的所有推廣碼列表。每個代理最多可建立 10 個自訂推廣碼,用於不同推廣渠道的追蹤。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

Response

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "agentId": 5,
      "code": "YOUTUBE2026",
      "label": "YouTube 頻道",
      "clickCount": 120,
      "convertCount": 15,
      "enabled": 1,
      "createdAt": "2026-01-10T08:00:00.000Z"
    },
    {
      "id": 2,
      "agentId": 5,
      "code": "TELEGRAM01",
      "label": "Telegram 群組",
      "clickCount": 80,
      "convertCount": 8,
      "enabled": 1,
      "createdAt": "2026-01-15T10:00:00.000Z"
    }
  ]
}

業務邏輯

  • clickCount:透過此推廣碼的點擊次數
  • convertCount:透過此推廣碼成功註冊的用戶數
  • 結果按 createdAt 倒序排列
  • 推廣碼與主代理碼(agentCode)是獨立的,使用時皆可作為 refCode 追蹤

18. POST /api/affiliate/referral-codes

功能說明

建立新的自訂推廣碼。推廣碼僅允許英數字,長度 3-30 字元,會自動轉為大寫。每個代理最多 10 個推廣碼。推廣碼在全站唯一(包含其他代理的推廣碼與主代理碼)。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
codebodystring推廣碼(3-30 英數字)
labelbodystring渠道標籤(最長 50 字元)
json
{
  "code": "YOUTUBE2026",
  "label": "YouTube 頻道"
}

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 3,
    "agentId": 5,
    "code": "YOUTUBE2026",
    "label": "YouTube 頻道",
    "clickCount": 0,
    "convertCount": 0,
    "enabled": 1,
    "createdAt": "2026-02-28T10:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001非代理身份
2002已達上限(推廣碼數量已達 10 個)
2003推廣碼已被使用(與其他推廣碼或主代理碼重複)

業務邏輯

  • 推廣碼自動轉大寫(code.toUpperCase()
  • 唯一性檢查同時查詢 alliance-referral-code 表與 auth-user.agentCode 欄位
  • 只允許英數字(正則:/^[a-zA-Z0-9]+$/
  • 建立時 clickCountconvertCount 初始為 0

19. DELETE /api/affiliate/referral-codes/:id

功能說明

刪除指定的推廣碼。只能刪除自己擁有的推廣碼。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>
idpathnumber推廣碼 ID

Response

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001推廣碼不存在(或不屬於當前代理)

業務邏輯

  • 查詢條件同時比對 idagentId,確保只能刪除自己的推廣碼
  • 刪除後已綁定的下線不受影響(綁定關係記錄在 auth-user 表的 level1/2/3AgentId

20. GET /api/affiliate/vip-milestones

功能說明

查看代理的 VIP 里程碑獎勵進度。顯示所有啟用中的 VIP 里程碑配置,以及每位直屬下線的 VIP 等級與已領取的里程碑獎勵狀態。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "milestones": [
      {
        "vipLevel": 3,
        "bonusAmount": "5",
        "description": "下線達 VIP 3 獎勵"
      },
      {
        "vipLevel": 5,
        "bonusAmount": "20",
        "description": "下線達 VIP 5 獎勵"
      }
    ],
    "members": [
      {
        "memberId": 42,
        "account": "us***01",
        "vipLevel": 5,
        "claimed": [
          { "vipLevel": 3, "bonusAmount": "5", "claimed": true },
          { "vipLevel": 5, "bonusAmount": "20", "claimed": true }
        ]
      },
      {
        "memberId": 43,
        "account": "te***er",
        "vipLevel": 2,
        "claimed": []
      }
    ],
    "totalBonusEarned": "25.000000"
  }
}

業務邏輯

  • milestones:所有啟用中的 VIP 里程碑配置,按 vipLevel 升序
  • members:直屬下線(level1AgentId = agentId)的 VIP 等級與領取狀態
  • claimed:每位下線已觸發且達標的里程碑列表
  • claimed.claimedtrue 表示已發放獎勵,false 理論上不會出現(達標即發放)
  • totalBonusEarned:歷史累計從 VIP 里程碑獲得的獎勵總額
  • VIP 里程碑獎勵在下線升級 VIP 時由 VipService 自動觸發 checkAndAwardMilestone()
  • 獎勵直接入帳到代理的 affiliate-balance.available
  • 使用唯一約束防止重複發放(agentId + memberId + vipLevel

21. GET /api/affiliate/tier-info

功能說明

查看代理的階層資訊。顯示當前階層、升級所需條件(下一階層的最低累計收入與最低活躍下線數)、所有階層配置。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

欄位位置型別必填說明
AuthorizationheaderstringBearer <JWT token>

Response

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "currentTier": {
      "tierCode": "silver",
      "tierName": "Silver Agent"
    },
    "totalEarned": "800.000000",
    "activeMembers": 12,
    "nextTier": {
      "tierCode": "gold",
      "tierName": "Gold Agent",
      "minTotalEarned": "5000",
      "minActiveMembers": 20
    },
    "allTiers": [
      {
        "tierCode": "bronze",
        "tierName": "Bronze Agent",
        "minTotalEarned": "0",
        "minActiveMembers": 0
      },
      {
        "tierCode": "silver",
        "tierName": "Silver Agent",
        "minTotalEarned": "500",
        "minActiveMembers": 5
      },
      {
        "tierCode": "gold",
        "tierName": "Gold Agent",
        "minTotalEarned": "5000",
        "minActiveMembers": 20
      },
      {
        "tierCode": "platinum",
        "tierName": "Platinum Agent",
        "minTotalEarned": "50000",
        "minActiveMembers": 50
      }
    ]
  }
}

錯誤碼

錯誤碼說明
2001非代理身份

業務邏輯

  • currentTier:當前代理階層(預設 bronze
  • totalEarned:歷史累計收入(從 affiliate-balance.totalEarned
  • activeMembers:直屬下線人數(level1AgentId = agentId 的用戶數)
  • nextTier:下一階層的升級條件,已是最高階層時為 null
  • 階層由低到高:bronzesilvergoldplatinum
  • 代理階層影響佣金比例(不同階層 x 不同層級 x 不同遊戲類型有不同的佣金比例)
  • allTierssortOrder 升序排列

代理推廣系統 API 文件 — Admin 與 Alliance 管理端點

模組路徑:/api/affiliate/admin/* Controller:src/modules/affiliate/affiliate.controller.ts 認證:所有端點均需 AdminJwtAuthGuard(後台管理員 JWT)


一、Admin 代理管理端點

1. GET /affiliate/admin/agents — 代理列表

取得所有代理帳號的列表,支援關鍵字搜尋、代理階層篩選、註冊日期範圍篩選、直屬下線人數範圍篩選。透過 @AdminSiteCode() 裝飾器自動讀取站點代碼進行多站點過濾。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Headerx-site-codestring站點代碼(由 @AdminSiteCode() 讀取)
Querypagenumber頁碼(預設 1)
QuerypageSizenumber每頁筆數(預設 20,最大 50)
Querykeywordstring搜尋關鍵字(比對 account / agentCode / email)
QueryagentTierstring代理階層篩選:bronze / silver / gold / platinum
QuerystartDatestring註冊起始日 YYYY-MM-DD
QueryendDatestring註冊結束日 YYYY-MM-DD
QuerymemberCountRangestring直屬下線人數範圍:0-10 / 10-50 / 50-100 / 100+

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "account": "agent001",
        "email": "agent@example.com",
        "name": "張三",
        "agentCode": "AGT7K9N2X",
        "agentTier": "silver",
        "createdAt": "2026-01-15T08:30:00.000Z",
        "available": "120.500000",
        "frozen": "30.000000",
        "totalEarned": "500.000000",
        "totalWithdrawn": "350.000000",
        "directMemberCount": "25",
        "siteCode": "C9"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}

業務邏輯

  • 透過 LEFT JOIN affiliate-balance 表取得佣金餘額和代理階層
  • 透過子查詢計算直屬下線人數(level1AgentId 聚合)
  • siteCode 篩選作用於 auth-user.siteCode 欄位
  • 排序:建立時間 DESC

2. POST /affiliate/admin/create-agent — 新增代理

手動將一個前台用戶設定為代理身份。可自訂推廣碼或由系統自動產生(格式:AGT + 6 位隨機英數字)。建立時會同時初始化佣金餘額記錄。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
BodyuserIdnumber前台用戶 ID
BodyagentCodestring自訂推廣碼(3~20 字元,不填則自動產生)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "agentCode": "AGT7K9N2X"
  }
}

錯誤碼

錯誤碼說明
2001用戶不存在
2002推廣碼已被使用
2003該用戶已是代理

業務邏輯

  • 寫入 auth-user.agentCode 欄位
  • 自動在 affiliate-balance 表建立餘額記錄(初始金額皆為 0)
  • 自動產生的推廣碼排除容易混淆的字元(O/0/I/1)

3. GET /affiliate/admin/settlements — 佣金結算列表

查詢全部代理的佣金結算紀錄(不限特定代理),支援按結算狀態和結算週期篩選。管理員可從此列表進入審核流程。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Querypagenumber頁碼(預設 1)
QuerypageSizenumber每頁筆數(預設 10,最大 50)
Querystatusstring結算狀態:pending / pendingReview / approved / rejected
QueryweekStartstring結算週期起始日 YYYY-MM-DD

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "agentId": 5,
        "weekStart": "2026-02-17",
        "weekEnd": "2026-02-23",
        "activeMemberCount": 12,
        "totalNetLoss": "5000.000000",
        "level1Commission": "150.000000",
        "level2Commission": "30.000000",
        "level3Commission": "10.000000",
        "totalCommission": "190.000000",
        "gameTypeBreakdown": { "slot": "100.000000", "live": "50.000000", "sports": "40.000000" },
        "periodType": "weekly",
        "status": "pending",
        "riskFlagged": 0,
        "riskReasons": null,
        "reviewedBy": null,
        "reviewedAt": null,
        "siteCode": "C9",
        "createdAt": "2026-02-24T03:00:15.000Z",
        "updatedAt": "2026-02-24T03:00:15.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 25,
      "totalPages": 3
    }
  }
}

業務邏輯

  • periodType 區分週結 (weekly) 和日結 (daily)
  • 結算狀態流程:pending → (pendingReview,若有風控標記) → approved / rejected
  • riskFlagged: 1 表示該結算紀錄觸發風控檢測(可查看對應風控紀錄)
  • gameTypeBreakdown 為 JSON 物件,key 為遊戲類型名稱,value 為該類型的佣金總額
  • 排序:結算週期起始日 DESC

4. POST /affiliate/admin/settlements/:id/review — 結算審核

對指定的佣金結算紀錄進行審核,可核准或拒絕。核准時佣金自動入帳到代理的可用餘額(available)和累計收益(totalEarned)。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Pathidnumber結算紀錄 ID
Bodyactionstring審核動作:approved / rejected
Bodyreasonstring拒絕原因(僅 rejected 時使用)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "success": true
  }
}

錯誤碼

錯誤碼說明
2001結算紀錄不存在
2002該紀錄已審核完畢(不可重複審核)

業務邏輯

  • 核准 (approved)
    1. 更新結算狀態為 approved,記錄審核人帳號和審核時間
    2. totalCommission 金額加入代理的 affiliate-balance.availabletotalEarned
    3. 金額使用 truncateUsd() 無條件捨去至 6 位小數
  • 拒絕 (rejected)
    1. 更新結算狀態為 rejected,記錄審核人帳號和審核時間
    2. 佣金不入帳
  • 審核人帳號從 req.user.email 取得

5. GET /affiliate/admin/settlements/:id/risk-logs — 結算風控紀錄

查詢指定結算紀錄的風控檢測結果。風控在每次結算時自動執行,檢測代理與下線之間是否存在可疑的 IP 或裝置關聯。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Pathidnumber結算紀錄 ID

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "settlementId": 10,
      "agentId": 5,
      "memberId": null,
      "riskType": "same_ip",
      "detail": "{\"ip\":\"192.168.1.100\",\"userIds\":[12,15,18]}",
      "siteCode": "C9",
      "createdAt": "2026-02-24T03:00:20.000Z"
    },
    {
      "id": 2,
      "settlementId": 10,
      "agentId": 5,
      "memberId": 12,
      "riskType": "agent_member_ip",
      "detail": "{\"ip\":\"192.168.1.100\",\"memberId\":12}",
      "siteCode": "C9",
      "createdAt": "2026-02-24T03:00:20.000Z"
    }
  ]
}

風控類型 (riskType)

類型說明
same_ip多位下線會員共用相同 IP 登入(>=2 人)
same_device多位下線會員共用相同裝置指紋(>=2 人)
agent_member_ip代理本人與下線會員使用相同 IP 登入
agent_member_device代理本人與下線會員使用相同裝置指紋

業務邏輯

  • 風控檢測範圍為結算週期(weekStart ~ weekEnd)內的登入紀錄
  • detail 為 JSON 字串,內含可疑的 IP/裝置和相關用戶 ID
  • 觸發風控時,結算狀態會自動改為 pendingReviewriskFlagged 設為 1
  • 排序:建立時間 DESC

6. GET /affiliate/admin/withdrawals — 代理提款列表

查詢全部代理的提款申請紀錄,支援按狀態篩選。管理員可從此列表進入提款審核流程。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Querypagenumber頁碼(預設 1)
QuerypageSizenumber每頁筆數(預設 10,最大 50)
Querystatusstring提款狀態:pending / approved / rejected / completed

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "agentId": 5,
        "amount": "100.000000",
        "method": "bank",
        "bankCardId": 3,
        "cryptoAddressId": null,
        "status": "pending",
        "rejectReason": null,
        "reviewedBy": null,
        "reviewedAt": null,
        "completedAt": null,
        "siteCode": "C9",
        "createdAt": "2026-02-25T10:30:00.000Z",
        "updatedAt": "2026-02-25T10:30:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 5,
      "totalPages": 1
    }
  }
}

業務邏輯

  • 提款狀態流程:pendingapprovedcompleted(或 pendingrejected
  • methodbank(銀行卡)或 crypto(加密貨幣),對應 bankCardIdcryptoAddressId
  • 排序:建立時間 DESC

7. POST /affiliate/admin/withdrawals/:id/review — 提款審核

對指定的代理提款申請進行審核。拒絕時凍結金額自動退回代理的可用餘額。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Pathidnumber提款紀錄 ID
Bodyactionstring審核動作:approved / rejected
BodyrejectReasonstring拒絕原因(僅 rejected 時使用)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "success": true
  }
}

錯誤碼

錯誤碼說明
2001提款紀錄不存在
2002該紀錄已審核完畢(狀態須為 pending

業務邏輯

  • 核准 (approved)
    1. 更新提款狀態為 approved,記錄審核人帳號和時間
    2. 凍結金額保持不變(等待人工出款確認後才轉入 totalWithdrawn
  • 拒絕 (rejected)
    1. 更新提款狀態為 rejected,記錄拒絕原因
    2. 凍結金額退回:frozen -= amount, available += amount
  • 僅接受 status === 'pending' 的提款紀錄
  • 審核人帳號從 req.user.email 取得

8. POST /affiliate/admin/withdrawals/:id/complete — 提款完成

標記已核准的提款為完成狀態(代表實際出款已完成)。凍結金額轉入累計提領金額。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Pathidnumber提款紀錄 ID

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "success": true
  }
}

錯誤碼

錯誤碼說明
2001提款紀錄不存在
2002狀態須為 approved(只有已核准的才可完成)

業務邏輯

  • 餘額變動:frozen -= amount, totalWithdrawn += amount
  • 更新提款狀態為 completed,記錄完成時間
  • 三階段提款流程完整:pendingapproved(審核通過)→ completed(實際出款完成)

9. POST /affiliate/admin/bind — 手動綁定/解綁/轉移

管理員手動操作會員與代理之間的綁定關係。支援三種動作:綁定、解綁、轉移。所有操作都會寫入審計紀錄。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
BodymemberIdnumber會員 user ID
BodyagentIdnumber代理 user ID
Bodyactionstring動作:admin_bind / admin_unbind / admin_transfer
Bodyremarkstring備註/原因

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "success": true
  }
}

錯誤碼

錯誤碼說明
2001會員不存在
2002代理不存在(或該用戶不是代理)

業務邏輯

  • admin_bind / admin_transfer
    • 設定會員的三層代理鏈:level1AgentId = agent.id, level2AgentId = agent.level1AgentId, level3AgentId = agent.level2AgentId
    • 轉移等同於覆蓋原有綁定
  • admin_unbind
    • 清除會員的所有代理綁定:level1AgentId = null, level2AgentId = null, level3AgentId = null
  • 操作者帳號(operatorAccount)從 req.user.email 取得
  • 寫入 affiliate-bind-log 審計紀錄

10. GET /affiliate/admin/bind-logs — 綁定審計紀錄

查詢代理與會員之間的綁定操作歷史紀錄,包含註冊自動綁定和管理員手動操作。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Querypagenumber頁碼(預設 1)
QuerypageSizenumber每頁筆數(預設 20,最大 50)
QuerymemberIdnumber篩選特定會員
QueryagentIdnumber篩選特定代理

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "memberId": 10,
        "agentId": 5,
        "refCode": "AGT7K9N2X",
        "action": "register_bind",
        "ip": "203.0.113.50",
        "device": "Mozilla/5.0...",
        "operatorAccount": null,
        "remark": null,
        "siteCode": "C9",
        "createdAt": "2026-01-20T14:30:00.000Z"
      },
      {
        "id": 2,
        "memberId": 10,
        "agentId": 8,
        "refCode": "AGTXYZ123",
        "action": "admin_transfer",
        "ip": null,
        "device": null,
        "operatorAccount": "admin@example.com",
        "remark": "用戶要求轉移至新代理",
        "siteCode": "C9",
        "createdAt": "2026-02-10T09:15:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "total": 2,
      "totalPages": 1
    }
  }
}

綁定動作類型 (action)

動作說明
register_bind用戶註冊時透過推廣碼自動綁定
admin_bind管理員手動綁定
admin_unbind管理員手動解綁
admin_transfer管理員手動轉移至其他代理

11. POST /affiliate/admin/set-agent-tier — 設定代理等級

手動調整指定代理的階層等級。會直接更新 affiliate-balance.agentTier 欄位,影響後續佣金計算所使用的佣金費率。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
BodyagentIdnumber代理 user ID
BodytierCodestring階層碼:bronze / silver / gold / platinum

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "agentId": 5,
    "agentTier": "silver"
  }
}

錯誤碼

錯誤碼說明
2001代理不存在(在 affiliate-balance 表中找不到)
2002無效的階層碼(在 alliance-agent-tier 表中找不到)

業務邏輯

  • 代理階層影響佣金費率:不同階層在 alliance-commission-rate 表中有不同的 commissionRate
  • 手動設定會覆蓋自動升級結果
  • 此操作不需要符合自動升級門檻(minTotalEarned / minActiveMembers

12. POST /affiliate/admin/trigger-settlement — 手動觸發週結算

手動觸發一次佣金週結算(等同 Cron Job 每週一 03:00 執行的邏輯)。主要用於測試或補結算。結算範圍為上一個完整週(上週一至上週日)。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "agentsProcessed": 15,
    "totalCommission": "2500.000000",
    "flaggedCount": 2
  }
}

業務邏輯

  • 冪等保證:同一個 weekStart + periodType 不會重複結算
  • 結算流程:
    1. 載入佣金比例 (buildRateMap())
    2. 查詢該期間所有有效注單(status = 'valid'
    3. 依會員的三層代理鏈逐層計算佣金(基於下線淨輸 netLoss × 佣金比例)
    4. 佣金比例查詢優先順序:{tier}-{level}-{gameType}{tier}-{level}-*(wildcard fallback)
    5. 建立 affiliate-settlementaffiliate-commission 記錄
    6. 執行風控檢測(若觸發則標記 riskFlagged,狀態改為 pendingReview
  • agentsProcessed:本次處理的代理數量
  • totalCommission:本次產生的佣金總額(USD)
  • flaggedCount:觸發風控標記的結算數量

13. POST /affiliate/admin/trigger-daily-settlement — 手動觸發日結算

手動觸發一次佣金日結算(等同 Cron Job 每日 03:30 執行的邏輯)。結算範圍為昨日整天。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "agentsProcessed": 10,
    "totalCommission": "800.000000",
    "flaggedCount": 1
  }
}

業務邏輯

  • 邏輯同週結算,但 periodType = 'daily',範圍為前一天(00:00:00 ~ 23:59:59)
  • 冪等保證同週結算(同日不重複結算)
  • 結算完畢後同樣執行風控檢測

二、Alliance 聯盟管理端點

14. GET /affiliate/admin/commission-rates — 佣金比例列表

取得所有佣金比例設定記錄。佣金比例決定不同代理階層、不同層級(直屬/二級/三級)、不同遊戲類型的佣金百分比。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "agentTier": "bronze",
      "agentLevel": 1,
      "gameType": null,
      "commissionRate": "0.25",
      "enabled": 1,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    },
    {
      "id": 2,
      "agentTier": "bronze",
      "agentLevel": 1,
      "gameType": "slot",
      "commissionRate": "0.28",
      "enabled": 1,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ]
}

業務邏輯

  • 排序:agentTier ASCagentLevel ASCgameType ASC
  • gameType = null 表示「全部遊戲類型 fallback」(通配)
  • commissionRate 為百分比(例:"0.25" 表示 25%)
  • Unique constraint:(agentTier, agentLevel, gameType) 組合唯一
  • 結算時查詢優先順序:精確遊戲類型 → 通配 (null)

15. POST /affiliate/admin/commission-rates — 新增/更新佣金比例

新增或更新一條佣金比例設定。當 body 包含 id 時為更新,不包含 id 時為新增。更新後自動清除公開資訊快取。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Bodyidnumber有值 = 更新,無值 = 新增
BodyagentTierstring代理階層:bronze / silver / gold / platinum
BodyagentLevelnumber代理層級:1(直屬) / 2(二級) / 3(三級)
BodygameTypestring遊戲類型(不填 = 全部 fallback):sports / slot / live / lottery / chess / esports / crypto / fish
BodycommissionRatenumber佣金比例(%),範圍 0~100
Bodyenablednumber是否啟用:0 / 1(預設 1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "agentTier": "silver",
    "agentLevel": 1,
    "gameType": "slot",
    "commissionRate": "30",
    "enabled": 1,
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-02-28T15:30:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001更新時指定的 id 不存在

業務邏輯

  • 完整模板包含 4 階層 x 3 層級 x (1 通配 + 8 遊戲類型) = 108 條記錄
  • 新增/更新後自動清除 cache:alliance:public-info 快取
  • commissionRate 存為 decimal(5,2) 型別

16. DELETE /affiliate/admin/commission-rates/:id — 刪除佣金比例

刪除指定的佣金比例設定。刪除後自動清除公開資訊快取。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Pathidnumber佣金比例記錄 ID

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001指定的記錄不存在

17. GET /affiliate/admin/vip-milestones — VIP 里程碑列表

取得所有 VIP 里程碑設定。VIP 里程碑定義當下線會員達到指定 VIP 等級時,代理可獲得的獎勵金額。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "vipLevel": 3,
      "bonusAmount": "5.000000",
      "description": "下線達 VIP 3 獎勵",
      "enabled": 1,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    },
    {
      "id": 2,
      "vipLevel": 5,
      "bonusAmount": "20.000000",
      "description": "下線達 VIP 5 獎勵",
      "enabled": 1,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ]
}

業務邏輯

  • 排序:vipLevel ASC
  • vipLevel 為 unique constraint,每個 VIP 等級只能有一個里程碑
  • 里程碑獎勵由 VipService.recalculateUserVip() 觸發,會員升級後自動呼叫 AllianceService.checkAndAwardMilestone()
  • 獎勵直接入帳到直屬代理(L1)的 affiliate-balance.availabletotalEarned
  • 獎勵冪等:每位代理對同一會員的同一 VIP 等級只發放一次(unique constraint on alliance-vip-milestone-log

18. POST /affiliate/admin/vip-milestones — 新增/更新 VIP 里程碑

新增或更新一條 VIP 里程碑設定。當 body 包含 id 時為更新,不包含 id 時為新增。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Bodyidnumber有值 = 更新,無值 = 新增
BodyvipLevelnumber觸發的 VIP 等級(1~15)
BodybonusAmountnumber獎勵金額(USD),最小 0
Bodydescriptionstring里程碑說明(最長 100 字元)
Bodyenablednumber是否啟用:0 / 1(預設 1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 3,
    "vipLevel": 7,
    "bonusAmount": "50",
    "description": "下線達 VIP 7 獎勵",
    "enabled": 1,
    "createdAt": "2026-02-28T15:30:00.000Z",
    "updatedAt": "2026-02-28T15:30:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001更新時指定的 id 不存在
2002新增時 vipLevel 已存在(unique constraint)

業務邏輯

  • 更新後自動清除 cache:alliance:public-info 快取
  • bonusAmount 存為 decimal(18,6) 型別

19. DELETE /affiliate/admin/vip-milestones/:id — 刪除 VIP 里程碑

刪除指定的 VIP 里程碑設定。刪除後自動清除公開資訊快取。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
PathidnumberVIP 里程碑記錄 ID

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001指定的記錄不存在

20. GET /affiliate/admin/agent-tiers — 代理階層列表

取得所有代理階層設定。代理階層定義不同等級的門檻條件和佣金費率對應。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "tierCode": "bronze",
      "tierName": "Bronze Agent",
      "minTotalEarned": "0.000000",
      "minActiveMembers": 0,
      "sortOrder": 1,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    },
    {
      "id": 2,
      "tierCode": "silver",
      "tierName": "Silver Agent",
      "minTotalEarned": "500.000000",
      "minActiveMembers": 5,
      "sortOrder": 2,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    },
    {
      "id": 3,
      "tierCode": "gold",
      "tierName": "Gold Agent",
      "minTotalEarned": "5000.000000",
      "minActiveMembers": 20,
      "sortOrder": 3,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    },
    {
      "id": 4,
      "tierCode": "platinum",
      "tierName": "Platinum Agent",
      "minTotalEarned": "50000.000000",
      "minActiveMembers": 50,
      "sortOrder": 4,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ]
}

業務邏輯

  • 排序:sortOrder ASC
  • tierCode 為 unique constraint
  • minTotalEarned:自動升級的累計佣金門檻(USD)
  • minActiveMembers:自動升級所需的最低活躍直屬下線人數(0 = 無要求)
  • 預設 4 個階層:bronze → silver → gold → platinum

21. POST /affiliate/admin/agent-tiers — 新增/更新代理階層

新增或更新一個代理階層設定。當 body 包含 id 時為更新,不包含 id 時為新增。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Bodyidnumber有值 = 更新,無值 = 新增
BodytierCodestring階層識別碼:bronze / silver / gold / platinum
BodytierNamestring顯示名稱(最長 50 字元)
BodyminTotalEarnednumber自動升級門檻(USD 累計佣金),最小 0
BodyminActiveMembersnumber最低活躍直屬下線人數,最小 0
BodysortOrdernumber排序順序(預設 1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 2,
    "tierCode": "silver",
    "tierName": "Silver Agent",
    "minTotalEarned": "500",
    "minActiveMembers": 5,
    "sortOrder": 2,
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-02-28T16:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001更新時指定的 id 不存在

業務邏輯

  • 更新後自動清除 cache:alliance:public-info 快取
  • minTotalEarned 存為 decimal(18,6) 型別

22. DELETE /affiliate/admin/agent-tiers/:id — 刪除代理階層

刪除指定的代理階層設定。刪除後自動清除公開資訊快取。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Pathidnumber代理階層記錄 ID

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001指定的記錄不存在

注意事項

  • 刪除已被代理使用的階層(affiliate-balance.agentTier 引用中)不會阻止刪除,但會導致結算時查不到對應的佣金比例(fallback 為 0)

23. GET /affiliate/admin/preview-template — 預覽聯盟模板資料

回傳預設模板的完整資料(代理階層 + 佣金比例 + VIP 里程碑),僅供預覽,不寫入資料庫。管理員可在確認後呼叫 load-template 寫入。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "tiers": [
      { "tierCode": "bronze", "tierName": "Bronze Agent", "minTotalEarned": "0", "minActiveMembers": 0, "sortOrder": 1 },
      { "tierCode": "silver", "tierName": "Silver Agent", "minTotalEarned": "500", "minActiveMembers": 5, "sortOrder": 2 },
      { "tierCode": "gold", "tierName": "Gold Agent", "minTotalEarned": "5000", "minActiveMembers": 20, "sortOrder": 3 },
      { "tierCode": "platinum", "tierName": "Platinum Agent", "minTotalEarned": "50000", "minActiveMembers": 50, "sortOrder": 4 }
    ],
    "commissionRates": [
      { "agentTier": "bronze", "agentLevel": 1, "gameType": null, "commissionRate": "0.25", "enabled": 1 },
      { "agentTier": "bronze", "agentLevel": 1, "gameType": "sports", "commissionRate": "0.25", "enabled": 1 },
      { "agentTier": "bronze", "agentLevel": 1, "gameType": "slot", "commissionRate": "0.28", "enabled": 1 }
    ],
    "milestones": [
      { "vipLevel": 3, "bonusAmount": "5", "description": "下線達 VIP 3 獎勵", "enabled": 1 },
      { "vipLevel": 5, "bonusAmount": "20", "description": "下線達 VIP 5 獎勵", "enabled": 1 },
      { "vipLevel": 7, "bonusAmount": "50", "description": "下線達 VIP 7 獎勵", "enabled": 1 },
      { "vipLevel": 10, "bonusAmount": "200", "description": "下線達 VIP 10 獎勵", "enabled": 1 },
      { "vipLevel": 13, "bonusAmount": "1000", "description": "下線達 VIP 13 獎勵", "enabled": 1 }
    ]
  }
}

預設模板內容

類型數量說明
代理階層4 個bronze / silver / gold / platinum
佣金比例108 條4 階層 x 3 層級 x (1 通配 + 8 遊戲類型)
VIP 里程碑5 個VIP 3/5/7/10/13

佣金比例基礎費率(通配)

階層直屬 (L1)二級 (L2)三級 (L3)
bronze25%10%5%
silver30%12%6%
gold35%15%8%
platinum40%18%10%

遊戲類型費率微調:slot +3%, live +2%, crypto -5%, fish -5%(其餘類型 +0%)


24. POST /affiliate/admin/load-template — 帶入聯盟模板

將模板資料寫入資料庫,在 transaction 內先刪除所有現有記錄再插入新記錄。可傳入自訂資料覆蓋預設模板。此操作會清空全部現有的佣金比例、代理階層和 VIP 里程碑設定。

認證需求AdminJwtAuthGuard

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer <admin-jwt-token>
Bodytiersarray自訂代理階層陣列(不傳則使用預設模板)
BodycommissionRatesarray自訂佣金比例陣列(不傳則使用預設模板)
Bodymilestonesarray自訂 VIP 里程碑陣列(不傳則使用預設模板)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "tiers": 4,
    "commissionRates": 108,
    "milestones": 5
  }
}

業務邏輯

  • 原子操作 (Transaction):在 transaction 內先 DELETE 三張表的全部記錄,再批量 INSERT
  • 若 body 的 tiers / commissionRates / milestones 為空陣列或未提供,則使用預設模板資料
  • 操作完成後自動清除 cache:alliance:public-info 快取
  • 回傳值為各類型寫入的記錄數量
  • 注意:此操作會覆蓋所有現有設定,屬於破壞性操作,前端應在呼叫前顯示確認對話框

三、相關資料表結構

affiliate-settlement

欄位型別說明
idint (PK)自增主鍵
agentIdint代理 user ID
weekStartdate結算開始日
weekEnddate結算結束日
activeMemberCountint本期活躍下線人數
totalNetLossdecimal(18,6)下線總淨輸 (USD)
level1Commissiondecimal(18,6)一級佣金
level2Commissiondecimal(18,6)二級佣金
level3Commissiondecimal(18,6)三級佣金
totalCommissiondecimal(18,6)佣金總額
gameTypeBreakdownjson各遊戲類型佣金分解
periodTypevarchar(10)結算週期:weekly / daily
statusvarchar(20)pending / pendingReview / approved / rejected
riskFlaggedtinyint(1)是否有風控標記
riskReasonstext風控原因 (JSON array)
reviewedByvarchar(50)審核人帳號
reviewedAtdatetime審核時間
siteCodevarchar(30)站點代碼

Unique: (siteCode, agentId, weekStart)

affiliate-withdrawal

欄位型別說明
idint (PK)自增主鍵
agentIdint代理 user ID
amountdecimal(18,6)提款金額 (USD)
methodvarchar(10)提款方式:bank / crypto
bankCardIdint?銀行卡 ID
cryptoAddressIdint?加密錢包 ID
statusvarchar(20)pending / approved / rejected / completed
rejectReasonvarchar(255)拒絕原因
reviewedByvarchar(50)審核人帳號
reviewedAtdatetime審核時間
completedAtdatetime完成時間
siteCodevarchar(30)站點代碼

affiliate-risk-log

欄位型別說明
idint (PK)自增主鍵
settlementIdint關聯結算 ID
agentIdint代理 user ID
memberIdint?相關會員 user ID
riskTypevarchar(50)風控類型
detailtext詳細資訊 (JSON)
siteCodevarchar(30)站點代碼

alliance-commission-rate

欄位型別說明
idint (PK)自增主鍵
agentTiervarchar(20)代理階層
agentLeveltinyint代理層級 (1/2/3)
gameTypevarchar(20)?遊戲類型 (null=通配)
commissionRatedecimal(5,2)佣金比例 (%)
enabledtinyint(1)是否啟用

Unique: (agentTier, agentLevel, gameType)

alliance-agent-tier

欄位型別說明
idint (PK)自增主鍵
tierCodevarchar(20)階層識別碼 (unique)
tierNamevarchar(50)顯示名稱
minTotalEarneddecimal(18,6)升級門檻 (USD)
minActiveMembersint最低活躍下線人數
sortOrderint排序

alliance-vip-milestone

欄位型別說明
idint (PK)自增主鍵
vipLevelint觸發 VIP 等級 (unique)
bonusAmountdecimal(18,6)獎勵金額 (USD)
descriptionvarchar(100)?說明
enabledtinyint(1)是否啟用

四、端點總覽

#方法路徑說明
1GET/affiliate/admin/agents代理列表(含多站篩選)
2POST/affiliate/admin/create-agent建立代理
3GET/affiliate/admin/settlements結算列表
4POST/affiliate/admin/settlements/:id/review審核結算
5GET/affiliate/admin/settlements/:id/risk-logs風控紀錄
6GET/affiliate/admin/withdrawals提款列表
7POST/affiliate/admin/withdrawals/:id/review審核提款
8POST/affiliate/admin/withdrawals/:id/complete提款完成
9POST/affiliate/admin/bind手動綁定/解綁/轉移
10GET/affiliate/admin/bind-logs綁定紀錄
11POST/affiliate/admin/set-agent-tier設定代理等級
12POST/affiliate/admin/trigger-settlement手動觸發週結
13POST/affiliate/admin/trigger-daily-settlement手動觸發日結
14GET/affiliate/admin/commission-rates佣金比例列表
15POST/affiliate/admin/commission-rates新增/更新佣金比例
16DELETE/affiliate/admin/commission-rates/:id刪除佣金比例
17GET/affiliate/admin/vip-milestonesVIP 里程碑列表
18POST/affiliate/admin/vip-milestones新增/更新 VIP 里程碑
19DELETE/affiliate/admin/vip-milestones/:id刪除 VIP 里程碑
20GET/affiliate/admin/agent-tiers代理階層列表
21POST/affiliate/admin/agent-tiers新增/更新代理階層
22DELETE/affiliate/admin/agent-tiers/:id刪除代理階層
23GET/affiliate/admin/preview-template預覽聯盟模板
24POST/affiliate/admin/load-template帶入聯盟模板

共 24 個端點,全部需要 AdminJwtAuthGuard 認證。

Admin 後台管理模組 — 完整 API 文件

本文件涵蓋 c9-be 後端專案中 AdminController 的所有端點(76+ 個),路由前綴為 /api/admin。 所有端點除認證相關(login / register / send-verify-code / verify-email)外,皆需攜帶 AdminJWT Bearer Token。


目錄

  1. 認證管理
  2. 個人資料
  3. 權限管理
  4. 管理員 CRUD
  5. 群組 CRUD
  6. 操作紀錄
  7. 活動管理
  8. 財務管理
  9. 報表管理
  10. 風控管理
  11. R2 雲端儲存

通用說明

認證機制

所有後台 API 端點皆使用 AdminJwtAuthGuard,需在 HTTP Header 中攜帶:

Authorization: Bearer <admin_jwt_token>

JWT Payload 結構:

json
{
  "sub": 1,
  "email": "admin@example.com",
  "tokenVersion": 5,
  "role": "admin",
  "groupId": 1,
  "groupType": "root"
}

RBAC 權限檢查

大部分端點同時使用 PermissionsGuard + @RequirePermissions('module:action') 進行權限檢查。

16 個權限模組admin, admin-group, admin-log, user, deposit, withdrawal, promo, promo-tag, affiliate, vip, game, risk, report, vendor, finance, site-config

2 種動作read, write

4 種群組類型

群組類型權限範圍
root全部權限,略過所有檢查
super_admin全部權限(除 site-config
general_admin僅讀取權限(除 site-config
custom依群組 permissions JSON 陣列自訂

多站點篩選

使用 @AdminSiteCode() 裝飾器的端點支援多站點篩選:

  • 優先讀取 x-site-code HTTP Header
  • 其次讀取 siteCode Query Parameter
  • 若均未提供則為 null(表示全站)

統一回應格式

json
{
  "code": 200,
  "message": "ok",
  "result": "<回傳資料>",
  "timestamp": "2026-03-01T00:00:00.000Z",
  "path": "/api/admin/xxx"
}

業務錯誤回應(HTTP 仍為 200):

json
{
  "code": 2001,
  "message": "錯誤訊息(依當前語系)",
  "timestamp": "2026-03-01T00:00:00.000Z",
  "path": "/api/admin/xxx"
}

1. 認證管理

POST /api/admin/login — 管理員登入

功能說明

管理員透過 Email 與密碼登入後台管理系統。系統會驗證 Email 是否存在、密碼是否正確、帳號是否為啟用狀態。驗證通過後簽發 AdminJWT Token,Token 中包含管理員 ID、Email、tokenVersion、角色資訊及所屬群組。若管理員已啟用 Google Authenticator 2FA,登入時需額外驗證。

認證需求:無(公開端點)

Request

欄位類型必填說明範例
emailstring (Email)管理員 Email"admin@example.com"
passwordstring (min: 6)管理員密碼,至少 6 碼"admin1234"
json
// Request Body
{
  "email": "admin@example.com",
  "password": "admin1234"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "admin": {
      "id": 1,
      "email": "admin@example.com",
      "name": "超級管理員",
      "groupId": 1,
      "status": 1
    }
  }
}

錯誤碼

錯誤碼說明
2001Email 或密碼錯誤
2002帳號已停用

業務邏輯說明

  • 系統先查詢 admin-user 資料表確認 Email 是否存在
  • 使用 bcrypt 比對密碼雜湊
  • 確認帳號 status === 1(啟用中)
  • 簽發 JWT 時帶入 tokenVersion,供後續強制登出機制使用
  • 登入成功後記錄操作紀錄

前端注意事項

  • 登入成功後將 token 存入 NextAuth session
  • 後續所有 API 請求需在 Authorization Header 帶入 Bearer <token>
  • 若收到 HTTP 401 回應,前端需清除 session 並導向登入頁

POST /api/admin/send-verify-code — 發送驗證碼

功能說明

向指定 Email 發送 6 位數驗證碼,用於管理員註冊流程的 Email 驗證步驟。驗證碼透過 Resend API 發送至指定信箱,具有過期時間限制,且短時間內不可重複發送。

認證需求:無(公開端點)

Request

欄位類型必填說明範例
emailstring (Email)接收驗證碼的 Email"admin@example.com"
json
{
  "email": "admin@example.com"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001請先提供 Email
2002驗證碼已發送,請稍後再試
2003Email 發送失敗

業務邏輯說明

  • 驗證碼存入 Redis 快取,設有 TTL 過期時間
  • 同一 Email 在短時間內重複請求會回傳 2002 錯誤
  • 驗證碼為 6 位純數字

前端注意事項

  • 發送後應啟動倒數計時器(如 60 秒),防止用戶連續點擊
  • 成功後引導用戶至驗證碼輸入介面

POST /api/admin/verify-email — 驗證 Email

功能說明

驗證用戶輸入的 6 位數驗證碼是否正確。此步驟為管理員註冊流程的前置條件,只有通過 Email 驗證後才能進行註冊。

認證需求:無(公開端點)

Request

欄位類型必填說明範例
emailstring (Email)管理員 Email"admin@example.com"
codestring (length: 6)6 位數驗證碼"123456"
json
{
  "email": "admin@example.com",
  "code": "123456"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001驗證碼不存在或已過期
2002驗證碼錯誤

業務邏輯說明

  • 從 Redis 中讀取該 Email 對應的驗證碼進行比對
  • 驗證成功後在 Redis 標記該 Email 已驗證
  • 驗證碼使用後即失效(一次性)

前端注意事項

  • 驗證成功後才啟用「註冊」按鈕
  • 驗證碼長度固定 6 位,前端需做格式驗證

POST /api/admin/register — 管理員註冊

功能說明

完成 Email 驗證後,管理員可透過此端點完成註冊。系統會檢查 Email 是否已被使用、是否已通過 Email 驗證、指定的群組是否存在。註冊成功後自動建立管理員帳號並分配至指定群組。

認證需求:無(公開端點)

Request

欄位類型必填說明範例
emailstring (Email)管理員 Email"admin@example.com"
passwordstring (min: 6)密碼,至少 6 碼"admin1234"
namestring管理員顯示名稱"超級管理員"
codestring6 位驗證碼"123456"
groupIdnumber所屬群組 ID1
json
{
  "email": "admin@example.com",
  "password": "admin1234",
  "name": "超級管理員",
  "code": "123456",
  "groupId": 1
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "email": "admin@example.com",
    "name": "超級管理員",
    "groupId": 1
  }
}

錯誤碼

錯誤碼說明
2001Email 已存在
2002Email 尚未驗證
2003群組不存在

業務邏輯說明

  • 密碼以 bcrypt 雜湊後儲存
  • 若未指定 groupId,系統會分配預設群組
  • 註冊完成後 Redis 中的驗證標記會被清除
  • tokenVersion 初始值為 0

前端注意事項

  • 註冊流程:發送驗證碼 -> 驗證 Email -> 填寫資料 -> 註冊
  • 註冊成功後自動導向登入頁

2. 個人資料

GET /api/admin/profile — 取得管理員個人資料

功能說明

取得當前已登入管理員的完整個人資料,包含基本資訊、所屬群組名稱、Google Authenticator 啟用狀態等。此端點用於後台個人資料頁面的資料載入。

認證需求AdminJwtAuthGuard + PermissionsGuard(無特定權限要求,僅需登入)

Request

  • Headers: Authorization: Bearer <token>
  • 無其他參數

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "email": "admin@example.com",
    "name": "超級管理員",
    "groupId": 1,
    "groupName": "root",
    "status": 1,
    "googleAuthEnabled": 0,
    "createdAt": "2026-02-22T00:00:00.000Z"
  }
}

業務邏輯說明

  • 從 JWT Token 中取得 adminId,查詢 admin-user 資料表
  • JOIN admin-group 取得群組名稱
  • googleAuthEnabled0(未啟用)或 1(已啟用)

前端注意事項

  • 用於渲染個人資料頁面
  • googleAuthEnabled 用於決定是否顯示「啟用 2FA」或「停用 2FA」按鈕

POST /api/admin/google-auth/generate — 產生 Google Authenticator 密鑰與 QR Code

功能說明

為當前管理員產生 TOTP 密鑰與對應的 QR Code 圖片(Base64 編碼的 PNG)。管理員可使用 Google Authenticator App 掃描 QR Code 以綁定帳號。產生的密鑰會暫存於管理員記錄中,待驗證成功後才正式啟用。

認證需求AdminJwtAuthGuard + PermissionsGuard(僅需登入)

Request

  • Headers: Authorization: Bearer <token>
  • 無其他參數

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "secret": "JBSWY3DPEHPK3PXP",
    "qrCode": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..."
  }
}

業務邏輯說明

  • 使用 speakeasy 套件產生 TOTP Secret
  • QR Code 包含 otpauth://totp/C9:admin@example.com?secret=xxx&issuer=C9 格式的 URI
  • Secret 暫存於 admin-user.googleAuthSecret 欄位,但 googleAuthEnabled 維持為 0
  • 需後續呼叫 /google-auth/enable 端點才會正式啟用

前端注意事項

  • qrCode 直接作為 <img>src 顯示
  • 同時顯示 secret 供無法掃描的用戶手動輸入
  • 前端元件:google-auth-dialog.tsx(狀態流程:idle -> qr -> verify)

POST /api/admin/google-auth/enable — 啟用 Google Authenticator

功能說明

管理員掃描 QR Code 後,輸入 Google Authenticator App 顯示的 6 位數驗證碼,系統驗證成功後正式啟用 2FA。啟用後每次登入將需要額外提供 TOTP 驗證碼。

認證需求AdminJwtAuthGuard + PermissionsGuard(僅需登入)

Request

欄位類型必填說明範例
codestring (length: 6)Google Authenticator 6 位數驗證碼"123456"
json
{
  "code": "123456"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001請先產生密鑰(需先呼叫 /google-auth/generate
2002驗證碼錯誤

業務邏輯說明

  • 使用 speakeasy.totp.verify() 驗證 TOTP 碼
  • 驗證成功後將 googleAuthEnabled 設為 1
  • Secret 保留在資料庫中供後續登入驗證使用

前端注意事項

  • 啟用成功後關閉對話框並刷新 Profile 資料
  • 提醒用戶備份 Secret,避免遺失手機後無法登入

POST /api/admin/google-auth/disable — 停用 Google Authenticator

功能說明

管理員輸入當前的 6 位數 TOTP 驗證碼以確認身份,驗證成功後停用 Google Authenticator 2FA。停用後登入將不再需要 TOTP 驗證碼。

認證需求AdminJwtAuthGuard + PermissionsGuard(僅需登入)

Request

欄位類型必填說明範例
codestring (length: 6)Google Authenticator 6 位數驗證碼"654321"
json
{
  "code": "654321"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001請先產生密鑰
2002驗證碼錯誤

業務邏輯說明

  • 必須提供正確的 TOTP 驗證碼才能停用,防止未授權操作
  • 停用後 googleAuthEnabled 設為 0googleAuthSecret 清空

前端注意事項

  • 停用操作需先彈出確認對話框
  • 成功後刷新 Profile 資料,UI 切回「啟用」按鈕狀態

3. 權限管理

GET /api/admin/permissions/all — 取得所有可用權限模組與動作

功能說明

取得系統中所有可配置的權限模組名稱與可用動作列表。此端點用於群組管理頁面的權限勾選介面,讓管理員了解有哪些權限可以分配給群組。

認證需求AdminJwtAuthGuard + PermissionsGuard(僅需登入)

Request

  • Headers: Authorization: Bearer <token>
  • 無其他參數

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "modules": [
      "admin",
      "admin-group",
      "admin-log",
      "user",
      "deposit",
      "withdrawal",
      "promo",
      "promo-tag",
      "affiliate",
      "vip",
      "game",
      "risk",
      "report",
      "vendor",
      "finance",
      "site-config"
    ],
    "actions": ["read", "write"]
  }
}

業務邏輯說明

  • 回傳來自 constants/permissions.ts 中定義的靜態常數
  • 權限格式為 module:action,如 admin:readdeposit:write
  • 共 16 個模組 x 2 個動作 = 32 個可配置權限

前端注意事項

  • 用於群組建立/編輯頁面的權限矩陣
  • 前端使用 usePermissions() hook 進行 RBAC 檢查
  • root 群組類型不受權限限制,super_admin 除了 site-config 外擁有全部權限

4. 管理員 CRUD

GET /api/admin/list — 管理員列表

功能說明

取得後台管理員列表,支援分頁、關鍵字搜尋(Email / 名稱)、啟用狀態篩選、群組篩選及日期範圍篩選。此端點用於系統管理中的管理員列表頁面。注意此為全站共用設定,不區分站點。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼,預設 11
pageSizeQuerynumber每頁筆數,預設 2020
keywordQuerystring搜尋 email / name"admin"
statusQuerynumber啟用狀態(1=啟用, 0=停用)1
groupIdQuerynumber群組 ID 篩選1
startDateQuerystring起始日期 (YYYY-MM-DD)"2026-01-01"
endDateQuerystring結束日期 (YYYY-MM-DD)"2026-12-31"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "email": "admin@example.com",
        "name": "超級管理員",
        "groupId": 1,
        "groupName": "root",
        "status": 1,
        "createdAt": "2026-02-22T00:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "total": 1,
      "totalPages": 1
    }
  }
}

業務邏輯說明

  • 使用 parsePagination() 工具函數正規化分頁參數
  • keyword 同時模糊搜尋 emailname 欄位
  • JOIN admin-group 表取得 groupName
  • 使用 applyDateRange() 套用日期區間篩選
  • 密碼欄位不回傳

前端注意事項

  • 此頁面在 Header SiteSelector 中自動隱藏站點選擇(全站共用)
  • 使用 FilterBar 元件管理篩選條件
  • 使用 SimpleTable 元件渲染列表,搭配 pagination 元件

GET /api/admin/:id — 取得單一管理員

功能說明

根據管理員 ID 取得單一管理員的完整資料,包含所屬群組名稱。用於管理員編輯頁面的資料載入。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin:read 權限

Request

參數位置類型必填說明範例
idURL Paramnumber管理員 ID1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "email": "admin@example.com",
    "name": "超級管理員",
    "groupId": 1,
    "groupName": "root",
    "status": 1,
    "createdAt": "2026-02-22T00:00:00.000Z"
  }
}

前端注意事項

  • 用於編輯對話框的資料初始化
  • 注意此端點路由 /:id 的位置可能與其他路由衝突,NestJS 會按宣告順序匹配

POST /api/admin/create — 建立管理員

功能說明

由具有 admin:write 權限的管理員建立新的管理員帳號。系統會檢查 Email 是否已被使用,以及指定的群組是否存在。建立成功後會記錄操作紀錄,包含操作者、被建立的管理員資訊。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin:write 權限

Request

欄位類型必填說明範例
emailstring (Email)管理員 Email"new_admin@example.com"
passwordstring (min: 6)密碼,至少 6 碼"pass1234"
namestring管理員顯示名稱"新管理員"
groupIdnumber所屬群組 ID2
json
{
  "email": "new_admin@example.com",
  "password": "pass1234",
  "name": "新管理員",
  "groupId": 2
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 2,
    "email": "new_admin@example.com",
    "name": "新管理員",
    "groupId": 2
  }
}

錯誤碼

錯誤碼說明
2001Email 已存在
2002群組不存在

業務邏輯說明

  • 密碼以 bcrypt 雜湊後儲存
  • 自動記錄操作紀錄至 admin-operation-log
  • 操作紀錄包含 adminId(操作者)、action: 'create'module: 'admin'detail(被建立者資訊)、ip

前端注意事項

  • 建立成功後需刷新管理員列表
  • 群組下拉選單的選項來自群組列表 API

PATCH /api/admin/:id — 更新管理員

功能說明

更新指定管理員的資料,支援修改名稱、密碼、群組、啟用狀態。所有欄位皆為選填,僅更新有提供的欄位。停用管理員後該管理員將無法登入。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin:write 權限

Request

參數位置類型必填說明範例
idURL Paramnumber管理員 ID1
nameBodystring新名稱"新名稱"
passwordBodystring (min: 6)新密碼"newpass123"
groupIdBodynumber新群組 ID2
statusBodynumber (0 or 1)啟用狀態0
json
{
  "name": "新名稱",
  "status": 0
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "email": "admin@example.com",
    "name": "新名稱",
    "groupId": 1,
    "status": 0
  }
}

錯誤碼

錯誤碼說明
2001管理員不存在

業務邏輯說明

  • 若更新密碼,新密碼同樣以 bcrypt 雜湊後儲存
  • 停用管理員 (status: 0) 後,該管理員的 JWT 驗證將失敗
  • 操作紀錄會記錄變更前後的差異

前端注意事項

  • 更新成功後需刷新列表
  • 密碼欄位為選填,空白表示不更新密碼

DELETE /api/admin/:id — 刪除管理員

功能說明

刪除指定的管理員帳號。系統會檢查目標管理員是否存在,且不允許管理員刪除自己。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin:write 權限

Request

參數位置類型必填說明範例
idURL Paramnumber管理員 ID2

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001管理員不存在
2002不可刪除自己

業務邏輯說明

  • 硬刪除(物理刪除),資料從資料庫中移除
  • 刪除前檢查 req.user.id !== targetId
  • 記錄刪除操作至操作紀錄

前端注意事項

  • 刪除前需使用 useConfirm() 彈出確認對話框
  • 刪除成功後刷新列表

5. 群組 CRUD

GET /api/admin/groups/list — 管理員群組列表

功能說明

取得後台管理員群組列表,支援分頁、關鍵字搜尋(群組名稱)、啟用狀態篩選及日期範圍篩選。群組為 RBAC 權限系統的核心,每個管理員必須隸屬於一個群組。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin-group:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
keywordQuerystring搜尋群組名稱"admin"
statusQuerynumber啟用狀態(1/0)1
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "name": "Root",
        "groupType": "root",
        "permissions": ["admin:read", "admin:write", "..."],
        "description": "超級管理員群組",
        "status": 1,
        "memberCount": 2,
        "createdAt": "2026-02-22T00:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "total": 4,
      "totalPages": 1
    }
  }
}

業務邏輯說明

  • memberCount 為該群組下的管理員數量(透過 COUNT 子查詢計算)
  • permissions 為 JSON 陣列,格式為 ["module:action", ...]
  • 群組權限快取於 Redis 中(key: cache:admin-group:perms:{groupId},TTL: 60 秒)

前端注意事項

  • 此頁面為全站共用設定,不顯示站點選擇器
  • 顯示 memberCount 有助於判斷群組是否可安全刪除

GET /api/admin/groups/:id — 取得單一群組

功能說明

根據群組 ID 取得單一群組的完整資料,包含權限列表。用於群組編輯頁面的資料載入。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin-group:read 權限

Request

參數位置類型必填說明
idURL Paramnumber群組 ID

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "name": "Root",
    "groupType": "root",
    "permissions": ["admin:read", "admin:write"],
    "description": "超級管理員群組",
    "status": 1,
    "createdAt": "2026-02-22T00:00:00.000Z"
  }
}

POST /api/admin/groups/create — 建立管理員群組

功能說明

建立新的管理員群組,可指定群組名稱、權限列表及描述。新建群組的 groupType 預設為 custom

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin-group:write 權限

Request

欄位類型必填說明範例
namestring群組名稱"客服組"
permissionsstring[]權限列表["user:read", "deposit:read"]
descriptionstring群組描述"客服人員專用"
json
{
  "name": "客服組",
  "permissions": ["user:read", "deposit:read", "withdrawal:read"],
  "description": "客服人員專用群組"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 5,
    "name": "客服組",
    "groupType": "custom",
    "permissions": ["user:read", "deposit:read", "withdrawal:read"],
    "description": "客服人員專用群組",
    "status": 1
  }
}

業務邏輯說明

  • 新建群組自動設為 groupType: 'custom'
  • permissions 欄位儲存為 JSON 陣列
  • 建立成功後清除相關群組權限快取

PATCH /api/admin/groups/:id — 更新管理員群組

功能說明

更新指定群組的名稱、權限列表、描述或啟用狀態。更新權限後會立即清除 Redis 中該群組的權限快取,確保下次權限檢查使用最新資料。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin-group:write 權限

Request

參數位置類型必填說明範例
idURL Paramnumber群組 ID5
nameBodystring群組名稱"新群組名稱"
permissionsBodystring[]權限列表["admin:read", "deposit:read"]
descriptionBodystring群組描述"更新描述"
statusBodynumber啟用狀態0

錯誤碼

錯誤碼說明
2001群組不存在

業務邏輯說明

  • 更新 permissions 後會刪除 cache:admin-group:perms:{groupId} 快取
  • 操作紀錄記錄變更內容

DELETE /api/admin/groups/:id — 刪除管理員群組

功能說明

刪除指定的管理員群組。若群組下仍有管理員,則無法刪除,需先將管理員移至其他群組。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin-group:write 權限

Request

參數位置類型必填說明
idURL Paramnumber群組 ID

錯誤碼

錯誤碼說明
2001群組不存在
2002群組下仍有管理員,不可刪除

前端注意事項

  • 刪除前需使用 useConfirm() 確認
  • 如群組下有管理員,前端可提示用戶先移轉管理員

6. 操作紀錄

GET /api/admin/logs/list — 管理員操作紀錄列表

功能說明

查詢後台管理員的操作紀錄,支援按管理員 ID、操作模組、操作動作、日期範圍進行篩選。所有管理員的新增/修改/刪除操作都會被記錄,用於稽核追蹤。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 admin-log:read 權限

Request

參數位置類型必填說明範例
adminIdQuerynumber管理員 ID1
moduleQuerystring操作模組"admin"
actionQuerystring操作動作"create"
startDateQuerystring起始日期 (YYYY-MM-DD)"2026-01-01"
endDateQuerystring結束日期 (YYYY-MM-DD)"2026-12-31"
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "adminId": 1,
        "adminEmail": "admin@example.com",
        "action": "create",
        "module": "admin",
        "detail": {
          "targetId": 2,
          "email": "new_admin@example.com",
          "name": "新管理員"
        },
        "ip": "127.0.0.1",
        "createdAt": "2026-02-22T00:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "total": 1,
      "totalPages": 1
    }
  }
}

業務邏輯說明

  • admin-operation-log 資料表記錄所有管理操作
  • detail 為 JSON 欄位,記錄操作的具體內容(如變更前後差異)
  • ip 來源為 req.ip
  • 此為全站共用紀錄,不區分站點

前端注意事項

  • 此頁面在 Header SiteSelector 中自動隱藏站點選擇
  • detail 欄位可使用 JSON 展開元件顯示
  • 可依 moduleaction 建立下拉篩選器

7. 活動管理

7.1 優惠活動 (Promo)

GET /api/admin/promos/list — 活動列表

功能說明

取得後台活動管理列表,支援分頁、標籤篩選、啟用狀態篩選、日期範圍篩選、標題搜尋及條件類型篩選。此端點供後台管理員瀏覽所有活動(包含已關閉的),與前台 API 不同的是不會過濾未啟用的活動。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
tagQuerystring活動標籤"新手"
enabledQuerynumber啟用狀態(1=開啟, 0=關閉)1
startDateQuerystring起始日期 (YYYY-MM-DD)"2026-01-01"
endDateQuerystring結束日期 (YYYY-MM-DD)"2026-12-31"
titleQuerystring標題搜尋"首存"
conditionTypeQuerystring條件類型"first_deposit"
x-site-codeHeaderstring站點代碼(多站點篩選)"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "title": { "zh-TW": "新手首存禮", "en-US": "Welcome Bonus" },
        "content": { "zh-TW": "<p>首次存款即送 10 USD</p>", "en-US": "<p>First deposit bonus 10 USD</p>" },
        "tag": "新手",
        "enabled": 1,
        "conditionType": "first_deposit",
        "conditionValue": "0",
        "rewardAmount": "10.000000",
        "maxClaims": 0,
        "turnoverMultiplier": "3.00",
        "startTime": "2026-03-01T00:00:00.000Z",
        "endTime": "2026-04-01T00:00:00.000Z",
        "imgPc": { "zh-TW": "https://cdn.example.com/promo/1_pc_zh-TW.jpg" },
        "imgMobile": { "zh-TW": "https://cdn.example.com/promo/1_mobile_zh-TW.jpg" },
        "siteCode": "C9",
        "claimCount": 15,
        "createdAt": "2026-02-20T00:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 5, "totalPages": 1 }
  }
}

業務邏輯說明

  • activeOnly: false — 後台列表不過濾已過期或未啟用的活動
  • title 使用 CAST(promo.title AS CHAR) LIKE :keyword 進行 JSON 欄位模糊搜尋
  • conditionType 支援: none, deposit_threshold, vip_level, first_deposit
  • rewardAmount 以 USD decimal(18,6) 儲存
  • turnoverMultiplier 為打碼量倍數,decimal(10,2)
  • maxClaims: 0 表示無限制領取
  • siteCode 篩選透過 @AdminSiteCode() 裝飾器自動讀取

前端注意事項

  • 已支援多站點 SiteTabs 切換
  • titlecontent 為多語系 JSON,前端用 resolveText() 解析顯示
  • 活動圖片分為 PC 版和行動版,各語系獨立

GET /api/admin/promos/:id — 取得單一活動

功能說明

根據活動 ID 取得活動的完整原始資料(不做多語系解析),用於編輯頁面載入。支援多站點篩選確保只能存取所屬站點的活動。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo:read 權限

Request

參數位置類型必填說明
idURL Paramnumber活動 ID
x-site-codeHeaderstring站點代碼

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "title": { "zh-TW": "新手首存禮", "en-US": "Welcome Bonus" },
    "content": { "zh-TW": "<p>首次存款即送 10 USD</p>", "en-US": "<p>First deposit bonus 10 USD</p>" },
    "actionHtml": "<a href='/deposit'>立即存款</a>",
    "tag": "新手",
    "enabled": 1,
    "conditionType": "first_deposit",
    "conditionValue": "0",
    "rewardAmount": "10.000000",
    "maxClaims": 0,
    "turnoverMultiplier": "3.00",
    "startTime": "2026-03-01T00:00:00.000Z",
    "endTime": "2026-04-01T00:00:00.000Z",
    "imgPc": { "zh-TW": "https://cdn.example.com/promo/1_pc_zh-TW.jpg" },
    "imgMobile": { "zh-TW": "https://cdn.example.com/promo/1_mobile_zh-TW.jpg" },
    "siteCode": "C9",
    "createdAt": "2026-02-20T00:00:00.000Z",
    "updatedAt": "2026-02-25T00:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001查無此活動

POST /api/admin/promos/create — 建立活動

功能說明

建立新的優惠活動,支援多語系標題/內容、活動圖片上傳(PC/Mobile 各語系版本)、領取條件設定、獎勵金額設定等。活動圖片會上傳至 Cloudflare R2 儲存。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo:write 權限

Request

Content-Type: multipart/form-data

欄位類型必填說明範例
titleJSON string活動標題(多語系)'{"zh-TW":"新手首存禮","en-US":"Welcome Bonus"}'
contentJSON string活動內容 HTML(多語系)'{"zh-TW":"<p>...</p>"}'
actionHtmlstring渲染連結/按鈕 HTML"<a href='/deposit'>立即存款</a>"
startTimestring (ISO 8601)活動開始時間"2026-03-01T00:00:00.000Z"
endTimestring (ISO 8601)活動結束時間"2026-04-01T00:00:00.000Z"
tagstring (max: 30)活動標籤"新手"
enablednumber是否啟用(預設 1)1
conditionTypestring領取條件類型"first_deposit"
conditionValuestring門檻值(預設 "0")"100"
rewardAmountnumber獎勵金額 (USD)10
maxClaimsnumber最大領取總數(0=無限)0
turnoverMultipliernumber打碼量倍數(0=無要求)3
imgPcZhTWFilePC 版圖片(繁中)-
imgPcEnUSFilePC 版圖片(英文)-
imgMobileZhTWFileMobile 版圖片(繁中)-
imgMobileEnUSFileMobile 版圖片(英文)-

conditionType 可用值

說明
none無條件
deposit_threshold存款門檻(conditionValue 為存款金額)
vip_levelVIP 等級門檻(conditionValue 為 VIP 等級)
first_deposit首次存款

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 6,
    "title": { "zh-TW": "新手首存禮", "en-US": "Welcome Bonus" },
    "siteCode": "C9",
    "createdAt": "2026-03-01T00:00:00.000Z"
  }
}

業務邏輯說明

  • 圖片檔案限制:僅接受 image/* MIME 類型,最大 5MB
  • 圖片上傳至 R2 路徑:{siteName}/promos/{promoId}_{platform}_{locale}.{ext}
  • titlecontent 以 JSON string 傳入(因為使用 multipart/form-data),後端自動 JSON.parse()
  • rewardAmount 以 USD decimal(18,6) 儲存
  • siteCode@AdminSiteCode() 決定寫入的站點

前端注意事項

  • 使用 FormData 組裝請求
  • JSON 欄位(title, content)需先 JSON.stringify() 再塞入 FormData
  • 使用 Tiptap HTML 編輯器(htmlEditor 元件)編輯 content

PATCH /api/admin/promos/:id — 更新活動

功能說明

更新指定活動的資料,所有欄位皆為選填。支援更新圖片(新上傳的圖片會覆蓋舊圖片)。使用 PartialType(CreatePromoDto) 繼承,所有建立時的欄位都可選填更新。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo:write 權限

Request

Content-Type: multipart/form-data

  • URL Param: id (活動 ID)
  • Body: 同 POST /promos/create,所有欄位皆為選填
  • 圖片檔案為選填,僅上傳的語系版本會更新

錯誤碼

錯誤碼說明
2001查無此活動

DELETE /api/admin/promos/:id — 刪除活動

功能說明

刪除指定的優惠活動。關聯的活動領取紀錄(promo-claim)不會被刪除。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo:write 權限

Request

參數位置類型必填說明
idURL Paramnumber活動 ID

錯誤碼

錯誤碼說明
2001查無此活動

7.2 活動標籤 (Promo Tags)

GET /api/admin/promo-tags/list — 活動標籤列表

功能說明

取得所有活動標籤列表,不分頁。標籤用於活動分類,每個標籤可設定多語系名稱、顏色、排序順序及啟用狀態。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo-tag:read 權限

Request

參數位置類型必填說明
x-site-codeHeaderstring站點代碼

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "name": "新手",
      "label": { "zh-TW": "新手活動", "en-US": "Beginner" },
      "color": "#FF6B6B",
      "sortOrder": 1,
      "enabled": 1,
      "siteCode": "C9"
    },
    {
      "id": 2,
      "name": "VIP",
      "label": { "zh-TW": "VIP 專屬", "en-US": "VIP Exclusive" },
      "color": "#4ECDC4",
      "sortOrder": 2,
      "enabled": 1,
      "siteCode": "C9"
    }
  ]
}

POST /api/admin/promo-tags/create — 建立活動標籤

功能說明

建立新的活動標籤,用於活動分類。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo-tag:write 權限

Request

欄位類型必填說明範例
namestring標籤內部名稱"新手"
labelRecord<string, string>多語系顯示名稱{"zh-TW":"新手活動","en-US":"Beginner"}
colorstring標籤顏色 (HEX)"#FF6B6B"
sortOrdernumber排序順序1
enablednumber啟用狀態 (0/1)1

業務邏輯說明

  • siteCode@AdminSiteCode() 自動帶入
  • name 為內部使用的標籤識別碼,label 為前端顯示的多語系名稱

PATCH /api/admin/promo-tags/:id — 更新活動標籤

功能說明

更新指定活動標籤的資料,所有欄位皆為選填。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo-tag:write 權限

Request

參數位置類型必填說明
idURL Paramnumber標籤 ID
nameBodystring標籤名稱
labelBodyRecord<string, string>多語系顯示名稱
colorBodystring標籤顏色
sortOrderBodynumber排序順序
enabledBodynumber啟用狀態

DELETE /api/admin/promo-tags/:id — 刪除活動標籤

功能說明

刪除指定的活動標籤。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 promo-tag:write 權限

Request

參數位置類型必填說明
idURL Paramnumber標籤 ID

8. 財務管理

8.1 存款審核

GET /api/admin/finance/deposit-review — 存款訂單審核列表

功能說明

取得所有存款訂單列表,支援按訂單狀態、支付方式、日期範圍篩選。管理員可在此頁面審核存款訂單。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard(無指定 @RequirePermissions,但受 PermissionsGuard 保護)

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
statusQuerystring訂單狀態"pending"
paymentMethodQuerystring支付方式"fiat"
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
x-site-codeHeaderstring站點代碼"C9"

訂單狀態 (status)

說明
pending待處理
created已建立
paid已支付
failed失敗

支付方式 (paymentMethod)

說明
fiat法幣(ATM 轉帳)
credit信用卡
crypto加密貨幣 (USDT)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "orderId": "DEP20260301001",
        "userId": 5,
        "userAccount": "user001",
        "amount": "100.000000",
        "amountLocal": "3100.00",
        "currency": "TWD",
        "exchangeRate": "31.0000000000",
        "paymentMethod": "fiat",
        "status": "pending",
        "channelName": { "zh-TW": "萬通金流 ATM" },
        "siteCode": "C9",
        "createdAt": "2026-03-01T10:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 10, "totalPages": 1 }
  }
}

業務邏輯說明

  • amount 為系統內部金額 (USD decimal(18,6))
  • amountLocal 為用戶當地幣別金額
  • exchangeRate 為當時的即時匯率 (decimal(18,10))
  • 金額轉換公式:amount = amountLocal / exchangeRate(以台灣銀行即時匯率換算)

PATCH /api/admin/finance/deposit-review/:id — 審核存款訂單

功能說明

審核指定的存款訂單,可通過 (approve) 或拒絕 (reject)。通過後系統會自動將金額加入用戶餘額,拒絕時需提供拒絕原因。

認證需求AdminJwtAuthGuard + PermissionsGuard

Request

參數位置類型必填說明範例
idURL Paramstring訂單 ID"1"
actionBodystring審核動作"approve""reject"
rejectReasonBodystring拒絕原因(拒絕時必填)"資料不符"
json
{
  "action": "approve"
}

json
{
  "action": "reject",
  "rejectReason": "資料不符"
}

業務邏輯說明

  • 通過後自動更新用戶餘額
  • 存款確認後觸發任務系統進度更新(MissionService.updateDepositProgress()
  • 審核者的 email 會被記錄

8.2 用戶管理

GET /api/admin/finance/users — 前台用戶列表

功能說明

取得前台用戶列表,包含用戶餘額資訊。支援分頁、關鍵字搜尋(帳號/名稱)及多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 user:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
keywordQuerystring搜尋帳號/名稱"user001"
x-site-codeHeaderstring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "account": "user001",
        "name": "王小明",
        "email": "user@example.com",
        "mobile": "0912345678",
        "balance": "1000.500000",
        "vipLevel": 3,
        "status": 1,
        "siteCode": "C9",
        "createdAt": "2026-01-15T00:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 50, "totalPages": 3 }
  }
}

GET /api/admin/finance/users/:id — 取得單一用戶完整資訊

功能說明

取得指定前台用戶的完整資訊,包含餘額、VIP 等級、錢包資訊、登入紀錄等。用於用戶詳情頁面。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 user:read 權限

Request

參數位置類型必填說明
idURL Paramstring用戶 ID
x-site-codeHeaderstring站點代碼

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "account": "user001",
    "name": "王小明",
    "email": "user@example.com",
    "mobile": "0912345678",
    "balance": "1000.500000",
    "vipLevel": 3,
    "totalEffectiveBet": "50000.000000",
    "siteCode": "C9",
    "bankCards": [],
    "creditCards": [],
    "cryptoAddresses": [],
    "recentLogins": [],
    "createdAt": "2026-01-15T00:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
1001查無此用戶

PATCH /api/admin/finance/users/:id — 更新前台用戶基本資料

功能說明

由管理員更新前台用戶的基本資料(名稱、Email、手機號碼)。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 user:write 權限

Request

參數位置類型必填說明範例
idURL Paramstring用戶 ID"1"
nameBodystring用戶名稱"王大明"
emailBodystring | nullEmail(傳 null 清除)"new@example.com"
mobileBodystring | null手機號碼(傳 null 清除)"0987654321"

8.3 用戶金流群組

PATCH /api/admin/users/:userId/vendor-group — 更新用戶所屬金流群組

功能說明

將指定用戶分配到指定金流群組,或傳 null 清除群組分配。金流群組決定用戶可使用的入金通道。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 user:write 權限

Request

參數位置類型必填說明範例
userIdURL Paramstring用戶 ID"1"
vendorGroupIdBodynumber | null金流群組 ID(null 清除)2
json
{
  "vendorGroupId": 2
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

8.4 人工調帳

POST /api/admin/finance/adjust-balance — 人工調節用戶餘額

功能說明

管理員手動調整前台用戶的餘額,支援加值和扣款兩種操作。此操作會記錄在操作紀錄中,包含操作者、操作類型、金額及原因。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

欄位類型必填說明範例
userIdnumber用戶 ID1
amountnumber調整金額 (USD)100
typestring調整類型"add""deduct"
reasonstring調整原因"補償客訴"
json
{
  "userId": 1,
  "amount": 100,
  "type": "add",
  "reason": "補償客訴"
}

業務邏輯說明

  • type: "add" — 增加用戶餘額
  • type: "deduct" — 扣減用戶餘額
  • 金額以 USD decimal(18,6) 精度處理
  • 操作紀錄包含管理員 ID、操作類型、金額、原因

前端注意事項

  • 已支援多站點 SiteTabs
  • 此為高風險操作,建議使用 useConfirm() 確認

8.5 銀行卡管理

GET /api/admin/finance/bank-cards — 銀行卡列表

功能說明

取得所有用戶的銀行卡列表,支援按用戶 ID、審核狀態、日期範圍及關鍵字篩選。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
userIdQuerynumber用戶 ID1
statusQuerynumber審核狀態1
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
keywordQuerystring搜尋帳號/持卡人"王"
x-site-codeHeaderstring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "userId": 5,
        "userAccount": "user001",
        "bankCode": "004",
        "bankAccount": "12345678901234",
        "branch": "台北分行",
        "holderName": "王小明",
        "status": 1,
        "siteCode": "C9",
        "createdAt": "2026-02-01T00:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 5, "totalPages": 1 }
  }
}

PATCH /api/admin/finance/bank-cards/:id/review — 銀行卡審核

功能說明

審核用戶提交的銀行卡資料,更新審核狀態。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

參數位置類型必填說明範例
idURL Paramnumber銀行卡 ID1
statusBodynumber審核狀態1 (通過) 或 2 (拒絕)

PATCH /api/admin/finance/bank-cards/:id — 銀行卡欄位更新

功能說明

由管理員直接更新銀行卡的欄位資料。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

參數位置類型必填說明
idURL Paramnumber銀行卡 ID
bankCodeBodystring銀行代碼
bankAccountBodystring銀行帳號
branchBodystring分行名稱
holderNameBodystring持卡人姓名
statusBodynumber狀態

POST /api/admin/finance/bank-cards — 新增銀行卡

功能說明

由管理員為指定用戶新增銀行卡。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

欄位類型必填說明範例
userIdnumber用戶 ID1
bankCodestring銀行代碼"004"
bankAccountstring銀行帳號"12345678901234"
branchstring分行名稱"台北分行"
holderNamestring持卡人姓名"王小明"

DELETE /api/admin/finance/bank-cards/:id — 刪除銀行卡

功能說明

由管理員刪除指定的銀行卡記錄。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

參數位置類型必填說明
idURL Paramnumber銀行卡 ID

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "success": true }
}

8.6 信用卡管理

GET /api/admin/finance/credit-cards — 信用卡列表

功能說明

取得所有用戶的信用卡列表,支援按用戶 ID、審核狀態、日期範圍及關鍵字篩選。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
userIdQuerynumber用戶 ID1
statusQuerynumber審核狀態1
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
keywordQuerystring搜尋關鍵字"王"
x-site-codeHeaderstring站點代碼"C9"

PATCH /api/admin/finance/credit-cards/:id/review — 信用卡審核

功能說明

審核用戶提交的信用卡資料,更新審核狀態。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

參數位置類型必填說明
idURL Paramnumber信用卡 ID
statusBodynumber審核狀態

PATCH /api/admin/finance/credit-cards/:id — 信用卡欄位更新

功能說明

由管理員直接更新信用卡的欄位資料。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

參數位置類型必填說明
idURL Paramnumber信用卡 ID
cardNumberBodystring卡號
holderNameBodystring持卡人姓名
expiryDateBodystring有效期限
statusBodynumber狀態

POST /api/admin/finance/credit-cards — 新增信用卡

功能說明

由管理員為指定用戶新增信用卡。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

欄位類型必填說明範例
userIdnumber用戶 ID1
cardNumberstring卡號"4111111111111111"
holderNamestring持卡人姓名"王小明"
cvvstringCVV 安全碼"123"
expiryDatestring有效期限"12/28"

DELETE /api/admin/finance/credit-cards/:id — 刪除信用卡

功能說明

由管理員刪除指定的信用卡記錄。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "success": true }
}

8.7 虛擬錢包地址管理

GET /api/admin/finance/crypto-addresses — 虛擬錢包地址列表

功能說明

取得所有用戶的加密貨幣錢包地址列表,支援按用戶 ID、審核狀態、日期範圍及關鍵字篩選。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
userIdQuerynumber用戶 ID1
statusQuerynumber審核狀態1
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
keywordQuerystring搜尋地址/幣種"TRC"
x-site-codeHeaderstring站點代碼"C9"

PATCH /api/admin/finance/crypto-addresses/:id/review — 虛擬錢包地址審核

功能說明

審核用戶提交的虛擬錢包地址,更新審核狀態。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

參數位置類型必填說明
idURL Paramnumber地址 ID
statusBodynumber審核狀態

PATCH /api/admin/finance/crypto-addresses/:id — 虛擬錢包地址欄位更新

功能說明

由管理員直接更新虛擬錢包地址的欄位資料。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

參數位置類型必填說明
idURL Paramnumber地址 ID
walletNameBodystring錢包名稱
currencyBodystring幣種
networkBodystring網路
addressBodystring地址
statusBodynumber狀態

POST /api/admin/finance/crypto-addresses — 新增虛擬錢包地址

功能說明

由管理員為指定用戶新增虛擬錢包地址。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Request

欄位類型必填說明範例
userIdnumber用戶 ID1
walletNamestring錢包名稱"我的 USDT 錢包"
currencystring幣種"USDT"
networkstring網路"TRC20"
addressstring錢包地址"TYDzsYUEr..."

DELETE /api/admin/finance/crypto-addresses/:id — 刪除虛擬錢包地址

功能說明

由管理員刪除指定的虛擬錢包地址記錄。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 finance:write 權限

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "success": true }
}

8.8 提領管理

GET /api/admin/finance/withdrawals — 提領列表

功能說明

取得所有用戶的提領訂單列表,支援按訂單狀態、用戶 ID、關鍵字(搜尋帳號/名稱/地址)、日期範圍篩選。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 withdrawal:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
statusQuerystring訂單狀態"pending"
userIdQuerynumber用戶 ID1
keywordQuerystring搜尋帳號/名稱/地址"user001"
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
x-site-codeHeaderstring站點代碼"C9"

提領狀態流程

pending (待審核) → approved (已核准) → completed (已完成)
                → rejected (已拒絕)

POST /api/admin/finance/withdrawals/:id/review — 提領審核

功能說明

審核指定的提領訂單,可通過 (approve) 或拒絕 (reject)。通過後進入待出款狀態,拒絕時需提供原因。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 withdrawal:write 權限

Request

參數位置類型必填說明範例
idURL Paramnumber提領單 ID1
actionBodystring審核動作"approve""reject"
rejectReasonBodystring拒絕原因"餘額不足"

業務邏輯說明

  • 審核者 email 會記錄在提領單中
  • 拒絕時金額自動退回用戶餘額

POST /api/admin/finance/withdrawals/:id/upload-proof — 上傳代付證明

功能說明

為已核准的提領單上傳代付(轉帳)證明檔案。支援圖片和 PDF 格式,最大 10MB。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 withdrawal:write 權限

Request

Content-Type: multipart/form-data

參數位置類型必填說明
idURL Paramnumber提領單 ID
fileBody (File)File代付證明檔案

檔案限制

  • MIME 類型:image/*application/pdf
  • 最大檔案大小:10MB

業務邏輯說明

  • 檔案上傳至 R2 儲存
  • 檔案路徑記錄在提領單中
  • 操作者 email 記錄於提領單

POST /api/admin/finance/withdrawals/:id/complete — 提領確認出款完成

功能說明

將提領單標記為已完成出款。此為提領流程的最終步驟。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 withdrawal:write 權限

Request

參數位置類型必填說明
idURL Paramnumber提領單 ID

業務邏輯說明

  • 提領單狀態更新為 completed
  • 操作者 email 記錄於提領單
  • 完整提領流程:pending → approved → (upload proof) → completed

8.9 金流群組管理

GET /api/admin/vendor-groups/list — 金流群組列表

功能說明

取得所有金流群組列表。金流群組用於管理不同的入金通道組合,用戶可被分配至不同群組以使用不同的入金方式。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:read 權限

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "name": { "zh-TW": "台灣金流組", "en-US": "TWD Group" },
      "enabled": 1,
      "channelCount": 3,
      "createdAt": "2026-01-01T00:00:00.000Z"
    }
  ]
}

POST /api/admin/vendor-groups/create — 新增金流群組

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:write 權限

Request

欄位類型必填說明範例
nameRecord<string, string>多語系群組名稱{"zh-TW":"台灣金流組","en-US":"TWD Group"}
enablednumber啟用狀態1

PATCH /api/admin/vendor-groups/:id — 更新金流群組

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:write 權限

Request

參數位置類型必填說明
idURL Paramnumber群組 ID
nameBodyRecord<string, string>多語系名稱
enabledBodynumber啟用狀態

DELETE /api/admin/vendor-groups/:id — 刪除金流群組

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:write 權限


GET /api/admin/vendor-groups/:id/channels — 取得群組的通道 ID 列表

功能說明

取得指定金流群組下所有關聯的通道 ID 列表。用於編輯群組時的通道勾選介面。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:read 權限

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [1, 3, 5]
}

PUT /api/admin/vendor-groups/:id/channels — 設定群組的通道關聯

功能說明

設定指定金流群組與通道的多對多關聯。此操作會完整取代現有關聯(先清除再新增)。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:write 權限

Request

參數位置類型必填說明範例
idURL Paramnumber群組 ID1
channelIdsBodynumber[]通道 ID 陣列[1, 3, 5]
json
{
  "channelIds": [1, 3, 5]
}

8.10 金流通道管理

GET /api/admin/vendor-channels/list — 金流通道列表

功能說明

取得所有金流通道列表,可選擇按群組 ID 篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:read 權限

Request

參數位置類型必填說明範例
groupIdQuerynumber群組 ID 篩選1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "name": { "zh-TW": "萬通金流 ATM", "en-US": "Wantong ATM" },
      "storeCode": "WANTONG_001",
      "currency": "TWD",
      "paymentMethods": ["fiat"],
      "enabled": 1,
      "createdAt": "2026-01-01T00:00:00.000Z"
    }
  ]
}

POST /api/admin/vendor-channels/create — 新增金流通道

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:write 權限

Request

欄位類型必填說明範例
nameRecord<string, string>多語系通道名稱{"zh-TW":"萬通 ATM"}
storeCodestring金流商店代碼"WANTONG_001"
secret1string密鑰 1"sk_..."
secret2string密鑰 2-
secret3string密鑰 3-
secret4string密鑰 4-
currencystring幣別"TWD"
paymentMethodsstring[]支付方式["fiat", "credit"]
paymentAddressstring收款地址-
enablednumber啟用狀態1

PATCH /api/admin/vendor-channels/:id — 更新金流通道

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:write 權限

Request

參數位置類型必填說明
idURL Paramnumber通道 ID
其他BodyRecord<string, any>任意可更新欄位

DELETE /api/admin/vendor-channels/:id — 刪除金流通道

認證需求AdminJwtAuthGuard + PermissionsGuard,需 vendor:write 權限


9. 報表管理

GET /api/admin/reports/players — 玩家報表

功能說明

取得玩家報表,包含投注統計、存款統計等彙總資料。支援多條件篩選,包括關鍵字搜尋、VIP 等級篩選(含比較運算子)、錢包資訊篩選(銀行卡/信用卡/加密地址模糊搜尋)、線上狀態篩選等。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
keywordQuerystring搜尋帳號/名稱"user001"
vipLevelQuerystringVIP 等級"5"
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
bankAccountQuerystring銀行卡帳號(模糊搜尋)"1234"
cardNumberQuerystring信用卡卡號(模糊搜尋)"4111"
cryptoAddressQuerystring加密錢包地址(模糊搜尋)"TYDz"
vipOpQuerystringVIP 比較運算子"gt", "eq", "lt"
vipFilterLevelQuerystringVIP 比較等級 (1-15)"5"
onlineQuerystring僅顯示線上玩家"true"
x-site-codeHeaderstring站點代碼"C9"

VIP 比較運算子 (vipOp)

說明
gt大於指定等級
eq等於指定等級
lt小於指定等級

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "account": "user001",
        "name": "王小明",
        "balance": "1000.500000",
        "vipLevel": 3,
        "totalDeposit": "5000.000000",
        "totalWithdrawal": "2000.000000",
        "totalEffectiveBet": "50000.000000",
        "totalWinLoss": "-500.000000",
        "lastLoginAt": "2026-03-01T08:00:00.000Z",
        "siteCode": "C9",
        "createdAt": "2026-01-15T00:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 100, "totalPages": 5 }
  }
}

業務邏輯說明

  • 所有金額以 USD decimal(18,6) 表示
  • totalWinLoss 為負值表示平台盈利(玩家虧損)
  • vipOp + vipFilterLevel 組合使用:如 vipOp=gt&vipFilterLevel=5 表示篩選 VIP > 5 的玩家
  • online 篩選使用登入紀錄的最近活動時間判斷
  • 銀行卡/信用卡/加密地址搜尋透過 JOIN 子查詢實現

前端注意事項

  • 已支援多站點 SiteTabs(使用 useMultiSiteTabs hook)
  • VIP 篩選使用 Select 下拉元件
  • 支援 CSV 匯出(透過 /reports/export/players

GET /api/admin/reports/vip-players — VIP 玩家查詢

功能說明

專門查詢 VIP 玩家的報表,提供比基礎玩家報表更豐富的 VIP 相關篩選條件,包含 VIP 等級範圍、層級、保級狀態、VIP 鎖定、累積投注範圍等。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
keywordQuerystring搜尋帳號/名稱"user001"
vipLevelMinQuerystringVIP 等級下限 (1-15)"3"
vipLevelMaxQuerystringVIP 等級上限 (1-15)"10"
tierQuerystring層級"gold"
relegationStatusQuerystring保級狀態"0"
vipHoldQuerystringVIP 鎖定"1"
minBetQuerystring最低累積投注 (USD)"10000"
maxBetQuerystring最高累積投注 (USD)"100000"
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
x-site-codeHeaderstring站點代碼"C9"

層級 (tier)

說明
bronze青銅
gold黃金
platinum白金
diamond鑽石

保級狀態 (relegationStatus)

說明
0正常
1警告
2降級風險

VIP 鎖定 (vipHold)

說明
0未鎖定
1已鎖定(VIP 5+ 支援)

業務邏輯說明

  • 後端使用 @AdminSiteCode() 進行站點篩選
  • minBet / maxBet 用於篩選累積有效投注額範圍
  • VIP 鎖定的玩家在月度保級檢查時不會被降級

GET /api/admin/reports/bet-records — 投注紀錄報表

功能說明

取得全站投注紀錄報表,支援按用戶 ID、遊戲類型、投注狀態、日期範圍篩選。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
userIdQuerynumber用戶 ID1
keywordQuerystring搜尋關鍵字"user001"
gameTypeQuerystring遊戲類型"slot"
statusQuerystring投注狀態"valid"
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
x-site-codeHeaderstring站點代碼"C9"

遊戲類型 (gameType)

代碼數值說明
SPORTS1體育
SLOT2老虎機
LIVE3真人
LOTTERY4彩票
CHESS5棋牌
ESPORTS8電競
CRYPTO9加密遊戲
FISH10捕魚

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "userId": 5,
        "userAccount": "user001",
        "gameType": "slot",
        "gameName": "Fortune Tiger",
        "providerCode": "rsg",
        "betAmount": "10.000000",
        "betEffective": "10.000000",
        "payout": "25.000000",
        "winLoss": "15.000000",
        "status": "valid",
        "siteCode": "C9",
        "betTime": "2026-03-01T10:30:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 500, "totalPages": 25 }
  }
}

GET /api/admin/reports/overview — 總體報表

功能說明

取得全站摘要統計資料,包含總存款、總提款、總投注、總盈虧、活躍用戶數等。可按日期範圍篩選。支援每日摘要表格。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
x-site-codeHeaderstring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "summary": {
      "totalDeposit": "500000.000000",
      "totalWithdrawal": "300000.000000",
      "totalBet": "2000000.000000",
      "totalPayout": "1900000.000000",
      "totalWinLoss": "-100000.000000",
      "activeUsers": 150,
      "newUsers": 30,
      "depositCount": 500,
      "withdrawalCount": 200
    },
    "dailySummary": [
      {
        "date": "2026-03-01",
        "deposit": "10000.000000",
        "withdrawal": "5000.000000",
        "bet": "50000.000000",
        "payout": "48000.000000",
        "winLoss": "-2000.000000",
        "activeUsers": 45,
        "newUsers": 5
      }
    ]
  }
}

業務邏輯說明

  • totalWinLoss 為負值表示平台盈利(玩家總虧損)
  • dailySummary 按日期分組提供每日統計
  • 所有金額以 USD decimal(18,6) 表示

GET /api/admin/reports/profit-loss — 損益報表

功能說明

取得損益報表,支援按日、週、月分組統計。包含投注額、派彩額、盈虧等數據。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
groupByQuerystring分組方式"day", "week", "month"
x-site-codeHeaderstring站點代碼"C9"

分組方式 (groupBy)

說明
day按日分組(預設)
week按週分組
month按月分組

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "period": "2026-03-01",
      "totalBet": "50000.000000",
      "totalPayout": "48000.000000",
      "profitLoss": "2000.000000",
      "betCount": 1200,
      "uniqueUsers": 45
    }
  ]
}

GET /api/admin/reports/games — 遊戲報表

功能說明

取得遊戲統計報表,按遊戲類型和遊戲平台(供應商)分組統計投注額、派彩額、盈虧等。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
gamePlatformQuerystring遊戲平台(供應商代碼)"rsg"
x-site-codeHeaderstring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "gameType": "slot",
      "gameTypeName": "老虎機",
      "gamePlatform": "rsg",
      "totalBet": "100000.000000",
      "totalPayout": "95000.000000",
      "profitLoss": "5000.000000",
      "betCount": 5000,
      "uniqueUsers": 80
    }
  ]
}

GET /api/admin/reports/promos — 優惠報表

功能說明

取得活動領取統計報表,顯示每個活動的領取次數、發放總額等。支援分頁和日期範圍篩選。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
x-site-codeHeaderstring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "promoId": 1,
        "promoTitle": "新手首存禮",
        "claimCount": 150,
        "totalReward": "1500.000000",
        "uniqueClaimers": 120,
        "tag": "新手",
        "enabled": 1
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 5, "totalPages": 1 }
  }
}

GET /api/admin/reports/player-summary — 玩家簡表

功能說明

取得精簡的用戶列表,包含餘額、累積有效投注、VIP 等級等關鍵指標。支援排序(按餘額、投注、建立時間、VIP 等級)。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
keywordQuerystring搜尋關鍵字"user001"
sortByQuerystring排序欄位"balance"
sortOrderQuerystring排序方向"ASC""DESC"
x-site-codeHeaderstring站點代碼"C9"

排序欄位 (sortBy)

說明
balance餘額
totalEffectiveBet累積有效投注
createdAt建立時間
vipLevelVIP 等級

GET /api/admin/reports/r2-logs — R2 操作紀錄

功能說明

查詢 Cloudflare R2 雲端儲存的操作紀錄,包含檔案上傳、刪除等操作。支援按模組、動作、管理員 ID、關鍵字(檔案路徑/檔名)及日期範圍篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
moduleQuerystring模組"site-config", "promo", "storage"
actionQuerystring動作"upload", "delete"
adminIdQuerynumber管理員 ID1
keywordQuerystring搜尋檔案路徑/檔名"logo"
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "adminId": 1,
        "adminEmail": "admin@example.com",
        "action": "upload",
        "module": "storage",
        "objectKey": "C9/images/logo.png",
        "originalName": "logo.png",
        "fileSize": 102400,
        "mimeType": "image/png",
        "ip": "127.0.0.1",
        "userAgent": "Mozilla/5.0...",
        "detail": null,
        "createdAt": "2026-03-01T10:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 50, "totalPages": 3 }
  }
}

前端注意事項

  • 前端已從報表模組移至 /system/cloud-storage-logs 路由
  • 支援 OS/Browser 解析(從 userAgent 欄位)
  • 支援 Detail Dialog 展開完整資訊
  • 顯示 mimeType 欄位

GET /api/admin/reports/export/:type — 匯出報表 (CSV)

功能說明

將指定類型的報表匯出為 CSV 格式。回傳 CSV 字串內容及建議的檔案名稱。支援所有報表類型的匯出。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 report:read 權限

Request

參數位置類型必填說明範例
typeURL Paramstring報表類型"players"
其他QueryRecord<string, string>各報表的篩選參數-
x-site-codeHeaderstring站點代碼"C9"

支援的報表類型 (type)

說明
players玩家報表
bet-records投注紀錄
deposits存款紀錄
overview總覽
profit-loss損益報表
games遊戲報表
promos活動報表
player-summary玩家簡表
history歷史紀錄

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "csv": "帳號,名稱,餘額,VIP等級\nuser001,王小明,1000.50,3\n...",
    "filename": "players-2026-03-01.csv"
  }
}

前端注意事項

  • 使用 ExportButton 元件觸發匯出
  • 前端接收 CSV 字串後建立 Blob 下載
  • 檔案名稱格式:{type}-{date}.csv

10. 風控管理

10.1 IP 黑白名單

GET /api/admin/risk/ip-rules — IP 黑白名單列表

功能說明

取得 IP 黑白名單列表,支援按類型(黑名單/白名單)、IP 關鍵字篩選。黑名單中的 IP 將被阻止存取前台;白名單中的 IP 將跳過部分安全檢查。支援多站點篩選,每個站點可獨立配置 IP 規則。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
typeQuerystring規則類型"blacklist""whitelist"
keywordQuerystringIP 關鍵字搜尋"192.168"
x-site-codeHeaderstring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "ip": "192.168.1.100",
        "type": "blacklist",
        "remark": "異常登入行為",
        "siteCode": "C9",
        "createdAt": "2026-02-28T00:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 5, "totalPages": 1 }
  }
}

前端注意事項

  • 已支援多站點 SiteTabs + FilterBar 統一風格
  • 類型篩選使用 Select 下拉元件

POST /api/admin/risk/ip-rules — 新增 IP 規則

功能說明

新增一筆 IP 黑名單或白名單規則。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:write 權限

Request

欄位類型必填說明範例
ipstringIP 地址"192.168.1.100"
typestring規則類型"blacklist""whitelist"
remarkstring備註"異常登入行為"

業務邏輯說明

  • siteCode@AdminSiteCode() 自動帶入,若未提供則預設為 "C9"
  • IP 支援單一地址格式

PATCH /api/admin/risk/ip-rules/:id — 更新 IP 規則

功能說明

更新指定 IP 規則的內容。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:write 權限

Request

參數位置類型必填說明
idURL Paramnumber規則 ID
ipBodystringIP 地址
typeBodystring規則類型
remarkBodystring備註

DELETE /api/admin/risk/ip-rules/:id — 刪除 IP 規則

功能說明

刪除指定的 IP 黑白名單規則。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:write 權限

Request

參數位置類型必填說明
idURL Paramnumber規則 ID

10.2 登入失敗紀錄

GET /api/admin/risk/login-failures — 登入失敗列表

功能說明

查詢前台用戶的登入失敗紀錄,支援按關鍵字(帳號/IP/裝置等)和日期範圍篩選。用於監控異常登入行為和潛在的暴力破解攻擊。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
keywordQuerystring搜尋關鍵字"user001"
startDateQuerystring起始日期"2026-01-01"
endDateQuerystring結束日期"2026-12-31"
x-site-codeHeaderstring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "account": "user001",
        "ip": "203.0.113.50",
        "device": "Mozilla/5.0 (Windows NT 10.0)...",
        "reason": "password_mismatch",
        "siteCode": "C9",
        "createdAt": "2026-03-01T08:30:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 10, "totalPages": 1 }
  }
}

業務邏輯說明

  • 資料來源為 auth-user-login-log 表中 action 為失敗的記錄
  • keyword 搜尋範圍包含帳號、IP、裝置資訊

10.3 IP/FP 查詢

GET /api/admin/risk/lookup — 風控關鍵字查詢

功能說明

透過 IP 地址、裝置指紋 (Fingerprint)、帳號、姓名、Email 或手機號碼反查關聯的用戶資訊。此功能用於風控調查,可找出使用相同 IP 或裝置的多個帳號(潛在多開/作弊行為)。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:read 權限

Request

參數位置類型必填說明範例
keywordQuerystring查詢關鍵字"192.168.1.100"
x-site-codeHeaderstring站點代碼"C9"

keyword 支援的搜尋範圍

  • IP 地址(精確或模糊匹配)
  • 裝置指紋(FingerprintJS 產生的 hash)
  • 用戶帳號
  • 用戶姓名
  • Email 地址
  • 手機號碼

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "userId": 1,
      "account": "user001",
      "name": "王小明",
      "email": "user@example.com",
      "mobile": "0912345678",
      "ip": "192.168.1.100",
      "device": "abc123fingerprint",
      "lastLogin": "2026-03-01T08:00:00.000Z",
      "siteCode": "C9"
    },
    {
      "userId": 5,
      "account": "user005",
      "name": "李大華",
      "ip": "192.168.1.100",
      "device": "abc123fingerprint",
      "lastLogin": "2026-03-01T09:00:00.000Z",
      "siteCode": "C9"
    }
  ]
}

業務邏輯說明

  • 查詢 auth-user-login-log 表及 auth-user
  • 相同 IP 或裝置指紋出現在多個帳號表示可能的異常行為
  • 裝置指紋由前端 FingerprintJS 產生,登入/註冊時傳入後端記錄

前端注意事項

  • 已支援多站點 SiteTabs + FilterBar 統一風格
  • 結果以表格形式展示,同一 IP/FP 出現多筆記錄時可高亮顯示

10.4 遊戲黑名單

GET /api/admin/risk/game-blacklist — 遊戲黑名單列表

功能說明

取得遊戲黑名單列表。可將特定用戶封鎖於全部遊戲、特定類型遊戲、或特定遊戲。支援按用戶 ID、遊戲類型篩選。支援多站點篩選。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:read 權限

Request

參數位置類型必填說明範例
pageQuerynumber頁碼1
pageSizeQuerynumber每頁筆數20
userIdQuerynumber用戶 ID1
gameTypeQuerystring遊戲類型"slot"
x-site-codeHeaderstring站點代碼"C9"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "userId": 5,
        "userAccount": "user005",
        "gameType": null,
        "productId": null,
        "remark": "作弊行為,封鎖所有遊戲",
        "siteCode": "C9",
        "createdAt": "2026-02-28T00:00:00.000Z"
      },
      {
        "id": 2,
        "userId": 10,
        "userAccount": "user010",
        "gameType": "slot",
        "productId": null,
        "remark": "封鎖老虎機類型",
        "siteCode": "C9",
        "createdAt": "2026-02-28T00:00:00.000Z"
      },
      {
        "id": 3,
        "userId": 15,
        "userAccount": "user015",
        "gameType": "slot",
        "productId": 123,
        "remark": "封鎖特定遊戲",
        "siteCode": "C9",
        "createdAt": "2026-02-28T00:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "total": 3, "totalPages": 1 }
  }
}

封鎖層級說明

gameTypeproductId效果
nullnull封鎖用戶所有遊戲
"slot"null封鎖用戶該類型所有遊戲
"slot"123封鎖用戶特定遊戲

POST /api/admin/risk/game-blacklist — 新增遊戲黑名單

功能說明

將指定用戶加入遊戲黑名單。可設定封鎖範圍:全部遊戲、特定類型遊戲、或特定遊戲。封鎖後該用戶啟動遊戲時會收到錯誤碼 5010

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:write 權限

Request

欄位類型必填說明範例
userIdnumber用戶 ID5
gameTypestring遊戲類型(null=全部)"slot"
productIdnumber遊戲產品 ID(null=該類型全部)123
remarkstring備註"作弊行為"
json
{
  "userId": 5,
  "gameType": null,
  "productId": null,
  "remark": "作弊行為,封鎖所有遊戲"
}

業務邏輯說明

  • siteCode@AdminSiteCode() 自動帶入,若未提供則預設為 "C9"
  • 遊戲啟動時 GameService 會呼叫 AdminRiskService.isUserBlockedFromGame() 檢查封鎖狀態
  • 封鎖檢查順序:全部封鎖 > 類型封鎖 > 特定遊戲封鎖
  • 被封鎖的用戶啟動遊戲時收到錯誤碼 5010

DELETE /api/admin/risk/game-blacklist/:id — 刪除遊戲黑名單

功能說明

移除指定的遊戲黑名單記錄,解除該用戶的遊戲封鎖。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 risk:write 權限

Request

參數位置類型必填說明
idURL Paramnumber黑名單 ID

11. R2 雲端儲存

所有 R2 儲存管理端點使用 storage:manage 權限,操作 Cloudflare R2(S3 相容)物件儲存。 每個操作都會自動記錄至 r2-operation-log 資料表,用於稽核追蹤。

GET /api/admin/r2/list — R2 檔案列表

功能說明

列出指定路徑前綴下的所有檔案和資料夾。支援分頁式瀏覽(使用 continuation token),可指定站點 ID 使用該站點的 R2 設定(不同站點可能使用不同的 R2 bucket)。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 storage:manage 權限

Request

參數位置類型必填說明範例
prefixQuerystring路徑前綴(資料夾路徑)"C9/images/"
continuationTokenQuerystring分頁 token(上一次回傳的)"abc123..."
maxKeysQuerynumber每頁最大筆數(預設 100)100
siteConfigIdQuerynumber指定站點 ID1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "folders": [
      { "name": "images/", "prefix": "C9/images/" },
      { "name": "promos/", "prefix": "C9/promos/" }
    ],
    "files": [
      {
        "key": "C9/logo.png",
        "size": 102400,
        "lastModified": "2026-02-28T10:00:00.000Z",
        "etag": "\"abc123\"",
        "url": "https://cdn.example.com/C9/logo.png"
      }
    ],
    "isTruncated": false,
    "nextContinuationToken": null,
    "keyCount": 3
  }
}

業務邏輯說明

  • 使用 S3 ListObjectsV2Command 實現
  • prefix 必須以 / 結尾才能瀏覽資料夾內容
  • foldersfiles 分開回傳,方便前端分別渲染資料夾和檔案
  • url 為公開存取 URL(使用 R2_PUBLIC_URL 環境變數拼接)
  • siteConfigId 可指定不同站點的 R2 配置

前端注意事項

  • 前端實作檔案管理器介面,以樹狀結構展示
  • 支援點擊資料夾進入子目錄
  • 使用 continuationToken 實現「載入更多」功能

POST /api/admin/r2/upload — R2 上傳檔案

功能說明

上傳一或多個檔案至 R2 儲存。支援批次上傳(最多同時 20 個檔案),每個檔案最大 50MB。上傳後自動記錄操作紀錄,包含管理員 ID、檔案路徑、原始檔名、檔案大小、MIME 類型、操作者 IP 及 User-Agent。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 storage:manage 權限

Request

Content-Type: multipart/form-data

欄位類型必填說明範例
filesFile[]檔案陣列(最多 20 個)-
prefixstring上傳路徑前綴(資料夾路徑)"C9/images/"
siteConfigIdstring指定站點 ID"1"

檔案限制

  • 最多同時上傳 20 個檔案
  • 單一檔案最大:50MB
  • 不限制 MIME 類型

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "uploaded": [
      {
        "key": "C9/images/banner.jpg",
        "url": "https://cdn.example.com/C9/images/banner.jpg",
        "size": 204800
      },
      {
        "key": "C9/images/icon.png",
        "url": "https://cdn.example.com/C9/images/icon.png",
        "size": 51200
      }
    ]
  }
}

業務邏輯說明

  • 檔案儲存路徑:{prefix}{originalFilename}
  • 使用 memoryStorage() 暫存上傳檔案於記憶體
  • 每個檔案上傳完成後立即記錄操作紀錄
  • 操作紀錄包含:adminIdaction: 'upload'module: 'storage'objectKeyoriginalNamefileSizemimeTypeipuserAgent

前端注意事項

  • 使用 FormData 組裝多檔案上傳
  • 顯示上傳進度條
  • 上傳完成後刷新檔案列表

POST /api/admin/r2/delete — R2 批次刪除檔案

功能說明

批次刪除指定的 R2 檔案。傳入檔案 key 陣列,系統會逐一刪除並記錄操作紀錄。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 storage:manage 權限

Request

欄位類型必填說明範例
keysstring[]檔案 key 陣列["C9/images/old.jpg", "C9/images/temp.png"]
siteConfigIdnumber指定站點 ID1
json
{
  "keys": ["C9/images/old.jpg", "C9/images/temp.png"],
  "siteConfigId": 1
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "deleted": 2
  }
}

業務邏輯說明

  • 使用 S3 DeleteObjectsCommand 批次刪除
  • 每個被刪除的 key 都會獨立記錄操作紀錄
  • 不存在的 key 不會報錯(靜默跳過)

POST /api/admin/r2/move — R2 移動檔案/資料夾

功能說明

移動(重新命名)檔案或資料夾。實際操作為複製到新位置後刪除原始檔案。若為資料夾移動,會遞迴處理資料夾下所有檔案。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 storage:manage 權限

Request

欄位類型必填說明範例
sourceKeystring原始路徑"C9/images/old.jpg"
destinationKeystring目標路徑"C9/images/new.jpg"
isFolderboolean是否為資料夾移動false
siteConfigIdnumber指定站點 ID1
json
{
  "sourceKey": "C9/images/old-folder/",
  "destinationKey": "C9/images/new-folder/",
  "isFolder": true,
  "siteConfigId": 1
}

業務邏輯說明

  • 檔案移動:CopyObject + DeleteObject
  • 資料夾移動:列出資料夾下所有物件,逐一複製到新前綴,再批次刪除原始物件
  • 操作紀錄記錄 sourceKeydestinationKey

前端注意事項

  • 支援拖放式移動檔案/資料夾
  • 資料夾移動可能需要較長時間,建議顯示處理中狀態

POST /api/admin/r2/create-folder — R2 建立資料夾

功能說明

在 R2 儲存中建立新的資料夾(實際上是建立一個以 / 結尾的空物件)。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 storage:manage 權限

Request

欄位類型必填說明範例
prefixstring父資料夾路徑"C9/images/"
namestring新資料夾名稱"banners"
siteConfigIdstring指定站點 ID"1"
json
{
  "prefix": "C9/images/",
  "name": "banners",
  "siteConfigId": "1"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "key": "C9/images/banners/"
  }
}

業務邏輯說明

  • 實際操作為 PutObject,建立一個 key 為 {prefix}{name}/ 的空物件
  • S3 本身無資料夾概念,以 / 結尾的空物件模擬資料夾
  • 操作紀錄記錄建立的資料夾路徑

POST /api/admin/r2/delete-folder — R2 刪除資料夾(遞迴)

功能說明

遞迴刪除指定的資料夾及其下所有檔案和子資料夾。此為破壞性操作,不可復原。

認證需求AdminJwtAuthGuard + PermissionsGuard,需 storage:manage 權限

Request

欄位類型必填說明範例
prefixstring資料夾路徑(必須以 / 結尾)"C9/images/temp/"
siteConfigIdstring指定站點 ID"1"
json
{
  "prefix": "C9/images/temp/",
  "siteConfigId": "1"
}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "deleted": 15
  }
}

業務邏輯說明

  • prefix 必須以 / 結尾,否則回傳 400 Bad Request
  • 先列出 prefix 下所有物件,再批次刪除
  • deleted 回傳實際刪除的物件數量
  • 操作紀錄記錄 deletedCount

前端注意事項

  • 此為高風險操作,前端必須使用 useConfirm() 彈出確認對話框
  • 建議在確認文字中顯示資料夾名稱和預估的檔案數量
  • 刪除完成後刷新檔案列表

附錄

端點總覽表

#方法路徑權限說明
1POST/admin/login管理員登入
2POST/admin/send-verify-code發送驗證碼
3POST/admin/verify-email驗證 Email
4POST/admin/register管理員註冊
5GET/admin/profile登入取得個人資料
6POST/admin/google-auth/generate登入產生 2FA QR Code
7POST/admin/google-auth/enable登入啟用 2FA
8POST/admin/google-auth/disable登入停用 2FA
9GET/admin/permissions/all登入取得權限列表
10GET/admin/listadmin:read管理員列表
11GET/admin/:idadmin:read取得單一管理員
12POST/admin/createadmin:write建立管理員
13PATCH/admin/:idadmin:write更新管理員
14DELETE/admin/:idadmin:write刪除管理員
15GET/admin/groups/listadmin-group:read群組列表
16GET/admin/groups/:idadmin-group:read取得單一群組
17POST/admin/groups/createadmin-group:write建立群組
18PATCH/admin/groups/:idadmin-group:write更新群組
19DELETE/admin/groups/:idadmin-group:write刪除群組
20GET/admin/logs/listadmin-log:read操作紀錄列表
21GET/admin/promos/listpromo:read活動列表
22GET/admin/promos/:idpromo:read取得單一活動
23POST/admin/promos/createpromo:write建立活動
24PATCH/admin/promos/:idpromo:write更新活動
25DELETE/admin/promos/:idpromo:write刪除活動
26GET/admin/promo-tags/listpromo-tag:read標籤列表
27POST/admin/promo-tags/createpromo-tag:write建立標籤
28PATCH/admin/promo-tags/:idpromo-tag:write更新標籤
29DELETE/admin/promo-tags/:idpromo-tag:write刪除標籤
30GET/admin/finance/deposit-review-存款訂單列表
31PATCH/admin/finance/deposit-review/:id-審核存款
32GET/admin/finance/usersuser:read用戶列表
33GET/admin/finance/users/:iduser:read用戶詳情
34PATCH/admin/finance/users/:iduser:write更新用戶
35PATCH/admin/users/:userId/vendor-groupuser:write更新金流群組
36POST/admin/finance/adjust-balancefinance:write人工調帳
37GET/admin/finance/bank-cardsfinance:read銀行卡列表
38PATCH/admin/finance/bank-cards/:id/reviewfinance:write銀行卡審核
39PATCH/admin/finance/bank-cards/:idfinance:write更新銀行卡
40POST/admin/finance/bank-cardsfinance:write新增銀行卡
41DELETE/admin/finance/bank-cards/:idfinance:write刪除銀行卡
42GET/admin/finance/credit-cardsfinance:read信用卡列表
43PATCH/admin/finance/credit-cards/:id/reviewfinance:write信用卡審核
44PATCH/admin/finance/credit-cards/:idfinance:write更新信用卡
45POST/admin/finance/credit-cardsfinance:write新增信用卡
46DELETE/admin/finance/credit-cards/:idfinance:write刪除信用卡
47GET/admin/finance/crypto-addressesfinance:read虛擬錢包列表
48PATCH/admin/finance/crypto-addresses/:id/reviewfinance:write虛擬錢包審核
49PATCH/admin/finance/crypto-addresses/:idfinance:write更新虛擬錢包
50POST/admin/finance/crypto-addressesfinance:write新增虛擬錢包
51DELETE/admin/finance/crypto-addresses/:idfinance:write刪除虛擬錢包
52GET/admin/finance/withdrawalswithdrawal:read提領列表
53POST/admin/finance/withdrawals/:id/reviewwithdrawal:write提領審核
54POST/admin/finance/withdrawals/:id/upload-proofwithdrawal:write上傳代付證明
55POST/admin/finance/withdrawals/:id/completewithdrawal:write確認出款完成
56GET/admin/vendor-groups/listvendor:read金流群組列表
57POST/admin/vendor-groups/createvendor:write新增金流群組
58PATCH/admin/vendor-groups/:idvendor:write更新金流群組
59DELETE/admin/vendor-groups/:idvendor:write刪除金流群組
60GET/admin/vendor-groups/:id/channelsvendor:read群組通道列表
61PUT/admin/vendor-groups/:id/channelsvendor:write設定群組通道
62GET/admin/vendor-channels/listvendor:read金流通道列表
63POST/admin/vendor-channels/createvendor:write新增金流通道
64PATCH/admin/vendor-channels/:idvendor:write更新金流通道
65DELETE/admin/vendor-channels/:idvendor:write刪除金流通道
66GET/admin/reports/playersreport:read玩家報表
67GET/admin/reports/vip-playersreport:readVIP 玩家查詢
68GET/admin/reports/bet-recordsreport:read投注紀錄
69GET/admin/reports/overviewreport:read總體報表
70GET/admin/reports/profit-lossreport:read損益報表
71GET/admin/reports/gamesreport:read遊戲報表
72GET/admin/reports/promosreport:read優惠報表
73GET/admin/reports/player-summaryreport:read玩家簡表
74GET/admin/reports/r2-logsreport:readR2 操作紀錄
75GET/admin/reports/export/:typereport:read匯出報表 CSV
76GET/admin/risk/ip-rulesrisk:readIP 規則列表
77POST/admin/risk/ip-rulesrisk:write新增 IP 規則
78PATCH/admin/risk/ip-rules/:idrisk:write更新 IP 規則
79DELETE/admin/risk/ip-rules/:idrisk:write刪除 IP 規則
80GET/admin/risk/login-failuresrisk:read登入失敗列表
81GET/admin/risk/lookuprisk:read風控關鍵字查詢
82GET/admin/risk/game-blacklistrisk:read遊戲黑名單列表
83POST/admin/risk/game-blacklistrisk:write新增遊戲黑名單
84DELETE/admin/risk/game-blacklist/:idrisk:write刪除遊戲黑名單
85GET/admin/r2/liststorage:manageR2 檔案列表
86POST/admin/r2/uploadstorage:manageR2 上傳檔案
87POST/admin/r2/deletestorage:manageR2 批次刪除
88POST/admin/r2/movestorage:manageR2 移動檔案
89POST/admin/r2/create-folderstorage:manageR2 建立資料夾
90POST/admin/r2/delete-folderstorage:manageR2 刪除資料夾

本文件由 AI 自動產生,對應原始碼位置:/Users/chenshoupei/c9/c9-be/src/modules/admin/admin.controller.ts

C9 Platform API 文件 — Part 5:存款、提領、錢包、金流商、活動、站內信、站點配置

本文件涵蓋 7 大模組共 50+ 個端點,適用於 PM、前端開發、UI 設計師參考。 所有金額均使用 USD decimal(18,6),截斷規則:Math.floor(value * 1e6) / 1e6。 多站點架構:所有端點均支援 site-name header 白牌路由。


Deposit 存款模組

路由前綴:/api/deposit 存款是平台核心金流入口。前端統一透過 POST /deposit 建單,後端根據 channelId + paymentMethod 自動路由至對應金流商(萬通 ATM / 萬通信用卡 / USDT 加密貨幣)。匯率由台灣銀行即時匯率 API 及 Binance Public API 提供。


GET /api/deposit/exchange-rate — 取得法幣匯率

功能說明 取得以 USD 為基底的即時匯率資訊。資料來源為台灣銀行即時匯率 API (tw-exchange)。前端在存款頁面載入時應呼叫此端點,讓用戶看到當前匯率,計算存入金額對應的 USD 數量。若不帶 currency 參數則回傳所有可用幣別的匯率。

認證需求:無(公開端點)

Request

位置參數型別必填說明範例
Querycurrencystring幣別代碼,不帶則回傳全部TWD
QuerybankCodestring銀行代碼(預設 twbktwbk

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "time": "2026/02/22 14:30",
    "base": "USD",
    "TWD": { "buy": 31.82, "sell": 32.15 }
  }
}

錯誤碼

錯誤碼說明
2001銀行代碼格式錯誤
2002匯率解析失敗(台銀 API 不可用時)

業務邏輯說明

  • buy 為銀行買入匯率(用戶賣出外幣換台幣);sell 為銀行賣出匯率(用戶買入外幣)
  • 存款時使用 sell 匯率(用戶用台幣換 USD)
  • 匯率資料會依銀行開盤時間更新,非營業時間可能返回最後一次報價
  • time 為匯率更新時間,格式 YYYY/MM/DD HH:mm

前端注意事項 / UI 設計提示

  • 存款頁面載入時呼叫取得匯率,顯示「1 USD ≈ XX TWD」
  • 建議每 5 分鐘輪詢更新匯率(匯率會隨市場波動)
  • 匯率僅供參考,實際入帳以建單時鎖定的匯率為準

GET /api/deposit/crypto-rate — 取得加密貨幣匯率

功能說明 取得以 USDT 為基底的加密貨幣即時匯率。資料來源為 Binance Public API。前端在用戶選擇加密貨幣入金時呼叫此端點,顯示各幣種對 USDT 的當前價格。若不帶 symbol 參數則回傳所有支援幣種(BTC、ETH、BNB、SOL、TRX)。

認證需求:無(公開端點)

Request

位置參數型別必填說明範例
Querysymbolstring幣種代碼,不帶則回傳全部BTC

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "base": "USDT",
    "time": "2026-02-27T08:00:00.000Z",
    "BTC": { "price": 95432.5 },
    "ETH": { "price": 2654.3 },
    "BNB": { "price": 652.1 },
    "SOL": { "price": 178.5 },
    "TRX": { "price": 0.245 }
  }
}

錯誤碼

錯誤碼說明
2007加密貨幣匯率取得失敗(Binance API 不可用)

業務邏輯說明

  • 價格代表「1 個該幣種 = 多少 USDT」
  • USDT 入金時不需匯率轉換(1 USDT = 1 USD 在系統內等值處理)
  • Binance API 為公開端點,無需認證

前端注意事項 / UI 設計提示

  • 加密貨幣存款頁面顯示即時匯率供用戶參考
  • 建議搭配幣種圖示(BTC/ETH/BNB/SOL/TRX)顯示
  • 匯率波動大,建議每 1-2 分鐘更新

GET /api/deposit/channels — 取得金流通道(含匯率)

功能說明 取得當前用戶可用的金流通道列表,同時附帶各通道的即時匯率。後端會根據用戶所屬的金流群組(vendor-group),回傳該群組下所有啟用的金流通道。此端點整合了通道資訊與匯率,前端一次呼叫即可取得存款所需的全部資訊。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer {JWT Token}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "groupId": 1,
    "groupName": "default",
    "channels": [
      {
        "id": 1,
        "name": "wantong",
        "currency": "TWD",
        "paymentMethods": ["fiat", "credit"],
        "paymentAddress": null,
        "enabled": 1,
        "exchangeRate": { "buy": 31.82, "sell": 32.15 }
      },
      {
        "id": 2,
        "name": "usdt",
        "currency": "USDT",
        "paymentMethods": ["crypto"],
        "paymentAddress": "TXqH4j5xGZz8vKJm9bN2rL7pWcYfDsAeUo",
        "network": "TRC-20",
        "enabled": 1,
        "exchangeRate": null
      }
    ]
  }
}

業務邏輯說明

  • 用戶註冊時會依語系自動分配金流群組(zh-TW → TWD 群組)
  • paymentMethods 陣列表示該通道支援的支付方式:fiat(ATM 轉帳)、credit(信用卡)、crypto(加密貨幣)
  • USDT 通道的 paymentAddress 為平台收款錢包地址,network 為鏈路(如 TRC-20)
  • exchangeRate 為即時匯率,USDT 通道為 null(1:1 等值)
  • enabled = 0 表示通道暫停使用

前端注意事項 / UI 設計提示

  • 根據 paymentMethods 顯示對應的支付方式選項卡(ATM / 信用卡 / 加密貨幣)
  • USDT 通道需顯示 paymentAddress 讓用戶複製,並標註 network 提醒使用正確鏈路
  • 隱藏 enabled = 0 的通道或顯示為「維護中」
  • 匯率動態顯示在金額輸入框旁(如「≈ 31.82 TWD/USD」)

GET /api/deposit/orders — 取得存款訂單列表

功能說明 取得當前登入用戶的存款訂單歷史紀錄,支援分頁與狀態篩選。回傳的訂單不含 callbackData(金流商回調原始資料,僅後台管理員可見)。前端在「存款紀錄」頁面呼叫此端點。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {JWT Token}
Querypagenumber頁碼(預設 1)1
QuerypageSizenumber每頁筆數(預設 10,最大 50)10
Querystatusstring訂單狀態篩選pending / created / paid / failed
QuerystartDatestring開始日期 (YYYY-MM-DD)2026-01-01
QueryendDatestring結束日期 (YYYY-MM-DD)2026-02-28

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "channelName": "wantong",
        "currency": "TWD",
        "subOrder": "ORD20260222001",
        "orderAmount": 1000,
        "paymentMethod": "fiat",
        "status": "paid",
        "payAmount": 1000,
        "payTime": "2026-02-22 15:30:00",
        "usdAmount": "31.250000",
        "exchangeRate": "32.0000000000",
        "createdAt": "2026-02-22T07:00:00.000Z",
        "updatedAt": "2026-02-22T07:30:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 1,
      "totalPages": 1
    }
  }
}

業務邏輯說明

  • 訂單狀態流程:pending(建單中)→ created(金流商已受理)→ paid(已付款,上分完成)/ failed(失敗或逾期)
  • orderAmount 為原幣別金額(如 TWD 1000);usdAmount 為轉換後的 USD 金額
  • exchangeRate 為建單時鎖定的匯率,精度 decimal(18,10)
  • 僅回傳當前用戶的訂單(後端以 JWT userId 過濾)

前端注意事項 / UI 設計提示

  • 訂單狀態使用不同顏色標籤:pending=灰色、created=藍色、paid=綠色、failed=紅色
  • 顯示原幣別金額 + USD 金額(如「TWD 1,000 ≈ USD 31.25」)
  • 支援日期範圍篩選 + 狀態下拉篩選
  • payTime 為實際付款時間,pending/created 狀態下為 null

POST /api/deposit — 建立存款訂單

功能說明 統一存款入口端點。前端帶入 channelId(金流通道 ID)和 paymentMethod(支付方式),後端自動路由至對應的金流商(萬通 ATM / 萬通信用卡 / USDT)建立訂單。建單成功後,ATM 方式會取得代收帳號,信用卡方式會取得付款頁面連結,USDT 方式則直接記錄訂單等待鏈上確認。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {JWT Token}
BodychannelIdnumber金流通道 ID(從 GET /deposit/channels 取得)1
BodypaymentMethodstring支付方式:fiat / credit / crypto"fiat"
BodysubOrderstring商家訂單編號(8-24 碼)"ORD20260222001"
BodyorderAmountnumber訂單金額(USDT),最小 0.00000120.5
BodyexpectedCodestringATM 預期收款銀行代碼(fiat 時使用)"004"
BodyexpectedAccountstringATM 預期收款帳號(fiat 時使用)"12345678901234"
BodyuserCardLastValuestring信用卡末五碼(credit 時使用,用於後續驗證)"12345"
BodyproductDesstring商品描述"儲值"
Bodymsgstring備註訊息"備註"
BodypayerNamestring付款人姓名"王小明"
BodypayerMobilestring付款人手機"0912345678"
BodypayerEmailstring付款人 Email"user@example.com"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "status": true,
    "sub_order": "ORD20260222001"
  }
}

錯誤碼

錯誤碼說明
2003不支援的金流通道類型
2004不支援的支付方式
2005匯率轉換失敗,無法取得有效匯率
2006金流通道尚未建置完成

業務邏輯說明

  • 後端根據 channelId 查找通道,再依 paymentMethod 路由:
    • fiat → 萬通 ATM 建單(WantongService.addAtm
    • credit → 萬通信用卡建單(WantongService.addCard
    • crypto → USDT 訂單記錄
  • 建單時會鎖定當前匯率,存入 deposit-order
  • orderAmount 為 USDT 金額;ATM/信用卡方式後端會自動轉換為對應法幣金額
  • subOrder 由前端產生,建議使用時間戳 + 隨機碼確保唯一性
  • 建單成功不代表付款完成,須等待金流商 S2S 回調(callback/pay)才會上分

前端注意事項 / UI 設計提示

  • subOrder 前端自行產生(格式建議:ORD + 時間戳 + 4碼隨機數)
  • ATM 方式:建單後顯示代收帳號(從回調取得),提示用戶至 ATM 轉帳
  • 信用卡方式:建單後可能取得跳轉 URL,引導用戶至金流商頁面完成付款
  • USDT 方式:顯示平台收款地址 + 鏈路 + 金額,提示用戶手動轉帳
  • 建單後導向訂單詳情頁,輪詢或 WebSocket 監聽狀態變更

PATCH /api/deposit/:id/confirm — 確認已轉帳 + 上傳繳費證明

功能說明 用戶完成 ATM 轉帳或 USDT 轉帳後,呼叫此端點上傳繳費證明圖片,表示「我已經付款了」。後端會將圖片上傳至 Cloudflare R2 儲存,並更新訂單狀態。此端點主要用於手動確認場景(如 USDT 鏈上確認較慢時,讓用戶主動回報)。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {JWT Token}
Headersite-namestring站點名稱(R2 路徑用)a1
URL Paramidnumber存款訂單 ID1
Body (FormData)fileFile繳費證明圖片(image/*,最大 5MB)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "proofImage": "deposit-proof/a1/1_1234567890.jpg",
    "status": "created"
  }
}

錯誤碼

錯誤碼說明
2010請上傳繳費證明圖片

業務邏輯說明

  • 僅接受圖片檔案(image/*),最大 5MB
  • 圖片上傳至 R2 路徑:deposit-proof/{siteName}/{orderId}_{timestamp}.{ext}
  • 上傳後訂單不會立即變為 paid,仍需等待金流商回調或後台管理員審核
  • 只有訂單所有者(JWT userId 與訂單 userId 一致)才能操作

前端注意事項 / UI 設計提示

  • 使用 multipart/form-data 格式上傳
  • 支援拍照上傳或從相簿選擇
  • 圖片預覽 + 壓縮(建議前端壓縮至 1MB 以下再上傳)
  • 上傳成功後顯示「已提交,等待確認」提示

Withdrawal 提領模組

路由前綴:/api/withdrawal 提領採三階段流程:用戶申請(pending)→ 後台審核(approved / rejected)→ 確認出款完成(completed)。提領前須通過 Email 驗證碼 + 打碼量檢查。


POST /api/withdrawal/send-code — 發送提領驗證碼

功能說明 發送 6 碼數字驗證碼到用戶已綁定的信箱。此為提領流程的第一步,用戶必須先取得驗證碼才能提交提領申請。後端會檢查用戶是否已完成信箱與手機驗證,若未完成則拒絕發送。驗證碼透過 Resend API 發送 Email。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer {JWT Token}

無 Body 參數。

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": null
}

錯誤碼

錯誤碼說明
2001用戶信箱未驗證(須先完成 Email 綁定)
2002用戶手機未驗證(須先完成手機綁定)

業務邏輯說明

  • 驗證碼有效期約 5 分鐘,存入 Redis
  • 同一用戶短時間內重複發送會覆蓋上一組驗證碼
  • 信箱與手機必須都已驗證才允許提領(風控要求)

前端注意事項 / UI 設計提示

  • 提領頁面先檢查用戶是否已綁定信箱 + 手機,若未綁定引導至個人設定頁
  • 「發送驗證碼」按鈕點擊後倒數 60 秒防重複點擊
  • 驗證碼輸入框 6 格分離式設計(提升用戶體驗)

POST /api/withdrawal/request — 提交提領申請

功能說明 提交 USDT 提領申請。用戶需指定提領金額、目標加密錢包 ID 與 Email 驗證碼。後端會檢查驗證碼正確性、錢包審核狀態、餘額充足性、以及打碼量是否達標。通過所有檢查後建立提領單,扣除用戶餘額(凍結),等待後台審核。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {JWT Token}
Bodyamountnumber提領金額(USD)100
BodycryptoAddressIdnumber加密錢包 ID(須 status=1 已審核通過)1
BodyverifyCodestring郵箱驗證碼(6 碼)"123456"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "userId": 123,
    "amount": "100.000000",
    "cryptoAddressId": 1,
    "status": "pending",
    "createdAt": "2026-02-22T10:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001驗證碼未發送(須先呼叫 send-code
2002驗證碼錯誤
2003用戶信箱未驗證
2004用戶手機未驗證
2005該錢包不存在或未通過審核
2006提領金額需大於 0
2007餘額不足
2008優惠打碼量未完成(有領取的活動獎勵尚未完成打碼要求)
2009存款打碼量不足(存款後需完成一定投注量才可提領)

業務邏輯說明

  • 提領目前僅支援 USDT(加密貨幣),需先綁定並通過審核的加密錢包
  • 提交時立即從用戶餘額扣除(凍結),防止超額提領
  • 若審核被拒絕(rejected),凍結金額退回用戶餘額
  • 打碼量檢查:每次存款後需達到 1 倍存款金額的有效投注量;活動獎勵有各自的打碼倍數要求
  • 驗證碼驗證後即失效(一次性使用)

前端注意事項 / UI 設計提示

  • 提領頁面流程:選擇錢包 → 輸入金額 → 發送驗證碼 → 輸入驗證碼 → 確認提領
  • 顯示用戶可用餘額(扣除凍結金額)
  • 金額輸入框限制最小值與最大值(最大值 = 可用餘額)
  • 錢包下拉選單只顯示 status=1(已審核通過)的錢包
  • 提交前呼叫 GET /withdrawal/turnover-status 檢查打碼量,未達標則禁用提領按鈕

GET /api/withdrawal/list — 用戶提領紀錄

功能說明 取得當前登入用戶的提領歷史紀錄,支援分頁、狀態篩選與日期範圍篩選。前端在「提領紀錄」頁面呼叫此端點。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {JWT Token}
Querypagenumber頁碼(預設 1)1
QuerypageSizenumber每頁筆數(預設 10)10
Querystatusstring狀態篩選pending / approved / rejected / completed
QuerystartDatestring起始日期 (YYYY-MM-DD)2026-01-01
QueryendDatestring結束日期 (YYYY-MM-DD)2026-12-31

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "amount": "100.000000",
        "status": "pending",
        "cryptoAddress": "TXqH4j5xGZz8vKJm9bN2rL7pWcYfDsAeUo",
        "network": "TRC-20",
        "createdAt": "2026-02-22T10:00:00.000Z",
        "updatedAt": "2026-02-22T10:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "pageSize": 10,
      "total": 1,
      "totalPages": 1
    }
  }
}

業務邏輯說明

  • 提領狀態流程:pending(待審核)→ approved(已核准)→ completed(已完成);或 pendingrejected(已拒絕,退回餘額)
  • 僅回傳當前用戶的提領紀錄

前端注意事項 / UI 設計提示

  • 狀態標籤顏色:pending=橙色、approved=藍色、completed=綠色、rejected=紅色
  • 被拒絕的訂單顯示拒絕原因
  • 支援日期範圍 + 狀態下拉篩選

GET /api/withdrawal/turnover-status — 查詢打碼量狀態

功能說明 查詢當前用戶的存款打碼量與優惠打碼量完成進度。前端可根據回傳的 canWithdraw 欄位控制提領按鈕的啟用/禁用狀態。此端點整合了存款打碼量(每次存款需完成 1 倍有效投注)和優惠打碼量(活動獎勵的打碼倍數要求)兩項檢查。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明
HeaderAuthorizationstringBearer {JWT Token}

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "canWithdraw": false,
    "deposit": {
      "totalDeposits": "1000.000000",
      "multiplier": 1,
      "requiredTurnover": "1000.000000",
      "completedTurnover": "750.000000",
      "remaining": "250.000000",
      "completed": false
    },
    "promo": {
      "pendingCount": 1,
      "completed": false,
      "items": [
        {
          "promoId": 1,
          "promoTitle": "新手首存禮",
          "rewardAmount": "10.000000",
          "requiredTurnover": "50.000000",
          "completedTurnover": "30.000000",
          "remaining": "20.000000"
        }
      ]
    }
  }
}

業務邏輯說明

  • canWithdraw = true 僅當 deposit.completedpromo.completed 皆為 true
  • 存款打碼量:requiredTurnover = totalDeposits * multiplier(預設 1 倍)
  • 優惠打碼量:每個領取的活動獎勵有各自的 turnoverMultiplierrequiredTurnover = rewardAmount * turnoverMultiplier
  • 打碼量透過有效投注累計(bet-order 表的 betEffective

前端注意事項 / UI 設計提示

  • 提領頁面顯示打碼量進度條(存款 + 優惠分開顯示)
  • canWithdraw = false 時禁用提領按鈕,顯示「尚未達到提領門檻」
  • 顯示各項打碼量的完成百分比(completedTurnover / requiredTurnover * 100
  • 優惠打碼量若有多項,逐一列出每個活動的進度

GET /api/withdrawal/admin/list — [Admin] 提領列表

功能說明 後台管理員取得全站提領訂單列表,支援分頁與狀態篩選。用於後台提領審核頁面。

認證需求JwtAuthGuard(注意:此處使用前台 Guard,實際應搭配 AdminJwtAuthGuard)

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {Admin JWT Token}
Querypagenumber頁碼(預設 1)1
QuerypageSizenumber每頁筆數(預設 10)10
Querystatusstring狀態篩選pending / approved / rejected / completed
QuerystartDatestring起始日期2026-01-01
QueryendDatestring結束日期2026-12-31

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "userId": 123,
        "userAccount": "user001",
        "amount": "100.000000",
        "status": "pending",
        "cryptoAddress": "TXqH4j5xGZz8vKJm9bN2rL7pWcYfDsAeUo",
        "network": "TRC-20",
        "createdAt": "2026-02-22T10:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 10, "total": 1, "totalPages": 1 }
  }
}

前端注意事項 / UI 設計提示

  • 後台提領管理頁面,按狀態分頁籤顯示(待審核 / 已核准 / 已完成 / 已拒絕)
  • 待審核訂單行提供「審核」按鈕

POST /api/withdrawal/admin/:id/review — [Admin] 審核提領

功能說明 後台管理員審核提領單。approve 通過審核(進入等待出款狀態),reject 拒絕並退回凍結金額至用戶餘額。

認證需求JwtAuthGuard

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {Admin JWT Token}
URL Paramidnumber提領單 ID1
Bodyactionstring審核動作:approve / reject"approve"
BodyrejectReasonstring拒絕原因(action=reject 時必填)"資料異常"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "success": true }
}

錯誤碼

錯誤碼說明
2001查無此提領單
2002該提領單狀態不允許審核(非 pending 狀態)

業務邏輯說明

  • approve:狀態從 pendingapproved,凍結金額維持
  • reject:狀態從 pendingrejected,凍結金額退回用戶可用餘額
  • 審核人帳號會記錄在提領單中

POST /api/withdrawal/admin/:id/complete — [Admin] 確認出款完成

功能說明 管理員確認已完成鏈上匯款,標記提領單為已完成。此為提領流程的最後一步,凍結金額正式出帳。

認證需求JwtAuthGuard

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {Admin JWT Token}
URL Paramidnumber提領單 ID1

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "success": true }
}

錯誤碼

錯誤碼說明
2001查無此提領單
2002該提領單狀態不允許完成(非 approved 狀態)

業務邏輯說明

  • approved 狀態的訂單可完成
  • 完成後凍結金額正式扣除,不可逆

Wallet 錢包模組

錢包模組管理用戶的三種支付/收款工具:銀行卡、信用卡、加密貨幣錢包地址。 所有錢包工具新增後預設 status=0(待審核),需後台管理員審核通過(status=1)後才可用於存款/提領。


銀行卡 (Bank Card)

路由前綴:/api/wallet/bank-card 整個 Controller 使用 @UseGuards(JwtAuthGuard),所有端點皆需前台用戶 JWT。


POST /api/wallet/bank-card/add — 新增銀行卡

功能說明 用戶新增銀行卡,須上傳身分證正面、背面及銀行存摺封面三張圖片作為審核資料。圖片上傳至 Cloudflare R2 儲存。新增後狀態為 status=0(待審核),需後台管理員審核通過後才可用於存款。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
HeaderAuthorizationstringBearer {JWT Token}
Headersite-namestring站點名稱(R2 路徑用)a1
Body (FormData)bankCodestring銀行代碼"004"
Body (FormData)bankAccountstring銀行帳號(純數字,至少 8 碼)"12345678901234"
Body (FormData)branchstring分行名稱"台北分行"
Body (FormData)holderNamestring持卡人姓名(至少 2 字)"王小明"
Body (FormData)idCardFrontFile身分證正面(image/*,最大 5MB)
Body (FormData)idCardBackFile身分證背面(image/*,最大 5MB)
Body (FormData)passbookCoverFile存摺封面(image/*,最大 5MB)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "userId": 1,
    "bankCode": "004",
    "bankAccount": "12345678901234",
    "branch": "台北分行",
    "holderName": "王小明",
    "idCardFront": "bank-card/xxx.jpg",
    "idCardBack": "bank-card/xxx.jpg",
    "passbookCover": "bank-card/xxx.jpg",
    "status": 0,
    "createdAt": "2026-02-22T00:00:00.000Z",
    "updatedAt": "2026-02-22T00:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001請上傳身分證正面
2002請上傳身分證反面
2003請上傳銀行存摺封面
2004此銀行卡已存在(同一銀行帳號不可重複綁定)

業務邏輯說明

  • 使用 FileFieldsInterceptor 處理多檔案上傳(三個欄位各一張圖)
  • 圖片驗證:僅接受 image/* MIME type,單檔最大 5MB
  • bankAccount 驗證:只允許純數字,至少 8 碼
  • 新增後 status=0(待審核),需後台 PATCH /admin/finance/bank-cards/:id 審核

前端注意事項 / UI 設計提示

  • 表單使用 multipart/form-data 格式
  • 三個圖片上傳區域,建議加入圖片預覽功能
  • 銀行代碼可用下拉選單(搭配台灣銀行代碼清單)
  • 銀行帳號輸入限制為純數字
  • 提交後提示「審核中,請等待 1-3 個工作天」

GET /api/wallet/bank-card/list — 取得銀行卡列表

功能說明 取得當前用戶已綁定的銀行卡列表。圖片欄位會回傳完整的 R2 URL(可直接顯示)。

認證需求JwtAuthGuard(前台用戶 JWT)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "userId": 1,
      "bankCode": "004",
      "bankAccount": "12345678901234",
      "branch": "台北分行",
      "holderName": "王小明",
      "idCardFront": "https://r2.example.com/bank-card/xxx.jpg",
      "idCardBack": "https://r2.example.com/bank-card/xxx.jpg",
      "passbookCover": "https://r2.example.com/bank-card/xxx.jpg",
      "status": 0,
      "createdAt": "2026-02-22T00:00:00.000Z",
      "updatedAt": "2026-02-22T00:00:00.000Z"
    }
  ]
}

前端注意事項 / UI 設計提示

  • 卡片式列表顯示,每張銀行卡顯示銀行名稱 + 帳號後四碼
  • 狀態標籤:0=審核中(橙色)、1=已通過(綠色)、2=已拒絕(紅色)
  • 已通過的銀行卡在存款時可選擇

DELETE /api/wallet/bank-card/:id — 刪除銀行卡

功能說明 刪除指定的銀行卡。僅能刪除自己的銀行卡。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明
URL Paramidnumber銀行卡 ID

Response 範例

json
{ "code": 200, "message": "ok", "result": null }

錯誤碼

錯誤碼說明
2001查無此銀行卡(ID 不存在或不屬於當前用戶)

信用卡 (Credit Card)

路由前綴:/api/wallet/credit-card


POST /api/wallet/credit-card/add — 新增信用卡

功能說明 用戶新增信用卡。不需上傳圖片,直接填寫卡號、持卡人姓名、CVV、到期日即可。新增後預設 status=0(待審核)。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
BodycardNumberstring信用卡卡號(13-19 位純數字)"4111111111111111"
BodyholderNamestring持卡人姓名(至少 2 字)"王小明"
Bodycvvstring安全碼 CVV/CVC(3-4 位數字)"123"
BodyexpiryDatestring到期日(MM/YY 格式)"12/27"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "userId": 1,
    "cardNumber": "4111111111111111",
    "holderName": "王小明",
    "cvv": "123",
    "expiryDate": "12/27",
    "status": 0,
    "createdAt": "2026-02-22T00:00:00.000Z",
    "updatedAt": "2026-02-22T00:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001此信用卡已綁定(同一卡號不可重複)

前端注意事項 / UI 設計提示

  • 卡號輸入:每 4 碼自動加空格顯示(提交時去除空格)
  • CVV 輸入:密碼模式(type="password"
  • 到期日:MM/YY 格式,可用兩個下拉選單(月/年)
  • 卡號格式驗證:Luhn 演算法(前端可做基本檢查)

GET /api/wallet/credit-card/list — 取得信用卡列表

功能說明 取得當前用戶已綁定的信用卡列表。

認證需求JwtAuthGuard

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "userId": 1,
      "cardNumber": "4111111111111111",
      "holderName": "王小明",
      "cvv": "123",
      "expiryDate": "12/27",
      "status": 0,
      "createdAt": "2026-02-22T00:00:00.000Z",
      "updatedAt": "2026-02-22T00:00:00.000Z"
    }
  ]
}

前端注意事項 / UI 設計提示

  • 卡號顯示時遮罩中間數字(如 4111 **** **** 1111
  • CVV 不顯示(安全考量)

DELETE /api/wallet/credit-card/:id — 刪除信用卡

認證需求JwtAuthGuard

錯誤碼2001 查無此信用卡

Response 範例

json
{ "code": 200, "message": "ok", "result": null }

加密貨幣錢包地址 (Crypto Address)

路由前綴:/api/wallet/crypto-address


POST /api/wallet/crypto-address/add — 新增加密貨幣錢包地址

功能說明 用戶新增加密貨幣錢包地址,用於提領時的收款地址。需指定錢包名稱、幣別、鏈路與地址。新增後預設 status=0(待審核),須後台審核通過(status=1)後才可用於提領。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
BodywalletNamestring錢包名稱"otis的USDT錢包"
Bodycurrencystring幣別(預設 USDT)"USDT"
Bodynetworkstring鏈路(預設 TRC-20)"TRC-20"
Bodyaddressstring錢包地址(至少 10 碼)"TXqH4j5xGZz8vKJm9bN2rL7pWcYfDsAeUo"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "userId": 1,
    "walletName": "otis的USDT錢包",
    "currency": "USDT",
    "network": "TRC-20",
    "address": "TXqH4j5xGZz8vKJm9bN2rL7pWcYfDsAeUo",
    "status": 0,
    "createdAt": "2026-02-22T00:00:00.000Z",
    "updatedAt": "2026-02-22T00:00:00.000Z"
  }
}

錯誤碼

錯誤碼說明
2001此錢包地址已綁定(同一地址不可重複)

業務邏輯說明

  • 提領時只能選擇 status=1(已審核通過)的錢包地址
  • 目前主要支援 USDT / TRC-20 組合
  • 錢包地址至少 10 碼,防止誤輸入

前端注意事項 / UI 設計提示

  • 鏈路下拉選單:TRC-20 / ERC-20 / BEP-20 等
  • 幣別下拉選單:USDT(目前僅支援)
  • 地址輸入支援 QR Code 掃描(行動版)
  • 提醒用戶確認鏈路與地址正確,轉錯鏈路會導致資金丟失

GET /api/wallet/crypto-address/list — 取得加密貨幣錢包列表

認證需求JwtAuthGuard

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "userId": 1,
      "walletName": "otis的USDT錢包",
      "currency": "USDT",
      "network": "TRC-20",
      "address": "TXqH4j5xGZz8vKJm9bN2rL7pWcYfDsAeUo",
      "status": 0,
      "createdAt": "2026-02-22T00:00:00.000Z",
      "updatedAt": "2026-02-22T00:00:00.000Z"
    }
  ]
}

DELETE /api/wallet/crypto-address/:id — 刪除加密貨幣錢包地址

認證需求JwtAuthGuard

錯誤碼2001 查無此錢包地址

Response 範例

json
{ "code": 200, "message": "ok", "result": null }

Vendor 金流商模組

路由前綴:/api/vendor 金流商模組提供金流通道查詢,以及萬通金流(Wantong)和 USDT 兩個子模組的建單與回調處理。 前端通常不直接呼叫子模組端點,而是透過 POST /deposit 統一入口建單。


GET /api/vendor/channels — 取得用戶金流通道列表

功能說明 取得當前用戶所屬金流群組的通道列表。此端點與 GET /deposit/channels 類似,但不附帶匯率資訊。前端建議使用 GET /deposit/channels(含匯率)而非此端點。

認證需求JwtAuthGuard(前台用戶 JWT)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "groupId": 1,
    "groupName": "default",
    "channels": [
      {
        "id": 1,
        "name": "wantong",
        "currency": "TWD",
        "paymentMethods": ["fiat", "credit"],
        "paymentAddress": null,
        "enabled": 1
      },
      {
        "id": 2,
        "name": "usdt",
        "currency": "USDT",
        "paymentMethods": ["crypto"],
        "paymentAddress": "TXqH4j5xGZz8vKJm9bN2rL7pWcYfDsAeUo",
        "network": "TRC-20",
        "enabled": 1
      }
    ]
  }
}

錯誤碼

錯誤碼說明
2001用戶未分配金流群組(註冊異常)
2002金流群組不存在或已停用
2003金流通道不存在或不屬於此群組

萬通金流子模組 (Wantong)

路由前綴:/api/vendor/wantong 萬通金流支援 ATM 虛擬帳號轉帳與信用卡線上刷卡兩種方式。


POST /api/vendor/wantong/add-atm — 萬通 ATM 建單

功能說明 向萬通金流發送 ATM 虛擬帳號建單請求。建單成功後萬通會回傳代收銀行帳號與繳費期限。前端通常不直接呼叫此端點,而是透過 POST /deposit 統一入口(paymentMethod: 'fiat')自動路由至此。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
BodysubOrderstring商家訂單編號(8-24 碼)"ORD20260222001"
BodyorderAmountnumber訂單金額(整數,TWD)1000
BodyexpectedCodestring預期收款銀行代碼"004"
BodyexpectedAccountstring預期收款帳號"12345678901234"
BodyproductDesstring商品描述"儲值"
Bodymsgstring備註訊息"備註"
BodypayerNamestring付款人姓名"王小明"
BodypayerMobilestring付款人手機"0912345678"
BodypayerEmailstring付款人 Email"user@example.com"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "status": true, "sub_order": "ORD20260222001" }
}

錯誤碼

錯誤碼說明
2001查無可用的萬通金流通道
2002建立 ATM 訂單失敗(萬通 API 回傳錯誤)
2003ATM 訂單請求異常(網路錯誤等)

業務邏輯說明

  • 建單成功後,萬通會透過 POST /vendor/wantong/callback/add 回調推送代收帳號資訊
  • 用戶完成 ATM 轉帳後,萬通會透過 POST /vendor/wantong/callback/pay 回調通知付款成功
  • 付款成功後後端自動上分(增加用戶 USD 餘額)

POST /api/vendor/wantong/add-card — 萬通信用卡建單

功能說明 向萬通金流發送信用卡建單請求。建單成功後用戶會被導向至金流商的信用卡付款頁面。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
BodysubOrderstring商家訂單編號(8-24 碼)"ORD20260222001"
BodyorderAmountnumber訂單金額(整數,TWD)1000
BodyuserCardLastValuestring信用卡末五碼(用於後續驗證)"12345"
BodyproductDesstring商品描述"儲值"
Bodymsgstring備註訊息
BodypayerNamestring付款人姓名"王小明"
BodypayerMobilestring付款人手機"0912345678"
BodypayerEmailstring付款人 Email"user@example.com"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "status": true, "sub_order": "ORD20260222001" }
}

錯誤碼

錯誤碼說明
2001查無可用的萬通金流通道
2002建立信用卡訂單失敗
2003信用卡訂單請求異常

POST /api/vendor/wantong/callback/add — 萬通建單回調

功能說明 萬通金流伺服器呼叫的 S2S 回調端點(不需 JWT)。萬通在受理建單請求後,會推送代收帳號資訊到此端點。後端會更新訂單的繳費資訊(代收銀行帳號、繳費期限等)。

認證需求:無(公開端點,萬通 S2S 呼叫)

Request(由萬通推送)

參數型別說明
sub_orderstring商家訂單編號
check_sumstringMD5 校驗碼
expire_datestring繳費期限
order_amountnumber應繳金額
receiving_codestring代收銀行代碼
receiving_accountstring代收銀行帳號

Response 範例

json
{ "received": true }

POST /api/vendor/wantong/callback/pay — 萬通付款成功回調

功能說明 萬通金流伺服器呼叫的 S2S 回調端點(不需 JWT)。用戶完成付款後,萬通推送付款成功通知。後端會自動上分(增加用戶 USD 餘額),並更新訂單狀態為 paid

認證需求:無(公開端點,萬通 S2S 呼叫)

Request(由萬通推送)

參數型別說明
sub_orderstring商家訂單編號
check_sumstringMD5 校驗碼
pay_amountnumber實際繳款金額
pay_timestring實際繳款時間
payment_accountstring實際繳款帳號(ATM 專用)
expected_codestring預約銀行代碼(ATM 專用)
expected_accountstring預約銀行帳戶(ATM 專用)
vertify_pay_accountnumber帳號比對狀態:0=無法比對 / 1=不一致 / 2=一致(ATM 專用)
vertify_pay_amountnumber金額比對狀態:0=不符 / 1=一致
user_pay_cardstring實際刷卡卡號(信用卡專用)
vertify_card_last_valuenumber卡號末五碼驗證:0=未使用 / 1=不一致 / 2=一致(信用卡專用)

Response 範例

json
{ "received": true }

業務邏輯說明

  • 回調會驗證 check_sum MD5 校驗碼確保請求來自萬通
  • 付款成功後自動上分:usdAmount = payAmount / exchangeRate
  • 上分後訂單狀態從 createdpaid
  • 上分後自動觸發任務系統的存款進度更新

USDT 子模組

路由前綴:/api/vendor/usdt


POST /api/vendor/usdt/callback/pay — USDT 付款確認回調

功能說明 USDT 付款確認的 S2S 回調端點(不需 JWT)。當系統偵測到鏈上轉帳完成(或人工確認)時呼叫此端點。後端會自動上分並更新訂單狀態為 paid

認證需求:無(S2S 端點)

Request

參數型別必填說明
subOrderstring商家訂單編號
payAmountnumber實際繳款金額(USDT)
txHashstring鏈上交易 hash
payTimestring繳款時間

Response 範例

json
{ "received": true }

業務邏輯說明

  • USDT 1:1 等值 USD,不需匯率轉換
  • txHash 可用於後台核對鏈上交易
  • 上分後自動觸發任務系統的存款進度更新

Promo 活動模組

路由前綴:/api/promo 活動模組管理優惠活動的建立、查詢、領取。支援多語系標題/內容、多裝置圖片(PC/Mobile)、領取條件檢查(首存/存款門檻/VIP 等級)、打碼量追蹤。


GET /api/promo — 取得活動列表

功能說明 取得活動列表,支援分頁、標籤篩選、僅顯示進行中活動。此端點使用 OptionalJwtAuthGuard——登入用戶可看到 isClaimed(是否已領取)和 isClaimable(是否可領取)的個人化狀態;未登入用戶這兩個欄位皆為 false。圖片欄位回傳完整 R2 URL,依用戶當前語系解析。

認證需求OptionalJwtAuthGuard(可選認證,未登入也可存取)

Request

位置參數型別必填說明範例
Querypagenumber頁碼(預設 1)1
QuerypageSizenumber每頁筆數(預設 10,最大 50)10
Querytagstring活動標籤篩選"新手"
QueryactiveOnlystring僅顯示進行中(1=是)"1"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "title": "新手首存禮",
        "imgPc": "https://r2.example.com/promo/pc-xxx.jpg",
        "imgMobile": "https://r2.example.com/promo/mobile-xxx.jpg",
        "content": "<p>首次存款即送 10 USD</p>",
        "actionHtml": "<a href=\"/deposit\">立即存款</a>",
        "startTime": "2026-03-01T00:00:00.000Z",
        "endTime": "2026-04-01T00:00:00.000Z",
        "tag": "新手",
        "enabled": 1,
        "conditionType": "first_deposit",
        "conditionValue": "0",
        "rewardAmount": "10.000000",
        "maxClaims": 100,
        "claimedCount": 23,
        "createdAt": "2026-02-22T00:00:00.000Z",
        "updatedAt": "2026-02-22T00:00:00.000Z",
        "isActive": true,
        "isClaimed": false,
        "isClaimable": true
      }
    ],
    "pagination": { "page": 1, "pageSize": 10, "total": 1, "totalPages": 1 }
  }
}

業務邏輯說明

  • isActive:當前時間在 startTime ~ endTime 之間且 enabled=1
  • isClaimed:當前用戶已領取過此活動(僅登入時計算)
  • isClaimable:活動進行中 + 未領取 + 名額未滿 + 滿足條件(僅登入時計算)
  • titlecontent 為多語系 JSON,後端依用戶語系解析後回傳單一字串
  • imgPcimgMobile 為完整 R2 URL,依語系選擇對應版本

前端注意事項 / UI 設計提示

  • 活動列表頁面:頂部標籤篩選列(從 GET /promo/tags 取得標籤列表)
  • PC 版顯示 imgPc,行動版顯示 imgMobile
  • 已領取的活動顯示「已領取」徽章
  • 可領取的活動顯示「立即領取」按鈕
  • actionHtml 為 HTML 片段,可安全渲染(如導向存款頁的按鈕)

GET /api/promo/claims — 取得領取紀錄

功能說明 取得當前用戶的活動獎勵領取紀錄,支援分頁與狀態篩選。包含每個已領取活動的打碼量進度。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
Querypagenumber頁碼(預設 1)1
QuerypageSizenumber每頁筆數(預設 10,最大 50)10
Querytabstring篩選:all / pending(打碼中)/ completed(已完成)"all"
QuerystartDatestring起始日期 (YYYY-MM-DD)2026-01-01
QueryendDatestring結束日期 (YYYY-MM-DD)2026-12-31

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "promoId": 1,
        "promoTitle": "新手首存禮",
        "promoTag": "新手",
        "rewardAmount": "10.000000",
        "requiredTurnover": "50.000000",
        "completedTurnover": "30.000000",
        "turnoverCompleted": 0,
        "claimedAt": "2026-02-22T10:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 10, "total": 1, "totalPages": 1 }
  }
}

業務邏輯說明

  • turnoverCompleted:0=打碼中、1=已完成
  • requiredTurnover = rewardAmount * turnoverMultiplier
  • 打碼量透過有效投注自動累計

前端注意事項 / UI 設計提示

  • 分頁籤:全部 / 打碼中 / 已完成
  • 每項紀錄顯示打碼量進度條
  • 打碼中的項目標記為橙色,已完成標記為綠色

GET /api/promo/tags — 取得已啟用的活動標籤

功能說明 取得所有已啟用(enabled=1)的活動標籤,按 sortOrder 排序。前台活動列表頁面用於顯示標籤篩選列。

認證需求:無(公開端點)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    { "id": 1, "name": "新手", "sortOrder": 1, "enabled": 1 },
    { "id": 2, "name": "每週", "sortOrder": 2, "enabled": 1 }
  ]
}

GET /api/promo/:id — 取得活動詳情

功能說明 取得指定活動的完整詳情,包含 isActiveisClaimedisClaimable 計算欄位。使用 OptionalJwtAuthGuard,未登入用戶 isClaimedisClaimable 皆為 false

認證需求OptionalJwtAuthGuard(可選認證)

Request

位置參數型別必填說明
URL Paramidnumber活動 ID

錯誤碼2001 查無此活動


POST /api/promo — 建立活動

功能說明 建立新的優惠活動。支援多語系標題與內容(JSON 格式),可上傳 PC 版與 Mobile 版圖片(各語系各一張)。圖片上傳至 Cloudflare R2。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
Headersite-namestring站點名稱(R2 路徑用)a1
Body (FormData)titleJSON string活動標題(多語系){"zh-TW":"新手首存禮","en-US":"Welcome Bonus"}
Body (FormData)contentJSON string活動內容(多語系 HTML){"zh-TW":"<p>送 10 USD</p>","en-US":"<p>10 USD bonus</p>"}
Body (FormData)actionHtmlstring渲染連結/按鈕 HTML<a href="/deposit">立即存款</a>
Body (FormData)startTimestring開始時間 (ISO 8601)2026-03-01T00:00:00.000Z
Body (FormData)endTimestring結束時間 (ISO 8601)2026-04-01T00:00:00.000Z
Body (FormData)tagstring活動標籤(最長 30 字)新手
Body (FormData)enablednumber是否開啟(0=關 1=開,預設 1)1
Body (FormData)conditionTypestring領取條件類型none / deposit_threshold / vip_level / first_deposit
Body (FormData)conditionValuestring門檻值(存款金額或 VIP 等級,預設 0)"0"
Body (FormData)rewardAmountnumber獎勵金額(USD)10
Body (FormData)maxClaimsnumber最大領取總數(0=無限,預設 0)100
Body (FormData)turnoverMultipliernumber打碼量倍數(0=無要求,預設 0)3
Body (FormData)imgPcZhTWFilePC 版圖片(繁中版,max 5MB)
Body (FormData)imgPcEnUSFilePC 版圖片(英文版,max 5MB)
Body (FormData)imgMobileZhTWFileMobile 版圖片(繁中版,max 5MB)
Body (FormData)imgMobileEnUSFileMobile 版圖片(英文版,max 5MB)

業務邏輯說明

  • conditionType 條件類型:
    • none:無條件,任何人可領取
    • first_deposit:首次存款後可領取,conditionValue 為最低存款金額(0=不限金額)
    • deposit_threshold:累積存款達門檻可領取,conditionValue 為門檻金額
    • vip_level:VIP 等級達標可領取,conditionValue 為最低 VIP 等級
  • turnoverMultiplier:領取獎勵後需完成的打碼倍數(如 3 倍 = 獎勵金額 * 3 的有效投注量)
  • 圖片依語系分版上傳,前端請求時後端依用戶語系回傳對應版本

PATCH /api/promo/:id — 更新活動

功能說明 更新指定活動。所有欄位皆為 optional,只傳需要更新的欄位。上傳新圖片會取代舊圖。

認證需求JwtAuthGuard

錯誤碼2001 查無此活動


DELETE /api/promo/:id — 刪除活動

認證需求JwtAuthGuard

錯誤碼2001 查無此活動

Response 範例

json
{ "code": 200, "message": "ok", "result": null }

POST /api/promo/:id/claim — 領取活動獎勵

功能說明 用戶領取指定活動的獎勵。後端會檢查所有領取條件(活動進行中、未領取過、名額未滿、滿足條件類型要求),通過後將獎勵金額直接發放到用戶 USD 餘額,並建立 promo-claim 紀錄(含打碼量追蹤)。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明
URL Paramidnumber活動 ID

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "rewardAmount": "10.000000",
    "newBalance": "110.000000"
  }
}

錯誤碼

錯誤碼說明
2001此活動已領取過
2002此活動領取名額已滿
2003尚未滿足領取條件

業務邏輯說明

  • 獎勵直接加到用戶 USD 餘額(即時到帳)
  • 若活動有 turnoverMultiplier > 0,領取後會建立打碼量追蹤紀錄
  • 打碼量未完成前,提領會被 2008 錯誤碼擋下
  • claimedCount 會即時遞增,達到 maxClaims 後不可再領取

前端注意事項 / UI 設計提示

  • 領取前彈出確認對話框(含獎勵金額與打碼要求說明)
  • 領取成功後顯示動畫 + 更新餘額顯示
  • 按鈕狀態即時切換:「立即領取」→「已領取」

Inbox 站內信模組

路由前綴:/api/inbox 站內信分為系統廣播(userId=null,全站可見)與個人通知(userId 指定用戶)。 後台管理員發送,前台用戶閱讀/標記已讀。


GET /api/inbox — 取得站內信列表

功能說明 取得當前用戶的站內信列表,支援分頁、分類篩選與範圍篩選。系統廣播(userId=null)所有用戶可見;個人通知(userId=當前用戶)僅對應用戶可見。isRead 透過 notification-read 表判斷。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明範例
Querypagenumber頁碼(預設 1)1
QuerypageSizenumber每頁筆數(預設 10,最大 50)10
Querycategorystring分類篩選system / promo
Queryscopestring範圍:personal=個人通知 / system=系統廣播personal

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "items": [
      {
        "id": 1,
        "title": "系統維護通知",
        "content": "<p>系統將於今晚維護</p>",
        "category": "system",
        "isRead": false,
        "createdAt": "2026-02-24T00:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 10, "total": 1, "totalPages": 1 }
  }
}

業務邏輯說明

  • titlecontent 為多語系 JSON,後端依用戶語系解析後回傳單一字串
  • isRead 透過 notification-read 表查詢(用戶維度)
  • scope=personal 篩選 userId=當前用戶 的通知
  • scope=system 篩選 userId=null 的系統廣播

前端注意事項 / UI 設計提示

  • 站內信頁面分頁籤:全部 / 系統通知 / 活動通知
  • 未讀信件加粗顯示或加上紅點
  • content 為 HTML,安全渲染(使用 v-htmldangerouslySetInnerHTML
  • Header 上顯示未讀數量紅點(搭配 GET /inbox/unread-count

GET /api/inbox/unread-count — 取得未讀通知數量

功能說明 取得當前用戶的未讀通知數量。前端用於在 Header 導航列的郵件圖示旁顯示未讀紅點 / 數字。支援按範圍篩選。

認證需求JwtAuthGuard(前台用戶 JWT)

Request

位置參數型別必填說明
Queryscopestring範圍:personal / system

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": { "unreadCount": 5 }
}

前端注意事項 / UI 設計提示

  • 建議登入後每 60 秒輪詢一次更新未讀數
  • 數量 > 99 時顯示 99+
  • 數量 = 0 時隱藏紅點

POST /api/inbox/:id/read — 標記單則通知為已讀

認證需求JwtAuthGuard

錯誤碼2001 查無此通知

Response 範例

json
{ "code": 200, "message": "ok", "result": null }

POST /api/inbox/read-all — 全部標記為已讀

認證需求JwtAuthGuard

Response 範例

json
{ "code": 200, "message": "ok", "result": null }

前端注意事項 / UI 設計提示

  • 列表頁面提供「全部已讀」按鈕
  • 操作後更新列表中所有項目的 isRead 狀態 + 清除未讀數紅點

POST /api/inbox/admin/send — [Admin] 發送通知

功能說明 後台管理員發送站內通知。可發送給特定用戶(指定 userId)或全站廣播(不帶 userId)。標題與內容皆為多語系 JSON 格式,前台依用戶語系自動解析顯示。

認證需求AdminJwtAuthGuard(後台管理員 JWT)

Request

位置參數型別必填說明範例
BodyuserIdnumber目標用戶 ID(不傳=全站通知)123
Bodytitleobject通知標題(多語系 JSON){"zh-TW":"系統維護通知","en-US":"Maintenance Notice"}
Bodycontentobject通知內容(多語系 HTML){"zh-TW":"<p>今晚維護</p>","en-US":"<p>Maintenance tonight</p>"}
Bodycategorystring分類:system / promo"system"

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "id": 1,
    "userId": null,
    "title": { "zh-TW": "系統維護通知", "en-US": "System Maintenance Notice" },
    "content": { "zh-TW": "<p>系統將於今晚維護</p>", "en-US": "<p>System maintenance tonight</p>" },
    "category": "system",
    "createdAt": "2026-02-24T00:00:00.000Z"
  }
}

業務邏輯說明

  • userId=null 時為全站廣播,所有用戶信箱均可見
  • userId 指定時為個人通知,僅該用戶可見
  • 回傳原始多語系 JSON(Admin 看到全部語系版本)

前端注意事項 / UI 設計提示

  • 後台發送表單需提供 5 個語系的輸入欄位(zh-TW, en-US, zh-CN, th-TH, vi-VN)
  • 內容欄位使用 HTML 編輯器(Tiptap)
  • 發送對象:下拉選單可選「全站廣播」或指定用戶 ID

GET /api/inbox/admin/list — [Admin] 通知列表

功能說明 後台管理員查看所有已發送的通知列表。支援分頁、分類篩選、範圍篩選、關鍵字搜尋(用戶 ID)、日期範圍篩選、多站點篩選。回傳原始多語系 JSON。

認證需求AdminJwtAuthGuard(後台管理員 JWT)

Request

位置參數型別必填說明
Querypagenumber頁碼
QuerypageSizenumber每頁筆數
Querycategorystring分類:system / promo
Queryscopestring範圍:personal / system
Querykeywordstring搜尋用戶 ID
QuerystartDatestring開始日期
QueryendDatestring結束日期
Headerx-site-codestring站點代碼(多站點篩選)

DELETE /api/inbox/admin/:id — [Admin] 刪除通知

認證需求AdminJwtAuthGuard

錯誤碼2001 查無此通知

Response 範例

json
{ "code": 200, "message": "ok", "result": null }

SiteConfig 站點配置模組

路由前綴:/api/site-config 管理白牌平台的站點設定,包含站點基本資訊、主題色系、域名素材(logo/favicon)、客服管道、吉祥物等。 公開端點供前台取得當前站點設定;Admin 端點供後台管理多站點配置。


GET /api/site-config — 取得當前站點設定(公開)

功能說明 公開 API,不需登入。根據環境變數 SITE_CODE 或 request header 回傳當前站點的完整設定,包含當前選中主題的完整色號體系。前台 App 啟動時呼叫此端點取得站點配置、主題色彩、可用主題列表等資訊。

認證需求:無(公開端點)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "siteCode": "C9",
    "siteName": "C9",
    "siteDescription": "最佳線上娛樂平台",
    "supportedLocales": ["zh-TW", "en-US"],
    "activeTheme": {
      "themeId": "default-emerald",
      "themeName": "翡翠綠",
      "primary": { "base": "#34d399", "dark": "#059669", "light": "#6ee7b7", "glow": "16, 185, 129" },
      "accent": { "gold": "#fbbf24", "info": "#38bdf8", "violet": "#a78bfa", "cyan": "#22d3ee", "error": "#fb7185" },
      "surface": { "page": "#0f172a", "navbar": "#1e293b", "card": "#131f30", "modal": "#0c1a2e", "sidebar": ["#0d1b2a", "#0a1628", "#071020"] },
      "text": { "primary": "rgba(255,255,255,1)", "secondary": "rgba(255,255,255,0.6)", "muted": "rgba(255,255,255,0.4)", "hint": "rgba(255,255,255,0.25)" },
      "border": { "subtle": "rgba(255,255,255,0.06)", "default": "rgba(255,255,255,0.1)", "strong": "rgba(255,255,255,0.15)" }
    },
    "availableThemes": [
      { "themeId": "default-emerald", "themeName": "翡翠綠" }
    ]
  }
}

錯誤碼2001 查無此站點設定

業務邏輯說明

  • activeTheme 為當前啟用主題的完整色號,前台用 CSS 變數注入
  • availableThemes 為所有可用主題(用戶可在前台切換)
  • supportedLocales 決定前台顯示的語系切換選項
  • 多語系欄位(siteNamesiteDescription)依 request 語系解析

前端注意事項 / UI 設計提示

  • App 啟動時呼叫,將主題色號注入 CSS 變數
  • 主題色號對應 30+ CSS custom properties(primary-base, surface-page 等)
  • availableThemes 用於主題切換下拉選單

GET /api/site-config/admin/list — [Admin] 取得所有站點設定

功能說明 取得所有站點的設定列表(含主題),回傳原始多語系 JSON。後台站點管理頁面與 SiteFilterInitializer 啟動時呼叫。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:read 權限)

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": [
    {
      "id": 1,
      "siteCode": "C9",
      "prefix": "a1",
      "siteName": { "zh-TW": "C9 娛樂城", "en-US": "C9 Casino" },
      "siteDescription": { "zh-TW": "最佳線上娛樂平台", "en-US": "Best Online Entertainment" },
      "supportedLocales": ["zh-TW", "en-US"],
      "activeThemeId": 1,
      "enabled": 1,
      "themes": [...]
    }
  ]
}

POST /api/site-config/admin — [Admin] 新增站點設定

功能說明 建立新的站點設定。siteCodeprefix 必須唯一。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

Request

位置參數型別必填說明範例
BodysiteCodestring站點代碼(唯一,最長 30 碼)"C9"
Bodyprefixstring白牌前綴(唯一,對應 R2 路徑)"a1"
Bodylayoutstring前台模板代碼(預設 a1"a1"
BodysiteNameobject站點名稱(多語系 JSON){"zh-TW":"C9 娛樂城"}
BodysiteDescriptionobject站點介紹(多語系 JSON)
BodysupportedLocalesstring[]支援語系["zh-TW","en-US"]

錯誤碼2004 站點代碼或前綴已存在


PATCH /api/site-config/admin/:id — [Admin] 更新站點設定

功能說明 更新指定站點的設定。此端點支援更新站點的所有配置項目,包含基本資訊、域名設置、客服配置、三方登入、遊戲商配置、服務商配置、佈局配置(底部導航、頁尾、了解更多)等。所有欄位均為 optional,只傳需要更新的欄位。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

Request(常用欄位)

位置參數型別必填說明
URL Paramidnumber站點設定 ID
Bodyprefixstring白牌前綴
Bodylayoutstring前台模板代碼
BodysiteNameobject站點名稱(多語系)
BodysiteDescriptionobject站點介紹(多語系)
BodysupportedLocalesstring[]支援語系
BodyactiveThemeIdnumber當前使用的主題 ID
Bodyenablednumber是否啟用(0=關 1=開)
BodyagentTourEnablednumber代理導覽功能開關
BodyagentTourIntervalSecnumber代理導覽重新提醒間隔(秒)
BodydepositMethodsobject前台存款通路開關(fiat/credit/crypto)
Bodydomainsarray域名設置(含 logo/favicon/支援語系)
BodybottomBarEnablednumber下導列功能開關
BodybottomBarConfigobject下導列配置(mobile/desktop)
BodyfooterConfigarray頁腳配置
BodylearnMoreConfigarray站點介紹 FAQ 配置
BodycustomerServiceConfigobject客服設置(8 種管道 + LiveChat)
BodyoauthProvidersobject三方登入設定(Google/Telegram/白名單)
BodygameProvidersobject遊戲商設定(RSG/BetSolutions/LiveSports)
BodynotificationConfigobject自動通知設定
BodytemplateVariablesarray模板變數
BodyserviceProvidersobject服務商設定(Resend/Twilio/R2)

錯誤碼2001 查無此站點設定


DELETE /api/site-config/admin/:id — [Admin] 刪除站點設定

功能說明 刪除站點及其所有關聯主題(cascade 刪除)。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

錯誤碼2001 查無此站點設定


GET /api/site-config/admin/:siteConfigId/themes — [Admin] 取得指定站點的主題列表

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:read


POST /api/site-config/admin/:siteConfigId/themes — [Admin] 新增主題

功能說明 為指定站點建立新主題。包含完整色號體系:主色系(primary)、強調色(accent)、表面色(surface)、文字色(text)、邊框色(border)。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

Request

位置參數型別必填說明範例
URL ParamsiteConfigIdnumber站點設定 ID1
BodythemeIdstring主題識別碼(最長 50 碼)"default-emerald"
BodythemeNameobject主題名稱(多語系){"zh-TW":"翡翠綠","en-US":"Emerald"}
Bodyprimaryobject主色系{"base":"#34d399","dark":"#059669","light":"#6ee7b7","glow":"16,185,129"}
Bodyaccentobject強調色{"gold":"#fbbf24","info":"#38bdf8","violet":"#a78bfa","cyan":"#22d3ee","error":"#fb7185"}
Bodysurfaceobject表面色{"page":"#0f172a","navbar":"#1e293b","card":"#131f30","modal":"#0c1a2e","sidebar":["#0d1b2a","#0a1628","#071020"]}
Bodytextobject文字色{"primary":"rgba(255,255,255,1)","secondary":"rgba(255,255,255,0.6)","muted":"rgba(255,255,255,0.4)","hint":"rgba(255,255,255,0.25)"}
Bodyborderobject邊框色{"subtle":"rgba(255,255,255,0.06)","default":"rgba(255,255,255,0.1)","strong":"rgba(255,255,255,0.15)"}
Bodyenablednumber是否啟用(0=關 1=開,預設 1)1

錯誤碼2001 查無此站點設定


PATCH /api/site-config/admin/themes/:id — [Admin] 更新主題

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

所有欄位為 optional(PartialType(CreateSiteThemeDto)),只傳需更新的欄位。

錯誤碼2002 查無此主題


DELETE /api/site-config/admin/themes/:id — [Admin] 刪除主題

功能說明 刪除指定主題。若刪除的是當前 activeTheme(站點正在使用的主題),會自動清除 activeThemeId 欄位。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

錯誤碼2002 查無此主題


POST /api/site-config/admin/:id/domain-asset — [Admin] 上傳域名資源

功能說明 上傳指定站點的域名資源(logo / favicon)。支援 logoSmall(小 logo)、logoBig(大 logo)、favicon 三種資源類型。圖片上傳至 Cloudflare R2,並更新對應域名的資源 URL。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

Request

位置參數型別必填說明範例
URL Paramidnumber站點設定 ID1
Body (FormData)hostnamestring域名"example.com"
Body (FormData)assetTypestring資源類型"logoSmall" / "logoBig" / "favicon"
Body (FormData)fileFile圖片檔案(image/*,最大 5MB)

錯誤碼2001 查無此站點設定


POST /api/site-config/admin/:id/customer-service-icon — [Admin] 上傳客服管道圖示

功能說明 上傳客服管道的自訂圖示。圖片上傳至 R2,回傳 R2 key 供後續設定使用。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

Request

位置參數型別必填說明
URL Paramidnumber站點設定 ID
Body (FormData)fileFile圖示圖片(image/*,最大 2MB)

錯誤碼2001 查無此站點設定

前端注意事項 / UI 設計提示

  • 客服圖示建議尺寸 64x64 或 128x128 px
  • 最大 2MB(比其他上傳的 5MB 限制更小)

PATCH /api/site-config/admin/:siteConfigId/mascots — [Admin] 更新吉祥物列表

功能說明 整包替換指定站點的吉祥物清單。傳入完整的吉祥物陣列,會覆蓋原有的吉祥物列表。吉祥物圖片需先上傳至 R2,再將 URL 傳入此端點。

認證需求AdminJwtAuthGuard + PermissionsGuard(需 site-config:write

Request

位置參數型別必填說明
URL ParamsiteConfigIdnumber站點設定 ID
Bodymascotsarray吉祥物清單
Bodymascots[].idstring吉祥物識別碼
Bodymascots[].labelstring吉祥物名稱
Bodymascots[].urlstring吉祥物圖片 R2 URL

Response 範例

json
{
  "code": 200,
  "message": "ok",
  "result": {
    "mascots": [
      {
        "id": "c9-dragon",
        "label": "翡翠龍",
        "url": "https://pub-xxx.r2.dev/a1/avatars/mascots/c9-dragon.png"
      }
    ]
  }
}

錯誤碼2001 查無此站點設定

前端注意事項 / UI 設計提示

  • 吉祥物管理頁面:拖拽排序 + 新增/刪除
  • 圖片先透過 R2 上傳端點取得 URL,再傳入此 API
  • 用於前台用戶頭像選擇(非上傳自訂頭像的替代方案)

S2S 伺服器回調(不公開)

遊戲商回調端點

以下端點為遊戲商(BetSolutions、RSG)的 Server-to-Server 回調端點,不包含在 Swagger API 文件中(使用 @ApiExcludeEndpoint() 裝飾器隱藏)。這些端點由遊戲商伺服器直接呼叫,處理遊戲內的下注、派彩、回滾等操作。

BetSolutions S2S 回調

  • 路由前綴:/api/game/betsolutions/
  • 功能:處理 BetSolutions 遊戲商的錢包操作回調
  • 包含:認證(Auth)、下注(Bet)、派彩(Win)、回滾(Rollback)等端點
  • 錢包模式:轉帳錢包(Transfer Wallet),每次操作直接更新用戶餘額

RSG S2S 回調

  • 路由前綴:/api/game/rsg/
  • 功能:處理 RSG 遊戲商的錢包操作回調
  • 特殊:使用 DES 加解密(desKey + desIv)保護通訊安全
  • 包含:驗證(Verify)、下注(Debit)、派彩(Credit)、取消(Cancel)等端點

設計原則

  • 不對外暴露在 Swagger 文件中,避免外部誤呼叫
  • 每個回調端點都有嚴格的簽章/加密驗證機制
  • 下注結算完成後自動觸發連鎖操作:VIP 等級重算、活動打碼量累計、任務投注進度更新
  • 所有金額操作使用 decimal(18,6) 精度,配合 truncateUsd() 無條件捨去