yumi-golang/docs/Yumi游戏王活动接口.md
2026-07-18 15:32:58 +08:00

13 KiB
Raw Blame History

Yumi 游戏王活动接口

本文对应 chatapp3-golang 的 Yumi Game King 实现。业务只接受 LIKEI 租户的权威游戏金币消耗事件,不提供事件白名单输入框,也不会统计通用钱包支出。

1. 通用约定

  • H5 外部网关地址:/go/app/h5/game-king/yumi/*Go 服务内路由不带 /go
  • 后台外部网关地址:/go/resident-activity/game-king/*Go 服务内路由不带 /go
  • H5 和后台均传 Authorization。H5 支持 Bearer <token>;后台沿用 Console 完整 Authorization。
  • 内部任务传 X-Internal-Token: <HTTP_INTERNAL_CALLBACK_SECRET>
  • 时间字段均为毫秒时间戳;timeZone 使用 IANA 时区,默认 Asia/Riyadh
  • 所有业务 ID 在 JSON 响应中均为字符串,避免 JavaScript 丢失 Snowflake 精度。请求中的 ID 可传字符串或数字,建议统一传字符串。
  • 成功响应的数据同时位于 databody
{
  "status": true,
  "errorCode": 0,
  "errorMsg": "success",
  "code": "success",
  "message": "success",
  "data": {},
  "body": {},
  "time": 1784300000000
}
  • 错误时 HTTP 状态码与 errorCode 一致,稳定业务码位于 code / errorCodeName

2. H5 接口

2.1 活动详情

GET /go/app/h5/game-king/yumi/detail?activityId={activityId}

activityId 可不传;不传时按登录用户 sysOrigin=LIKEI 解析当前可见活动。返回活动、固定七个奖项、六个排名奖励档、服务器时间和活动状态。

关键返回字段:

{
  "activity": {
    "id": "190000000000000001",
    "activityCode": "YUMI_GAME_KING_S1",
    "sysOrigin": "LIKEI",
    "startTime": 1784300000000,
    "endTime": 1784900000000,
    "coinPerDraw": 1000,
    "eventTypes": ["GAME_CONSUME_GOLD"],
    "rankingType": "TYCOON",
    "rankingPeriod": "OVERALL",
    "settlementStatus": "NOT_STARTED"
  },
  "prizes": [],
  "rankRewards": [],
  "supportedRankingTypes": ["TYCOON"],
  "supportedRankingPeriods": ["DAILY", "OVERALL"],
  "activityStatus": "UPCOMING",
  "serverTime": 1784299900000
}

activityStatusUPCOMINGACTIVEENDEDDISABLED

2.2 我的进度

GET /go/app/h5/game-king/yumi/me?activityId={activityId}

返回:

  • totalConsumed:活动时间窗内累计游戏消耗金币。
  • earnedChancesfloor(totalConsumed / coinPerDraw)
  • usedChances / availableChances:已用和可用抽奖次数。
  • nextChanceRemaining:距离下一次机会仍需消耗的金币。
  • rank:总榜名次;无消费时为 null

2.3 抽奖

POST /go/app/h5/game-king/yumi/draw

{
  "activityId": "190000000000000001",
  "requestId": "9af73ee9-65ca-4cf5-b23c-f4c222c3ee6e"
}
  • activityIdrequestId 均必填;requestId 最长 64 字符。客户端一次点击生成一个稳定值;超时重试必须复用原值。
  • 幂等范围是 activityId + userId + requestId。已经提交的结果即使活动随后结束,也会原样返回,且不会再次扣次数或发奖。
  • 权重仅表示相对概率;库存 -1 为无限,0 为耗尽,正数为剩余库存。有限库存使用条件更新防止超发。
  • 返回 recordprizeremainingChancesidempotentReplay

2.4 排行榜

GET /go/app/h5/game-king/yumi/ranking?activityId={activityId}&rankingType=TYCOON&period=OVERALL&limit=30

  • rankingType:正式口径仅 TYCOON。旧 H5 请求 VICTORIOUS 时返回空榜,避免兼容页面报错。
  • periodDAILYOVERALL;配置页固定保存 OVERALLH5 仍可切日榜。
  • limit 默认 30最大 100。
  • 排序固定为:消费金币降序、达到当前消费值的事件时间升序、用户 ID 升序。
  • DAILY 按活动时区自然日统计;活动结束结算只使用 OVERALL Top30。
  • 用户资料通过 Java 批量接口补齐;资料服务异常时榜单仍返回 userId 和分数。

2.5 我的抽奖记录

GET /go/app/h5/game-king/yumi/draw-records?activityId={activityId}&limit=20

limit 最大 100。返回奖品快照及 PENDING / PROCESSING / SUCCESS / FAILED / UNKNOWN 发放状态。

3. 后台管理接口

所有接口要求菜单别名 ResidentGameKingActivity。写接口还要求对应按钮权限:

权限别名 接口用途
resident-activity:yumi-game-king:edit 保存活动、奖项、排名奖励
resident-activity:yumi-game-king:enable 启停活动
resident-activity:yumi-game-king:settle 人工触发到期结算
resident-activity:yumi-game-king:reconcile FAILED 重试、UNKNOWN 核账
resident-activity:yumi-game-king:backfill 权威事件补数

3.1 活动配置

方法 地址 说明
GET /go/resident-activity/game-king/list 活动列表
GET /go/resident-activity/game-king/detail/{id} 完整详情
POST /go/resident-activity/game-king/save 新增或修改
PUT /go/resident-activity/game-king/enable/{id}?enabled=true 启停;布尔值只接受 true/false/1/0
GET /go/resident-activity/game-king/prizes/{id} 七个奖项
PUT /go/resident-activity/game-king/prizes/{id} 保存七个奖项
GET /go/resident-activity/game-king/rank-rewards/{id} 六个排名奖励档
PUT /go/resident-activity/game-king/rank-rewards/{id} 保存六个排名奖励档

POST /save 示例:

{
  "id": "0",
  "activityCode": "YUMI_GAME_KING_S1",
  "activityName": "Yumi Game King",
  "activityDesc": "",
  "sysOrigin": "LIKEI",
  "timeZone": "Asia/Riyadh",
  "startTime": 1784300000000,
  "endTime": 1784900000000,
  "settlementDelayMinutes": 30,
  "coinPerDraw": 1000,
  "rankingType": "TYCOON",
  "rankingPeriod": "OVERALL",
  "enabled": false,
  "prizes": [
    {
      "id": "0",
      "prizeName": "Prize 1",
      "prizeImage": "https://example.com/prize-1.png",
      "weight": 100,
      "stock": -1,
      "resourceGroupId": "190000000000000101",
      "enabled": true,
      "sortOrder": 1
    }
  ],
  "rankRewards": [
    {
      "id": "0",
      "startRank": 1,
      "endRank": 1,
      "resourceGroupId": "190000000000000201",
      "rewardName": "Top 1"
    }
  ]
}

示例数组为节省篇幅只展示一项,真实启用配置必须满足:

  • 恰好 7 个奖项,全部启用,sortOrder 完整覆盖 1..7 且不重复;权重必须大于 0。
  • 恰好 6 个排名档:1234-78-1011-30
  • sysOrigin 固定 LIKEIrankingType 固定 TYCOON;配置 rankingPeriod 固定 OVERALL
  • 首次启用时 startTime 必须严格晚于当前时间;同租户启用活动时间窗不能重叠。
  • 计数窗口严格为 [startTime, endTime)。活动开始后锁定时间、口径、奖项、排名奖励和启停状态,只允许修改名称、说明等展示信息。
  • settlementDelayMinutes 默认 30可配置 0..1440。
  • 每次保存/启用及每次真实发奖前都会重新校验奖励资源组:必须存在、已上架、明确属于 LIKEI 且非空,并比对冻结的完整奖励项快照。

单独保存奖项请求:

{
  "activityId": "190000000000000001",
  "prizes": []
}

单独保存排名奖励请求:

{
  "activityId": "190000000000000001",
  "rankRewards": []
}

路径 ID 与 body activityId 必须一致。

3.2 抽奖发放管理

方法 地址 说明
GET /go/resident-activity/game-king/draw-records/{id}?deliveryStatus=&limit=100 查看抽奖和发放明细
POST /go/resident-activity/game-king/draw/retry/{recordId} 只重试 FAILED
POST /go/resident-activity/game-king/delivery-item/resolve/{itemId}?delivered=true 人工核销 UNKNOWN

delivered 只接受 true/false/1/0

  • true:已通过 trackId/businessNo 确认下游到账,只记为成功,不再发送。
  • false:已确认下游未到账,先转为 FAILED,再执行一次显式重试。
  • 非法或缺失值返回 400绝不默认为 false

3.3 排名结算

方法 地址 说明
GET /go/resident-activity/game-king/settlement-records/{id} 查看冻结名次与发放状态
POST /go/resident-activity/game-king/settlement/{id} 到结算时间后人工冻结并派发一小批
POST /go/resident-activity/game-king/settlement/retry/{recordId} 只重试 FAILED

结算只冻结一次 OVERALL Top30。记录和发奖任务在同一事务生成人工接口每次最多派发该活动 4 个 PENDING,其余由内部定时任务继续。单项硬超时 20 秒,因此最坏外部调用约 80 秒,并为数据库处理留出余量。

3.4 事件补数

POST /go/resident-activity/game-king/backfill/{id}?lastId=0&limit=200

  • 只读取 task_center_event_archive_outboxLIKEI + GAME_CONSUME_GOLD + 活动时间窗 数据。
  • 在任何归档扫描前都会检查迁移 058 的四列在线索引;索引未完成、不可见或列序不符时返回 503 backfill_index_required,不会退化为全表扫描。
  • 仅允许对已启用且结算尚未冻结的活动补数;活动结束后的结算等待期仍可补,进入结算后返回 409游标不会静默推进。
  • 调用方只能传不透明游标 lastIdlimit,不能传 eventType 或钱包口径;limit 最大 500。
  • 返回 eventTypescannedacceptednextLastIdfinished。未完成时用返回的 nextLastId 继续。
  • 若结算恰好在一页补数中途冻结,接口返回 409 且不返回新的游标;调用方保留原 lastId,已成功写入的前序事件会由活动账本幂等跳过。
  • 活动账本以 activityId + eventId 幂等,实时事件和补数重复不会重复累计。

4. 游戏消耗事件接入

唯一统计事件是任务中心的 GAME_CONSUME_GOLD。事件经过任务中心统一校验后、在任务中心自身业务去重之前同步扇出给 Game KingGame King 使用独立账本保证幂等。

已接入事件 ID 口径:

  • Java 游戏:GAME_CONSUME:<provider>:<orderId>,已覆盖 YOMILINGXIANHOTGAMEBAISHUN_LEGACYLUDOTURNTABLE
  • Go GameOpenGAME_OPEN_CONSUME:<orderId>
  • Go HotGameGAME_HOTGAME_CONSUME:<orderId>
  • Go BaishunBAISHUN_GAME_CONSUME:<sysOrigin>:<orderId>

RocketMQ 默认配置:

  • topicRC_DEFAULT_APP_ORDINARY
  • tagtask_center_event
  • consumer grouptask-center-progress
  • 开关:CHATAPP_TASK_CENTER_MQ_ENABLED(兼容 TASK_CENTER_EVENT_MQ_ENABLED

活动累计使用事件的 occurredAt 判断 [startTime,endTime),因此活动开始前的历史游戏消耗不会计入。只接受正数 deltaValue;退款、胜利收入和非游戏钱包消耗不计入。

5. 发奖和异常状态

发奖以“奖励资源组”为一个下游调用单元,使用 Java SendActivityRewardorigin=GAME_KING_AWARDtrackId=deliveryItem.id。后台明细同时返回稳定 trackIdbusinessNo、用户 ID、资源组 ID 和冻结奖励项。

  • PENDING:本地事务已提交,尚未领取。
  • PROCESSING:已领取,正在校验/调用下游。
  • SUCCESSJava 明确成功。
  • FAILED:在发奖请求发出前就已明确失败,例如资源组不存在、下架、跨租户或与冻结快照不一致;只允许运营显式重试。
  • UNKNOWN:发奖请求已尝试但结果不能证明,或 PROCESSING 超过 10 分钟;禁止自动重试,必须先核账再 resolve。

一旦活动启用,奖励组内的实际奖励项内容必须保持与冻结快照一致。若运营修改了组内容,发放会在调用 Java 前进入 FAILED;应先恢复原快照或完成明确处置,再重试。

6. 内部结算任务

POST /internal/game-king/settle-due

HeaderX-Internal-Token

本 Go 服务不启动 ticker。由 chatapp-cron 周期调用:

  • 每次最多修复 20 个过期 PROCESSING,并扫描、冻结 10 个到期活动。
  • 每次最多派发全局 4 个 PENDING;单项硬超时 20 秒,调用方超时应配置为至少 120 秒。
  • 只自动恢复 PENDINGFAILED / UNKNOWN 永不自动重试;超过 10 分钟的 PROCESSING 只转为 UNKNOWN

响应:

{
  "scanned": 2,
  "settled": 2,
  "failedIds": []
}

7. 数据库迁移与性能

迁移文件:migrations/058_yumi_game_king.sql

  • 新建活动、七奖项、六档奖励、幂等消费账本、总榜/日榜聚合、抽奖、结算和发放表。
  • 排行读取物化聚合并使用复合排序索引,不对消费流水做运行时 GROUP BY
  • 补数给 task_center_event_archive_outbox 增加 (sys_origin,event_type,occurred_at,id) 复合索引,以范围 + keyset 扫描,避免全表排序。
  • 迁移不执行历史 UPDATE/DELETE/backfill。新增复合索引仍属于线上 DDL执行前应按目标 MySQL 版本、表数据量和 DDL 策略确认锁影响,并在低峰发布。