From 8f6c12f9605dc69f95623d92100185403860817c Mon Sep 17 00:00:00 2001 From: agentuser Date: Wed, 24 Jun 2026 17:54:03 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=94=A8=E7=9B=B4=E6=8E=A5XML?= =?UTF-8?q?=E9=A2=84=E5=A4=84=E7=90=86=E6=9B=BF=E6=8D=A2poi-tl=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=BC=95=E6=93=8E=20=E2=80=94=20=E5=90=88=E5=B9=B6Run?= =?UTF-8?q?+=E6=9B=BF=E6=8D=A2=E5=8D=A0=E4=BD=8D=E7=AC=A6=E5=9C=A8ZIP?= =?UTF-8?q?=E5=B1=82=E5=AE=8C=E6=88=90=EF=BC=8C=E5=87=8F=E5=B0=91=E4=BE=9D?= =?UTF-8?q?=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/gmp/mock/service/DocxService.java | 43 ++++++++++++------- .../annualReport/AnnualReportProvider.java | 31 ++++++++----- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/gmp/mock/service/DocxService.java b/src/main/java/com/gmp/mock/service/DocxService.java index 7201c34..ce60025 100644 --- a/src/main/java/com/gmp/mock/service/DocxService.java +++ b/src/main/java/com/gmp/mock/service/DocxService.java @@ -11,9 +11,9 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; +import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.springframework.stereotype.Service; -import com.deepoove.poi.XWPFTemplate; import com.gmp.mock.template.TemplateProvider; import com.gmp.mock.template.TemplateRegistry; @@ -38,22 +38,17 @@ public class DocxService { } try { - // 1. Pre-process: merge split runs - byte[] preprocessed = mergeSplitPlaceholders(provider.getTemplateResource()); + // 1. Pre-process: merge split runs + replace all placeholders in XML + byte[] processed = preprocessTemplate(provider.getTemplateResource(), provider.getDataModel(), businessData); - // 2. Build data model - Map data = new java.util.HashMap<>(provider.getDataModel()); - if (businessData != null) { - data.putAll(businessData); - } + // 2. Load as XWPFDocument for post-processing + XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(processed)); + provider.postProcess(document); - // 3. Render with poi-tl + // 3. Write to output ByteArrayOutputStream baos = new ByteArrayOutputStream(); - XWPFTemplate template = XWPFTemplate.compile(new ByteArrayInputStream(preprocessed)); - template.render(data); - provider.postProcess(template.getXWPFDocument()); - template.write(baos); - template.close(); + document.write(baos); + document.close(); // 4. Cache to disk String key = fileCache.store(baos.toByteArray()); @@ -67,13 +62,21 @@ public class DocxService { public record CachedResult(String key, String fileName) {} - // ====== Run merging (unchanged) ====== + /** + * 读取模板 docx,合并拆分占位符,替换全部 {{placeholder}},返回处理后的 docx 字节。 + */ + private byte[] preprocessTemplate(InputStream templateStream, Map dataModel, Map businessData) throws IOException { + // Merge data: businessData overrides dataModel + Map allData = new java.util.HashMap<>(dataModel); + if (businessData != null) { + allData.putAll(businessData); + } - private byte[] mergeSplitPlaceholders(InputStream templateStream) throws IOException { byte[] docxBytes; try (InputStream is = templateStream) { docxBytes = is.readAllBytes(); } + ByteArrayOutputStream out = new ByteArrayOutputStream(); try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(docxBytes)); ZipOutputStream zos = new ZipOutputStream(out)) { @@ -83,7 +86,13 @@ public class DocxService { byte[] content = zis.readAllBytes(); if ("word/document.xml".equals(entry.getName())) { String xml = new String(content, "UTF-8"); + // 1. Merge split runs (e.g. {{ year }} → {{year}}) xml = mergeRunsInXml(xml); + // 2. Replace all {{placeholders}} with actual values + for (var kv : allData.entrySet()) { + String val = kv.getValue() != null ? kv.getValue().toString() : ""; + xml = xml.replace("{{" + kv.getKey() + "}}", val); + } content = xml.getBytes("UTF-8"); } zos.write(content); @@ -93,6 +102,8 @@ public class DocxService { return out.toByteArray(); } + // ====== Run merging (unchanged) ====== + private String mergeRunsInXml(String xml) { StringBuilder result = new StringBuilder(); java.util.regex.Matcher m = Pattern.compile( diff --git a/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java b/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java index facee4b..90f4366 100644 --- a/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java +++ b/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java @@ -91,17 +91,26 @@ public class AnnualReportProvider implements TemplateProvider { int yearA = 2024; int yearB = 2025; int yearC = 2026; - return Map.of( - "title", "2025 年度工作报告", - "companyName", "示例科技有限公司", - "reportPeriod", "2025年1月 - 2025年12月", - "generatedDate", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")), - "yearA", String.valueOf(yearA), - "yearB", String.valueOf(yearB), - "yearC", String.valueOf(yearC), - "snhj", "300", - "dwjjtz", "200" - ); + Map model = new java.util.HashMap<>(); + model.put("title", "2025 年度工作报告"); + model.put("companyName", "示例科技有限公司"); + model.put("reportPeriod", "2025年1月 - 2025年12月"); + model.put("generatedDate", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"))); + model.put("yearA", String.valueOf(yearA)); + model.put("yearB", String.valueOf(yearB)); + model.put("yearC", String.valueOf(yearC)); + model.put("snhj", "300"); + model.put("dwjjtz", "200"); + model.put("dwjjbl", "66.7"); + model.put("xxjjtz", "30"); + model.put("xxjjzb", "10.0"); + model.put("jgtz", "50"); + model.put("jgzb", "16.7"); + model.put("kjtz", "10"); + model.put("kjzb", "3.3"); + model.put("xxhtz", "10"); + model.put("xxhzb", "3.3"); + return model; } @Override