diff --git a/inspect-main/inspect-main-task/src/main/java/com/inspect/reportfont/ReportFontEmbedder.java b/inspect-main/inspect-main-task/src/main/java/com/inspect/reportfont/ReportFontEmbedder.java index bc6034b..a3830bb 100644 --- a/inspect-main/inspect-main-task/src/main/java/com/inspect/reportfont/ReportFontEmbedder.java +++ b/inspect-main/inspect-main-task/src/main/java/com/inspect/reportfont/ReportFontEmbedder.java @@ -66,15 +66,17 @@ public class ReportFontEmbedder { /** * 报告用到的 5 个嵌入字体部件(与 fontTable 模板的条目一一对应): * 部件名 -> {字体文件, 混淆 key}。字体文件位于 classpath fonts/ 或外部目录。 - * 注意:黑体在模板中有 regular/bold/italic 三个样式条目,均引用同一份黑体; - * 方正小标宋、仿宋各只嵌 bold(模板如此,标题/表头均加粗使用)。 + * 注意:fontTable 模板中「仿宋_GB2312」有 regular/bold/italic 三个样式条目 + * (rId1..3,对应 font1..3),正文/表头加粗/签署表斜体都引用同一份仿宋_GB2312; + * 方正小标宋简体只嵌 bold(rId4/font4,标题使用); + * 黑体只嵌 bold(rId5/font5,标题字体)。 */ private static final List EMBEDDED_FONTS = Arrays.asList( - new EmbeddedFont("font1", "simhei.ttf", "DAF739B1-18F1-4906-820A-C106608C2B73"), - new EmbeddedFont("font2", "simhei.ttf", "3039BACA-5140-4C4C-8AFB-F7B61E374105"), - new EmbeddedFont("font3", "simhei.ttf", "73410ACE-E9FA-46ED-BA3D-9ECB6C9FEF4E"), + new EmbeddedFont("font1", "仿宋_GB2312.ttf", "DAF739B1-18F1-4906-820A-C106608C2B73"), + new EmbeddedFont("font2", "仿宋_GB2312.ttf", "3039BACA-5140-4C4C-8AFB-F7B61E374105"), + new EmbeddedFont("font3", "仿宋_GB2312.ttf", "73410ACE-E9FA-46ED-BA3D-9ECB6C9FEF4E"), new EmbeddedFont("font4", "方正小标宋简体.ttf", "4D5F1B72-2D28-435A-B7BC-848B95F30EBB"), - new EmbeddedFont("font5", "simfang.ttf", "818E8C25-A7E2-4610-9234-360D3B3511E5")); + new EmbeddedFont("font5", "simhei.ttf", "818E8C25-A7E2-4610-9234-360D3B3511E5")); /** * 外部字体目录({@code report.donghuan.fonts-dir}), diff --git a/inspect-main/inspect-main-task/src/main/java/com/inspect/resultmain/controller/DonghuanReportExportController.java b/inspect-main/inspect-main-task/src/main/java/com/inspect/resultmain/controller/DonghuanReportExportController.java index 569b56b..b0b89f4 100644 --- a/inspect-main/inspect-main-task/src/main/java/com/inspect/resultmain/controller/DonghuanReportExportController.java +++ b/inspect-main/inspect-main-task/src/main/java/com/inspect/resultmain/controller/DonghuanReportExportController.java @@ -57,10 +57,12 @@ import java.util.zip.ZipOutputStream; * 样式要点(与模板逐项对齐): * *

@@ -76,8 +78,8 @@ import java.util.zip.ZipOutputStream; public class DonghuanReportExportController extends BaseController { // ============================ 样式常量(与模板一致) ============================ - /** 正文 */ - private static final String FONT_BODY = "仿宋"; + /** 正文/表格字体:仿宋_GB2312(华电公文格式,已随 docx 嵌入) */ + private static final String FONT_BODY = "仿宋_GB2312"; /** 一级小标题 */ private static final String FONT_HEAD = "黑体"; /** 报告标题 */ @@ -93,11 +95,11 @@ public class DonghuanReportExportController extends BaseController { /** 表头白字 */ private static final String COLOR_WHITE = "FFFFFF"; - /** 正文 12pt(半磅 24) */ + /** 表格正文/正文 12pt(半磅 24)小四 */ private static final int SZ_BODY = 24; - /** 小标题 14pt(半磅 28) 四号 */ + /** 表头/栏头 14pt(半磅 28)四号 */ private static final int SZ_HEAD_FOURTH = 28; - /** 小标题 16pt(半磅 32)三号 */ + /** 一级/二级标题 16pt(半磅 32)三号 */ private static final int SZ_HEAD_THIRD = 32; /** 行高 atLeast(twips) */ private static final int ROW_H = 454; @@ -455,14 +457,14 @@ public class DonghuanReportExportController extends BaseController { private XWPFDocument getXWPFDocumentDonghuan(InspectionReport report, Map tempFileMap) { XWPFDocument doc = new XWPFDocument(); - // 嵌入报告字体(仿宋/黑体/方正小标宋),docx 自带字体,查看方无需安装 + // 嵌入报告字体(仿宋_GB2312/黑体/方正小标宋),docx 自带字体,查看方无需安装 reportFontEmbedder.embed(doc); setupPage(doc); buildCoverPage1(doc); // 第1页:42pt 大标题 + 分页 buildCoverPage2(doc, report); // 第2页:logo + 报告标题 + 副标题 + 签署表 buildSectionBasic(doc, report); // 一、巡检基本情况 buildSectionStatus(doc); // 二、巡检状态说明 - buildSectionOverview(doc, report); // 三、巡检结果概览 + buildSectionOverview(doc, report, tempFileMap); // 三、巡检结果概览(缺陷汇总含图片信息) buildSectionDetail(doc, report, tempFileMap); // 四、巡检详情 return doc; } @@ -513,7 +515,7 @@ public class DonghuanReportExportController extends BaseController { grid.setCharSpace(BigInteger.ZERO); } - /** 第1页封面:42pt 仿宋大标题 + 分页符 */ + /** 第1页封面:42pt 仿宋_GB2312 大标题 + 分页符 */ private void buildCoverPage1(XWPFDocument doc) { XWPFParagraph p = addPara(doc, "#动环智能巡检", FONT_BODY, 84, false, false, null, ParagraphAlignment.CENTER, 120, 120); @@ -540,12 +542,12 @@ public class DonghuanReportExportController extends BaseController { addPara(doc, "", null, SZ_BODY, false, false, null, null, 600, null); addPara(doc, "", null, SZ_BODY, false, false, null, null, 200, null); - // 报告标题:站名 + 巡检报告(方正小标宋简体 22pt 加粗 深蓝) + // 报告标题:站名 + 巡检报告(方正小标宋简体 22pt 深蓝,不加粗) // TODO 归档时调整:站名是否已含「场站/电站」后缀 // 站名为空时标题不拼占位符(标题本身不会空白) String station = report.getStationName(); String reportTitle = (StringUtils.isEmpty(station) ? "" : station) + "巡检报告"; - addPara(doc, reportTitle, FONT_TITLE, 44, true, false, COLOR_DARK, + addPara(doc, reportTitle, FONT_TITLE, 44, false, false, COLOR_DARK, ParagraphAlignment.CENTER, 0, 100); // 副标题 addPara(doc, "【巡检类型:动环智能巡检】", FONT_BODY, SZ_HEAD_THIRD, false, false, @@ -569,11 +571,11 @@ public class DonghuanReportExportController extends BaseController { for (int i = 0; i < labels.length; i++) { setRow(table.getRow(i), null, true); setRowJcCenter(table.getRow(i)); - // 标签:仿宋 12pt 加粗 深蓝 居中 + // 标签:仿宋_GB2312 12pt 加粗 深蓝 居中 styleCell(table.getRow(i).getCell(0), labels[i], FONT_BODY, SZ_BODY, true, false, COLOR_DARK, false, ParagraphAlignment.CENTER, 0, 0, 1800, null, 60, 150, 60, 120); - // 值:仿宋 12pt 灰色斜体 居中 + // 值:仿宋_GB2312 12pt 灰色斜体 居中 styleCell(table.getRow(i).getCell(1), values[i], FONT_BODY, SZ_BODY, false, true, COLOR_GRAY, false, ParagraphAlignment.CENTER, 0, 0, 2886, null, 60, 150, 60, 120); @@ -582,10 +584,10 @@ public class DonghuanReportExportController extends BaseController { /** 一、巡检基本情况 */ private void buildSectionBasic(XWPFDocument doc, InspectionReport report) { - XWPFParagraph h = addPara(doc, "一、巡检基本情况", FONT_HEAD, SZ_HEAD_FOURTH, true, false, + XWPFParagraph h = addPara(doc, "一、巡检基本情况", FONT_HEAD, SZ_HEAD_THIRD, false, false, null, null, 160, 80); setKeepNext(h); - setFirstLineIndentOneChar(h, SZ_HEAD_FOURTH); // 标题首行缩进 1 字符 + setFirstLineIndentOneChar(h, SZ_HEAD_THIRD); // 标题首行缩进 1 字符 XWPFTable t = addTable(doc, 7, 4, new int[]{1827, 2655, 1920, 2659}, 9061, false); List dataList = report.getReportDatalist(); @@ -593,7 +595,7 @@ public class DonghuanReportExportController extends BaseController { // R0 表头(跨4列,深蓝底白字) setRow(t.getRow(0), ROW_H, false); mergeRowCells(t, 0, 0, 3); - styleCell(t.getRow(0).getCell(0), "场站基本信息", FONT_BODY, SZ_BODY, true, false, + styleCell(t.getRow(0).getCell(0), "场站基本信息", FONT_BODY, SZ_HEAD_FOURTH, true, false, COLOR_WHITE, true, ParagraphAlignment.CENTER, 0, 0, 9061, 4, 0, CELL_MAR, 0, CELL_MAR); @@ -607,7 +609,7 @@ public class DonghuanReportExportController extends BaseController { // ==================== R2:巡视任务基本信息(新增表头) ==================== setRow(t.getRow(2), ROW_H, false); mergeRowCells(t, 2, 0, 3); - styleCell(t.getRow(2).getCell(0), "巡视任务基本信息", FONT_BODY, SZ_BODY, true, false, + styleCell(t.getRow(2).getCell(0), "巡视任务基本信息", FONT_BODY, SZ_HEAD_FOURTH, true, false, COLOR_WHITE, true, ParagraphAlignment.CENTER, 0, 0, 9061, 4, 0, CELL_MAR, 0, CELL_MAR); @@ -616,7 +618,7 @@ public class DonghuanReportExportController extends BaseController { bodyCell(t.getRow(3).getCell(0), "巡检任务", 1827); mergeRowCells(t, 3, 1, 3); styleCell(t.getRow(3).getCell(1), val(report.getInspectionTaskName()), FONT_BODY, SZ_BODY, - false, false, null, false, ParagraphAlignment.LEFT, 0, 0, + false, false, null, false, ParagraphAlignment.CENTER, 0, 0, 1827 + 2655 + 1920 + 2659 - 1827, 3, 0, CELL_MAR, 0, CELL_MAR); // R4 巡检开始时间 | xx | 巡检结束时间 | xx @@ -642,16 +644,16 @@ public class DonghuanReportExportController extends BaseController { bodyCell(t.getRow(6).getCell(0), "审核意见", 1827); mergeRowCells(t, 6, 1, 3); styleCell(t.getRow(6).getCell(1), val(report.getDescription()), FONT_BODY, SZ_BODY, - false, false, null, false, ParagraphAlignment.LEFT, 0, 0, + false, false, null, false, ParagraphAlignment.CENTER, 0, 0, 1827 + 2655 + 1920 + 2659 - 1827, 3, 0, CELL_MAR, 0, CELL_MAR); } /** 二、巡检状态说明(静态文案,与模板一致) */ private void buildSectionStatus(XWPFDocument doc) { - XWPFParagraph h = addPara(doc, "二、巡检状态说明", FONT_HEAD, SZ_HEAD_FOURTH, true, false, + XWPFParagraph h = addPara(doc, "二、巡检状态说明", FONT_HEAD, SZ_HEAD_THIRD, false, false, null, null, 160, 80); setKeepNext(h); - setFirstLineIndentOneChar(h, SZ_HEAD_FOURTH); // 标题首行缩进 1 字符 + setFirstLineIndentOneChar(h, SZ_HEAD_THIRD); // 标题首行缩进 1 字符 XWPFTable t = addTable(doc, 4, 3, new int[]{1831, 4115, 3114}, 9060, false); String[][] rows = { @@ -677,12 +679,13 @@ public class DonghuanReportExportController extends BaseController { } } - /** 三、巡检结果概览:(一)巡检统计 + (二)缺陷汇总表 */ - private void buildSectionOverview(XWPFDocument doc, InspectionReport report) { - XWPFParagraph h = addPara(doc, "三、巡检结果概览", FONT_HEAD, SZ_HEAD_FOURTH, true, false, + /** 三、巡检结果概览:(一)巡检统计 + (二)缺陷汇总表(含图片信息列) */ + private void buildSectionOverview(XWPFDocument doc, InspectionReport report, + Map tempFileMap) { + XWPFParagraph h = addPara(doc, "三、巡检结果概览", FONT_HEAD, SZ_HEAD_THIRD, false, false, null, null, 160, 80); setKeepNext(h); - setFirstLineIndentOneChar(h, SZ_HEAD_FOURTH); // 标题首行缩进 1 字符 + setFirstLineIndentOneChar(h, SZ_HEAD_THIRD); // 标题首行缩进 1 字符 List dataList = report.getReportDatalist() != null ? report.getReportDatalist() : Collections.emptyList(); @@ -691,7 +694,7 @@ public class DonghuanReportExportController extends BaseController { String statusNormal = messageUtils.get("正常"); // (一)巡检统计 - XWPFParagraph h1 = addPara(doc, "(一)巡检统计", FONT_BODY, SZ_HEAD_THIRD, true, false, + XWPFParagraph h1 = addPara(doc, "(一)巡检统计", FONT_BODY, SZ_HEAD_THIRD, false, false, null, null, 80, 40); setKeepNext(h1); setFirstLineIndentOneChar(h1, SZ_HEAD_THIRD); // 标题首行缩进 1 字符 @@ -712,28 +715,32 @@ public class DonghuanReportExportController extends BaseController { } // (二)缺陷汇总表 - XWPFParagraph h2 = addPara(doc, "(二)缺陷汇总表", FONT_BODY, SZ_HEAD_THIRD, true, false, + XWPFParagraph h2 = addPara(doc, "(二)缺陷汇总表", FONT_BODY, SZ_HEAD_THIRD, false, false, null, null, 80, 40); setKeepNext(h2); setFirstLineIndentOneChar(h2, SZ_HEAD_THIRD); // 标题首行缩进 1 字符 List defectList = dataList.stream() .filter(d -> statusDefect.equals(d.getPointStatus())) .collect(Collectors.toList()); - int[] summaryWidths = {717, 1200, 1230, 1245, 1215, 1710, 990, 727}; + // 按批注要求:异常汇总(缺陷汇总)列表增加「图片信息」列,展示异常点位图片 + int[] summaryWidths = {650, 1088, 1115, 1128, 1100, 1549, 896, 658, 850}; int summaryTotalWidth = Arrays.stream(summaryWidths).sum(); // 无缺陷数据时保留一行(合并整行输出文字说明),避免表格空白 int summaryRows = 1 + Math.max(defectList.size(), 1); - XWPFTable summaryTable = addTable(doc, summaryRows, 8, summaryWidths, 9034, false); + XWPFTable summaryTable = addTable(doc, summaryRows, 9, summaryWidths, summaryTotalWidth, false); String[] summaryHeaders = {"序号", "区域名称", "间隔名称", "设备名称", "部件名称", - "巡检点位名称", "巡检", "状态"}; + "巡检点位名称", "巡检", "状态", "图片信息"}; setRow(summaryTable.getRow(0), ROW_H, true); for (int i = 0; i < summaryHeaders.length; i++) { headerCell(summaryTable.getRow(0).getCell(i), summaryHeaders[i], summaryWidths[i]); } // 「巡检 / 结果」表头换行 XWPFParagraph headP = summaryTable.getRow(0).getCell(6).getParagraphs().get(0); - setRunFont(headP.createRun(), "结果", FONT_BODY, SZ_BODY, true, false, COLOR_WHITE, true); + setRunFont(headP.createRun(), "结果", FONT_BODY, SZ_HEAD_FOURTH, true, false, COLOR_WHITE, true); int num = 1; + // 图片信息列缩略图尺寸:宽 = 单元格内容宽(列宽 - 左右边距),高按缺陷图比例等比缩放 + long summaryImgW = (summaryWidths[8] - 2L * CELL_MAR) * 635L; + long summaryImgH = summaryImgW * IMG_DEFECT_H / IMG_DEFECT_W; for (int ri = 0; ri < defectList.size(); ri++) { InspectionReportData d = defectList.get(ri); setRow(summaryTable.getRow(ri + 1), ROW_H, true); @@ -744,25 +751,28 @@ public class DonghuanReportExportController extends BaseController { bodyCell(summaryTable.getRow(ri + 1).getCell(ci), vals[ci], summaryWidths[ci], false, 120, 120); } + // 图片信息:该异常点位图片(优先缺陷图,其次原图;均无则横杠占位) + imageCell(summaryTable.getRow(ri + 1).getCell(8), + defectSummaryImagePath(d, tempFileMap), summaryImgW, summaryImgH, summaryWidths[8]); } if (defectList.isEmpty()) { // 无缺陷数据时:整行合并输出文字说明(原占位横杠改为「本次巡检未发现缺陷项」) setRow(summaryTable.getRow(1), ROW_H, true); - mergeRowCells(summaryTable, 1, 0, 7); + mergeRowCells(summaryTable, 1, 0, 8); styleCell(summaryTable.getRow(1).getCell(0), messageUtils.get("本次巡检未发现缺陷项"), FONT_BODY, SZ_BODY, false, false, null, false, ParagraphAlignment.CENTER, 120, 120, - summaryTotalWidth, 8, 0, CELL_MAR, 0, CELL_MAR); + summaryTotalWidth, 9, 0, CELL_MAR, 0, CELL_MAR); } } /** 四、巡检详情:每个点位一张 8 行 × 4 列详情表 */ private void buildSectionDetail(XWPFDocument doc, InspectionReport report, Map tempFileMap) { - XWPFParagraph h = addPara(doc, "四、巡检详情", FONT_HEAD, SZ_HEAD_FOURTH, true, false, + XWPFParagraph h = addPara(doc, "四、巡检详情", FONT_HEAD, SZ_HEAD_THIRD, false, false, null, null, 160, 80); setKeepNext(h); - setFirstLineIndentOneChar(h, SZ_HEAD_FOURTH); // 标题首行缩进 1 字符 + setFirstLineIndentOneChar(h, SZ_HEAD_THIRD); // 标题首行缩进 1 字符 List dataList = report.getReportDatalist() != null ? report.getReportDatalist().stream().filter(item -> messageUtils.get("缺陷").equals(item.getPointStatus())).collect(Collectors.toList()) : Collections.emptyList(); @@ -797,7 +807,7 @@ public class DonghuanReportExportController extends BaseController { bodyCell(t.getRow(5).getCell(0), "巡检状态", grid[0], false, 120, 120); mergeRowCells(t, 5, 1, 3); styleCell(t.getRow(5).getCell(1), val(d.getPointStatus()), FONT_BODY, SZ_BODY, - false, false, null, false, ParagraphAlignment.LEFT, 120, 120, + false, false, null, false, ParagraphAlignment.CENTER, 120, 120, grid[1] + grid[2] + grid[3], 3, 0, CELL_MAR, 0, CELL_MAR); // R6 巡检原图 | 图片文件名(跨3列) @@ -806,7 +816,7 @@ public class DonghuanReportExportController extends BaseController { bodyCell(t.getRow(6).getCell(0), "巡检原图", grid[0], false, 120, 120); mergeRowCells(t, 6, 1, 3); styleCell(t.getRow(6).getCell(1), val(imgName), FONT_BODY, SZ_BODY, - false, false, null, false, ParagraphAlignment.LEFT, 120, 120, + false, false, null, false, ParagraphAlignment.CENTER, 120, 120, grid[1] + grid[2] + grid[3], 3, 0, CELL_MAR, 0, CELL_MAR); // R7 基准图 | 巡检原图照片 | 缺陷图 | 缺陷图照片 @@ -1032,17 +1042,17 @@ public class DonghuanReportExportController extends BaseController { } } - /** 表头单元格:深蓝底 + 白字 + 加粗 + 居中 */ + /** 表头单元格:深蓝底 + 白字 + 加粗 + 居中(仿宋_GB2312 四号 14pt,与模板表头一致) */ private void headerCell(XWPFTableCell cell, String text, int tcw) { - styleCell(cell, text, FONT_BODY, SZ_BODY, true, false, COLOR_WHITE, true, + styleCell(cell, text, FONT_BODY, SZ_HEAD_FOURTH, true, false, COLOR_WHITE, true, ParagraphAlignment.CENTER, 0, 0, tcw, null, 0, CELL_MAR, 0, CELL_MAR); } - /** 正文单元格:仿宋 12pt,可选居中/行前段后间距 */ + /** 正文单元格:仿宋_GB2312 小四 12pt 居中、单倍行距(表格文字居中,与模板一致;center 参数保留兼容调用方) */ private void bodyCell(XWPFTableCell cell, String text, int tcw, boolean center, int before, int after) { styleCell(cell, text, FONT_BODY, SZ_BODY, false, false, null, false, - center ? ParagraphAlignment.CENTER : ParagraphAlignment.LEFT, + ParagraphAlignment.CENTER, before, after, tcw, null, 0, CELL_MAR, 0, CELL_MAR); } @@ -1054,10 +1064,10 @@ public class DonghuanReportExportController extends BaseController { bodyCell(cell, text, tcw, false, 0, 0); } - /** 图片单元格:细边框 + 垂直居中 + 图片(保持模板尺寸);无图时输出横杠占位 */ + /** 图片单元格:细边框 + 垂直居中 + 图片居中(保持模板尺寸);无图时输出横杠占位 */ private void imageCell(XWPFTableCell cell, Path imagePath, long w, long h, int tcw) { styleCell(cell, "", FONT_BODY, SZ_BODY, false, false, null, false, - ParagraphAlignment.LEFT, 120, 120, tcw, null, 0, CELL_MAR, 0, CELL_MAR); + ParagraphAlignment.CENTER, 120, 120, tcw, null, 0, CELL_MAR, 0, CELL_MAR); XWPFParagraph p = cell.getParagraphs().get(0); if (imagePath == null) { // 无图片时以横杠占位,避免空白单元格 @@ -1156,6 +1166,32 @@ public class DonghuanReportExportController extends BaseController { return list.stream().map(getter).filter(StringUtils::isNotEmpty).distinct().count(); } + /** + * 缺陷汇总「图片信息」列取图:优先该异常点位的缺陷图(结果图 img,最能体现异常), + * 其次退回巡检原图(imgSrc);都取不到(或未下载成功)返回 null,由调用方输出横杠占位。 + */ + private Path defectSummaryImagePath(InspectionReportData d, Map tempFileMap) { + if (d.getReportImgList() == null) { + return null; + } + Path fallback = null; + for (InspectionReportImg img : d.getReportImgList()) { + if (img == null) { + continue; + } + if (StringUtils.isNotEmpty(img.getImg())) { + Path p = tempFileMap.get(img.getImg()); + if (p != null) { + return p; + } + } + if (fallback == null && StringUtils.isNotEmpty(img.getImgSrc())) { + fallback = tempFileMap.get(img.getImgSrc()); + } + } + return fallback; + } + /** 点位第一张图片的文件名(imgType=0),无则空串 */ private String firstImageName(InspectionReportData d) { if (d.getReportImgList() == null) { diff --git a/inspect-main/inspect-main-task/src/main/resources/fonts/仿宋_GB2312.ttf b/inspect-main/inspect-main-task/src/main/resources/fonts/仿宋_GB2312.ttf new file mode 100644 index 0000000..16eb427 Binary files /dev/null and b/inspect-main/inspect-main-task/src/main/resources/fonts/仿宋_GB2312.ttf differ diff --git a/inspect-main/inspect-main-task/src/main/resources/report/wordparts/fontTable.xml b/inspect-main/inspect-main-task/src/main/resources/report/wordparts/fontTable.xml index 637d657..a61cfaf 100644 --- a/inspect-main/inspect-main-task/src/main/resources/report/wordparts/fontTable.xml +++ b/inspect-main/inspect-main-task/src/main/resources/report/wordparts/fontTable.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file