refactor(网页发布): 首次发布与更新共用统一发布组装

- handleSubmit 与 handleUpdateSubmit 原先逐字复制 压包→媒体→metadata→parts→submitGamePublish
- 抽成组件内 runUnifiedPublish(target, versionNumber, priceMudPoints),成功收尾仍各模式自理
- 新增字段或校验变化不再需要改两处
This commit is contained in:
2026-10-07 19:23:21 +08:00
parent 9440ef037e
commit f3c3f279bc
@@ -48,7 +48,11 @@ import {
resolveGamePublishPriceMudPoints,
resolvePublishMetadataError,
} from './gamePublishMetadata';
import { createGamePublishSubmission } from './gamePublishSubmission';
import {
createGamePublishSubmission,
type GamePublishSubmissionSuccess,
type GamePublishSubmissionTarget,
} from './gamePublishSubmission';
import { prepareGamePackage } from './gameZipPackage';
type GamePublishPageProps = {
@@ -418,6 +422,42 @@ export function GamePublishPage({
}
}
/**
* 统一发布接口的一次完整组装:压包 → 读媒体 → 组 metadata/parts → 提交。
*
* 首次发布与更新模式只差 `target` 与冻结版本号;编排必须共用一处,否则新增字段或校验变化时
* 两条链路会各自漂移。成功后的收尾由调用方按模式各自处理。
*/
async function runUnifiedPublish(input: {
target: GamePublishSubmissionTarget;
versionNumber: number;
priceMudPoints: number;
}): Promise<GamePublishSubmissionSuccess> {
const prepared = await prepareGamePackage(packageFile as File);
const submission = buildGamePublishMediaSubmission(
coverImage,
screenshotImages,
);
const gameMetadata = buildPublishGameMetadata(submission);
const media = buildPublishMediaParts(submission);
return submitGamePublish({
ownerUserId: currentUserId,
title,
target: input.target,
versionNumber: input.versionNumber,
packageSha256: prepared.sha256,
packageBytes: prepared.totalBytes,
packageFileCount: prepared.fileCount,
packageEntryPath: 'index.html',
priceMudPoints: input.priceMudPoints,
gameMetadata,
packageBody: new Blob([prepared.bytes as unknown as BlobPart], {
type: 'application/zip',
}),
media,
});
}
/**
* 更新模式:走统一发布接口在既有 `gameId` 下创建不可变新版本并送审;
* 公开版本只在管理员通过后切换,期间旧版本继续在线。
@@ -453,29 +493,11 @@ export function GamePublishPage({
setIsSubmitting(true);
setError('');
try {
const prepared = await prepareGamePackage(packageFile);
const submission = buildGamePublishMediaSubmission(
coverImage,
screenshotImages,
);
const gameMetadata = buildPublishGameMetadata(submission);
const media = buildPublishMediaParts(submission);
// 统一发布接口只用 gameId + 冻结版本号做身份,不再读后端版本上限。
const result = await submitGamePublish({
ownerUserId: currentUserId,
title,
const result = await runUnifiedPublish({
target: { mode: 'update', gameId: updateContext.gameId },
versionNumber: updateContext.versionNumber + 1,
packageSha256: prepared.sha256,
packageBytes: prepared.totalBytes,
packageFileCount: prepared.fileCount,
packageEntryPath: 'index.html',
priceMudPoints: price.priceMudPoints,
gameMetadata,
packageBody: new Blob([prepared.bytes as unknown as BlobPart], {
type: 'application/zip',
}),
media,
});
setPublishedGameId(updateContext.gameId);
setUpdateContext((current) =>
@@ -528,30 +550,12 @@ export function GamePublishPage({
setIsSubmitting(true);
setError('');
try {
const prepared = await prepareGamePackage(packageFile);
const submission = buildGamePublishMediaSubmission(
coverImage,
screenshotImages,
);
const gameMetadata = buildPublishGameMetadata(submission);
const media = buildPublishMediaParts(submission);
// 首次发布只调一次统一接口:草稿里的 projectKey + versionNumber 是自然幂等键,
// 响应丢失后重试会命中同一个游戏与版本,而不是新建第二份。
const result = await submitGamePublish({
ownerUserId: currentUserId,
title,
const result = await runUnifiedPublish({
target: { mode: 'create' },
versionNumber: 1,
packageSha256: prepared.sha256,
packageBytes: prepared.totalBytes,
packageFileCount: prepared.fileCount,
packageEntryPath: 'index.html',
priceMudPoints: price.priceMudPoints,
gameMetadata,
packageBody: new Blob([prepared.bytes as unknown as BlobPart], {
type: 'application/zip',
}),
media,
});
setPublishedGameId(result.gameId);
} catch (submitError: unknown) {