fix: carry package count into inbound snapshot

This commit is contained in:
2026-08-08 10:16:33 +08:00
parent 71fde99ceb
commit dd8147c535
9 changed files with 68 additions and 11 deletions
@@ -55,7 +55,8 @@ public class InternalInboundOrderController {
if (command.tenantId() <= 0 || command.orderId() <= 0 || command.warehouseId() == null
|| command.warehouseId() <= 0 || command.customerName() == null || command.customerName().isBlank()
|| command.inboundCode() == null || command.inboundCode().isBlank()
|| command.inboundCode().length() > 40) {
|| command.inboundCode().length() > 40
|| command.totalPackageCount() != null && command.totalPackageCount() < 1) {
throw new BizException(ResultCode.BAD_REQUEST, "订单入库快照不完整");
}
return ApiResponse.success(service.createFromApprovedOrder(command));
@@ -30,8 +30,25 @@ public record OrderApprovedInboundCommand(
String destinationName,
String publicRemark,
String expressCompanyName,
String expressTrackingNo
String expressTrackingNo,
Integer totalPackageCount
) {
/** 兼容尚未携带订单总箱数的历史事件。 */
public OrderApprovedInboundCommand(
long tenantId, long orderId, String orderNo, String inboundCode, Long customerId, String customerName,
Long warehouseId, String warehouseName, Long deliveryWarehouseId, String deliveryWarehouseName,
LocalDateTime expectedArrivalAt, String businessType, String channelCode, String channelName,
Long carrierId, String carrierName, String taxMode, String pricingTaxMode, String containerMode,
Boolean customsInspection, String destinationType, Long destinationWarehouseId, String destinationCode,
String destinationName, String publicRemark, String expressCompanyName, String expressTrackingNo
) {
this(tenantId, orderId, orderNo, inboundCode, customerId, customerName, warehouseId, warehouseName,
deliveryWarehouseId, deliveryWarehouseName, expectedArrivalAt, businessType, channelCode, channelName,
carrierId, carrierName, taxMode, pricingTaxMode, containerMode, customsInspection, destinationType,
destinationWarehouseId, destinationCode, destinationName, publicRemark, expressCompanyName,
expressTrackingNo, null);
}
/** 兼容未携带报价包税维度的历史调用方。 */
public OrderApprovedInboundCommand(
long tenantId, long orderId, String orderNo, String inboundCode, Long customerId, String customerName,
@@ -45,6 +62,6 @@ public record OrderApprovedInboundCommand(
deliveryWarehouseId, deliveryWarehouseName, expectedArrivalAt, businessType, channelCode, channelName,
carrierId, carrierName, taxMode, null, containerMode, customsInspection, destinationType,
destinationWarehouseId, destinationCode, destinationName, publicRemark, expressCompanyName,
expressTrackingNo);
expressTrackingNo, null);
}
}
@@ -68,6 +68,7 @@ public class InboundOrderCreationService {
inbound.setDestinationName(command.destinationName());
inbound.setExpressCompanyName(command.expressCompanyName());
inbound.setExpressTrackingNo(command.expressTrackingNo());
inbound.setTotalPackageCount(command.totalPackageCount());
inbound.setPublicRemark(command.publicRemark());
inbound.setCreatedBy(0L);
inbound.setUpdatedBy(0L);
@@ -53,6 +53,7 @@ class InboundOrderCreationServiceTest {
assertThat(inboundCaptor.getValue().getCarrierId()).isNull();
assertThat(inboundCaptor.getValue().getTaxMode()).isEqualTo("BUY_DECLARATION");
assertThat(inboundCaptor.getValue().getPricingTaxMode()).isEqualTo("TAX_EXCLUDED");
assertThat(inboundCaptor.getValue().getTotalPackageCount()).isEqualTo(4);
assertThat(createdId).isEqualTo(inboundCaptor.getValue().getId());
assertThat(replayedId).isEqualTo(900L);
}
@@ -97,6 +98,6 @@ class InboundOrderCreationServiceTest {
return new OrderApprovedInboundCommand(
10001L, 77L, "FL-77", inboundCode, 7L, "测试客户", 9L, "义乌仓",
null, null, null, "FBA", "AIR", "空运", null, null,
"BUY_DECLARATION", "TAX_EXCLUDED", null, null, null, null, null, null, null, null, null);
"BUY_DECLARATION", "TAX_EXCLUDED", null, null, null, null, null, null, null, null, null, 4);
}
}
@@ -35,7 +35,7 @@ public interface FirstlegInboundClient {
Long carrierId, String carrierName, String taxMode, String pricingTaxMode, String containerMode,
Boolean customsInspection, String destinationType, Long destinationWarehouseId,
String destinationCode, String destinationName, String publicRemark,
String expressCompanyName, String expressTrackingNo
String expressCompanyName, String expressTrackingNo, Integer totalPackageCount
) {
}
}
@@ -425,7 +425,7 @@ public class OrderApplicationService {
event.setEventType(target == OrderStatus.APPROVED ? "FIRST_LEG_ORDER_APPROVED" : "FIRST_LEG_ORDER_CANCELLED");
try {
event.setPayloadJson(target == OrderStatus.APPROVED
? json.writeValueAsString(inboundCommand(order))
? json.writeValueAsString(inboundCommand(order, totalPackageCount(order)))
: json.writeValueAsString(Map.of("tenantId", order.getTenantId(), "orderId", order.getId(),
"orderNo", order.getOrderNo(), "status", target.name())));
} catch (JsonProcessingException exception) { throw new BizException("Cannot serialize order event"); }
@@ -433,6 +433,10 @@ public class OrderApplicationService {
repository.insertOutbox(event);
}
static FirstlegInboundClient.OrderApprovedInboundCommand inboundCommand(OrderEntity order) {
return inboundCommand(order, null);
}
static FirstlegInboundClient.OrderApprovedInboundCommand inboundCommand(OrderEntity order,
Integer totalPackageCount) {
return new FirstlegInboundClient.OrderApprovedInboundCommand(
order.getTenantId(), order.getId(), order.getOrderNo(), order.getInboundCode(),
order.getCustomerId(), order.getCustomerName(),
@@ -443,7 +447,18 @@ public class OrderApplicationService {
order.getTaxMode(), order.getPricingTaxMode(),
order.getContainerMode(), order.getCustomsInspection(), order.getDestinationType(),
order.getDestinationWarehouseId(), order.getAmazonWarehouseCode(), order.getDestinationWarehouseName(),
order.getCustomerRemark(), order.getExpressCompany(), order.getExpressTrackingNo());
order.getCustomerRemark(), order.getExpressCompany(), order.getExpressTrackingNo(), totalPackageCount);
}
private int totalPackageCount(OrderEntity order) {
long total = repository.findCargo(order.getTenantId(), order.getId()).stream()
.map(com.baiyue.order.entity.OrderCargoEntity::getPackageCount)
.filter(java.util.Objects::nonNull)
.mapToLong(Integer::longValue)
.sum();
if (total < 1 || total > 1_000_000) {
throw new BizException("订单货物总箱数必须为1至1000000箱");
}
return (int) total;
}
private String nextNo(long orderId) {
return "FL" + orderId;
@@ -55,7 +55,7 @@ public class OrderInboundOutboxPublisher {
failPermanently(event, "order snapshot is unavailable");
continue;
}
command = OrderApplicationService.inboundCommand(order);
command = OrderApplicationService.inboundCommand(order, totalPackageCount(order));
if (incomplete(command)) {
failPermanently(event, "rebuilt order snapshot is incomplete");
continue;
@@ -77,7 +77,17 @@ public class OrderInboundOutboxPublisher {
return command.orderNo() == null || command.orderNo().isBlank()
|| command.inboundCode() == null || command.inboundCode().isBlank()
|| command.customerName() == null || command.customerName().isBlank()
|| command.warehouseId() == null || command.warehouseId() <= 0;
|| command.warehouseId() == null || command.warehouseId() <= 0
|| command.totalPackageCount() == null || command.totalPackageCount() < 1;
}
private int totalPackageCount(com.baiyue.order.entity.OrderEntity order) {
long total = repository.findCargo(order.getTenantId(), order.getId()).stream()
.map(com.baiyue.order.entity.OrderCargoEntity::getPackageCount)
.filter(java.util.Objects::nonNull)
.mapToLong(Integer::longValue)
.sum();
return total > 0 && total <= 1_000_000 ? (int) total : 0;
}
private void failPermanently(OutboxEventEntity event, String reason) {
@@ -217,13 +217,17 @@ class OrderApplicationServiceStateTest {
order.setInboundCode("RK-1");
when(repository.findOwned(10001L, 1L)).thenReturn(order);
when(repository.saveOrder(10001L, order)).thenReturn(1);
OrderCargoEntity cargo = new OrderCargoEntity();
cargo.setPackageCount(4);
when(repository.findCargo(10001L, 1L)).thenReturn(List.of(cargo));
service(repository, mock(OrderEligibilityGuard.class), mock(OrderMasterDataGuard.class))
.transit(10001L, 2L, "u", 1L, OrderStatus.APPROVED, null);
assertThat(order.getStatus()).isEqualTo(OrderStatus.APPROVED);
verify(repository).insertOutbox(argThat(event -> event.getPayloadJson().contains("\"inboundCode\":\"RK-1\"")
&& event.getPayloadJson().contains("\"carrierId\":null")));
&& event.getPayloadJson().contains("\"carrierId\":null")
&& event.getPayloadJson().contains("\"totalPackageCount\":4")));
}
@Test
@@ -290,10 +294,11 @@ class OrderApplicationServiceStateTest {
order.setTaxMode("BUY_DECLARATION");
order.setPricingTaxMode("TAX_EXCLUDED");
var command = OrderApplicationService.inboundCommand(order);
var command = OrderApplicationService.inboundCommand(order, 4);
assertThat(command.taxMode()).isEqualTo("BUY_DECLARATION");
assertThat(command.pricingTaxMode()).isEqualTo("TAX_EXCLUDED");
assertThat(command.totalPackageCount()).isEqualTo(4);
}
@Test
@@ -36,6 +36,7 @@ class OrderInboundOutboxPublisherTest {
org.assertj.core.api.Assertions.assertThat(command.getValue().warehouseId()).isEqualTo(31L);
org.assertj.core.api.Assertions.assertThat(command.getValue().taxMode()).isEqualTo("BUY_DECLARATION");
org.assertj.core.api.Assertions.assertThat(command.getValue().pricingTaxMode()).isEqualTo("TAX_EXCLUDED");
org.assertj.core.api.Assertions.assertThat(command.getValue().totalPackageCount()).isEqualTo(4);
verify(fixture.repository).markOutboxPublished(eq(11L), eq(41L), any(LocalDateTime.class));
verify(fixture.repository, never()).markOutboxRetry(anyLong(), anyLong(), any());
verify(fixture.repository, never()).markOutboxFailed(any(), anyLong());
@@ -114,6 +115,12 @@ class OrderInboundOutboxPublisherTest {
order.setTaxMode("BUY_DECLARATION");
order.setPricingTaxMode("TAX_EXCLUDED");
when(repository.findOwned(event.getTenantId(), event.getAggregateId())).thenReturn(order);
var firstCargo = new com.baiyue.order.entity.OrderCargoEntity();
firstCargo.setPackageCount(1);
var secondCargo = new com.baiyue.order.entity.OrderCargoEntity();
secondCargo.setPackageCount(3);
when(repository.findCargo(event.getTenantId(), event.getAggregateId()))
.thenReturn(List.of(firstCargo, secondCargo));
}
OrderInboundOutboxPublisher publisher = new OrderInboundOutboxPublisher();
ReflectionTestUtils.setField(publisher, "repository", repository);