feat: add self-operated analytics and enforce Lastleg module boundaries
This commit is contained in:
+20
-32
@@ -1,15 +1,11 @@
|
||||
package com.baiyue.lastleg.fleet.business;
|
||||
|
||||
import com.baiyue.lastleg.delivery.entity.DeliveryOrder;
|
||||
import com.baiyue.lastleg.delivery.mapper.DeliverySourceEvidenceMapper;
|
||||
import com.baiyue.lastleg.delivery.model.SourceDeliveryCompletionPolicy;
|
||||
import com.baiyue.lastleg.fleet.trip.FleetBookingIncomingGateway;
|
||||
import com.baiyue.lastleg.looseboard.entity.LocalLooseboardOrder;
|
||||
import com.baiyue.lastleg.looseboard.mapper.*;
|
||||
import com.baiyue.lastleg.trucking.entity.UsTruckOrder;
|
||||
import com.baiyue.lastleg.trucking.entity.UsTruckOrderStatusHistory;
|
||||
import com.baiyue.lastleg.trucking.mapper.*;
|
||||
import com.baiyue.lastleg.service.LastlegOrderNoGenerator;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.transaction.annotation.*;
|
||||
@@ -26,15 +22,14 @@ class FleetPoolOrderBridgeTest {
|
||||
LocalLooseboardHistoryMapper looseHistory=mock(LocalLooseboardHistoryMapper.class);
|
||||
UsTruckOrderMapper trucks=mock(UsTruckOrderMapper.class);
|
||||
UsTruckOrderStatusHistoryMapper truckHistory=mock(UsTruckOrderStatusHistoryMapper.class);
|
||||
LastlegOrderNoGenerator numbers=mock(LastlegOrderNoGenerator.class);
|
||||
DeliverySourceEvidenceMapper evidence=mock(DeliverySourceEvidenceMapper.class);
|
||||
FleetPoolOrderNumberGenerator numbers=mock(FleetPoolOrderNumberGenerator.class);
|
||||
FleetPoolDeliveryEvidence evidence=mock(FleetPoolDeliveryEvidence.class);
|
||||
com.baiyue.lastleg.appointmentplanning.AppointmentSourceLinker appointmentSources=mock(com.baiyue.lastleg.appointmentplanning.AppointmentSourceLinker.class);
|
||||
FleetPoolOrderBridge service;
|
||||
FleetPoolOrderLink row; LocalLooseboardOrder looseOrder; UsTruckOrder truckOrder;
|
||||
@BeforeEach void setup(){
|
||||
service=new FleetPoolOrderBridge(links,loose,looseHistory,trucks,truckHistory,numbers,new ObjectMapper().findAndRegisterModules(),evidence,appointmentSources);
|
||||
when(evidence.dispatchIds("9",77)).thenReturn(List.of(70L));
|
||||
when(evidence.returns("9",70)).thenReturn(List.of());
|
||||
|
||||
when(numbers.next(anyString(),any(),anyLong())).thenReturn("LOCAL-001");
|
||||
when(links.lockBooking(7L)).thenAnswer(a->row);when(links.lockBookingNo("FB-7")).thenAnswer(a->row);
|
||||
when(links.lockIncoming("9","WT-7")).thenAnswer(a->row);
|
||||
@@ -66,16 +61,13 @@ class FleetPoolOrderBridgeTest {
|
||||
doThrow(new IllegalStateException("appointment binding conflict")).when(appointmentSources).materialized("9","FB-7",77L);
|
||||
assertThatThrownBy(this::incoming).isInstanceOf(IllegalStateException.class).hasMessageContaining("appointment binding conflict");
|
||||
}
|
||||
FleetBookingIncomingGateway.Evidence receipt(){return new FleetBookingIncomingGateway.Evidence("WT-7","WT-7","92","FBA:ONT8","READY_FOR_DISPATCH","hash",List.of(
|
||||
new FleetBookingIncomingGateway.Pallet("101","PALLET-1",2,BigDecimal.TEN,BigDecimal.ONE),
|
||||
new FleetBookingIncomingGateway.Pallet("102","PALLET-2",2,BigDecimal.TEN,BigDecimal.ONE)),4);}
|
||||
Receipt receipt(){return new Receipt("WT-7","92","FBA:ONT8","READY_FOR_DISPATCH",List.of(
|
||||
new ReceivedPallet("101",2),new ReceivedPallet("102",2)),4);}
|
||||
void dispatch(String type){service.confirm(input(type),"op");incoming();service.receiving("9","WT-7","pda");
|
||||
service.acceptedReceipt(7,receipt(),3,"op");service.boundDispatch(7,"9","DISPATCH-7","op");}
|
||||
DeliveryOrder delivery(int signed){var d=new DeliveryOrder();d.setId(71L);d.setDispatchOrderId(70L);d.setDeliveryGroupKey("CODE:ONT8");d.setCustomerId("9");d.setDispatchNo("DISPATCH-7");d.setDeliveryNo("DELIVERY-7");
|
||||
d.setStatus("DELIVERED");d.setPackageCount(4);d.setSignedPackages(signed);d.setSignedAt(LocalDateTime.now());
|
||||
when(evidence.deliveries("9",70)).thenReturn(List.of(new SourceDeliveryCompletionPolicy.Delivery(71,70,"CODE:ONT8",4,(long)signed,d.getSignedAt(),d.getSignedAt().minusHours(1))));
|
||||
when(evidence.picks("9",70)).thenReturn(List.of(new SourceDeliveryCompletionPolicy.Pick(1,70,"CODE:ONT8",77,4,1L,null,null,null)));
|
||||
return d;}
|
||||
SignedDelivery delivery(int signed){
|
||||
when(evidence.fullySigned("9","WT-7",77,4)).thenReturn(signed>=4);
|
||||
return new SignedDelivery("9","DISPATCH-7","DELIVERY-7",true,LocalDateTime.now());}
|
||||
@Test void looseboardConfirmationCreatesOnlyCommercialRecordAndReplaysWithoutAnotherOrder(){
|
||||
var result=service.confirm(input("LOCAL_LOOSEBOARD"),"op");
|
||||
assertThat(result.businessOrderNo()).isEqualTo("LOCAL-001");assertThat(looseOrder.getLastlegOrderId()).isNull();
|
||||
@@ -130,9 +122,9 @@ class FleetPoolOrderBridgeTest {
|
||||
@Test void incomingBlocksCancellationAndWrongReceiptIdentityOrDuplicatePhysicalPallets(){
|
||||
service.confirm(input("US_TRUCK"),"op");incoming();
|
||||
assertThatThrownBy(()->service.cancelBeforeIncoming(7,"9","op","取消")).hasMessageContaining("已登记来货");
|
||||
var r=receipt();var wrong=new FleetBookingIncomingGateway.Evidence("OTHER",r.inboundCode(),r.warehouseId(),r.destinationKey(),r.status(),r.receiptHash(),r.pallets(),4);
|
||||
var r=receipt();var wrong=new Receipt("OTHER",r.warehouseId(),r.destinationKey(),r.status(),r.pallets(),4);
|
||||
assertThatThrownBy(()->service.acceptedReceipt(7,wrong,3,"op")).hasMessageContaining("来源");
|
||||
var duplicate=new FleetBookingIncomingGateway.Evidence(r.incomingNo(),r.inboundCode(),r.warehouseId(),r.destinationKey(),r.status(),r.receiptHash(),List.of(r.pallets().get(0),r.pallets().get(0)),4);
|
||||
var duplicate=new Receipt(r.incomingNo(),r.warehouseId(),r.destinationKey(),r.status(),List.of(r.pallets().get(0),r.pallets().get(0)),4);
|
||||
assertThatThrownBy(()->service.acceptedReceipt(7,duplicate,3,"op")).hasMessageContaining("重复");
|
||||
}
|
||||
@Test void actualDepartureAndFullSignatureDriveLooseboardExactlyOnce(){
|
||||
@@ -157,22 +149,15 @@ class FleetPoolOrderBridgeTest {
|
||||
assertThatThrownBy(()->service.confirm(missing,"op")).hasMessageContaining("独立实托数");
|
||||
verifyNoInteractions(trucks,loose,links);
|
||||
}
|
||||
@Test void anotherCoShippedSourceReturnDoesNotBlockThisFullySignedCommercialOrder(){
|
||||
@Test void deliveryOwnedProofCompletesOnlyThisCommercialSource(){
|
||||
dispatch("US_TRUCK");service.departed("9","DISPATCH-7",Map.of(77L,4),"op",LocalDateTime.now());
|
||||
var d=delivery(6);d.setPackageCount(8);
|
||||
when(evidence.deliveries("9",70)).thenReturn(List.of(new SourceDeliveryCompletionPolicy.Delivery(71,70,"CODE:ONT8",8,6L,d.getSignedAt(),d.getSignedAt().minusHours(1))));
|
||||
when(evidence.picks("9",70)).thenReturn(List.of(new SourceDeliveryCompletionPolicy.Pick(1,70,"CODE:ONT8",77,4,1L,null,null,null),
|
||||
new SourceDeliveryCompletionPolicy.Pick(2,70,"CODE:ONT8",78,4,2L,null,null,null)));
|
||||
when(evidence.returns("9",70)).thenReturn(List.of(new SourceDeliveryCompletionPolicy.Returned(9,71,78,2,d.getSignedAt().minusMinutes(5))));
|
||||
service.signed(d,Map.of(77L,4L,78L,4L),"op",LocalDateTime.now());assertThat(truckOrder.getStatus()).isEqualTo("COMPLETED");
|
||||
service.signed(delivery(4),Map.of(77L,4L,78L,4L),"op",LocalDateTime.now());
|
||||
assertThat(truckOrder.getStatus()).isEqualTo("COMPLETED");verify(evidence).fullySigned("9","WT-7",77,4);
|
||||
}
|
||||
@Test void thisSourceReturnOrMissingHistoryCannotBeCountedAsWholeSignature(){
|
||||
@Test void missingDeliveryOwnedProofCannotCompleteCommercialOrder(){
|
||||
dispatch("US_TRUCK");service.departed("9","DISPATCH-7",Map.of(77L,4),"op",LocalDateTime.now());
|
||||
var d=delivery(2);
|
||||
when(evidence.returns("9",70)).thenReturn(List.of(new SourceDeliveryCompletionPolicy.Returned(9,71,77,2,d.getSignedAt().minusMinutes(5))));
|
||||
service.signed(d,Map.of(77L,4L),"op",LocalDateTime.now());assertThat(truckOrder.getStatus()).isEqualTo("IN_TRANSIT");
|
||||
d=delivery(4);when(evidence.dispatchIds("9",77)).thenReturn(List.of());
|
||||
service.signed(d,Map.of(77L,4L),"op",LocalDateTime.now());assertThat(truckOrder.getStatus()).isEqualTo("IN_TRANSIT");
|
||||
service.signed(delivery(2),Map.of(77L,4L),"op",LocalDateTime.now());
|
||||
assertThat(truckOrder.getStatus()).isEqualTo("IN_TRANSIT");
|
||||
}
|
||||
@Test void bindingDoesNotMeanDepartureAndCannotCompleteBeforeActualDeparture(){
|
||||
dispatch("US_TRUCK");assertThat(truckOrder.getStatus()).isEqualTo("PENDING_DISPATCH");
|
||||
@@ -185,6 +170,9 @@ class FleetPoolOrderBridgeTest {
|
||||
assertThat(method.getAnnotation(Transactional.class).propagation()).isEqualTo(Propagation.MANDATORY);
|
||||
assertThat(Arrays.stream(FleetPoolOrderBridge.class.getDeclaredFields()).map(f->f.getType().getSimpleName()))
|
||||
.noneMatch(name->name.contains("FleetTrip")||name.contains("FleetBooking")||name.contains("Fee")||name.contains("Inventory"));
|
||||
assertThat(Arrays.stream(FleetPoolOrderBridge.class.getDeclaredFields()).map(f->f.getType().getPackageName()))
|
||||
.noneMatch(name->name.startsWith("com.baiyue.lastleg.delivery")||name.equals("com.baiyue.lastleg.fleet.trip")
|
||||
||name.equals("com.baiyue.lastleg.service"));
|
||||
}
|
||||
@Test void poolOnlyCallbacksCannotSucceedWithoutTheirCommercialLink(){
|
||||
assertThatThrownBy(()->service.acceptedReceipt(7,receipt(),1,"op")).hasMessageContaining("缺少唯一商业订单关联");
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.baiyue.lastleg.fleet.business;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
class FleetPoolOrderNumberGeneratorTest {
|
||||
private final FleetPoolOrderNumberGenerator generator=new FleetPoolOrderNumberGenerator();
|
||||
@Test void existingLooseboardAndTruckFormatLocationAndSuffixArePreserved(){
|
||||
String day=LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
|
||||
assertThat(generator.next("LOOSEBOARD","FBA:ONT8",1234567)).isEqualTo("LB-ONT-"+day+"-234567");
|
||||
assertThat(generator.next("TRUCK","nyc",1000001)).isEqualTo("TR-NYC-"+day+"-000001");
|
||||
}
|
||||
@Test void existingPrivateDestinationAndShortCodeRulesArePreserved(){
|
||||
String day=LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
|
||||
assertThat(generator.next("TRUCK","private address",9)).isEqualTo("TR-WH-"+day+"-000009");
|
||||
assertThat(generator.next("LOOSEBOARD","ab12",9)).isEqualTo("LB-AB12-"+day+"-000009");
|
||||
assertThat(generator.next("TRUCK",null,-1)).isEqualTo("TR-WH-"+day+"-999999");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user