添加模板生成工具类
This commit is contained in:
parent
64d4900aa3
commit
0786fb99a8
|
|
@ -0,0 +1,367 @@
|
||||||
|
package org.jeecg.common.util;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.jeecg.modules.entity.vo.NuclideAnalysisInfo;
|
||||||
|
import org.jeecg.modules.entity.vo.NuclideRatioResult;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class DistributionAnalysisToolkit {
|
||||||
|
/**
|
||||||
|
* 区间统计结果
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class IntervalStat {
|
||||||
|
private final String interval;
|
||||||
|
private final int count;
|
||||||
|
private final List<Double> values;
|
||||||
|
private final Map<String, Integer> levelDistribution;
|
||||||
|
|
||||||
|
public IntervalStat(String interval, int count, List<Double> values, Map<String, Integer> levelDistribution) {
|
||||||
|
this.interval = interval;
|
||||||
|
this.count = count;
|
||||||
|
this.values = values;
|
||||||
|
this.levelDistribution = levelDistribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntervalStat(String interval, List<NuclideAnalysisInfo> nuclideData) {
|
||||||
|
values = new ArrayList<>();
|
||||||
|
levelDistribution = new HashMap<>();
|
||||||
|
this.interval = interval;
|
||||||
|
this.count = nuclideData.size();
|
||||||
|
for (NuclideAnalysisInfo nuclideAnalysis : nuclideData) {
|
||||||
|
values.add(nuclideAnalysis.getConc());
|
||||||
|
levelDistribution.merge(nuclideAnalysis.getCategory(), 1, Integer::sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getInterval() {
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Double> getValues() {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KDE曲线点
|
||||||
|
*/
|
||||||
|
public static class KDEPoint {
|
||||||
|
private final double x;
|
||||||
|
private final double density;
|
||||||
|
|
||||||
|
public KDEPoint(double x, double density) {
|
||||||
|
this.x = x;
|
||||||
|
this.density = density;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDensity() {
|
||||||
|
return density;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CDF曲线点
|
||||||
|
*/
|
||||||
|
public static class CDFPoint {
|
||||||
|
private final double value;
|
||||||
|
private final double cumulativeProb;
|
||||||
|
|
||||||
|
public CDFPoint(double value, double cumulativeProb) {
|
||||||
|
this.value = value;
|
||||||
|
this.cumulativeProb = cumulativeProb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCumulativeProb() {
|
||||||
|
return cumulativeProb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据区间统计
|
||||||
|
*
|
||||||
|
* @param data 原始数据
|
||||||
|
* @param start 起始值
|
||||||
|
* @param step 区间宽度
|
||||||
|
* @return 区间统计结果列表
|
||||||
|
*/
|
||||||
|
public static List<IntervalStat> calculateIntervalStats(List<NuclideAnalysisInfo> data, double start, double step) {
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("数据不能为空");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 计算结束边界
|
||||||
|
double max = data.stream()
|
||||||
|
.mapToDouble(NuclideAnalysisInfo::getConc)
|
||||||
|
.max().orElse(0);
|
||||||
|
double end = Math.ceil(max / step) * step + step;
|
||||||
|
|
||||||
|
// 初始化区间映射
|
||||||
|
Map<String, List<NuclideAnalysisInfo>> intervalMap = new TreeMap<>();
|
||||||
|
for (double lower = start; lower < end; lower += step) {
|
||||||
|
double upper = lower + step;
|
||||||
|
String key = String.format("[%.1f, %.1f)", lower, upper);
|
||||||
|
intervalMap.put(key, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分配数据到区间
|
||||||
|
for (NuclideAnalysisInfo nuclide : data) {
|
||||||
|
try {
|
||||||
|
double value = nuclide.getConc();
|
||||||
|
double lower = Math.floor(value / step) * step;
|
||||||
|
String key = String.format("[%.1f, %.1f)", lower, lower + step);
|
||||||
|
intervalMap.get(key).add(nuclide);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为统计结果对象
|
||||||
|
List<IntervalStat> stats = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, List<NuclideAnalysisInfo>> entry : intervalMap.entrySet()) {
|
||||||
|
stats.add(new IntervalStat(entry.getKey(), entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<IntervalStat> calculateIntervalStats(List<Double> data, double start, double step,String ratio) {
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("数据不能为空");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 计算结束边界
|
||||||
|
double max =data.stream().mapToDouble(Double::doubleValue).max().orElse(0) ;
|
||||||
|
double end = Math.ceil(max / step) * step + step;
|
||||||
|
|
||||||
|
// 初始化区间映射
|
||||||
|
Map<String, List<Double>> intervalMap = new TreeMap<>();
|
||||||
|
for (double lower = start; lower < end; lower += step) {
|
||||||
|
double upper = lower + step;
|
||||||
|
String key = String.format("[%.1f, %.1f)", lower, upper);
|
||||||
|
intervalMap.put(key, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分配数据到区间
|
||||||
|
for (double value : data) {
|
||||||
|
try {
|
||||||
|
double lower = Math.floor(value / step) * step;
|
||||||
|
String key = String.format("[%.1f, %.1f)", lower, lower + step);
|
||||||
|
intervalMap.get(key).add(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为统计结果对象
|
||||||
|
List<IntervalStat> stats = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, List<Double>> entry : intervalMap.entrySet()) {
|
||||||
|
stats.add(new IntervalStat(entry.getKey(), entry.getValue().size(),entry.getValue(),new HashMap<>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算95%累积线
|
||||||
|
*
|
||||||
|
* @param data 原始数据
|
||||||
|
* @return 95%累积线值
|
||||||
|
*/
|
||||||
|
public static double calculate95thPercentile(List<Double> data) {
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("数据不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序数据
|
||||||
|
List<Double> sortedData = new ArrayList<>(data);
|
||||||
|
Collections.sort(sortedData);
|
||||||
|
|
||||||
|
int n = sortedData.size();
|
||||||
|
double position = 0.95 * (n - 1);
|
||||||
|
|
||||||
|
int lowerIndex = (int) Math.floor(position);
|
||||||
|
int upperIndex = (int) Math.ceil(position);
|
||||||
|
|
||||||
|
if (lowerIndex == upperIndex) {
|
||||||
|
return sortedData.get(lowerIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 线性插值
|
||||||
|
double lowerValue = sortedData.get(lowerIndex);
|
||||||
|
double upperValue = sortedData.get(upperIndex);
|
||||||
|
double fraction = position - lowerIndex;
|
||||||
|
|
||||||
|
return lowerValue + fraction * (upperValue - lowerValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算累积分布函数(CDF)
|
||||||
|
*
|
||||||
|
* @param data 原始数据
|
||||||
|
* @return CDF点列表
|
||||||
|
*/
|
||||||
|
public static List<CDFPoint> calculateCDF(List<Double> data) {
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("数据不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序数据
|
||||||
|
List<Double> sortedData = new ArrayList<>(data);
|
||||||
|
Collections.sort(sortedData);
|
||||||
|
|
||||||
|
// 计算累积分布
|
||||||
|
List<CDFPoint> cdfPoints = new ArrayList<>();
|
||||||
|
int n = sortedData.size();
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
double value = sortedData.get(i);
|
||||||
|
double cumulativeProbability = (i + 1.0) / n;
|
||||||
|
cdfPoints.add(new CDFPoint(value, cumulativeProbability));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cdfPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核函数接口
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface KernelFunction {
|
||||||
|
double apply(double u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常用核函数实现
|
||||||
|
public static final KernelFunction GAUSSIAN_KERNEL = u -> Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);
|
||||||
|
public static final KernelFunction EPANECHNIKOV_KERNEL = u -> (Math.abs(u) <= 1) ? 0.75 * (1 - u * u) : 0;
|
||||||
|
public static final KernelFunction TRIANGULAR_KERNEL = u -> (Math.abs(u) <= 1) ? 1 - Math.abs(u) : 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用Silverman规则计算最佳带宽
|
||||||
|
*/
|
||||||
|
public static double calculateBandwidthSilverman(List<Double> data) {
|
||||||
|
int n = data.size();
|
||||||
|
if (n <= 1) return 1.0;
|
||||||
|
|
||||||
|
// 计算标准差
|
||||||
|
double mean = data.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
|
||||||
|
double variance = data.stream()
|
||||||
|
.mapToDouble(x -> Math.pow(x - mean, 2))
|
||||||
|
.average()
|
||||||
|
.orElse(0.0);
|
||||||
|
double sigma = Math.sqrt(variance);
|
||||||
|
|
||||||
|
// 计算四分位距(IQR)
|
||||||
|
List<Double> sortedData = new ArrayList<>(data);
|
||||||
|
Collections.sort(sortedData);
|
||||||
|
|
||||||
|
double q1 = sortedData.get((int) Math.ceil(0.25 * n - 1));
|
||||||
|
double q3 = sortedData.get((int) Math.ceil(0.75 * n - 1));
|
||||||
|
double iqr = q3 - q1;
|
||||||
|
|
||||||
|
// Silverman规则
|
||||||
|
return 0.9 * Math.min(sigma, iqr / 1.34) * Math.pow(n, -0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核密度估计
|
||||||
|
*/
|
||||||
|
public static List<KDEPoint> kernelDensityEstimate(
|
||||||
|
List<Double> data, KernelFunction kernel, double bandwidth) {
|
||||||
|
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("数据不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算数据范围
|
||||||
|
double min = Collections.min(data);
|
||||||
|
double max = Collections.max(data);
|
||||||
|
double range = max - min;
|
||||||
|
double minX = min - 0.1 * range;
|
||||||
|
double maxX = max + 0.1 * range;
|
||||||
|
|
||||||
|
// 生成评估点
|
||||||
|
int numPoints = 200;
|
||||||
|
double step = (maxX - minX) / (numPoints - 1);
|
||||||
|
|
||||||
|
List<KDEPoint> kdePoints = new ArrayList<>();
|
||||||
|
int n = data.size();
|
||||||
|
|
||||||
|
for (int i = 0; i < numPoints; i++) {
|
||||||
|
double x = minX + i * step;
|
||||||
|
double sum = 0.0;
|
||||||
|
|
||||||
|
for (double value : data) {
|
||||||
|
double u = (x - value) / bandwidth;
|
||||||
|
sum += kernel.apply(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
double density = sum / (n * bandwidth);
|
||||||
|
kdePoints.add(new KDEPoint(x, density));
|
||||||
|
}
|
||||||
|
|
||||||
|
return kdePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动KDE计算(自动选择带宽)
|
||||||
|
*/
|
||||||
|
public static List<KDEPoint> autoKDE(List<Double> data, KernelFunction kernel) {
|
||||||
|
double bandwidth = calculateBandwidthSilverman(data);
|
||||||
|
return kernelDensityEstimate(data, kernel, bandwidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//获取浓度值集合
|
||||||
|
public static List<Double> convertConcToDoubleList(List<NuclideAnalysisInfo> nuclideList) {
|
||||||
|
return nuclideList.stream() // 创建流
|
||||||
|
.map(NuclideAnalysisInfo::getConc) // 提取conc值
|
||||||
|
.collect(Collectors.toList()); // 收集为List<Double>
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Double> convertRatioToDoubleList(List<NuclideRatioResult> nuclideList) {
|
||||||
|
return nuclideList.stream() // 创建流
|
||||||
|
.map(NuclideRatioResult::getRatio) // Ratio
|
||||||
|
.collect(Collectors.toList()); // 收集为List<Double>
|
||||||
|
}
|
||||||
|
|
||||||
|
//累积和
|
||||||
|
public static List<Double> cumulativeSum(List<Double> data) {
|
||||||
|
List<Double> result = new ArrayList<>();
|
||||||
|
double sum = 0.0;
|
||||||
|
|
||||||
|
for (double value : data) {
|
||||||
|
sum += value;
|
||||||
|
result.add(sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
package org.jeecg.modules.service.impl;
|
||||||
|
|
||||||
|
import org.jeecg.modules.service.IGenerateHtmlReport;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.thymeleaf.TemplateEngine;
|
||||||
|
import org.thymeleaf.context.Context;
|
||||||
|
import org.thymeleaf.templatemode.TemplateMode;
|
||||||
|
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HtmlReportGenerator implements IGenerateHtmlReport {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TemplateEngine templateEngine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法,初始化Thymeleaf模板引擎
|
||||||
|
* 默认从resources/templates/目录加载HTML模板
|
||||||
|
*/
|
||||||
|
public void generatorInit() {
|
||||||
|
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
|
||||||
|
templateResolver.setPrefix("/templates/");
|
||||||
|
templateResolver.setSuffix(".html");
|
||||||
|
templateResolver.setTemplateMode(TemplateMode.HTML);
|
||||||
|
templateResolver.setCharacterEncoding("UTF-8");
|
||||||
|
this.templateEngine.setTemplateResolver(templateResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成基础HTML报告
|
||||||
|
* @param data 报告数据,键值对形式
|
||||||
|
* @return 生成的HTML字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateBasicHtmlReport(Map<String, Object> data) {
|
||||||
|
Context context = new Context();
|
||||||
|
context.setVariables(data);
|
||||||
|
return templateEngine.process("report", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成带自定义样式的HTML报告
|
||||||
|
* @param data 报告数据,键值对形式
|
||||||
|
* @param style 自定义CSS样式字符串
|
||||||
|
* @return 生成的HTML字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateStyledHtmlReport(Map<String, Object> data, String style) {
|
||||||
|
Context context = new Context();
|
||||||
|
context.setVariables(data);
|
||||||
|
context.setVariable("customStyle", style);
|
||||||
|
return templateEngine.process("styled-report", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成HTML报告并保存到文件
|
||||||
|
* @param data 报告数据,键值对形式
|
||||||
|
* @param filePath 文件保存路径(包含文件名)
|
||||||
|
* @return 保存成功返回true,失败返回false
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean generateHtmlReportToFile(Map<String, Object> data, String filePath) {
|
||||||
|
try {
|
||||||
|
String html = generateBasicHtmlReport(data);
|
||||||
|
Files.write(Paths.get(filePath), html.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用指定模板生成HTML报告
|
||||||
|
* @param data 报告数据,键值对形式
|
||||||
|
* @param templatePath 模板文件路径(相对于classpath)
|
||||||
|
* @return 生成的HTML字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateHtmlReportWithTemplate(Map<String, Object> data, String templatePath) {
|
||||||
|
ClassLoaderTemplateResolver tempResolver = new ClassLoaderTemplateResolver();
|
||||||
|
tempResolver.setPrefix("/");
|
||||||
|
tempResolver.setSuffix("");
|
||||||
|
tempResolver.setTemplateMode(TemplateMode.HTML);
|
||||||
|
|
||||||
|
TemplateEngine tempEngine = new TemplateEngine();
|
||||||
|
tempEngine.setTemplateResolver(tempResolver);
|
||||||
|
|
||||||
|
Context context = new Context();
|
||||||
|
context.setVariables(data);
|
||||||
|
|
||||||
|
return tempEngine.process(templatePath, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成高级HTML报告(可选包含图表和交互功能)
|
||||||
|
* @param data 报告数据,键值对形式
|
||||||
|
* @param includeCharts 是否包含图表(需要data中包含chartData)
|
||||||
|
* @param interactive 是否添加交互功能
|
||||||
|
* @return 生成的HTML字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateAdvancedHtmlReport(Map<String, Object> data, boolean includeCharts, boolean interactive) {
|
||||||
|
Context context = new Context();
|
||||||
|
context.setVariables(data);
|
||||||
|
context.setVariable("includeCharts", includeCharts);
|
||||||
|
context.setVariable("interactive", interactive);
|
||||||
|
return templateEngine.process("advanced-report", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成多页HTML报告
|
||||||
|
* @param pagesData 多页数据,Key为页面名称,Value为页面数据
|
||||||
|
* @return 生成的HTML字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateMultiPageHtmlReport(Map<String, Map<String, Object>> pagesData) {
|
||||||
|
Context context = new Context();
|
||||||
|
context.setVariable("pages", pagesData);
|
||||||
|
return templateEngine.process("multi-page-report", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成HTML报告字节数组(便于网络传输)
|
||||||
|
* @param data 报告数据,键值对形式
|
||||||
|
* @return UTF-8编码的HTML字节数组
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public byte[] generateHtmlReportAsBytes(Map<String, Object> data) {
|
||||||
|
return generateBasicHtmlReport(data).getBytes(StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成分页HTML报告
|
||||||
|
* @param data 数据列表,每个元素为一组键值对
|
||||||
|
* @param itemsPerPage 每页显示的项目数
|
||||||
|
* @return 生成的HTML字符串(包含分页导航)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generatePaginatedHtmlReport(List<Map<String, Object>> data, int itemsPerPage) {
|
||||||
|
Context context = new Context();
|
||||||
|
int totalPages = (int) Math.ceil((double) data.size() / itemsPerPage);
|
||||||
|
context.setVariable("totalPages", totalPages);
|
||||||
|
context.setVariable("itemsPerPage", itemsPerPage);
|
||||||
|
context.setVariable("data", data);
|
||||||
|
return templateEngine.process("paginated-report", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成响应式HTML报告(适配移动设备)
|
||||||
|
* @param data 报告数据,键值对形式
|
||||||
|
* @return 生成的HTML字符串(包含响应式CSS)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateResponsiveHtmlReport(Map<String, Object> data) {
|
||||||
|
Context context = new Context();
|
||||||
|
context.setVariables(data);
|
||||||
|
return templateEngine.process("responsive-report", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成Base64编码的HTML报告(便于嵌入其他系统)
|
||||||
|
* @param data 报告数据,键值对形式
|
||||||
|
* @return Base64编码的HTML字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateHtmlReportAsBase64(Map<String, Object> data) {
|
||||||
|
return Base64.getEncoder().encodeToString(generateBasicHtmlReport(data).getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user