Files
henan-backend/docs/流水抽奖接口文档.md
2026-09-21 19:21:35 +08:00

16 KiB
Raw Permalink Blame History

流水抽奖接口文档(Cocos 客户端对接)

一、功能说明

玩家无需额外付费,仅凭当日已发生的游戏下注流水自动累计抽奖资格。

核心规则:

规则 说明
流水来源 仅统计游戏消费流水,排除 Roll 房、商城消费、福利类
兑换比例 每个宝箱可单独配置流水门槛(默认 3000 流水 = 1 次)
过期机制 按自然日统计,次日零点自动清零,不累积
抽奖消耗 只消耗"今日流水额度",不扣余额、不扣积分
中奖发放 饰品直接进背包(tt_box_records.source = 14)
余额变动 只有玩家在背包分解该饰品时才产生余额/积分变动(source = 63 流水抽奖奖励)
同时开启 全站同一时间只有一个抽奖活动为开启状态

参与统计的流水来源码(TtAccountRecordSource.getTurnoverLotteryCodes()):

source 含义
2 经典开箱消费
4 对战消费
40 幸运升级消费
38 疯狂转盘消费
60 奥林匹斯消费

二、通用约定

  • Base URL:{host}/api/turnoverLottery
  • 请求方式:全部为 POST
  • Content-Type:application/json
  • 鉴权:需登录态,请求头携带 token。userId 由后端从 token 解析,客户端不要传 userId

统一响应结构

{
  "code": 200,
  "msg": "操作成功",
  "data": {}
}
code 含义
200 成功
500 业务失败,提示语见 msg
701 未实名认证,客户端统一弹实名认证窗口

客户端判定成功请以 code === 200 为准;失败时直接向玩家展示 msg。


三、接口清单

# 接口 路径 用途
1 获取抽奖信息 POST /api/turnoverLottery/myStats 进入抽奖页拉取流水、次数、宝箱与奖品
2 执行抽奖 POST /api/turnoverLottery/draw 点击抽奖,返回中奖结果供播放动画
3 我的抽奖记录 POST /api/turnoverLottery/myRecords 展示玩家历史中奖记录
4 全服中奖记录 POST /api/turnoverLottery/globalRecords 展示全服玩家中奖公示(跑马灯/滚动列表)

四、接口详情

1. 获取抽奖信息

POST /api/turnoverLottery/myStats

进入抽奖页时调用,一次性返回玩家流水统计 + 所有开启中的抽奖宝箱及其奖品。

请求参数

无需参数,body 可不传(或传空对象 {})。

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "totalTurnover": 12000.00,
    "usedTurnover": 3000.00,
    "availableTurnover": 9000.00,
    "lotteryList": [
      {
        "id": 1,
        "lotteryName": "每日流水抽奖",
        "status": 1,
        "turnoverThreshold": 3000,
        "sort": 0,
        "lotteryChances": 3,
        "totalValue": 8250.00,
        "prizes": [
          {
            "id": 6,
            "ornamentName": "M9 刺刀 | 渐变之色",
            "price": 8200.00,
            "imageUrl": "/profile/upload/2026/08/08/xxx.png",
            "ornamentLevel": 3,
            "exteriorName": "略有磨损",
            "levelImg": "/profile/upload/level/red.png",
            "displayCount": 10,
            "displayPercent": "50.00%",
            "realCount": 5,
            "realPercent": "45.45%",
            "sort": 0
          }
        ]
      }
    ]
  }
}

字段说明

顶层字段

字段 类型 说明
totalTurnover Decimal 今日累计有效流水总额
usedTurnover Decimal 今日已消耗的流水(抽奖已用掉的部分)
availableTurnover Decimal 当前可用流水 = totalTurnover - usedTurnover,不会为负
lotteryList Array 开启中的抽奖宝箱列表,按 sort 升序

lotteryList[] 宝箱字段

