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

# Conflicts:
#	deploy/maven/settings-cn.xml
This commit is contained in:
2026-09-03 15:23:55 +08:00
28 changed files with 867 additions and 156 deletions
@@ -68,6 +68,7 @@ class OrderDeclarationUpdateContractTest {
assertThat(map(row.get("actualVolumeCbm")).get("description").toString()).contains("仅 Firstleg 实测");
assertThat(map(row.get("estimatedVolumeCbm")).get("description").toString()).contains("平台运营");
assertThat(source).contains("ORDER_DECLARATION_UPDATED_V1", "platform:order:declaration:edit", "A0409",
"仅待审核、待测量数据、待分柜阶段允许修改",
"/internal/firstleg/v1/inbounds/order-channel-changes/validate",
"/internal/firstleg/v1/pricing/order-channel-changes/validate",
"/internal/firstleg/v1/pricing/order-channel-changes/apply");
@@ -0,0 +1,77 @@
package com.baiyue.order.service;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.order.client.FirstlegUnifiedOrderClient;
import com.baiyue.order.entity.OrderEntity;
import com.baiyue.order.entity.OrderStatus;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(OrderDeclarationEditGuard.class)
@TestPropertySource(properties = "baiyue.security.internal-service-token=token")
class OrderDeclarationEditGuardTest {
@MockBean private FirstlegUnifiedOrderClient firstleg;
@Resource private OrderDeclarationEditGuard guard;
@Test
void allowsPendingReviewWithoutFirstlegAndWaitSplitWithAuthoritativeSummary() {
OrderEntity pendingReview = order(OrderStatus.SUBMITTED);
assertThatCode(() -> guard.requireEditable(pendingReview)).doesNotThrowAnyException();
OrderEntity waitSplit = order(OrderStatus.APPROVED);
var request = new FirstlegUnifiedOrderClient.BatchRequest(11L, List.of(21L));
var waitSplitSummary = summary("WAIT_SPLIT");
when(firstleg.summaries("token", request)).thenReturn(ApiResponse.success(List.of(waitSplitSummary)));
assertThatCode(() -> guard.requireEditable(waitSplit)).doesNotThrowAnyException();
verify(firstleg).summaries("token", request);
}
@Test
void rejectsLoadingAndLaterStatuses() {
OrderEntity order = order(OrderStatus.APPROVED);
var request = new FirstlegUnifiedOrderClient.BatchRequest(11L, List.of(21L));
for (String status : List.of("LOADING", "LOADED", "DELIVERED")) {
var fulfillmentSummary = summary(status);
when(firstleg.summaries("token", request)).thenReturn(ApiResponse.success(List.of(fulfillmentSummary)));
assertThatThrownBy(() -> guard.requireEditable(order))
.isInstanceOf(BizException.class).hasMessageContaining("已进入装柜或后续阶段");
}
}
@Test
void failsClosedWhenFirstlegSummaryIsMissing() {
OrderEntity order = order(OrderStatus.APPROVED);
var request = new FirstlegUnifiedOrderClient.BatchRequest(11L, List.of(21L));
when(firstleg.summaries("token", request)).thenReturn(ApiResponse.success(List.of()));
assertThatThrownBy(() -> guard.requireEditable(order))
.isInstanceOf(BizException.class).hasMessageContaining("状态校验服务暂时不可用");
}
private FirstlegUnifiedOrderClient.Summary summary(String mainStatus) {
var inbound = mock(FirstlegUnifiedOrderClient.Inbound.class);
when(inbound.orderId()).thenReturn(21L);
var summary = mock(FirstlegUnifiedOrderClient.Summary.class);
when(summary.inbound()).thenReturn(inbound);
when(summary.mainStatus()).thenReturn(mainStatus);
return summary;
}
private OrderEntity order(OrderStatus status) {
OrderEntity order = new OrderEntity();
order.setId(21L); order.setTenantId(11L); order.setStatus(status);
return order;
}
}
@@ -58,6 +58,7 @@ class OrderDeclarationUpdateServiceTest {
.contains("channelCode", "channelName", "customerRemark", "expectedArrivalAt", "estimatedVolumeCbm",
"cargoName", "packageCount", "totalQuantity", "lengthCm", "grossWeightKg");
verify(pricingChannelGuard).requireAvailable(100L, 31L, "NEW", "FBA");
verify(declarationEditGuard).requireEditable(order);
verify(repository).insertHistory(any());
verify(repository, never()).insertOutbox(any());
assertThat(cargo.getPackageCount()).isEqualTo(20);
@@ -129,6 +130,23 @@ class OrderDeclarationUpdateServiceTest {
any(), any(), any(), any(), any(), anyBoolean());
}
@Test
void rejectsDeclarationUpdateWhenFulfillmentHasReachedLoading() {
OrderEntity order = order(OrderStatus.APPROVED, 3);
when(repository.findOwned(100L, 200L)).thenReturn(order);
org.mockito.Mockito.doThrow(new BizException(com.baiyue.common.core.api.ResultCode.CONFLICT,
"订单已进入装柜或后续阶段,不能修改客户申报资料"))
.when(declarationEditGuard).requireEditable(order);
assertThatThrownBy(() -> service.update(100L, 200L, 700L, "平台操作员",
request(3, "OLD", "旧渠道", "备注")))
.isInstanceOf(BizException.class).hasMessageContaining("已进入装柜");
verify(repository, never()).findCargo(anyLong(), anyLong());
verify(repository, never()).updateDeclarationHeader(anyLong(), anyLong(), anyInt(), anyLong(),
any(), any(), any(), any(), any(), anyBoolean());
}
@Test
void updatesPlanningFieldsWithCurrentDeclarationSnapshotAndVersion() {
OrderEntity order = order(OrderStatus.SUBMITTED, 3);