From b89d4940a99c1366a38b7076f40a8ae9d8c5e495 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Fri, 22 May 2026 16:09:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=8B=BC=E5=9B=BE?= =?UTF-8?q?=E8=8D=89=E7=A8=BF=E5=86=99=E5=85=A5=E6=AD=A5=E9=AA=A4=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .hermes/shared-memory/pitfalls.md | 8 +++++++ ...玩法创作】平å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md | 2 +- .../miniGameDraftGenerationProgress.test.ts | 23 +++++++++++++++++++ .../miniGameDraftGenerationProgress.ts | 12 +++++++++- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/.hermes/shared-memory/pitfalls.md b/.hermes/shared-memory/pitfalls.md index 04bfcdd0..25c1b2df 100644 --- a/.hermes/shared-memory/pitfalls.md +++ b/.hermes/shared-memory/pitfalls.md @@ -1024,6 +1024,14 @@ - 验è¯ï¼š`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx` 覆盖抓大鹅和拼图生æˆåŽè‡ªåŠ¨è¯•çŽ© / 返回结果页。 - å…³è”:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`ã€`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md`。 +## 拼图最åŽä¸€æ­¥åˆ° 100% 但ä¸å˜ç»¿ä¼˜å…ˆçœ‹é˜¶æ®µæ˜ å°„ + +- 现象:拼图è‰ç¨¿ç”Ÿæˆè·‘完所有步骤åŽï¼Œæ€»è¿›åº¦ä»åœåœ¨ 98%,最åŽä¸€æ­¥â€œå†™å…¥æ­£å¼è‰ç¨¿â€æ˜¾ç¤º 100% 但å¡ç‰‡ä¸å˜ç»¿ï¼Œè§†è§‰ä¸Šåƒè¿˜åœ¨è¿›è¡Œä¸­ã€‚ +- åŽŸå› ï¼šè¿›åº¦æ¡æ€»è¿›åº¦åˆ»æ„ä¿ç•™ 98% 作为未收到 action 回包å‰çš„安全余é‡ï¼Œä½†æœ€åŽä¸€æ­¥çš„ç»¿è‰²å®Œæˆæ€åªçœ‹æ­¥éª¤çжæ€ï¼›å¦‚果时间轴已ç»è·‘到 `puzzle-select-image` 末尾å´è¿˜æ²¡æ”¶åˆ° `ready` 回包,最åŽä¸€æ­¥ä¼šä¸€ç›´ä¿æŒ active。 +- 处ç†ï¼š`buildMiniGameDraftGenerationProgress` 需è¦åœ¨æ‹¼å›¾æœ€åŽä¸€æ­¥æ—¶ï¼ŒæŠŠâ€œé¢„计写入时长已耗尽â€å•独判为 completed,é¿å…出现“进行中 100%â€ã€‚ +- 验è¯ï¼š`npm test -- src/services/miniGameDraftGenerationProgress.test.ts`。 +- å…³è”:`src/services/miniGameDraftGenerationProgress.ts`ã€`src/services/miniGameDraftGenerationProgress.test.ts`ã€`docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md`。 + ## 微信支付回调验签ä¸è¦ç”¨å•†æˆ·ç§é’¥ - 现象:微信å°ç¨‹åºæ”¯ä»˜ä¸‹å•能返回 `prepay_id`,但真实支付通知验签失败,或者本地实现误把商户 API ç§é’¥å½“作回调验签 key。 diff --git a/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md b/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md index dec72bac..ad3b3c28 100644 --- a/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md +++ b/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md @@ -79,7 +79,7 @@ RPG / 拼图等è¿è¡Œæ€å­˜æ¡£é€‰æ‹©å…¥å£ç»Ÿä¸€åœ¨ä¸ªäººä¸­å¿ƒ `常用功能 > - 支æŒç”»é¢æè¿°ç”Ÿå›¾ã€å¤šå‚考图生图ã€ä¸Šä¼ æˆ–历å²ç”Ÿæˆä¸»å›¾åŽ AI é‡ç»˜ã€ä¸Šä¼ æˆ–历å²ç”Ÿæˆä¸»å›¾åŽä¸é‡ç»˜ï¼›ä¸»é“¾è¦æ±‚æµè§ˆå™¨å…ˆç» `/api/assets/direct-upload-tickets` ç›´ä¼  OSS 并确认 `asset_object`,创作 action åªæäº¤ `referenceImageAssetObjectId(s)`,由åŽç«¯æ ¡éªŒ owner / bucket / kind / MIME / size åŽç­¾å‘ OSS åªè¯» URL 并下载为 VectorEngine `/v1/images/edits` çš„ multipart `image` part。本地上传 Data URL ä¸ŽåŽ†å² `/generated-*` 图片路径仅ä¿ç•™ä¸ºæ—§è‰ç¨¿ã€æ—§å…¥å£æˆ–未è¿ç§»å®¢æˆ·ç«¯çš„兼容输入;关闭 AI é‡ç»˜æ—¶ï¼ŒåŽç«¯ç»Ÿä¸€è§£æžä¸ºé¦–关或当å‰å…³å¡æ­£å¼å›¾åŽå†æŒä¹…化,ä¸è°ƒç”¨ç¬¬ä¸€æ®µæ‹¼å›¾é¦–图生æˆã€‚ - è‰ç¨¿ç”Ÿæˆä¼šå…ˆæŒä¹…化 `generationStatus=generating` çš„ä½œå“æ‘˜è¦ï¼Œç”Ÿæˆå®Œæˆå¹¶å›žå†™å…³å¡æ‹¼å›¾ç”»é¢ã€å…³å¡ç”»é¢å‚考图ã€UI spritesheet 和关å¡èƒŒæ™¯å›¾åŽå†å˜ä¸º `ready`;当å‰ä¸è‡ªåŠ¨ç”ŸæˆèƒŒæ™¯éŸ³ä¹ã€‚生æˆé¡µè¿›åº¦ä¸å†æŒ‰å›ºå®š 5 分钟展示,而按实际开始时间和当å‰è·¯å¾„çš„åˆ†æ­¥éª¤é¢„è®¡æ—¶é•¿æŽ¨è¿›ï¼›ä»»ä¸€åŒæ­¥ action 回包到达时立å³ä»¥çœŸå®žå®Œæˆ/失败结果冻结进度。 - ä½œå“æž¶æ‹¼å›¾è‰ç¨¿çš„“生æˆä¸­â€é®ç½©åªè¡¨ç¤ºåˆå§‹è‰ç¨¿è¿˜æ²¡æœ‰å¯æŸ¥çœ‹ç»“果;åªè¦ä½œå“摘è¦ã€é¦–å…³å°é¢æˆ–任一关å¡å€™é€‰å›¾å·²ç»å¯ç”¨ï¼ŒåŽç»­ UI 背景é‡ç”Ÿæˆå’Œè¿½åŠ å…³å¡ç”Ÿå›¾éƒ½å¿…é¡»ä½œä¸ºç»“æžœé¡µå±€éƒ¨ç”Ÿæˆæ€å¤„ç†ï¼Œä¸èƒ½é˜»æ­¢æ‰“å¼€è‰ç¨¿ç»“果页。 -- 拼图è‰ç¨¿ç¼–译是长耗时 action,å‰ç«¯ action 请求默认等待 `1_800_000ms`(30 分钟)且ä¸è‡ªåЍé‡è¯•ã€‚æ¯æ¬¡ `gpt-image-2` 调用的预期用时按 90 秒计算;完整 AI é‡ç»˜è·¯å¾„为 `编译首关è‰ç¨¿` 8 ç§’ã€`生æˆå…³å¡åç§°` 10 ç§’ã€`ç”Ÿæˆæ‹¼å›¾é¦–图` 90 ç§’ã€`生æˆå…³å¡ç”»é¢` 90 ç§’ã€`生æˆUI与背景` 90 ç§’ã€`写入正å¼è‰ç¨¿` 10 秒,åˆè®¡çº¦ 298 秒。上传图且关闭 AI é‡ç»˜æ—¶å¿…须跳过 `ç”Ÿæˆæ‹¼å›¾é¦–图`,直接进入 `生æˆå…³å¡ç”»é¢` å’Œ `生æˆUI与背景`,åˆè®¡çº¦ 208 秒。生æˆé¡µæ¢å¤æ—¶å¿…é¡»æ²¿ç”¨ä½œå“æ‘˜è¦ `updatedAt` 作为原始 `startedAtMs`,失败/å®Œæˆæ€ç”¨ `finishedAtMs` 冻结耗时,ä¸èƒ½åœ¨é”屿ˆ–返回è‰ç¨¿é¡µåŽé‡æ–°ä»Ž 0 计时。 +- 拼图è‰ç¨¿ç¼–译是长耗时 action,å‰ç«¯ action 请求默认等待 `1_800_000ms`(30 分钟)且ä¸è‡ªåЍé‡è¯•ã€‚æ¯æ¬¡ `gpt-image-2` 调用的预期用时按 90 秒计算;完整 AI é‡ç»˜è·¯å¾„为 `编译首关è‰ç¨¿` 8 ç§’ã€`生æˆå…³å¡åç§°` 10 ç§’ã€`ç”Ÿæˆæ‹¼å›¾é¦–图` 90 ç§’ã€`生æˆå…³å¡ç”»é¢` 90 ç§’ã€`生æˆUI与背景` 90 ç§’ã€`写入正å¼è‰ç¨¿` 10 秒,åˆè®¡çº¦ 298 秒。上传图且关闭 AI é‡ç»˜æ—¶å¿…须跳过 `ç”Ÿæˆæ‹¼å›¾é¦–图`,直接进入 `生æˆå…³å¡ç”»é¢` å’Œ `生æˆUI与背景`,åˆè®¡çº¦ 208 秒。生æˆé¡µæ¢å¤æ—¶å¿…é¡»æ²¿ç”¨ä½œå“æ‘˜è¦ `updatedAt` 作为原始 `startedAtMs`,失败/å®Œæˆæ€ç”¨ `finishedAtMs` 冻结耗时,ä¸èƒ½åœ¨é”屿ˆ–返回è‰ç¨¿é¡µåŽé‡æ–°ä»Ž 0 计时。未收到 action 回包å‰ï¼Œæ€»è¿›åº¦ä»æœ€å¤šåœåœ¨ 98%,但当预计写入时长耗尽且ä»å¤„于 `写入正å¼è‰ç¨¿` 时,该步骤自身应显示已完æˆï¼Œä¸èƒ½å‡ºçŽ°â€œè¿›è¡Œä¸­ 100%â€ã€‚ - è‹¥æµè§ˆå™¨é”å±ã€æ¯å±æˆ–网络切æ¢å¯¼è‡´ compile 请求失败,å‰ç«¯åœ¨æ ‡è®°å¤±è´¥å‰å¿…须先å¤è¯» `getPuzzleAgentSession(sessionId)`ï¼›åªæœ‰æœ€æ–° session ä»ç¼º `draft.coverImageSrc`ã€é¦–å…³ `coverImageSrc` 或候选图时æ‰å±•示失败,å¤è¯»åˆ°å·²ç”Ÿæˆè‰ç¨¿æ—¶æŒ‰æˆåŠŸæ”¶å°¾ã€åˆ·æ–°ä½œå“架并继续自动试玩/结果页链路。 - 拼图å‚考图 AI é‡ç»˜èµ° VectorEngine `/v1/images/edits`;无å‚考图时走 `/v1/images/generations`。两者模型都使用 `gpt-image-2`,å‚考图由åŽç«¯ä½œä¸º multipart `image` part 传入编辑接å£ã€‚ - æ¯æ¬¡æ–°å»ºå…³å¡ç”Ÿæˆæˆ–釿–°ç”Ÿæˆå…³å¡å›¾éƒ½å¿…须由 `api-server` 串起当å‰å…³å¡èµ„产包:AI é‡ç»˜å¼€å¯æ—¶ç¬¬ä¸€æ®µæ²¿ç”¨è‰ç¨¿ç”Ÿæˆç¬¬ä¸€å…³çš„æ‹¼å›¾ä¸»å›¾æç¤ºè¯é…置和模型 / 尺寸 / å‚è€ƒå›¾è§„åˆ™ç”Ÿæˆ `coverImageSrc/coverAssetId` ä½œä¸ºå…³å¡æ‹¼å›¾ç”»é¢å’Œç»“果页预览图,æç¤ºè¯æ¥æºåŒæ ·æŒ‰æ˜¾å¼ç”»é¢æè¿°ã€å…³å¡ç”»é¢æè¿°ã€è‰ç¨¿æ‘˜è¦é¡ºåºå›žé€€ï¼Œä¸”å›ºå®šè¦æ±‚è¾“å‡ºç”»é¢æ¯”例为 `1:1`;上传图且关闭 AI é‡ç»˜æ—¶è·³è¿‡è¿™ä¸€æ®µï¼ŒæŠŠä¸Šä¼ å›¾æˆ–历å²å›¾æŒä¹…化为 `sourceType=uploaded` 的正å¼å€™é€‰ã€‚éšåŽç”¨æ­£å¼å€™é€‰å›¾ä½œä¸ºå‚考,`9:16` 生æˆå®Œæ•´æ‹¼å›¾æ¸¸æˆå…³å¡ç”»é¢å¹¶å†™å…¥ `levelSceneImageSrc/levelSceneImageObjectKey`,æç¤ºè¯å¿…é¡»è¦æ±‚é“具按钮上ä¸è¦æ˜¾ç¤ºæ¬¡æ•°æ ‡æ³¨ï¼Œä¸”返回按钮和设置按钮æ—ç¦æ­¢æ ‡æ³¨æ–‡å­—ï¼›UI spritesheet 与关å¡çº¯èƒŒæ™¯åœ¨å…³å¡ç”»é¢å®ŒæˆåŽå¹¶å‘生æˆï¼Œspritesheet 用 `1:1`ã€`1k` 先生æˆçº¯ç»¿è‰²ç»¿å¹•背景图,åŽç«¯ä¸Šä¼  OSS å‰å¿…须把绿幕扣æˆé€æ˜Ž PNG,å†å†™å…¥ `uiSpritesheetImageSrc/uiSpritesheetImageObjectKey`,按钮顺åºå›ºå®šä¸ºè¿”回ã€è®¾ç½®ã€ä¸‹ä¸€å…³ã€æç¤ºã€åŽŸå›¾ã€å†»ç»“,按钮素æè‡ªèº«ä¿ç•™å¯¹åº”中文文字,返回和设置按钮ä¸å¾—é¢å¤–生æˆç™½è‰²å¤–圈ã€ç™½åº•圆环或浮雕外框;纯背景用 `9:16`ã€`1k` 写入 `levelBackgroundImageSrc/levelBackgroundImageObjectKey`,æç¤ºè¯å¿…须包å«â€œç¦æ­¢åœ¨èƒŒæ™¯ä¸­å‡ºçŽ°äººåƒæˆ–和拼图画é¢ä¸­ä¸»ä½“一致的内容â€ã€‚è¿è¡Œæ€ä¸ç›´æŽ¥ä½¿ç”¨ç¬¬äºŒæ®µå®Œæ•´å…³å¡ç”»é¢ï¼Œä½†å¿…é¡»æŒä¹…化它用于追踪和åŽç»­å†ç”Ÿæˆã€‚结果页局部关å¡ç”Ÿæˆè¿›åº¦æŒ‰ AI é‡ç»˜å¼€å¯çº¦ 270 ç§’ã€å…³é—­ AI é‡ç»˜çº¦ 180 秒展示。 diff --git a/src/services/miniGameDraftGenerationProgress.test.ts b/src/services/miniGameDraftGenerationProgress.test.ts index 75a08bc7..0172b366 100644 --- a/src/services/miniGameDraftGenerationProgress.test.ts +++ b/src/services/miniGameDraftGenerationProgress.test.ts @@ -70,6 +70,25 @@ describe('miniGameDraftGenerationProgress', () => { expect(writeBackProgress?.steps[5]?.status).toBe('active'); }); + test('puzzle write-back step turns completed once rounded progress reaches 100%', () => { + const state: MiniGameDraftGenerationState = { + kind: 'puzzle', + phase: 'compile', + startedAtMs: 1_000, + completedAssetCount: 0, + totalAssetCount: 0, + error: null, + }; + + const progress = buildMiniGameDraftGenerationProgress(state, 298_950); + + expect(progress?.phaseId).toBe('puzzle-select-image'); + expect(progress?.overallProgress).toBe(98); + expect(progress?.estimatedRemainingMs).toBe(50); + expect(progress?.steps[5]?.completed).toBe(1); + expect(progress?.steps[5]?.status).toBe('completed'); + }); + test('puzzle direct upload generation skips the first image generation step', () => { const state: MiniGameDraftGenerationState = { kind: 'puzzle', @@ -116,6 +135,10 @@ describe('miniGameDraftGenerationProgress', () => { expect(progress?.overallProgress).toBe(98); expect(progress?.estimatedRemainingMs).toBe(0); expect(progress?.steps[5]?.completed).toBe(1); + expect(progress?.steps[5]?.status).toBe('completed'); + expect(progress?.steps.every((step) => step.status === 'completed')).toBe( + true, + ); }); test('puzzle ready copy points to result page work info completion', () => { diff --git a/src/services/miniGameDraftGenerationProgress.ts b/src/services/miniGameDraftGenerationProgress.ts index d21e7dc6..9d0060fe 100644 --- a/src/services/miniGameDraftGenerationProgress.ts +++ b/src/services/miniGameDraftGenerationProgress.ts @@ -484,7 +484,17 @@ function buildMiniGameProgressSteps( activeStepProgressRatio: number, ) { return steps.map((step, index) => { - const isCompleted = state.phase === 'ready' || index < activeStepIndex; + // 中文注释:拼图è‰ç¨¿ç¼–译的 action 回包æ‰ä»£è¡¨å¯è¿›å…¥ç»“果页; + // 但预计写入时长已耗尽时,最åŽä¸€æ­¥è‡ªèº«åº”呈现已完æˆï¼Œé¿å…出现“进行中 100%â€ã€‚ + const isPuzzleWriteStepCompleted = + state.kind === 'puzzle' && + state.phase !== 'failed' && + step.id === 'puzzle-select-image' && + clampProgress(activeStepProgressRatio * 100) >= 100; + const isCompleted = + state.phase === 'ready' || + index < activeStepIndex || + isPuzzleWriteStepCompleted; const isActive = state.phase !== 'failed' && !isCompleted && index === activeStepIndex; const isAssetStep = step.id === state.phase && state.totalAssetCount > 0;