merge: codex/auth-spacetime-fail-closed into master
This commit is contained in:
@@ -276,7 +276,7 @@ RPG / 拼图等运行态存档仍以 `/api/profile/save-archives` 的后端列
|
||||
- 创作 Tab 表单:填写作品标题、简介、主题 / 竞技背景描述、玩家形象描述、对手形象描述、拟声词和难度。拟声词支持换行、逗号、顿号、斜杠或竖线分隔;未手动编辑时随主题 / 形象描述自动重算,手动编辑后保持创作者自定义。
|
||||
- 草稿编译:`POST /api/creation/bark-battle/drafts` 写入配置 JSON,返回包含 `draftId`、稳定 `workId`、`configVersion` 和 `rulesetVersion` 的草稿结果。
|
||||
- 生成页:`bark-battle-generating` 自动并行产出玩家形象、对手形象和竞技背景三图;前端生成页 UI 和其它玩法保持同一圆环主视觉,`media/create_bg_video.mp4` 作为固定全屏页面背景层循环静音播放,主进度圆环居中展示总进度,只保留当前步骤名称和当前步骤进度,不再渲染三行槽位列表。视频层需要显式触发播放。三图都走 Bark Battle 专用后端生图接口 `POST /api/creation/bark-battle/images/generate`,由后端按 `player-character`、`opponent-character`、`ui-background` 分别拼装正式提示词、写入 `generated-bark-battle-assets` 私有资产前缀并返回实际 prompt。玩家 / 对手形象提示词必须保持用户形象描述,不强行注入狗相关主体,并要求正面、单个完整形象和透明背景。部分失败也继续进入结果页。
|
||||
- 结果页:围绕三图槽位展示错误态与已生成结果,只保留单槽重试、重新生成和上传,不再提供一次生成按钮、音频配置入口或排名配置。
|
||||
- 结果页:围绕三图槽位展示错误态与已生成结果,只保留单槽重试、重新生成和上传,不再提供一次生成按钮、音频配置入口或排名配置;生成回写 `partial_failed` 时作品架不再显示整卡“生成中”遮罩,由结果页槽位错误承接失败。
|
||||
- 手动上传:结果页通过平台资产直传 `/api/assets/direct-upload-tickets` 与 `/api/assets/objects/confirm` 写入私有资产,再把返回的历史 generated 路径写回草稿配置。
|
||||
- 发布:结果页确认后必须携带草稿返回的同一个 `workId` 和结果页最终 `publishedSnapshot` 调用 `POST /api/creation/bark-battle/works/publish`;SpacetimeDB 发布态的 `config_json` 必须使用该最终快照,works summary 若拿到 `publishedSnapshotJson` 也优先使用最终快照映射封面三图。发布成功后先进入统一作品详情页,再由详情页进入正式 runtime;缺少 `workId` 的旧草稿状态需要重新生成草稿。
|
||||
- 作品架:Bark Battle 草稿 / 已发布列表优先读取后端 `/works`,但创建、生成完成、保存或发布后的本地摘要必须在后端 read model 尚未回读到同 `workId` 前继续保留;创作中心作品架同时接入 pending shelf 兜底,避免 ready 且三图齐全的草稿在刷新窗口期从“我的草稿 / 已发布”中消失。
|
||||
|
||||
@@ -1048,7 +1048,7 @@ test('buildCreationWorkShelfItems maps bark battle works with scene role cover a
|
||||
);
|
||||
});
|
||||
|
||||
test('bark battle draft generating state follows pending assets or missing three images', () => {
|
||||
test('bark battle draft generating state only follows pending assets', () => {
|
||||
const draft = {
|
||||
workId: 'bark-battle-work-draft',
|
||||
draftId: 'bark-battle-draft-1',
|
||||
@@ -1073,6 +1073,12 @@ test('bark battle draft generating state follows pending assets or missing three
|
||||
|
||||
expect(hasBarkBattleRequiredImages(draft)).toBe(false);
|
||||
expect(isPersistedBarkBattleDraftGenerating(draft)).toBe(true);
|
||||
expect(
|
||||
isPersistedBarkBattleDraftGenerating({
|
||||
...draft,
|
||||
generationStatus: 'partial_failed',
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isPersistedBarkBattleDraftGenerating({
|
||||
...draft,
|
||||
|
||||
@@ -1111,10 +1111,9 @@ export function isPersistedBarkBattleDraftGenerating(
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
item.generationStatus === 'pending_assets' ||
|
||||
!hasBarkBattleRequiredImages(item)
|
||||
);
|
||||
// 中文注释:汪汪声浪生成失败后会回写 partial_failed 并进入结果页承接错误槽位,
|
||||
// 不能因为三图未齐就继续把作品架整卡锁成“生成中”。
|
||||
return item.generationStatus === 'pending_assets';
|
||||
}
|
||||
|
||||
export function hasBarkBattleRequiredImages(item: BarkBattleWorkSummary) {
|
||||
|
||||
@@ -2094,6 +2094,8 @@ function buildDraftCompletionDialogSource(
|
||||
return formatPlatformTaskCompletionSource('方洞挑战草稿', sourceId);
|
||||
case 'jump-hop':
|
||||
return formatPlatformTaskCompletionSource('跳一跳草稿', sourceId);
|
||||
case 'wooden-fish':
|
||||
return formatPlatformTaskCompletionSource('敲木鱼草稿', sourceId);
|
||||
case 'puzzle':
|
||||
return formatPlatformTaskCompletionSource('拼图草稿', sourceId);
|
||||
case 'visual-novel':
|
||||
@@ -8887,6 +8889,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
setWoodenFishGenerationState(generationState);
|
||||
setIsWoodenFishBusy(true);
|
||||
setSelectionStage('wooden-fish-generating');
|
||||
markDraftGenerating('wooden-fish', [created.session.sessionId]);
|
||||
markPendingDraftGenerating('wooden-fish', created.session.sessionId);
|
||||
|
||||
try {
|
||||
const response = await woodenFishClient.executeAction(
|
||||
@@ -8921,6 +8925,30 @@ export function PlatformEntryFlowShellImpl({
|
||||
setWoodenFishGenerationState(
|
||||
createReadyWoodenFishGenerationState(generationState),
|
||||
);
|
||||
if (response.work) {
|
||||
setWoodenFishWorks((current) => [
|
||||
response.work!.summary,
|
||||
...current.filter(
|
||||
(item) => item.workId !== response.work!.summary.workId,
|
||||
),
|
||||
]);
|
||||
markPendingDraftReady(
|
||||
'wooden-fish',
|
||||
created.session.sessionId,
|
||||
false,
|
||||
);
|
||||
markDraftReady(
|
||||
'wooden-fish',
|
||||
[
|
||||
created.session.sessionId,
|
||||
response.work.summary.workId,
|
||||
response.work.summary.profileId,
|
||||
response.work.summary.sourceSessionId,
|
||||
],
|
||||
false,
|
||||
);
|
||||
void refreshWoodenFishShelf().catch(() => undefined);
|
||||
}
|
||||
setSelectionStage('wooden-fish-result');
|
||||
} catch (error) {
|
||||
const errorMessage = resolveRpgCreationErrorMessage(
|
||||
@@ -8955,7 +8983,15 @@ export function PlatformEntryFlowShellImpl({
|
||||
setIsWoodenFishBusy(false);
|
||||
}
|
||||
},
|
||||
[createReadyWoodenFishGenerationState, setSelectionStage],
|
||||
[
|
||||
createReadyWoodenFishGenerationState,
|
||||
markDraftGenerating,
|
||||
markDraftReady,
|
||||
markPendingDraftGenerating,
|
||||
markPendingDraftReady,
|
||||
refreshWoodenFishShelf,
|
||||
setSelectionStage,
|
||||
],
|
||||
);
|
||||
|
||||
const retryWoodenFishDraftGeneration = useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user