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

304 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 可传字符串或数字,建议统一传字符串。
- 成功响应的数据同时位于 `data``body`
```json
{
"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` 解析当前可见活动。返回活动、固定七个奖项、六个排名奖励档、服务器时间和活动状态。
关键返回字段:
```json
{
"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
}
```
`activityStatus``UPCOMING``ACTIVE``ENDED``DISABLED`
### 2.2 我的进度
`GET /go/app/h5/game-king/yumi/me?activityId={activityId}`
返回:
- `totalConsumed`:活动时间窗内累计游戏消耗金币。
- `earnedChances``floor(totalConsumed / coinPerDraw)`
- `usedChances` / `availableChances`:已用和可用抽奖次数。
- `nextChanceRemaining`:距离下一次机会仍需消耗的金币。
- `rank`:总榜名次;无消费时为 `null`
### 2.3 抽奖
`POST /go/app/h5/game-king/yumi/draw`
```json
{
"activityId": "190000000000000001",
"requestId": "9af73ee9-65ca-4cf5-b23c-f4c222c3ee6e"
}
```
- `activityId``requestId` 均必填;`requestId` 最长 64 字符。客户端一次点击生成一个稳定值;超时重试必须复用原值。
- 幂等范围是 `activityId + userId + requestId`。已经提交的结果即使活动随后结束,也会原样返回,且不会再次扣次数或发奖。
- 权重仅表示相对概率;库存 `-1` 为无限,`0` 为耗尽,正数为剩余库存。有限库存使用条件更新防止超发。
- 返回 `record``prize``remainingChances``idempotentReplay`
### 2.4 排行榜
`GET /go/app/h5/game-king/yumi/ranking?activityId={activityId}&rankingType=TYCOON&period=OVERALL&limit=30`
- `rankingType`:正式口径仅 `TYCOON`。旧 H5 请求 `VICTORIOUS` 时返回空榜,避免兼容页面报错。
- `period``DAILY``OVERALL`;配置页固定保存 `OVERALL`H5 仍可切日榜。
- `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` 示例:
```json
{
"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 个排名档:`1``2``3``4-7``8-10``11-30`
- `sysOrigin` 固定 `LIKEI``rankingType` 固定 `TYCOON`;配置 `rankingPeriod` 固定 `OVERALL`
- 首次启用时 `startTime` 必须严格晚于当前时间;同租户启用活动时间窗不能重叠。
- 计数窗口严格为 `[startTime, endTime)`。活动开始后锁定时间、口径、奖项、排名奖励和启停状态,只允许修改名称、说明等展示信息。
- `settlementDelayMinutes` 默认 30可配置 0..1440。
- 每次保存/启用及每次真实发奖前都会重新校验奖励资源组:必须存在、已上架、明确属于 `LIKEI` 且非空,并比对冻结的完整奖励项快照。
单独保存奖项请求:
```json
{
"activityId": "190000000000000001",
"prizes": []
}
```
单独保存排名奖励请求:
```json
{
"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_outbox``LIKEI + GAME_CONSUME_GOLD + 活动时间窗` 数据。
- 在任何归档扫描前都会检查迁移 058 的四列在线索引;索引未完成、不可见或列序不符时返回 `503 backfill_index_required`,不会退化为全表扫描。
- 仅允许对已启用且结算尚未冻结的活动补数;活动结束后的结算等待期仍可补,进入结算后返回 409游标不会静默推进。
- 调用方只能传不透明游标 `lastId``limit`,不能传 eventType 或钱包口径;`limit` 最大 500。
- 返回 `eventType``scanned``accepted``nextLastId``finished`。未完成时用返回的 `nextLastId` 继续。
- 若结算恰好在一页补数中途冻结,接口返回 409 且不返回新的游标;调用方保留原 `lastId`,已成功写入的前序事件会由活动账本幂等跳过。
- 活动账本以 `activityId + eventId` 幂等,实时事件和补数重复不会重复累计。
## 4. 游戏消耗事件接入
唯一统计事件是任务中心的 `GAME_CONSUME_GOLD`。事件经过任务中心统一校验后、在任务中心自身业务去重之前同步扇出给 Game KingGame King 使用独立账本保证幂等。
已接入事件 ID 口径:
- Java 游戏:`GAME_CONSUME:<provider>:<orderId>`,已覆盖 `YOMI``LINGXIAN``HOTGAME``BAISHUN_LEGACY``LUDO``TURNTABLE`
- Go GameOpen`GAME_OPEN_CONSUME:<orderId>`
- Go HotGame`GAME_HOTGAME_CONSUME:<orderId>`
- Go Baishun`BAISHUN_GAME_CONSUME:<sysOrigin>:<orderId>`
RocketMQ 默认配置:
- topic`RC_DEFAULT_APP_ORDINARY`
- tag`task_center_event`
- consumer group`task-center-progress`
- 开关:`CHATAPP_TASK_CENTER_MQ_ENABLED`(兼容 `TASK_CENTER_EVENT_MQ_ENABLED`
活动累计使用事件的 `occurredAt` 判断 `[startTime,endTime)`,因此活动开始前的历史游戏消耗不会计入。只接受正数 `deltaValue`;退款、胜利收入和非游戏钱包消耗不计入。
## 5. 发奖和异常状态
发奖以“奖励资源组”为一个下游调用单元,使用 Java `SendActivityReward``origin=GAME_KING_AWARD``trackId=deliveryItem.id`。后台明细同时返回稳定 `trackId``businessNo`、用户 ID、资源组 ID 和冻结奖励项。
- `PENDING`:本地事务已提交,尚未领取。
- `PROCESSING`:已领取,正在校验/调用下游。
- `SUCCESS`Java 明确成功。
- `FAILED`:在发奖请求发出前就已明确失败,例如资源组不存在、下架、跨租户或与冻结快照不一致;只允许运营显式重试。
- `UNKNOWN`:发奖请求已尝试但结果不能证明,或 `PROCESSING` 超过 10 分钟;禁止自动重试,必须先核账再 resolve。
一旦活动启用,奖励组内的实际奖励项内容必须保持与冻结快照一致。若运营修改了组内容,发放会在调用 Java 前进入 `FAILED`;应先恢复原快照或完成明确处置,再重试。
## 6. 内部结算任务
`POST /internal/game-king/settle-due`
Header`X-Internal-Token`
本 Go 服务不启动 ticker。由 `chatapp-cron` 周期调用:
- 每次最多修复 20 个过期 `PROCESSING`,并扫描、冻结 10 个到期活动。
- 每次最多派发全局 4 个 `PENDING`;单项硬超时 20 秒,调用方超时应配置为至少 120 秒。
- 只自动恢复 `PENDING``FAILED` / `UNKNOWN` 永不自动重试;超过 10 分钟的 `PROCESSING` 只转为 `UNKNOWN`
响应:
```json
{
"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 策略确认锁影响,并在低峰发布。