Эх сурвалжийг харах

fix:修改传感器数据导出接口,调整格式,改为从前端获取图片文件插入pdf

黄渊昊 1 сар өмнө
parent
commit
cfce995046

+ 3 - 3
pom.xml

@@ -384,9 +384,9 @@
                 <version>8.3.0</version>
             </dependency>
             <dependency>
-                <groupId>com.itextpdf</groupId>
-                <artifactId>itext-asian</artifactId>
-                <version>5.2.0</version>
+                <groupId>org.springframework.integration</groupId>
+                <artifactId>spring-integration-mqtt</artifactId>
+                <version>5.3.2.RELEASE</version>
             </dependency>
 
 <!--

+ 4 - 15
snowy-plugin/snowy-plugin-coldchain/pom.xml

@@ -122,23 +122,12 @@
         </dependency>
         <dependency>
             <groupId>com.itextpdf</groupId>
-            <artifactId>itextpdf</artifactId>
-            <version>5.5.6</version>
+            <artifactId>layout</artifactId>
+            <version>7.2.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>
+            <groupId>org.springframework.integration</groupId>
+            <artifactId>spring-integration-mqtt</artifactId>
         </dependency>
     </dependencies>
 </project>

+ 4 - 3
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/controller/AppController.java

@@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 import rk.netDevice.sdk.p2.ParamItem;
 import vip.xiaonuo.coldchain.modular.app.param.*;
 import vip.xiaonuo.coldchain.modular.app.param.export.AppTrendParam;
