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

# Conflicts:
#	deploy/sql/头程数据库统一增量-MySQL8.sql
This commit is contained in:
2026-09-03 11:55:46 +08:00
12 changed files with 230 additions and 27 deletions
@@ -29,7 +29,7 @@ class OrderDeclarationUpdateContractTest {
assertThat(OrderDeclarationDtos.UpdateRequest.class.getRecordComponents())
.extracting(component -> component.getName())
.containsExactly("expectedVersion", "customerRemark", "expectedArrivalAt",
"channelCode", "channelName", "cargoes")
"estimatedVolumeCbm", "channelCode", "channelName", "cargoes")
.doesNotContain("receivingWarehouseId", "receivingWarehouseName",
"destinationWarehouseId", "destinationWarehouseName", "inboundCode", "orderNo");
assertThat(OrderDeclarationDtos.Cargo.class.getRecordComponents())
@@ -40,17 +40,33 @@ class OrderDeclarationUpdateContractTest {
"measurementReviewStatus", "feeReviewStatus");
}
@Test
void planningEndpointOnlyAcceptsVersionArrivalAndOperatorEstimate() throws Exception {
var method = PlatformUnifiedOrderController.class.getDeclaredMethod("updatePlanning",
com.baiyue.common.security.AuthenticatedUser.class, long.class, long.class,
OrderDeclarationDtos.PlanningUpdateRequest.class);
assertThat(method.getAnnotation(PutMapping.class).value()).containsExactly("/{orderId}/planning");
assertThat(method.getAnnotation(PreAuthorize.class).value())
.isEqualTo("hasAuthority('platform:order:declaration:edit')");
assertThat(OrderDeclarationDtos.PlanningUpdateRequest.class.getRecordComponents())
.extracting(component -> component.getName())
.containsExactly("expectedVersion", "expectedArrivalAt", "estimatedVolumeCbm");
}
@Test
void openApiSeparatesDeclaredAndMeasuredFacts() throws Exception {
String source = new ClassPathResource("openapi/order-v1.yaml")
.getContentAsString(StandardCharsets.UTF_8);
Map<String, Object> document = map(new Yaml().load(source));
assertThat(map(document.get("paths")))
.containsKey("/api/platform/v1/orders/{orderId}/declaration");
.containsKeys("/api/platform/v1/orders/{orderId}/declaration",
"/api/platform/v1/orders/{orderId}/planning");
Map<String, Object> schemas = map(map(document.get("components")).get("schemas"));
Map<String, Object> row = map(map(schemas.get("UnifiedOrderRow")).get("properties"));
assertThat(row).containsKeys("expectedVolumeCbm", "actualVolumeCbm", "expectedArrivalAt", "channelChanged");
assertThat(row).containsKeys("orderVersion", "estimatedVolumeCbm", "expectedVolumeCbm",
"actualVolumeCbm", "expectedArrivalAt", "channelChanged");
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",
@@ -43,7 +43,7 @@ class OrderDeclarationUpdateServiceTest {
when(repository.findOwned(100L, 200L)).thenReturn(order);
when(repository.findCargo(100L, 200L)).thenReturn(List.of(cargo));
when(repository.updateDeclarationHeader(eq(100L), eq(200L), eq(3), eq(700L),
any(), any(), anyString(), anyString(), anyBoolean())).thenReturn(1);
any(), any(), any(), anyString(), anyString(), anyBoolean())).thenReturn(1);
when(repository.updateDeclarationCargo(eq(100L), eq(200L), any())).thenReturn(1);
var request = request(3, "NEW", "新渠道", "新备注");
@@ -54,7 +54,7 @@ class OrderDeclarationUpdateServiceTest {
assertThat(response.version()).isEqualTo(4);
assertThat(response.channelChanged()).isTrue();
assertThat(response.changes()).extracting(OrderDeclarationDtos.FieldChange::field)
.contains("channelCode", "channelName", "customerRemark", "expectedArrivalAt",
.contains("channelCode", "channelName", "customerRemark", "expectedArrivalAt", "estimatedVolumeCbm",
"cargoName", "packageCount", "totalQuantity", "lengthCm", "grossWeightKg");
verify(pricingChannelGuard).requireAvailable(100L, 31L, "NEW", "FBA");
verify(repository).insertHistory(any());
@@ -71,7 +71,7 @@ class OrderDeclarationUpdateServiceTest {
request(3, "OLD", "旧渠道", "备注")))
.isInstanceOf(BizException.class).hasMessageContaining("版本已变化");
verify(repository, never()).updateDeclarationHeader(anyLong(), anyLong(), anyInt(), anyLong(),
any(), any(), any(), any(), anyBoolean());
any(), any(), any(), any(), any(), anyBoolean());
}
@Test
@@ -80,7 +80,7 @@ class OrderDeclarationUpdateServiceTest {
when(repository.findOwned(100L, 200L)).thenReturn(order);
when(repository.findCargo(100L, 200L)).thenReturn(List.of(cargo()));
when(repository.updateDeclarationHeader(anyLong(), anyLong(), anyInt(), anyLong(), any(), any(),
any(), any(), anyBoolean())).thenReturn(1);
any(), any(), any(), anyBoolean())).thenReturn(1);
when(repository.updateDeclarationCargo(anyLong(), anyLong(), any())).thenReturn(1);
when(json.writeValueAsString(any())).thenReturn("{\"schemaVersion\":1}");
@@ -98,7 +98,7 @@ class OrderDeclarationUpdateServiceTest {
when(repository.findOwned(100L, 200L)).thenReturn(order);
when(repository.findCargo(100L, 200L)).thenReturn(List.of(cargo()));
when(repository.updateDeclarationHeader(anyLong(), anyLong(), anyInt(), anyLong(), any(), any(),
any(), any(), anyBoolean())).thenReturn(1);
any(), any(), any(), anyBoolean())).thenReturn(1);
when(repository.updateDeclarationCargo(anyLong(), anyLong(), any())).thenReturn(1);
when(json.writeValueAsString(any())).thenReturn("{\"schemaVersion\":1}");
@@ -125,7 +125,34 @@ class OrderDeclarationUpdateServiceTest {
verify(fulfillmentChannelGuard).requireEligible(order, 4L, "NEW");
verify(pricingChannelGuard, never()).requireOrderChannelChangeEligible(any(), anyLong(), anyString(), anyString());
verify(repository, never()).updateDeclarationHeader(anyLong(), anyLong(), anyInt(), anyLong(),
any(), any(), any(), any(), anyBoolean());
any(), any(), any(), any(), any(), anyBoolean());
}
@Test
void updatesPlanningFieldsWithCurrentDeclarationSnapshotAndVersion() {
OrderEntity order = order(OrderStatus.SUBMITTED, 3);
order.setExpectedArrivalAt(LocalDateTime.of(2026, 9, 10, 8, 30));
order.setEstimatedVolumeCbm(new BigDecimal("0.25"));
OrderCargoEntity cargo = cargo();
when(repository.findOwned(100L, 200L)).thenReturn(order);
when(repository.findCargo(100L, 200L)).thenReturn(List.of(cargo));
when(repository.updateDeclarationHeader(eq(100L), eq(200L), eq(3), eq(700L),
eq("旧备注"), eq(LocalDateTime.of(2026, 9, 12, 16, 0)), eq(new BigDecimal("0.50")),
eq("OLD"), eq("旧渠道"), eq(false))).thenReturn(1);
when(repository.updateDeclarationCargo(eq(100L), eq(200L), any())).thenReturn(1);
var response = service.updatePlanning(100L, 200L, 700L, "平台操作员",
new OrderDeclarationDtos.PlanningUpdateRequest(3,
LocalDateTime.of(2026, 9, 12, 16, 0), new BigDecimal("0.50")));
assertThat(response.version()).isEqualTo(4);
assertThat(response.changes()).extracting(OrderDeclarationDtos.FieldChange::field)
.containsExactlyInAnyOrder("expectedArrivalAt", "estimatedVolumeCbm");
verify(pricingChannelGuard, never()).requireAvailable(anyLong(), anyLong(), anyString(), anyString());
verify(fulfillmentChannelGuard, never()).requireEligible(any(), anyLong(), anyString());
verify(repository, never()).updateDeclarationCargo(anyLong(), anyLong(), any());
assertThat(cargo.getCargoName()).isEqualTo("旧品名");
assertThat(cargo.getPackageCount()).isEqualTo(10);
}
private OrderEntity order(OrderStatus status, int version) {
@@ -156,6 +183,7 @@ class OrderDeclarationUpdateServiceTest {
"8518", "851821", 10, new BigDecimal("5"), new BigDecimal("1000"),
"USD", "塑料", "音响", "Brand", "新备注");
return new OrderDeclarationDtos.UpdateRequest(version, remark,
LocalDateTime.of(2026, 9, 15, 10, 30), channelCode, channelName, List.of(cargo));
LocalDateTime.of(2026, 9, 15, 10, 30), new BigDecimal("2.5"),
channelCode, channelName, List.of(cargo));
}
}