补齐订单详情V4总览接口

This commit is contained in:
andy
2026-07-20 14:38:09 +07:00
parent b50e004b06
commit 8f893991dd
14 changed files with 659 additions and 40 deletions

View File

@@ -512,6 +512,111 @@ class ReservationFrontendQueryControllerTest {
.andExpect(jsonPath("$.v4_order_tasks[1].source_received_at").value("2026-07-08T09:00:00Z"));
}
@Test
void shouldReturnOrderDetailWithV4OverviewNextActionAndCardTimeline() throws Exception {
SourceMessageCaptureResult source = captureSourceMessage(
"mail-frontend-order-detail-v4-overview-001",
"Frontend V4 Overview",
Instant.parse("2026-07-08T11:00:00Z"));
Long orderId = 930000000000005001L;
insertActiveGroupOrder(orderId, source.inboxId(), "GRP-FRONTEND-V4-OVERVIEW-001");
ReservationV4OrderTaskSnapshot orderTask = insertV4OrderTask(
930000000000005101L,
source,
orderId,
"order-overview",
"GRP-FRONTEND-V4-OVERVIEW-001",
Instant.parse("2026-07-08T11:00:00Z"),
HOTEL_ID);
insertV4TaskCard(orderTask, ReservationV4CardType.SOURCE_MESSAGE_DISPLAY.name(), null, 0, 10,
ReservationV4CardStatus.READONLY.name(), null, "{}");
ReservationV4TaskCardSnapshot basicCard = insertV4TaskCard(
orderTask, ReservationV4CardType.BASIC_INFORMATION.name(), null, 0, 20,
ReservationV4CardStatus.CONFIRMED.name(), null, """
{"basic_information":{"account_code":"QBD_TRAVEL"}}
""");
confirmV4TaskCard(basicCard.id(), """
{
"basic_information": {
"account_code": "QBD_TRAVEL",
"account_name": "Q.B.D. TRAVEL GROUP CO., LTD",
"market_code": "LEISURE",
"source_code": "TRAVEL_AGENT"
}
}
""", "frontend-query-admin", Instant.parse("2026-07-08T11:10:00Z"));
ReservationV4TaskCardSnapshot roomCard = insertV4TaskCard(
orderTask, ReservationV4CardType.ROOM_INFORMATION.name(), "NEW_BOOKING", 1, 30,
ReservationV4CardStatus.CONFIRMED.name(), null, "{}");
confirmV4TaskCard(roomCard.id(), """
{
"business_fields": {
"arrival_date": "2026-07-26",
"departure_date": "2026-07-29",
"rate_code": "BAR",
"room_items": [
{"room_type_code": "RM1", "room_count": 2},
{"room_type_code": "RM2", "room_count": 1}
]
}
}
""", "frontend-query-admin", Instant.parse("2026-07-08T11:20:00Z"));
insertV4TaskCard(orderTask, ReservationV4CardType.ROOM_INFORMATION.name(), "UPDATE_BOOKING", 2, 40,
ReservationV4CardStatus.PENDING_CONFIRM.name(), null, """
{
"business_fields": {
"arrival_date": "2099-01-01",
"departure_date": "2099-01-02",
"rate_code": "SHOULD_NOT_BE_ORDER_FACT",
"room_items": [
{"room_type_code": "SHOULD_NOT_APPEAR", "room_count": 99}
]
}
}
""");
ReservationV4TaskCardSnapshot paymentCard = insertV4TaskCard(
orderTask, ReservationV4CardType.PAYMENT.name(), "PAYMENT", 3, 60,
ReservationV4CardStatus.REVIEW_REQUIRED.name(), "PENDING", "{}");
performAuthorized(mockMvc, adminToken(), get("/api/reservation/orders/{orderId}", orderId)
.param("hotel_id", HOTEL_ID)
.param("include_tasks", "true"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.order_overview.account_code").value("QBD_TRAVEL"))
.andExpect(jsonPath("$.order_overview.account_name").value("Q.B.D. TRAVEL GROUP CO., LTD"))
.andExpect(jsonPath("$.order_overview.market_code").value("LEISURE"))
.andExpect(jsonPath("$.order_overview.source_code").value("TRAVEL_AGENT"))
.andExpect(jsonPath("$.order_overview.arrival_date").value("2026-07-26"))
.andExpect(jsonPath("$.order_overview.departure_date").value("2026-07-29"))
.andExpect(jsonPath("$.order_overview.rate_code").value("BAR"))
.andExpect(jsonPath("$.order_overview.room_items.length()").value(2))
.andExpect(jsonPath("$.order_overview.room_items[0].room_type_code").value("RM1"))
.andExpect(jsonPath("$.order_overview.room_items[0].room_count").value(2))
.andExpect(jsonPath("$.order_overview.room_items[?(@.room_type_code=='SHOULD_NOT_APPEAR')]")
.isEmpty())
.andExpect(jsonPath("$.order_overview.payment_card_status").value("REVIEW_REQUIRED"))
.andExpect(jsonPath("$.order_overview.latest_confirmed_at").value("2026-07-08T11:20:00Z"))
.andExpect(jsonPath("$.next_v4_action.order_task_id").value(orderTask.id().toString()))
.andExpect(jsonPath("$.next_v4_action.card_id").value(paymentCard.id().toString()))
.andExpect(jsonPath("$.next_v4_action.action_type").value("REVIEW"))
.andExpect(jsonPath("$.next_v4_action.action_status").value("REVIEW_REQUIRED"))
.andExpect(jsonPath("$.next_v4_action.open_order_task_count").value(1))
.andExpect(jsonPath("$.related_source_messages.length()").value(1))
.andExpect(jsonPath("$.related_source_messages[0].source_message_id").value(source.inboxId().toString()))
.andExpect(jsonPath("$.v4_order_tasks[0].cards.length()").value(5))
.andExpect(jsonPath("$.v4_order_tasks[0].cards[1].card_id").value(basicCard.id().toString()))
.andExpect(jsonPath("$.v4_order_tasks[0].cards[1].card_type").value("BASIC_INFORMATION"))
.andExpect(jsonPath("$.v4_order_tasks[0].cards[1].card_status").value("CONFIRMED"))
.andExpect(jsonPath("$.v4_order_tasks[0].cards[1].confirmed_by").value("frontend-query-admin"))
.andExpect(jsonPath("$.v4_order_tasks[0].cards[1].confirmed_at").value("2026-07-08T11:10:00Z"))
.andExpect(jsonPath("$.v4_order_tasks[0].cards[1].ai_payload_json").doesNotExist())
.andExpect(jsonPath("$.v4_order_tasks[0].cards[1].display_payload_json").doesNotExist())
.andExpect(jsonPath("$.v4_order_tasks[0].cards[1].confirmed_payload_json").doesNotExist())
.andExpect(jsonPath("$.v4_order_tasks[0].cards[4].card_id").value(paymentCard.id().toString()))
.andExpect(jsonPath("$.v4_order_tasks[0].cards[4].card_status").value("REVIEW_REQUIRED"))
.andExpect(jsonPath("$.v4_order_tasks[0].cards[4].review_status").value("PENDING"));
}
@Test
void shouldHideV4OrderTaskTimelineWhenIncludeTasksFalseAndIgnoreCrossHotelRows() throws Exception {
SourceMessageCaptureResult source = captureSourceMessage(
@@ -853,6 +958,20 @@ class ReservationFrontendQueryControllerTest {
""", Timestamp.valueOf(LocalDateTime.ofInstant(updatedAt, ZoneOffset.UTC)), orderTaskId, cardSortOrder);
}
private void confirmV4TaskCard(Long cardId, String confirmedPayloadJson, String confirmedBy, Instant confirmedAt) {
jdbcTemplate.update("""
UPDATE workflow_reservation_v4_task_card
SET confirmed_payload_json = ?,
confirmed_by = ?,
confirmed_at = ?,
updated_at = ?
WHERE id = ?
""", confirmedPayloadJson, confirmedBy,
Timestamp.valueOf(LocalDateTime.ofInstant(confirmedAt, ZoneOffset.UTC)),
Timestamp.valueOf(LocalDateTime.ofInstant(confirmedAt, ZoneOffset.UTC)),
cardId);
}
private ReservationV4OrderTaskSnapshot insertV4OrderTask(
Long aiBatchId,
SourceMessageCaptureResult source,