|
|
@ -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<String, Object> 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<Attachment> 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<Attachment> 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<String, Object> getData() { |
|
|
|
// 1. 准备数据(用户填写的内容)
|
|
|
|
Map<String, Object> 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<String> IMAGE_PATHS = List.of( |
|
|
|
"D:\\Users\\14212\\Desktop\\故障图片.png", |
|
|
|
"D:\\Users\\14212\\Desktop\\故障图片.png", |
|
|
|
"D:\\Users\\14212\\Desktop\\故障图片.png" |
|
|
|
// 可以是 0 个、1 个、N 个
|
|
|
|
); |
|
|
|
} |
|
|
|