feat(jump-hop): 优化跳一跳素材生成与背景底图 #55

Merged
kdletters merged 11 commits from codex/tiaoyitiao into master 2026-06-05 23:47:33 +08:00
3 changed files with 108 additions and 1 deletions
Showing only changes of commit 36969726b4 - Show all commits

View File

@@ -283,7 +283,10 @@ pub async fn start_jump_hop_run(
) -> Result<Json<Value>, Response> {
let Json(payload) = jump_hop_json(payload, &request_context, JUMP_HOP_RUNTIME_PROVIDER)?;
ensure_non_empty(&request_context, &payload.profile_id, "profileId")?;
let is_draft_runtime = payload.runtime_mode.as_deref() == Some("draft");
let is_draft_runtime = payload
.runtime_mode
.as_deref()
.is_some_and(is_jump_hop_draft_runtime_mode);
let owner_user_id = principal.subject().to_string();
let principal_kind = principal.kind().as_str();
let run = state
@@ -1240,6 +1243,10 @@ fn build_jump_hop_work_play_tracking_draft(
WorkPlayTrackingDraft::runtime_principal("jump-hop", work_id, principal, source_route)
}
fn is_jump_hop_draft_runtime_mode(runtime_mode: &str) -> bool {
runtime_mode.trim().eq_ignore_ascii_case("draft")
}
fn build_jump_hop_draft(payload: &JumpHopWorkspaceCreateRequest) -> JumpHopDraftResponse {
let theme_text = normalize_theme_text(&payload.theme_text, &payload.work_title);
JumpHopDraftResponse {
@@ -1422,6 +1429,14 @@ fn current_utc_micros() -> i64 {
mod tests {
use super::*;
#[test]
fn jump_hop_draft_runtime_mode_detection_matches_client_normalization() {
assert!(is_jump_hop_draft_runtime_mode("draft"));
assert!(is_jump_hop_draft_runtime_mode(" DRAFT "));
assert!(!is_jump_hop_draft_runtime_mode("published"));
assert!(!is_jump_hop_draft_runtime_mode(""));
}
#[test]
fn jump_hop_tile_atlas_prompt_uses_dedicated_five_by_five_floor_layout() {
let prompt = build_jump_hop_tile_atlas_prompt("森林冒险", "森林主题清爽游戏化立体感平台");

View File

@@ -37,3 +37,93 @@ test('jump hop creation keeps image2 generation requests alive long enough', asy
}),
);
});
test('jump hop work detail preserves flattened back button asset', async () => {
const backButtonAsset = {
assetId: 'back-button-1',
imageSrc: '/generated-jump-hop-assets/back-button-1.png',
imageObjectKey: 'jump-hop/back-button-1.png',
assetObjectId: 'asset-object-back-button-1',
generationProvider: 'image2',
prompt: '主题返回按钮',
width: 1024,
height: 1024,
};
const characterAsset = {
assetId: 'character-1',
imageSrc: 'builtin://jump-hop/default-character',
imageObjectKey: '',
assetObjectId: 'character-object-1',
generationProvider: 'builtin-three',
prompt: '内置默认角色',
width: 0,
height: 0,
};
const draft = {
templateId: 'jump-hop',
templateName: '跳一跳',
profileId: 'profile-1',
themeText: '森林茶馆',
workTitle: '森林茶馆跳一跳',
workDescription: '森林茶馆主题',
themeTags: ['森林茶馆', '跳一跳'],
difficulty: 'standard',
stylePreset: 'minimal-blocks',
defaultCharacter: null,
characterPrompt: '内置默认角色',
tilePrompt: '森林茶馆主题地块',
endMoodPrompt: null,
characterAsset,
tileAtlasAsset: characterAsset,
tileAssets: [],
path: {
seed: 'profile-1',
difficulty: 'standard',
platforms: [],
scoring: {
perfectRadiusRatio: 0.24,
hitRadiusRatio: 0.52,
maxChargeMs: 1200,
minChargeMs: 80,
maxJumpDistance: 5,
},
},
coverComposite: null,
backButtonAsset: null,
generationStatus: 'ready',
};
requestJsonMock.mockResolvedValue({
item: {
runtimeKind: 'jump-hop',
workId: 'work-1',
profileId: 'profile-1',
ownerUserId: 'owner-1',
sourceSessionId: 'session-1',
themeText: '森林茶馆',
workTitle: '森林茶馆跳一跳',
workDescription: '森林茶馆主题',
themeTags: ['森林茶馆', '跳一跳'],
difficulty: 'standard',
stylePreset: 'minimal-blocks',
coverImageSrc: null,
publicationStatus: 'published',
playCount: 0,
updatedAt: '2026-06-05T00:00:00Z',
publishedAt: '2026-06-05T00:00:00Z',
publishReady: true,
generationStatus: 'ready',
draft,
path: draft.path,
defaultCharacter: null,
characterAsset,
tileAtlasAsset: characterAsset,
tileAssets: [],
backButtonAsset,
},
});
const { jumpHopClient } = await import('./jumpHopClient');
const response = await jumpHopClient.getWorkDetail('profile-1');
expect(response.item.backButtonAsset).toEqual(backButtonAsset);
});

View File

@@ -136,6 +136,8 @@ function normalizeJumpHopWorkProfile(
characterAsset: flattened.characterAsset,
tileAtlasAsset: flattened.tileAtlasAsset,
tileAssets: flattened.tileAssets,
backButtonAsset:
flattened.backButtonAsset ?? flattened.draft?.backButtonAsset ?? null,
};
}