125 lines
7.1 KiB
JSON
125 lines
7.1 KiB
JSON
{
|
||
"info": {
|
||
"name": "黄金矿工 - 临时期发布(test-period)",
|
||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||
"description": "POST /goldMiner/admin,action=publish_test_period。仅测试环境可用。配合 gold-miner-test-period.postman_environment.json 使用。填写 baseUrl/adminToken 后默认立即开启 30 分钟;时间与期 ID 自动保存,重复 Send 保持同一期。自定义时间、奖励和下一期见 gold-miner-test-period.README.md。请求会真实发布测试活动;不会模拟支付或发放奖励。"
|
||
},
|
||
"auth": {
|
||
"type": "noauth"
|
||
},
|
||
"item": [
|
||
{
|
||
"name": "发布临时期(默认立即开启 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": []
|
||
}
|
||
]
|
||
}
|