尾程订单列表查询修改
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
package com.baiyue.lastleg.client;
|
||||||
|
|
||||||
|
import com.baiyue.common.core.api.ApiResponse;
|
||||||
|
import com.baiyue.common.core.api.PageResult;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@FeignClient(name="order-service", contextId="lastlegOrderPreDepartureClient", path="/internal/order/v1/predeparture-orders")
|
||||||
|
public interface OrderPreDepartureClient {
|
||||||
|
@GetMapping
|
||||||
|
ApiResponse<PageResult<Row>> page(@RequestHeader("X-Internal-Service-Token") String token,
|
||||||
|
@RequestParam(required=false) Long customerId, @RequestParam(required=false) String orderNo,
|
||||||
|
@RequestParam(required=false) String inboundCode, @RequestParam int page, @RequestParam int size);
|
||||||
|
record Row(Long orderId, Long customerId, String orderNo, String inboundCode, String mainStatus,
|
||||||
|
String customerName, String lxCode, String receivingWarehouseName, String destinationWarehouseName,
|
||||||
|
String destinationCode, Integer packageCount, BigDecimal volumeCbm, BigDecimal grossWeightKg,
|
||||||
|
BigDecimal netWeightKg, LocalDateTime expectedArrivalAt, String containerNo, String cabinetNo,
|
||||||
|
LocalDateTime estimatedSailingAt, LocalDateTime actualSailingAt, LocalDateTime createdAt, LocalDateTime updatedAt) {}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import com.baiyue.lastleg.entity.LastlegOrderOperationLog;
|
|||||||
import com.baiyue.lastleg.entity.LastlegOrderManualNode;
|
import com.baiyue.lastleg.entity.LastlegOrderManualNode;
|
||||||
import com.baiyue.lastleg.entity.ReceivingWarehouseOperation;
|
import com.baiyue.lastleg.entity.ReceivingWarehouseOperation;
|
||||||
import com.baiyue.lastleg.entity.ContainerArrivalTimeOperation;
|
import com.baiyue.lastleg.entity.ContainerArrivalTimeOperation;
|
||||||
|
import com.baiyue.lastleg.client.OrderPreDepartureClient;
|
||||||
import com.baiyue.lastleg.inboundplan.pda.gateway.WarehouseResourceGateway;
|
import com.baiyue.lastleg.inboundplan.pda.gateway.WarehouseResourceGateway;
|
||||||
import com.baiyue.lastleg.inboundplan.entity.InboundPlanCarton;
|
import com.baiyue.lastleg.inboundplan.entity.InboundPlanCarton;
|
||||||
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanCartonMapper;
|
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanCartonMapper;
|
||||||
@@ -42,6 +43,7 @@ import org.springframework.http.HttpStatus;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@@ -87,12 +89,23 @@ public class LastlegOrderService {
|
|||||||
@Resource private ContainerShippingGateMapper containerShippingGates;
|
@Resource private ContainerShippingGateMapper containerShippingGates;
|
||||||
@Resource private ReturnOrderReferenceResolver returnOrderReferences;
|
@Resource private ReturnOrderReferenceResolver returnOrderReferences;
|
||||||
@Resource private com.baiyue.lastleg.transfer.service.TransferQuantityService transferQuantities;
|
@Resource private com.baiyue.lastleg.transfer.service.TransferQuantityService transferQuantities;
|
||||||
|
@Resource private OrderPreDepartureClient orderPreDepartureClient;
|
||||||
|
@Value("${baiyue.security.internal-service-token:}") private String internalServiceToken;
|
||||||
|
|
||||||
public PageResult<LastlegOrderSummary> page(String customerId, LastlegOrderRequests.Query query) {
|
public PageResult<LastlegOrderSummary> page(String customerId, LastlegOrderRequests.Query query) {
|
||||||
return page(customerId, query, null);
|
return page(customerId, query, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PageResult<LastlegOrderSummary> page(String customerId, LastlegOrderRequests.Query query, String relabelNo) {
|
public PageResult<LastlegOrderSummary> page(String customerId, LastlegOrderRequests.Query query, String relabelNo) {
|
||||||
|
if (query.sailingStage() == LastlegOrderRequests.SailingStage.BEFORE_DEPARTURE) {
|
||||||
|
Long customer = parseLong(customerId);
|
||||||
|
var response = orderPreDepartureClient.page(internalServiceToken, customer, query.orderNo(), query.inboundCode(),
|
||||||
|
query.page().intValue(), query.size().intValue());
|
||||||
|
if (response == null || response.data() == null) throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "订单服务暂时不可用");
|
||||||
|
List<LastlegOrderSummary> rows = response.data().records().stream().map(this::preDepartureSummary).toList();
|
||||||
|
return new PageResult<>(rows, response.data().page(), response.data().size(), response.data().total(), response.data().pages());
|
||||||
|
}
|
||||||
|
if (query.sailingStage() == null && relabelNo == null) return aggregateAllPage(customerId, query);
|
||||||
Page<LastlegOrder> page = orderMapper.selectPage(
|
Page<LastlegOrder> page = orderMapper.selectPage(
|
||||||
new Page<>(query.page(), query.size()), filter(customerId, query, relabelNo).orderByDesc(LastlegOrder::getCreatedAt).orderByDesc(LastlegOrder::getId));
|
new Page<>(query.page(), query.size()), filter(customerId, query, relabelNo).orderByDesc(LastlegOrder::getCreatedAt).orderByDesc(LastlegOrder::getId));
|
||||||
Map<Long, List<LastlegOrderMapper.PalletMergeRelationRow>> mergeRelations = palletMergeRelations(page.getRecords());
|
Map<Long, List<LastlegOrderMapper.PalletMergeRelationRow>> mergeRelations = palletMergeRelations(page.getRecords());
|
||||||
@@ -112,6 +125,53 @@ public class LastlegOrderService {
|
|||||||
page.getCurrent(), page.getSize(), page.getTotal(), page.getPages());
|
page.getCurrent(), page.getSize(), page.getTotal(), page.getPages());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 全部页签合并订单服务的未开船头程数据与本服务已有尾程单,尾程记录优先覆盖同一来源订单。 */
|
||||||
|
private PageResult<LastlegOrderSummary> aggregateAllPage(String customerId, LastlegOrderRequests.Query query) {
|
||||||
|
var source = orderPreDepartureClient.page(internalServiceToken, parseLong(customerId), query.orderNo(), query.inboundCode(), 1, 2000);
|
||||||
|
if (source == null || source.data() == null) throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "订单服务暂时不可用");
|
||||||
|
List<LastlegOrder> existing = orderMapper.selectList(filter(customerId, query, null)
|
||||||
|
.orderByDesc(LastlegOrder::getCreatedAt).orderByDesc(LastlegOrder::getId));
|
||||||
|
Map<String, String> existingNodes = containerNodes(existing);
|
||||||
|
Map<Long, LastlegOrderMapper.OrderWeightRow> existingWeights = orderWeights(existing);
|
||||||
|
Map<String, LastlegOrderSummary> merged = new LinkedHashMap<>();
|
||||||
|
source.data().records().forEach(row -> {
|
||||||
|
var summary = preDepartureSummary(row);
|
||||||
|
merged.put(orderKey(summary.customerId(), summary.sourceOrderNo()), summary);
|
||||||
|
});
|
||||||
|
for (LastlegOrder order : existing) {
|
||||||
|
String key = orderKey(order.getCustomerId(), StringUtils.hasText(order.getSourceOrderNo()) ? order.getSourceOrderNo() : order.getOrderNo());
|
||||||
|
merged.put(key, summary(order, List.of(), StringUtils.hasText(order.getSourceContainerBookingId())
|
||||||
|
? existingNodes.get(order.getSourceContainerBookingId()) : null, "UNSCHEDULED",
|
||||||
|
existingWeights.get(order.getId()), List.of(), null, null));
|
||||||
|
}
|
||||||
|
List<LastlegOrderSummary> rows = merged.values().stream().sorted(java.util.Comparator.comparing(LastlegOrderSummary::createdAt,
|
||||||
|
java.util.Comparator.nullsLast(java.util.Comparator.reverseOrder()))).toList();
|
||||||
|
int from = Math.min((query.page().intValue() - 1) * query.size().intValue(), rows.size());
|
||||||
|
int to = Math.min(from + query.size().intValue(), rows.size());
|
||||||
|
long total = rows.size();
|
||||||
|
return new PageResult<>(rows.subList(from, to), query.page(), query.size(), total,
|
||||||
|
total == 0 ? 0 : (total + query.size() - 1) / query.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String orderKey(String customerId, String orderNo) {
|
||||||
|
return String.valueOf(customerId) + ":" + String.valueOf(orderNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将订单服务权威头程摘要投影为只读尾程列表行;尚未生成尾程单的字段保持为空。 */
|
||||||
|
private LastlegOrderSummary preDepartureSummary(OrderPreDepartureClient.Row row) {
|
||||||
|
LastlegOrder order = new LastlegOrder();
|
||||||
|
// 头程聚合行尚未生成尾程单,尾程订单号必须保持为空;来源头程订单号单独保留。
|
||||||
|
order.setId(row.orderId()); order.setOrderNo(null); order.setOrderType("FIRST_LEG");
|
||||||
|
order.setInboundCode(row.inboundCode()); order.setLxCode(row.lxCode()); order.setSourceOrderNo(row.orderNo());
|
||||||
|
order.setCustomerId(row.customerId() == null ? null : String.valueOf(row.customerId())); order.setCustomerName(row.customerName());
|
||||||
|
order.setSourceWarehouseName(row.receivingWarehouseName()); order.setOverseasWarehouseName(row.receivingWarehouseName());
|
||||||
|
order.setFbaWarehouseCode(row.destinationCode());
|
||||||
|
order.setContainerNo(row.containerNo()); order.setPackageCount(row.packageCount()); order.setVolumeCbm(row.volumeCbm());
|
||||||
|
order.setEstimatedArrivalAt(row.expectedArrivalAt()); order.setCurrentStatus(row.mainStatus());
|
||||||
|
order.setCreatedAt(row.createdAt()); order.setUpdatedAt(row.updatedAt()); order.setVersion(0);
|
||||||
|
return summary(order, List.of(), null, "UNSCHEDULED", null, List.of(), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public PageResult<ContainerPlanProgressSummary> containerProgressPage(
|
public PageResult<ContainerPlanProgressSummary> containerProgressPage(
|
||||||
LastlegOrderRequests.ContainerProgressQuery query) {
|
LastlegOrderRequests.ContainerProgressQuery query) {
|
||||||
|
|||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
package com.baiyue.order.controller;
|
||||||
|
|
||||||
|
import com.baiyue.common.core.api.ApiResponse;
|
||||||
|
import com.baiyue.common.core.api.PageResult;
|
||||||
|
import com.baiyue.order.dto.PreDepartureOrderDtos;
|
||||||
|
import com.baiyue.order.dto.UnifiedOrderQuery;
|
||||||
|
import com.baiyue.order.dto.UnifiedOrderResponse;
|
||||||
|
import com.baiyue.order.service.UnifiedOrderQueryService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/** Service-only aggregate query. It intentionally has no platform-user permission. */
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/internal/order/v1/predeparture-orders")
|
||||||
|
public class InternalPreDepartureOrderController {
|
||||||
|
@Resource private UnifiedOrderQueryService service;
|
||||||
|
@Value("${baiyue.security.internal-service-token}") private String expectedToken;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ApiResponse<PageResult<PreDepartureOrderDtos.Row>> page(
|
||||||
|
@RequestHeader(value="X-Internal-Service-Token", required=false) String token,
|
||||||
|
@RequestParam(required=false) Long customerId, @RequestParam(required=false) String orderNo,
|
||||||
|
@RequestParam(required=false) String inboundCode, @RequestParam(defaultValue="1") int page,
|
||||||
|
@RequestParam(defaultValue="200") int size) {
|
||||||
|
if (expectedToken == null || expectedToken.isBlank() || token == null || !java.security.MessageDigest.isEqual(expectedToken.getBytes(StandardCharsets.UTF_8), token.getBytes(StandardCharsets.UTF_8))) throw new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.FORBIDDEN);
|
||||||
|
if (page < 1 || size < 1 || size > 2000) throw new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.BAD_REQUEST);
|
||||||
|
Map<Long, UnifiedOrderResponse.Row> all = new LinkedHashMap<>();
|
||||||
|
// 由统一订单服务一次返回全部履约状态,再按实际开船事实筛选。
|
||||||
|
for (long sourcePage = 1; ; sourcePage++) {
|
||||||
|
UnifiedOrderQuery q = new UnifiedOrderQuery(null, orderNo, "PRE_DEPARTURE", customerId, null, null,
|
||||||
|
inboundCode, null, null, null, null, null, null, null, null, null, null, null,
|
||||||
|
null, null, sourcePage, 200, null);
|
||||||
|
var result = service.platformPage(customerId, q);
|
||||||
|
result.records().forEach(row -> { if (row.transport() == null || row.transport().actualSailingAt() == null) all.putIfAbsent(row.orderId(), row); });
|
||||||
|
if (sourcePage >= result.pages()) break;
|
||||||
|
}
|
||||||
|
List<PreDepartureOrderDtos.Row> rows = all.values().stream().sorted(Comparator.comparing(UnifiedOrderResponse.Row::createdAt, Comparator.nullsLast(Comparator.reverseOrder())))
|
||||||
|
.map(this::row).toList();
|
||||||
|
int from = Math.min((page - 1) * size, rows.size());
|
||||||
|
int to = Math.min(from + size, rows.size());
|
||||||
|
long total = rows.size(), pages = total == 0 ? 0 : (total + size - 1) / size;
|
||||||
|
return ApiResponse.success(new PageResult<>(rows.subList(from, to), page, size, total, pages));
|
||||||
|
}
|
||||||
|
private PreDepartureOrderDtos.Row row(UnifiedOrderResponse.Row r) {
|
||||||
|
var t = r.transport();
|
||||||
|
return new PreDepartureOrderDtos.Row(r.orderId(), r.customerId(), r.orderNo(), r.inboundCode(), r.mainStatus(),
|
||||||
|
r.customerName(), r.lxCode(), r.receivingWarehouseName(), r.destinationWarehouseName(), r.destinationCode(),
|
||||||
|
r.totalPackageCount(), r.actualVolumeCbm() == null ? r.expectedVolumeCbm() : r.actualVolumeCbm(),
|
||||||
|
r.actualGrossWeightKg(), r.totalNetWeightKg(), r.expectedArrivalAt(), t == null ? null : t.containerNo(),
|
||||||
|
t == null ? null : t.cabinetNo(), t == null ? null : t.estimatedSailingAt(), t == null ? null : t.actualSailingAt(), r.createdAt(), r.updatedAt());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.baiyue.order.dto;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/** Internal read model used by lastleg to show orders before an actual sailing fact exists. */
|
||||||
|
public final class PreDepartureOrderDtos {
|
||||||
|
private PreDepartureOrderDtos() {}
|
||||||
|
public record Query(Long customerId, String orderNo, String inboundCode, int page, int size) {}
|
||||||
|
public record Row(Long orderId, Long customerId, String orderNo, String inboundCode,
|
||||||
|
String mainStatus, String customerName, String lxCode,
|
||||||
|
String receivingWarehouseName, String destinationWarehouseName,
|
||||||
|
String destinationCode, Integer packageCount, java.math.BigDecimal volumeCbm,
|
||||||
|
java.math.BigDecimal grossWeightKg, java.math.BigDecimal netWeightKg,
|
||||||
|
LocalDateTime expectedArrivalAt, String containerNo, String cabinetNo,
|
||||||
|
LocalDateTime estimatedSailingAt, LocalDateTime actualSailingAt,
|
||||||
|
LocalDateTime createdAt, LocalDateTime updatedAt) {}
|
||||||
|
}
|
||||||
@@ -148,7 +148,7 @@ public class UnifiedOrderQueryService {
|
|||||||
|
|
||||||
/** 状态投影和当前页摘要共用原筛选,候选订单用于服务内批量收敛范围。 */
|
/** 状态投影和当前页摘要共用原筛选,候选订单用于服务内批量收敛范围。 */
|
||||||
private FirstlegUnifiedOrderClient.Query statusRequest(Long tenantId, UnifiedOrderQuery query, List<Long> candidates) {
|
private FirstlegUnifiedOrderClient.Query statusRequest(Long tenantId, UnifiedOrderQuery query, List<Long> candidates) {
|
||||||
String status = Set.of("PENDING_REVIEW", "COMPLETED").contains(
|
String status = Set.of("PENDING_REVIEW", "COMPLETED", "PRE_DEPARTURE").contains(
|
||||||
query.mainStatus() == null ? "" : query.mainStatus()) ? null : query.mainStatus();
|
query.mainStatus() == null ? "" : query.mainStatus()) ? null : query.mainStatus();
|
||||||
return new FirstlegUnifiedOrderClient.Query(tenantId, query.keyword(), query.orderNo(), status,
|
return new FirstlegUnifiedOrderClient.Query(tenantId, query.keyword(), query.orderNo(), status,
|
||||||
query.customerId(), query.receivingWarehouseId(), query.destinationWarehouseId(), query.inboundCode(),
|
query.customerId(), query.receivingWarehouseId(), query.destinationWarehouseId(), query.inboundCode(),
|
||||||
@@ -261,7 +261,7 @@ public class UnifiedOrderQueryService {
|
|||||||
private PageResult<UnifiedOrderResponse.Row> pageFromFirstleg(Long tenantId, UnifiedOrderQuery query,
|
private PageResult<UnifiedOrderResponse.Row> pageFromFirstleg(Long tenantId, UnifiedOrderQuery query,
|
||||||
boolean includeCoLoadGroups) {
|
boolean includeCoLoadGroups) {
|
||||||
List<FirstlegUnifiedOrderClient.StatusRow> matching = matchingStatusRows(tenantId, query).stream()
|
List<FirstlegUnifiedOrderClient.StatusRow> matching = matchingStatusRows(tenantId, query).stream()
|
||||||
.filter(row -> !StringUtils.hasText(query.mainStatus()) || query.mainStatus().equals(row.mainStatus()))
|
.filter(row -> !StringUtils.hasText(query.mainStatus()) || "PRE_DEPARTURE".equals(query.mainStatus()) || query.mainStatus().equals(row.mainStatus()))
|
||||||
.collect(Collectors.collectingAndThen(Collectors.toMap(FirstlegUnifiedOrderClient.StatusRow::orderId,
|
.collect(Collectors.collectingAndThen(Collectors.toMap(FirstlegUnifiedOrderClient.StatusRow::orderId,
|
||||||
Function.identity(), (left, right) -> left, java.util.LinkedHashMap::new),
|
Function.identity(), (left, right) -> left, java.util.LinkedHashMap::new),
|
||||||
values -> List.copyOf(values.values())));
|
values -> List.copyOf(values.values())));
|
||||||
@@ -682,7 +682,7 @@ public class UnifiedOrderQueryService {
|
|||||||
if (query.taxMode() != null && !Set.of("TAX_REFUND", "BUY_DECLARATION").contains(query.taxMode())) {
|
if (query.taxMode() != null && !Set.of("TAX_REFUND", "BUY_DECLARATION").contains(query.taxMode())) {
|
||||||
throw new BizException(ResultCode.BAD_REQUEST, "taxMode仅支持TAX_REFUND或BUY_DECLARATION");
|
throw new BizException(ResultCode.BAD_REQUEST, "taxMode仅支持TAX_REFUND或BUY_DECLARATION");
|
||||||
}
|
}
|
||||||
if (StringUtils.hasText(query.mainStatus())
|
if (StringUtils.hasText(query.mainStatus()) && !"PRE_DEPARTURE".equals(query.mainStatus())
|
||||||
&& !MAIN_STATUSES.contains(query.mainStatus())
|
&& !MAIN_STATUSES.contains(query.mainStatus())
|
||||||
&& !WORKFLOW_STATUSES.contains(query.mainStatus())
|
&& !WORKFLOW_STATUSES.contains(query.mainStatus())
|
||||||
) {
|
) {
|
||||||
@@ -708,7 +708,7 @@ public class UnifiedOrderQueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean requiresFirstlegQuery(UnifiedOrderQuery query) {
|
private boolean requiresFirstlegQuery(UnifiedOrderQuery query) {
|
||||||
return (StringUtils.hasText(query.mainStatus())
|
return ("PRE_DEPARTURE".equals(query.mainStatus()) || StringUtils.hasText(query.mainStatus())
|
||||||
&& !Set.of("PENDING_REVIEW", "COMPLETED").contains(query.mainStatus()))
|
&& !Set.of("PENDING_REVIEW", "COMPLETED").contains(query.mainStatus()))
|
||||||
|| query.expectedArrivalDate() != null
|
|| query.expectedArrivalDate() != null
|
||||||
|| StringUtils.hasText(query.feeReviewStatus()) || StringUtils.hasText(query.containerNo())
|
|| StringUtils.hasText(query.feeReviewStatus()) || StringUtils.hasText(query.containerNo())
|
||||||
|
|||||||
Reference in New Issue
Block a user