修复V4复核字段指针运行时校验

This commit is contained in:
andy
2026-07-21 14:46:27 +07:00
parent ef6658aa34
commit 3527659dd0
5 changed files with 111 additions and 84 deletions

View File

@@ -1696,8 +1696,12 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
logReviewPointerRejected(card, pointer, "V4_REVIEW_VALUE_INVALID", confirmedPayload);
throw error(HttpStatus.BAD_REQUEST, "V4_REVIEW_VALUE_INVALID", "复核字段值必须是标量或 null不能替换对象或数组。");
}
boolean stableRoomInformationPointer = isStableRoomInformationReviewPointer(confirmedPayload, pointer);
JsonNode current = findPointerValue(confirmedPayload, segments);
if (current == null || current.isMissingNode()) {
if (stableRoomInformationPointer) {
return;
}
logReviewPointerRejected(card, pointer, "CURRENT_POINTER_MISSING", confirmedPayload);
throw error(HttpStatus.BAD_REQUEST, "V4_REVIEW_POINTER_NOT_ALLOWED", "复核字段不在当前卡允许编辑字段内。");
}
@@ -1780,11 +1784,13 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
}
List<String> querySideEditablePointers = roomInformationQuerySideEditablePointers(card, confirmedPayload);
List<String> commandSideAllowedPointers = roomInformationCommandSideAllowedPointers(card, confirmedPayload);
Set<String> validationErrorPointers = collectValidationErrorPointers(card.validationErrorsJson());
log.warn(
"V4 review pointer rejected. review_pointer_policy=m002_v4_review_pointer_runtime_trace_v1, "
"V4 review pointer rejected. review_pointer_policy=m002_v4_review_pointer_runtime_fix_v1, "
+ "order_task_id={}, card_id={}, card_type={}, event_type={}, card_status={}, review_status={}, "
+ "incoming_pointer={}, query_side_editable_pointers={}, command_side_allowed_pointers={}, "
+ "reject_reason={}, stable_room_information_payload={}",
+ "validation_error_pointers={}, reject_reason={}, "
+ "display_payload_has_room_information_final_values={}, stable_room_information_payload={}",
card.v4OrderTaskId(),
card.id(),
card.cardType(),
@@ -1794,10 +1800,18 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
safeLogPointer(pointer),
querySideEditablePointers,
commandSideAllowedPointers,
validationErrorPointers,
reasonCode,
isStableRoomInformationPayload(confirmedPayload),
isStableRoomInformationPayload(confirmedPayload));
}
private boolean isStableRoomInformationReviewPointer(ObjectNode confirmedPayload, String pointer) {
return isStableRoomInformationPayload(confirmedPayload)
&& pointer != null
&& pointer.startsWith("/room_information/final_values/");
}
private List<String> roomInformationQuerySideEditablePointers(
ReservationV4TaskCardSnapshot card,
ObjectNode confirmedPayload) {
@@ -1814,77 +1828,7 @@ public class ReservationV4CommandServiceImpl implements ReservationV4CommandServ
private List<String> roomInformationCommandSideAllowedPointers(
ReservationV4TaskCardSnapshot card,
ObjectNode confirmedPayload) {
if (!isStableRoomInformationPayload(confirmedPayload)) {
return List.of();
}
JsonNode roomInformation = confirmedPayload.path("room_information");
JsonNode finalValues = roomInformation.path("final_values");
if (!finalValues.isObject()) {
return List.of();
}
List<String> pointers = new ArrayList<>();
collectRoomInformationCommandAllowedPointers(
pointers,
card,
confirmedPayload,
finalValues,
List.of());
return List.copyOf(pointers);
}
private void collectRoomInformationCommandAllowedPointers(
List<String> pointers,
ReservationV4TaskCardSnapshot card,
ObjectNode confirmedPayload,
JsonNode current,
List<String> finalValuePath) {
if (current == null || current.isMissingNode() || current.isNull()) {
addRoomInformationCommandAllowedPointer(pointers, card, confirmedPayload, current, finalValuePath);
return;
}
if (current.isObject()) {
current.fields().forEachRemaining(field -> collectRoomInformationCommandAllowedPointers(
pointers,
card,
confirmedPayload,
field.getValue(),
appendPath(finalValuePath, field.getKey())));
return;
}
if (current.isArray()) {
for (int index = 0; index < current.size(); index++) {
collectRoomInformationCommandAllowedPointers(
pointers,
card,
confirmedPayload,
current.get(index),
appendPath(finalValuePath, String.valueOf(index)));
}
return;
}
addRoomInformationCommandAllowedPointer(pointers, card, confirmedPayload, current, finalValuePath);
}
private void addRoomInformationCommandAllowedPointer(
List<String> pointers,
ReservationV4TaskCardSnapshot card,
ObjectNode confirmedPayload,
JsonNode current,
List<String> finalValuePath) {
if (finalValuePath.isEmpty()) {
return;
}
JsonNode roomInformation = confirmedPayload.path("room_information");
ReservationV4RoomInformationFieldPolicy.RoomInformationWriteDecision decision =
ReservationV4RoomInformationFieldPolicy.finalValueWriteDecision(
card.eventType(),
textAt(roomInformation, "booking_type"),
finalValuePath,
roomInformation.path("final_values"));
String pointer = "/room_information/final_values/" + toJsonPointer(finalValuePath);
if (decision.writable() && isReviewPointerAllowedForResolution(card, confirmedPayload, pointer, current)) {
pointers.add(pointer);
}
return roomInformationQuerySideEditablePointers(card, confirmedPayload);
}
private String safeLogPointer(String pointer) {