This commit is contained in:
2026-08-15 12:12:34 +08:00
parent 08187363a1
commit 3a9558bd1c
9 changed files with 159 additions and 11 deletions
@@ -9,6 +9,7 @@ import com.baiyue.firstleg.client.OrderCargoClient;
import com.baiyue.firstleg.entity.*;
import com.baiyue.firstleg.repository.SmartLoadingRepository;
import com.baiyue.firstleg.loading.bridge.service.LoadingModelBridgeService;
import com.baiyue.firstleg.split.service.SplitPreloadService;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -35,6 +36,7 @@ public class SmartLoadingService {
@Resource private OrderCargoClient orderCargoClient;
@Resource private LoadingPickService loadingPickService;
@Resource private LoadingModelBridgeService loadingModelBridge;
@Resource private SplitPreloadService splitPreloadService;
@Resource private ObjectMapper objectMapper;
@Value("${baiyue.security.internal-service-token:}") private String internalToken;
public SmartLoadingService() { }
@@ -104,6 +106,8 @@ public class SmartLoadingService {
if (candidate == null) throw conflict("入库货物不存在或尚未完成入库");
requireSameChannel(booking, candidate);
requireSameCustomerScope(booking, candidate);
splitPreloadService.ensurePendingForLoadingAllocation(candidate.accountId(), candidate.inboundId(),
operatorId, operatorName);
OrderCargoClient.CargoBinding cargo = loadCargo(candidate);
LoadingPlanItemEntity existing = repository.activeItem(planId, candidate.accountId(), request.inboundDetailId());
int total = request.packageCount() + (existing == null ? 0 : existing.getPackageCount());
@@ -235,6 +239,9 @@ public class SmartLoadingService {
throw validation("录入真实柜号后才能推送预装清单并开启PDA作业");
}
if (key.equals(plan.getPushIdempotencyKey())) {
if ("EDITING".equals(plan.getPlanStatus())) {
ensurePendingPreloads(repository.items(planId), operatorId, operatorName);
}
loadingModelBridge.bindAllocatedBeforeLoading(plan.getId(), operatorId, operatorName, key);
loadingPickService.beginIfReady(plan.getContainerBookingId(), operatorId, operatorName);
return view(plan, booking);
@@ -242,7 +249,7 @@ public class SmartLoadingService {
if (StringUtils.hasText(plan.getPushIdempotencyKey())) throw conflict("预装清单已推送,不能使用新的幂等键重复推送");
if (!"EDITING".equals(plan.getPlanStatus()) || !Objects.equals(plan.getVersion(), request.expectedVersion())) throw conflict("仅编辑中的计划可开始装柜且版本必须匹配");
List<LoadingPlanItemEntity> items = repository.items(planId);
if (items.isEmpty()) throw validation("装柜明细不能为空");
ensurePendingPreloads(items, operatorId, operatorName);
checkPlanCapacity(booking, items);
try {
if (repository.push(planId, key, request.expectedVersion(), operatorId, operatorName) != 1) throw conflict("装柜计划已被并发修改");
@@ -254,6 +261,17 @@ public class SmartLoadingService {
return view(pushed, requireBooking(pushed.getContainerBookingId()));
}
private void ensurePendingPreloads(List<LoadingPlanItemEntity> items, long operatorId, String operatorName) {
if (items.isEmpty()) throw validation("装柜明细不能为空");
if (items.stream().anyMatch(item -> item.getAccountId() == null || item.getInboundId() == null)) {
throw conflict("装柜明细缺少客户或入库单稳定归属,不能推送预装计划");
}
items.stream().collect(java.util.stream.Collectors.toMap(
item -> item.getAccountId() + "|" + item.getInboundId(), item -> item, (left, right) -> left))
.values().forEach(item -> splitPreloadService.ensurePendingForLoadingAllocation(
item.getAccountId(), item.getInboundId(), operatorId, operatorName));
}
private LoadingPlanItemEntity snapshot(long planId, Candidate c, OrderCargoClient.CargoBinding cargo, int count, long op, String name, int seq) {
LoadingPlanItemEntity i = new LoadingPlanItemEntity(); i.setLoadingPlanId(planId); i.setAccountId(c.accountId()); i.setCustomerName(c.customerName());
i.setInboundId(c.inboundId()); i.setInboundDetailId(c.inboundDetailId()); i.setOrderId(c.orderId()); i.setOrderNo(c.orderNo()); i.setInboundCode(c.inboundCode());
@@ -22,6 +22,7 @@ import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import java.time.LocalDateTime;
import java.util.List;
@@ -72,6 +73,34 @@ public class SplitPreloadService {
new SplitActionResponse("WAIT_SPLIT", result));
}
/** Ensures the inbound-level preload lifecycle exists before a loading allocation can be pushed. */
@Transactional
public void ensurePendingForLoadingAllocation(long tenantId, long inboundId, long operatorId,
String operatorName) {
InboundSnapshot inbound = requireInbound(tenantId, inboundId);
requireWaitingSplit(inbound, "仅待分柜订单可以建立智能装柜预装计划");
PreloadPlanEntity plan = repository.findPlan(tenantId, inboundId);
if (plan != null) {
if (!PreloadStatus.PENDING.equals(plan.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "预装计划已进入确认流程,不能修改智能装柜分配");
}
return;
}
plan = new PreloadPlanEntity();
plan.setTenantId(tenantId); plan.setInboundId(inboundId); plan.setStatus(PreloadStatus.PENDING);
plan.setVersion(0); plan.setItemsJson("[]"); plan.setRemark("智能装柜分配自动建立");
plan.setCreatedBy(operatorId); plan.setCreatedByName(operatorName);
plan.setUpdatedBy(operatorId); plan.setUpdatedByName(operatorName);
try {
repository.createPlan(plan);
} catch (DuplicateKeyException exception) {
PreloadPlanEntity concurrent = repository.findPlan(tenantId, inboundId);
if (concurrent == null || !PreloadStatus.PENDING.equals(concurrent.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "预装计划已被并发修改,请刷新后重试");
}
}
}
@Transactional
public SplitActionResponse startConfirming(long tenantId, long inboundId, long operatorId,
String operatorName, String key,