工会报销导出添加二维码
This commit is contained in:
+91
-5
@@ -19,6 +19,20 @@ import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseSer
|
||||
import com.deepoove.poi.XWPFTemplate;
|
||||
import com.deepoove.poi.config.Configure;
|
||||
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
||||
import com.deepoove.poi.data.PictureType;
|
||||
import com.deepoove.poi.data.Pictures;
|
||||
import com.deepoove.poi.template.ElementTemplate;
|
||||
import com.google.zxing.*;
|
||||
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
|
||||
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
import com.google.zxing.qrcode.QRCodeReader;
|
||||
import org.apache.poi.xwpf.usermodel.*;
|
||||
import org.apache.poi.util.Units;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -45,13 +59,13 @@ public class UnionReimburseExportServiceImpl implements UnionReimburseExportServ
|
||||
/** 当前“我的报销”导出仅允许单据经办人,状态白名单与页面按钮保持一致。 */
|
||||
@Override
|
||||
public byte[] exportPdf(String id) {
|
||||
return renderDocument(loadExportForm(id, false, false), true);
|
||||
return renderDocument(loadExportForm(id, false, false), true, false);
|
||||
}
|
||||
|
||||
/** 汇总打印支持所有状态,并严格按汇总权限和所属工会范围读取,复用我的报销 PDF 模板。 */
|
||||
@Override
|
||||
public byte[] exportCollectPdf(String id) {
|
||||
return renderDocument(loadExportForm(id, true, true), true);
|
||||
return renderDocument(loadExportForm(id, true, true), true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,8 +103,8 @@ public class UnionReimburseExportServiceImpl implements UnionReimburseExportServ
|
||||
return form;
|
||||
}
|
||||
|
||||
/** 两份模板仅保留布局差异,Word/PDF 共用字段组装和循环行配置。 */
|
||||
private byte[] renderDocument(UnionReimburse form, boolean asPdf) {
|
||||
/** 两份模板共用字段和循环行配置;collectPrint 仅为汇总打印补充编号二维码。 */
|
||||
private byte[] renderDocument(UnionReimburse form, boolean asPdf, boolean collectPrint) {
|
||||
String templateCode = "UNION_REIMBURSE_PROJECT_1".equals(form.getReimburseProject())
|
||||
? CONDOLENCE_TEMPLATE : ACTIVITY_TEMPLATE;
|
||||
Path docx = null;
|
||||
@@ -107,7 +121,11 @@ public class UnionReimburseExportServiceImpl implements UnionReimburseExportServ
|
||||
try (InputStream input = sysOfficeTemplateUtil.getTemplate(templateCode);
|
||||
XWPFTemplate template = XWPFTemplate.compile(input, config);
|
||||
OutputStream output = Files.newOutputStream(docx)) {
|
||||
template.render(buildExportData(form)).write(output);
|
||||
Map<String, Object> data = buildExportData(form);
|
||||
if (collectPrint) {
|
||||
prepareCollectQrCode(template, data, form.getDocumentNo());
|
||||
}
|
||||
template.render(data).write(output);
|
||||
}
|
||||
if (!asPdf) {
|
||||
return Files.readAllBytes(docx);
|
||||
@@ -139,6 +157,74 @@ public class UnionReimburseExportServiceImpl implements UnionReimburseExportServ
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总打印沿用老系统的单据编号二维码,仅修改本次生成的文档,不覆盖模板。
|
||||
* @param template 当前已编译模板,优先使用 qrcode 图片标记,其次替换标题区的固定二维码
|
||||
* @param data 本次渲染数据,qrcode 字段为 80×80 的 PNG 图片对象
|
||||
* @param documentNo 原始单据编号,缺失时拒绝打印,避免用业务 ID 冒充编号
|
||||
*/
|
||||
private void prepareCollectQrCode(XWPFTemplate template, Map<String, Object> data,
|
||||
String documentNo) throws Exception {
|
||||
if (StrUtil.isBlank(documentNo)) {
|
||||
throw new BaseException("报销单编号为空,无法生成打印二维码,请先核对单据编号");
|
||||
}
|
||||
var matrix = new QRCodeWriter().encode(documentNo, BarcodeFormat.QR_CODE, 80, 80);
|
||||
var image = MatrixToImageWriter.toBufferedImage(matrix);
|
||||
data.put("qrcode", Pictures.ofBufferedImage(image, PictureType.PNG).size(80, 80).create());
|
||||
boolean hasTag = template.getElementTemplates().stream()
|
||||
.anyMatch(item -> item instanceof ElementTemplate
|
||||
&& "qrcode".equals(((ElementTemplate) item).getTagName()));
|
||||
if (hasTag) {
|
||||
return;
|
||||
}
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "png", bytes);
|
||||
XWPFDocument document = template.getXWPFDocument();
|
||||
// 部分现用模板内嵌了固定二维码。仅识别第一张业务表格之前的二维码,保留徽标等普通图片。
|
||||
boolean replaced = false;
|
||||
for (IBodyElement element : document.getBodyElements()) {
|
||||
if (element instanceof XWPFTable) break;
|
||||
if (!(element instanceof XWPFParagraph)) continue;
|
||||
for (XWPFRun run : ((XWPFParagraph) element).getRuns()) {
|
||||
for (XWPFPicture picture : run.getEmbeddedPictures()) {
|
||||
if (picture.getPictureData() == null) continue;
|
||||
var existing = ImageIO.read(new ByteArrayInputStream(picture.getPictureData().getData()));
|
||||
if (existing == null) continue;
|
||||
try {
|
||||
new QRCodeReader().decode(new BinaryBitmap(new HybridBinarizer(
|
||||
new BufferedImageLuminanceSource(existing))));
|
||||
} catch (NotFoundException | ChecksumException | FormatException e) {
|
||||
continue;
|
||||
}
|
||||
String relationship = document.addPictureData(bytes.toByteArray(), Document.PICTURE_TYPE_PNG);
|
||||
picture.getCTPicture().getBlipFill().getBlip().setEmbed(relationship);
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (replaced) return;
|
||||
// 无二维码标记或固定二维码的模板,在正文开头添加右对齐图片,不依赖模板重新上传。
|
||||
XWPFParagraph paragraph;
|
||||
if (document.getBodyElements().isEmpty()) {
|
||||
paragraph = document.createParagraph();
|
||||
} else {
|
||||
IBodyElement first = document.getBodyElements().get(0);
|
||||
var cursor = first instanceof XWPFParagraph
|
||||
? ((XWPFParagraph) first).getCTP().newCursor()
|
||||
: ((XWPFTable) first).getCTTbl().newCursor();
|
||||
try {
|
||||
paragraph = document.insertNewParagraph(cursor);
|
||||
} finally {
|
||||
cursor.dispose();
|
||||
}
|
||||
}
|
||||
paragraph.setAlignment(ParagraphAlignment.RIGHT);
|
||||
paragraph.setSpacingBefore(0);
|
||||
paragraph.setSpacingAfter(0);
|
||||
paragraph.createRun().addPicture(new ByteArrayInputStream(bytes.toByteArray()),
|
||||
Document.PICTURE_TYPE_PNG, "qrcode.png", Units.pixelToEMU(80), Units.pixelToEMU(80));
|
||||
}
|
||||
|
||||
/** 平铺字段供两类模板复用,支付总额与发票总额分别计算,不使用第一条支付代替全部明细。 */
|
||||
@Override
|
||||
public Map<String, Object> buildExportData(UnionReimburse form) {
|
||||
|
||||
Reference in New Issue
Block a user