first commit
This commit is contained in:
@@ -0,0 +1,727 @@
|
||||
# 疯狂转盘 前端接口文档
|
||||
|
||||
> **版本**:v2.0
|
||||
> **更新时间**:2025-01
|
||||
> **适用前端**:Web/H5/小程序
|
||||
> **Base URL**:`{host}/api/wheel`
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
1. [玩法概述](#一玩法概述)
|
||||
2. [通用约定](#二通用约定)
|
||||
3. [接口清单](#三接口清单)
|
||||
4. [接口详情](#四接口详情)
|
||||
5. [时间轴使用指南](#五时间轴使用指南)
|
||||
6. [前端对接要点](#六前端对接要点)
|
||||
|
||||
---
|
||||
|
||||
## 一、玩法概述
|
||||
|
||||
疯狂转盘是一款 **选颜色+符号 → 下注 → 转盘停 → 命中得饰品** 的真人轮盘游戏。
|
||||
|
||||
### 核心规则
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **盘面结构** | 内外圈各 24 格,索引一一对应<br>• 外圈符号:固定布局(A金/B红/C紫/D蓝)<br>• 内圈颜色:每轮随机排布(金6/红8/紫10) |
|
||||
| **下注方式** | 选 1 个「颜色×符号」组合 + 1 个下注额<br>**每轮每人最多 50 注** |
|
||||
| **下注额档位** | 10 档:5 / 10 / 20 / 50 / 100 / 200 / 500 / 1000 / 2000 / 5000 元 |
|
||||
| **倍率** | 12 个倍率(3 色 × 4 符号),所有档位共用<br>例:gold+D 倍率=22,押 100 元中奖得 2200 元 |
|
||||
| **命中判定** | 下注的 `color+symbol` == 开奖的 `resultColor+resultSymbol` |
|
||||
| **中奖奖励** | 饰品价值 = **下注额 × 倍率**<br>直接进背包,无需选择"保留/分解" |
|
||||
| **未中奖励** | 每注发 **0.01 元安慰饰品**(保证每注必有产出) |
|
||||
| **决策池** | 单一真实奖池,所有人同一个落点<br>• `real`(真实池,参与落点/RTP 决策)= 普通(02)+主播(01)+招商(04)+卡商(05)<br>• `display`(展示,可下注/返奖但不入真实池)= 机器人(03)+白号(06) |
|
||||
|
||||
### 一局生命周期
|
||||
|
||||
```
|
||||
newRound 创建
|
||||
↓
|
||||
下注期 (30s) ← 玩家可调用 /dobet 下注
|
||||
↓
|
||||
封盘期 (2s) ← 不再接受下注,倒计时"即将开奖"
|
||||
↓
|
||||
开奖 + 动画 (7s) ← 后端出结果,前端播转盘动画
|
||||
↓
|
||||
结果展示 → 下一局
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、通用约定
|
||||
|
||||
### 2.1 请求方式
|
||||
|
||||
- **协议**:HTTPS
|
||||
- **Method**:全部 `POST`
|
||||
- **Content-Type**:`application/json`
|
||||
- **鉴权**:`Authorization: Bearer {token}`(登录后从响应头或登录接口获取)
|
||||
|
||||
> ⚠️ **重要**:`userId` 由后端从 token 解析,**客户端不要传 userId 参数**。
|
||||
|
||||
### 2.2 统一响应结构
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
| code | 含义 | 前端处理 |
|
||||
|------|------|----------|
|
||||
| 200 | 成功 | 正常处理 data |
|
||||
| 500 | 业务失败 | Toast 显示 msg |
|
||||
| 701 | 未实名认证 | 弹实名认证弹窗 |
|
||||
| 702 | 账户余额不足 | 提示充值 |
|
||||
| 703 | 玩家未充值,不能游玩 | 引导首充 |
|
||||
|
||||
### 2.3 货币单位
|
||||
|
||||
- 所有金额字段为 **String 类型**,单位:**元**(精确到小数点后 2 位)
|
||||
- 示例:`"balance": "9800.50"` 表示 9800.50 元
|
||||
|
||||
### 2.4 时间格式
|
||||
|
||||
- **时间戳**:`Long` 类型,**秒级** Unix 时间戳(如 `1738300000`)
|
||||
- **时间字符串**:`yyyy-MM-dd HH:mm:ss`(东八区)
|
||||
|
||||
---
|
||||
|
||||
## 三、接口清单
|
||||
|
||||
| # | 接口名称 | 路径 | 用途 | 调用时机 |
|
||||
|---|---------|------|------|---------|
|
||||
| 1 | ~~获取游戏配置~~ | ~~`/game_config`~~ | **已废弃**,数据已合并到 `/last_round_info` | ~~进入游戏页时调用 1 次~~ |
|
||||
| 2 | 投注 | `/dobet` | 下注一笔 | 玩家点击下注按钮 |
|
||||
| 3 | 获取当前局状态 | `/last_round_info` | **一次拿全**:配置+状态+开奖+下注 | **轮询**(1-2 秒间隔) |
|
||||
| 4 | 我的下注记录 | `/history_bet_info` | 我的全部下注记录(中奖+未中奖,分页) | 打开"记录"页 |
|
||||
| 5 | 全服中奖记录 | `/round_history_bet_info` | 全服中奖公示(只含真正命中,分页) | 打开"中奖榜"页 |
|
||||
| 6 | 历史开奖结果 | `/round_results` | 每一轮的开奖符号(分页) | 打开"历史开奖"页 |
|
||||
|
||||
---
|
||||
|
||||
## 四、接口详情
|
||||
|
||||
### 4.1 ~~获取游戏配置~~ (已废弃)
|
||||
|
||||
> **⚠️ 已废弃**:本接口数据已全部合并到 `/last_round_info`,前端只需轮询一个接口即可。
|
||||
> 旧接口保留仅为兼容旧版前端,新前端请直接使用 `/last_round_info`。
|
||||
|
||||
---
|
||||
|
||||
### 4.2 投注
|
||||
|
||||
**`POST /api/wheel/dobet`**
|
||||
|
||||
下注一笔。**每轮每人上限 50 注**。
|
||||
|
||||
#### 请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"bet": 100,
|
||||
"color": "gold",
|
||||
"symbol": "D"
|
||||
}
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `bet` | Integer | ✅ | 下注额,必须是 10 档之一 |
|
||||
| `color` | String | ✅ | 内圈色:`gold` / `red` / `purple` |
|
||||
| `symbol` | String | ✅ | 外圈符:`A` / `B` / `C` / `D` |
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"roundId": 10086,
|
||||
"cost": "100.00",
|
||||
"balance": "9700.50",
|
||||
"credits": "1200.00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `roundId` | Long | 本回合 ID |
|
||||
| `cost` | String | 本次扣款(= 下注额) |
|
||||
| `balance` | String | 下注后余额(金币) |
|
||||
| `credits` | String | 下注后积分 |
|
||||
|
||||
#### 常见错误
|
||||
|
||||
| code | msg | 场景 |
|
||||
|------|-----|------|
|
||||
| 500 | 网站维护中...... | 全站维护 |
|
||||
| 500 | 疯狂转盘维护中...... | 转盘维护 |
|
||||
| 500 | 游戏不在投注期 | 已封盘/开奖中 |
|
||||
| 500 | 每轮最多下注50注 | 超出上限 |
|
||||
| 500 | 该下注组合未配置奖品 | 矩阵缺失(联系运营) |
|
||||
| 702 | 账户余额不足 | 需充值 |
|
||||
| 703 | 玩家未充值不能游玩任何游戏 | 需首充 |
|
||||
|
||||
---
|
||||
|
||||
### 4.3 获取当前局状态
|
||||
|
||||
**`POST /api/wheel/last_round_info`**
|
||||
|
||||
**核心接口**,轮询调用(建议 1-2 秒间隔),**一次返回全部数据**:游戏配置 + 当前回合状态 + 开奖结果 + 我的下注。
|
||||
|
||||
#### 请求参数
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"roundId": 10086,
|
||||
"periodNo": 202501011,
|
||||
"serverTime": 1738300050,
|
||||
"startTime": 1738300000,
|
||||
"bettedTime": 1738300030,
|
||||
"spinTime": 1738300032,
|
||||
"endTime": 1738300039,
|
||||
"status": "spined",
|
||||
|
||||
"outerSymbols": ["A","D","C","D","B","D","C","D","B","C","D","C","A","D","C","D","C","D","B","D","C","D","C","D"],
|
||||
"bets": [5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000],
|
||||
"myPoolType": "real",
|
||||
"rewardMatrix": [
|
||||
{
|
||||
"betAmount": 5,
|
||||
"color": "gold",
|
||||
"symbol": "D",
|
||||
"multiplier": 22,
|
||||
"prizeValue": "110.00",
|
||||
"prizeName": "黄金玫瑰",
|
||||
"prizeGrade": 3,
|
||||
"prizeExterior": "崭新出厂",
|
||||
"prizeImage": "https://example.com/prize.png",
|
||||
"prizeLevelImg": "https://example.com/level3.png"
|
||||
}
|
||||
],
|
||||
|
||||
"innerColors": ["gold","gold","red","purple","purple","red","gold","purple","red","purple","gold","purple","red","purple","gold","gold","purple","purple","red","gold","purple","red","purple"],
|
||||
"resultIndex": 5,
|
||||
"resultColor": "gold",
|
||||
"resultSymbol": "D",
|
||||
"totalWin": "2200.00",
|
||||
"userBets": [
|
||||
{
|
||||
"color": "gold",
|
||||
"symbol": "D",
|
||||
"count": 3,
|
||||
"totalBet": "300.00"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"symbol": "A",
|
||||
"count": 1,
|
||||
"totalBet": "50.00"
|
||||
}
|
||||
],
|
||||
"winResult": [
|
||||
{
|
||||
"color": "gold",
|
||||
"symbol": "D",
|
||||
"hit": true,
|
||||
"boxRecordId": 123456,
|
||||
"prizeName": "黄金玫瑰",
|
||||
"prizeImage": "https://example.com/prize.png",
|
||||
"prizeGrade": 5,
|
||||
"prizeLevelImg": "https://example.com/level5.png",
|
||||
"prizeExterior": "崭新出厂",
|
||||
"prizeValue": "2200.00"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"symbol": "A",
|
||||
"hit": false,
|
||||
"boxRecordId": 123457,
|
||||
"prizeName": "安慰饰品",
|
||||
"prizeImage": "https://example.com/consolation.png",
|
||||
"prizeGrade": 1,
|
||||
"prizeLevelImg": "https://example.com/level1.png",
|
||||
"prizeExterior": "崭新出厂",
|
||||
"prizeValue": "0.01"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应字段说明
|
||||
|
||||
**游戏配置字段**(原 `/game_config` 接口数据,现已合并):
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `outerSymbols` | Array\<String\> | 外圈 24 格符号固定布局(`A`/`B`/`C`/`D`)<br>例:`["A","D","C","D",...]` |
|
||||
| `bets` | Array\<Integer\> | 10 档下注额 |
|
||||
| `myPoolType` | String | 当前用户所属决策池<br>`real`:真实池(普通02/主播01/招商04/卡商05,参与落点与 RTP)<br>`display`:展示(机器人03/白号06,可下注/返奖但不入真实池)<br>`null`:不可参与(如超管99) |
|
||||
| `rewardMatrix` | Array | 奖励矩阵(120 条,10 档 × 3 色 × 4 符号)<br>每条含 `betAmount`/`color`/`symbol`/`multiplier` 及饰品信息(名称/图片/品质图片/磨损度/价格) |
|
||||
|
||||
**当局状态字段**:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `roundId` | Long | 回合 ID |
|
||||
| `periodNo` | Long | 全局唯一期号 |
|
||||
| `serverTime` | Long | **服务器当前时间**(秒级时间戳)<br>用于和其他时间点对比算倒计时 |
|
||||
| `startTime` | Long | 本局开始时间 |
|
||||
| `bettedTime` | Long | **封盘时间点** = startTime + 下注时间<br>到这个时刻后不能再下注 |
|
||||
| `spinTime` | Long | **开奖时间点** = bettedTime + 封盘时间<br>到这个时刻后开始播转盘动画 |
|
||||
| `endTime` | Long | **本局结束时间** = spinTime + 开奖动画时间<br>动画播完、下一局即将开始 |
|
||||
| `status` | String | `betting`:投注期<br>`spined`:封盘/开奖中 |
|
||||
| `innerColors` | Array\<String\> | **本轮内圈 24 色实际排列**(`gold`/`red`/`purple`)<br>例:`["gold","red","purple",...]` 用于渲染盘面 |
|
||||
| `resultIndex` | Integer | 开奖格子索引(0-23)<br>未开奖时为 `null` |
|
||||
| `resultColor` | String | 开奖颜色 `gold`/`red`/`purple`<br>未开奖时为 `null` |
|
||||
| `resultSymbol` | String | 开奖符号 `A`/`B`/`C`/`D`<br>未开奖时为 `null` |
|
||||
| `totalWin` | String | 当前用户本轮总返奖(饰品价值合计) |
|
||||
| `userBets` | Array | **当前用户本轮下注汇总**(按颜色+符号聚合,见下表) |
|
||||
| `winResult` | Array | **当前用户本轮发奖明细**(每注一条,见下表) |
|
||||
|
||||
**userBets 结构**(我的下注汇总,按颜色+符号聚合):
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `color` | String | 下注的颜色 |
|
||||
| `symbol` | String | 下注的符号 |
|
||||
| `count` | Integer | 下注次数 |
|
||||
| `totalBet` | String | 总下注额 |
|
||||
|
||||
**winResult 结构**(我的发奖明细,每注一条):
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `color` | String | 下注的颜色 |
|
||||
| `symbol` | String | 下注的符号 |
|
||||
| `hit` | Boolean | 是否命中(`true`=中奖矩阵饰品,`false`=安慰饰品) |
|
||||
| `boxRecordId` | Long | **背包记录ID**(用于调用分解/出售接口) |
|
||||
| `prizeName` | String | 实际发放的饰品名称 |
|
||||
| `prizeImage` | String | 饰品图片 URL |
|
||||
| `prizeGrade` | Integer | 饰品品质等级 |
|
||||
| `prizeLevelImg` | String | 饰品品质图片 URL |
|
||||
| `prizeExterior` | String | 饰品磨损度 |
|
||||
| `prizeValue` | String | 饰品价格(命中=下注额×倍率;安慰=0.01) |
|
||||
|
||||
> 💡 **前端使用提示**:
|
||||
> - 未开奖时 `winResult` 为空
|
||||
> - 已开奖后,`winResult` 每注一条:`hit=true` 是中奖饰品,`hit=false` 是安慰饰品(0.01元)
|
||||
> - 使用 `boxRecordId` 调用分解/出售接口
|
||||
|
||||
---
|
||||
|
||||
### 4.4 我的下注记录
|
||||
|
||||
**`POST /api/wheel/history_bet_info`**
|
||||
|
||||
当前登录用户自己的全部下注记录(中奖+未中奖),分页。未中奖注单(`win = 0.01` 安慰奖)的饰品字段为 `null`。
|
||||
|
||||
#### 请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNum": 1,
|
||||
"pageSize": 10
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"total": 25,
|
||||
"rows": [
|
||||
{
|
||||
"periodNo": 2026090725038,
|
||||
"endTime": "2026-09-07 09:14:49",
|
||||
"userId": 666299,
|
||||
"nickName": "李四2",
|
||||
"avatar": "/profile/upload/xxx.png",
|
||||
"bet": "5.00",
|
||||
"betSymbol": "D",
|
||||
"win": "40.00",
|
||||
"betCount": 3,
|
||||
"prizeGrade": 1,
|
||||
"prizeName": "某某饰品",
|
||||
"prizeImage": "/profile/upload/xxx.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `total` | Long | 总中奖注数 |
|
||||
| `rows` | Array | 中奖注单列表 |
|
||||
| `rows[].periodNo` | Long | 完整期号 |
|
||||
| `rows[].endTime` | String | 回合结束时间(yyyy-MM-dd HH:mm:ss) |
|
||||
| `rows[].userId` | Long | 用户 ID |
|
||||
| `rows[].nickName` | String | 用户昵称(完整,不脱敏) |
|
||||
| `rows[].avatar` | String | 用户头像 |
|
||||
| `rows[].bet` | String | 单注下注额 |
|
||||
| `rows[].betSymbol` | String | 中奖符号 |
|
||||
| `rows[].win` | String | 中奖额 |
|
||||
| `rows[].betCount` | Integer | 该中奖符号下注次数 |
|
||||
| `rows[].prizeGrade` | Integer | 饰品品质等级(对应 tt_ornaments_level.id:1金/2红/3紫/4蓝) |
|
||||
| `rows[].prizeName` | String | 饰品名称 |
|
||||
| `rows[].prizeImage` | String | 饰品图片 |
|
||||
|
||||
---
|
||||
|
||||
### 4.5 全服中奖记录
|
||||
|
||||
**`POST /api/wheel/round_history_bet_info`**
|
||||
|
||||
全服中奖公示(只保留真正命中 `win > 0.01` 的注单,排除 0.01 安慰奖),分页。结构与 `history_bet_info` 完全一致,只是数据范围为全服所有用户。
|
||||
|
||||
#### 请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNum": 1,
|
||||
"pageSize": 30
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"total": 99,
|
||||
"rows": [
|
||||
{
|
||||
"periodNo": 2026090725038,
|
||||
"endTime": "2026-09-07 09:14:49",
|
||||
"userId": 666299,
|
||||
"nickName": "李四2",
|
||||
"avatar": "/profile/upload/xxx.png",
|
||||
"bet": "5.00",
|
||||
"betSymbol": "D",
|
||||
"win": "40.00",
|
||||
"betCount": 3,
|
||||
"prizeGrade": 1,
|
||||
"prizeName": "某某饰品",
|
||||
"prizeImage": "/profile/upload/xxx.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `total` | Long | 总中奖注数 |
|
||||
| `rows` | Array | 中奖注单列表 |
|
||||
| `rows[].periodNo` | Long | 完整期号 |
|
||||
| `rows[].endTime` | String | 回合结束时间 |
|
||||
| `rows[].userId` | Long | 中奖用户 ID |
|
||||
| `rows[].nickName` | String | 中奖用户昵称(完整,不脱敏) |
|
||||
| `rows[].avatar` | String | 中奖用户头像 |
|
||||
| `rows[].bet` | String | 单注下注额 |
|
||||
| `rows[].betSymbol` | String | 中奖符号 |
|
||||
| `rows[].win` | String | 中奖额 |
|
||||
| `rows[].betCount` | Integer | 该中奖符号下注次数 |
|
||||
| `rows[].prizeGrade` | Integer | 饰品品质等级(对应 tt_ornaments_level.id:1金/2红/3紫/4蓝) |
|
||||
| `rows[].prizeName` | String | 饰品名称 |
|
||||
| `rows[].prizeImage` | String | 饰品图片 |
|
||||
|
||||
---
|
||||
|
||||
### 4.6 历史开奖结果
|
||||
|
||||
**`POST /api/wheel/round_results`**
|
||||
|
||||
获取每一轮的开奖符号(用于展示历史开奖记录,如右侧滚动的历史中奖列表)。
|
||||
|
||||
#### 请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNum": 1,
|
||||
"pageSize": 20
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `pageNum` | Integer | 否 | 页码(默认 1) |
|
||||
| `pageSize` | Integer | 否 | 每页条数(默认 20) |
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"rows": [
|
||||
{
|
||||
"periodNo": 2025010100086,
|
||||
"date": "20250101",
|
||||
"roundId": 86,
|
||||
"resultIndex": 5,
|
||||
"resultColor": "gold",
|
||||
"resultSymbol": "D",
|
||||
"endTime": "2025-01-01 12:15:39"
|
||||
},
|
||||
{
|
||||
"periodNo": 2025010100085,
|
||||
"date": "20250101",
|
||||
"roundId": 85,
|
||||
"resultIndex": 12,
|
||||
"resultColor": "red",
|
||||
"resultSymbol": "A",
|
||||
"endTime": "2025-01-01 12:15:00"
|
||||
}
|
||||
],
|
||||
"total": 2156
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `rows` | Array | 历史开奖记录列表 |
|
||||
| `rows[].periodNo` | Long | **全局唯一期号**(13位,前8位=日期yyyyMMdd,后5位=局号) |
|
||||
| `rows[].date` | String | **日期**(yyyyMMdd,从 periodNo 拆分而来) |
|
||||
| `rows[].roundId` | Long | **当天局号**(从 periodNo 拆分而来) |
|
||||
| `rows[].resultIndex` | Integer | 开奖格子索引(0-23) |
|
||||
| `rows[].resultColor` | String | 开奖颜色(gold/red/purple) |
|
||||
| `rows[].resultSymbol` | String | 开奖符号(A/B/C/D) |
|
||||
| `rows[].endTime` | String | 本局结束时间(yyyy-MM-dd HH:mm:ss) |
|
||||
| `total` | Long | 总记录数 |
|
||||
|
||||
#### 前端展示建议
|
||||
|
||||
```javascript
|
||||
// 展示期号
|
||||
function formatPeriodNo(periodNo) {
|
||||
const str = String(periodNo).padStart(13, '0');
|
||||
const year = str.slice(0, 4);
|
||||
const month = str.slice(4, 6);
|
||||
const day = str.slice(6, 8);
|
||||
const round = parseInt(str.slice(8)); // 去前导零
|
||||
return `${year}-${month}-${day}-${round}`;
|
||||
}
|
||||
|
||||
// 渲染列表
|
||||
data.rows.forEach(item => {
|
||||
// 期号:2025-01-01-86
|
||||
const periodText = formatPeriodNo(item.periodNo);
|
||||
|
||||
// 开奖结果:金色箱子 × 15
|
||||
const symbolIcon = getSymbolIcon(item.resultColor, item.resultSymbol);
|
||||
|
||||
renderHistoryItem(periodText, symbolIcon);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、时间轴使用指南
|
||||
|
||||
**核心**:后端返回的是**具体时间点**(startTime/bettedTime/spinTime/endTime),前端根据这些时间点和 `serverTime` 做倒计时和阶段切换。
|
||||
|
||||
### 一局的完整时间轴
|
||||
|
||||
```
|
||||
startTime (本局创建)
|
||||
│
|
||||
├─ 【下注期】serverTime < bettedTime
|
||||
│ 前端显示:倒计时 "距封盘 X 秒",接受下注
|
||||
│ 倒计时 = bettedTime - serverTime
|
||||
│
|
||||
├─ bettedTime (封盘时刻)
|
||||
│
|
||||
├─ 【封盘期】bettedTime ≤ serverTime < spinTime
|
||||
│ 前端显示:"封盘中",倒计时 "X 秒后开奖"
|
||||
│ 倒计时 = spinTime - serverTime
|
||||
│
|
||||
├─ spinTime (开奖时刻)
|
||||
│
|
||||
├─ 【开奖中】spinTime ≤ serverTime < endTime
|
||||
│ 前端显示:播转盘旋转动画
|
||||
│ 指针停在 resultIndex 格,显示 resultColor+resultSymbol
|
||||
│ 动画时长 = endTime - spinTime(由后台"开奖动画时间"配置)
|
||||
│
|
||||
├─ endTime (本局结束)
|
||||
│
|
||||
└─ 【等待下一局】serverTime ≥ endTime
|
||||
前端显示:倒计时 "下一局 X 秒后开始"
|
||||
继续轮询,拿到新 roundId 进入新一轮
|
||||
```
|
||||
|
||||
### 前端倒计时计算公式
|
||||
|
||||
| 阶段 | 判断条件 | 倒计时公式 | 显示文案 |
|
||||
|------|---------|-----------|---------|
|
||||
| **下注期** | `serverTime < bettedTime` | `bettedTime - serverTime` | "距封盘 X 秒" |
|
||||
| **封盘期** | `bettedTime ≤ serverTime < spinTime` | `spinTime - serverTime` | "X 秒后开奖" |
|
||||
| **开奖中** | `spinTime ≤ serverTime < endTime` | 播动画 | "转盘旋转中" |
|
||||
| **结果展示** | `serverTime ≥ endTime` | `下一局startTime - serverTime` | "下一局 X 秒后" |
|
||||
|
||||
### 关键提醒
|
||||
|
||||
1. **后端不返回时长配置**(下注时间/封盘时间/动画时间),只返回时间点。前端不需要自己算,直接用时间点差值。
|
||||
2. **时间点会随后台配置变化**:运营改"下注时间"从 30→50 秒,新局的 `bettedTime` 就会自动延后,前端倒计时自动变长。
|
||||
3. **开奖动画时长** = `endTime - spinTime`,这个窗口内播动画 + 展示结果。
|
||||
|
||||
---
|
||||
|
||||
## 六、前端对接要点
|
||||
|
||||
### 6.1 进入游戏页
|
||||
|
||||
```javascript
|
||||
// 只需轮询 /last_round_info,一次拿到配置+状态
|
||||
setInterval(async () => {
|
||||
const round = await getLastRoundInfo();
|
||||
|
||||
// 首次进入:用配置数据渲染静态元素
|
||||
if (firstTime) {
|
||||
renderWheel(round.outerSymbols);
|
||||
renderBetPanel(round.bets, round.rewardMatrix);
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
// 每次轮询:更新动态状态
|
||||
updateRoundState(round);
|
||||
}, 2000);
|
||||
```
|
||||
```
|
||||
|
||||
### 6.2 轮询当前局状态
|
||||
|
||||
```javascript
|
||||
// 每 1-2 秒调用一次
|
||||
setInterval(async () => {
|
||||
const round = await getLastRoundInfo();
|
||||
|
||||
// 根据 status 和时间点切换 UI
|
||||
if (round.status === 'betting') {
|
||||
if (round.serverTime < round.bettedTime) {
|
||||
// 下注期
|
||||
showCountdown('距封盘', round.bettedTime - round.serverTime);
|
||||
enableBetting(true);
|
||||
} else {
|
||||
// 封盘期(status 可能还未切到 spined)
|
||||
showCountdown('即将开奖', round.spinTime - round.serverTime);
|
||||
enableBetting(false);
|
||||
}
|
||||
} else if (round.status === 'spined') {
|
||||
// 已开奖
|
||||
if (round.resultIndex !== null) {
|
||||
// 播转盘动画,指针停在 resultIndex
|
||||
playSpinAnimation(round.resultIndex, round.innerColors);
|
||||
// 展示发奖明细(winResult 每注一条,hit 区分中奖/安慰)
|
||||
showWinResult(round.winResult, round.totalWin);
|
||||
}
|
||||
// 倒计时下一局
|
||||
if (round.serverTime >= round.endTime) {
|
||||
showCountdown('下一局', estimateNextRound());
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
```
|
||||
|
||||
### 6.3 下注
|
||||
|
||||
```javascript
|
||||
async function doBet(color, symbol, bet) {
|
||||
try {
|
||||
const res = await api.post('/api/wheel/dobet', { color, symbol, bet });
|
||||
if (res.code === 200) {
|
||||
// 扣款成功,更新余额
|
||||
updateBalance(res.data.balance, res.data.credits);
|
||||
// Toast 提示
|
||||
showToast('下注成功');
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.code === 702) {
|
||||
showToast('余额不足,请充值');
|
||||
} else if (err.code === 500) {
|
||||
showToast(err.msg); // "游戏不在投注期" / "每轮最多50注"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 展示饰品信息
|
||||
|
||||
```javascript
|
||||
// winResult 每注一条,hit 区分中奖/安慰
|
||||
round.winResult.forEach(item => {
|
||||
if (item.hit) {
|
||||
// 中奖,展示矩阵饰品
|
||||
showPrize({
|
||||
name: item.prizeName,
|
||||
image: item.prizeImage,
|
||||
levelImg: item.prizeLevelImg,
|
||||
exterior: item.prizeExterior,
|
||||
value: item.prizeValue,
|
||||
boxRecordId: item.boxRecordId
|
||||
});
|
||||
} else {
|
||||
// 未中,展示安慰饰品
|
||||
showConsolation(item.color, item.symbol);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 6.5 渲染盘面(innerColors)
|
||||
|
||||
```javascript
|
||||
// innerColors 是 24 个元素的数组,已经是 gold/red/purple
|
||||
// 例:["gold","red","purple",...]
|
||||
function renderWheel(innerColors, outerSymbols) {
|
||||
for (let i = 0; i < 24; i++) {
|
||||
const color = innerColors[i]; // 'gold' / 'red' / 'purple'
|
||||
const symbol = outerSymbols[i]; // 'A' / 'B' / 'C' / 'D'
|
||||
|
||||
// 渲染第 i 格:内圈色 + 外圈符号
|
||||
renderCell(i, color, symbol);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.6 注意事项
|
||||
|
||||
| 注意项 | 说明 |
|
||||
|-------|------|
|
||||
| **单接口轮询** | 只需轮询 `/last_round_info`,配置数据已合并,无需单独调 `/game_config` |
|
||||
| **不要自动重发 dobet** | 下注接口**非幂等**,失败时提示玩家手动重试 |
|
||||
| **轮询间隔** | 建议 1-2 秒,不要过于频繁 |
|
||||
| **时区** | 所有时间戳为东八区(UTC+8) |
|
||||
| **饰品已入背包** | 中奖后饰品直接进背包,无需"保留/分解"抉择<br>要兑现走通用背包分解接口 |
|
||||
| **维护模式** | 后台"游戏关闭"开关打开后:<br>① 新局无法下注(`dobet` 返回 500 维护提示)<br>② 轮询接口返回 `isMaintenance: true`,前端应弹窗并跳转<br>③ 进行中回合不强制结算,玩家可继续看结果 |
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user