diff --git a/ruoyi-modules/ruoyi-demo/pom.xml b/ruoyi-modules/ruoyi-demo/pom.xml
index 560ea6e..b0e204a 100644
--- a/ruoyi-modules/ruoyi-demo/pom.xml
+++ b/ruoyi-modules/ruoyi-demo/pom.xml
@@ -113,6 +113,10 @@
org.dromara
ruoyi-common-websocket
+
+ org.dromara
+ ruoyi-system
+
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SiteEvaluationInfoController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SiteEvaluationInfoController.java
index 91f7025..444b93b 100644
--- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SiteEvaluationInfoController.java
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SiteEvaluationInfoController.java
@@ -1,5 +1,6 @@
package org.dromara.demo.controller;
+import java.io.IOException;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -103,4 +104,9 @@ public class SiteEvaluationInfoController extends BaseController {
@PathVariable String[] ids) {
return toAjax(siteEvaluationInfoService.deleteWithValidByIds(List.of(ids), true));
}
+
+ @GetMapping("/download/world")
+ public void download( HttpServletResponse response) throws Exception {
+ siteEvaluationInfoService.download(response);
+ }
}
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ISiteEvaluationInfoService.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ISiteEvaluationInfoService.java
index 4b2711e..b1c6b1e 100644
--- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ISiteEvaluationInfoService.java
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ISiteEvaluationInfoService.java
@@ -1,11 +1,13 @@
package org.dromara.demo.service;
import com.fasterxml.jackson.core.JsonProcessingException;
+import jakarta.servlet.http.HttpServletResponse;
import org.dromara.demo.domain.vo.SiteEvaluationInfoVo;
import org.dromara.demo.domain.bo.SiteEvaluationInfoBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
+import java.io.IOException;
import java.util.Collection;
import java.util.List;
@@ -66,4 +68,6 @@ public interface ISiteEvaluationInfoService {
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection ids, Boolean isValid);
+
+ void download(HttpServletResponse response) throws Exception;
}
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/SiteEvaluationInfoServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/SiteEvaluationInfoServiceImpl.java
index a60cae4..bf65661 100644
--- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/SiteEvaluationInfoServiceImpl.java
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/SiteEvaluationInfoServiceImpl.java
@@ -1,9 +1,21 @@
package org.dromara.demo.service.impl;
+import cn.afterturn.easypoi.word.WordExportUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.util.Units;
+import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.apache.poi.xwpf.usermodel.XWPFRun;
+import org.dromara.common.core.service.OssService;
import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
@@ -11,8 +23,15 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
+import org.dromara.common.oss.core.OssClient;
+import org.dromara.common.oss.factory.OssFactory;
+import org.dromara.demo.domain.Attachment;
import org.dromara.demo.domain.EvaluationDataJson;
+import org.dromara.demo.domain.WordExportTest;
import org.dromara.demo.domain.vo.AttachmentVo;
+import org.dromara.system.domain.vo.SysOssVo;
+import org.dromara.system.service.ISysOssService;
+import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.dromara.demo.domain.bo.SiteEvaluationInfoBo;
import org.dromara.demo.domain.vo.SiteEvaluationInfoVo;
@@ -20,11 +39,13 @@ import org.dromara.demo.domain.SiteEvaluationInfo;
import org.dromara.demo.mapper.SiteEvaluationInfoMapper;
import org.dromara.demo.service.ISiteEvaluationInfoService;
+import java.io.*;
+import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
-import java.util.List;
-import java.util.Map;
-import java.util.Collection;
+import java.util.*;
/**
* 现场考核信息Service业务层处理
@@ -38,7 +59,8 @@ public class SiteEvaluationInfoServiceImpl implements ISiteEvaluationInfoService
private final SiteEvaluationInfoMapper baseMapper;
private final ObjectMapper objectMapper = new ObjectMapper();
-
+ @Resource
+ private final ISysOssService ossService;
/**
* 查询现场考核信息
*
@@ -198,4 +220,319 @@ public class SiteEvaluationInfoServiceImpl implements ISiteEvaluationInfoService
}
return baseMapper.deleteByIds(ids) > 0;
}
+ @Override
+ public void download(HttpServletResponse response) throws Exception {
+ // 1. 准备数据
+ Map data = getData();
+ String siteName = (String) data.get("siteName");
+ String fileName = siteName + "现场技术核查评分表.docx";
+
+ // 2. 加载模板
+ InputStream templateIs = this.getClass().getClassLoader()
+ .getResourceAsStream("score_template.docx");
+ if (templateIs == null) {
+ throw new FileNotFoundException("模板未找到");
+ }
+
+ try (templateIs) {
+ // 3. 创建临时文件 → 填充模板
+ File tempFile = File.createTempFile("temp", ".docx");
+ tempFile.deleteOnExit();
+ Files.copy(templateIs, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+
+ XWPFDocument document = WordExportUtil.exportWord07(tempFile.getAbsolutePath(), data);
+ AttachmentVo attachmentVo = new AttachmentVo();
+ List attachmentList = new ArrayList<>();
+
+ Attachment attachment1 = new Attachment();
+ attachment1.setDescription("描述1");
+ attachment1.setOssId("1964745602352324609");
+ attachment1.setName("故障图片.png");
+ attachmentList.add(attachment1);
+
+ Attachment attachment2 = new Attachment();
+ attachment2.setDescription("描述2");
+ attachment2.setOssId("1964745602352324609");
+ attachment2.setName("故障图片.png");
+ attachmentList.add(attachment2);
+ attachmentVo.setAttachmentList(attachmentList);
+
+ appendImages(document,attachmentVo ,ossService);
+
+ // ✅ 4. 设置响应头
+ String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
+
+ response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
+ response.setHeader("Content-Disposition",
+ "attachment1; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
+ response.setCharacterEncoding("UTF-8");
+
+ // ✅ 5. 写入输出流
+ document.write(response.getOutputStream());
+
+ // ✅ 6. 关闭 document
+ document.close();
+
+ // ✅ 7. 强制刷新输出缓冲区(关键!)
+ response.flushBuffer();
+ }
+ // ✅ try 结束前,数据已发出
+ }
+
+ private static void appendImages(XWPFDocument document,
+ AttachmentVo attachmentVo,
+ ISysOssService ossService) throws Exception {
+
+ if (attachmentVo == null || attachmentVo.getAttachmentList() == null || attachmentVo.getAttachmentList().isEmpty()) {
+ System.out.println("📭 无图片需要插入");
+ return;
+ }
+
+ List attachmentList = attachmentVo.getAttachmentList();
+
+ for (int i = 0; i < attachmentList.size(); i++) {
+ Attachment attachment = attachmentList.get(i);
+ String ossIdStr = attachment.getOssId();
+ String description = attachment.getDescription();
+ String imageName = attachment.getName();
+
+ if (ossIdStr == null || ossIdStr.trim().isEmpty()) {
+ System.err.println("⚠️ 缺少 ossId,跳过图片:" + imageName);
+ continue;
+ }
+
+ Long ossId;
+ try {
+ ossId = Long.valueOf(ossIdStr);
+ } catch (NumberFormatException e) {
+ System.err.println("⚠️ ossId 格式错误:" + ossIdStr);
+ continue;
+ }
+
+ // 查询文件信息
+ SysOssVo sysOss = SpringUtils.getAopProxy(ossService).getById(ossId);
+ if (sysOss == null) {
+ System.err.println("⚠️ 文件不存在,ossId: " + ossId);
+ continue;
+ }
+
+ // 获取 OssClient
+ OssClient storage = OssFactory.instance(sysOss.getService());
+
+ // ✅ 使用 ByteArrayOutputStream 获取图片字节流
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ // 调用已有方法,把文件写入 baos
+ storage.download(sysOss.getFileName(), baos);
+ byte[] imageBytes = baos.toByteArray();
+
+ if (imageBytes.length == 0) {
+ System.err.println("⚠️ 下载图片为空:" + ossId);
+ continue;
+ }
+
+ // 创建图片段落(居中)
+ XWPFParagraph imgParagraph = document.createParagraph();
+ imgParagraph.setAlignment(ParagraphAlignment.CENTER);
+ XWPFRun imgRun = imgParagraph.createRun();
+
+ // 推断图片类型
+ int pictureType = getPictureType(imageName);
+
+ // 插入图片
+ imgRun.addPicture(
+ new ByteArrayInputStream(imageBytes),
+ pictureType,
+ imageName,
+ Units.toEMU(300),
+ Units.toEMU(150)
+ );
+
+ } catch (Exception e) {
+ System.err.println("⚠️ 图片插入失败 (ossId: " + ossId + "):" + e.getMessage());
+ e.printStackTrace();
+ continue;
+ } finally {
+ baos.close(); // 关闭流
+ }
+
+ // 添加图注
+ if (description != null && !description.trim().isEmpty()) {
+ XWPFParagraph captionParagraph = document.createParagraph();
+ captionParagraph.setAlignment(ParagraphAlignment.CENTER);
+ XWPFRun captionRun = captionParagraph.createRun();
+ captionRun.setText("图" + (i + 1) + ":" + description);
+ captionRun.setColor("666666");
+ captionRun.setFontSize(10);
+ captionRun.setItalic(true);
+ captionRun.setFontFamily("宋体");
+ }
+
+ // 每张图后空一行
+ document.createParagraph();
+ }
+ }
+
+ // ✅ 根据文件扩展名判断图片类型
+ private static int getPictureType(String filename) {
+ String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
+ return switch (ext) {
+ case "png" -> XWPFDocument.PICTURE_TYPE_PNG;
+ case "jpg", "jpeg" -> XWPFDocument.PICTURE_TYPE_JPEG;
+ case "gif" -> XWPFDocument.PICTURE_TYPE_GIF;
+ case "bmp" -> XWPFDocument.PICTURE_TYPE_BMP;
+ default -> XWPFDocument.PICTURE_TYPE_JPEG;
+ };
+ }
+
+ @NotNull
+ private static Map getData() {
+ // 1. 准备数据(用户填写的内容)
+ Map data = new LinkedHashMap<>();
+ data.put("siteName", "临安区龙岗镇站");
+ // ✅ 自动生成当前时间,并格式化为 "yyyy-MM-dd HH:mm:ss"
+ String currentDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ data.put("checkDateTime", currentDateTime);
+ data.put("operationUnit", "安徽蓝盾光电子股份有限公司");
+ data.put("checkPeople", "李工");
+ data.put("totalScore", "140"); // 28项 × 5分 = 140
+ // === 简单粗暴:score_01 到 score_28 全部设为 5 ===
+ for (int i = 1; i <= 28; i++) {
+ data.put("score_" + String.format("%02d", i), "5");
+ }
+ // -------------------- 零气测试 --------------------
+ double zero_flow = 5.0; // 零气MFC设定流量(L/min)
+ double zero_std = 4.98; // 标准流量计实测值(L/min)
+ // -------------------- 标气测试 --------------------
+ double span_flow = 100.0; // 标气MFC设定流量(ml/min)
+ double span_std = 101.5; // 标准流量计实测值(ml/min)
+
+ data.put("zero_flow", String.valueOf(zero_flow));
+ data.put("zero_std", String.valueOf(zero_std));
+ data.put("zero_error", calcError(zero_flow, zero_std)); // 自动计算
+
+ data.put("span_flow", String.valueOf(span_flow));
+ data.put("span_std", String.valueOf(span_std));
+ data.put("span_error", calcError(span_flow, span_std)); // 自动计算
+
+ // -------------------- SO2 流量测试 --------------------
+ double soDisplay = 0.8; // SO2显示流量(L/min)
+ double soStd = 0.78; // 标准流量计测值(L/min)
+
+ data.put("so_display", String.valueOf(soDisplay));
+ data.put("so_std", String.valueOf(soStd));
+ data.put("so_error", calcError(soDisplay, soStd)); // 自动计算:相对误差
+
+ // -------------------- SO2 浓度测试 --------------------
+ double soConcOutput = 500.0; // 标气稀释输出浓度(ppb)
+ double soConcResponse = 492.0; // 仪器响应浓度(ppb)
+
+ data.put("so_conc_output", String.valueOf(soConcOutput));
+ data.put("so_conc_response", String.valueOf(soConcResponse));
+ data.put("so_conc_error", calcError(soConcResponse, soConcOutput)); // (响应 - 输出)/输出 × 100%
+
+ // -------------------- t90响应时间(用户填写,不参与计算)--------------------
+ data.put("so_time", "4.2"); // 用户手动填写,单位:min,不参与任何计算
+
+
+ // -------------------- NO2 流量测试 --------------------
+ double noDisplay = 0.8;
+ double noStd = 0.78;
+
+ data.put("no_display", String.valueOf(noDisplay));
+ data.put("no_std", String.valueOf(noStd));
+ data.put("no_error", calcError(noDisplay, noStd)); // 自动计算:相对误差
+
+ // -------------------- NO 浓度测试 --------------------
+ double noConcOutput = 500.0; // 标气稀释输出浓度(ppb)
+ double noConcResponse = 492.0; // 仪器响应浓度(ppb)
+
+ data.put("no_conc_output", String.valueOf(noConcOutput));
+ data.put("no_conc_response", String.valueOf(noConcResponse));
+ data.put("no_conc_error", calcError(noConcResponse, noConcOutput)); // (响应 - 输出)/输出 × 100%
+
+ // -------------------- t90响应时间(用户填写,不参与计算)--------------------
+ data.put("no_time", "4.2"); // 用户手动填写,单位:min,不参与任何计算
+
+
+ // -------------------- CO 流量测试 --------------------
+ double coDisplay = 0.8; // SO2显示流量(L/min)
+ double coStd = 0.78; // 标准流量计测值(L/min)
+
+ data.put("co_display", String.valueOf(coDisplay));
+ data.put("co_std", String.valueOf(coStd));
+ data.put("co_error", calcError(coDisplay, coStd)); // 自动计算:相对误差
+
+ // -------------------- NO2 浓度测试 --------------------
+ double coConcOutput = 500.0; // 标气稀释输出浓度(ppb)
+ double coConcResponse = 492.0; // 仪器响应浓度(ppb)
+
+ data.put("co_conc_output", String.valueOf(coConcOutput));
+ data.put("co_conc_response", String.valueOf(coConcResponse));
+ data.put("co_conc_error", calcError(coConcResponse, coConcOutput)); // (响应 - 输出)/输出 × 100%
+
+ // -------------------- t90响应时间(用户填写,不参与计算)--------------------
+ data.put("co_time", "4.2"); // 用户手动填写,单位:min,不参与任何计算
+
+
+ double oDisplay = 0.8; // SO2显示流量(L/min)
+ double oStd = 0.78; // 标准流量计测值(L/min)
+
+ data.put("o_display", String.valueOf(oDisplay));
+ data.put("o_std", String.valueOf(oStd));
+ data.put("o_error", calcError(oDisplay, oStd)); // 自动计算:相对误差
+
+ double oConcOutput = 500.0; // 标气稀释输出浓度(ppb)
+ double oConcResponse = 492.0; // 仪器响应浓度(ppb)
+
+ data.put("o_conc_output", String.valueOf(oConcOutput));
+ data.put("o_conc_response", String.valueOf(oConcResponse));
+ data.put("o_conc_error", calcError(oConcResponse, oConcOutput)); // (响应 - 输出)/输出 × 100%
+ // -------------------- t90响应时间(用户填写,不参与计算)--------------------
+ data.put("o_time", "4.2"); // 用户手动填写,单位:min,不参与任何计算
+
+ //----------PM10 测试----------
+ double pmTenDisplay = 0.8;
+ double pmTenStd = 0.78;
+
+ data.put("pm_ten_display", String.valueOf(pmTenDisplay));
+ data.put("pm_ten_std", String.valueOf(pmTenStd));
+ data.put("pm_ten_error", calcError(pmTenDisplay, pmTenStd)); // 自动计算:相对误差
+
+ data.put("pm_ten_k", 0.8);
+ data.put("slope_a", 0.8);
+
+
+ //----------PM2 测试----------
+ double pmTwoDisplay = 0.8;
+ double pmTwoStd = 0.78;
+
+ data.put("pm_two_display", String.valueOf(pmTwoDisplay));
+ data.put("pm_two_std", String.valueOf(pmTwoStd));
+ data.put("pm_two_error", calcError(pmTwoDisplay, pmTwoStd)); // 自动计算:相对误差
+
+ data.put("pm_two_k", 0.5);
+ data.put("slope_b", 0.5);
+
+ System.out.println("✅ 数据准备完成!");
+ System.out.println(JSONUtil.toJsonStr(data));
+ //System.out.println(JSONUtil.toJsonPrettyStr(data)); // 格式化 + 缩进输出
+ JSONObject jsonObject = JSONUtil.parseObj(data);
+ System.out.println(jsonObject.toStringPretty());
+ return data;
+ }
+
+ // 工具方法:计算相对误差,保留1位小数
+ private static String calcError(double mfcFlow, double stdFlow) {
+ if (Math.abs(stdFlow) < 0.0001) return "0.0"; // 防止除0
+ double error = (mfcFlow - stdFlow) / stdFlow * 100;
+ return String.format("%.1f", error);
+ }
+ // ✅ 假设这是你要插入的图片路径列表(可以来自数据库、文件夹扫描等)
+ private static final List IMAGE_PATHS = List.of(
+ "D:\\Users\\14212\\Desktop\\故障图片.png",
+ "D:\\Users\\14212\\Desktop\\故障图片.png",
+ "D:\\Users\\14212\\Desktop\\故障图片.png"
+ // 可以是 0 个、1 个、N 个
+ );
}