优化
This commit is contained in:
+24
-12
@@ -35,6 +35,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -122,19 +123,30 @@ public class CarrierScheduleQueryService {
|
||||
value.setManualReason(outboundManualReason(strategy, booking));
|
||||
value.setCreatedBy(operatorId); value.setCreatedByName(operatorName);
|
||||
value.setUpdatedBy(operatorId); value.setUpdatedByName(operatorName); value.setVersion(0);
|
||||
try { repository.insert(value); }
|
||||
catch (DuplicateKeyException exception) {
|
||||
CarrierScheduleQueryEntity concurrent = repository.findByBusinessScope(customerId, oceanBookingId,
|
||||
containerBookingId);
|
||||
if (concurrent != null) return concurrent;
|
||||
throw exception;
|
||||
String persistenceStage = "QUERY_TASK";
|
||||
try {
|
||||
try { repository.insert(value); }
|
||||
catch (DuplicateKeyException exception) {
|
||||
CarrierScheduleQueryEntity concurrent = repository.findByBusinessScope(customerId, oceanBookingId,
|
||||
containerBookingId);
|
||||
if (concurrent != null) return concurrent;
|
||||
throw exception;
|
||||
}
|
||||
persistenceStage = "NODE_FACT";
|
||||
CarrierScheduleNodeFactEntity departed = addNode(value.getId(), "FIRSTLEG", "OUTBOUND_CONFIRM", "OUTBOUND_CONFIRM",
|
||||
CarrierScheduleNode.DEPARTED_WAREHOUSE, now, null, now, null, null, "OUTBOUND_CONFIRM",
|
||||
sha256("OUTBOUND_CONFIRM|" + containerBookingId));
|
||||
if (departed != null) {
|
||||
persistenceStage = "CROSS_REGION_OUTBOX";
|
||||
nodeOutbox.publish(value, departed, requireContainerNo(containerBookingId));
|
||||
}
|
||||
persistenceStage = "QUERY_HISTORY";
|
||||
addHistory(value.getId(), "AUTO_CREATE", null, 0, "国内确认出仓自动创建", null, value,
|
||||
operatorId, operatorName);
|
||||
} catch (DataIntegrityViolationException exception) {
|
||||
throw conflict("船节点持久化契约与当前服务不一致(" + persistenceStage
|
||||
+ "),请确认OTHER/MANUAL约束及节点事实表已初始化");
|
||||
}
|
||||
CarrierScheduleNodeFactEntity departed = addNode(value.getId(), "FIRSTLEG", "OUTBOUND_CONFIRM", "OUTBOUND_CONFIRM",
|
||||
CarrierScheduleNode.DEPARTED_WAREHOUSE, now, null, now, null, null, "OUTBOUND_CONFIRM",
|
||||
sha256("OUTBOUND_CONFIRM|" + containerBookingId));
|
||||
if (departed != null) nodeOutbox.publish(value, departed, requireContainerNo(containerBookingId));
|
||||
addHistory(value.getId(), "AUTO_CREATE", null, 0, "国内确认出仓自动创建", null, value,
|
||||
operatorId, operatorName);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
+19
@@ -61,6 +61,25 @@ class CarrierScheduleNodeOutboxServiceTest {
|
||||
verify(mapper, never()).insert(org.mockito.ArgumentMatchers.any(InboundOutboxEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void initialOutboundDepartureUsesVersionZeroWithoutEnvelopeFailure() throws Exception {
|
||||
CarrierScheduleQueryEntity query = query();
|
||||
query.setVersion(0);
|
||||
CarrierScheduleNodeFactEntity fact = fact();
|
||||
fact.setStandardNode("DEPARTED_WAREHOUSE"); fact.setNodeSequence(10);
|
||||
fact.setProviderCode("FIRSTLEG"); fact.setProviderEventId("OUTBOUND_CONFIRM");
|
||||
fact.setProviderNodeCode("OUTBOUND_CONFIRM");
|
||||
|
||||
service.publish(query, fact, "CSNU1234567");
|
||||
|
||||
ArgumentCaptor<InboundOutboxEntity> captor = ArgumentCaptor.forClass(InboundOutboxEntity.class);
|
||||
verify(mapper).insert(captor.capture());
|
||||
EventEnvelope envelope = objectMapper.readValue(captor.getValue().getPayloadJson(), EventEnvelope.class);
|
||||
assertThat(envelope.aggregateVersion()).isZero();
|
||||
assertThat(envelope.handoverVersion()).isEqualTo(1L);
|
||||
assertThat(envelope.payload().path("standardNode").asText()).isEqualTo("DEPARTED_WAREHOUSE");
|
||||
}
|
||||
|
||||
private static CarrierScheduleQueryEntity query() {
|
||||
CarrierScheduleQueryEntity value = new CarrierScheduleQueryEntity();
|
||||
value.setId(2088000000000000001L); value.setCustomerId(2088000000000000002L);
|
||||
|
||||
+21
@@ -23,6 +23,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
@@ -166,6 +167,26 @@ class CarrierScheduleQueryServiceTest {
|
||||
verify(repository, times(1)).insertIfAbsent(any(CarrierScheduleNodeFactEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void outboundPersistenceContractMismatchReturnsExplicitBusinessConflict() {
|
||||
OceanBookingEntity booking = new OceanBookingEntity(); booking.setCustomerId(10001L);
|
||||
booking.setShippingCompanyName("示例船公司"); booking.setCarrierCode("OTHER");
|
||||
var container = new com.baiyue.firstleg.entity.ContainerBookingEntity();
|
||||
container.setContainerNo("CSNU1234567");
|
||||
when(portSchedules.oceanBooking(10L)).thenReturn(booking);
|
||||
when(portSchedules.containerBooking(20L)).thenReturn(container);
|
||||
when(portSchedules.allocation(10L, 20L)).thenReturn(new OceanBookingContainerAllocationEntity());
|
||||
org.mockito.Mockito.doThrow(new DataIntegrityViolationException("check constraint rejected"))
|
||||
.when(repository).insert(any(CarrierScheduleQueryEntity.class));
|
||||
|
||||
assertThatThrownBy(() -> service.ensureFromOutboundConfirm(10001L, 10L, 20L, 7L, "admin"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("船节点持久化契约与当前服务不一致")
|
||||
.hasMessageContaining("QUERY_TASK")
|
||||
.hasMessageContaining("OTHER/MANUAL");
|
||||
verify(repository, never()).insertIfAbsent(any(CarrierScheduleNodeFactEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void providerActualObservationAdvancesBusinessNodeWithoutChangingTaskStatus() {
|
||||
CarrierScheduleQueryEntity task = task(0, 0);
|
||||
|
||||
Reference in New Issue
Block a user