{ "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": [] } ] }