优化
This commit is contained in:
@@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,6 +30,10 @@ public interface FirstlegUnifiedOrderClient {
|
||||
|
||||
record Query(Long tenantId, String keyword, String orderNo, String mainStatus, Long customerId,
|
||||
Long receivingWarehouseId, Long destinationWarehouseId,
|
||||
String inboundCode, String internalOrderStatus, LocalDate expectedArrivalDate,
|
||||
String feeReviewStatus, String channelName, String containerNo, String cabinetNo,
|
||||
LocalDate sailingDate, Boolean loaded, Boolean fullContainer,
|
||||
String measurementReviewStatus, List<Long> candidateOrderIds,
|
||||
LocalDateTime createdFrom, LocalDateTime createdTo, long page, long size) {}
|
||||
record BatchRequest(Long tenantId, List<Long> orderIds) {}
|
||||
record DetailRequest(long tenantId, long orderId) {}
|
||||
|
||||
@@ -5,6 +5,7 @@ import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/** Unified product order list filters shared by platform and merchant entry points. */
|
||||
@@ -17,6 +18,18 @@ public record UnifiedOrderQuery(
|
||||
Long customerId,
|
||||
Long receivingWarehouseId,
|
||||
Long destinationWarehouseId,
|
||||
@Size(max = 64) String inboundCode,
|
||||
@Size(max = 32) String internalOrderStatus,
|
||||
LocalDate expectedArrivalDate,
|
||||
@Size(max = 32) String feeReviewStatus,
|
||||
@Size(max = 128) String channelName,
|
||||
@Size(max = 64) String containerNo,
|
||||
@Size(max = 64) String cabinetNo,
|
||||
LocalDate sailingDate,
|
||||
Boolean loaded,
|
||||
Boolean fullContainer,
|
||||
@Size(max = 32) String measurementReviewStatus,
|
||||
@Size(max = 64) String fbaCode,
|
||||
LocalDateTime createdFrom,
|
||||
LocalDateTime createdTo,
|
||||
@Min(1) long page,
|
||||
|
||||
@@ -166,6 +166,15 @@ public class OrderRepository {
|
||||
query.receivingWarehouseId())
|
||||
.eq(query.destinationWarehouseId() != null, OrderEntity::getDestinationWarehouseId,
|
||||
query.destinationWarehouseId())
|
||||
.eq(StringUtils.hasText(query.internalOrderStatus()), OrderEntity::getStatus,
|
||||
StringUtils.hasText(query.internalOrderStatus()) ? OrderStatus.valueOf(query.internalOrderStatus()) : null)
|
||||
.eq(StringUtils.hasText(query.channelName()), OrderEntity::getChannelName, query.channelName())
|
||||
.eq(query.fullContainer() != null, OrderEntity::getContainerMode,
|
||||
query.fullContainer() == null ? null : query.fullContainer() ? "FULL_CONTAINER" : "LCL")
|
||||
.apply(StringUtils.hasText(query.fbaCode()),
|
||||
"EXISTS (SELECT 1 FROM ord_order_cargo c WHERE c.tenant_id = ord_order.tenant_id "
|
||||
+ "AND c.order_id = ord_order.id AND c.deleted = 0 AND c.sku_no LIKE CONCAT('%', {0}, '%'))",
|
||||
query.fbaCode())
|
||||
.ge(query.createdFrom() != null, OrderEntity::getCreatedAt, query.createdFrom())
|
||||
.le(query.createdTo() != null, OrderEntity::getCreatedAt, query.createdTo())
|
||||
.and(StringUtils.hasText(query.keyword()), nested -> nested
|
||||
@@ -175,6 +184,15 @@ public class OrderRepository {
|
||||
return PageResult.from(orderMapper.selectPage(new Page<>(query.page(), query.size()), wrapper));
|
||||
}
|
||||
|
||||
public List<Long> findOrderIdsBySkuNo(Long tenantId, String fbaCode) {
|
||||
if (!StringUtils.hasText(fbaCode)) return List.of();
|
||||
QueryWrapper<OrderCargoEntity> wrapper = new QueryWrapper<OrderCargoEntity>()
|
||||
.select("DISTINCT order_id")
|
||||
.eq(tenantId != null, "tenant_id", tenantId)
|
||||
.like("sku_no", fbaCode);
|
||||
return cargoMapper.selectObjs(wrapper).stream().map(value -> ((Number) value).longValue()).toList();
|
||||
}
|
||||
|
||||
public List<OrderCargoEntity> findCargoByOrderIds(long tenantId, List<Long> orderIds) {
|
||||
if (orderIds.isEmpty()) return List.of();
|
||||
return cargoMapper.selectList(new LambdaQueryWrapper<OrderCargoEntity>()
|
||||
|
||||
@@ -41,7 +41,10 @@ public class UnifiedOrderQueryService {
|
||||
|
||||
public PageResult<UnifiedOrderResponse.Row> page(Long tenantId, UnifiedOrderQuery query) {
|
||||
validate(query);
|
||||
if (StringUtils.hasText(query.mainStatus()) && !"PENDING_REVIEW".equals(query.mainStatus())) {
|
||||
if (requiresFirstlegQuery(query)) {
|
||||
if ("PENDING_REVIEW".equals(query.mainStatus())) {
|
||||
return new PageResult<>(List.of(), query.page(), query.size(), 0, 0);
|
||||
}
|
||||
return pageFromFirstleg(tenantId, query);
|
||||
}
|
||||
List<OrderStatus> statuses = "PENDING_REVIEW".equals(query.mainStatus())
|
||||
@@ -77,9 +80,17 @@ public class UnifiedOrderQueryService {
|
||||
}
|
||||
|
||||
private PageResult<UnifiedOrderResponse.Row> pageFromFirstleg(Long tenantId, UnifiedOrderQuery query) {
|
||||
List<Long> candidateOrderIds = StringUtils.hasText(query.fbaCode())
|
||||
? orders.findOrderIdsBySkuNo(tenantId, query.fbaCode()) : null;
|
||||
if (candidateOrderIds != null && candidateOrderIds.isEmpty()) {
|
||||
return new PageResult<>(List.of(), query.page(), query.size(), 0, 0);
|
||||
}
|
||||
FirstlegUnifiedOrderClient.Query request = new FirstlegUnifiedOrderClient.Query(
|
||||
tenantId, query.keyword(), query.orderNo(), query.mainStatus(), query.customerId(),
|
||||
query.receivingWarehouseId(), query.destinationWarehouseId(), query.createdFrom(), query.createdTo(),
|
||||
query.receivingWarehouseId(), query.destinationWarehouseId(), query.inboundCode(),
|
||||
query.internalOrderStatus(), query.expectedArrivalDate(), query.feeReviewStatus(), query.channelName(),
|
||||
query.containerNo(), query.cabinetNo(), query.sailingDate(), query.loaded(), query.fullContainer(),
|
||||
query.measurementReviewStatus(), candidateOrderIds, query.createdFrom(), query.createdTo(),
|
||||
query.page(), query.size());
|
||||
PageResult<FirstlegUnifiedOrderClient.Summary> source = require(
|
||||
call(() -> firstleg.page(internalToken, request)), "统一订单履约查询响应无效");
|
||||
@@ -184,6 +195,20 @@ public class UnifiedOrderQueryService {
|
||||
&& query.createdFrom().isAfter(query.createdTo())) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "创建时间起点不能晚于终点");
|
||||
}
|
||||
if (StringUtils.hasText(query.internalOrderStatus())) {
|
||||
try { OrderStatus.valueOf(query.internalOrderStatus()); }
|
||||
catch (IllegalArgumentException exception) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "订单内部状态不合法");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requiresFirstlegQuery(UnifiedOrderQuery query) {
|
||||
return StringUtils.hasText(query.mainStatus()) && !"PENDING_REVIEW".equals(query.mainStatus())
|
||||
|| StringUtils.hasText(query.inboundCode()) || query.expectedArrivalDate() != null
|
||||
|| StringUtils.hasText(query.feeReviewStatus()) || StringUtils.hasText(query.containerNo())
|
||||
|| StringUtils.hasText(query.cabinetNo()) || query.sailingDate() != null || query.loaded() != null
|
||||
|| StringUtils.hasText(query.measurementReviewStatus());
|
||||
}
|
||||
|
||||
private <T> ApiResponse<T> call(java.util.concurrent.Callable<ApiResponse<T>> invocation) {
|
||||
|
||||
@@ -23,6 +23,18 @@ paths:
|
||||
- {name: customerId, in: query, required: false, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: receivingWarehouseId, in: query, required: false, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: destinationWarehouseId, in: query, required: false, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- $ref: '#/components/parameters/UnifiedInboundCode'
|
||||
- $ref: '#/components/parameters/UnifiedInternalOrderStatus'
|
||||
- $ref: '#/components/parameters/UnifiedExpectedArrivalDate'
|
||||
- $ref: '#/components/parameters/UnifiedFeeReviewStatus'
|
||||
- $ref: '#/components/parameters/UnifiedChannelName'
|
||||
- $ref: '#/components/parameters/UnifiedContainerNo'
|
||||
- $ref: '#/components/parameters/UnifiedCabinetNo'
|
||||
- $ref: '#/components/parameters/UnifiedSailingDate'
|
||||
- $ref: '#/components/parameters/UnifiedLoaded'
|
||||
- $ref: '#/components/parameters/UnifiedFullContainer'
|
||||
- $ref: '#/components/parameters/UnifiedMeasurementReviewStatus'
|
||||
- $ref: '#/components/parameters/UnifiedFbaCode'
|
||||
- $ref: '#/components/parameters/UnifiedCreatedFrom'
|
||||
- $ref: '#/components/parameters/UnifiedCreatedTo'
|
||||
- $ref: '#/components/parameters/UnifiedPage'
|
||||
@@ -54,6 +66,18 @@ paths:
|
||||
- {name: customerId, in: query, required: false, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: receivingWarehouseId, in: query, required: false, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: destinationWarehouseId, in: query, required: false, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- $ref: '#/components/parameters/UnifiedInboundCode'
|
||||
- $ref: '#/components/parameters/UnifiedInternalOrderStatus'
|
||||
- $ref: '#/components/parameters/UnifiedExpectedArrivalDate'
|
||||
- $ref: '#/components/parameters/UnifiedFeeReviewStatus'
|
||||
- $ref: '#/components/parameters/UnifiedChannelName'
|
||||
- $ref: '#/components/parameters/UnifiedContainerNo'
|
||||
- $ref: '#/components/parameters/UnifiedCabinetNo'
|
||||
- $ref: '#/components/parameters/UnifiedSailingDate'
|
||||
- $ref: '#/components/parameters/UnifiedLoaded'
|
||||
- $ref: '#/components/parameters/UnifiedFullContainer'
|
||||
- $ref: '#/components/parameters/UnifiedMeasurementReviewStatus'
|
||||
- $ref: '#/components/parameters/UnifiedFbaCode'
|
||||
- $ref: '#/components/parameters/UnifiedCreatedFrom'
|
||||
- $ref: '#/components/parameters/UnifiedCreatedTo'
|
||||
- $ref: '#/components/parameters/UnifiedPage'
|
||||
@@ -200,6 +224,54 @@ components:
|
||||
schema:
|
||||
type: string
|
||||
enum: [PENDING_REVIEW, WAIT_MEASURE, INBOUNDED, WAIT_SPLIT, LOADING, LOADED, DELIVERED]
|
||||
UnifiedInboundCode:
|
||||
{name: inboundCode, in: query, required: false, schema: {type: string, maxLength: 64}}
|
||||
UnifiedInternalOrderStatus:
|
||||
name: internalOrderStatus
|
||||
in: query
|
||||
required: false
|
||||
description: Order 内部生命周期状态;不是七态产品主状态
|
||||
schema: {type: string, enum: [DRAFT, SUBMITTED, APPROVED, ACCEPTED, REJECTED, CANCELLED, PROCESSING, COMPLETED]}
|
||||
UnifiedExpectedArrivalDate:
|
||||
{name: expectedArrivalDate, in: query, required: false, schema: {type: string, format: date}}
|
||||
UnifiedFeeReviewStatus:
|
||||
name: feeReviewStatus
|
||||
in: query
|
||||
required: false
|
||||
description: 当前最高版本入库费用审核状态;UNRECORDED 表示尚无费用记录
|
||||
schema: {type: string, maxLength: 32}
|
||||
UnifiedChannelName:
|
||||
{name: channelName, in: query, required: false, schema: {type: string, maxLength: 128}}
|
||||
UnifiedContainerNo:
|
||||
{name: containerNo, in: query, required: false, schema: {type: string, maxLength: 64}}
|
||||
UnifiedCabinetNo:
|
||||
{name: cabinetNo, in: query, required: false, schema: {type: string, maxLength: 64}}
|
||||
UnifiedSailingDate:
|
||||
{name: sailingDate, in: query, required: false, schema: {type: string, format: date}}
|
||||
UnifiedLoaded:
|
||||
name: loaded
|
||||
in: query
|
||||
required: false
|
||||
description: true 表示运输装载状态存在且不是 UNLOADED/PENDING;false 表示无该状态
|
||||
schema: {type: boolean}
|
||||
UnifiedFullContainer:
|
||||
name: fullContainer
|
||||
in: query
|
||||
required: false
|
||||
description: true 对应 FULL_CONTAINER,false 对应 LCL
|
||||
schema: {type: boolean}
|
||||
UnifiedMeasurementReviewStatus:
|
||||
name: measurementReviewStatus
|
||||
in: query
|
||||
required: false
|
||||
description: 测量审核状态;UNRECORDED 表示字段为空
|
||||
schema: {type: string, maxLength: 32}
|
||||
UnifiedFbaCode:
|
||||
name: fbaCode
|
||||
in: query
|
||||
required: false
|
||||
description: 兼容匹配订单货物 sku_no
|
||||
schema: {type: string, maxLength: 64}
|
||||
UnifiedCreatedFrom:
|
||||
name: createdFrom
|
||||
in: query
|
||||
|
||||
@@ -9,11 +9,13 @@ import com.baiyue.order.entity.OrderStatus;
|
||||
import com.baiyue.order.repository.OrderRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -68,8 +70,39 @@ class UnifiedOrderQueryServiceTest {
|
||||
verify(orders, never()).unifiedPage(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void expandedFulfillmentFiltersAreForwardedBeforeDatabasePaging() {
|
||||
UnifiedOrderQuery query = new UnifiedOrderQuery(
|
||||
null, null, "LOADING", 30001L, 11L, 22L, "RK-77", "APPROVED",
|
||||
LocalDate.of(2026, 8, 6), "APPROVED", "美森", "MSCU", "CAB-1",
|
||||
LocalDate.of(2026, 8, 8), true, true, "UNRECORDED", "FBA-1",
|
||||
null, null, 1, 20);
|
||||
when(orders.findOrderIdsBySkuNo(10001L, "FBA-1")).thenReturn(List.of(77L));
|
||||
when(firstleg.page(eq("internal-token"), any()))
|
||||
.thenReturn(ApiResponse.success(new PageResult<>(List.of(), 1, 20, 0, 0)));
|
||||
|
||||
service.page(10001L, query);
|
||||
|
||||
ArgumentCaptor<FirstlegUnifiedOrderClient.Query> captor =
|
||||
ArgumentCaptor.forClass(FirstlegUnifiedOrderClient.Query.class);
|
||||
verify(firstleg).page(eq("internal-token"), captor.capture());
|
||||
assertThat(captor.getValue()).satisfies(request -> {
|
||||
assertThat(request.inboundCode()).isEqualTo("RK-77");
|
||||
assertThat(request.internalOrderStatus()).isEqualTo("APPROVED");
|
||||
assertThat(request.expectedArrivalDate()).isEqualTo(LocalDate.of(2026, 8, 6));
|
||||
assertThat(request.containerNo()).isEqualTo("MSCU");
|
||||
assertThat(request.sailingDate()).isEqualTo(LocalDate.of(2026, 8, 8));
|
||||
assertThat(request.loaded()).isTrue();
|
||||
assertThat(request.fullContainer()).isTrue();
|
||||
assertThat(request.measurementReviewStatus()).isEqualTo("UNRECORDED");
|
||||
assertThat(request.candidateOrderIds()).containsExactly(77L);
|
||||
});
|
||||
}
|
||||
|
||||
private UnifiedOrderQuery query(String status) {
|
||||
return new UnifiedOrderQuery(null, null, status, null, null, null, null, null, 1, 20);
|
||||
return new UnifiedOrderQuery(null, null, status, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, 1, 20);
|
||||
}
|
||||
|
||||
private OrderEntity order(OrderStatus status) {
|
||||
|
||||
Reference in New Issue
Block a user