From 3910b8fccecdb6767903d814dbd31b4f77d3fa50 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 14 Sep 2026 17:52:01 +0800 Subject: [PATCH] feat: finalize dropship pallet print documents --- .../firstleg/client/PrintPdfClient.java | 1 - ...atformDropshipPalletizationController.java | 11 +++++++++++ .../mapper/DropshipPalletizationMapper.java | 10 ---------- .../InboundCartonLabelPrintService.java | 6 +----- .../openapi/dropship-palletization-v1.yaml | 9 ++++++++- .../DropshipPalletizationSqlContractTest.java | 4 +++- .../InboundCartonLabelPrintServiceTest.java | 12 ++++-------- print/README.md | 2 +- .../pdf/dropship-pallet-a4-v2-preview.pdf | Bin 12564 -> 12253 bytes .../print/dto/DropshipPalletA4PdfDtos.java | 1 - .../renderer/DropshipPalletA4Renderer.java | 9 --------- .../service/DropshipPalletA4PdfService.java | 6 ++---- .../resources/openapi/print-internal-v1.yaml | 4 +--- .../controller/InternalPdfControllerTest.java | 2 -- ...pshipPalletA4PdfServiceGenerationTest.java | 7 +++---- 15 files changed, 34 insertions(+), 50 deletions(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java b/firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java index 8eaf88622..4fbe1ea09 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java @@ -55,7 +55,6 @@ public interface PrintPdfClient { String downloadFileName, List documents) {} record DropshipPalletDocument(String palletNo, String destinationWarehouse, - int palletSequence, int totalPallets, String barcodeContent, List inboundRows, List skuRows) {} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformDropshipPalletizationController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformDropshipPalletizationController.java index a9569169a..52b93c7b7 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformDropshipPalletizationController.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformDropshipPalletizationController.java @@ -150,6 +150,17 @@ public class PlatformDropshipPalletizationController { return ApiResponse.success(service.inboundPallets(accountId, inboundId)); } + @GetMapping("/inbound-orders/{inboundId}/pallets/print") + @PreAuthorize("hasAuthority('platform:firstleg:inbound:list')") + public ResponseEntity printInboundPallets(@AuthenticationPrincipal AuthenticatedUser user, + @PathVariable @Positive long inboundId, + @RequestParam @Positive long accountId, + @RequestHeader(value = "X-Request-Id", required = false) String requestId) { + FirstlegScopeGuard.platform(user, accountId); + return printService.generateDropshipPallets( + service.inboundPallets(accountId, inboundId), service.inboundCode(accountId, inboundId), requestId); + } + @GetMapping("/inbound-orders/{inboundId}/pallets/{palletId}/print") @PreAuthorize("hasAuthority('platform:firstleg:inbound:list')") public ResponseEntity printPallet(@AuthenticationPrincipal AuthenticatedUser user, diff --git a/firstleg/src/main/java/com/baiyue/firstleg/mapper/DropshipPalletizationMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/mapper/DropshipPalletizationMapper.java index 6bcaff395..3376c9a59 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/mapper/DropshipPalletizationMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/mapper/DropshipPalletizationMapper.java @@ -254,15 +254,6 @@ public interface DropshipPalletizationMapper { """) List printSkuRows(@Param("palletId") long palletId); - @Select(""" - SELECT (SELECT COUNT(*) FROM fl_dropship_pallet listed - WHERE listed.task_id=target.task_id AND listed.id<=target.id) sequence_no, - (SELECT COUNT(*) FROM fl_dropship_pallet listed - WHERE listed.task_id=target.task_id) total - FROM fl_dropship_pallet target - WHERE target.id=#{palletId} - """) - PalletPosition palletPosition(@Param("palletId") long palletId); @Select(""" SELECT p.id pallet_id,p.pallet_no,'SEALED_PALLET' unit_type,TRUE indivisible,COUNT(DISTINCT c.id) carton_count, (SELECT COUNT(DISTINCT sku.sku_code) FROM fl_dropship_pallet_carton pc @@ -306,6 +297,5 @@ public interface DropshipPalletizationMapper { record PalletMetrics(BigDecimal weightKg,BigDecimal volumeCbm) {} @Getter @Setter class PrintInboundRow { private Long tenantId,inboundId; private String inboundCode,destinationWarehouse; private Integer cartonCount,itemQuantity; } @Getter @Setter class PrintSkuRow { private String skuCode; private Integer cartonCount,itemQuantity,minUnitsPerCarton,maxUnitsPerCarton; } - record PalletPosition(int sequenceNo,int total) {} @Getter @Setter class PalletLine { private Long palletId,tenantId,accountId,inboundId,orderId,warehouseId,destinationWarehouseId,inboundDetailId; private String palletNo,palletStatus,orderNo,inboundCode,channelCode,channelName,destinationWarehouseCode,destinationWarehouseName,customerName,skuCode,cargoName; private Integer cartonCount,actualSkuQuantity; private BigDecimal unitGrossWeightKg,unitNetWeightKg,unitVolumeCbm; } } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelPrintService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelPrintService.java index b959c9aac..3c20b1fd9 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelPrintService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/InboundCartonLabelPrintService.java @@ -172,12 +172,8 @@ public class InboundCartonLabelPrintService { .stream().map(row -> new PrintPdfClient.DropshipPalletSkuRow( row.getSkuCode(), number(row.getCartonCount()), number(row.getItemQuantity()), unitsPerCarton(row))).toList(); - DropshipPalletizationMapper.PalletPosition position = - palletizationMapper.palletPosition(pallet.palletId()); return new PrintPdfClient.DropshipPalletDocument( - pallet.palletNo(), destinationWarehouse, - position == null ? 1 : position.sequenceNo(), position == null ? 1 : position.total(), - pallet.palletNo(), inboundRows, skuRows); + pallet.palletNo(), destinationWarehouse, pallet.palletNo(), inboundRows, skuRows); } private String unitsPerCarton(DropshipPalletizationMapper.PrintSkuRow row) { diff --git a/firstleg/src/main/resources/openapi/dropship-palletization-v1.yaml b/firstleg/src/main/resources/openapi/dropship-palletization-v1.yaml index d2011e866..4d9956aa4 100644 --- a/firstleg/src/main/resources/openapi/dropship-palletization-v1.yaml +++ b/firstleg/src/main/resources/openapi/dropship-palletization-v1.yaml @@ -68,7 +68,7 @@ paths: /api/platform/v1/first-leg/pda-dropship/tasks/{taskId}/pallets/{palletId}/print: get: summary: PDA 获取预生成托盘码的 A4 托盘信息单 - description: 测量审核通过并生成托盘码后即可获取;每个托盘固定一页 A4,包含托盘号及其 Code 128 一维码、目的仓、托盘序号、入仓码/客户昵称/箱数/件数汇总以及 SKU/箱数/件数/每箱件数汇总,不展示作业状态、重量或体积。空托显示暂无已归托 SKU。仅当前仓库、当前操作员可访问。 + description: 测量审核通过并生成托盘码后即可获取;每个托盘固定一页 A4,包含托盘号及其 Code 128 一维码、目的仓、入仓码/客户昵称/箱数/件数汇总以及 SKU/箱数/件数/每箱件数汇总,不展示托盘序号、作业状态、重量或体积。空托显示暂无已归托 SKU。仅当前仓库、当前操作员可访问。 x-required-authority: platform:firstleg:pda:view parameters: - {$ref: '#/components/parameters/TaskId'} @@ -89,6 +89,13 @@ paths: summary: Admin 查询入仓单权威托盘结果 x-required-authority: platform:firstleg:inbound:list responses: {'200': {description: OK}} + /api/platform/v1/first-leg/pda-dropship/inbound-orders/{inboundId}/pallets/print: + get: + summary: Admin 合并打印入仓单关联的全部一件代发托盘 + description: 测量审核预生成托盘码后即可打印;每托一页,空托也保留托盘码和当前入仓单信息,供电脑连接打印机后在 PDA 作业前贴码。 + x-required-authority: platform:firstleg:inbound:list + responses: + '200': {description: 多页 A4 托盘信息单 PDF, content: {application/pdf: {schema: {type: string, format: binary}}}} /api/platform/v1/first-leg/pda-dropship/inbound-orders/{inboundId}/pallets/{palletId}/print: get: summary: 获取包含托盘码、目的仓、入仓码和 SKU 汇总的 A4 托盘信息单 diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/DropshipPalletizationSqlContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/DropshipPalletizationSqlContractTest.java index fe1bf0f99..8c4365b28 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/repository/DropshipPalletizationSqlContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/DropshipPalletizationSqlContractTest.java @@ -16,7 +16,8 @@ class DropshipPalletizationSqlContractTest { void inboundPalletReadAndPrintUseTheRegisteredPlatformInboundPermission() throws Exception { var controller = com.baiyue.firstleg.controller.PlatformDropshipPalletizationController.class; for (var method : controller.getDeclaredMethods()) { - if (method.getName().equals("inboundPallets") || method.getName().equals("printPallet")) { + if (method.getName().equals("inboundPallets") || method.getName().equals("printPallet") + || method.getName().equals("printInboundPallets")) { assertThat(method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class).value()) .isEqualTo("hasAuthority('platform:firstleg:inbound:list')"); } @@ -24,6 +25,7 @@ class DropshipPalletizationSqlContractTest { String api = new ClassPathResource("openapi/dropship-palletization-v1.yaml") .getContentAsString(StandardCharsets.UTF_8); assertThat(api).contains("x-required-authority: platform:firstleg:inbound:list") + .contains("/api/platform/v1/first-leg/pda-dropship/inbound-orders/{inboundId}/pallets/print:") .doesNotContain("x-required-authority: firstleg:inbound:list"); } diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelPrintServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelPrintServiceTest.java index 2d6c609de..43dfe4d4b 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelPrintServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/InboundCartonLabelPrintServiceTest.java @@ -45,7 +45,7 @@ class InboundCartonLabelPrintServiceTest { @Test void dropshipPalletLabelIsAvailableBeforeSeal() { Pallet open = dropshipPallet("OPEN"); - stubPalletFacts(90001L, 1, 2); + stubPalletFacts(90001L); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_PDF); when(print.generateDropshipPalletA4Documents(org.mockito.ArgumentMatchers.anyString(), @@ -66,8 +66,6 @@ class InboundCartonLabelPrintServiceTest { assertThat(request.getValue().documents()).singleElement().satisfies(document -> { assertThat(document.palletNo()).isEqualTo("DP-260911-0001"); assertThat(document.destinationWarehouse()).isEqualTo("NJDF"); - assertThat(document.palletSequence()).isEqualTo(1); - assertThat(document.totalPallets()).isEqualTo(2); assertThat(document.inboundRows()).singleElement().satisfies(row -> { assertThat(row.inboundCode()).isEqualTo("PCHK2608040174"); assertThat(row.customerNickname()).isEqualTo("小虫科技"); @@ -88,8 +86,8 @@ class InboundCartonLabelPrintServiceTest { Pallet first = dropshipPallet("OPEN"); Pallet second = new Pallet(90002L, "DP-260911-0002", "OPEN", 0, 0, 0, BigDecimal.ZERO, BigDecimal.ZERO, 1, List.of(), List.of(), null); - stubPalletFacts(90001L, 1, 2); - stubPalletFacts(90002L, 2, 2); + stubPalletFacts(90001L); + stubPalletFacts(90002L); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_PDF); when(print.generateDropshipPalletA4Documents(org.mockito.ArgumentMatchers.anyString(), @@ -242,7 +240,7 @@ class InboundCartonLabelPrintServiceTest { BigDecimal.ONE, BigDecimal.ONE, 1, List.of(item), List.of(), null); } - private void stubPalletFacts(long palletId, int sequence, int total) { + private void stubPalletFacts(long palletId) { DropshipPalletizationMapper.PrintInboundRow inbound = new DropshipPalletizationMapper.PrintInboundRow(); inbound.setTenantId(10001L); @@ -260,8 +258,6 @@ class InboundCartonLabelPrintServiceTest { sku.setMaxUnitsPerCarton(19); when(palletization.printSkuRows(palletId)) .thenReturn(palletId == 90001L ? List.of(sku) : List.of()); - when(palletization.palletPosition(palletId)) - .thenReturn(new DropshipPalletizationMapper.PalletPosition(sequence, total)); when(customerSnapshots.customerNickname(10001L)).thenReturn("小虫科技"); } } diff --git a/print/README.md b/print/README.md index bc2e22e8e..031bb4aaf 100644 --- a/print/README.md +++ b/print/README.md @@ -8,7 +8,7 @@ - 服务发现名:`print-service`。 - 内部地址:`POST /internal/print/v1/pdfs/inbound-marks`。 -- 一件代发 A4 托盘信息单:`POST /internal/print/v1/pdfs/dropship-pallet-a4-documents`;模板固定为 `DROPSHIP_PALLET_A4_V2`,采用黑白打印版,每托一页 A4,包含托盘 Code 128 一维码、目的仓、托盘序号、最多 10 行入仓码/客户昵称汇总和最多 34 行 SKU 汇总,不展示作业状态、重量或体积。 +- 一件代发 A4 托盘信息单:`POST /internal/print/v1/pdfs/dropship-pallet-a4-documents`;模板固定为 `DROPSHIP_PALLET_A4_V2`,采用黑白打印版,每托一页 A4,包含托盘 Code 128 一维码、目的仓、最多 10 行入仓码/客户昵称汇总和最多 34 行 SKU 汇总,不展示托盘序号、作业状态、重量或体积。 - A4 海运单据内部地址:`POST /internal/print/v1/pdfs/freight-a4-documents`。 - 海外仓排车 BOL 内部地址:`POST /internal/print/v1/pdfs/dispatch-bol-documents`;模板固定为 `DISPATCH_BILL_OF_LADING_A4_V2`,每个 document 对应一张 A4、包含 1-26 行明细。调用方首张按 18 行、续页按 26 行稳定分页;同页连续相同柜号和托盘号由 Print 纵向合并显示。 - 本地直连:`http://127.0.0.1:9207`。 diff --git a/print/output/pdf/dropship-pallet-a4-v2-preview.pdf b/print/output/pdf/dropship-pallet-a4-v2-preview.pdf index 6cc08e92cc2f842ede228e90e4051033cb5ab078..aa2a399dcc9d9cedfe7b5f367d14136d972888c3 100644 GIT binary patch delta 11120 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 inboundRows, List skuRows) {} diff --git a/print/src/main/java/com/baiyue/print/renderer/DropshipPalletA4Renderer.java b/print/src/main/java/com/baiyue/print/renderer/DropshipPalletA4Renderer.java index 1ff6f3f64..c12e0037e 100644 --- a/print/src/main/java/com/baiyue/print/renderer/DropshipPalletA4Renderer.java +++ b/print/src/main/java/com/baiyue/print/renderer/DropshipPalletA4Renderer.java @@ -35,9 +35,6 @@ public class DropshipPalletA4Renderer { stream.setLineWidth(PdfUnits.mmToPt(.25)); border(stream, units); colorText(stream, units, font, "一件代发备货托", 14, 18, 22, INK); - rightText(stream, units, font, - "第 " + data.palletSequence() + " 托 / 共 " + data.totalPallets() + " 托", - 10, 192, 22, new Color(75, 75, 75)); barcodeRenderer.draw(stream, barcodeMatrix, PdfUnits.mmToPt(18), units.topDownMmToPdfY(51), PdfUnits.mmToPt(80), PdfUnits.mmToPt(22)); @@ -157,12 +154,6 @@ public class DropshipPalletA4Renderer { colorText(s, u, f, fit(f, value, size, PdfUnits.mmToPt(widthMm)), size, x, y, color); } - private void rightText(PDPageContentStream s, PdfUnits u, PDFont f, String value, float size, - double right, double y, Color color) throws IOException { - double widthMm = f.getStringWidth(value) / 1000 * size / PdfUnits.mmToPt(1); - colorText(s, u, f, value, size, right - widthMm, y, color); - } - private void centeredCell(PDPageContentStream s, PdfUnits u, PDFont f, String value, float size, double x1, double x2, double y) throws IOException { String fitted = fit(f, value, size, PdfUnits.mmToPt(x2 - x1)); diff --git a/print/src/main/java/com/baiyue/print/service/DropshipPalletA4PdfService.java b/print/src/main/java/com/baiyue/print/service/DropshipPalletA4PdfService.java index 139baa94b..ca04d1565 100644 --- a/print/src/main/java/com/baiyue/print/service/DropshipPalletA4PdfService.java +++ b/print/src/main/java/com/baiyue/print/service/DropshipPalletA4PdfService.java @@ -68,8 +68,6 @@ public class DropshipPalletA4PdfService { private DropshipPalletA4PdfDtos.Document normalizeDocument(DropshipPalletA4PdfDtos.Document value, int index) { if (value == null) invalid("documents[" + index + "] is required"); - if (value.palletSequence() <= 0 || value.totalPallets() <= 0 - || value.palletSequence() > value.totalPallets()) invalid("documents[" + index + "] pallet position is invalid"); if (value.inboundRows() == null || value.inboundRows().isEmpty() || value.inboundRows().size() > MAX_INBOUND_ROWS) invalid("documents[" + index + "].inboundRows exceeds the one-page A4 capacity"); if (value.skuRows() == null || value.skuRows().size() > MAX_SKU_ROWS) invalid("documents[" + index + "].skuRows exceeds the one-page A4 capacity"); @@ -86,8 +84,8 @@ public class DropshipPalletA4PdfService { row.itemQuantity(), text(row.unitsPerCarton(), 30))); } return new DropshipPalletA4PdfDtos.Document(text(value.palletNo(), 80), - text(value.destinationWarehouse(), 200), value.palletSequence(), value.totalPallets(), - barcode(value.barcodeContent()), List.copyOf(inboundRows), List.copyOf(rows)); + text(value.destinationWarehouse(), 200), barcode(value.barcodeContent()), + List.copyOf(inboundRows), List.copyOf(rows)); } private String barcode(String value) { diff --git a/print/src/main/resources/openapi/print-internal-v1.yaml b/print/src/main/resources/openapi/print-internal-v1.yaml index c9c2adcb6..a0c53fbbc 100644 --- a/print/src/main/resources/openapi/print-internal-v1.yaml +++ b/print/src/main/resources/openapi/print-internal-v1.yaml @@ -290,12 +290,10 @@ components: items: { $ref: '#/components/schemas/DropshipPalletA4Document' } DropshipPalletA4Document: type: object - required: [palletNo, destinationWarehouse, palletSequence, totalPallets, barcodeContent, inboundRows, skuRows] + required: [palletNo, destinationWarehouse, barcodeContent, inboundRows, skuRows] properties: palletNo: { type: string, maxLength: 80 } destinationWarehouse: { type: string, maxLength: 200 } - palletSequence: { type: integer, minimum: 1 } - totalPallets: { type: integer, minimum: 1 } barcodeContent: type: string maxLength: 80 diff --git a/print/src/test/java/com/baiyue/print/controller/InternalPdfControllerTest.java b/print/src/test/java/com/baiyue/print/controller/InternalPdfControllerTest.java index a0e7e4e40..ec08c6df6 100644 --- a/print/src/test/java/com/baiyue/print/controller/InternalPdfControllerTest.java +++ b/print/src/test/java/com/baiyue/print/controller/InternalPdfControllerTest.java @@ -274,8 +274,6 @@ class InternalPdfControllerTest { "documents": [{ "palletNo": "DP-260911-0034", "destinationWarehouse": "NJDF", - "palletSequence": 1, - "totalPallets": 3, "barcodeContent": "DP-260911-0034", "inboundRows": [{ "inboundCode": "NYEXX2609110249", diff --git a/print/src/test/java/com/baiyue/print/service/DropshipPalletA4PdfServiceGenerationTest.java b/print/src/test/java/com/baiyue/print/service/DropshipPalletA4PdfServiceGenerationTest.java index cf2a311f8..9388c1bb6 100644 --- a/print/src/test/java/com/baiyue/print/service/DropshipPalletA4PdfServiceGenerationTest.java +++ b/print/src/test/java/com/baiyue/print/service/DropshipPalletA4PdfServiceGenerationTest.java @@ -27,7 +27,7 @@ class DropshipPalletA4PdfServiceGenerationTest { @Test void generatesOneA4PageWithPalletBarcodeAndSkuDetails() throws Exception { DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document( - "DP-260911-0034", "NJDF", 1, 3, "DP-260911-0034", + "DP-260911-0034", "NJDF", "DP-260911-0034", List.of(new DropshipPalletA4PdfDtos.InboundRow( "NYEXX2609110249", "小虫科技", 3, 90)), List.of( @@ -64,8 +64,7 @@ class DropshipPalletA4PdfServiceGenerationTest { "SKU-" + String.format("%03d", index), 2, index * 5, "5-10")); } DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document( - "DP-260911-0034", "NJDF", 1, 1, - "DP-260911-0034", inboundRows, rows); + "DP-260911-0034", "NJDF", "DP-260911-0034", inboundRows, rows); var generated = service.generate(new DropshipPalletA4PdfDtos.Request( DropshipPalletA4PdfDtos.TEMPLATE, new DropshipPalletA4PdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297")), @@ -85,7 +84,7 @@ class DropshipPalletA4PdfServiceGenerationTest { rows.add(new DropshipPalletA4PdfDtos.SkuRow("SKU-" + index, 1, 1, "1")); } DropshipPalletA4PdfDtos.Document document = new DropshipPalletA4PdfDtos.Document( - "DP-260911-0034", "NJDF", 1, 1, "DP-260911-0034", + "DP-260911-0034", "NJDF", "DP-260911-0034", List.of(new DropshipPalletA4PdfDtos.InboundRow("INBOUND", "客户", 35, 35)), rows); assertThatThrownBy(() -> service.generate(new DropshipPalletA4PdfDtos.Request(