MatchMaster/server/laf-cloud/functions/goldMiner/postman/gold-miner.postman_collection.json

3580 lines
173 KiB
JSON
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.

{
"info": {
"name": "黄金矿工 API 场景测试",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": "按当前后端代码更新:共 48 个请求(47 POST、1 GET),包含黄金矿工玩家接口、通关、原生/客服支付、奖励授权与保存确认、到期补发、普通/临时期管理及 activityConfig/list。08 公共日程无需账号;01 可单独 Runner;其他组按场景逐条执行,不要运行整个 Collection。详见 gold-miner.README.md。"
},
"auth": {
"type": "noauth"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need(['baseUrl']);",
"if (!/^https?:\\/\\//.test(pm.environment.get('baseUrl')) || /[<>]/.test(pm.environment.get('baseUrl'))) stop('请填写实际部署的 baseUrl');",
"const context=JSON.stringify([pm.environment.get('baseUrl'),pm.environment.get('uid')]);",
"const previous=pm.environment.get('gmContext');",
"if (previous && previous !== context) {",
" pm.environment.unset('gmInfo');",
" pm.environment.unset('gmActivitySchedule');",
" pm.environment.unset('expectedListedPeriodId');",
" pm.environment.unset('periodId');",
" pm.environment.unset('productId');",
" pm.environment.unset('priceFen');",
" pm.environment.unset('firstTaskId');",
" pm.environment.unset('secondTaskId');",
" pm.environment.unset('currentMainLevel');",
" pm.environment.unset('gmWinBefore');",
" pm.environment.unset('gmWinBody');",
" pm.environment.unset('gmWinAfter');",
" pm.environment.unset('legacySessionFrom');",
" pm.environment.unset('allowCustomerMessage');",
" pm.environment.unset('outTradeNo');",
" pm.environment.unset('orderPeriodId');",
" pm.environment.unset('gmGrant');",
" pm.environment.unset('grantId');",
" pm.environment.unset('coinBeforeDelivery');",
" pm.environment.unset('gmSavedGrantId');",
" pm.environment.unset('settlementCursor');",
" pm.environment.unset('expectedStatus');",
" pm.environment.unset('expectedProgressWins');",
" pm.environment.unset('claimPeriodId');",
" pm.environment.unset('claimTaskId');",
" pm.environment.unset('coinAfterDelivery');",
" pm.environment.unset('settlementId');",
" pm.environment.unset('expectedTargetPeriodId');",
" pm.environment.unset('mainLevelAfterWin');",
" pm.environment.unset('endlessSequence');",
" pm.environment.unset('expiredPeriodId');",
" pm.environment.unset('openid');",
" pm.environment.set('gmContext',context);",
" stop('已切换应用/账号,旧测试上下文已清理。请从第 01 组重新查询');",
"}",
"pm.environment.set('gmContext',context);"
]
}
}
],
"item": [
{
"name": "01 基础查询与鉴权(可单独 Runner)",
"description": "本组不提交通关或发货。info 可能初始化玩家期记录并激活已有权益;补发查询本身不执行结算。支持普通周四周期和 goldMiner:test: 临时期,expectedStatus 留空时只校验结构。",
"item": [
{
"name": "01 活动信息并保存当期变量",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"info\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:action=info、uid、token。返回 serverTime/status/periodId/tasks;有效且已达资格时另含 startsAt/endsAt/configVersion/priceFen/productId/progressWins/maxTarget。status 为 unavailable/locked/purchasable/purchase_disabled/unlocked。任务字段:taskId/targetWins/itemsSnapshot/claimStatus/completed/progress;claimStatus 为 locked/claimable/issuing/claimed。测试期时间可为任意窗口,不能按固定四天推算。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"",
"pm.test('活动状态结构', () => {",
" const d = r.data;",
" pm.expect(['unavailable','locked','purchasable','purchase_disabled','unlocked'].includes(d.status)).to.eql(true);",
" pm.expect(Number.isSafeInteger(d.serverTime)).to.eql(true);",
" for (const key of ['availability','qualified','entitlement','purchasable','currency','nextStartsAt','nextPeriodId','claimedThrough']) pm.expect(Object.prototype.hasOwnProperty.call(d,key)).to.eql(false);",
" if (pm.environment.get('expectedStatus')) pm.expect(d.status).to.eql(pm.environment.get('expectedStatus'));",
" if (['unavailable','locked'].includes(d.status)) { pm.expect(d.tasks).to.eql([]); return; }",
" pm.expect(Number.isSafeInteger(d.startsAt) && Number.isSafeInteger(d.endsAt) && d.startsAt < d.endsAt).to.eql(true);",
" pm.expect(d.serverTime >= d.startsAt && d.serverTime < d.endsAt).to.eql(true);",
" if (d.periodId.startsWith('goldMiner:test:')) {",
" pm.expect(/^goldMiner:test:[A-Za-z0-9_-]{1,60}$/.test(d.periodId)).to.eql(true);",
" } else {",
" pm.expect(/^goldMiner:\\d{4}-\\d{2}-\\d{2}$/.test(d.periodId)).to.eql(true);",
" pm.expect(d.endsAt - d.startsAt).to.eql(4 * 86400000);",
" pm.expect(new Date(d.startsAt + 8 * 3600000).getUTCDay()).to.eql(4);",
" pm.expect(new Date(d.startsAt + 8 * 3600000).getUTCHours()).to.eql(0);",
" }",
" pm.expect(Number.isSafeInteger(d.progressWins) && d.progressWins >= 0).to.eql(true);",
" pm.expect(d.tasks.length > 0).to.eql(true);",
" pm.expect(d.maxTarget).to.eql(d.tasks[d.tasks.length - 1].targetWins);",
" let lastTarget = 0;",
" d.tasks.forEach((t, i) => {",
" pm.expect(Object.keys(t).sort()).to.eql(['taskId','targetWins','itemsSnapshot','claimStatus','completed','progress'].sort());",
" pm.expect(t.targetWins > lastTarget).to.eql(true); lastTarget = t.targetWins;",
" pm.expect(t.progress).to.eql(Math.min(d.progressWins, t.targetWins));",
" pm.expect(t.completed).to.eql(d.progressWins >= t.targetWins);",
" if (d.status !== 'unlocked') pm.expect(t.claimStatus).to.eql(t.completed ? 'pending_unlock' : 'locked');",
" else if (!t.completed) pm.expect(t.claimStatus).to.eql('locked');",
" else pm.expect(['claimable','issuing','claimed'].includes(t.claimStatus)).to.eql(true);",
" });",
" if (String(pm.environment.get('expectedProgressWins') ?? '') !== '') pm.expect(d.progressWins).to.eql(Number(pm.environment.get('expectedProgressWins')));",
"});",
"if (r.code === 1) {",
" pm.environment.set('gmInfo', JSON.stringify(r.data));",
" if (['purchasable','purchase_disabled','unlocked'].includes(r.data.status)) {",
" for (const key of ['periodId','productId','priceFen']) pm.environment.set(key, r.data[key]);",
" pm.environment.set('firstTaskId', r.data.tasks[0].taskId);",
" pm.environment.set('secondTaskId', r.data.tasks[1]?.taskId || '');",
" } else {",
" for (const key of ['periodId','productId','priceFen','firstTaskId','secondTaskId']) pm.environment.unset(key);",
" if (r.data.status === 'locked') pm.environment.set('periodId',r.data.periodId);",
" }",
"}"
]
}
}
]
},
{
"name": "02 读取主线存档",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"read\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/userLevel",
"host": [
"{{baseUrl}}"
],
"path": [
"userLevel"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"if (r.code === 1) pm.environment.set('currentMainLevel', r.data.levelAmount);"
]
}
}
]
},
{
"name": "03 缺少 token",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"info\",\n \"uid\": \"{{uid}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:UNAUTHORIZED', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('UNAUTHORIZED'); });"
]
}
}
]
},
{
"name": "04 不支持的 IAA 账号域",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"info\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"gameName\": \"iaa\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:UNAUTHORIZED', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('UNAUTHORIZED'); });"
]
}
}
]
},
{
"name": "05 非法 action",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"invalid_action\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:INVALID_ACTION', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('INVALID_ACTION'); });"
]
}
}
]
},
{
"name": "06 非法分页游标",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"settlements\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"afterId\": 42\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token,afterId 可选分页游标;前端不传 limit,后端固定每页扫描 20 期。返回 status=pending_delivery/settling/no_pending_rewards、items、nextCursor;每项含 settlementId/periodId/rewards,奖励含 taskId/grantId/items。可发奖励优先返回,不受其他期结算阻塞;no_pending_rewards 表示该玩家当前环境下所有已结束期均无待发或待结算记录。\n\n"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:INVALID_INPUT', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('INVALID_INPUT'); });"
]
}
}
]
},
{
"name": "07 非法期 ID",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"claim\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"invalid-period\",\n \"taskId\": \"task_1\",\n \"requestId\": \"invalid-period-test\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/requestId。返回 grantId/taskId/items/claimStatus/claimedThrough/requiresClientDelivery。仅有效期内可申请;授权不加币。到期返回 PERIOD_SETTLEMENT_REQUIRED,转补发查询。\n\n"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:INVALID_PERIOD', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('INVALID_PERIOD'); });"
]
}
}
]
}
]
},
{
"name": "02 通关进度(按场景手动)",
"description": "先读活动和主线存档。每次新胜利前重新读取活动信息。新胜利请求生成 eventId,重试请求复用完整原始载荷;不要以新胜利请求代替重试。",
"item": [
{
"name": "01 主线新胜利",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{gmWinBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/userLevel",
"host": [
"{{baseUrl}}"
],
"path": [
"userLevel"
]
},
"description": "mainLevelAfterWin=currentMainLevel+1;默认预期 counted。资格关需设 qualification、刚读取存档为 40,且活动已配置开放。封顶场景设 capped。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([]);",
"",
"need(['uid','token','periodId','currentMainLevel','gmInfo']);",
"const info = JSON.parse(pm.environment.get('gmInfo'));",
"if (info.periodId !== pm.environment.get('periodId')) stop('请重新查询本期活动信息');",
"const expected = pm.environment.get('expectedProgressResult');",
"if (!['counted','qualification','capped'].includes(expected)) stop('expectedProgressResult 仅支持 counted/qualification/capped');",
"const level = integer('mainLevelAfterWin', 1);",
"const oldLevel = integer('currentMainLevel', 0);",
"const mode = 'main';",
"if (mode === 'main' && level !== oldLevel + 1) stop('主线只测试顺序过关:mainLevelAfterWin 必须为刚读取的 currentMainLevel + 1');",
"if (mode === 'endless' && level !== oldLevel) stop('无尽测试须保留当前主线存档');",
"if (expected === 'qualification' && (mode !== 'main' || level !== 41)) stop('资格关用例要求主线第 41 关');",
"if (expected !== 'qualification' && !['purchasable','purchase_disabled','unlocked'].includes(info.status)) stop('请先准备有资格且处于活动期的账号');",
"const input = {periodId: pm.environment.get('periodId'), eventId: 'win-' + pm.variables.replaceIn('{{$guid}}'), outcome:'win', mode};",
"if (mode === 'main') input.clearedMainLevel = level;",
"else input.endlessSequence = integer('endlessSequence', 1);",
"const data = {action:'save', uid:pm.environment.get('uid'), token:pm.environment.get('token'), levelAmount:level, goldMiner:input};",
"if (mode === 'endless') data.isWuXian = 'true';",
"pm.environment.set('gmWinBefore', info.progressWins || 0);",
"pm.environment.set('gmWinBody', JSON.stringify(data));",
"pm.environment.unset('gmWinAfter');"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"",
"const d = r.data?.goldMiner;",
"pm.test('活动子结果与计数', () => {",
" pm.expect(d.code).to.eql(1);",
" const before = Number(pm.environment.get('gmWinBefore'));",
" const expected = pm.environment.get('expectedProgressResult');",
" pm.expect(d.activityCounted).to.eql(expected === 'counted');",
" pm.expect(d.progressWins).to.eql(before + (expected === 'counted' ? 1 : 0));",
" if (expected === 'qualification') pm.expect(d.reason).to.eql('qualification_level');",
" else pm.expect(d.duplicate).to.eql(false);",
" if (expected === 'capped') pm.expect(d.progressWins).to.eql(JSON.parse(pm.environment.get('gmInfo')).maxTarget);",
"});",
"if (r.code === 1 && d?.code === 1) {",
" pm.environment.set('gmWinAfter', d.progressWins);",
" pm.environment.set('currentMainLevel', r.data.levelAmount);",
"}"
]
}
}
]
},
{
"name": "02 无尽新胜利",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{gmWinBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/userLevel",
"host": [
"{{baseUrl}}"
],
"path": [
"userLevel"
]
},
"description": "mainLevelAfterWin=currentMainLevel;endlessSequence 使用此账号本期从未上报的唯一序号。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([]);",
"",
"need(['uid','token','periodId','currentMainLevel','gmInfo']);",
"const info = JSON.parse(pm.environment.get('gmInfo'));",
"if (info.periodId !== pm.environment.get('periodId')) stop('请重新查询本期活动信息');",
"const expected = pm.environment.get('expectedProgressResult');",
"if (!['counted','qualification','capped'].includes(expected)) stop('expectedProgressResult 仅支持 counted/qualification/capped');",
"const level = integer('mainLevelAfterWin', 1);",
"const oldLevel = integer('currentMainLevel', 0);",
"const mode = 'endless';",
"if (mode === 'main' && level !== oldLevel + 1) stop('主线只测试顺序过关:mainLevelAfterWin 必须为刚读取的 currentMainLevel + 1');",
"if (mode === 'endless' && level !== oldLevel) stop('无尽测试须保留当前主线存档');",
"if (expected === 'qualification' && (mode !== 'main' || level !== 41)) stop('资格关用例要求主线第 41 关');",
"if (expected !== 'qualification' && !['purchasable','purchase_disabled','unlocked'].includes(info.status)) stop('请先准备有资格且处于活动期的账号');",
"const input = {periodId: pm.environment.get('periodId'), eventId: 'win-' + pm.variables.replaceIn('{{$guid}}'), outcome:'win', mode};",
"if (mode === 'main') input.clearedMainLevel = level;",
"else input.endlessSequence = integer('endlessSequence', 1);",
"const data = {action:'save', uid:pm.environment.get('uid'), token:pm.environment.get('token'), levelAmount:level, goldMiner:input};",
"if (mode === 'endless') data.isWuXian = 'true';",
"pm.environment.set('gmWinBefore', info.progressWins || 0);",
"pm.environment.set('gmWinBody', JSON.stringify(data));",
"pm.environment.unset('gmWinAfter');"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"",
"const d = r.data?.goldMiner;",
"pm.test('活动子结果与计数', () => {",
" pm.expect(d.code).to.eql(1);",
" const before = Number(pm.environment.get('gmWinBefore'));",
" const expected = pm.environment.get('expectedProgressResult');",
" pm.expect(d.activityCounted).to.eql(expected === 'counted');",
" pm.expect(d.progressWins).to.eql(before + (expected === 'counted' ? 1 : 0));",
" if (expected === 'qualification') pm.expect(d.reason).to.eql('qualification_level');",
" else pm.expect(d.duplicate).to.eql(false);",
" if (expected === 'capped') pm.expect(d.progressWins).to.eql(JSON.parse(pm.environment.get('gmInfo')).maxTarget);",
"});",
"if (r.code === 1 && d?.code === 1) {",
" pm.environment.set('gmWinAfter', d.progressWins);",
" pm.environment.set('currentMainLevel', r.data.levelAmount);",
"}"
]
}
}
]
},
{
"name": "03 原事件重试不重复累计",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{gmRetryBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/userLevel",
"host": [
"{{baseUrl}}"
],
"path": [
"userLevel"
]
},
"description": "仅紧接已计数的胜利;资格关和封顶未记录去重条目,不适用此用例。无尽重试会再次影响旧 addLevel 统计,只用独立测试账号。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);",
"need(['gmWinBody','gmWinAfter']);",
"const saved = JSON.parse(pm.environment.get('gmWinBody'));",
"saved.token = pm.environment.get('token');",
"pm.variables.set('gmRetryBody', JSON.stringify(saved));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('同一次胜利只计一次', () => { const d = r.data.goldMiner; pm.expect(d.code).to.eql(1); pm.expect(d.duplicate).to.eql(true); pm.expect(d.activityCounted).to.eql(false); pm.expect(d.progressWins).to.eql(Number(pm.environment.get('gmWinAfter'))); });"
]
}
}
]
},
{
"name": "04 同一事件 ID 改变通关载荷",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{gmConflictBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/userLevel",
"host": [
"{{baseUrl}}"
],
"path": [
"userLevel"
]
},
"description": "仅对已计数原事件。活动拒绝发生在普通存档之后;本请求保留原 levelAmount,避免推进主线存档。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);",
"need(['gmWinBody','gmWinAfter']);",
"const saved = JSON.parse(pm.environment.get('gmWinBody'));",
"saved.token = pm.environment.get('token');",
"pm.variables.set('gmRetryBody', JSON.stringify(saved));",
"if (saved.goldMiner.mode === 'main') saved.goldMiner.clearedMainLevel += 1; else saved.goldMiner.endlessSequence += 1;",
"pm.variables.set('gmConflictBody', JSON.stringify(saved));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('活动子结果拒绝冲突', () => { pm.expect(r.data.goldMiner.code).to.eql(0); pm.expect(r.data.goldMiner.errorCode).to.eql('EVENT_PAYLOAD_CONFLICT'); });"
]
}
}
]
},
{
"name": "05 结束后提交新的胜利",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{gmExpiredBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/userLevel",
"host": [
"{{baseUrl}}"
],
"path": [
"userLevel"
]
},
"description": "expiredPeriodId 为已结束的普通期或 goldMiner:test: 临时期;endlessSequence 为该旧期未记录序号。普通存档仍会执行,单独检查 data.goldMiner.errorCode=PERIOD_ENDED。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([]);",
"need(['uid','token','expiredPeriodId','currentMainLevel']);",
"const data = {action:'save',uid:pm.environment.get('uid'),token:pm.environment.get('token'),levelAmount:integer('currentMainLevel',1),isWuXian:'true',goldMiner:{periodId:pm.environment.get('expiredPeriodId'),eventId:'late-'+pm.variables.replaceIn('{{$guid}}'),outcome:'win',mode:'endless',endlessSequence:integer('endlessSequence',1)}};",
"pm.variables.set('gmExpiredBody',JSON.stringify(data));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('不补记过期进度', () => { pm.expect(r.data.goldMiner.code).to.eql(0); pm.expect(r.data.goldMiner.errorCode).to.eql('PERIOD_ENDED'); });"
]
}
}
]
}
]
},
{
"name": "03 原生支付与查单(手动)",
"description": "Android/iOS 创建入口按平台二选一;同一期另一平台也应复用原订单。签名请求本身不扣款;真实支付在客户端进行。",
"item": [
{
"name": "01 Android 创建订单(含篡改价格参数)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"itemid\": \"{{productId}}\",\n \"createRequestId\": \"{{createRequestId}}\",\n \"itemPrice\": 1,\n \"itemCount\": 999\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/orderPaySig",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"orderPaySig"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);",
"need(['gmInfo','productId','priceFen']);",
"const info = JSON.parse(pm.environment.get('gmInfo'));",
"if (info.status !== 'purchasable' || info.productId !== pm.environment.get('productId')) stop('请先查询可购买的本期活动');",
"pm.variables.set('createRequestId','buy-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"",
"pm.test('商品及金额取服务端快照', () => {",
" const sign = JSON.parse(r.data.signData);",
" pm.expect(sign.productId).to.eql(pm.environment.get('productId'));",
" pm.expect(sign.buyQuantity).to.eql(1);",
" pm.expect(sign.goodsPrice).to.eql(Number(pm.environment.get('priceFen')));",
" pm.expect(sign.outTradeNo).to.eql(r.data.outTradeNo);",
" pm.expect(typeof r.data.paySig).to.eql('string');",
" pm.expect(typeof r.data.signature).to.eql('string');",
"});",
"if (r.code === 1) { pm.environment.set('outTradeNo',r.data.outTradeNo); pm.environment.set('orderPeriodId',JSON.parse(pm.environment.get('gmInfo')).periodId); }"
]
}
}
]
},
{
"name": "02 原生 iOS 创建订单(含篡改价格参数)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"itemid\": \"{{productId}}\",\n \"createRequestId\": \"{{createRequestId}}\",\n \"itemPrice\": 1,\n \"itemCount\": 999\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/iosorderPaySig",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"iosorderPaySig"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);",
"need(['gmInfo','productId','priceFen']);",
"const info = JSON.parse(pm.environment.get('gmInfo'));",
"if (info.status !== 'purchasable' || info.productId !== pm.environment.get('productId')) stop('请先查询可购买的本期活动');",
"pm.variables.set('createRequestId','buy-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"",
"pm.test('商品及金额取服务端快照', () => {",
" const sign = JSON.parse(r.data.signData);",
" pm.expect(sign.productId).to.eql(pm.environment.get('productId'));",
" pm.expect(sign.buyQuantity).to.eql(1);",
" pm.expect(sign.goodsPrice).to.eql(Number(pm.environment.get('priceFen')));",
" pm.expect(sign.outTradeNo).to.eql(r.data.outTradeNo);",
" pm.expect(typeof r.data.paySig).to.eql('string');",
" pm.expect(typeof r.data.signature).to.eql('string');",
"});",
"if (r.code === 1) { pm.environment.set('outTradeNo',r.data.outTradeNo); pm.environment.set('orderPeriodId',JSON.parse(pm.environment.get('gmInfo')).periodId); }"
]
}
}
]
},
{
"name": "03 换 requestId 仍复用同一期订单",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"itemid\": \"{{productId}}\",\n \"createRequestId\": \"{{createRequestId}}\",\n \"itemPrice\": 1,\n \"itemCount\": 999\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/orderPaySig",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"orderPaySig"
]
},
"description": "仅紧接同一期下单,支付前执行;outTradeNo 必须来自本期首次创建。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"outTradeNo\", \"uid\", \"token\"]);",
"need(['gmInfo','productId','priceFen']);",
"const info = JSON.parse(pm.environment.get('gmInfo'));",
"if (info.status !== 'purchasable' || info.productId !== pm.environment.get('productId')) stop('请先查询可购买的本期活动');",
"pm.variables.set('createRequestId','buy-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('复用原订单', () => pm.expect(r.data.outTradeNo).to.eql(pm.environment.get('outTradeNo')));"
]
}
}
]
},
{
"name": "查单/通用领取:wx/getPayInfo",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"outTradeNo\": \"{{outTradeNo}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/getPayInfo",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"getPayInfo"
]
},
"description": "默认 pending;客户端真实支付并收到可信通知后设 paid,明确填写 expectedFulfillmentRoute 和 expectedTargetPeriodId。不会由此请求伪造支付。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"outTradeNo\", \"uid\", \"token\"]);",
"if (!['pending','paid'].includes(pm.environment.get('expectedPaymentState'))) stop('expectedPaymentState 只能为 pending/paid');",
"if (pm.environment.get('expectedPaymentState') === 'paid') need(['expectedFulfillmentRoute','expectedTargetPeriodId']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"",
"const expected = pm.environment.get('expectedPaymentState');",
"pm.test('查单匹配指定状态', () => {",
" if (expected === 'pending') { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('PAYMENT_PENDING'); return; }",
" pm.expect(r.code).to.eql(1); pm.expect(r.data.pay_state).to.eql(2);",
" pm.expect(r.data.rewardDelivery).to.eql('goldMiner.claim');",
" pm.expect(r.data.goldMiner.fulfillmentStatus).to.eql('fulfilled');",
" pm.expect(r.data.goldMiner.fulfillmentRoute).to.eql(pm.environment.get('expectedFulfillmentRoute'));",
" pm.expect(r.data.goldMiner.targetPeriodId).to.eql(pm.environment.get('expectedTargetPeriodId'));",
"});"
]
}
}
]
},
{
"name": "查单/通用领取:wx/iosgetPayInfo",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"outTradeNo\": \"{{outTradeNo}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/iosgetPayInfo",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"iosgetPayInfo"
]
},
"description": "默认 pending;客户端真实支付并收到可信通知后设 paid,明确填写 expectedFulfillmentRoute 和 expectedTargetPeriodId。不会由此请求伪造支付。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"outTradeNo\", \"uid\", \"token\"]);",
"if (!['pending','paid'].includes(pm.environment.get('expectedPaymentState'))) stop('expectedPaymentState 只能为 pending/paid');",
"if (pm.environment.get('expectedPaymentState') === 'paid') need(['expectedFulfillmentRoute','expectedTargetPeriodId']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"",
"const expected = pm.environment.get('expectedPaymentState');",
"pm.test('查单匹配指定状态', () => {",
" if (expected === 'pending') { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('PAYMENT_PENDING'); return; }",
" pm.expect(r.code).to.eql(1); pm.expect(r.data.pay_state).to.eql(2);",
" pm.expect(r.data.rewardDelivery).to.eql('goldMiner.claim');",
" pm.expect(r.data.goldMiner.fulfillmentStatus).to.eql('fulfilled');",
" pm.expect(r.data.goldMiner.fulfillmentRoute).to.eql(pm.environment.get('expectedFulfillmentRoute'));",
" pm.expect(r.data.goldMiner.targetPeriodId).to.eql(pm.environment.get('expectedTargetPeriodId'));",
"});"
]
}
}
]
},
{
"name": "查单/通用领取:wx/getOrderReward",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"outTradeNo\": \"{{outTradeNo}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/getOrderReward",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"getOrderReward"
]
},
"description": "默认 pending;客户端真实支付并收到可信通知后设 paid,明确填写 expectedFulfillmentRoute 和 expectedTargetPeriodId。不会由此请求伪造支付。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"outTradeNo\", \"uid\", \"token\"]);",
"if (!['pending','paid'].includes(pm.environment.get('expectedPaymentState'))) stop('expectedPaymentState 只能为 pending/paid');",
"if (pm.environment.get('expectedPaymentState') === 'paid') need(['expectedFulfillmentRoute','expectedTargetPeriodId']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"",
"const expected = pm.environment.get('expectedPaymentState');",
"pm.test('查单匹配指定状态', () => {",
" if (expected === 'pending') { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('PAYMENT_PENDING'); return; }",
" pm.expect(r.code).to.eql(1); pm.expect(r.data.pay_state).to.eql(2);",
" pm.expect(r.data.rewardDelivery).to.eql('goldMiner.claim');",
" pm.expect(r.data.goldMiner.fulfillmentStatus).to.eql('fulfilled');",
" pm.expect(r.data.goldMiner.fulfillmentRoute).to.eql(pm.environment.get('expectedFulfillmentRoute'));",
" pm.expect(r.data.goldMiner.targetPeriodId).to.eql(pm.environment.get('expectedTargetPeriodId'));",
"});"
]
}
}
]
},
{
"name": "无效签名通知不得解锁",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{gmCallbackBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/payCallBack",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"payCallBack"
]
},
"description": "用尚未支付的测试订单,openid 须为所属账号。发送后以 pending 查单确认仍未支付;正向验签必须用支付平台通知。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([]);",
"need(['outTradeNo','productId','priceFen','openid']);",
"const payload = JSON.stringify({OutTradeNo:pm.environment.get('outTradeNo'),OpenId:pm.environment.get('openid'),Env:0,GoodsInfo:{ProductId:pm.environment.get('productId'),Quantity:1,OrigPrice:integer('priceFen',1)}});",
"pm.variables.set('gmCallbackBody',JSON.stringify({Event:'minigame_game_pay_goods_deliver_notify',MiniGame:{Payload:payload,PayEventSig:'0'.repeat(64),IsMock:false}}));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('通知被拒绝', () => { pm.expect(typeof r.ErrCode).to.eql('number'); pm.expect(r.ErrCode === 0).to.eql(false); });"
]
}
}
]
}
]
},
{
"name": "04 任意档位领取与客户端保存(逐条手动)",
"description": "领取目标单独使用 claimPeriodId/claimTaskId,info 不会改写它们。领过的任务不得重新加币;确认幂等用同一 grantId。",
"item": [
{
"name": "01 获取奖励授权",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"claim\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"{{claimPeriodId}}\",\n \"taskId\": \"{{claimTaskId}}\",\n \"requestId\": \"{{claimRequestId}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/requestId。返回 grantId/taskId/items/claimStatus/claimedThrough/requiresClientDelivery。仅有效期内可申请;授权不加币。到期返回 PERIOD_SETTLEMENT_REQUIRED,转补发查询。\n\n普通领奖仅活动有效期内调用;历史补发使用 05 文件夹清单授权和发货确认。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"claimPeriodId\", \"claimTaskId\", \"uid\", \"token\"]);",
"pm.variables.set('claimRequestId','claim-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"",
"pm.test('奖励授权结构', () => {",
" pm.expect(r.data.taskId).to.eql(pm.environment.get('claimTaskId'));",
" pm.expect(typeof r.data.grantId).to.eql('string');",
" pm.expect(r.data.items.length > 0).to.eql(true);",
" pm.expect(['issuing','claimed'].includes(r.data.claimStatus)).to.eql(true);",
" pm.expect(r.data.requiresClientDelivery).to.eql(r.data.claimStatus !== 'claimed');",
"});",
"if (r.code === 1) {",
" const old = pm.environment.get('gmGrant') ? JSON.parse(pm.environment.get('gmGrant')) : null;",
" const current = {...r.data,periodId:pm.environment.get('claimPeriodId'),taskId:pm.environment.get('claimTaskId')};",
" if (!old || old.grantId !== current.grantId) { pm.environment.unset('gmSavedGrantId'); pm.environment.unset('coinAfterDelivery'); }",
" pm.environment.set('gmGrant',JSON.stringify(current));",
" pm.environment.set('grantId',current.grantId);",
"}"
]
}
}
]
},
{
"name": "02 重复领取返回原凭证",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"claim\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"{{claimPeriodId}}\",\n \"taskId\": \"{{claimTaskId}}\",\n \"requestId\": \"{{claimRequestId}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/requestId。返回 grantId/taskId/items/claimStatus/claimedThrough/requiresClientDelivery。仅有效期内可申请;授权不加币。到期返回 PERIOD_SETTLEMENT_REQUIRED,转补发查询。\n\n"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"claimPeriodId\", \"claimTaskId\", \"uid\", \"token\"]);",
"",
"need(['gmGrant','grantId','claimPeriodId','claimTaskId']);",
"const grant = JSON.parse(pm.environment.get('gmGrant'));",
"if (grant.grantId !== pm.environment.get('grantId') || grant.periodId !== pm.environment.get('claimPeriodId') || grant.taskId !== pm.environment.get('claimTaskId')) stop('凭证与所选期/任务不一致,请先领取所选任务');",
"pm.variables.set('claimRequestId','claim-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('重复领取无新奖励凭证', () => { const prior = JSON.parse(pm.environment.get('gmGrant')); pm.expect(r.data.grantId).to.eql(prior.grantId); pm.expect(r.data.items).to.eql(prior.items); pm.expect(r.data.claimStatus).to.eql(prior.claimStatus); pm.expect(r.data.requiresClientDelivery).to.eql(prior.claimStatus !== 'claimed'); });"
]
}
}
]
},
{
"name": "03 读取金币总余额",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"read\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/userCoin",
"host": [
"{{baseUrl}}"
],
"path": [
"userCoin"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('余额为非负数', () => pm.expect(Number.isFinite(r.data.coinAmount) && r.data.coinAmount >= 0).to.eql(true));",
"if (r.code === 1) pm.environment.set('coinBeforeDelivery',r.data.coinAmount);"
]
}
}
]
},
{
"name": "04 模拟客户端发货后保存总余额",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"save\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"coinAmount\": \"{{coinAfterDelivery}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/userCoin",
"host": [
"{{baseUrl}}"
],
"path": [
"userCoin"
]
},
"description": "仅独立测试账号、无其他设备写入时使用。领取后读余额,再手填 coinAfterDelivery;保存的是总余额。超时后先读服务端并核对原余额及凭证,不能重新相加。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);",
"",
"need(['gmGrant','grantId','claimPeriodId','claimTaskId']);",
"const grant = JSON.parse(pm.environment.get('gmGrant'));",
"if (grant.grantId !== pm.environment.get('grantId') || grant.periodId !== pm.environment.get('claimPeriodId') || grant.taskId !== pm.environment.get('claimTaskId')) stop('凭证与所选期/任务不一致,请先领取所选任务');",
"",
"if (grant.claimStatus !== 'issuing' || !grant.requiresClientDelivery) stop('该凭证已完成,不得再次加币');",
"if (pm.environment.get('gmSavedGrantId') === grant.grantId) stop('此凭证已保存成功,只需确认领取');",
"const before = integer('coinBeforeDelivery');",
"const after = integer('coinAfterDelivery');",
"if (!grant.items.every(i => i.type === 'coin')) stop('当前测试仅支持金币发货');",
"const amount = grant.items.reduce((sum,i) => sum+i.count,0);",
"if (after !== before + amount) stop('coinAfterDelivery 应为刚读取的总余额加本次奖励;请人工填写');"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('保存指定总余额', () => pm.expect(r.data.coinAmount).to.eql(Number(pm.environment.get('coinAfterDelivery'))));",
"if (r.code === 1 && r.data.coinAmount === Number(pm.environment.get('coinAfterDelivery'))) pm.environment.set('gmSavedGrantId',pm.environment.get('grantId'));"
]
}
}
]
},
{
"name": "05 保存后确认领取(可原请求重试)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"confirm_delivery\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"{{claimPeriodId}}\",\n \"taskId\": \"{{claimTaskId}}\",\n \"grantId\": \"{{grantId}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/grantId。返回 grantId/claimStatus/claimedThrough。仅在客户端已保存奖励后确认,重复确认幂等。\n\n完成后可重复确认、重复授权,再查询活动验证当前档 claimed 和下一档状态。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);",
"",
"need(['gmGrant','grantId','claimPeriodId','claimTaskId']);",
"const grant = JSON.parse(pm.environment.get('gmGrant'));",
"if (grant.grantId !== pm.environment.get('grantId') || grant.periodId !== pm.environment.get('claimPeriodId') || grant.taskId !== pm.environment.get('claimTaskId')) stop('凭证与所选期/任务不一致,请先领取所选任务');",
"",
"if (pm.environment.get('gmSavedGrantId') !== grant.grantId) stop('请先完成本凭证的金币保存');"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"",
"pm.test('确认同一凭证', () => { pm.expect(r.data.grantId).to.eql(pm.environment.get('grantId')); pm.expect(r.data.claimStatus).to.eql('claimed'); pm.expect(Number.isSafeInteger(r.data.claimedThrough) && r.data.claimedThrough >= 0).to.eql(true); });",
"if (r.code === 1) { const grant = JSON.parse(pm.environment.get('gmGrant')); grant.claimStatus='claimed'; grant.requiresClientDelivery=false; grant.claimedThrough=r.data.claimedThrough; pm.environment.set('gmGrant',JSON.stringify(grant)); }"
]
}
}
]
},
{
"name": "06 未付费不能领",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"claim\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"{{claimPeriodId}}\",\n \"taskId\": \"{{claimTaskId}}\",\n \"requestId\": \"{{claimRequestId}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/requestId。返回 grantId/taskId/items/claimStatus/claimedThrough/requiresClientDelivery。仅有效期内可申请;授权不加币。到期返回 PERIOD_SETTLEMENT_REQUIRED,转补发查询。\n\n未付费且任务尚未授权的已有玩家期记录。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"claimPeriodId\", \"claimTaskId\", \"uid\", \"token\"]);",
"pm.variables.set('claimRequestId','claim-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:NOT_PAID', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('NOT_PAID'); });"
]
}
}
]
},
{
"name": "07 未达标不能领",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"claim\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"{{claimPeriodId}}\",\n \"taskId\": \"{{claimTaskId}}\",\n \"requestId\": \"{{claimRequestId}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/requestId。返回 grantId/taskId/items/claimStatus/claimedThrough/requiresClientDelivery。仅有效期内可申请;授权不加币。到期返回 PERIOD_SETTLEMENT_REQUIRED,转补发查询。\n\n已付费,所选任务未达标。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"claimPeriodId\", \"claimTaskId\", \"uid\", \"token\"]);",
"pm.variables.set('claimRequestId','claim-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:TARGET_NOT_REACHED', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('TARGET_NOT_REACHED'); });"
]
}
}
]
},
{
"name": "08 跳过前一档领取已达标任务",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"claim\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"{{claimPeriodId}}\",\n \"taskId\": \"{{claimTaskId}}\",\n \"requestId\": \"{{claimRequestId}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/requestId。返回 grantId/taskId/items/claimStatus/claimedThrough/requiresClientDelivery。仅有效期内可申请;授权不加币。到期返回 PERIOD_SETTLEMENT_REQUIRED,转补发查询。\n\n已付费,所选后续任务已达标,但前一档未确认保存。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"claimPeriodId\", \"claimTaskId\", \"uid\", \"token\"]);",
"pm.variables.set('claimRequestId','claim-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"",
"pm.test('奖励授权结构', () => {",
" pm.expect(r.data.taskId).to.eql(pm.environment.get('claimTaskId'));",
" pm.expect(typeof r.data.grantId).to.eql('string');",
" pm.expect(r.data.items.length > 0).to.eql(true);",
" pm.expect(['issuing','claimed'].includes(r.data.claimStatus)).to.eql(true);",
" pm.expect(r.data.requiresClientDelivery).to.eql(r.data.claimStatus !== 'claimed');",
"});",
"if (r.code === 1) {",
" const old = pm.environment.get('gmGrant') ? JSON.parse(pm.environment.get('gmGrant')) : null;",
" const current = {...r.data,periodId:pm.environment.get('claimPeriodId'),taskId:pm.environment.get('claimTaskId')};",
" if (!old || old.grantId !== current.grantId) { pm.environment.unset('gmSavedGrantId'); pm.environment.unset('coinAfterDelivery'); }",
" pm.environment.set('gmGrant',JSON.stringify(current));",
" pm.environment.set('grantId',current.grantId);",
"}"
]
}
}
]
},
{
"name": "09 错误发货凭证不能确认",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"confirm_delivery\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"{{claimPeriodId}}\",\n \"taskId\": \"{{claimTaskId}}\",\n \"grantId\": \"postman-invalid-grant\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/grantId。返回 grantId/claimStatus/claimedThrough。仅在客户端已保存奖励后确认,重复确认幂等。\n\n所选任务须存在于该账号的玩家期记录。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"claimPeriodId\", \"claimTaskId\", \"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:INVALID_GRANT', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('INVALID_GRANT'); });"
]
}
}
]
}
]
},
{
"name": "05 到期补发(手动)",
"description": "查询所有历史已结束期的未确认奖励;后台 jobs/支付恢复负责结算。优先处理 items,有 nextCursor 即翻页,包括 settling 响应。扫描结束后从第一页复查;settling 时稍后重查,no_pending_rewards 时结束。05-05 选原 grantId,04-03/04 保存金币后使用 05-06 确认补发;ack_settlement 仅兼容展示确认。",
"item": [
{
"name": "01 清单第一页",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"settlements\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token,afterId 可选分页游标;前端不传 limit,后端固定每页扫描 20 期。返回 status=pending_delivery/settling/no_pending_rewards、items、nextCursor;每项含 settlementId/periodId/rewards,奖励含 taskId/grantId/items。可发奖励优先返回,不受其他期结算阻塞;no_pending_rewards 表示该玩家当前环境下所有已结束期均无待发或待结算记录。\n\n只查询后台已结算的未确认奖励。先处理可发奖励,有 nextCursor 即继续翻页;扫描结束后从第一页复查,settling 时稍后重查,no_pending_rewards 时结束。需部署 goldMiner/jobs 及每分钟触发器。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('补发清单结构', () => {",
" pm.expect(['settling','pending_delivery','no_pending_rewards'].includes(r.data.status)).to.eql(true);",
" pm.expect(Array.isArray(r.data.items)).to.eql(true);",
" pm.expect(r.data.nextCursor === null || typeof r.data.nextCursor === 'string').to.eql(true);",
" if (r.data.status === 'settling') pm.expect(r.data.items.length).to.eql(0);",
" if (r.data.status === 'no_pending_rewards') { pm.expect(r.data.items.length).to.eql(0); pm.expect(r.data.nextCursor).to.eql(null); }",
" r.data.items.forEach(s => {",
" pm.expect(typeof s.settlementId).to.eql('string'); pm.expect(typeof s.periodId).to.eql('string');",
" pm.expect(Array.isArray(s.rewards) && s.rewards.length > 0).to.eql(true);",
" s.rewards.forEach(g => { pm.expect(typeof g.grantId).to.eql('string'); pm.expect(typeof g.taskId).to.eql('string'); pm.expect(Array.isArray(g.items)).to.eql(true); });",
" });",
"});",
"if (r.code === 1) pm.environment.set('settlementCursor',r.data.nextCursor || '');"
]
}
}
]
},
{
"name": "02 下一页(空 items 也可能有下一页)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"settlements\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"afterId\": \"{{settlementCursor}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token,afterId 可选分页游标;前端不传 limit,后端固定每页扫描 20 期。返回 status=pending_delivery/settling/no_pending_rewards、items、nextCursor;每项含 settlementId/periodId/rewards,奖励含 taskId/grantId/items。可发奖励优先返回,不受其他期结算阻塞;no_pending_rewards 表示该玩家当前环境下所有已结束期均无待发或待结算记录。\n\n只查询后台已结算的未确认奖励。先处理可发奖励,有 nextCursor 即继续翻页;扫描结束后从第一页复查,settling 时稍后重查,no_pending_rewards 时结束。需部署 goldMiner/jobs 及每分钟触发器。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"settlementCursor\", \"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('补发清单结构', () => {",
" pm.expect(['settling','pending_delivery','no_pending_rewards'].includes(r.data.status)).to.eql(true);",
" pm.expect(Array.isArray(r.data.items)).to.eql(true);",
" pm.expect(r.data.nextCursor === null || typeof r.data.nextCursor === 'string').to.eql(true);",
" if (r.data.status === 'settling') pm.expect(r.data.items.length).to.eql(0);",
" if (r.data.status === 'no_pending_rewards') { pm.expect(r.data.items.length).to.eql(0); pm.expect(r.data.nextCursor).to.eql(null); }",
" r.data.items.forEach(s => {",
" pm.expect(typeof s.settlementId).to.eql('string'); pm.expect(typeof s.periodId).to.eql('string');",
" pm.expect(Array.isArray(s.rewards) && s.rewards.length > 0).to.eql(true);",
" s.rewards.forEach(g => { pm.expect(typeof g.grantId).to.eql('string'); pm.expect(typeof g.taskId).to.eql('string'); pm.expect(Array.isArray(g.items)).to.eql(true); });",
" });",
"});",
"if (r.code === 1) pm.environment.set('settlementCursor',r.data.nextCursor || '');"
]
}
}
]
},
{
"name": "03 未完成发货不能确认清单",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"ack_settlement\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"settlementId\": \"{{settlementId}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/settlementId。返回 settlementId/acknowledged。仅已全部确认发货时成功;这是兼容展示确认,不代替 confirm_settlement_delivery。\n\n清单有未确认奖励时,旧 ack_settlement 仍不能提前确认展示。此请求不是发货确认。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"settlementId\", \"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('拒绝:DELIVERY_PENDING', () => { pm.expect(r.code).to.eql(0); pm.expect(r.errorCode).to.eql('DELIVERY_PENDING'); });"
]
}
}
]
},
{
"name": "04 完成后确认清单(可重复)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"ack_settlement\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"settlementId\": \"{{settlementId}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/settlementId。返回 settlementId/acknowledged。仅已全部确认发货时成功;这是兼容展示确认,不代替 confirm_settlement_delivery。\n\n全部奖励确认后可选调用的旧展示确认,不是发货确认;新流程不要求调用。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"settlementId\", \"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('确认选定清单', () => { pm.expect(r.data.settlementId).to.eql(pm.environment.get('settlementId')); pm.expect(r.data.acknowledged).to.eql(true); });"
]
}
}
]
},
{
"name": "05 查询并选择首个待补发奖励(不调用 claim)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"settlements\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token,afterId 可选分页游标;前端不传 limit,后端固定每页扫描 20 期。返回 status=pending_delivery/settling/no_pending_rewards、items、nextCursor;每项含 settlementId/periodId/rewards,奖励含 taskId/grantId/items。可发奖励优先返回,不受其他期结算阻塞;no_pending_rewards 表示该玩家当前环境下所有已结束期均无待发或待结算记录。\n\n选择第一页首项;随后执行 04 文件夹的 03 读余额、04 保存余额,再执行本文件夹 06 确认。其他页奖励按返回标识选择,不走 claim。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('补发清单结构', () => {",
" pm.expect(['settling','pending_delivery','no_pending_rewards'].includes(r.data.status)).to.eql(true);",
" pm.expect(Array.isArray(r.data.items)).to.eql(true);",
" pm.expect(r.data.nextCursor === null || typeof r.data.nextCursor === 'string').to.eql(true);",
" if (r.data.status === 'settling') pm.expect(r.data.items.length).to.eql(0);",
" if (r.data.status === 'no_pending_rewards') { pm.expect(r.data.items.length).to.eql(0); pm.expect(r.data.nextCursor).to.eql(null); }",
" r.data.items.forEach(s => {",
" pm.expect(typeof s.settlementId).to.eql('string'); pm.expect(typeof s.periodId).to.eql('string');",
" pm.expect(Array.isArray(s.rewards) && s.rewards.length > 0).to.eql(true);",
" s.rewards.forEach(g => { pm.expect(typeof g.grantId).to.eql('string'); pm.expect(typeof g.taskId).to.eql('string'); pm.expect(Array.isArray(g.items)).to.eql(true); });",
" });",
"});",
"if (r.code === 1) pm.environment.set('settlementCursor',r.data.nextCursor || '');",
"",
"if (r.code === 1 && r.data.status === 'pending_delivery' && r.data.items.length) {",
" const s=r.data.items[0], g=s.rewards[0];",
" pm.environment.set('settlementId',s.settlementId);",
" pm.environment.set('claimPeriodId',s.periodId); pm.environment.set('claimTaskId',g.taskId);",
" pm.environment.set('grantId',g.grantId);",
" pm.environment.set('gmGrant',JSON.stringify({...g,periodId:s.periodId,claimStatus:'issuing',requiresClientDelivery:true}));",
"}"
]
}
}
]
},
{
"name": "06 保存金币后确认补发(重复安全)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"confirm_settlement_delivery\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"settlementId\": \"{{settlementId}}\",\n \"grantIds\": [\n \"{{grantId}}\"\n ]\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/settlementId/grantIds(1~100 个不重复授权 ID)。返回 settlementId/confirmedGrantIds/deliveryStatus(partial/client_saved)。本用例逐项确认,须先完成对应奖励保存。\n\n只在所选 grantId 的金币确定已保存后执行。接口支持批量 grantIds,手工联调默认逐项,沿用既有金币保存保护。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);",
"",
"need(['gmGrant','grantId','claimPeriodId','claimTaskId']);",
"const grant = JSON.parse(pm.environment.get('gmGrant'));",
"if (grant.grantId !== pm.environment.get('grantId') || grant.periodId !== pm.environment.get('claimPeriodId') || grant.taskId !== pm.environment.get('claimTaskId')) stop('凭证与所选期/任务不一致,请先领取所选任务');",
"",
"if (pm.environment.get('gmSavedGrantId') !== grant.grantId) stop('请先完成本凭证的金币保存');",
"need(['settlementId']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r=pm.response.json();",
"pm.test('业务成功',()=>pm.expect(r.code).to.eql(1));",
"pm.test('补发确认匹配授权',()=>{",
" pm.expect(r.data.settlementId).to.eql(pm.environment.get('settlementId'));",
" pm.expect(r.data.confirmedGrantIds).to.eql([pm.environment.get('grantId')]);",
" pm.expect(['partial','client_saved'].includes(r.data.deliveryStatus)).to.eql(true);",
"});"
]
}
}
]
},
{
"name": "07 到期后普通领奖被拒绝",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"claim\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"periodId\": \"{{expiredPeriodId}}\",\n \"taskId\": \"{{claimTaskId}}\",\n \"requestId\": \"expired-claim\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "请求:uid/token/periodId/taskId/requestId。返回 grantId/taskId/items/claimStatus/claimedThrough/requiresClientDelivery。仅有效期内可申请;授权不加币。到期返回 PERIOD_SETTLEMENT_REQUIRED,转补发查询。\n\n手填 expiredPeriodId 和该期有效 claimTaskId;到期后普通领奖必须拒绝,含已授权任务的重复请求。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"uid\", \"token\"]);",
"need(['expiredPeriodId','claimTaskId']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r=pm.response.json();",
"pm.test('过期只走补发',()=>{pm.expect(r.code).to.eql(0);pm.expect(r.errorCode).to.eql('PERIOD_SETTLEMENT_REQUIRED');});"
]
}
}
]
}
]
},
{
"name": "06 管理操作(手动)",
"description": "adminToken 对应服务端 ADMIN_TOKEN。逐条执行:索引、未来周配置、购买开关、测试临时期。publish_test_period 额外要求 PAYMENT_APP_ENV=test 和 GOLD_MINER_TEST_PERIODS_ENABLED=true。",
"item": [
{
"name": "01 创建活动索引",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"setup_indexes\",\n \"adminToken\": \"{{adminToken}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/admin",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"admin"
]
},
"description": "请求:action=setup_indexes、adminToken(ADMIN_TOKEN)。返回 code/msg。只准备现有设计需要的索引,不启用活动。\n\n"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"adminToken\"]);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));"
]
}
}
]
},
{
"name": "02 发布未来期配置",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{gmAdminBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/admin",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"admin"
]
},
"description": "configJson 格式见 README/API 文档,生效期必须为未来周四;同 configVersion 不重复发布。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"adminToken\", \"configJson\"]);",
"let config;",
"try { config=JSON.parse(pm.environment.get('configJson')); } catch { stop('configJson 必须是填写完成的配置 JSON'); }",
"const offers = config?.vipTiers === undefined ? [config] : config.vipTiers; if (!Array.isArray(offers) || !offers.length || (config.vipTiers && offers.length !== 6) || offers.some(o => !o || !Number.isSafeInteger(o.priceFen) || o.priceFen <= 0 || !Array.isArray(o.tasks) || !o.tasks.length)) stop('请填写正式确定的价格和任务;禁止发布空草稿');",
"pm.variables.set('gmAdminBody',JSON.stringify({action:'publish',adminToken:pm.environment.get('adminToken'),config}));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('配置发布成功', () => { const config=JSON.parse(pm.environment.get('configJson')); pm.expect(r.data.configVersion).to.eql(config.configVersion); pm.expect(r.data.effectiveFromPeriodId).to.eql(config.effectiveFromPeriodId); if (config.vipTiers) pm.expect(r.data.vipTiers).to.eql(config.vipTiers); else { pm.expect(r.data.priceFen).to.eql(config.priceFen); pm.expect(r.data.tasks).to.eql(config.tasks); } });"
]
}
}
]
},
{
"name": "03 修改已有期购买开关",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{gmAdminBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/admin",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"admin"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need([\"adminToken\", \"adminPeriodId\", \"purchaseEnabled\"]);",
"const value=pm.environment.get('purchaseEnabled');",
"if (!['true','false'].includes(value)) stop('purchaseEnabled 必须为 true 或 false');",
"pm.variables.set('gmAdminBody',JSON.stringify({action:'set_purchasable',adminToken:pm.environment.get('adminToken'),periodId:pm.environment.get('adminPeriodId'),purchaseEnabled:value==='true'}));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));"
]
}
}
]
},
{
"name": "04 发布测试临时期(默认立即开启 30 分钟)",
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"const get = key => String(pm.environment.get(key) ?? '').trim();",
"const baseUrl = get('baseUrl');",
"if (!/^https?:\\/\\/[^\\s/?#]+(?::\\d+)?(?:\\/[^\\s?#]*)?$/.test(baseUrl) || baseUrl.endsWith('/') || /[<>]/.test(baseUrl)) stop('请填写 baseUrl(部署域名,无末尾 /)');",
"const adminToken = pm.environment.get('adminToken');",
"if (typeof adminToken !== 'string' || !adminToken.trim()) stop('请填写测试服 ADMIN_TOKEN 对应的 adminToken');",
"function timestamp(value, label) {",
" if (!/^\\d+$/.test(value) || !Number.isSafeInteger(Number(value)) || Number(value) <= 0 || !Number.isFinite(new Date(Number(value)).getTime())) stop(label + ' 必须为有效的 Unix 毫秒整数');",
" return Number(value);",
"}",
"let config;",
"try { config = JSON.parse(get('tempConfig')); } catch { stop('tempConfig 必须为 JSON 配置对象'); }",
"if (!config || typeof config !== 'object' || Array.isArray(config)) stop('tempConfig 必须为 JSON 配置对象');",
"let periodId = get('tempPeriodId');",
"if (periodId && !/^goldMiner:test:[A-Za-z0-9_-]{1,60}$/.test(periodId)) stop('tempPeriodId 格式为 goldMiner:test:自定义标识');",
"const startsAt = get('tempStartsAt') ? timestamp(get('tempStartsAt'), 'tempStartsAt') : Date.now();",
"let endsAt;",
"if (get('tempEndsAt')) endsAt = timestamp(get('tempEndsAt'), 'tempEndsAt');",
"else {",
" const minutes = Number(get('tempDurationMinutes'));",
" if (!Number.isSafeInteger(minutes) || minutes <= 0) stop('tempDurationMinutes 必须为正整数分钟');",
" endsAt = timestamp(String(startsAt + minutes * 60000), '计算出的结束时间');",
"}",
"if (endsAt <= startsAt) stop('tempEndsAt 必须晚于 tempStartsAt');",
"const nextPeriodId = get('tempNextPeriodId');",
"if (nextPeriodId && !/^goldMiner:(?:test:[A-Za-z0-9_-]{1,60}|\\d{4}-\\d{2}-\\d{2})$/.test(nextPeriodId)) stop('tempNextPeriodId 格式错误');",
"const previousBase = get('tempDraftBaseUrl');",
"if (previousBase && previousBase !== baseUrl) stop('baseUrl 已切换,请使用另一份环境,或清空 tempDraftBaseUrl/tempPeriodId/tempStartsAt/tempEndsAt 后重新生成');",
"if (!periodId) periodId = 'goldMiner:test:' + pm.variables.replaceIn('{{$guid}}');",
"// Only non-secret identifiers/times are persisted. Retrying Send reuses the same period.",
"pm.environment.set('tempPeriodId', periodId);",
"pm.environment.set('tempStartsAt', String(startsAt));",
"pm.environment.set('tempEndsAt', String(endsAt));",
"pm.environment.set('tempDraftBaseUrl', baseUrl);",
"const body = { action: 'publish_test_period', adminToken, periodId, startsAt, endsAt, config };",
"if (nextPeriodId) body.nextPeriodId = nextPeriodId;",
"// Local scope avoids storing a second copy of the administrator secret in the environment.",
"pm.variables.set('testPeriodRequestBody', JSON.stringify(body));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('HTTP 200', function () { pm.response.to.have.status(200); });",
"const response = pm.response.json();",
"const sent = JSON.parse(pm.variables.get('testPeriodRequestBody'));",
"pm.test('临时期发布成功(失败时查看响应 errorCode/msg)', function () { pm.expect(response.code).to.eql(1); });",
"if (response.code === 1 && response.data) {",
" const data = response.data;",
" pm.test('期 ID、开始和结束时间与请求一致', function () {",
" pm.expect(data.periodId).to.eql(sent.periodId);",
" pm.expect(data.startsAt).to.eql(sent.startsAt);",
" pm.expect(data.endsAt).to.eql(sent.endsAt);",
" });",
" pm.test('奖励配置和商品快照与请求一致', function () {",
" for (const key of ['configVersion', 'unlockPassedLevel', 'timezone', 'currency', ...(sent.config.vipTiers ? ['vipTiers'] : ['productId', 'priceFen', 'tasks'])]) {",
" pm.expect(data.configSnapshot[key]).to.eql(sent.config[key]);",
" }",
" pm.expect(data.configSnapshot.effectiveFromPeriodId).to.eql(sent.periodId);",
" pm.expect(typeof data.configHash).to.eql('string');",
" });",
" pm.test('顺延目标不早于本期结束', function () {",
" pm.expect(Number.isSafeInteger(data.nextStartsAt) && data.nextStartsAt >= sent.endsAt).to.eql(true);",
" if (sent.nextPeriodId) pm.expect(data.nextPeriodId).to.eql(sent.nextPeriodId);",
" });",
"}"
]
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{testPeriodRequestBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/admin",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"admin"
]
},
"description": "参数在配套 Environment 中配置。tempPeriodId/tempStartsAt/tempEndsAt 留空时首次自动生成并保存。tempConfig 为完整业务配置 JSON,可修改门槛、价格及任务。tempNextPeriodId 可留空。已发布期不可修改;新建期时使用新的期 ID 及不重叠的时间。接口对应服务端 publish_test_period 动作,没有单独的 /test-period 路由。"
},
"response": []
}
]
},
{
"name": "07 旧 iOS 客服渠道",
"item": [
{
"name": "01 客服渠道预下单(服务端定价)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"create_order\",\n \"channel\": \"legacy_ios\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"itemid\": \"{{productId}}\",\n \"createRequestId\": \"{{createRequestId}}\",\n \"itemPrice\": 1,\n \"itemCount\": 999\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": "需服务端启用 GOLD_MINER_LEGACY_ENABLED。与原生下单使用不同账号或不同期;同一期不切换渠道。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need(['uid','token','gmInfo','productId','priceFen']);",
"const info = JSON.parse(pm.environment.get('gmInfo'));",
"if (info.status !== 'purchasable') stop('请先查询可购买的当期活动');",
"pm.variables.set('createRequestId','legacy-'+pm.variables.replaceIn('{{$guid}}'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('旧渠道订单使用服务端快照', () => {",
" pm.expect(r.data.channel).to.eql('legacy_ios');",
" pm.expect(r.data.quantity).to.eql(1);",
" pm.expect(r.data.priceFen).to.eql(Number(pm.environment.get('priceFen')));",
" pm.expect(r.data.productId).to.eql(pm.environment.get('productId'));",
" pm.expect(r.data.periodId).to.eql(JSON.parse(pm.environment.get('gmInfo')).periodId);",
" const session = JSON.parse(r.data.sessionFrom);",
" pm.expect(session.outTradeNo).to.eql(r.data.outTradeNo);",
" pm.expect(session.propName).to.eql(r.data.productId);",
" pm.expect(typeof session.goldMinerTicket).to.eql('string');",
"});",
"if (r.code === 1) { pm.environment.set('outTradeNo',r.data.outTradeNo); pm.environment.set('orderPeriodId',r.data.periodId); pm.environment.set('legacySessionFrom',r.data.sessionFrom); }"
]
}
}
]
},
{
"name": "02 重复预下单复用原订单",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"action\": \"create_order\",\n \"channel\": \"legacy_ios\",\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"itemid\": \"{{productId}}\",\n \"createRequestId\": \"{{createRequestId}}\",\n \"itemPrice\": 1,\n \"itemCount\": 999\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/index",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"index"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need(['uid','token','gmInfo','productId','priceFen']);",
"const info = JSON.parse(pm.environment.get('gmInfo'));",
"if (info.status !== 'purchasable') stop('请先查询可购买的当期活动');",
"pm.variables.set('createRequestId','legacy-'+pm.variables.replaceIn('{{$guid}}'));",
"need(['outTradeNo','legacySessionFrom']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('旧渠道订单使用服务端快照', () => {",
" pm.expect(r.data.channel).to.eql('legacy_ios');",
" pm.expect(r.data.quantity).to.eql(1);",
" pm.expect(r.data.priceFen).to.eql(Number(pm.environment.get('priceFen')));",
" pm.expect(r.data.productId).to.eql(pm.environment.get('productId'));",
" pm.expect(r.data.periodId).to.eql(JSON.parse(pm.environment.get('gmInfo')).periodId);",
" const session = JSON.parse(r.data.sessionFrom);",
" pm.expect(session.outTradeNo).to.eql(r.data.outTradeNo);",
" pm.expect(session.propName).to.eql(r.data.productId);",
" pm.expect(typeof session.goldMinerTicket).to.eql('string');",
"});",
"pm.test('复用原订单', () => pm.expect(r.data.outTradeNo).to.eql(pm.environment.get('outTradeNo')));",
"if (r.code === 1) pm.environment.set('legacySessionFrom',r.data.sessionFrom);"
]
}
}
]
},
{
"name": "03 客服回调发送支付链接(手动开启)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{{legacyCustomerBody}}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/KeFuInfo",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"KeFuInfo"
]
},
"description": "需测试账号在微信有效客服会话窗口内,服务端已有 wxaccess_token;此请求会真实发送消息,勿在 Runner 中批量执行。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need(['openid','legacySessionFrom']);",
"if (pm.environment.get('allowCustomerMessage') !== 'true') stop('此请求会向测试账号发送客服消息;确认后填写 allowCustomerMessage=true');",
"pm.variables.set('legacyCustomerBody',JSON.stringify({Event:'user_enter_tempsession',FromUserName:pm.environment.get('openid'),SessionFrom:pm.environment.get('legacySessionFrom')}));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('业务成功', () => pm.expect(r.code).to.eql(1));",
"pm.test('仍为原订单', () => pm.expect(r.data.outTradeNo).to.eql(pm.environment.get('outTradeNo')));"
]
}
}
]
},
{
"name": "04 登录态转单可重复调用",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"outTradeNo\": \"{{outTradeNo}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/checkIos",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"checkIos"
]
},
"description": "创建订单后即可调用;只校验/关联快照,不确认付款。支付页使用签名 time 票据而非登录态。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need(['uid','token','outTradeNo']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('转单明确成功', () => pm.expect(pm.response.json()).to.eql(true));"
]
}
}
]
},
{
"name": "05 无效转单票据拒绝",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"openid\": \"{{openid}}\",\n \"outTradeNo\": \"{{outTradeNo}}\",\n \"time\": \"invalid-ticket\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/checkIos",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"checkIos"
]
},
"description": ""
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need(['openid','outTradeNo']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('未授权转单拒绝', () => pm.expect(pm.response.json()).to.eql(false));"
]
}
}
]
},
{
"name": "06 无签名商户通知拒绝",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"event_type\": \"TRANSACTION.SUCCESS\",\n \"resource\": {}\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/goldMiner/merchantNotify",
"host": [
"{{baseUrl}}"
],
"path": [
"goldMiner",
"merchantNotify"
]
},
"description": "之后再次查单,确认未解锁。不提供伪造成功通知或支付密钥。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
""
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('HTTP 500', () => pm.response.to.have.status(500));",
"pm.test('无签名通知拒绝', () => pm.expect(pm.response.json().code).to.eql('FAIL'));"
]
}
}
]
},
{
"name": "07 旧渠道订单禁止再创建原生订单",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"uid\": \"{{uid}}\",\n \"token\": \"{{token}}\",\n \"itemid\": \"{{productId}}\",\n \"createRequestId\": \"{{createRequestId}}\",\n \"itemPrice\": 1,\n \"itemCount\": 999\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/wx/orderPaySig",
"host": [
"{{baseUrl}}"
],
"path": [
"wx",
"orderPaySig"
]
},
"description": "仅在旧渠道订单尚未付款时执行;原生支付签名配置也需已就绪。"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"function stop(message) { console.error(message); pm.execution.skipRequest(); throw new Error(message); }",
"function need(keys) { for (const key of keys) if (String(pm.environment.get(key) ?? '').trim() === '') stop('请先填写/取得变量:' + key); }",
"function integer(key, min = 0) { need([key]); const value = Number(pm.environment.get(key)); if (!Number.isSafeInteger(value) || value < min) stop(key + ' 必须为有效整数'); return value; }",
"",
"need(['uid','token','gmInfo','productId','priceFen']);",
"const info = JSON.parse(pm.environment.get('gmInfo'));",
"if (info.status !== 'purchasable') stop('请先查询可购买的当期活动');",
"pm.variables.set('createRequestId','legacy-'+pm.variables.replaceIn('{{$guid}}'));",
"need(['outTradeNo','legacySessionFrom']);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('渠道冲突', () => pm.expect(r.errorCode).to.eql('PAYMENT_CHANNEL_CONFLICT'));"
]
}
}
]
}
]
},
{
"name": "08 公共活动日程(无需登录,可单独 Runner)",
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。",
"item": [
{
"name": "01 公共活动日程(GET,无需登录)",
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": []
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('公共日程查询成功', () => pm.expect(r.code).to.eql(1));",
"let valid = false;",
"if (r.code === 1) pm.test('公共日程字段、时间窗口和排序', () => {",
" const d = r.data;",
" pm.expect(Object.keys(d).sort()).to.eql(['schemaVersion','serverNow','windowEndsAt','refreshAt','activities'].sort());",
" pm.expect(d.schemaVersion).to.eql(1);",
" pm.expect(Number.isSafeInteger(d.serverNow)).to.eql(true);",
" pm.expect(d.windowEndsAt).to.eql(d.serverNow + 7 * 86400000);",
" pm.expect(Array.isArray(d.activities)).to.eql(true);",
" const seen = new Set();",
" let refreshAt = d.serverNow + 30000;",
" d.activities.forEach(a => {",
" pm.expect(['goldMiner','cloudRise'].includes(a.activityId)).to.eql(true);",
" const fields = ['activityId','periodId','configVersion','startsAt','endsAt','unlockLevel','phase'];",
" if (a.activityId === 'goldMiner') fields.push('purchaseEnabled');",
" pm.expect(Object.keys(a).sort()).to.eql(fields.sort());",
" pm.expect(typeof a.periodId === 'string' && !!a.periodId && typeof a.configVersion === 'string' && !!a.configVersion).to.eql(true);",
" pm.expect(Number.isSafeInteger(a.startsAt) && Number.isSafeInteger(a.endsAt) && a.startsAt < a.endsAt).to.eql(true);",
" pm.expect(a.startsAt < d.windowEndsAt && a.endsAt > d.serverNow).to.eql(true);",
" pm.expect(Number.isSafeInteger(a.unlockLevel) && a.unlockLevel >= 0).to.eql(true);",
" pm.expect(a.phase).to.eql(a.startsAt <= d.serverNow ? 'active' : 'upcoming');",
" if (a.activityId === 'goldMiner') pm.expect(typeof a.purchaseEnabled).to.eql('boolean');",
" const key = JSON.stringify([a.activityId,a.periodId]);",
" pm.expect(seen.has(key)).to.eql(false);seen.add(key);",
" for (const boundary of [a.startsAt,a.endsAt]) if (boundary > d.serverNow) refreshAt = Math.min(refreshAt,boundary);",
" });",
" pm.expect(d.refreshAt).to.eql(refreshAt);",
" const sorted = [...d.activities].sort((a,b) => a.activityId < b.activityId ? -1 : a.activityId > b.activityId ? 1",
" : b.startsAt-a.startsAt || (a.periodId < b.periodId ? -1 : a.periodId > b.periodId ? 1 : 0));",
" pm.expect(d.activities).to.eql(sorted);",
" const expected = pm.environment.get('expectedListedPeriodId');",
" if (expected) pm.expect(d.activities.some(a=>a.activityId==='goldMiner' && a.periodId===expected)).to.eql(true);",
" valid = true;",
"});",
"// Cache only a valid success. Empty activities is valid; errors must preserve the previous cache.",
"if (valid) pm.environment.set('gmActivitySchedule',JSON.stringify(r.data));"
]
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/activityConfig/list",
"host": [
"{{baseUrl}}"
],
"path": [
"activityConfig",
"list"
]
},
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。"
},
"response": [
{
"name": "成功:临时期日程",
"originalRequest": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/activityConfig/list",
"host": [
"{{baseUrl}}"
],
"path": [
"activityConfig",
"list"
]
},
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。"
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"cookie": [],
"body": "{\n \"code\": 1,\n \"msg\": \"ok\",\n \"data\": {\n \"schemaVersion\": 1,\n \"serverNow\": 1789956000000,\n \"windowEndsAt\": 1790560800000,\n \"refreshAt\": 1789956030000,\n \"activities\": [\n {\n \"activityId\": \"goldMiner\",\n \"periodId\": \"goldMiner:test:20260921_1000\",\n \"configVersion\": \"temporaryV1\",\n \"startsAt\": 1789956000000,\n \"endsAt\": 1789959600000,\n \"unlockLevel\": 41,\n \"phase\": \"active\",\n \"purchaseEnabled\": true\n }\n ]\n }\n}"
},
{
"name": "成功:无开放或预告活动",
"originalRequest": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/activityConfig/list",
"host": [
"{{baseUrl}}"
],
"path": [
"activityConfig",
"list"
]
},
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。"
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"cookie": [],
"body": "{\n \"code\": 1,\n \"msg\": \"ok\",\n \"data\": {\n \"schemaVersion\": 1,\n \"serverNow\": 1789956000000,\n \"windowEndsAt\": 1790560800000,\n \"refreshAt\": 1789956030000,\n \"activities\": []\n }\n}"
},
{
"name": "查询失败:保留旧缓存",
"originalRequest": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/activityConfig/list",
"host": [
"{{baseUrl}}"
],
"path": [
"activityConfig",
"list"
]
},
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。"
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"cookie": [],
"body": "{\n \"code\": 0,\n \"msg\": \"活动信息暂时无法获取,请稍后重试\",\n \"data\": null\n}"
}
]
},
{
"name": "02 公共活动日程(POST,无需登录)",
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": []
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const r = pm.response.json();",
"pm.test('HTTP 200', () => pm.response.to.have.status(200));",
"pm.test('公共日程查询成功', () => pm.expect(r.code).to.eql(1));",
"let valid = false;",
"if (r.code === 1) pm.test('公共日程字段、时间窗口和排序', () => {",
" const d = r.data;",
" pm.expect(Object.keys(d).sort()).to.eql(['schemaVersion','serverNow','windowEndsAt','refreshAt','activities'].sort());",
" pm.expect(d.schemaVersion).to.eql(1);",
" pm.expect(Number.isSafeInteger(d.serverNow)).to.eql(true);",
" pm.expect(d.windowEndsAt).to.eql(d.serverNow + 7 * 86400000);",
" pm.expect(Array.isArray(d.activities)).to.eql(true);",
" const seen = new Set();",
" let refreshAt = d.serverNow + 30000;",
" d.activities.forEach(a => {",
" pm.expect(['goldMiner','cloudRise'].includes(a.activityId)).to.eql(true);",
" const fields = ['activityId','periodId','configVersion','startsAt','endsAt','unlockLevel','phase'];",
" if (a.activityId === 'goldMiner') fields.push('purchaseEnabled');",
" pm.expect(Object.keys(a).sort()).to.eql(fields.sort());",
" pm.expect(typeof a.periodId === 'string' && !!a.periodId && typeof a.configVersion === 'string' && !!a.configVersion).to.eql(true);",
" pm.expect(Number.isSafeInteger(a.startsAt) && Number.isSafeInteger(a.endsAt) && a.startsAt < a.endsAt).to.eql(true);",
" pm.expect(a.startsAt < d.windowEndsAt && a.endsAt > d.serverNow).to.eql(true);",
" pm.expect(Number.isSafeInteger(a.unlockLevel) && a.unlockLevel >= 0).to.eql(true);",
" pm.expect(a.phase).to.eql(a.startsAt <= d.serverNow ? 'active' : 'upcoming');",
" if (a.activityId === 'goldMiner') pm.expect(typeof a.purchaseEnabled).to.eql('boolean');",
" const key = JSON.stringify([a.activityId,a.periodId]);",
" pm.expect(seen.has(key)).to.eql(false);seen.add(key);",
" for (const boundary of [a.startsAt,a.endsAt]) if (boundary > d.serverNow) refreshAt = Math.min(refreshAt,boundary);",
" });",
" pm.expect(d.refreshAt).to.eql(refreshAt);",
" const sorted = [...d.activities].sort((a,b) => a.activityId < b.activityId ? -1 : a.activityId > b.activityId ? 1",
" : b.startsAt-a.startsAt || (a.periodId < b.periodId ? -1 : a.periodId > b.periodId ? 1 : 0));",
" pm.expect(d.activities).to.eql(sorted);",
" const expected = pm.environment.get('expectedListedPeriodId');",
" if (expected) pm.expect(d.activities.some(a=>a.activityId==='goldMiner' && a.periodId===expected)).to.eql(true);",
" valid = true;",
"});",
"// Cache only a valid success. Empty activities is valid; errors must preserve the previous cache.",
"if (valid) pm.environment.set('gmActivitySchedule',JSON.stringify(r.data));"
]
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"url": {
"raw": "{{baseUrl}}/activityConfig/list",
"host": [
"{{baseUrl}}"
],
"path": [
"activityConfig",
"list"
]
},
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。",
"body": {
"mode": "raw",
"raw": "{}",
"options": {
"raw": {
"language": "json"
}
}
}
},
"response": [
{
"name": "成功:临时期日程",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"url": {
"raw": "{{baseUrl}}/activityConfig/list",
"host": [
"{{baseUrl}}"
],
"path": [
"activityConfig",
"list"
]
},
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。",
"body": {
"mode": "raw",
"raw": "{}",
"options": {
"raw": {
"language": "json"
}
}
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"cookie": [],
"body": "{\n \"code\": 1,\n \"msg\": \"ok\",\n \"data\": {\n \"schemaVersion\": 1,\n \"serverNow\": 1789956000000,\n \"windowEndsAt\": 1790560800000,\n \"refreshAt\": 1789956030000,\n \"activities\": [\n {\n \"activityId\": \"goldMiner\",\n \"periodId\": \"goldMiner:test:20260921_1000\",\n \"configVersion\": \"temporaryV1\",\n \"startsAt\": 1789956000000,\n \"endsAt\": 1789959600000,\n \"unlockLevel\": 41,\n \"phase\": \"active\",\n \"purchaseEnabled\": true\n }\n ]\n }\n}"
},
{
"name": "成功:无开放或预告活动",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"url": {
"raw": "{{baseUrl}}/activityConfig/list",
"host": [
"{{baseUrl}}"
],
"path": [
"activityConfig",
"list"
]
},
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。",
"body": {
"mode": "raw",
"raw": "{}",
"options": {
"raw": {
"language": "json"
}
}
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"cookie": [],
"body": "{\n \"code\": 1,\n \"msg\": \"ok\",\n \"data\": {\n \"schemaVersion\": 1,\n \"serverNow\": 1789956000000,\n \"windowEndsAt\": 1790560800000,\n \"refreshAt\": 1789956030000,\n \"activities\": []\n }\n}"
},
{
"name": "查询失败:保留旧缓存",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"url": {
"raw": "{{baseUrl}}/activityConfig/list",
"host": [
"{{baseUrl}}"
],
"path": [
"activityConfig",
"list"
]
},
"description": "公开只读,无需 uid/token/adminToken,不传 action。GET 无请求体;POST 请求体为 {}。返回 schemaVersion、serverNow、windowEndsAt、refreshAt、activities。活动条目为 activityId/periodId/configVersion/startsAt/endsAt/unlockLevel/phase;黄金矿工另含 purchaseEnabled。phase=active/upcoming。含当前和未来七天的黄金矿工/百人赛日程;不表示玩家已达资格或已付费。成功空列表是正常结果;code=0/data=null 表示查询失败,保留缓存并重试。脚本只保存 gmActivitySchedule,不覆盖玩家 info 的 periodId 或奖励状态。可选 expectedListedPeriodId 断言指定黄金矿工期存在(必须落在查询窗口内)。",
"body": {
"mode": "raw",
"raw": "{}",
"options": {
"raw": {
"language": "json"
}
}
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"cookie": [],
"body": "{\n \"code\": 0,\n \"msg\": \"活动信息暂时无法获取,请稍后重试\",\n \"data\": null\n}"
}
]
}
]
}
]
}