跳到主要内容

集成需求文档 — GM工具端

版本: 1.0 | 日期: 2026-04-28


1. 目标

每次向玩家账号发放物品后,GM工具必须通过API发送一条记录到中心系统。一个请求可以在一次交易中包含多个物品 (batch items)。


2. 准备信息

在集成之前,请联系管理员获取:

信息描述示例
API_KEYGM工具专属API密钥gmtool_abc123xyz
GAME_ID游戏标识码game_rpg_01
BASE_URL中心系统地址https://central.vgp.com

3. 强制流程

第1步:GM在游戏中执行物品发放
第2步:接收结果(成功/失败)
第3步:调用 POST /api/gm/grant-log(异步,不阻塞UI)
第4步:如果API失败 → 最多重试3次
第5步:如果仍然失败 → 记录本地日志以供后续处理

注意: 无论第2步的结果是成功还是失败,都必须调用第3步的API。


4. API 调用

接口地址 (Endpoint)

POST {BASE_URL}/api/gm/grant-log

请求头 (Headers)

Content-Type:  application/json
Authorization: Bearer {API_KEY}
X-Game-ID: {GAME_ID}

请求体 (Request Body)

{
"gm_account": "gm_vana",
"gm_name": "Nguyen Van A",
"game_id": "game_rpg_01",
"vgpid": 8821043,
"granted_at": "2026-04-28T10:05:32+07:00",
"status": "success",
"reason": "服务器错误补偿",
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"items": [
{
"item_id": "item_diamond_001",
"item_name": "钻石",
"quantity": 500
},
{ "item_id": "item_gold_001", "item_name": "金币", "quantity": 1000 }
]
}

字段说明 — 交易级别

字段类型必填描述
gm_accountstringGM登录工具的用户名
gm_namestringGM显示名称
game_idstring游戏代码(从管理员处获取)
vgpidlong接收物品的玩家VGPID
granted_atstring (ISO 8601)执行时间,包含时区 +07:00
statusstring"success""failed"
reasonstring发放原因
idempotency_keyUUID v4每次执行生成一个新的UUID
itemsarray物品列表(≥ 1 个元素)

字段说明 — 每个 items[] 元素

字段类型必填描述
item_idstring游戏系统中的物品代码
item_namestring物品名称(用于显示)
quantityinteger发放数量 (> 0)

idempotency_key: 每次执行生成一个新的UUID v4。用于安全重试 — 如果使用相同key再次调用,服务器将返回409并忽略,防止重复记录。 items: 最少 1 个元素。每个请求最多 50 个元素。


5. 响应处理

HTTP 状态码含义操作
200 OK记录成功正常继续
409 Conflictidempotency_key 已存在忽略 — 安全,无重复记录
400 Bad RequestPayload错误 / 缺少字段检查数据,记录错误日志
401 UnauthorizedAPI密钥无效或已过期立即通知管理员
5xx / Timeout服务器 / 网络错误按第6节进行重试

6. 错误重试

第1次:30秒后重试
第2次:60秒后重试
第3次:120秒后重试
→ 如果仍然失败:写入本地日志(见第7节)
  • 每次调用超时时间:5秒
  • 异步运行(后台),不要阻塞GM屏幕

7. API失败时的本地日志

如果在3次重试后仍然失败,请将完整的payload记录到本地文件或数据库中,以便后续对账:

{
"timestamp": "2026-04-28T10:05:40+07:00",
"error": "connection timeout",
"payload": { ... }
}

8. 示例代码 (Python)

import uuid, asyncio, httpx
from datetime import datetime, timezone, timedelta

BASE_URL = "https://central.vgp.com"
API_KEY = "gmtool_abc123xyz"
GAME_ID = "game_rpg_01"
VN_TZ = timezone(timedelta(hours=7))

async def report_grant(gm_account, gm_name, vgpid,
items: list,
status, reason=None):
"""
items: [{ "item_id": str, "item_name": str, "quantity": int }, ...]
"""
payload = {
"gm_account": gm_account,
"gm_name": gm_name,
"game_id": GAME_ID,
"vgpid": vgpid,
"granted_at": datetime.now(VN_TZ).isoformat(),
"status": status,
"reason": reason,
"idempotency_key": str(uuid.uuid4()),
"items": items,
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"X-Game-ID": GAME_ID,
"Content-Type": "application/json",
}
delays = [30, 60, 120]
for delay in [0] + delays:
if delay:
await asyncio.sleep(delay)
try:
async with httpx.AsyncClient(timeout=5.0) as client:
r = await client.post(
f"{BASE_URL}/api/gm/grant-log",
json=payload, headers=headers
)
if r.status_code in (200, 409):
return True
if r.status_code == 401:
# 通知管理员,不再重试
_write_local_log("auth_error", payload)
return False
except Exception as e:
pass # 继续重试
_write_local_log("max_retry_exceeded", payload)
return False

def _write_local_log(error, payload):
# 写入本地文件或数据库
pass

9. 上线前检查清单

  • 已从管理员处获取 API_KEYGAME_IDBASE_URL
  • 已在测试环境 (staging) 完成测试
  • 确认 granted_at 格式为带有 +07:00 的 ISO 8601 标准
  • 确认 vgpidlong (长整型),而非字符串
  • 确认 idempotency_key 每次执行都会生成新的 UUID v4
  • 确认 API 调用是异步的,不阻塞UI
  • 已经实现 API 失败时的本地日志记录机制