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