# Xóc Đĩa 游戏 - 前端 API 接口文档 > **版本**: v1.0.0 > **更新日期**: 2026-01-08 > **基础URL**: `http://yourdomain.com` --- ## 📋 目录 1. [概述](#概述) 2. [WebSocket 实时通信](#websocket-实时通信) 3. [HTTP REST API](#http-rest-api) 4. [数据结构定义](#数据结构定义) 5. [错误码说明](#错误码说明) 6. [前端对接流程](#前端对接流程) --- ## 概述 ### 技术栈建议 - **WebSocket**: 实时游戏状态、投注更新、开奖推送 - **HTTP REST API**: 查询房间信息、历史记录、用户余额 ### 认证方式 所有 API 请求需要用户已登录,Session Cookie 会自动携带。 ```javascript // 前端登录后 Session 自动保存 fetch('/api/auth/login', { method: 'POST', credentials: 'include', // 重要:携带 Cookie headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'xxx', password: 'xxx' }) }) ``` --- ## WebSocket 实时通信 ### 连接信息 ``` WebSocket URL: ws://yourdomain.com/ws/xocdia 或 (SSL): wss://yourdomain.com/ws/xocdia ``` ### 连接示例 ```javascript const ws = new WebSocket('ws://yourdomain.com/ws/xocdia'); ws.onopen = () => { console.log('WebSocket 连接成功'); // 订阅房间 ws.send(JSON.stringify({ action: 'SUBSCRIBE_ROOM', data: { room_id: 1 } })); }; ws.onmessage = (event) => { const message = JSON.parse(event.data); handleServerEvent(message); }; ws.onerror = (error) => { console.error('WebSocket 错误:', error); }; ws.onclose = () => { console.log('WebSocket 连接关闭'); // 实现重连逻辑 }; ``` --- ## 🔽 服务器 → 客户端事件 ### 1. GAME_START - 期号开始 **说明**: 新期号开始,进入下注阶段 ```json { "event": "GAME_START", "data": { "period_id": 12345, "period_number": "20260108-001", "room_id": 1, "state": "betting", "countdown": 30, "server_time": "2026-01-08T10:00:00Z", "config": { "odds": { "even": 1.0, "odd": 1.0, "four_red": 10.0, "four_white": 10.0, "three_red": 3.5, "three_white": 3.5 }, "limits": { "min_bet": 10, "max_bet": 50000, "max_payout": 500000 }, "timing": { "betting_duration": 30 } } } } ``` **前端处理**: ```javascript function handleGameStart(data) { // 1. 显示期号信息 document.getElementById('period-number').textContent = data.period_number; // 2. 启动倒计时 startCountdown(data.countdown); // 3. 开放投注按钮 enableBettingButtons(); // 4. 清空上期投注记录 clearPreviousBets(); } ``` --- ### 2. COUNTDOWN_UPDATE - 倒计时更新 **说明**: 每秒推送一次剩余时间 ```json { "event": "COUNTDOWN_UPDATE", "data": { "period_id": 12345, "remaining": 25, "state": "betting" } } ``` **前端处理**: ```javascript function handleCountdownUpdate(data) { document.getElementById('countdown').textContent = data.remaining + '秒'; if (data.remaining <= 5) { // 最后 5 秒闪烁提醒 document.getElementById('countdown').classList.add('urgent'); } } ``` --- ### 3. BET_SUCCESS - 投注成功 **说明**: 用户投注成功的确认 ```json { "event": "BET_SUCCESS", "data": { "bet_id": 67890, "user_id": 456, "period_id": 12345, "period_number": "20260108-001", "bet_type": "odd", "bet_amount": 100, "odds": 1.0, "potential_win": 100, "remaining_balance": 9900, "timestamp": "2026-01-08T10:00:15Z" } } ``` **前端处理**: ```javascript function handleBetSuccess(data) { // 1. 更新余额显示 document.getElementById('balance').textContent = data.remaining_balance; // 2. 添加到投注列表 addBetToList({ type: data.bet_type, amount: data.bet_amount, potentialWin: data.potential_win }); // 3. 显示成功提示 showToast('投注成功!', 'success'); } ``` --- ### 4. BETTING_CLOSED - 封盘 **说明**: 下注阶段结束,不再接受投注 ```json { "event": "BETTING_CLOSED", "data": { "period_id": 12345, "period_number": "20260108-001", "state": "shaking", "message": "投注已封盘,等待开奖...", "total_bets_count": 150, "total_bets_amount": 25000 } } ``` **前端处理**: ```javascript function handleBettingClosed(data) { // 1. 禁用投注按钮 disableBettingButtons(); // 2. 停止倒计时 stopCountdown(); // 3. 显示"等待开奖"动画 showShakingAnimation(); // 4. 显示本期投注统计 showBetStats(data.total_bets_count, data.total_bets_amount); } ``` --- ### 5. GAME_RESULT - 开奖结果 **说明**: 开奖结果公布及用户结算信息 ```json { "event": "GAME_RESULT", "data": { "period_id": 12345, "period_number": "20260108-001", "result": { "coins": [1, 1, 1, 0], "pattern": "3_red_1_white", "red_count": 3, "white_count": 1, "display_text": "3 Đen 1 Trắng" }, "settlement": { "your_bets": [ { "bet_id": 67890, "bet_type": "odd", "bet_amount": 100, "odds": 1.0, "is_win": true, "win_amount": 100, "commission": 0, "net_profit": 100, "status": "win", "special_rule_applied": false } ], "total_bet_amount": 100, "total_win_amount": 100, "net_profit": 100, "new_balance": 10000 }, "statistics": { "total_players": 50, "total_bets_amount": 50000, "big_winners": [ { "username": "player***", "win_amount": 5000 } ] } } } ``` **前端处理**: ```javascript function handleGameResult(data) { // 1. 显示开奖动画 showResultAnimation(data.result.coins); // 2. 更新余额 document.getElementById('balance').textContent = data.settlement.new_balance; // 3. 显示输赢结果 data.settlement.your_bets.forEach(bet => { if (bet.is_win) { showWinNotification(bet.net_profit); } else { showLoseNotification(bet.bet_amount); } }); // 4. 更新投注历史 updateBetHistory(data); // 5. 显示本期统计 showGameStatistics(data.statistics); } ``` --- ### 6. BET_STATS_UPDATE - 实时投注统计 **说明**: 实时推送当前期号的投注分布(可选功能) ```json { "event": "BET_STATS_UPDATE", "data": { "period_id": 12345, "stats": { "even": { "amount": 10000, "count": 50, "percentage": 40 }, "odd": { "amount": 8000, "count": 40, "percentage": 32 }, "four_red": { "amount": 3000, "count": 15, "percentage": 12 } }, "total_amount": 25000, "total_count": 150 } } ``` **前端处理**: ```javascript function handleBetStatsUpdate(data) { // 更新投注分布图表 updateBetDistributionChart(data.stats); // 显示热门投注项 highlightPopularBets(data.stats); } ``` --- ### 7. CONFIG_UPDATED - 配置更新 **说明**: 房间配置被管理员修改 ```json { "event": "CONFIG_UPDATED", "data": { "room_id": 1, "new_config": { "odds": { "even": 1.0, "odd": 1.0, "four_red": 12.0, "four_white": 12.0, "three_red": 4.0, "three_white": 4.0 }, "limits": { "min_bet": 10, "max_bet": 100000 } }, "effective_from": "next_period", "message": "房间配置已更新,下期生效" } } ``` **前端处理**: ```javascript function handleConfigUpdated(data) { // 显示配置更新通知 showNotification(data.message, 'info'); // 可选:提前显示新赔率 previewNewOdds(data.new_config.odds); } ``` --- ### 8. ERROR - 错误通知 **说明**: 服务器推送的错误信息 ```json { "event": "ERROR", "data": { "code": "BET_AMOUNT_INVALID", "message": "投注金额超出限制", "details": { "min_bet": 10, "max_bet": 50000, "your_bet": 100000 } } } ``` --- ## 🔼 客户端 → 服务器事件 ### 1. SUBSCRIBE_ROOM - 订阅房间 **说明**: 连接后立即订阅房间,接收该房间的实时消息 ```json { "action": "SUBSCRIBE_ROOM", "data": { "room_id": 1 } } ``` --- ### 2. PLACE_BET - 下注 **说明**: 用户下注 ```json { "action": "PLACE_BET", "data": { "room_id": 1, "period_id": 12345, "bet_type": "odd", "bet_amount": 100, "client_timestamp": "2026-01-08T10:00:15Z" } } ``` **服务器响应**: `BET_SUCCESS` 或 `ERROR` 事件 --- ### 3. GET_GAME_STATE - 获取当前游戏状态 **说明**: 用户刷新页面或重连后,获取当前游戏状态 ```json { "action": "GET_GAME_STATE", "data": { "room_id": 1 } } ``` **服务器响应**: ```json { "event": "GAME_STATE", "data": { "room_id": 1, "current_period": { "period_id": 12345, "period_number": "20260108-001", "state": "betting", "countdown": 18, "started_at": "2026-01-08T10:00:00Z" }, "your_bets": [ { "bet_id": 67890, "bet_type": "odd", "bet_amount": 100, "status": "pending" } ], "config": { /* 房间配置 */ } } } ``` --- ### 4. GET_BET_HISTORY - 获取投注历史 **说明**: 查询用户历史投注记录 ```json { "action": "GET_BET_HISTORY", "data": { "room_id": 1, "limit": 20, "offset": 0 } } ``` **服务器响应**: ```json { "event": "BET_HISTORY", "data": { "bets": [ { "period_number": "20260108-001", "bet_type": "odd", "bet_amount": 100, "status": "win", "net_profit": 95, "result": { "coins": [1, 1, 1, 0], "pattern": "3_red_1_white" }, "created_at": "2026-01-08T10:00:15Z" } ], "total": 150, "limit": 20, "offset": 0 } } ``` --- ## HTTP REST API ### 1. 获取房间列表 **请求**: ```http GET /api/xocdia/rooms ``` **响应**: ```json { "success": true, "data": [ { "room_id": 1, "room_name": "Bàn Thu Phế", "room_type": "commission", "status": 1, "odds": { "even": 1.0, "odd": 1.0, "four_red": 10.0, "four_white": 10.0, "three_red": 3.5, "three_white": 3.5 }, "limits": { "min_bet": 10, "max_bet": 50000, "max_payout": 500000 }, "online_players": 50, "current_period": { "period_number": "20260108-001", "state": "betting", "countdown": 25 } } ] } ``` **前端调用示例**: ```javascript async function fetchRooms() { const response = await fetch('/api/xocdia/rooms', { credentials: 'include' }); const data = await response.json(); if (data.success) { renderRoomList(data.data); } } ``` --- ### 2. 获取房间详情 **请求**: ```http GET /api/xocdia/rooms/{room_id} ``` **响应**: ```json { "success": true, "data": { "room_id": 1, "room_name": "Bàn Thu Phế", "room_type": "commission", "commission_rate": 0.05, "odds": { /* ... */ }, "limits": { /* ... */ }, "timing": { "betting_duration": 30, "result_display_duration": 8 }, "statistics": { "today_periods": 50, "today_bets": 5000, "today_turnover": 500000 } } } ``` --- ### 3. 获取当前期号信息 **请求**: ```http GET /api/xocdia/periods/current?room_id=1 ``` **响应**: ```json { "success": true, "data": { "period_id": 12345, "period_number": "20260108-001", "room_id": 1, "state": "betting", "started_at": "2026-01-08T10:00:00Z", "countdown": 25, "config_snapshot": { "odds": { /* ... */ }, "limits": { /* ... */ } } } } ``` **前端调用示例**: ```javascript async function getCurrentPeriod(roomId) { const response = await fetch(`/api/xocdia/periods/current?room_id=${roomId}`, { credentials: 'include' }); const data = await response.json(); if (data.success && data.data) { return data.data; } return null; } ``` --- ### 4. 下注(HTTP 方式,备用) **请求**: ```http POST /api/xocdia/bet Content-Type: application/json { "room_id": 1, "period_id": 12345, "bet_type": "odd", "bet_amount": 100 } ``` **响应**: ```json { "success": true, "message": "投注成功", "data": { "bet_id": 67890, "remaining_balance": 9900, "potential_win": 100 } } ``` **错误响应**: ```json { "success": false, "code": "INSUFFICIENT_BALANCE", "message": "余额不足", "details": { "current_balance": 50, "required": 100 } } ``` **前端调用示例**: ```javascript async function placeBet(roomId, periodId, betType, betAmount) { try { const response = await fetch('/api/xocdia/bet', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ room_id: roomId, period_id: periodId, bet_type: betType, bet_amount: betAmount }) }); const data = await response.json(); if (data.success) { // 更新余额 updateBalance(data.data.remaining_balance); showToast('投注成功', 'success'); } else { showToast(data.message, 'error'); } return data; } catch (error) { console.error('投注失败:', error); showToast('网络错误,请重试', 'error'); } } ``` --- ### 5. 获取开奖历史 **请求**: ```http GET /api/xocdia/periods/history?room_id=1&limit=20&offset=0 ``` **响应**: ```json { "success": true, "data": { "periods": [ { "period_number": "20260108-001", "result": { "coins": [1, 1, 1, 0], "pattern": "3_red_1_white", "display_text": "3 Đen 1 Trắng" }, "settled_at": "2026-01-08T10:01:30Z", "total_bets": 150, "total_amount": 25000 } ], "total": 500, "limit": 20, "offset": 0 } } ``` --- ### 6. 获取用户投注记录 **请求**: ```http GET /api/xocdia/my-bets?room_id=1&limit=20&offset=0 ``` **响应**: ```json { "success": true, "data": { "bets": [ { "bet_id": 67890, "period_number": "20260108-001", "bet_type": "odd", "bet_amount": 100, "odds": 1.0, "status": "win", "win_amount": 100, "commission": 5, "net_profit": 95, "result": { "coins": [1, 1, 1, 0], "pattern": "3_red_1_white" }, "created_at": "2026-01-08T10:00:15Z", "settled_at": "2026-01-08T10:01:30Z" } ], "total": 150, "limit": 20, "offset": 0, "summary": { "total_bet_amount": 15000, "total_win_amount": 8000, "net_profit": -7000 } } } ``` --- ### 7. 获取用户余额 **请求**: ```http GET /api/user/balance ``` **响应**: ```json { "success": true, "data": { "balance": 10000.50, "currency": "VND" } } ``` --- ## 数据结构定义 ### GameState(游戏状态) ```typescript type GameState = 'waiting' | 'betting' | 'shaking' | 'settling' | 'showing'; ``` ### BetType(投注类型) ```typescript type BetType = | 'even' // 双 (Chẵn) | 'odd' // 单 (Lẻ) | 'four_red' // 4红 (4 Đen) | 'four_white' // 4白 (4 Trắng) | 'three_red' // 3红1白 (3 Đen 1 Trắng) | 'three_white'; // 1红3白 (1 Đen 3 Trắng) ``` ### BetStatus(投注状态) ```typescript type BetStatus = 'pending' | 'win' | 'lose' | 'refund'; ``` ### GameResult(开奖结果) ```typescript interface GameResult { coins: [number, number, number, number]; // [1,1,1,0] = 3红1白 pattern: string; // '3_red_1_white' red_count: number; // 3 white_count: number; // 1 display_text: string; // '3 Đen 1 Trắng' } ``` ### RoomConfig(房间配置) ```typescript interface RoomConfig { room_id: number; room_name: string; room_type: 'commission' | 'special_rule'; status: 0 | 1; // 0=关闭, 1=开放 odds: { even: number; odd: number; four_red: number; four_white: number; three_red: number; three_white: number; }; limits: { min_bet: number; max_bet: number; max_payout: number; }; timing: { betting_duration: number; result_display_duration: number; }; commission_rate?: number; // 抽水比例(0.05 = 5%) special_rule?: { enabled: boolean; trigger_result: string; affected_bet_types: string[]; action: 'refund' | 'half_win' | 'lose'; refund_percentage: number; }; } ``` --- ## 错误码说明 ### 客户端错误 (4xx) | 错误码 | 说明 | 解决方案 | |-------|------|---------| | `NOT_LOGGED_IN` | 用户未登录 | 跳转到登录页 | | `INSUFFICIENT_BALANCE` | 余额不足 | 提示用户充值 | | `BET_AMOUNT_INVALID` | 投注金额无效 | 检查最小/最大限制 | | `BET_TYPE_INVALID` | 投注类型无效 | 检查投注类型是否正确 | | `BETTING_CLOSED` | 已封盘 | 提示用户等待下一期 | | `PERIOD_NOT_FOUND` | 期号不存在 | 刷新页面获取最新期号 | | `DUPLICATE_BET` | 重复投注 | 防止重复提交 | ### 服务器错误 (5xx) | 错误码 | 说明 | 解决方案 | |-------|------|---------| | `SETTLEMENT_FAILED` | 结算失败 | 联系客服 | | `DATABASE_ERROR` | 数据库错误 | 稍后重试 | | `WEBSOCKET_ERROR` | WebSocket 连接错误 | 尝试重连 | --- ## 前端对接流程 ### 第一步:初始化 ```javascript class XocdiaGame { constructor(roomId) { this.roomId = roomId; this.ws = null; this.currentPeriod = null; this.userBets = []; this.balance = 0; } async init() { // 1. 获取房间配置 await this.fetchRoomConfig(); // 2. 获取用户余额 await this.fetchBalance(); // 3. 连接 WebSocket await this.connectWebSocket(); // 4. 订阅房间 this.subscribeRoom(); // 5. 获取当前游戏状态 this.getGameState(); } async fetchRoomConfig() { const response = await fetch(`/api/xocdia/rooms/${this.roomId}`, { credentials: 'include' }); const data = await response.json(); if (data.success) { this.roomConfig = data.data; this.renderRoomInfo(); } } async fetchBalance() { const response = await fetch('/api/user/balance', { credentials: 'include' }); const data = await response.json(); if (data.success) { this.balance = data.data.balance; this.updateBalanceDisplay(); } } connectWebSocket() { return new Promise((resolve, reject) => { this.ws = new WebSocket('ws://yourdomain.com/ws/xocdia'); this.ws.onopen = () => { console.log('WebSocket 连接成功'); resolve(); }; this.ws.onmessage = (event) => { const message = JSON.parse(event.data); this.handleServerEvent(message); }; this.ws.onerror = (error) => { console.error('WebSocket 错误:', error); reject(error); }; this.ws.onclose = () => { console.log('WebSocket 断开,尝试重连...'); setTimeout(() => this.connectWebSocket(), 3000); }; }); } subscribeRoom() { this.ws.send(JSON.stringify({ action: 'SUBSCRIBE_ROOM', data: { room_id: this.roomId } })); } getGameState() { this.ws.send(JSON.stringify({ action: 'GET_GAME_STATE', data: { room_id: this.roomId } })); } } ``` --- ### 第二步:处理服务器事件 ```javascript class XocdiaGame { // ... 上面的代码 ... handleServerEvent(message) { switch (message.event) { case 'GAME_START': this.handleGameStart(message.data); break; case 'COUNTDOWN_UPDATE': this.handleCountdownUpdate(message.data); break; case 'BET_SUCCESS': this.handleBetSuccess(message.data); break; case 'BETTING_CLOSED': this.handleBettingClosed(message.data); break; case 'GAME_RESULT': this.handleGameResult(message.data); break; case 'ERROR': this.handleError(message.data); break; case 'GAME_STATE': this.handleGameState(message.data); break; } } handleGameStart(data) { this.currentPeriod = data; this.userBets = []; // UI 更新 document.getElementById('period-number').textContent = data.period_number; this.startCountdown(data.countdown); this.enableBettingButtons(); } handleCountdownUpdate(data) { document.getElementById('countdown').textContent = data.remaining; if (data.remaining <= 5) { document.getElementById('countdown').classList.add('urgent'); } } handleBetSuccess(data) { // 更新余额 this.balance = data.remaining_balance; this.updateBalanceDisplay(); // 添加到投注列表 this.userBets.push(data); this.renderUserBets(); // 显示提示 this.showToast('投注成功!', 'success'); } handleBettingClosed(data) { this.disableBettingButtons(); this.stopCountdown(); this.showShakingAnimation(); } handleGameResult(data) { // 显示开奖动画 this.showResultAnimation(data.result); // 更新余额 this.balance = data.settlement.new_balance; this.updateBalanceDisplay(); // 显示输赢结果 this.showSettlementResult(data.settlement); // 更新历史记录 this.updateHistory(data); } handleError(data) { this.showToast(data.message, 'error'); } handleGameState(data) { // 恢复游戏状态(用于刷新后) this.currentPeriod = data.current_period; this.userBets = data.your_bets; if (data.current_period.state === 'betting') { this.startCountdown(data.current_period.countdown); this.enableBettingButtons(); } } } ``` --- ### 第三步:投注操作 ```javascript class XocdiaGame { // ... 上面的代码 ... async placeBet(betType, betAmount) { // 前端验证 if (!this.currentPeriod || this.currentPeriod.state !== 'betting') { this.showToast('当前不可下注', 'error'); return; } if (betAmount < this.roomConfig.limits.min_bet) { this.showToast(`最小投注 ${this.roomConfig.limits.min_bet}`, 'error'); return; } if (betAmount > this.roomConfig.limits.max_bet) { this.showToast(`最大投注 ${this.roomConfig.limits.max_bet}`, 'error'); return; } if (betAmount > this.balance) { this.showToast('余额不足', 'error'); return; } // 通过 WebSocket 发送 this.ws.send(JSON.stringify({ action: 'PLACE_BET', data: { room_id: this.roomId, period_id: this.currentPeriod.period_id, bet_type: betType, bet_amount: betAmount, client_timestamp: new Date().toISOString() } })); // 或通过 HTTP 发送(备用) // const response = await fetch('/api/xocdia/bet', { ... }); } // 快捷投注方法 betEven(amount) { this.placeBet('even', amount); } betOdd(amount) { this.placeBet('odd', amount); } betFourRed(amount) { this.placeBet('four_red', amount); } betFourWhite(amount) { this.placeBet('four_white', amount); } betThreeRed(amount) { this.placeBet('three_red', amount); } betThreeWhite(amount) { this.placeBet('three_white', amount); } } ``` --- ### 第四步:UI 渲染 ```javascript class XocdiaGame { // ... 上面的代码 ... renderRoomInfo() { document.getElementById('room-name').textContent = this.roomConfig.room_name; // 渲染赔率 document.getElementById('odds-even').textContent = `1:${this.roomConfig.odds.even}`; document.getElementById('odds-odd').textContent = `1:${this.roomConfig.odds.odd}`; document.getElementById('odds-four-red').textContent = `1:${this.roomConfig.odds.four_red}`; // ... } updateBalanceDisplay() { document.getElementById('balance').textContent = this.balance.toLocaleString(); } renderUserBets() { const container = document.getElementById('user-bets-list'); container.innerHTML = ''; this.userBets.forEach(bet => { const betElement = document.createElement('div'); betElement.className = 'bet-item'; betElement.innerHTML = ` ${this.getBetTypeLabel(bet.bet_type)} ${bet.bet_amount} 可赢 ${bet.potential_win} `; container.appendChild(betElement); }); } showResultAnimation(result) { const container = document.getElementById('result-animation'); container.innerHTML = ''; result.coins.forEach(coin => { const coinElement = document.createElement('div'); coinElement.className = coin === 1 ? 'coin red' : 'coin white'; coinElement.textContent = '●'; container.appendChild(coinElement); }); // 显示结果文字 document.getElementById('result-text').textContent = result.display_text; } showSettlementResult(settlement) { if (settlement.net_profit > 0) { this.showWinAnimation(settlement.net_profit); } else if (settlement.net_profit < 0) { this.showLoseAnimation(Math.abs(settlement.net_profit)); } } getBetTypeLabel(betType) { const labels = { 'even': '双 (Chẵn)', 'odd': '单 (Lẻ)', 'four_red': '4红', 'four_white': '4白', 'three_red': '3红1白', 'three_white': '1红3白' }; return labels[betType] || betType; } } ``` --- ### 第五步:使用示例 ```javascript // 初始化游戏 const game = new XocdiaGame(1); // room_id = 1 game.init().then(() => { console.log('游戏初始化完成'); // 绑定投注按钮 document.getElementById('btn-bet-even').addEventListener('click', () => { const amount = parseFloat(document.getElementById('bet-amount').value); game.betEven(amount); }); document.getElementById('btn-bet-odd').addEventListener('click', () => { const amount = parseFloat(document.getElementById('bet-amount').value); game.betOdd(amount); }); // 快捷金额按钮 document.getElementById('btn-bet-100').addEventListener('click', () => { document.getElementById('bet-amount').value = 100; }); document.getElementById('btn-bet-500').addEventListener('click', () => { document.getElementById('bet-amount').value = 500; }); }).catch(error => { console.error('游戏初始化失败:', error); }); ``` --- ## 📝 开发建议 ### 1. 错误处理 ```javascript // 全局错误处理 window.addEventListener('error', (event) => { console.error('全局错误:', event.error); // 上报到错误监控平台 }); // WebSocket 重连策略 class WebSocketReconnect { constructor(url, maxRetries = 5) { this.url = url; this.maxRetries = maxRetries; this.retryCount = 0; this.retryDelay = 1000; } connect() { this.ws = new WebSocket(this.url); this.ws.onclose = () => { if (this.retryCount < this.maxRetries) { this.retryCount++; setTimeout(() => { console.log(`重连中... (${this.retryCount}/${this.maxRetries})`); this.connect(); }, this.retryDelay * this.retryCount); } else { console.error('WebSocket 重连失败'); // 提示用户刷新页面 } }; this.ws.onopen = () => { this.retryCount = 0; }; } } ``` ### 2. 性能优化 ```javascript // 防抖投注(防止重复点击) const debounceBet = debounce((betType, amount) => { game.placeBet(betType, amount); }, 300); // 虚拟滚动(历史记录) function renderHistoryWithVirtualScroll(records) { // 使用虚拟滚动库,只渲染可见部分 } // 图片懒加载 const lazyImages = document.querySelectorAll('img[data-src]'); const imageObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; imageObserver.unobserve(img); } }); }); lazyImages.forEach(img => imageObserver.observe(img)); ``` ### 3. 测试建议 ```javascript // 模拟 WebSocket 消息(用于测试) function mockGameStart() { const mockEvent = { event: 'GAME_START', data: { period_id: 99999, period_number: 'TEST-001', room_id: 1, state: 'betting', countdown: 30, config: { /* ... */ } } }; game.handleServerEvent(mockEvent); } function mockGameResult() { const mockEvent = { event: 'GAME_RESULT', data: { result: { coins: [1, 1, 1, 0], pattern: '3_red_1_white' }, settlement: { /* ... */ } } }; game.handleServerEvent(mockEvent); } ``` --- ## 📞 技术支持 如有疑问,请联系后端开发团队。 **文档版本**: v1.0.0 **最后更新**: 2026-01-08 **维护者**: Backend Team