优化
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package com.baiyue.lastleg.service;
|
||||
|
||||
import com.baiyue.lastleg.client.DomesticWarehouseDirectoryClient;
|
||||
import feign.FeignException;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/** 通过国内 Warehouse Service 权威主档校验尾程转仓 FBA 目的仓。 */
|
||||
@Component
|
||||
public class FbaWarehouseValidator {
|
||||
private static final String INVALID_MESSAGE = "所选FBA仓不存在或已停用";
|
||||
|
||||
@Resource private DomesticWarehouseDirectoryClient client;
|
||||
@Value("${baiyue.security.internal-service-token}") private String internalToken;
|
||||
|
||||
public VerifiedFbaWarehouse required(String requestedId, String requestedCode, String requestedName) {
|
||||
long warehouseId = parseId(requestedId);
|
||||
var response = lookup(warehouseId);
|
||||
if (response == null || !response.successful() || response.data() == null
|
||||
|| response.data().warehouse() == null) {
|
||||
throw invalid();
|
||||
}
|
||||
DomesticWarehouseDirectoryClient.Warehouse warehouse = response.data().warehouse();
|
||||
if (!Objects.equals(warehouse.id(), warehouseId)
|
||||
|| !Objects.equals(warehouse.tenantId(), 0L)
|
||||
|| !"FBA".equals(warehouse.warehouseType())
|
||||
|| !"ACTIVE".equals(warehouse.status())
|
||||
|| !same(requestedCode, warehouse.warehouseCode())
|
||||
|| !same(requestedName, warehouse.warehouseName())) {
|
||||
throw invalid();
|
||||
}
|
||||
String region = firstRegion(response.data().addresses());
|
||||
return new VerifiedFbaWarehouse(warehouse.id(), warehouse.warehouseCode().trim(),
|
||||
warehouse.warehouseName().trim(), trimToNull(warehouse.countryCode()), region);
|
||||
}
|
||||
|
||||
private com.baiyue.common.core.api.ApiResponse<DomesticWarehouseDirectoryClient.Detail> lookup(long warehouseId) {
|
||||
try {
|
||||
return client.activeWarehouse(warehouseId, internalToken);
|
||||
} catch (FeignException exception) {
|
||||
if (exception.status() >= 400 && exception.status() < 500) throw invalid();
|
||||
throw unavailable(exception);
|
||||
} catch (ResponseStatusException exception) {
|
||||
throw exception;
|
||||
} catch (RuntimeException exception) {
|
||||
throw unavailable(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private long parseId(String value) {
|
||||
try {
|
||||
long id = Long.parseLong(value == null ? "" : value.trim());
|
||||
if (id <= 0) throw invalid();
|
||||
return id;
|
||||
} catch (NumberFormatException exception) {
|
||||
throw invalid();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean same(String requested, String actual) {
|
||||
return StringUtils.hasText(requested) && StringUtils.hasText(actual)
|
||||
&& requested.trim().equals(actual.trim());
|
||||
}
|
||||
|
||||
private String firstRegion(List<DomesticWarehouseDirectoryClient.Address> addresses) {
|
||||
if (addresses == null) return null;
|
||||
return addresses.stream().filter(Objects::nonNull)
|
||||
.map(DomesticWarehouseDirectoryClient.Address::stateProvince)
|
||||
.map(FbaWarehouseValidator::trimToNull).filter(Objects::nonNull).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private ResponseStatusException invalid() {
|
||||
return new ResponseStatusException(HttpStatus.BAD_REQUEST, INVALID_MESSAGE);
|
||||
}
|
||||
|
||||
private ResponseStatusException unavailable(RuntimeException cause) {
|
||||
return new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE,
|
||||
"国内FBA仓主数据暂不可用", cause);
|
||||
}
|
||||
|
||||
private static String trimToNull(String value) {
|
||||
return value == null || value.isBlank() ? null : value.trim();
|
||||
}
|
||||
|
||||
public record VerifiedFbaWarehouse(long id, String code, String name, String countryCode, String region) { }
|
||||
}
|
||||
@@ -6,7 +6,8 @@ import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.lastleg.dto.LastlegOrderDetailResponse;
|
||||
import com.baiyue.lastleg.dto.LastlegOrderRequests;
|
||||
import com.baiyue.lastleg.dto.LastlegOrderSummary;
|
||||
import com.baiyue.lastleg.dto.PendingContainerSummary;
|
||||
import com.baiyue.lastleg.dto.ContainerPlanProgressDetail;
|
||||
import com.baiyue.lastleg.dto.ContainerPlanProgressSummary;
|
||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderDetail;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderStatusHistory;
|
||||
@@ -39,6 +40,7 @@ public class LastlegOrderService {
|
||||
@Resource private LastlegOrderDetailMapper detailMapper;
|
||||
@Resource private LastlegOrderStatusHistoryMapper historyMapper;
|
||||
@Resource private InboundPlanCartonMapper cartonMapper;
|
||||
@Resource private FbaWarehouseValidator fbaWarehouseValidator;
|
||||
|
||||
public PageResult<LastlegOrderSummary> page(String customerId, LastlegOrderRequests.Query query) {
|
||||
Page<LastlegOrder> page = orderMapper.selectPage(
|
||||
@@ -48,20 +50,33 @@ public class LastlegOrderService {
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PageResult<PendingContainerSummary> pendingContainerPage(
|
||||
LastlegOrderRequests.PendingContainerQuery query) {
|
||||
public PageResult<ContainerPlanProgressSummary> containerProgressPage(
|
||||
LastlegOrderRequests.ContainerProgressQuery query) {
|
||||
if (query.createdFrom() != null && query.createdTo() != null
|
||||
&& query.createdFrom().isAfter(query.createdTo())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||
"createdFrom must not be after createdTo");
|
||||
}
|
||||
long total = orderMapper.countPendingContainers(query);
|
||||
List<PendingContainerSummary> records = total == 0 ? List.of()
|
||||
: orderMapper.pagePendingContainers(query, (query.page() - 1) * query.size(), query.size());
|
||||
long total = orderMapper.countContainerProgress(query);
|
||||
List<ContainerPlanProgressSummary> records = total == 0 ? List.of()
|
||||
: orderMapper.pageContainerProgress(query, (query.page() - 1) * query.size(), query.size());
|
||||
long pages = total == 0 ? 0 : (total + query.size() - 1) / query.size();
|
||||
return new PageResult<>(records, query.page(), query.size(), total, pages);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public ContainerPlanProgressDetail containerProgressDetail(String sourceContainerBookingId) {
|
||||
String bookingId = sourceContainerBookingId == null ? null : sourceContainerBookingId.trim();
|
||||
if (!StringUtils.hasText(bookingId)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "来源柜预订ID不能为空");
|
||||
}
|
||||
ContainerPlanProgressSummary summary = orderMapper.findContainerProgress(bookingId);
|
||||
if (summary == null) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "来源柜不存在");
|
||||
}
|
||||
return new ContainerPlanProgressDetail(summary, orderMapper.findContainerProgressItems(bookingId));
|
||||
}
|
||||
|
||||
public LastlegOrderDetailResponse detail(String customerId, long orderId) {
|
||||
LastlegOrder order = findOwned(customerId, orderId);
|
||||
List<LastlegOrderDetail> cargo = detailMapper.selectList(new LambdaQueryWrapper<LastlegOrderDetail>()
|
||||
@@ -83,6 +98,9 @@ public class LastlegOrderService {
|
||||
if (ids.isEmpty()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "At least one order is required");
|
||||
}
|
||||
FbaWarehouseValidator.VerifiedFbaWarehouse target = fbaWarehouseValidator.required(
|
||||
request.warehouseId(), request.warehouseCode(), request.warehouseName());
|
||||
String warehouseId = Long.toString(target.id());
|
||||
List<LastlegOrder> orders = orderMapper.selectList(new LambdaQueryWrapper<LastlegOrder>()
|
||||
.eq(StringUtils.hasText(customerId), LastlegOrder::getCustomerId, customerId)
|
||||
.in(LastlegOrder::getId, ids).last("FOR UPDATE"));
|
||||
@@ -95,25 +113,25 @@ public class LastlegOrderService {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
for (LastlegOrder order : orders) {
|
||||
String fromStatus = order.getCurrentStatus();
|
||||
order.setTransferWarehouseId(request.warehouseId());
|
||||
order.setTransferWarehouseCode(request.warehouseCode());
|
||||
order.setTransferWarehouseName(request.warehouseName());
|
||||
order.setTransferWarehouseId(warehouseId);
|
||||
order.setTransferWarehouseCode(target.code());
|
||||
order.setTransferWarehouseName(target.name());
|
||||
order.setCurrentStatus(DESTINATION_ASSIGNED);
|
||||
order.setUpdatedAt(now);
|
||||
orderMapper.updateById(order);
|
||||
|
||||
LastlegOrderDetail detailUpdate = new LastlegOrderDetail();
|
||||
detailUpdate.setDestinationWarehouseId(request.warehouseId());
|
||||
detailUpdate.setDestinationWarehouseCode(request.warehouseCode());
|
||||
detailUpdate.setDestinationWarehouseName(request.warehouseName());
|
||||
detailUpdate.setDestinationWarehouseId(warehouseId);
|
||||
detailUpdate.setDestinationWarehouseCode(target.code());
|
||||
detailUpdate.setDestinationWarehouseName(target.name());
|
||||
detailMapper.update(detailUpdate, new LambdaQueryWrapper<LastlegOrderDetail>()
|
||||
.eq(LastlegOrderDetail::getCustomerId, order.getCustomerId())
|
||||
.eq(LastlegOrderDetail::getOrderId, order.getId()));
|
||||
|
||||
InboundPlanCarton cartonUpdate = new InboundPlanCarton();
|
||||
cartonUpdate.setDestinationWarehouseId(request.warehouseId());
|
||||
cartonUpdate.setDestinationWarehouseCode(request.warehouseCode());
|
||||
cartonUpdate.setDestinationWarehouseName(request.warehouseName());
|
||||
cartonUpdate.setDestinationWarehouseId(warehouseId);
|
||||
cartonUpdate.setDestinationWarehouseCode(target.code());
|
||||
cartonUpdate.setDestinationWarehouseName(target.name());
|
||||
cartonMapper.update(cartonUpdate, new LambdaQueryWrapper<InboundPlanCarton>()
|
||||
.eq(InboundPlanCarton::getCustomerId, order.getCustomerId())
|
||||
.eq(InboundPlanCarton::getOrderId, order.getId()));
|
||||
@@ -124,7 +142,7 @@ public class LastlegOrderService {
|
||||
history.setFromStatus(fromStatus);
|
||||
history.setToStatus(DESTINATION_ASSIGNED);
|
||||
history.setRemark(StringUtils.hasText(request.remark())
|
||||
? request.remark() : "Destination warehouse assigned: " + request.warehouseCode());
|
||||
? request.remark() : "Destination warehouse assigned: " + target.code());
|
||||
history.setCreatedAt(now);
|
||||
historyMapper.insert(history);
|
||||
}
|
||||
|
||||
+10
@@ -190,6 +190,7 @@ public class LoadingCompletedConsumer {
|
||||
order.setContainerStatus("待转柜"); order.setAbnormal(false); order.setDetained(false);
|
||||
order.setPackageCount(source.packageCount()); order.setPalletCount(dropshipPallets.size());
|
||||
order.setProductQuantity(source.productQuantity()); order.setVolumeCbm(source.volumeCbm());
|
||||
order.setEstimatedWarehouseAt(parsePlannedArrival(payload.estimatedWarehouseArrivalAt()));
|
||||
order.setVolumeWeightKg(source.items().stream().map(FirstLegLoadingPlanCompletedV1.Item::dimensionalWeightKg)
|
||||
.filter(Objects::nonNull).reduce(BigDecimal.ZERO,BigDecimal::add));
|
||||
order.setCurrentStatus("PENDING_ASSIGNMENT"); order.setSourceEventId(event.eventId());
|
||||
@@ -237,6 +238,15 @@ public class LoadingCompletedConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime parsePlannedArrival(String value) {
|
||||
if (value == null || value.isBlank()) return null;
|
||||
try {
|
||||
return LocalDateTime.parse(value.trim());
|
||||
} catch (java.time.format.DateTimeParseException exception) {
|
||||
throw new IllegalArgumentException("事件计划到仓时间 estimatedWarehouseArrivalAt 无效", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private void createInboundOrderSnapshot(EventEnvelope event, FirstLegLoadingPlanCompletedV1 payload,
|
||||
FirstLegLoadingPlanCompletedV1.Order source, LastlegOrder order,
|
||||
String orderHash) {
|
||||
|
||||
Reference in New Issue
Block a user