From 68f8b4fa865f2a64345f37732baf4983f3d5ecc5 Mon Sep 17 00:00:00 2001 From: lili Date: Thu, 23 Jul 2026 00:45:25 -0700 Subject: [PATCH] fix(aigc): bind callback artifacts before completion --- .../callback/DifyCallbackTxService.java | 10 +++++++++ .../callback/DifyCallbackServiceImplTest.java | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/service/callback/DifyCallbackTxService.java b/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/service/callback/DifyCallbackTxService.java index 733caed2..e864e288 100644 --- a/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/service/callback/DifyCallbackTxService.java +++ b/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/service/callback/DifyCallbackTxService.java @@ -8,6 +8,7 @@ import com.wanxiang.huijing.game.module.aigc.enums.FailureReasonEnum; import com.wanxiang.huijing.game.module.aigc.service.task.AigcTaskService; import com.wanxiang.huijing.game.module.aigc.service.task.ReadinessScorer; import com.wanxiang.huijing.game.module.project.api.ProjectVersionApi; +import com.wanxiang.huijing.game.module.project.dto.ProjectVersionBindArtifactReqDTO; import com.wanxiang.huijing.game.module.project.dto.ProjectVersionCreateForPackageReqDTO; import com.wanxiang.huijing.game.module.runtime.api.RuntimePackageApi; import com.wanxiang.huijing.game.module.runtime.dto.RuntimePackageStoreReqDTO; @@ -218,6 +219,15 @@ public class DifyCallbackTxService { log.info("[handleCallbackTx] 落包完成 taskId={}, traceId={}, versionId={}, pkgId={}, checksum={}, bundleSize={}", task.getId(), reqVO.getTraceId(), versionId, pkgId, packed.checksum(), packed.bundleSize()); + // runtime 落包成功后,在同一事务内把相同权威摘要绑定到版本;失败会回滚并阻止任务完成。 + ProjectVersionBindArtifactReqDTO artifactReq = new ProjectVersionBindArtifactReqDTO(); + artifactReq.setVersionId(versionId); + artifactReq.setChecksum(packed.checksum()); + artifactReq.setBundleSize(packed.bundleSize()); + projectVersionApi.bindRuntimeArtifact(artifactReq).getCheckedData(); + log.info("[handleCallbackTx] 版本摘要绑定完成 taskId={}, traceId={}, versionId={}, checksum={}", + task.getId(), reqVO.getTraceId(), versionId, packed.checksum()); + // ===== 步骤⑦:回填任务(既有幂等:置 status=2 + version_id + progress 100 + finishTime) ===== aigcTaskService.completeWithVersion(task.getId(), versionId); log.info("[handleCallbackTx] 回调 succeeded 三表写入链完成 taskId={}, traceId={}, versionId={}", diff --git a/game-cloud/game-module-aigc/game-module-aigc-server/src/test/java/com/wanxiang/huijing/game/module/aigc/service/callback/DifyCallbackServiceImplTest.java b/game-cloud/game-module-aigc/game-module-aigc-server/src/test/java/com/wanxiang/huijing/game/module/aigc/service/callback/DifyCallbackServiceImplTest.java index 3425e91a..cf7065bd 100644 --- a/game-cloud/game-module-aigc/game-module-aigc-server/src/test/java/com/wanxiang/huijing/game/module/aigc/service/callback/DifyCallbackServiceImplTest.java +++ b/game-cloud/game-module-aigc/game-module-aigc-server/src/test/java/com/wanxiang/huijing/game/module/aigc/service/callback/DifyCallbackServiceImplTest.java @@ -7,6 +7,7 @@ import com.wanxiang.huijing.game.module.aigc.enums.AigcTaskStatusEnum; import com.wanxiang.huijing.game.module.aigc.service.task.AigcTaskService; import com.wanxiang.huijing.game.module.community.api.CommunityNotifyApi; import com.wanxiang.huijing.game.module.project.api.ProjectVersionApi; +import com.wanxiang.huijing.game.module.project.dto.ProjectVersionBindArtifactReqDTO; import com.wanxiang.huijing.game.module.project.dto.ProjectVersionCreateForPackageReqDTO; import com.wanxiang.huijing.game.module.runtime.api.RuntimePackageApi; import com.wanxiang.huijing.game.module.runtime.dto.RuntimePackageStoreReqDTO; @@ -95,6 +96,9 @@ class DifyCallbackServiceImplTest extends BaseMockitoUnitTest { void setUpService() { // MockitoExtension 已完成 @InjectMocks 注入,此处组装外层(构造器注入内层真实例) callbackService = new DifyCallbackServiceImpl(txService); + lenient().when(projectVersionApi.bindRuntimeArtifact(any(ProjectVersionBindArtifactReqDTO.class))) + .thenReturn(CommonResult.success(Boolean.TRUE)); + } // ============================== 用例1:succeeded 全链 ============================== @@ -339,6 +343,24 @@ class DifyCallbackServiceImplTest extends BaseMockitoUnitTest { assertEquals("llm_error", last.getFailureReason()); verify(aigcTaskService, never()).completeWithVersion(anyLong(), anyLong()); } + @Test + void testHandleCallback_bindRuntimeArtifactError_compensatesBeforeTaskCompletion() { + // runtime 落包成功但 project 摘要回写失败时必须向上抛错,任务不得完成。 + when(aigcTaskMapper.selectByTraceId("aigc-trace-1")).thenAnswer(inv -> queuedTask()); + when(projectVersionApi.createForPackage(any(ProjectVersionCreateForPackageReqDTO.class))) + .thenReturn(CommonResult.success(2048L)); + when(runtimePackageApi.storeForVersion(any(RuntimePackageStoreReqDTO.class))) + .thenReturn(CommonResult.success(99L)); + when(projectVersionApi.bindRuntimeArtifact(any(ProjectVersionBindArtifactReqDTO.class))) + .thenThrow(new ServiceException(1_100_000_008, "版本摘要绑定失败(模拟)")); + + ServiceException ex = assertThrows(ServiceException.class, + () -> callbackService.handleCallback(succeededReq())); + + assertEquals(1_100_000_008, ex.getCode()); + verify(runtimePackageApi).storeForVersion(any(RuntimePackageStoreReqDTO.class)); + verify(aigcTaskService, never()).completeWithVersion(anyLong(), anyLong()); + } // ============================== 用例10:failureReason 非法 ==============================