fix(firstleg): compare stable carrier facts separately from response audit hashes
This commit is contained in:
+24
-4
@@ -575,8 +575,8 @@ public class CarrierScheduleQueryService {
|
||||
fact.setSourceReference(trim(sourceReference)); fact.setRawPayloadHash(rawPayloadHash);
|
||||
if (repository.insertIfAbsent(fact)) return fact;
|
||||
CarrierScheduleNodeFactEntity existing = repository.nodeByContainer(containerBookingId, fact.getEventKey());
|
||||
if (existing != null && !Objects.equals(existing.getRawPayloadHash(), rawPayloadHash)) {
|
||||
throw conflict("相同providerEventId对应不同rawPayloadHash,拒绝覆盖既有节点事实");
|
||||
if (existing != null && !nodeFactHash(existing).equals(nodeFactHash(fact))) {
|
||||
throw conflict("相同providerEventId对应不同事件事实摘要,拒绝覆盖既有节点事实");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -595,12 +595,32 @@ public class CarrierScheduleQueryService {
|
||||
fact.setSourceReference(trim(sourceReference)); fact.setRawPayloadHash(rawPayloadHash);
|
||||
if (repository.insertIfAbsent(fact)) return true;
|
||||
CarrierScheduleExceptionFactEntity existing = repository.exception(queryId, fact.getEventKey());
|
||||
if (existing != null && !Objects.equals(existing.getRawPayloadHash(), rawPayloadHash)) {
|
||||
throw conflict("相同providerEventId对应不同rawPayloadHash,拒绝覆盖既有异常事实");
|
||||
if (existing != null && !exceptionFactHash(existing).equals(exceptionFactHash(fact))) {
|
||||
throw conflict("相同providerEventId对应不同事件事实摘要,拒绝覆盖既有异常事实");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// rawPayloadHash audits the whole provider response. Sibling events, ETA and query time
|
||||
// may change between polls; compare the immutable event projection instead of that audit hash.
|
||||
private String nodeFactHash(CarrierScheduleNodeFactEntity fact) {
|
||||
return hash("PROVIDER_NODE_FACT_V1", fact.getContainerBookingId(), java.util.Arrays.asList(
|
||||
fact.getProviderCode(), fact.getProviderEventId(), fact.getProviderNodeCode(), fact.getStandardNode(),
|
||||
factTimestamp(fact.getEstimatedAt()), factTimestamp(fact.getActualAt()),
|
||||
fact.getLocationCode(), fact.getLocationName(), fact.getSourceReference()));
|
||||
}
|
||||
|
||||
private String exceptionFactHash(CarrierScheduleExceptionFactEntity fact) {
|
||||
return hash("PROVIDER_EXCEPTION_FACT_V1", fact.getQueryId(), java.util.Arrays.asList(
|
||||
fact.getProviderCode(), fact.getProviderEventId(), fact.getProviderStatus(), fact.getExceptionCode(),
|
||||
fact.getActive(), fact.getLocationCode(), fact.getLocationName(), fact.getSourceReference()));
|
||||
}
|
||||
|
||||
private static LocalDateTime factTimestamp(LocalDateTime value) {
|
||||
// Match DATETIME(3) storage rounding, including a carry into the next second.
|
||||
return value == null ? null : value.plusNanos(500_000).truncatedTo(java.time.temporal.ChronoUnit.MILLIS);
|
||||
}
|
||||
|
||||
private String requireContainerNo(long containerBookingId) {
|
||||
ContainerBookingEntity container = portSchedules.containerBooking(containerBookingId);
|
||||
if (container == null) {
|
||||
|
||||
Reference in New Issue
Block a user