This commit is contained in:
2026-09-16 12:02:38 +08:00
parent de29e639e4
commit 912feca56f
11 changed files with 275 additions and 17 deletions
@@ -14,6 +14,15 @@ import java.util.Map;
@FeignClient(name = "print-service", contextId = "firstlegPrintPdfClient",
url = "${baiyue.clients.print.url:}", path = "/internal/print/v1/pdfs")
public interface PrintPdfClient {
@PostMapping(value = "/fba-pallet-labels", consumes = "application/json")
ResponseEntity<byte[]> generateFbaPalletLabels(
@RequestHeader("X-Internal-Service-Token") String token,
@RequestHeader(value = "X-Request-Id", required = false) String requestId,
@RequestBody FbaPalletRequest request);
record FbaPalletRequest(String templateCode, PageSpec pageSpec, String downloadFileName, List<FbaPalletLabel> labels) {}
record FbaPalletLabel(String fbaWarehouseCode, String palletCode) {}
@PostMapping(value = "/inbound-marks", consumes = "application/json")
ResponseEntity<byte[]> generateInboundMarks(
@RequestHeader("X-Internal-Service-Token") String token,
@@ -32,6 +32,8 @@ import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -636,11 +638,7 @@ public class InboundMeasurementService {
|| cargo.cargoId() <= 0 || !java.util.Objects.equals(cargo.orderId(), inbound.getOrderId()))) {
throw new BizException(ResultCode.CONFLICT, "订单服务返回的货物明细归属不一致");
}
long packages = cargoes.stream()
.map(OrderCargoClient.CargoLabelBinding::packageCount)
.filter(java.util.Objects::nonNull)
.mapToLong(Integer::longValue)
.sum();
long packages = physicalPackageCount(cargoes, inbound.getFulfillmentType());
if (packages > Integer.MAX_VALUE) {
throw new BizException(ResultCode.VALIDATION_FAILED, "客户最新申报总箱数超过系统上限");
}
@@ -671,6 +669,41 @@ public class InboundMeasurementService {
return new LatestDeclaration(snapshot, Map.copyOf(fbaShipmentNos));
}
/**
* 普通订单每条货物明细代表独立箱规;一件代发允许同一物理箱包含多个 SKU,
* 因此同一 packingGroupNo 只计算一次箱数。该口径与箱唛生成保持一致。
*/
private long physicalPackageCount(List<OrderCargoClient.CargoLabelBinding> cargoes,
String fulfillmentType) {
if (!"ONE_CLICK_DROPSHIP".equals(fulfillmentType)) {
return cargoes.stream().mapToLong(this::requireDeclaredPackageCount).sum();
}
Map<String, List<OrderCargoClient.CargoLabelBinding>> grouped = new LinkedHashMap<>();
int single = 0;
for (OrderCargoClient.CargoLabelBinding cargo : cargoes) {
requireDeclaredPackageCount(cargo);
String group = StringUtils.hasText(cargo.packingGroupNo())
? cargo.packingGroupNo().trim() : "__single_" + (++single);
grouped.computeIfAbsent(group, ignored -> new ArrayList<>()).add(cargo);
}
return grouped.entrySet().stream().mapToLong(entry -> {
List<OrderCargoClient.CargoLabelBinding> lines = entry.getValue();
int count = lines.get(0).packageCount();
if (lines.stream().anyMatch(line -> line.packageCount() != count)) {
throw new BizException(ResultCode.VALIDATION_FAILED,
"同一混箱组合的箱数必须一致:" + entry.getKey());
}
return count;
}).sum();
}
private int requireDeclaredPackageCount(OrderCargoClient.CargoLabelBinding cargo) {
if (cargo.packageCount() == null || cargo.packageCount() < 1) {
throw new BizException(ResultCode.VALIDATION_FAILED, "订单货物明细缺少有效箱数");
}
return cargo.packageCount();
}
private List<OrderCargoClient.CargoLabelBinding> loadDeclarationCargoes(InboundOrderEntity inbound) {
try {
ApiResponse<List<OrderCargoClient.CargoLabelBinding>> response = orderCargoClient.listForCartonLabels(
@@ -38,7 +38,6 @@ import java.util.Objects;
/** 散货托盘码按真实柜号、FBA 地址内部编号和仓内序号组成;一件代发继续使用装箱打托事实。 */
@Service
public class LoadingPalletLabelService {
private static final BigDecimal PAGE_MM = new BigDecimal("100");
@Resource private LoadingPalletLabelMapper mapper;
@Resource private PrintPdfClient printClient;
@Resource private WarehouseMasterDataClient warehouseClient;
@@ -85,13 +84,12 @@ public class LoadingPalletLabelService {
: manifest.groups().stream().filter(group -> group.destinationWarehouseCode().equalsIgnoreCase(warehouseCode))
.flatMap(group -> group.labels().stream()).limit(count).toList();
if (selected.isEmpty() || (count != null && selected.size() != count)) throw new BizException(ResultCode.BAD_REQUEST, "所选 FBA 仓或打印张数无效");
List<PrintPdfClient.Label> labels = selected.stream().map(label -> new PrintPdfClient.Label(
label.palletCode(), label.destinationWarehouseName(), "PALLET " + label.palletIndex(),
"CONTAINER " + label.containerNo(), label.palletCode(), null)).toList();
List<PrintPdfClient.FbaPalletLabel> labels = selected.stream().map(label -> new PrintPdfClient.FbaPalletLabel(
label.destinationWarehouseCode(), label.palletCode())).toList();
String suffix = warehouseCode == null ? "全部" : warehouseCode + "-" + count + "张";
var pdfRequest = new PrintPdfClient.InboundMarkPdfRequest("INBOUND_MARK_V1",
new PrintPdfClient.PageSpec(PAGE_MM, PAGE_MM), manifest.containerNo() + "-托盘码-" + suffix + ".pdf", labels);
try { return requirePdf(printClient.generateInboundMarks(internalToken, requestId, pdfRequest)); }
var pdfRequest = new PrintPdfClient.FbaPalletRequest("FBA_PALLET_A4_LANDSCAPE_V1",
new PrintPdfClient.PageSpec(new BigDecimal("297"), new BigDecimal("210")), manifest.containerNo() + "-托盘码-" + suffix + ".pdf", labels);
try { return requirePdf(printClient.generateFbaPalletLabels(internalToken, requestId, pdfRequest)); }
catch (FeignException | NoFallbackAvailableException exception) { throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "打印服务暂不可用,请稍后重试"); }
}