实现前端P0订单任务查询接口

This commit is contained in:
andy
2026-07-08 14:18:57 +08:00
parent b58c6baafe
commit 936a5921b0
29 changed files with 1582 additions and 56 deletions

View File

@@ -0,0 +1,218 @@
package cn.nianxx.thhotel.workflows.reservation.control;
import static org.hamcrest.Matchers.nullValue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import cn.nianxx.thhotel.ThHotelApplication;
import cn.nianxx.thhotel.platform.message.common.request.CaptureSourceMessageCommand;
import cn.nianxx.thhotel.platform.message.common.result.SourceMessageCaptureResult;
import cn.nianxx.thhotel.platform.message.service.SourceMessageCaptureService;
import java.time.Instant;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
@SpringBootTest(classes = ThHotelApplication.class)
@AutoConfigureMockMvc
@ActiveProfiles("test")
class ReservationFrontendQueryControllerTest {
private static final String HOTEL_ID = "HOTEL-TEST";
@Autowired
private MockMvc mockMvc;
@Autowired
private SourceMessageCaptureService captureService;
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
void shouldReturnTaskWorkbenchListWithRealtimeAvailability() throws Exception {
SourceMessageCaptureResult source = captureSourceMessage(
"mail-frontend-task-list-001",
"Frontend Query List Smoke");
Long orderId = 930000000000000101L;
Long firstTaskId = 930000000000000301L;
Long secondTaskId = 930000000000000302L;
insertActiveGroupOrder(orderId, source.inboxId(), "GRP-FRONTEND-LIST-001");
insertTransition(930000000000000201L, source.inboxId(), 1, "GRP-FRONTEND-LIST-001",
"New Booking", "NEW_BOOKING", "NEW_BOOKING");
insertTransition(930000000000000202L, source.inboxId(), 2, "GRP-FRONTEND-LIST-001",
"Update Booking", "UPDATE_BOOKING", "UPDATE_BOOKING");
insertTask(firstTaskId, orderId, source.inboxId(), 930000000000000201L, "New Booking",
"NEW_BOOKING", "NEW_BOOKING", "PENDING_CONFIRM", 1);
insertTask(secondTaskId, orderId, source.inboxId(), 930000000000000202L, "Update Booking",
"UPDATE_BOOKING", "UPDATE_BOOKING", "PENDING_CONFIRM", 2);
mockMvc.perform(get("/api/reservation/tasks")
.param("hotel_id", HOTEL_ID)
.param("order_id", orderId.toString())
.param("page_num", "1")
.param("page_size", "20"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.items[0].task_id").value(firstTaskId.toString()))
.andExpect(jsonPath("$.items[0].order_id").value(orderId.toString()))
.andExpect(jsonPath("$.items[0].hotel_id").value(HOTEL_ID))
.andExpect(jsonPath("$.items[0].display_order_key").value("GRP-FRONTEND-LIST-001"))
.andExpect(jsonPath("$.items[0].task_type").value("NEW_BOOKING"))
.andExpect(jsonPath("$.items[0].task_status").value("PENDING_CONFIRM"))
.andExpect(jsonPath("$.items[0].queue_sequence").value(1))
.andExpect(jsonPath("$.items[0].queue_participation").value(true))
.andExpect(jsonPath("$.items[0].can_process").value(true))
.andExpect(jsonPath("$.items[0].readonly_reason_code").value("PROCESSABLE"))
.andExpect(jsonPath("$.items[0].source_message_id").value(source.inboxId().toString()))
.andExpect(jsonPath("$.items[0].source_subject").value("Frontend Query List Smoke"))
.andExpect(jsonPath("$.items[1].task_id").value(secondTaskId.toString()))
.andExpect(jsonPath("$.items[1].task_type").value("UPDATE_BOOKING"))
.andExpect(jsonPath("$.items[1].can_process").value(false))
.andExpect(jsonPath("$.items[1].readonly_reason_code").value("PREVIOUS_TASK_NOT_FINISHED"))
.andExpect(jsonPath("$.page.page_num").value(1))
.andExpect(jsonPath("$.page.page_size").value(20))
.andExpect(jsonPath("$.page.total").value(2));
mockMvc.perform(get("/api/reservation/tasks")
.param("hotel_id", HOTEL_ID)
.param("keyword", "Frontend Query List Smoke")
.param("page_num", "1")
.param("page_size", "20"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.items[0].source_subject").value("Frontend Query List Smoke"))
.andExpect(jsonPath("$.items[1].source_subject").value("Frontend Query List Smoke"))
.andExpect(jsonPath("$.page.total").value(2));
}
@Test
void shouldReturnOrderDetailWithTaskTimeline() throws Exception {
SourceMessageCaptureResult source = captureSourceMessage(
"mail-frontend-order-detail-001",
"Frontend Query Detail Smoke");
Long orderId = 930000000000000401L;
Long firstTaskId = 930000000000000601L;
Long secondTaskId = 930000000000000602L;
insertActiveGroupOrder(orderId, source.inboxId(), "GRP-FRONTEND-DETAIL-001");
insertTransition(930000000000000501L, source.inboxId(), 1, "GRP-FRONTEND-DETAIL-001",
"New Booking", "NEW_BOOKING", "NEW_BOOKING");
insertTransition(930000000000000502L, source.inboxId(), 2, "GRP-FRONTEND-DETAIL-001",
"Update Booking", "UPDATE_BOOKING", "UPDATE_BOOKING");
insertTask(firstTaskId, orderId, source.inboxId(), 930000000000000501L, "New Booking",
"NEW_BOOKING", "NEW_BOOKING", "COMPLETED", 1);
insertTask(secondTaskId, orderId, source.inboxId(), 930000000000000502L, "Update Booking",
"UPDATE_BOOKING", "UPDATE_BOOKING", "PENDING_CONFIRM", 2);
mockMvc.perform(get("/api/reservation/orders/{orderId}", orderId)
.param("hotel_id", HOTEL_ID)
.param("include_tasks", "true")
.param("include_source_summary", "true"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.order.order_id").value(orderId.toString()))
.andExpect(jsonPath("$.order.hotel_id").value(HOTEL_ID))
.andExpect(jsonPath("$.order.order_status").value("ACTIVE"))
.andExpect(jsonPath("$.order.temporary_order_no").value("TMP-" + orderId))
.andExpect(jsonPath("$.order.group_code").value("GRP-FRONTEND-DETAIL-001"))
.andExpect(jsonPath("$.order.confirmation_number").value(nullValue()))
.andExpect(jsonPath("$.order.display_name").value("GRP-FRONTEND-DETAIL-001"))
.andExpect(jsonPath("$.tasks[0].task_id").value(firstTaskId.toString()))
.andExpect(jsonPath("$.tasks[0].task_status").value("COMPLETED"))
.andExpect(jsonPath("$.tasks[0].readonly_reason_code").value("TASK_FINISHED"))
.andExpect(jsonPath("$.tasks[1].task_id").value(secondTaskId.toString()))
.andExpect(jsonPath("$.tasks[1].task_status").value("PENDING_CONFIRM"))
.andExpect(jsonPath("$.tasks[1].can_process").value(true))
.andExpect(jsonPath("$.tasks[1].readonly_reason_code").value("PROCESSABLE"))
.andExpect(jsonPath("$.warnings.length()").value(0));
mockMvc.perform(get("/api/reservation/orders/{orderId}", orderId)
.param("hotel_id", HOTEL_ID)
.param("include_tasks", "false"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.order.order_id").value(orderId.toString()))
.andExpect(jsonPath("$.tasks.length()").value(0));
}
private SourceMessageCaptureResult captureSourceMessage(String externalMessageId, String subject) {
return captureService.capture(new CaptureSourceMessageCommand(
HOTEL_ID,
"AGENTBUS",
"EMAIL",
externalMessageId,
"thread-" + externalMessageId,
"frame-" + externalMessageId,
"session-frontend-query",
Instant.parse("2026-07-08T08:00:00Z"),
"guest@example.test",
subject,
"Please handle booking message.",
"<html><body>Please handle booking message.</body></html>",
"{\"source\":{\"external_message_id\":\"" + externalMessageId + "\"}}",
"agentbus-outlook-v1",
List.of()
));
}
private void insertActiveGroupOrder(Long orderId, Long sourceMessageId, String groupCode) {
jdbcTemplate.update("""
INSERT INTO workflow_reservation_order (
id, hotel_id, order_key_type, order_business_key, active_business_key,
temporary_order_code, order_status, business_key_source, display_name,
source_message_id, version, created_at, updated_at
)
VALUES (?, ?, 'GROUP_CODE', ?, ?, ?, 'ACTIVE', 'AI_CANDIDATE', ?, ?, 0,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
""", orderId, HOTEL_ID, groupCode, groupCode, "TMP-" + orderId, groupCode, sourceMessageId);
}
private void insertTransition(
Long transitionId,
Long sourceMessageId,
Integer sourceEventIndex,
String groupCode,
String aiTaskType,
String systemTaskType,
String taskCardType) {
jdbcTemplate.update("""
INSERT INTO workflow_reservation_ai_transition (
id, hotel_id, batch_id, source_message_id, source_event_index, array_index,
execution_order, catalog_code, skill_id, result_type, ai_task_type,
system_task_type, task_card_type, task_subtype, current_or_history,
group_code, item_payload_sha256, item_idempotency_key, blocked_until_parent_completed,
ai_payload_json, case_keys_json, extracted_fields_json, created_at, updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, 'S02', 'frontend_query_skill',
'normal_task', ?, ?, ?, 'frontend_query', 'current', ?, ?, ?, 0,
'{}', '{}', '{}', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
""", transitionId, HOTEL_ID, transitionId - 1, sourceMessageId, sourceEventIndex,
sourceEventIndex, sourceEventIndex, aiTaskType, systemTaskType, taskCardType, groupCode,
"0".repeat(64), transitionId.toString());
}
private void insertTask(
Long taskId,
Long orderId,
Long sourceMessageId,
Long transitionId,
String aiTaskType,
String systemTaskType,
String taskCardType,
String taskStatus,
Integer executionOrder) {
jdbcTemplate.update("""
INSERT INTO workflow_reservation_task (
id, hotel_id, order_id, source_message_id, ai_transition_id,
result_type, ai_task_type, system_task_type, task_card_type, task_subtype,
task_status, queue_participation, execution_order, blocked_until_parent_completed,
version, created_at, updated_at
)
VALUES (?, ?, ?, ?, ?, 'normal_task', ?, ?, ?, 'frontend_query',
?, 1, ?, 0, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
""", taskId, HOTEL_ID, orderId, sourceMessageId, transitionId, aiTaskType,
systemTaskType, taskCardType, taskStatus, executionOrder);
}
}