Archived
refactor: 用直接XML预处理替换poi-tl模板引擎 — 合并Run+替换占位符在ZIP层完成,减少依赖
This commit is contained in:
@@ -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<String, Object> 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<String, Object> dataModel, Map<String, Object> businessData) throws IOException {
|
||||
// Merge data: businessData overrides dataModel
|
||||
Map<String, Object> 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(
|
||||
|
||||
@@ -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<String, Object> 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
|
||||
|
||||
Reference in New Issue
Block a user