|
@@ -1,225 +1,225 @@
|
|
|
-package com.yaoyicloud.render;
|
|
|
-
|
|
|
-import java.io.IOException;
|
|
|
-import java.nio.file.Paths;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.UUID;
|
|
|
-
|
|
|
-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渲染器
|
|
|
- *
|
|
|
- */
|
|
|
-public final class FinancialInfoRender extends AbstractRender {
|
|
|
-
|
|
|
- public FinancialInfoRender(String cwd) {
|
|
|
- super(cwd);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Docx 渲染
|
|
|
- *
|
|
|
- * @param info 数据
|
|
|
- * @param templateFileContent 模板内容
|
|
|
- * @return 本地文件目录
|
|
|
- * @throws IOException
|
|
|
- */
|
|
|
- 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);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+//package com.yaoyicloud.render;
|
|
|
+//
|
|
|
+//import java.io.IOException;
|
|
|
+//import java.nio.file.Paths;
|
|
|
+//import java.util.HashMap;
|
|
|
+//import java.util.List;
|
|
|
+//import java.util.Map;
|
|
|
+//import java.util.UUID;
|
|
|
+//
|
|
|
+//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渲染器
|
|
|
+// *
|
|
|
+// */
|
|
|
+//public final class FinancialInfoRender extends AbstractRender {
|
|
|
+//
|
|
|
+// public FinancialInfoRender(String cwd) {
|
|
|
+// super(cwd);
|
|
|
+// }
|
|
|
+//
|
|
|
+// /**
|
|
|
+// * Docx 渲染
|
|
|
+// *
|
|
|
+// * @param info 数据
|
|
|
+// * @param templateFileContent 模板内容
|
|
|
+// * @return 本地文件目录
|
|
|
+// * @throws IOException
|
|
|
+// */
|
|
|
+//// 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);
|
|
|
+// }
|
|
|
+// }
|
|
|
+//}
|