优化
This commit is contained in:
+33
@@ -9,6 +9,12 @@ import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.firstleg.dto.OrderApprovedInboundCommand;
|
||||
import com.baiyue.firstleg.dto.InboundOrderReference;
|
||||
import com.baiyue.firstleg.service.InboundOrderCreationService;
|
||||
import com.baiyue.firstleg.service.UnifiedInboundQueryService;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundDetail;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundQuery;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundRequest;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundSummary;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -28,6 +34,8 @@ import java.security.MessageDigest;
|
||||
public class InternalInboundOrderController {
|
||||
@Resource
|
||||
private InboundOrderCreationService service;
|
||||
@Resource
|
||||
private UnifiedInboundQueryService unifiedQueryService;
|
||||
@Value("${baiyue.security.internal-service-token}")
|
||||
private String internalToken;
|
||||
private byte[] expectedToken;
|
||||
@@ -60,6 +68,31 @@ public class InternalInboundOrderController {
|
||||
request.tenantId(), request.orderIds().stream().distinct().toList()));
|
||||
}
|
||||
|
||||
@PostMapping("/unified-orders/query")
|
||||
public ApiResponse<PageResult<UnifiedInboundSummary>> unifiedPage(
|
||||
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
|
||||
@Valid @RequestBody UnifiedInboundQuery request) {
|
||||
requireToken(token);
|
||||
return ApiResponse.success(unifiedQueryService.page(request));
|
||||
}
|
||||
|
||||
@PostMapping("/unified-orders/summaries")
|
||||
public ApiResponse<List<UnifiedInboundSummary>> unifiedSummaries(
|
||||
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
|
||||
@Valid @RequestBody UnifiedInboundRequest.BatchRequest request) {
|
||||
requireToken(token);
|
||||
return ApiResponse.success(unifiedQueryService.summaries(
|
||||
request.tenantId(), request.orderIds().stream().distinct().toList()));
|
||||
}
|
||||
|
||||
@PostMapping("/unified-orders/detail")
|
||||
public ApiResponse<UnifiedInboundDetail> unifiedDetail(
|
||||
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
|
||||
@Valid @RequestBody UnifiedInboundRequest.DetailRequest request) {
|
||||
requireToken(token);
|
||||
return ApiResponse.success(unifiedQueryService.detail(request.tenantId(), request.orderId()));
|
||||
}
|
||||
|
||||
private void requireToken(String token) {
|
||||
if (token == null || !MessageDigest.isEqual(expectedToken, token.getBytes(StandardCharsets.UTF_8))) {
|
||||
throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效");
|
||||
|
||||
@@ -6,7 +6,8 @@ import java.util.Set;
|
||||
public final class InboundProcessStatus {
|
||||
public static final Set<String> VALUES = Set.of(
|
||||
"WAIT_MEASURE", "MEASURING", "MEASURE_PENDING", "MEASURE_REJECTED",
|
||||
"WAIT_PRICING", "FEE_CONFIRMING", "INBOUNDING", "INBOUNDED", "VOIDED"
|
||||
"WAIT_PRICING", "FEE_CONFIRMING", "INBOUNDING", "INBOUNDED",
|
||||
"WAIT_SPLIT", "LOADING", "LOADED", "DELIVERED", "VOIDED"
|
||||
);
|
||||
|
||||
private InboundProcessStatus() {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.baiyue.firstleg.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/** Detail payload for the product-level order detail. */
|
||||
public record UnifiedInboundDetail(
|
||||
UnifiedInboundSummary summary,
|
||||
List<TimelineEvent> timeline
|
||||
) {
|
||||
public record TimelineEvent(String source, String eventType, String fromStatus, String toStatus,
|
||||
String action, String remark, Long operatorId, String operatorName,
|
||||
LocalDateTime occurredAt) {}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.baiyue.firstleg.dto;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/** Order service internal query for the product-level order line. */
|
||||
public record UnifiedInboundQuery(
|
||||
Long tenantId,
|
||||
@Size(max = 64) String keyword,
|
||||
@Size(max = 64) String orderNo,
|
||||
@Size(max = 32) String mainStatus,
|
||||
Long customerId,
|
||||
Long receivingWarehouseId,
|
||||
Long destinationWarehouseId,
|
||||
LocalDateTime createdFrom,
|
||||
LocalDateTime createdTo,
|
||||
@Min(1) long page,
|
||||
@Min(1) @Max(200) long size
|
||||
) {
|
||||
public UnifiedInboundQuery {
|
||||
if (page == 0) page = 1;
|
||||
if (size == 0) size = 20;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.baiyue.firstleg.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Internal request contracts used by Order service. */
|
||||
public final class UnifiedInboundRequest {
|
||||
private UnifiedInboundRequest() {}
|
||||
|
||||
public record BatchRequest(Long tenantId,
|
||||
@NotEmpty @Size(max = 200) List<@Positive Long> orderIds) {}
|
||||
|
||||
public record DetailRequest(@Positive long tenantId, @Positive long orderId) {}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.baiyue.firstleg.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/** Product-level status and operational summaries returned to Order service. */
|
||||
public record UnifiedInboundSummary(
|
||||
InboundOrderSummary inbound,
|
||||
String mainStatus,
|
||||
String nextAction,
|
||||
PreloadSummary preload,
|
||||
LoadingSummary loading,
|
||||
DeliverySummary delivery
|
||||
) {
|
||||
public record PreloadSummary(String status, Integer version, Integer itemCount, LocalDateTime updatedAt) {}
|
||||
/**
|
||||
* loadingVersion is currently a compatibility alias of pickingVersion because no separate
|
||||
* loading aggregate row exists. C04 loading complete must use inbound.version instead.
|
||||
*/
|
||||
public record LoadingSummary(String status, Integer loadingVersion, Integer pickingVersion,
|
||||
Integer expectedCount, Integer pickedCount, Integer evidenceCount,
|
||||
String sealNo, LocalDateTime updatedAt) {}
|
||||
public record DeliverySummary(String status, LocalDateTime completedAt, String trackingNo) {}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundQuery;
|
||||
|
||||
/** 入库单只读仓储;平台分页可跨企业查询,详情继续强制租户隔离。 */
|
||||
@Repository
|
||||
@@ -103,6 +104,34 @@ public class InboundOrderRepository {
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
/** Internal unified-query accessors. They always retain the tenant predicate when one is supplied. */
|
||||
public Page<InboundOrderEntity> pageUnifiedEntities(UnifiedInboundQuery query, List<String> processStatuses) {
|
||||
LambdaQueryWrapper<InboundOrderEntity> wrapper = new LambdaQueryWrapper<InboundOrderEntity>()
|
||||
.eq(query.tenantId() != null, InboundOrderEntity::getTenantId, query.tenantId())
|
||||
.in(processStatuses != null && !processStatuses.isEmpty(), InboundOrderEntity::getProcessStatus, processStatuses)
|
||||
.like(StringUtils.hasText(query.orderNo()), InboundOrderEntity::getOrderNo, query.orderNo())
|
||||
.and(StringUtils.hasText(query.keyword()), nested -> nested
|
||||
.like(InboundOrderEntity::getOrderNo, query.keyword())
|
||||
.or().like(InboundOrderEntity::getInboundCode, query.keyword())
|
||||
.or().like(InboundOrderEntity::getCustomerName, query.keyword()))
|
||||
.eq(query.customerId() != null, InboundOrderEntity::getCustomerId, query.customerId())
|
||||
.eq(query.receivingWarehouseId() != null, InboundOrderEntity::getWarehouseId, query.receivingWarehouseId())
|
||||
.eq(query.destinationWarehouseId() != null, InboundOrderEntity::getDeliveryWarehouseId, query.destinationWarehouseId())
|
||||
.ge(query.createdFrom() != null, InboundOrderEntity::getCreatedAt, query.createdFrom())
|
||||
.le(query.createdTo() != null, InboundOrderEntity::getCreatedAt, query.createdTo())
|
||||
.orderByDesc(InboundOrderEntity::getCreatedAt).orderByDesc(InboundOrderEntity::getId);
|
||||
return orders.selectPage(new Page<>(query.page(), query.size()), wrapper);
|
||||
}
|
||||
|
||||
public List<InboundOrderEntity> findByOrderIds(Long tenantId, List<Long> orderIds) {
|
||||
if (orderIds == null || orderIds.isEmpty()) return List.of();
|
||||
return orders.selectList(new LambdaQueryWrapper<InboundOrderEntity>()
|
||||
.eq(tenantId != null, InboundOrderEntity::getTenantId, tenantId)
|
||||
.in(InboundOrderEntity::getOrderId, orderIds)
|
||||
.orderByDesc(InboundOrderEntity::getCreatedAt));
|
||||
}
|
||||
|
||||
|
||||
public InboundOrderDetail detail(long tenantId, InboundOrderEntity order) {
|
||||
long inboundId = order.getId();
|
||||
InboundTransportSnapshotEntity transport = transports.selectOne(
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baiyue.firstleg.loading.entity.LoadingEvidenceEntity;
|
||||
import com.baiyue.firstleg.loading.entity.LoadingPickTaskEntity;
|
||||
import com.baiyue.firstleg.loading.mapper.LoadingEvidenceMapper;
|
||||
import com.baiyue.firstleg.loading.mapper.LoadingPickTaskMapper;
|
||||
import com.baiyue.firstleg.split.entity.PreloadPlanEntity;
|
||||
import com.baiyue.firstleg.split.mapper.PreloadPlanMapper;
|
||||
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
||||
import com.baiyue.firstleg.entity.InboundTransportSnapshotEntity;
|
||||
import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
|
||||
import com.baiyue.firstleg.mapper.InboundTransportSnapshotMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Read-only access to phase-three operational summaries for unified order queries. */
|
||||
@Repository
|
||||
public class UnifiedInboundReadRepository {
|
||||
@Resource private PreloadPlanMapper preloads;
|
||||
@Resource private LoadingPickTaskMapper picks;
|
||||
@Resource private LoadingEvidenceMapper evidences;
|
||||
@Resource private InboundStatusHistoryMapper histories;
|
||||
@Resource private InboundTransportSnapshotMapper transports;
|
||||
|
||||
public PreloadPlanEntity findPreload(long tenantId, long inboundId) {
|
||||
return preloads.selectOne(new LambdaQueryWrapper<PreloadPlanEntity>()
|
||||
.eq(PreloadPlanEntity::getTenantId, tenantId)
|
||||
.eq(PreloadPlanEntity::getInboundId, inboundId));
|
||||
}
|
||||
|
||||
public LoadingPickTaskEntity findPick(long tenantId, long inboundId) {
|
||||
return picks.selectOne(new LambdaQueryWrapper<LoadingPickTaskEntity>()
|
||||
.eq(LoadingPickTaskEntity::getTenantId, tenantId)
|
||||
.eq(LoadingPickTaskEntity::getInboundId, inboundId));
|
||||
}
|
||||
|
||||
public List<LoadingEvidenceEntity> findEvidence(long tenantId, long inboundId) {
|
||||
return evidences.selectList(new LambdaQueryWrapper<LoadingEvidenceEntity>()
|
||||
.eq(LoadingEvidenceEntity::getTenantId, tenantId)
|
||||
.eq(LoadingEvidenceEntity::getInboundId, inboundId)
|
||||
.orderByAsc(LoadingEvidenceEntity::getEvidenceType));
|
||||
}
|
||||
|
||||
public Map<Long, PreloadPlanEntity> findPreloads(Map<Long, Long> tenantByInbound) {
|
||||
if (tenantByInbound.isEmpty()) return Map.of();
|
||||
return preloads.selectList(new LambdaQueryWrapper<PreloadPlanEntity>()
|
||||
.in(PreloadPlanEntity::getInboundId, tenantByInbound.keySet()))
|
||||
.stream().filter(item -> owns(tenantByInbound, item.getInboundId(), item.getTenantId()))
|
||||
.collect(Collectors.toMap(PreloadPlanEntity::getInboundId, Function.identity(), (left, right) -> left));
|
||||
}
|
||||
|
||||
public Map<Long, LoadingPickTaskEntity> findPicks(Map<Long, Long> tenantByInbound) {
|
||||
if (tenantByInbound.isEmpty()) return Map.of();
|
||||
return picks.selectList(new LambdaQueryWrapper<LoadingPickTaskEntity>()
|
||||
.in(LoadingPickTaskEntity::getInboundId, tenantByInbound.keySet()))
|
||||
.stream().filter(item -> owns(tenantByInbound, item.getInboundId(), item.getTenantId()))
|
||||
.collect(Collectors.toMap(LoadingPickTaskEntity::getInboundId, Function.identity(), (left, right) -> left));
|
||||
}
|
||||
|
||||
public Map<Long, List<LoadingEvidenceEntity>> findEvidence(Map<Long, Long> tenantByInbound) {
|
||||
if (tenantByInbound.isEmpty()) return Map.of();
|
||||
return evidences.selectList(new LambdaQueryWrapper<LoadingEvidenceEntity>()
|
||||
.in(LoadingEvidenceEntity::getInboundId, tenantByInbound.keySet())
|
||||
.orderByAsc(LoadingEvidenceEntity::getEvidenceType))
|
||||
.stream().filter(item -> owns(tenantByInbound, item.getInboundId(), item.getTenantId()))
|
||||
.collect(Collectors.groupingBy(LoadingEvidenceEntity::getInboundId));
|
||||
}
|
||||
|
||||
public Map<Long, InboundStatusHistoryEntity> findDeliveryHistory(Map<Long, Long> tenantByInbound) {
|
||||
if (tenantByInbound.isEmpty()) return Map.of();
|
||||
return histories.selectList(new LambdaQueryWrapper<InboundStatusHistoryEntity>()
|
||||
.in(InboundStatusHistoryEntity::getInboundId, tenantByInbound.keySet())
|
||||
.and(nested -> nested.eq(InboundStatusHistoryEntity::getAction, "COMPLETE_DELIVERY")
|
||||
.or().eq(InboundStatusHistoryEntity::getToStatus, "DELIVERED"))
|
||||
.orderByDesc(InboundStatusHistoryEntity::getOccurredAt))
|
||||
.stream().filter(item -> owns(tenantByInbound, item.getInboundId(), item.getTenantId()))
|
||||
.collect(Collectors.toMap(InboundStatusHistoryEntity::getInboundId,
|
||||
Function.identity(), (latest, ignored) -> latest));
|
||||
}
|
||||
|
||||
public InboundTransportSnapshotEntity findTransport(long tenantId, long inboundId) {
|
||||
return transports.selectOne(new LambdaQueryWrapper<InboundTransportSnapshotEntity>()
|
||||
.eq(InboundTransportSnapshotEntity::getTenantId, tenantId)
|
||||
.eq(InboundTransportSnapshotEntity::getInboundId, inboundId));
|
||||
}
|
||||
|
||||
public Map<Long, InboundTransportSnapshotEntity> findTransports(Map<Long, Long> tenantByInbound) {
|
||||
if (tenantByInbound.isEmpty()) return Map.of();
|
||||
return transports.selectList(new LambdaQueryWrapper<InboundTransportSnapshotEntity>()
|
||||
.in(InboundTransportSnapshotEntity::getInboundId, tenantByInbound.keySet()))
|
||||
.stream().filter(item -> owns(tenantByInbound, item.getInboundId(), item.getTenantId()))
|
||||
.collect(Collectors.toMap(InboundTransportSnapshotEntity::getInboundId,
|
||||
Function.identity(), (left, right) -> left));
|
||||
}
|
||||
|
||||
private static boolean owns(Map<Long, Long> tenantByInbound, Long inboundId, Long tenantId) {
|
||||
return inboundId != null && tenantId != null && tenantId.equals(tenantByInbound.get(inboundId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.firstleg.dto.InboundOrderDetail;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundDetail;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundQuery;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundSummary;
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import com.baiyue.firstleg.entity.InboundTransportSnapshotEntity;
|
||||
import com.baiyue.firstleg.loading.entity.LoadingEvidenceEntity;
|
||||
import com.baiyue.firstleg.repository.InboundOrderRepository;
|
||||
import com.baiyue.firstleg.repository.UnifiedInboundReadRepository;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Read-only adapter from internal inbound states to the seven product main statuses. */
|
||||
@Service
|
||||
public class UnifiedInboundQueryService {
|
||||
private static final Set<String> MAIN_STATUSES = Set.of(
|
||||
"PENDING_REVIEW", "WAIT_MEASURE", "INBOUNDED", "WAIT_SPLIT", "LOADING", "LOADED", "DELIVERED");
|
||||
|
||||
@Resource private InboundOrderRepository inbounds;
|
||||
@Resource private UnifiedInboundReadRepository operations;
|
||||
@Resource private ObjectMapper json;
|
||||
|
||||
public PageResult<UnifiedInboundSummary> page(UnifiedInboundQuery query) {
|
||||
validate(query);
|
||||
if ("PENDING_REVIEW".equals(query.mainStatus())) {
|
||||
return new PageResult<>(List.of(), query.page(), query.size(), 0, 0);
|
||||
}
|
||||
Page<InboundOrderEntity> source = inbounds.pageUnifiedEntities(query, internalStatuses(query.mainStatus()));
|
||||
List<UnifiedInboundSummary> records = summaries(source.getRecords());
|
||||
return new PageResult<>(records, source.getCurrent(), source.getSize(), source.getTotal(), source.getPages());
|
||||
}
|
||||
|
||||
public List<UnifiedInboundSummary> summaries(Long tenantId, List<Long> orderIds) {
|
||||
Map<Long, UnifiedInboundSummary> byOrder = new LinkedHashMap<>();
|
||||
for (UnifiedInboundSummary item : summaries(inbounds.findByOrderIds(tenantId, orderIds)))
|
||||
byOrder.putIfAbsent(item.inbound().orderId(), item);
|
||||
return orderIds.stream().distinct().map(byOrder::get).filter(java.util.Objects::nonNull).toList();
|
||||
}
|
||||
|
||||
public UnifiedInboundDetail detail(long tenantId, long orderId) {
|
||||
InboundOrderEntity inbound = inbounds.findByOrderId(tenantId, orderId);
|
||||
if (inbound == null) return null;
|
||||
InboundOrderDetail detail = inbounds.detail(tenantId, inbound);
|
||||
List<UnifiedInboundDetail.TimelineEvent> timeline = detail.statusHistory().stream()
|
||||
.map(item -> new UnifiedInboundDetail.TimelineEvent(
|
||||
"FIRSTLEG", "STATUS", item.fromStatus(), item.toStatus(), item.action(), item.reason(),
|
||||
item.operatorId(), item.operatorName(), item.occurredAt()))
|
||||
.toList();
|
||||
return new UnifiedInboundDetail(summary(inbound, detail,
|
||||
operations.findPreload(tenantId, inbound.getId()), operations.findPick(tenantId, inbound.getId()),
|
||||
operations.findEvidence(tenantId, inbound.getId()), deliveryAt(detail.statusHistory()),
|
||||
operations.findTransport(tenantId, inbound.getId())), timeline);
|
||||
}
|
||||
|
||||
private List<UnifiedInboundSummary> summaries(List<InboundOrderEntity> records) {
|
||||
Map<Long, Long> owners = records.stream().collect(Collectors.toMap(
|
||||
InboundOrderEntity::getId, InboundOrderEntity::getTenantId, (left, right) -> left));
|
||||
var preloads = operations.findPreloads(owners);
|
||||
var picks = operations.findPicks(owners);
|
||||
var evidences = operations.findEvidence(owners);
|
||||
var deliveries = operations.findDeliveryHistory(owners);
|
||||
var transports = operations.findTransports(owners);
|
||||
return records.stream().map(inbound -> summary(inbound, null, preloads.get(inbound.getId()),
|
||||
picks.get(inbound.getId()), evidences.getOrDefault(inbound.getId(), List.of()),
|
||||
deliveries.get(inbound.getId()) == null ? null : deliveries.get(inbound.getId()).getOccurredAt(),
|
||||
transports.get(inbound.getId()))).toList();
|
||||
}
|
||||
|
||||
private UnifiedInboundSummary summary(InboundOrderEntity inbound, InboundOrderDetail detail,
|
||||
com.baiyue.firstleg.split.entity.PreloadPlanEntity preload,
|
||||
com.baiyue.firstleg.loading.entity.LoadingPickTaskEntity pick,
|
||||
List<LoadingEvidenceEntity> evidence, LocalDateTime deliveryAt,
|
||||
InboundTransportSnapshotEntity transport) {
|
||||
String mainStatus = mainStatus(inbound.getProcessStatus());
|
||||
String sealNo = evidence.stream().filter(item -> "SEAL".equals(item.getEvidenceType()))
|
||||
.map(LoadingEvidenceEntity::getSealNo).filter(StringUtils::hasText).findFirst().orElse(null);
|
||||
var preloadSummary = preload == null ? null : new UnifiedInboundSummary.PreloadSummary(
|
||||
preload.getStatus(), preload.getVersion(), itemCount(preload.getItemsJson()), preload.getUpdatedAt());
|
||||
var loadingSummary = pick == null && evidence.isEmpty() ? null : new UnifiedInboundSummary.LoadingSummary(
|
||||
pick == null ? null : pick.getStatus(), pick == null ? null : pick.getVersion(),
|
||||
pick == null ? null : pick.getVersion(), pick == null ? null : pick.getExpectedCount(),
|
||||
pick == null ? null : pick.getPickedCount(), evidence.size(), sealNo,
|
||||
pick == null ? evidence.stream().map(LoadingEvidenceEntity::getUpdatedAt)
|
||||
.filter(java.util.Objects::nonNull).max(LocalDateTime::compareTo).orElse(null) : pick.getUpdatedAt());
|
||||
var deliverySummary = "LOADED".equals(mainStatus) || "DELIVERED".equals(mainStatus)
|
||||
? new UnifiedInboundSummary.DeliverySummary(
|
||||
"DELIVERED".equals(mainStatus) ? "COMPLETED" : "PENDING", deliveryAt,
|
||||
inbound.getExpressTrackingNo()) : null;
|
||||
var inboundSummary = detail == null
|
||||
? com.baiyue.firstleg.dto.InboundOrderSummary.from(inbound, transport, null) : detail.summary();
|
||||
return new UnifiedInboundSummary(inboundSummary, mainStatus,
|
||||
nextAction(mainStatus, inbound.getProcessStatus(), preload == null ? null : preload.getStatus()),
|
||||
preloadSummary, loadingSummary, deliverySummary);
|
||||
}
|
||||
|
||||
private LocalDateTime deliveryAt(List<InboundOrderDetail.StatusHistorySummary> history) {
|
||||
return history.stream()
|
||||
.filter(item -> "COMPLETE_DELIVERY".equals(item.action()) || "DELIVERED".equals(item.toStatus()))
|
||||
.map(InboundOrderDetail.StatusHistorySummary::occurredAt)
|
||||
.filter(java.util.Objects::nonNull).max(LocalDateTime::compareTo).orElse(null);
|
||||
}
|
||||
|
||||
static String mainStatus(String status) {
|
||||
if (status == null) return null;
|
||||
return switch (status) {
|
||||
case "WAIT_MEASURE", "MEASURING", "MEASURE_PENDING", "MEASURE_REJECTED" -> "WAIT_MEASURE";
|
||||
case "WAIT_PRICING", "FEE_CONFIRMING", "INBOUNDING", "WAIT_SPLIT" -> "WAIT_SPLIT";
|
||||
case "INBOUNDED" -> "INBOUNDED";
|
||||
case "LOADING", "LOADED", "DELIVERED" -> status;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
static String nextAction(String mainStatus, String processStatus, String preloadStatus) {
|
||||
if (mainStatus == null) return null;
|
||||
return switch (mainStatus) {
|
||||
case "PENDING_REVIEW" -> "APPROVE_ORDER";
|
||||
case "WAIT_MEASURE" -> "MEASURE_PENDING".equals(processStatus) ? "CONFIRM_MEASUREMENT" : null;
|
||||
case "WAIT_SPLIT" -> !"WAIT_SPLIT".equals(processStatus) ? null
|
||||
: "CONFIRMED".equals(preloadStatus) ? null
|
||||
: preloadStatus == null ? "SMART_SPLIT" : "CONFIRM_PRELOAD";
|
||||
case "LOADING" -> "COMPLETE_LOADING";
|
||||
case "LOADED" -> "COMPLETE_DELIVERY";
|
||||
case "DELIVERED" -> null;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private List<String> internalStatuses(String mainStatus) {
|
||||
if (!StringUtils.hasText(mainStatus)) return List.of();
|
||||
return switch (mainStatus) {
|
||||
case "WAIT_MEASURE" -> List.of("WAIT_MEASURE", "MEASURING", "MEASURE_PENDING", "MEASURE_REJECTED");
|
||||
case "WAIT_SPLIT" -> List.of("WAIT_PRICING", "FEE_CONFIRMING", "INBOUNDING", "WAIT_SPLIT");
|
||||
case "INBOUNDED", "LOADING", "LOADED", "DELIVERED" -> List.of(mainStatus);
|
||||
default -> List.of("__NO_MATCH__");
|
||||
};
|
||||
}
|
||||
|
||||
private int itemCount(String itemsJson) {
|
||||
if (!StringUtils.hasText(itemsJson)) return 0;
|
||||
try {
|
||||
JsonNode node = json.readTree(itemsJson);
|
||||
return node.isArray() ? node.size() : 0;
|
||||
} catch (Exception ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(UnifiedInboundQuery query) {
|
||||
if (StringUtils.hasText(query.mainStatus()) && !MAIN_STATUSES.contains(query.mainStatus())) {
|
||||
throw new com.baiyue.common.core.exception.BizException(
|
||||
com.baiyue.common.core.api.ResultCode.BAD_REQUEST, "统一订单主状态不合法");
|
||||
}
|
||||
if (query.createdFrom() != null && query.createdTo() != null
|
||||
&& query.createdFrom().isAfter(query.createdTo())) {
|
||||
throw new com.baiyue.common.core.exception.BizException(
|
||||
com.baiyue.common.core.api.ResultCode.BAD_REQUEST, "创建时间起点不能晚于终点");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user