实现Rooming List确认自动置DEF
This commit is contained in:
@@ -9,11 +9,11 @@
|
|||||||
|
|
||||||
## 1. 当前 Checkpoint
|
## 1. 当前 Checkpoint
|
||||||
|
|
||||||
- 名称:`M002-V4-room-information-display-model-frontend`
|
- 名称:`M002-V4-rooming-list-confirm-def-backend`
|
||||||
- 状态:Frontend Done,已把 V4 任务详情页 `ROOM_INFORMATION` 卡从通用 JSON 展示改为业务表单展示。
|
- 状态:Backend Done,已实现 V4 `ROOMING_LIST` 卡确认后 Group Booking Status 自动置 `DEF` 的后端联动。
|
||||||
- 目标:前端消费后端 `display_payload.room_information` 中的 `current_values`、`proposed_values`、`final_values`、`change_summary[]` 和 `group_booking_status_options[]`,并按 `fields[]` 的 `/room_information/final_values/...` 白名单提交确认 / 复核。
|
- 目标:普通确认或 `REVIEW_REQUIRED` 复核确认 Rooming List 卡时,如果同订单为 Group 且存在可更新的已确认 Room Information 快照,后端强制覆盖 `group_booking_status=DEF` 并保留审计;Fit 不变更。
|
||||||
- 边界:本 checkpoint 不展示 Agent raw payload、`business_fields` 推导结果、`target_order.locator_value` 编辑项、邮件正文、附件 URL、PMS raw response;不做 Payment 附件预览、Rooming List Excel、Account + booking type Rate Code lookup 或真实 PMS / OPERA / OHIP。
|
- 边界:本 checkpoint 不改变 Room Information 查询响应结构;不解析名单、不预览附件、不生成 Excel、不导入 PMS、不调用 M010 Rooming List 工具;不推进 Payment 附件预览、Account + booking type Rate Code lookup、真实 PMS / OPERA / OHIP 或 M011 CP4。
|
||||||
- 联调备注:`PENDING_CONFIRM` 点击“确认卡片”调用普通 confirm,提交 `confirmed_payload.room_information.final_values` 中当前 `fields[]` 可编辑字段;`REVIEW_REQUIRED` 保持同一张业务卡,按钮仍显示“确认卡片”,内部调用 `review-resolution`,`field_overrides[].field_pointer` 使用 `/room_information/final_values/...`。
|
- 联调备注:前端确认 `ROOMING_LIST` 时只需按卡片状态调用普通 confirm 或 `review-resolution`;后端负责自动 DEF 和 `V4_ROOMING_LIST_AUTO_DEF` 审计。没有可更新 Room Information 投影时确认仍成功,不创建不完整 Room Information,只写安全审计提示。
|
||||||
|
|
||||||
## 2. 当前优先级
|
## 2. 当前优先级
|
||||||
|
|
||||||
|
|||||||
@@ -342,6 +342,28 @@ public class MybatisReservationV4WorkflowRepository implements ReservationV4Work
|
|||||||
return updated == 1;
|
return updated == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 version 乐观锁更新已确认 V4 任务卡的确认快照,用于后续业务卡触发的订单事实派生修正。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean updateConfirmedTaskCardPayloadWithVersion(
|
||||||
|
String hotelId,
|
||||||
|
Long taskCardId,
|
||||||
|
Long expectedVersion,
|
||||||
|
String confirmedPayloadJson,
|
||||||
|
LocalDateTime updatedAt) {
|
||||||
|
int updated = taskCardMapper.update(Wrappers.<ReservationV4TaskCardEntity>lambdaUpdate()
|
||||||
|
.set(ReservationV4TaskCardEntity::getConfirmedPayloadJson, confirmedPayloadJson)
|
||||||
|
.set(ReservationV4TaskCardEntity::getUpdatedAt, updatedAt)
|
||||||
|
.setSql("version = version + 1")
|
||||||
|
.eq(ReservationV4TaskCardEntity::getHotelId, hotelId)
|
||||||
|
.eq(ReservationV4TaskCardEntity::getId, taskCardId)
|
||||||
|
.eq(ReservationV4TaskCardEntity::getVersion, expectedVersion)
|
||||||
|
.eq(ReservationV4TaskCardEntity::getCardStatus, ReservationV4CardStatus.CONFIRMED.name())
|
||||||
|
.isNull(ReservationV4TaskCardEntity::getLogicDeletedAt));
|
||||||
|
return updated == 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在复核场景确认 V4 订单任务归属,只更新当前订单任务的本地订单绑定和归属状态。
|
* 在复核场景确认 V4 订单任务归属,只更新当前订单任务的本地订单绑定和归属状态。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -110,6 +110,16 @@ public interface ReservationV4WorkflowRepository {
|
|||||||
String confirmedBy,
|
String confirmedBy,
|
||||||
LocalDateTime confirmedAt);
|
LocalDateTime confirmedAt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 version 乐观锁更新已确认 V4 任务卡的确认快照,用于后续业务卡触发的订单事实派生修正。
|
||||||
|
*/
|
||||||
|
boolean updateConfirmedTaskCardPayloadWithVersion(
|
||||||
|
String hotelId,
|
||||||
|
Long taskCardId,
|
||||||
|
Long expectedVersion,
|
||||||
|
String confirmedPayloadJson,
|
||||||
|
LocalDateTime updatedAt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在复核场景确认 V4 订单任务归属,只更新当前订单任务的本地订单绑定和归属状态。
|
* 在复核场景确认 V4 订单任务归属,只更新当前订单任务的本地订单绑定和归属状态。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
|
|||||||
private static final String ACTION_V4_CARD_CONFIRM = "V4_CARD_CONFIRM";
|
private static final String ACTION_V4_CARD_CONFIRM = "V4_CARD_CONFIRM";
|
||||||
private static final String ACTION_V4_CARD_REVIEW_RESOLVE = "V4_CARD_REVIEW_RESOLVE";
|
private static final String ACTION_V4_CARD_REVIEW_RESOLVE = "V4_CARD_REVIEW_RESOLVE";
|
||||||
private static final String ACTION_V4_SOURCE_NOTIFICATION_ACK = "V4_SOURCE_NOTIFICATION_ACK";
|
private static final String ACTION_V4_SOURCE_NOTIFICATION_ACK = "V4_SOURCE_NOTIFICATION_ACK";
|
||||||
|
private static final String ACTION_V4_ROOMING_LIST_AUTO_DEF = "V4_ROOMING_LIST_AUTO_DEF";
|
||||||
private static final String EVENT_NEW_BOOKING = "NEW_BOOKING";
|
private static final String EVENT_NEW_BOOKING = "NEW_BOOKING";
|
||||||
private static final String EVENT_UPDATE_BOOKING = "UPDATE_BOOKING";
|
private static final String EVENT_UPDATE_BOOKING = "UPDATE_BOOKING";
|
||||||
private static final String EVENT_CANCEL_BOOKING = "CANCEL_BOOKING";
|
private static final String EVENT_CANCEL_BOOKING = "CANCEL_BOOKING";
|
||||||
@@ -68,6 +69,7 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
|
|||||||
private static final String BOOKING_TYPE_FIT = "FIT";
|
private static final String BOOKING_TYPE_FIT = "FIT";
|
||||||
private static final String LOCATOR_TYPE_GROUP_CODE = "GROUP_CODE";
|
private static final String LOCATOR_TYPE_GROUP_CODE = "GROUP_CODE";
|
||||||
private static final String GROUP_BOOKING_STATUS_TEN = "TEN";
|
private static final String GROUP_BOOKING_STATUS_TEN = "TEN";
|
||||||
|
private static final String GROUP_BOOKING_STATUS_DEF = "DEF";
|
||||||
private static final Map<String, String> GROUP_BOOKING_STATUS_LABELS = Map.of(
|
private static final Map<String, String> GROUP_BOOKING_STATUS_LABELS = Map.of(
|
||||||
"TEN", "TEN-Tentative",
|
"TEN", "TEN-Tentative",
|
||||||
"DEF", "DEF-Definite",
|
"DEF", "DEF-Definite",
|
||||||
@@ -183,6 +185,7 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
|
|||||||
}
|
}
|
||||||
refreshOrderTaskStatus(orderTask, now);
|
refreshOrderTaskStatus(orderTask, now);
|
||||||
writeCardConfirmAudit(orderTask, card, actorId, confirmedPayloadJson, now);
|
writeCardConfirmAudit(orderTask, card, actorId, confirmedPayloadJson, now);
|
||||||
|
applyRoomingListGroupDefIfNeeded(orderTask, card, actorId, now);
|
||||||
return queryService.getOrderTaskDetail(orderTask.hotelId(), orderTask.id());
|
return queryService.getOrderTaskDetail(orderTask.hotelId(), orderTask.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +261,7 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
|
|||||||
}
|
}
|
||||||
refreshOrderTaskStatus(latestOrderTask, now);
|
refreshOrderTaskStatus(latestOrderTask, now);
|
||||||
writeCardReviewResolveAudit(orderTask, card, actorId, normalizedOverrides, confirmedOrderId, request, now);
|
writeCardReviewResolveAudit(orderTask, card, actorId, normalizedOverrides, confirmedOrderId, request, now);
|
||||||
|
applyRoomingListGroupDefIfNeeded(latestOrderTask, card, actorId, now);
|
||||||
return queryService.getOrderTaskDetail(orderTask.hotelId(), orderTask.id());
|
return queryService.getOrderTaskDetail(orderTask.hotelId(), orderTask.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1186,6 +1190,134 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
|
|||||||
return roomInformationDisplayValues(orderTask.targetBookingType(), current, true);
|
return roomInformationDisplayValues(orderTask.targetBookingType(), current, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rooming List 确认后,Group 订单需要自动把 Group Booking Status 收口为 DEF。
|
||||||
|
*/
|
||||||
|
private void applyRoomingListGroupDefIfNeeded(
|
||||||
|
ReservationV4OrderTaskSnapshot orderTask,
|
||||||
|
ReservationV4TaskCardSnapshot card,
|
||||||
|
String actorId,
|
||||||
|
LocalDateTime now) {
|
||||||
|
if (!ReservationV4CardType.ROOMING_LIST.name().equals(card.cardType())
|
||||||
|
|| !BOOKING_TYPE_GROUP.equals(orderTask.targetBookingType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ReservationV4TaskCardSnapshot roomInformationCard = latestConfirmedRoomInformationCard(orderTask);
|
||||||
|
if (roomInformationCard == null) {
|
||||||
|
writeRoomingListAutoDefAudit(
|
||||||
|
orderTask,
|
||||||
|
card,
|
||||||
|
null,
|
||||||
|
actorId,
|
||||||
|
null,
|
||||||
|
"SKIPPED_NO_ROOM_INFORMATION_PROJECTION",
|
||||||
|
now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ObjectNode payload = confirmedPayloadObject(roomInformationCard.confirmedPayloadJson());
|
||||||
|
JsonNode finalValuesNode = payload.path("room_information").path("final_values");
|
||||||
|
if (!finalValuesNode.isObject()) {
|
||||||
|
writeRoomingListAutoDefAudit(
|
||||||
|
orderTask,
|
||||||
|
card,
|
||||||
|
roomInformationCard,
|
||||||
|
actorId,
|
||||||
|
null,
|
||||||
|
"SKIPPED_ROOM_INFORMATION_FINAL_VALUES_MISSING",
|
||||||
|
now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ObjectNode finalValues = (ObjectNode) finalValuesNode;
|
||||||
|
String previousStatus = textAt(finalValues, "group_booking_status");
|
||||||
|
finalValues.put("group_booking_status", GROUP_BOOKING_STATUS_DEF);
|
||||||
|
normalizeRoomInformationDerivedFields(BOOKING_TYPE_GROUP, finalValues);
|
||||||
|
payload.withObject("/room_information").set(
|
||||||
|
"change_summary",
|
||||||
|
changeSummary(payload.path("room_information").path("current_values"), finalValues));
|
||||||
|
boolean updated = workflowRepository.updateConfirmedTaskCardPayloadWithVersion(
|
||||||
|
roomInformationCard.hotelId(),
|
||||||
|
roomInformationCard.id(),
|
||||||
|
roomInformationCard.version(),
|
||||||
|
toJson(payload),
|
||||||
|
now);
|
||||||
|
writeRoomingListAutoDefAudit(
|
||||||
|
orderTask,
|
||||||
|
card,
|
||||||
|
roomInformationCard,
|
||||||
|
actorId,
|
||||||
|
previousStatus,
|
||||||
|
updated ? "UPDATED" : "SKIPPED_VERSION_CHANGED",
|
||||||
|
now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找同订单最新一张已确认 Room Information 卡,作为 Rooming List 自动 DEF 的可更新订单投影。
|
||||||
|
*/
|
||||||
|
private ReservationV4TaskCardSnapshot latestConfirmedRoomInformationCard(ReservationV4OrderTaskSnapshot orderTask) {
|
||||||
|
if (orderTask.orderId() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<ReservationV4OrderTaskSnapshot> orderTasks = workflowRepository.findOrderTasksByOrderIds(
|
||||||
|
orderTask.hotelId(),
|
||||||
|
List.of(orderTask.orderId()));
|
||||||
|
List<Long> updatableOrderTaskIds = new ArrayList<>();
|
||||||
|
for (ReservationV4OrderTaskSnapshot candidate : orderTasks) {
|
||||||
|
updatableOrderTaskIds.add(candidate.id());
|
||||||
|
if (Objects.equals(candidate.id(), orderTask.id())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updatableOrderTaskIds.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ReservationV4TaskCardSnapshot latest = null;
|
||||||
|
for (ReservationV4TaskCardSnapshot candidate : workflowRepository.findTaskCardsByOrderTaskIds(
|
||||||
|
orderTask.hotelId(),
|
||||||
|
updatableOrderTaskIds)) {
|
||||||
|
if (!isUpdatableConfirmedRoomInformationCard(candidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (latest == null || isNewerTaskCard(candidate, latest)) {
|
||||||
|
latest = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断任务卡是否是已确认且具备稳定 final_values 的 Room Information 快照。
|
||||||
|
*/
|
||||||
|
private boolean isUpdatableConfirmedRoomInformationCard(ReservationV4TaskCardSnapshot card) {
|
||||||
|
if (card == null
|
||||||
|
|| !ReservationV4CardType.ROOM_INFORMATION.name().equals(card.cardType())
|
||||||
|
|| !ReservationV4CardStatus.CONFIRMED.name().equals(card.cardStatus())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ObjectNode confirmedPayload = confirmedPayloadObject(card.confirmedPayloadJson());
|
||||||
|
return BOOKING_TYPE_GROUP.equals(textAt(confirmedPayload.path("room_information"), "booking_type"))
|
||||||
|
&& confirmedPayload.path("room_information").path("final_values").isObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在候选 Room Information 快照中选择最近一次更新的卡片。
|
||||||
|
*/
|
||||||
|
private boolean isNewerTaskCard(ReservationV4TaskCardSnapshot candidate, ReservationV4TaskCardSnapshot current) {
|
||||||
|
LocalDateTime candidateTime = candidate.updatedAt() == null ? candidate.createdAt() : candidate.updatedAt();
|
||||||
|
LocalDateTime currentTime = current.updatedAt() == null ? current.createdAt() : current.updatedAt();
|
||||||
|
if (candidateTime != null && currentTime != null && !candidateTime.equals(currentTime)) {
|
||||||
|
return candidateTime.isAfter(currentTime);
|
||||||
|
}
|
||||||
|
return candidate.id() != null && current.id() != null && candidate.id() > current.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取确认快照为可变 ObjectNode,解析失败或非对象时降级为空对象。
|
||||||
|
*/
|
||||||
|
private ObjectNode confirmedPayloadObject(String confirmedPayloadJson) {
|
||||||
|
JsonNode payload = parseJson(confirmedPayloadJson);
|
||||||
|
return payload.isObject() ? ((ObjectNode) payload).deepCopy() : objectMapper.createObjectNode();
|
||||||
|
}
|
||||||
|
|
||||||
private ObjectNode confirmedRoomInformationFinalValues(String confirmedPayloadJson) {
|
private ObjectNode confirmedRoomInformationFinalValues(String confirmedPayloadJson) {
|
||||||
JsonNode payload = parseJson(confirmedPayloadJson);
|
JsonNode payload = parseJson(confirmedPayloadJson);
|
||||||
JsonNode finalValues = payload.path("room_information").path("final_values");
|
JsonNode finalValues = payload.path("room_information").path("final_values");
|
||||||
@@ -1944,6 +2076,47 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
|
|||||||
now));
|
now));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入 Rooming List 确认触发 Group Booking Status 自动 DEF 的安全审计摘要。
|
||||||
|
*/
|
||||||
|
private void writeRoomingListAutoDefAudit(
|
||||||
|
ReservationV4OrderTaskSnapshot orderTask,
|
||||||
|
ReservationV4TaskCardSnapshot roomingListCard,
|
||||||
|
ReservationV4TaskCardSnapshot roomInformationCard,
|
||||||
|
String actorId,
|
||||||
|
String previousStatus,
|
||||||
|
String updateStatus,
|
||||||
|
LocalDateTime now) {
|
||||||
|
Map<String, Object> beforeSnapshot = new LinkedHashMap<>();
|
||||||
|
beforeSnapshot.put("trigger", "ROOMING_LIST_CONFIRM");
|
||||||
|
beforeSnapshot.put("v4_order_task_id", orderTask.id().toString());
|
||||||
|
beforeSnapshot.put("rooming_list_card_id", roomingListCard.id().toString());
|
||||||
|
beforeSnapshot.put("room_information_card_id",
|
||||||
|
roomInformationCard == null ? null : roomInformationCard.id().toString());
|
||||||
|
beforeSnapshot.put("previous_group_booking_status", previousStatus);
|
||||||
|
Map<String, Object> afterSnapshot = new LinkedHashMap<>();
|
||||||
|
afterSnapshot.put("trigger", "ROOMING_LIST_CONFIRM");
|
||||||
|
afterSnapshot.put("v4_order_task_id", orderTask.id().toString());
|
||||||
|
afterSnapshot.put("rooming_list_card_id", roomingListCard.id().toString());
|
||||||
|
afterSnapshot.put("room_information_card_id",
|
||||||
|
roomInformationCard == null ? null : roomInformationCard.id().toString());
|
||||||
|
afterSnapshot.put("update_status", updateStatus);
|
||||||
|
afterSnapshot.put("target_group_booking_status", GROUP_BOOKING_STATUS_DEF);
|
||||||
|
afterSnapshot.put("target_group_booking_status_label", GROUP_BOOKING_STATUS_LABELS.get(GROUP_BOOKING_STATUS_DEF));
|
||||||
|
auditRepository.insertAuditLog(new ReservationAuditLogDraft(
|
||||||
|
orderTask.hotelId(),
|
||||||
|
orderTask.orderId(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ACTOR_TYPE_USER,
|
||||||
|
actorId,
|
||||||
|
ACTION_V4_ROOMING_LIST_AUTO_DEF,
|
||||||
|
"Rooming List 确认触发 Group Booking Status 自动置 DEF。",
|
||||||
|
toJson(beforeSnapshot),
|
||||||
|
toJson(afterSnapshot),
|
||||||
|
now));
|
||||||
|
}
|
||||||
|
|
||||||
private void writeCardReviewResolveAudit(
|
private void writeCardReviewResolveAudit(
|
||||||
ReservationV4OrderTaskSnapshot orderTask,
|
ReservationV4OrderTaskSnapshot orderTask,
|
||||||
ReservationV4TaskCardSnapshot card,
|
ReservationV4TaskCardSnapshot card,
|
||||||
|
|||||||
@@ -194,6 +194,199 @@ class ReservationV4CommandControllerTest {
|
|||||||
assertAuditCount("V4_CARD_CONFIRM", "v4-command-admin", seeded.orderTask().id().toString(), 2);
|
assertAuditCount("V4_CARD_CONFIRM", "v4-command-admin", seeded.orderTask().id().toString(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSetGroupBookingStatusDefWhenConfirmingGroupRoomingListFromTen() throws Exception {
|
||||||
|
Long orderId = 990000000000080001L;
|
||||||
|
SeededOrderTask roomInformation = seedOrderTask(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-ten-room-info-001",
|
||||||
|
Instant.parse("2026-07-19T01:14:00Z"),
|
||||||
|
orderId);
|
||||||
|
confirmBasicCard(roomInformation);
|
||||||
|
confirmRoomInformationBusinessCard(roomInformation, "TEN");
|
||||||
|
SeededOrderTask roomingList = seedRoomingListOrderTask(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-ten-list-001",
|
||||||
|
Instant.parse("2026-07-19T01:15:00Z"),
|
||||||
|
orderId,
|
||||||
|
"GROUP");
|
||||||
|
confirmBasicCard(roomingList);
|
||||||
|
|
||||||
|
performAuthorized(mockMvc, adminToken(), post("/api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm",
|
||||||
|
roomingList.orderTask().id(),
|
||||||
|
roomingList.businessCard().id())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"version": 0
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.business_cards[0].card_status").value("CONFIRMED"));
|
||||||
|
|
||||||
|
String confirmedPayload = confirmedPayloadJson(roomInformation.businessCard().id());
|
||||||
|
org.assertj.core.api.Assertions.assertThat(confirmedPayload)
|
||||||
|
.contains("\"group_booking_status\":\"DEF\"")
|
||||||
|
.contains("\"group_booking_status_label\":\"DEF-Definite\"");
|
||||||
|
assertAuditCount("V4_ROOMING_LIST_AUTO_DEF", "v4-command-admin",
|
||||||
|
roomInformation.businessCard().id().toString(), "\"update_status\":\"UPDATED\"", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldOverrideGroupBookingStatusInqToDefWhenConfirmingGroupRoomingList() throws Exception {
|
||||||
|
Long orderId = 990000000000080002L;
|
||||||
|
SeededOrderTask roomInformation = seedOrderTask(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-inq-room-info-001",
|
||||||
|
Instant.parse("2026-07-19T01:16:00Z"),
|
||||||
|
orderId);
|
||||||
|
confirmBasicCard(roomInformation);
|
||||||
|
confirmRoomInformationBusinessCard(roomInformation, "INQ");
|
||||||
|
SeededOrderTask roomingList = seedRoomingListOrderTask(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-inq-list-001",
|
||||||
|
Instant.parse("2026-07-19T01:17:00Z"),
|
||||||
|
orderId,
|
||||||
|
"GROUP");
|
||||||
|
confirmBasicCard(roomingList);
|
||||||
|
|
||||||
|
performAuthorized(mockMvc, adminToken(), post("/api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm",
|
||||||
|
roomingList.orderTask().id(),
|
||||||
|
roomingList.businessCard().id())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"version": 0
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
org.assertj.core.api.Assertions.assertThat(confirmedPayloadJson(roomInformation.businessCard().id()))
|
||||||
|
.contains("\"group_booking_status\":\"DEF\"")
|
||||||
|
.contains("\"group_booking_status_label\":\"DEF-Definite\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSetGroupBookingStatusDefWhenResolvingGroupRoomingListReview() throws Exception {
|
||||||
|
Long orderId = 990000000000080005L;
|
||||||
|
SeededOrderTask roomInformation = seedOrderTask(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-review-room-info-001",
|
||||||
|
Instant.parse("2026-07-19T01:20:10Z"),
|
||||||
|
orderId);
|
||||||
|
confirmBasicCard(roomInformation);
|
||||||
|
confirmRoomInformationBusinessCard(roomInformation, "TEN");
|
||||||
|
SeededOrderTask roomingList = seedReviewRoomingListOrderTask(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-review-list-001",
|
||||||
|
Instant.parse("2026-07-19T01:20:20Z"),
|
||||||
|
orderId);
|
||||||
|
confirmBasicCard(roomingList);
|
||||||
|
|
||||||
|
performAuthorized(mockMvc, adminToken(), post(
|
||||||
|
"/api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution",
|
||||||
|
roomingList.orderTask().id(),
|
||||||
|
roomingList.businessCard().id())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"reason": "确认 Rooming List 事项"
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.business_cards[0].card_status").value("CONFIRMED"));
|
||||||
|
|
||||||
|
org.assertj.core.api.Assertions.assertThat(confirmedPayloadJson(roomInformation.businessCard().id()))
|
||||||
|
.contains("\"group_booking_status\":\"DEF\"")
|
||||||
|
.contains("\"group_booking_status_label\":\"DEF-Definite\"");
|
||||||
|
assertAuditCount("V4_ROOMING_LIST_AUTO_DEF", "v4-command-admin",
|
||||||
|
roomInformation.businessCard().id().toString(), "\"update_status\":\"UPDATED\"", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotChangeGroupBookingStatusWhenConfirmingFitRoomingList() throws Exception {
|
||||||
|
Long orderId = 990000000000080003L;
|
||||||
|
SeededOrderTask roomInformation = seedOrderTaskWithBusinessCard(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-fit-room-info-001",
|
||||||
|
Instant.parse("2026-07-19T01:18:00Z"),
|
||||||
|
orderId,
|
||||||
|
"FIT",
|
||||||
|
"CONFIRMATION_NUMBER",
|
||||||
|
"FIT-V4-COMMAND-001",
|
||||||
|
ReservationV4CardType.ROOM_INFORMATION.name(),
|
||||||
|
"NEW_BOOKING",
|
||||||
|
"""
|
||||||
|
{"card_type":"ROOM_INFORMATION","event_type":"NEW_BOOKING","guest_name":"FIT Guest","room_items":[{"room_type_code":"KING","room_count":1}]}
|
||||||
|
""");
|
||||||
|
confirmBasicCard(roomInformation);
|
||||||
|
confirmRoomInformationBusinessCard(roomInformation, null);
|
||||||
|
String beforePayload = confirmedPayloadJson(roomInformation.businessCard().id());
|
||||||
|
SeededOrderTask roomingList = seedRoomingListOrderTask(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-fit-list-001",
|
||||||
|
Instant.parse("2026-07-19T01:19:00Z"),
|
||||||
|
orderId,
|
||||||
|
"FIT");
|
||||||
|
confirmBasicCard(roomingList);
|
||||||
|
|
||||||
|
performAuthorized(mockMvc, adminToken(), post("/api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm",
|
||||||
|
roomingList.orderTask().id(),
|
||||||
|
roomingList.businessCard().id())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"version": 0
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
org.assertj.core.api.Assertions.assertThat(confirmedPayloadJson(roomInformation.businessCard().id()))
|
||||||
|
.isEqualTo(beforePayload)
|
||||||
|
.doesNotContain("\"group_booking_status\":\"DEF\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldConfirmGroupRoomingListWithoutRoomInformationProjection() throws Exception {
|
||||||
|
Long orderId = 990000000000080004L;
|
||||||
|
SeededOrderTask roomingList = seedRoomingListOrderTask(
|
||||||
|
HOTEL_ID,
|
||||||
|
"mail-v4-command-rooming-def-no-projection-001",
|
||||||
|
Instant.parse("2026-07-19T01:20:00Z"),
|
||||||
|
orderId,
|
||||||
|
"GROUP");
|
||||||
|
confirmBasicCard(roomingList);
|
||||||
|
|
||||||
|
performAuthorized(mockMvc, adminToken(), post("/api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm",
|
||||||
|
roomingList.orderTask().id(),
|
||||||
|
roomingList.businessCard().id())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"version": 0
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.order_task.order_task_status").value("COMPLETED"))
|
||||||
|
.andExpect(jsonPath("$.business_cards[0].card_status").value("CONFIRMED"));
|
||||||
|
Integer roomInformationCount = jdbcTemplate.queryForObject(
|
||||||
|
"""
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM workflow_reservation_v4_task_card
|
||||||
|
WHERE hotel_id = ?
|
||||||
|
AND card_type = 'ROOM_INFORMATION'
|
||||||
|
AND source_message_id = ?
|
||||||
|
AND logic_deleted_at IS NULL
|
||||||
|
""",
|
||||||
|
Integer.class,
|
||||||
|
HOTEL_ID,
|
||||||
|
roomingList.orderTask().sourceMessageId());
|
||||||
|
org.assertj.core.api.Assertions.assertThat(roomInformationCount).isZero();
|
||||||
|
assertAuditCount("V4_ROOMING_LIST_AUTO_DEF", "v4-command-admin",
|
||||||
|
roomingList.businessCard().id().toString(), "\"update_status\":\"SKIPPED_NO_ROOM_INFORMATION_PROJECTION\"", 1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldRejectBasicInformationConfirmWhenAccountCodeUnknown() throws Exception {
|
void shouldRejectBasicInformationConfirmWhenAccountCodeUnknown() throws Exception {
|
||||||
SeededOrderTask seeded = seedOrderTask(
|
SeededOrderTask seeded = seedOrderTask(
|
||||||
@@ -1354,6 +1547,36 @@ class ReservationV4CommandControllerTest {
|
|||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void confirmRoomInformationBusinessCard(SeededOrderTask seeded, String groupBookingStatus) throws Exception {
|
||||||
|
String groupBookingStatusFragment = groupBookingStatus == null
|
||||||
|
? ""
|
||||||
|
: """
|
||||||
|
,"group_booking_status": "%s"
|
||||||
|
""".formatted(groupBookingStatus);
|
||||||
|
performAuthorized(mockMvc, adminToken(), post("/api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm",
|
||||||
|
seeded.orderTask().id(),
|
||||||
|
seeded.businessCard().id())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"confirmed_payload": {
|
||||||
|
"room_information": {
|
||||||
|
"final_values": {
|
||||||
|
"arrival_date": "2026-07-26",
|
||||||
|
"departure_date": "2026-07-29",
|
||||||
|
"rate_code": "BAR",
|
||||||
|
"fit_name": "FIT Guest",
|
||||||
|
"breakfast_included": true,
|
||||||
|
"room_items": [{"room_type_code": "TWN", "room_count": 2}]%s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".formatted(groupBookingStatusFragment)))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
private void resolveBusinessReviewCard(SeededOrderTask seeded) throws Exception {
|
private void resolveBusinessReviewCard(SeededOrderTask seeded) throws Exception {
|
||||||
performAuthorized(mockMvc, adminToken(), post(
|
performAuthorized(mockMvc, adminToken(), post(
|
||||||
"/api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution",
|
"/api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution",
|
||||||
@@ -1371,6 +1594,17 @@ class ReservationV4CommandControllerTest {
|
|||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String confirmedPayloadJson(Long cardId) {
|
||||||
|
return jdbcTemplate.queryForObject(
|
||||||
|
"""
|
||||||
|
SELECT confirmed_payload_json
|
||||||
|
FROM workflow_reservation_v4_task_card
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
String.class,
|
||||||
|
cardId);
|
||||||
|
}
|
||||||
|
|
||||||
private SeededOrderTask seedOrderTask(String hotelId, String externalMessageId, Instant receivedAt) {
|
private SeededOrderTask seedOrderTask(String hotelId, String externalMessageId, Instant receivedAt) {
|
||||||
return seedOrderTask(hotelId, externalMessageId, receivedAt, null);
|
return seedOrderTask(hotelId, externalMessageId, receivedAt, null);
|
||||||
}
|
}
|
||||||
@@ -1391,6 +1625,30 @@ class ReservationV4CommandControllerTest {
|
|||||||
Instant receivedAt,
|
Instant receivedAt,
|
||||||
Long orderId,
|
Long orderId,
|
||||||
String businessDisplayPayloadJson) {
|
String businessDisplayPayloadJson) {
|
||||||
|
return seedOrderTaskWithBusinessCard(
|
||||||
|
hotelId,
|
||||||
|
externalMessageId,
|
||||||
|
receivedAt,
|
||||||
|
orderId,
|
||||||
|
"GROUP",
|
||||||
|
"GROUP_CODE",
|
||||||
|
"GRP-V4-COMMAND-001",
|
||||||
|
ReservationV4CardType.ROOM_INFORMATION.name(),
|
||||||
|
"NEW_BOOKING",
|
||||||
|
businessDisplayPayloadJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeededOrderTask seedOrderTaskWithBusinessCard(
|
||||||
|
String hotelId,
|
||||||
|
String externalMessageId,
|
||||||
|
Instant receivedAt,
|
||||||
|
Long orderId,
|
||||||
|
String targetBookingType,
|
||||||
|
String targetLocatorType,
|
||||||
|
String targetLocatorValue,
|
||||||
|
String businessCardType,
|
||||||
|
String businessEventType,
|
||||||
|
String businessDisplayPayloadJson) {
|
||||||
SourceMessageCaptureResult source = captureSourceMessage(
|
SourceMessageCaptureResult source = captureSourceMessage(
|
||||||
hotelId,
|
hotelId,
|
||||||
externalMessageId,
|
externalMessageId,
|
||||||
@@ -1404,9 +1662,9 @@ class ReservationV4CommandControllerTest {
|
|||||||
"order-command-" + externalMessageId,
|
"order-command-" + externalMessageId,
|
||||||
1,
|
1,
|
||||||
orderId,
|
orderId,
|
||||||
"GROUP",
|
targetBookingType,
|
||||||
"GROUP_CODE",
|
targetLocatorType,
|
||||||
"GRP-V4-COMMAND-001",
|
targetLocatorValue,
|
||||||
ReservationV4TargetResolutionStatus.RESOLVED.name(),
|
ReservationV4TargetResolutionStatus.RESOLVED.name(),
|
||||||
ReservationV4OrderTaskStatus.OPEN.name(),
|
ReservationV4OrderTaskStatus.OPEN.name(),
|
||||||
LocalDateTime.ofInstant(receivedAt, ZoneOffset.UTC),
|
LocalDateTime.ofInstant(receivedAt, ZoneOffset.UTC),
|
||||||
@@ -1422,11 +1680,34 @@ class ReservationV4CommandControllerTest {
|
|||||||
{"card_type":"BASIC_INFORMATION","order_ref":"ORDER-COMMAND","basic_information":{"account_code":"QBD_TRAVEL","manual_review":null}}
|
{"card_type":"BASIC_INFORMATION","order_ref":"ORDER-COMMAND","basic_information":{"account_code":"QBD_TRAVEL","manual_review":null}}
|
||||||
""");
|
""");
|
||||||
ReservationV4TaskCardSnapshot businessCard = insertCard(orderTask, hotelId,
|
ReservationV4TaskCardSnapshot businessCard = insertCard(orderTask, hotelId,
|
||||||
ReservationV4CardType.ROOM_INFORMATION.name(), "NEW_BOOKING", 1, 30,
|
businessCardType, businessEventType, 1, 30,
|
||||||
ReservationV4CardStatus.PENDING_CONFIRM.name(), null, businessDisplayPayloadJson);
|
ReservationV4CardStatus.PENDING_CONFIRM.name(), null, businessDisplayPayloadJson);
|
||||||
return new SeededOrderTask(orderTask, sourceCard, basicCard, businessCard);
|
return new SeededOrderTask(orderTask, sourceCard, basicCard, businessCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SeededOrderTask seedRoomingListOrderTask(
|
||||||
|
String hotelId,
|
||||||
|
String externalMessageId,
|
||||||
|
Instant receivedAt,
|
||||||
|
Long orderId,
|
||||||
|
String targetBookingType) {
|
||||||
|
String locatorType = "GROUP".equals(targetBookingType) ? "GROUP_CODE" : "CONFIRMATION_NUMBER";
|
||||||
|
String locatorValue = "GROUP".equals(targetBookingType) ? "GRP-V4-ROOMING-001" : "FIT-V4-ROOMING-001";
|
||||||
|
return seedOrderTaskWithBusinessCard(
|
||||||
|
hotelId,
|
||||||
|
externalMessageId,
|
||||||
|
receivedAt,
|
||||||
|
orderId,
|
||||||
|
targetBookingType,
|
||||||
|
locatorType,
|
||||||
|
locatorValue,
|
||||||
|
ReservationV4CardType.ROOMING_LIST.name(),
|
||||||
|
"ROOMING_LIST",
|
||||||
|
"""
|
||||||
|
{"card_type":"ROOMING_LIST","event_type":"ROOMING_LIST","business_fields":{"attachment_ids":["att-rooming-list-001"],"rooming_list_action":"CONFIRM_RECEIVED"}}
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
private SeededOrderTask seedReviewOrderTask(
|
private SeededOrderTask seedReviewOrderTask(
|
||||||
String hotelId,
|
String hotelId,
|
||||||
String externalMessageId,
|
String externalMessageId,
|
||||||
@@ -1501,6 +1782,32 @@ class ReservationV4CommandControllerTest {
|
|||||||
String businessStatus,
|
String businessStatus,
|
||||||
String businessDisplayPayloadJson,
|
String businessDisplayPayloadJson,
|
||||||
String businessValidationErrorsJson) {
|
String businessValidationErrorsJson) {
|
||||||
|
return seedReviewOrderTaskWithBusinessCard(
|
||||||
|
hotelId,
|
||||||
|
externalMessageId,
|
||||||
|
receivedAt,
|
||||||
|
orderId,
|
||||||
|
targetResolutionStatus,
|
||||||
|
basicStatus,
|
||||||
|
businessStatus,
|
||||||
|
ReservationV4CardType.ROOM_INFORMATION.name(),
|
||||||
|
"NEW_BOOKING",
|
||||||
|
businessDisplayPayloadJson,
|
||||||
|
businessValidationErrorsJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeededOrderTask seedReviewOrderTaskWithBusinessCard(
|
||||||
|
String hotelId,
|
||||||
|
String externalMessageId,
|
||||||
|
Instant receivedAt,
|
||||||
|
Long orderId,
|
||||||
|
String targetResolutionStatus,
|
||||||
|
String basicStatus,
|
||||||
|
String businessStatus,
|
||||||
|
String businessCardType,
|
||||||
|
String businessEventType,
|
||||||
|
String businessDisplayPayloadJson,
|
||||||
|
String businessValidationErrorsJson) {
|
||||||
SourceMessageCaptureResult source = captureSourceMessage(
|
SourceMessageCaptureResult source = captureSourceMessage(
|
||||||
hotelId,
|
hotelId,
|
||||||
externalMessageId,
|
externalMessageId,
|
||||||
@@ -1532,11 +1839,34 @@ class ReservationV4CommandControllerTest {
|
|||||||
{"card_type":"BASIC_INFORMATION","order_ref":"ORDER-REVIEW","basic_information":{"account_code":null,"manual_review":true,"missing_fields":["/basic_information/account_code"]}}
|
{"card_type":"BASIC_INFORMATION","order_ref":"ORDER-REVIEW","basic_information":{"account_code":null,"manual_review":true,"missing_fields":["/basic_information/account_code"]}}
|
||||||
""");
|
""");
|
||||||
ReservationV4TaskCardSnapshot businessCard = insertCard(orderTask, hotelId,
|
ReservationV4TaskCardSnapshot businessCard = insertCard(orderTask, hotelId,
|
||||||
ReservationV4CardType.ROOM_INFORMATION.name(), "NEW_BOOKING", 1, 30,
|
businessCardType, businessEventType, 1, 30,
|
||||||
businessStatus, reviewStatusFor(businessStatus), businessDisplayPayloadJson, businessValidationErrorsJson);
|
businessStatus, reviewStatusFor(businessStatus), businessDisplayPayloadJson, businessValidationErrorsJson);
|
||||||
return new SeededOrderTask(orderTask, sourceCard, basicCard, businessCard);
|
return new SeededOrderTask(orderTask, sourceCard, basicCard, businessCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SeededOrderTask seedReviewRoomingListOrderTask(
|
||||||
|
String hotelId,
|
||||||
|
String externalMessageId,
|
||||||
|
Instant receivedAt,
|
||||||
|
Long orderId) {
|
||||||
|
return seedReviewOrderTaskWithBusinessCard(
|
||||||
|
hotelId,
|
||||||
|
externalMessageId,
|
||||||
|
receivedAt,
|
||||||
|
orderId,
|
||||||
|
orderId == null
|
||||||
|
? ReservationV4TargetResolutionStatus.UNRESOLVED.name()
|
||||||
|
: ReservationV4TargetResolutionStatus.RESOLVED.name(),
|
||||||
|
ReservationV4CardStatus.PENDING_CONFIRM.name(),
|
||||||
|
ReservationV4CardStatus.REVIEW_REQUIRED.name(),
|
||||||
|
ReservationV4CardType.ROOMING_LIST.name(),
|
||||||
|
"ROOMING_LIST",
|
||||||
|
"""
|
||||||
|
{"card_type":"ROOMING_LIST","event_type":"ROOMING_LIST","business_fields":{"attachment_ids":["att-rooming-list-review-001"],"rooming_list_action":"CONFIRM_RECEIVED"}}
|
||||||
|
""",
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
private String reviewStatusFor(String cardStatus) {
|
private String reviewStatusFor(String cardStatus) {
|
||||||
return ReservationV4CardStatus.REVIEW_REQUIRED.name().equals(cardStatus)
|
return ReservationV4CardStatus.REVIEW_REQUIRED.name().equals(cardStatus)
|
||||||
? ReservationReviewStatus.PENDING.name()
|
? ReservationReviewStatus.PENDING.name()
|
||||||
@@ -1637,6 +1967,15 @@ class ReservationV4CommandControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assertAuditCount(String action, String actorId, String snapshotFragment, int expectedCount) {
|
private void assertAuditCount(String action, String actorId, String snapshotFragment, int expectedCount) {
|
||||||
|
assertAuditCount(action, actorId, snapshotFragment, null, expectedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAuditCount(
|
||||||
|
String action,
|
||||||
|
String actorId,
|
||||||
|
String snapshotFragment,
|
||||||
|
String secondSnapshotFragment,
|
||||||
|
int expectedCount) {
|
||||||
Integer count = jdbcTemplate.queryForObject(
|
Integer count = jdbcTemplate.queryForObject(
|
||||||
"""
|
"""
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
@@ -1645,12 +1984,15 @@ class ReservationV4CommandControllerTest {
|
|||||||
AND action = ?
|
AND action = ?
|
||||||
AND actor_id = ?
|
AND actor_id = ?
|
||||||
AND after_snapshot_json LIKE ?
|
AND after_snapshot_json LIKE ?
|
||||||
|
AND (? IS NULL OR after_snapshot_json LIKE ?)
|
||||||
""",
|
""",
|
||||||
Integer.class,
|
Integer.class,
|
||||||
HOTEL_ID,
|
HOTEL_ID,
|
||||||
action,
|
action,
|
||||||
actorId,
|
actorId,
|
||||||
"%" + snapshotFragment + "%");
|
"%" + snapshotFragment + "%",
|
||||||
|
secondSnapshotFragment,
|
||||||
|
secondSnapshotFragment == null ? null : "%" + secondSnapshotFragment + "%");
|
||||||
org.assertj.core.api.Assertions.assertThat(count).isEqualTo(expectedCount);
|
org.assertj.core.api.Assertions.assertThat(count).isEqualTo(expectedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user