Merge remote-tracking branch 'origin/test' into dev

# Conflicts:
#	deploy/sql/头程数据库统一增量-MySQL8.sql
#	event-bridge-service/src/main/java/com/baiyue/eventbridge/metrics/EventBridgeMetrics.java
#	firstleg/src/main/resources/db/phase3-container-customs-schema.sql
#	order/src/main/resources/db/order.sql
#	scripts/db/incremental.sql
This commit is contained in:
2026-09-02 17:56:20 +08:00
45 changed files with 1005 additions and 1063 deletions
@@ -3,6 +3,8 @@ package com.baiyue.firstleg.service;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.dto.ContainerFeeDetailDtos;
import com.baiyue.firstleg.dto.ContainerFeeDetailDtos.AdjustRequest;
import com.baiyue.firstleg.dto.ContainerFeeDetailDtos.Allocation;
import com.baiyue.firstleg.dto.ContainerFeeDetailDtos.AllocationGroupRequest;
import com.baiyue.firstleg.entity.ContainerFeeDetailEntity;
import com.baiyue.firstleg.entity.FeeItemEntity;
import com.baiyue.firstleg.entity.LoadingPlanEntity;
@@ -233,15 +235,71 @@ class ContainerFeeDetailServiceTest {
assertThat(captor.getValue().getPaymentVoucherFileIds()).containsExactly(901L);
}
@Test
void createsAtomicCrossCustomerAllocationAndAllowsMissingInboundCode() {
LoadingPlanEntity plan = new LoadingPlanEntity();
plan.setId(41L);
when(loading.plan(41L)).thenReturn(plan);
LoadingPlanItemEntity first = loadingItem(10001L, 501L, "FL-501", null, null);
LoadingPlanItemEntity second = loadingItem(10002L, 502L, "FL-502", 602L, "IB-602");
when(loading.items(41L)).thenReturn(List.of(first, second));
when(feeItems.findItem(11L)).thenReturn(item());
var fee = new ContainerFeeDetailDtos.SaveRequest(11L, "RECEIVABLE",
LocalDate.of(2026, 8, 20), new BigDecimal("300.00"), "CNY", null, "柜级操作费");
var request = new AllocationGroupRequest(fee, List.of(
new Allocation(10001L, 501L, "FL-501", null, null, new BigDecimal("120.00")),
new Allocation(10002L, 502L, "FL-502", 602L, "IB-602", new BigDecimal("180.00"))));
List<ContainerFeeDetailEntity> result = service.createAllocationGroup(41L, 9L, request);
assertThat(result).hasSize(2);
ArgumentCaptor<ContainerFeeDetailEntity> captor = ArgumentCaptor.forClass(ContainerFeeDetailEntity.class);
verify(repository, org.mockito.Mockito.times(2)).insert(captor.capture());
assertThat(captor.getAllValues()).extracting(ContainerFeeDetailEntity::getTenantId)
.containsExactly(10001L, 10002L);
assertThat(captor.getAllValues().get(0).getAllocationOrderNo()).isEqualTo("FL-501");
assertThat(captor.getAllValues().get(0).getAllocationInboundId()).isNull();
assertThat(captor.getAllValues().get(0).getAllocationInboundCode()).isNull();
assertThat(captor.getAllValues()).extracting(ContainerFeeDetailEntity::getAllocationGroupId)
.doesNotContainNull().allMatch(captor.getAllValues().get(0).getAllocationGroupId()::equals);
}
@Test
void rejectsAllocationWhenInboundIdAndCodeAreOnlyPartiallyProvided() {
LoadingPlanEntity plan = new LoadingPlanEntity();
plan.setId(41L);
when(loading.plan(41L)).thenReturn(plan);
when(loading.items(41L)).thenReturn(List.of(loadingItem(10001L, 501L, "FL-501", null, null)));
when(feeItems.findItem(11L)).thenReturn(item());
var fee = new ContainerFeeDetailDtos.SaveRequest(11L, "RECEIVABLE",
LocalDate.of(2026, 8, 20), new BigDecimal("100.00"), "CNY", null, null);
var request = new AllocationGroupRequest(fee, List.of(
new Allocation(10001L, 501L, "FL-501", 601L, null, new BigDecimal("100.00"))));
assertThatThrownBy(() -> service.createAllocationGroup(41L, 9L, request))
.isInstanceOf(BizException.class).hasMessageContaining("同时填写或同时留空");
verify(repository, never()).insert(any());
}
private void ownedPlan() {
LoadingPlanEntity plan = new LoadingPlanEntity();
plan.setId(41L);
when(loading.plan(41L)).thenReturn(plan);
LoadingPlanItemEntity item = new LoadingPlanItemEntity();
item.setAccountId(10001L);
item.setOrderId(501L);
item.setOrderNo("FL-501");
when(loading.items(41L)).thenReturn(List.of(item));
}
private static LoadingPlanItemEntity loadingItem(long accountId, long orderId, String orderNo,
Long inboundId, String inboundCode) {
LoadingPlanItemEntity value = new LoadingPlanItemEntity();
value.setAccountId(accountId); value.setOrderId(orderId); value.setOrderNo(orderNo);
value.setInboundId(inboundId); value.setInboundCode(inboundCode);
return value;
}
private static FeeItemEntity item() {
FeeItemEntity value = new FeeItemEntity();
value.setId(11L);