Browse Source

fix:修复首页趋势图数据缺失bug
fix:修改传感器数据导出接口,调整格式,并插入数据折线图
feat:新增传感器监测数据温湿度co2最大、最小、平均值

黄渊昊 1 month ago
parent
commit
2de7d9dd3e

+ 18 - 0
pom.xml

@@ -383,6 +383,24 @@
                 <artifactId>mysql-connector-j</artifactId>
                 <version>8.3.0</version>
             </dependency>
+            <dependency>
+                <groupId>com.itextpdf</groupId>
+                <artifactId>itext-asian</artifactId>
+                <version>5.2.0</version>
+            </dependency>
+
+<!--
+            <dependency>
+                <groupId>cn.idev.excel</groupId>
+                <artifactId>fastexcel</artifactId>
+                <version>1.0.0</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.apache.poi</groupId>
+                        <artifactId>poi</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>-->
 
             <!-- postgresql -->
             <!--<dependency>

+ 38 - 0
snowy-plugin/snowy-plugin-coldchain/pom.xml

@@ -102,5 +102,43 @@
             <artifactId>spring-integration-mqtt</artifactId>
             <version>5.5.5</version>
         </dependency>
+
+        <!--<dependency>
+            <groupId>cn.idev.excel</groupId>
+            <artifactId>fastexcel</artifactId>
+            <version>1.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>-->
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itext7-core</artifactId>
+            <version>7.2.0</version>
+            <type>pom</type>
+        </dependency>
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itextpdf</artifactId>
+            <version>5.5.6</version>
+        </dependency>
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itext-asian</artifactId>
+            <version>5.2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jfree</groupId>
+            <artifactId>jfreechart</artifactId>
+            <version>1.5.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jfree</groupId>
+            <artifactId>jcommon</artifactId>
+            <version>1.0.21</version>
+        </dependency>
     </dependencies>
 </project>

+ 360 - 13
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/service/AppDeviceService.java

@@ -19,9 +19,45 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.github.jfcloud.influxdb.flux.AggregationWindow;
 import com.google.common.collect.Lists;
+import com.itextpdf.awt.DefaultFontMapper;
+import com.itextpdf.io.image.ImageData;
+import com.itextpdf.io.image.ImageDataFactory;
+import com.itextpdf.kernel.colors.ColorConstants;
+import com.itextpdf.kernel.font.PdfFont;
+import com.itextpdf.kernel.font.PdfFontFactory;
+import com.itextpdf.kernel.geom.PageSize;
+import com.itextpdf.kernel.geom.Rectangle;
+import com.itextpdf.kernel.pdf.PdfDocument;
+import com.itextpdf.kernel.pdf.PdfWriter;
+import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
+import com.itextpdf.layout.Canvas;
+import com.itextpdf.layout.Document;
+import com.itextpdf.layout.element.*;
+import com.itextpdf.layout.properties.HorizontalAlignment;
+import com.itextpdf.layout.properties.TextAlignment;
+import com.itextpdf.text.BadElementException;
+import com.itextpdf.text.pdf.PdfContentByte;
+import com.itextpdf.text.pdf.PdfTemplate;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.StandardChartTheme;
+import org.jfree.chart.axis.CategoryAxis;
+import org.jfree.chart.axis.CategoryLabelPositions;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.CategoryPlot;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.category.LineAndShapeRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.category.CategoryDataset;
+import org.jfree.data.category.DefaultCategoryDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeriesCollection;
 import org.springframework.stereotype.Service;
 import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
@@ -39,11 +75,14 @@ import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRe
 import vip.xiaonuo.common.enums.CommonDeleteFlagEnum;
 import vip.xiaonuo.common.exception.CommonException;
 
-import java.io.IOException;
-import java.io.OutputStream;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -502,7 +541,7 @@ public class AppDeviceService {
         return monitorTargetRegionService.updateById(monitorTargetRegion);
     }
 
