实现 M002 V3 字段矩阵 room_items 迁移

This commit is contained in:
andy
2026-07-12 00:40:12 +08:00
parent 155db63028
commit ab303eea39
13 changed files with 404 additions and 53 deletions

View File

@@ -140,7 +140,7 @@ class SuperAgentTaskResultControllerTest {
SELECT COUNT(*)
FROM workflow_reservation_task_card
WHERE ai_payload_json LIKE '%New Booking%'
AND field_contract_version = 'code-v1'
AND field_contract_version = '20260711-p0'
""", Long.class);
org.assertj.core.api.Assertions.assertThat(transitionCount).isEqualTo(1L);
@@ -1416,7 +1416,7 @@ class SuperAgentTaskResultControllerTest {
.andExpect(jsonPath("$.source_received_at").value(matchesPattern(UTC_INSTANT_PATTERN)))
.andExpect(jsonPath("$.external_conversation_id").value("thread-mail-detail-matrix-001"))
.andExpect(jsonPath("$.conversation_message_count").value(1))
.andExpect(jsonPath("$.field_contract_version").value("code-v1"))
.andExpect(jsonPath("$.field_contract_version").value("20260711-p0"))
.andExpect(jsonPath("$.fields.length()").value(18))
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].display_name")
.value(contains("Confirmation No.")))
@@ -1461,8 +1461,10 @@ class SuperAgentTaskResultControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.task_id").value(taskId))
.andExpect(jsonPath("$.task_status").value("PENDING_CONFIRM"))
.andExpect(jsonPath("$.draft_payload.field_values['extracted_fields.room_quantity']").value(3))
.andExpect(jsonPath("$.draft_payload.field_values['extracted_fields.pms_room_type_code']").value("RM3"));
.andExpect(jsonPath("$.draft_payload.field_values['extracted_fields.room_items.0.room_quantity']").value(3))
.andExpect(jsonPath("$.draft_payload.field_values['extracted_fields.room_items.0.pms_room_type_code']").value("RM3"))
.andExpect(jsonPath("$.draft_payload.legacy_field_values['extracted_fields.room_quantity']").value(3))
.andExpect(jsonPath("$.draft_payload.legacy_field_values['extracted_fields.pms_room_type_code']").value("RM3"));
String draftPayloadJson = jdbcTemplate.queryForObject("""
SELECT draft_payload_json
@@ -1474,6 +1476,7 @@ class SuperAgentTaskResultControllerTest {
FROM workflow_reservation_task_card
WHERE task_id = ?
""", String.class, Long.valueOf(taskId));
assertThat(draftPayloadJson).contains("\"extracted_fields.room_items.0.room_quantity\":3");
assertThat(draftPayloadJson).contains("\"extracted_fields.room_quantity\":3");
assertThat(aiPayloadJson).contains("\"room_quantity\":2");
}
@@ -1503,8 +1506,12 @@ class SuperAgentTaskResultControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.task_id").value(taskId))
.andExpect(jsonPath("$.task_status").value("READY"))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_quantity']").value(4))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.pms_room_type_code']").value("RM4"))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_items.0.room_quantity']").value(4))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_items.0.pms_room_type_code']").value("RM4"))
.andExpect(jsonPath("$.confirmed_payload.legacy_field_values['extracted_fields.room_quantity']").value(4))
.andExpect(jsonPath("$.confirmed_payload.legacy_field_values['extracted_fields.pms_room_type_code']").value("RM4"))
.andExpect(jsonPath("$.confirmed_payload.effective_payload.extracted_fields.room_items[0].room_quantity").value(4))
.andExpect(jsonPath("$.confirmed_payload.effective_payload.extracted_fields.room_items[0].pms_room_type_code").value("RM4"))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.arrival_date']").value("2026-08-01"))
.andExpect(jsonPath("$.opera_operations.length()").value(2))
.andExpect(jsonPath("$.opera_operations[0].operation_sequence").value(1))
@@ -1544,6 +1551,7 @@ class SuperAgentTaskResultControllerTest {
""", Long.class, Long.valueOf(taskId));
assertThat(taskStatus).isEqualTo("READY");
assertThat(confirmedAtCount).isEqualTo(1L);
assertThat(confirmedPayloadJson).contains("\"extracted_fields.room_items.0.room_quantity\":4");
assertThat(confirmedPayloadJson).contains("\"extracted_fields.room_quantity\":4");
assertThat(auditCount).isEqualTo(1L);
}
@@ -1744,7 +1752,8 @@ class SuperAgentTaskResultControllerTest {
"""))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error_code").value("TASK_FIELD_VALIDATION_FAILED"))
.andExpect(jsonPath("$.details[0]").value(containsString("extracted_fields.pms_room_type_code")));
.andExpect(jsonPath("$.details[0]")
.value(containsString("extracted_fields.room_items.0.pms_room_type_code")));
}
@Test
@@ -1854,10 +1863,16 @@ class SuperAgentTaskResultControllerTest {
.andExpect(jsonPath("$.review_resolution.field_overrides[0].field_pointer")
.value("/extracted_fields/pms_room_type_code"))
.andExpect(jsonPath("$.review_resolution.field_overrides[0].field_path")
.value("extracted_fields.room_items.0.pms_room_type_code"))
.andExpect(jsonPath("$.review_resolution.field_overrides[0].legacy_field_path")
.value("extracted_fields.pms_room_type_code"))
.andExpect(jsonPath("$.review_resolution.field_overrides[0].value").value("RM3"))
.andExpect(jsonPath("$.review_resolution.resolved_at").value(matchesPattern(UTC_INSTANT_PATTERN)))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.pms_room_type_code']")
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_items.0.pms_room_type_code']")
.value("RM3"))
.andExpect(jsonPath("$.confirmed_payload.legacy_field_values['extracted_fields.pms_room_type_code']")
.value("RM3"))
.andExpect(jsonPath("$.confirmed_payload.effective_payload.extracted_fields.room_items[0].pms_room_type_code")
.value("RM3"))
.andExpect(jsonPath("$.opera_operations.length()").value(2));

View File

@@ -1,8 +1,11 @@
package cn.nianxx.thhotel.workflows.reservation.control;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -348,6 +351,73 @@ class SuperAgentTaskResultP0FixtureRegressionTest {
assertThat(taskCount).isEqualTo(3L);
}
@Test
void shouldSaveDraftAndConfirmRoomItemsFromP0FieldPaths() throws Exception {
ObjectNode event = fixture("row_multiple_derived.json").path("message_events").get(0).deepCopy();
useEventGroupCode(event, "CHILD-ROW-FIELD-001");
((ObjectNode) event.path("extracted_fields")).put("parent_group_code", "PARENT-ROW-FIELD-001");
String externalId = "p0-room-items-field-migration-001";
captureSourceMessage(externalId);
MvcResult result = mockMvc.perform(signedPost(
businessRoot(externalId, event),
"nonce-p0-room-items-field-migration-create-001"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.items[0].route_code").value("R02_NEW_GROUP_BLOCK_NORMAL"))
.andReturn();
String taskId = com.jayway.jsonpath.JsonPath.read(
result.getResponse().getContentAsString(),
"$.items[0].task_id");
mockMvc.perform(get("/api/reservation/tasks/{taskId}", taskId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_quantity')].field_pointer")
.value(contains("/extracted_fields/room_items/0/room_quantity")))
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_type_raw')].field_pointer")
.value(contains("/extracted_fields/room_items/0/room_type_raw")))
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].field_pointer")
.value(contains("/extracted_fields/room_items/0/pms_room_type_code")))
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].value")
.value(contains("RM2")));
mockMvc.perform(put("/api/reservation/tasks/{taskId}/draft", taskId)
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"field_values": {
"extracted_fields.room_items.0.room_quantity": 4,
"/extracted_fields/room_items/0/pms_room_type_code": "RM3"
}
}
"""))
.andExpect(status().isOk())
.andExpect(jsonPath("$.draft_payload.field_values['extracted_fields.room_items.0.room_quantity']")
.value(4))
.andExpect(jsonPath("$.draft_payload.field_values['extracted_fields.room_items.0.pms_room_type_code']")
.value("RM3"));
mockMvc.perform(post("/api/reservation/tasks/{taskId}/confirm", taskId)
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"field_values": {
"extracted_fields.room_items.0.room_quantity": 5,
"extracted_fields.room_items.0.pms_room_type_code": "RM4"
}
}
"""))
.andExpect(status().isOk())
.andExpect(jsonPath("$.task_status").value("READY"))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_items.0.room_quantity']")
.value(5))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_items.0.pms_room_type_code']")
.value("RM4"))
.andExpect(jsonPath("$.confirmed_payload.effective_payload.extracted_fields.room_items[0].room_quantity")
.value(5))
.andExpect(jsonPath("$.confirmed_payload.effective_payload.extracted_fields.room_items[0].pms_room_type_code")
.value("RM4"));
}
@Test
void shouldFailClosedWhenV3BusinessEventMissesRequiredField() throws Exception {
ObjectNode event = fixture("row_multiple_derived.json").path("message_events").get(0).deepCopy();
@@ -417,6 +487,13 @@ class SuperAgentTaskResultP0FixtureRegressionTest {
result.getResponse().getContentAsString(),
"$.items[0].order_id");
mockMvc.perform(get("/api/reservation/tasks/{taskId}", taskId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].field_pointer")
.value(contains("/extracted_fields/room_items/0/pms_room_type_code")))
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_type_raw')].value")
.value(contains("SUITE")));
mockMvc.perform(post("/api/reservation/tasks/{taskId}/manual-review-resolutions", taskId)
.contentType(MediaType.APPLICATION_JSON)
.content("""
@@ -436,12 +513,14 @@ class SuperAgentTaskResultP0FixtureRegressionTest {
.andExpect(jsonPath("$.review_resolution.field_overrides[0].field_pointer")
.value("/extracted_fields/room_items/0/pms_room_type_code"))
.andExpect(jsonPath("$.review_resolution.field_overrides[0].field_path")
.value("extracted_fields.pms_room_type_code"))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_type']")
.value("extracted_fields.room_items.0.pms_room_type_code"))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_items.0.room_type_raw']")
.value("SUITE"))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_quantity']")
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_items.0.room_quantity']")
.value(2))
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.pms_room_type_code']")
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.room_items.0.pms_room_type_code']")
.value("SU1"))
.andExpect(jsonPath("$.confirmed_payload.effective_payload.extracted_fields.room_items[0].pms_room_type_code")
.value("SU1"));
Long sourceTaskCount = jdbcTemplate.queryForObject("""