完善一键回归与真实输入准备

This commit is contained in:
2026-04-03 14:18:11 +08:00
parent 129ea935db
commit 114c524044
14 changed files with 599 additions and 29 deletions

View File

@@ -734,8 +734,9 @@ const devWorkbenchHTML = `<!doctype html>
<button class="ghost" id="btn-flow-result">Finish + Result</button>
<button class="secondary" id="btn-flow-admin-default-publish">一键默认绑定发布</button>
<button class="secondary" id="btn-flow-admin-runtime-publish">一键补齐 Runtime 并发布</button>
<button class="secondary" id="btn-flow-standard-regression">一键标准回归</button>
</div>
<div class="muted-note">这些流程会复用当前表单里的手机号、设备、event、channel 等输入。“一键默认绑定发布” 会自动执行Get Event -> Import Presentation -> Import Bundle -> Save Event Defaults -> Build Source -> Publish Build -> Get Release。“一键补齐 Runtime 并发布” 会在缺少默认 runtime 时自动创建 Runtime Binding再继续发布链。</div>
<div class="muted-note">这些流程会复用当前表单里的手机号、设备、event、channel 等输入。“一键默认绑定发布” 会自动执行Get Event -> Import Presentation -> Import Bundle -> Save Event Defaults -> Build Source -> Publish Build -> Get Release。“一键补齐 Runtime 并发布” 会在缺少默认 runtime 时自动创建 Runtime Binding再继续发布链。“一键标准回归” 会继续执行play -> launch -> start -> finish -> result -> history。</div>
<div class="subpanel">
<div class="muted-note">预期结果</div>
<div class="kv">
@@ -746,6 +747,18 @@ const devWorkbenchHTML = `<!doctype html>
<div>判定 <code id="flow-admin-verdict">待执行</code></div>
</div>
</div>
<div class="subpanel">
<div class="muted-note">回归结果汇总</div>
<div class="kv">
<div>发布链 <code id="flow-regression-publish-result">待执行</code></div>
<div>Play <code id="flow-regression-play-result">待执行</code></div>
<div>Launch <code id="flow-regression-launch-result">待执行</code></div>
<div>Result <code id="flow-regression-result-result">待执行</code></div>
<div>History <code id="flow-regression-history-result">待执行</code></div>
<div>Session ID <code id="flow-regression-session-id">-</code></div>
<div>总判定 <code id="flow-regression-overall">待执行</code></div>
</div>
</div>
</section>
<section class="panel" data-modes="common">
@@ -2217,6 +2230,70 @@ const devWorkbenchHTML = `<!doctype html>
$('flow-admin-verdict').textContent = verdict;
}
function resetStandardRegressionExpectation() {
$('flow-regression-publish-result').textContent = '待执行';
$('flow-regression-play-result').textContent = '待执行';
$('flow-regression-launch-result').textContent = '待执行';
$('flow-regression-result-result').textContent = '待执行';
$('flow-regression-history-result').textContent = '待执行';
$('flow-regression-session-id').textContent = '-';
$('flow-regression-overall').textContent = '待执行';
}
function setStandardRegressionExpectation(summary) {
const publishText = summary && summary.publish ? summary.publish : '待执行';
const playText = summary && summary.play ? summary.play : '待执行';
const launchText = summary && summary.launch ? summary.launch : '待执行';
const resultText = summary && summary.result ? summary.result : '待执行';
const historyText = summary && summary.history ? summary.history : '待执行';
const sessionId = summary && summary.sessionId ? summary.sessionId : '-';
const overall = summary && summary.overall ? summary.overall : '待执行';
$('flow-regression-publish-result').textContent = publishText;
$('flow-regression-play-result').textContent = playText;
$('flow-regression-launch-result').textContent = launchText;
$('flow-regression-result-result').textContent = resultText;
$('flow-regression-history-result').textContent = historyText;
$('flow-regression-session-id').textContent = sessionId;
$('flow-regression-overall').textContent = overall;
}
function extractList(payload) {
if (Array.isArray(payload)) {
return payload;
}
if (!payload || typeof payload !== 'object') {
return [];
}
if (Array.isArray(payload.items)) {
return payload.items;
}
if (Array.isArray(payload.results)) {
return payload.results;
}
if (Array.isArray(payload.sessions)) {
return payload.sessions;
}
return [];
}
function listContainsSession(list, sessionId) {
if (!sessionId) {
return false;
}
return list.some(function(item) {
if (!item || typeof item !== 'object') {
return false;
}
if (item.id && item.id === sessionId) {
return true;
}
if (item.session && item.session.id && item.session.id === sessionId) {
return true;
}
return false;
});
}
async function runAdminDefaultPublishFlow(options) {
const ensureRuntime = options && options.ensureRuntime === true;
const flowTitle = ensureRuntime ? 'flow-admin-runtime-publish' : 'flow-admin-default-publish';
@@ -2407,6 +2484,140 @@ const devWorkbenchHTML = `<!doctype html>
return releaseDetail;
}
async function runStandardRegressionFlow() {
const flowTitle = 'flow-standard-regression';
const eventId = $('event-id').value || $('admin-event-ref-id').value;
if (!trimmedOrUndefined(eventId)) {
throw new Error('event id is required');
}
resetStandardRegressionExpectation();
writeLog(flowTitle + '.step', { step: 'prepare-release', eventId: eventId });
const releaseDetail = await runAdminDefaultPublishFlow({ ensureRuntime: true });
const publishPass = $('flow-admin-verdict').textContent.indexOf('通过') === 0;
writeLog(flowTitle + '.step', {
step: 'login-wechat',
code: $('wechat-code').value,
deviceKey: $('wechat-device').value
});
const login = await request('POST', '/auth/login/wechat-mini', {
code: $('wechat-code').value,
clientType: 'wechat',
deviceKey: $('wechat-device').value
});
state.accessToken = login.data.tokens.accessToken;
state.refreshToken = login.data.tokens.refreshToken;
writeLog(flowTitle + '.step', {
step: 'event-play',
eventId: eventId
});
const play = await request('GET', '/events/' + encodeURIComponent(eventId) + '/play', undefined, true);
const playPass = !!(play.data && play.data.play && play.data.resolvedRelease && play.data.resolvedRelease.manifestUrl);
writeLog(flowTitle + '.step', {
step: 'event-launch',
eventId: eventId,
releaseId: $('event-release-id').value || state.releaseId,
variantId: trimmedOrUndefined($('event-variant-id').value)
});
const launch = await request('POST', '/events/' + encodeURIComponent(eventId) + '/launch', {
releaseId: $('event-release-id').value,
variantId: trimmedOrUndefined($('event-variant-id').value),
clientType: $('sms-client-type').value,
deviceKey: $('event-device').value
}, true);
state.sessionId = launch.data.launch.business.sessionId;
state.sessionToken = launch.data.launch.business.sessionToken;
syncState();
const launchPass = !!(
launch.data &&
launch.data.launch &&
launch.data.launch.business &&
launch.data.launch.business.sessionId &&
launch.data.launch.resolvedRelease &&
launch.data.launch.resolvedRelease.manifestUrl
);
writeLog(flowTitle + '.step', {
step: 'session-start',
sessionId: state.sessionId
});
await request('POST', '/sessions/' + encodeURIComponent(state.sessionId) + '/start', {
sessionToken: state.sessionToken
});
writeLog(flowTitle + '.step', {
step: 'session-finish',
sessionId: state.sessionId,
status: $('finish-status').value
});
await request('POST', '/sessions/' + encodeURIComponent(state.sessionId) + '/finish', {
sessionToken: state.sessionToken,
status: $('finish-status').value,
summary: buildFinishSummary()
});
writeLog(flowTitle + '.step', {
step: 'session-result',
sessionId: state.sessionId
});
const sessionResult = await request('GET', '/sessions/' + encodeURIComponent(state.sessionId) + '/result', undefined, true);
const resultPass = !!(
sessionResult.data &&
sessionResult.data.session &&
sessionResult.data.session.id === state.sessionId &&
sessionResult.data.result
);
writeLog(flowTitle + '.step', {
step: 'history-check',
sessionId: state.sessionId
});
const mySessions = await request('GET', '/me/sessions?limit=10', undefined, true);
const myResults = await request('GET', '/me/results?limit=10', undefined, true);
const sessionsList = extractList(mySessions.data);
const resultsList = extractList(myResults.data);
const historyPass = listContainsSession(sessionsList, state.sessionId) && listContainsSession(resultsList, state.sessionId);
const summary = {
publish: publishPass ? '通过:发布链可重复跑通' : '未通过:发布链未返回通过判定',
play: playPass ? '通过play 返回 resolvedRelease / play 摘要' : '未通过play 缺少关键摘要',
launch: launchPass ? '通过launch 返回 manifest + session' : '未通过launch 缺少 manifest 或 session',
result: resultPass ? '通过:单局 result 可直接回查' : '未通过:单局 result 未回查成功',
history: historyPass ? '通过me/sessions + me/results 均收录本局' : '未通过history 未同时收录本局',
sessionId: state.sessionId || '-',
overall: publishPass && playPass && launchPass && resultPass && historyPass ? '通过launch / play / result / history 回归已跑通' : '未通过:请看上面分项和日志'
};
setStandardRegressionExpectation(summary);
writeLog(flowTitle + '.expected', {
eventId: eventId,
releaseId: releaseDetail && releaseDetail.data ? releaseDetail.data.id : state.releaseId,
sessionId: state.sessionId,
publish: summary.publish,
play: summary.play,
launch: summary.launch,
result: summary.result,
history: summary.history,
overall: summary.overall
});
persistState();
return {
data: {
eventId: eventId,
releaseId: releaseDetail && releaseDetail.data ? releaseDetail.data.id : state.releaseId,
sessionId: state.sessionId,
publish: publishPass,
play: playPass,
launch: launchPass,
result: resultPass,
history: historyPass,
overall: publishPass && playPass && launchPass && resultPass && historyPass
}
};
}
function setStatus(text, isError = false) {
statusEl.textContent = text;
statusEl.className = isError ? 'status error' : 'status';
@@ -4124,6 +4335,10 @@ const devWorkbenchHTML = `<!doctype html>
return await runAdminDefaultPublishFlow({ ensureRuntime: true });
});
$('btn-flow-standard-regression').onclick = () => run('flow-standard-regression', async () => {
return await runStandardRegressionFlow();
});
[
'sms-client-type', 'sms-scene', 'sms-mobile', 'sms-device', 'sms-country', 'sms-code',
'wechat-code', 'wechat-device', 'local-config-file', 'config-event-id', 'config-runtime-binding-id', 'config-presentation-id', 'config-content-bundle-id', 'entry-channel-code', 'entry-channel-type',

View File

@@ -23,6 +23,7 @@ type DemoBootstrapSummary struct {
VariantManualEventID string `json:"variantManualEventId"`
VariantManualRelease string `json:"variantManualReleaseId"`
VariantManualCardID string `json:"variantManualCardId"`
CleanedSessionCount int64 `json:"cleanedSessionCount"`
}
func (s *Store) EnsureDemoData(ctx context.Context) (*DemoBootstrapSummary, error) {
@@ -597,6 +598,23 @@ func (s *Store) EnsureDemoData(ctx context.Context) (*DemoBootstrapSummary, erro
return nil, fmt.Errorf("ensure variant manual demo card: %w", err)
}
var cleanedSessionCount int64
if err := tx.QueryRow(ctx, `
WITH cleaned AS (
UPDATE game_sessions
SET
status = 'cancelled',
ended_at = NOW(),
updated_at = NOW()
WHERE event_id = ANY($1::uuid[])
AND status IN ('launched', 'running')
RETURNING 1
)
SELECT COUNT(*) FROM cleaned
`, []string{eventID, manualEventID}).Scan(&cleanedSessionCount); err != nil {
return nil, fmt.Errorf("cleanup demo ongoing sessions: %w", err)
}
if err := tx.Commit(ctx); err != nil {
return nil, err
}
@@ -619,5 +637,6 @@ func (s *Store) EnsureDemoData(ctx context.Context) (*DemoBootstrapSummary, erro
VariantManualEventID: "evt_demo_variant_manual_001",
VariantManualRelease: manualReleaseRow.PublicID,
VariantManualCardID: manualCardPublicID,
CleanedSessionCount: cleanedSessionCount,
}, nil
}