From 0fe8ff41aadf37889950a76ee9f44774d680a2e8 Mon Sep 17 00:00:00 2001 From: WangGuangYuan Date: Tue, 8 Apr 2025 15:50:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=AD=A6=E6=B1=89=E7=94=B5=E7=AB=99=E5=B7=A1?= =?UTF-8?q?=E8=A7=86=E6=8A=A5=E5=91=8A=E6=94=AF=E6=8C=81word=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- inspect-base/inspect-base-core/pom.xml | 5 + .../PatrolTaskResultMainController.java | 452 +++++++++++++++++- 2 files changed, 448 insertions(+), 9 deletions(-) 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()));