package com.hvlink.service.impl;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hvlink.entity.dto.asn.AsnPackDTO;
import com.hvlink.entity.param.asn.AsnPackExportParam;
import com.hvlink.entity.param.asn.AsnPackLabelParam;
import com.hvlink.entity.po.asn.AsnPackPO;
import com.hvlink.entity.vo.asn.AsnPackLabelVO;
import com.hvlink.mapper.asn.AsnPackMapper;
import com.hvlink.pagination.PageResult;
import com.hvlink.service.IAsnPackService;
import com.hvlink.utils.BeanCopyUtils;
import com.hvlink.utils.QRCodeUtils;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* ASN包装信息(AsnPack)表服务实现类
*
* @author sunzhuang
* @since 2025-09-23 16:39:02
*/
@Slf4j
@RequiredArgsConstructor
@Service("asnPackService")
public class AsnPackServiceImpl extends ServiceImpl<AsnPackMapper, AsnPackPO> implements IAsnPackService {
private final AsnPackMapper asnPackMapper;
/**
* 查询标签信息
*
* @param asnPackLabelParam 请求参数
* @return 查询结果
*/
@Override
public PageResult<AsnPackLabelVO> queryPage(AsnPackLabelParam asnPackLabelParam) {
// 查询标签数据
Page<AsnPackDTO> page = new Page<>(asnPackLabelParam.getPageIndex(), asnPackLabelParam.getPageSize());
Page<AsnPackDTO> dtoPage = asnPackMapper.queryLabelPage(page, asnPackLabelParam);
// 转换 DTO 到 VO
List<AsnPackLabelVO> voList = Collections.emptyList();
if (!CollectionUtils.isEmpty(dtoPage.getRecords())) {
voList = dtoPage.getRecords().stream()
.map(dto -> BeanCopyUtils.copyBean(dto, AsnPackLabelVO.class))
.collect(Collectors.toList());
}
// 构建分页结果
PageResult<AsnPackLabelVO> result = new PageResult<>();
result.setRecords(voList);
result.setTotal(dtoPage.getTotal());
result.setPageIndex(asnPackLabelParam.getPageIndex());
result.setPageSize(asnPackLabelParam.getPageSize());
return result;
}
@Override
public Mono<ResponseEntity<Resource>> exportLabels(AsnPackExportParam exportParam) {
return Mono.defer(() -> {
try {
// 参数验证
if (exportParam == null || exportParam.getIds() == null || exportParam.getIds().isEmpty()) {
return Mono.error(new IllegalArgumentException("导出参数或ID列表不能为空"));
}
// 查询导出数据
List<AsnPackDTO> exportData = asnPackMapper.selectByIds(exportParam.getIds());
if (exportData.isEmpty()) {
return Mono.error(new RuntimeException("未找到导出数据"));
}
// 验证导出类型
if (!isValidExportType(exportParam.getExportType())) {
return Mono.error(new IllegalArgumentException("不支持的导出类型: " + exportParam.getExportType()));
}
// 根据导出类型选择模板
String templatePath = getTemplatePath(exportParam.getExportType());
// 生成PDF字节数组
byte[] pdfBytes = generatePdfBytes(exportData, exportParam.getExportType(), templatePath);
// 创建资源
Resource resource = new ByteArrayResource(pdfBytes);
// 设置响应头
String fileName = buildFileName(exportParam.getExportType());
ResponseEntity<Resource> responseEntity = ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.contentType(MediaType.APPLICATION_PDF)
.contentLength(pdfBytes.length)
.body(resource);
return Mono.just(responseEntity);
} catch (Exception e) {
log.error("标签导出失败", e);
return Mono.error(new RuntimeException("标签导出失败: " + e.getMessage()));
}
});
}
private String getTemplatePath(String exportType) {
switch (exportType.toUpperCase()) {
case "P":
return "static/templates/P.pdf"; // 托标签模板
case "C":
return "static/templates/C.pdf"; // 箱标签模板
case "B":
return "static/templates/B.pdf"; // 包标签模板
default:
throw new IllegalArgumentException("不支持的导出类型: " + exportType);
}
}
private boolean isValidExportType(String exportType) {
if (exportType == null) return false;
String type = exportType.toUpperCase();
return type.equals("P") || type.equals("C") || type.equals("B");
}
private String buildFileName(String exportType) {
String typeName = "";
switch (exportType.toUpperCase()) {
case "P":
typeName = "托标签";
break;
case "C":
typeName = "箱标签";
break;
case "B":
typeName = "包标签";
break;
default:
typeName = "标签";
}
// 构建文件名
String fileName = typeName + "_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".pdf";
try {
// 对文件名进行URL编码
return java.net.URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
} catch (Exception e) {
log.warn("文件名编码失败,使用默认文件名", e);
return "export_" + System.currentTimeMillis() + ".pdf";
}
}
private byte[] generatePdfBytes(List<AsnPackDTO> data, String exportType, String templatePath) throws Exception {
ClassPathResource templateResource = new ClassPathResource(templatePath);
if (!templateResource.exists()) {
throw new RuntimeException("模板文件不存在: " + templatePath);
}
try (InputStream templateStream = templateResource.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
PdfReader reader = new PdfReader(templateStream);
PdfStamper stamper = new PdfStamper(reader, outputStream);
AcroFields form = stamper.getAcroFields();
// 添加中文字体支持
BaseFont simSunFont = BaseFont.createFont("C:/Windows/Fonts/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
form.addSubstitutionFont(simSunFont);
// 填充第一个数据项
AsnPackDTO item = data.get(0);
fillFormFields(form, item, exportType);
// 添加二维码到指定位置
addQRCode(stamper, form, item, exportType);
stamper.setFormFlattening(true); // 将表单转为静态文本
stamper.close(); // 关闭 stamper 会自动写入修改后的内容
reader.close();
return outputStream.toByteArray();
}
}
private void fillFormFields(AcroFields form, AsnPackDTO item, String exportType) throws Exception {
Map<String, String> fieldValues = buildFieldValues(item, exportType);
log.info("要填充的数据: {}", fieldValues);
// 填充表单字段
for (Map.Entry<String, String> entry : fieldValues.entrySet()) {
String fieldName = entry.getKey();
String fieldValue = entry.getValue();
if (form.getFields().containsKey(fieldName)) {
if (StringUtils.hasText(fieldValue)) {
try {
form.setField(fieldName, fieldValue);
log.info("成功填充字段: {} = {}", fieldName, fieldValue);
} catch (Exception e) {
log.error("填充字段失败: {}, 值: {}", fieldName, fieldValue, e);
}
} else {
log.warn("字段值为空: {}", fieldName);
}
} else {
log.warn("未找到表单字段: {}", fieldName);
}
}
}
private Map<String, String> buildFieldValues(AsnPackDTO item, String exportType) {
Map<String, String> values = new HashMap<>();
// 公共字段
values.put("part_desc", item.getPartDesc());
values.put("part_code", item.getPartCode());
values.put("supplier_name", item.getSupplierName());
values.put("supplier_code", item.getSupplierCode());
values.put("order_no", item.getAsnNo());
values.put("production_batch", item.getProductionBatch());
values.put("case_code", generateCaseCode(item, exportType));
values.put("remark", item.getRemark());
// 数量相关字段
switch (exportType.toUpperCase()) {
case "P":
values.put("quantity", formatQuantity(item.getQuantity()));
values.put("pack_quantity", formatQuantity(item.getPackQuantity()));
values.put("unique_code", generateUniqueCode("P", item.getSupplierCode()));
values.put("master_label", "MASTER LABEL " + item.getWarehouseCode());
break;
case "C":
values.put("quantity", formatQuantity(item.getQuantity()));
values.put("pack_quantity", formatQuantity(item.getPackQuantity()));
values.put("unique_code", generateUniqueCode("C", item.getSupplierCode()));
break;
case "B":
values.put("quantity", formatQuantity(item.getQuantity()));
values.put("pack_quantity", formatQuantity(item.getPackQuantity()));
values.put("unique_code", generateUniqueCode("B", item.getSupplierCode()));
break;
}
return values;
}
private String generateCaseCode(AsnPackDTO item, String exportType) {
switch (exportType.toUpperCase()) {
case "P":
return "1/1"; // X/Y格式
case "C":
return "1/5-1"; // X/Y-Z格式
case "B":
return "1/10-C-1"; // X/Y-C-Z格式
default:
return item.getCaseCode();
}
}
private String generateUniqueCode(String prefix, String supplierCode) {
SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
String dateStr = sdf.format(new Date());
String sequence = String.format("%06d", System.currentTimeMillis() % 1000000);
return prefix + supplierCode + dateStr + sequence;
}
private String formatQuantity(Double quantity) {
return quantity != null ? String.valueOf(quantity) : "0";
}
private void addQRCode(PdfStamper stamper, AcroFields form, AsnPackDTO item, String exportType) throws Exception {
try {
String uniqueCode = generateUniqueCode(
exportType.equals("P") ? "P" :
exportType.equals("C") ? "C" : "B",
item.getSupplierCode()
);
// 生成二维码
ByteArrayOutputStream qrCodeStream = QRCodeUtils.generateQRCode(uniqueCode, 100, 100);
Image qrCodeImage = Image.getInstance(qrCodeStream.toByteArray());
// 获取字段位置
List<AcroFields.FieldPosition> fieldPositions = form.getFieldPositions("QRcode");
if (fieldPositions == null || fieldPositions.isEmpty()) {
log.warn("未找到 QRcode 字段的位置信息");
return;
}
AcroFields.FieldPosition position = fieldPositions.get(0);
int pageNo = position.page;
Rectangle rect = position.position;
PdfContentByte canvas = stamper.getOverContent(pageNo); // 使用上层绘制,避免干扰底层
qrCodeImage.setAbsolutePosition(rect.getLeft(), rect.getBottom());
qrCodeImage.scaleToFit(rect.getWidth(), rect.getHeight());
canvas.addImage(qrCodeImage);
} catch (Exception e) {
log.error("添加二维码失败", e);
throw e; // 显式抛出便于调试
}
}
}
请把我的导出PDF功能完全根据下面的代码修改,只保留包标签模板的填充和导出,我需要这样测试,并且不要二维码生成
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
public class Snippet {
// 利用模板生成pdf
public static void fillTemplate() {
// 模板路径
String templatePath = "E:/测试3.pdf";
// 生成的新文件路径
String newPDFPath = "E:/ceshi.pdf";
PdfReader reader;
FileOutputStream out;
ByteArrayOutputStream bos;
PdfStamper stamper;
try {
out = new FileOutputStream(newPDFPath);// 输出流
reader = new PdfReader(templatePath);// 读取pdf模板
bos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, bos);
AcroFields form = stamper.getAcroFields();
String[] str = { "123456789", "TOP__ONE", "男", "1991-01-01", "130222111133338888", "河北省保定市" };
int i = 0;
java.util.Iterator<String> it = form.getFields().keySet().iterator();
while (it.hasNext()) {
String name = it.next().toString();
System.out.println(name);
form.setField(name, str[i++]);
}
stamper.setFormFlattening(true);// 如果为false那么生成的PDF文件还能编辑,一定要设为true
stamper.close();
Document doc = new Document();
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);
copy.addPage(importPage);
doc.close();
} catch (IOException e) {
System.out.println(1);
} catch (DocumentException e) {
System.out.println(2);
}
}
public static void main(String[] args) {
fillTemplate();
}
}
最新发布