补齐前端P0查询接口
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package cn.nianxx.thhotel.platform.message.control;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
@@ -125,6 +126,62 @@ class SourceMessageControllerTest {
|
||||
org.assertj.core.api.Assertions.assertThat(auditCount).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReadConversationOriginalContentAndRelatedReservationSummaryWithoutFrontendKey() throws Exception {
|
||||
SourceMessageCaptureResult first = captureService.capture(conversationCommand(
|
||||
"mail-conversation-p0-001",
|
||||
"conversation-p0-001",
|
||||
"First full text body with booking context.",
|
||||
"<html><body><p>First HTML body</p><img src=\"cid:image-001\" /></body></html>",
|
||||
List.of(
|
||||
media("INLINE_IMAGE", "image-001.png", "image/png", 100L,
|
||||
"https://media.example.test/image-001.png?token=secret", "image-001"),
|
||||
media("ATTACHMENT", "first.pdf", "application/pdf", 1000L,
|
||||
"https://media.example.test/first.pdf?token=secret", "attachment-001")
|
||||
)));
|
||||
SourceMessageCaptureResult second = captureService.capture(conversationCommand(
|
||||
"mail-conversation-p0-002",
|
||||
"conversation-p0-001",
|
||||
"Second full text body.",
|
||||
"<html><body><p>Second HTML body</p></body></html>",
|
||||
List.of()));
|
||||
Long orderId = 940000000000000101L;
|
||||
Long transitionId = 940000000000000201L;
|
||||
Long taskId = 940000000000000301L;
|
||||
insertRelatedOrder(orderId, first.inboxId(), "GRP-CONVERSATION-P0-001");
|
||||
insertRelatedTransition(transitionId, first.inboxId(), "GRP-CONVERSATION-P0-001");
|
||||
insertRelatedTask(taskId, orderId, first.inboxId(), transitionId);
|
||||
|
||||
mockMvc.perform(get("/api/source-messages/{sourceMessageId}/conversation", first.inboxId()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.conversation.external_conversation_id").value("conversation-p0-001"))
|
||||
.andExpect(jsonPath("$.conversation.message_count").value(2))
|
||||
.andExpect(jsonPath("$.messages.length()").value(2))
|
||||
.andExpect(jsonPath("$.messages[?(@.external_message_id=='mail-conversation-p0-001')].text_body")
|
||||
.value(contains(containsString("First full text body"))))
|
||||
.andExpect(jsonPath("$.messages[?(@.external_message_id=='mail-conversation-p0-001')].html_body")
|
||||
.value(contains(containsString("First HTML body"))))
|
||||
.andExpect(jsonPath("$.messages[?(@.external_message_id=='mail-conversation-p0-001')].inline_images[0].externalUrl")
|
||||
.value(contains("https://media.example.test/image-001.png?token=secret")))
|
||||
.andExpect(jsonPath("$.messages[?(@.external_message_id=='mail-conversation-p0-001')].attachments[0].externalUrl")
|
||||
.value(contains("https://media.example.test/first.pdf?token=secret")))
|
||||
.andExpect(jsonPath("$.messages[?(@.external_message_id=='mail-conversation-p0-001')].related_orders[0].order_id")
|
||||
.value(contains(orderId.toString())))
|
||||
.andExpect(jsonPath("$.messages[?(@.external_message_id=='mail-conversation-p0-001')].related_tasks[0].task_id")
|
||||
.value(contains(taskId.toString())))
|
||||
.andExpect(jsonPath("$.messages[?(@.external_message_id=='mail-conversation-p0-002')].text_body")
|
||||
.value(contains(containsString("Second full text body"))));
|
||||
|
||||
Long auditCount = jdbcTemplate.queryForObject("""
|
||||
SELECT COUNT(*)
|
||||
FROM platform_source_message_original_access_audit
|
||||
WHERE inbox_id IN (?, ?)
|
||||
AND actor_id = 'system:source-message-conversation'
|
||||
AND access_scene = 'source-message-conversation'
|
||||
""", Long.class, first.inboxId(), second.inboxId());
|
||||
org.assertj.core.api.Assertions.assertThat(auditCount).isEqualTo(2L);
|
||||
}
|
||||
|
||||
private CaptureSourceMessageCommand command(
|
||||
String externalMessageId,
|
||||
String externalConversationId,
|
||||
@@ -155,4 +212,81 @@ class SourceMessageControllerTest {
|
||||
"attachment-api-001"))
|
||||
);
|
||||
}
|
||||
|
||||
private CaptureSourceMessageCommand conversationCommand(
|
||||
String externalMessageId,
|
||||
String externalConversationId,
|
||||
String textBody,
|
||||
String htmlBody,
|
||||
List<CaptureSourceMessageMedia> mediaItems) {
|
||||
return new CaptureSourceMessageCommand(
|
||||
"HOTEL-TEST",
|
||||
"AGENTBUS",
|
||||
"EMAIL",
|
||||
externalMessageId,
|
||||
externalConversationId,
|
||||
"frame-" + externalMessageId,
|
||||
"session-api",
|
||||
Instant.parse("2026-07-06T10:00:00Z"),
|
||||
"guest@example.test",
|
||||
"Reservation update",
|
||||
textBody,
|
||||
htmlBody,
|
||||
"{\"source\":{\"external_message_id\":\"" + externalMessageId + "\"}}",
|
||||
"agentbus-outlook-v1",
|
||||
mediaItems
|
||||
);
|
||||
}
|
||||
|
||||
private CaptureSourceMessageMedia media(
|
||||
String mediaType,
|
||||
String fileName,
|
||||
String contentType,
|
||||
Long sizeBytes,
|
||||
String externalUrl,
|
||||
String externalMediaId) {
|
||||
return new CaptureSourceMessageMedia(mediaType, fileName, contentType, sizeBytes, externalUrl, externalMediaId);
|
||||
}
|
||||
|
||||
private void insertRelatedOrder(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 (?, 'HOTEL-TEST', 'GROUP_CODE', ?, ?, ?, 'ACTIVE', 'AI_CANDIDATE', ?,
|
||||
?, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
""", orderId, groupCode, groupCode, "TMP-" + orderId, groupCode, sourceMessageId);
|
||||
}
|
||||
|
||||
private void insertRelatedTransition(Long transitionId, Long sourceMessageId, String groupCode) {
|
||||
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 (?, 'HOTEL-TEST', ?, ?, 1, 1, 1, 'S02', 'conversation_skill',
|
||||
'normal_task', 'New Booking', 'NEW_BOOKING', 'NEW_BOOKING', 'new_group_block',
|
||||
'current', ?, ?, ?, 0, '{}', '{}', '{}', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
""", transitionId, transitionId - 1, sourceMessageId, groupCode, "1".repeat(64),
|
||||
transitionId.toString());
|
||||
}
|
||||
|
||||
private void insertRelatedTask(Long taskId, Long orderId, Long sourceMessageId, Long transitionId) {
|
||||
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 (?, 'HOTEL-TEST', ?, ?, ?, 'normal_task', 'New Booking',
|
||||
'NEW_BOOKING', 'NEW_BOOKING', 'new_group_block', 'PENDING_CONFIRM',
|
||||
1, 1, 0, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
""", taskId, orderId, sourceMessageId, transitionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.nianxx.thhotel.workflows.reservation.control;
|
||||
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
@@ -90,10 +91,18 @@ class ReservationFrontendQueryControllerTest {
|
||||
.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[0].source_sender_summary").value("g***@example.test"))
|
||||
.andExpect(jsonPath("$.items[0].source_received_at").value(matchesPattern(UTC_INSTANT_PATTERN)))
|
||||
.andExpect(jsonPath("$.items[0].external_conversation_id")
|
||||
.value("thread-mail-frontend-task-list-001"))
|
||||
.andExpect(jsonPath("$.items[0].conversation_message_count").value(1))
|
||||
.andExpect(jsonPath("$.items[0].created_at").value(matchesPattern(UTC_INSTANT_PATTERN)))
|
||||
.andExpect(jsonPath("$.items[0].updated_at").value(matchesPattern(UTC_INSTANT_PATTERN)))
|
||||
.andExpect(jsonPath("$.items[1].task_id").value(secondTaskId.toString()))
|
||||
.andExpect(jsonPath("$.items[1].task_type").value("UPDATE_BOOKING"))
|
||||
.andExpect(jsonPath("$.items[1].external_conversation_id")
|
||||
.value("thread-mail-frontend-task-list-001"))
|
||||
.andExpect(jsonPath("$.items[1].conversation_message_count").value(1))
|
||||
.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))
|
||||
@@ -148,6 +157,13 @@ class ReservationFrontendQueryControllerTest {
|
||||
.andExpect(jsonPath("$.order.updated_at").value(matchesPattern(UTC_INSTANT_PATTERN)))
|
||||
.andExpect(jsonPath("$.tasks[0].task_id").value(firstTaskId.toString()))
|
||||
.andExpect(jsonPath("$.tasks[0].task_status").value("COMPLETED"))
|
||||
.andExpect(jsonPath("$.tasks[0].source_message_id").value(source.inboxId().toString()))
|
||||
.andExpect(jsonPath("$.tasks[0].source_subject").value("Frontend Query Detail Smoke"))
|
||||
.andExpect(jsonPath("$.tasks[0].source_sender_summary").value("g***@example.test"))
|
||||
.andExpect(jsonPath("$.tasks[0].source_received_at").value(matchesPattern(UTC_INSTANT_PATTERN)))
|
||||
.andExpect(jsonPath("$.tasks[0].external_conversation_id")
|
||||
.value("thread-mail-frontend-order-detail-001"))
|
||||
.andExpect(jsonPath("$.tasks[0].conversation_message_count").value(1))
|
||||
.andExpect(jsonPath("$.tasks[0].readonly_reason_code").value("TASK_FINISHED"))
|
||||
.andExpect(jsonPath("$.tasks[0].created_at").value(matchesPattern(UTC_INSTANT_PATTERN)))
|
||||
.andExpect(jsonPath("$.tasks[1].task_id").value(secondTaskId.toString()))
|
||||
@@ -165,6 +181,62 @@ class ReservationFrontendQueryControllerTest {
|
||||
.andExpect(jsonPath("$.tasks.length()").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnOrderListWithOpenTaskCountAndNextProcessableTask() throws Exception {
|
||||
SourceMessageCaptureResult source = captureSourceMessage(
|
||||
"mail-frontend-order-list-001",
|
||||
"Frontend Query Order List Smoke");
|
||||
Long activeOrderId = 930000000000000701L;
|
||||
Long endedOrderId = 930000000000000702L;
|
||||
Long pendingTaskId = 930000000000000901L;
|
||||
Long completedTaskId = 930000000000000902L;
|
||||
Long failedTaskId = 930000000000000903L;
|
||||
insertGroupOrder(activeOrderId, source.inboxId(), "GRP-FRONTEND-ORDERS-ACTIVE-001", "ACTIVE");
|
||||
insertGroupOrder(endedOrderId, source.inboxId(), "GRP-FRONTEND-ORDERS-ENDED-001", "ENDED");
|
||||
insertTransition(930000000000000801L, source.inboxId(), 1, "GRP-FRONTEND-ORDERS-ACTIVE-001",
|
||||
"New Booking", "NEW_BOOKING", "NEW_BOOKING");
|
||||
insertTransition(930000000000000802L, source.inboxId(), 2, "GRP-FRONTEND-ORDERS-ACTIVE-001",
|
||||
"Update Booking", "UPDATE_BOOKING", "UPDATE_BOOKING");
|
||||
insertTransition(930000000000000803L, source.inboxId(), 3, "GRP-FRONTEND-ORDERS-ACTIVE-001",
|
||||
"Cancel Booking", "CANCEL_BOOKING", "CANCEL_BOOKING");
|
||||
insertTask(pendingTaskId, activeOrderId, source.inboxId(), 930000000000000801L, "New Booking",
|
||||
"NEW_BOOKING", "NEW_BOOKING", "PENDING_CONFIRM", 1);
|
||||
insertTask(completedTaskId, activeOrderId, source.inboxId(), 930000000000000802L, "Update Booking",
|
||||
"UPDATE_BOOKING", "UPDATE_BOOKING", "COMPLETED", 2);
|
||||
insertTask(failedTaskId, activeOrderId, source.inboxId(), 930000000000000803L, "Cancel Booking",
|
||||
"CANCEL_BOOKING", "CANCEL_BOOKING", "FAILED", 3);
|
||||
|
||||
mockMvc.perform(get("/api/reservation/orders")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("keyword", "GRP-FRONTEND-ORDERS-")
|
||||
.param("page_num", "1")
|
||||
.param("page_size", "20"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items.length()").value(2))
|
||||
.andExpect(jsonPath("$.items[?(@.order_id=='" + activeOrderId + "')].order_status")
|
||||
.value(contains("ACTIVE")))
|
||||
.andExpect(jsonPath("$.items[?(@.order_id=='" + activeOrderId + "')].open_task_count")
|
||||
.value(contains(1)))
|
||||
.andExpect(jsonPath("$.items[?(@.order_id=='" + activeOrderId + "')].next_processable_task_id")
|
||||
.value(contains(pendingTaskId.toString())))
|
||||
.andExpect(jsonPath("$.items[?(@.order_id=='" + endedOrderId + "')].order_status")
|
||||
.value(contains("ENDED")))
|
||||
.andExpect(jsonPath("$.items[?(@.order_id=='" + endedOrderId + "')].open_task_count")
|
||||
.value(contains(0)))
|
||||
.andExpect(jsonPath("$.page.total").value(2));
|
||||
|
||||
mockMvc.perform(get("/api/reservation/orders")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("order_status", "ENDED")
|
||||
.param("keyword", "GRP-FRONTEND-ORDERS-")
|
||||
.param("page_num", "1")
|
||||
.param("page_size", "20"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items.length()").value(1))
|
||||
.andExpect(jsonPath("$.items[0].order_id").value(endedOrderId.toString()))
|
||||
.andExpect(jsonPath("$.items[0].order_status").value("ENDED"));
|
||||
}
|
||||
|
||||
private SourceMessageCaptureResult captureSourceMessage(String externalMessageId, String subject) {
|
||||
return captureService.capture(new CaptureSourceMessageCommand(
|
||||
HOTEL_ID,
|
||||
@@ -186,15 +258,19 @@ class ReservationFrontendQueryControllerTest {
|
||||
}
|
||||
|
||||
private void insertActiveGroupOrder(Long orderId, Long sourceMessageId, String groupCode) {
|
||||
insertGroupOrder(orderId, sourceMessageId, groupCode, "ACTIVE");
|
||||
}
|
||||
|
||||
private void insertGroupOrder(Long orderId, Long sourceMessageId, String groupCode, String orderStatus) {
|
||||
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,
|
||||
VALUES (?, ?, 'GROUP_CODE', ?, ?, ?, ?, 'AI_CANDIDATE', ?, ?, 0,
|
||||
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
""", orderId, HOTEL_ID, groupCode, groupCode, "TMP-" + orderId, groupCode, sourceMessageId);
|
||||
""", orderId, HOTEL_ID, groupCode, groupCode, "TMP-" + orderId, orderStatus, groupCode, sourceMessageId);
|
||||
}
|
||||
|
||||
private void insertTransition(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package cn.nianxx.thhotel.workflows.reservation.control;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
@@ -48,6 +50,7 @@ class SuperAgentTaskResultControllerTest {
|
||||
private static final String ENDPOINT = "/api/integrations/superagent/task-results";
|
||||
private static final String CLIENT_ID = "superagent-test-client";
|
||||
private static final String SECRET = "test-superagent-secret";
|
||||
private static final String UTC_INSTANT_PATTERN = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@@ -519,16 +522,29 @@ class SuperAgentTaskResultControllerTest {
|
||||
mockMvc.perform(get("/api/reservation/tasks/{taskId}", taskId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.task_card_type").value("NEW_BOOKING"))
|
||||
.andExpect(jsonPath("$.source_subject").value("M002 SuperAgent intake"))
|
||||
.andExpect(jsonPath("$.source_sender_summary").value("g***@example.test"))
|
||||
.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("$.fields.length()").value(18))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].display_name")
|
||||
.value(org.hamcrest.Matchers.contains("Confirmation No.")))
|
||||
.value(contains("Confirmation No.")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].result_type")
|
||||
.value(contains("normal_task")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].task_type")
|
||||
.value(contains("New Booking")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].task_subtype")
|
||||
.value(contains("new_fit_reservation")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].default_value_source")
|
||||
.value(contains("AI提取值")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].editable")
|
||||
.value(org.hamcrest.Matchers.contains("是")))
|
||||
.value(contains("是")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].write_path")
|
||||
.value(org.hamcrest.Matchers.contains("confirmed_payload_json.case_keys.confirmation_number")))
|
||||
.value(contains("confirmed_payload_json.case_keys.confirmation_number")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.confirmation_number')].value")
|
||||
.value(org.hamcrest.Matchers.contains("CNF-MATRIX-001")));
|
||||
.value(contains("CNF-MATRIX-001")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user