二期内容提交
This commit is contained in:
@@ -18,4 +18,6 @@ public class InboundProperties {
|
||||
private boolean allowRejectedMeasurement = false;
|
||||
private BigDecimal defaultPalletCapacityCbm = new BigDecimal("1.200000");
|
||||
private Duration reservationTtl = Duration.ofMinutes(30);
|
||||
/** 共享收货仓资源所属租户;0 表示与入库业务租户一致。 */
|
||||
private long warehouseResourceTenantId = 0L;
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.baiyue.firstleg.controller;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.firstleg.dto.OrderApprovedInboundCommand;
|
||||
import com.baiyue.firstleg.service.InboundOrderCreationService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/** 接收订单服务审核通过事件,创建入库单。 */
|
||||
@RestController
|
||||
@RequestMapping("/internal/firstleg/v1/inbounds")
|
||||
public class InternalInboundOrderController {
|
||||
private final InboundOrderCreationService service;
|
||||
private final byte[] expectedToken;
|
||||
|
||||
public InternalInboundOrderController(InboundOrderCreationService service,
|
||||
@Value("${baiyue.security.internal-service-token}") String token) {
|
||||
this.service = service;
|
||||
this.expectedToken = token.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@PostMapping("/order-approved")
|
||||
public ApiResponse<Long> createFromApprovedOrder(
|
||||
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
|
||||
@RequestBody OrderApprovedInboundCommand command) {
|
||||
if (token == null || !MessageDigest.isEqual(expectedToken, token.getBytes(StandardCharsets.UTF_8))) {
|
||||
throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效");
|
||||
}
|
||||
if (command.tenantId() <= 0 || command.orderId() <= 0 || command.warehouseId() == null
|
||||
|| command.warehouseId() <= 0 || command.customerName() == null || command.customerName().isBlank()) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "订单入库快照不完整");
|
||||
}
|
||||
return ApiResponse.success(service.createFromApprovedOrder(command));
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ public record InboundOrderSummary(
|
||||
Long id, Long orderId, String orderNo, String inboundCode,
|
||||
String processStatus, String orderStatusSnapshot, String measureReviewStatus,
|
||||
String feeStatus, String feeReviewStatus, String notificationStatus,
|
||||
Integer version,
|
||||
LocalDateTime expectedArrivalAt,
|
||||
Long customerId, String customerName, Long companyId, String companyName,
|
||||
Long warehouseId, String warehouseName,
|
||||
@@ -35,6 +36,7 @@ public record InboundOrderSummary(
|
||||
order.getId(), order.getOrderId(), order.getOrderNo(), order.getInboundCode(),
|
||||
order.getProcessStatus(), order.getOrderStatusSnapshot(), order.getMeasureReviewStatus(),
|
||||
order.getFeeStatus(), order.getFeeReviewStatus(), order.getNotificationStatus(),
|
||||
order.getVersion(),
|
||||
order.getExpectedArrivalAt(), order.getCustomerId(), order.getCustomerName(),
|
||||
order.getCompanyId(), order.getCompanyName(), order.getWarehouseId(), order.getWarehouseName(),
|
||||
order.getDeliveryWarehouseId(), order.getDeliveryWarehouseName(), order.getBusinessType(),
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.baiyue.firstleg.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/** 订单审核通过后创建入库单的内部服务契约。 */
|
||||
public record OrderApprovedInboundCommand(
|
||||
long tenantId,
|
||||
long orderId,
|
||||
String orderNo,
|
||||
Long customerId,
|
||||
String customerName,
|
||||
Long warehouseId,
|
||||
String warehouseName,
|
||||
Long deliveryWarehouseId,
|
||||
String deliveryWarehouseName,
|
||||
LocalDateTime expectedArrivalAt,
|
||||
String businessType,
|
||||
String channelCode,
|
||||
String channelName,
|
||||
Long carrierId,
|
||||
String carrierName,
|
||||
String taxMode,
|
||||
String containerMode,
|
||||
Boolean customsInspection,
|
||||
String destinationType,
|
||||
Long destinationWarehouseId,
|
||||
String destinationCode,
|
||||
String destinationName,
|
||||
String publicRemark,
|
||||
String expressCompanyName,
|
||||
String expressTrackingNo
|
||||
) {
|
||||
}
|
||||
@@ -40,6 +40,8 @@ public class InboundOrderEntity extends BaseEntity {
|
||||
private String businessType;
|
||||
private String channelCode;
|
||||
private String channelName;
|
||||
private Long carrierId;
|
||||
private String carrierName;
|
||||
private String taxMode;
|
||||
private String containerMode;
|
||||
private Boolean customsInspection;
|
||||
|
||||
+11
-1
@@ -9,6 +9,9 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Repository
|
||||
public class InboundPalletAllocationRepository {
|
||||
@@ -21,6 +24,11 @@ public class InboundPalletAllocationRepository {
|
||||
@Transactional
|
||||
public void replace(long tenantId, long inboundId, long warehouseId, BigDecimal capacity,
|
||||
BatchResult result) {
|
||||
Map<Long, String> reservationNos = mapper.selectList(new LambdaQueryWrapper<InboundPalletAllocationEntity>()
|
||||
.eq(InboundPalletAllocationEntity::getTenantId, tenantId)
|
||||
.eq(InboundPalletAllocationEntity::getInboundId, inboundId))
|
||||
.stream().collect(Collectors.toMap(InboundPalletAllocationEntity::getPalletId,
|
||||
InboundPalletAllocationEntity::getReservationNo, (left, right) -> left));
|
||||
mapper.delete(new LambdaQueryWrapper<InboundPalletAllocationEntity>()
|
||||
.eq(InboundPalletAllocationEntity::getTenantId, tenantId)
|
||||
.eq(InboundPalletAllocationEntity::getInboundId, inboundId));
|
||||
@@ -37,7 +45,9 @@ public class InboundPalletAllocationRepository {
|
||||
value.setAllocatedVolumeCbm(source.allocatedVolumeCbm());
|
||||
value.setLocationId(source.locationId());
|
||||
value.setLocationCode(source.locationCode());
|
||||
value.setReservationNo(source.reservationNo());
|
||||
// 仓储确认响应只返回 reservationId;沿用预占阶段的预占单号快照。
|
||||
value.setReservationNo(source.reservationNo() != null ? source.reservationNo()
|
||||
: reservationNos.get(source.palletId()));
|
||||
value.setAllocationStatus(normalize(source.state()));
|
||||
value.setCreatedAt(now);
|
||||
value.setUpdatedAt(now);
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baiyue.firstleg.dto.OrderApprovedInboundCommand;
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
||||
import com.baiyue.firstleg.mapper.InboundOrderMapper;
|
||||
import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/** 将已审核的订单快照幂等转换为待测量入库单。 */
|
||||
@Service
|
||||
public class InboundOrderCreationService {
|
||||
private final InboundOrderMapper orders;
|
||||
private final InboundStatusHistoryMapper histories;
|
||||
|
||||
public InboundOrderCreationService(InboundOrderMapper orders, InboundStatusHistoryMapper histories) {
|
||||
this.orders = orders;
|
||||
this.histories = histories;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public long createFromApprovedOrder(OrderApprovedInboundCommand command) {
|
||||
InboundOrderEntity existing = orders.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<InboundOrderEntity>()
|
||||
.eq(InboundOrderEntity::getTenantId, command.tenantId())
|
||||
.eq(InboundOrderEntity::getOrderId, command.orderId()));
|
||||
if (existing != null) {
|
||||
return existing.getId();
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
InboundOrderEntity inbound = new InboundOrderEntity();
|
||||
inbound.setId(IdWorker.getId());
|
||||
inbound.setTenantId(command.tenantId());
|
||||
inbound.setOrderId(command.orderId());
|
||||
inbound.setOrderNo(command.orderNo());
|
||||
inbound.setInboundCode("RK-" + command.orderId());
|
||||
inbound.setProcessStatus("WAIT_MEASURE");
|
||||
inbound.setOrderStatusSnapshot("APPROVED");
|
||||
inbound.setMeasureReviewStatus("NOT_SUBMITTED");
|
||||
inbound.setFeeStatus("UNPRICED");
|
||||
inbound.setFeeReviewStatus("NOT_SUBMITTED");
|
||||
inbound.setNotificationStatus("NOT_CREATED");
|
||||
inbound.setExpectedArrivalAt(command.expectedArrivalAt());
|
||||
inbound.setCustomerId(command.customerId());
|
||||
inbound.setCustomerName(command.customerName());
|
||||
inbound.setWarehouseId(command.warehouseId());
|
||||
inbound.setWarehouseName(command.warehouseName());
|
||||
inbound.setDeliveryWarehouseId(command.deliveryWarehouseId());
|
||||
inbound.setDeliveryWarehouseName(command.deliveryWarehouseName());
|
||||
inbound.setBusinessType(command.businessType());
|
||||
inbound.setChannelCode(command.channelCode());
|
||||
inbound.setChannelName(command.channelName());
|
||||
inbound.setCarrierId(command.carrierId());
|
||||
inbound.setCarrierName(command.carrierName());
|
||||
inbound.setTaxMode(command.taxMode());
|
||||
inbound.setContainerMode(command.containerMode());
|
||||
inbound.setCustomsInspection(command.customsInspection());
|
||||
inbound.setDestinationType(command.destinationType());
|
||||
inbound.setDestinationWarehouseId(command.destinationWarehouseId());
|
||||
inbound.setDestinationCode(command.destinationCode());
|
||||
inbound.setDestinationName(command.destinationName());
|
||||
inbound.setExpressCompanyName(command.expressCompanyName());
|
||||
inbound.setExpressTrackingNo(command.expressTrackingNo());
|
||||
inbound.setPublicRemark(command.publicRemark());
|
||||
inbound.setCreatedBy(0L);
|
||||
inbound.setUpdatedBy(0L);
|
||||
inbound.setCreatedAt(now);
|
||||
inbound.setUpdatedAt(now);
|
||||
inbound.setDeleted(false);
|
||||
inbound.setVersion(0);
|
||||
orders.insert(inbound);
|
||||
|
||||
InboundStatusHistoryEntity history = new InboundStatusHistoryEntity();
|
||||
history.setId(IdWorker.getId());
|
||||
history.setTenantId(command.tenantId());
|
||||
history.setInboundId(inbound.getId());
|
||||
history.setToStatus("WAIT_MEASURE");
|
||||
history.setAction("ORDER_APPROVED");
|
||||
history.setOperatorId(0L);
|
||||
history.setOperatorName("订单服务");
|
||||
history.setOccurredAt(now);
|
||||
histories.insert(history);
|
||||
return inbound.getId();
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -39,10 +39,12 @@ public class MeasurementLocationOccupationService {
|
||||
int measurementVersion) {
|
||||
InboundOrderEntity order = requireOrder(tenantId, inboundId);
|
||||
long warehouseId = requireWarehouse(order);
|
||||
long resourceTenantId = properties.getWarehouseResourceTenantId() > 0
|
||||
? properties.getWarehouseResourceTenantId() : tenantId;
|
||||
BigDecimal volume = requireVolume(order);
|
||||
String key = "measure-" + inboundId + "-" + measurementId + "-" + measurementVersion;
|
||||
WarehousePalletLocationClient.BatchResult reserved = requireSuccess(call(() -> warehouse.reserve(
|
||||
internalToken, new WarehousePalletLocationClient.ReserveRequest(tenantId, warehouseId,
|
||||
internalToken, new WarehousePalletLocationClient.ReserveRequest(resourceTenantId, warehouseId,
|
||||
SOURCE_SERVICE, inboundId, order.getInboundCode(), key, operatorId, volume,
|
||||
properties.getDefaultPalletCapacityCbm(), "STANDARD",
|
||||
LocalDateTime.now().plus(properties.getReservationTtl()), true))),
|
||||
@@ -51,7 +53,7 @@ public class MeasurementLocationOccupationService {
|
||||
properties.getDefaultPalletCapacityCbm(), reserved);
|
||||
try {
|
||||
WarehousePalletLocationClient.BatchResult occupied = requireSuccess(call(() -> warehouse.confirm(
|
||||
internalToken, new WarehousePalletLocationClient.ConfirmRequest(tenantId, warehouseId,
|
||||
internalToken, new WarehousePalletLocationClient.ConfirmRequest(resourceTenantId, warehouseId,
|
||||
SOURCE_SERVICE, inboundId, operatorId, volume,
|
||||
properties.getDefaultPalletCapacityCbm()))), "测量完成后库位占用失败");
|
||||
allocations.replace(tenantId, inboundId, warehouseId,
|
||||
@@ -59,7 +61,7 @@ public class MeasurementLocationOccupationService {
|
||||
} catch (RuntimeException failure) {
|
||||
try {
|
||||
warehouse.release(internalToken, new WarehousePalletLocationClient.ReleaseRequest(
|
||||
tenantId, SOURCE_SERVICE, inboundId, operatorId, "测量完成库位占用失败"));
|
||||
resourceTenantId, SOURCE_SERVICE, inboundId, operatorId, "测量完成库位占用失败"));
|
||||
} catch (RuntimeException releaseFailure) {
|
||||
failure.addSuppressed(releaseFailure);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user