From 46d240e37d84671c6677ae1af87ff617fdfb0a71 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 4 May 2026 03:02:24 +0800 Subject: [PATCH] feat: add custom world opening cg contract --- docs/technical/README.md | 1 + packages/shared/src/contracts/runtime.ts | 32 ++++++++++++++++++++++++ server-rs/crates/api-server/src/app.rs | 11 ++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/technical/README.md b/docs/technical/README.md index 3e503e5e..b78e055f 100644 --- a/docs/technical/README.md +++ b/docs/technical/README.md @@ -81,6 +81,7 @@ - [BIG_FISH_MAIN_IMAGE_TRANSPARENT_BACKGROUND_ALIGNMENT_2026-04-28.md](./BIG_FISH_MAIN_IMAGE_TRANSPARENT_BACKGROUND_ALIGNMENT_2026-04-28.md):记录大鱼吃小鱼等级主图与动作关键帧正式图在 Rust 后端复用 RPG 角色主图透明背景 alpha 后处理的对齐口径,并明确场地背景不走该处理。 - [PUZZLE_IMAGE_AND_FRONTEND_RULES_ALIGNMENT_2026-04-29.md](./PUZZLE_IMAGE_AND_FRONTEND_RULES_ALIGNMENT_2026-04-29.md):记录拼图生成图片回到 1:1,运行时拖动、交换、合并与拆分由前端即时裁决,以及移动端棋盘贴近屏幕边缘的落地边界。 - [PUZZLE_FORM_CREATION_FLOW_2026-04-29.md](./PUZZLE_FORM_CREATION_FLOW_2026-04-29.md):冻结拼图填表式创作入口、初始表单自动保存草稿、生成前退出后的表单恢复,以及草稿编译/首图生成的前后端边界。 +- [PUZZLE_PICTURE_ONLY_CREATION_AND_AI_TAGS_2026-05-03.md](./PUZZLE_PICTURE_ONLY_CREATION_AND_AI_TAGS_2026-05-03.md):记录拼图入口只填写画面描述、首关名默认作品名、作品描述和标签初始为空、AI 生成 6 个作品标签以及发布前校验的落地规则。 - [PUZZLE_LEADERBOARD_FRONTEND_LEVEL_AND_RPG_COMING_SOON_2026-04-30.md](./PUZZLE_LEADERBOARD_FRONTEND_LEVEL_AND_RPG_COMING_SOON_2026-04-30.md):记录拼图第二关排行榜提交以前端当前关卡为准、不被 SpacetimeDB 旧 run 快照误杀,以及 RPG 创作入口改为敬请期待的落地边界。 - [PUZZLE_NEXT_LEVEL_AND_SIMILAR_WORK_HANDOFF_2026-04-30.md](./PUZZLE_NEXT_LEVEL_AND_SIMILAR_WORK_HANDOFF_2026-04-30.md):记录拼图通关后优先同作品下一关、无下一关时按 RPG/build 标签语义相似度返回三个候选作品,并在跨作品时只切换到候选作品第 1 张图、运行时关卡序号继续累进的落地规则。 - [PUZZLE_FAILURE_EXTENSION_AND_SAVE_ARCHIVE_2026-05-01.md](./PUZZLE_FAILURE_EXTENSION_AND_SAVE_ARCHIVE_2026-05-01.md):记录拼图失败后重新开始/付费续时,以及进入作品与过关后同步存档页投影的落地规则。 diff --git a/packages/shared/src/contracts/runtime.ts b/packages/shared/src/contracts/runtime.ts index d482e6ca..3a3b7f7e 100644 --- a/packages/shared/src/contracts/runtime.ts +++ b/packages/shared/src/contracts/runtime.ts @@ -253,6 +253,38 @@ export type CustomWorldThemeMode = export type CustomWorldProfileRecord = JsonObject & { id?: string; + openingCg?: CustomWorldOpeningCgProfile | null; +}; + +export type CustomWorldOpeningCgStatus = + | 'not_started' + | 'storyboard_generating' + | 'video_generating' + | 'ready' + | 'failed'; + +export type CustomWorldOpeningCgProfile = { + id: string; + status: CustomWorldOpeningCgStatus; + storyboardImageSrc?: string | null; + storyboardAssetId?: string | null; + videoSrc?: string | null; + videoAssetId?: string | null; + posterImageSrc?: string | null; + posterAssetId?: string | null; + storyboardPrompt?: string | null; + videoPrompt?: string | null; + imageModel: 'gpt-image-2'; + videoModel: string; + aspectRatio: '16:9'; + imageSize: '2k'; + videoResolution: '480p'; + durationSeconds: 15; + pointCost: 80; + estimatedWaitMinutes: 10; + generatedAt?: string | null; + updatedAt: string; + errorMessage?: string | null; }; export type CustomWorldLibraryEntry = { diff --git a/server-rs/crates/api-server/src/app.rs b/server-rs/crates/api-server/src/app.rs index bd9bf73d..ae7c0e31 100644 --- a/server-rs/crates/api-server/src/app.rs +++ b/server-rs/crates/api-server/src/app.rs @@ -61,8 +61,8 @@ use crate::{ }, custom_world_ai::{ generate_custom_world_cover_image, generate_custom_world_entity, - generate_custom_world_scene_image, generate_custom_world_scene_npc, - upload_custom_world_cover_image, + generate_custom_world_opening_cg, generate_custom_world_scene_image, + generate_custom_world_scene_npc, upload_custom_world_cover_image, }, error_middleware::normalize_error_response, health::health_check, @@ -991,6 +991,13 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) + .route( + "/api/runtime/custom-world/opening-cg", + post(generate_custom_world_opening_cg).route_layer(middleware::from_fn_with_state( + state.clone(), + require_bearer_auth, + )), + ) .route( "/api/profile/browse-history", get(get_runtime_browse_history)