From 010475a4c3145a762eecd15b653c3fd2377072ef Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 11 Sep 2026 16:21:14 +0800 Subject: [PATCH] fix: prepare mixed-SKU dropship acceptance data --- .../service/InboundCartonLabelService.java | 14 +++++++++-- .../InboundCartonLabelServiceTest.java | 25 +++++++++++++++++++ scripts/admin-test-env-modules/README.md | 2 +- scripts/admin-test-env-modules/orders.mjs | 1 - 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelService.java index 8713f0e8b..d26123817 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelService.java @@ -358,8 +358,18 @@ public class InboundCartonLabelService implements CartonLabelBindingResolver { Map> skuRowsByLabel) { List lines = skuRowsByLabel.get(label.getId()); if (lines == null || lines.isEmpty()) return label.getSkuCode(); - return lines.stream().map(InboundCartonLabelSkuMapper.SkuRow::getSkuCode) - .filter(StringUtils::hasText).distinct().collect(java.util.stream.Collectors.joining(" / ")); + List skuCodes = lines.stream().map(InboundCartonLabelSkuMapper.SkuRow::getSkuCode) + .filter(StringUtils::hasText).map(String::trim).distinct().toList(); + if (skuCodes.isEmpty()) return label.getSkuCode(); + String joined = String.join(" / ", skuCodes); + if (joined.codePointCount(0, joined.length()) <= 64) return joined; + String suffix = " / +" + Math.max(0, skuCodes.size() - 1) + " SKU"; + int firstLimit = 64 - suffix.codePointCount(0, suffix.length()); + String first = skuCodes.get(0); + if (first.codePointCount(0, first.length()) > firstLimit) { + first = first.substring(0, first.offsetByCodePoints(0, firstLimit)); + } + return first + suffix; } private void requireIds(long tenantId, long inboundId) { diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelServiceTest.java index e807067c1..505f3dfb2 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelServiceTest.java @@ -19,6 +19,7 @@ import org.springframework.test.util.ReflectionTestUtils; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -98,6 +99,23 @@ class InboundCartonLabelServiceTest { any(Long.class), any(String.class), any(), any(Integer.class), any(Integer.class)); } + @Test + void mixedCartonSkuSummaryStaysWithinPrintContractLimit() { + InboundCartonLabelEntity label = new InboundCartonLabelEntity(); + label.setId(90001L); + List rows = List.of( + skuRow(90001L, "SKU-SMALL-ADMIN-API-20260911081504632"), + skuRow(90001L, "SKU-MEDIUM-ADMIN-API-20260911081504632"), + skuRow(90001L, "SKU-LARGE-ADMIN-API-20260911081504632"), + skuRow(90001L, "SKU-EXTRA-LARGE-ADMIN-API-20260911081504632")); + + String summary = ReflectionTestUtils.invokeMethod(service, "displaySkuCodes", label, + Map.of(90001L, rows)); + + assertThat(summary.codePointCount(0, summary.length())).isLessThanOrEqualTo(64); + assertThat(summary).startsWith("SKU-SMALL-").endsWith(" / +3 SKU"); + } + @Test void repeatedAndPartialPrintsReadFrozenMappingWithoutReallocation() { InboundOrderEntity inbound = inbound(); @@ -312,6 +330,13 @@ class InboundCartonLabelServiceTest { units, new BigDecimal("60"), new BigDecimal("40"), new BigDecimal("30"), null, group); } + private InboundCartonLabelSkuMapper.SkuRow skuRow(long labelId, String skuCode) { + InboundCartonLabelSkuMapper.SkuRow row = new InboundCartonLabelSkuMapper.SkuRow(); + row.setCartonLabelId(labelId); + row.setSkuCode(skuCode); + return row; + } + private List labels(InboundOrderEntity inbound, int total) { List values = new ArrayList<>(); for (int index = 1; index <= total; index++) { diff --git a/scripts/admin-test-env-modules/README.md b/scripts/admin-test-env-modules/README.md index 526dd48b9..48f054289 100644 --- a/scripts/admin-test-env-modules/README.md +++ b/scripts/admin-test-env-modules/README.md @@ -39,7 +39,7 @@ export async function run(context) { 货柜查询使用 `/api/platform/v1/containers/domestic` 列表和 `/domestic/{id}` 详情,响应为 `WorkbenchItem`(`container`、`workbenchStage`、`statusSummary`、`allowedActions`),不接受旧扁平实体。默认 `phase3-container-loading` 同时验证页面的 ACTIVE、状态、关键字组合查询及详情结构;`container-detail-route-contract.test.mjs` 离线核对 Admin、Controller 和人工分段/同柜脚本读路径,并反证旧 DTO。定向真实 HTTP 使用总入口 `--modules phase3-container-loading`,仍自动展开本轮前置并创建测试资源;不借历史柜。 -一件代发的 `inbound-measurement` 前置在测量草稿录入预计托数;审核通过时系统一次性预生成全部 DP 托盘码,允许空托先打印。PDA 既支持先清点箱再扫预生成托盘码批量归托,也支持先扫托盘后逐箱直接归托;预计托数不得超过实测箱数。验收逐托封托并确认全部箱数已绑定、全部预生成托盘已封托后任务才完成,默认一件代发后续链复用该前置。 +一件代发的 `inbound-measurement` 前置在测量草稿录入预计托数;审核通过时系统一次性预生成全部 DP 托盘码,允许空托先打印。PDA 既支持先清点箱再扫预生成托盘码批量归托,也支持先扫托盘后逐箱直接归托;预计托数不得超过实测箱数。验收逐托封托并确认全部箱数已绑定、全部预生成托盘已封托后任务才完成,默认一件代发后续链复用该前置。人工验收造数可传 `--prepare-dropship-manual-count true`,脚本会停在任务未领取、箱未清点、托盘未绑定的状态。 该前置保存成功解析且清点的原始 `boxCode`,随装柜完成生产 fixture 传入默认 `phase4-overseas-pda`。默认矩阵同时创建 FBA 和 OVERSEAS 两种一件代发,必须先将全部原箱码经海外 `current/cartons/resolve` 精确解析到同一入仓码及冻结目的类型,再进入实收及库存验证;OVERSEAS 逐箱实收,A0404、错误箱码/归属或数量缺失均阻断,不扩展推算箱号或排除失败候选。现行 V2 托盘 SKU 分组必须携带 `sourceInboundDetailId`,离线合同同时锁定两个生产分支来源明细和上述真实 HTTP 调用链。 diff --git a/scripts/admin-test-env-modules/orders.mjs b/scripts/admin-test-env-modules/orders.mjs index 64bf7e032..296027a1d 100644 --- a/scripts/admin-test-env-modules/orders.mjs +++ b/scripts/admin-test-env-modules/orders.mjs @@ -444,7 +444,6 @@ function buildPayload(context, profile, master, productImageFileId, customerAddr }, receiver, cargoes: selectedBoxes.map((box, index) => { - const volumeCbm = box.lengthCm * box.widthCm * box.heightCm / 1_000_000 const unitsPerPackage = 10 + index const packageCount = dropship ? mixedPackageCount : Number(packageCounts[index] || packageCountPerSku) const carton = dropship ? selectedBoxes[0] : box