diff --git a/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md b/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md index 47791407..f0973a34 100644 --- a/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md +++ b/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md @@ -149,7 +149,8 @@ RPG / 拼图等运行态存档仍以 `/api/profile/save-archives` 的后端列 3. 地块只调用一次 image2,输出一张 `5行*5列`、`1:1`、单一纯洋红 `#FF00FF` key 背景的主题地块图集;跳一跳地块常包含草地、花、雪、白石和云朵,后端透明化必须使用跳一跳专用洋红 key,不启用近白底扣除,也不清理非边缘连通的 key 色像素,避免把绿色或白色主体误扣;后处理必须对边缘连通 key 色做容差清理、去彩边 defringe 和底部残影清理,主体图不得自带洋红阴影、紫色底边、粉色脏边、彩色光晕或发光底边,运行态阴影统一由 DOM 绘制;地块造型提示词要求以主题物体本身外轮廓为准,允许苹果近似圆形、香蕉近似长条或长方形、西瓜近似扇形等自然差异,只统一单格规格、安全留白、正面30度视角和 2D/2.5D 手绘风格包装;所有地块素材必须保持统一正面30度视角,相机位于物体正前方略高位置、镜头向下约30度,必须看到清晰正面、侧壁、下沿、明显自身厚度和少量上表面,主体正面或侧壁可见面积必须接近或大于顶面面积,顶面只能作为辅助可见面;水果主题需要明确要求橙瓣看到橙皮正面外侧和果肉厚度、椰子看到壳的正面侧壁和切口厚度、浆果不能只是从上往下看的圆形球顶;避免生成纯俯视、正上方俯拍、鸟瞰地图块、平铺俯拍、圆形顶视图或扁平图标;主题物体本身必须是唯一可落脚体,只能用自身切面、边缘厚度、花瓣层或果皮边表现承重,禁止在主题物体下方额外垫石台、土墩、木板、圆台、托盘、岛屿底座或通用地板;前端和后端默认 `tilePrompt` 都必须使用“正面30度视角主题物体图集,物体本身作为跳跃落点”的口径,不再提交“平台素材 / 跳台 / 地块 / 地砖”等会把模型拉回通用平台造型的词,后端生成前也会清洗旧草稿遗留的这些词; 4. 背景底图同样由 image2 生成,复用现有 `coverComposite` / `coverImageSrc` 作为运行态背景读写字段,OSS 槽位固定为 `background/image.png`,不新增 SpacetimeDB 字段;提示词必须严格以用户主题关键词为背景主题,结构以左右两侧氛围为主,中央纵轴 1/2 区域保持少元素、简洁、可读且有纵深感,两侧允许更强立体层次和行进感;背景只作为底图,禁止生成跳板、地块、落脚物、角色、UI、文字、路径箭头或海报排版; 5. 后端按从上到下、从左到右均匀切分为 `tile-01` 到 `tile-25` 的透明 PNG,每个切片必须使用唯一 slot/path 持久化,不能按重复的 `tileType` 复用槽位; -6. 结果页只展示陶泥儿 logo 透明角色预览、地块池预览和首屏 3 地块预览;不再提供旧角色图生成槽。 +6. 结果页只展示陶泥儿 logo 透明角色预览、地块池预览和首屏 3 地块预览;不再提供旧角色图生成槽; +7. 前端跳一跳创作 client 的创建会话与执行生成动作请求都必须使用 20 分钟等待窗口,避免背景底图、地块图集、切片、抠图和 OSS 写入仍在后端执行时被共创会话默认 15 秒超时中断。 运行态规则真相必须沉到 `module-jump-hop`,前端只做拖拽蓄力、角色位移、投影和落地反馈。失败、成功跳跃次数、游戏时长冻结、运行态快照和发布作品状态以后端为准。v1 不保留公开 combo / perfect / 通关语义,旧 `score` 兼容映射为成功跳跃次数。公开列表应走 `jump_hop_gallery_card_view` 订阅缓存,不要每次 HTTP 请求调用 procedure 组装全量列表。 diff --git a/src/services/jump-hop/jumpHopClient.test.ts b/src/services/jump-hop/jumpHopClient.test.ts new file mode 100644 index 00000000..7f284fc0 --- /dev/null +++ b/src/services/jump-hop/jumpHopClient.test.ts @@ -0,0 +1,39 @@ +import { beforeEach, expect, test, vi } from 'vitest'; + +const requestJsonMock = vi.hoisted(() => vi.fn()); + +const { createCreationAgentClientMock } = vi.hoisted(() => ({ + createCreationAgentClientMock: vi.fn(), +})); + +vi.mock('../creation-agent', () => ({ + createCreationAgentClient: createCreationAgentClientMock, +})); + +vi.mock('../apiClient', () => ({ + requestJson: requestJsonMock, +})); + +beforeEach(() => { + vi.resetModules(); + createCreationAgentClientMock.mockReset(); + createCreationAgentClientMock.mockReturnValue({ + createSession: vi.fn(), + getSession: vi.fn(), + sendMessage: vi.fn(), + streamMessage: vi.fn(), + executeAction: vi.fn(), + }); + requestJsonMock.mockReset(); +}); + +test('jump hop creation keeps image2 generation requests alive long enough', async () => { + await import('./jumpHopClient'); + + expect(createCreationAgentClientMock).toHaveBeenCalledWith( + expect.objectContaining({ + createSessionTimeoutMs: 20 * 60 * 1000, + executeActionTimeoutMs: 20 * 60 * 1000, + }), + ); +}); diff --git a/src/services/jump-hop/jumpHopClient.ts b/src/services/jump-hop/jumpHopClient.ts index 779c415a..aeea825e 100644 --- a/src/services/jump-hop/jumpHopClient.ts +++ b/src/services/jump-hop/jumpHopClient.ts @@ -31,6 +31,8 @@ import { const JUMP_HOP_API_BASE = '/api/creation/jump-hop/sessions'; const JUMP_HOP_WORKS_API_BASE = '/api/creation/jump-hop/works'; const JUMP_HOP_RUNTIME_API_BASE = '/api/runtime/jump-hop'; +// 中文注释:跳一跳创作会等待背景图、25 格图集、切片和 OSS 写入,不能沿用共创会话默认 15 秒超时。 +const JUMP_HOP_GENERATION_TIMEOUT_MS = 20 * 60 * 1000; const JUMP_HOP_RUNTIME_READ_RETRY: ApiRetryOptions = { maxRetries: 1, baseDelayMs: 120, @@ -87,6 +89,8 @@ const jumpHopCreationClient = createCreationAgentClient< streamIncomplete: '跳一跳共创消息流式结果不完整', executeAction: '执行跳一跳共创操作失败', }, + createSessionTimeoutMs: JUMP_HOP_GENERATION_TIMEOUT_MS, + executeActionTimeoutMs: JUMP_HOP_GENERATION_TIMEOUT_MS, }); type FlattenedJumpHopWorkProfileResponse = Omit<