From 2c9c0db974a17fea4abcbba5b5a10fc22b917875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E9=B9=8F=E9=A3=9E?= Date: Mon, 15 Dec 2025 11:50:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(export):=20=E6=B7=BB=E5=8A=A0=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=A0=87=E7=AD=BE=E5=9B=BE=E7=89=87ZIP=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E6=A0=87=E7=AD=BE=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在库存物料条码打印接口中新增导出物料标签图片ZIP接口,支持批量生成PNG图片压缩包 - 新增HtmlToImageUtil工具类,使用wkhtmltoimage将HTML转换为PNG图片 - 在储位管理中新增导出标签ZIP接口,实现储位二维码批量导出功能 - 普通物料订单及生产订单相关控制器新增导出物料标签图片ZIP接口,支持订单物料标签图片批量下载 - 生产订单副产品管理新增导出物料标签图片ZIP接口,支持副产品标签导出为ZIP - 优化二维码标签HTML模板(dp-1.html、qitao.html、spitem.html),调整尺寸和样式以适配打印需求 - 公共DTO类DeliverNormalOrderItemDTO添加打印名称字段printLabel,标签中显示以改善展示信息 - 相关接口返回Content-Type设为application/zip,支持ZIP文件响应UI下载体验 --- .../controller/BarcodePrintingController.java | 89 +++++++++++++- .../wms/admin/controller/BinController.java | 10 ++ .../controller/InProduceOrderController.java | 71 ++++++++++- .../InProduceOrderSurplusController.java | 68 ++++++++++- .../controller/NormalOrderController.java | 58 ++++++++- .../StructuralPackageOrderController.java | 113 ++++++++++++++++-- .../admin/service/BinControllerService.java | 49 ++++++-- .../nflg/wms/admin/util/HtmlToImageUtil.java | 68 +++++++++++ .../main/resources/template/qrcode/dp-1.html | 40 ++++--- .../main/resources/template/qrcode/qitao.html | 40 +++++-- .../resources/template/qrcode/spitem.html | 107 ++++++++++++----- .../main/resources/template/qrcode/tray.html | 32 +++-- .../resources/template/储位二维码.html | 32 ++--- .../pojo/dto/DeliverNormalOrderItemDTO.java | 5 + ...liverStructuralPackageOrderTrayItemVO.java | 5 + 15 files changed, 667 insertions(+), 120 deletions(-) create mode 100644 nflg-wms-admin/src/main/java/com/nflg/wms/admin/util/HtmlToImageUtil.java diff --git a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/BarcodePrintingController.java b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/BarcodePrintingController.java index 515fc69e..d713d003 100644 --- a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/BarcodePrintingController.java +++ b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/BarcodePrintingController.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; +import com.nflg.wms.admin.util.HtmlToImageUtil; import com.nflg.wms.admin.util.PdfGeneratorUtil; import com.nflg.wms.admin.util.QRCodeUtil; import com.nflg.wms.admin.util.ThymeleafUtil; @@ -34,7 +35,9 @@ import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -52,6 +55,8 @@ import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * 库存物料条码打印 @@ -202,7 +207,7 @@ public class BarcodePrintingController extends BaseController { @PostMapping("exportMaterialsPdf") public void exportMaterials(HttpServletResponse response, @Valid @RequestBody @NotNull BarcodePrintingIdsQO request) throws Exception { List codelist = printingService.listByIds(request.getIds()); - VUtil.trueThrowBusinessError(Objects.isNull(codelist)).throwMessage("订单不存在"); + VUtil.trueThrowBusinessError(Objects.isNull(codelist)).throwMessage("物料不存在"); List datas = new ArrayList<>(); // Integer i = 1; for (WmsInventoryBarcodePrinting item : codelist) { @@ -223,7 +228,7 @@ public class BarcodePrintingController extends BaseController { dto.setRowNo(""); dto.setPrintNo(IdUtil.getSnowflakeNextIdStr()); dto.setPrintNum("1"); - String qCode = QRCodeUtil.generateQRCodeBase64(generateQRContent(dto, serialNumber), 100, 100); + String qCode = QRCodeUtil.generateQRCodeBase64(generateQRContent(dto, serialNumber), 100, 100); dto.setQrCode(qCode); datas.add(dto); //i += 1; @@ -254,13 +259,89 @@ public class BarcodePrintingController extends BaseController { Map variables = new HashMap<>(); variables.put("list", datas); if (Objects.equals(request.getType(), 1)) { - String html = ThymeleafUtil.generator("/template/qrcode/", "Inv-1", ".html", variables); + String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1", ".html", variables); URL baseUrl = new ClassPathResource("template/qrcode/").getURL(); PdfGeneratorUtil.generatePdf("库存物料条码(逐个)", html, baseUrl.toString(), response); } else { - String html = ThymeleafUtil.generator("/template/qrcode/", "Inv-2", ".html", variables); + String html = ThymeleafUtil.generator("/template/qrcode/", "dp-2", ".html", variables); URL baseUrl = new ClassPathResource("template/qrcode/").getURL(); PdfGeneratorUtil.generatePdf("库存物料条码(整张)", html, baseUrl.toString(), response); } } + + /** + * 导出物料标签图片ZIP + * @param ids id列表 + */ + @PostMapping(value = "exportItemImageZip", produces = "application/zip") + public ResponseEntity exportItemImageZip(@RequestBody @NotEmpty List ids) throws Exception { + List codelist = printingService.listByIds(ids); + VUtil.trueThrowBusinessError(Objects.isNull(codelist)).throwMessage("物料不存在"); + List datas = new ArrayList<>(); + // Integer i = 1; + for (WmsInventoryBarcodePrinting item : codelist) { + if (StrUtil.isNotBlank(item.getSerialNumbers())) { + List serialNumbers = StrUtil.split(item.getSerialNumbers(), ","); + for (String serialNumber : serialNumbers) { + DeliverNormalOrderItemDTO dto = new DeliverNormalOrderItemDTO(); + dto.setMaterialNo(item.getMaterialNo()); + dto.setMaterialDesc(item.getMaterialDes()); + dto.setBatchNo(StrUtil.isBlank(item.getBatchNumber()) ? "" : item.getBatchNumber()); + dto.setExternalOrderNo(""); + dto.setRowNo(""); + dto = new DeliverNormalOrderItemDTO(); + dto.setMaterialNo(item.getMaterialNo()); + dto.setMaterialDesc(item.getMaterialDes()); + dto.setBatchNo(StrUtil.isBlank(item.getBatchNumber()) ? "" : item.getBatchNumber()); + dto.setExternalOrderNo(""); + dto.setRowNo(""); + dto.setPrintNo(IdUtil.getSnowflakeNextIdStr()); + dto.setPrintNum("1"); + String qCode = QRCodeUtil.generateQRCodeBase64(generateQRContent(dto, serialNumber), 100, 100); + dto.setQrCode(qCode); + datas.add(dto); + //i += 1; + } + } else { + int codeNum = item.getQty().divide(item.getPackingNum(), 0, RoundingMode.UP).intValue(); + for (int j = 0; j < codeNum; j++) { + DeliverNormalOrderItemDTO dto = new DeliverNormalOrderItemDTO(); + dto.setMaterialNo(item.getMaterialNo()); + dto.setMaterialDesc(item.getMaterialDes()); + dto.setBatchNo(StrUtil.isBlank(item.getBatchNumber()) ? "" : item.getBatchNumber()); + dto.setExternalOrderNo(""); + dto.setRowNo(""); + dto = new DeliverNormalOrderItemDTO(); + dto.setMaterialNo(item.getMaterialNo()); + dto.setMaterialDesc(item.getMaterialDes()); + dto.setBatchNo(StrUtil.isBlank(item.getBatchNumber()) ? "" : item.getBatchNumber()); + dto.setExternalOrderNo(""); + dto.setRowNo(""); + dto.setPrintNo(IdUtil.getSnowflakeNextIdStr()); + dto.setPrintNum(item.getPackingNum().toString()); + String qCode = QRCodeUtil.generateQRCodeBase64(generateQRContent(dto, ""), 100, 100); + dto.setQrCode(qCode); + datas.add(dto); + } + } + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + for (DeliverNormalOrderItemDTO it : datas) { + Map variables = new HashMap<>(); + variables.put("list", datas); + String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1", ".html", variables); + ZipEntry entry = new ZipEntry(it.getPrintNo() + ".png"); + zos.putNextEntry(entry); + byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 60); + zos.write(imageBytes, 0, imageBytes.length); + zos.closeEntry(); + } + } + byte[] zipBytes = baos.toByteArray(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf("application/zip")); + headers.setContentLength(zipBytes.length); + return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK); + } } diff --git a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/BinController.java b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/BinController.java index 263bd7e4..ea431c5a 100644 --- a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/BinController.java +++ b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/BinController.java @@ -15,6 +15,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -115,6 +116,15 @@ public class BinController extends BaseController { binControllerService.exportPdf(response, ids); } + /** + * 导出标签ZIP + * @param ids 储位id列表 + */ + @PostMapping("exportZip") + public ResponseEntity exportZip(@Valid @RequestBody @NotEmpty List ids) throws Exception { + return binControllerService.exportZip(ids); + } + /** * 获取库存信息 * @param warehouseId 仓库id diff --git a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/InProduceOrderController.java b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/InProduceOrderController.java index f9145ed7..8f830645 100644 --- a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/InProduceOrderController.java +++ b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/InProduceOrderController.java @@ -10,10 +10,7 @@ import com.nflg.wms.admin.repository.InMaterialScanRecordRespository; import com.nflg.wms.admin.service.BasdeSerialNumberControllerService; import com.nflg.wms.admin.service.BinService; import com.nflg.wms.admin.service.SapService; -import com.nflg.wms.admin.util.NoUtil; -import com.nflg.wms.admin.util.PdfGeneratorUtil; -import com.nflg.wms.admin.util.QRCodeUtil; -import com.nflg.wms.admin.util.ThymeleafUtil; +import com.nflg.wms.admin.util.*; import com.nflg.wms.common.constant.Constant; import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.PageData; @@ -35,15 +32,22 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; +import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.net.URL; import java.time.Instant; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * 生产订单入库管理 @@ -242,6 +246,65 @@ public class InProduceOrderController extends BaseController { } } + /** + * 导出物料标签图片ZIP + * @param id 订单ID + */ + @GetMapping(value = "exportItemImageZip", produces = "application/zip") + public ResponseEntity exportItemImageZip(@Valid @RequestParam @NotNull Long id) throws Exception { + WmsInProduceOrder order = produceOrderService.getById(id); + VUtil.trueThrowBusinessError(Objects.isNull(order)).throwMessage("订单不存在"); + List list = produceOrderItemService.getByOrderId(id); + if (order.getList()) { + list.removeIf(item -> Objects.equals(item.getParentId(), 0L)); + } + List datas = new ArrayList<>(); + for (WmsInProduceOrderItem item : list) { + BigDecimal[] result = item.getNum().divideAndRemainder(BigDecimal.ONE); + for (int i = 0, count = result[0].intValue(); i < count; i++) { + DeliverNormalOrderItemDTO dto = new DeliverNormalOrderItemDTO(); + dto.setMaterialNo(item.getMaterialNo()); + dto.setMaterialDesc(item.getMaterialDesc()); + dto.setExternalOrderNo(order.getOrderNo()); + dto.setRowNo(""); + dto.setIndex(i); + dto.setPrintNo(IdUtil.getSnowflakeNextIdStr()); + dto.setPrintLabel("自制件"); + if (i == count - 1 && result[1].compareTo(BigDecimal.ZERO) > 0) { + dto.setPrintNum(DF.format(result[1])); + } else { + dto.setPrintNum(DF.format(BigDecimal.ONE)); + } + dto.setSupplierCode(""); + dto.setBatchNo(item.getBatchNo()); + dto.setQrCode(QRCodeUtil.generateQRCodeBase64(generateQRContent(dto, ""), 100, 100)); + datas.add(dto); + } + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + for (DeliverNormalOrderItemDTO it : datas) { + Map variables = new HashMap<>(); + variables.put("list", datas); + String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1", ".html", variables); +// HtmlToImageUtil.convertToPng(html, 800); +// response.setContentType("image/png"); +// response.getOutputStream().write(HtmlToImageUtil.convertToPng(html, 60)); +// break; + ZipEntry entry = new ZipEntry(it.getPrintNo() + ".png"); + zos.putNextEntry(entry); + byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 60); + zos.write(imageBytes, 0, imageBytes.length); + zos.closeEntry(); + } + } + byte[] zipBytes = baos.toByteArray(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf("application/zip")); + headers.setContentLength(zipBytes.length); + return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK); + } + /** * 导出报工单PDF(单个) * @param id 订单id diff --git a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/InProduceOrderSurplusController.java b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/InProduceOrderSurplusController.java index b7d934ac..632c8a42 100644 --- a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/InProduceOrderSurplusController.java +++ b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/InProduceOrderSurplusController.java @@ -10,10 +10,7 @@ import com.nflg.wms.admin.pojo.dto.PdfPageDTO; import com.nflg.wms.admin.repository.InMaterialScanRecordRespository; import com.nflg.wms.admin.service.BasdeSerialNumberControllerService; import com.nflg.wms.admin.service.SapService; -import com.nflg.wms.admin.util.NoUtil; -import com.nflg.wms.admin.util.PdfGeneratorUtil; -import com.nflg.wms.admin.util.QRCodeUtil; -import com.nflg.wms.admin.util.ThymeleafUtil; +import com.nflg.wms.admin.util.*; import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.PageData; import com.nflg.wms.common.pojo.document.InMaterialScanRecord; @@ -37,15 +34,22 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; +import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.net.URL; import java.time.Instant; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * 生产订单副产品(拆解)管理 @@ -371,6 +375,62 @@ public class InProduceOrderSurplusController extends BaseController { } } + /** + * 导出物料标签图片ZIP + * @param id 订单ID + */ + @GetMapping(value = "exportItemImageZip", produces = "application/zip") + public ResponseEntity exportItemImageZip(@Valid @RequestParam @NotNull Long id) throws Exception { + WmsInProduceOrderSurplus order = inProduceOrderSurplusService.getById(id); + VUtil.trueThrowBusinessError(Objects.isNull(order)).throwMessage("订单不存在"); + List list = inProduceOrderSurplusItemService.getList(id); + List datas = new ArrayList<>(); + for (WmsInProduceOrderSurplusItem item : list) { + BigDecimal[] result = item.getNum().divideAndRemainder(BigDecimal.ONE); + for (int i = 0, count = result[0].intValue(); i < count; i++) { + DeliverNormalOrderItemDTO dto = new DeliverNormalOrderItemDTO(); + dto.setMaterialNo(item.getMatnr()); + dto.setMaterialDesc(item.getMaktx2()); + dto.setExternalOrderNo(order.getAufnr()); + dto.setRowNo(""); + dto.setIndex(i); + dto.setPrintNo(IdUtil.getSnowflakeNextIdStr()); + dto.setPrintLabel("自制件"); + if (i == count - 1 && result[1].compareTo(BigDecimal.ZERO) > 0) { + dto.setPrintNum(DF.format(result[1])); + } else { + dto.setPrintNum(DF.format(BigDecimal.ONE)); + } + dto.setSupplierCode(""); + dto.setBatchNo(""); + dto.setQrCode(QRCodeUtil.generateQRCodeBase64(generateQRContent(dto, ""), 100, 100)); + datas.add(dto); + } + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + for (DeliverNormalOrderItemDTO it : datas) { + Map variables = new HashMap<>(); + variables.put("list", datas); + String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1", ".html", variables); +// HtmlToImageUtil.convertToPng(html, 800); +// response.setContentType("image/png"); +// response.getOutputStream().write(HtmlToImageUtil.convertToPng(html, 60)); +// break; + ZipEntry entry = new ZipEntry(it.getPrintNo() + ".png"); + zos.putNextEntry(entry); + byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 60); + zos.write(imageBytes, 0, imageBytes.length); + zos.closeEntry(); + } + } + byte[] zipBytes = baos.toByteArray(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf("application/zip")); + headers.setContentLength(zipBytes.length); + return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK); + } + /** * 导出拆解单 */ diff --git a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/NormalOrderController.java b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/NormalOrderController.java index fe1a2fce..8e88832a 100644 --- a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/NormalOrderController.java +++ b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/NormalOrderController.java @@ -6,10 +6,7 @@ import cn.hutool.core.util.StrUtil; import com.nflg.wms.admin.pojo.dto.SAPMaterialInfoInOrderDTO; import com.nflg.wms.admin.service.BasdeSerialNumberControllerService; import com.nflg.wms.admin.service.SapService; -import com.nflg.wms.admin.util.NoUtil; -import com.nflg.wms.admin.util.PdfGeneratorUtil; -import com.nflg.wms.admin.util.QRCodeUtil; -import com.nflg.wms.admin.util.ThymeleafUtil; +import com.nflg.wms.admin.util.*; import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.PageData; import com.nflg.wms.common.pojo.dto.DeliverNormalOrderItemDTO; @@ -36,14 +33,21 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; +import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.net.URL; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * 普通物料订单管理 @@ -181,6 +185,7 @@ public class NormalOrderController extends BaseController { DeliverNormalOrderItemDTO dto = Convert.convert(DeliverNormalOrderItemDTO.class, order); dto.setIndex(i); dto.setPrintNo(order.getId() + "-" + i); + dto.setPrintLabel(dto.getPrintNo()); if (i == order.getLableNum() - 1) { BigDecimal n = order.getBatchNum().divideAndRemainder(order.getMinPackageNum())[1]; if (n.compareTo(BigDecimal.ZERO) == 0) { @@ -270,6 +275,7 @@ public class NormalOrderController extends BaseController { dto.setBatchNo(NoUtil.getBatchNo(supplierNo)); dto.setIndex(i); dto.setPrintNo(order.getId() + "-" + i); + dto.setPrintLabel(dto.getPrintNo()); if (i == order.getLableNum() - 1) { BigDecimal n = order.getBatchNum().divideAndRemainder(order.getMinPackageNum())[1]; if (n.compareTo(BigDecimal.ZERO) == 0) { @@ -303,4 +309,48 @@ public class NormalOrderController extends BaseController { URL baseUrl = new ClassPathResource("template/qrcode/").getURL(); PdfGeneratorUtil.generatePdf("普通物料条码(整张)", html, baseUrl.toString(), response); } + + /** + * 导出采购单物料图片ZIP + * @param ids 要打印的id列表 + */ + @PostMapping(value = "exportOrderItemImageZip", produces = "application/zip") + public ResponseEntity exportOrderItemImageZip(@Valid @RequestBody @NotNull List ids) throws Exception { + List orders = deliverNormalOrderService.getList(ids); + VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(orders)).throwMessage("没有需要打印的数据"); + return exportItemImageZip(convert(orders)); + } + + /** + * 导出打印送货单物料图片ZIP + * @param datas 要打印的数据 + */ + @PostMapping(value = "deliver/exportOrderItemImageZip", produces = "application/zip") + public void exportDeliverOrderPdfPerPage(@Valid @RequestBody @NotEmpty List datas) throws Exception { + exportItemImageZip(convert1(datas.get(0).getSupplierNo(), datas)); + } + + /** + * 导出标签图片ZIP + */ + private ResponseEntity exportItemImageZip(List list) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + for (DeliverNormalOrderItemDTO it : list) { + Map variables = new HashMap<>(); + variables.put("list", List.of(it)); + String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1", ".html", variables); + ZipEntry entry = new ZipEntry(it.getPrintNo() + ".png"); + zos.putNextEntry(entry); + byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 60); + zos.write(imageBytes, 0, imageBytes.length); + zos.closeEntry(); + } + } + byte[] zipBytes = baos.toByteArray(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf("application/zip")); + headers.setContentLength(zipBytes.length); + return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK); + } } \ No newline at end of file diff --git a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/StructuralPackageOrderController.java b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/StructuralPackageOrderController.java index b957038f..b682f9f7 100644 --- a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/StructuralPackageOrderController.java +++ b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/controller/StructuralPackageOrderController.java @@ -22,10 +22,7 @@ import com.nflg.wms.admin.service.BasdeSerialNumberControllerService; import com.nflg.wms.admin.service.BinService; import com.nflg.wms.admin.service.SapService; import com.nflg.wms.admin.service.StructuralPackageControllerService; -import com.nflg.wms.admin.util.NoUtil; -import com.nflg.wms.admin.util.PdfGeneratorUtil; -import com.nflg.wms.admin.util.QRCodeUtil; -import com.nflg.wms.admin.util.ThymeleafUtil; +import com.nflg.wms.admin.util.*; import com.nflg.wms.common.constant.OrderState; import com.nflg.wms.common.constant.STATE; import com.nflg.wms.common.exception.NflgException; @@ -51,7 +48,9 @@ import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -71,6 +70,8 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * 钢构件订单管理 @@ -456,13 +457,14 @@ public class StructuralPackageOrderController extends BaseController { for (DeliverStructuralPackageOrderTrayItemVO it : list) { for (int i = 0, count = it.getShipmentNum().intValue(); i < count; i++) { DeliverStructuralPackageOrderTrayItemVO vo = BeanUtil.copy(it, DeliverStructuralPackageOrderTrayItemVO.class); + vo.setQrCodeId(it.getId() + "-" + (i + 1)); vo.setQrCode(it.getId() + "-" + (i + 1) + "^" + it.getMaterialNo() + "_" + it.getTrayNo() + "_1"); vo.setQrCodeImage(QRCodeUtil.generateQRCodeBase64(vo.getQrCode(), 200, 200)); datas.add(vo); } - if (it.getShipmentNum().remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) > 0) { DeliverStructuralPackageOrderTrayItemVO vo = BeanUtil.copy(it, DeliverStructuralPackageOrderTrayItemVO.class); + vo.setQrCodeId(it.getId() + "-" + (it.getShipmentNum().intValue() + 1)); vo.setQrCode(it.getId() + "-" + (it.getShipmentNum().intValue() + 1) + "^" + it.getMaterialNo() + "_" + it.getTrayNo() + "_" + it.getShipmentNum().remainder(BigDecimal.ONE)); vo.setQrCodeImage(QRCodeUtil.generateQRCodeBase64(vo.getQrCode(), 200, 200)); datas.add(vo); @@ -476,6 +478,59 @@ public class StructuralPackageOrderController extends BaseController { PdfGeneratorUtil.generatePdf(trayVO.getExternalOrderNo() + "-" + trayVO.getWorkbenchCode() + "标签", html, baseUrl.toString(), response); } + /** + * 导出零件标签图片ZIP + * @param id 托盘id + */ + @GetMapping(value = "exportItemImageZip", produces = "application/zip") + public ResponseEntity exportItemImageZip(HttpServletResponse response, @Valid @RequestParam @NotNull Long id) throws Exception { + DeliverStructuralPackageOrderExtendVO trayVO = deliverStructuralPackageOrderTrayService.getInfo(id); + VUtil.trueThrowBusinessError(Objects.isNull(trayVO)).throwMessage("数据不存在"); + List list = deliverStructuralPackageOrderTrayItemService.getListVOByTrayId(id); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + List datas = new ArrayList<>(); + int index = 0; + for (DeliverStructuralPackageOrderTrayItemVO it : list) { + datas.clear(); + index++; + for (int i = 0, count = it.getShipmentNum().intValue(); i < count; i++) { + DeliverStructuralPackageOrderTrayItemVO vo = BeanUtil.copy(it, DeliverStructuralPackageOrderTrayItemVO.class); + vo.setQrCodeId(it.getId() + "-" + (i + 1)); + vo.setQrCode(it.getId() + "-" + (i + 1) + "^" + it.getMaterialNo() + "_" + it.getTrayNo() + "_1"); + vo.setQrCodeImage(QRCodeUtil.generateQRCodeBase64(vo.getQrCode(), 200, 200)); + datas.add(vo); + } + if (it.getShipmentNum().remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) > 0) { + DeliverStructuralPackageOrderTrayItemVO vo = BeanUtil.copy(it, DeliverStructuralPackageOrderTrayItemVO.class); + vo.setQrCodeId(it.getId() + "-" + (it.getShipmentNum().intValue() + 1)); + vo.setQrCode(it.getId() + "-" + (it.getShipmentNum().intValue() + 1) + "^" + it.getMaterialNo() + "_" + it.getTrayNo() + "_" + it.getShipmentNum().remainder(BigDecimal.ONE)); + vo.setQrCodeImage(QRCodeUtil.generateQRCodeBase64(vo.getQrCode(), 200, 200)); + datas.add(vo); + } + Map variables = new HashMap<>(); + variables.put("list", datas); + variables.put("info", trayVO); + variables.put("index", index); + String html = ThymeleafUtil.generator("/template/qrcode/", "spitem", ".html", variables); +// HtmlToImageUtil.convertToPng(html, 800); +// response.setContentType("image/png"); +// response.getOutputStream().write(HtmlToImageUtil.convertToPng(html, 60)); +// break; + ZipEntry entry = new ZipEntry(it.getId() + ".png"); + zos.putNextEntry(entry); + byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 60); + zos.write(imageBytes, 0, imageBytes.length); + zos.closeEntry(); + } + } + byte[] zipBytes = baos.toByteArray(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf("application/zip")); + headers.setContentLength(zipBytes.length); + return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK); + } + /** * 导出托盘标签PDF * @param id 托盘id @@ -492,6 +547,25 @@ public class StructuralPackageOrderController extends BaseController { PdfGeneratorUtil.generatePdf("托盘标签" + trayVO.getTrayNo(), html, baseUrl.toString(), response); } + /** + * 导出托盘标签图片 + * @param id 托盘id + */ + @GetMapping(value = "exportTrayImage", produces = MediaType.IMAGE_PNG_VALUE) + public ResponseEntity exportTrayImage(@Valid @RequestParam @NotNull Long id) throws Exception { + DeliverStructuralPackageOrderExtendVO trayVO = deliverStructuralPackageOrderTrayService.getInfo(id); + VUtil.trueThrowBusinessError(Objects.isNull(trayVO)).throwMessage("数据不存在"); + trayVO.setQrCode(QRCodeUtil.generateQRCodeBase64(trayVO.getTrayNo(), 100, 100)); + Map variables = new HashMap<>(); + variables.put("info", trayVO); + String html = ThymeleafUtil.generator("/template/qrcode/", "tray", ".html", variables); + byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 60); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.IMAGE_PNG); + headers.setContentLength(imageBytes.length); + return new ResponseEntity<>(imageBytes, headers, HttpStatus.OK); + } + /** * 导出齐套标签PDF * @param id 托盘id @@ -510,6 +584,27 @@ public class StructuralPackageOrderController extends BaseController { PdfGeneratorUtil.generatePdf("齐套标签" + trayVO.getTrayNo(), html, baseUrl.toString(), response); } + /** + * 导出齐套标签图片 + * @param id 托盘id + */ + @GetMapping(value = "exportQiTaoImage", produces = MediaType.IMAGE_PNG_VALUE) + public ResponseEntity exportQiTaoImage(@Valid @RequestParam @NotNull Long id) throws Exception { + DeliverStructuralPackageOrderExtendVO trayVO = deliverStructuralPackageOrderTrayService.getInfo(id); + VUtil.trueThrowBusinessError(Objects.isNull(trayVO)).throwMessage("数据不存在"); + String temp = trayVO.getExternalOrderNo() + "$" + trayVO.getRowNo() + "$" + trayVO.getPackageNo(); + String uniqueCode = DigestUtil.md5Hex(temp); + trayVO.setQrCode(QRCodeUtil.generateQRCodeBase64(uniqueCode + "$" + temp, 100, 100)); + Map variables = new HashMap<>(); + variables.put("info", trayVO); + String html = ThymeleafUtil.generator("/template/qrcode/", "qitao", ".html", variables); + byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 60); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.IMAGE_PNG); + headers.setContentLength(imageBytes.length); + return new ResponseEntity<>(imageBytes, headers, HttpStatus.OK); + } + /** * 根据托盘号获取托盘信息(PDA使用) */ @@ -1054,10 +1149,6 @@ public class StructuralPackageOrderController extends BaseController { } else if (!DateTimeUtil.isValidDate(expectDeliveryDate)) { sb.append("期望交期无效;"); } - String rowNo = StrUtil.trim(data.get("行项目*").toString()); - if (StrUtil.isBlank(rowNo)) { - sb.append("行项目不能为空;"); - } String snum = StrUtil.trim(data.get("数量*").toString()); BigDecimal num = null; if (StrUtil.isBlank(snum)) { @@ -1071,6 +1162,7 @@ public class StructuralPackageOrderController extends BaseController { } } String poNum = StrUtil.trim(data.get("采购订单号*").toString()); + String rowNo = StrUtil.trim(data.get("行项目*").toString()); BigDecimal transportNum = num; if (StrUtil.isBlank(poNum)) { sb.append("采购订单号不能为空;"); @@ -1081,6 +1173,9 @@ public class StructuralPackageOrderController extends BaseController { if (CollectionUtil.isEmpty(orders)) { sb.append("采购订单号无效;"); } else { + if (StrUtil.isBlank(rowNo)) { + sb.append("行项目不能为空;"); + } SAPMaterialInfoInOrderDTO order = orders.stream() .filter(it -> StrUtil.equals(it.getEbelp(), rowNo)) .findFirst() diff --git a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/service/BinControllerService.java b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/service/BinControllerService.java index 70e63876..0d061a6b 100644 --- a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/service/BinControllerService.java +++ b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/service/BinControllerService.java @@ -5,6 +5,7 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.nflg.wms.admin.util.HtmlToImageUtil; import com.nflg.wms.admin.util.PdfGeneratorUtil; import com.nflg.wms.admin.util.QRCodeUtil; import com.nflg.wms.admin.util.ThymeleafUtil; @@ -36,8 +37,11 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -53,7 +57,10 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +@Slf4j @Component public class BinControllerService { @@ -102,14 +109,14 @@ public class BinControllerService { if (updateCheckAndImport(data)) { return ApiResult.success(); } else { - try(ByteArrayOutputStream osOut = new ByteArrayOutputStream()) { + try (ByteArrayOutputStream osOut = new ByteArrayOutputStream()) { new Workbook() .addSheet(new ListSheet<>(data)) .writeTo(osOut); - try(ByteArrayInputStream isIn = new ByteArrayInputStream(osOut.toByteArray())) { + try (ByteArrayInputStream isIn = new ByteArrayInputStream(osOut.toByteArray())) { return ApiResult.error(STATE.DataNoCheckPass, "导入文件失败", fileUploadService.upload("temp/" + DateTimeUtil.format(LocalDate.now(), "yyyyMMdd") + "/" + IdUtil.fastUUID() + ".xlsx", isIn, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")); } - }catch (Exception e){ + } catch (Exception e) { return ApiResult.error(STATE.BusinessError, "保存文件出错"); } } @@ -155,9 +162,9 @@ public class BinControllerService { bin.setUpdateTime(LocalDateTime.now()); } } - if (StrUtil.isBlank(dto.getName())){ + if (StrUtil.isBlank(dto.getName())) { sb.append("仓库名称不能为空;"); - }else { + } else { bin.setName(dto.getName()); } bin.setRemark(dto.getRemark()); @@ -206,8 +213,10 @@ public class BinControllerService { List bins = wmsBinService.listByIds(ids); Map variables = new HashMap<>(); variables.put("datas", bins.stream().map(bin -> new QRCodeDTO() - .setNo(bin.getNo()) - .setQrCode(QRCodeUtil.generateQRCodeBase64(bin.getNo(), 100, 100)))); + .setNo(bin.getNo()) + .setQrCode(QRCodeUtil.generateQRCodeBase64(bin.getNo(), 100, 100)) + ).toList() + ); String html = ThymeleafUtil.generator("/template/", "储位二维码", ".html", variables); PdfGeneratorUtil.generatePdf("储位二维码", html, response); } @@ -215,4 +224,30 @@ public class BinControllerService { public List getInventory(Long warehouseId) { return wmsBinService.getInventory(warehouseId); } + + public ResponseEntity exportZip(@Valid @NotEmpty List ids) throws Exception { + List bins = wmsBinService.listByIds(ids); + List datas = bins.stream().map(bin -> new QRCodeDTO() + .setNo(bin.getNo()) + .setQrCode(QRCodeUtil.generateQRCodeBase64(bin.getNo(), 100, 100))) + .toList(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + for (QRCodeDTO it : datas) { + Map variables = new HashMap<>(); + variables.put("datas", List.of(it)); + String html = ThymeleafUtil.generator("/template/", "储位二维码", ".html", variables); + ZipEntry entry = new ZipEntry(it.getNo() + ".png"); + zos.putNextEntry(entry); + byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 40); + zos.write(imageBytes, 0, imageBytes.length); + zos.closeEntry(); + } + } + byte[] zipBytes = baos.toByteArray(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf("application/zip")); + headers.setContentLength(zipBytes.length); + return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK); + } } diff --git a/nflg-wms-admin/src/main/java/com/nflg/wms/admin/util/HtmlToImageUtil.java b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/util/HtmlToImageUtil.java new file mode 100644 index 00000000..0d901992 --- /dev/null +++ b/nflg-wms-admin/src/main/java/com/nflg/wms/admin/util/HtmlToImageUtil.java @@ -0,0 +1,68 @@ +package com.nflg.wms.admin.util; + +import cn.hutool.core.io.FileUtil; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Slf4j +public class HtmlToImageUtil { + + /** + * 将HTML内容转换为PNG图片 + * @param htmlContent HTML内容 + * @param width 图片宽度,单位mm + * @return PNG图片字节数组 + * @throws Exception 转换异常 + */ + public static byte[] convertToPng(String htmlContent, int width) throws Exception { + Path inputHtmlPath = null, outputImgPath = null; + try { + Path tempDir = Paths.get(System.getProperty("java.io.tmpdir"), "wkhtml"); + if (!Files.exists(tempDir)) { + Files.createDirectories(tempDir); + } + long t = System.currentTimeMillis(); + inputHtmlPath = tempDir.resolve("input_" + t + ".html"); + outputImgPath = tempDir.resolve("output_" + t + ".png"); + Files.write(inputHtmlPath, htmlContent.getBytes()); + List command = new ArrayList<>(); + command.add("wkhtmltoimage"); + // 可选参数:设置宽度 (例如 1200px) + command.add("--width"); + command.add(String.valueOf((int) (width * 3.78))); + // 可选参数:指定图片类型 (例如 PNG) + command.add("--format"); + command.add("png"); + //质量 + command.add("--quality"); + command.add("100"); + // 输入文件路径 + command.add(inputHtmlPath.toAbsolutePath().toString()); + // 输出文件路径 + command.add(outputImgPath.toAbsolutePath().toString()); + ProcessBuilder builder = new ProcessBuilder(command); + Process process = builder.start(); + int exitCode = process.waitFor(); + if (exitCode != 0) { + // 读取错误流,帮助调试 + String error = new String(process.getErrorStream().readAllBytes()); + throw new IOException("html转换图片失败,退出码: " + exitCode + ", 错误信息: " + error); + } + return FileUtil.readBytes(outputImgPath); + } finally { + if (Objects.nonNull(inputHtmlPath)) { + Files.deleteIfExists(inputHtmlPath); + } + if (Objects.nonNull(outputImgPath)) { + Files.deleteIfExists(outputImgPath); + } + } + } +} diff --git a/nflg-wms-admin/src/main/resources/template/qrcode/dp-1.html b/nflg-wms-admin/src/main/resources/template/qrcode/dp-1.html index 91afae03..d9570cd8 100644 --- a/nflg-wms-admin/src/main/resources/template/qrcode/dp-1.html +++ b/nflg-wms-admin/src/main/resources/template/qrcode/dp-1.html @@ -6,22 +6,19 @@ - -
- -
20250227100950-0
+
+ +
20250227100950-0
-
logo
+
+
logo
SAP编码:
-
名称:
+
名称:
数量:
-
批次号:
+
批次号:
diff --git a/nflg-wms-admin/src/main/resources/template/qrcode/qitao.html b/nflg-wms-admin/src/main/resources/template/qrcode/qitao.html index cb240e6b..594f2af4 100644 --- a/nflg-wms-admin/src/main/resources/template/qrcode/qitao.html +++ b/nflg-wms-admin/src/main/resources/template/qrcode/qitao.html @@ -5,44 +5,58 @@ 齐套标签 - + - - - diff --git a/nflg-wms-admin/src/main/resources/template/qrcode/spitem.html b/nflg-wms-admin/src/main/resources/template/qrcode/spitem.html index 3e85e4a2..0d44230d 100644 --- a/nflg-wms-admin/src/main/resources/template/qrcode/spitem.html +++ b/nflg-wms-admin/src/main/resources/template/qrcode/spitem.html @@ -4,44 +4,93 @@ 钢构件物料标签 -
左踏板连接架
- + + + 采购单号: + S2507090053
- - - - - - - - - - - - - - - - - - -
833-33-07-NF左踏板连接架(0000101627)
- - 833-33-07-NF
SAP编码:2100080450
- -
77777
+
+
1
+ + + + + + + + + + + + + + + + + + +
+
+ 833-33-07-NF左踏板连接架(0000101627)833-33-07-NF左踏板连接架NF左踏板连接架NF左踏板连接架(0000101627) +
+
+ + 833-33-07-NF
SAP编码2100080450
+ +
+ 1244565616465_1 +
+
\ No newline at end of file diff --git a/nflg-wms-admin/src/main/resources/template/qrcode/tray.html b/nflg-wms-admin/src/main/resources/template/qrcode/tray.html index 1f212bfb..640b19ac 100644 --- a/nflg-wms-admin/src/main/resources/template/qrcode/tray.html +++ b/nflg-wms-admin/src/main/resources/template/qrcode/tray.html @@ -5,16 +5,18 @@ 托盘标签 - + - - - diff --git a/nflg-wms-admin/src/main/resources/template/储位二维码.html b/nflg-wms-admin/src/main/resources/template/储位二维码.html index 304655cd..d3988109 100644 --- a/nflg-wms-admin/src/main/resources/template/储位二维码.html +++ b/nflg-wms-admin/src/main/resources/template/储位二维码.html @@ -6,26 +6,23 @@ 二维码
左踏板连接架
- + + + 托盘号: + S2507090053