From 1d95298e5dd629c6790b06f46d8839364415d4d3 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 20 Oct 2026 12:03:09 +0800 Subject: [PATCH] Keep absent BOL appointment numbers blank without weakening text validation --- .../print/service/DispatchBolPdfService.java | 12 +++++- .../DispatchBolPdfServiceGenerationTest.java | 40 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/print/src/main/java/com/baiyue/print/service/DispatchBolPdfService.java b/print/src/main/java/com/baiyue/print/service/DispatchBolPdfService.java index 213c1a826..badf3f26e 100644 --- a/print/src/main/java/com/baiyue/print/service/DispatchBolPdfService.java +++ b/print/src/main/java/com/baiyue/print/service/DispatchBolPdfService.java @@ -64,6 +64,7 @@ public class DispatchBolPdfService { return new DispatchBolPdfDtos.Request(DispatchBolPdfDtos.TEMPLATE, request.pageSpec(), fileName, List.copyOf(documents)); } + /** 核对页面容量并规范化打印字段,预约号缺失时保持空白。 */ private DispatchBolPdfDtos.Document normalizeDocument(DispatchBolPdfDtos.Document value, int index) { int maxRows = value != null && value.pageNumber() == 1 ? MAX_FIRST_PAGE_ROWS : MAX_CONTINUATION_PAGE_ROWS; if (value == null || value.cargoRows() == null || value.cargoRows().isEmpty() || value.cargoRows().size() > maxRows) { @@ -77,15 +78,22 @@ public class DispatchBolPdfService { text(row.warehouseNo()), text(row.fbaShipmentId()), text(row.poReferenceNo()), row.packageCount())); } return new DispatchBolPdfDtos.Document(text(value.shipFromName()), text(value.shipFromAddress()), text(value.carrierName()), - text(value.isaNumber()), text(value.appointmentTime()), text(value.trailerNumber()), text(value.sealNumber()), + optionalText(value.isaNumber()), text(value.appointmentTime()), text(value.trailerNumber()), text(value.sealNumber()), text(value.fbaWarehouseCode()), text(value.shipToName()), text(value.shipToAddress()), text(value.dispatchNo()), text(value.fbaShipmentIds()), text(value.poReferenceNos()), value.totalPackages(), value.pageNumber(), value.pageCount(), List.copyOf(rows)); } + /** 普通缺失字段沿用占位符,预约号通过可空文本单独处理。 */ private String text(String value) { + String normalized = optionalText(value); + return normalized.isBlank() ? "—" : normalized; + } + + /** 保留空白语义,同时复用长度、规范化和控制字符校验。 */ + private String optionalText(String value) { String normalized = line(value); if (normalized.codePointCount(0, normalized.length()) > 300 || normalized.codePoints().anyMatch(c -> Character.isISOControl(c))) invalid("document text is invalid"); - return normalized.isBlank() ? "—" : normalized; + return normalized; } private String line(String value) { return value == null ? "" : Normalizer.normalize(value, Normalizer.Form.NFC).strip(); } private void invalid(String message) { throw new BizException(ResultCode.VALIDATION_FAILED, message); } diff --git a/print/src/test/java/com/baiyue/print/service/DispatchBolPdfServiceGenerationTest.java b/print/src/test/java/com/baiyue/print/service/DispatchBolPdfServiceGenerationTest.java index 7e8aaf050..cc1f744bf 100644 --- a/print/src/test/java/com/baiyue/print/service/DispatchBolPdfServiceGenerationTest.java +++ b/print/src/test/java/com/baiyue/print/service/DispatchBolPdfServiceGenerationTest.java @@ -1,6 +1,7 @@ package com.baiyue.print.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.baiyue.print.config.PrintPdfProperties; import com.baiyue.print.dto.DispatchBolPdfDtos; @@ -73,6 +74,45 @@ class DispatchBolPdfServiceGenerationTest { "FBA19TEST", "PO-TEST", 44, pageNumber, pageCount, rows); } + /** 未预约仍能生成真实PDF,预约栏不打印占位符或null。 */ + @Test + void leavesMissingAppointmentNumbersBlankInTheRenderedPdf() throws Exception { + for (String appointment : new String[] { null, "", " " }) { + var generated = service.generate(appointmentRequest(appointment)); + try (PDDocument pdf = Loader.loadPDF(generated.content())) { + assertThat(pdf.getNumberOfPages()).isEqualTo(1); + assertThat(new PDFTextStripper().getText(pdf)).contains("ISA #:", "BILL OF LADING", "PALLET-01") + .doesNotContain("ISA #: —", "ISA #: null"); + } + } + } + + /** 空值放开不削弱预约文本的长度和控制字符边界。 */ + @Test + void keepsAppointmentTextValidationAndTrimsPresentNumbers() throws Exception { + assertThatThrownBy(() -> service.generate(appointmentRequest("ISA\u0000BAD"))) + .isInstanceOf(com.baiyue.common.core.exception.BizException.class); + assertThatThrownBy(() -> service.generate(appointmentRequest("A".repeat(301)))) + .isInstanceOf(com.baiyue.common.core.exception.BizException.class); + var generated = service.generate(appointmentRequest(" ISA-ONE ")); + try (PDDocument pdf = Loader.loadPDF(generated.content())) { + assertThat(new PDFTextStripper().getText(pdf)).contains("ISA #: ISA-ONE"); + } + } + + /** 只替换预约字段,其他页面参数使用既有真实渲染夹具。 */ + private DispatchBolPdfDtos.Request appointmentRequest(String appointment) { + var original = page(1, 1, rows(1, 1)); + var document = new DispatchBolPdfDtos.Document(original.shipFromName(), original.shipFromAddress(), + original.carrierName(), appointment, original.appointmentTime(), original.trailerNumber(), + original.sealNumber(), original.fbaWarehouseCode(), original.shipToName(), original.shipToAddress(), + original.dispatchNo(), original.fbaShipmentIds(), original.poReferenceNos(), original.totalPackages(), + original.pageNumber(), original.pageCount(), original.cargoRows()); + return new DispatchBolPdfDtos.Request(DispatchBolPdfDtos.TEMPLATE, + new DispatchBolPdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297")), + "appointment-bol.pdf", List.of(document)); + } + private List rows(int start, int end) { List rows = new ArrayList<>(); for (int index = start; index <= end; index++) {