字段 类型 说明
id Long 宝箱 ID,抽奖时作为 lotteryId 传入
lotteryName String 宝箱名称
status Int 状态,始终为 1(接口只返回开启中的)
turnoverThreshold Int 该宝箱的流水门槛(多少流水抽 1 次)
sort Int 展示排序,越小越靠前
lotteryChances Int 该宝箱可抽次数 = availableTurnover ÷ turnoverThreshold 向下取整
totalValue Decimal 奖池总价值(按初始真实数量 × 单价累加)
prizes Array 奖品列表,按 sort 升序

prizes[] 奖品字段

字段 类型 说明
id Long 奖品 ID
ornamentName String 饰品名称
price Decimal 饰品价值
imageUrl String 饰品图片
ornamentLevel Int 品质等级 ID
exteriorName String 磨损度(崭新出厂/略有磨损/久经沙场/破损不堪/战痕累累/无涂装),可能为 null
levelImg String 品质底图,可用于饰品卡片背景
displayCount Int 展示数量
displayPercent String 展示爆率,如 "50.00%",用于前端展示给玩家
realCount Int 当前剩余库存
realPercent String 真实爆率(内部数据,不建议展示给玩家)
sort Int 展示排序

图片路径处理:imageUrl / levelImg 若以 / 开头(且非 //),是服务器相对路径,需拼接资源域名前缀;否则为完整 URL 直接使用。

展示建议:转盘/宝箱格子的爆率请使用 displayPercent,realPercent 是后台真实概率,不要展示。


2. 执行抽奖

POST /api/turnoverLottery/draw

玩家点击抽奖时调用。建议点击立即请求接口,拿到结果后再播放动画——饰品在接口返回成功时已经入背包,动画只是表现层,即使动画中途活动被关闭也不影响已发的奖。

请求参数

参数 类型 必填 说明
lotteryId Long 是 宝箱 ID,取 myStats 返回的 lotteryList[].id
{
  "lotteryId": 1
}

成功响应

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "prizeId": 6,
    "ornamentName": "M9 刺刀 | 渐变之色",
    "price": 8200.00,
    "imageUrl": "/profile/upload/2026/08/08/xxx.png",
    "ornamentLevel": 3,
    "exteriorName": "略有磨损",
    "lotteryName": "每日流水抽奖",
    "costTurnover": 3000,
    "msg": "抽奖成功!获得: M9 刺刀 | 渐变之色"
  }
}
字段 类型 说明
prizeId Long 中奖奖品 ID,可与 myStats 的 prizes[].id 对应,用于定位动画停留格子
ornamentName String 中奖饰品名称
price Decimal 饰品价值
imageUrl String 饰品图片
ornamentLevel Int 品质等级 ID
exteriorName String 磨损度,可能为 null
lotteryName String 宝箱名称
costTurnover Int 本次消耗的流水值
msg String 中奖文案,可直接展示

失败响应(code = 500,提示语在外层 msg)

msg 触发场景 客户端处理建议
抽奖活动不存在或未开启 活动被后台关闭或已删除 提示后刷新 myStats
抽奖次数不足 可用流水不够一次门槛 提示玩家去游戏累计流水
奖品配置为空 该宝箱未配置奖品 提示"活动准备中"
奖品库存不足 全部奖品初始数量为 0 提示"活动准备中"
抽奖流水门槛配置错误,请联系管理员 门槛配置为 0 或负数 提示联系客服
抽奖处理中,请勿重复操作 同一用户并发点击被锁拦截 禁用按钮防连点,稍后可重试
抽奖处理中,请稍后再试 同一宝箱有其他玩家正在抽 短暂延迟后自动重试一次
系统繁忙,请稍后重试 服务端异常(事务已回滚,未扣流水未发奖) 提示重试

重要:抽奖接口已加双层 Redis 分布式锁(用户级 + 宝箱级),客户端仍需在按钮上做防连点,避免频繁触发"抽奖处理中"。

