From 9bcbe8a3ac18874b28df73ec34d5c012210320a4 Mon Sep 17 00:00:00 2001 From: andy Date: Sat, 11 Jul 2026 14:34:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DV3=E7=A9=BA=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E6=A0=B9=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReservationAiTaskIntakeServiceImpl.java | 3 ++ .../SuperAgentTaskResultControllerTest.java | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationAiTaskIntakeServiceImpl.java b/server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationAiTaskIntakeServiceImpl.java index f6dc3db..242c287 100644 --- a/server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationAiTaskIntakeServiceImpl.java +++ b/server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationAiTaskIntakeServiceImpl.java @@ -226,6 +226,9 @@ public class ReservationAiTaskIntakeServiceImpl implements ReservationAiTaskInta JsonNode messageEvents = root.path("message_events"); JsonNode unhandledIntents = root.path("unhandled_current_intents"); int itemCount = messageEvents.size() + unhandledIntents.size(); + if (itemCount <= 0) { + throw error(HttpStatus.BAD_REQUEST, "TASK_RESULTS_EMPTY", "message_events 和 unhandled_current_intents 不能同时为空。"); + } String requestPayloadSha256 = sha256(rawBody == null ? "" : rawBody); String batchIdempotencyKey = sha256(BATCH_KEY_PREFIX + "|" + sourceMessageId + "|" + requestPayloadSha256); diff --git a/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/SuperAgentTaskResultControllerTest.java b/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/SuperAgentTaskResultControllerTest.java index 85b7c6d..bb6a8d6 100644 --- a/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/SuperAgentTaskResultControllerTest.java +++ b/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/SuperAgentTaskResultControllerTest.java @@ -805,6 +805,38 @@ class SuperAgentTaskResultControllerTest { assertThat(transitionCount).isEqualTo(1L); } + @Test + void shouldRejectEmptyV3BusinessRootWithoutPersistingBatch() throws Exception { + SourceMessageCaptureResult source = captureSourceMessage("mail-v3-empty-business-root-001"); + String body = """ + { + "source_message": { + "source_message_id": "mail-v3-empty-business-root-001", + "subject": "Empty business root", + "from": null, + "cc": [], + "received_at": null, + "source_channel": "Email" + }, + "message_events": [], + "case_candidates": [], + "extraction_warnings": [], + "unhandled_current_intents": [] + } + """; + + mockMvc.perform(signedPost(body, "nonce-v3-empty-business-root-001")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error_code").value("TASK_RESULTS_EMPTY")); + + Long batchCount = jdbcTemplate.queryForObject(""" + SELECT COUNT(*) + FROM workflow_reservation_ai_batch + WHERE source_message_id = ? + """, Long.class, source.inboxId()); + assertThat(batchCount).isZero(); + } + @Test void shouldPersistAdapterContractErrorWhenV3EventRouteCannotBeDerived() throws Exception { SourceMessageCaptureResult source = captureSourceMessage("mail-v3-adapter-error-001");