123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- package com.yaoyicloud.render;
- import java.io.IOException;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import com.fasterxml.jackson.core.type.TypeReference;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.yaoyicloud.config.FilerepoProperties;
- import com.yaoyicloud.message.FxyProtos;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.poi.xwpf.usermodel.XWPFParagraph;
- import org.apache.poi.xwpf.usermodel.XWPFRun;
- import org.apache.poi.xwpf.usermodel.XWPFTable;
- import org.apache.poi.xwpf.usermodel.XWPFTableCell;
- import org.apache.poi.xwpf.usermodel.XWPFTableRow;
- import com.deepoove.poi.XWPFTemplate;
- import com.deepoove.poi.config.Configure;
- import com.deepoove.poi.config.ConfigureBuilder;
- import com.deepoove.poi.exception.RenderException;
- import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
- import com.deepoove.poi.policy.RenderPolicy;
- import com.deepoove.poi.template.ElementTemplate;
- import com.deepoove.poi.template.run.RunTemplate;
- import com.deepoove.poi.util.TableTools;
- import com.google.protobuf.util.JsonFormat;
- import com.yaoyicloud.message.FxyProtos.FinancialInfo;
- import com.yaoyicloud.tools.Util;
- /**
- * FinancialInfo渲染器
- *
- */
- @Slf4j
- public final class FinancialInfoRender extends AbstractRender {
- private final FilerepoProperties filerepoProperties;
- public FinancialInfoRender(String cwd, FilerepoProperties filerepoProperties, FilerepoProperties filerepoProperties1) {
- super(cwd);
- this.filerepoProperties = filerepoProperties1;
- }
- @Override
- protected String getBasicPath() throws IOException {
- return filerepoProperties.getBasePath();
- }
- @Override
- protected String getReportImagePath() {
- return filerepoProperties.getReportImagePath();
- }
- /**
- * Docx 渲染
- *
- * @param info 数据
- * @param templateFileContent 模板内容
- * @return 本地文件目录
- * @throws IOException
- */
- public String renderDocx(String info, Map<String, Object> addtionalMap, byte[] templateFileContent, String relationId) throws IOException {
- log.info("开始渲染附件模块,relationId: {}", relationId);
- // 配置POI-TL渲染器
- ConfigureBuilder builder = Configure.builder();
- RenderPolicy indicatorsRenderPolicy = this.indicatorsRenderPolicy();
- builder.bind("basicInfoChecks", indicatorsRenderPolicy);
- FxyProtos.BasicInfo.Builder basicInfoBuilder = FxyProtos.BasicInfo.newBuilder();
- JsonFormat.parser().merge(info, basicInfoBuilder);
- FxyProtos.BasicInfo defaultInstance = FxyProtos.BasicInfo.getDefaultInstance();
- FxyProtos.BasicInfo mergedProto = defaultInstance.toBuilder()
- .mergeFrom(basicInfoBuilder.build())
- .build();
- String completeJson = JsonFormat.printer()
- .includingDefaultValueFields()
- .print(mergedProto);
- ObjectMapper objectMapper = new ObjectMapper();
- Map<String, Object> data = objectMapper.readValue(completeJson, new TypeReference<Map<String, Object>>() {});
- data.replaceAll((k, v) -> v.equals("") ? "-" : v);
- if (addtionalMap != null) {
- data.putAll(addtionalMap);
- }
- fillBasicDefaultValues(data);
- try {
- // 渲染文档
- String resultPath = this.renderDocx(data, templateFileContent, builder, relationId);
- log.info("渲染附件模块成功,文件路径: {}", resultPath);
- return resultPath;
- } catch (Exception e) {
- log.error("渲染附件模块失败,relationId: {}", relationId, e);
- throw new IOException("文档渲染失败", e);
- }
- }
- /**
- * 填充默认值,确保所有必要字段都存在
- */
- private void fillBasicDefaultValues(Map<String, Object> data) {
- Map<String, Object> basicInfoSummary = (Map<String, Object>) data.get("basicInfoSummary");
- basicInfoSummary.replaceAll((k, v) -> v.equals("") ? "-" : v);
- Map<String, Object> platformExt = (Map<String, Object>) data.get("platformExt");
- platformExt.replaceAll((k, v) -> v.equals("") ? "-" : v);
- Object certReceived = data.get("certReceived");
- if (certReceived.equals("-")) {
- data.put("certReceived", "否");
- }
- Object bankLicense = data.get("bankLicense");
- if (bankLicense.equals("-")) {
- data.put("bankLicense", "否");
- }
- }
- // public String renderDocx(FinancialInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
- //
- // // 不需要定制展示逻辑的时候,使用protobuf的转json方法
- // String jsonStr = JsonFormat.printer().print(info);
- //
- // // 注: 报告模板的模板变量按照json序列化的结果命名
- // // 注: 目前的实现假设:一个session对应一个cwd目录
- // ConfigureBuilder builder = Configure.builder();
- // builder.bind("indicators", new LoopRowIncludeStatisticsTableRenderPolicy("values"));
- // builder.bind("financialDataSeq", new LoopColumnStaticTableRenderPolicy("[", "]", false, true, 2));
- // builder.bind("financialCheckDetails", new LoopRowCutAndMergeFirstColTableRenderPolicy());
- // // 注意使用了SpringEL之后,每个模板变量都要设置值,不然会报错
- // builder.useSpringEL();
- //
- // this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
- // Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
- // return this.docxResultPath;
- // }
- /**
- * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
- */
- public class LoopColumnStaticTableRenderPolicy implements RenderPolicy {
- private String prefix;
- private String suffix;
- private boolean onSameLine;
- private boolean reverse;
- private int valColIndex;
- public LoopColumnStaticTableRenderPolicy() {
- this(false);
- }
- public LoopColumnStaticTableRenderPolicy(boolean onSameLine) {
- this("[", "]", onSameLine);
- }
- public LoopColumnStaticTableRenderPolicy(String prefix, String suffix) {
- this(prefix, suffix, false);
- }
- public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine) {
- this(prefix, suffix, onSameLine, false);
- }
- public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse) {
- this(prefix, suffix, onSameLine, false, 1);
- }
- public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse,
- int valRowIndex) {
- this.prefix = prefix;
- this.suffix = suffix;
- this.onSameLine = onSameLine;
- this.reverse = reverse;
- this.valColIndex = valRowIndex;
- }
- @Override
- public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
- RunTemplate runTemplate = (RunTemplate) eleTemplate;
- XWPFRun run = runTemplate.getRun();
- try {
- if (!TableTools.isInsideTable(run)) {
- throw new IllegalStateException(
- "The template tag " + runTemplate.getSource() + " must be inside a table");
- }
- XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
- XWPFTable table = tagCell.getTableRow().getTable();
- run.setText("", 0);
- int templateColIndex = getTemplateColIndex(tagCell);
- // 模版变量列总是写在左边
- int minIndex = templateColIndex;
- int maxIndex = table.getRows().get(valColIndex).getTableCells().size() - 1;
- int currIndex = reverse ? maxIndex : minIndex;
- int indexDelta = reverse ? -1 : 1;
- // 目前expression当作数据Map的key来对待,将来可以当作POI-TL变量一致化的处理
- Map<Integer, String> idx2Expression = new HashMap<>();
- for (int i = 0; i < table.getRows().size(); i++) {
- XWPFTableCell cell = table.getRows().get(i).getCell(templateColIndex);
- String text = cell.getText().trim();
- if (text.startsWith(prefix) && text.endsWith(suffix)) {
- idx2Expression.put(i, text.substring(1, text.length() - 1));
- cell.setText("");
- }
- }
- int rowSize = table.getRows().size();
- @SuppressWarnings("unchecked")
- List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
- for (Map<String, Object> realData : mpData) {
- for (int i = 0; i < rowSize; i++) {
- if (!idx2Expression.containsKey(i)) {
- continue;
- }
- XWPFTableRow row = table.getRow(i);
- XWPFTableCell valueCell = row.getCell(currIndex);
- String valStr = realData.getOrDefault(idx2Expression.get(i), "-").toString();
- valueCell.setText(valStr);
- }
- currIndex += indexDelta;
- }
- } catch (Exception e) {
- throw new RenderException("HackLoopTable for " + eleTemplate + "error: " + e.getMessage(), e);
- }
- }
- private int getTemplateColIndex(XWPFTableCell tagCell) {
- return onSameLine ? Util.getColIndexOfFirstRow(tagCell) : (Util.getColIndexOfFirstRow(tagCell) + 1);
- }
- }
- public class LoopRowCutAndMergeFirstColTableRenderPolicy extends LoopRowTableRenderPolicy {
- @Override
- public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
- RunTemplate runTemplate = (RunTemplate) eleTemplate;
- XWPFRun run = runTemplate.getRun();
- XWPFTable table = null;
- try {
- if (!TableTools.isInsideTable(run)) {
- throw new IllegalStateException(
- "The template tag " + runTemplate.getSource() + " must be inside a table");
- }
- // Reserve the first two rows
- XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
- table = tagCell.getTableRow().getTable();
- for (int i = table.getNumberOfRows() - 1; i > 1; i--) {
- table.removeRow(i);
- }
- } catch (Exception e) {
- throw new RenderException(
- "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
- }
- // in case data not sorted by rank
- @SuppressWarnings("unchecked")
- List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
- mpData.sort((a, b) -> Integer.valueOf(a.getOrDefault("rank", "0").toString())
- - Integer.valueOf(b.getOrDefault("rank", "0").toString()));
- super.render(eleTemplate, data, template);
- try {
- // merge the first column
- Util.mergeFirstNColSimple(table, 1, 0);
- } catch (Exception e) {
- throw new RenderException(
- "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
- }
- }
- }
- public class LoopRowIncludeStatisticsTableRenderPolicy extends LoopRowTableRenderPolicy {
- private String valueTag;
- public LoopRowIncludeStatisticsTableRenderPolicy(String valueTag) {
- this.valueTag = valueTag;
- }
- @Override
- public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
- @SuppressWarnings("unchecked")
- List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
- for (Map<String, Object> row : mpData) {
- @SuppressWarnings("unchecked")
- List<String> values = (List<String>) row.get(valueTag);
- row.put("avg", values.stream().mapToLong(Long::valueOf).average().orElse(Double.NaN));
- }
- super.render(eleTemplate, data, template);
- }
- }
- }
|