抽奖成功后的处理

  1. 用 prizeId 定位动画停留位置并播放中奖动画
  2. 动画结束后重新调用 myStats 刷新可用流水与剩余次数
  3. 饰品已在背包,如需"立即分解"走通用背包分解接口 /api/userPackSack/decompose,分解产生的余额/积分变动来源为 63 流水抽奖奖励

3. 我的抽奖记录

POST /api/turnoverLottery/myRecords

分页查询当前登录玩家的历史中奖记录,按抽奖时间倒序。

请求参数

参数 类型 必填 默认 说明
lotteryId Long 否 全部 按宝箱筛选,不传查全部
pageNum Int 否 1 页码,从 1 开始
pageSize Int 否 10 每页条数,最大 100
{
  "pageNum": 1,
  "pageSize": 20
}

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "total": 35,
    "pageNum": 1,
    "pageSize": 20,
    "rows": [
      {
        "id": 128,
        "userId": 688660,
        "lotteryId": 1,
        "prizeId": 6,
        "ornamentId": null,
        "ornamentName": "M9 刺刀 | 渐变之色",
        "imageUrl": "/profile/upload/2026/08/08/xxx.png",
        "ornamentsPrice": 8200.00,
        "costTurnover": 3000,
        "createTime": "2026-08-08 17:18:37"
      }
    ]
  }
}
字段 类型 说明
total Long 总记录数,用于分页组件
pageNum Int 当前页码
pageSize Int 每页条数
rows Array 记录列表
rows[].id Long 记录 ID
rows[].lotteryId Long 宝箱 ID
rows[].prizeId Long 奖品 ID
rows[].ornamentName String 饰品名称
rows[].imageUrl String 饰品图片
rows[].ornamentsPrice Decimal 奖品价值
rows[].costTurnover Int 该次消耗的流水值
rows[].createTime String 抽奖时间 yyyy-MM-dd HH:mm:ss

失败时返回 code = 500,msg 为 查询失败,请稍后重试。


4. 全服中奖记录

POST /api/turnoverLottery/globalRecords

分页查询全服所有玩家的中奖记录,按中奖时间倒序。用于抽奖页的中奖公示区(跑马灯、滚动榜单)。

已在服务端过滤:机器人(user_type='03')、白号(is_blank_account=1)、封禁用户(status='1')、已删除用户(del_flag!='0')的记录不会出现在结果里,客户端无需再做过滤。

请求参数

参数 类型 必填 默认 说明
lotteryId Long 否 全部 按宝箱筛选,不传查全部宝箱
minPrice Decimal 否 不限 最低奖品价值门槛,只展示价值 ≥ 该值的记录(做"大奖公示"时用)
pageNum Int 否 1 页码,从 1 开始
pageSize Int 否 20 每页条数,最大 100
{
  "minPrice": 1000,
  "pageNum": 1,
  "pageSize": 20
}

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "total": 1286,
    "pageNum": 1,
    "pageSize": 20,
    "rows": [
      {
        "id": 128,
        "userId": 688660,
        "nickName": "云深不知",
        "avatar": "/profile/avatar/2026/08/08/aaa.png",
        "lotteryId": 1,
        "lotteryName": "每日流水抽奖",
        "prizeId": 6,
        "ornamentName": "M9 刺刀 | 渐变之色",
        "imageUrl": "/profile/upload/2026/08/08/xxx.png",
        "ornamentsPrice": 8200.00,
        "ornamentLevel": 3,
        "levelImg": "/profile/upload/level/red.png",
        "exteriorName": "略有磨损",
        "createTime": "2026-08-08 17:18:37"
      }
    ]
  }
}

字段说明

