This commit is contained in:
2026-08-31 10:09:14 +08:00
parent ff6a8498a6
commit aa583efa4a
14 changed files with 320 additions and 92 deletions
@@ -0,0 +1,23 @@
package com.baiyue.order.client;
import com.baiyue.common.core.api.ApiResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import java.util.List;
/** Platform-scoped read contract for Firstleg co-load group aggregation. */
@FeignClient(name = "firstleg-service", contextId = "orderFirstlegColoadGroupClient",
url = "${baiyue.clients.firstleg.url:}", path = "/api/platform/v1/first-leg/coload-groups")
public interface FirstlegColoadGroupClient {
@PostMapping("/batch-query")
ApiResponse<List<OrderGroupInfo>> batchQuery(@RequestHeader("Authorization") String authorization,
@RequestBody BatchQueryRequest request);
record BatchQueryRequest(List<Long> orderIds) {}
record OrderGroupInfo(Long orderId, GroupSnapshot group) {}
record GroupSnapshot(Long groupId, String groupNo, String status, List<Member> members) {}
record Member(Long tenantId, Long orderId, Long inboundId, Integer fullPackageCount) {}
}
@@ -24,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@@ -41,11 +42,12 @@ public class PlatformUnifiedOrderController {
@Operation(summary = "统一订单列表", description = "返回七态产品主状态及测量、预装、装柜、派送摘要。")
public ApiResponse<PageResult<UnifiedOrderResponse.Row>> page(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestHeader("Authorization") String authorization,
@RequestParam(required = false) Long tenantId,
@Valid UnifiedOrderQuery query) {
OrderScopeGuard.platform(user);
if (tenantId != null) OrderScopeGuard.platform(user, tenantId);
return ApiResponse.success(service.page(tenantId, query));
return ApiResponse.success(service.platformPage(tenantId, query, authorization));
}
@GetMapping("/{orderId}")
@@ -53,9 +55,11 @@ public class PlatformUnifiedOrderController {
@Operation(summary = "统一订单详情", description = "合并订单和履约时间线;平台响应包含内部备注。")
public ApiResponse<UnifiedOrderResponse.Detail> detail(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestHeader("Authorization") String authorization,
@RequestParam long tenantId,
@PathVariable long orderId) {
return ApiResponse.success(service.detail(OrderScopeGuard.platform(user, tenantId), orderId, true));
return ApiResponse.success(service.platformDetail(
OrderScopeGuard.platform(user, tenantId), orderId, authorization));
}
@GetMapping("/{orderId}/fulfillment-tree")
@@ -32,6 +32,7 @@ public final class UnifiedOrderResponse {
List<String> skuNos, Integer totalQuantity, String shipmentId, String referenceId,
MeasurementSummary measurement, PreloadSummary preload,
LoadingSummary loading, DeliverySummary delivery, TransportSummary transport,
CoLoadGroupSummary coLoadGroup,
String customerRemark, String latestInternalNote,
LocalDateTime createdAt, LocalDateTime updatedAt, LocalDateTime requestedPickupAt
) {}
@@ -39,6 +40,11 @@ public final class UnifiedOrderResponse {
public record OrderRelationSummary(String role, Long rootOrderId, Long parentOrderId,
Integer splitSequence, Integer childCount) {}
/** Platform-only Firstleg co-load group view; identifiers stay strings for routing only. */
public record CoLoadGroupSummary(String groupId, String groupNo, String status,
Integer memberCount, List<CoLoadGroupMemberSummary> members) {}
public record CoLoadGroupMemberSummary(String orderId, String inboundCode) {}
public record Detail(Row summary, OrderDetailResponse order,
List<TimelineEvent> timeline) {}
@@ -5,6 +5,7 @@ import com.baiyue.common.core.api.PageResult;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.order.client.FirstlegUnifiedOrderClient;
import com.baiyue.order.client.FirstlegColoadGroupClient;
import com.baiyue.order.dto.OrderDetailResponse;
import com.baiyue.order.dto.UnifiedOrderQuery;
import com.baiyue.order.dto.UnifiedOrderResponse;
@@ -44,15 +45,26 @@ public class UnifiedOrderQueryService {
@Resource private OrderRepository orders;
@Resource private FirstlegUnifiedOrderClient firstleg;
@Resource private FirstlegColoadGroupClient coloadGroups;
@Value("${baiyue.security.internal-service-token}") private String internalToken;
public PageResult<UnifiedOrderResponse.Row> page(Long tenantId, UnifiedOrderQuery query) {
return page(tenantId, query, null);
}
public PageResult<UnifiedOrderResponse.Row> platformPage(Long tenantId, UnifiedOrderQuery query,
String authorization) {
return page(tenantId, query, authorization);
}
private PageResult<UnifiedOrderResponse.Row> page(Long tenantId, UnifiedOrderQuery query,
String platformAuthorization) {
validate(query);
if (requiresFirstlegQuery(query)) {
if ("PENDING_REVIEW".equals(query.mainStatus())) {
return new PageResult<>(List.of(), query.page(), query.size(), 0, 0);
}
return pageFromFirstleg(tenantId, query);
return pageFromFirstleg(tenantId, query, platformAuthorization);
}
List<OrderStatus> statuses = "PENDING_REVIEW".equals(query.mainStatus())
? List.of(OrderStatus.SUBMITTED) : ACTIVE_ORDER_STATUSES;
@@ -64,13 +76,24 @@ public class UnifiedOrderQueryService {
Map<Long, CargoSummary> cargoSummaries = cargoSummaries(tenantId,
source.records().stream().map(OrderEntity::getId).toList());
Map<Long, Integer> childCounts = childCounts(source.records());
Map<Long, UnifiedOrderResponse.CoLoadGroupSummary> coLoadByOrder = coLoadGroups(
source.records().stream().map(OrderEntity::getId).toList(), platformAuthorization);
List<UnifiedOrderResponse.Row> rows = source.records().stream()
.map(order -> row(order, inboundByOrder.get(order.getId()), latestNotes.get(order.getId()),
childCounts, cargoSummaries.get(order.getId()))).toList();
childCounts, cargoSummaries.get(order.getId()), coLoadByOrder.get(order.getId()))).toList();
return new PageResult<>(rows, source.page(), source.size(), source.total(), source.pages());
}
public UnifiedOrderResponse.Detail detail(long tenantId, long orderId, boolean includeInternalNotes) {
return detail(tenantId, orderId, includeInternalNotes, null);
}
public UnifiedOrderResponse.Detail platformDetail(long tenantId, long orderId, String authorization) {
return detail(tenantId, orderId, true, authorization);
}
private UnifiedOrderResponse.Detail detail(long tenantId, long orderId, boolean includeInternalNotes,
String platformAuthorization) {
OrderEntity order = orders.findOwned(tenantId, orderId);
if (order == null) throw new BizException(ResultCode.NOT_FOUND, "订单不存在");
OrderDetailResponse orderDetail = new OrderDetailResponse(order, orders.findCargo(tenantId, orderId),
@@ -90,7 +113,8 @@ public class UnifiedOrderQueryService {
.findFirst()
.orElse(null));
UnifiedOrderResponse.Row summary = row(order, inbound == null ? null : inbound.summary(), latestNote,
childCounts(List.of(order)), CargoSummary.from(orderDetail.cargoes()), creatorName);
childCounts(List.of(order)), CargoSummary.from(orderDetail.cargoes()), creatorName,
coLoadGroups(List.of(orderId), platformAuthorization).get(orderId));
List<UnifiedOrderResponse.TimelineEvent> timeline = new ArrayList<>();
orderDetail.statusHistory().forEach(item -> timeline.add(new UnifiedOrderResponse.TimelineEvent(
"ORDER", "STATUS", orderMainStatus(item.getToStatus()), item.getAction(), item.getReason(),
@@ -105,7 +129,8 @@ public class UnifiedOrderQueryService {
return new UnifiedOrderResponse.Detail(summary, orderDetail, timeline);
}
private PageResult<UnifiedOrderResponse.Row> pageFromFirstleg(Long tenantId, UnifiedOrderQuery query) {
private PageResult<UnifiedOrderResponse.Row> pageFromFirstleg(Long tenantId, UnifiedOrderQuery query,
String platformAuthorization) {
List<Long> candidateOrderIds = StringUtils.hasText(query.fbaCode())
? orders.findOrderIdsBySkuNo(tenantId, query.fbaCode()) : null;
if (candidateOrderIds != null && candidateOrderIds.isEmpty()) {
@@ -126,10 +151,13 @@ public class UnifiedOrderQueryService {
Map<Long, String> latestNotes = latestNotes(tenantId, orderIds);
Map<Long, CargoSummary> cargoSummaries = cargoSummaries(tenantId, orderIds);
Map<Long, Integer> childCounts = childCounts(List.copyOf(orderById.values()));
Map<Long, UnifiedOrderResponse.CoLoadGroupSummary> coLoadByOrder =
coLoadGroups(orderIds, platformAuthorization);
List<UnifiedOrderResponse.Row> records = source.records().stream()
.map(summary -> row(summary, orderById.get(summary.inbound().orderId()),
latestNotes.get(summary.inbound().orderId()), childCounts,
cargoSummaries.get(summary.inbound().orderId())))
cargoSummaries.get(summary.inbound().orderId()),
coLoadByOrder.get(summary.inbound().orderId())))
.toList();
return new PageResult<>(records, source.page(), source.size(), source.total(), source.pages());
}
@@ -168,6 +196,40 @@ public class UnifiedOrderQueryService {
.collect(Collectors.toMap(OrderEntity::getParentOrderId, ignored -> 1, Integer::sum));
}
private Map<Long, UnifiedOrderResponse.CoLoadGroupSummary> coLoadGroups(
List<Long> orderIds, String platformAuthorization) {
if (!StringUtils.hasText(platformAuthorization) || orderIds.isEmpty()) return Map.of();
List<FirstlegColoadGroupClient.OrderGroupInfo> data = require(
call(() -> coloadGroups.batchQuery(platformAuthorization,
new FirstlegColoadGroupClient.BatchQueryRequest(orderIds.stream().distinct().toList()))),
"同柜组摘要响应无效");
List<Long> memberOrderIds = data.stream()
.filter(Objects::nonNull).map(FirstlegColoadGroupClient.OrderGroupInfo::group)
.filter(Objects::nonNull).flatMap(group -> safeMembers(group).stream())
.map(FirstlegColoadGroupClient.Member::orderId).filter(Objects::nonNull).distinct().toList();
Map<Long, String> inboundCodeByOrder = orders.findByIds(null, memberOrderIds).stream()
.collect(Collectors.toMap(OrderEntity::getId, OrderEntity::getInboundCode, (left, right) -> left));
return data.stream().filter(Objects::nonNull)
.filter(info -> info.orderId() != null && info.group() != null)
.collect(Collectors.toMap(FirstlegColoadGroupClient.OrderGroupInfo::orderId,
info -> coLoadGroup(info.group(), inboundCodeByOrder), (left, right) -> left));
}
private List<FirstlegColoadGroupClient.Member> safeMembers(FirstlegColoadGroupClient.GroupSnapshot group) {
return group.members() == null ? List.of() : group.members();
}
private UnifiedOrderResponse.CoLoadGroupSummary coLoadGroup(
FirstlegColoadGroupClient.GroupSnapshot group, Map<Long, String> inboundCodeByOrder) {
List<UnifiedOrderResponse.CoLoadGroupMemberSummary> members = safeMembers(group).stream()
.filter(member -> member.orderId() != null)
.map(member -> new UnifiedOrderResponse.CoLoadGroupMemberSummary(
string(member.orderId()), inboundCodeByOrder.get(member.orderId())))
.toList();
return new UnifiedOrderResponse.CoLoadGroupSummary(
string(group.groupId()), group.groupNo(), group.status(), members.size(), members);
}
private UnifiedOrderResponse.OrderRelationSummary relation(OrderEntity order,
Map<Long, Integer> childCounts) {
if (order == null) return null;
@@ -184,12 +246,20 @@ public class UnifiedOrderQueryService {
private UnifiedOrderResponse.Row row(OrderEntity order, FirstlegUnifiedOrderClient.Summary summary,
String latestInternalNote, Map<Long, Integer> childCounts,
CargoSummary cargo) {
return row(order, summary, latestInternalNote, childCounts, cargo, null);
return row(order, summary, latestInternalNote, childCounts, cargo, null, null);
}
private UnifiedOrderResponse.Row row(OrderEntity order, FirstlegUnifiedOrderClient.Summary summary,
String latestInternalNote, Map<Long, Integer> childCounts,
CargoSummary cargo, String creatorName) {
CargoSummary cargo,
UnifiedOrderResponse.CoLoadGroupSummary coLoadGroup) {
return row(order, summary, latestInternalNote, childCounts, cargo, null, coLoadGroup);
}
private UnifiedOrderResponse.Row row(OrderEntity order, FirstlegUnifiedOrderClient.Summary summary,
String latestInternalNote, Map<Long, Integer> childCounts,
CargoSummary cargo, String creatorName,
UnifiedOrderResponse.CoLoadGroupSummary coLoadGroup) {
if (summary == null) {
String mainStatus = orderMainStatus(order.getStatus());
return new UnifiedOrderResponse.Row(order.getId(), order.getTenantId(), order.getOrderNo(), mainStatus,
@@ -204,7 +274,7 @@ public class UnifiedOrderQueryService {
order.getCustomsInspection(), order.getDestinationType(), order.getAmazonWarehouseCode(),
null, null, null, null, netWeightKg(cargo), null, null, order.getExpressCompany(), order.getExpressTrackingNo(),
StringUtils.hasText(creatorName) ? creatorName : string(order.getCreatedBy()), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo),
null, null, null, null, null,
null, null, null, null, null, coLoadGroup,
order.getCustomerRemark(), latestInternalNote,
order.getCreatedAt(), order.getUpdatedAt(), order.getRequestedPickupAt());
}
@@ -224,13 +294,14 @@ public class UnifiedOrderQueryService {
inbound.measureCompletedAt(), inbound.inboundedAt(), inbound.expressCompanyName(), inbound.expressTrackingNo(),
StringUtils.hasText(creatorName) ? creatorName : inbound.createdByName(), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo), measurement(inbound),
preload(summary.preload()), loading(summary.loading()),
delivery(summary.delivery()), transport(inbound), order.getCustomerRemark(), latestInternalNote,
delivery(summary.delivery()), transport(inbound), coLoadGroup, order.getCustomerRemark(), latestInternalNote,
order.getCreatedAt(), order.getUpdatedAt(), inbound.requestedPickupAt());
}
private UnifiedOrderResponse.Row row(FirstlegUnifiedOrderClient.Summary summary, OrderEntity order,
String latestInternalNote, Map<Long, Integer> childCounts,
CargoSummary cargo) {
CargoSummary cargo,
UnifiedOrderResponse.CoLoadGroupSummary coLoadGroup) {
var inbound = summary.inbound();
return new UnifiedOrderResponse.Row(inbound.orderId(), inbound.tenantId(), inbound.orderNo(),
summary.mainStatus(), summary.nextAction(), relation(order, childCounts),
@@ -250,7 +321,8 @@ public class UnifiedOrderQueryService {
inbound.measureCompletedAt(), inbound.inboundedAt(), inbound.expressCompanyName(), inbound.expressTrackingNo(),
inbound.createdByName(), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo),
measurement(inbound), preload(summary.preload()), loading(summary.loading()),
delivery(summary.delivery()), transport(inbound), order == null ? null : order.getCustomerRemark(), latestInternalNote,
delivery(summary.delivery()), transport(inbound), coLoadGroup,
order == null ? null : order.getCustomerRemark(), latestInternalNote,
inbound.createdAt(), inbound.updatedAt(), inbound.requestedPickupAt());
}