@@ -141,9 +142,9 @@ public class AppController {
      * 导出传感器数据
      */
     @Operation(summary = "导出传感器数据")
-    @GetMapping("/device/export")
-    public void export(HttpServletResponse response, AppTrendParam trendParam) {
-        appDeviceService.export(response, trendParam);
+    @PostMapping("/device/export")
+    public void export(HttpServletResponse response, AppTrendParam trendParam, MultipartFile file) {
+        appDeviceService.export(response, trendParam ,file);
     }
 
     /**

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

@@ -13,52 +13,29 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.StopWatch;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
-import com.alibaba.excel.EasyExcel;
-import com.alibaba.excel.support.ExcelTypeEnum;
 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.layout.element.Image;
 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 com.itextpdf.layout.properties.UnitValue;
 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 org.springframework.web.multipart.MultipartFile;
 import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 import vip.xiaonuo.coldchain.core.service.JfcloudSensorDataService;
@@ -75,8 +52,6 @@ import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRe
 import vip.xiaonuo.common.enums.CommonDeleteFlagEnum;
 import vip.xiaonuo.common.exception.CommonException;
 
-import java.awt.*;
-import java.awt.image.BufferedImage;
 import java.io.*;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
@@ -557,121 +532,6 @@ public class AppDeviceService {
         SensorEchartDataResult sensorEchartDataResult = queryDataByDeviceIdAndRoads(appDeviceQueryParams);
         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();
-        for (char type : sensorType.toCharArray()) {
-            LinkedHashSet<String> x;
-            List<Float> y;
-            if (type == 'W') {
-                exportParam.setDataType("温度");
-                x = sensorEchartDataResult.getTemperature().getX();
-                y = sensorEchartDataResult.getTemperature().getY();
-            } else if (type == 'S') {
-                exportParam.setDataType("湿度");
-                x = sensorEchartDataResult.getHumidity().getX();
-                y = sensorEchartDataResult.getHumidity().getY();
-            } else if (type == 'C') {
-                exportParam.setDataType("二氧化碳浓度");
-                x = sensorEchartDataResult.getCo2().getX();
-                y = sensorEchartDataResult.getCo2().getY();
-            } else {
-                log.warn("未知的传感器类型:{}", type);
-                continue;
-            }
-
-            int i = 0;
-            for (String s : x) {
-                ExportParam exportParamOut = BeanUtil.copyProperties(exportParam, ExportParam.class);
-                exportParamOut.setTime(s);
-                exportParamOut.setData(y.get(i));
-                i++;
-                exportParamList.add(exportParamOut);
-            }
-        }
-
-        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()) {
-            // 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("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();
@@ -876,6 +736,203 @@ public class AppDeviceService {
             });
             document.add(dataTable);
 
+            document.close();
+        } catch (Exception e) {
+            throw new RuntimeException("PDF生成失败", e);
+        } finally {
+            stopWatch.stop();
+            log.info("PDF导出耗时:{}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
+        }
+    }*/
+
+    public void export(HttpServletResponse response, AppTrendParam trendParam, MultipartFile file) {
+        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);
+
+            float[] columnWidths = {2, 4, 2, 3};
+
+            // 设备信息表格
+            Table deviceTable = new Table(UnitValue.createPercentArray(columnWidths))
+                    .useAllAvailableWidth()
+                    .setHorizontalAlignment(HorizontalAlignment.CENTER);;
+            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(UnitValue.createPercentArray(columnWidths))
+                    .useAllAvailableWidth()
+                    .setHorizontalAlignment(HorizontalAlignment.CENTER);
+            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);
+
+            if (!file.isEmpty()) {
+                Paragraph curve = new Paragraph("温度曲线")
+                        .setFont(chineseFont)
+                        .setTextAlignment(TextAlignment.CENTER)
+                        .setFontSize(15)
+                        .setBold();
+                document.add(curve);
+
+                try {
+                    // 获取图片字节数据
+                    byte[] imageBytes = file.getBytes();
+
+                    ImageData imageData = ImageDataFactory.create(imageBytes);
+                    Image image = new Image(imageData);
+
+                    float pageWidth = PageSize.A4.getWidth();
+                    float scaledWidth = pageWidth * 0.8f;
+
+                    image.scaleToFit(scaledWidth, image.getImageHeight());
+                    image.setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    document.add(image);
+
+                } catch (IOException e) {
+                    throw new RuntimeException("图片处理失败: " + e.getMessage());
+                }
+            }
+
+            // 温度数据表格
+            Paragraph dataTitle = new Paragraph("温度数据")
+                    .setFont(chineseFont)
+                    .setTextAlignment(TextAlignment.CENTER)
+                    .setFontSize(15)
+                    .setBold()
+                    .setMarginBottom(2);
+            document.add(dataTitle);
+
+            Table dataTable = new Table(new float[]{2,1,2,1,2,1,2,1}).useAllAvailableWidth();
+            for (int i = 0; i < 4; i++) {
+                dataTable.addHeaderCell(createCell("时间", true));
+                dataTable.addHeaderCell(createCell("°C", true));
+            }
+
+            // 数据分组逻辑(每组4条记录)
+            List<List<ExportParam>> groups = new ArrayList<>();
+            List<ExportParam> currentGroup = new ArrayList<>();
+            for (ExportParam param : exportParamList) {
+                currentGroup.add(param);
+                if (currentGroup.size() == 4) { // 每组4条记录(每行显示4个时间+温度)
+                    groups.add(currentGroup);
+                    currentGroup = new ArrayList<>();
+                }
+            }
+            // 处理剩余数据
+            if (!currentGroup.isEmpty()) {
+                groups.add(currentGroup);
+            }
+
+            // 填充表格数据
+            for (List<ExportParam> group : groups) {
+                // 添加每组数据
+                for (ExportParam param : group) {
+                    dataTable.addCell(createCell(param.getTime(), false));
+                    dataTable.addCell(createCell(String.format("%.1f", param.getData()), false));
+                }
+                // 补全空单元格(每组不足4条时)
+                int missing = 4 - group.size();
+                for (int i = 0; i < missing; i++) {
+                    dataTable.addCell(createCell("", false));
+                    dataTable.addCell(createCell("", false));
+                }
+            }
+            document.add(dataTable);
+
             document.close();
         } catch (Exception e) {
             throw new RuntimeException("PDF生成失败", e);

+ 6 - 3
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitornotice/controller/MonitorNoticeController.java

@@ -21,7 +21,10 @@ import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
 import vip.xiaonuo.coldchain.core.alarm.service.SensorAlarmService;
 import vip.xiaonuo.coldchain.modular.app.param.export.AppTrendParam;
@@ -129,11 +132,11 @@ public class MonitorNoticeController {
      * 导出传感器数据
      */
     @Operation(summary = "导出传感器数据")
-    @GetMapping("/coldchain/monitornotice/export")
-    public void export(HttpServletResponse response, TrendParam trendParam) {
+    @PostMapping("/coldchain/monitornotice/export")
+    public void export(HttpServletResponse response, TrendParam trendParam, MultipartFile file) {
         MonitorDevice monitorDevice = monitorDeviceService.queryEntity(trendParam.getDeviceId());
         AppTrendParam appTrendParam = BeanUtil.copyProperties(trendParam, AppTrendParam.class);
         appTrendParam.setSensorCode(monitorDevice.getDeviceCode());
-        appDeviceService.export(response, appTrendParam);
+        appDeviceService.export(response, appTrendParam, file);
     }
 }

+ 7 - 0
snowy-web-app/src/main/resources/application.properties

@@ -32,6 +32,13 @@ spring.datasource.dynamic.datasource.master.username=root
 spring.datasource.dynamic.datasource.master.password=Root123...
 spring.datasource.dynamic.strict=true
 
+# MQTT
+spring.mqtt.url=mqtt://coldchain.nzkcloud.com:51183
+spring.mqtt.username=coldchain
+spring.mqtt.password=C123456
+spring.mqtt.client-id=provider-id
+spring.mqtt.default.topic=topic
+
 # influxdb
 spring.data.influxdb.url=${INFLUXDB_URL:http://jfcloud-k6-influxdb:8086}
 ## 10.0.1.200