C9 Platform — QA 測試規格書
版本:v1.0 目標讀者:QA 工程師、測試人員 涵蓋範圍:前台 (c9-ec)、後台 (c9-ims)、後端 (c9-be) 全功能測試 最後更新:2026-03-01
目錄
- 第 1 章:測試環境
- 第 2 章:前台測試案例 (c9-ec)
- 第 3 章:後台測試案例 (c9-ims)
- 第 4 章:後端 API 測試案例 (c9-be)
- 第 5 章:業務流程端對端測試
- 第 6 章:多站點隔離測試
- 第 7 章:安全性測試
- 第 8 章:效能測試建議
- 第 9 章:回歸測試清單
- 第 10 章:Bug 報告模板
- 附錄 A:測試資料準備指南
- 附錄 B:API 測試工具使用指南
- 附錄 C:常見測試陷阱
- 附錄 D:自動化測試擴展建議
- 附錄 E:邊界值與異常情境測試
- 附錄 F:跨模組整合測試情境
- 附錄 G:測試術語表
- 附錄 H:測試案例編號規則
- 附錄 I:環境與部署測試
- 附錄 J:測試排程與計劃模板
- 附錄 K:資料庫 Schema 測試
- 附錄 L:API Contract 測試
- 附錄 M:行動裝置與響應式測試
第 1 章:測試環境
1.1 系統需求
1.1.1 硬體需求
| 項目 | 最低需求 | 建議配置 |
|---|---|---|
| CPU | 4 核心 | 8 核心以上 |
| 記憶體 | 8 GB | 16 GB 以上 |
| 硬碟 | 20 GB 可用空間 | 50 GB SSD |
| 網路 | 穩定寬頻連線 | 100 Mbps 以上 |
1.1.2 軟體需求
| 軟體 | 版本 | 用途 | 安裝方式 |
|---|---|---|---|
| Node.js | 20.x 以上 | JavaScript 執行環境 | nvm install 20 或官網下載 |
| Yarn | 1.22.x | c9-ec / c9-be 套件管理 | npm install -g yarn |
| pnpm | 8.x 以上 | c9-ims 套件管理 | npm install -g pnpm |
| MySQL | 8.0 以上 | 主資料庫 | brew install mysql (macOS) |
| Redis | 7.x 以上 | 快取伺服器 | brew install redis (macOS) |
| Git | 2.40 以上 | 版本控制 | brew install git (macOS) |
| GitHub CLI | 2.x | GitHub 操作 | brew install gh (macOS) |
1.1.3 瀏覽器需求(前端測試)
| 瀏覽器 | 版本 | 優先級 |
|---|---|---|
| Google Chrome | 最新穩定版 | P1(主要測試瀏覽器) |
| Mozilla Firefox | 最新穩定版 | P2 |
| Safari | 最新版 | P2(macOS / iOS) |
| Microsoft Edge | 最新版 | P3 |
| Chrome Mobile | 最新版 | P1(行動版測試) |
| Safari Mobile | 最新版 | P2(iOS 測試) |
1.1.4 開發工具
| 工具 | 用途 | 說明 |
|---|---|---|
| VS Code | 程式碼編輯 | 建議安裝 ESLint、Prettier、Vue Official、i18n Ally 擴充 |
| Postman / Insomnia | API 測試 | 手動 API 呼叫與除錯 |
| MySQL Workbench | 資料庫管理 | 查看資料表與資料內容 |
| Redis Desktop Manager | Redis 管理 | 查看快取資料 |
| Chrome DevTools | 前端除錯 | 網路、Console、Application(Cookie)檢查 |
1.2 環境安裝
1.2.1 專案下載
# 1. 克隆主專案(含子模組)
git clone --recurse-submodules <repo-url> c9
cd c9
# 2. 若子模組未初始化
git submodule init
git submodule update1.2.2 依賴安裝
# 根目錄(安裝 concurrently 等工具)
cd c9
yarn install
# 前台 (c9-ec)
cd c9-ec
yarn install
# 後台 (c9-ims)
cd c9-ims
pnpm install
# 後端 (c9-be)
cd c9-be
yarn install1.2.3 環境變數設定
c9-be(後端)
在 c9-be/ 目錄下建立 .env 檔案:
# 資料庫
DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=your_password
DB_DATABASE=c9_dev
# Redis
REDIS_URL=redis://localhost:6379
# JWT
JWT_SECRET=your_jwt_secret
ADMIN_JWT_SECRET=your_admin_jwt_secret
# 站點
SITE_CODE=C9
# R2 存儲(Cloudflare)
R2_ACCESS_KEY_ID=your_r2_key
R2_SECRET_ACCESS_KEY=your_r2_secret
R2_ENDPOINT=your_r2_endpoint
R2_BUCKET=your_r2_bucket
# 其他服務(可選)
RESEND_API_KEY=your_resend_key
TWILIO_ACCOUNT_SID=your_twilio_sid
TWILIO_AUTH_TOKEN=your_twilio_tokenc9-ims(後台)
在 c9-ims/ 目錄下建立 .env.local 檔案:
NEXT_PUBLIC_SITE_ID=a1
NEXT_PUBLIC_API_BASE_URL=http://localhost:8080/api
NEXTAUTH_SECRET=your_nextauth_secret
NEXTAUTH_URL=http://localhost:3011c9-ec(前台)
在 c9-ec/ 目錄下建立 .env 檔案:
NUXT_PUBLIC_API_BASE=http://localhost:8080/api
NUXT_PUBLIC_SITE_NAME=C91.2.4 資料庫初始化
# 1. 建立資料庫
mysql -u root -p -e "CREATE DATABASE c9_dev CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 2. 啟動後端(TypeORM 自動同步 Schema)
cd c9-be
yarn dev
# 3. 執行 Seed 腳本(產生測試資料)
npx ts-node scripts/seed-all.tsSeed 資料說明:
- 涵蓋 37+ 張資料表
- 建立 5 個站點配置(C9, A1, B2, D3, E4)
- 每站建立 30 名測試用戶
- 包含 VIP 等級、返水設定、活動、遊戲商等基礎資料
- 建立管理員帳號(含 root / super_admin / general_admin 各群組)
1.3 啟動服務
1.3.1 一鍵啟動(推薦)
# 在 c9/ 根目錄
yarn dev同時啟動三個專案,終端顯示彩色標籤:
[ec]藍色 — 前台[ims]綠色 — 後台[be]黃色 — 後端
1.3.2 個別啟動
| 指令 | 說明 | 存取位址 |
|---|---|---|
yarn dev:ec | 啟動前台 | http://localhost:3010 |
yarn dev:ims | 啟動後台 | http://localhost:3011 |
yarn dev:be | 啟動後端 | http://localhost:8080/api |
1.3.3 服務埠號對照
| 服務 | 埠號 | 說明 |
|---|---|---|
| c9-ec(前台) | 3010 | Nuxt 開發伺服器 |
| c9-ims(後台) | 3011 | Next.js 開發伺服器(Turbopack) |
| c9-be(後端 API) | 8080 | NestJS 開發伺服器 |
| Swagger UI | 8080/api/docs | 互動式 API 文件 |
| MySQL | 3306 | 資料庫 |
| Redis | 6379 | 快取 |
1.3.4 健康檢查
啟動後進行以下驗證:
| 檢查項目 | 方法 | 預期結果 |
|---|---|---|
| 後端 API | curl http://localhost:8080/api | 回傳 JSON 回應 |
| Swagger UI | 瀏覽器開啟 http://localhost:8080/api/docs | 顯示 Swagger 頁面 |
| 前台首頁 | 瀏覽器開啟 http://localhost:3010 | 顯示首頁畫面 |
| 後台登入 | 瀏覽器開啟 http://localhost:3011 | 顯示登入頁面 |
| 資料庫連線 | 後端啟動日誌無錯誤 | TypeORM 連線成功訊息 |
| Redis 連線 | 後端啟動日誌無錯誤 | Redis 連線成功訊息 |
1.4 測試工具與指令
1.4.1 測試框架對照
| 專案 | 測試框架 | 測試類型 | 說明 |
|---|---|---|---|
| c9-ec | Vitest | 單元測試 | Composable、工具函數測試 |
| c9-ec | Vitest + @nuxt/test-utils | 元件測試 | Vue 元件渲染測試 |
| c9-ec | Playwright | E2E 測試 | 瀏覽器端對端自動化測試 |
| c9-ims | TypeScript Compiler | 型別檢查 | yarn typecheck 確保型別正確 |
| c9-be | Jest | 單元測試 | Service / Controller 單元測試 |
| c9-be | Jest + Supertest | E2E 測試 | API 端對端整合測試 |
1.4.2 測試指令總表
c9-ec(前台)
| 指令 | 說明 | 涵蓋範圍 |
|---|---|---|
yarn test | 執行所有測試 | 單元 + 元件 |
yarn test:unit | 只跑單元測試 | Composable、工具函數 |
yarn test:nuxt | 只跑元件測試 | Vue 元件渲染與互動 |
yarn test:e2e | E2E 測試 | Playwright 瀏覽器自動化 |
yarn lint | 程式碼檢查 | ESLint 規則 |
yarn format | 格式化 | Prettier 格式化 |
c9-ims(後台)
| 指令 | 說明 | 涵蓋範圍 |
|---|---|---|
yarn typecheck | TypeScript 型別檢查 | 全專案型別正確性 |
yarn lint | 程式碼檢查 | ESLint 規則 |
yarn format | 格式化 | Prettier 格式化 |
c9-be(後端)
| 指令 | 說明 | 涵蓋範圍 |
|---|---|---|
yarn test | 執行所有單元測試 | Jest 單元測試 |
yarn test:e2e | 執行 E2E 測試 | API 整合測試 |
yarn lint | 程式碼檢查 | ESLint 規則 |
yarn format | 格式化 | Prettier 格式化 |
1.4.3 Seed 測試資料
# 在 c9-be/ 目錄下執行
npx ts-node scripts/seed-all.ts產生的測試資料:
| 類別 | 數量 | 說明 |
|---|---|---|
| 站點配置 | 5 個 | C9(預設站)、A1、B2、D3、E4 |
| 前台用戶 | 150 人 | 每站 30 人(含各 VIP 等級) |
| 管理員 | 10+ 人 | root / super_admin / general_admin / custom 各群組 |
| 管理員群組 | 4 個 | root / super_admin / general_admin / custom |
| VIP 等級 | 每站 16 級 | VIP 0 ~ VIP 15 |
| VIP 反水 | 每站 128 筆 | 16 等級 x 8 遊戲類型 |
| 遊戲供應商 | 每站 5+ 個 | BetSolutions / RSG 等 |
| 遊戲分類 | 每站 8 個 | SPORTS / SLOT / LIVE / LOTTERY / CHESS / ESPORTS / CRYPTO / FISH |
| 活動 | 每站 10+ 個 | 各類優惠活動 |
| 活動標籤 | 每站 5+ 個 | 分類標籤 |
| 站點主題 | 每站 2+ 個 | 預設 + 自訂主題 |
| 金流群組 | 每站 2+ 個 | 金流通道分組 |
| 金流通道 | 每站 5+ 個 | ATM / 信用卡 / USDT |
| 代理等級 | 每站 4 個 | bronze / silver / gold / platinum |
| 佣金費率 | 每站 96 筆 | 4 等級 x 3 級別 x 8 遊戲類型 |
測試帳號(由 Seed 產生):
| 角色 | 帳號格式 | 密碼 | 說明 |
|---|---|---|---|
| 前台用戶 | testuser01 ~ testuser30 | Test123456 | 每站 30 人 |
| Root 管理員 | root_admin | Admin123456 | 最高權限 |
| Super Admin | super_admin | Admin123456 | 除站點設定外全權限 |
| General Admin | general_admin | Admin123456 | 唯讀權限 |
| Custom Admin | custom_admin | Admin123456 | 自訂權限 |
注意:以上帳號密碼僅供開發測試環境使用,正式環境請務必更換。
1.5 測試優先級定義
| 優先級 | 代號 | 說明 | 回歸測試頻率 |
|---|---|---|---|
| 最高 | P1 | 核心業務流程,阻斷性問題 | 每次部署 |
| 高 | P2 | 重要功能,影響用戶體驗 | 每週 |
| 中 | P3 | 次要功能,不影響主流程 | 每月 |
| 低 | P4 | 美觀、文案等非功能性問題 | 視情況 |
1.6 測試嚴重程度定義
| 嚴重程度 | 代號 | 說明 | 範例 |
|---|---|---|---|
| 致命 | Critical | 系統崩潰、資料遺失、金流錯誤 | 存款金額計算錯誤、系統無法啟動 |
| 嚴重 | Major | 主要功能無法使用 | 無法登入、無法存款、遊戲無法啟動 |
| 一般 | Minor | 次要功能異常或 UI 瑕疵 | 篩選結果不正確、排序錯誤 |
| 輕微 | Trivial | 文案錯字、美觀問題 | 按鈕對齊偏移、翻譯缺漏 |
第 2 章:前台測試案例 (c9-ec)
測試對象:http://localhost:3010框架:Nuxt 4.2 (Vue 3.5 + TypeScript) 支援語系:zh-TW, en-US, zh-CN, th-TH, vi-VN
2.1 首頁測試
前置條件:
- 前台服務已啟動 (port 3010)
- 後端 API 服務已啟動 (port 8080)
- 已有 Seed 測試資料
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-HOME-001 | 首頁正常載入並顯示 Banner | 1. 開啟瀏覽器 2. 輸入 http://localhost:3010 3. 等待頁面載入完成 | 1. 頁面成功載入,無 Console 錯誤 2. Banner 區域顯示至少一張輪播圖 3. 頁面標題正確顯示站點名稱 | P1 |
| TC-HOME-002 | Banner 輪播自動切換 | 1. 開啟首頁 2. 觀察 Banner 區域 3. 等待約 5 秒 | 1. Banner 圖片自動切換至下一張 2. 切換動畫流暢 3. 輪播指示點同步更新 4. 手動點擊指示點可跳轉 | P3 |
| TC-HOME-003 | 即時賽事區顯示比賽資料 | 1. 開啟首頁 2. 捲動至即時賽事區域 3. 檢查賽事卡片內容 | 1. 顯示當前即時賽事列表 2. 每個賽事卡片包含:隊伍名稱、比分、聯賽名稱 3. 資料每 30 分鐘自動更新(API-Football 快取) | P2 |
| TC-HOME-004 | 精選活動正確顯示 | 1. 開啟首頁 2. 捲動至精選活動區域 3. 檢查活動卡片 | 1. 顯示當前站點的活動列表 2. 每個活動卡片含標題、縮圖、描述 3. 點擊卡片可進入活動詳情頁 4. 僅顯示 enabled 狀態的活動 | P2 |
| TC-HOME-005 | 導航至遊戲大廳 | 1. 開啟首頁 2. 點擊導航列「遊戲大廳」或遊戲分類快捷入口 | 1. 成功跳轉至遊戲大廳頁面 2. URL 變更為對應路由 3. 遊戲分類 Tab 正確顯示 | P1 |
| TC-HOME-006 | 未登入點擊存款導航 | 1. 確認未登入狀態 2. 開啟首頁 3. 點擊「存款」按鈕或導航項目 | 1. 自動導向登入頁面 2. 登入後自動跳轉回存款頁面 3. URL 帶有 redirect 參數 | P1 |
| TC-HOME-007 | 底部導航列正確顯示(行動版) | 1. 使用 Chrome DevTools 切換至行動裝置模式(375px) 2. 開啟首頁 | 1. 底部導航列顯示 2. 包含首頁、遊戲、存款、我的等項目 3. 點擊各項目可正常導航 | P2 |
| TC-HOME-008 | 頁尾資訊顯示 | 1. 捲動至頁面底部 2. 檢查頁尾區域 | 1. 顯示站點 Logo 2. 顯示版權資訊 3. 顯示相關連結(了解更多、客服等) 4. 連結可正常點擊 | P3 |
| TC-HOME-009 | 客服入口顯示 | 1. 開啟首頁 2. 檢查客服浮動按鈕 | 1. 客服浮動圖示顯示在右下角 2. 點擊展開客服管道列表 3. 各管道連結可正常開啟 4. LiveChat 嵌入腳本正常載入(若啟用) | P2 |
| TC-HOME-010 | 吉祥物動畫顯示 | 1. 開啟首頁 2. 檢查吉祥物區域 | 1. 吉祥物圖片/動畫正確載入 2. 互動動畫流暢 3. 不影響頁面其他元素操作 | P4 |
2.2 註冊流程測試
前置條件:
- 前台服務已啟動
- 後端 API 服務已啟動
- 未登入狀態
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-REG-001 | 有效帳密郵箱註冊 | 1. 進入註冊頁面 2. 輸入帳號:testqa001(6-20 英數字) 3. 輸入密碼:Qa123456!(8+ 位含大小寫數字) 4. 輸入 Email:testqa001@test.com 5. 勾選同意條款 6. 點擊「註冊」按鈕 | 1. 表單驗證通過(Zod) 2. 成功建立帳號 3. 自動登入並跳轉至首頁 4. JWT Token 寫入 Cookie 5. 用戶資料寫入資料庫 | P1 |
| TC-REG-002 | 重複帳號註冊 | 1. 進入註冊頁面 2. 輸入已存在的帳號(如 testuser01) 3. 填寫其他欄位 4. 點擊「註冊」 | 1. 顯示錯誤訊息「帳號已存在」 2. 錯誤訊息透過 ERROR_CODES 查表取得 3. 不建立新帳號 4. 錯誤訊息語系與當前語系一致 | P1 |
| TC-REG-003 | 無效 Email 格式 | 1. 進入註冊頁面 2. 輸入 Email:invalid-email 3. 點擊其他欄位觸發驗證 | 1. Zod 驗證攔截 2. Email 欄位顯示紅色外框 3. 顯示格式錯誤提示 4. 註冊按鈕不可點擊或提交後被擋 | P2 |
| TC-REG-004 | 弱密碼驗證 | 1. 進入註冊頁面 2. 依序測試以下密碼:a. 123(太短) b. abcdefgh(無數字) c. 12345678(無字母) 3. 觀察驗證提示 | 1. 每種弱密碼都顯示對應錯誤提示 2. 密碼強度指示器更新 3. 表單無法提交 | P2 |
| TC-REG-005 | 推薦碼綁定代理 | 1. 透過帶有 refCode 的連結進入:http://localhost:3010?ref=ABC123 2. 進入註冊頁面 3. 確認推薦碼欄位已自動填入 4. 完成註冊 | 1. refCode 自動帶入推薦碼欄位 2. 註冊成功後自動綁定上線代理 3. 在代理系統中可查到下線關係 4. affiliate-bind-log 寫入紀錄 | P1 |
| TC-REG-006 | Google OAuth 註冊 | 1. 進入註冊頁面 2. 點擊「Google 登入」按鈕 3. 完成 Google OAuth 授權流程 | 1. 導向 Google OAuth 授權頁面 2. 授權後回調至前台 3. 自動建立帳號(若首次) 4. 自動登入並跳轉至首頁 5. 用戶資料含 Google 關聯資訊 | P1 |
| TC-REG-007 | Telegram OAuth 註冊 | 1. 進入註冊頁面 2. 點擊「Telegram 登入」按鈕 3. 完成 Telegram OAuth 授權流程 | 1. 導向 Telegram 授權 Widget 2. 授權後回調至前台 3. 自動建立帳號(若首次) 4. 自動登入並跳轉至首頁 | P2 |
| TC-REG-008 | 裝置指紋擷取 | 1. 進入註冊頁面 2. 使用瀏覽器開發者工具監控網路請求 3. 完成註冊流程 | 1. FingerprintJS v5 初始化成功 2. 註冊 API 請求包含 deviceFingerprint 欄位 3. 指紋寫入 auth-user 表 4. Console 無 FingerprintJS 錯誤 | P2 |
| TC-REG-009 | 依語系自動分配金流群組 | 1. 切換語系至 en-US 2. 完成新用戶註冊 3. 登入後檢查可用金流通道 | 1. 用戶自動分配至對應語系的金流群組 2. 存款頁面顯示對應群組的通道 3. 不同語系用戶可能看到不同金流選項 | P2 |
| TC-REG-010 | 空白必填欄位提交 | 1. 進入註冊頁面 2. 不填寫任何欄位 3. 直接點擊「註冊」按鈕 | 1. 所有必填欄位顯示驗證錯誤 2. 表單不提交 3. 各欄位錯誤提示文字正確(當前語系) | P2 |
| TC-REG-011 | 帳號長度邊界值 | 1. 測試帳號長度 5 字元(低於最小值) 2. 測試帳號長度 6 字元(最小值) 3. 測試帳號長度 20 字元(最大值) 4. 測試帳號長度 21 字元(超過最大值) | 1. 5 字元 → 驗證失敗 2. 6 字元 → 驗證通過 3. 20 字元 → 驗證通過 4. 21 字元 → 驗證失敗 | P3 |
| TC-REG-012 | 帳號特殊字元 | 1. 輸入包含特殊字元的帳號:test@#$ 2. 嘗試提交 | 1. 帳號只允許英文字母和數字 2. 特殊字元被拒絕 3. 顯示格式錯誤提示 | P3 |
2.3 登入流程測試
前置條件:
- 已有註冊帳號(使用 Seed 資料
testuser01) - 未登入狀態
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-LOGIN-001 | 有效帳密登入 | 1. 進入登入頁面 2. 輸入帳號:testuser01 3. 輸入密碼:Test123456 4. 點擊「登入」按鈕 | 1. 登入成功 2. JWT Token 寫入 Cookie(7 天過期) 3. 自動跳轉至首頁 4. 導航列顯示用戶資訊 5. auth-user-login-log 寫入登入紀錄 | P1 |
| TC-LOGIN-002 | 密碼錯誤 | 1. 進入登入頁面 2. 輸入帳號:testuser01 3. 輸入錯誤密碼 4. 點擊「登入」 | 1. 顯示錯誤訊息(透過 ERROR_CODES 查表) 2. 不建立 Session 3. 停留在登入頁面 4. 登入失敗紀錄寫入資料庫 | P1 |
| TC-LOGIN-003 | 不存在的帳號 | 1. 進入登入頁面 2. 輸入帳號:nonexistent_user 3. 輸入任意密碼 4. 點擊「登入」 | 1. 顯示錯誤訊息 2. 錯誤訊息不洩漏帳號是否存在(安全考量) 3. 停留在登入頁面 | P1 |
| TC-LOGIN-004 | Google OAuth 登入 | 1. 進入登入頁面 2. 點擊「Google 登入」按鈕 3. 使用已綁定的 Google 帳號授權 | 1. 導向 Google OAuth 頁面 2. 授權後回調成功 3. 已綁定帳號直接登入 4. JWT Token 正確寫入 | P1 |
| TC-LOGIN-005 | Telegram OAuth 登入 | 1. 進入登入頁面 2. 點擊「Telegram 登入」Widget 3. 使用已綁定的 Telegram 帳號授權 | 1. Telegram Widget 載入正常 2. 授權後回調成功 3. 已綁定帳號直接登入 | P2 |
| TC-LOGIN-006 | 2FA 驗證(Google Authenticator) | 1. 使用已啟用 2FA 的帳號 2. 輸入正確帳密 3. 系統要求輸入 6 位 TOTP 驗證碼 4. 輸入正確驗證碼 | 1. 帳密驗證通過後,顯示 2FA 輸入介面 2. 輸入正確 TOTP → 登入成功 3. 輸入錯誤 TOTP → 顯示驗證失敗 4. TOTP 有 30 秒有效期 | P1 |
| TC-LOGIN-007 | 裝置指紋隨登入傳送 | 1. 使用瀏覽器開發者工具監控網路請求 2. 進行正常登入流程 3. 檢查登入 API 請求 Body | 1. 登入請求包含 deviceFingerprint 欄位 2. 指紋值為 FingerprintJS 產生的字串 3. 後端儲存至登入紀錄 | P2 |
| TC-LOGIN-008 | JWT Token 有效期 | 1. 正常登入 2. 使用 Chrome DevTools > Application > Cookies 檢查 Token 3. 驗證過期時間 | 1. JWT Token 存在於 Cookie 中 2. Token 過期時間為 7 天 3. Token 內含用戶 ID 和角色資訊 | P2 |
| TC-LOGIN-009 | 受保護頁面重導向 | 1. 確認未登入狀態 2. 直接存取 http://localhost:3010/deposit 3. 完成登入流程 | 1. 未登入時自動跳轉至登入頁 2. URL 帶有 redirect 參數指向原始頁面 3. 登入成功後自動跳轉回存款頁面 | P1 |
| TC-LOGIN-010 | 登出功能 | 1. 已登入狀態 2. 點擊導航列「登出」按鈕 | 1. JWT Token 從 Cookie 中移除 2. 跳轉至首頁或登入頁 3. 無法再存取受保護頁面 4. 後端 Token 失效 | P1 |
| TC-LOGIN-011 | 多次登入失敗 | 1. 使用正確帳號 2. 連續輸入 5 次錯誤密碼 3. 觀察系統反應 | 1. 每次失敗都寫入登入失敗紀錄 2. 後台可查到失敗紀錄(含 IP 和時間) 3. 視風控設定可能觸發帳號鎖定或驗證碼 | P2 |
| TC-LOGIN-012 | Token 過期後行為 | 1. 登入成功取得 Token 2. 手動修改 Cookie 中 Token 的過期時間為過去 3. 重新載入頁面 | 1. 過期 Token 被後端拒絕(401) 2. 前端自動清除 Token 3. 重導向至登入頁面 | P1 |
2.4 遊戲大廳測試
前置條件:
- 已登入狀態
- Seed 資料含遊戲供應商和遊戲分類
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-GAME-001 | 遊戲大廳載入 8 個分類 Tab | 1. 導航至遊戲大廳 2. 檢查分類 Tab 列 | 1. 顯示 8 個遊戲分類 Tab:體育(SPORTS)、老虎機(SLOT)、真人(LIVE)、彩票(LOTTERY)、棋牌(CHESS)、電競(ESPORTS)、加密(CRYPTO)、捕魚(FISH) 2. 預設選中第一個 Tab 3. Tab 圖示和文字正確 | P1 |
| TC-GAME-002 | 切換遊戲分類 Tab | 1. 在遊戲大廳頁面 2. 依序點擊每個分類 Tab | 1. 每次切換顯示對應分類的供應商列表 2. 供應商卡片含 Logo 和名稱 3. 切換動畫流暢 4. 無已提供的遊戲商 → 顯示空狀態 | P1 |
| TC-GAME-003 | 搜尋遊戲名稱 | 1. 在遊戲大廳頁面 2. 在搜尋框輸入遊戲名稱關鍵字 3. 觀察搜尋結果 | 1. 即時篩選顯示匹配的遊戲 2. 支援模糊搜尋 3. 無結果時顯示空狀態提示 4. 清除搜尋框後恢復完整列表 | P2 |
| TC-GAME-004 | 點擊供應商展開遊戲列表 | 1. 在遊戲大廳選擇某分類 2. 點擊某個供應商卡片 | 1. 展開或跳轉至該供應商的遊戲列表 2. 遊戲列表含遊戲名稱、縮圖 3. 可點擊單一遊戲進入 | P1 |
| TC-GAME-005 | 最近遊玩顯示 | 1. 先啟動並遊玩幾款遊戲 2. 回到遊戲大廳 3. 檢查「最近遊玩」區域 | 1. 顯示最近遊玩的遊戲(最多 10 款) 2. 按遊玩時間倒序排列 3. 點擊可重新進入遊戲 4. 未遊玩過任何遊戲時不顯示此區域 | P3 |
| TC-GAME-006 | 排行榜顯示 | 1. 導航至遊戲大廳 2. 檢查排行榜區域 | 1. 顯示排行榜資料(大贏家等) 2. 包含用戶名稱(部分遮罩)、金額、遊戲名稱 3. 資料正確排序 | P3 |
| TC-GAME-007 | 啟動遊戲(iframe) | 1. 已登入狀態 2. 選擇一款遊戲 3. 點擊「進入遊戲」 | 1. 遊戲 iframe 成功載入 2. 遊戲介面正確顯示 3. 餘額資訊正確 4. API 呼叫 /game/launch 成功 5. game-play-log 寫入紀錄 | P1 |
| TC-GAME-008 | 試玩模式(Demo) | 1. 未登入或已登入狀態 2. 選擇支援試玩的遊戲 3. 點擊「試玩」按鈕 | 1. 遊戲以試玩模式啟動 2. 使用虛擬餘額 3. 不影響真實帳戶餘額 4. 部分遊戲可能不支援試玩 | P2 |
| TC-GAME-009 | 風控黑名單阻擋 | 1. 透過後台將測試用戶加入遊戲黑名單 2. 使用該用戶登入前台 3. 嘗試啟動被封鎖的遊戲 | 1. 遊戲啟動被阻擋 2. 返回錯誤碼 5010 3. 顯示對應錯誤訊息 4. 全封鎖用戶無法啟動任何遊戲 | P1 |
| TC-GAME-010 | 代表遊戲顯示(子分類) | 1. 在遊戲大廳查看分類 2. 檢查有子分類的遊戲類型 | 1. 子分類顯示代表遊戲(第一款) 2. 點擊可展開完整子分類遊戲列表 3. 無子分類的類型直接顯示遊戲列表 | P3 |
| TC-GAME-011 | 未登入時啟動遊戲 | 1. 未登入狀態 2. 嘗試點擊「進入遊戲」 | 1. 跳轉至登入頁面 2. 登入後回到遊戲頁面 3. 可重新點擊進入遊戲 | P1 |
| TC-GAME-012 | 遊戲載入失敗處理 | 1. 模擬遊戲供應商 API 異常 2. 嘗試啟動遊戲 | 1. 顯示友善的錯誤提示 2. 提供重試按鈕 3. 不出現白畫面或無限載入 | P2 |
2.5 存款流程測試
前置條件:
- 已登入狀態
- 用戶已分配金流群組
- 有可用的金流通道
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-DEP-001 | 法幣存款(ATM) | 1. 進入存款頁面 2. 選擇「銀行轉帳 / ATM」通道 3. 輸入存款金額 4. 確認提交 | 1. 通道列表正確載入(依金流群組篩選) 2. 顯示匯率資訊(TWD → USD) 3. 產生存款訂單 4. 顯示付款資訊(銀行帳號等) 5. 訂單狀態為「待處理」 | P1 |
| TC-DEP-002 | 信用卡存款 | 1. 進入存款頁面 2. 選擇「信用卡」通道 3. 輸入存款金額 4. 確認提交 | 1. 跳轉至信用卡付款頁面 2. 顯示金額與匯率 3. 產生存款訂單 4. 付款完成後回調更新狀態 | P1 |
| TC-DEP-003 | 加密貨幣存款(USDT) | 1. 進入存款頁面 2. 選擇「USDT」通道 3. 輸入存款金額 | 1. 顯示 USDT 存款地址(TRC-20 或 ERC-20) 2. 顯示 QR Code 3. 顯示匯率(USDT → USD) 4. 產生存款訂單 | P1 |
| TC-DEP-004 | 匯率顯示正確性 | 1. 進入存款頁面 2. 輸入 1000 TWD 3. 檢查換算結果 | 1. 顯示台灣銀行即時匯率 2. 換算後 USD 金額正確 3. 使用無條件捨去(Math.floor(value * 1e6) / 1e6) 4. 顯示精度為小數 6 位 | P1 |
| TC-DEP-005 | 建立存款訂單 | 1. 選擇通道並輸入金額 2. 確認提交 3. 檢查訂單 | 1. API 回應 code: 200 2. deposit-order 表新增一筆紀錄 3. 訂單狀態為 pending 4. 訂單含正確金額(USD)和匯率 5. 訂單含 siteCode | P1 |
| TC-DEP-006 | 存款歷史紀錄 | 1. 完成一筆或多筆存款 2. 進入交易紀錄頁面 3. 查看存款歷史 | 1. 列出所有存款訂單 2. 每筆含:訂單編號、金額、狀態、時間 3. 支援分頁瀏覽 4. 可篩選狀態 | P2 |
| TC-DEP-007 | 最小/最大金額驗證 | 1. 輸入低於最小存款金額(如 0 或 1) 2. 輸入高於最大存款金額 3. 嘗試提交 | 1. 低於最小值 → 顯示最小金額限制提示 2. 高於最大值 → 顯示最大金額限制提示 3. 表單不提交 4. 邊界值(等於最小/最大)→ 允許 | P2 |
| TC-DEP-008 | 幣值轉換精度 | 1. 輸入會產生長小數的金額 2. 完成存款流程 3. 檢查資料庫中的金額 | 1. USD 金額精度為 decimal(18,6) 2. 使用截斷而非四捨五入 3. 匯率精度為 decimal(18,10) 4. 計算結果與前端顯示一致 | P1 |
| TC-DEP-009 | 無可用通道時顯示 | 1. 使用未分配金流群組的帳號 2. 進入存款頁面 | 1. 顯示「暫無可用存款方式」提示 2. 不顯示空白頁面 3. 提供聯絡客服引導 | P3 |
| TC-DEP-010 | 存款後餘額更新 | 1. 記錄當前餘額 2. 完成一筆存款(待管理員審核通過) 3. 審核通過後重新載入 | 1. 審核通過前餘額不變 2. 審核通過後餘額增加(存款 USD 金額) 3. 餘額顯示精度正確 | P1 |
2.6 提款流程測試
前置條件:
- 已登入狀態
- 帳戶有足夠餘額
- 已綁定至少一張銀行卡或加密錢包
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-WD-001 | 銀行卡提款 | 1. 進入提款頁面 2. 選擇已審核通過的銀行卡 3. 輸入提款金額 4. 確認提交 | 1. 銀行卡列表僅顯示審核通過的卡片 2. 顯示可提款餘額 3. 訂單建立成功,狀態為 pending 4. withdrawal-order 表新增紀錄 | P1 |
| TC-WD-002 | 加密錢包提款 | 1. 進入提款頁面 2. 選擇已審核通過的加密地址(TRC-20 / ERC-20) 3. 輸入提款金額 4. 確認提交 | 1. 加密地址列表正確顯示 2. 顯示鏈類型(TRC-20 / ERC-20) 3. 訂單建立成功 4. 顯示預估到帳時間 | P1 |
| TC-WD-003 | 超額提款 | 1. 進入提款頁面 2. 輸入金額超過可用餘額 3. 嘗試提交 | 1. 前端驗證攔截 2. 顯示「餘額不足」提示 3. 訂單不建立 4. 即使繞過前端驗證,後端也會拒絕 | P1 |
| TC-WD-004 | 提款訂單狀態 | 1. 成功建立提款訂單 2. 檢查訂單狀態 | 1. 初始狀態為 pending(待審核) 2. 管理員審核後變為 approved 或 rejected 3. 完成後變為 completed 4. 各狀態轉換時間記錄正確 | P1 |
| TC-WD-005 | 提款歷史紀錄 | 1. 進入交易紀錄頁面 2. 切換至提款歷史分頁 | 1. 列出所有提款訂單 2. 含訂單編號、金額、狀態、提款方式、時間 3. 支援分頁 4. 可篩選狀態 | P2 |
| TC-WD-006 | 凍結餘額更新 | 1. 記錄當前餘額和凍結餘額 2. 建立提款訂單 3. 檢查餘額變化 | 1. 可用餘額減少(扣除提款金額) 2. 凍結餘額增加(加上提款金額) 3. 總餘額不變 4. 提款拒絕後凍結餘額退回可用餘額 | P1 |
| TC-WD-007 | 無綁定卡片時提款 | 1. 使用未綁定任何銀行卡/加密地址的帳號 2. 進入提款頁面 | 1. 顯示「請先綁定提款方式」提示 2. 提供跳轉至錢包管理的連結 3. 無法直接提交提款 | P2 |
| TC-WD-008 | 提款最小金額限制 | 1. 輸入低於最小提款金額 2. 嘗試提交 | 1. 顯示最小提款金額限制 2. 表單不提交 3. 邊界值等於最小值 → 允許 | P2 |
2.7 錢包管理測試
前置條件:
- 已登入狀態
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-WALLET-001 | 新增銀行卡(有效資料) | 1. 進入錢包管理頁面 2. 點擊「新增銀行卡」 3. 填寫:銀行名稱、分行、帳號、持卡人姓名 4. 提交 | 1. 表單驗證通過 2. 銀行卡建立成功 3. 狀態為 pending(待審核) 4. 列表中顯示新卡片 | P1 |
| TC-WALLET-002 | 新增銀行卡(無效資料) | 1. 進入新增銀行卡表單 2. 輸入無效銀行帳號格式 3. 留空必填欄位 4. 嘗試提交 | 1. Zod 驗證攔截 2. 各欄位顯示對應錯誤提示 3. 表單不提交 | P2 |
| TC-WALLET-003 | 刪除銀行卡 | 1. 在銀行卡列表中 2. 點擊某張卡片的「刪除」按鈕 3. 確認刪除 | 1. 顯示確認對話框 2. 確認後卡片從列表移除 3. 資料庫標記為刪除 4. 若有進行中的提款使用此卡 → 不允許刪除 | P2 |
| TC-WALLET-004 | 新增信用卡 | 1. 進入錢包管理 2. 點擊「新增信用卡」 3. 填寫卡號、有效期、CVV、持卡人 4. 提交 | 1. 卡號格式驗證(Luhn 演算法) 2. 信用卡建立成功 3. 卡號部分遮罩顯示 4. 狀態為 pending | P1 |
| TC-WALLET-005 | 新增加密地址(TRC-20) | 1. 進入錢包管理 2. 點擊「新增加密地址」 3. 選擇網路:TRC-20 4. 輸入 USDT 地址 5. 提交 | 1. 地址格式驗證(TRC-20 格式) 2. 地址建立成功 3. 顯示鏈類型標籤 4. 狀態為 pending | P1 |
| TC-WALLET-006 | 銀行卡審核狀態顯示 | 1. 新增一張銀行卡 2. 查看列表中的狀態標籤 | 1. 待審核 → 黃色/橙色標籤 pending 2. 審核通過 → 綠色標籤 approved 3. 審核拒絕 → 紅色標籤 rejected 4. 只有 approved 狀態的卡片可用於提款 | P2 |
| TC-WALLET-007 | 查看卡片詳情 | 1. 點擊某張卡片查看詳情 | 1. 顯示完整卡片資訊 2. 敏感資訊部分遮罩(如卡號中間位數) 3. 顯示建立時間和審核狀態 | P3 |
| TC-WALLET-008 | 新增加密地址(ERC-20) | 1. 進入錢包管理 2. 選擇網路:ERC-20 3. 輸入以 0x 開頭的地址 4. 提交 | 1. 地址格式驗證(ERC-20 格式,0x 開頭) 2. 地址建立成功 3. 與 TRC-20 地址區分顯示 | P2 |
2.8 VIP 頁面測試
前置條件:
- 已登入狀態
- 用戶有 VIP 等級資料
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-VIP-001 | VIP 狀態卡顯示 | 1. 進入 VIP 頁面 2. 檢查 VIP 狀態卡 | 1. 顯示當前 VIP 等級(如 VIP 3) 2. 顯示等級圖示/徽章 3. 顯示距離下一等級的進度條 4. 顯示累計投注金額 5. 顯示下一等級門檻金額 | P1 |
| TC-VIP-002 | 等級列表展示 | 1. 在 VIP 頁面檢查等級列表區域 | 1. 顯示所有 VIP 等級(動態取得,不限 15 級) 2. 每個等級含名稱、門檻、圖示 3. 當前等級高亮顯示 4. 已達成等級與未達成等級視覺區分 | P1 |
| TC-VIP-003 | 福利說明顯示 | 1. 在 VIP 頁面檢查福利區域 | 1. 每個等級的福利內容正確顯示 2. 包含返水比例、生日禮金等資訊 3. 資料與後端設定一致 | P2 |
| TC-VIP-004 | 我的返水紀錄 | 1. 在 VIP 頁面切換至「我的返水」分頁 2. 檢查返水紀錄列表 | 1. 顯示返水發放紀錄 2. 每筆含日期、遊戲類型、投注金額、返水金額 3. 支援分頁 4. 僅顯示當前用戶的紀錄 | P2 |
| TC-VIP-005 | 返水比例表 | 1. 在 VIP 頁面查看返水比例表 | 1. 顯示各等級 x 各遊戲類型的返水比例 2. 8 種遊戲類型對應列 3. 當前等級行高亮 4. 比例精度 decimal(5,2) | P2 |
| TC-VIP-006 | VIP 等級動態取得 | 1. 檢查 VIP 頁面的等級數量 2. 對照後端資料庫設定 | 1. 等級數量由後端動態決定(不硬編碼) 2. 若後端新增等級,前端自動顯示 3. 下拉選項等也同步更新 | P2 |
| TC-VIP-007 | 保級資訊顯示 | 1. VIP 5+ 用戶檢查保級資訊 | 1. 顯示保級所需的月度投注門檻 2. 顯示當月已投注金額 3. 顯示保級截止日期 4. VIP 5 以下不顯示保級資訊 | P3 |
2.9 代理中心測試
前置條件:
- 已登入且為代理身分的帳號
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-AFF-001 | 代理儀表板顯示 | 1. 進入代理中心 2. 檢查儀表板資訊 | 1. 顯示下線總人數 2. 顯示本週/本月佣金 3. 顯示累計佣金 4. 顯示代理等級(bronze/silver/gold/platinum) 5. 資料正確且即時 | P1 |
| TC-AFF-002 | 下線樹狀顯示 | 1. 進入代理中心 2. 查看「我的下線」 | 1. 以樹狀或列表顯示下線結構 2. 最多 3 層代理結構 3. 下線帳號經遮罩處理(maskAccount) 4. 顯示各層下線人數 | P1 |
| TC-AFF-003 | 佣金紀錄列表 | 1. 進入代理中心 2. 查看「佣金紀錄」 | 1. 列出佣金發放紀錄 2. 每筆含來源下線、遊戲類型、金額、日期 3. 支援日期篩選 4. 支援分頁 | P2 |
| TC-AFF-004 | 結算紀錄與狀態 | 1. 進入代理中心 2. 查看「結算紀錄」 | 1. 顯示週結/日結紀錄 2. 狀態:pending → approved/rejected → completed 3. 含結算金額、下線人數、風控結果 4. 可查看結算明細 | P1 |
| TC-AFF-005 | 代理餘額提款 | 1. 代理帳戶有佣金餘額 2. 在代理中心點擊「提款」 3. 輸入金額並提交 | 1. 顯示可提款佣金餘額 2. 輸入金額驗證(不超過餘額) 3. 建立代理提款訂單 4. 訂單狀態為 pending 5. 代理餘額凍結對應金額 | P1 |
| TC-AFF-006 | 聯盟資訊頁面 | 1. 在代理中心查看「聯盟資訊」 | 1. 顯示代理等級資訊(agent-tier) 2. 顯示佣金費率表(按等級 x 遊戲類型) 3. 顯示 VIP 里程碑獎勵列表 4. 公開 API 資料正確 | P2 |
| TC-AFF-007 | 推薦碼管理 | 1. 進入代理中心 2. 查看「推薦碼管理」 | 1. 顯示已建立的推薦碼列表(含短碼和連結) 2. 可新增推薦碼(最多 10 個) 3. 可刪除推薦碼 4. 每個推薦碼可追蹤點擊數 5. 超過 10 個 → 顯示上限提示 | P1 |
| TC-AFF-008 | 非代理用戶存取 | 1. 使用非代理身分帳號 2. 嘗試存取代理中心頁面 | 1. 顯示代理申請頁面或提示 2. 無法查看代理資料 3. 提供申請成為代理的入口 | P2 |
2.10 任務系統測試
前置條件:
- 已登入狀態
- 有進行中的任務
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-MISSION-001 | 任務列表顯示 | 1. 進入任務頁面 2. 檢查任務列表 | 1. 分類顯示每日/每週/每月任務 2. 每個任務含名稱、描述、獎勵金額 3. 顯示任務條件(存款/投注金額) 4. 顯示任務有效期 | P1 |
| TC-MISSION-002 | 進度條正確更新 | 1. 查看某個投注任務的進度 2. 進行一筆投注 3. 返回任務頁面檢查進度 | 1. 進度條百分比更新 2. 已完成/總量數字更新 3. 投注後自動連動更新(無需手動刷新) | P1 |
| TC-MISSION-003 | 領取已完成任務獎勵 | 1. 完成一個任務(進度 100%) 2. 點擊「領取」按鈕 | 1. 獎勵金額加入帳戶餘額 2. 任務狀態變為「已領取」 3. mission-claim 表寫入紀錄 4. 不可重複領取 | P1 |
| TC-MISSION-004 | 未完成任務無法領取 | 1. 查看進度未達 100% 的任務 2. 嘗試點擊「領取」 | 1. 領取按鈕為禁用狀態(disabled) 2. 或點擊後提示「任務尚未完成」 3. 不發放獎勵 | P2 |
| TC-MISSION-005 | 存款任務連動 | 1. 查看存款任務進度 2. 完成一筆存款(審核通過) 3. 檢查任務進度 | 1. 存款確認後自動更新存款任務進度 2. 金額正確累計 3. 達標後可領取獎勵 | P1 |
| TC-MISSION-006 | 每日任務重置 | 1. 完成並領取今日任務 2. 等待隔日(或調整系統時間) 3. 檢查任務列表 | 1. 每日任務進度重置為 0 2. 可重新完成 3. 領取狀態重置 | P2 |
2.11 站內信測試
前置條件:
- 已登入狀態
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-INBOX-001 | 通知列表載入 | 1. 點擊導航列的通知圖示 2. 檢查通知列表 | 1. 列出所有站內信 2. 按時間倒序排列 3. 未讀與已讀視覺區分 4. 顯示發送時間 | P1 |
| TC-INBOX-002 | 點擊標記已讀 | 1. 在通知列表中 2. 點擊一封未讀通知 | 1. 通知內容展開/跳轉至詳情 2. 該通知狀態變為已讀 3. notification-read 表寫入紀錄 4. 未讀計數減 1 | P1 |
| TC-INBOX-003 | 未讀計數 Badge | 1. 有未讀通知時 2. 檢查導航列通知圖示 | 1. 顯示紅色數字 Badge 2. 數字為未讀通知數量 3. 全部已讀後 Badge 消失 4. 新通知即時更新計數 | P2 |
| TC-INBOX-004 | 多語系通知內容 | 1. 切換語系至 en-US 2. 查看通知列表 3. 切回 zh-TW 查看 | 1. 通知標題/內容依當前語系顯示 2. 若通知有多語系版本則切換 3. 若無對應語系則使用預設語系 | P3 |
| TC-INBOX-005 | 通知分頁 | 1. 有大量通知(20+) 2. 捲動或翻頁 | 1. 支援分頁或無限捲動載入 2. 資料正確載入 3. 不重複顯示 | P3 |
2.12 KYC 測試
前置條件:
- 已登入狀態
- 帳號未完成 KYC 驗證
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-KYC-001 | 步驟一:基本資料填寫 | 1. 進入 KYC 驗證頁面 2. 填寫基本資料(姓名、生日、地址等) 3. 點擊「下一步」 | 1. 表單驗證通過 2. 資料暫存 3. 進入步驟二 4. 必填欄位未填寫 → 驗證攔截 | P1 |
| TC-KYC-002 | 步驟二:證件上傳 | 1. 在步驟二頁面 2. 上傳身份證正面/反面照片 3. 上傳自拍照 | 1. 支援 JPG / PNG 格式 2. 檔案大小限制(如 5MB) 3. 預覽上傳圖片 4. 格式或大小不符 → 錯誤提示 | P1 |
| TC-KYC-003 | 步驟三:活體偵測 | 1. 在步驟三頁面 2. 開啟攝影機 3. 依指示完成活體偵測 | 1. 攝影機權限請求 2. 活體偵測指引顯示 3. 完成偵測後進入下一步 4. 偵測失敗 → 可重試 | P2 |
| TC-KYC-004 | 步驟四:確認送審 | 1. 完成前三步驟 2. 確認資料無誤 3. 點擊「送審」 | 1. 顯示所有已填資料摘要 2. 可返回修改 3. 送審後狀態變為「審核中」 4. 無法重複送審 | P1 |
| TC-KYC-005 | 進度保留 | 1. 完成步驟一後離開頁面 2. 重新進入 KYC 頁面 | 1. 自動跳到最後一個未完成步驟 2. 已填資料保留不遺失 3. 進度指示器正確顯示 | P2 |
2.13 主題切換測試
前置條件:
- 前台服務已啟動
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-THEME-001 | 主題選擇器顯示 | 1. 開啟前台 2. 找到主題切換器(設定或個人選單中) | 1. 顯示 6 組預設主題選項 2. 每個主題有預覽色塊 3. 當前主題已選中標記 | P2 |
| TC-THEME-002 | 切換主題生效 | 1. 選擇不同主題 2. 觀察頁面變化 | 1. CSS 變數即時更新 2. 主色調(primary)變更 3. 強調色(accent)變更 4. 表面色(surface)變更 5. 文字色(text)變更 6. 邊框色(border)變更 7. 過渡動畫流暢 | P2 |
| TC-THEME-003 | 主題持久化 | 1. 選擇某個主題 2. 重新載入頁面 3. 開新分頁 | 1. 刷新後主題不還原 2. Cookie 中保存主題選擇 3. 新分頁也使用相同主題 | P2 |
| TC-THEME-004 | 主題與各頁面相容性 | 1. 切換至非預設主題 2. 瀏覽各主要頁面 | 1. 所有頁面配色協調 2. 文字對比度足夠(可閱讀) 3. 按鈕狀態(hover/active/disabled)明確 4. 無色彩衝突 | P3 |
2.14 多語系測試
前置條件:
- 前台服務已啟動
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-I18N-001 | 切換全部 5 種語系 | 1. 依序切換至:zh-TW、en-US、zh-CN、th-TH、vi-VN 2. 每次切換後檢查頁面 | 1. 每種語系都可正常切換 2. 無 Console 錯誤 3. 語系選擇器更新為當前語系 4. Cookie NEXT_LOCALE 正確設定 | P1 |
| TC-I18N-002 | 頁面文字更新 | 1. 切換語系 2. 檢查:導航列、按鈕、表單標籤、提示訊息 | 1. 所有可見文字都翻譯為對應語系 2. 無殘留其他語系文字 3. 無翻譯 key 直接顯示(如 common.save) | P1 |
| TC-I18N-003 | Locale Cookie 設定 | 1. 切換語系至 en-US 2. 檢查 Cookie | 1. Cookie 名稱正確 2. Cookie 值為 en-US 3. 重新載入後語系維持 en-US 4. 策略為 no_prefix(URL 不顯示語系) | P2 |
| TC-I18N-004 | 後端錯誤訊息語系 | 1. 切換語系至 en-US 2. 觸發一個業務錯誤(如重複帳號註冊) 3. 檢查錯誤訊息 | 1. 錯誤訊息為英文 2. 後端根據 Request Header locales 返回對應語系訊息 3. 前端透過 ERROR_CODES 查表顯示 | P1 |
| TC-I18N-005 | 日期/數字格式化 | 1. 切換不同語系 2. 檢查日期和金額顯示 | 1. zh-TW:2026/03/01、$1,234.56 2. en-US:03/01/2026、$1,234.56 3. 各語系遵循當地格式慣例 4. 數字千分位分隔正確 | P3 |
| TC-I18N-006 | 站點語系限制 | 1. 若站點 supportedLocales 僅支援部分語系 2. 嘗試切換至不支援的語系 | 1. 不支援的語系在切換器中隱藏或禁用 2. 直接修改 Cookie 至不支援語系 → 自動導向預設語系 3. localeGuard 正確運作 | P2 |
2.15 響應式設計測試
前置條件:
- 使用 Chrome DevTools Device Toolbar 模擬各種螢幕尺寸
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RWD-001 | 手機版(320px) | 1. 設定螢幕寬度 320px 2. 瀏覽首頁、遊戲大廳、存款、個人中心 | 1. 佈局正確不溢出 2. 文字不被截斷(重要資訊) 3. 按鈕可點擊(touch target >= 44px) 4. 無水平捲動條 5. 圖片自適應縮放 | P1 |
| TC-RWD-002 | 平板版(768px) | 1. 設定螢幕寬度 768px 2. 瀏覽各主要頁面 | 1. 佈局切換為平板模式 2. 側邊欄可收合 3. 內容區域利用更多空間 4. 表格可能需要水平捲動 | P2 |
| TC-RWD-003 | 桌面版(1280px) | 1. 設定螢幕寬度 1280px 2. 瀏覽各主要頁面 | 1. 完整佈局顯示 2. 側邊欄永久顯示 3. 內容居中或最大寬度限制 4. 所有功能可正常操作 | P1 |
| TC-RWD-004 | 底部導航列(手機) | 1. 設定螢幕寬度 375px 2. 檢查底部導航列 3. 設定 1024px 檢查 | 1. 手機版顯示底部導航列 2. 桌面版隱藏底部導航列 3. 切換斷點正確(通常 768px) | P2 |
| TC-RWD-005 | 側邊欄覆蓋(手機) | 1. 手機版模式 2. 開啟側邊欄(漢堡選單) 3. 與側邊欄互動 | 1. 側邊欄以覆蓋模式出現 2. 背景遮罩顯示 3. 點擊遮罩關閉側邊欄 4. 側邊欄滑入/滑出動畫流暢 | P2 |
| TC-RWD-006 | 橫向模式(手機) | 1. 設定螢幕為手機橫向(667x375) 2. 瀏覽各頁面 | 1. 佈局正確適配 2. 遊戲 iframe 佔滿可用空間 3. 表單可正常操作 | P3 |
第 3 章:後台測試案例 (c9-ims)
測試對象:http://localhost:3011框架:Next.js 16 (React 19 + TypeScript) 支援語系:zh-TW, en-US, zh-CN, th-TH, vi-VN 認證方式:NextAuth 5 beta (JWT 策略 + Credentials Provider)
3.1 登入與 2FA 測試
前置條件:
- 後台服務已啟動 (port 3011)
- 後端 API 服務已啟動 (port 8080)
- 已有管理員 Seed 資料
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-ADMIN-LOGIN-001 | 管理員正常登入 | 1. 開啟 http://localhost:3011 2. 輸入帳號:root_admin 3. 輸入密碼:Admin123456 4. 點擊「登入」 | 1. 登入成功 2. 跳轉至 Dashboard 頁面 3. Header 顯示管理員名稱 4. 側邊欄導航完整顯示 5. AdminJWT Token 正確取得 | P1 |
| TC-ADMIN-LOGIN-002 | 錯誤密碼登入 | 1. 輸入正確帳號 2. 輸入錯誤密碼 3. 點擊「登入」 | 1. 顯示錯誤訊息 2. 停留在登入頁面 3. 登入失敗紀錄寫入資料庫 4. 密碼欄位清空 | P1 |
| TC-ADMIN-LOGIN-003 | 空白欄位提交 | 1. 不填寫帳號密碼 2. 點擊「登入」 | 1. 表單驗證攔截 2. 顯示必填欄位提示 3. 不發送 API 請求 | P2 |
| TC-ADMIN-LOGIN-004 | 2FA 啟用流程 | 1. 登入後進入個人資料頁 2. 點擊「啟用 Google Authenticator」 3. 掃描 QR Code 4. 輸入 6 位驗證碼 5. 確認啟用 | 1. 顯示 QR Code 和 Secret 字串 2. 對話框狀態:idle → qr → verify 3. 輸入正確 TOTP → 啟用成功 4. 輸入錯誤 TOTP → 提示錯誤 5. 啟用後下次登入需 2FA | P1 |
| TC-ADMIN-LOGIN-005 | 2FA 停用流程 | 1. 已啟用 2FA 的管理員登入 2. 進入個人資料頁 3. 點擊「停用 Google Authenticator」 4. 輸入當前 TOTP 驗證碼 | 1. 需驗證當前 TOTP 才能停用 2. 驗證通過 → 2FA 停用成功 3. 下次登入不再要求 2FA 4. 驗證失敗 → 無法停用 | P1 |
| TC-ADMIN-LOGIN-006 | 2FA 登入驗證 | 1. 使用已啟用 2FA 的帳號 2. 輸入正確帳密 3. 系統要求 TOTP 4. 輸入 Google Authenticator 產生的 6 位碼 | 1. 帳密正確後顯示 TOTP 輸入介面 2. 正確 TOTP → 登入成功 3. 錯誤 TOTP → 提示驗證失敗 4. TOTP 30 秒更新,前後各一個有效 | P1 |
| TC-ADMIN-LOGIN-007 | 登出功能 | 1. 已登入狀態 2. 點擊右上角登出 | 1. Session 清除 2. Token 失效 3. 跳轉至登入頁面 4. 無法直接存取管理頁面 | P1 |
| TC-ADMIN-LOGIN-008 | Session 過期 | 1. 登入後等待 Session 過期 2. 嘗試操作 | 1. API 回傳 401 2. 自動清除 Token 3. 重導向至登入頁面 4. 顯示 Session 過期提示 | P1 |
| TC-ADMIN-LOGIN-009 | 未授權存取 | 1. 未登入狀態 2. 直接存取 http://localhost:3011/dashboard | 1. 自動重導向至登入頁面 2. 登入後跳回原頁面 | P1 |
| TC-ADMIN-LOGIN-010 | 個人資料修改 | 1. 登入後進入個人資料 2. 修改暱稱/密碼 3. 儲存 | 1. 修改成功顯示 Toast 通知 2. 資料庫更新 3. Header 顯示新暱稱 4. 若修改密碼需重新登入 | P2 |
3.2 多站點切換測試
前置條件:
- 已登入管理員帳號
- Seed 資料含多個站點(C9, A1, B2, D3, E4)
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SITE-001 | 全部站點模式 — SiteTabs 顯示 | 1. 在 Header 的 SiteSelector 選擇「全部站點」 2. 進入任一支援多站點的頁面(如玩家列表) | 1. 頁面頂部出現 SiteTabs 2. 每個站點一個 Tab(C9, A1, B2...) 3. 預設選中第一個 Tab 4. Tab 上顯示站點名稱 | P1 |
| TC-SITE-002 | 獨立站點模式 — 資料篩選 | 1. 在 SiteSelector 選擇某站點(如 A1) 2. 進入玩家列表頁面 | 1. SiteTabs 僅顯示一個 Tab (A1) 2. 資料自動篩選為 A1 站的資料 3. API 請求 Header 帶有 x-site-code: A1 4. 不顯示其他站點資料 | P1 |
| TC-SITE-003 | 站點切換資料刷新 | 1. 在「全部站點」模式下查看玩家列表 2. Header 切換到「A1」站 3. 再切回「全部站點」 | 1. 每次切換 AdminContentWrapper 重新掛載(key 變更) 2. 頁面資料完全重新載入 3. TanStack Query cache 被清除 4. 無殘留舊站點資料 | P1 |
| TC-SITE-004 | SiteTabs 點擊切換 | 1. 選擇「全部站點」 2. 進入有 SiteTabs 的頁面 3. 依序點擊各 Tab | 1. 點擊 Tab 後資料切換為該站點 2. API 請求帶入對應 siteCode 3. 分頁重置為第一頁 4. 篩選條件保留 | P1 |
| TC-SITE-005 | SiteSelector 隱藏頁面 | 1. 進入管理員列表頁面 (/system/admins) 2. 進入群組管理頁面 (/system/groups) 3. 進入操作紀錄頁面 (/system/logs) | 1. 以上三個頁面 Header 不顯示 SiteSelector 2. 這些頁面為全站共用設定 3. 資料不區分站點 | P2 |
| TC-SITE-006 | Query Cache 清除 | 1. 在 A1 站查看玩家列表 2. 切換至 B2 站 3. 切回 A1 站 | 1. 每次切換清除 TanStack Query cache 2. 不使用舊的快取資料 3. 重新發送 API 請求 4. 資料一致性保證 | P2 |
| TC-SITE-007 | 站點初始化流程 | 1. 重新載入後台頁面 2. 觀察 Network 請求 | 1. SiteFilterInitializer 發送 /site-config/admin/list 請求 2. 站點列表正確填入 siteFilterStore 3. SiteSelector 下拉選單包含所有站點 4. 預設選擇「全部站點」或上次選擇(sessionStorage) | P2 |
| TC-SITE-008 | 站點資料隔離(列表) | 1. 在「全部站點」模式切換到 C9 Tab 2. 記錄玩家數量 3. 切換到 A1 Tab 4. 記錄玩家數量 | 1. 各站點玩家數量不同 2. C9 站不顯示 A1 的玩家 3. 與資料庫實際資料一致 4. 分頁總數正確 | P1 |
3.3 權限管理測試
前置條件:
- 有不同群組的管理員帳號(root / super_admin / general_admin / custom)
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PERM-001 | Root 用戶全權限 | 1. 以 root_admin 登入 2. 檢查側邊欄選單 3. 嘗試存取所有頁面 | 1. 側邊欄顯示所有 14+ 選單群組 2. 所有頁面可正常存取 3. 所有 CRUD 操作可執行 4. 包含站點設定(site-config) | P1 |
| TC-PERM-002 | Super Admin 權限 | 1. 以 super_admin 登入 2. 檢查側邊欄選單 | 1. 顯示大部分選單 2. 「站點設定」相關頁面不顯示 3. 其他功能可正常操作 | P1 |
| TC-PERM-003 | General Admin 唯讀 | 1. 以 general_admin 登入 2. 嘗試編輯操作(如修改玩家資料) | 1. 可查看列表和詳情 2. 新增/編輯/刪除按鈕隱藏或禁用 3. 直接呼叫 API 也被後端 Guard 擋下 4. 顯示「無權限」提示 | P1 |
| TC-PERM-004 | Custom 群組自訂權限 | 1. 以 custom_admin 登入 2. 該群組僅有 user:read 和 deposit:read 權限 3. 檢查側邊欄 | 1. 僅顯示玩家管理和存款相關選單 2. 其他選單隱藏 3. 直接存取無權限頁面 → 顯示 AccessDenied 元件 | P1 |
| TC-PERM-005 | 無權限頁面顯示 | 1. 以受限帳號登入 2. 直接輸入無權限頁面的 URL 3. 嘗試存取 | 1. 顯示 AccessDenied 元件 2. 提示「您沒有權限存取此頁面」 3. 不洩漏頁面內容 4. 提供返回首頁連結 | P1 |
| TC-PERM-006 | 寫入操作權限阻擋 | 1. 以 general_admin(唯讀)登入 2. 使用開發者工具直接呼叫寫入 API 3. 觀察回應 | 1. 後端 PermissionsGuard 攔截 2. 回傳 403 Forbidden 3. 操作不執行 4. 操作紀錄記錄此次嘗試 | P1 |
| TC-PERM-007 | 權限模組列表 | 1. 以 root 登入 2. 進入群組管理 3. 查看權限勾選列表 | 1. 顯示 16 個權限模組 2. 每個模組含 read/write 權限 3. 模組:admin, admin-group, admin-log, user, deposit, withdrawal, promo, promo-tag, affiliate, vip, game, risk, report, vendor, finance, site-config | P2 |
| TC-PERM-008 | 動態權限更新 | 1. 以 root 登入修改 custom 群組權限 2. 以 custom_admin 重新登入 3. 檢查可存取頁面 | 1. 權限修改立即生效 2. 新增的權限對應選單出現 3. 移除的權限對應選單消失 4. 不需重新部署 | P2 |
3.4 系統管理測試
3.4.1 管理員管理
前置條件:
- 以 root 或有 admin:write 權限的帳號登入
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-ADMIN-001 | 管理員列表顯示 | 1. 進入系統管理 > 管理員列表 2. 檢查列表內容 | 1. 顯示所有管理員(不區分站點) 2. 含帳號、暱稱、群組、狀態、建立時間 3. 支援分頁 4. SiteSelector 不顯示 | P1 |
| TC-SYS-ADMIN-002 | 新增管理員 | 1. 點擊「新增管理員」 2. 填寫帳號、密碼、暱稱 3. 選擇群組 4. 提交 | 1. 表單驗證通過 2. 管理員建立成功 3. Toast 通知「新增成功」 4. 列表刷新 5. 新管理員可登入 | P1 |
| TC-SYS-ADMIN-003 | 編輯管理員 | 1. 在列表中點擊某管理員的「編輯」 2. 修改暱稱或群組 3. 儲存 | 1. 彈出編輯對話框或跳轉 2. 顯示當前資訊 3. 修改成功更新列表 4. 不可修改帳號 | P1 |
| TC-SYS-ADMIN-004 | 停用管理員 | 1. 對某管理員點擊「停用」 2. 確認操作 | 1. 確認對話框顯示 2. 停用後狀態變更 3. 被停用管理員無法登入 4. 列表狀態標籤更新 | P1 |
| TC-SYS-ADMIN-005 | 刪除管理員 | 1. 對某管理員點擊「刪除」 2. 確認刪除 | 1. 確認對話框(variant: destructive) 2. 刪除後從列表消失 3. 不可刪除自己 4. 不可刪除最後一個 root 管理員 | P1 |
3.4.2 群組管理
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-GROUP-001 | 群組列表顯示 | 1. 進入系統管理 > 群組管理 | 1. 顯示所有群組 2. 含群組名稱、類型、成員數、權限數 3. 預設群組:root / super_admin / general_admin | P1 |
| TC-SYS-GROUP-002 | 新增自訂群組 | 1. 點擊「新增群組」 2. 輸入群組名稱 3. 勾選權限 4. 提交 | 1. 群組建立成功 2. 權限列表正確勾選 3. 可分配給管理員 4. 類型為 custom | P1 |
| TC-SYS-GROUP-003 | 編輯群組權限 | 1. 點擊 custom 群組「編輯」 2. 修改權限勾選 3. 儲存 | 1. 權限修改成功 2. 影響該群組所有管理員 3. 不可編輯預設群組(root / super_admin / general_admin) | P1 |
| TC-SYS-GROUP-004 | 刪除群組 | 1. 對無成員的自訂群組點擊「刪除」 2. 確認 | 1. 刪除成功 2. 有成員的群組不可刪除(需先移除成員) 3. 預設群組不可刪除 | P2 |
3.4.3 操作紀錄
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-LOG-001 | 操作紀錄列表 | 1. 進入系統管理 > 操作紀錄 | 1. 顯示管理員操作紀錄 2. 含操作者、動作、模組、時間、IP 3. 按時間倒序 4. 不區分站點(全站共用) | P2 |
| TC-SYS-LOG-002 | 篩選操作紀錄 | 1. 使用日期範圍篩選 2. 使用管理員帳號篩選 3. 使用操作類型篩選 | 1. 篩選結果正確 2. 多條件組合篩選有效 3. 分頁正確 | P2 |
3.4.4 站點設定
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-SITE-001 | 站點列表顯示 | 1. 進入系統管理 > 站點設定 | 1. 顯示所有站點(C9, A1...) 2. 含站點代碼、名稱、狀態 3. 含主題配置預覽 | P1 |
| TC-SYS-SITE-002 | 新增站點 | 1. 點擊「新增站點」 2. 填寫站點代碼、名稱等 3. 提交 | 1. 站點建立成功 2. 自動出現在 SiteSelector 下拉 3. siteCode 唯一不重複 | P1 |
| TC-SYS-SITE-003 | 編輯站點設定 | 1. 點擊某站點「編輯」 2. 修改站點名稱或設定 3. 儲存 | 1. 設定更新成功 2. 影響前台顯示 3. features 開關正確生效 | P1 |
| TC-SYS-SITE-004 | 刪除站點 | 1. 刪除非預設站點 2. 確認操作 | 1. 確認對話框(含 cascade 提醒) 2. 站點刪除 3. 相關主題 cascade 刪除 4. 預設站點不可刪除 | P1 |
3.4.5 客服配置
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-CS-001 | 客服管道設定 | 1. 進入系統管理 > 客服配置 2. 選擇站點 Tab | 1. 顯示 8 種客服管道設定(line / telegram / wechat / facebook / instagram / twitter / discord / custom) 2. 每管道含 label、link、icon、sortOrder、enabled 3. 支援多語系 label | P1 |
| TC-SYS-CS-002 | 編輯客服管道 | 1. 修改某管道的連結和標籤 2. 切換啟用/停用 3. 儲存 | 1. 修改成功 2. 前台即時反映(客服浮動按鈕) 3. 停用的管道在前台隱藏 | P1 |
| TC-SYS-CS-003 | LiveChat 設定 | 1. 啟用 LiveChat 功能 2. 輸入嵌入腳本 3. 儲存 | 1. LiveChat 開關獨立 2. 腳本正確儲存 3. 前台載入 LiveChat Widget | P2 |
| TC-SYS-CS-004 | 同預設站點複製 | 1. 在非預設站 Tab 點擊「同預設站點」 2. 確認複製 | 1. 預設站客服設定複製至當前站 2. 前端狀態拷貝(需手動儲存) 3. 不影響預設站設定 | P2 |
3.4.6 域名設置
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-DOMAIN-001 | 域名列表顯示 | 1. 進入系統管理 > 域名設置 2. 選擇站點 | 1. 顯示各站點域名配置 2. 含 hostname、Logo(大/小)、Favicon 3. 含支援語系列表 | P1 |
| TC-SYS-DOMAIN-002 | 上傳域名素材 | 1. 點擊上傳 Logo 或 Favicon 2. 選擇圖片檔案 | 1. 圖片上傳至 R2 2. 預覽正確顯示 3. 支援 logoSmall / logoBig / favicon | P1 |
| TC-SYS-DOMAIN-003 | 同預設站點複製 | 1. 在非預設站點擊「同預設站點」 | 1. 複製時保留 hostname 和 supportedLocales 2. 同步 Logo、Favicon 等素材 3. 直接 API 寫入 | P2 |
3.4.7 三方登入配置
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-OAUTH-001 | OAuth 設定顯示 | 1. 進入系統管理 > 三方登入 2. 選擇站點 Tab | 1. 顯示 Google / Telegram OAuth 設定 2. 含 Client ID、Client Secret、啟用狀態 | P1 |
| TC-SYS-OAUTH-002 | 修改 OAuth 設定 | 1. 修改 Google OAuth 的 Client ID 2. 切換啟用狀態 3. 儲存 | 1. 設定更新成功 2. 前台 OAuth 按鈕隨啟用/停用顯示/隱藏 | P1 |
3.4.8 雲端儲存
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-R2-001 | 檔案列表顯示 | 1. 進入系統管理 > 雲端儲存 2. 選擇站點 Tab | 1. 顯示 R2 儲存空間的檔案/資料夾列表 2. 含檔名、大小、類型、修改時間 3. 支援資料夾導航 | P2 |
| TC-SYS-R2-002 | 上傳檔案 | 1. 點擊「上傳」 2. 選擇檔案 3. 確認上傳 | 1. 檔案上傳至 R2 2. 列表即時更新 3. 操作紀錄寫入 r2-operation-log | P2 |
| TC-SYS-R2-003 | 建立資料夾 | 1. 點擊「新增資料夾」 2. 輸入資料夾名稱 | 1. 資料夾建立成功 2. 列表顯示新資料夾 | P3 |
| TC-SYS-R2-004 | 刪除檔案 | 1. 選擇檔案 2. 點擊「刪除」 3. 確認 | 1. 檔案從 R2 移除 2. 列表更新 3. 操作紀錄寫入 | P2 |
| TC-SYS-R2-005 | 移動檔案 | 1. 選擇檔案 2. 選擇目標資料夾 3. 確認移動 | 1. 檔案移至新位置 2. 原位置不再顯示 3. 新位置正確顯示 | P3 |
3.4.9 雲端儲存日誌
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-R2LOG-001 | 日誌列表顯示 | 1. 進入系統管理 > 雲端儲存日誌 | 1. 顯示 R2 操作紀錄 2. 含操作類型(上傳/刪除)、檔案名、操作者 3. 解析 OS / 瀏覽器(UA 解析) 4. 含 mimeType 資訊 | P2 |
| TC-SYS-R2LOG-002 | 日誌詳情 | 1. 點擊某筆紀錄的「詳情」 | 1. 顯示 Detail Dialog 2. 完整 User-Agent 字串 3. 操作時間、IP 等完整資訊 | P3 |
3.4.10 佈局配置
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SYS-LAYOUT-001 | 底部導航列配置 | 1. 進入系統管理 > 底部導航列 2. 選擇站點 Tab | 1. 顯示前台行動版底部 Tab 項目 2. 可新增/刪除/排序項目 3. 各項目含 icon、label、link | P2 |
| TC-SYS-LAYOUT-002 | 頁尾配置 | 1. 進入系統管理 > 頁尾配置 | 1. 顯示前台頁尾設定 2. 可設定連結、版權資訊 3. 支援多語系 | P3 |
| TC-SYS-LAYOUT-003 | 了解更多配置 | 1. 進入系統管理 > 了解更多 | 1. 顯示「了解更多」區塊配置 2. 可設定內容和連結 3. 支援多語系 | P3 |
3.5 玩家管理測試
3.5.1 全部玩家
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PLAYER-001 | 玩家列表顯示 | 1. 進入玩家管理 > 全部玩家 2. 選擇站點 Tab | 1. 顯示當前站點的玩家列表 2. 含帳號、暱稱、VIP 等級、餘額、狀態、註冊時間 3. 支援分頁 4. SiteTabs 正確運作 | P1 |
| TC-PLAYER-002 | 關鍵字搜尋 | 1. 在 FilterBar 輸入帳號關鍵字 2. 點擊搜尋 | 1. 列表篩選為匹配結果 2. 支援帳號/暱稱/Email 模糊搜尋 3. 無結果 → 顯示空狀態 | P1 |
| TC-PLAYER-003 | VIP 等級篩選 | 1. 在 FilterBar 選擇 VIP 等級 2. 搜尋 | 1. 僅顯示該 VIP 等級的玩家 2. VIP 等級下拉選項動態取得(不硬編碼) 3. 結果正確 | P2 |
| TC-PLAYER-004 | 玩家詳情頁 | 1. 點擊某玩家的帳號連結 2. 進入詳情頁 [id] | 1. 顯示完整玩家資料 2. 含基本資訊、餘額、VIP、投注統計 3. 含銀行卡/信用卡/加密地址列表 4. 含登入紀錄 | P1 |
| TC-PLAYER-005 | 編輯玩家資料 | 1. 在玩家詳情頁點擊「編輯」 2. 修改部分欄位 3. 儲存 | 1. 可修改暱稱、狀態等欄位 2. 不可修改帳號 3. 修改成功更新顯示 4. 操作紀錄寫入 | P1 |
| TC-PLAYER-006 | 日期範圍篩選 | 1. 設定註冊日期範圍 2. 搜尋 | 1. 僅顯示該日期範圍內註冊的玩家 2. 開始/結束日期選擇器正確 3. 結果正確排序 | P2 |
3.5.2 新註冊玩家
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PLAYER-NEW-001 | 新註冊列表 | 1. 進入玩家管理 > 新註冊玩家 2. 選擇站點 Tab | 1. 顯示近期新註冊玩家 2. 預設顯示今日/近 7 天註冊 3. 含註冊來源(直接/推薦碼) | P1 |
| TC-PLAYER-NEW-002 | VIP 篩選 | 1. 選擇 VIP 等級篩選 | 1. VIP 等級下拉選項動態取得 2. 篩選結果正確 | P2 |
3.5.3 線上玩家
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PLAYER-ONLINE-001 | 線上玩家列表 | 1. 進入玩家管理 > 線上玩家 | 1. 顯示當前在線玩家 2. 含帳號、VIP、最後活動時間、IP 3. 資料即時更新 | P2 |
3.5.4 登入失敗紀錄
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PLAYER-FAIL-001 | 登入失敗紀錄列表 | 1. 進入玩家管理 > 登入失敗紀錄 2. 選擇站點 Tab | 1. 顯示登入失敗紀錄 2. 含帳號、IP、時間、失敗原因 3. 支援關鍵字和日期篩選 | P2 |
| TC-PLAYER-FAIL-002 | 日期篩選 | 1. 設定日期範圍 2. 搜尋 | 1. 僅顯示日期範圍內的紀錄 2. 結果正確 | P3 |
3.6 活動管理測試
3.6.1 優惠活動列表
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PROMO-001 | 活動列表顯示 | 1. 進入活動管理 > 優惠活動 2. 選擇站點 Tab | 1. 顯示當前站點的活動列表 2. 含標題、狀態、開始/結束日期、類型 3. 支援分頁 | P1 |
| TC-PROMO-002 | 新增活動 | 1. 點擊「新增活動」 2. 填寫標題(多語系)、描述 3. 使用 Tiptap 編輯器編寫內容 4. 上傳活動圖片 5. 設定條件(如最低存款、打碼量) 6. 提交 | 1. 多語系標題正確儲存 2. HTML 內容正確儲存 3. 圖片上傳至 R2 4. 條件規則正確設定 5. 活動建立成功,出現在列表中 | P1 |
| TC-PROMO-003 | 編輯活動 | 1. 點擊某活動的「編輯」 2. 修改標題或內容 3. 儲存 | 1. 編輯頁載入正確資料 2. Tiptap 編輯器正確渲染既有 HTML 3. 修改成功更新 | P1 |
| TC-PROMO-004 | 啟用/停用活動 | 1. 切換活動的啟用狀態 | 1. 狀態切換成功 2. 停用的活動在前台不顯示 3. 啟用後前台重新顯示 | P1 |
| TC-PROMO-005 | 刪除活動 | 1. 對活動點擊「刪除」 2. 確認 | 1. 確認對話框顯示 2. 刪除成功 3. 列表更新 4. 相關領取紀錄保留(軟刪除) | P2 |
3.6.2 活動標籤
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PROMO-TAG-001 | 標籤列表 | 1. 進入活動管理 > 活動標籤 2. 選擇站點 Tab | 1. 顯示活動分類標籤列表 2. 含名稱、排序、使用數量 | P2 |
| TC-PROMO-TAG-002 | CRUD 操作 | 1. 新增標籤 2. 編輯標籤名稱 3. 刪除未使用的標籤 | 1. 各操作正確執行 2. 已使用的標籤不可刪除 3. 新標籤可在活動編輯時選擇 | P2 |
3.7 財務管理測試
3.7.1 人工調節金額
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-FIN-ADJUST-001 | 調帳表單 | 1. 進入財務管理 > 人工調帳 2. 選擇站點 Tab 3. 搜尋用戶 4. 輸入調整金額(正值增加/負值減少) 5. 填寫原因 6. 確認提交 | 1. 用戶搜尋功能(帶 siteCode) 2. 金額驗證(decimal 精度) 3. 調帳成功更新用戶餘額 4. 操作紀錄寫入 5. Toast 通知 | P1 |
| TC-FIN-ADJUST-002 | 負值調帳超額 | 1. 輸入負值超過用戶餘額 2. 提交 | 1. 後端拒絕(餘額不足) 2. 顯示錯誤訊息 3. 餘額不變 | P1 |
3.7.2 存款設置
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-FIN-DEPSETTING-001 | 金流群組/通道 | 1. 進入財務管理 > 存款設置 2. 選擇站點 Tab | 1. 顯示金流群組列表 2. 每群組下含通道列表 3. 按站點篩選 | P1 |
| TC-FIN-DEPSETTING-002 | 通道啟停 | 1. 切換某通道啟用狀態 | 1. 通道啟停即時生效 2. 停用後前台不顯示該通道 | P1 |
3.7.3 存款審核
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-FIN-DEPOSIT-001 | 存款訂單列表 | 1. 進入財務管理 > 存款審核 2. 選擇站點 Tab | 1. 顯示存款訂單列表 2. 含訂單編號、用戶、金額(USD)、狀態、通道、時間 3. 支援多條件篩選:訂單ID、用戶ID、關鍵字、支付方式、狀態、日期 | P1 |
| TC-FIN-DEPOSIT-002 | 審核通過 | 1. 選擇 pending 狀態訂單 2. 點擊「通過」 3. 確認 | 1. 訂單狀態變為 approved 2. 用戶餘額增加(USD 金額) 3. 操作紀錄寫入 4. 列表即時更新 | P1 |
| TC-FIN-DEPOSIT-003 | 審核拒絕 | 1. 選擇 pending 訂單 2. 點擊「拒絕」 3. 輸入拒絕原因 4. 確認 | 1. 訂單狀態變為 rejected 2. 用戶餘額不變 3. 拒絕原因寫入 | P1 |
| TC-FIN-DEPOSIT-004 | 篩選組合 | 1. 同時設定狀態 + 日期 + 關鍵字篩選 2. 搜尋 | 1. 三個條件同時生效 2. 結果正確 3. 分頁總數正確 | P2 |
3.7.4 提款管理
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-FIN-WD-001 | 提款列表 | 1. 進入財務管理 > 提款管理 2. 選擇站點 Tab | 1. 顯示提款訂單列表 2. 含訂單ID、狀態、用戶ID、金額、提款方式 3. 篩選:訂單ID、狀態、用戶ID、關鍵字、網路、日期 | P1 |
| TC-FIN-WD-002 | 審核通過 | 1. 選擇 pending 提款 2. 點擊「核准」 | 1. 狀態變為 approved 2. 凍結餘額不變(等待完成) | P1 |
| TC-FIN-WD-003 | 上傳轉帳憑證 | 1. 對 approved 訂單上傳憑證 | 1. 圖片上傳至 R2 2. 憑證連結寫入訂單 | P2 |
| TC-FIN-WD-004 | 標記完成 | 1. 對 approved 訂單點擊「完成」 | 1. 狀態變為 completed 2. 凍結餘額釋放(扣除) 3. 完整提款流程結束 | P1 |
| TC-FIN-WD-005 | 審核拒絕 | 1. 拒絕 pending 提款 | 1. 狀態變為 rejected 2. 凍結餘額退回可用餘額 3. 用戶總餘額不變 | P1 |
3.7.5 銀行卡管理
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-FIN-BANK-001 | 銀行卡列表 | 1. 進入財務管理 > 銀行卡列表 2. 選擇站點 Tab | 1. 顯示銀行卡列表 2. 篩選:關鍵字、用戶ID、狀態、銀行代碼、持卡人、日期 3. SiteTabs 正確 | P1 |
| TC-FIN-BANK-002 | 審核銀行卡 | 1. 對 pending 銀行卡點擊「審核」 2. 選擇通過或拒絕 | 1. 通過 → 用戶可用此卡提款 2. 拒絕 → 卡片不可用 3. 狀態標籤更新 | P1 |
3.7.6 信用卡管理
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-FIN-CREDIT-001 | 信用卡列表與審核 | 同銀行卡管理模式,篩選:關鍵字、用戶ID、狀態、持卡人、日期 | 同銀行卡管理預期結果 | P1 |
3.7.7 加密地址管理
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-FIN-CRYPTO-001 | 加密地址列表與審核 | 同銀行卡管理模式,篩選:關鍵字、用戶ID、狀態、網路、幣種、日期 | 同銀行卡管理預期結果,額外含鏈類型(TRC-20/ERC-20)篩選 | P1 |
3.8 遊戲管理測試
3.8.1 遊戲供應商
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-GAME-PROV-001 | 供應商列表 | 1. 進入遊戲管理 > 遊戲供應商 2. 選擇站點 Tab | 1. 顯示當前站點的供應商列表 2. 含 providerCode、名稱、gameType、狀態 3. SiteTabs + siteCode 篩選 | P1 |
| TC-GAME-PROV-002 | 新增供應商 | 1. 點擊「新增」 2. 填寫資訊 3. 提交 | 1. 供應商建立成功 2. siteCode 自動帶入當前站點 3. 列表更新 | P1 |
| TC-GAME-PROV-003 | 編輯供應商 | 1. 點擊「編輯」 2. 修改欄位 3. 儲存 | 1. 更新成功 2. 不可修改 providerCode 3. 列表即時反映 | P1 |
| TC-GAME-PROV-004 | 刪除供應商 | 1. 刪除供應商 2. 確認 | 1. 確認對話框 2. 刪除成功 3. 相關遊戲資料影響評估 | P2 |
| TC-GAME-PROV-005 | 同預設站點(API 複製) | 1. 在非預設站 Tab 2. 點擊「同預設站點」 3. 確認 | 1. 確認對話框顯示 source → target 站點 2. 呼叫 POST /game/admin/copy-site-data type=providers 3. Transaction 內先刪後插 4. 列表重新載入 5. 僅目標站點受影響 | P1 |
| TC-GAME-PROV-006 | 帶入模板 | 1. 點擊「帶入模板」 2. 預覽模板內容(可編輯) 3. 確認帶入 | 1. templatePreviewDialog 顯示預設供應商 2. 可修改後再確認 3. 依 AdminSiteCode 寫入指定站點 4. 列表更新 | P1 |
3.8.2 遊戲分類設定
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-GAME-TYPE-001 | 分類列表 | 1. 進入遊戲管理 > 遊戲分類 2. 選擇站點 Tab | 1. 顯示 8 種遊戲分類設定 2. 含類型代碼、名稱、排序、狀態 | P1 |
| TC-GAME-TYPE-002 | CRUD 操作 | 1. 新增/編輯/刪除分類 | 1. 各操作正確(同供應商模式) 2. gameType 正確(1-10 對應表) | P1 |
| TC-GAME-TYPE-003 | 同預設站點 | 同供應商模式,type=typeConfigs | 同供應商預期 | P1 |
| TC-GAME-TYPE-004 | 帶入模板 | 同供應商模式 | 同供應商預期 | P1 |
3.9 VIP 管理測試
3.9.1 VIP 等級
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-VIP-LEVEL-001 | 等級列表 | 1. 進入 VIP 管理 > VIP 等級 2. 選擇站點 Tab | 1. 顯示各 VIP 等級設定 2. 含等級、名稱、門檻、福利 3. 等級數量由後端決定(不限 15 級) | P1 |
| TC-VIP-LEVEL-002 | 新增等級 | 1. 點擊「新增等級」 2. 設定門檻和福利 3. 提交 | 1. 等級建立成功 2. 自動排序 3. 前台 VIP 頁面更新 | P1 |
| TC-VIP-LEVEL-003 | 編輯等級 | 1. 修改某等級門檻 2. 儲存 | 1. 門檻更新 2. 不影響已達此等級的用戶 | P1 |
| TC-VIP-LEVEL-004 | 同預設站點 | 1. 非預設站 Tab 2. 點擊「同預設站點」 | 1. POST /vip/admin/copy-site-data type=levels 2. 預設站等級複製至目標站 3. 目標站原有等級被取代 | P1 |
| TC-VIP-LEVEL-005 | 帶入模板 | 1. 點擊「帶入模板」 2. 預覽並確認 | 1. 模板預覽正確 2. 帶入後僅影響當前站 3. 等級設定更新 | P1 |
3.9.2 返水設定
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-VIP-REBATE-001 | 返水比例表 | 1. 進入 VIP 管理 > 返水設定 2. 選擇站點 Tab | 1. 顯示 N 等級 x 8 遊戲類型的返水矩陣 2. 每個儲存格可內嵌編輯 3. 精度 decimal(5,2) | P1 |
| TC-VIP-REBATE-002 | 行內編輯 | 1. 直接點擊某個返水比例 2. 修改數值 3. Tab 到下一格 | 1. 可直接在表格中修改 2. 修改即時顯示 3. 支援 Tab 切換輸入 | P1 |
| TC-VIP-REBATE-003 | 批次儲存 | 1. 修改多個返水比例 2. 點擊「儲存」 | 1. bulk upsert API 呼叫 2. 所有修改一次儲存 3. unique key: siteCode + level + gameType 4. 成功 Toast 通知 | P1 |
| TC-VIP-REBATE-004 | 同預設站點 | 同 VIP 等級,type=rebates | 返水設定從預設站複製 | P1 |
3.9.3 VIP 玩家
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-VIP-PLAYER-001 | VIP 玩家列表 | 1. 進入 VIP 管理 > VIP 玩家 2. 選擇站點 Tab | 1. 顯示玩家 VIP 資訊 2. 含帳號、VIP 等級、累計投注、保級狀態 3. 支援篩選和排序 | P1 |
3.10 報表測試
3.10.1 總覽報表
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RPT-OVERVIEW-001 | 總覽頁面 | 1. 進入報表 > 總覽 2. 選擇站點 Tab(useMultiSiteTabs) | 1. 顯示統計卡片:總存款、總提款、總投注、活躍用戶等 2. 每日摘要表格 3. 支援日期範圍篩選 | P1 |
| TC-RPT-OVERVIEW-002 | 日期篩選 | 1. 設定開始/結束日期 2. 搜尋 | 1. 統計數據更新為指定日期範圍 2. 圖表/表格同步更新 | P1 |
3.10.2 玩家報表
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RPT-PLAYER-001 | 玩家報表列表 | 1. 進入報表 > 玩家報表 2. 選擇站點 Tab | 1. 顯示玩家統計資料 2. 篩選:關鍵字、VIP 等級(select 動態)、日期 3. 含存款/提款/投注總額 | P1 |
| TC-RPT-PLAYER-002 | CSV 匯出 | 1. 設定篩選條件 2. 點擊 ExportButton | 1. 下載 CSV 檔案 2. 資料與頁面顯示一致 3. 含所有欄位 4. 檔名含日期 | P2 |
3.10.3 投注紀錄
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RPT-BET-001 | 投注紀錄列表 | 1. 進入報表 > 投注紀錄 2. 選擇站點 Tab | 1. 顯示投注紀錄 2. 篩選:關鍵字、遊戲類型(select)、遊戲平台(select)、狀態、日期 3. 含投注/派彩金額(USD) | P1 |
| TC-RPT-BET-002 | 遊戲類型篩選 | 1. 選擇特定遊戲類型 2. 搜尋 | 1. 僅顯示該類型投注 2. 類型選項完整(8 種) | P2 |
3.10.4 遊戲報表
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RPT-GAME-001 | 遊戲報表 | 1. 進入報表 > 遊戲報表 2. 篩選遊戲類型/平台/日期 | 1. 按遊戲類型或平台彙總 2. 含總投注、總派彩、利潤 3. 支援 CSV 匯出 | P1 |
3.10.5 損益報表
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RPT-PL-001 | 損益報表 | 1. 進入報表 > 損益報表 2. 選擇分組方式(日/週/月) 3. 選擇遊戲類型 4. 設定日期 | 1. 按分組方式彙總損益 2. 含收入、支出、淨利 3. 數據正確(與其他報表交叉驗證) | P1 |
3.10.6 玩家摘要
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RPT-SUMMARY-001 | 玩家摘要 | 1. 進入報表 > 玩家摘要 2. 篩選:關鍵字、VIP 等級、排序方式、排序方向、日期 | 1. 顯示玩家綜合摘要 2. 支援排序(投注額/存款額等) 3. 支援 CSV 匯出 | P1 |
3.10.7 活動報表
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RPT-PROMO-001 | 活動報表 | 1. 進入報表 > 活動報表 2. 設定日期篩選 | 1. 顯示活動領取統計 2. 含活動名稱、領取人數、發放金額 3. 目前尚未加入多站點 | P2 |
3.11 風控管理測試
3.11.1 IP 黑白名單
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RISK-IP-001 | IP 規則列表 | 1. 進入風控管理 > IP 規則 2. 選擇站點 Tab | 1. 顯示 IP 黑白名單列表 2. 篩選:類型(黑/白名單)、IP 關鍵字、日期範圍 3. 含 IP、類型、備註、建立時間 | P1 |
| TC-RISK-IP-002 | 新增 IP 規則 | 1. 點擊「新增」 2. 輸入 IP 地址 3. 選擇類型(黑名單/白名單) 4. 填寫備註 5. 提交 | 1. 規則建立成功 2. 黑名單 IP 的用戶無法登入/操作 3. 白名單 IP 跳過風控檢查 | P1 |
| TC-RISK-IP-003 | 刪除 IP 規則 | 1. 刪除某條 IP 規則 | 1. 規則移除 2. 該 IP 不再被攔截/放行 | P1 |
| TC-RISK-IP-004 | 類型篩選 | 1. 篩選僅顯示黑名單 2. 篩選僅顯示白名單 | 1. 正確區分黑白名單 2. 結果正確 | P2 |
3.11.2 IP/FP 檢查
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RISK-LOOKUP-001 | IP 反查用戶 | 1. 進入風控管理 > IP 檢查 2. 選擇站點 Tab 3. 輸入 IP 地址 4. 搜尋 | 1. 顯示使用該 IP 登入的所有用戶 2. 含帳號、登入時間 3. 可識別共用 IP 的嫌疑帳號 | P1 |
| TC-RISK-LOOKUP-002 | 裝置指紋反查 | 1. 輸入裝置指紋值 2. 搜尋 | 1. 顯示使用該指紋的用戶 2. 可識別多重帳號嫌疑 | P1 |
| TC-RISK-LOOKUP-003 | 多條件搜尋 | 1. 使用帳號/姓名/Email/手機搜尋 | 1. FilterBar 支援多種搜尋條件 2. 結果按站點篩選 | P2 |
3.11.3 遊戲黑名單
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-RISK-GAME-001 | 遊戲黑名單列表 | 1. 進入風控管理 > 遊戲黑名單 2. 選擇站點 Tab | 1. 顯示遊戲黑名單列表 2. 篩選:用戶ID、用戶帳號、遊戲類型、日期 3. 含封鎖類型(全封鎖/類型封鎖/特定遊戲封鎖) | P1 |
| TC-RISK-GAME-002 | 新增遊戲黑名單 | 1. 點擊「新增」 2. 選擇用戶 3. 選擇封鎖類型 4. 提交 | 1. 全封鎖 → 用戶所有遊戲都無法啟動 2. 類型封鎖 → 特定遊戲類型無法啟動 3. 特定遊戲封鎖 → 單一遊戲無法啟動 4. 前台啟動遊戲回傳錯誤碼 5010 | P1 |
| TC-RISK-GAME-003 | 移除黑名單 | 1. 刪除某條黑名單規則 | 1. 用戶恢復遊戲存取 2. 即時生效 | P1 |
3.12 代理中心測試(後台)
3.12.1 代理列表
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-AFF-ADMIN-001 | 代理列表顯示 | 1. 進入代理中心 > 代理列表 2. 選擇站點 Tab | 1. 顯示代理列表(AdminSiteCode 篩選) 2. 含代理帳號、等級、下線數、佣金累計 3. 支援分頁和篩選 | P1 |
| TC-AFF-ADMIN-002 | 手動建立代理 | 1. 點擊「新增代理」 2. 選擇已存在的用戶 3. 提交 | 1. 用戶升級為代理 2. 分配預設代理等級 | P1 |
| TC-AFF-ADMIN-003 | 手動綁定上下線 | 1. 使用手動綁定功能 2. 指定上線和下線 | 1. 綁定成功 2. affiliate-bind-log 寫入紀錄 3. 最多 3 層代理結構 4. 不可建立循環關係 | P1 |
3.12.2 佣金費率
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-AFF-RATE-001 | 費率列表 | 1. 進入代理中心 > 佣金費率 2. 選擇站點 Tab | 1. 顯示按代理等級 x 遊戲類型的費率表 2. 4 等級 x 3 級別 x 8 遊戲類型 = 96 筆 3. 可編輯比例 | P1 |
| TC-AFF-RATE-002 | 帶入模板 | 1. 點擊「帶入模板」 2. 預覽 3. 確認 | 1. 預設費率模板載入 2. 確認後寫入資料庫 3. atomic transaction | P1 |
3.12.3 結算紀錄
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-AFF-SETTLE-001 | 結算列表 | 1. 進入代理中心 > 結算紀錄 | 1. 顯示佣金結算紀錄 2. 含結算金額、狀態、日期 3. 篩選 status + 日期 | P1 |
| TC-AFF-SETTLE-002 | 結算審核 | 1. 對 pending 結算點擊「審核」 2. 選擇 approve / reject | 1. 審核結果更新 2. approved → 佣金入帳 3. rejected → 不入帳 | P1 |
| TC-AFF-SETTLE-003 | 風控紀錄查看 | 1. 點擊某結算的「風控紀錄」 | 1. 顯示風控檢測結果 2. 含異常下線檢測、金額異常等 3. affiliate-risk-log 資料 | P2 |
3.12.4 代理提款
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-AFF-WD-001 | 代理提款列表 | 1. 進入代理中心 > 代理提款 | 1. 顯示代理提款訂單 2. 篩選 status + method 3. 三階段:待審核 → 已核准 → 已完成 | P1 |
| TC-AFF-WD-002 | 三階段流程 | 1. 審核 → 核准 2. 完成 → 標記完成 | 1. pending → approved → completed 2. 每階段狀態正確轉換 3. 佣金餘額正確扣除 | P1 |
3.12.5 其他代理頁面
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-AFF-BIND-001 | 綁定紀錄 | 1. 進入綁定紀錄頁面 2. 篩選 action + refCode + 日期 | 1. 顯示所有綁定/解綁紀錄 2. 篩選正確 | P2 |
| TC-AFF-TIER-001 | 代理等級管理 | 1. 進入代理等級頁面 2. CRUD 操作 | 1. 4 個預設等級:bronze / silver / gold / platinum 2. 可修改等級條件 3. 帶入模板可用 | P2 |
| TC-AFF-VIP-MILE-001 | VIP 里程碑 | 1. 進入 VIP 里程碑頁面 2. CRUD 操作 | 1. 下線 VIP 等級達標獎勵配置 2. 帶入模板可用 | P2 |
| TC-AFF-TOUR-001 | 代理導覽 | 1. 進入代理導覽頁面 2. 切換站點 Tab(模式 B) | 1. 每站獨立設定 2. 模式 B 設定頁正確運作 | P3 |
| TC-AFF-MANUAL-001 | 手動觸發結算 | 1. 點擊「觸發週結」 2. 確認 | 1. 手動執行週結排程 2. 結算紀錄產生 3. 含風控檢測 | P2 |
| TC-AFF-MANUAL-002 | 手動觸發日結 | 1. 點擊「觸發日結」 2. 確認 | 1. 手動執行日結排程 2. 日結紀錄產生 | P2 |
| TC-AFF-SET-TIER-001 | 手動設定代理等級 | 1. 選擇代理 2. 設定新等級 | 1. 等級手動調整成功 2. 不受自動升級邏輯影響 | P2 |
第 4 章:後端 API 測試案例 (c9-be)
測試對象:http://localhost:8080/api框架:NestJS v11 (TypeScript 5.7 strict) Swagger UI:http://localhost:8080/api/docs測試工具:Postman / Insomnia / Jest + Supertest 統一回應格式:
{ code: 200, message: "ok", result, timestamp, path }
4.1 認證 API(前台用戶)
Base:
/api/authGuard:JwtAuthGuard(需登入端點)/ 無(公開端點)
4.1.1 註冊與登入
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AUTH-001 | POST | /auth/register | { account: "qauser01", password: "Qa123456!", email: "qa01@test.com" } | code: 200, result 含 token + userDetail | P1 |
| TC-API-AUTH-002 | POST | /auth/register | { account: "testuser01" } (已存在帳號) | code != 200, message 含帳號已存在錯誤 | P1 |
| TC-API-AUTH-003 | POST | /auth/register | { account: "qa", password: "123" } (太短) | code != 200, 驗證錯誤 | P2 |
| TC-API-AUTH-004 | POST | /auth/register | 附帶 refCode: "ABC123" | code: 200, 用戶自動綁定代理上線 | P1 |
| TC-API-AUTH-005 | POST | /auth/login | { account: "testuser01", password: "Test123456" } | code: 200, result 含 token + userDetail | P1 |
| TC-API-AUTH-006 | POST | /auth/login | { account: "testuser01", password: "wrong" } | code != 200, 登入失敗訊息 | P1 |
| TC-API-AUTH-007 | POST | /auth/login | { account: "nonexistent" } | code != 200, 錯誤訊息不洩漏帳號是否存在 | P1 |
| TC-API-AUTH-008 | POST | /auth/login | 含 deviceFingerprint: "fp_xxxxx" | code: 200, 指紋寫入 login-log | P2 |
4.1.2 OAuth 登入
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AUTH-009 | POST | /auth/login/google | { idToken: "google_id_token" } | code: 200, 自動建立/登入帳號 | P1 |
| TC-API-AUTH-010 | POST | /auth/login/telegram | { id, first_name, hash, auth_date } | code: 200, Telegram 驗證 + 登入 | P2 |
4.1.3 驗證碼
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AUTH-011 | POST | /auth/send-email-verify | { email: "user@test.com" } | code: 200, Resend API 發送驗證碼 | P2 |
| TC-API-AUTH-012 | POST | /auth/send-mobile-verify | { phone: "+886912345678" } | code: 200, Twilio 發送 SMS | P2 |
| TC-API-AUTH-013 | POST | /auth/verify-email | { email, verifyCode } | code: 200, Email 驗證完成 | P2 |
| TC-API-AUTH-014 | POST | /auth/verify-mobile | { phone, verifyCode } | code: 200, 手機驗證完成 | P2 |
4.1.4 用戶資料
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AUTH-015 | GET | /auth/user-detail | Bearer Token | code: 200, 完整用戶資料 | P1 |
| TC-API-AUTH-016 | PATCH | /auth/user-detail | { nickname: "新暱稱" } | code: 200, 資料更新 | P2 |
| TC-API-AUTH-017 | PATCH | /auth/avatar | FormData 含圖片 | code: 200, 頭像上傳至 R2 | P3 |
| TC-API-AUTH-018 | POST | /auth/set-password | { oldPassword, newPassword } | code: 200, 密碼變更 | P1 |
4.1.5 2FA (Google Authenticator)
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AUTH-019 | POST | /auth/google-auth/enable | Bearer Token | code: 200, 回傳 QR Code URL + Secret | P1 |
| TC-API-AUTH-020 | POST | /auth/google-auth/verify | { token: "123456" } (TOTP) | code: 200, 2FA 啟用成功 | P1 |
| TC-API-AUTH-021 | PATCH | /auth/google-auth/disable | { token: "123456" } | code: 200, 2FA 停用 | P1 |
| TC-API-AUTH-022 | POST | /auth/logout | Bearer Token | code: 200, Token 失效 | P1 |
4.1.6 Token 驗證
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AUTH-023 | GET | /auth/user-detail | 無 Token | HTTP 401, code: 401 | P1 |
| TC-API-AUTH-024 | GET | /auth/user-detail | 過期 Token | HTTP 401, code: 401 | P1 |
| TC-API-AUTH-025 | GET | /auth/user-detail | 無效 Token (亂碼) | HTTP 401, code: 401 | P1 |
| TC-API-AUTH-026 | GET | /auth/user-detail | AdminJWT Token (非前台 Token) | HTTP 401, 前後台 Token 隔離 | P1 |
4.2 管理員 API(後台)
Base:
/api/adminGuard:AdminJwtAuthGuard+PermissionsGuardHeader:Authorization: Bearer <AdminJWT>
4.2.1 管理員認證
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-ADMIN-001 | POST | /admin/login | { account: "root_admin", password: "Admin123456" } | code: 200, AdminJWT Token | P1 |
| TC-API-ADMIN-002 | POST | /admin/login | 錯誤密碼 | code != 200, 登入失敗 | P1 |
| TC-API-ADMIN-003 | POST | /admin/register | { account, password, nickname, groupId } | code: 200, 管理員建立 | P1 |
| TC-API-ADMIN-004 | GET | /admin/profile | Bearer AdminJWT | code: 200, 管理員個人資料 | P1 |
4.2.2 管理員 CRUD
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-ADMIN-005 | GET | /admin/list | ?page=1&pageSize=20 | code: 200, result.items[] + result.pagination | P1 |
| TC-API-ADMIN-006 | GET | /admin/:id | 有效 admin ID | code: 200, 單一管理員資料 | P1 |
| TC-API-ADMIN-007 | PATCH | /admin/:id | { nickname: "新名稱" } | code: 200, 更新成功 | P1 |
| TC-API-ADMIN-008 | DELETE | /admin/:id | 有效 admin ID | code: 200, 刪除成功 | P1 |
| TC-API-ADMIN-009 | DELETE | /admin/:id | 自己的 ID | code != 200, 不可刪除自己 | P1 |
4.2.3 群組 CRUD
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-ADMIN-010 | GET | /admin/groups/list | 無 | code: 200, 群組列表含權限 | P1 |
| TC-API-ADMIN-011 | POST | /admin/groups | { name, permissions: ["user:read", "deposit:read"] } | code: 200, 群組建立 | P1 |
| TC-API-ADMIN-012 | PATCH | /admin/groups/:id | { permissions: [...] } | code: 200, 權限更新 | P1 |
| TC-API-ADMIN-013 | DELETE | /admin/groups/:id | 無成員群組 | code: 200, 刪除成功 | P2 |
| TC-API-ADMIN-014 | DELETE | /admin/groups/:id | 有成員群組 | code != 200, 需先移除成員 | P2 |
| TC-API-ADMIN-015 | GET | /admin/permissions/all | 無 | code: 200, 16 個模組 × 2 權限(read/write) | P2 |
4.2.4 操作紀錄
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-ADMIN-016 | GET | /admin/logs/list | ?page=1&pageSize=20 | code: 200, 操作紀錄列表 | P2 |
4.2.5 財務管理 API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-FIN-001 | GET | /admin/finance/deposit-review/list | ?page=1&siteCode=C9 | code: 200, 存款訂單列表 | P1 |
| TC-API-FIN-002 | POST | /admin/finance/deposit-review/:id | { action: "approve" } | code: 200, 訂單審核通過,用戶餘額增加 | P1 |
| TC-API-FIN-003 | POST | /admin/finance/deposit-review/:id | { action: "reject", reason: "..." } | code: 200, 訂單拒絕 | P1 |
| TC-API-FIN-004 | GET | /admin/finance/users/list | ?page=1&siteCode=C9&keyword=test | code: 200, 用戶列表含篩選 | P1 |
| TC-API-FIN-005 | PATCH | /admin/finance/users/:userId | { nickname, status } | code: 200, 用戶資料更新 | P1 |
| TC-API-FIN-006 | GET | /admin/finance/users/:userId | 有效用戶 ID | code: 200, 用戶詳情 | P1 |
| TC-API-FIN-007 | POST | /admin/finance/adjust-balance | { userId, amount: 100, reason: "..." } | code: 200, 餘額增加 100 USD | P1 |
| TC-API-FIN-008 | POST | /admin/finance/adjust-balance | { userId, amount: -99999 } (超額) | code != 200, 餘額不足 | P1 |
| TC-API-FIN-009 | GET | /admin/finance/bank-cards/list | ?siteCode=C9&status=pending | code: 200, 篩選銀行卡 | P1 |
| TC-API-FIN-010 | POST | /admin/finance/bank-cards/:id/review | { action: "approve" } | code: 200, 審核通過 | P1 |
| TC-API-FIN-011 | GET | /admin/finance/credit-cards/list | ?siteCode=C9 | code: 200, 信用卡列表 | P1 |
| TC-API-FIN-012 | POST | /admin/finance/credit-cards/:id/review | { action: "approve" } | code: 200, 審核通過 | P1 |
| TC-API-FIN-013 | GET | /admin/finance/crypto-addresses/list | ?siteCode=C9&network=TRC-20 | code: 200, 加密地址列表 | P1 |
| TC-API-FIN-014 | POST | /admin/finance/crypto-addresses/:id/review | { action: "approve" } | code: 200, 審核通過 | P1 |
| TC-API-FIN-015 | GET | /admin/finance/withdrawals/list | ?siteCode=C9&status=pending | code: 200, 提款列表 | P1 |
| TC-API-FIN-016 | POST | /admin/finance/withdrawals/:id/review | { action: "approve" } | code: 200, 提款審核通過 | P1 |
| TC-API-FIN-017 | POST | /admin/finance/withdrawals/:id/review | { action: "reject" } | code: 200, 提款拒絕,凍結餘額退回 | P1 |
| TC-API-FIN-018 | POST | /admin/finance/withdrawals/:id/upload-proof | FormData 含憑證圖片 | code: 200, 憑證上傳至 R2 | P2 |
| TC-API-FIN-019 | POST | /admin/finance/withdrawals/:id/complete | 無 | code: 200, 提款完成,凍結餘額扣除 | P1 |
| TC-API-FIN-020 | PUT | /admin/users/:userId/vendor-group | { vendorGroupId } | code: 200, 金流群組分配 | P2 |
4.2.6 活動管理 API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-PROMO-001 | GET | /admin/promos/list | ?siteCode=C9&page=1 | code: 200, 活動列表 | P1 |
| TC-API-PROMO-002 | POST | /admin/promos | { title: {...}, content: {...}, siteCode } | code: 200, 活動建立 | P1 |
| TC-API-PROMO-003 | PATCH | /admin/promos/:id | { title: {...} } | code: 200, 活動更新 | P1 |
| TC-API-PROMO-004 | DELETE | /admin/promos/:id | 無 | code: 200, 活動刪除 | P2 |
| TC-API-PROMO-005 | GET | /admin/promo-tags/list | ?siteCode=C9 | code: 200, 標籤列表 | P2 |
| TC-API-PROMO-006 | POST | /admin/promo-tags | { name, siteCode } | code: 200, 標籤建立 | P2 |
| TC-API-PROMO-007 | PATCH | /admin/promo-tags/:id | { name } | code: 200, 標籤更新 | P2 |
| TC-API-PROMO-008 | DELETE | /admin/promo-tags/:id | 無 | code: 200, 標籤刪除 | P2 |
4.2.7 風控 API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-RISK-001 | GET | /admin/risk/ip-rules/list | ?siteCode=C9&type=blacklist | code: 200, IP 規則列表 | P1 |
| TC-API-RISK-002 | POST | /admin/risk/ip-rules | { ip: "1.2.3.4", type: "blacklist", note, siteCode } | code: 200, 規則建立 | P1 |
| TC-API-RISK-003 | DELETE | /admin/risk/ip-rules/:id | 無 | code: 200, 規則刪除 | P1 |
| TC-API-RISK-004 | PATCH | /admin/risk/ip-rules/:id | { note: "..." } | code: 200, 規則更新 | P2 |
| TC-API-RISK-005 | GET | /admin/risk/lookup | ?siteCode=C9&keyword=192.168 | code: 200, IP/FP 反查結果 | P1 |
| TC-API-RISK-006 | GET | /admin/risk/game-blacklist/list | ?siteCode=C9 | code: 200, 遊戲黑名單列表 | P1 |
| TC-API-RISK-007 | POST | /admin/risk/game-blacklist | { userId, blockType: "all", siteCode } | code: 200, 遊戲黑名單建立 | P1 |
| TC-API-RISK-008 | DELETE | /admin/risk/game-blacklist/:id | 無 | code: 200, 黑名單移除 | P1 |
| TC-API-RISK-009 | GET | /admin/risk/login-failures | ?siteCode=C9&keyword=test&startDate&endDate | code: 200, 登入失敗紀錄 | P2 |
4.2.8 報表 API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-RPT-001 | GET | /admin/reports/overview | ?siteCode=C9&startDate&endDate | code: 200, 總覽統計(卡片 + 每日摘要) | P1 |
| TC-API-RPT-002 | GET | /admin/reports/players | ?siteCode=C9&vipLevel=5&page=1 | code: 200, 玩家報表 | P1 |
| TC-API-RPT-003 | GET | /admin/reports/vip-players | ?siteCode=C9 (@AdminSiteCode) | code: 200, VIP 玩家報表 | P1 |
| TC-API-RPT-004 | GET | /admin/reports/bet-records | ?siteCode=C9&gameType=SLOT&page=1 | code: 200, 投注紀錄 | P1 |
| TC-API-RPT-005 | GET | /admin/reports/games | ?siteCode=C9&gameType=LIVE | code: 200, 遊戲報表 | P1 |
| TC-API-RPT-006 | GET | /admin/reports/profit-loss | ?siteCode=C9&groupBy=daily | code: 200, 損益報表 | P1 |
| TC-API-RPT-007 | GET | /admin/reports/player-summary | ?siteCode=C9&sortBy=totalBet&sortOrder=desc | code: 200, 玩家摘要 | P1 |
| TC-API-RPT-008 | GET | /admin/reports/promos | ?startDate&endDate | code: 200, 活動報表 | P2 |
| TC-API-RPT-009 | GET | /admin/reports/r2-logs | ?page=1&pageSize=20 | code: 200, R2 操作紀錄 | P2 |
| TC-API-RPT-010 | GET | /admin/reports/export | ?type=players&siteCode=C9 | 回傳 CSV 檔案下載 | P2 |
4.2.9 R2 檔案操作 API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-R2-001 | GET | /admin/r2/list | ?prefix=images/ | code: 200, 檔案列表 | P2 |
| TC-API-R2-002 | POST | /admin/r2/upload | FormData 含檔案 | code: 200, 檔案 URL | P2 |
| TC-API-R2-003 | DELETE | /admin/r2/delete | { key: "images/xxx.png" } | code: 200, 刪除成功 | P2 |
| TC-API-R2-004 | POST | /admin/r2/move | { sourceKey, targetKey } | code: 200, 移動成功 | P3 |
| TC-API-R2-005 | POST | /admin/r2/create-folder | { prefix: "images/new/" } | code: 200, 資料夾建立 | P3 |
| TC-API-R2-006 | DELETE | /admin/r2/delete-folder | { prefix: "images/old/" } | code: 200, 資料夾刪除 | P3 |
4.2.10 金流商 API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-VENDOR-001 | GET | /admin/vendor-groups/list | ?siteCode=C9 | code: 200, 金流群組列表 | P1 |
| TC-API-VENDOR-002 | POST | /admin/vendor-groups | { name, siteCode } | code: 200, 群組建立 | P1 |
| TC-API-VENDOR-003 | PATCH | /admin/vendor-groups/:id | { name } | code: 200, 群組更新 | P2 |
| TC-API-VENDOR-004 | DELETE | /admin/vendor-groups/:id | 無 | code: 200, 群組刪除 | P2 |
| TC-API-VENDOR-005 | GET | /admin/vendor-channels/list | ?groupId=1 | code: 200, 通道列表 | P1 |
| TC-API-VENDOR-006 | POST | /admin/vendor-channels | { name, type, groupId } | code: 200, 通道建立 | P1 |
| TC-API-VENDOR-007 | PATCH | /admin/vendor-channels/:id | { enabled: false } | code: 200, 通道更新 | P1 |
| TC-API-VENDOR-008 | DELETE | /admin/vendor-channels/:id | 無 | code: 200, 通道刪除 | P2 |
4.3 遊戲 API
Base:
/api/game
4.3.1 前台遊戲端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-GAME-001 | GET | /game/providers | ?gameType=SLOT + site-name header | code: 200, 該分類供應商列表 | P1 |
| TC-API-GAME-002 | POST | /game/launch | { providerCode, gameId } + Bearer Token | code: 200, result.url 遊戲 iframe URL | P1 |
| TC-API-GAME-003 | POST | /game/launch | 被風控封鎖的用戶 | code: 5010, 遊戲黑名單阻擋 | P1 |
| TC-API-GAME-004 | POST | /game/demo | { providerCode, gameId } | code: 200, 試玩 URL(無需登入) | P2 |
| TC-API-GAME-005 | GET | /game/recent | Bearer Token | code: 200, 最近遊玩列表(最多 10) | P3 |
4.3.2 遊戲 Admin API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-GAME-006 | GET | /game/admin/providers | ?gameType=SLOT + @AdminSiteCode | code: 200, 供應商列表(按站點) | P1 |
| TC-API-GAME-007 | POST | /game/admin/providers | { providerCode, name, gameType, siteCode } | code: 200, 供應商建立 | P1 |
| TC-API-GAME-008 | PATCH | /game/admin/providers/:id | { name } | code: 200, 供應商更新 | P1 |
| TC-API-GAME-009 | DELETE | /game/admin/providers/:id | 無 | code: 200, 供應商刪除 | P2 |
| TC-API-GAME-010 | GET | /game/admin/type-configs | @AdminSiteCode | code: 200, 分類列表 | P1 |
| TC-API-GAME-011 | POST | /game/admin/type-configs | { gameType, name, siteCode } | code: 200, 分類建立 | P1 |
| TC-API-GAME-012 | PATCH | /game/admin/type-configs/:id | { name } | code: 200, 分類更新 | P1 |
| TC-API-GAME-013 | DELETE | /game/admin/type-configs/:id | 無 | code: 200, 分類刪除 | P2 |
| TC-API-GAME-014 | GET | /game/admin/preview-template | 無 | code: 200, 預設 providers + typeConfigs | P1 |
| TC-API-GAME-015 | POST | /game/admin/load-template | { providers: [...], typeConfigs: [...] } + @AdminSiteCode | code: 200, 模板寫入指定站點 | P1 |
| TC-API-GAME-016 | POST | /game/admin/copy-site-data | { sourceSiteCode: "C9", targetSiteCode: "A1", type: "providers" } | code: 200, 跨站複製(transaction 先刪後插) | P1 |
| TC-API-GAME-017 | POST | /game/admin/copy-site-data | { sourceSiteCode: "C9", targetSiteCode: "A1", type: "typeConfigs" } | code: 200, 分類跨站複製 | P1 |
4.3.3 S2S 回調
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-GAME-018 | POST | /game/betsolutions/callback | BetSolutions S2S 格式 (bet/win/refund) | 供應商格式回應,bet-order + game-transaction 寫入 | P1 |
| TC-API-GAME-019 | POST | /game/rsg/callback | RSG S2S 格式(DES 加密) | DES 解密 → 處理 → 加密回應 | P1 |
| TC-API-GAME-020 | POST | /game/betsolutions/callback | 重複交易 ID (idempotency) | 回傳之前的結果,不重複處理 | P1 |
4.4 VIP API
Base:
/api/vip
4.4.1 前台 VIP 端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-VIP-001 | GET | /vip/levels | site-name header | code: 200, 該站所有 VIP 等級列表 | P1 |
| TC-API-VIP-002 | GET | /vip/user-status | Bearer Token | code: 200, 用戶 VIP 狀態(等級、進度、保級) | P1 |
| TC-API-VIP-003 | GET | /vip/my-rebates | Bearer Token + ?page=1 | code: 200, 用戶反水紀錄 | P2 |
| TC-API-VIP-004 | GET | /vip/rebate-table | site-name header | code: 200, 等級 x 遊戲類型反水比例表 | P2 |
4.4.2 VIP Admin API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-VIP-005 | GET | /vip/admin/levels | @AdminSiteCode | code: 200, 等級列表(按站點) | P1 |
| TC-API-VIP-006 | POST | /vip/admin/levels | { level, name, threshold, siteCode } | code: 200, 等級建立 | P1 |
| TC-API-VIP-007 | PATCH | /vip/admin/levels/:id | { threshold } | code: 200, 等級更新 | P1 |
| TC-API-VIP-008 | DELETE | /vip/admin/levels/:id | 無 | code: 200, 等級刪除 | P2 |
| TC-API-VIP-009 | POST | /vip/admin/rebates/bulk-upsert | { items: [{ siteCode, level, gameType, rate }] } | code: 200, 批次更新反水 | P1 |
| TC-API-VIP-010 | GET | /vip/admin/rebates | @AdminSiteCode | code: 200, 反水設定列表 | P1 |
| TC-API-VIP-011 | POST | /vip/admin/copy-site-data | { sourceSiteCode, targetSiteCode, type: "levels" } | code: 200, VIP 等級跨站複製 | P1 |
| TC-API-VIP-012 | POST | /vip/admin/copy-site-data | { sourceSiteCode, targetSiteCode, type: "rebates" } | code: 200, 返水跨站複製 | P1 |
| TC-API-VIP-013 | GET | /vip/admin/preview-template | 無 | code: 200, VIP 模板預覽 | P1 |
| TC-API-VIP-014 | POST | /vip/admin/load-template | { levels: [...], rebates: [...] } + @AdminSiteCode | code: 200, 模板載入 | P1 |
4.5 代理 API
Base:
/api/affiliate
4.5.1 前台代理端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AFF-001 | POST | /affiliate/track-click | { refCode: "ABC123" } | code: 200, 點擊紀錄寫入 | P2 |
| TC-API-AFF-002 | POST | /affiliate/apply-agent | Bearer Token | code: 200, 申請成為代理 | P1 |
| TC-API-AFF-003 | GET | /affiliate/dashboard | Bearer Token | code: 200, 代理儀表板資料 | P1 |
| TC-API-AFF-004 | GET | /affiliate/downlines | Bearer Token + ?page=1 | code: 200, 下線列表(帳號遮罩) | P1 |
| TC-API-AFF-005 | GET | /affiliate/commissions | Bearer Token + ?page=1 | code: 200, 佣金紀錄 | P1 |
| TC-API-AFF-006 | GET | /affiliate/settlements | Bearer Token + ?page=1 | code: 200, 結算紀錄 | P1 |
| TC-API-AFF-007 | POST | /affiliate/withdraw | { amount, method } + Bearer Token | code: 200, 代理提款申請 | P1 |
| TC-API-AFF-008 | GET | /affiliate/balance | Bearer Token | code: 200, 佣金餘額(含凍結) | P1 |
4.5.2 聯盟公開端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AFF-009 | GET | /affiliate/alliance-info | site-name header | code: 200, 聯盟介紹資訊 | P2 |
| TC-API-AFF-010 | GET | /affiliate/tier-info | site-name header | code: 200, 代理等級資訊 | P2 |
| TC-API-AFF-011 | GET | /affiliate/vip-milestones | site-name header | code: 200, VIP 里程碑列表 | P2 |
| TC-API-AFF-012 | GET | /affiliate/referral-codes | Bearer Token | code: 200, 推薦碼列表 | P1 |
| TC-API-AFF-013 | POST | /affiliate/referral-codes | { code: "MYCODE" } + Bearer Token | code: 200, 推薦碼建立(max 10) | P1 |
| TC-API-AFF-014 | DELETE | /affiliate/referral-codes/:id | Bearer Token | code: 200, 推薦碼刪除 | P1 |
4.5.3 代理 Admin API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-AFF-ADMIN-001 | GET | /affiliate/admin/agents | @AdminSiteCode | code: 200, 代理列表 | P1 |
| TC-API-AFF-ADMIN-002 | POST | /affiliate/admin/create-agent | { userId } | code: 200, 手動建立代理 | P1 |
| TC-API-AFF-ADMIN-003 | POST | /affiliate/admin/bind | { parentId, childId } | code: 200, 手動綁定上下線 | P1 |
| TC-API-AFF-ADMIN-004 | GET | /affiliate/admin/bind-logs | ?action&refCode&startDate&endDate | code: 200, 綁定紀錄 | P2 |
| TC-API-AFF-ADMIN-005 | GET | /affiliate/admin/settlements | ?status&startDate&endDate | code: 200, 結算列表 | P1 |
| TC-API-AFF-ADMIN-006 | POST | /affiliate/admin/settlements/:id/review | { action: "approve" } | code: 200, 結算審核 | P1 |
| TC-API-AFF-ADMIN-007 | GET | /affiliate/admin/settlements/:id/risk-logs | 無 | code: 200, 風控紀錄 | P2 |
| TC-API-AFF-ADMIN-008 | GET | /affiliate/admin/withdrawals | ?status&method | code: 200, 代理提款列表 | P1 |
| TC-API-AFF-ADMIN-009 | POST | /affiliate/admin/withdrawals/:id/review | { action: "approve" } | code: 200, 提款審核 | P1 |
| TC-API-AFF-ADMIN-010 | POST | /affiliate/admin/withdrawals/:id/complete | 無 | code: 200, 提款完成 | P1 |
| TC-API-AFF-ADMIN-011 | GET | /affiliate/admin/commission-rates | @AdminSiteCode | code: 200, 佣金費率列表 | P1 |
| TC-API-AFF-ADMIN-012 | POST | /affiliate/admin/commission-rates | { items: [...] } | code: 200, 費率批次更新 | P1 |
| TC-API-AFF-ADMIN-013 | GET | /affiliate/admin/agent-tiers | @AdminSiteCode | code: 200, 代理等級列表 | P1 |
| TC-API-AFF-ADMIN-014 | POST | /affiliate/admin/agent-tiers | { items: [...] } | code: 200, 等級批次更新 | P1 |
| TC-API-AFF-ADMIN-015 | GET | /affiliate/admin/vip-milestones | @AdminSiteCode | code: 200, VIP 里程碑列表 | P2 |
| TC-API-AFF-ADMIN-016 | POST | /affiliate/admin/vip-milestones | { items: [...] } | code: 200, 里程碑批次更新 | P2 |
| TC-API-AFF-ADMIN-017 | GET | /affiliate/admin/preview-template | 無 | code: 200, 代理模板預覽 | P1 |
| TC-API-AFF-ADMIN-018 | POST | /affiliate/admin/load-template | @AdminSiteCode | code: 200, 模板載入(atomic) | P1 |
| TC-API-AFF-ADMIN-019 | POST | /affiliate/admin/set-agent-tier | { agentId, tierId } | code: 200, 手動設定等級 | P2 |
| TC-API-AFF-ADMIN-020 | POST | /affiliate/admin/trigger-settlement | 無 | code: 200, 手動觸發週結 | P2 |
| TC-API-AFF-ADMIN-021 | POST | /affiliate/admin/trigger-daily-settlement | 無 | code: 200, 手動觸發日結 | P2 |
4.6 金流 API
4.6.1 錢包端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-WALLET-001 | GET | /wallet/bank-cards | Bearer Token | code: 200, 用戶銀行卡列表 | P1 |
| TC-API-WALLET-002 | POST | /wallet/bank-cards | { bankName, branch, accountNumber, holderName } | code: 200, 銀行卡建立 | P1 |
| TC-API-WALLET-003 | DELETE | /wallet/bank-cards/:id | Bearer Token | code: 200, 銀行卡刪除 | P2 |
| TC-API-WALLET-004 | GET | /wallet/credit-cards | Bearer Token | code: 200, 信用卡列表 | P1 |
| TC-API-WALLET-005 | POST | /wallet/credit-cards | { cardNumber, expiry, cvv, holderName } | code: 200, 信用卡建立 | P1 |
| TC-API-WALLET-006 | DELETE | /wallet/credit-cards/:id | Bearer Token | code: 200, 信用卡刪除 | P2 |
| TC-API-WALLET-007 | GET | /wallet/crypto-addresses | Bearer Token | code: 200, 加密地址列表 | P1 |
| TC-API-WALLET-008 | POST | /wallet/crypto-addresses | { network: "TRC-20", address, currency: "USDT" } | code: 200, 加密地址建立 | P1 |
| TC-API-WALLET-009 | DELETE | /wallet/crypto-addresses/:id | Bearer Token | code: 200, 加密地址刪除 | P2 |
4.6.2 存款端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-DEP-001 | GET | /deposit/list | Bearer Token + ?page=1 | code: 200, 存款訂單列表 | P1 |
| TC-API-DEP-002 | POST | /deposit | { channelId, amount } + Bearer Token | code: 200, 建立存款訂單(後端路由至對應金流商) | P1 |
| TC-API-DEP-003 | GET | /deposit/:id | Bearer Token | code: 200, 訂單詳情 | P2 |
| TC-API-DEP-004 | GET | /deposit/exchange-rate | ?currency=TWD | code: 200, 台灣銀行即時匯率 | P1 |
4.6.3 提款端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-WD-001 | GET | /withdrawal/list | Bearer Token + ?page=1 | code: 200, 提款訂單列表 | P1 |
| TC-API-WD-002 | POST | /withdrawal | { walletId, walletType, amount } + Bearer Token | code: 200, 建立提款訂單 | P1 |
| TC-API-WD-003 | POST | /withdrawal | 金額超過餘額 | code != 200, 餘額不足 | P1 |
| TC-API-WD-004 | GET | /withdrawal/:id | Bearer Token | code: 200, 訂單詳情 | P2 |
4.6.4 金流回調
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-VENDOR-CB-001 | POST | /vendor/wantong/callback | 萬通金流回調格式 | 回傳成功確認,訂單狀態更新 | P1 |
| TC-API-VENDOR-CB-002 | POST | /vendor/usdt/callback | USDT 確認回調 | 回傳成功確認,訂單狀態更新 | P1 |
| TC-API-VENDOR-CB-003 | POST | /vendor/wantong/callback | 重複回調 (idempotency) | 不重複處理,回傳成功 | P1 |
4.7 其他 API
4.7.1 站內信
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-INBOX-001 | GET | /inbox/list | Bearer Token + ?page=1 | code: 200, 通知列表 | P1 |
| TC-API-INBOX-002 | GET | /inbox/:id | Bearer Token | code: 200, 通知詳情 + 標記已讀 | P1 |
| TC-API-INBOX-003 | GET | /inbox/unread-count | Bearer Token | code: 200, 未讀數量 | P1 |
| TC-API-INBOX-004 | POST | /inbox/read-all | Bearer Token | code: 200, 全部標記已讀 | P2 |
4.7.2 任務系統
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-MISSION-001 | GET | /mission/list | Bearer Token | code: 200, 任務列表含進度 | P1 |
| TC-API-MISSION-002 | POST | /mission/:id/claim | Bearer Token | code: 200, 領取獎勵(已完成任務) | P1 |
| TC-API-MISSION-003 | POST | /mission/:id/claim | 未完成任務 | code != 200, 任務尚未完成 | P1 |
4.7.3 站點設定 Admin API
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-SITECONF-001 | GET | /site-config/admin/list | 無 | code: 200, 所有站點列表(含主題) | P1 |
| TC-API-SITECONF-002 | POST | /site-config/admin | { siteCode, name, ... } | code: 200, 站點建立 | P1 |
| TC-API-SITECONF-003 | PATCH | /site-config/admin/:id | { name } | code: 200, 站點更新 | P1 |
| TC-API-SITECONF-004 | DELETE | /site-config/admin/:id | 有效站點 ID | code: 200, 站點刪除(cascade 主題) | P1 |
| TC-API-SITECONF-005 | GET | /site-config/admin/:siteConfigId/themes | 有效 siteConfigId | code: 200, 主題列表 | P1 |
| TC-API-SITECONF-006 | POST | /site-config/admin/:siteConfigId/themes | { name, colors: { primary, accent, ... } } | code: 200, 主題建立 | P2 |
| TC-API-SITECONF-007 | PATCH | /site-config/admin/themes/:id | { colors: { primary: "#xxx" } } | code: 200, 主題更新 | P2 |
| TC-API-SITECONF-008 | DELETE | /site-config/admin/themes/:id | 有效主題 ID | code: 200, 主題刪除 | P2 |
| TC-API-SITECONF-009 | POST | /site-config/admin/:id/domain-asset | FormData (logoSmall/logoBig/favicon) | code: 200, 素材上傳至 R2 | P2 |
| TC-API-SITECONF-010 | POST | /site-config/admin/:id/customer-service-icon | FormData (客服圖示) | code: 200, 圖示上傳 | P3 |
| TC-API-SITECONF-011 | PATCH | /site-config/admin/:siteConfigId/mascots | { mascots: ["url1", "url2"] } | code: 200, 吉祥物更新 | P3 |
| TC-API-SITECONF-012 | GET | /site-config/admin/:siteCode/customer-service | 有效 siteCode | code: 200, 客服設定(8 管道) | P1 |
4.7.4 公用端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-COMMON-001 | GET | /common/enums | 無 | code: 200, 完整 ERROR_CODES + 枚舉常數 | P1 |
| TC-API-COMMON-002 | GET | /ranking/list | site-name header | code: 200, 排行榜資料 | P3 |
| TC-API-COMMON-003 | GET | /bet-record/list | Bearer Token + ?page=1 | code: 200, 用戶投注紀錄 | P2 |
| TC-API-COMMON-004 | GET | /bet-record/:id | Bearer Token | code: 200, 投注詳情 | P2 |
| TC-API-COMMON-005 | GET | /live-sports/matches | site-name header | code: 200, 即時賽事列表(30 分鐘快取) | P3 |
4.7.5 前台站點設定
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-SITECONF-F-001 | GET | /site-config/info | site-name header | code: 200, 站點資訊(名稱、Logo、features) | P1 |
| TC-API-SITECONF-F-002 | GET | /site-config/theme | site-name header | code: 200, 當前主題配色 | P1 |
| TC-API-SITECONF-F-003 | GET | /site-config/customer-service | site-name header | code: 200, 客服管道列表 | P1 |
4.7.6 前台活動端點
| TC-ID | 方法 | 端點 | 請求內容 | 預期回應 | 優先級 |
|---|---|---|---|---|---|
| TC-API-PROMO-F-001 | GET | /promo/list | site-name header + ?page=1 | code: 200, 活動列表(僅 enabled) | P1 |
| TC-API-PROMO-F-002 | GET | /promo/:id | site-name header | code: 200, 活動詳情 | P1 |
| TC-API-PROMO-F-003 | POST | /promo/:id/claim | Bearer Token | code: 200, 領取活動(檢查條件) | P1 |
| TC-API-PROMO-F-004 | POST | /promo/:id/claim | 不符合條件 | code != 200, 條件未達標 | P1 |
第 5 章:業務流程端對端測試
本章描述跨系統的完整業務流程,驗證前台、後台、後端三個專案協同運作的正確性。 每個流程包含完整步驟、各階段預期狀態,以及需驗證的資料一致性。
5.1 完整存款流程
流程概述
前台提交存款 → 後端建立訂單 → 金流商回調確認 → 後台管理員審核 → 用戶餘額更新詳細步驟
步驟 1:前台提交存款
| 操作 | 驗證項目 |
|---|---|
| 1.1 用戶登入前台 | JWT Token 取得,用戶資訊正確 |
| 1.2 進入存款頁面 | 金流通道列表載入(依金流群組篩選) |
| 1.3 選擇存款通道(如 ATM) | 通道資訊顯示正確 |
| 1.4 輸入存款金額(如 30,000 TWD) | 匯率換算正確顯示(台灣銀行即時匯率) |
| 1.5 確認提交 | API POST /deposit 呼叫成功 |
步驟 1 結束狀態:
deposit-order表新增一筆記錄- 訂單狀態:
pending - 訂單金額:USD(截斷至 6 位小數)
- 訂單匯率:使用
tw-exchange取得的即時匯率 - 用戶餘額:不變
步驟 2:金流商回調
| 操作 | 驗證項目 |
|---|---|
| 2.1 用戶完成付款(ATM 轉帳/信用卡/USDT) | 金流商處理完成 |
2.2 金流商發送回調至 /vendor/wantong/callback 或 /vendor/usdt/callback | 回調格式正確、簽名驗證 |
| 2.3 後端接收回調 | 回調處理成功,回傳確認 |
步驟 2 結束狀態:
- 訂單狀態可能更新為
callback_received(視實作) - 回調紀錄寫入
- Idempotency:重複回調不重複處理
步驟 3:後台管理員審核
| 操作 | 驗證項目 |
|---|---|
| 3.1 管理員登入後台 | AdminJWT 取得 |
| 3.2 進入財務管理 > 存款審核 | 訂單列表正確顯示 |
| 3.3 選擇站點 Tab + 篩選 pending 訂單 | siteCode 篩選正確 |
| 3.4 點擊「通過」並確認 | API POST /admin/finance/deposit-review/:id |
步驟 3 結束狀態(審核通過):
- 訂單狀態:
approved - 用戶餘額:增加 USD 金額
- 操作紀錄:寫入
admin-operation-log
步驟 3 結束狀態(審核拒絕):
- 訂單狀態:
rejected - 用戶餘額:不變
- 拒絕原因:記錄在訂單中
步驟 4:驗證餘額
| 驗證項目 | 預期結果 |
|---|---|
| 前台餘額顯示 | 餘額增加 = 存款金額(TWD) / 匯率,截斷至 6 位 |
auth-user 表 balance 欄位 | 數值正確 |
| 匯率精度 | decimal(18,10),使用即時匯率 |
| 金額精度 | decimal(18,6),無條件捨去 |
關鍵驗證公式
存款 USD = Math.floor(TWD_Amount / ExchangeRate * 1e6) / 1e6
用戶新餘額 = 原餘額 + 存款 USD異常場景
| 場景 | 預期行為 |
|---|---|
| 金流商回調失敗 | 訂單保持 pending,可手動處理 |
| 審核後金流商又回調 | Idempotency 處理,不重複加款 |
| 同時審核同一訂單 | 併發控制,只處理一次 |
| 匯率在審核前後變動 | 使用建單時的匯率,非審核時匯率 |
5.2 完整提款流程
流程概述
前台提交提款 → 餘額凍結 → 後台審核 → 上傳憑證 → 標記完成 → 凍結餘額扣除詳細步驟
步驟 1:前台提交提款
| 操作 | 驗證項目 |
|---|---|
| 1.1 用戶登入,確認餘額充足 | 餘額 >= 提款金額 |
| 1.2 進入提款頁面 | 已審核通過的銀行卡/加密地址列表 |
| 1.3 選擇提款方式(如銀行卡) | 僅顯示 approved 的卡片 |
| 1.4 輸入提款金額 | 驗證不超過可用餘額 |
| 1.5 確認提交 | API POST /withdrawal |
步驟 1 結束狀態:
| 欄位 | 值 |
|---|---|
withdrawal-order 狀態 | pending |
| 用戶可用餘額 | 原餘額 - 提款金額 |
| 用戶凍結餘額 | 原凍結 + 提款金額 |
| 用戶總資產 | 不變(可用 + 凍結 = 原總額) |
步驟 2:後台審核
| 操作 | 驗證項目 |
|---|---|
| 2.1 管理員進入提款管理 | 訂單列表正確 |
| 2.2 檢查訂單資訊 | 用戶、金額、提款方式正確 |
| 2.3 選擇「核准」或「拒絕」 | API 呼叫 |
審核通過後狀態:
| 欄位 | 值 |
|---|---|
| 訂單狀態 | approved |
| 用戶可用餘額 | 不變(維持步驟 1) |
| 用戶凍結餘額 | 不變(維持步驟 1) |
審核拒絕後狀態:
| 欄位 | 值 |
|---|---|
| 訂單狀態 | rejected |
| 用戶可用餘額 | 原餘額(凍結退回) |
| 用戶凍結餘額 | 原凍結(扣回提款金額) |
步驟 3:上傳轉帳憑證(可選)
| 操作 | 驗證項目 |
|---|---|
| 3.1 管理員上傳轉帳截圖 | 圖片上傳至 R2 |
| 3.2 憑證連結寫入訂單 | 可在訂單詳情查看 |
步驟 4:標記完成
| 操作 | 驗證項目 |
|---|---|
| 4.1 管理員點擊「完成」 | API POST /admin/finance/withdrawals/:id/complete |
完成後狀態:
| 欄位 | 值 |
|---|---|
| 訂單狀態 | completed |
| 用戶可用餘額 | 不變 |
| 用戶凍結餘額 | 原凍結 - 提款金額(釋放) |
| 用戶總資產 | 原總額 - 提款金額 |
餘額變化摘要
假設用戶原始餘額 1000 USD,提款 200 USD:
| 階段 | 可用餘額 | 凍結餘額 | 總資產 | 提款訂單狀態 |
|---|---|---|---|---|
| 初始 | 1000 | 0 | 1000 | - |
| 提交後 | 800 | 200 | 1000 | pending |
| 審核通過 | 800 | 200 | 1000 | approved |
| 完成 | 800 | 0 | 800 | completed |
| 審核拒絕(替代) | 1000 | 0 | 1000 | rejected |
5.3 遊戲投注連動流程
流程概述
啟動遊戲 → 遊戲內下注 → S2S 回調 → 更新投注訂單
→ VIP 等級重算
→ 活動打碼量更新
→ 任務進度更新詳細步驟
步驟 1:啟動遊戲
| 操作 | 驗證項目 |
|---|---|
| 1.1 用戶在前台點擊「進入遊戲」 | API POST /game/launch |
| 1.2 風控黑名單檢查 | 未被封鎖 → 通過;已封鎖 → 錯誤碼 5010 |
| 1.3 遊戲 iframe 載入 | 遊戲供應商 URL 回傳成功 |
| 1.4 遊戲紀錄寫入 | game-play-log 新增紀錄 |
步驟 2:遊戲內下注(S2S 回調)
| 操作 | 驗證項目 |
|---|---|
| 2.1 用戶在遊戲內下注 100 USD | 遊戲供應商發送 S2S bet 回調 |
2.2 後端接收 /game/betsolutions/callback 或 /game/rsg/callback | 驗證簽名/DES 解密 |
| 2.3 扣除用戶餘額 100 USD | 轉帳錢包模式 |
| 2.4 寫入交易紀錄 | game-transaction 新增 debit 紀錄 |
| 2.5 寫入投注訂單 | bet-order + bet-detail 新增 |
步驟 2 結束狀態:
- 用戶餘額:減少 100 USD
bet-order狀態:pending(等待結算)game-transaction類型:debit
步驟 3:派彩(S2S 回調)
| 操作 | 驗證項目 |
|---|---|
| 3.1 遊戲結算,供應商發送 win 回調 | 派彩 250 USD |
| 3.2 後端增加用戶餘額 250 USD | 轉帳錢包模式 |
| 3.3 更新投注訂單 | bet-order 狀態 → settled |
| 3.4 寫入交易紀錄 | game-transaction 新增 credit 紀錄 |
步驟 4:連動系統更新
投注結算後,後端自動觸發以下 4 個連動:
4a. VIP 等級重算
| 驗證項目 | 預期結果 |
|---|---|
| 累計投注金額更新 | auth-user.totalBet 增加 100 USD |
| VIP 等級檢查 | 若累計投注達到下一等級門檻 → 自動升級 |
| VIP 等級只升不降 | 只在升級時更新,不會降級 |
| 升級後反水比例更新 | 新等級對應的返水比例生效 |
4b. 活動打碼量更新
| 驗證項目 | 預期結果 |
|---|---|
| 活動打碼進度 | 若用戶有參與需打碼的活動,打碼量增加 |
promo-claim 進度 | 打碼量欄位更新 |
| 達標自動發放 | 打碼量達標的活動可領取獎勵 |
4c. 任務進度更新
| 驗證項目 | 預期結果 |
|---|---|
| 投注任務進度 | mission-progress 更新 |
| 進度百分比 | 正確計算 |
| 任務完成 | 達標後可領取 → mission-claim |
4d. 代理佣金記錄
| 驗證項目 | 預期結果 |
|---|---|
| 若用戶有上線代理 | affiliate-commission 寫入佣金紀錄 |
| 三層代理佣金 | 各層代理按費率計算佣金 |
| 佣金金額精度 | decimal(18,6) |
冪等性驗證
| 場景 | 預期行為 |
|---|---|
| 相同交易 ID 重複發送 bet 回調 | 不重複扣款,回傳原結果 |
| 相同交易 ID 重複發送 win 回調 | 不重複派彩,回傳原結果 |
| refund 回調 | 退還已扣款金額,bet-order 狀態更新 |
5.4 代理佣金結算流程
流程概述
用戶透過推薦碼註冊 → 綁定代理上線 → 用戶投注 → 佣金產生
→ 每週一自動週結 → 風控檢測 → 管理員審核
→ 代理提款 → 三階段審核 → 完成詳細步驟
步驟 1:代理綁定
| 操作 | 驗證項目 |
|---|---|
1.1 代理建立推薦碼 AGENT01 | alliance-referral-code 表新增 |
1.2 新用戶透過 ?ref=AGENT01 連結進入 | 推薦碼記錄到前端 |
| 1.3 用戶完成註冊 | 自動綁定代理上線 |
| 1.4 綁定紀錄 | affiliate-bind-log 寫入 action=bind |
步驟 1 結束狀態:
- 用戶的上線代理 = AGENT01 的擁有者
- 代理下線人數 + 1
- 三層代理結構:若 AGENT01 有上線,則建立最多 3 層關係
步驟 2:用戶投注產生佣金
| 操作 | 驗證項目 |
|---|---|
| 2.1 下線用戶在前台投注 1000 USD | S2S 回調處理 |
| 2.2 自動計算各層代理佣金 | 依等級 x 遊戲類型的費率計算 |
| 2.3 佣金紀錄寫入 | affiliate-commission 表新增 |
佣金計算範例(假設 SLOT 遊戲):
| 代理層級 | 代理等級 | 費率 | 佣金 (USD) |
|---|---|---|---|
| 一級代理(直屬上線) | gold | 0.5% | 5.00 |
| 二級代理 | silver | 0.2% | 2.00 |
| 三級代理 | bronze | 0.1% | 1.00 |
步驟 3:自動週結(Cron)
| 操作 | 驗證項目 |
|---|---|
| 3.1 每週一 03:00 觸發 Cron Job | affiliate-settlement.service.ts |
| 3.2 彙總該週佣金 | 按代理分組計算總額 |
| 3.3 風控檢測 | affiliate-risk.service.ts 檢查異常下線 |
| 3.4 建立結算紀錄 | affiliate-settlement 表新增 |
步驟 3 結束狀態:
- 結算紀錄狀態:
pending(待審核) - 風控紀錄:
affiliate-risk-log寫入檢測結果 - 可能觸發的風控項目:
- 異常高額佣金
- 短時間大量下線註冊
- 下線投注模式異常
步驟 4:管理員審核
| 操作 | 驗證項目 |
|---|---|
| 4.1 管理員進入代理中心 > 結算紀錄 | 列表顯示 pending 結算 |
| 4.2 查看風控紀錄 | 評估風險等級 |
| 4.3 審核通過/拒絕 | API 呼叫 |
審核通過後:
- 結算狀態:
approved - 代理佣金餘額增加
審核拒絕後:
- 結算狀態:
rejected - 佣金不入帳
步驟 5:代理提款
| 操作 | 驗證項目 |
|---|---|
| 5.1 代理在前台提交提款申請 | affiliate-withdrawal 表新增 |
| 5.2 管理員審核提款 | pending → approved |
| 5.3 管理員標記完成 | approved → completed |
三階段提款狀態:
| 階段 | 代理佣金餘額 | 代理凍結餘額 | 提款狀態 |
|---|---|---|---|
| 提交前 | 500 | 0 | - |
| 提交後 | 300 | 200 | pending |
| 審核通過 | 300 | 200 | approved |
| 完成 | 300 | 0 | completed |
| 審核拒絕(替代) | 500 | 0 | rejected |
5.5 VIP 保級/降級流程
流程概述
用戶升至 VIP 5+ → 每月保級檢查 → 投注未達門檻 → relegationMissCount++
→ 連續未達 → 降級 VIP 4 → 反水比例調整詳細步驟
步驟 1:用戶達到 VIP 5
| 操作 | 驗證項目 |
|---|---|
| 1.1 用戶累計投注達到 VIP 5 門檻 | 自動升級至 VIP 5 |
| 1.2 VIP 等級更新 | auth-user.vipLevel = 5 |
| 1.3 保級機制啟動 | VIP 5+ 支援保級鎖定 |
步驟 2:第一個月保級檢查
| 操作 | 驗證項目 |
|---|---|
| 2.1 每月 1 號 01:00 觸發 Cron Job | VipService 保級排程 |
| 2.2 檢查上月投注金額 | 對比 VIP 5 月度保級門檻 |
| 2.3 投注未達門檻 | relegationMissCount += 1 |
步驟 2 結束狀態(未達標):
- VIP 等級:維持 VIP 5(首次未達不降級)
relegationMissCount:1- 用戶可在本月補足投注
步驟 3:第二個月保級檢查
| 操作 | 驗證項目 |
|---|---|
| 3.1 再次觸發月度檢查 | Cron Job |
| 3.2 上月投注仍未達門檻 | relegationMissCount += 1 (= 2) |
| 3.3 觸發降級 | 依保級規則降至 VIP 4 |
步驟 3 結束狀態(連續未達標):
- VIP 等級:降至 VIP 4
relegationMissCount:重置為 0- 反水比例調整為 VIP 4 對應的費率
步驟 4:驗證連動影響
| 驗證項目 | 預期結果 |
|---|---|
| 前台 VIP 狀態頁 | 顯示 VIP 4 |
| 反水結算 | 使用 VIP 4 的返水比例 |
| VIP 福利 | 調整為 VIP 4 對應福利 |
| 可重新升級 | 累計投注達 VIP 5 門檻可再次升級 |
保級成功場景
| 操作 | 結果 |
|---|---|
| 月度投注 >= 保級門檻 | relegationMissCount 重置為 0 |
| VIP 等級 | 維持不變 |
| 保級鎖定(VIP 5+) | 繼續保護 |
第 6 章:多站點隔離測試
測試目標:驗證各站點資料完全隔離,不同站點之間無法互相存取資料。 前置條件:Seed 資料含至少 2 個站點(如 C9 和 A1),各站點有獨立資料。
6.1 資料隔離測試
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-ISO-001 | A 站資料不可見於 B 站 | 1. 在 C9 站建立一筆活動 2. 切換至 A1 站 3. 查看活動列表 | 1. C9 的活動不顯示在 A1 列表中 2. 各站點資料完全隔離 3. API 返回的資料僅含當前站點 | P1 |
| TC-ISO-002 | siteCode 篩選覆蓋所有 Entity | 1. 對每個含 siteCode 的 Entity 進行測試 2. 使用不同站點查詢 | 驗證以下 Entity 的 siteCode 篩選正確:auth-user, deposit-order, withdrawal-order, bank-card, credit-card, crypto-address, game-provider, game-type-config, game-transaction, bet-order, bet-detail, vip-level, vip-rebate, vip-rebate-log, promo, promo-claim, promo-tag, affiliate-commission, affiliate-settlement, affiliate-balance, affiliate-withdrawal, affiliate-click, affiliate-bind-log, alliance-commission-rate, alliance-agent-tier, alliance-vip-milestone, alliance-referral-code, notification, site-theme, risk-ip-rule, risk-game-blacklist, mission, mission-progress, mission-claim, vendor-group, vendor-channel, rank-list, r2-operation-log | P1 |
| TC-ISO-003 | 管理員切站後資料刷新 | 1. 在後台查看 C9 站玩家列表(記錄數量和帳號) 2. 切換至 A1 站 3. 查看玩家列表 | 1. 玩家列表完全不同 2. 數量可能不同 3. AdminContentWrapper 重新掛載 4. 無殘留 C9 資料 | P1 |
| TC-ISO-004 | 同預設站點只影響目標站 | 1. 記錄 C9 站遊戲供應商列表 2. 記錄 A1 站遊戲供應商列表 3. 記錄 B2 站遊戲供應商列表 4. 在 A1 站執行「同預設站點」 5. 檢查三個站點資料 | 1. C9 站(預設站)資料不變 2. A1 站資料更新為 C9 的內容 3. B2 站資料不受影響 4. 只有目標站被修改 | P1 |
| TC-ISO-005 | 模板載入只影響當前站 | 1. 在 A1 站載入 VIP 模板 2. 檢查 C9 站 VIP 等級 3. 檢查 B2 站 VIP 等級 | 1. A1 站 VIP 等級更新為模板內容 2. C9 站不受影響 3. B2 站不受影響 4. @AdminSiteCode 正確限定範圍 | P1 |
| TC-ISO-006 | 全站共用 Entity 跨站可見 | 1. 建立一個管理員帳號 2. 切換不同站點 3. 檢查管理員列表 | 1. admin-user 不含 siteCode,全站共用 2. admin-group 全站共用 3. admin-operation-log 全站共用 4. 這些表格不受站點切換影響 | P1 |
6.2 多站點 API 隔離測試
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-ISO-API-001 | x-site-code Header 篩選 | 1. 發送 API 帶 x-site-code: C9 2. 發送相同 API 帶 x-site-code: A1 3. 比較回傳資料 | 1. 兩次回傳的資料不同 2. C9 回傳僅含 siteCode=C9 的資料 3. A1 回傳僅含 siteCode=A1 的資料 | P1 |
| TC-ISO-API-002 | siteCode query param 篩選 | 1. 發送 API 帶 ?siteCode=C9 2. 發送相同 API 帶 ?siteCode=A1 | 1. 結果同上(@AdminSiteCode 先 header 後 query) 2. 查詢結果只含對應站點資料 | P1 |
| TC-ISO-API-003 | 不帶 siteCode | 1. 發送列表 API 不帶任何 siteCode 2. 檢查回傳資料 | 1. @AdminSiteCode 返回 null 2. 回傳所有站點資料(全站模式) 3. 或依模組邏輯決定預設行為 | P2 |
| TC-ISO-API-004 | 跨站寫入阻擋 | 1. 以 A1 站的 siteCode 查詢 C9 的資料 ID 2. 嘗試修改該筆資料 | 1. 應被阻擋或拒絕 2. 不可跨站修改資料 3. siteCode 檢查在 Service 層 | P1 |
| TC-ISO-API-005 | SiteCodeSubscriber 自動填入 | 1. 建立新 Entity(不帶 siteCode) 2. 檢查資料庫中的 siteCode | 1. TypeORM Subscriber 自動填入 SITE_CODE 環境變數值 2. 不需手動指定 siteCode | P2 |
| TC-ISO-API-006 | getSiteConfigs 回傳全站 | 1. 呼叫 /site-config/admin/list 2. 不帶 siteCode | 1. 必須回傳所有站點資料 2. 此 API 跳過 siteCode 篩選 3. 前端用此填充 SiteSelector | P1 |
6.3 前台多站點隔離
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-ISO-EC-001 | 前台域名配置 | 1. 檢查 useConfig.ts 的 domainConfig 2. 使用不同 hostname 存取 | 1. 不同域名對應不同站點 2. site-name header 自動設定 3. 遊戲、活動、VIP 等資料對應該站點 | P1 |
| TC-ISO-EC-002 | 前台主題隔離 | 1. A 站使用主題 1 2. B 站使用主題 2 3. 分別存取 | 1. 各站點顯示對應主題色彩 2. CSS 變數不互相干擾 3. 主題來源:後端 site-theme 表 | P2 |
| TC-ISO-EC-003 | 前台遊戲列表隔離 | 1. A 站有遊戲商 X 2. B 站沒有遊戲商 X 3. 分別查看遊戲大廳 | 1. A 站顯示遊戲商 X 2. B 站不顯示遊戲商 X 3. 遊戲列表完全獨立 | P1 |
| TC-ISO-EC-004 | 前台活動隔離 | 1. A 站有活動 Y 2. B 站沒有活動 Y | 1. 各站只顯示自己的活動 2. 不可跨站領取活動 | P1 |
| TC-ISO-EC-005 | 前台客服管道隔離 | 1. A 站啟用 Line 客服 2. B 站未啟用 Line 客服 | 1. A 站顯示 Line 客服入口 2. B 站不顯示 Line 客服 3. 各管道獨立配置 | P2 |
第 7 章:安全性測試
測試目標:驗證系統在認證、授權、資料保護、輸入驗證等方面的安全性,確保平台不受常見攻擊向量的威脅。 前置條件:系統已部署完成,具備有效的測試帳號(前台用戶、後台管理員各權限等級)。 涵蓋範圍:JWT 認證、RBAC 權限、2FA 雙因素驗證、輸入注入防護、密碼安全、資料存取控制、API 安全。
7.1 JWT 認證測試
測試範圍:前台用戶 JWT Bearer Token(7 天過期)與後台管理員 AdminJWT(獨立策略,payload 含
role: 'admin')。 相關元件:JwtAuthGuard、AdminJwtAuthGuard、OptionalJwtAuthGuard。
7.1.1 前台 JWT 認證
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-JWT-001 | 有效 JWT 存取受保護端點 | 1. 使用帳密登入取得 JWT Token 2. 在 Authorization header 帶入 Bearer {token} 3. 呼叫 GET /api/auth/profile | 1. HTTP 200 2. 回傳用戶個人資料 3. code: 200 | P1 |
| TC-SEC-JWT-002 | 過期 JWT 存取端點 | 1. 取得有效 JWT Token 2. 模擬 Token 過期(修改 exp 時間戳或等待 7 天) 3. 使用過期 Token 呼叫 API | 1. HTTP 401 2. code: 401 3. message: "Unauthorized" 4. 前端收到 401 後觸發 token 重取流程 | P1 |
| TC-SEC-JWT-003 | 格式錯誤的 JWT | 1. 構造一個格式錯誤的 JWT(如 Bearer abc.def.ghi) 2. 使用該 Token 呼叫受保護端點 | 1. HTTP 401 2. Token 解析失敗 3. 不回傳任何敏感錯誤訊息 | P1 |
| TC-SEC-JWT-004 | 缺少 Authorization Header | 1. 不帶任何 Authorization header 2. 呼叫 GET /api/auth/profile | 1. HTTP 401 2. message: "Unauthorized" 3. 明確指示需要認證 | P1 |
| TC-SEC-JWT-005 | 前台 Token 存取後台端點 | 1. 使用前台用戶登入取得 JWT Token 2. 使用該 Token 呼叫 GET /api/admin/list | 1. HTTP 401 2. AdminJwtAuthGuard 拒絕前台 Token 3. 前後台認證策略完全隔離 4. payload 不含 role: 'admin' | P1 |
| TC-SEC-JWT-006 | 後台 Token 存取前台端點 | 1. 使用後台管理員登入取得 AdminJWT Token 2. 使用該 Token 呼叫 GET /api/auth/profile | 1. HTTP 401 2. JwtAuthGuard 拒絕 Admin Token 3. 兩套策略互不通用 | P1 |
| TC-SEC-JWT-007 | tokenVersion 不匹配強制重認證 | 1. 用戶登入取得 JWT(含 tokenVersion) 2. 後台管理員修改該用戶資料(觸發 tokenVersion 遞增) 3. 用戶使用原 Token 呼叫 API | 1. tokenVersion 檢查失敗 2. Redis 快取 cache:auth:tv:{userId} 中的版本不匹配 3. 強制用戶重新登入 | P1 |
| TC-SEC-JWT-008 | 修改密碼後舊 Token 失效 | 1. 用戶登入取得 JWT Token 2. 用戶修改密碼 3. 使用修改前的 Token 呼叫 API | 1. 密碼修改觸發 tokenVersion 遞增 2. 舊 Token 失效 3. 需重新登入取得新 Token | P1 |
| TC-SEC-JWT-009 | 登出後 Token 失效 | 1. 用戶登入取得 JWT Token 2. 呼叫登出 API 3. 使用登出前的 Token 呼叫受保護端點 | 1. 登出操作使 Token 失效(tokenVersion 或黑名單) 2. 舊 Token 無法使用 3. 返回 401 | P1 |
| TC-SEC-JWT-010 | 相同帳號多裝置並發登入 | 1. 在瀏覽器 A 登入帳號 X 2. 在瀏覽器 B 登入帳號 X 3. 兩邊同時呼叫 API | 1. 兩邊都能正常使用(系統允許多裝置登入) 2. 各自 Token 獨立有效 3. 裝置指紋記錄分別儲存 | P2 |
7.1.2 後台 AdminJWT 認證
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-ADMJWT-001 | 管理員有效 Token 存取 | 1. 後台管理員登入取得 AdminJWT 2. 呼叫 GET /api/admin/list | 1. HTTP 200 2. 回傳管理員列表 3. Token payload 含 role: 'admin' | P1 |
| TC-SEC-ADMJWT-002 | 管理員 Token 過期 | 1. 取得 AdminJWT Token 2. 模擬 Token 過期 3. 呼叫後台 API | 1. HTTP 401 2. 前端 NextAuth 觸發 session 刷新 3. 若刷新失敗導向登入頁 | P1 |
| TC-SEC-ADMJWT-003 | 管理員 tokenVersion 失效 | 1. 管理員 A 登入取得 Token 2. Root 管理員修改管理員 A 的權限 3. 管理員 A 使用原 Token 呼叫 API | 1. tokenVersion 遞增 2. Redis cache:admin:tv:{adminId} 檢查失敗 3. 強制管理員 A 重新登入 | P1 |
| TC-SEC-ADMJWT-004 | 管理員帳號停用後 Token 失效 | 1. 管理員正常登入 2. Root 停用該管理員帳號 3. 被停用的管理員呼叫 API | 1. Token 驗證時檢查帳號狀態 2. 已停用帳號拒絕存取 3. 返回 401 | P1 |
| TC-SEC-ADMJWT-005 | NextAuth Session 與 JWT 同步 | 1. 管理員登入(NextAuth 建立 session) 2. 檢查 apiClient 的 Authorization header 3. 呼叫後台 API | 1. NextAuth JWT 策略正確產生 session 2. apiClient 從 SessionSync 快取取得 token 3. Bearer token 正確傳遞 | P2 |
| TC-SEC-ADMJWT-006 | 401 自動重試機制 | 1. 管理員 Token 因某原因失效 2. 前端呼叫 API 收到 401 3. 觀察 apiClient 的行為 | 1. apiClient interceptor 捕獲 401 2. 清除舊 token → 重取 session → retry 3. 若 retry 仍失敗,導向登入頁 4. 不會無限重試 | P2 |
7.1.3 OptionalJwtAuthGuard 測試
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-OPTJWT-001 | 帶 Token 的可選認證 | 1. 使用有效 Token 呼叫支援可選認證的端點(如遊戲列表) 2. 檢查回應 | 1. HTTP 200 2. 回傳含用戶相關資訊(如最近遊玩記錄) 3. 請求內可取得用戶身份 | P2 |
| TC-SEC-OPTJWT-002 | 不帶 Token 的可選認證 | 1. 不帶 Authorization header 呼叫相同端點 2. 檢查回應 | 1. HTTP 200(不返回 401) 2. 回傳公開資料(無用戶專屬內容) 3. 用戶身份為 null | P2 |
| TC-SEC-OPTJWT-003 | 帶無效 Token 的可選認證 | 1. 使用無效 Token 呼叫可選認證端點 | 1. HTTP 200(不返回 401) 2. 視為未登入 3. 回傳公開資料 | P3 |
7.2 權限(RBAC)測試
測試範圍:16 個權限模組 × 2 個操作(read/write)= 32 種權限。 權限模組:admin, admin-group, admin-log, user, deposit, withdrawal, promo, promo-tag, affiliate, vip, game, risk, report, vendor, finance, site-config。 群組類型:root(全權限)、super_admin(除 site-config)、general_admin(唯讀)、custom(自訂)。 相關元件:
PermissionsGuard、@RequirePermissions()、usePermissionshook。
7.2.1 群組類型權限測試
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-RBAC-001 | root 群組全權限存取 | 1. 以 root 群組管理員登入 2. 逐一存取所有 32 種權限對應的 API 端點 3. 包含所有 16 模組的 read 和 write | 1. 全部 32 個權限檢查通過 2. 包含 site-config:read 和 site-config:write 3. isRoot() 返回 true 4. 所有 API 返回 200 | P1 |
| TC-SEC-RBAC-002 | super_admin 權限範圍 | 1. 以 super_admin 群組管理員登入 2. 存取 site-config 相關端點 3. 存取其他 15 個模組的 read/write 端點 | 1. site-config 相關操作被拒絕(403) 2. 其他 15 個模組的 30 個權限全部通過 3. isRoot() 返回 false 4. can('site-config:read') 返回 false | P1 |
| TC-SEC-RBAC-003 | general_admin 唯讀權限 | 1. 以 general_admin 群組管理員登入 2. 嘗試讀取各模組資料 3. 嘗試寫入(新增/修改/刪除)操作 | 1. 所有 read 操作通過 2. 所有 write 操作被拒絕(403) 3. canRead('*') 返回 true 4. canWrite('*') 返回 false | P1 |
| TC-SEC-RBAC-004 | custom 群組自訂權限 | 1. 建立 custom 群組,僅勾選 user:read、deposit:read、deposit:write 2. 以該群組管理員登入 3. 測試各操作 | 1. user:read → 通過 2. user:write → 拒絕 3. deposit:read → 通過 4. deposit:write → 通過 5. 其他模組全部拒絕 | P1 |
| TC-SEC-RBAC-005 | 無 write 權限嘗試寫入 | 1. 以只有 promo:read 權限的管理員登入 2. 嘗試 POST /api/admin/promos/create | 1. HTTP 403 Forbidden 2. PermissionsGuard 攔截 3. @RequirePermissions('promo:write') 檢查失敗 | P1 |
| TC-SEC-RBAC-006 | 無 read 權限嘗試讀取 | 1. 以沒有 report:read 權限的管理員登入 2. 嘗試 GET /api/admin/reports/overview | 1. HTTP 403 Forbidden 2. 無法取得報表資料 3. 權限檢查在 Controller 層級觸發 | P1 |
| TC-SEC-RBAC-007 | PermissionsGuard 元資料檢查 | 1. 對一個需要 game:write 權限的端點 2. 分別用有/無該權限的帳號存取 3. 檢查 Guard 行為 | 1. Guard 讀取 @RequirePermissions('game:write') metadata 2. 與管理員群組權限比對 3. 有權限 → 放行 4. 無權限 → 403 | P1 |
| TC-SEC-RBAC-008 | 群組權限快取失效測試 | 1. 管理員 A 登入(群組權限被快取至 Redis,60s TTL) 2. Root 修改管理員 A 所屬群組的權限 3. 管理員 A 在快取有效期內呼叫 API 4. 等待 60 秒後再次呼叫 | 1. 快取有效期內:使用舊權限(可能仍能存取) 2. 快取過期後:使用新權限 3. Redis key: cache:admin-group:perms:{groupId} 4. TTL 60 秒 | P2 |
7.2.2 前端權限控制測試
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-RBAC-009 | Sidebar 選單依權限隱藏 | 1. 以 custom 群組管理員登入(僅有 user、deposit 權限) 2. 檢查側邊欄選單項目 | 1. 僅顯示「玩家管理」和「財務管理 > 存款」相關項目 2. 遊戲管理、VIP、代理等選單隱藏 3. sidebar 檢查 Feature Flag + RBAC | P1 |
| TC-SEC-RBAC-010 | 直接 URL 存取無權限頁面 | 1. 以無 game 權限的管理員登入 2. 在瀏覽器直接輸入 /game/providers URL | 1. 頁面顯示 AccessDenied 元件 2. 不顯示任何資料 3. 提示無存取權限 | P1 |
| TC-SEC-RBAC-011 | usePermissions Hook 行為 | 1. 在 React DevTools 檢查 usePermissions 返回值 2. 驗證各方法 | 1. can('admin:read') 正確返回 boolean 2. canRead('admin') 等同 can('admin:read') 3. canWrite('admin') 等同 can('admin:write') 4. isRoot() 正確判斷 groupType | P2 |
7.2.3 各模組權限逐一驗證
| TC-ID | 模組 | read 端點 | write 端點 | 預期行為 |
|---|---|---|---|---|
| TC-SEC-PERM-001 | admin | GET /admin/list | POST /admin/register | 管理員列表讀取 / 新增管理員 |
| TC-SEC-PERM-002 | admin-group | GET /admin/groups/list | POST /admin/groups/create | 群組列表讀取 / 新增群組 |
| TC-SEC-PERM-003 | admin-log | GET /admin/logs/list | N/A(僅 read) | 操作紀錄讀取 |
| TC-SEC-PERM-004 | user | GET /admin/finance/users/list | PATCH /admin/finance/users/:id | 用戶列表讀取 / 編輯用戶 |
| TC-SEC-PERM-005 | deposit | GET /admin/finance/deposit-review/list | POST /admin/finance/deposit-review/:id/review | 存款列表讀取 / 審核存款 |
| TC-SEC-PERM-006 | withdrawal | GET /admin/finance/withdrawals/list | POST /admin/finance/withdrawals/:id/review | 提款列表讀取 / 審核提款 |
| TC-SEC-PERM-007 | promo | GET /admin/promos/list | POST /admin/promos/create | 活動列表讀取 / 新增活動 |
| TC-SEC-PERM-008 | promo-tag | GET /admin/promo-tags/list | POST /admin/promo-tags/create | 活動標籤讀取 / 新增標籤 |
| TC-SEC-PERM-009 | affiliate | GET /affiliate/admin/agents | POST /affiliate/admin/create-agent | 代理列表讀取 / 新增代理 |
| TC-SEC-PERM-010 | vip | GET /vip/admin/levels | POST /vip/admin/levels | VIP 等級讀取 / 新增等級 |
| TC-SEC-PERM-011 | game | GET /game/admin/providers | POST /game/admin/providers | 遊戲供應商讀取 / 新增供應商 |
| TC-SEC-PERM-012 | risk | GET /admin/risk/ip-rules/list | POST /admin/risk/ip-rules/create | IP 規則讀取 / 新增規則 |
| TC-SEC-PERM-013 | report | GET /admin/reports/overview | N/A(僅 read + export) | 報表讀取 / 匯出 |
| TC-SEC-PERM-014 | vendor | GET /admin/vendor-groups/list | POST /admin/vendor-groups/create | 金流群組讀取 / 新增群組 |
| TC-SEC-PERM-015 | finance | GET /admin/finance/bank-cards/list | POST /admin/finance/adjust-balance | 銀行卡讀取 / 手動調帳 |
| TC-SEC-PERM-016 | site-config | GET /site-config/admin/list | POST /site-config/admin | 站點列表讀取 / 新增站點 |
7.3 2FA(Google Authenticator)測試
測試範圍:後台管理員的 TOTP 雙因素認證(Google Authenticator)。 相關元件:
google-auth-dialog.tsx(狀態:idle → qr → verify)、後端speakeasy套件。 流程:啟用 → QR Code + Secret → 掃描 → 輸入 6 位驗證碼確認;停用 → 輸入當前 TOTP 驗證碼。
7.3.1 啟用 2FA 流程
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-2FA-001 | 請求啟用 2FA 產生 QR Code | 1. 管理員登入後台 2. 進入個人資料頁 3. 點擊「啟用 Google Authenticator」 4. 檢查對話框狀態 | 1. 對話框從 idle 進入 qr 狀態 2. 顯示 QR Code 圖片 3. 顯示 Secret Key(供手動輸入) 4. QR Code 可被 Google Authenticator App 掃描 5. Secret 為 base32 編碼 | P1 |
| TC-SEC-2FA-002 | 輸入正確 TOTP 碼啟用 | 1. 掃描 QR Code 取得 TOTP 2. 在對話框輸入當前 6 位驗證碼 3. 點擊確認 | 1. 對話框從 qr 進入 verify 狀態 2. 驗證通過 3. 2FA 啟用成功 4. 個人資料頁顯示 2FA 已啟用 5. 後端儲存加密後的 Secret | P1 |
| TC-SEC-2FA-003 | 輸入錯誤 TOTP 碼 | 1. 掃描 QR Code 2. 輸入錯誤的 6 位數字(如 000000) 3. 點擊確認 | 1. 驗證失敗 2. 顯示錯誤提示 3. 2FA 未啟用 4. 可重新輸入 5. Secret 未被儲存 | P1 |
| TC-SEC-2FA-004 | 輸入非數字或位數不足 | 1. 在驗證碼欄位輸入字母或 5 位數字 2. 嘗試提交 | 1. 前端 Zod 驗證攔截 2. 顯示格式錯誤提示 3. 不發送 API 請求 | P2 |
| TC-SEC-2FA-005 | 已啟用 2FA 再次嘗試啟用 | 1. 管理員已啟用 2FA 2. 再次點擊啟用按鈕 | 1. 系統提示已啟用 2. 或不顯示啟用按鈕(改為停用按鈕) | P3 |
7.3.2 使用 2FA 登入
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-2FA-006 | 啟用 2FA 後登入需要驗證碼 | 1. 管理員已啟用 2FA 2. 輸入帳號密碼點擊登入 3. 檢查登入流程 | 1. 帳密驗證通過後 2. 顯示 TOTP 驗證碼輸入欄位 3. 不直接發放 JWT Token 4. 需完成兩步驟才能登入 | P1 |
| TC-SEC-2FA-007 | 輸入正確 TOTP 碼登入 | 1. 帳密驗證通過進入 2FA 步驟 2. 從 Google Authenticator App 取得當前驗證碼 3. 輸入驗證碼提交 | 1. TOTP 驗證通過 2. 發放 AdminJWT Token 3. 成功登入後台 4. 導向 Dashboard 頁面 | P1 |
| TC-SEC-2FA-008 | 輸入錯誤 TOTP 碼登入 | 1. 帳密驗證通過進入 2FA 步驟 2. 輸入錯誤驗證碼 | 1. 登入失敗 2. 顯示驗證碼錯誤 3. 不發放 Token 4. 可重試 5. 記錄登入失敗紀錄 | P1 |
| TC-SEC-2FA-009 | 時間窗口容差測試 | 1. 使用剛過期的 TOTP 碼(前一個 30 秒週期) 2. 嘗試登入 | 1. speakeasy 預設允許 ±1 個時間窗口(30 秒容差) 2. 剛過期的碼可能仍然有效 3. 超過容差範圍的碼無效 | P2 |
| TC-SEC-2FA-010 | 未啟用 2FA 的管理員登入 | 1. 管理員未啟用 2FA 2. 輸入帳密登入 | 1. 正常帳密驗證後直接發放 Token 2. 不顯示 TOTP 步驟 3. 直接導向 Dashboard | P1 |
7.3.3 停用 2FA
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-2FA-011 | 輸入正確 TOTP 停用 2FA | 1. 管理員已啟用 2FA 2. 進入個人資料頁點擊「停用 2FA」 3. 輸入當前 TOTP 驗證碼 4. 確認停用 | 1. 驗證碼正確 2. 2FA 成功停用 3. 後端清除 Secret 4. 後續登入不再要求 TOTP | P1 |
| TC-SEC-2FA-012 | 輸入錯誤 TOTP 嘗試停用 | 1. 管理員已啟用 2FA 2. 點擊停用 3. 輸入錯誤驗證碼 | 1. 驗證失敗 2. 2FA 仍然啟用 3. 防止未授權停用 | P1 |
| TC-SEC-2FA-013 | 停用後重新啟用 | 1. 管理員停用 2FA 2. 重新啟用 2FA 3. 掃描新 QR Code | 1. 產生全新的 Secret 2. 舊 QR Code/Secret 失效 3. 需重新在 App 中設定 | P2 |
7.4 輸入驗證與注入測試
測試範圍:SQL 注入、XSS 跨站腳本、路徑遍歷、命令注入、CSRF、檔案上傳安全、請求大小限制。 核心防護:TypeORM 參數化查詢、Zod 前端驗證、class-validator 後端驗證、Helmet 安全標頭。
7.4.1 SQL 注入防護
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-INJ-001 | 搜尋欄位 SQL 注入 | 1. 在玩家搜尋欄位輸入 ' OR 1=1 -- 2. 點擊搜尋 3. 檢查 API 請求和回應 | 1. TypeORM 使用參數化查詢(:keyword) 2. 輸入被視為普通字串 3. 不回傳所有資料 4. 回傳空結果或僅匹配的資料 | P1 |
| TC-SEC-INJ-002 | 登入表單 SQL 注入 | 1. 在登入帳號欄位輸入 admin' OR '1'='1 2. 密碼隨意輸入 3. 嘗試登入 | 1. 登入失敗 2. TypeORM 參數化查詢防護 3. 不回傳 JWT Token 4. 記錄登入失敗紀錄 | P1 |
| TC-SEC-INJ-003 | URL 參數 SQL 注入 | 1. 在 URL 參數中注入 SQL(如 ?userId=1;DROP TABLE auth-user) 2. 發送 API 請求 | 1. 參數經過 class-validator 驗證 2. 非法格式被拒絕 3. 資料庫不受影響 4. 返回 400 Bad Request | P1 |
| TC-SEC-INJ-004 | JSON body SQL 注入 | 1. 在 POST/PATCH 請求的 JSON body 中注入 SQL 語句 2. 如 { "username": "admin'; DROP TABLE--" } | 1. DTO class-validator 驗證字串格式 2. TypeORM 參數化查詢 3. 不執行注入的 SQL | P1 |
| TC-SEC-INJ-005 | 排序欄位 SQL 注入 | 1. 在排序參數中注入 SQL(如 ?sortBy=id;DELETE FROM users) 2. 發送列表 API 請求 | 1. 排序欄位經過白名單驗證 2. 非允許的欄位被忽略或拒絕 3. 使用預設排序 | P2 |
| TC-SEC-INJ-006 | LIKE 搜尋特殊字元 | 1. 在搜尋欄位輸入 %、_、\ 等 SQL LIKE 特殊字元 2. 檢查搜尋結果 | 1. 特殊字元被正確轉義 2. 搜尋 % 不會匹配所有記錄 3. 精確搜尋包含這些字元的資料 | P2 |
7.4.2 XSS 跨站腳本防護
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-XSS-001 | 文字輸入欄位 XSS | 1. 在任何文字輸入欄位輸入 <script>alert('XSS')</script> 2. 提交表單 3. 在列表頁查看該資料 | 1. 腳本不被執行 2. React/Vue 預設轉義 HTML 3. 顯示為純文字 <script>alert('XSS')</script> 4. 無彈窗出現 | P1 |
| TC-SEC-XSS-002 | i18n 多語系欄位 XSS | 1. 在多語系 JSON 欄位中注入 {"zh-TW": "<img src=x onerror=alert(1)>"} 2. 儲存並在前台顯示 | 1. resolveText() 解析後的文字經過轉義 2. 不執行 onerror 事件 3. 前端框架自動轉義 HTML | P1 |
| TC-SEC-XSS-003 | 富文本編輯器(Tiptap)XSS | 1. 在 Tiptap 編輯器中透過 HTML 模式輸入 <script>alert('XSS')</script> 2. 儲存活動內容 3. 在前台查看 | 1. Tiptap 內建 sanitizer 過濾危險標籤 2. <script> 標籤被移除 3. 只保留允許的 HTML 標籤(p, h1-h6, strong, em, ul, ol, li, a, img 等) 4. 安全的 HTML 輸出 | P1 |
| TC-SEC-XSS-004 | URL 參數 XSS(反射型) | 1. 在 URL 中注入 ?search=<script>alert(1)</script> 2. 頁面載入 | 1. 搜尋參數不直接渲染為 HTML 2. 經過 React/Vue 轉義 3. 無腳本執行 | P1 |
| TC-SEC-XSS-005 | 站內信內容 XSS | 1. 管理員發送含惡意腳本的站內信 2. 前台用戶查看信件 | 1. 信件內容經過 sanitize 2. 惡意腳本被移除或轉義 3. 安全顯示信件內容 | P1 |
| TC-SEC-XSS-006 | HTTP Response Header 安全 | 1. 檢查 API 回應的 HTTP Headers | 1. 包含 X-Content-Type-Options: nosniff 2. 包含 X-Frame-Options: DENY 或 SAMEORIGIN 3. 包含 X-XSS-Protection: 1; mode=block(如適用) 4. Content-Type 正確設定 | P2 |
7.4.3 路徑遍歷與命令注入
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-PATH-001 | 檔案上傳路徑遍歷 | 1. 上傳檔案時修改 filename 為 ../../etc/passwd 2. 或使用 ..%2F..%2F 編碼形式 3. 發送上傳請求 | 1. 檔案名稱經過清理(sanitize) 2. 路徑遍歷字元被移除 3. 檔案儲存至 R2 指定路徑 4. 不會存取伺服器本地檔案系統 | P1 |
| TC-SEC-PATH-002 | R2 路徑操作遍歷 | 1. 在 R2 create-folder API 中使用 ../../ 路徑 2. 在 R2 delete API 中使用遍歷路徑 | 1. R2 模組驗證路徑合法性 2. 不允許遍歷到其他 bucket/prefix 3. 路徑限定在站點目錄下 | P1 |
| TC-SEC-CMD-001 | 搜尋參數命令注入 | 1. 在搜尋欄位輸入 ; rm -rf / 或 ` | cat /etc/passwd` 2. 發送 API 請求 | 1. 後端不執行 shell 命令 2. 所有查詢透過 ORM 3. 輸入被視為普通字串 |
| TC-SEC-CMD-002 | 檔案名稱命令注入 | 1. 上傳檔案名稱含 $(whoami).jpg 2. 或 `whoami`.jpg | 1. 檔案名稱被清理 2. 特殊字元被移除或轉義 3. R2 上傳使用安全的 key 生成 | P2 |
7.4.4 CSRF 與請求安全
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-CSRF-001 | CSRF 防護驗證 | 1. 從外部網站構造一個 POST 請求到後台 API 2. 不帶 JWT Token 3. 發送請求 | 1. 請求被拒絕(缺少 Authorization) 2. JWT Token 機制本身防護 CSRF(Token 在 header 中,非 cookie 自動攜帶) | P1 |
| TC-SEC-CSRF-002 | Cookie 安全屬性 | 1. 檢查 NextAuth session cookie 的屬性 2. 檢查 locale cookie 屬性 | 1. Session cookie 設定 HttpOnly 2. Session cookie 設定 Secure(生產環境) 3. SameSite 設定為 Lax 或 Strict | P2 |
| TC-SEC-REQ-001 | 超大請求 body 拒絕 | 1. 構造一個超過限制大小的 JSON body(如 50MB) 2. 發送 POST 請求 | 1. NestJS 的 body-parser 限制生效 2. 返回 413 Payload Too Large 3. 伺服器不因大請求而崩潰 | P1 |
| TC-SEC-REQ-002 | 上傳檔案大小限制 | 1. 嘗試上傳超過限制的檔案(R2 上傳) 2. 檢查回應 | 1. 檔案大小限制生效 2. 返回錯誤提示 3. 不佔用伺服器過多記憶體 | P1 |
| TC-SEC-REQ-003 | Content-Type 驗證 | 1. 發送 JSON API 但使用 Content-Type: text/plain 2. 檢查回應 | 1. NestJS 驗證 Content-Type 2. 非預期格式被拒絕或正確處理 | P3 |
7.4.5 檔案上傳安全
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-FILE-001 | 偽裝 Content-Type 上傳 | 1. 將 .exe 檔案的 Content-Type 改為 image/png 2. 嘗試上傳 | 1. 後端驗證實際檔案內容(magic bytes) 2. 或白名單驗證副檔名 3. 拒絕不合法的檔案類型 | P1 |
| TC-SEC-FILE-002 | 上傳含惡意 metadata 的圖片 | 1. 製作含 XSS payload 的 EXIF 資料的圖片 2. 上傳至 R2 | 1. 圖片 metadata 不被解析為 HTML 2. 直接以 image/* MIME 回傳 3. 不會觸發腳本執行 | P2 |
| TC-SEC-FILE-003 | 空檔案上傳 | 1. 上傳一個 0 bytes 的檔案 | 1. 前端或後端驗證檔案非空 2. 返回驗證錯誤 | P3 |
| TC-SEC-FILE-004 | 超長檔案名稱 | 1. 使用 300 字元以上的檔案名稱上傳 | 1. 檔案名稱被截斷或拒絕 2. 不導致系統錯誤 | P3 |
7.5 密碼安全測試
測試範圍:密碼儲存方式、密碼強度要求、密碼變更流程、OAuth 用戶密碼設定。
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-PWD-001 | 密碼以 bcrypt 雜湊儲存 | 1. 建立一個測試帳號 2. 查詢資料庫中的密碼欄位 3. 檢查儲存格式 | 1. 密碼不以明文儲存 2. 使用 bcrypt 雜湊($2b$ 開頭) 3. 包含 salt(bcrypt 內建) 4. 無法反推原始密碼 | P1 |
| TC-SEC-PWD-002 | 密碼最低強度要求 | 1. 嘗試註冊密碼為 123 2. 嘗試密碼為 abcdef 3. 嘗試密碼為 Abc123!@# | 1. 過短密碼被拒絕 2. 不符合複雜度要求的密碼被拒絕 3. 符合要求的密碼通過 4. Zod schema 驗證生效 | P1 |
| TC-SEC-PWD-003 | 修改密碼需驗證當前密碼 | 1. 登入帳號 2. 進入修改密碼頁面 3. 不輸入當前密碼直接設定新密碼 4. 輸入錯誤的當前密碼 5. 輸入正確的當前密碼 | 1. 不輸入當前密碼 → 驗證失敗 2. 錯誤的當前密碼 → 拒絕修改 3. 正確的當前密碼 → 允許修改 4. 修改成功後舊 Token 失效 | P1 |
| TC-SEC-PWD-004 | OAuth 用戶設定密碼 | 1. 使用 Google OAuth 註冊的用戶 2. 進入設定密碼頁面 3. 直接設定新密碼(不需要當前密碼) | 1. OAuth 用戶首次設定密碼不需要舊密碼 2. 設定完成後可使用帳密登入 3. 仍可使用 OAuth 登入 | P2 |
| TC-SEC-PWD-005 | 登入失敗紀錄記錄 | 1. 使用錯誤密碼嘗試登入 3 次 2. 查詢 auth-user-login-log 表 | 1. 每次失敗都記錄一筆紀錄 2. 包含 IP 地址、時間戳、帳號 3. 後台可查詢登入失敗紀錄 4. 支援按日期和關鍵字篩選 | P1 |
| TC-SEC-PWD-006 | 密碼不在 API 回應中洩漏 | 1. 呼叫用戶列表 API 2. 呼叫用戶詳情 API 3. 檢查所有回應欄位 | 1. 任何 API 回應都不包含密碼欄位 2. 即使是 hash 過的密碼也不回傳 3. TypeORM select 排除 password 欄位 | P1 |
| TC-SEC-PWD-007 | 管理員密碼安全 | 1. 建立管理員帳號 2. 查詢管理員資料 API 3. 檢查密碼欄位 | 1. 管理員密碼同樣以 bcrypt 儲存 2. API 不回傳密碼 3. 管理員修改密碼需驗證舊密碼 | P1 |
| TC-SEC-PWD-008 | 密碼與帳號不可相同 | 1. 嘗試設定密碼與帳號相同 | 1. 驗證拒絕 2. 提示密碼不可與帳號相同(若有此規則) | P3 |
7.6 資料存取控制測試
測試範圍:確保用戶只能存取自己的資料,代理只能看到自己的下線,前台和後台完全隔離。
7.6.1 前台用戶資料隔離
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-ACCESS-001 | 用戶 A 無法存取用戶 B 的錢包 | 1. 用戶 A 登入取得 JWT 2. 嘗試呼叫用戶 B 的銀行卡/加密地址 API 3. 或修改請求中的 userId | 1. 後端根據 JWT payload 的 userId 查詢 2. 無法取得他人資料 3. 返回空結果或 403 | P1 |
| TC-SEC-ACCESS-002 | 用戶 A 無法存取用戶 B 的交易紀錄 | 1. 用戶 A 登入 2. 嘗試查看用戶 B 的存款/提款/投注紀錄 | 1. API 根據 JWT userId 過濾 2. 僅返回用戶 A 自己的交易紀錄 3. 無法透過修改參數取得他人資料 | P1 |
| TC-SEC-ACCESS-003 | 用戶 A 無法修改用戶 B 的資料 | 1. 用戶 A 登入 2. 嘗試修改用戶 B 的個人資料(修改請求中的 ID) | 1. 後端驗證操作者身份 2. 拒絕跨用戶修改 3. 返回 403 或忽略不屬於自己的 ID | P1 |
| TC-SEC-ACCESS-004 | 用戶餘額只能自查 | 1. 用戶 A 登入 2. 查詢餘額 API 3. 嘗試查詢用戶 B 的餘額 | 1. 餘額 API 根據 JWT userId 返回 2. 無法查詢他人餘額 3. 不接受外部傳入 userId 參數 | P1 |
7.6.2 代理資料隔離
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-ACCESS-005 | 代理 A 無法查看代理 B 的下線 | 1. 代理 A 登入 2. 呼叫下線列表 API 3. 嘗試查詢代理 B 的下線 | 1. API 根據代理身份過濾 2. 僅返回代理 A 的下線 3. 無法跨代理查詢 | P1 |
| TC-SEC-ACCESS-006 | 非代理用戶無法存取代理 API | 1. 一般用戶(未成為代理)登入 2. 呼叫代理相關 API(如 /affiliate/dashboard) | 1. 代理身份檢查失敗 2. 返回錯誤(非代理用戶) 3. 無法存取代理功能 | P1 |
| TC-SEC-ACCESS-007 | maskAccount 隱私保護 | 1. 代理查看下線列表 2. 檢查下線帳號顯示 | 1. 帳號透過 maskAccount() 處理 2. 中間字元被遮蔽(如 ab***fg) 3. 保護下線隱私 | P2 |
| TC-SEC-ACCESS-008 | 代理結算只能查看自己的 | 1. 代理 A 登入 2. 查看佣金結算列表 3. 嘗試存取代理 B 的結算紀錄 | 1. 結算紀錄依代理 ID 過濾 2. 僅顯示自己的結算 3. 無法跨代理查詢 | P1 |
7.6.3 前後台隔離
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-ACCESS-009 | 前台用戶無法存取 /admin 端點 | 1. 前台用戶登入取得 JWT 2. 呼叫 GET /api/admin/list 3. 呼叫 GET /api/admin/reports/overview | 1. 所有 /admin/* 端點拒絕前台 Token 2. AdminJwtAuthGuard 攔截 3. 返回 401 | P1 |
| TC-SEC-ACCESS-010 | 後台管理員無法使用前台功能 | 1. 後台管理員取得 AdminJWT 2. 嘗試呼叫 POST /api/deposit(前台存款) 3. 嘗試呼叫 POST /api/game/launch(遊戲啟動) | 1. 前台端點要求 JwtAuthGuard 2. AdminJWT 不被接受 3. 返回 401 | P1 |
| TC-SEC-ACCESS-011 | admin-user 與 auth-user 完全分離 | 1. 確認資料庫中 admin-user 表和 auth-user 表 2. 驗證無 JOIN 或共用 ID 的情況 | 1. 兩張表完全獨立 2. 帳號系統分離 3. 無法透過前台帳號登入後台 4. 無法透過後台帳號使用前台功能 | P1 |
7.7 API 安全測試
測試範圍:CORS 配置、速率限制、回應安全、敏感資訊洩漏防護。
7.7.1 CORS 跨域安全
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-API-001 | CORS 白名單驗證 | 1. 從允許的來源(如 localhost:3010)發送請求 2. 從不允許的來源(如 evil.com)發送請求 3. 檢查 Access-Control-Allow-Origin | 1. 允許的來源 → 請求通過,CORS header 正確 2. 不允許的來源 → 被瀏覽器阻擋 3. 不使用 * 通配符(生產環境) | P1 |
| TC-SEC-API-002 | CORS preflight OPTIONS 請求 | 1. 從跨域來源發送 OPTIONS 請求 2. 檢查回應 header | 1. 返回正確的 Access-Control-Allow-Methods 2. 返回正確的 Access-Control-Allow-Headers(含 Authorization, x-site-code, site-name, locales) 3. Access-Control-Max-Age 設定合理值 | P2 |
| TC-SEC-API-003 | CORS 不允許攜帶 Cookie | 1. 檢查 Access-Control-Allow-Credentials | 1. 根據系統配置正確設定 2. 若使用 JWT header 認證,credentials 可設為 false | P3 |
7.7.2 速率限制
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-RATE-001 | 登入端點速率限制 | 1. 對 POST /api/auth/login 短時間內發送 100 次請求 2. 觀察回應變化 | 1. 前 N 次正常回應 2. 超過限制後返回 429 Too Many Requests 3. 防止暴力破解 | P1 |
| TC-SEC-RATE-002 | 註冊端點速率限制 | 1. 對 POST /api/auth/register 短時間內大量請求 | 1. 速率限制生效 2. 超過後返回 429 3. 防止批量註冊 | P1 |
| TC-SEC-RATE-003 | 管理員登入速率限制 | 1. 對 POST /api/admin/login 短時間內大量請求 | 1. 速率限制生效 2. 記錄登入失敗 3. 防止管理員帳號暴力破解 | P1 |
| TC-SEC-RATE-004 | 一般 API 速率限制 | 1. 對一般列表 API 短時間內發送大量請求(如 1000 次/秒) | 1. 速率限制或負載保護生效 2. 伺服器不因大量請求而崩潰 3. 返回適當的錯誤碼 | P2 |
7.7.3 回應安全與資訊洩漏防護
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-LEAK-001 | 錯誤回應不洩漏內部資訊 | 1. 觸發一個伺服器錯誤(如存取不存在的端點) 2. 檢查錯誤回應內容 | 1. 不包含 stack trace 2. 不包含資料庫連線資訊 3. 不包含檔案路徑 4. 只回傳通用錯誤訊息 | P1 |
| TC-SEC-LEAK-002 | 404 回應不洩漏路由資訊 | 1. 存取不存在的 API 路徑 2. 檢查回應 | 1. 返回標準 404 格式 2. 不列出可用路由 3. 不暴露內部路由結構 | P2 |
| TC-SEC-LEAK-003 | Server header 不洩漏版本 | 1. 檢查 API 回應的 Server header | 1. 不包含 NestJS/Express 版本資訊 2. 或使用 Helmet 移除/覆蓋 Server header | P2 |
| TC-SEC-LEAK-004 | Swagger UI 生產環境關閉 | 1. 在生產環境存取 /api/docs | 1. Swagger UI 在生產環境不可用 2. 返回 404 3. 避免 API 結構洩漏 | P1 |
| TC-SEC-LEAK-005 | 敏感 Header 不暴露 | 1. 檢查回應 headers 2. 確認不含敏感資訊 | 1. 不包含 X-Powered-By(或已被 Helmet 移除) 2. 不包含內部 IP 3. 不包含 debug 資訊 | P2 |
| TC-SEC-API-006 | 裝置指紋安全傳輸 | 1. 前台登入時附帶 FingerprintJS 產生的指紋 2. 檢查傳輸方式 | 1. 指紋透過 HTTPS 加密傳輸 2. 指紋儲存在後端(非前端可讀) 3. 指紋用於風控反查,不回傳給其他用戶 | P2 |
7.7.4 API 端點安全總結
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-API-007 | 所有寫入端點需認證 | 1. 列出所有 POST/PATCH/DELETE 端點 2. 不帶 Token 逐一呼叫 | 1. 所有寫入端點返回 401 2. 無任何未保護的寫入端點 3. 公開端點僅限 GET 查詢 | P1 |
| TC-SEC-API-008 | 管理員操作紀錄完整 | 1. 管理員執行各種操作(新增/修改/刪除) 2. 查詢 admin-operation-log | 1. 所有管理員操作都有紀錄 2. 包含操作者、操作類型、時間、IP 3. 紀錄不可被刪除或修改 | P1 |
| TC-SEC-API-009 | HTTP Method 限制 | 1. 對 GET 端點發送 DELETE 請求 2. 對 POST 端點發送 GET 請求 | 1. 方法不匹配返回 405 Method Not Allowed 2. 或返回 404 Not Found | P3 |
| TC-SEC-API-010 | HTTPS 強制(生產環境) | 1. 在生產環境嘗試 HTTP 存取 | 1. 自動重定向至 HTTPS 2. 或直接拒絕 HTTP 連線 3. HSTS header 設定 | P1 |
7.8 OAuth 安全測試
測試範圍:Google OAuth 與 Telegram OAuth 登入流程的安全性。
7.8.1 Google OAuth 安全
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-OAUTH-001 | Google OAuth 正常流程 | 1. 點擊 Google 登入按鈕 2. 重定向至 Google 授權頁 3. 授權成功回調 | 1. 正確重定向至 Google OAuth URL 2. callback URL 正確 3. 取得 Google profile 並建立/綁定帳號 4. 發放 JWT Token | P1 |
| TC-SEC-OAUTH-002 | OAuth state 參數防 CSRF | 1. 檢查 OAuth URL 中的 state 參數 2. 偽造 state 回調 | 1. OAuth URL 包含隨機 state 參數 2. 回調驗證 state 一致性 3. 偽造的 state 被拒絕 | P1 |
| TC-SEC-OAUTH-003 | OAuth 回調偽造 | 1. 不經過 Google 直接呼叫 callback URL 2. 帶上偽造的 code | 1. 偽造的 code 無法交換 token 2. 回調被拒絕 3. 不建立帳號 | P1 |
| TC-SEC-OAUTH-004 | OAuth Token 不洩漏 | 1. 檢查前端代碼和 URL 2. 驗證 Google access_token 不暴露 | 1. Google access_token 僅在後端使用 2. 前端不儲存 Google token 3. 只使用系統自己的 JWT | P2 |
| TC-SEC-OAUTH-005 | Google 帳號已綁定其他用戶 | 1. 用戶 A 已綁定 Google 帳號 X 2. 用戶 B 嘗試用 Google 帳號 X 登入 | 1. 系統識別 Google 帳號已綁定 2. 自動登入用戶 A 3. 不建立重複帳號 | P1 |
7.8.2 Telegram OAuth 安全
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-OAUTH-006 | Telegram OAuth 正常流程 | 1. 點擊 Telegram 登入 Widget 2. 授權成功 | 1. Telegram 回傳用戶資訊 2. 後端驗證 hash 簽名 3. 建立/綁定帳號 | P1 |
| TC-SEC-OAUTH-007 | Telegram hash 簽名驗證 | 1. 偽造 Telegram 回調資料 2. 修改 hash 值 | 1. 後端計算 HMAC-SHA256 驗證 hash 2. 偽造的 hash 被拒絕 3. 不建立帳號 | P1 |
| TC-SEC-OAUTH-008 | Telegram 資料過期檢查 | 1. 使用超過有效期的 Telegram 授權資料 | 1. auth_date 時間戳檢查 2. 過期的授權被拒絕 | P2 |
7.9 裝置指紋安全測試
測試範圍:FingerprintJS v5 產生的裝置指紋在認證和風控中的安全應用。
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-FP-001 | 指紋產生唯一性 | 1. 在瀏覽器 A 產生指紋 2. 在瀏覽器 B 產生指紋 3. 比較兩個指紋 | 1. 不同瀏覽器/裝置產生不同指紋 2. 相同瀏覽器多次產生相同指紋 3. 指紋不可預測 | P2 |
| TC-SEC-FP-002 | 指紋隨登入記錄 | 1. 用戶登入時帶 fingerprint 2. 查詢 auth-user-login-log | 1. 登入紀錄包含指紋 2. 可用於風控反查 3. 指紋欄位不為空 | P1 |
| TC-SEC-FP-003 | 指紋用於風控反查 | 1. 查詢某指紋的所有關聯帳號 2. 在後台 IP/FP 檢查頁面搜尋 | 1. 回傳使用相同指紋的所有帳號 2. 可識別同一裝置多帳號 3. 支援多站點篩選 | P1 |
| TC-SEC-FP-004 | 指紋不回傳給前端用戶 | 1. 檢查所有前台 API 回應 2. 確認無指紋欄位 | 1. 用戶 API 不回傳其他用戶的指紋 2. 指紋僅用於後台管理和風控 | P2 |
| TC-SEC-FP-005 | 指紋 HTTPS 加密傳輸 | 1. 監聽登入請求 2. 確認指紋傳輸方式 | 1. 指紋透過 HTTPS POST body 傳送 2. 不在 URL 參數中暴露 3. 不在 Cookie 中明文儲存 | P2 |
7.10 S2S 遊戲回調安全測試
測試範圍:遊戲商 Server-to-Server 回調端點的安全性。
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-S2S-001 | BetSolutions 回調驗證 | 1. 使用正確的簽名呼叫 BetSolutions 回調 2. 使用錯誤簽名呼叫 | 1. 正確簽名 → 處理成功 2. 錯誤簽名 → 拒絕處理 3. 驗證機制保護回調端點 | P1 |
| TC-SEC-S2S-002 | RSG 回調 DES 加解密 | 1. 使用正確 DES 密鑰加密回調資料 2. 使用錯誤密鑰加密 | 1. 正確密鑰 → 成功解密並處理 2. 錯誤密鑰 → 解密失敗,拒絕處理 | P1 |
| TC-SEC-S2S-003 | 回調冪等性 | 1. 相同的遊戲交易回調發送兩次 2. 檢查用戶餘額和紀錄 | 1. 第二次回調不重複處理 2. 餘額不重複增減 3. game_transaction 紀錄唯一 | P1 |
| TC-SEC-S2S-004 | 回調端點 IP 白名單 | 1. 從允許的 IP 呼叫回調 2. 從不允許的 IP 呼叫回調 | 1. 允許的 IP → 正常處理 2. 不允許的 IP → 拒絕(若有 IP 白名單) | P2 |
| TC-SEC-S2S-005 | 回調資料完整性 | 1. 發送缺少必要欄位的回調 2. 發送包含額外欄位的回調 | 1. 缺少必要欄位 → 驗證失敗 2. 額外欄位 → 忽略或拒絕 3. 不因不完整資料而崩潰 | P1 |
| TC-SEC-S2S-006 | 回調金額驗證 | 1. 發送負數金額的回調 2. 發送超大金額的回調 | 1. 負數金額 → 拒絕或適當處理 2. 超大金額 → 驗證範圍限制 3. 不因異常金額導致餘額錯誤 | P1 |
7.11 金流安全測試
測試範圍:存款、提款、調帳等金流操作的安全性和一致性。
7.11.1 存款安全
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-FIN-001 | 存款回調簽名驗證 | 1. 使用正確簽名的金流商回調 2. 使用偽造簽名的回調 | 1. 正確簽名 → 更新訂單狀態 + 增加餘額 2. 偽造簽名 → 拒絕處理 3. 不因偽造回調增加餘額 | P1 |
| TC-SEC-FIN-002 | 存款訂單不可重複確認 | 1. 對同一存款訂單發送兩次成功回調 | 1. 第一次回調正常處理 2. 第二次回調因狀態已變更而忽略 3. 餘額只增加一次 | P1 |
| TC-SEC-FIN-003 | 存款金額篡改防護 | 1. 提交存款申請(金額 1000 TWD) 2. 在回調中嘗試修改金額為 10000 TWD | 1. 後端驗證回調金額與訂單金額一致 2. 不一致時拒絕或標記異常 3. 不因金額篡改入帳錯誤 | P1 |
| TC-SEC-FIN-004 | 匯率時間鎖定 | 1. 用戶發起存款(匯率 A) 2. 匯率變動為 B 3. 金流商回調成功 | 1. 使用發起時的匯率 A 計算 USD 2. 不因回調時匯率 B 變動而改變金額 3. 訂單記錄使用的匯率值 | P1 |
7.11.2 提款安全
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-FIN-005 | 提款餘額即時扣減 | 1. 用戶餘額 1000 USD 2. 申請提款 800 USD 3. 立即再申請提款 300 USD | 1. 第一筆提款申請成功,餘額凍結 800 2. 第二筆提款檢查可用餘額(200 USD) 3. 第二筆因餘額不足被拒絕 4. 無超額提款 | P1 |
| TC-SEC-FIN-006 | 提款審核權限 | 1. 非 withdrawal:write 權限的管理員嘗試審核 2. 有權限的管理員審核 | 1. 無權限 → 403 2. 有權限 → 正常審核 3. 審核紀錄記入 admin-operation-log | P1 |
| TC-SEC-FIN-007 | 提款至錯誤地址防護 | 1. 提款至非用戶本人的銀行卡/加密地址 | 1. 後端驗證提款目標屬於該用戶 2. 非本人地址 → 拒絕 3. 防止資金轉移至他人帳戶 | P1 |
7.11.3 調帳安全
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-SEC-FIN-008 | 手動調帳需 finance:write 權限 | 1. 無 finance:write 的管理員嘗試調帳 2. 有權限的管理員調帳 | 1. 無權限 → 403 2. 有權限 → 調帳成功 3. 調帳紀錄完整 | P1 |
| TC-SEC-FIN-009 | 調帳操作紀錄不可刪改 | 1. 管理員執行手動調帳 2. 查詢操作紀錄 3. 嘗試修改紀錄 | 1. 調帳操作自動記錄 2. 含操作者、金額、原因、時間 3. 紀錄不可被修改或刪除 | P1 |
| TC-SEC-FIN-010 | 調帳金額限制 | 1. 嘗試調帳超大金額(如 999999999) 2. 嘗試調帳為負數餘額 | 1. 有金額上限限制 2. 不可將餘額調為負數 3. decimal(18,6) 範圍內 | P2 |
7.12 安全性測試總結矩陣
說明:以下矩陣總結所有安全性測試的涵蓋範圍和狀態。
7.12.1 OWASP Top 10 對應
| OWASP 風險 | 對應測試 | 覆蓋狀態 |
|---|---|---|
| A01:2021 - Broken Access Control | TC-SEC-RBAC-, TC-SEC-ACCESS- | 完整覆蓋 |
| A02:2021 - Cryptographic Failures | TC-SEC-PWD-001, TC-SEC-FIN-001 | 部分覆蓋 |
| A03:2021 - Injection | TC-SEC-INJ-, TC-SEC-XSS- | 完整覆蓋 |
| A04:2021 - Insecure Design | TC-SEC-FIN-, TC-SEC-S2S- | 部分覆蓋 |
| A05:2021 - Security Misconfiguration | TC-SEC-API-, TC-SEC-LEAK- | 完整覆蓋 |
| A06:2021 - Vulnerable Components | 需定期依賴掃描 | 建議加入 CI |
| A07:2021 - Auth Failures | TC-SEC-JWT-, TC-SEC-2FA-, TC-SEC-OAUTH-* | 完整覆蓋 |
| A08:2021 - Software/Data Integrity | TC-SEC-S2S-003, TC-SEC-FIN-002 | 部分覆蓋 |
| A09:2021 - Logging Failures | TC-SEC-API-008, TC-SEC-FIN-009 | 部分覆蓋 |
| A10:2021 - SSRF | TC-SEC-PATH-, TC-SEC-CMD- | 基本覆蓋 |
7.12.2 安全測試案例統計
| 章節 | 測試案例數 | P1 | P2 | P3 |
|---|---|---|---|---|
| 7.1 JWT 認證 | 16 | 12 | 4 | 0 |
| 7.2 RBAC 權限 | 27 | 18 | 6 | 3 |
| 7.3 2FA 測試 | 13 | 8 | 3 | 2 |
| 7.4 輸入驗證 | 20 | 13 | 5 | 2 |
| 7.5 密碼安全 | 8 | 5 | 2 | 1 |
| 7.6 資料存取 | 11 | 9 | 2 | 0 |
| 7.7 API 安全 | 14 | 7 | 5 | 2 |
| 7.8 OAuth 安全 | 8 | 5 | 3 | 0 |
| 7.9 裝置指紋 | 5 | 2 | 3 | 0 |
| 7.10 S2S 回調 | 6 | 5 | 1 | 0 |
| 7.11 金流安全 | 10 | 8 | 2 | 0 |
| 合計 | 138 | 92 | 36 | 10 |
第 8 章:效能測試建議
測試目標:建立系統效能基準線,識別潛在瓶頸,確保在正常和高負載情境下系統表現穩定。 適用工具:Apache JMeter、k6、Artillery、Lighthouse(前端)、MySQL EXPLAIN ANALYZE。 注意事項:效能測試應在獨立環境執行,避免影響開發或生產環境。
8.1 API 回應時間基準
量測方式:每個端點至少測試 100 次,取 95th percentile(P95)作為基準。 前置條件:資料庫含 Seed 資料(5 站點 × 30 用戶 + 相關資料)。
8.1.1 回應時間目標
| 分類 | 目標(P95) | 量測方式 | 說明 |
|---|---|---|---|
| 列表查詢(分頁) | < 200ms | 含 SQL 查詢 + 序列化 | 如 /admin/finance/users/list?page=1&pageSize=20 |
| 單筆記錄查詢 | < 100ms | 含 SQL 查詢 + 序列化 | 如 GET /admin/finance/users/:id |
| 新增/修改操作 | < 300ms | 含驗證 + SQL 寫入 | 如 POST /admin/promos/create |
| 報表產生 | < 1000ms | 含聚合查詢 + 計算 | 如 GET /admin/reports/overview |
| 檔案上傳(R2) | < 2000ms | 含檔案傳輸 + R2 寫入 | 如 POST /admin/r2/upload |
| 遊戲啟動 | < 500ms | 含用戶驗證 + 遊戲商 API | 如 POST /game/launch |
| 登入/認證 | < 200ms | 含密碼驗證 + JWT 產生 | 如 POST /auth/login |
| 批次操作 | < 500ms | 含批次 SQL 寫入 | 如 VIP rebate bulk upsert |
| CSV 匯出 | < 3000ms | 含查詢 + CSV 生成 | 如 GET /admin/reports/export?type=players |
| 站點設定載入 | < 150ms | 含 Redis 快取查詢 | 如 GET /site-config/list |
8.1.2 各模組端點效能基準
| 模組 | 端點範例 | 目標(P95) | 資料量假設 |
|---|---|---|---|
| 認證 | POST /auth/login | < 200ms | bcrypt 比對 |
| 認證 | POST /auth/register | < 300ms | 含 bcrypt hash + DB insert |
| 用戶 | GET /admin/finance/users/list | < 200ms | 10,000 用戶 |
| 存款 | POST /deposit | < 300ms | 含金流商 API 呼叫 |
| 存款 | GET /admin/finance/deposit-review/list | < 200ms | 50,000 訂單 |
| 提款 | GET /admin/finance/withdrawals/list | < 200ms | 30,000 訂單 |
| 遊戲 | GET /game/providers | < 100ms | 20 供應商 |
| 遊戲 | POST /game/launch | < 500ms | 含遊戲商 API |
| VIP | GET /vip/admin/levels | < 100ms | 15 等級 |
| VIP | POST /vip/admin/rebates/bulk | < 500ms | 15 等級 × 8 類型 = 120 筆 |
| 報表 | GET /admin/reports/overview | < 1000ms | 含 SUM/COUNT 聚合 |
| 報表 | GET /admin/reports/profit-loss | < 1000ms | 含分組聚合 |
| 報表 | GET /admin/reports/bet-records | < 200ms | 100,000 投注 |
| 代理 | GET /affiliate/admin/agents | < 200ms | 5,000 代理 |
| 代理 | POST /affiliate/admin/trigger-settlement | < 5000ms | 含大量計算 |
| 風控 | GET /admin/risk/ip-rules/list | < 100ms | 1,000 規則 |
| 站內信 | GET /inbox/list | < 150ms | 10,000 信件 |
| R2 | POST /admin/r2/upload | < 2000ms | 5MB 檔案 |
| R2 | GET /admin/r2/list | < 500ms | 含 R2 API 呼叫 |
8.2 大量資料分頁效能
測試目標:驗證在不同資料量下分頁查詢的效能表現,特別是深度分頁的效能降級。
8.2.1 分頁效能測試情境
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PERF-PAGE-001 | 1,000 筆資料分頁 | 1. 確保表格有 1,000 筆資料 2. 查詢 page=1, pageSize=20 3. 查詢 page=25, pageSize=20 4. 查詢 page=50, pageSize=20 | 1. 所有頁碼回應時間 < 200ms 2. total 正確為 1,000 3. 最後一頁資料正確 4. 分頁元件正確顯示 | P1 |
| TC-PERF-PAGE-002 | 10,000 筆資料含篩選 | 1. 確保表格有 10,000 筆資料 2. 加入日期範圍篩選 3. 加入狀態篩選 4. 分頁查詢 | 1. 篩選後回應時間 < 300ms 2. total 反映篩選後數量 3. 篩選條件有使用索引 4. 組合篩選效能可接受 | P1 |
| TC-PERF-PAGE-003 | 100,000 筆資料深度分頁 | 1. 確保表格有 100,000 筆資料 2. 查詢 page=5000, pageSize=20(OFFSET 99,980) 3. 量測回應時間 | 1. 深度分頁可能效能降級(OFFSET 大時 MySQL 效能下降) 2. 回應時間 < 1000ms(可接受) 3. 若超過,考慮 cursor-based pagination 4. 記錄效能數據供優化參考 | P2 |
| TC-PERF-PAGE-004 | 大量資料排序效能 | 1. 100,000 筆資料 2. 依 createdAt DESC 排序 3. 依非索引欄位排序 | 1. 有索引欄位排序 < 200ms 2. 無索引欄位排序可能較慢 3. 記錄差異供索引優化 | P2 |
| TC-PERF-PAGE-005 | 空結果快速回應 | 1. 使用篩選條件使結果為空 2. 量測回應時間 | 1. 空結果回應時間 < 100ms 2. 正確回傳 { items: [], total: 0 } 3. 不因計算 total 而延遲 | P3 |
8.2.2 分頁工具函式效能
| 項目 | 說明 | 預期行為 |
|---|---|---|
parsePagination(query) | 解析分頁參數 | 1. 預設 page=1, pageSize=20 2. 最大 pageSize 限制(防止一次查全部) 3. 無效參數使用預設值 |
applyDateRange(qb, col, start, end) | 日期範圍篩選 | 1. 使用 BETWEEN 查詢 2. 有使用 createdAt 索引 3. 時區轉換正確(UTC+8) |
QueryBuilder .skip().take() | TypeORM 分頁 | 1. 轉換為 LIMIT ? OFFSET ? 2. OFFSET 大時效能紀錄 |
8.3 Redis 快取效能
測試目標:驗證 Redis 快取的命中率、失效機制、以及快取與資料庫間的一致性。
8.3.1 快取 Key 與 TTL 規格
| Key Pattern | 用途 | TTL | 命中率目標 | 失效觸發 |
|---|---|---|---|---|
cache:auth:tv:{userId} | 前台用戶 tokenVersion | 60s | > 95% | 用戶修改密碼、管理員修改用戶、登出 |
cache:admin:tv:{adminId} | 後台管理員 tokenVersion | 60s | > 95% | 管理員修改密碼、Root 修改管理員、停用帳號 |
cache:admin-group:perms:{groupId} | 群組權限 | 60s | > 90% | Root 修改群組權限 |
cache:vip:levels:{siteCode} | VIP 等級列表 | 3600s | > 95% | 管理員修改 VIP 等級 |
cache:vip:rebates:{siteCode} | VIP 返水規則 | 3600s | > 95% | 管理員修改返水規則 |
cache:common:enums:{locale} | 系統枚舉(錯誤碼等) | persistent | > 99% | 系統重啟 |
cache:live-sports | 即時賽事 | 1800s | > 90% | Cron 每 30 分鐘更新 |
cache:exchange-rate | 匯率 | 3600s | > 95% | 匯率更新 |
8.3.2 快取效能測試
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PERF-CACHE-001 | 快取命中 vs 未命中時間差 | 1. 清除 Redis 快取 2. 第一次呼叫 API(cache miss) 3. 第二次呼叫相同 API(cache hit) 4. 比較回應時間 | 1. Cache miss: 含 DB 查詢時間 2. Cache hit: 顯著快於 miss(通常 < 10ms) 3. 時間差至少 2-5 倍 | P1 |
| TC-PERF-CACHE-002 | 快取失效正確性 | 1. 查詢 VIP 等級(建立快取) 2. 管理員修改 VIP 等級 3. 再次查詢 VIP 等級 | 1. 修改操作後快取被清除 2. 第三步查詢取得最新資料 3. 不會回傳過期的快取資料 | P1 |
| TC-PERF-CACHE-003 | tokenVersion 快取一致性 | 1. 用戶登入(tokenVersion 寫入 Redis) 2. 用戶修改密碼(tokenVersion +1) 3. 使用舊 Token 呼叫 API | 1. Redis 中的 tokenVersion 已更新 2. 舊 Token 的版本不匹配 3. 強制重新認證 | P1 |
| TC-PERF-CACHE-004 | 群組權限快取 60 秒 TTL | 1. 管理員 A 登入,觸發群組權限快取 2. Root 修改群組權限 3. 管理員 A 立即呼叫 API 4. 等 60 秒後再呼叫 | 1. 立即呼叫:可能使用舊權限(快取未過期) 2. 60 秒後:使用新權限 3. TTL 機制正確運作 | P2 |
| TC-PERF-CACHE-005 | Redis 連線中斷容錯 | 1. 模擬 Redis 連線中斷 2. 呼叫需要快取的 API | 1. 系統不崩潰 2. 降級為直接查詢 DB 3. 效能降低但功能正常 4. Redis 恢復後自動重連 | P2 |
| TC-PERF-CACHE-006 | 快取 key 命名空間隔離 | 1. 檢查不同站點的快取 key 2. C9 站和 A1 站的 VIP 快取 | 1. cache:vip:levels:C9 和 cache:vip:levels:A1 分開 2. 修改 C9 站不影響 A1 站的快取 3. 站點間快取完全隔離 | P1 |
8.4 並發測試
測試目標:驗證系統在並發存取下的穩定性和資料一致性。
8.4.1 並發情境測試
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PERF-CONC-001 | 10 並發登入 | 1. 準備 10 個不同帳號 2. 同時發送登入請求 3. 驗證所有回應 | 1. 全部 10 個登入成功 2. 各自取得獨立的 JWT Token 3. 無 Token 混淆 4. 伺服器回應穩定 | P1 |
| TC-PERF-CONC-002 | 100 並發列表查詢 | 1. 100 個已認證的連線 2. 同時發送列表查詢(不同頁碼) 3. 驗證所有回應 | 1. 全部 100 個查詢成功 2. 各自回傳正確的頁碼資料 3. 無資料錯亂 4. 回應時間 P95 < 500ms | P1 |
| TC-PERF-CONC-003 | 10 並發存款訂單 | 1. 10 個不同用戶同時發起存款 2. 各自不同金額 3. 驗證所有訂單和餘額 | 1. 全部 10 個訂單建立成功 2. 無 race condition 3. 無重複入帳(double-credit) 4. 各用戶餘額正確 5. 訂單號唯一 | P1 |
| TC-PERF-CONC-004 | 50 並發報表查詢 | 1. 50 個管理員同時查詢報表 2. 包含不同報表類型 3. 監控伺服器資源 | 1. 全部查詢成功 2. 伺服器 CPU 使用率不超過 80% 3. 記憶體使用穩定 4. 無 OOM 崩潰 | P2 |
| TC-PERF-CONC-005 | 並發結算與提款 | 1. 觸發代理佣金結算 2. 同時有代理發起提款請求 3. 驗證資料一致性 | 1. 結算和提款不衝突 2. 佣金餘額正確 3. 不超額提款 4. 使用資料庫交易(transaction)保護 | P1 |
| TC-PERF-CONC-006 | 並發遊戲回調 | 1. 模擬 10 個遊戲回調同時到達(下注/派彩) 2. 針對同一用戶 | 1. 餘額計算正確 2. 無重複處理回調 3. game_transaction 紀錄完整 4. 使用樂觀鎖或交易保護 | P1 |
| TC-PERF-CONC-007 | 並發活動領取 | 1. 50 個用戶同時領取同一活動 2. 活動有領取上限 | 1. 領取數量不超過上限 2. 無超額領取 3. 各用戶只能領取一次 4. 使用 unique constraint 或交易 | P1 |
| TC-PERF-CONC-008 | 並發 VIP 反水結算 | 1. 每日 00:05 觸發反水結算 2. 同時有大量投注回調 3. 驗證反水計算 | 1. 結算使用截止時間(如 00:00) 2. 00:00 後的投注計入下一日 3. 反水金額正確 4. 無遺漏或重複 | P2 |
8.4.2 並發負載測試建議
| 場景 | 並發數 | 持續時間 | 成功率目標 | 回應時間目標(P95) |
|---|---|---|---|---|
| 正常負載 | 50 users | 5 min | > 99.9% | < 200ms |
| 高峰負載 | 200 users | 10 min | > 99.5% | < 500ms |
| 壓力測試 | 500 users | 15 min | > 99.0% | < 1000ms |
| 極限測試 | 1000 users | 5 min | > 95.0% | < 2000ms |
| 持久測試 | 100 users | 1 hour | > 99.9% | 無記憶體洩漏 |
8.5 資料庫查詢效能
測試目標:確保關鍵查詢有使用索引,避免全表掃描,優化慢查詢。
8.5.1 關鍵索引驗證
| 資料表 | 欄位 | 索引類型 | 用途 | 驗證方式 |
|---|---|---|---|---|
auth-user | siteCode | INDEX | 多站點篩選 | EXPLAIN SELECT * FROM auth_user WHERE siteCode = 'C9' |
auth-user | account | UNIQUE | 帳號查詢/登入 | 登入查詢必須使用此索引 |
auth-user | email | INDEX | Email 查詢 | OAuth 登入使用 |
deposit-order | siteCode, status | COMPOSITE INDEX | 存款審核列表 | 篩選站點 + 狀態 |
deposit-order | userId | INDEX | 用戶存款記錄 | 用戶查詢自己的訂單 |
deposit-order | createdAt | INDEX | 日期範圍篩選 | 報表查詢 |
bet-order | siteCode, createdAt | COMPOSITE INDEX | 投注報表 | 按站點 + 日期查詢 |
bet-order | userId | INDEX | 用戶投注記錄 | 用戶查詢/VIP 計算 |
vip-level | siteCode | INDEX | 各站 VIP 配置 | 站點篩選 |
vip-rebate | siteCode, level, gameType | UNIQUE | 返水規則唯一鍵 | Bulk upsert |
game-provider | siteCode | INDEX | 各站遊戲商 | 站點篩選 |
risk-ip-rule | siteCode, type | INDEX | 風控規則 | 類型 + 站點篩選 |
affiliate-settlement | agentId, status | COMPOSITE INDEX | 代理結算 | 代理查詢自己的結算 |
notification | userId, isRead | COMPOSITE INDEX | 站內信列表 | 用戶查詢未讀信件 |
admin-operation-log | createdAt | INDEX | 操作紀錄 | 日期範圍查詢 |
r2-operation-log | siteCode, createdAt | COMPOSITE INDEX | R2 日誌 | 站點 + 日期查詢 |
8.5.2 慢查詢偵測
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PERF-DB-001 | 開啟 MySQL 慢查詢日誌 | 1. 設定 slow_query_log = 1 2. 設定 long_query_time = 0.5(500ms) 3. 執行完整功能測試 4. 檢查慢查詢日誌 | 1. 記錄所有超過 500ms 的查詢 2. 分析慢查詢的 EXPLAIN 3. 優化缺少索引的查詢 | P1 |
| TC-PERF-DB-002 | 報表聚合查詢 EXPLAIN | 1. 對 overview 報表的聚合查詢執行 EXPLAIN ANALYZE 2. 檢查是否使用索引 | 1. SUM/COUNT 查詢使用適當索引 2. 無全表掃描(除非資料量小) 3. 執行計劃合理 | P1 |
| TC-PERF-DB-003 | 多表 JOIN 查詢 | 1. 對含 JOIN 的查詢執行 EXPLAIN 2. 如用戶列表含 VIP 等級 | 1. JOIN 使用索引 2. 驅動表選擇正確 3. 無 Using temporary / Using filesort(或在可接受範圍) | P2 |
| TC-PERF-DB-004 | JSON 欄位搜尋效能 | 1. 對 JSON 欄位使用 CAST(column AS CHAR) LIKE :keyword 2. 檢查效能 | 1. 效能可能較差(無法使用索引) 2. 在可接受範圍內 3. 若太慢,考慮增加搜尋專用欄位 | P3 |
8.6 前端效能
測試目標:確保前台和後台的載入速度和互動效能符合標準。 量測工具:Lighthouse、Chrome DevTools Performance、Web Vitals。
8.6.1 Core Web Vitals 目標
| 指標 | 說明 | 前台 (c9-ec) 目標 | 後台 (c9-ims) 目標 |
|---|---|---|---|
| FCP (First Contentful Paint) | 首次內容繪製 | < 1.5s | < 2.0s |
| LCP (Largest Contentful Paint) | 最大內容繪製 | < 2.5s | < 3.0s |
| FID (First Input Delay) | 首次輸入延遲 | < 100ms | < 100ms |
| CLS (Cumulative Layout Shift) | 累積版面位移 | < 0.1 | < 0.1 |
| TTI (Time to Interactive) | 可互動時間 | < 3.0s | < 4.0s |
| TTFB (Time to First Byte) | 首位元組時間 | < 500ms | < 500ms |
8.6.2 前端效能測試項目
| TC-ID | 測試說明 | 測試步驟 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-PERF-FE-001 | 前台首頁載入效能 | 1. 使用 Lighthouse 測試前台首頁 2. 量測 FCP / LCP / CLS | 1. Performance 分數 > 80 2. FCP < 1.5s 3. LCP < 2.5s 4. CLS < 0.1 | P1 |
| TC-PERF-FE-002 | 後台 Dashboard 載入效能 | 1. 使用 Lighthouse 測試後台 Dashboard 2. 量測各指標 | 1. Performance 分數 > 70(後台功能較複雜) 2. LCP < 3.0s 3. 所有 API 並行載入 | P1 |
| TC-PERF-FE-003 | Bundle 大小監控 | 1. 執行 yarn build 2. 檢查產出的 bundle 大小 3. 分析最大的 chunk | 1. 主 bundle < 500KB (gzipped) 2. 無不必要的大型依賴 3. 動態 import 正確分割 | P2 |
| TC-PERF-FE-004 | 圖片延遲載入 | 1. 打開含大量圖片的頁面(如遊戲大廳) 2. 觀察網路請求 3. 捲動頁面 | 1. 可視區域外的圖片不預載 2. 捲動時才載入 3. 使用 lazy loading 或 Intersection Observer | P2 |
| TC-PERF-FE-005 | TanStack Query 快取效能 | 1. 載入列表頁 2. 切換到其他頁面 3. 返回列表頁 4. 觀察是否重新載入 | 1. staleTime(5 min)內不重新請求 2. 直接使用快取資料 3. 頁面瞬間顯示 4. 背景 refetch 不阻塞 UI | P2 |
| TC-PERF-FE-006 | 大量資料表格渲染 | 1. 載入含 100 筆資料的表格 2. 量測渲染時間 3. 測試排序和篩選 | 1. 表格渲染 < 100ms 2. 排序操作流暢 3. 無 UI 卡頓 4. 分頁切換順暢 | P2 |
| TC-PERF-FE-007 | SiteSelector 切換效能 | 1. 在 Header 切換站點 2. 量測頁面刷新時間 3. 驗證 AdminContentWrapper remount | 1. 切換觸發 TanStack Query cache 清除 2. AdminContentWrapper key 變更導致 remount 3. 新站點資料載入 < 500ms 4. 無殘留舊資料 | P1 |
| TC-PERF-FE-008 | 路由切換效能 | 1. 在後台不同頁面間快速切換 2. 量測切換時間 | 1. 頁面切換 < 300ms(含 API 載入) 2. 無白屏閃爍 3. loading 狀態正確顯示 4. scrollToTop 正確觸發 | P2 |
| TC-PERF-FE-009 | 多語系切換效能 | 1. 切換語系(如 zh-TW → en-US) 2. 量測頁面更新時間 | 1. 語系切換 < 200ms 2. 所有文字正確更新 3. 無需重新載入頁面 4. Cookie 正確設定 | P3 |
| TC-PERF-FE-010 | 記憶體洩漏檢查 | 1. 長時間使用後台(30 分鐘) 2. 反覆切換頁面/站點 3. 使用 Chrome DevTools Memory 監控 | 1. 記憶體使用量穩定 2. 無持續增長趨勢 3. 切換頁面後舊元件正確釋放 4. 無 detached DOM 累積 | P2 |
8.7 Cron 排程效能
測試目標:驗證排程任務在資料量增長時的執行效能和穩定性。
8.7.1 反水結算效能
| 資料量 | 用戶數 | 投注紀錄數 | 預期執行時間 | 備註 |
|---|---|---|---|---|
| 小型站 | 100 | 1,000 | < 5s | 基本場景 |
| 中型站 | 1,000 | 50,000 | < 30s | 一般營運 |
| 大型站 | 10,000 | 500,000 | < 3min | 高峰營運 |
| 超大站 | 50,000 | 5,000,000 | < 15min | 壓力測試 |
測試步驟:
- 使用對應資料量的 Seed 資料
- 手動觸發反水結算 Cron
- 量測從開始到結束的執行時間
- 監控 MySQL CPU、記憶體使用
- 確認所有用戶反水金額正確
8.7.2 代理週結效能
| 資料量 | 代理數 | 下線總數 | 投注紀錄數 | 預期執行時間 |
|---|---|---|---|---|
| 小型 | 50 | 500 | 5,000 | < 10s |
| 中型 | 500 | 5,000 | 100,000 | < 1min |
| 大型 | 2,000 | 30,000 | 1,000,000 | < 5min |
重點關注:
- 三層代理結構的遞迴計算效能
- 風控檢測查詢效能
- 結算記錄批次寫入效能
8.7.3 月度保級效能
| 用戶數 | VIP 等級分佈 | 預期執行時間 | 備註 |
|---|---|---|---|
| 1,000 | 均勻分佈 | < 10s | 基本場景 |
| 10,000 | 80% 低級 | < 1min | 一般營運 |
| 50,000 | 混合分佈 | < 5min | 壓力測試 |
8.8 效能監控指標
測試目標:定義系統效能監控的關鍵指標和告警門檻。
8.8.1 後端監控指標
| 指標 | 正常範圍 | 告警門檻 | 嚴重門檻 | 量測方式 |
|---|---|---|---|---|
| API 平均回應時間 | < 100ms | > 300ms | > 1000ms | Prometheus + Grafana |
| API P99 回應時間 | < 500ms | > 1000ms | > 3000ms | Prometheus |
| 請求成功率 | > 99.9% | < 99.5% | < 99.0% | HTTP status code |
| 每秒請求數 (RPS) | 50-500 | > 1000 | > 2000 | 負載監控 |
| MySQL 查詢時間 | < 50ms | > 200ms | > 1000ms | Slow query log |
| MySQL 連線數 | < 50 | > 80 | > 100 | max_connections |
| Redis 記憶體使用 | < 50% | > 70% | > 90% | Redis INFO |
| Redis 命中率 | > 95% | < 90% | < 80% | Redis INFO |
| Node.js 記憶體 | < 512MB | > 768MB | > 1024MB | process.memoryUsage() |
| Node.js CPU | < 50% | > 70% | > 90% | OS monitoring |
8.8.2 前端監控指標
| 指標 | 正常範圍 | 告警門檻 | 量測方式 |
|---|---|---|---|
| FCP | < 1.5s | > 2.5s | Web Vitals |
| LCP | < 2.5s | > 4.0s | Web Vitals |
| CLS | < 0.1 | > 0.25 | Web Vitals |
| JS Bundle 大小 (gzip) | < 500KB | > 800KB | Build output |
| API 呼叫數/頁面 | < 10 | > 20 | Network tab |
| DOM Node 數量 | < 1500 | > 3000 | DevTools |
| JS Heap Size | < 100MB | > 200MB | DevTools Memory |
8.9 效能測試報告模板
# 效能測試報告
## 測試環境
- **硬體**: [CPU / RAM / Disk]
- **軟體**: [Node.js / MySQL / Redis version]
- **資料量**: [Seed 資料規模]
- **測試工具**: [k6 / JMeter / Artillery]
## API 回應時間
| 端點 | 方法 | P50 | P95 | P99 | Max | 目標 | 結果 |
|------|------|-----|-----|-----|-----|------|------|
| /auth/login | POST | Xms | Xms | Xms | Xms | <200ms | Pass/Fail |
| /admin/finance/users/list | GET | Xms | Xms | Xms | Xms | <200ms | Pass/Fail |
| ... | ... | ... | ... | ... | ... | ... | ... |
## 並發測試
| 場景 | VUs | 持續 | 成功率 | P95 | 結果 |
|------|-----|------|--------|-----|------|
| 正常負載 | 50 | 5min | X% | Xms | Pass/Fail |
| 高峰負載 | 200 | 10min | X% | Xms | Pass/Fail |
| ... | ... | ... | ... | ... | ... |
## 資料庫查詢
| 查詢 | 資料量 | 執行時間 | 使用索引 | 結果 |
|------|--------|---------|---------|------|
| 用戶列表 | 10K | Xms | Yes/No | Pass/Fail |
| 報表聚合 | 100K | Xms | Yes/No | Pass/Fail |
| ... | ... | ... | ... | ... |
## Cron 排程
| 排程 | 資料量 | 執行時間 | 目標 | 結果 |
|------|--------|---------|------|------|
| 反水結算 | 10K users | Xs | <30s | Pass/Fail |
| 佣金週結 | 500 agents | Xs | <60s | Pass/Fail |
| ... | ... | ... | ... | ... |
## 前端效能
| 頁面 | FCP | LCP | CLS | TTI | Lighthouse Score |
|------|-----|-----|-----|-----|-----------------|
| 前台首頁 | Xs | Xs | X | Xs | X |
| 後台 Dashboard | Xs | Xs | X | Xs | X |
| ... | ... | ... | ... | ... | ... |
## 發現問題
| 問題 | 影響 | 建議 |
|------|------|------|
| ... | ... | ... |
## 結論
[效能是否達標,是否建議發布]8.10 效能優化建議追蹤
| 優化項目 | 當前狀態 | 預期改善 | 優先級 | 備註 |
|---|---|---|---|---|
| 深度分頁改用 cursor-based | 未實施 | 深度分頁效能提升 10x | P2 | page > 1000 時明顯 |
| 報表聚合結果快取 | 未實施 | 重複查詢命中率 > 90% | P2 | TTL 5 min |
| 列表查詢 COUNT 優化 | 未實施 | COUNT 查詢時間減少 50% | P3 | 使用估算或分離計數 |
| JSON 欄位搜尋索引 | 未實施 | 模糊搜尋效能提升 5x | P3 | 新增搜尋專用欄位 |
| 前端圖片 WebP 轉換 | 未實施 | 圖片大小減少 30% | P3 | Nuxt Image 模組 |
| API 回應 gzip 壓縮 | 已實施 | 回應大小減少 60% | - | NestJS 內建 |
| TanStack Query staleTime | 已實施 | 重複請求減少 80% | - | 5 min staleTime |
| Redis 快取策略 | 已實施 | DB 查詢減少 70% | - | 分層 TTL |
第 9 章:回歸測試清單
使用方式:每次版本發布前,逐項勾選確認。未通過的項目需建立 Bug 報告。 優先級說明:P1 為每次必測,P2 為重大版本必測,P3 為季度測試。 格式:
- [ ] TC-ID: 說明— 未測試;- [x] TC-ID: 說明— 已通過。
9.1 認證模組回歸
9.1.1 前台用戶註冊
- [ ] TC-REG-AUTH-001: 帳號密碼註冊 → 成功建立帳號並取得 JWT
- [ ] TC-REG-AUTH-002: 帳號已存在 → 顯示錯誤訊息(ERROR_CODES 查表)
- [ ] TC-REG-AUTH-003: 密碼不符合強度要求 → Zod 驗證攔截
- [ ] TC-REG-AUTH-004: Email 格式驗證 → 不合法 Email 被拒絕
- [ ] TC-REG-AUTH-005: 註冊時裝置指紋記錄 → FingerprintJS v5 正確產生並傳送
9.1.2 前台用戶登入
- [ ] TC-REG-AUTH-006: 帳號密碼登入 → 成功取得 JWT Token(7 天過期)
- [ ] TC-REG-AUTH-007: 錯誤密碼登入 → 顯示錯誤訊息,記錄登入失敗
- [ ] TC-REG-AUTH-008: Google OAuth 登入 → 成功建立/綁定帳號
- [ ] TC-REG-AUTH-009: Telegram OAuth 登入 → 成功建立/綁定帳號
- [ ] TC-REG-AUTH-010: 登入時裝置指紋記錄 → 新增
auth-user-login-log紀錄 - [ ] TC-REG-AUTH-011: 不存在的帳號登入 → 統一錯誤訊息(不洩漏帳號存在與否)
9.1.3 Token 與 Session 管理
- [ ] TC-REG-AUTH-012: JWT Token 7 天內有效 → API 正常存取
- [ ] TC-REG-AUTH-013: JWT Token 過期 → 返回 401,前端觸發重新登入
- [ ] TC-REG-AUTH-014: 登出 → Token 失效,清除 session
- [ ] TC-REG-AUTH-015: tokenVersion 變更 → 舊 Token 失效
- [ ] TC-REG-AUTH-016: 多裝置同時登入 → 各自 Token 獨立
9.1.4 密碼管理
- [ ] TC-REG-AUTH-017: 修改密碼(需驗證舊密碼) → 成功後舊 Token 失效
- [ ] TC-REG-AUTH-018: OAuth 用戶首次設定密碼(不需舊密碼) → 成功
- [ ] TC-REG-AUTH-019: 密碼以 bcrypt 儲存 → 資料庫無明文
9.1.5 後台管理員認證
- [ ] TC-REG-AUTH-020: 管理員帳密登入 → 取得 AdminJWT
- [ ] TC-REG-AUTH-021: 管理員 2FA 登入流程 → 帳密 + TOTP 雙步驟
- [ ] TC-REG-AUTH-022: NextAuth 5 Session 管理 → JWT 策略正確
- [ ] TC-REG-AUTH-023: 管理員登出 → 清除 session + Token
- [ ] TC-REG-AUTH-024: 管理員 Token 過期 → 401 重試機制
9.1.6 2FA 管理
- [ ] TC-REG-AUTH-025: 啟用 Google Authenticator → QR Code + Secret
- [ ] TC-REG-AUTH-026: 正確 TOTP 碼驗證 → 啟用成功
- [ ] TC-REG-AUTH-027: 錯誤 TOTP 碼 → 啟用失敗
- [ ] TC-REG-AUTH-028: 停用 2FA(需當前 TOTP) → 成功停用
- [ ] TC-REG-AUTH-029: 停用後重新啟用 → 新 Secret 產生
9.1.7 頭像管理
- [ ] TC-REG-AUTH-030: 上傳頭像 → R2 儲存成功
- [ ] TC-REG-AUTH-031: 更換頭像 → 舊頭像清除
- [ ] TC-REG-AUTH-032: 頭像顯示 → 正確載入
9.2 遊戲模組回歸
9.2.1 遊戲大廳(前台)
- [ ] TC-REG-GAME-001: 遊戲大廳載入 → 顯示所有已啟用的遊戲類型 Tab
- [ ] TC-REG-GAME-002: 體育 (SPORTS) 分類 → 正確顯示體育遊戲
- [ ] TC-REG-GAME-003: 老虎機 (SLOT) 分類 → 正確顯示老虎機遊戲
- [ ] TC-REG-GAME-004: 真人 (LIVE) 分類 → 正確顯示真人遊戲
- [ ] TC-REG-GAME-005: 彩票 (LOTTERY) 分類 → 正確顯示彩票遊戲
- [ ] TC-REG-GAME-006: 棋牌 (CHESS) 分類 → 正確顯示棋牌遊戲
- [ ] TC-REG-GAME-007: 電競 (ESPORTS) 分類 → 正確顯示電競遊戲
- [ ] TC-REG-GAME-008: 加密貨幣 (CRYPTO) 分類 → 正確顯示加密遊戲
- [ ] TC-REG-GAME-009: 捕魚 (FISH) 分類 → 正確顯示捕魚遊戲
9.2.2 遊戲功能
- [ ] TC-REG-GAME-010: 遊戲搜尋 → 關鍵字模糊搜尋正確
- [ ] TC-REG-GAME-011: 遊戲啟動(已登入) → 正確跳轉遊戲頁面
- [ ] TC-REG-GAME-012: 遊戲試玩(免登入) → 試玩模式正確
- [ ] TC-REG-GAME-013: 風控黑名單攔截 → 被封鎖用戶無法啟動遊戲(錯誤碼 5010)
- [ ] TC-REG-GAME-014: 最近遊玩記錄 → 顯示用戶最近玩過的遊戲
- [ ] TC-REG-GAME-015: 遊戲下注回調 → 餘額正確扣減
- [ ] TC-REG-GAME-016: 遊戲派彩回調 → 餘額正確增加
- [ ] TC-REG-GAME-017: 投注後連動 → VIP 等級重算 + 活動打碼量 + 任務進度
9.2.3 遊戲管理(後台)
- [ ] TC-REG-GAME-018: 遊戲供應商列表 → 按站點顯示(SiteTabs)
- [ ] TC-REG-GAME-019: 新增遊戲供應商 → 成功建立(含 siteCode)
- [ ] TC-REG-GAME-020: 編輯遊戲供應商 → 更新成功
- [ ] TC-REG-GAME-021: 刪除遊戲供應商 → 刪除成功
- [ ] TC-REG-GAME-022: 遊戲分類列表 → 按站點顯示(SiteTabs)
- [ ] TC-REG-GAME-023: 新增遊戲分類 → 成功建立(含 siteCode)
- [ ] TC-REG-GAME-024: 遊戲模板載入 → 預覽後確認寫入(按站點)
- [ ] TC-REG-GAME-025: 同預設站點複製 → transaction 內先刪後插
- [ ] TC-REG-GAME-026: 跨站複製遊戲資料 → sourceSiteCode → targetSiteCode
9.3 金流模組回歸
9.3.1 存款功能(前台)
- [ ] TC-REG-FIN-001: 存款入口 →
POST /deposit統一入口 - [ ] TC-REG-FIN-002: ATM 存款(萬通金流) → 訂單建立 + 導向金流頁面
- [ ] TC-REG-FIN-003: 信用卡存款(萬通金流) → 訂單建立 + 信用卡表單
- [ ] TC-REG-FIN-004: USDT 加密貨幣存款 → 訂單建立 + 顯示收款地址
- [ ] TC-REG-FIN-005: 存款金額 → TWD 轉 USD(台銀即時匯率)
- [ ] TC-REG-FIN-006: USD 截斷規則 →
Math.floor(value * 1e6) / 1e6 - [ ] TC-REG-FIN-007: 金流群組依語系分配 → 正確路由至對應通道
- [ ] TC-REG-FIN-008: 存款回調 → 金流商回調更新訂單狀態
- [ ] TC-REG-FIN-009: 存款成功後連動 → 餘額增加 + 任務進度更新
9.3.2 提款功能(前台)
- [ ] TC-REG-FIN-010: 提款申請 → 建立提款訂單
- [ ] TC-REG-FIN-011: 提款餘額檢查 → 餘額不足時拒絕
- [ ] TC-REG-FIN-012: 提款至銀行卡 → 資料正確
- [ ] TC-REG-FIN-013: 提款至加密地址 → 資料正確
- [ ] TC-REG-FIN-014: 提款手續費計算 → 正確扣除
9.3.3 金流管理(後台)
- [ ] TC-REG-FIN-015: 存款審核列表 → 多站點 SiteTabs + 篩選
- [ ] TC-REG-FIN-016: 存款審核(通過) → 狀態更新 + 餘額增加
- [ ] TC-REG-FIN-017: 存款審核(拒絕) → 狀態更新 + 餘額不變
- [ ] TC-REG-FIN-018: 提款審核列表 → 多站點 SiteTabs + 篩選
- [ ] TC-REG-FIN-019: 提款審核(通過) → 狀態更新
- [ ] TC-REG-FIN-020: 提款審核(拒絕) → 狀態更新 + 餘額返還
- [ ] TC-REG-FIN-021: 提款上傳憑證 → R2 儲存 + 關聯訂單
- [ ] TC-REG-FIN-022: 提款標記完成 → 最終狀態更新
- [ ] TC-REG-FIN-023: 手動調帳 → 餘額正確增減 + 紀錄
9.3.4 金流商管理(後台)
- [ ] TC-REG-FIN-024: 金流群組列表 → 多站點顯示
- [ ] TC-REG-FIN-025: 金流群組 CRUD → 新增/編輯/刪除
- [ ] TC-REG-FIN-026: 金流通道列表 → 多站點顯示
- [ ] TC-REG-FIN-027: 金流通道 CRUD → 新增/編輯/刪除
- [ ] TC-REG-FIN-028: 用戶金流群組分配 → 正確設定
9.3.5 匯率
- [ ] TC-REG-FIN-029: 即時匯率取得 → 台灣銀行匯率 API
- [ ] TC-REG-FIN-030: 匯率精度 → decimal(18,10) 小數 10 位
9.4 錢包模組回歸
9.4.1 銀行卡
- [ ] TC-REG-WALLET-001: 新增銀行卡 → 前台用戶新增成功
- [ ] TC-REG-WALLET-002: 銀行卡列表 → 顯示用戶所有銀行卡
- [ ] TC-REG-WALLET-003: 刪除銀行卡 → 成功刪除
- [ ] TC-REG-WALLET-004: 後台銀行卡審核列表 → 多站點 SiteTabs + 篩選
- [ ] TC-REG-WALLET-005: 後台銀行卡審核(通過/拒絕) → 狀態正確更新
9.4.2 信用卡
- [ ] TC-REG-WALLET-006: 新增信用卡 → 前台用戶新增成功
- [ ] TC-REG-WALLET-007: 信用卡列表 → 顯示用戶所有信用卡
- [ ] TC-REG-WALLET-008: 刪除信用卡 → 成功刪除
- [ ] TC-REG-WALLET-009: 後台信用卡審核列表 → 多站點 SiteTabs + 篩選
- [ ] TC-REG-WALLET-010: 後台信用卡審核(通過/拒絕) → 狀態正確更新
9.4.3 加密錢包地址
- [ ] TC-REG-WALLET-011: 新增加密地址 → 前台用戶新增成功
- [ ] TC-REG-WALLET-012: 加密地址列表 → 顯示用戶所有地址
- [ ] TC-REG-WALLET-013: 刪除加密地址 → 成功刪除
- [ ] TC-REG-WALLET-014: 後台加密地址審核列表 → 多站點 SiteTabs + 篩選
- [ ] TC-REG-WALLET-015: 後台加密地址審核(通過/拒絕) → 狀態正確更新
9.5 VIP 模組回歸
9.5.1 VIP 等級(前台)
- [ ] TC-REG-VIP-001: VIP 等級頁面 → 顯示該站點所有 VIP 等級
- [ ] TC-REG-VIP-002: 當前 VIP 等級 → 正確顯示用戶 VIP 等級
- [ ] TC-REG-VIP-003: 升級進度 → 累積投注金額和升級門檻
- [ ] TC-REG-VIP-004: VIP 等級只升不降 → 投注累積自動升級
9.5.2 VIP 反水
- [ ] TC-REG-VIP-005: 反水規則頁面 → 顯示各等級各遊戲類型反水率
- [ ] TC-REG-VIP-006: 每日反水結算(00:05 Cron) → 正確計算並發放
- [ ] TC-REG-VIP-007: 反水含 8 種遊戲類型 → sports/slot/live/lottery/chess/esports/crypto/fish
- [ ] TC-REG-VIP-008: 反水精度 → decimal(18,6) 無條件捨去
9.5.3 VIP 保級
- [ ] TC-REG-VIP-009: 月度保級檢查(每月 1 號 01:00 Cron) → 正確執行
- [ ] TC-REG-VIP-010: VIP 5+ 保級鎖定 → 高等級不因保級失敗而降級
9.5.4 VIP 管理(後台)
- [ ] TC-REG-VIP-011: VIP 等級列表 → 多站點 SiteTabs
- [ ] TC-REG-VIP-012: VIP 等級 CRUD → 新增/編輯/刪除
- [ ] TC-REG-VIP-013: VIP 等級數量可自由擴充 → 不限 15 級
- [ ] TC-REG-VIP-014: 返水設定列表 → 多站點 SiteTabs
- [ ] TC-REG-VIP-015: 返水設定 CRUD → bulk upsert(unique key: siteCode + level + gameType)
- [ ] TC-REG-VIP-016: VIP 等級帶入模板 → 只影響當前站點
- [ ] TC-REG-VIP-017: VIP 等級同預設站點 → 複製預設站配置到目標站
- [ ] TC-REG-VIP-018: 返水設定帶入模板 → 只影響當前站點
- [ ] TC-REG-VIP-019: 返水設定同預設站點 → 複製預設站配置到目標站
- [ ] TC-REG-VIP-020: VIP 玩家列表 → 多站點 SiteTabs + 篩選
- [ ] TC-REG-VIP-021: 其他頁面 VIP 下拉選項 → 動態取得(不硬編碼)
9.6 代理模組回歸
9.6.1 代理申請與綁定
- [ ] TC-REG-AFF-001: 申請成為代理 → 成功設定為代理身份
- [ ] TC-REG-AFF-002: 推廣碼上限(每人最多 10 個) → 超過時拒絕
- [ ] TC-REG-AFF-003: 推廣碼雙重查找 → 聯盟碼 → 主代理碼
- [ ] TC-REG-AFF-004: 三層代理結構 → 最多 3 層上下線關係
- [ ] TC-REG-AFF-005: 手動綁定上下線 → 後台管理員操作
9.6.2 佣金與結算
- [ ] TC-REG-AFF-006: 下線投注產生佣金 → 按佣金費率計算
- [ ] TC-REG-AFF-007: 佣金費率 → 按代理等級 × 遊戲類型配置
- [ ] TC-REG-AFF-008: 佣金週結(每週一 03:00 Cron) → 正確結算
- [ ] TC-REG-AFF-009: 佣金日結(每日 03:30 Cron) → 正確結算
- [ ] TC-REG-AFF-010: 結算風控檢測 → 異常下線自動標記
9.6.3 代理提款
- [ ] TC-REG-AFF-011: 代理提款申請 → 佣金餘額充足時建立
- [ ] TC-REG-AFF-012: 代理提款三階段 → 待審核 → 已核准 → 已完成
- [ ] TC-REG-AFF-013: 代理提款審核(後台) → 通過/拒絕
9.6.4 聯盟系統
- [ ] TC-REG-AFF-014: 代理等級 → bronze/silver/gold/platinum
- [ ] TC-REG-AFF-015: VIP 里程碑 → 下線 VIP 等級達標自動發放獎勵
- [ ] TC-REG-AFF-016: 佣金費率模板 → 4 等級 × 3 級別 × 8 遊戲類型 = 96 筆
- [ ] TC-REG-AFF-017: 聯盟公開端點 → alliance-info / tier-info / vip-milestones / referral-codes
9.6.5 代理管理(後台)
- [ ] TC-REG-AFF-018: 代理列表 → 多站點 SiteTabs + 篩選
- [ ] TC-REG-AFF-019: 新增代理 → 手動綁定用戶為代理
- [ ] TC-REG-AFF-020: 佣金結算列表 → 篩選 status + 日期
- [ ] TC-REG-AFF-021: 結算審核 → approve / reject
- [ ] TC-REG-AFF-022: 結算風控紀錄 → 查看風控檢測結果
- [ ] TC-REG-AFF-023: 代理提款列表 → 篩選 status + method
- [ ] TC-REG-AFF-024: 代理提款審核 → approve / reject / complete
- [ ] TC-REG-AFF-025: 綁定紀錄 → 篩選 action + refCode + 日期
- [ ] TC-REG-AFF-026: 佣金費率 CRUD → 按代理等級 × 遊戲類型
- [ ] TC-REG-AFF-027: VIP 里程碑 CRUD → 下線 VIP 等級獎勵
- [ ] TC-REG-AFF-028: 代理等級 CRUD → bronze/silver/gold/platinum
- [ ] TC-REG-AFF-029: 帶入模板 → 預覽後確認寫入
- [ ] TC-REG-AFF-030: 設定代理等級 → 手動調整
- [ ] TC-REG-AFF-031: 觸發結算 → 手動觸發週結/日結
- [ ] TC-REG-AFF-032: 帳號遮罩 → maskAccount() 正確隱藏
9.7 活動模組回歸
9.7.1 活動功能(前台)
- [ ] TC-REG-PROMO-001: 活動列表 → 顯示該站點所有進行中活動
- [ ] TC-REG-PROMO-002: 活動詳情 → 正確顯示多語系內容(resolveText)
- [ ] TC-REG-PROMO-003: 活動領取 → 符合條件時成功領取
- [ ] TC-REG-PROMO-004: 活動條件檢查 → 不符合條件時顯示原因
- [ ] TC-REG-PROMO-005: 打碼量追蹤 → 投注後正確累計打碼進度
- [ ] TC-REG-PROMO-006: 重複領取防護 → 已領取的活動不可再領
9.7.2 活動管理(後台)
- [ ] TC-REG-PROMO-007: 活動列表 → 多站點 SiteTabs + 篩選
- [ ] TC-REG-PROMO-008: 新增活動 → 含多語系內容 + 條件設定
- [ ] TC-REG-PROMO-009: 編輯活動 → 更新成功
- [ ] TC-REG-PROMO-010: 刪除活動 → 刪除成功
- [ ] TC-REG-PROMO-011: 活動上下架 → 狀態切換正確
9.7.3 活動標籤(後台)
- [ ] TC-REG-PROMO-012: 活動標籤列表 → 多站點 SiteTabs
- [ ] TC-REG-PROMO-013: 標籤 CRUD → 新增/編輯/刪除
- [ ] TC-REG-PROMO-014: 活動關聯標籤 → 篩選功能正確
- [ ] TC-REG-PROMO-015: 標籤多語系名稱 → 5 個語系正確顯示
9.8 任務模組回歸
9.8.1 任務功能
- [ ] TC-REG-MISSION-001: 任務列表 → 顯示每日/週/月任務
- [ ] TC-REG-MISSION-002: 存款任務進度 → 存款確認後自動更新
- [ ] TC-REG-MISSION-003: 投注任務進度 → 投注結算後自動更新
- [ ] TC-REG-MISSION-004: 任務完成領取獎勵 → 成功領取
- [ ] TC-REG-MISSION-005: 每日任務重置 → 每日 00:00 重置進度
- [ ] TC-REG-MISSION-006: 每週任務重置 → 每週一重置進度
- [ ] TC-REG-MISSION-007: 每月任務重置 → 每月 1 號重置進度
- [ ] TC-REG-MISSION-008: 任務獎勵精度 → decimal(18,6) 無條件捨去
- [ ] TC-REG-MISSION-009: 重複領取防護 → 已領取的任務不可再領
- [ ] TC-REG-MISSION-010: 任務進度不因重複投注而超額 → 進度上限為目標值
9.9 站內信模組回歸
9.9.1 站內信功能(前台)
- [ ] TC-REG-INBOX-001: 站內信列表 → 顯示用戶收件匣
- [ ] TC-REG-INBOX-002: 未讀信件計數 → 正確顯示未讀數量
- [ ] TC-REG-INBOX-003: 閱讀信件 → 標記為已讀
- [ ] TC-REG-INBOX-004: 刪除信件 → 成功刪除
- [ ] TC-REG-INBOX-005: 信件分頁 → 大量信件正確分頁
9.9.2 站內信管理(後台)
- [ ] TC-REG-INBOX-006: 站內信列表 → 多站點 SiteTabs
- [ ] TC-REG-INBOX-007: 發送站內信 → 指定用戶或全站廣播
- [ ] TC-REG-INBOX-008: 信件多語系內容 → 5 個語系正確
- [ ] TC-REG-INBOX-009: 信件設定 → 模式 B 設定頁(各站獨立)
- [ ] TC-REG-INBOX-010: 信件內容 XSS 防護 → 安全顯示
9.10 風控模組回歸
9.10.1 IP 黑白名單
- [ ] TC-REG-RISK-001: IP 規則列表 → 多站點 SiteTabs + 篩選(類型/IP/日期)
- [ ] TC-REG-RISK-002: 新增 IP 黑名單 → 成功建立
- [ ] TC-REG-RISK-003: 新增 IP 白名單 → 成功建立
- [ ] TC-REG-RISK-004: 編輯 IP 規則 → 更新成功
- [ ] TC-REG-RISK-005: 刪除 IP 規則 → 刪除成功
- [ ] TC-REG-RISK-006: 黑名單 IP 阻擋 → 該 IP 用戶被限制
9.10.2 遊戲黑名單
- [ ] TC-REG-RISK-007: 遊戲黑名單列表 → 多站點 SiteTabs + 篩選(用戶/遊戲類型/日期)
- [ ] TC-REG-RISK-008: 新增遊戲黑名單 → 全封鎖/類型封鎖/特定遊戲封鎖
- [ ] TC-REG-RISK-009: 遊戲黑名單生效 → 被封鎖用戶無法啟動遊戲(錯誤碼 5010)
- [ ] TC-REG-RISK-010: 刪除遊戲黑名單 → 用戶恢復遊戲權限
9.10.3 IP/裝置指紋查詢
- [ ] TC-REG-RISK-011: IP 查詢 → 多站點 SiteTabs + 關鍵字搜尋
- [ ] TC-REG-RISK-012: 裝置指紋查詢 → 反查使用該指紋的用戶
- [ ] TC-REG-RISK-013: 帳號/姓名/Email/手機查詢 → 反查 IP 和指紋
- [ ] TC-REG-RISK-014: 登入失敗紀錄 → 多站點 + 關鍵字 + 日期篩選
- [ ] TC-REG-RISK-015: 登入失敗紀錄含 IP 和時間 → 風控分析依據
9.11 報表模組回歸
9.11.1 各報表功能
- [ ] TC-REG-REPORT-001: 玩家報表 → 多站點 SiteTabs + 關鍵字/VIP 等級/日期篩選
- [ ] TC-REG-REPORT-002: 投注紀錄 → 多站點 SiteTabs + 關鍵字/遊戲類型/平台/狀態/日期篩選
- [ ] TC-REG-REPORT-003: 總覽報表 → 多站點 SiteTabs + 日期篩選 + 統計卡片
- [ ] TC-REG-REPORT-004: 損益報表 → 多站點 SiteTabs + 分組(日/週/月)/遊戲類型/日期篩選
- [ ] TC-REG-REPORT-005: 遊戲報表 → 多站點 SiteTabs + 遊戲類型/平台/日期篩選
- [ ] TC-REG-REPORT-006: 玩家摘要 → 多站點 SiteTabs + 關鍵字/VIP/排序/日期篩選
- [ ] TC-REG-REPORT-007: 活動報表 → 日期篩選(待加入多站點)
9.11.2 報表通用功能
- [ ] TC-REG-REPORT-008: 分頁功能 → 所有報表分頁正確
- [ ] TC-REG-REPORT-009: 排序功能 → 點擊欄位標頭排序
- [ ] TC-REG-REPORT-010: CSV 匯出 → ExportButton 匯出正確
- [ ] TC-REG-REPORT-011: 空資料顯示 →
tc("noData")提示 - [ ] TC-REG-REPORT-012: 日期範圍篩選 → 正確篩選 createdAt
- [ ] TC-REG-REPORT-013: 重置篩選 → 清除所有篩選條件
9.11.3 報表數據正確性
- [ ] TC-REG-REPORT-014: 總覽報表數據 → 存款/提款/投注總額正確
- [ ] TC-REG-REPORT-015: 損益報表計算 → 收入 - 支出 = 損益
- [ ] TC-REG-REPORT-016: 遊戲報表統計 → 各遊戲類型數據正確
- [ ] TC-REG-REPORT-017: 玩家報表含 VIP 資訊 → 等級正確顯示
- [ ] TC-REG-REPORT-018: 投注紀錄完整 → 每筆投注都有紀錄
- [ ] TC-REG-REPORT-019: 報表金額精度 → decimal(18,6) 正確顯示
- [ ] TC-REG-REPORT-020: 報表時區 → UTC+8 正確顯示
9.12 系統管理模組回歸
9.12.1 管理員管理
- [ ] TC-REG-SYS-001: 管理員列表 → 全站共用(不區分站點)
- [ ] TC-REG-SYS-002: 新增管理員 → 帳號/密碼/群組設定
- [ ] TC-REG-SYS-003: 編輯管理員 → 修改資料/群組
- [ ] TC-REG-SYS-004: 停用管理員 → 帳號停用後無法登入
- [ ] TC-REG-SYS-005: 管理員密碼重設 → 管理員可修改自己密碼
9.12.2 群組管理
- [ ] TC-REG-SYS-006: 群組列表 → 全站共用
- [ ] TC-REG-SYS-007: 新增群組 → 設定群組類型和權限
- [ ] TC-REG-SYS-008: 編輯群組權限 → 16 模組 × 2 操作
- [ ] TC-REG-SYS-009: 刪除群組 → 無管理員使用才可刪除
- [ ] TC-REG-SYS-010: 四種群組類型 → root/super_admin/general_admin/custom
9.12.3 操作紀錄
- [ ] TC-REG-SYS-011: 操作紀錄列表 → 全站共用 + 日期篩選
- [ ] TC-REG-SYS-012: 操作紀錄內容 → 操作者/類型/時間/IP
- [ ] TC-REG-SYS-013: 紀錄不可刪改 → 稽核安全
9.12.4 站點設定
- [ ] TC-REG-SYS-014: 站點列表 → 所有站點(含主題)
- [ ] TC-REG-SYS-015: 新增站點 → 建立新站點配置
- [ ] TC-REG-SYS-016: 編輯站點 → 更新站點設定
- [ ] TC-REG-SYS-017: 刪除站點 → cascade 刪除主題
9.12.5 客服配置
- [ ] TC-REG-SYS-018: 客服配置 → 8 種管道(line/telegram/wechat/facebook/instagram/twitter/discord/custom)
- [ ] TC-REG-SYS-019: 客服管道設定 → 多語系 label + icon + link + sortOrder + enabled
- [ ] TC-REG-SYS-020: LiveChat 嵌入腳本 → 獨立開關
- [ ] TC-REG-SYS-021: 客服配置各站獨立 → 同預設站點複製
9.12.6 域名設置
- [ ] TC-REG-SYS-022: 域名設置 → 模式 B(各站獨立)
- [ ] TC-REG-SYS-023: 域名素材上傳 → logoSmall / logoBig / favicon
9.12.7 雲端儲存
- [ ] TC-REG-SYS-024: R2 檔案列表 → 多站點 SiteTabs
- [ ] TC-REG-SYS-025: R2 檔案上傳 → 上傳至正確路徑
- [ ] TC-REG-SYS-026: R2 檔案刪除 → 刪除成功
- [ ] TC-REG-SYS-027: R2 資料夾管理 → 建立/刪除資料夾
- [ ] TC-REG-SYS-028: R2 操作紀錄 → 雲端儲存日誌正確記錄
9.12.8 其他系統設定
- [ ] TC-REG-SYS-029: 三方登入配置 → 各站獨立(模式 B)
- [ ] TC-REG-SYS-030: 遊戲商配置 → 各站獨立(模式 B)
- [ ] TC-REG-SYS-031: 服務商配置 → 各站獨立(模式 B)
9.12.9 詳細測試步驟:認證模組
以下為認證模組回歸測試的詳細步驟,適用於首次執行或新人上手。
TC-REG-AUTH-001 詳細步驟:帳號密碼註冊
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 開啟前台首頁 http://localhost:3010 | 頁面正確載入 |
| 2 | 點擊右上角「註冊」按鈕 | 跳轉至註冊頁面 |
| 3 | 輸入帳號:test_new_user_001 | 帳號欄位接受輸入 |
| 4 | 輸入密碼:NewUser123!@# | 密碼強度指示器顯示 |
| 5 | 輸入確認密碼:NewUser123!@# | 兩次密碼一致 |
| 6 | 輸入 Email:test@example.com | Email 格式驗證通過 |
| 7 | 點擊「註冊」按鈕 | 發送 POST /api/auth/register |
| 8 | 檢查 API 回應 | code: 200, 包含 token 和 user |
| 9 | 檢查頁面跳轉 | 自動登入並跳轉至首頁 |
| 10 | 檢查 Header 用戶名稱 | 顯示 test_new_user_001 |
| 11 | 查詢資料庫 | auth-user 表新增一筆記錄,siteCode 正確 |
| 12 | 查詢密碼儲存 | 密碼以 $2b$ 開頭的 bcrypt hash 儲存 |
TC-REG-AUTH-006 詳細步驟:帳號密碼登入
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 開啟前台登入頁面 | 登入表單正確顯示 |
| 2 | 輸入帳號:test_user_C9_001 | 帳號欄位接受輸入 |
| 3 | 輸入密碼:Test123!@# | 密碼欄位接受輸入 |
| 4 | 確認 FingerprintJS 已載入 | 瀏覽器指紋已產生 |
| 5 | 點擊「登入」按鈕 | 發送 POST /api/auth/login(含 fingerprint) |
| 6 | 檢查 API 回應 | code: 200, result.token 為有效 JWT |
| 7 | 解碼 JWT payload | 包含 userId, account, siteCode, exp(7天後) |
| 8 | 檢查頁面狀態 | 跳轉至首頁,Header 顯示用戶資訊 |
| 9 | 檢查 Cookie | locale cookie 已設定 |
| 10 | 查詢 auth-user-login-log | 新增一筆登入記錄,含 IP、指紋、時間 |
TC-REG-AUTH-020 詳細步驟:後台管理員登入
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 開啟後台登入頁面 http://localhost:3011 | 登入表單正確顯示 |
| 2 | 輸入帳號:root_admin | 帳號欄位接受輸入 |
| 3 | 輸入密碼:Root123!@# | 密碼欄位接受輸入 |
| 4 | 點擊「登入」按鈕 | 發送 POST /api/admin/login |
| 5 | 檢查 API 回應 | code: 200, result.token 含 role: 'admin' |
| 6 | 檢查 NextAuth session | Session 正確建立,JWT 策略 |
| 7 | 檢查頁面跳轉 | 導向 Dashboard 頁面 |
| 8 | 檢查 Sidebar | 顯示完整選單(root 權限) |
| 9 | 檢查 SiteSelector | Header 顯示站點選擇下拉選單 |
| 10 | 檢查 SiteFilterInitializer | 站點列表已載入(呼叫 /site-config/admin/list) |
9.12.10 詳細測試步驟:金流模組
TC-REG-FIN-001 詳細步驟:前台存款流程
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 前台用戶已登入 | Header 顯示用戶名稱和餘額 |
| 2 | 記錄當前餘額(如 100.000000 USD) | 存款前基準值 |
| 3 | 進入存款頁面 | 顯示可用的存款方式 |
| 4 | 選擇 ATM 存款 | ATM 存款表單顯示 |
| 5 | 輸入金額 1000 TWD | 金額驗證通過 |
| 6 | 確認匯率顯示 | 顯示當前台銀匯率和折算 USD |
| 7 | 計算預期 USD | Math.floor(1000 / rate * 1e6) / 1e6 |
| 8 | 點擊「確認存款」 | 發送 POST /api/deposit |
| 9 | 檢查回應 | 訂單建立成功,含訂單號和金流頁 URL |
| 10 | 模擬金流商回調成功 | 發送回調至 /api/vendor/callback |
| 11 | 檢查訂單狀態 | deposit-order 狀態更新為 completed |
| 12 | 檢查用戶餘額 | 餘額增加 = 步驟 7 的 USD 金額 |
| 13 | 檢查任務進度 | 存款任務進度更新 |
| 14 | 確認精度 | USD 金額 decimal(18,6) 無條件捨去 |
TC-REG-FIN-015 詳細步驟:後台存款審核
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 後台管理員登入(需 deposit:read + deposit:write 權限) | 成功登入 |
| 2 | 進入「財務管理 > 存款審核」 | 頁面載入 |
| 3 | 觀察 SiteTabs | 顯示站點 Tab(全站模式下顯示所有站) |
| 4 | 選擇 C9 站 Tab | 列表僅顯示 C9 站資料 |
| 5 | 使用篩選條件 | 按訂單 ID / 用戶 ID / 狀態 / 日期 篩選 |
| 6 | 找到待審核的訂單 | 狀態顯示「待審核」標籤 |
| 7 | 點擊「審核」按鈕 | 審核對話框彈出 |
| 8 | 選擇「通過」並確認 | 發送 POST /api/admin/finance/deposit-review/:id/review |
| 9 | 檢查訂單狀態更新 | 狀態變為「已通過」 |
| 10 | 檢查用戶餘額 | 餘額已增加對應金額 |
| 11 | 檢查操作紀錄 | admin-operation-log 記錄審核操作 |
9.12.11 詳細測試步驟:VIP 模組
TC-REG-VIP-006 詳細步驟:每日反水結算
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 確認用戶 VIP 等級(如 VIP 5) | 資料庫確認 |
| 2 | 確認反水規則(C9 站 VIP 5 各遊戲類型) | 查詢 vip-rebate 表 |
| 3 | 確認用戶前一日有投注紀錄 | 查詢 bet-order 表 |
| 4 | 記錄各遊戲類型的投注金額 | 分 8 類統計 |
| 5 | 手動觸發反水結算(或等待 00:05 Cron) | Cron job 執行 |
| 6 | 計算預期反水 | 各類型投注 × 對應反水率 |
| 7 | 加總所有遊戲類型反水 | 使用 truncateUsd() 截斷 |
| 8 | 檢查 vip-rebate-log | 新增反水發放紀錄 |
| 9 | 檢查用戶餘額 | 餘額增加 = 步驟 7 的反水總額 |
| 10 | 確認多站點隔離 | C9 站反水不影響 A1 站用戶 |
TC-REG-VIP-016 詳細步驟:VIP 帶入模板
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 後台進入「VIP > 等級管理」 | 頁面載入 |
| 2 | 選擇 A1 站 Tab | 顯示 A1 站 VIP 等級 |
| 3 | 記錄 A1 站當前 VIP 等級數量和設定 | 記錄基準值 |
| 4 | 記錄 C9 站(預設站)VIP 等級設定 | 記錄來源值 |
| 5 | 點擊「帶入模板」按鈕 | 預覽對話框彈出 |
| 6 | 確認模板內容 | 顯示預設 VIP 等級設定 |
| 7 | 可編輯模板內容 | 修改部分設定 |
| 8 | 點擊確認 | 發送 POST /vip/admin/load-template(含 siteCode=A1) |
| 9 | 檢查 A1 站 VIP 等級 | 更新為模板內容 |
| 10 | 檢查 C9 站 VIP 等級 | 不受影響(仍為原始設定) |
| 11 | 檢查 B2 站 VIP 等級 | 不受影響 |
9.12.12 詳細測試步驟:代理模組
TC-REG-AFF-008 詳細步驟:佣金週結
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 確認代理 A 有下線用戶 | 查詢代理關係 |
| 2 | 確認下線用戶上週有投注 | 查詢 bet-order |
| 3 | 確認代理 A 的佣金費率 | 按等級 × 遊戲類型查詢 commission-rates |
| 4 | 手動觸發週結(POST /affiliate/admin/trigger-settlement) | 或等待每週一 03:00 Cron |
| 5 | 檢查結算風控 | affiliate-risk-log 記錄 |
| 6 | 檢查結算記錄 | affiliate-settlement 新增記錄 |
| 7 | 計算預期佣金 | 各遊戲類型投注 × 對應佣金費率 |
| 8 | 驗證佣金金額 | 結算金額 = 步驟 7 的計算結果 |
| 9 | 檢查代理佣金餘額 | affiliate-balance 增加 |
| 10 | 檢查結算狀態 | 依風控結果決定:pending / approved |
9.12.13 詳細測試步驟:報表模組
TC-REG-REPORT-003 詳細步驟:總覽報表
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 後台管理員登入(需 report:read 權限) | 成功登入 |
| 2 | 進入「報表 > 總覽」 | 頁面載入 |
| 3 | 觀察 SiteTabs | 顯示站點 Tab |
| 4 | 選擇 C9 站 Tab | 資料篩選為 C9 站 |
| 5 | 設定日期範圍(近 7 天) | 篩選條件更新 |
| 6 | 檢查統計卡片 | 顯示:總存款、總提款、總投注、總派彩、淨利 |
| 7 | 驗證總存款 | = SUM(deposit-order.amount WHERE status=completed AND siteCode=C9 AND dateRange) |
| 8 | 驗證總提款 | = SUM(withdrawal-order.amount WHERE status=completed AND siteCode=C9 AND dateRange) |
| 9 | 驗證總投注 | = SUM(bet-order.betAmount WHERE siteCode=C9 AND dateRange) |
| 10 | 驗證總派彩 | = SUM(bet-order.payoutAmount WHERE siteCode=C9 AND dateRange) |
| 11 | 驗證淨利 | = 總存款 - 總提款 - 總派彩 + 總投注(或定義的公式) |
| 12 | 檢查每日摘要表格 | 按日分群,各日數據正確 |
| 13 | 切換 A1 站 Tab | 資料切換為 A1 站,數值不同 |
9.12.14 詳細測試步驟:風控模組
TC-REG-RISK-008 詳細步驟:遊戲黑名單
| 步驟 | 操作 | 驗證點 |
|---|---|---|
| 1 | 後台管理員進入「風控 > 遊戲黑名單」 | 頁面載入 |
| 2 | 選擇 C9 站 Tab | 列表篩選為 C9 站 |
| 3 | 點擊「新增黑名單」 | 新增對話框彈出 |
| 4 | 選擇用戶(如 test_user_C9_001) | 用戶搜尋功能 |
| 5 | 選擇封鎖類型:「全封鎖」 | 全部遊戲類型封鎖 |
| 6 | 點擊確認 | 發送 POST API,建立黑名單 |
| 7 | 確認列表更新 | 新增一筆黑名單記錄 |
| 8 | 前台切換至 test_user_C9_001 | 登入該帳號 |
| 9 | 嘗試啟動任意遊戲 | 發送 POST /api/game/launch |
| 10 | 檢查回應 | 錯誤碼 5010(遊戲黑名單) |
| 11 | 確認無法進入遊戲 | 前台顯示被封鎖提示 |
| 12 | 回到後台刪除黑名單 | 刪除成功 |
| 13 | 前台再次啟動遊戲 | 成功進入遊戲 |
9.12.15 Cron 排程回歸測試
測試方式:手動觸發排程或修改 Cron 時間,驗證排程邏輯正確性。
每日排程
- [ ] TC-REG-CRON-001: 每日反水結算(00:05) → 計算前一日投注的反水並發放
- [ ] TC-REG-CRON-002: 反水結算只計算前一日資料 → 不包含當日投注
- [ ] TC-REG-CRON-003: 反水結算多站點隔離 → 各站獨立計算
- [ ] TC-REG-CRON-004: 反水結算含 8 種遊戲類型 → 每種類型獨立計算
- [ ] TC-REG-CRON-005: 反水結算用戶無投注 → 不產生反水紀錄
- [ ] TC-REG-CRON-006: 代理佣金日結(03:30) → 計算前一日的佣金
- [ ] TC-REG-CRON-007: 日結含風控檢測 → 異常下線標記
- [ ] TC-REG-CRON-008: 即時賽事更新(每 30 分鐘) → API-Football 資料快取更新
每週排程
- [ ] TC-REG-CRON-009: 代理佣金週結(每週一 03:00) → 計算上週佣金
- [ ] TC-REG-CRON-010: 週結含風控檢測 → 結算風控紀錄完整
- [ ] TC-REG-CRON-011: 週結結果 → 產生 affiliate-settlement 記錄
每月排程
- [ ] TC-REG-CRON-012: VIP 月度保級(每月 1 號 01:00) → 檢查上月投注
- [ ] TC-REG-CRON-013: 保級失敗 → VIP 4 以下可能降級
- [ ] TC-REG-CRON-014: VIP 5+ 保級鎖定 → 不因保級失敗而降級
- [ ] TC-REG-CRON-015: 保級多站點隔離 → 各站獨立保級規則
排程錯誤處理
- [ ] TC-REG-CRON-016: 排程執行中伺服器重啟 → 不產生重複結算
- [ ] TC-REG-CRON-017: 排程執行失敗 → 錯誤日誌記錄
- [ ] TC-REG-CRON-018: 排程重複觸發防護 → 使用鎖機制防止並發
9.12.16 統一回應格式回歸
- [ ] TC-REG-RESP-001: 成功回應格式 →
{ code: 200, message: "ok", result, timestamp, path } - [ ] TC-REG-RESP-002: 業務錯誤格式 →
HTTP 200, code != 200, message 為當前語系錯誤訊息 - [ ] TC-REG-RESP-003: 未授權格式 →
HTTP 401, { code: 401, message: "Unauthorized" } - [ ] TC-REG-RESP-004: timestamp 格式 → ISO 8601 格式
- [ ] TC-REG-RESP-005: path 欄位 → 正確反映請求路徑
- [ ] TC-REG-RESP-006: 錯誤碼查表 → ERROR_CODES[path][code] 在前端正確映射
- [ ] TC-REG-RESP-007: GET /common/enums → 回傳完整 ERROR_CODES
- [ ] TC-REG-RESP-008: 各語系錯誤訊息 → 切換語系後錯誤訊息對應變化
- [ ] TC-REG-RESP-009: 前端不硬寫錯誤文字 → 所有錯誤來自 ERROR_CODES 查表
- [ ] TC-REG-RESP-010: 分頁回應格式 →
{ items: [], pagination: { total, page, pageSize, totalPages } }
9.12.17 元件庫回歸
共用元件驗證
- [ ] TC-REG-COMP-001: SiteTabs — 多站 Tab 切換正確、activeSiteId 追蹤
- [ ] TC-REG-COMP-002: SiteTabs — defaultLabel 顯示「預設站點」文字
- [ ] TC-REG-COMP-003: SiteTabs — copyLabel 顯示「同預設站點」按鈕
- [ ] TC-REG-COMP-004: SiteTabs — onCopyFromDefault 回調觸發
- [ ] TC-REG-COMP-005: FilterBar — text 類型欄位正確篩選
- [ ] TC-REG-COMP-006: FilterBar — date 類型欄位日期選擇器
- [ ] TC-REG-COMP-007: FilterBar — select 類型欄位下拉選單
- [ ] TC-REG-COMP-008: FilterBar — onSearch 搜尋回調
- [ ] TC-REG-COMP-009: FilterBar — onReset 重置所有篩選
- [ ] TC-REG-COMP-010: SimpleTable — 資料列表渲染
- [ ] TC-REG-COMP-011: SimpleTable — loading 狀態顯示 Spinner
- [ ] TC-REG-COMP-012: SimpleTable — emptyText 空資料提示
- [ ] TC-REG-COMP-013: SimpleTable — rowKey 唯一識別
- [ ] TC-REG-COMP-014: SimpleTable — pagination 分頁整合
- [ ] TC-REG-COMP-015: Pagination — 頁碼顯示正確
- [ ] TC-REG-COMP-016: Pagination — onPageChange 回調
- [ ] TC-REG-COMP-017: Pagination — 首頁/末頁/上一頁/下一頁
- [ ] TC-REG-COMP-018: StatusBadge — 各狀態對應正確顏色(colorMap)
- [ ] TC-REG-COMP-019: ConfirmDialog — 標題/描述/變體(destructive)
- [ ] TC-REG-COMP-020: ConfirmDialog — 確認返回 true、取消返回 false
- [ ] TC-REG-COMP-021: HtmlEditor — Tiptap 富文本編輯
- [ ] TC-REG-COMP-022: HtmlEditor — 安全 HTML 輸出(XSS 防護)
- [ ] TC-REG-COMP-023: ExportButton — CSV 匯出觸發
- [ ] TC-REG-COMP-024: ExportButton — 檔案下載成功
- [ ] TC-REG-COMP-025: LoadingSpinner — 載入動畫顯示
- [ ] TC-REG-COMP-026: AccessDenied — 無權限提示頁面
- [ ] TC-REG-COMP-027: TemplatePreviewDialog — 模板預覽顯示
- [ ] TC-REG-COMP-028: TemplatePreviewDialog — 可編輯 + 確認
- [ ] TC-REG-COMP-029: GoogleAuthDialog — idle → qr → verify 狀態流轉
- [ ] TC-REG-COMP-030: ThemeInjector — CSS 變數正確注入
Hook 驗證
- [ ] TC-REG-HOOK-001: useApi — Facade 合併所有 domain hook
- [ ] TC-REG-HOOK-002: useApiQuery — 通用查詢(自動提取 result)
- [ ] TC-REG-HOOK-003: useApiListQuery — 列表查詢(自動正規化分頁)
- [ ] TC-REG-HOOK-004: useApiMutation — 變更操作(invalidateKeys)
- [ ] TC-REG-HOOK-005: useMultiSiteTabs — visibleSites / activeSiteId / activeSiteCode
- [ ] TC-REG-HOOK-006: useMultiSiteTabs — onSiteChange 回調(重置分頁)
- [ ] TC-REG-HOOK-007: useInitEnums — 啟動時拉取 /common/enums
- [ ] TC-REG-HOOK-008: useNotify — success / error / warning / info Toast
- [ ] TC-REG-HOOK-009: usePermissions — can / canRead / canWrite / isRoot
- [ ] TC-REG-HOOK-010: useDomainConfig — 域名解析 siteCode / baseURL
- [ ] TC-REG-HOOK-011: useR2Url — R2 儲存 URL 產生
- [ ] TC-REG-HOOK-012: useConfirm — 確認對話框 hook
Store 驗證
- [ ] TC-REG-STORE-001: siteFilterStore — selectedSiteCode 正確更新
- [ ] TC-REG-STORE-002: siteFilterStore — sites 列表正確填充
- [ ] TC-REG-STORE-003: siteFilterStore — sessionStorage 持久化
- [ ] TC-REG-STORE-004: enumStore — 錯誤碼枚舉快取
- [ ] TC-REG-STORE-005: enumStore — ERROR_CODES[path][code] 查表
- [ ] TC-REG-STORE-006: uiStore — sidebar 展開/收合狀態
9.12.18 API Client 回歸
apiClient 攔截器驗證
- [ ] TC-REG-API-001: 動態 baseURL → 依域名解析 domainConfig
- [ ] TC-REG-API-002: site-name Header → 白牌路由自動注入
- [ ] TC-REG-API-003: locales Header → 從 NEXT_LOCALE cookie 讀取
- [ ] TC-REG-API-004: x-site-code Header → 從 siteFilterStore 注入
- [ ] TC-REG-API-005: Authorization Header → Bearer JWT 從 SessionSync
- [ ] TC-REG-API-006: 401 重試機制 → 清 token → 重取 session → retry
- [ ] TC-REG-API-007: 401 重試失敗 → 導向登入頁
- [ ] TC-REG-API-008: 401 不無限重試 → 最多重試 1 次
- [ ] TC-REG-API-009: getSiteConfigs 跳過 x-site-code → 必須回傳全站資料
httpRequest 三層錯誤處理驗證
- [ ] TC-REG-API-010: 第一層:enumStore 查表 → errorCodes[path][code]
- [ ] TC-REG-API-011: 第一層::id 萬用路徑匹配 → /admin/finance/users/:id
- [ ] TC-REG-API-012: 第二層:呼叫端 errorMessage → string 覆寫
- [ ] TC-REG-API-013: 第二層:呼叫端 errorMessage → Record<code, msg> 覆寫
- [ ] TC-REG-API-014: 第三層:自動 Toast → toast.error(message)
- [ ] TC-REG-API-015: errorToast: false → 停用自動 Toast
9.12.19 前台 (c9-ec) 核心功能回歸
Composable 驗證
- [ ] TC-REG-EC-001: useConfig — domainConfig[hostname] 域名設定
- [ ] TC-REG-EC-002: useConfig — site-name header 自動注入
- [ ] TC-REG-EC-003: Pinia 狀態管理 — 用戶狀態正確
- [ ] TC-REG-EC-004: Nuxt UI v4 — 元件正確渲染
- [ ] TC-REG-EC-005: FingerprintJS v5 — 指紋正確產生
- [ ] TC-REG-EC-006: Zod v4 前端驗證 — 表單驗證正確
前台多語系驗證
- [ ] TC-REG-EC-007: @nuxtjs/i18n — no_prefix 策略(cookie)
- [ ] TC-REG-EC-008: 語系切換 → Cookie 更新
- [ ] TC-REG-EC-009: 5 個語系檔案同步 → 無裸 key
- [ ] TC-REG-EC-010: ICU 格式轉義 → 大括號顯示正確
前台佈局驗證
- [ ] TC-REG-EC-011: A1 佈局 — components/A1/ 元件正確
- [ ] TC-REG-EC-012: 響應式斷點 — mobile / tablet / desktop
- [ ] TC-REG-EC-013: CSS 變數主題注入 — 6 組預設主題
- [ ] TC-REG-EC-014: 底部導航列 — 行動版 Tab
前台 API 整合
- [ ] TC-REG-EC-015: API 統一回應格式 → code / message / result
- [ ] TC-REG-EC-016: ERROR_CODES 查表 → 不硬寫錯誤文字
- [ ] TC-REG-EC-017: JWT 過期處理 → 自動重新登入
- [ ] TC-REG-EC-018: site-name header → 白牌路由正確
9.12.20 後端 (c9-be) 核心功能回歸
框架驗證
- [ ] TC-REG-BE-001: NestJS v11 啟動正常
- [ ] TC-REG-BE-002: TypeORM 0.3.28 連線正常
- [ ] TC-REG-BE-003: MySQL utf8mb4 字元集
- [ ] TC-REG-BE-004: MySQL TZ +08:00
- [ ] TC-REG-BE-005: Redis (@keyv/redis) 連線正常
- [ ] TC-REG-BE-006: Swagger UI 開發環境可用
Guard 與 Decorator 驗證
- [ ] TC-REG-BE-007: JwtAuthGuard — 前台用戶 JWT 認證
- [ ] TC-REG-BE-008: OptionalJwtAuthGuard — 可選認證(未認證返回 null)
- [ ] TC-REG-BE-009: AdminJwtAuthGuard — 後台管理員 JWT 認證
- [ ] TC-REG-BE-010: PermissionsGuard — RBAC 權限檢查
- [ ] TC-REG-BE-011: @AdminSiteCode() — header 優先於 query param
- [ ] TC-REG-BE-012: @SiteName() — 讀取 site-name header
- [ ] TC-REG-BE-013: @RequirePermissions() — 權限元資料
- [ ] TC-REG-BE-014: SiteCodeSubscriber — Entity insert 自動填入 siteCode
工具函式驗證
- [ ] TC-REG-BE-015: parsePagination(query) — 預設 page=1, pageSize=20
- [ ] TC-REG-BE-016: applyDateRange(qb, col, start, end) — BETWEEN 查詢
- [ ] TC-REG-BE-017: truncateUsd(value) — Math.floor(value * 1e6) / 1e6
- [ ] TC-REG-BE-018: resolveText(record, locale) — 多語系文字解析
- [ ] TC-REG-BE-019: maskAccount(account) — 帳號遮罩
Cron 排程驗證
- [ ] TC-REG-BE-020: @nestjs/schedule — Cron 裝飾器正確觸發
- [ ] TC-REG-BE-021: 每日反水結算 — 00:05 觸發
- [ ] TC-REG-BE-022: 月度保級 — 每月 1 號 01:00 觸發
- [ ] TC-REG-BE-023: 代理週結 — 每週一 03:00 觸發
- [ ] TC-REG-BE-024: 代理日結 — 每日 03:30 觸發
- [ ] TC-REG-BE-025: 賽事更新 — 每 30 分鐘觸發
9.13 多站點回歸
9.13.1 站點切換
- [ ] TC-REG-SITE-001: SiteSelector 下拉選單 → 顯示「全部站點」和各站點
- [ ] TC-REG-SITE-002: 切換至全部站點 → SiteTabs 顯示所有站 Tab
- [ ] TC-REG-SITE-003: 切換至單站點 → SiteTabs 僅顯示該站 Tab
- [ ] TC-REG-SITE-004: 站點切換清除快取 → TanStack Query cache 清除
- [ ] TC-REG-SITE-005: AdminContentWrapper remount → key={selectedSiteCode} 觸發重掛載
9.13.2 資料隔離
- [ ] TC-REG-SITE-006: 各站點資料獨立 → 不同站點列表資料不同
- [ ] TC-REG-SITE-007: x-site-code Header → apiClient 自動注入
- [ ] TC-REG-SITE-008: siteCode query param → 全站模式手動傳入
- [ ] TC-REG-SITE-009: SiteCodeSubscriber → Entity insert 自動填入 siteCode
9.13.3 複製與模板
- [ ] TC-REG-SITE-010: 同預設站點(前端拷貝) → 5 個設定頁面正確拷貝
- [ ] TC-REG-SITE-011: 同預設站點(後端 API) → 遊戲/VIP 正確複製
- [ ] TC-REG-SITE-012: 帶入模板 → 只影響當前站點
- [ ] TC-REG-SITE-013: 複製不影響其他站 → B2 站資料不受 A1 複製影響
9.13.4 特殊頁面
- [ ] TC-REG-SITE-014: 管理員/群組/紀錄 → SiteSelector 自動隱藏
- [ ] TC-REG-SITE-015: getSiteConfigs → 必須回傳全站資料(跳過 siteCode 篩選)
9.14 多語系回歸
9.14.1 語系切換
- [ ] TC-REG-I18N-001: 繁體中文 (zh-TW) → 所有文字正確顯示
- [ ] TC-REG-I18N-002: 英文 (en-US) → 所有文字正確顯示
- [ ] TC-REG-I18N-003: 簡體中文 (zh-CN) → 所有文字正確顯示
- [ ] TC-REG-I18N-004: 泰文 (th-TH) → 所有文字正確顯示
- [ ] TC-REG-I18N-005: 越南文 (vi-VN) → 所有文字正確顯示
9.14.2 語系功能
- [ ] TC-REG-I18N-006: Cookie 儲存語系偏好 → 重新載入後保留
- [ ] TC-REG-I18N-007: 後端錯誤訊息多語系 → 根據 locales header 回傳
- [ ] TC-REG-I18N-008: 日期格式多語系 → 各語系日期格式正確
- [ ] TC-REG-I18N-009: ICU 格式大括號轉義 →
'{'xxx'}'正確顯示 - [ ] TC-REG-I18N-010: 5 個語系檔案同步 → 所有 key 都存在於所有檔案
- [ ] TC-REG-I18N-011: ERROR_CODES 查表 → 前端不硬寫錯誤文字
9.15 主題回歸
9.15.1 主題功能
- [ ] TC-REG-THEME-001: 前台 6 組主題預設 → 各主題正確套用
- [ ] TC-REG-THEME-002: 自訂主題 → 後台設定完整色號體系(primary/accent/surface/text/border)
- [ ] TC-REG-THEME-003: CSS 變數注入 → 前台 CSS variables 正確
- [ ] TC-REG-THEME-004: OKLCH 色彩(後台) → 30+ CSS custom properties 正確
- [ ] TC-REG-THEME-005: Light/Dark 模式(後台) → 切換正確
- [ ] TC-REG-THEME-006: 主題持久化 → 重新載入後保留主題設定
- [ ] TC-REG-THEME-007: 各站獨立主題 → site-theme 表按站點區分
9.16 前台佈局回歸
9.16.1 響應式設計
- [ ] TC-REG-LAYOUT-001: 桌面版(>= 1024px) → 完整 sidebar + 主內容區
- [ ] TC-REG-LAYOUT-002: 平板版(768px - 1023px) → 收合 sidebar
- [ ] TC-REG-LAYOUT-003: 手機版(< 768px) → 底部導航列 + 漢堡選單
- [ ] TC-REG-LAYOUT-004: 前台 A1 佈局 → 元件在
components/A1/下
9.16.2 佈局元件
- [ ] TC-REG-LAYOUT-005: 底部導航列 → 行動版 Tab 項目正確(後台可配置)
- [ ] TC-REG-LAYOUT-006: 頁尾 → 連結和版權資訊正確(後台可配置)
- [ ] TC-REG-LAYOUT-007: 「了解更多」區塊 → 內容正確(後台可配置)
- [ ] TC-REG-LAYOUT-008: 吉祥物 → R2 圖片正確載入(後台可配置)
9.16.3 後台佈局
- [ ] TC-REG-LAYOUT-009: (admin) Route Group → 登入後含 sidebar + header
- [ ] TC-REG-LAYOUT-010: (auth) Route Group → 登入頁面獨立佈局
- [ ] TC-REG-LAYOUT-011: Sidebar → 14+ 群組、50+ 項目、Feature Flag + RBAC
- [ ] TC-REG-LAYOUT-012: Header → SiteSelector + 個人資料 + 語系切換
- [ ] TC-REG-LAYOUT-013: scrollToTop → 路由變更自動捲至頂部
- [ ] TC-REG-LAYOUT-014: localeGuard → 不支援的語系自動導向
第 10 章:Bug 報告模板
使用說明:所有測試中發現的缺陷,統一使用以下模板建立 Bug 報告,確保資訊完整、可追蹤。
10.1 Bug 報告標準模板
### Bug 報告
**Bug-ID**: BUG-[模組]-[序號]
**標題**: [簡短描述問題,不超過 80 字元]
---
#### 基本資訊
| 欄位 | 內容 |
|------|------|
| **嚴重程度** | Critical / Major / Minor / Trivial |
| **優先級** | P1 / P2 / P3 / P4 |
| **發現日期** | YYYY-MM-DD |
| **報告人** | [姓名] |
| **指派給** | [開發人員] |
| **狀態** | New / Open / In Progress / Fixed / Verified / Closed |
| **模組** | [所屬模組,如:auth / game / finance / vip / affiliate / risk / report / system] |
| **相關測試案例** | [TC-ID,如:TC-SEC-JWT-002] |
#### 環境資訊
| 項目 | 詳情 |
|------|------|
| **瀏覽器** | Chrome 120 / Safari 17 / Firefox 121 / Edge 120 |
| **作業系統** | macOS 14 / Windows 11 / iOS 17 / Android 14 |
| **螢幕解析度** | 1920x1080 / 1440x900 / 375x812 / 390x844 |
| **語系** | zh-TW / en-US / zh-CN / th-TH / vi-VN |
| **站點** | C9 / A1 / [siteCode] |
| **專案** | c9-ec / c9-ims / c9-be |
| **環境** | localhost / staging / production |
| **分支** | master / [branch-name] |
| **Commit** | [commit hash] |
#### 前置條件
> 描述重現 Bug 前系統需要處於什麼狀態。
1. [條件 1,如:用戶已登入且 VIP 等級為 5]
2. [條件 2,如:站點設定已包含遊戲供應商 BetSolutions]
3. [條件 3,如:Redis 快取已清空]
#### 重現步驟
1. [步驟 1,如:登入後台管理系統]
2. [步驟 2,如:進入「財務管理 > 存款審核」頁面]
3. [步驟 3,如:切換站點至 A1]
4. [步驟 4,如:點擊「搜尋」按鈕]
5. [步驟 5,如:觀察列表顯示]
**重現率**: 100% / 80% / 偶發
#### 預期結果
> 描述系統應該呈現的正確行為。
- [預期結果 1]
- [預期結果 2]
#### 實際結果
> 描述系統實際呈現的錯誤行為。
- [實際結果 1]
- [實際結果 2]
#### 附件
- **截圖**: [附加截圖或標註]
- **影片**: [螢幕錄影 URL]
- **Console 錯誤**:[複製瀏覽器 Console 的錯誤訊息]
- **API 請求/回應**:
```json
// Request
{
"method": "GET",
"url": "/api/admin/finance/deposit-review/list",
"headers": { "x-site-code": "A1" }
}
// Response
{
"code": 500,
"message": "Internal Server Error",
"result": null
}- 後端日誌:
[相關的後端日誌片段]備註
- [任何補充資訊]
- [可能的根因推測]
- [暫時解決方案]
---
## 10.2 嚴重程度定義
| 等級 | 名稱 | 定義 | 範例 |
|------|------|------|------|
| **S1** | Critical(嚴重) | 系統崩潰、資料遺失、安全漏洞、核心功能完全無法使用 | 登入功能完全失效、用戶餘額計算錯誤、SQL 注入漏洞、資料庫連線中斷、JWT Token 可被偽造 |
| **S2** | Major(重大) | 核心功能異常,無替代方案 | 存款流程失敗、遊戲無法啟動、VIP 等級計算錯誤、代理佣金結算異常、報表數據不正確、提款審核無法操作 |
| **S3** | Minor(一般) | 功能異常但有替代方案,或非核心功能異常 | 篩選條件無效(可手動翻頁)、分頁計算偏差、排序不正確、匯出 CSV 少欄位、SiteTabs 切換後未重置分頁 |
| **S4** | Trivial(輕微) | 外觀問題、錯字、微小 UI 不一致 | 圖示顯示不正確、文字對齊偏差、翻譯缺失或錯誤、按鈕 hover 效果不一致、空白頁面缺少 loading 動畫 |
### 嚴重程度判定流程系統可以正常使用嗎? ├── 否 → 系統崩潰/無法啟動? │ ├── 是 → S1 Critical │ └── 否 → 核心功能(認證/金流/遊戲)受影響? │ ├── 是 → S2 Major │ └── 否 → S3 Minor └── 是 → 功能有問題嗎? ├── 是 → 有替代方案嗎? │ ├── 是 → S3 Minor │ └── 否 → S2 Major └── 否 → 僅外觀/文字問題 → S4 Trivial
---
## 10.3 優先級定義
| 等級 | 名稱 | SLA(修復期限) | 說明 | 適用場景 |
|------|------|----------------|------|----------|
| **P1** | 緊急 | 4 小時內修復 | 生產環境阻斷性問題,影響所有用戶 | 登入/存款/提款完全失效、安全漏洞、資料庫崩潰 |
| **P2** | 高 | 24 小時內修復 | 核心功能嚴重異常 | 遊戲無法啟動、VIP 計算錯誤、報表數據錯誤、代理結算異常 |
| **P3** | 中 | 1 週內修復 | 一般功能問題 | 篩選不正確、分頁問題、非核心頁面異常 |
| **P4** | 低 | 下次發布時修復 | 外觀/改善需求 | UI 微調、翻譯修正、非關鍵增強功能 |
### 嚴重程度與優先級的對應關係
| | P1(緊急) | P2(高) | P3(中) | P4(低) |
|---|---|---|---|---|
| **S1(Critical)** | 常見 | 罕見 | - | - |
| **S2(Major)** | 可能 | 常見 | 可能 | - |
| **S3(Minor)** | - | 可能 | 常見 | 可能 |
| **S4(Trivial)** | - | - | 罕見 | 常見 |
> **說明**:嚴重程度和優先級是兩個獨立維度。S2 的 Bug 可能因為只影響少數用戶而被標記為 P3;S3 的 Bug 可能因為影響重要客戶而被提升為 P2。
---
## 10.4 Bug 生命週期
### 狀態流轉圖┌─────────────────────────────────────────────────────────┐ │ │ │ ┌─────┐ ┌──────┐ ┌─────────────┐ │ │ │ New │────▶│ Open │────▶│ In Progress │ │ │ └─────┘ └──────┘ └──────┬──────┘ │ │ │ │ │ │ │ ▼ │ │ │ ┌───────────┐ │ │ │ │ Fixed │ │ │ │ └─────┬─────┘ │ │ │ │ │ │ │ ▼ │ │ │ ┌──────────┐ ┌────────┐ │ │ │ │ Verified │───▶│ Closed │ │ │ │ └────┬─────┘ └────────┘ │ │ │ │ │ │ │ │ 驗證失敗 │ │ │ ▼ │ │ │ ┌──────────┐ │ │ │ │ Reopened │ │ │ │ └──────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────┐ │ │ │ Won't Fix / Duplicate / │ │ │ │ Cannot Reproduce / By Design │ │ │ └────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘
### 各狀態說明
| 狀態 | 說明 | 負責人 | 動作 |
|------|------|--------|------|
| **New** | 新建立的 Bug 報告,尚未審查 | QA | 填寫完整 Bug 報告 |
| **Open** | 已審查確認為有效 Bug,等待指派 | 測試經理 | 確認嚴重程度/優先級,指派開發人員 |
| **In Progress** | 開發人員正在修復中 | 開發人員 | 分析根因,撰寫修復程式碼 |
| **Fixed** | 已完成修復,等待 QA 驗證 | 開發人員 | 提交 commit,標註修復的 Bug-ID |
| **Verified** | QA 驗證修復成功 | QA | 重新執行相關測試案例 |
| **Closed** | Bug 完全解決,流程結束 | QA | 更新測試報告 |
| **Reopened** | 驗證時發現修復不完整 | QA | 說明仍然存在的問題 |
| **Won't Fix** | 決定不修復(風險可接受) | 測試經理/PM | 記錄決策原因 |
| **Duplicate** | 與已知 Bug 重複 | QA | 關聯原始 Bug-ID |
| **Cannot Reproduce** | 無法重現 | QA/開發人員 | 要求提供更多資訊或環境 |
| **By Design** | 屬於設計行為,非 Bug | PM | 確認需求文件 |
---
## 10.5 測試報告模板
### 10.5.1 測試週期報告
```markdown
# 測試報告
## 基本資訊
| 項目 | 內容 |
|------|------|
| **測試週期** | YYYY-MM-DD ~ YYYY-MM-DD |
| **測試範圍** | [列出此次測試涵蓋的模組] |
| **測試環境** | localhost / staging / production |
| **後端版本** | c9-be commit: [hash] |
| **前台版本** | c9-ec commit: [hash] |
| **後台版本** | c9-ims commit: [hash] |
| **資料庫** | MySQL 8.0, Seed 資料版本: [date] |
| **測試人員** | [姓名列表] |
---
## 測試結果統計
### 整體統計
| 狀態 | 數量 | 百分比 |
|------|------|--------|
| 通過 (Passed) | XX | XX% |
| 失敗 (Failed) | XX | XX% |
| 阻塞 (Blocked) | XX | XX% |
| 未執行 (Not Run) | XX | XX% |
| **合計** | **XX** | **100%** |
### 各模組通過率
| 模組 | 測試案例數 | 通過 | 失敗 | 阻塞 | 通過率 |
|------|-----------|------|------|------|--------|
| 認證模組 | XX | XX | XX | XX | XX% |
| 遊戲模組 | XX | XX | XX | XX | XX% |
| 金流模組 | XX | XX | XX | XX | XX% |
| 錢包模組 | XX | XX | XX | XX | XX% |
| VIP 模組 | XX | XX | XX | XX | XX% |
| 代理模組 | XX | XX | XX | XX | XX% |
| 活動模組 | XX | XX | XX | XX | XX% |
| 任務模組 | XX | XX | XX | XX | XX% |
| 站內信模組 | XX | XX | XX | XX | XX% |
| 風控模組 | XX | XX | XX | XX | XX% |
| 報表模組 | XX | XX | XX | XX | XX% |
| 系統管理 | XX | XX | XX | XX | XX% |
| 多站點 | XX | XX | XX | XX | XX% |
| 多語系 | XX | XX | XX | XX | XX% |
| 安全性 | XX | XX | XX | XX | XX% |
| **總計** | **XX** | **XX** | **XX** | **XX** | **XX%** |
---
## 新發現缺陷
### 缺陷統計
| 嚴重程度 | 新增 | 修復 | 未關閉 |
|----------|------|------|--------|
| S1 Critical | XX | XX | XX |
| S2 Major | XX | XX | XX |
| S3 Minor | XX | XX | XX |
| S4 Trivial | XX | XX | XX |
| **合計** | **XX** | **XX** | **XX** |
### 缺陷清單
| Bug-ID | 標題 | 嚴重程度 | 優先級 | 模組 | 狀態 | 指派 |
|--------|------|----------|--------|------|------|------|
| BUG-AUTH-001 | [標題] | S2 | P2 | 認證 | Open | [人員] |
| BUG-FIN-001 | [標題] | S1 | P1 | 金流 | In Progress | [人員] |
| ... | ... | ... | ... | ... | ... | ... |
---
## 風險評估
### 高風險項目
| 風險 | 影響 | 緩解措施 |
|------|------|----------|
| [描述風險 1] | [影響範圍] | [建議處理方式] |
| [描述風險 2] | [影響範圍] | [建議處理方式] |
### 未測試項目
| 項目 | 原因 | 計劃 |
|------|------|------|
| [未測項目 1] | [阻塞原因] | [後續計劃] |
| [未測項目 2] | [阻塞原因] | [後續計劃] |
---
## 結論與建議
### 測試結論
- [ ] 建議發布:所有 P1/P2 缺陷已修復並驗證
- [ ] 有條件發布:存在未關閉的 P2 缺陷,但有暫時解決方案
- [ ] 不建議發布:存在未關閉的 P1 缺陷
### 改善建議
1. [建議 1]
2. [建議 2]
3. [建議 3]
---
## 附錄
### 測試案例執行明細
| TC-ID | 測試說明 | 結果 | 備註 |
|-------|----------|------|------|
| TC-XXX-001 | ... | Pass/Fail/Block | [備註] |
| ... | ... | ... | ... |10.5.2 每日測試摘要模板
# 每日測試摘要 — YYYY-MM-DD
## 今日進度
| 項目 | 數量 |
|------|------|
| 執行測試案例 | XX |
| 通過 | XX |
| 失敗 | XX |
| 新建 Bug | XX |
| 驗證通過 Bug | XX |
## 新發現缺陷
| Bug-ID | 標題 | 嚴重程度 | 模組 |
|--------|------|----------|------|
| ... | ... | ... | ... |
## 阻塞項目
| 項目 | 阻塞原因 | 需要支援 |
|------|----------|----------|
| ... | ... | ... |
## 明日計劃
- [ ] [計劃 1]
- [ ] [計劃 2]附錄 A:測試資料準備指南
目標:提供完整的測試資料建立流程,確保測試環境一致性和可重複性。
A.1 Seed 腳本
A.1.1 執行 Seed
C9 平台提供一鍵 Seed 腳本,可建立 5 個站點 × 30 個用戶及所有相關資料。
# 確保後端已安裝相依套件
cd c9-be && yarn install
# 執行 Seed 腳本(建立所有測試資料)
npx ts-node scripts/seed-all.tsA.1.2 Seed 資料涵蓋範圍
| 資料表 | 數量 | 說明 |
|---|---|---|
site-config | 5 | 站點配置(C9, A1, B2, D3, E4) |
site-theme | 5+ | 各站點主題 |
auth-user | 150 | 5 站 × 30 用戶 |
admin-user | 5+ | 各權限等級管理員 |
admin-group | 4 | root / super_admin / general_admin / custom |
game-provider | 10+ | 各站遊戲供應商 |
game-type-config | 40+ | 8 遊戲類型 × 5 站 |
vip-level | 75+ | 15 等級 × 5 站 |
vip-rebate | 600+ | 15 等級 × 8 遊戲類型 × 5 站 |
deposit-order | 300+ | 各種狀態的存款訂單 |
withdrawal-order | 200+ | 各種狀態的提款訂單 |
bet-order | 500+ | 各遊戲類型的投注紀錄 |
promo | 30+ | 各站活動 |
promo-tag | 20+ | 活動標籤 |
notification | 100+ | 站內信 |
bank-card | 50+ | 銀行卡資料 |
credit-card | 30+ | 信用卡資料 |
crypto-address | 30+ | 加密地址 |
vendor-group | 10+ | 金流群組 |
vendor-channel | 20+ | 金流通道 |
risk-ip-rule | 20+ | IP 黑白名單 |
risk-game-blacklist | 10+ | 遊戲黑名單 |
affiliate-* | 50+ | 代理相關資料 |
alliance-* | 100+ | 聯盟佣金費率/等級/里程碑 |
mission | 15+ | 任務資料 |
A.1.3 Seed 後驗證
# 確認資料正確建立(進入 MySQL)
mysql -u root -p
# 檢查各表資料量
SELECT 'site-config' AS tbl, COUNT(*) AS cnt FROM `site-config`
UNION ALL SELECT 'auth-user', COUNT(*) FROM `auth-user`
UNION ALL SELECT 'admin-user', COUNT(*) FROM `admin-user`
UNION ALL SELECT 'game-provider', COUNT(*) FROM `game-provider`
UNION ALL SELECT 'vip-level', COUNT(*) FROM `vip-level`
UNION ALL SELECT 'deposit-order', COUNT(*) FROM `deposit-order`;A.2 測試管理員帳號
A.2.1 建立測試管理員
# 方式 1:透過 Seed 腳本(自動建立)
npx ts-node scripts/seed-all.ts
# 方式 2:透過 API(手動建立)
curl -X POST http://localhost:8080/api/admin/register \
-H "Content-Type: application/json" \
-d '{
"account": "test_root",
"password": "Test123!@#",
"name": "測試管理員",
"groupId": 1
}'A.2.2 預設管理員帳號
| 帳號 | 密碼 | 群組 | 用途 |
|---|---|---|---|
root_admin | Root123!@# | root | 最高權限,可存取所有功能 |
super_admin | Super123!@# | super_admin | 進階管理員(無 site-config 權限) |
general_admin | General123!@# | general_admin | 一般管理員(僅讀取權限) |
custom_admin | Custom123!@# | custom | 自訂權限(測試 RBAC) |
2fa_admin | Twofa123!@# | root | 已啟用 2FA 的管理員 |
A.2.3 各群組權限對照
| 模組 | root | super_admin | general_admin | custom(範例) |
|---|---|---|---|---|
| admin | R/W | R/W | R | R |
| admin-group | R/W | R/W | R | - |
| admin-log | R | R | R | - |
| user | R/W | R/W | R | R/W |
| deposit | R/W | R/W | R | R/W |
| withdrawal | R/W | R/W | R | R |
| promo | R/W | R/W | R | - |
| promo-tag | R/W | R/W | R | - |
| affiliate | R/W | R/W | R | - |
| vip | R/W | R/W | R | R |
| game | R/W | R/W | R | - |
| risk | R/W | R/W | R | R |
| report | R | R | R | R |
| vendor | R/W | R/W | R | - |
| finance | R/W | R/W | R | R/W |
| site-config | R/W | - | - | - |
R = Read, W = Write, - = 無權限
A.3 測試前台用戶帳號
A.3.1 Seed 用戶帳號格式
Seed 腳本建立的用戶帳號格式為:
帳號格式: test_user_{siteCode}_{number}
密碼統一: Test123!@#| 帳號範例 | 站點 | VIP 等級 | 代理身份 | 說明 |
|---|---|---|---|---|
test_user_C9_001 | C9 | 0 | 否 | 普通新用戶 |
test_user_C9_002 | C9 | 5 | 否 | VIP 5 用戶 |
test_user_C9_003 | C9 | 10 | 是 | VIP 10 + 代理 |
test_user_C9_004 | C9 | 15 | 否 | 最高 VIP 等級 |
test_user_A1_001 | A1 | 0 | 否 | A1 站普通用戶 |
test_user_A1_002 | A1 | 3 | 是 | A1 站代理 |
oauth_user_C9_001 | C9 | 0 | 否 | Google OAuth 用戶 |
A.3.2 特殊測試帳號
| 帳號 | 用途 | 特殊設定 |
|---|---|---|
test_blacklist_001 | 遊戲黑名單測試 | 已被加入遊戲全封鎖黑名單 |
test_blacklist_002 | 遊戲類型封鎖測試 | 已被封鎖 SLOT 類型遊戲 |
test_agent_top | 頂層代理 | 無上線代理 |
test_agent_mid | 中層代理 | 上線為 test_agent_top |
test_agent_bot | 底層代理 | 上線為 test_agent_mid(第 3 層) |
test_rich_user | 高餘額用戶 | USD 餘額 100,000.000000 |
test_poor_user | 低餘額用戶 | USD 餘額 0.000000 |
A.4 測試遊戲資料
A.4.1 遊戲供應商
| providerCode | 名稱 | 支援遊戲類型 | S2S 回調 |
|---|---|---|---|
betsolutions | BetSolutions | SLOT, LIVE, CHESS | /game/betsolutions/callback |
rsg | RSG | SLOT, FISH | /game/rsg/callback(DES 加解密) |
A.4.2 遊戲類型 ID 對照
| gameType | ID | 中文名稱 |
|---|---|---|
| SPORTS | 1 | 體育 |
| SLOT | 2 | 老虎機 |
| LIVE | 3 | 真人 |
| LOTTERY | 4 | 彩票 |
| CHESS | 5 | 棋牌 |
| ESPORTS | 8 | 電競 |
| CRYPTO | 9 | 加密貨幣 |
| FISH | 10 | 捕魚 |
A.5 測試金流資料
A.5.1 金流測試環境
| 金流商 | 測試環境 | 測試帳號 |
|---|---|---|
| 萬通金流(ATM) | 沙盒模式 | 詳見萬通金流沙盒文件 |
| 萬通金流(信用卡) | 沙盒模式 | 測試信用卡號: 4111-1111-1111-1111 |
| USDT(加密貨幣) | 測試網 | 使用 Testnet 地址 |
A.5.2 銀行卡測試資料
| 銀行代碼 | 銀行名稱 | 測試帳號 | 持卡人 |
|---|---|---|---|
| 004 | 台灣銀行 | 0041234567890 | 測試用戶一 |
| 005 | 土地銀行 | 0051234567890 | 測試用戶二 |
| 012 | 台北富邦 | 0121234567890 | 測試用戶三 |
A.5.3 加密地址測試資料
| 網路 | 幣種 | 測試地址 | 說明 |
|---|---|---|---|
| TRC20 | USDT | TTestAddress123456789 | Tron 測試網地址 |
| ERC20 | USDT | 0xTestAddress123456789 | Ethereum 測試網地址 |
| BEP20 | USDT | 0xBscTestAddress12345 | BSC 測試網地址 |
A.6 測試資料清理
A.6.1 清理腳本
# 清除所有測試資料(危險操作!僅限開發環境)
mysql -u root -p -e "
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE \`deposit-order\`;
TRUNCATE TABLE \`withdrawal-order\`;
TRUNCATE TABLE \`bet-order\`;
TRUNCATE TABLE \`bet-detail\`;
TRUNCATE TABLE \`game-transaction\`;
TRUNCATE TABLE \`game-play-log\`;
TRUNCATE TABLE \`vip-rebate-log\`;
TRUNCATE TABLE \`promo-claim\`;
TRUNCATE TABLE \`mission-progress\`;
TRUNCATE TABLE \`mission-claim\`;
TRUNCATE TABLE \`notification\`;
TRUNCATE TABLE \`notification-read\`;
TRUNCATE TABLE \`admin-operation-log\`;
TRUNCATE TABLE \`r2-operation-log\`;
TRUNCATE TABLE \`auth-user-login-log\`;
SET FOREIGN_KEY_CHECKS = 1;
" c9_dbA.6.2 Redis 快取清理
# 清除所有 Redis 快取
redis-cli FLUSHALL
# 清除特定前綴的快取
redis-cli KEYS "cache:vip:*" | xargs redis-cli DEL
redis-cli KEYS "cache:auth:*" | xargs redis-cli DEL
redis-cli KEYS "cache:admin:*" | xargs redis-cli DEL附錄 B:API 測試工具使用指南
目標:提供使用各種工具進行 API 測試的操作指南。
B.1 Swagger UI
B.1.1 存取方式
URL: http://localhost:8080/api/docsSwagger UI 在開發環境自動啟用,提供互動式 API 文件和測試功能。
B.1.2 認證步驟
取得 Token:
- 在 Swagger 中找到
POST /api/auth/login(前台用戶)或POST /api/admin/login(後台管理員) - 填入帳號密碼,執行請求
- 從回應中複製
result.token
- 在 Swagger 中找到
設定認證:
- 點擊頁面右上方的「Authorize」按鈕
- 在 Bearer Token 欄位輸入:
Bearer {token} - 點擊「Authorize」確認
測試 API:
- 選擇要測試的端點
- 填入必要參數
- 點擊「Try it out」→「Execute」
- 查看 Response
B.1.3 注意事項
- Swagger UI 僅在開發環境可用,生產環境應關閉
- 部分端點需要設定
x-site-codeheader(在 Parameters 中填入) - 檔案上傳端點需使用 multipart/form-data 格式
B.2 cURL 測試範例
B.2.1 前台認證
# 登入取得 Token
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-H "site-name: C9" \
-d '{
"account": "test_user_C9_001",
"password": "Test123!@#",
"fingerprint": "test-fingerprint-001"
}'
# 回應範例
# {
# "code": 200,
# "message": "ok",
# "result": {
# "token": "eyJhbGciOiJIUzI1NiIs...",
# "user": { "id": 1, "account": "test_user_C9_001", ... }
# }
# }B.2.2 帶認證的 API 呼叫
# 設定 Token 變數
TOKEN="eyJhbGciOiJIUzI1NiIs..."
# 查詢個人資料
curl -X GET http://localhost:8080/api/auth/profile \
-H "Authorization: Bearer $TOKEN" \
-H "site-name: C9" \
-H "locales: zh-TW"
# 查詢遊戲供應商列表
curl -X GET "http://localhost:8080/api/game/providers?gameType=2" \
-H "Authorization: Bearer $TOKEN" \
-H "site-name: C9"
# 查詢站內信
curl -X GET "http://localhost:8080/api/inbox/list?page=1&pageSize=20" \
-H "Authorization: Bearer $TOKEN" \
-H "site-name: C9"B.2.3 後台 Admin API
# 管理員登入
curl -X POST http://localhost:8080/api/admin/login \
-H "Content-Type: application/json" \
-d '{
"account": "root_admin",
"password": "Root123!@#"
}'
# 設定 Admin Token
ADMIN_TOKEN="eyJhbGciOiJIUzI1NiIs..."
# 查詢用戶列表(指定站點)
curl -X GET "http://localhost:8080/api/admin/finance/users/list?page=1&pageSize=20" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "x-site-code: C9"
# 查詢用戶列表(全站)
curl -X GET "http://localhost:8080/api/admin/finance/users/list?page=1&pageSize=20" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# 查詢站點列表
curl -X GET http://localhost:8080/api/site-config/admin/list \
-H "Authorization: Bearer $ADMIN_TOKEN"
# 查詢報表(帶篩選)
curl -X GET "http://localhost:8080/api/admin/reports/overview?startDate=2026-01-01&endDate=2026-03-01" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "x-site-code: C9"
# 查詢 VIP 等級
curl -X GET "http://localhost:8080/api/vip/admin/levels" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "x-site-code: C9"B.2.4 多站點測試
# 使用 x-site-code header 切換站點
# C9 站
curl -X GET "http://localhost:8080/api/admin/finance/deposit-review/list?page=1" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "x-site-code: C9"
# A1 站(相同端點,不同站點)
curl -X GET "http://localhost:8080/api/admin/finance/deposit-review/list?page=1" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "x-site-code: A1"
# 使用 siteCode query param(備選方式)
curl -X GET "http://localhost:8080/api/admin/finance/deposit-review/list?page=1&siteCode=C9" \
-H "Authorization: Bearer $ADMIN_TOKEN"B.2.5 檔案上傳測試
# R2 檔案上傳
curl -X POST http://localhost:8080/api/admin/r2/upload \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "site-name: C9" \
-F "file=@/path/to/test-image.png" \
-F "folder=test"
# 域名素材上傳(logo)
curl -X POST http://localhost:8080/api/site-config/admin/1/domain-asset \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-F "logoSmall=@/path/to/logo-small.png"B.3 Postman 設定指南
B.3.1 環境變數設定
| 變數名 | 初始值 | 說明 |
|---|---|---|
base_url | http://localhost:8080/api | API 基礎 URL |
user_token | (空) | 前台用戶 JWT Token |
admin_token | (空) | 後台管理員 AdminJWT Token |
site_code | C9 | 當前測試站點 |
site_name | C9 | 前台 site-name header |
locale | zh-TW | 當前測試語系 |
B.3.2 Pre-request Script(自動認證)
// 自動登入並設定 Token
const baseUrl = pm.environment.get("base_url");
// 前台用戶自動登入
pm.sendRequest({
url: baseUrl + "/auth/login",
method: "POST",
header: {
"Content-Type": "application/json",
"site-name": pm.environment.get("site_name")
},
body: {
mode: "raw",
raw: JSON.stringify({
account: "test_user_C9_001",
password: "Test123!@#",
fingerprint: "postman-test"
})
}
}, function (err, response) {
if (!err) {
const result = response.json();
if (result.code === 200) {
pm.environment.set("user_token", result.result.token);
}
}
});B.3.3 Collection 結構建議
C9 Platform API Tests/
├── Auth/
│ ├── [POST] Register
│ ├── [POST] Login
│ ├── [GET] Profile
│ └── [POST] Logout
├── Admin Auth/
│ ├── [POST] Admin Login
│ └── [GET] Admin Profile
├── Game/
│ ├── [GET] Providers
│ ├── [POST] Launch
│ └── [GET] Type Configs
├── Finance/
│ ├── [POST] Deposit
│ ├── [GET] Deposit List (Admin)
│ ├── [POST] Deposit Review (Admin)
│ ├── [GET] Withdrawal List (Admin)
│ └── [POST] Withdrawal Review (Admin)
├── VIP/
│ ├── [GET] Levels
│ ├── [GET] Rebates
│ └── [GET] Admin Levels
├── Reports/
│ ├── [GET] Overview
│ ├── [GET] Profit Loss
│ ├── [GET] Bet Records
│ └── [GET] Player Summary
├── Site Config/
│ ├── [GET] Site List
│ ├── [GET] Themes
│ └── [PATCH] Update Site
└── Risk Control/
├── [GET] IP Rules
├── [POST] Create IP Rule
└── [GET] Game BlacklistB.3.4 共用 Header 設定
在 Collection 層級設定共用 Header:
| Header | Value | 說明 |
|---|---|---|
Authorization | Bearer 或 Bearer | 依測試對象選擇 |
Content-Type | application/json | JSON 格式 |
x-site-code | | 多站點篩選(Admin API) |
site-name | | 前台站點名稱 |
locales | | 多語系 |
B.4 自動化 API 測試腳本
B.4.1 使用 k6 進行負載測試
// k6-load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 50, // 50 虛擬用戶
duration: '5m', // 持續 5 分鐘
thresholds: {
http_req_duration: ['p(95)<500'], // P95 < 500ms
http_req_failed: ['rate<0.01'], // 失敗率 < 1%
},
};
const BASE_URL = 'http://localhost:8080/api';
export function setup() {
// 登入取得 Token
const loginRes = http.post(`${BASE_URL}/admin/login`, JSON.stringify({
account: 'root_admin',
password: 'Root123!@#',
}), { headers: { 'Content-Type': 'application/json' } });
return { token: loginRes.json().result.token };
}
export default function (data) {
const headers = {
'Authorization': `Bearer ${data.token}`,
'x-site-code': 'C9',
};
// 測試列表查詢
const listRes = http.get(
`${BASE_URL}/admin/finance/users/list?page=1&pageSize=20`,
{ headers }
);
check(listRes, {
'status is 200': (r) => r.status === 200,
'has items': (r) => r.json().result !== null,
});
sleep(1);
}# 執行 k6 負載測試
k6 run k6-load-test.jsB.5 完整 API 端點測試矩陣
以下矩陣列出所有 205+ 端點的測試狀態追蹤表。QA 可用此表逐一確認每個端點的測試覆蓋。
B.5.1 認證模組 API(17 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | POST | /auth/register | 無 | [ ] | 帳號密碼註冊 |
| 2 | POST | /auth/login | 無 | [ ] | 帳密登入(含指紋) |
| 3 | POST | /auth/google | 無 | [ ] | Google OAuth 登入 |
| 4 | POST | /auth/telegram | 無 | [ ] | Telegram OAuth 登入 |
| 5 | GET | /auth/profile | JWT | [ ] | 取得個人資料 |
| 6 | PATCH | /auth/profile | JWT | [ ] | 更新個人資料 |
| 7 | POST | /auth/change-password | JWT | [ ] | 修改密碼 |
| 8 | POST | /auth/set-password | JWT | [ ] | OAuth 用戶設定密碼 |
| 9 | POST | /auth/logout | JWT | [ ] | 登出 |
| 10 | POST | /auth/upload-avatar | JWT | [ ] | 上傳頭像 |
| 11 | POST | /auth/enable-2fa | JWT | [ ] | 啟用 2FA |
| 12 | POST | /auth/verify-2fa | JWT | [ ] | 驗證 2FA |
| 13 | POST | /auth/disable-2fa | JWT | [ ] | 停用 2FA |
| 14 | GET | /auth/2fa-status | JWT | [ ] | 查詢 2FA 狀態 |
| 15 | POST | /auth/refresh-token | JWT | [ ] | 刷新 Token |
| 16 | GET | /auth/check-account | 無 | [ ] | 檢查帳號是否存在 |
| 17 | GET | /auth/check-email | 無 | [ ] | 檢查 Email 是否存在 |
B.5.2 遊戲模組 API(17 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | GET | /game/providers | Optional JWT | [ ] | 遊戲供應商列表 |
| 2 | GET | /game/providers/:id | Optional JWT | [ ] | 供應商詳情 |
| 3 | GET | /game/type-configs | Optional JWT | [ ] | 遊戲類型配置 |
| 4 | POST | /game/launch | JWT | [ ] | 啟動遊戲 |
| 5 | POST | /game/demo | 無 | [ ] | 遊戲試玩 |
| 6 | GET | /game/recent | JWT | [ ] | 最近遊玩 |
| 7 | POST | /game/betsolutions/callback | S2S | [ ] | BetSolutions 回調 |
| 8 | POST | /game/rsg/callback | S2S | [ ] | RSG 回調 |
| 9 | GET | /game/admin/providers | AdminJWT | [ ] | 管理供應商列表 |
| 10 | POST | /game/admin/providers | AdminJWT | [ ] | 新增供應商 |
| 11 | PATCH | /game/admin/providers/:id | AdminJWT | [ ] | 更新供應商 |
| 12 | DELETE | /game/admin/providers/:id | AdminJWT | [ ] | 刪除供應商 |
| 13 | GET | /game/admin/type-configs | AdminJWT | [ ] | 管理類型配置 |
| 14 | POST | /game/admin/type-configs | AdminJWT | [ ] | 新增類型配置 |
| 15 | PATCH | /game/admin/type-configs/:id | AdminJWT | [ ] | 更新類型配置 |
| 16 | DELETE | /game/admin/type-configs/:id | AdminJWT | [ ] | 刪除類型配置 |
| 17 | POST | /game/admin/copy-site-data | AdminJWT | [ ] | 跨站複製 |
B.5.3 金流模組 API(26 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | POST | /deposit | JWT | [ ] | 存款 |
| 2 | GET | /deposit/orders | JWT | [ ] | 存款訂單列表 |
| 3 | GET | /deposit/exchange-rate | JWT | [ ] | 即時匯率 |
| 4 | POST | /vendor/callback/:code | S2S | [ ] | 金流回調 |
| 5 | GET | /wallet/bank-cards | JWT | [ ] | 銀行卡列表 |
| 6 | POST | /wallet/bank-cards | JWT | [ ] | 新增銀行卡 |
| 7 | DELETE | /wallet/bank-cards/:id | JWT | [ ] | 刪除銀行卡 |
| 8 | GET | /wallet/credit-cards | JWT | [ ] | 信用卡列表 |
| 9 | POST | /wallet/credit-cards | JWT | [ ] | 新增信用卡 |
| 10 | DELETE | /wallet/credit-cards/:id | JWT | [ ] | 刪除信用卡 |
| 11 | GET | /wallet/crypto-addresses | JWT | [ ] | 加密地址列表 |
| 12 | POST | /wallet/crypto-addresses | JWT | [ ] | 新增加密地址 |
| 13 | DELETE | /wallet/crypto-addresses/:id | JWT | [ ] | 刪除加密地址 |
| 14 | POST | /withdrawal | JWT | [ ] | 提款 |
| 15 | GET | /withdrawal/orders | JWT | [ ] | 提款訂單列表 |
| 16 | GET | /admin/finance/deposit-review/list | AdminJWT | [ ] | 存款審核列表 |
| 17 | POST | /admin/finance/deposit-review/:id/review | AdminJWT | [ ] | 審核存款 |
| 18 | GET | /admin/finance/withdrawals/list | AdminJWT | [ ] | 提款列表 |
| 19 | POST | /admin/finance/withdrawals/:id/review | AdminJWT | [ ] | 審核提款 |
| 20 | POST | /admin/finance/withdrawals/:id/upload | AdminJWT | [ ] | 上傳憑證 |
| 21 | POST | /admin/finance/withdrawals/:id/complete | AdminJWT | [ ] | 完成提款 |
| 22 | POST | /admin/finance/adjust-balance | AdminJWT | [ ] | 手動調帳 |
| 23 | GET | /admin/vendor-groups/list | AdminJWT | [ ] | 金流群組列表 |
| 24 | POST | /admin/vendor-groups/create | AdminJWT | [ ] | 新增金流群組 |
| 25 | GET | /admin/vendor-channels/list | AdminJWT | [ ] | 金流通道列表 |
| 26 | POST | /admin/vendor-channels/create | AdminJWT | [ ] | 新增金流通道 |
B.5.4 VIP 模組 API(13 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | GET | /vip/levels | JWT | [ ] | VIP 等級列表 |
| 2 | GET | /vip/my-level | JWT | [ ] | 我的 VIP 等級 |
| 3 | GET | /vip/rebates | JWT | [ ] | 反水規則 |
| 4 | GET | /vip/admin/levels | AdminJWT | [ ] | 管理 VIP 等級 |
| 5 | POST | /vip/admin/levels | AdminJWT | [ ] | 新增 VIP 等級 |
| 6 | PATCH | /vip/admin/levels/:id | AdminJWT | [ ] | 更新 VIP 等級 |
| 7 | DELETE | /vip/admin/levels/:id | AdminJWT | [ ] | 刪除 VIP 等級 |
| 8 | GET | /vip/admin/rebates | AdminJWT | [ ] | 管理反水規則 |
| 9 | POST | /vip/admin/rebates/bulk | AdminJWT | [ ] | 批次更新反水 |
| 10 | POST | /vip/admin/load-template | AdminJWT | [ ] | 載入模板 |
| 11 | GET | /vip/admin/preview-template | AdminJWT | [ ] | 預覽模板 |
| 12 | POST | /vip/admin/copy-site-data | AdminJWT | [ ] | 同預設站點 |
| 13 | GET | /admin/reports/vip-players | AdminJWT | [ ] | VIP 玩家列表 |
B.5.5 代理模組 API(51 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | POST | /affiliate/apply | JWT | [ ] | 申請成為代理 |
| 2 | GET | /affiliate/dashboard | JWT | [ ] | 代理儀表板 |
| 3 | GET | /affiliate/downlines | JWT | [ ] | 下線列表 |
| 4 | GET | /affiliate/commissions | JWT | [ ] | 佣金明細 |
| 5 | GET | /affiliate/settlements | JWT | [ ] | 結算紀錄 |
| 6 | POST | /affiliate/withdraw | JWT | [ ] | 代理提款 |
| 7 | GET | /affiliate/withdrawals | JWT | [ ] | 提款紀錄 |
| 8 | GET | /affiliate/referral-codes | JWT | [ ] | 推廣碼列表 |
| 9 | POST | /affiliate/referral-codes | JWT | [ ] | 建立推廣碼 |
| 10 | DELETE | /affiliate/referral-codes/:id | JWT | [ ] | 刪除推廣碼 |
| 11 | GET | /affiliate/alliance-info | 無 | [ ] | 聯盟資訊(公開) |
| 12 | GET | /affiliate/tier-info | 無 | [ ] | 等級資訊(公開) |
| 13 | GET | /affiliate/vip-milestones | 無 | [ ] | VIP 里程碑(公開) |
| 14 | GET | /affiliate/admin/agents | AdminJWT | [ ] | 代理列表 |
| 15 | POST | /affiliate/admin/create-agent | AdminJWT | [ ] | 新增代理 |
| 16 | GET | /affiliate/admin/settlements | AdminJWT | [ ] | 結算列表 |
| 17 | POST | /affiliate/admin/settlements/:id/review | AdminJWT | [ ] | 審核結算 |
| 18 | GET | /affiliate/admin/settlements/:id/risk-logs | AdminJWT | [ ] | 風控紀錄 |
| 19 | GET | /affiliate/admin/withdrawals | AdminJWT | [ ] | 提款列表 |
| 20 | POST | /affiliate/admin/withdrawals/:id/review | AdminJWT | [ ] | 審核提款 |
| 21 | POST | /affiliate/admin/withdrawals/:id/complete | AdminJWT | [ ] | 完成提款 |
| 22 | POST | /affiliate/admin/bind | AdminJWT | [ ] | 手動綁定 |
| 23 | GET | /affiliate/admin/bind-logs | AdminJWT | [ ] | 綁定紀錄 |
| 24 | GET | /affiliate/admin/commission-rates | AdminJWT | [ ] | 佣金費率 |
| 25 | POST | /affiliate/admin/commission-rates | AdminJWT | [ ] | 設定費率 |
| 26 | DELETE | /affiliate/admin/commission-rates | AdminJWT | [ ] | 刪除費率 |
| 27 | GET | /affiliate/admin/vip-milestones | AdminJWT | [ ] | VIP 里程碑 |
| 28 | POST | /affiliate/admin/vip-milestones | AdminJWT | [ ] | 設定里程碑 |
| 29 | DELETE | /affiliate/admin/vip-milestones | AdminJWT | [ ] | 刪除里程碑 |
| 30 | GET | /affiliate/admin/agent-tiers | AdminJWT | [ ] | 代理等級 |
| 31 | POST | /affiliate/admin/agent-tiers | AdminJWT | [ ] | 設定等級 |
| 32 | DELETE | /affiliate/admin/agent-tiers | AdminJWT | [ ] | 刪除等級 |
| 33 | GET | /affiliate/admin/preview-template | AdminJWT | [ ] | 預覽模板 |
| 34 | POST | /affiliate/admin/load-template | AdminJWT | [ ] | 載入模板 |
| 35 | POST | /affiliate/admin/set-agent-tier | AdminJWT | [ ] | 設定代理等級 |
| 36 | POST | /affiliate/admin/trigger-settlement | AdminJWT | [ ] | 觸發週結 |
| 37 | POST | /affiliate/admin/trigger-daily-settlement | AdminJWT | [ ] | 觸發日結 |
其餘 14 個代理端點(前台代理操作和其他管理端點)依相同格式追蹤。
B.5.6 站點管理 API(12 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | GET | /site-config/admin/list | AdminJWT | [ ] | 站點列表 |
| 2 | POST | /site-config/admin | AdminJWT | [ ] | 新增站點 |
| 3 | PATCH | /site-config/admin/:id | AdminJWT | [ ] | 更新站點 |
| 4 | DELETE | /site-config/admin/:id | AdminJWT | [ ] | 刪除站點 |
| 5 | GET | /site-config/admin/:id/themes | AdminJWT | [ ] | 主題列表 |
| 6 | POST | /site-config/admin/:id/themes | AdminJWT | [ ] | 新增主題 |
| 7 | PATCH | /site-config/admin/themes/:id | AdminJWT | [ ] | 更新主題 |
| 8 | DELETE | /site-config/admin/themes/:id | AdminJWT | [ ] | 刪除主題 |
| 9 | POST | /site-config/admin/:id/domain-asset | AdminJWT | [ ] | 上傳域名素材 |
| 10 | POST | /site-config/admin/:id/customer-service-icon | AdminJWT | [ ] | 上傳客服圖示 |
| 11 | PATCH | /site-config/admin/:id/mascots | AdminJWT | [ ] | 更新吉祥物 |
| 12 | GET | /site-config/admin/:code/customer-service | AdminJWT | [ ] | 取得客服設定 |
B.5.7 報表 API(10 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | GET | /admin/reports/players | AdminJWT | [ ] | 玩家報表 |
| 2 | GET | /admin/reports/vip-players | AdminJWT | [ ] | VIP 玩家 |
| 3 | GET | /admin/reports/bet-records | AdminJWT | [ ] | 投注紀錄 |
| 4 | GET | /admin/reports/overview | AdminJWT | [ ] | 總覽報表 |
| 5 | GET | /admin/reports/profit-loss | AdminJWT | [ ] | 損益報表 |
| 6 | GET | /admin/reports/games | AdminJWT | [ ] | 遊戲報表 |
| 7 | GET | /admin/reports/promos | AdminJWT | [ ] | 活動報表 |
| 8 | GET | /admin/reports/player-summary | AdminJWT | [ ] | 玩家摘要 |
| 9 | GET | /admin/reports/r2-logs | AdminJWT | [ ] | R2 日誌 |
| 10 | GET | /admin/reports/export | AdminJWT | [ ] | CSV 匯出 |
B.5.8 後台管理 API(25 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | POST | /admin/login | 無 | [ ] | 管理員登入 |
| 2 | POST | /admin/register | AdminJWT | [ ] | 新增管理員 |
| 3 | GET | /admin/profile | AdminJWT | [ ] | 管理員個人資料 |
| 4 | PATCH | /admin/profile | AdminJWT | [ ] | 更新個人資料 |
| 5 | POST | /admin/google-auth/setup | AdminJWT | [ ] | 設定 2FA |
| 6 | POST | /admin/google-auth/verify | AdminJWT | [ ] | 驗證 2FA |
| 7 | POST | /admin/google-auth/disable | AdminJWT | [ ] | 停用 2FA |
| 8 | GET | /admin/google-auth/status | AdminJWT | [ ] | 2FA 狀態 |
| 9 | GET | /admin/permissions/all | AdminJWT | [ ] | 所有權限 |
| 10 | GET | /admin/list | AdminJWT | [ ] | 管理員列表 |
| 11 | GET | /admin/:id | AdminJWT | [ ] | 管理員詳情 |
| 12 | PATCH | /admin/:id | AdminJWT | [ ] | 更新管理員 |
| 13 | DELETE | /admin/:id | AdminJWT | [ ] | 刪除管理員 |
| 14 | PATCH | /admin/:id/status | AdminJWT | [ ] | 停用/啟用 |
| 15 | GET | /admin/groups/list | AdminJWT | [ ] | 群組列表 |
| 16 | POST | /admin/groups/create | AdminJWT | [ ] | 新增群組 |
| 17 | GET | /admin/groups/:id | AdminJWT | [ ] | 群組詳情 |
| 18 | PATCH | /admin/groups/:id | AdminJWT | [ ] | 更新群組 |
| 19 | DELETE | /admin/groups/:id | AdminJWT | [ ] | 刪除群組 |
| 20 | GET | /admin/logs/list | AdminJWT | [ ] | 操作紀錄 |
| 21 | GET | /admin/finance/users/list | AdminJWT | [ ] | 用戶列表 |
| 22 | GET | /admin/finance/users/:id | AdminJWT | [ ] | 用戶詳情 |
| 23 | PATCH | /admin/finance/users/:id | AdminJWT | [ ] | 更新用戶 |
| 24 | PUT | /admin/users/:userId/vendor-group | AdminJWT | [ ] | 金流群組 |
| 25 | GET | /admin/r2/list | AdminJWT | [ ] | R2 列表 |
B.5.9 風控 API(8 個端點)
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | GET | /admin/risk/ip-rules/list | AdminJWT | [ ] | IP 規則列表 |
| 2 | POST | /admin/risk/ip-rules/create | AdminJWT | [ ] | 新增 IP 規則 |
| 3 | PATCH | /admin/risk/ip-rules/:id | AdminJWT | [ ] | 更新 IP 規則 |
| 4 | DELETE | /admin/risk/ip-rules/:id | AdminJWT | [ ] | 刪除 IP 規則 |
| 5 | GET | /admin/risk/lookup | AdminJWT | [ ] | IP/FP 反查 |
| 6 | GET | /admin/risk/login-failures | AdminJWT | [ ] | 登入失敗 |
| 7 | GET | /admin/risk/game-blacklist | AdminJWT | [ ] | 遊戲黑名單 |
| 8 | POST | /admin/risk/game-blacklist | AdminJWT | [ ] | 新增黑名單 |
B.5.10 其他 API
| # | Method | 端點 | 認證 | 測試狀態 | 備註 |
|---|---|---|---|---|---|
| 1 | GET | /common/enums | 無 | [ ] | 系統枚舉 |
| 2 | GET | /common/health | 無 | [ ] | 健康檢查 |
| 3 | GET | /ranking/list | Optional JWT | [ ] | 排行榜 |
| 4 | GET | /bet-record/list | JWT | [ ] | 用戶投注紀錄 |
| 5 | GET | /live-sports/matches | 無 | [ ] | 即時賽事 |
| 6 | GET | /inbox/list | JWT | [ ] | 站內信列表 |
| 7 | PATCH | /inbox/:id/read | JWT | [ ] | 標記已讀 |
| 8 | DELETE | /inbox/:id | JWT | [ ] | 刪除信件 |
| 9 | GET | /promo/list | Optional JWT | [ ] | 活動列表 |
| 10 | GET | /promo/:id | Optional JWT | [ ] | 活動詳情 |
| 11 | POST | /promo/:id/claim | JWT | [ ] | 領取活動 |
| 12 | GET | /mission/list | JWT | [ ] | 任務列表 |
| 13 | POST | /mission/:id/claim | JWT | [ ] | 領取任務 |
| 14 | GET | /site-config/list | 無 | [ ] | 站點配置(前台) |
| 15 | GET | /site-config/theme | 無 | [ ] | 站點主題(前台) |
B.6 API 測試檢查點模板
對每個 API 端點,可使用以下檢查點清單驗證。
B.6.1 通用 API 檢查點
## API 測試檢查點:[端點名稱]
### 基本驗證
- [ ] HTTP Status Code 正確(200/201/400/401/403/404/500)
- [ ] 回應格式正確:`{ code, message, result, timestamp, path }`
- [ ] timestamp 為有效 ISO 8601 格式
- [ ] path 對應請求路徑
### 認證驗證
- [ ] 無 Token → 401(受保護端點)
- [ ] 無效 Token → 401
- [ ] 過期 Token → 401
- [ ] 前台 Token 存取後台端點 → 401
- [ ] 無權限 → 403
### 參數驗證
- [ ] 必填參數缺失 → 400
- [ ] 參數格式錯誤 → 400
- [ ] 參數超出範圍 → 400
- [ ] SQL 注入字串 → 安全處理
- [ ] XSS 字串 → 安全處理
### 分頁驗證(列表端點)
- [ ] page=1, pageSize=20 → 正確回傳
- [ ] page=0 或負數 → 使用預設值
- [ ] pageSize=0 或超大值 → 限制最大值
- [ ] 空結果 → items=[], total=0
- [ ] total 數值正確
### 多站點驗證
- [ ] x-site-code header → 正確篩選
- [ ] siteCode query param → 正確篩選
- [ ] 無 siteCode → 全站資料或預設行為
- [ ] 跨站存取 → 正確隔離
### 錯誤處理
- [ ] 業務錯誤 → code 非 200,message 有意義
- [ ] 錯誤碼在 ERROR_CODES 中 → 前端可查表
- [ ] 錯誤訊息多語系 → 根據 locales header 變化B.6.2 寫入端點額外檢查點
### 資料寫入驗證
- [ ] 新增成功 → 資料庫有對應記錄
- [ ] 更新成功 → 資料庫記錄已更新
- [ ] 刪除成功 → 資料庫記錄已刪除(或軟刪除)
- [ ] siteCode 正確 → Entity 的 siteCode 為預期值
- [ ] createdAt/updatedAt → 時間戳正確
### 並發安全
- [ ] 重複提交 → 不產生重複資料
- [ ] 並發修改 → 使用交易或樂觀鎖
- [ ] 操作紀錄 → admin-operation-log 記錄完整附錄 C:常見測試陷阱
目標:列出 C9 平台測試中常見的陷阱和注意事項,幫助 QA 避免踩雷。
C.1 i18n 相關陷阱
C.1.1 ICU 格式大括號轉義
問題:next-intl 使用 ICU MessageFormat,大括號 {} 會被解析為變數佔位符。
// 錯誤 — 導致 IntlError: FORMATTING_ERROR
"格式為 {變數名}"
// 正確 — 使用單引號轉義大括號
"格式為 '{'變數名'}'"測試注意:
- 若頁面出現
IntlError或空白文字,先檢查 i18n JSON 中是否有未轉義的大括號 - 新增翻譯後需測試所有 5 個語系檔案(zh-TW, en-US, zh-CN, th-TH, vi-VN)
- 使用
useTranslations("common")的 key 最常出問題
C.1.2 語系檔案不同步
問題:新增 i18n key 時只更新部分語系檔案,導致其他語系顯示 key 而非翻譯文字。
檢測方式:
# 比較各語系檔案的 key 數量
cd c9-ims/src/messages
wc -l zh-TW.json en-US.json zh-CN.json th-TH.json vi-VN.json
# 找出缺失的 key(需要自訂腳本比對)建議:每次新增翻譯後,切換到每個語系確認頁面不會出現裸 key。
C.1.3 日期格式多語系
問題:不同語系的日期格式不同(如 zh-TW: 2026/03/01, en-US: 03/01/2026),測試時需注意。
注意:報表中的日期篩選和顯示需在各語系下驗證格式正確。
C.2 金額精度陷阱
C.2.1 USD 截斷規則
核心規則:所有金額使用 Math.floor(value * 1e6) / 1e6 無條件捨去至 6 位小數。
// 正確
truncateUsd(1.2345679) // → 1.234567(捨去第 7 位)
// 常見錯誤:使用 Math.round(四捨五入)
Math.round(1.2345679 * 1e6) / 1e6 // → 1.234568(錯誤!應為 1.234567)
// 常見錯誤:使用 toFixed(四捨五入 + 字串轉換)
(1.2345679).toFixed(6) // → "1.234568"(錯誤!)測試注意:
- 存款匯率轉換後的 USD 金額必須使用截斷(非四捨五入)
- VIP 反水計算結果必須截斷
- 代理佣金計算結果必須截斷
- JavaScript 浮點數精度問題:
0.1 + 0.2 !== 0.3,需特別注意
C.2.2 各欄位精度對照
| 用途 | 型別 | 精度 | 常見錯誤 |
|---|---|---|---|
| 金額 | decimal(18,6) | 6 位小數 | 使用 Math.round 而非 Math.floor |
| 匯率 | decimal(18,10) | 10 位小數 | 儲存時精度丟失 |
| 百分比 | decimal(5,2) | 2 位小數 | 返水百分比超過 99.99% |
| 倍率 | decimal(10,2) | 2 位小數 | 打碼倍率計算錯誤 |
C.2.3 金額比較
問題:浮點數比較可能因精度問題失敗。
// 錯誤
if (balance === 100.00) { ... }
// 正確(比較截斷後的值)
if (Math.abs(balance - 100.00) < 1e-6) { ... }C.3 時區陷阱
C.3.1 系統時區 UTC+8
核心設定:MySQL TZ 設定為 +08:00,所有 Cron 排程基於伺服器時間。
| 排程 | 觸發時間(UTC+8) | 注意事項 |
|---|---|---|
| 每日反水結算 | 00:05 | 計算前一日 00:00~23:59 的投注 |
| 月度保級 | 每月 1 號 01:00 | 計算上月整月的投注 |
| 代理週結 | 每週一 03:00 | 計算上週一至週日的佣金 |
| 代理日結 | 每日 03:30 | 計算前一日的佣金 |
| 賽事更新 | 每 30 分鐘 | API-Football 資料快取 |
測試注意:
- 測試 Cron 排程時,確認伺服器時區為 UTC+8
- 日期範圍篩選的開始和結束時間需考慮時區
createdAt欄位在 MySQL 中儲存為 UTC+8 時間
C.3.2 前端日期處理
問題:前端的 new Date() 使用瀏覽器本地時區,可能與伺服器 UTC+8 不一致。
測試建議:
- 在不同時區的瀏覽器中測試日期篩選功能
- 確認報表中的日期顯示與後端一致
applyDateRange()使用的是 MySQL BETWEEN,確認邊界條件
C.4 快取陷阱
C.4.1 Redis 快取導致資料延遲
問題:Redis 快取有 TTL,修改資料後可能需要等待快取過期才能看到最新資料。
| 快取 | TTL | 影響 |
|---|---|---|
| tokenVersion | 60s | 修改用戶後最多 60 秒舊 Token 仍有效 |
| 群組權限 | 60s | 修改權限後最多 60 秒舊權限仍生效 |
| VIP 等級 | 3600s | 修改 VIP 後最多 1 小時前台顯示舊資料 |
| VIP 返水 | 3600s | 修改返水規則後最多 1 小時反水仍用舊規則 |
測試建議:
- 若測試資料不一致,先清除 Redis 快取再驗證
- 測試快取失效場景時,注意等待 TTL 過期
- 使用
redis-cli KEYS "cache:*"檢查快取狀態
C.4.2 TanStack Query 快取(前端)
問題:TanStack Query 的 staleTime 預設 5 分鐘,5 分鐘內不會重新發送 API 請求。
影響場景:
- 在後台修改資料後,前台 5 分鐘內可能顯示舊資料
- 切換頁面後返回,若快取未過期則使用快取資料
- SiteSelector 切換時會清除快取,但手動操作可能不會
測試建議:
- 測試資料一致性時,使用強制 refetch(重新整理頁面或切換站點)
- 驗證
queryClient.invalidateQueries()是否在適當時機被呼叫
C.5 多站點陷阱
C.5.1 x-site-code Header vs siteCode Query Param
優先順序:@AdminSiteCode() 裝飾器先讀 header,再讀 query param。
| 場景 | Header | Query Param | 結果 |
|---|---|---|---|
| Header 選單站 | x-site-code: C9 | - | siteCode = "C9" |
| 全站 + Tab | - | siteCode=A1 | siteCode = "A1" |
| 兩者都有 | x-site-code: C9 | siteCode=A1 | siteCode = "C9"(header 優先) |
| 兩者都無 | - | - | siteCode = null(全站) |
常見陷阱:
- apiClient interceptor 自動注入
x-site-code,若已在 query param 傳入不同站點,header 會覆蓋 getSiteConfigsAPI 必須跳過 siteCode 篩選,否則只能看到當前站
C.5.2 SiteCodeSubscriber 自動填入
問題:SiteCodeSubscriber 在 Entity insert 時自動從環境變數 SITE_CODE 填入 siteCode。
測試注意:
- 若
SITE_CODE未設定,siteCode 可能為空或 undefined - 後台 Admin API 不依賴
SITE_CODE環境變數,而是透過@AdminSiteCode()取得 - Seed 腳本手動設定 siteCode,不依賴 Subscriber
C.5.3 AdminContentWrapper remount
問題:AdminContentWrapper 的 key={selectedSiteCode ?? "__all__"} 會在站點切換時強制 remount 所有子元件。
測試注意:
- 切換站點會導致所有表單狀態丟失(未儲存的資料會消失)
- 切換站點會觸發所有子頁面的 useEffect 重新執行
- 若有長時間操作(如編輯活動),切換站點會中斷
C.6 NextAuth Session 陷阱
C.6.1 Token 快取在 apiClient
問題:apiClient 從 SessionSync 快取取得 JWT Token,若 session 過期但快取未清除,會導致 401 錯誤。
401 重試流程:
- API 返回 401
- apiClient interceptor 清除舊 token
- 重取 session(NextAuth getSession)
- 使用新 token retry 原始請求
- 若 retry 仍失敗,導向登入頁
測試注意:
- 測試 401 重試機制時,確認不會無限重試
- 確認 session 過期後重新登入流程順暢
- 確認 SessionSync 在登出時正確清除
C.6.2 Credentials Provider 限制
問題:NextAuth 5 beta 的 Credentials Provider 不會自動刷新 token。
影響:管理員長時間不操作後可能需要重新登入。
C.7 其他常見陷阱
C.7.1 TypeORM Relation 載入
問題:TypeORM 預設不載入 relation,需要明確使用 relations: ['xxx'] 或 leftJoinAndSelect。
測試影響:若 API 回傳的物件缺少關聯資料(如用戶缺少 VIP 資訊),可能是 relation 未正確載入。
C.7.2 MySQL CAST 搜尋
問題:JSON 欄位的模糊搜尋使用 CAST(column AS CHAR) LIKE :keyword,效能較差且無法使用索引。
測試注意:在大量資料下搜尋 JSON 欄位可能明顯變慢。
C.7.3 R2 路徑命名
問題:R2 檔案路徑使用站點名稱作為 prefix,不同站點的檔案在不同目錄下。
測試注意:
- 上傳檔案的
@SiteName()header 決定儲存路徑 - 跨站存取 R2 檔案可能因路徑不同而失敗
C.7.4 前端路由必須使用 @/i18n/navigation
問題:使用 next/navigation 的 useRouter、Link 等會丟失 locale 前綴。
// 錯誤 — 會丟失 locale
import { useRouter } from "next/navigation";
// 正確
import { useRouter } from "@/i18n/navigation";測試注意:若頁面跳轉後語系突然變成預設語系,可能是使用了錯誤的 import。
C.7.5 Zod v4 驗證
問題:前後端都使用 Zod 進行驗證,但版本可能不一致,schema 定義可能有差異。
測試注意:
- 前端 Zod 驗證通過不代表後端也會通過
- 後端 class-validator 可能有額外的驗證規則
- 測試邊界值(空字串、null、undefined、超長字串等)
附錄 D:自動化測試擴展建議
目標:提供 C9 平台自動化測試的擴展方向和實施建議。
D.1 前台 (c9-ec) Vitest 擴展計劃
D.1.1 現況
- 測試框架:Vitest
- 測試類型:單元測試 + 元件測試
- 執行方式:
yarn test(全部)、yarn test:unit(單元)、yarn test:nuxt(元件)
D.1.2 擴展目標
| 測試層級 | 當前覆蓋率 | 目標覆蓋率 | 說明 |
|---|---|---|---|
| Composables | 30% | 80% | 45 個 composable 的邏輯測試 |
| Components | 20% | 60% | 77 個元件的渲染和交互測試 |
| Pages | 10% | 50% | 20 個頁面的整合測試 |
| Utils | 50% | 90% | 工具函式的單元測試 |
D.1.3 優先測試目標
| 優先級 | 目標 | 原因 |
|---|---|---|
| P1 | useConfig.ts | 多站點域名配置核心 |
| P1 | 金額計算相關 composables | 精度問題影響資金安全 |
| P1 | 認證相關 composables | 安全性核心 |
| P2 | 遊戲大廳元件 | 核心用戶體驗 |
| P2 | 存款/提款流程 | 金流核心功能 |
| P3 | VIP 顯示元件 | 等級和反水顯示 |
| P3 | 多語系切換 | i18n 正確性 |
D.1.4 測試範例
// composables/__tests__/useConfig.test.ts
import { describe, it, expect, vi } from 'vitest';
import { useConfig } from '../useConfig';
describe('useConfig', () => {
it('should return correct site config for C9 domain', () => {
vi.stubGlobal('location', { hostname: 'c9.example.com' });
const config = useConfig();
expect(config.siteCode).toBe('C9');
expect(config.siteName).toBe('C9');
});
it('should inject site-name header', () => {
// 驗證 HTTP 請求帶有 site-name header
});
});D.2 後端 (c9-be) Jest 擴展計劃
D.2.1 現況
- 測試框架:Jest
- 測試類型:單元測試 + E2E 測試
- 執行方式:
yarn test(單元)、yarn test:e2e(E2E)
D.2.2 擴展目標
| 測試層級 | 當前覆蓋率 | 目標覆蓋率 | 說明 |
|---|---|---|---|
| Service 層 | 20% | 70% | 核心業務邏輯 |
| Controller 層 | 10% | 50% | API 端點整合測試 |
| Guard/Decorator | 30% | 80% | 認證和權限 |
| Utils | 50% | 90% | 工具函式 |
| Cron 排程 | 0% | 60% | 排程邏輯 |
D.2.3 優先測試目標
| 優先級 | 模組 | 測試重點 |
|---|---|---|
| P1 | auth | 註冊/登入/JWT/tokenVersion |
| P1 | admin | RBAC 權限驗證 |
| P1 | deposit | 存款流程/匯率轉換/金額計算 |
| P1 | vip | VIP 等級計算/反水結算 |
| P2 | affiliate | 佣金計算/結算流程/風控 |
| P2 | game | 遊戲回調/餘額更新 |
| P2 | withdrawal | 提款流程/審核 |
| P3 | promo | 活動條件/領取 |
| P3 | mission | 任務進度/完成 |
D.2.4 測試範例
// modules/vip/__tests__/vip.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { VipService } from '../vip.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { VipLevel } from '../entities/vip-level.entity';
describe('VipService', () => {
let service: VipService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
VipService,
{
provide: getRepositoryToken(VipLevel),
useValue: {
find: jest.fn().mockResolvedValue([
{ level: 0, requiredBet: 0, siteCode: 'C9' },
{ level: 1, requiredBet: 1000, siteCode: 'C9' },
]),
},
},
],
}).compile();
service = module.get<VipService>(VipService);
});
it('should calculate VIP level based on total bet', async () => {
const level = await service.calculateLevel(500, 'C9');
expect(level).toBe(0);
});
it('should upgrade to level 1 when bet reaches threshold', async () => {
const level = await service.calculateLevel(1000, 'C9');
expect(level).toBe(1);
});
it('should never downgrade VIP level', async () => {
// VIP 等級只升不降的測試
});
});D.3 Playwright E2E 測試計劃
D.3.1 現況
- 前台 (c9-ec) 已設定 Playwright
- 執行方式:
yarn test:e2e
D.3.2 E2E 測試情境
| 優先級 | 情境 | 步驟概述 |
|---|---|---|
| P1 | 完整存款流程 | 登入 → 選擇存款方式 → 輸入金額 → 提交 → 驗證餘額 |
| P1 | 完整提款流程 | 登入 → 新增銀行卡 → 申請提款 → 驗證訂單 |
| P1 | 遊戲啟動流程 | 登入 → 進入遊戲大廳 → 選擇遊戲 → 啟動 |
| P2 | 後台管理員操作 | 登入 → 審核存款 → 審核提款 → 查看報表 |
| P2 | 多站點切換 | 登入 → 切換站點 → 驗證資料隔離 |
| P2 | VIP 升級流程 | 投注 → VIP 等級檢查 → 反水結算 |
| P3 | 代理推廣流程 | 申請代理 → 建立推廣碼 → 下線註冊 → 佣金結算 |
| P3 | 多語系切換 | 切換 5 種語系 → 驗證所有頁面文字 |
D.3.3 Playwright 設定範例
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
timeout: 30000,
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3010',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
{ name: 'Mobile Safari', use: { ...devices['iPhone 13'] } },
],
webServer: [
{ command: 'yarn dev:ec', port: 3010, reuseExistingServer: true },
{ command: 'yarn dev:be', port: 8080, reuseExistingServer: true },
],
});D.3.4 E2E 測試範例
// tests/e2e/deposit.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Deposit Flow', () => {
test.beforeEach(async ({ page }) => {
// 登入
await page.goto('/login');
await page.fill('[name="account"]', 'test_user_C9_001');
await page.fill('[name="password"]', 'Test123!@#');
await page.click('button[type="submit"]');
await page.waitForURL('/');
});
test('should complete ATM deposit', async ({ page }) => {
await page.goto('/deposit');
await page.click('text=ATM');
await page.fill('[name="amount"]', '1000');
await page.click('button:has-text("確認存款")');
// 驗證訂單建立
await expect(page.locator('.deposit-success')).toBeVisible();
});
});D.4 CI/CD 整合建議
D.4.1 GitHub Actions Workflow
# .github/workflows/test.yml
name: CI Test
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
test-backend:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: c9_test
ports: ['3306:3306']
redis:
image: redis:7
ports: ['6379:6379']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: cd c9-be && yarn install --frozen-lockfile
- name: Run unit tests
run: cd c9-be && yarn test --coverage
- name: Run E2E tests
run: cd c9-be && yarn test:e2e
test-frontend-ec:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: cd c9-ec && yarn install --frozen-lockfile
- name: Run tests
run: cd c9-ec && yarn test
- name: Type check
run: cd c9-ec && yarn typecheck
test-frontend-ims:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: cd c9-ims && pnpm install --frozen-lockfile
- name: Type check
run: cd c9-ims && yarn typecheck
- name: Lint
run: cd c9-ims && yarn lintD.4.2 測試覆蓋率目標
| 專案 | 當前估計 | 短期目標(3 個月) | 中期目標(6 個月) | 長期目標(1 年) |
|---|---|---|---|---|
| c9-be | ~15% | 40% | 60% | 75% |
| c9-ec | ~10% | 30% | 50% | 65% |
| c9-ims | ~5% | 20% | 40% | 55% |
D.4.3 覆蓋率報告整合
# c9-be: Jest 覆蓋率報告
cd c9-be && yarn test --coverage --coverageReporters=lcov
# c9-ec: Vitest 覆蓋率報告
cd c9-ec && yarn test --coverage
# 使用 Codecov 或 Coveralls 追蹤覆蓋率趨勢D.4.4 自動化測試最佳實踐
| 實踐 | 說明 |
|---|---|
| 測試金字塔 | 大量單元測試 > 適量整合測試 > 少量 E2E 測試 |
| 測試獨立性 | 每個測試案例獨立執行,不依賴其他測試的結果或狀態 |
| 測試資料隔離 | 使用 beforeEach 設定/afterEach 清除測試資料 |
| 避免 sleep | 使用 waitFor / waitForSelector 而非固定等待時間 |
| Mock 外部服務 | Mock 金流商 API、遊戲商 API、匯率 API |
| 持續執行 | 每次 PR 自動觸發測試,merge 前必須通過 |
| 平行執行 | 使用 Vitest/Jest 的平行執行功能加速 |
| Snapshot 測試 | 對穩定的 UI 元件使用 snapshot 防止意外變更 |
| 錯誤截圖 | E2E 測試失敗時自動截圖,方便除錯 |
附錄 E:邊界值與異常情境測試
目標:針對系統各模組的邊界條件和異常情境設計測試案例,確保系統在極端情況下仍能正確運作。
E.1 金額邊界值測試
E.1.1 金額精度邊界
| TC-ID | 測試說明 | 輸入值 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-EDGE-AMT-001 | 最小正金額 | 0.000001 USD | 系統接受並正確處理 | P2 |
| TC-EDGE-AMT-002 | 零金額存款 | 0.000000 USD | 系統拒絕(最小存款限制) | P1 |
| TC-EDGE-AMT-003 | 負數金額 | -100.000000 USD | 系統拒絕(Zod/class-validator 驗證) | P1 |
| TC-EDGE-AMT-004 | 超大金額 | 999999999999.999999 USD | 不超過 decimal(18,6) 範圍 | P2 |
| TC-EDGE-AMT-005 | 截斷精度驗證 | 100.1234567 USD | 截斷為 100.123456(非四捨五入) | P1 |
| TC-EDGE-AMT-006 | 浮點精度問題 | 0.1 + 0.2 | 結果為 0.300000(非 0.30000000000000004) | P1 |
| TC-EDGE-AMT-007 | 匯率精度邊界 | 匯率 0.0000000001 | decimal(18,10) 正確儲存 | P2 |
| TC-EDGE-AMT-008 | 百分比上限 | 99.99% 反水率 | decimal(5,2) 正確處理 | P2 |
| TC-EDGE-AMT-009 | 百分比零值 | 0.00% 反水率 | 反水為 0,不發放 | P2 |
| TC-EDGE-AMT-010 | 餘額為零時提款 | 餘額 0.000000 | 提款被拒絕,友善提示 | P1 |
E.1.2 匯率邊界
| TC-ID | 測試說明 | 情境 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-EDGE-RATE-001 | 匯率 API 不可用 | 台銀匯率 API 回應失敗 | 使用快取匯率或拒絕存款(不可用預設匯率) | P1 |
| TC-EDGE-RATE-002 | 匯率為零 | API 回傳匯率 0 | 系統拒絕計算(除以零防護) | P1 |
| TC-EDGE-RATE-003 | 匯率異常高 | API 回傳匯率 99999 | 金額計算結果極小但正確 | P2 |
| TC-EDGE-RATE-004 | 匯率異常低 | API 回傳匯率 0.001 | 金額計算結果極大,可能觸發上限 | P2 |
| TC-EDGE-RATE-005 | 匯率快取過期 | 快取 TTL 3600s 過期 | 重新取得最新匯率 | P2 |
E.2 字串邊界值測試
E.2.1 帳號和密碼
| TC-ID | 測試說明 | 輸入值 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-EDGE-STR-001 | 空帳號 | "" | 驗證失敗,提示必填 | P1 |
| TC-EDGE-STR-002 | 超長帳號(255 字元) | "a" * 255 | 超過限制長度時拒絕 | P2 |
| TC-EDGE-STR-003 | 帳號含特殊字元 | "user@#$%^" | 依系統規則接受或拒絕 | P2 |
| TC-EDGE-STR-004 | 帳號含空格 | "user name" | 拒絕或自動去空格 | P2 |
| TC-EDGE-STR-005 | 帳號含 Unicode | "用戶名稱" | 依系統規則接受或拒絕 | P3 |
| TC-EDGE-STR-006 | 空密碼 | "" | 驗證失敗,提示必填 | P1 |
| TC-EDGE-STR-007 | 超長密碼(1000 字元) | "a" * 1000 | bcrypt 有 72 bytes 限制 | P3 |
| TC-EDGE-STR-008 | 密碼含 Unicode | "密碼Abc123!@#" | 系統正確處理 Unicode 密碼 | P3 |
| TC-EDGE-STR-009 | 帳號前後空格 | " admin " | 自動 trim 或拒絕 | P2 |
| TC-EDGE-STR-010 | Email 超長 | "a" * 250 + "@test.com" | 超過限制時拒絕 | P3 |
E.2.2 搜尋和篩選
| TC-ID | 測試說明 | 輸入值 | 預期結果 | 優先級 |
|---|---|---|---|---|
| TC-EDGE-SEARCH-001 | 空搜尋 | "" | 回傳所有資料(忽略空搜尋) | P2 |
| TC-EDGE-SEARCH-002 | 超長搜尋字串 | "a" * 1000 | 不崩潰,正確處理 | P3 |
| TC-EDGE-SEARCH-003 | SQL 特殊字元搜尋 | "%_'" | 正確轉義,安全搜尋 | P1 |
| TC-EDGE-SEARCH-004 | 只有空格的搜尋 | " " | 視為空搜尋或 trim 後處理 | P3 |
| TC-EDGE-SEARCH-005 | Emoji 搜尋 | "🎮" | 正確處理 UTF-8 4 bytes 字元 | P3 |
| TC-EDGE-SEARCH-006 | HTML 標籤搜尋 | "" | 正常拒絕或轉義 |
L.2.2 POST /api/auth/login
請求格式:
{
"account": "string",
"password": "string",
"fingerprint": "string (選填)"
}成功回應 result:
{
"token": "JWT string",
"user": {
"id": "number",
"account": "string",
"email": "string|null",
"vipLevel": "number",
"balance": "string (decimal)"
}
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 正常登入 | 正確帳密 | code: 200, result.token 存在 |
| 密碼錯誤 | 錯誤密碼 | 業務錯誤碼 |
| 帳號不存在 | 不存在帳號 | 業務錯誤碼(同密碼錯誤,不洩漏帳號存在性) |
| 帳號停用 | status='disabled' | 業務錯誤碼 |
| Token 格式 | 解碼 JWT | 含 userId, account, siteCode, tokenVersion |
| Token 過期 | 設 exp | 7 天後過期 |
L.2.3 POST /api/auth/google-login
請求格式:
{
"idToken": "string (Google OAuth ID Token)",
"fingerprint": "string (選填)"
}| 驗證項目 | 預期結果 |
|---|---|
| 有效 idToken(首次) | 自動建立用戶 + 回傳 token |
| 有效 idToken(已綁定) | 直接登入回傳 token |
| 無效 idToken | 業務錯誤碼 |
| 過期 idToken | 業務錯誤碼 |
L.2.4 POST /api/auth/avatar
請求格式:multipart/form-data,file 欄位
| 驗證項目 | 預期結果 |
|---|---|
| 上傳 JPG < 2MB | code: 200, result 含 avatarUrl |
| 上傳 PNG < 2MB | code: 200 |
| 上傳 > 5MB | 400 檔案過大 |
| 上傳非圖片 | 400 格式不支援 |
| 未登入上傳 | 401 |
L.3 存提款模組 API Contract
L.3.1 POST /api/deposit
請求格式:
{
"amount": "number (> 0)",
"currency": "string (TWD/USDT)",
"paymentMethod": "string (atm/credit/crypto)",
"vendorChannelId": "number (選填)"
}成功回應 result:
{
"orderId": "number",
"orderNo": "string",
"amount": "string (decimal)",
"amountUsd": "string (decimal)",
"exchangeRate": "string (decimal)",
"paymentUrl": "string (金流商跳轉 URL)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 正常存款 | 合法金額 | code: 200, 含 orderNo |
| 金額為 0 | amount: 0 | 400 驗證錯誤 |
| 金額為負 | amount: -100 | 400 驗證錯誤 |
| 金額超限 | amount: 99999999 | 業務錯誤碼 |
| 未知支付方式 | paymentMethod: "xxx" | 400 驗證錯誤 |
| 未登入 | 無 Token | 401 |
| 匯率精度 | 驗證 exchangeRate | decimal(18,10) |
| USD 截斷 | 驗證 amountUsd | Math.floor 無條件捨去 |
L.3.2 GET /api/deposit/exchange-rate
成功回應 result:
{
"currency": "TWD",
"rate": "32.1500000000",
"updatedAt": "2026-03-02T12:00:00.000Z"
}| 驗證項目 | 預期結果 |
|---|---|
| 匯率格式 | decimal(18,10) 字串 |
| 更新時間 | ISO 8601 |
| 快取有效期 | 同一分鐘內相同 |
L.3.3 POST /api/withdrawal
請求格式:
{
"amount": "number (> 0, USD)",
"method": "string (bank/crypto)",
"walletId": "number (bank-card 或 crypto-address 的 ID)",
"twoFaCode": "string (選填,6位數字)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 正常提領 | 餘額足夠 | code: 200, 含 orderNo |
| 餘額不足 | amount > balance | 業務錯誤碼 |
| 錢包未審核 | walletId 狀態非 approved | 業務錯誤碼 |
| 錢包不存在 | walletId: 99999 | 業務錯誤碼 |
| 金額精度 | 超過 6 位小數 | 截斷至 6 位 |
L.4 遊戲模組 API Contract
L.4.1 GET /api/game/providers
Query 參數:gameType (選填), siteCode (由 header 帶入)
成功回應 result:
[
{
"id": "number",
"providerCode": "string",
"name": { "zh-TW": "string", "en-US": "string" },
"icon": "string|null",
"enabled": "boolean",
"sortOrder": "number",
"gameType": "number"
}
]| 驗證項目 | 預期結果 |
|---|---|
| name 多語系 | 至少含 zh-TW 和 en-US |
| gameType 篩選 | 回傳僅含指定 gameType |
| enabled 篩選 | 前台 API 只回啟用的 |
| 排序 | 按 sortOrder ASC |
L.4.2 POST /api/game/launch
請求格式:
{
"providerCode": "string",
"gameId": "string",
"demo": "boolean (選填,預設 false)"
}成功回應 result:
{
"launchUrl": "string (遊戲啟動 URL)",
"token": "string (遊戲 session token)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 正常啟動 | 合法 provider + gameId | code: 200, 含 launchUrl |
| 遊戲不存在 | gameId: "invalid" | 業務錯誤碼 |
| 供應商不存在 | providerCode: "xxx" | 業務錯誤碼 |
| 遊戲黑名單 | 用戶被封鎖 | code: 5010 |
| 試玩模式 | demo: true,未登入 | code: 200(不需 token) |
| 試玩模式 | demo: true,已登入 | code: 200 |
L.4.3 S2S Callback — BetSolutions
請求格式(由遊戲商發送):
{
"Token": "string",
"TransactionId": "string",
"RoundId": "string",
"Amount": "number",
"Type": "string (Bet/Win/Refund/Rollback)",
"Signature": "string (HMAC)"
}| 驗證項目 | 預期結果 |
|---|---|
| 簽名正確 | 正常處理交易 |
| 簽名錯誤 | 拒絕,回 error code |
| 重複 TransactionId | 冪等處理,不重複扣款 |
| 餘額不足(Bet) | 回傳 InsufficientBalance |
| Token 無效 | 回傳 InvalidToken |
L.4.4 S2S Callback — RSG
請求格式(DES 加密):
加密的 JSON body,需 DES 解密| 驗證項目 | 預期結果 |
|---|---|
| DES 解密成功 | 正常處理交易 |
| 解密失敗 | 拒絕,回 error |
| 交易類型:下注 | 扣除餘額 |
| 交易類型:派彩 | 增加餘額 |
| 交易類型:退款 | 退回餘額 |
L.5 VIP 模組 API Contract
L.5.1 GET /api/vip/levels
成功回應 result:
[
{
"id": "number",
"level": "number",
"name": { "zh-TW": "string", "en-US": "string" },
"requiredBetAmount": "string (decimal)",
"retentionBetAmount": "string (decimal)",
"upgradeBonus": "string (decimal)",
"monthlyBonus": "string (decimal)",
"retentionLocked": "boolean"
}
]| 驗證項目 | 預期結果 |
|---|---|
| 排序 | 按 level ASC |
| 金額格式 | decimal(18,6) 字串 |
| 等級連續 | 0, 1, 2, ... 無跳號 |
| siteCode 篩選 | 僅回當前站的等級 |
| name 多語系 | 至少含 5 種語系 |
L.5.2 Admin VIP API
POST /api/vip/admin/copy-site-data:
{
"sourceSiteCode": "string",
"targetSiteCode": "string",
"type": "string (levels/rebates)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 複製等級 | type: "levels" | 目標站等級與來源站一致 |
| 複製返水 | type: "rebates" | 目標站返水與來源站一致 |
| 來源站不存在 | sourceSiteCode: "XXX" | 業務錯誤碼 |
| 目標站=來源站 | same siteCode | 業務錯誤碼或忽略 |
| Transaction 原子性 | 複製中途失敗 | 全部 rollback |
L.6 代理模組 API Contract
L.6.1 GET /api/affiliate/dashboard
成功回應 result:
{
"totalDownlines": "number",
"activeDownlines": "number",
"totalCommission": "string (decimal)",
"pendingCommission": "string (decimal)",
"balance": "string (decimal)",
"frozenBalance": "string (decimal)",
"agentTier": "string",
"referralCodes": ["string"]
}| 驗證項目 | 預期結果 |
|---|---|
| 金額格式 | 全部 decimal(18,6) |
| 下線數量 | >= 0 |
| 代理等級 | bronze/silver/gold/platinum 之一 |
| 推廣碼數量 | <= 10 |
L.6.2 POST /api/affiliate/referral-codes
請求格式:
{
"code": "string (英數字,唯一)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 建立推廣碼 | 合法 code | code: 200 |
| 重複代碼 | 已存在的 code | 業務錯誤碼 |
| 超過上限 | 已有 10 個 | 業務錯誤碼 |
| 空代碼 | code: "" | 400 驗證錯誤 |
| 特殊字元 | code: "ab!@#" | 400 驗證錯誤 |
L.6.3 Admin 代理 API
POST /api/affiliate/admin/settlements/:id/review:
{
"action": "string (approve/reject)",
"reason": "string (選填,reject 時必填)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 核准結算 | action: "approve" | 狀態變 approved |
| 拒絕結算 | action: "reject", reason: "..." | 狀態變 rejected |
| 拒絕無理由 | action: "reject", 無 reason | 400 |
| 重複審核 | 已 approved 的再次審核 | 業務錯誤碼 |
| 結算不存在 | :id = 99999 | 404 或業務錯誤 |
L.7 站點配置模組 API Contract
L.7.1 GET /api/site-config/admin/list
成功回應 result:
[
{
"id": "number",
"siteCode": "string",
"name": "string",
"domains": ["string"],
"supportedLocales": ["string"],
"defaultLocale": "string",
"features": { "key": "boolean" },
"activeThemeId": "number|null",
"themes": [
{
"id": "number",
"name": "string",
"colors": { "primary": "string", "accent": "string" },
"mode": "string"
}
]
}
]| 驗證項目 | 預期結果 |
|---|---|
| 回傳所有站點 | 不受 x-site-code 影響 |
| 含主題列表 | themes 為陣列 |
| domains 格式 | 合法域名陣列 |
| supportedLocales | 在 5 種語系範圍內 |
L.7.2 POST /api/site-config/admin
請求格式:
{
"siteCode": "string (唯一)",
"name": "string",
"domains": ["string"],
"supportedLocales": ["zh-TW", "en-US"],
"defaultLocale": "zh-TW",
"features": {}
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 正常建立 | 合法資料 | code: 200, 回傳新站點 |
| siteCode 重複 | 已存在的 code | 業務錯誤碼 |
| 空 siteCode | siteCode: "" | 400 |
| 無效語系 | supportedLocales: ["xx-YY"] | 400 或業務錯誤 |
L.7.3 PATCH /api/site-config/admin/:id
| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 更新名稱 | 成功更新 | |
| 更新 features | { features: {...} } | 成功更新 |
| 更新不存在 | :id = 99999 | 404 或業務錯誤 |
| 部分更新 | 只傳 name | 其他欄位不變 |
L.7.4 主題 CRUD API
POST /api/site-config/admin/:siteConfigId/themes:
{
"name": "string",
"colors": {
"primary": "string (OKLCH)",
"accent": "string (OKLCH)",
"surface": "string (OKLCH)",
"text": "string (OKLCH)",
"border": "string (OKLCH)"
},
"mode": "string (light/dark)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 建立主題 | 合法色彩 | code: 200 |
| 站點不存在 | :siteConfigId = 99999 | 404 |
| 缺少色彩 | colors 缺 primary | 400 |
| 刪除啟用中主題 | activeThemeId 指向此主題 | 成功刪除,activeThemeId 清空 |
L.8 後台管理模組 API Contract
L.8.1 POST /api/admin/login
請求格式:
{
"account": "string",
"password": "string",
"twoFaCode": "string (選填,啟用 2FA 時必填)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 正常登入 | 正確帳密 | code: 200, 含 AdminJWT |
| 密碼錯誤 | 錯誤密碼 | 業務錯誤碼 |
| 需要 2FA | 已啟用但未傳 code | 業務錯誤碼(需 2FA) |
| 2FA 碼錯誤 | 錯誤的 6 位碼 | 業務錯誤碼 |
| 2FA 碼過期 | 超過 30 秒的 TOTP | 業務錯誤碼 |
| JWT payload | 解碼 token | 含 adminId, account, role:'admin', groupType |
L.8.2 權限驗證端點
| 端點 | 所需權限 | 無權限回應 |
|---|---|---|
| GET /admin/list | admin:read | 403 |
| POST /admin/register | admin:write | 403 |
| GET /admin/finance/deposit-review/list | deposit:read | 403 |
| POST /admin/finance/deposit-review/:id/review | deposit:write | 403 |
| GET /admin/reports/players | report:read | 403 |
| POST /admin/risk/ip-rules | risk:write | 403 |
| GET /admin/groups/list | admin-group:read | 403 |
| POST /admin/promos | promo:write | 403 |
| GET /admin/finance/users/list | user:read | 403 |
| DELETE /admin/vendor-channels/:id | vendor:write | 403 |
L.8.3 分頁回應格式
所有列表 API 應回傳統一分頁格式:
{
"items": [],
"pagination": {
"page": "number",
"pageSize": "number",
"total": "number",
"totalPages": "number"
}
}或簡化格式:
{
"items": [],
"total": "number"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 第一頁 | page=1, pageSize=20 | items.length <= 20 |
| 超出頁碼 | page=9999 | items = [], total 不變 |
| pageSize 上限 | pageSize=1000 | 限制為最大值(通常 100) |
| pageSize=0 | pageSize=0 | 400 或使用預設值 |
| 不傳 page | 省略 page | 預設 page=1 |
L.9 風控模組 API Contract
L.9.1 POST /api/admin/risk/ip-rules
請求格式:
{
"ip": "string (IPv4/IPv6)",
"type": "string (blacklist/whitelist)",
"reason": "string (選填)",
"expiresAt": "string (ISO 8601, 選填)",
"siteCode": "string (選填)"
}| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| 新增黑名單 | 合法 IP | code: 200 |
| 新增白名單 | type: "whitelist" | code: 200 |
| 無效 IP | ip: "999.999.999.999" | 400 |
| IPv6 | ip: "::1" | code: 200 |
| 到期時間 | 過去時間 | 400 或業務錯誤 |
| 重複 IP | 同站同 IP 同類型 | 業務錯誤碼 |
L.9.2 GET /api/admin/risk/lookup
Query 參數:keyword (IP/指紋/帳號/姓名/Email/手機), siteCode (選填)
| 驗證項目 | 輸入 | 預期結果 |
|---|---|---|
| IP 查詢 | keyword: "192.168.1.1" | 回傳相關用戶列表 |
| 指紋查詢 | keyword: fingerprint hash | 回傳相關用戶列表 |
| 帳號查詢 | keyword: "testuser" | 回傳相關登入紀錄 |
| 空關鍵字 | keyword: "" | 400 或回傳空 |
L.10 報表模組 API Contract
L.10.1 GET /api/admin/reports/overview
Query 參數:startDate, endDate, siteCode (選填)
成功回應 result:
{
"summary": {
"totalDeposit": "string (decimal)",
"totalWithdrawal": "string (decimal)",
"totalBet": "string (decimal)",
"totalWin": "string (decimal)",
"netProfit": "string (decimal)",
"newUsers": "number",
"activeUsers": "number"
},
"daily": [
{
"date": "string (YYYY-MM-DD)",
"deposit": "string",
"withdrawal": "string",
"bet": "string",
"win": "string",
"newUsers": "number"
}
]
}| 驗證項目 | 預期結果 |
|---|---|
| 金額格式 | 全部 decimal(18,6) 字串 |
| 日期排序 | daily 按日期 ASC |
| siteCode 篩選 | 僅含指定站點資料 |
| 日期範圍 | 僅含範圍內日期 |
| netProfit 計算 | totalBet - totalWin(或含其他) |
L.10.2 GET /api/admin/reports/bet-records
Query 參數:
page,pageSizekeyword(帳號/注單號)gameType(1-10)providerCodestatus(pending/settled/cancelled)startDate,endDatesiteCode
| 驗證項目 | 預期結果 |
|---|---|
| 分頁 | 含 items + pagination |
| 遊戲類型篩選 | 僅含指定 gameType |
| 狀態篩選 | 僅含指定 status |
| 日期範圍 | createdAt 在範圍內 |
| 關鍵字搜尋 | 帳號或注單號模糊匹配 |
| 金額欄位 | betAmount, winAmount 為 decimal 字串 |
L.10.3 GET /api/admin/reports/export
Query 參數:type (players/bet-records/overview/profit-loss/games/player-summary), 加上各報表的篩選參數
| 驗證項目 | 預期結果 |
|---|---|
| Content-Type | text/csv 或 application/octet-stream |
| 檔案名稱 | Content-Disposition 含有意義的檔名 |
| CSV 格式 | 標題行 + 資料行,UTF-8 BOM |
| 大量資料 | 不因資料量大而 timeout |
| 空資料 | 僅有標題行 |
附錄 M:行動裝置與響應式測試
驗證前台 (c9-ec) 和後台 (c9-ims) 在不同裝置與視窗尺寸下的顯示與互動
M.1 測試裝置矩陣
M.1.1 手機裝置
| 裝置 | 螢幕尺寸 | 解析度 | DPR | OS | 優先級 |
|---|---|---|---|---|---|
| iPhone 15 Pro | 6.1" | 393×852 | 3x | iOS 17 | P1 |
| iPhone 15 Pro Max | 6.7" | 430×932 | 3x | iOS 17 | P2 |
| iPhone SE (3rd) | 4.7" | 375×667 | 2x | iOS 16 | P2 |
| Samsung Galaxy S24 | 6.2" | 360×780 | 3x | Android 14 | P1 |
| Samsung Galaxy S24 Ultra | 6.8" | 412×915 | 3.5x | Android 14 | P2 |
| Google Pixel 8 | 6.2" | 412×915 | 2.625x | Android 14 | P2 |
| Samsung Galaxy A54 | 6.4" | 360×800 | 2x | Android 13 | P3 |
| Xiaomi 14 | 6.36" | 360×800 | 3x | Android 14 | P3 |
M.1.2 平板裝置
| 裝置 | 螢幕尺寸 | 解析度 | DPR | OS | 優先級 |
|---|---|---|---|---|---|
| iPad Pro 12.9" | 12.9" | 1024×1366 | 2x | iPadOS 17 | P1 |
| iPad Air (5th) | 10.9" | 820×1180 | 2x | iPadOS 17 | P2 |
| iPad mini (6th) | 8.3" | 744×1133 | 2x | iPadOS 17 | P3 |
| Samsung Galaxy Tab S9 | 11" | 800×1280 | 1.5x | Android 14 | P3 |
M.1.3 桌面斷點
| 斷點名稱 | 寬度範圍 | 典型裝置 | 優先級 |
|---|---|---|---|
| sm | 640px - 767px | 大手機橫向 | P2 |
| md | 768px - 1023px | 平板直向 | P1 |
| lg | 1024px - 1279px | 平板橫向/小筆電 | P1 |
| xl | 1280px - 1535px | 標準筆電 | P1 |
| 2xl | 1536px+ | 桌面大螢幕 | P2 |
M.2 前台 (c9-ec) 響應式測試
M.2.1 首頁 (Home)
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 導航列 | 漢堡選單展開/收合 | 漢堡選單或部分顯示 | 完整導航列 |
| Banner 輪播 | 全寬 + 觸控滑動 | 全寬 + 觸控 | 全寬 + 箭頭導航 |
| 遊戲分類 | 橫向滾動 Tab | 2 行 Grid | 單行 Tab |
| 遊戲卡片 | 2 欄 Grid | 3 欄 Grid | 4-6 欄 Grid |
| 底部導航列 | 固定底部 5 Tab | 隱藏 | 隱藏 |
| 客服浮動按鈕 | 右下角 | 右下角 | 右下角 |
| 跑馬燈 | 顯示(文字截斷) | 顯示 | 顯示 |
| 頁尾 | 單欄堆疊 | 2 欄 | 4 欄 |
M.2.2 登入/註冊頁
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 表單寬度 | 全寬 padding 16px | 居中 max-w-md | 居中 max-w-md |
| 輸入框 | 全寬 | 全寬 | 全寬 |
| 密碼顯示切換 | 觸控可用 | 觸控/點擊 | 點擊 |
| 社群登入按鈕 | 全寬堆疊 | 橫排 | 橫排 |
| 鍵盤彈出 | 表單不被遮擋 | 表單不被遮擋 | N/A |
| 驗證訊息 | 顯示在欄位下方 | 同左 | 同左 |
M.2.3 遊戲大廳
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 分類篩選 | 橫向滾動 Tab | 橫向 Tab | 固定 Tab |
| 遊戲卡片 | 2 欄,觸控啟動 | 3 欄 | 4-6 欄 |
| 搜尋框 | 全寬,點擊展開 | 固定寬度 | 固定寬度 |
| 載入更多 | 下拉觸發 | 按鈕 | 按鈕 |
| 遊戲詳情 | 全螢幕 modal | 側邊 drawer | modal |
| 試玩按鈕 | 可觸控 | 可觸控 | hover 顯示 |
M.2.4 個人中心
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 側邊選單 | 底部 Tab 導航 | 左側摺疊選單 | 左側固定選單 |
| 頭像上傳 | 觸控可用 | 觸控/點擊 | 點擊 |
| 餘額顯示 | 顯眼位置 | 頂部 | 頂部 |
| 交易紀錄 | 卡片式列表 | 表格式 | 表格式 |
| 銀行卡列表 | 卡片堆疊 | 2 欄 | 3 欄 |
M.2.5 存款頁
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 支付方式 | 全寬 Tab | 橫排 Tab | 橫排 Tab |
| 金額輸入 | 全寬,數字鍵盤 | 全寬 | 固定寬度 |
| 快捷金額按鈕 | 2x3 Grid | 3x2 Grid | 橫排 |
| QR Code | 居中顯示 | 居中 | 居中 |
| 匯率資訊 | 金額下方 | 側邊 | 側邊 |
M.2.6 VIP 頁面
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 等級進度條 | 全寬 | 全寬 | 居中 max-w |
| 等級卡片 | 橫向滾動 | 2 欄 Grid | 3-4 欄 Grid |
| 返水表格 | 橫向滾動 | 完整顯示 | 完整顯示 |
| 等級權益 | 折疊式列表 | 表格 | 表格 |
M.2.7 代理推廣頁
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 推廣碼列表 | 卡片堆疊 | 表格 | 表格 |
| 複製按鈕 | 觸控可用 | 同左 | 同左 |
| 統計資訊 | 2 欄 Grid | 4 欄 Grid | 4 欄 Grid |
| 下線列表 | 帳號遮罩 + 卡片 | 表格 | 表格 |
| 佣金圖表 | 全寬 | 全寬 | 居中 max-w |
M.3 後台 (c9-ims) 響應式測試
M.3.1 Sidebar 導航
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| Sidebar 狀態 | 預設隱藏 | 預設摺疊 | 預設展開 |
| 觸發方式 | 漢堡按鈕 | 漢堡按鈕 | Toggle 按鈕 |
| Overlay | 有背景遮罩 | 有背景遮罩 | 無 |
| 子選單 | 可展開 | 可展開 | 可展開 |
| Active 狀態 | 高亮顯示 | 同左 | 同左 |
M.3.2 Header
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| SiteSelector | 下拉正常 | 下拉正常 | 下拉正常 |
| 語系切換 | 下拉正常 | 下拉正常 | 下拉正常 |
| 個人選單 | 下拉正常 | 下拉正常 | 下拉正常 |
| 麵包屑 | 隱藏或截斷 | 顯示 | 顯示 |
M.3.3 列表頁面(通用)
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| SiteTabs | 橫向滾動 | 橫向滾動 | 完整顯示 |
| FilterBar | 欄位堆疊 | 2 欄 | 4 欄 |
| SimpleTable | 橫向滾動 | 部分滾動 | 完整顯示 |
| 分頁 | 簡化(< > 頁碼) | 完整 | 完整 |
| 操作按鈕 | dropdown 或 icon | 文字按鈕 | 文字按鈕 |
| 匯出按鈕 | Icon | Icon + 文字 | Icon + 文字 |
M.3.4 表單頁面(通用)
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 表單寬度 | 全寬 | 居中 max-w-2xl | 居中 max-w-2xl |
| 欄位排列 | 單欄 | 雙欄 | 雙欄 |
| 日期選擇器 | 原生 picker | Popover | Popover |
| Select | 原生或 Popover | Popover | Popover |
| 提交按鈕 | 全寬底部固定 | 右對齊 | 右對齊 |
| 驗證訊息 | 欄位下方 | 同左 | 同左 |
M.3.5 Dialog / Modal
| 測試項目 | 手機 (< 768px) | 平板 (768-1023px) | 桌面 (1024px+) |
|---|---|---|---|
| 寬度 | 近全寬(padding) | max-w-lg | max-w-lg |
| 高度 | max-h-[80vh] 滾動 | max-h-[80vh] | max-h-[80vh] |
| 關閉方式 | X 按鈕 + 背景點擊 | 同左 + ESC | 同左 + ESC |
| 表單內 Dialog | 可正常輸入 | 同左 | 同左 |
| 確認 Dialog | 按鈕可觸控 | 同左 | 同左 |
M.4 觸控互動測試
M.4.1 手勢操作
| 手勢 | 測試頁面 | 預期行為 |
|---|---|---|
| 左右滑動 | 前台 Banner | 切換輪播 |
| 左右滑動 | 前台遊戲分類 | 切換分類 Tab |
| 下拉 | 前台遊戲列表 | 載入更多(若有) |
| 長按 | 前台推廣碼 | 複製文字(系統選單) |
| 捏合縮放 | 所有頁面 | 應禁止或正常 |
| 雙擊 | 所有頁面 | 不應觸發非預期行為 |
M.4.2 觸控目標尺寸
| 元素 | 最小尺寸 | 驗證方式 |
|---|---|---|
| 按鈕 | 44 x 44 px | Chrome DevTools 測量 |
| 連結 | 44 x 44 px 觸控區域 | 實機測試 |
| 核取方塊 | 44 x 44 px 觸控區域 | 實機測試 |
| 下拉選項 | 44 px 高度 | 實機測試 |
| Tab 標籤 | 44 px 高度 | 實機測試 |
| 關閉按鈕 | 44 x 44 px | 不可過小 |
M.5 橫向/直向切換測試
M.5.1 前台測試項目
| 頁面 | 直向→橫向 | 橫向→直向 |
|---|---|---|
| 首頁 | 佈局正常調整 | 佈局正常恢復 |
| 遊戲大廳 | Grid 欄位增加 | Grid 欄位減少 |
| 遊戲中 | 遊戲視窗適應 | 遊戲視窗適應 |
| 存款頁 | 表單寬度調整 | 表單寬度調整 |
| VIP 頁 | 卡片排列調整 | 卡片排列調整 |
M.5.2 後台測試項目
| 頁面 | 直向→橫向 | 橫向→直向 |
|---|---|---|
| 列表頁 | 表格橫向滾動消失 | 表格橫向滾動出現 |
| 儀表板 | 圖表重新適應 | 圖表重新適應 |
| 表單頁 | 欄位排列調整 | 欄位排列調整 |
共用驗證:
- [ ] 切換後無 JavaScript 錯誤
- [ ] 切換後無元素重疊
- [ ] 切換後滾動位置合理
- [ ] 切換後 Modal/Dialog 不脫位
- [ ] 切換後表單輸入不遺失
M.6 鍵盤與無障礙測試
M.6.1 Tab 鍵導航順序
| 頁面 | 預期 Tab 順序 |
|---|---|
| 登入頁 | 帳號 → 密碼 → 登入按鈕 → 社群登入 |
| 註冊頁 | 帳號 → 密碼 → 確認密碼 → Email → 手機 → 推廣碼 → 註冊按鈕 |
| 後台列表 | SiteTabs → FilterBar → 搜尋按鈕 → 表格 → 分頁 |
| Dialog | 第一個可聚焦元素 → ... → 確認按鈕 → 取消按鈕 |
M.6.2 ARIA 標籤驗證
| 元素 | 必要 ARIA | 驗證 |
|---|---|---|
| 導航列 | role="navigation", aria-label | 螢幕閱讀器可識別 |
| 對話框 | role="dialog", aria-modal="true" | 聚焦困在 Dialog 內 |
| 表格 | role="table" 或 <table> | 螢幕閱讀器可讀行列 |
| 按鈕 | aria-label(icon-only 按鈕) | 有語義說明 |
| 表單 | <label> 關聯或 aria-label | 欄位有標籤 |
| 載入中 | aria-busy="true" | 通知輔助技術 |
| 分頁 | aria-current="page" | 標示當前頁 |
| Toast | role="alert" | 自動通知 |
M.6.3 色彩對比度
| 元素類型 | WCAG AA 要求 | 驗證工具 |
|---|---|---|
| 一般文字 | 對比度 >= 4.5:1 | Chrome DevTools / axe |
| 大型文字 (18px+) | 對比度 >= 3:1 | Chrome DevTools / axe |
| UI 元件 | 對比度 >= 3:1 | Chrome DevTools / axe |
| 狀態標籤 | 對比度 >= 3:1 | 確認各 StatusBadge 顏色 |
| 連結 | 對比度 >= 4.5:1 + 可辨識 | 不只用顏色區分 |
M.7 效能基準(行動裝置)
M.7.1 Lighthouse 行動版指標
| 指標 | 前台目標 | 後台目標 | 測量條件 |
|---|---|---|---|
| Performance | >= 80 | >= 70 | Mobile, Throttled 4G |
| FCP | < 2.0s | < 2.5s | Throttled 4G |
| LCP | < 3.0s | < 3.5s | Throttled 4G |
| CLS | < 0.1 | < 0.15 | 頁面載入完成 |
| TBT | < 300ms | < 400ms | Throttled 4G |
| TTI | < 4.0s | < 5.0s | Throttled 4G |
M.7.2 網路條件模擬
| 網路類型 | 下載速度 | 延遲 | 測試重點 |
|---|---|---|---|
| 4G Fast | 12 Mbps | 40ms | 基準測試 |
| 4G Slow | 4 Mbps | 100ms | 一般使用者 |
| 3G | 1.5 Mbps | 300ms | 最差情境 |
| Offline → Online | N/A | N/A | 斷線恢復 |
| 測試項目 | 3G 預期 | 4G 預期 |
|---|---|---|
| 首頁載入 | < 8s | < 3s |
| 登入操作 | < 5s | < 2s |
| 遊戲列表 | < 6s | < 2.5s |
| 圖片載入 | lazy load 運作 | lazy load 運作 |
| API 失敗 | 顯示重試提示 | 正常回應 |
文件結尾
版本歷史:
- v1.0 (2026-03-01):初版,涵蓋第 1-6 章(測試環境、前台/後台/後端測試案例、E2E 測試、多站點隔離)
- v2.0 (2026-03-02):新增第 7-10 章(安全性測試、效能測試、回歸測試清單、Bug 報告模板)及附錄 A-J
- v2.1 (2026-03-02):新增附錄 K(資料庫 Schema 測試)、L(API Contract 測試)、M(行動裝置與響應式測試)
文件統計:
- 總行數:8,200+
- 總測試案例數:~950+
- 涵蓋模組數:16 個權限模組 + 23 個後端模組
- 涵蓋頁面數:前台 20 頁 + 後台 68 頁
- 涵蓋 API 端點數:205+
- 涵蓋資料表數:49 張
- 附錄數量:13 個(A-M)
文件維護:本文件應隨系統功能更新同步維護,每次重大功能上線後更新對應的測試案例。 問題回饋:若發現文件錯誤或遺漏,請建立 Issue 標記為
doc-bug。