From 831c9ced5c3e6d3f32de8d635a76ca8906ed1e8f Mon Sep 17 00:00:00 2001 From: gjh <1421wake> Date: Mon, 8 Sep 2025 01:42:45 +0800 Subject: [PATCH] =?UTF-8?q?word=E6=A8=A1=E6=9D=BF=E5=AF=BC=E5=87=BAdemov10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-modules/ruoyi-demo/pom.xml | 4 + .../SiteEvaluationInfoController.java | 6 + .../service/ISiteEvaluationInfoService.java | 4 + .../impl/SiteEvaluationInfoServiceImpl.java | 345 +++++++++++++++++- 4 files changed, 355 insertions(+), 4 deletions(-) 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 个 + ); }