优化
This commit is contained in:
+4
@@ -57,6 +57,10 @@ class FleetHandoverPdaQueryTest {
|
||||
assertThrows(org.springframework.web.server.ResponseStatusException.class, () -> service.status(92, "PC-A"));
|
||||
verifyNoInteractions(history, handovers);
|
||||
}
|
||||
@Test void sameWarehouseEmptyEvidenceCannotBorrowTheTruckHeaderQuantity() {
|
||||
when(dispatches.transitEvidence("PC-A",false)).thenReturn(new FleetTransitEvidence("PC-A","92",30,List.of()));
|
||||
assertThrows(org.springframework.web.server.ResponseStatusException.class,()->service.status(92,"PC-A"));verifyNoInteractions(history,handovers);
|
||||
}
|
||||
@Test void controllerHasOnlyReadRouteAndRequiresThePdaIdentityPolicy() {
|
||||
var policy = PdaFleetHandoverController.class.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class).value();
|
||||
assertTrue(policy.contains("lastleg:pda:operate")); assertTrue(policy.contains("ROLE_OVERSEAS_WAREHOUSE_PDA_OPERATOR"));
|
||||
|
||||
-1
@@ -98,7 +98,6 @@ class FleetHandoverTransactionTest {
|
||||
assertEquals("11", result.snapshot().previous().vehicleId()); assertEquals("12", result.snapshot().replacement().vehicleId());
|
||||
assertEquals(result.handoverNo(), service.confirm("TR-ONE", "another-operator", command).handoverNo());
|
||||
verify(dispatches, times(1)).handover(any(), any());
|
||||
verify(dispatches, never()).depart(anyString(), anyString(), anyString(), anyString(), any(), any());
|
||||
assertEquals(1, repository.count(1));
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -46,13 +46,13 @@ class FleetPickupServiceTest {
|
||||
source.setDeliveryMethod("WAREHOUSE_PICKUP");when(repository.task(1L)).thenReturn(null);when(repository.lockTask(1L)).thenReturn(null);
|
||||
var other=source(2L,"C2","WT-2","200");when(repository.source("C2","WT-2",true)).thenReturn(other);
|
||||
var mixed=new FleetPickupModels.Create(List.of(new FleetPickupModels.Source("C1","WT-1"),new FleetPickupModels.Source("C2","WT-2")),plan.plannedDepartureAt(),plan.expectedDriverEndAt(),null,"create-key-2");
|
||||
assertThatThrownBy(()->service.create("OP",mixed)).hasMessageContaining("同一海外仓");verify(trips,never()).create(anyString(),any());
|
||||
assertThatThrownBy(()->service.create("OP",mixed)).hasMessageContaining("同一海外仓");verify(trips,never()).createPickup(anyString(),any());
|
||||
var duplicate=new FleetPickupModels.Create(List.of(plan.sources().get(0),plan.sources().get(0)),plan.plannedDepartureAt(),plan.expectedDriverEndAt(),null,"create-key-3");
|
||||
assertThatThrownBy(()->service.create("OP",duplicate)).hasMessageContaining("重复选择");
|
||||
}
|
||||
@Test void alreadyScheduledSourceIsNotScheduledAgain() {
|
||||
var plan=new FleetPickupModels.Create(List.of(new FleetPickupModels.Source("C1","WT-1")),Instant.now(),Instant.now().plusSeconds(7200),null,"create-key-1");
|
||||
assertThatThrownBy(()->service.create("OP",plan)).hasMessageContaining("已安排车次");verify(trips,never()).create(anyString(),any());
|
||||
assertThatThrownBy(()->service.create("OP",plan)).hasMessageContaining("已安排车次");verify(trips,never()).createPickup(anyString(),any());
|
||||
}
|
||||
@Test void pickupQuantityDoesNotOverwriteForecastAndReplayDoesNotWriteTwice() {
|
||||
when(repository.ownsUpload(1L,"TR-1",88L)).thenReturn(true);
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.baiyue.lastleg.fleet.trip;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.baiyue.lastleg.dispatch.port.*;
|
||||
import com.baiyue.lastleg.dispatch.service.DispatchAggregatePersistenceTest.Fixture;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/** 真实主单/Fleet/预订Mapper及事务;沿GET route所用应用读取检验站序,不调用环境HTTP。 */
|
||||
class FleetDispatchRoutePersistenceTest {
|
||||
@Test void lastPalletRemovalPrunesOnlyItsStopPreservesOrderAndAppendsNewDestinations() throws Exception {
|
||||
var s=new Scenario();var f=s.f;
|
||||
var created=f.tx.execute(t->f.service.create(f.actor,f.configured("route-prune",List.of(f.source(1,"101",501,"ONT8",3),f.source(2,"102",502,"LAX9",5),f.source(3,"103",503,"SEA1",4)))));
|
||||
var origin=s.route(created.dispatchNo()).stops().get(0);
|
||||
var ordered=f.tx.execute(t->f.service.reorderRoute(f.actor,created.dispatchNo(),new DispatchWorkModels.RouteCommand(created.recordVersion(),"route-order",List.of("FBA:SEA1","FBA:ONT8","FBA:LAX9"),"确认卸货顺序")));
|
||||
long item=s.item(1);var request=new DispatchCommands.ReasonCommand(ordered.recordVersion(),"remove-last-ont8","移出本目的地最后一托");
|
||||
var removed=f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),item,request));
|
||||
assertThat(s.keys(created.dispatchNo())).containsExactly("ORIGIN:9","FBA:SEA1","FBA:LAX9");
|
||||
var added=f.tx.execute(t->f.service.addCargo(f.actor,created.dispatchNo(),new DispatchCommands.AddCargo(removed.recordVersion(),"append-new-destination",List.of(f.source(4,"104",504,"ABE2",2)),List.of())));
|
||||
assertThat(s.keys(created.dispatchNo())).containsExactly("ORIGIN:9","FBA:SEA1","FBA:LAX9","FBA:ABE2");
|
||||
assertThat(s.route(created.dispatchNo()).stops().get(0)).isEqualTo(origin);
|
||||
var beforeReplay=s.state(created.dispatchNo());var replay=f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),item,request));
|
||||
assertThat(replay.replayed()).isTrue();assertThat(replay.recordVersion()).isEqualTo(removed.recordVersion());assertThat(s.state(created.dispatchNo())).isEqualTo(beforeReplay);assertThat(s.route(created.dispatchNo()).recordVersion()).isEqualTo(added.recordVersion());
|
||||
}
|
||||
|
||||
@Test void anotherPalletAtTheSameDestinationKeepsTheStopAndAnEmptyTruckKeepsOnlyItsOrigin() throws Exception {
|
||||
var s=new Scenario();var f=s.f;var created=f.tx.execute(t->f.service.create(f.actor,f.configured("route-same-destination",List.of(f.source(1,"101",501,"ONT8",3),f.source(2,"102",502,"ONT8",5)))));
|
||||
var origin=s.route(created.dispatchNo()).stops().get(0);String trip=s.trip();
|
||||
var preview=f.fleet.capacityPlan(trip);var measurements=preview.cargo().stream().map(c->new FleetExecutionModels.Measurement(c.warehousePalletId(),1,BigDecimal.TEN,BigDecimal.ONE,1)).toList();
|
||||
f.tx.execute(t->f.fleet.confirmCapacityPlan(trip,f.actor.operatorId(),new FleetExecutionModels.Confirm(preview.recordVersion(),preview.manifestHash(),measurements,"实际计划容量")));
|
||||
assertThat(f.db.queryForObject("SELECT planned_load_json FROM ll_fleet_trip",String.class)).isNotNull();
|
||||
var first=f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),s.item(1),new DispatchCommands.ReasonCommand(created.recordVersion(),"remove-first-same-destination","移出一托")));
|
||||
assertThat(s.keys(created.dispatchNo())).containsExactly("ORIGIN:9","FBA:ONT8");assertThat(f.db.queryForObject("SELECT planned_load_json FROM ll_fleet_trip",String.class)).isNull();
|
||||
var empty=f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),s.item(2),new DispatchCommands.ReasonCommand(first.recordVersion(),"remove-all-cargo","移出最后一托")));
|
||||
assertThat(s.keys(created.dispatchNo())).containsExactly("ORIGIN:9");assertThat(s.route(created.dispatchNo()).stops().get(0)).isEqualTo(origin);assertThat(empty.counts().packageCount()).isZero();assertThat(f.db.queryForObject("SELECT package_count FROM ll_fleet_trip_dispatch",Integer.class)).isZero();
|
||||
}
|
||||
|
||||
@ParameterizedTest @ValueSource(strings={"CONFIRMED","RECEIVED","BOUND"})
|
||||
void activeBookingKeepsItsOriginalStopAndCancelledBookingsDoNotLeaveGhostStops(String status) throws Exception {
|
||||
var s=new Scenario();var f=s.f;var created=f.tx.execute(t->f.service.create(f.actor,f.configured("route-booking-"+status,List.of(f.source(1,"101",501,"ONT8",3),f.source(2,"102",502,"LAX9",5)))));
|
||||
var ordered=f.tx.execute(t->f.service.reorderRoute(f.actor,created.dispatchNo(),new DispatchWorkModels.RouteCommand(created.recordVersion(),"booking-stop-order",List.of("FBA:ONT8","FBA:LAX9"),"原承诺站序")));
|
||||
s.booking(status,"FBA:ONT8");var original=s.route(created.dispatchNo()).stops();
|
||||
var removed=f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),s.item(1),new DispatchCommands.ReasonCommand(ordered.recordVersion(),"remove-booked-destination","保留有效外接卸货点")));
|
||||
assertThat(s.route(created.dispatchNo()).stops()).isEqualTo(original);
|
||||
f.db.update("UPDATE ll_fleet_external_booking SET status='CANCELLED'");
|
||||
f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),s.item(2),new DispatchCommands.ReasonCommand(removed.recordVersion(),"remove-after-booking-cancel","外接已取消后移出剩余货物")));
|
||||
assertThat(s.keys(created.dispatchNo())).containsExactly("ORIGIN:9");
|
||||
}
|
||||
|
||||
@Test void missingRequiredBookingStopRejectsTheWholeRemovalAndDoesNotInventAStop() throws Exception {
|
||||
var s=new Scenario();var f=s.f;var created=f.tx.execute(t->f.service.create(f.actor,f.configured("route-missing-booking",List.of(f.source(1,"101",501,"ONT8",3),f.source(2,"102",502,"LAX9",5)))));
|
||||
s.booking("CONFIRMED","FBA:UNSUPPORTED");var before=s.state(created.dispatchNo());
|
||||
assertThatThrownBy(()->f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),s.item(1),new DispatchCommands.ReasonCommand(created.recordVersion(),"reject-missing-booking-stop","外接目的地不完整")))).hasMessageContaining("已确认外接预订");
|
||||
assertThat(s.state(created.dispatchNo())).isEqualTo(before);assertThat(s.keys(created.dispatchNo())).doesNotContain("FBA:UNSUPPORTED");
|
||||
}
|
||||
|
||||
@Test void failureAfterTripRouteWriteRollsBackMainClaimsQuantitiesAndLedgerThenTheSameKeyCanSucceed() throws Exception {
|
||||
var s=new Scenario();var f=s.f;var created=f.tx.execute(t->f.service.create(f.actor,f.configured("route-write-rollback",List.of(f.source(1,"101",501,"ONT8",3),f.source(2,"102",502,"LAX9",5)))));
|
||||
long item=s.item(2);var request=new DispatchCommands.ReasonCommand(created.recordVersion(),"route-failed-original","实际事务回滚");var before=s.state(created.dispatchNo());
|
||||
f.db.execute("ALTER TABLE ll_fleet_trip_dispatch ADD CONSTRAINT reject_quantity_three CHECK(package_count<>3)");
|
||||
assertThatThrownBy(()->f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),item,request))).isInstanceOf(org.springframework.dao.DataIntegrityViolationException.class);
|
||||
assertThat(s.state(created.dispatchNo())).isEqualTo(before);
|
||||
f.db.execute("ALTER TABLE ll_fleet_trip_dispatch DROP CONSTRAINT reject_quantity_three");
|
||||
var saved=f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),item,request));assertThat(saved.replayed()).isFalse();assertThat(s.keys(created.dispatchNo())).containsExactly("ORIGIN:9","FBA:ONT8");
|
||||
var after=s.state(created.dispatchNo());assertThat(f.tx.execute(t->f.service.removeCargo(f.actor,created.dispatchNo(),item,request)).replayed()).isTrue();assertThat(s.state(created.dispatchNo())).isEqualTo(after);
|
||||
}
|
||||
|
||||
private record State(long version,int packages,String cargoHash,String review,long fleetVersion,String stops,String plan,int bindingPackages,long items,long activeClaims,long commands,long history,long fleetHistory) { }
|
||||
private static final class Scenario {
|
||||
final Fixture f=new Fixture();final FleetBookingMapper bookings;
|
||||
Scenario() throws Exception {
|
||||
f.session.getConfiguration().addMapper(FleetBookingMapper.class);bookings=f.session.getMapper(FleetBookingMapper.class);
|
||||
var table=TableInfoHelper.getTableInfo(FleetBookingEntity.class);var columns=new ArrayList<String>();columns.add(table.getKeyColumn()+" BIGINT PRIMARY KEY");
|
||||
for(var field:table.getFieldList())columns.add(field.getColumn()+" "+sqlType(field.getPropertyType()));
|
||||
f.db.execute("CREATE TABLE "+table.getTableName()+"("+String.join(",",columns)+")");
|
||||
var reservations=new FleetBookingReservations(bookings,f.json);ReflectionTestUtils.setField(f.nativeFleetService,"bookingReservations",reservations);ReflectionTestUtils.setField(f.fleet,"bookingReservations",reservations);
|
||||
}
|
||||
long item(long plan){return f.db.queryForObject("SELECT id FROM ll_dispatch_item WHERE plan_pallet_id=?",Long.class,plan);}
|
||||
String trip(){return f.db.queryForObject("SELECT trip_no FROM ll_fleet_trip",String.class);}
|
||||
DispatchWorkModels.Route route(String number){return f.tx.execute(t->f.service.route(f.actor,number));}
|
||||
List<String> keys(String number){return route(number).stops().stream().map(DispatchWorkModels.Stop::destinationKey).toList();}
|
||||
/** 预订是本切片的持久化输入;未声称执行原外接确认流程。 */
|
||||
void booking(String status,String destination) throws Exception {
|
||||
var row=new FleetBookingEntity();row.setId(901L);row.setTripId(f.db.queryForObject("SELECT id FROM ll_fleet_trip",Long.class));row.setTripNo(trip());row.setBookingNo("BOOKING-ROUTE");row.setCustomerId("201");row.setStatus(status);row.setDestinationKey(destination);row.setDestinationLabel(destination);row.setRecordVersion(0L);row.setCreatedAt(LocalDateTime.now(ZoneOffset.UTC));row.setUpdatedAt(row.getCreatedAt());
|
||||
row.setCargoJson(f.json.writeValueAsString(new FleetBookingModels.Cargo(destination,"测试外接货",1,1,BigDecimal.ONE,BigDecimal.ONE,null,null,null,null,null,null)));bookings.insert(row);
|
||||
}
|
||||
State state(String number){var root=f.repository.find(number);return new State(root.getRecordVersion(),root.getPackageCount(),root.getCargoHash(),root.getDispatchReviewJson(),f.db.queryForObject("SELECT record_version FROM ll_fleet_trip",Long.class),f.db.queryForObject("SELECT stops_json FROM ll_fleet_trip",String.class),f.db.queryForObject("SELECT planned_load_json FROM ll_fleet_trip",String.class),f.db.queryForObject("SELECT package_count FROM ll_fleet_trip_dispatch",Integer.class),count("ll_dispatch_item"),f.db.queryForObject("SELECT COUNT(*) FROM ll_dispatch_pallet_lock WHERE status='ACTIVE'",Long.class),count("ll_dispatch_command"),count("ll_dispatch_history"),count("ll_fleet_trip_history"));}
|
||||
long count(String table){return f.db.queryForObject("SELECT COUNT(*) FROM "+table,Long.class);}
|
||||
static String sqlType(Class<?> type){return type==Long.class||type==long.class?"BIGINT":type==Integer.class||type==int.class?"INT":type==BigDecimal.class?"DECIMAL(18,6)":type==LocalDateTime.class?"TIMESTAMP(3)":"VARCHAR(8000)";}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -19,6 +19,7 @@ class FleetTripApiContractTest {
|
||||
for(var spec:java.util.List.of(document,preparation,pool))paths.putAll((Map<?,?>)spec.get("paths"));
|
||||
String module = Files.readString(Path.of("../iam/src/main/resources/db/lastleg-permissions.sql"));
|
||||
module += Files.readString(Path.of("../iam/src/main/resources/db/fleet-capacity-offer-permissions.sql"));
|
||||
module += Files.readString(Path.of("../iam/src/main/resources/db/phase5-delivery-permissions.sql"));
|
||||
String deployed = Files.readString(Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql"));
|
||||
int count = 0;
|
||||
for (var controller : java.util.List.of(PlatformFleetTripController.class, PlatformFleetBookingController.class, PlatformFleetCapacityOfferController.class)) {
|
||||
@@ -46,7 +47,7 @@ class FleetTripApiContractTest {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
assertThat(count).isEqualTo(54);
|
||||
assertThat(count).isEqualTo(46);
|
||||
verifyRefs(document, document);
|
||||
verifyRefs(preparation, preparation);
|
||||
verifyRefs(pool, pool);
|
||||
|
||||
+5
-4
@@ -4,7 +4,8 @@ import com.baiyue.lastleg.dispatch.service.DispatchAggregatePersistenceTest.Fixt
|
||||
import com.baiyue.lastleg.dispatch.port.*;
|
||||
import com.baiyue.lastleg.delivery.entity.*;
|
||||
import com.baiyue.lastleg.delivery.mapper.*;
|
||||
import com.baiyue.lastleg.masterdata.vehicle.application.service.VehicleService;
|
||||
import com.baiyue.lastleg.masterdata.vehicle.application.VehicleApplicationService;
|
||||
import com.baiyue.lastleg.masterdata.vehicle.application.port.out.VehicleRepository;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.*;
|
||||
@@ -51,7 +52,7 @@ class FleetTripPersistenceTest {
|
||||
var scenario=new CompletedCargo();var f=scenario.f;var inspection=scenario.completion.inspection(trip(f));assertThat(inspection.canComplete()).isTrue();
|
||||
var request=new FleetCompletionModels.Complete(inspection.recordVersion(),"complete-original","全部货物实际履约");
|
||||
var complete=f.tx.execute(s->scenario.completion.complete(trip(f),"operator",request));assertThat(complete.trip().status()).isEqualTo("COMPLETED");
|
||||
assertThat(f.tx.execute(s->scenario.completion.complete(trip(f),"operator",request))).isEqualTo(complete);
|
||||
FleetTripModels.Detail recovered=f.tx.execute(s->scenario.completion.complete(trip(f),"operator",request));assertThat(recovered).isEqualTo(complete);
|
||||
assertThat(complete.trip().vehicleRelease()).isNull();assertThat(complete.trip().driverRelease()).isNull();
|
||||
var end=Instant.now().minusSeconds(10);var vehicle=new FleetCompletionModels.Release(complete.trip().recordVersion(),"VEHICLE",end,"实际交车点","RETURNED","vehicle-end-original","外配车实际结束");
|
||||
var released=f.tx.execute(s->scenario.completion.release(trip(f),"operator",vehicle));assertThat(released.trip().vehicleRelease()).isNotNull();assertThat(released.trip().driverRelease()).isNull();
|
||||
@@ -75,14 +76,14 @@ class FleetTripPersistenceTest {
|
||||
private static String trip(Fixture f){return f.db.queryForObject("SELECT trip_no FROM ll_fleet_trip",String.class);}
|
||||
/** 已发运起始事实作为本切片fixture;客户单据实际Mapper读取,未模拟仓储查询成功。 */
|
||||
private static final class CompletedCargo {
|
||||
final Fixture f;final VehicleService vehicleService=mock(VehicleService.class);final FleetTripCompletionService completion;
|
||||
final Fixture f;final VehicleApplicationService vehicleService=mock(VehicleApplicationService.class);final FleetTripCompletionService completion;
|
||||
CompletedCargo() throws Exception {
|
||||
f=new Fixture();var created=f.tx.execute(s->f.service.create(f.actor,f.configured("fleet-completion",List.of(f.source(1,"101",501,"ONT8",3)))));f.approve(created);f.picked(1,"101",501,3);
|
||||
f.db.update("UPDATE ll_dispatch_order SET stage='DELIVERING',picked_package_count=3,shipped_package_count=3,departed_at=?",LocalDateTime.now(ZoneOffset.UTC).minusMinutes(10));
|
||||
f.db.update("UPDATE ll_fleet_trip SET status='IN_TRANSIT',departed_at=?",LocalDateTime.now(ZoneOffset.UTC).minusMinutes(10));
|
||||
var root=f.repository.find(created.dispatchNo());var outbound=new OutboundOrder();outbound.setId(600L);outbound.setCustomerId("101");outbound.setOutboundNo("OB-REAL");outbound.setDispatchOrderId(root.getId());outbound.setOverseasWarehouseId("9");outbound.setStatus("OUTBOUNDED");outbound.setPackageCount(3);f.session.getMapper(OutboundOrderMapper.class).insert(outbound);
|
||||
var delivery=new DeliveryOrder();delivery.setId(700L);delivery.setCustomerId("101");delivery.setDeliveryNo("DL-REAL");delivery.setDispatchOrderId(root.getId());delivery.setOutboundOrderId(600L);delivery.setOverseasWarehouseId("9");delivery.setStatus("DELIVERED");delivery.setPackageCount(3);delivery.setSignedPackages(3);delivery.setSignedAt(LocalDateTime.now().minusMinutes(5));delivery.setVersion(0L);delivery.setDestinationType("FBA");delivery.setDestinationWarehouseCode("ONT8");f.session.getMapper(DeliveryOrderMapper.class).insert(delivery);
|
||||
completion=new FleetTripCompletionService(f.session.getMapper(FleetTripMapper.class),f.session.getMapper(FleetTripDispatchMapper.class),f.session.getMapper(FleetResourceMapper.class),(FleetDispatchGateway)org.springframework.test.util.ReflectionTestUtils.getField(f.nativeFleetService,"dispatches"),f.nativeFleetService,vehicleService,f.json);
|
||||
completion=new FleetTripCompletionService(f.session.getMapper(FleetTripMapper.class),f.session.getMapper(FleetTripDispatchMapper.class),f.session.getMapper(FleetResourceMapper.class),(FleetDispatchGateway)org.springframework.test.util.ReflectionTestUtils.getField(f.nativeFleetService,"dispatches"),f.nativeFleetService,mock(VehicleRepository.class),vehicleService,f.json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -50,7 +50,8 @@ public class VehicleFulfillmentQueryTest {
|
||||
jdbc.execute("CREATE ALIAS JSON_CONTAINS FOR 'com.baiyue.lastleg.fleet.trip.VehicleFulfillmentQueryTest.jsonContains'");
|
||||
jdbc.execute("CREATE ALIAS JSON_SET FOR 'com.baiyue.lastleg.fleet.trip.VehicleFulfillmentQueryTest.jsonSet'");
|
||||
jdbc.execute("CREATE TABLE ll_fleet_trip(id BIGINT PRIMARY KEY,trip_no VARCHAR(64),vehicle_id BIGINT,status VARCHAR(32))");
|
||||
jdbc.execute("CREATE TABLE ll_fleet_trip_dispatch(dispatch_id BIGINT PRIMARY KEY,trip_id BIGINT,customer_id VARCHAR(32))");
|
||||
jdbc.execute("CREATE TABLE ll_fleet_trip_dispatch(dispatch_id BIGINT PRIMARY KEY,trip_id BIGINT UNIQUE)");
|
||||
jdbc.execute("CREATE TABLE ll_dispatch_item(id BIGINT PRIMARY KEY,dispatch_order_id BIGINT,customer_id VARCHAR(32))");
|
||||
jdbc.execute("CREATE TABLE ll_fleet_trip_history(id BIGINT PRIMARY KEY,trip_id BIGINT,record_version BIGINT,occurred_at TIMESTAMP,action VARCHAR(64),reason VARCHAR(255),snapshot_json CLOB,UNIQUE(trip_id,record_version))");
|
||||
jdbc.execute("CREATE TABLE ll_fleet_trip_handover(id BIGINT PRIMARY KEY,trip_id BIGINT,trip_version BIGINT,snapshot_json CLOB,UNIQUE(trip_id,trip_version))");
|
||||
jdbc.execute("CREATE TABLE ll_delivery_order(id BIGINT PRIMARY KEY,delivery_no VARCHAR(64),dispatch_no VARCHAR(64),dispatch_order_id BIGINT,customer_id VARCHAR(32),customer_name VARCHAR(100),bol_no VARCHAR(64),destination_type VARCHAR(32),destination_warehouse_code VARCHAR(64),destination_warehouse_name VARCHAR(100),delivery_address VARCHAR(255),planned_delivery_at TIMESTAMP,status VARCHAR(32),created_at TIMESTAMP)");
|
||||
@@ -61,8 +62,9 @@ public class VehicleFulfillmentQueryTest {
|
||||
when(vehicles.findById(anyLong())).thenReturn(Optional.of(mock(VehicleRepository.VehicleSnapshot.class)));
|
||||
service = new VehicleFulfillmentService(vehicles, mapper, JSON);
|
||||
jdbc.update("INSERT INTO ll_fleet_trip VALUES(1,'TR-1',8,'PLANNED')");
|
||||
jdbc.update("INSERT INTO ll_fleet_trip_dispatch VALUES(11,1,'CUSTOMER-A'),(12,1,'CUSTOMER-B')");
|
||||
delivery(21,11,"CUSTOMER-A"); delivery(22,12,"CUSTOMER-B");
|
||||
jdbc.update("INSERT INTO ll_fleet_trip_dispatch VALUES(11,1)");
|
||||
jdbc.update("INSERT INTO ll_dispatch_item VALUES(1,11,'CUSTOMER-A'),(2,11,'CUSTOMER-B')");
|
||||
delivery(21,11,"CUSTOMER-A"); delivery(22,11,"CUSTOMER-B");
|
||||
}
|
||||
private void delivery(long deliveryId, long dispatchId, String customer) {
|
||||
jdbc.update("INSERT INTO ll_delivery_order(id,delivery_no,dispatch_no,dispatch_order_id,customer_id,customer_name,destination_type,destination_warehouse_code,status,created_at) VALUES(?,?,?,?,?,?,'FBA','ONT8','WAIT_DELIVERY',TIMESTAMP '2026-10-03 00:00:00')",
|
||||
@@ -72,7 +74,7 @@ public class VehicleFulfillmentQueryTest {
|
||||
var trip = new HashMap<String,Object>();
|
||||
if (vehicle != null) { trip.put("vehicleId",vehicle); trip.put("vehiclePlate","PLATE-"+vehicle); }
|
||||
if (driver != null) { trip.put("driverId",driver); trip.put("driverName","司机"+driver); }
|
||||
String snapshot = JSON.writeValueAsString(Map.of("trip",trip,"dispatches",List.of(Map.of("dispatchNo","DP-11"),Map.of("dispatchNo","DP-12"))));
|
||||
String snapshot = JSON.writeValueAsString(Map.of("trip",trip,"dispatches",List.of(Map.of("dispatchNo","DP-11"))));
|
||||
jdbc.update("INSERT INTO ll_fleet_trip_history VALUES(?,1,?,TIMESTAMP '2026-10-03 00:00:00',?,'更换车辆',?)",version+1,version,action,snapshot);
|
||||
}
|
||||
@Test void currentPlanUsesStableVehicleIdentityAndCustomerScopedDispatchJoin() {
|
||||
@@ -108,7 +110,7 @@ public class VehicleFulfillmentQueryTest {
|
||||
assertThat(in.records().get(0).relationship()).isEqualTo("REPLACED_IN");
|
||||
assertThat(out.records().get(0).previousVehiclePlate()).isEqualTo("PLATE-7");
|
||||
assertThat(out.records().get(0).vehiclePlate()).isEqualTo("PLATE-8");
|
||||
assertThat(out.records().get(0).dispatchNos()).containsExactly("DP-11","DP-12");
|
||||
assertThat(out.records().get(0).dispatchNos()).containsExactly("DP-11");
|
||||
assertThat(out.records().get(0).occurredAt().toString()).isEqualTo("2026-10-03T00:00:00Z");
|
||||
assertThat(service.changes(77,new VehicleFulfillmentModels.Query(1,10)).total()).isZero();
|
||||
}
|
||||
@@ -127,14 +129,14 @@ public class VehicleFulfillmentQueryTest {
|
||||
jdbc.update("UPDATE ll_fleet_trip SET vehicle_id=9,status='IN_TRANSIT'");
|
||||
String snapshot = JSON.writeValueAsString(Map.of("replacement", Map.of("vehicleId", "9"),
|
||||
"carriedDeliveries", List.of(Map.of("customerId", "CUSTOMER-B", "deliveryNo", "DL-22")),
|
||||
"cargo", Map.of("remainingCargo", List.of(Map.of("dispatchNo", "DP-12"), Map.of("dispatchNo", "DP-12")))));
|
||||
"cargo", Map.of("remainingCargo", List.of(Map.of("dispatchNo", "DP-11"), Map.of("dispatchNo", "DP-11")))));
|
||||
jdbc.update("INSERT INTO ll_fleet_trip_handover VALUES(1,1,1,?)", snapshot);
|
||||
assertThat(service.deliveries(8,new VehicleFulfillmentModels.Query(1,10)).records()).hasSize(2);
|
||||
assertThat(service.deliveries(9,new VehicleFulfillmentModels.Query(1,10)).records())
|
||||
.extracting(VehicleFulfillmentModels.Delivery::deliveryNo).containsExactly("DL-22");
|
||||
assertThat(service.changes(9,new VehicleFulfillmentModels.Query(1,10)).records()).singleElement().satisfies(change -> {
|
||||
assertThat(change.relationship()).isEqualTo("REPLACED_IN");
|
||||
assertThat(change.dispatchNos()).containsExactly("DP-12");
|
||||
assertThat(change.dispatchNos()).containsExactly("DP-11");
|
||||
});
|
||||
jdbc.update("UPDATE ll_delivery_order SET status='DELIVERED' WHERE id=22");
|
||||
assertThat(service.deliveries(9,new VehicleFulfillmentModels.Query(1,10)).total()).isEqualTo(1);
|
||||
|
||||
Reference in New Issue
Block a user