-    public void export(HttpServletResponse response, AppTrendParam trendParam) {
+    /*public void export(HttpServletResponse response, AppTrendParam trendParam) {
         StopWatch stopWatch = new StopWatch("传感器数据导出");
 
         stopWatch.start("查询设备信息");
@@ -554,22 +593,330 @@ public class AppDeviceService {
                 exportParamList.add(exportParamOut);
             }
         }
-        String fileName = "传感器数据";
-        String sheetName = "传感器数据";
-        response.setContentType("application/vnd.ms-excel");
+
+        String fileName = "传感器数据.pdf";
+        response.setContentType("application/pdf");
         response.setCharacterEncoding("utf-8");
+        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" +
+                URLEncoder.encode(fileName, StandardCharsets.UTF_8));
 
-        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + URLEncoder.encode(fileName, StandardCharsets.UTF_8) + ".xls");
         try (OutputStream outputStream = response.getOutputStream()) {
-            EasyExcel.write(outputStream, ExportParam.class)
-                    .excelType(ExcelTypeEnum.XLS)
-                    .sheet(sheetName)
-                    .doWrite(exportParamList);
+            // 1. 初始化PDF文档
+            PdfWriter writer = new PdfWriter(outputStream);
+            PdfDocument pdfDoc = new PdfDocument(writer);
+            Document document = new Document(pdfDoc, PageSize.A4.rotate());
+
+            // 2. 创建表格(根据字段数量设置列数)
+            Table table = new Table(ExportParam.class.getDeclaredFields ().length); // 时间、区域、设备编码、传感器路数、数据类型、数据值
+            table.useAllAvailableWidth();
+
+            // 3. 添加表格标题
+            Paragraph title = new Paragraph("传感器数据报表")
+                    .setTextAlignment(TextAlignment.CENTER)
+                    .setFontSize(16);
+            document.add(title);
+
+            // 4. 添加表头
+            String[] headers = {"时间", "区域名称", "设备编码", "传感器路数", "数据类型", "数据值"};
+            for (String header : headers) {
+                table.addHeaderCell(new Cell().add(new Paragraph(header))
+                        .setFont(PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H"))
+                        .setBackgroundColor(ColorConstants.LIGHT_GRAY)
+                        .setTextAlignment(TextAlignment.CENTER));
+            }
+
+            // 5. 填充表格数据
+            for (ExportParam param : exportParamList) {
+                addPdfTableRow(table, param.getTime());
+                addPdfTableRow(table, param.getRegionName());
+                addPdfTableRow(table, param.getDeviceCode());
+                addPdfTableRow(table, String.valueOf(param.getSensorRoad()));
+                addPdfTableRow(table, param.getDataType());
+                addPdfTableRow(table, String.valueOf(param.getData()));
+            }
+
+            // 6. 将表格加入文档
+            document.add(table);
+
+            // 7. 关闭资源
+            document.close();
+            log.info("PDF生成完成");
+
         } catch (IOException e) {
-            throw new RuntimeException(e);
+            throw new RuntimeException("PDF生成失败", e);
+        } finally {
+            stopWatch.stop();
+            log.info("PDF导出耗时:{}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
+        }
+    }
+
+    // 辅助方法:添加表格单元格
+    private void addPdfTableRow(Table table, String content) throws IOException {
+        table.addCell(new Cell().add(new Paragraph(content))
+                .setFont(PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H"))
+                .setTextAlignment(TextAlignment.CENTER));
+    }*/
+    public void export(HttpServletResponse response, AppTrendParam trendParam) {
+        StopWatch stopWatch = new StopWatch("传感器数据导出");
+
+        stopWatch.start("查询设备信息");
+        MonitorTargetRegion monitorTargetRegion = monitorTargetRegionService.findOneByDeviceCodeAndSensorNo(trendParam.getSensorCode(), trendParam.getRoads());
+        stopWatch.stop();
+        if (Objects.isNull(monitorTargetRegion)) {
+            throw new CommonException("未找到设备编号 [{}] 和传感器编号 [{}] 对应的监控目标区域。", trendParam.getSensorCode(), trendParam.getRoads());
+        }
+
+        stopWatch.start("查询传感器数据");
+        AppDeviceQueryParams appDeviceQueryParams = BeanUtil.copyProperties(trendParam, AppDeviceQueryParams.class);
+        appDeviceQueryParams.setSensorRoute(trendParam.getRoads());
+        SensorEchartDataResult sensorEchartDataResult = queryDataByDeviceIdAndRoads(appDeviceQueryParams);
+        stopWatch.stop();
+
+        // 计算统计信息
+        stopWatch.start("计算统计信息");
+        List<Float> temperatureData = sensorEchartDataResult.getTemperature().getY();
+        float maxTemp = Collections.max(temperatureData);
+        float minTemp = Collections.min(temperatureData);
+        float avgTemp = (float) temperatureData.stream().mapToDouble(f -> f).average().orElse(0.0);
+        stopWatch.stop();
+
+        stopWatch.start("导出数据");
+        List<ExportParam> exportParamList = new ArrayList<>();
+        ExportParam exportParam = new ExportParam();
+        exportParam.setRegionName(monitorTargetRegion.getName());
+        exportParam.setDeviceCode(trendParam.getSensorCode());
+        exportParam.setSensorRoad(trendParam.getRoads());
+        String sensorType = monitorTargetRegion.getSensorType().toUpperCase();
+
+        // 仅处理温度数据('W'类型)
+        if (sensorType.contains("W")) {
+            exportParam.setDataType("温度");
+            LinkedHashSet<String> x = sensorEchartDataResult.getTemperature().getX();
+            List<Float> y = sensorEchartDataResult.getTemperature().getY();
+            int i = 0;
+            for (String time : x) {
+                ExportParam exportParamOut = BeanUtil.copyProperties(exportParam, ExportParam.class);
+                exportParamOut.setTime(time);
+                exportParamOut.setData(y.get(i));
+                exportParamList.add(exportParamOut);
+                i++;
+            }
+        }
+
+        String fileName = "传感器数据报告.pdf";
+        response.setContentType("application/pdf");
+        response.setCharacterEncoding("utf-8");
+        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" +
+                URLEncoder.encode(fileName, StandardCharsets.UTF_8));
+
+        try (OutputStream outputStream = response.getOutputStream()) {
+            PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
+            PdfWriter writer = new PdfWriter(outputStream);
+            PdfDocument pdfDoc = new PdfDocument(writer);
+            Document document = new Document(pdfDoc, PageSize.A4);
+            document.setMargins(15, 20, 10, 20);
+
+            // 报告标题
+            Paragraph title = new Paragraph("传感器数据报告")
+                    .setFont(chineseFont)
+                    .setTextAlignment(TextAlignment.CENTER)
+                    .setFontSize(20)
+                    .setBold()
+                    .setMarginBottom(10);
+            document.add(title);
+
+            // 报告下载时间
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+            Paragraph downloadTime = new Paragraph("报告下载时间: " + sdf.format(new Date()))
+                    .setFont(chineseFont)
+                    .setFontSize(10)
+                    .setTextAlignment(TextAlignment.LEFT);
+            document.add(downloadTime);
+
+            Paragraph deviceInfo = new Paragraph("设备信息")
+                    .setFont(chineseFont)
+                    .setTextAlignment(TextAlignment.CENTER)
+                    .setFontSize(15)
+                    .setBold()
+                    .setMarginBottom(2);
+            document.add(deviceInfo);
+
+            // 设备信息表格
+            Table deviceTable = new Table(new float[]{2, 3, 2, 3}).useAllAvailableWidth();
+            addDeviceTableRow(deviceTable, "设备名称:", monitorTargetRegion.getName().split("-")[0], "温度上限:", monitorTargetRegion.getTemperatureUp() + "°C");
+            addDeviceTableRow(deviceTable, "点位名称:", monitorTargetRegion.getName(), "温度下限:", monitorTargetRegion.getTemperatureDown() + "");
+            addDeviceTableRow(deviceTable, "冷链设备编号:", trendParam.getSensorCode(), "传感器路数:", trendParam.getRoads() + "路");
+            deviceTable.setMarginBottom(10);
+            document.add(deviceTable);
+
+            Paragraph data = new Paragraph("记录信息")
+                    .setFont(chineseFont)
+                    .setTextAlignment(TextAlignment.CENTER)
+                    .setFontSize(15)
+                    .setBold()
+                    .setMarginBottom(2);
+            document.add(data);
+
+            // 记录信息表格
+            Table recordTable = new Table(new float[]{2, 3, 2, 3}).useAllAvailableWidth();
+            addRecordTableRow(recordTable, "开始时间:", trendParam.getStartTime(), "最高温度:", String.format("%.1f°C", maxTemp));
+            addRecordTableRow(recordTable, "结束时间:", trendParam.getEndTime(), "最低温度:", String.format("%.1f°C", minTemp));
+            addRecordTableRow(recordTable, "", "", "平均温度:", String.format("%.1f°C", avgTemp));
+            recordTable.setMarginBottom(10);
+            document.add(recordTable);
+
+            Paragraph curve = new Paragraph("温度曲线")
+                    .setFont(chineseFont)
+                    .setTextAlignment(TextAlignment.CENTER)
+                    .setFontSize(15)
+                    .setBold();
+            document.add(curve);
+
+            try {
+                // 1. 创建适用于String类型X轴的数据集)
+                DefaultCategoryDataset dataset = new DefaultCategoryDataset();
+                List<Float> yValues = sensorEchartDataResult.getTemperature().getY();
+                List<String> xLabels = new ArrayList<>(sensorEchartDataResult.getTemperature().getX());
+
+                // 填充分类数据集(行键,数值,列键)
+                for (int i = 0; i < xLabels.size(); i++) {
+                    dataset.addValue(yValues.get(i), "温度", xLabels.get(i)); // 列键使用时间字符串
+                }
+
+                // 2. 创建分类折线图(替换核心方法)[1,3](@ref)
+                JFreeChart chart = ChartFactory.createLineChart(
+                        null, // 隐藏标题
+                        "时间",  // X轴标签(原"时间序列"改为更直观的"时间")
+                        "温度(℃)",
+                        dataset,
+                        PlotOrientation.VERTICAL,
+                        false, // 不显示图例
+                        false,
+                        false
+                );
+
+                // 3. 样式优化(需调整为CategoryPlot)[5,7](@ref)
+                CategoryPlot plot = chart.getCategoryPlot();
+                plot.setBackgroundPaint(Color.WHITE);
+
+                // 设置X/Y轴字体(解决中文显示问题)[3,8](@ref)
+                Font songFont = new Font("宋体", Font.PLAIN, 11);
+                CategoryAxis domainAxis = plot.getDomainAxis();
+                domainAxis.setLabelFont(songFont);
+                domainAxis.setTickLabelFont(songFont);
+                domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45); // 标签倾斜45度防重叠
+
+                ValueAxis rangeAxis = plot.getRangeAxis();
+                rangeAxis.setLabelFont(songFont);
+                double minValue;
+                double maxValue;
+                if (Collections.min(yValues) > 0) {
+                    minValue = Collections.min(yValues) * 0.95; // 下边界留5%余量
+                } else {
+                    minValue = Collections.min(yValues) * 1.05;
+                }
+                if (Collections.max(yValues) < 0) {
+                    maxValue = Collections.max(yValues) * 0.95; // 下边界留5%余量
+                } else {
+                    maxValue = Collections.max(yValues) * 1.05; // 上边界留5%余量
+                }
+                rangeAxis.setRange(minValue, maxValue);
+
+                // 设置刻度密度(根据数据范围动态调整)
+                if ((maxValue - minValue) < 50) { // 温差较小时缩小刻度间隔
+                    rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
+                }
+
+                // 4. 自定义折线样式(使用CategoryItemRenderer)[5](@ref)
+                LineAndShapeRenderer renderer = new LineAndShapeRenderer();
+                renderer.setSeriesPaint(0, new Color(79, 129, 189));
+                renderer.setSeriesStroke(0, new BasicStroke(1.8f));
+                renderer.setSeriesShapesVisible(0, true); // 显示数据点
+                plot.setRenderer(renderer);
+
+
+                // 5. 处理时间标签过长问题(动态调整间隔)[6,7](@ref)
+                if(xLabels.size() > 20) { // 当数据点超过20个时启用标签间隔
+                    domainAxis.setTickLabelsVisible(true);
+                    domainAxis.setTickMarksVisible(true);
+//                    domainAxis.setMaximumCategoryLabelLines(3); // 允许换行显示
+                    domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90); // 垂直显示
+                }
+
+                // 6. 生成图像并插入PDF(保持原有逻辑)
+                BufferedImage chartImage = chart.createBufferedImage(600, 450); // 加宽画布
+                ImageData imageData = ImageDataFactory.create(chartImage, null);
+                com.itextpdf.layout.element.Image pdfImage = new com.itextpdf.layout.element.Image(imageData)
+                        .setHorizontalAlignment(HorizontalAlignment.CENTER);
+                document.add(pdfImage);
+
+            } catch (Exception e) {
+                log.error("折线图生成失败: {}", e.getMessage());
+                throw new RuntimeException("图表生成异常", e);
+            }
+
+            document.add(new AreaBreak());
+
+            // 温度数据表格
+            Paragraph dataTitle = new Paragraph("温度数据")
+                    .setFont(chineseFont)
+                    .setTextAlignment(TextAlignment.CENTER)
+                    .setFontSize(15)
+                    .setBold()
+                    .setMarginBottom(2);
+            document.add(dataTitle);
+
+            Table dataTable = new Table(2).useAllAvailableWidth();
+            dataTable.addHeaderCell(createCell("时间", true));
+            dataTable.addHeaderCell(createCell("°C", true));
+
+            exportParamList.forEach(param -> {
+                dataTable.addCell(createCell(param.getTime(), false));
+                dataTable.addCell(createCell(String.format("%.1f", param.getData()), false));
+            });
+            document.add(dataTable);
+
+            document.close();
+        } catch (Exception e) {
+            throw new RuntimeException("PDF生成失败", e);
         } finally {
             stopWatch.stop();
-            log.info("Excel导出耗时:{}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
+            log.info("PDF导出耗时:{}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
+        }
+    }
+
+    // 辅助方法:添加设备信息行
+    private void addDeviceTableRow(Table table, String label1, String value1, String label2, String value2) {
+        table.addCell(createCell(label1, true));
+        table.addCell(createCell(value1, false));
+        table.addCell(createCell(label2, true));
+        table.addCell(createCell(value2, false));
+    }
+
+    // 辅助方法:添加记录信息行
+    private void addRecordTableRow(Table table, String label1, String value1, String label2, String value2) {
+        table.addCell(createCell(label1, true));
+        table.addCell(createCell(value1, false));
+        table.addCell(createCell(label2, true));
+        table.addCell(createCell(value2, false));
+    }
+
+    // 辅助方法:创建带样式的单元格
+    private Cell createCell(String content, boolean isHeader) {
+        Cell cell = null;
+        try {
+            cell = new Cell().add(new Paragraph(content))
+                    .setFont(PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H"))
+                    .setPadding(5);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        if (isHeader) {
+            cell.setBackgroundColor(ColorConstants.LIGHT_GRAY)
+                    .setTextAlignment(TextAlignment.CENTER);
+        } else {
+            cell.setTextAlignment(TextAlignment.LEFT);
         }
+        return cell;
     }
 }

+ 10 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitornotice/entity/Trend.java

@@ -29,6 +29,16 @@ public class Trend {
     @Schema(description = "温度报警下限")
     private Float temperatureDown;
 
+    private Float tMax;
+    private Float tMin;
+    private Float tAvg;
+    private Float hMax;
+    private Float hMin;
+    private Float hAvg;
+    private Float cMax;
+    private Float cMin;
+    private Float cAvg;
+
     /**
      * 湿度报警上限 temperature humidity co2
      */

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitornotice/mapper/mapping/MonitorNoticeMapper.xml

@@ -4,7 +4,7 @@
     <select id="getCountByDate" resultType="Integer">
         SELECT COUNT(*)
         FROM sensor_alarm
-        WHERE CREATE_TIME > '${day} 00:00:00'
+        WHERE CREATE_TIME >= '${day} 00:00:00'
           AND CREATE_TIME like #{day}
           AND (
             DATE_FORMAT(CREATE_TIME, '%H:%i:%S') between #{startTime} and #{endTime}

+ 30 - 4
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitornotice/service/impl/MonitorNoticeServiceImpl.java

@@ -32,12 +32,10 @@ import vip.xiaonuo.coldchain.modular.monitornotice.service.MonitorNoticeService;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRegionService;
 
+import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
 import java.time.LocalTime;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 import static java.time.LocalTime.now;
 
@@ -170,7 +168,35 @@ public class MonitorNoticeServiceImpl extends ServiceImpl<MonitorNoticeMapper, M
         appDeviceQueryParams.setSensorRoute(trendParam.getRoads());
         appDeviceQueryParams.setAggregationWindow(trendParam.getAggregationWindow());
         SensorEchartDataResult sensorEchartDataResult = appDeviceService.queryDataByDeviceIdAndRoads(appDeviceQueryParams);
+        List<Float> tList = sensorEchartDataResult.getTemperature().getY();
+        List<Float> hList = sensorEchartDataResult.getHumidity().getY();
+        List<Float> cList = sensorEchartDataResult.getCo2().getY();
         Trend trend = BeanUtil.copyProperties(oneByDeviceCodeAndSensorNo, Trend.class);
+        DecimalFormat df = new DecimalFormat("#.00");
+        if (!tList.isEmpty()) {
+            double tMax = tList.stream().mapToDouble(Number::floatValue).max().getAsDouble();
+            double tMin = tList.stream().mapToDouble(Number::floatValue).min().getAsDouble();
+            double tAvg = tList.stream().mapToDouble(Number::floatValue).average().getAsDouble();
+            trend.setTMax((float) tMax);
+            trend.setTMin((float) tMin);
+            trend.setTAvg(Float.parseFloat(df.format(tAvg)));
+        }
+        if (!hList.isEmpty()) {
+            double hMax = hList.stream().mapToDouble(Number::floatValue).min().getAsDouble();
+            double hMin = hList.stream().mapToDouble(Number::floatValue).max().getAsDouble();
+            double hAvg = hList.stream().mapToDouble(Number::floatValue).min().getAsDouble();
+            trend.setHMax((float) hMax);
+            trend.setHMin((float) hMin);
+            trend.setHAvg((float) hAvg);
+        }
+        if (!cList.isEmpty()) {
+            double cMax = cList.stream().mapToDouble(Number::floatValue).min().getAsDouble();
+            double cMin = cList.stream().mapToDouble(Number::floatValue).max().getAsDouble();
+            double cAvg = cList.stream().mapToDouble(Number::floatValue).min().getAsDouble();
+            trend.setCMax((float) cMax);
+            trend.setCMin((float) cMin);
+            trend.setCAvg((float) cAvg);
+        }
         trend.setSensorEchartDataResult(sensorEchartDataResult);
         return trend;
     }

+ 11 - 0
snowy-web-app/pom.xml

@@ -137,6 +137,17 @@
             <artifactId>weixin-java-miniapp</artifactId>
             <version>4.6.0</version>
         </dependency>
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itext7-core</artifactId>
+            <version>7.2.0</version>
+            <type>pom</type>
+        </dependency>
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itext-asian</artifactId>
+            <version>5.2.0</version>
+        </dependency>
     </dependencies>
 
     <build>