diff --git a/inspect-base/inspect-base-core/pom.xml b/inspect-base/inspect-base-core/pom.xml
index 74290c2..ca1a43e 100644
--- a/inspect-base/inspect-base-core/pom.xml
+++ b/inspect-base/inspect-base-core/pom.xml
@@ -90,6 +90,11 @@
org.apache.poi
poi-ooxml
+
+ org.apache.poi
+ ooxml-schemas
+ 1.4
+
javax.servlet
javax.servlet-api
diff --git a/inspect-main/inspect-main-task/src/main/java/com/inspect/resultmain/controller/PatrolTaskResultMainController.java b/inspect-main/inspect-main-task/src/main/java/com/inspect/resultmain/controller/PatrolTaskResultMainController.java
index 6557d12..5ba76ea 100644
--- a/inspect-main/inspect-main-task/src/main/java/com/inspect/resultmain/controller/PatrolTaskResultMainController.java
+++ b/inspect-main/inspect-main-task/src/main/java/com/inspect/resultmain/controller/PatrolTaskResultMainController.java
@@ -29,19 +29,15 @@ import com.inspect.task.service.IPatrolTaskService;
import com.inspect.taskinfo.service.IPatrolTaskInfoService;
import com.inspect.taskstatus.service.IPatrolTaskStatusService;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
+import java.io.*;
+import java.math.BigInteger;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.io.function.IOConsumer;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
@@ -58,9 +54,12 @@ import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.util.Units;
import org.apache.poi.xssf.streaming.SXSSFDrawing;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xwpf.usermodel.*;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@@ -198,7 +197,7 @@ public class PatrolTaskResultMainController extends BaseController {
(new Thread(() -> {
long beginTime = (new Date()).getTime();
logger.info("[EXPORT REPORTS] StartTime: {}, reportId: {}", DateUtil.now(), reportId);
- exportToExcel(String.valueOf(reportId));
+ exportExcelAndWord(String.valueOf(reportId));
PrintUtil.useTime("EXPORT REPORTS END", beginTime);
})).start();
return toAjax(1);
@@ -250,6 +249,353 @@ public class PatrolTaskResultMainController extends BaseController {
return areaName;
}
+
+ private XWPFDocument getXWPFDocument(InspectionReport report, Map images) {
+ XWPFDocument doc = new XWPFDocument();
+
+ // 标题
+ addTitle(doc, "巡视报告");
+
+ // 基本信息表格
+ XWPFTable baseInfoTable = doc.createTable(7, 4);
+ baseInfoTable.setWidth("100%");
+
+ // 设置表格样式
+ setTableStyle(baseInfoTable);
+
+ // 填充基础信息
+ fillBaseInfo(baseInfoTable, report);
+
+
+ // 点位汇总
+ List statusList = Arrays.asList("异常", "待人工确认", "正常");
+ for (String status : statusList) {
+ addStatusSection(doc, report, status, images);
+ }
+
+ return doc;
+ }
+
+ private void addTitle(XWPFDocument doc, String title) {
+ XWPFParagraph titlePara = doc.createParagraph();
+ titlePara.setAlignment(ParagraphAlignment.CENTER);
+ XWPFRun titleRun = titlePara.createRun();
+ titleRun.setText(title);
+ titleRun.setFontFamily("宋体");
+ titleRun.setFontSize(20);
+ titleRun.setBold(true);
+ addEmptyParagraph(doc); // 空行
+ }
+
+ // 样式设置方法
+ private void setTableStyle(XWPFTable table) {
+ table.setCellMargins(100, 100, 100, 100); // 设置单元格边距
+ for (XWPFTableRow row : table.getRows()) {
+ for (XWPFTableCell cell : row.getTableCells()) {
+ setCellStyle(cell);
+ }
+ }
+ }
+
+ private void fillBaseInfo(XWPFTable table, InspectionReport report) {
+
+ fillRow(table, 0, "变电站", report.getStationName(), "电压等级", report.getVoltLevel());
+ fillRow(table, 1, "巡视计划创建日期", formatDate(report.getInspectionDate()),
+ "变电站类别", report.getStationType());
+ fillRow(table, 2, "巡视任务", report.getInspectionTaskName(),
+ "环境信息", report.getEnvInfo());
+ fillRow(table, 3, "审核人", report.getCheckPerson(),
+ "审核时间", formatDateTime(report.getCheckTime()));
+ fillRow(table, 4, "巡视开始时间", formatDateTime(report.getInspectionStartTime()),
+ "巡视结束时间", formatDateTime(report.getInspectionEndTime()));
+ fillRowWithMerge(table, 5, "巡视统计", report.getPatrolStatistics(), 3);
+
+ // 新增:巡视结论(合并右侧3列)
+ fillRowWithMerge(table, 6, "巡视结论", report.getDescription(), 3);
+ }
+
+ private void fillRow(XWPFTable table, int rowNum, String... values) {
+ XWPFTableRow row = table.getRow(rowNum);
+ for (int i = 0; i < values.length; i++) {
+ XWPFTableCell cell = row.getCell(i);
+ CTP ctP = cell.getCTTc().sizeOfPArray() == 0 ? cell.getCTTc().addNewP() : cell.getCTTc().getPArray(0);
+ XWPFParagraph par = cell.getParagraph(ctP);
+ par.createRun().setText(values[i]);
+ // 左侧标题列(索引0,2)设置特殊样式
+ if (i % 2 == 0) {
+ setTitleCellStyle(cell);
+ } else {
+ setValueCellStyle(cell);
+ }
+ }
+ }
+
+ // 标题单元格样式(宋体小五加粗)
+ private void setTitleCellStyle(XWPFTableCell cell) {
+ cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
+ for (XWPFParagraph para : cell.getParagraphs()) {
+ para.setAlignment(ParagraphAlignment.CENTER);
+ for (XWPFRun run : para.getRuns()) {
+ run.setFontFamily("宋体");
+ run.setFontSize(9);
+ run.setBold(true);
+ run.setColor("000000");
+ }
+ }
+
+ // 单元格背景色(浅灰色)
+ CTTcPr tcPr = cell.getCTTc().addNewTcPr();
+ CTShd shading = tcPr.addNewShd();
+ shading.setFill("D3D3D3");
+ shading.setVal(STShd.Enum.forString("clear"));
+ }
+
+ // 值单元格样式
+ private void setValueCellStyle(XWPFTableCell cell) {
+ cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
+ for (XWPFParagraph para : cell.getParagraphs()) {
+ para.setAlignment(ParagraphAlignment.CENTER);
+ for (XWPFRun run : para.getRuns()) {
+ run.setFontFamily("宋体");
+ run.setFontSize(9);
+ run.setColor("000000");
+ }
+ }
+ }
+
+ /**
+ * 填充带合并的表格行
+ * @param table 表格对象
+ * @param rowNum 行号
+ * @param title 左侧标题
+ * @param content 右侧内容
+ * @param mergeCols 需要合并的列数
+ */
+ private void fillRowWithMerge(XWPFTable table, int rowNum, String title, String content, int mergeCols) {
+ XWPFTableRow row = table.getRow(rowNum);
+
+ // 设置左侧标题单元格
+ XWPFTableCell titleCell = row.getCell(0);
+ CTP ctPTitle = titleCell.getCTTc().sizeOfPArray() == 0 ? titleCell.getCTTc().addNewP() : titleCell.getCTTc().getPArray(0);
+ XWPFParagraph parTitle = titleCell.getParagraph(ctPTitle);
+ parTitle.createRun().setText(title);
+ setTitleCellStyle(titleCell);
+
+ // 设置右侧合并内容单元格
+ XWPFTableCell contentCell = row.getCell(1);
+ CTP ctPContent = contentCell.getCTTc().sizeOfPArray() == 0 ? contentCell.getCTTc().addNewP() : contentCell.getCTTc().getPArray(0);
+ XWPFParagraph parContent = contentCell.getParagraph(ctPContent);
+ parContent.createRun().setText(content);
+ // 合并右侧单元格
+ for (int i = 1; i <= mergeCols; i++) {
+ if (i > 1) {
+ row.getCell(i).removeParagraph(0);
+ mergeCellsHorizontal(table, rowNum, 1, i);
+ }
+ setMergedContentStyle(row.getCell(i)); // 内容样式
+ }
+ }
+
+ // 合并单元格工具方法(横向)
+ private void mergeCellsHorizontal(XWPFTable table, int row, int startCol, int endCol) {
+ for (int i = startCol; i <= endCol; i++) {
+ CTTcPr tcPr = table.getRow(row).getCell(i).getCTTc().getTcPr();
+ if (tcPr == null) tcPr = table.getRow(row).getCell(i).getCTTc().addNewTcPr();
+ if (i == startCol) {
+ tcPr.addNewHMerge().setVal(STMerge.RESTART);
+ } else {
+ tcPr.addNewHMerge().setVal(STMerge.CONTINUE);
+ }
+ }
+ }
+
+
+ /**
+ * 合并内容单元格样式
+ */
+ private void setMergedContentStyle(XWPFTableCell cell) {
+ for (XWPFParagraph para : cell.getParagraphs()) {
+ para.setAlignment(ParagraphAlignment.LEFT);
+ para.setSpacingAfter(0);
+ para.setIndentationFirstLine(400); // 首行缩进
+
+ for (XWPFRun run : para.getRuns()) {
+ run.setFontFamily("宋体");
+ run.setFontSize(9);
+ run.setColor("000000");
+ }
+ }
+
+ // 自动换行设置
+ CTTcPr tcPr = cell.getCTTc().addNewTcPr();
+ tcPr.addNewNoWrap().setVal(STOnOff.FALSE);
+ }
+
+
+ private void addStatusSection(XWPFDocument doc, InspectionReport report,
+ String status, Map images) {
+ addSectionHeader(doc, status + "点位汇总");
+
+ XWPFTable table = doc.createTable(1, 9);
+ table.setWidth("100%");
+
+
+ // 创建表头
+ String[] headers = {"编号", "区域", "设备", "点位", "数据来源",
+ "采集时间", "巡视结果", "点位状态", "巡视图像"};
+ XWPFTableRow headerRow = table.getRow(0);
+ for (int i = 0; i < headers.length; i++) {
+ XWPFTableCell cell = headerRow.getCell(i);
+ CTP ctP = cell.getCTTc().sizeOfPArray() == 0 ? cell.getCTTc().addNewP() : cell.getCTTc().getPArray(0);
+ XWPFParagraph par = cell.getParagraph(ctP);
+ par.createRun().setText(headers[i]);
+ }
+ setTableHeaderStyle(table);
+ // 填充数据
+ List dataList = report.getReportDatalist().stream()
+ .filter(d -> status.equals(d.getPointStatus()))
+ .collect(Collectors.toList());
+
+ for (InspectionReportData data : dataList) {
+ XWPFTableRow row = table.createRow();
+ fillDataRow(row, data, images);
+ }
+ addEmptyParagraph(doc);
+ }
+
+ private void addSectionHeader(XWPFDocument doc, String text) {
+ XWPFParagraph para = doc.createParagraph();
+ para.setSpacingBefore(200); // 段前间距
+ XWPFRun run = para.createRun();
+ run.setText(text);
+ run.setFontSize(14);
+ run.setBold(true);
+ }
+
+ private void setTableHeaderStyle(XWPFTable table) {
+ if (table == null || table.getRows().isEmpty()) return;
+
+ // 获取表头行(第一行)
+ XWPFTableRow headerRow = table.getRow(0);
+
+ // 设置表格整体边框
+ CTTblPr tblPr = table.getCTTbl().getTblPr();
+ if (tblPr == null) {
+ tblPr = table.getCTTbl().addNewTblPr();
+ }
+
+ // 表格边框设置
+ CTTblBorders borders = tblPr.addNewTblBorders();
+ borders.addNewBottom().setVal(STBorder.SINGLE);
+ borders.addNewLeft().setVal(STBorder.SINGLE);
+ borders.addNewRight().setVal(STBorder.SINGLE);
+ borders.addNewTop().setVal(STBorder.SINGLE);
+ borders.addNewInsideH().setVal(STBorder.SINGLE);
+ borders.addNewInsideV().setVal(STBorder.SINGLE);
+
+ // 设置表头单元格样式
+ for (XWPFTableCell cell : headerRow.getTableCells()) {
+ CTTcPr tcPr = cell.getCTTc().addNewTcPr();
+ cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
+ // 单元格边距设置
+ tcPr.addNewTcMar();
+ CTTcMar tcMar = tcPr.getTcMar();
+ tcMar.addNewLeft().setW(BigInteger.valueOf(100));
+ tcMar.addNewRight().setW(BigInteger.valueOf(100));
+ tcMar.addNewTop().setW(BigInteger.valueOf(50));
+ tcMar.addNewBottom().setW(BigInteger.valueOf(50));
+
+ // 背景色填充(浅灰色)
+ CTShd shading = tcPr.addNewShd();
+ shading.setFill("D3D3D3");
+ shading.setVal(STShd.Enum.forString("clear"));
+
+ // 设置文本样式为宋体小五号加粗
+ for (XWPFParagraph para : cell.getParagraphs()) {
+ para.setAlignment(ParagraphAlignment.CENTER);
+ for (XWPFRun run : para.getRuns()) {
+ run.setBold(true);
+ run.setFontSize(9);
+ run.setFontFamily("宋体");
+ run.setColor("000000");
+ }
+ }
+ }
+ }
+
+
+ private void fillDataRow(XWPFTableRow row, InspectionReportData data,
+ Map images) {
+ String[] values = {
+ String.valueOf(data.getCode()),
+ data.getArea(),
+ data.getEqName(),
+ data.getPointName(),
+ data.getDataSources(),
+ formatDateTime(data.getAcquisitionTime()),
+ data.getInspectionResults(),
+ data.getPointStatus(),
+ "" // 图片占位
+ };
+
+ for (int i = 0; i < values.length; i++) {
+ XWPFTableCell cell = row.getTableCells().get(i);
+ CTP ctP = cell.getCTTc().sizeOfPArray() == 0 ? cell.getCTTc().addNewP() : cell.getCTTc().getPArray(0);
+ XWPFParagraph par = cell.getParagraph(ctP);
+ par.createRun().setText(values[i]);
+ setCellStyle(cell);
+ }
+
+ // 插入图片
+ if (data.getReportImgList() != null) {
+ data.getReportImgList().stream()
+ .filter(img -> "0".equals(img.getImgType()))
+ .forEach(img -> insertImageToCell(row.getCell(8),
+ images.get(img.getImgSrc())));
+ }
+ }
+
+ private void insertImageToCell(XWPFTableCell cell, byte[] imageData) {
+ if (imageData == null) return;
+
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(imageData)) {
+ CTP ctP = cell.getCTTc().sizeOfPArray() == 0 ? cell.getCTTc().addNewP() : cell.getCTTc().getPArray(0);
+ XWPFParagraph par = cell.getParagraph(ctP);
+ XWPFRun run = par.createRun();
+ run.addPicture(bis, XWPFDocument.PICTURE_TYPE_JPEG,
+ "image.jpg", Units.toEMU(100), Units.toEMU(100));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ private void setCellStyle(XWPFTableCell cell) {
+ cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
+
+ XWPFParagraph para = cell.getParagraphs().get(0);
+ para.setAlignment(ParagraphAlignment.CENTER);
+ para.setSpacingAfter(0);
+ para.setSpacingBefore(0);
+ XWPFRun run = para.getRuns().isEmpty() ? para.createRun() : para.getRuns().get(0);
+ run.setFontSize(9);
+ run.setFontFamily("宋体");
+
+ }
+
+ private void addEmptyParagraph(XWPFDocument doc) {
+ doc.createParagraph().createRun().addBreak();
+ }
+
+ private String formatDate(Date date) {
+ return date != null ? DateUtils.format(DateUtils.yyyyMMdd2, date) : "";
+ }
+
+ private String formatDateTime(Date date) {
+ return date != null ? DateUtils.format(DateUtils.yyyyMMddHHmmss2, date) : "";
+ }
+
+
public void exportToExcel(String lineId) {
List images = new ArrayList<>();
InspectionReport inspectionReport =
@@ -315,6 +661,94 @@ public class PatrolTaskResultMainController extends BaseController {
}
}
+ public void exportExcelAndWord(String lineId) {
+ List images = new ArrayList<>();
+ InspectionReport inspectionReport =
+ inspectionReportService.selectInspectionReportByLineId(Long.valueOf(lineId));
+ InspectionReportData inspectionReportData = new InspectionReportData();
+ inspectionReportData.setReportId(String.valueOf(inspectionReport.getLineId()));
+ List inspectionReportDataList =
+ inspectionReportDataService.selectInspectionReportDataList(inspectionReportData);
+ if (!inspectionReportDataList.isEmpty()) {
+ inspectionReport.setReportDatalist(inspectionReportDataList);
+ }
+
+ for (InspectionReportData reportData : inspectionReportDataList) {
+ InspectionReportImg inspectionReportImg = new InspectionReportImg();
+ inspectionReportImg.setReportInfoId(reportData.getLineId());
+ List reportImgList =
+ inspectionReportImgService.selectInspectionReportImgList(inspectionReportImg);
+ reportData.setReportImgList(reportImgList);
+ for (InspectionReportImg reportImg : reportImgList) {
+ if (StringUtils.isNotEmpty(reportImg.getImgSrc()) && reportImg.getImgType().equals("0")) {
+ images.add(reportImg.getImgSrc());
+ }
+ }
+ }
+
+ Map streamHashMap = new HashMap<>();
+ for (String algorithmBaseImagePath : images) {
+ try {
+ sftpClient.downLoad(algorithmBaseImagePath, (inputStream) -> {
+ byte[] bytes = streamHashMap.get(algorithmBaseImagePath);
+ byte[] byInputStream = getStringByInputStream(inputStream);
+ if (bytes == null) {
+ streamHashMap.put(algorithmBaseImagePath, byInputStream);
+ }
+
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ HSSFWorkbook hssfWorkbook = getHSSFWorkbook(inspectionReport, streamHashMap);
+ XWPFDocument wordDocument = getXWPFDocument(inspectionReport, streamHashMap);
+ logger.info("[ARCHIVE] hssfWorkbook: {}", hssfWorkbook);
+ try {
+ String basePath = this.basePath + stationCode + "/Model/";
+ Date startTime = inspectionReport.getInspectionStartTime() != null ?
+ inspectionReport.getInspectionStartTime() : new Date();
+ String timestamp = DateUtils.format(DateUtils.yyyyMMddHHmmss2, startTime) + "_" + System.currentTimeMillis();
+ String reportName = inspectionReport.getInspectionTaskName() + "_" + timestamp;
+ // 上传Excel
+ uploadFile(basePath, reportName, ".xls", hssfWorkbook::write);
+ // 上传Word
+ uploadFile(basePath, reportName, ".docx", wordDocument::write);
+ inspectionReport.setFilePath(basePath + reportName + ".xls");
+ inspectionReportService.updateInspectionReport(inspectionReport);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 上传文件到SFTP服务器
+ *
+ * @param basePath 文件上传的基础路径
+ * @param fileName 文件名
+ * @param suffix 文件后缀
+ * @param writer 文件写入器,用于将文件内容写入到输出流中
+ * @throws Exception 如果上传过程中发生异常,则抛出异常
+ */
+ private void uploadFile(String basePath, String fileName, String suffix, IOConsumer writer) {
+ SftpUploadEntity sftpUploadEntity = new SftpUploadEntity();
+ sftpUploadEntity.setFilePath(basePath);
+ sftpUploadEntity.setFileName(fileName);
+ sftpUploadEntity.setSuffix(suffix);
+
+ try {
+ String filePath = sftpClient.upload(sftpUploadEntity, outputStream -> {
+ writer.accept(outputStream);
+ outputStream.flush();
+ });
+ logger.info("文件上传成功: {}", filePath);
+ } catch (Exception e) {
+ logger.error("文件上传失败", e);
+ }
+ }
+
public HSSFWorkbook getHSSFWorkbook(InspectionReport inspectionReport, Map inputStreamMap) {
HSSFWorkbook hssfWorkbook = new HSSFWorkbook();
HSSFSheet hssfSheet = hssfWorkbook.createSheet(String.valueOf(System.currentTimeMillis()));