This commit is contained in:
2026-09-16 11:06:33 +08:00
parent a4c659f4c6
commit a098c4377c
46 changed files with 356 additions and 109 deletions
@@ -92,7 +92,7 @@ class OceanBookingServiceTest {
private void saveAndReleaseWithoutCustomer(boolean initialCustomer) throws Exception {
stubMasterData();
when(suppliers.booking(81L, "订舱代理")).thenReturn(
new WarehouseSupplierClient.Snapshot(81L, "订舱代理", "BOOKING", "ACTIVE", List.of("MATSON")));
new WarehouseSupplierClient.Snapshot(81L, "订舱代理", "BOOKING", "ACTIVE", List.of("MATSON"), null));
var tree = objectMapper.valueToTree(completeDraft());
((com.fasterxml.jackson.databind.node.ObjectNode) tree).putNull("customerId").putNull("customerName");
DraftData emptyCustomer = objectMapper.treeToValue(tree, DraftData.class);
@@ -243,7 +243,7 @@ class OceanBookingServiceTest {
stubMasterData();
when(suppliers.booking(81L, null)).thenReturn(
new WarehouseSupplierClient.Snapshot(81L, "订舱代理", "BOOKING", "ACTIVE", List.of("SML")));
new WarehouseSupplierClient.Snapshot(81L, "订舱代理", "BOOKING", "ACTIVE", List.of("SML"), null));
assertThatThrownBy(() -> service.create(9L, "admin", "agent-mismatch",
new CreateRequest(0, completeDraft())))
.isInstanceOf(BizException.class).hasMessage("所选订舱代理未关联当前船公司");
@@ -253,8 +253,8 @@ class OceanBookingServiceTest {
@Test
void bookingAgentCandidatesAreFilteredByCarrierCode() {
when(suppliers.bookingCandidates("MATSON")).thenReturn(List.of(
new WarehouseSupplierClient.Snapshot(81L, "匹配代理", "BOOKING", "ACTIVE", List.of("MATSON")),
new WarehouseSupplierClient.Snapshot(82L, "不匹配代理", "BOOKING", "ACTIVE", List.of("SML"))));
new WarehouseSupplierClient.Snapshot(81L, "匹配代理", "BOOKING", "ACTIVE", List.of("MATSON"), null),
new WarehouseSupplierClient.Snapshot(82L, "不匹配代理", "BOOKING", "ACTIVE", List.of("SML"), null)));
assertThat(service.bookingAgents("matson")).extracting(BookingAgentOption::name)
.containsExactly("匹配代理");
@@ -268,7 +268,7 @@ class OceanBookingServiceTest {
when(repository.parties(1L)).thenReturn(parties());
when(repository.requirements(1L)).thenReturn(List.of(requirement()));
when(suppliers.booking(81L, null)).thenReturn(
new WarehouseSupplierClient.Snapshot(81L, "订舱代理", "BOOKING", "ACTIVE", List.of("SML")));
new WarehouseSupplierClient.Snapshot(81L, "订舱代理", "BOOKING", "ACTIVE", List.of("SML"), null));
assertThatThrownBy(() -> service.release(1L, 9L, "admin", "release-agent-mismatch",
new ReleaseRequest(3, "确认")))
@@ -392,7 +392,7 @@ class OceanBookingServiceTest {
new WarehouseMasterDataClient.Detail(new WarehouseMasterDataClient.Warehouse(
701L, 0L, "SZX", null, "深圳中转仓", "DOMESTIC", "ACTIVE"))));
when(suppliers.booking(81L, null)).thenReturn(
new WarehouseSupplierClient.Snapshot(81L, "订舱代理", "BOOKING", "ACTIVE", List.of("MATSON")));
new WarehouseSupplierClient.Snapshot(81L, "订舱代理", "BOOKING", "ACTIVE", List.of("MATSON"), null));
when(partners.active(any(), eq(0L), eq(71L))).thenReturn(ApiResponse.success(
new com.baiyue.firstleg.client.WarehousePartnerClient.Snapshot(71L, 0L, "船公司", "CARRIER",
"MATSON", List.of("CARRIER"), "ACTIVE")));
@@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.Collection;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainerQuery;
import com.baiyue.firstleg.dto.SmartLoadingDtos.InboundCandidateQuery;
@@ -34,6 +35,12 @@ class SmartLoadingCandidateMapperContractTest {
.contains("WHERE c.pallet_id=#{palletId}")
.contains("GROUP BY o.tenant_id,o.id")
.doesNotContain("#{inboundId}");
String planMembers = String.join(" ", com.baiyue.firstleg.mapper.DropshipPalletizationMapper.class
.getDeclaredMethod("palletInboundMembersByPalletIds", Collection.class)
.getAnnotation(Select.class).value());
assertThat(planMembers).contains("COUNT(DISTINCT c.id) availablePackageCount")
.contains("<foreach collection=\"palletIds\"")
.contains("GROUP BY c.pallet_id,o.tenant_id,o.id");
}
@Test
void candidateHasOnlyTheCanonicalMybatisConstructor() {
@@ -220,7 +220,7 @@ class ContainerFeeDetailServiceTest {
when(feeItems.findItem(11L)).thenReturn(item());
when(suppliers.fee(71L, null)).thenReturn(
new com.baiyue.firstleg.client.WarehouseSupplierClient.Snapshot(
71L, "现有供应商", "TRUCKING", "ACTIVE", List.of()));
71L, "现有供应商", "TRUCKING", "ACTIVE", List.of(), "DOMESTIC"));
LocalDateTime paidAt = LocalDateTime.of(2026, 8, 20, 10, 0);
var request = new ContainerFeeDetailDtos.SaveRequest(11L, "PAYABLE",
LocalDate.of(2026, 8, 20), new BigDecimal("300.00"), "CNY", 71L,
@@ -57,11 +57,11 @@ class ContainerServiceTest {
when(smartLoadingService.createForContainer(anyLong(), anyLong(), anyString())).thenAnswer(invocation ->
new com.baiyue.firstleg.dto.SmartLoadingDtos.Plan(501L, "LP-260818-0001", "EDITING",
invocation.getArgument(0, Long.class), null, null, 0, true, true, null,
"NOT_STARTED", null, null, null, List.of(), List.of()));
"NOT_STARTED", null, null, null, List.of(), List.of(), List.of()));
when(warehouses.active(anyString(), anyLong(), anyLong())).thenAnswer(invocation ->
activeWarehouse(invocation.getArgument(2, Long.class), "DOMESTIC", "上海中转仓"));
when(suppliers.trucking(202L, "拖车公司A"))
.thenReturn(new WarehouseSupplierClient.Snapshot(202L, "拖车公司A", "TRUCKING", "ACTIVE", List.of()));
when(suppliers.overseasTrucking(202L, "拖车公司A"))
.thenReturn(new WarehouseSupplierClient.Snapshot(202L, "拖车公司A", "TRUCKING", "ACTIVE", List.of(), "OVERSEAS"));
}
@Test
@@ -159,6 +159,40 @@ class SmartLoadingServiceTest {
verify(coloadGroups,never()).findByInbound(anyLong(),anyLong());
}
@Test
void returnsEveryInboundCompositionWhenAPlanCollapsesASealedPalletToOneRow() {
LoadingPlanEntity plan = plan("EDITING", 2);
when(repository.plan(1L)).thenReturn(plan);
when(repository.booking(99L)).thenReturn(booking());
LoadingPlanItemEntity first=item(101L,1L,201L,6);
first.setInboundId(180L); first.setOrderId(280L); first.setOrderNo("O180"); first.setInboundCode("I180");
first.setCustomerName("客户"); first.setPalletId(801L); first.setPalletNo("DP-0025"); first.setUnitType("SEALED_PALLET");
LoadingPlanItemEntity second=item(102L,1L,202L,5);
second.setInboundId(181L); second.setOrderId(281L); second.setOrderNo("O181"); second.setInboundCode("I181");
second.setCustomerName("客户"); second.setPalletId(801L); second.setPalletNo("DP-0025"); second.setUnitType("SEALED_PALLET");
when(repository.items(1L)).thenReturn(List.of(first,second));
var firstMember=new com.baiyue.firstleg.mapper.DropshipPalletizationMapper.PlanPalletMemberRow();
firstMember.setPalletId(801L); firstMember.setAccountId(1L); firstMember.setInboundId(180L);
firstMember.setOrderId(280L); firstMember.setOrderNo("O180"); firstMember.setInboundCode("I180");
firstMember.setCustomerName("客户"); firstMember.setAvailablePackageCount(6);
var secondMember=new com.baiyue.firstleg.mapper.DropshipPalletizationMapper.PlanPalletMemberRow();
secondMember.setPalletId(801L); secondMember.setAccountId(1L); secondMember.setInboundId(181L);
secondMember.setOrderId(281L); secondMember.setOrderNo("O181"); secondMember.setInboundCode("I181");
secondMember.setCustomerName("客户"); secondMember.setAvailablePackageCount(5);
when(dropshipPallets.palletInboundMembersByPalletIds(List.of(801L)))
.thenReturn(List.of(firstMember,secondMember));
Plan result=service.detail(1L);
assertThat(result.items()).singleElement().satisfies(row->assertThat(row.getPackageCount()).isEqualTo(11));
assertThat(result.palletMembers()).singleElement().satisfies(pallet->{
assertThat(pallet.palletId()).isEqualTo(801L);
assertThat(pallet.members()).extracting(GroupMemberCandidate::inboundCode).containsExactly("I180","I181");
assertThat(pallet.members()).extracting(GroupMemberCandidate::availablePackageCount).containsExactly(6,5);
});
verify(dropshipPallets).palletInboundMembersByPalletIds(List.of(801L));
}
@Test
void restoresLoadingPlanForContinuedAssignment() {
LoadingPlanEntity plan = plan("LOADING", 3);
@@ -59,7 +59,7 @@ class TruckAppointmentFeeServiceTest {
assertThat(request.getValue().fee().remark()).contains("TA-260914-0111");
assertThat(request.getValue().fee().businessDate()).isEqualTo(DEPARTED.toLocalDate());
assertThat(request.getValue().allocations().get(0).amount()).isEqualByComparingTo("100");
verify(suppliers).trucking(8L, null);
verify(suppliers).domesticTrucking(8L, null);
verify(containers).approveAllocationGroup(10L, 500L, 0L, "系统(拖车离仓完成)");
}
@@ -43,8 +43,8 @@ class TruckAppointmentPayableFlowTest {
item.setFeeTypeName("拖车费"); item.setStatus("ACTIVE"); item.setPickupPayable(true); item.setSupplierCategory("TRUCKING");
when(items.findItemByCode("FEE_TYPE_108")).thenReturn(item);
when(items.findItem(108L)).thenReturn(item);
var supplier = new WarehouseSupplierClient.Snapshot(8L, "测试车队", "TRUCKING", "ACTIVE", List.of());
when(suppliers.trucking(8L, null)).thenReturn(supplier);
var supplier = new WarehouseSupplierClient.Snapshot(8L, "测试车队", "TRUCKING", "ACTIVE", List.of(), "DOMESTIC");
when(suppliers.domesticTrucking(8L, null)).thenReturn(supplier);
when(suppliers.fee(8L, "TRUCKING")).thenReturn(supplier);
var plan = new LoadingPlanEntity(); plan.setId(10L); plan.setContainerBookingId(1L);
var container = new ContainerBookingEntity(); container.setId(1L); container.setContainerNo("TEST1234567");
@@ -58,8 +58,8 @@ class TruckAppointmentServiceTest {
return null;
}).when(repository).insert(any());
lenient().when(documentNumbers.next("TA")).thenReturn("TA-260818-0001");
when(suppliers.trucking(any(), any())).thenReturn(
new WarehouseSupplierClient.Snapshot(8L, "Canonical Carrier", "TRUCKING", "ACTIVE", java.util.List.of()));
when(suppliers.domesticTrucking(any(), any())).thenReturn(
new WarehouseSupplierClient.Snapshot(8L, "Canonical Carrier", "TRUCKING", "ACTIVE", java.util.List.of(), "DOMESTIC"));
lenient().when(repository.attachments(anyLong())).thenReturn(List.of());
OceanBookingEntity booking = releasedBooking();
lenient().when(oceanBookings.find(99L)).thenReturn(booking);
@@ -536,7 +536,7 @@ class TruckAppointmentServiceTest {
@Test void createRejectsInvalidTruckingSupplierWithoutWriting() {
SaveRequest request = validRequest();
when(suppliers.trucking(8L, null)).thenThrow(
when(suppliers.domesticTrucking(8L, null)).thenThrow(
new BizException(com.baiyue.common.core.api.ResultCode.CONFLICT, "拖车服务商未启用"));
assertThatThrownBy(() -> service.create(7L, "admin", request))
@@ -28,7 +28,7 @@ class WarehouseSupplierSnapshotServiceTest {
@Test
void rejectsFreeTextWithoutSupplierId() {
assertThatThrownBy(() -> service.trucking(null, "任意承运商"))
assertThatThrownBy(() -> service.domesticTrucking(null, "任意承运商"))
.isInstanceOf(BizException.class).hasMessageContaining("自由文本");
verifyNoInteractions(client);
}
@@ -36,7 +36,7 @@ class WarehouseSupplierSnapshotServiceTest {
@Test
void resolvesActiveBookingSupplierByCategoryWithoutScopeAndUsesServerName() {
when(client.get("internal-token", 7L, true, "BOOKING"))
.thenReturn(ApiResponse.success(new WarehouseSupplierClient.Snapshot(7L, "海运代理", "BOOKING", "ACTIVE", java.util.List.of("MATSON"))));
.thenReturn(ApiResponse.success(new WarehouseSupplierClient.Snapshot(7L, "海运代理", "BOOKING", "ACTIVE", java.util.List.of("MATSON"), null)));
var snapshot = service.booking(7L, "海运代理");
assertThat(snapshot.name()).isEqualTo("海运代理");
verify(client).get("internal-token", 7L, true, "BOOKING");
@@ -46,7 +46,7 @@ class WarehouseSupplierSnapshotServiceTest {
void feeWithoutCategoryAcceptsAnyActiveWarehouseSupplier() {
when(client.get("internal-token", 8L, true, null))
.thenReturn(ApiResponse.success(new WarehouseSupplierClient.Snapshot(
8L, "任意启用供应商", "CERTIFICATE", "ACTIVE", java.util.List.of())));
8L, "任意启用供应商", "CERTIFICATE", "ACTIVE", java.util.List.of(), null)));
assertThat(service.fee(8L, null).name()).isEqualTo("任意启用供应商");
verify(client).get("internal-token", 8L, true, null);
@@ -55,7 +55,7 @@ class WarehouseSupplierSnapshotServiceTest {
@Test
void rejectsClientNameThatDiffersFromWarehouseSnapshot() {
when(client.get(anyString(), eq(9L), eq(true), eq("CUSTOMS")))
.thenReturn(ApiResponse.success(new WarehouseSupplierClient.Snapshot(9L, "真实报关行", "CUSTOMS", "ACTIVE", java.util.List.of())));
.thenReturn(ApiResponse.success(new WarehouseSupplierClient.Snapshot(9L, "真实报关行", "CUSTOMS", "ACTIVE", java.util.List.of(), null)));
assertThatThrownBy(() -> service.customs(9L, "伪造名称"))
.isInstanceOf(BizException.class).hasMessageContaining("不一致");
}
@@ -64,9 +64,29 @@ class WarehouseSupplierSnapshotServiceTest {
void rejectsSupplierSnapshotWithWrongCategoryOrInactiveStatus() {
when(client.get("internal-token", 7L, true, "BOOKING"))
.thenReturn(ApiResponse.success(new WarehouseSupplierClient.Snapshot(
7L, "错误代理", "TRUCKING", "ACTIVE", java.util.List.of())));
7L, "错误代理", "TRUCKING", "ACTIVE", java.util.List.of(), "DOMESTIC")));
assertThatThrownBy(() -> service.booking(7L, null))
.isInstanceOf(BizException.class).hasMessageContaining("类别不匹配");
}
@Test void truckingScopesAreMutuallyExclusiveAndMissingScopeIsRejected() {
for (String scope : new String[] {"DOMESTIC", "OVERSEAS", null}) {
when(client.get("internal-token", 7L, true, "TRUCKING"))
.thenReturn(ApiResponse.success(new WarehouseSupplierClient.Snapshot(
7L, "车队", "TRUCKING", "ACTIVE", java.util.List.of(), scope)));
if ("DOMESTIC".equals(scope)) {
assertThat(service.domesticTrucking(7L, null).name()).isEqualTo("车队");
} else {
assertThatThrownBy(() -> service.domesticTrucking(7L, null))
.isInstanceOf(BizException.class).hasMessageContaining("国内拖车");
}
if ("OVERSEAS".equals(scope)) {
assertThat(service.overseasTrucking(7L, null).name()).isEqualTo("车队");
} else {
assertThatThrownBy(() -> service.overseasTrucking(7L, null))
.isInstanceOf(BizException.class).hasMessageContaining("海外车队");
}
}
}
}
@@ -72,7 +72,7 @@ class TerminalDrayageServiceTest {
new FleetSelectionRequest(0, 91L, null, null)))
.isInstanceOf(BizException.class)
.hasMessage("货柜实际到达目的港后才能安排码头车队");
verify(suppliers, never()).trucking(anyLong(), any());
verify(suppliers, never()).overseasTrucking(anyLong(), any());
}
@Test
@@ -82,8 +82,8 @@ class TerminalDrayageServiceTest {
when(mapper.context(11L)).thenReturn(context);
when(mapper.lock(11L)).thenReturn(null);
when(mapper.operation(11L, "fleet-1")).thenReturn(null);
when(suppliers.trucking(91L, "用户提示名")).thenReturn(
new WarehouseSupplierClient.Snapshot(91L, "服务端车队", "TRUCKING", "ACTIVE", List.of()));
when(suppliers.overseasTrucking(91L, "用户提示名")).thenReturn(
new WarehouseSupplierClient.Snapshot(91L, "服务端车队", "TRUCKING", "ACTIVE", List.of(), "OVERSEAS"));
AtomicReference<TerminalDrayageEntity> stored = new AtomicReference<>();
when(mapper.insert(any(TerminalDrayageEntity.class))).thenAnswer(invocation -> {
TerminalDrayageEntity value = invocation.getArgument(0);
@@ -256,8 +256,8 @@ class TerminalDrayageServiceTest {
when(mapper.lockContext(11L)).thenReturn(context);
when(mapper.context(11L)).thenReturn(context);
when(mapper.lock(11L)).thenReturn(null);
when(suppliers.trucking(91L, null)).thenReturn(
new WarehouseSupplierClient.Snapshot(91L, "服务端车队", "TRUCKING", "ACTIVE", List.of()));
when(suppliers.overseasTrucking(91L, null)).thenReturn(
new WarehouseSupplierClient.Snapshot(91L, "服务端车队", "TRUCKING", "ACTIVE", List.of(), "OVERSEAS"));
AtomicReference<TerminalDrayageEntity> stored = new AtomicReference<>();
AtomicReference<Operation> operation = new AtomicReference<>();
when(mapper.insert(any(TerminalDrayageEntity.class))).thenAnswer(invocation -> {
@@ -279,7 +279,7 @@ class TerminalDrayageServiceTest {
assertThat(replay).isEqualTo(first);
verify(mapper, times(1)).insert(any(TerminalDrayageEntity.class));
verify(mapper, times(1)).insertOperation(anyLong(), anyLong(), anyString(), anyString(), anyString(), anyInt(), anyLong());
verify(suppliers, times(1)).trucking(91L, null);
verify(suppliers, times(1)).overseasTrucking(91L, null);
}
@Test
@@ -317,7 +317,7 @@ class TerminalDrayageServiceTest {
void selectingSameFleetDoesNotQueueMailOrChangeVersion() {
when(mapper.lockContext(11L)).thenReturn(context("ARRIVED_DESTINATION_PORT", null));
when(mapper.lock(11L)).thenReturn(row(3));
when(suppliers.trucking(91L, null)).thenReturn(new WarehouseSupplierClient.Snapshot(91L,"车队","TRUCKING","ACTIVE",List.of()));
when(suppliers.overseasTrucking(91L, null)).thenReturn(new WarehouseSupplierClient.Snapshot(91L,"车队","TRUCKING","ACTIVE",List.of(), "OVERSEAS"));
service.selectFleet(11L,7L,"admin","same-fleet",new FleetSelectionRequest(3,91L,null,null));
verifyNoInteractions(mails);
verify(mapper,never()).updateAggregate(any(),anyInt(),anyLong(),anyString());
@@ -332,7 +332,7 @@ class TerminalDrayageServiceTest {
when(mapper.lock(11L)).thenReturn(row);
when(mapper.find(11L)).thenReturn(row);
when(mapper.updateAggregate(any(),anyInt(),anyLong(),anyString())).thenReturn(1);
when(suppliers.trucking(92L,null)).thenReturn(new WarehouseSupplierClient.Snapshot(92L,"新车队","TRUCKING","ACTIVE",List.of()));
when(suppliers.overseasTrucking(92L,null)).thenReturn(new WarehouseSupplierClient.Snapshot(92L,"新车队","TRUCKING","ACTIVE",List.of(), "OVERSEAS"));
service.selectFleet(11L,7L,"admin","change-fleet",new FleetSelectionRequest(3,92L,null,null));
var ordered = org.mockito.Mockito.inOrder(mails,mapper);
ordered.verify(mails).replaceFleet(11L,"MSCU1234567",91L,92L,7L);