字段 类型 说明
total Long 总记录数
pageNum Int 当前页码
pageSize Int 每页条数
rows Array 记录列表,按中奖时间倒序
rows[].id Long 记录 ID
rows[].userId Int 中奖用户 ID
rows[].nickName String 中奖用户昵称,用于公示展示
rows[].avatar String 中奖用户头像
rows[].lotteryId Long 宝箱 ID
rows[].lotteryName String 宝箱名称
rows[].prizeId Long 奖品 ID
rows[].ornamentName String 饰品名称
rows[].imageUrl String 饰品图片
rows[].ornamentsPrice Decimal 奖品价值
rows[].ornamentLevel Int 品质等级 ID
rows[].levelImg String 品质底图,可作饰品卡片背景
rows[].exteriorName String 磨损度,可能为 null
rows[].createTime String 中奖时间 yyyy-MM-dd HH:mm:ss

失败时返回 code = 500,msg 为 查询失败,请稍后重试。

与 myRecords 的区别:myRecords 只返回当前登录玩家自己的记录(不含昵称/头像/品质图);globalRecords 返回全服记录并附带中奖者昵称、头像、品质底图,专供公示展示。

轮询建议:公示区如需实时滚动,建议 15~30 秒轮询一次 pageNum=1,不要高频请求。


五、典型调用流程

进入抽奖页
   │
   ├─→ POST /myStats
   │      拿到 availableTurnover、lotteryList
   │      渲染宝箱、奖品格子(爆率用 displayPercent)
   │      显示可抽次数 lotteryChances
   │
   ├─→ 玩家点击抽奖(按钮置灰防连点)
   │      POST /draw { lotteryId }
   │         ├─ code=200 → 用 prizeId 定位并播放动画
   │         │              动画结束后重新 POST /myStats 刷新
   │         └─ code=500 → 按钮恢复,展示 msg
   │
   ├─→ 查看我的中奖记录
   │      POST /myRecords { pageNum, pageSize }
   │
   └─→ 中奖公示区(跑马灯,15~30s 轮询)
          POST /globalRecords { pageNum: 1, pageSize: 20 }

六、对接注意事项

  1. 不要传 userId:所有接口的用户身份从 token 解析,传了也会被忽略。
  2. myStats 无需 body:之前该接口曾要求传 {},现已改为无参,body 传或不传都可以。
  3. 次数按宝箱各算:lotteryChances 在每个宝箱内单独计算(因为门槛可能不同),不是全局共享的次数。多个宝箱的次数是"同一份可用流水"的不同换算结果,抽任一宝箱都会消耗同一份流水。
  4. 抽奖不动余额:客户端不要在抽奖后刷新余额,抽奖不产生金币/积分变化;只有分解饰品才会变。
  5. 动画与发奖解耦:接口返回成功即已发奖入背包,动画播放期间活动被关闭不影响结果。
  6. 爆率字段区分:给玩家看 displayPercent,realPercent / realCount 属于内部数据。
  7. 奖池自动重置:当宝箱所有奖品剩余库存耗尽时,下一次抽奖会自动按后台配置的初始真实数量重置奖池,客户端无需处理。
  8. 全服记录已过滤脏数据:globalRecords 服务端已排除机器人、白号、封禁及已删除用户,客户端直接展示即可。
  9. 公示区别高频轮询:globalRecords 建议 15~30 秒轮询一次,pageSize 上限 100。

七、相关代码位置

内容 文件
用户端接口 skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TurnoverLotteryUserController.java
业务实现 skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TurnoverLotteryServiceImpl.java
全服记录查询 skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TurnoverLotteryRecordMapper.java
全服记录 VO skins-model/src/main/java/com/ruoyi/domain/vo/lottery/TurnoverLotteryWinVO.java
奖品实体 skins-model/src/main/java/com/ruoyi/domain/entity/lottery/TtTurnoverLotteryPrize.java
记录实体 skins-model/src/main/java/com/ruoyi/domain/entity/lottery/TtTurnoverLotteryRecord.java
建表 SQL docs/updata_sql.md(搜索"流水抽奖")