package com.estate.expert.admin.stat.report.builder; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy; import com.estate.expert.admin.service.UserFileService; import com.estate.expert.admin.stat.report.render.ChengduHengtaiPictureRenderPolicy; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.springframework.core.io.ClassPathResource; import java.io.IOException; import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * 成都衡泰第三方检查报告构建器 */ public class ChengduHengtaiSystemReportBuilder implements ReportBuilderBase { private static final String PICTURE_ROW_TITLE = "问题图片"; private static final int EMPTY_PICTURE_ROW_HEIGHT = 240; private static final String LEVEL_ONE = "一级隐患"; private static final String LEVEL_TWO = "二级隐患"; private static final Set HAZARD_LEVELS = Set.of( LEVEL_ONE, LEVEL_TWO, "三级隐患", "四级隐患"); private static final String LEVEL_ONE_COLOR = "FF0000"; private static final String LEVEL_TWO_COLOR = "FF9900"; private static final Set HAZARD_TABLE_LABELS = Set.of( "问题部位", "隐患等级", "问题描述", "违反条款", "建议措施", PICTURE_ROW_TITLE); private static final String TEMPLATE_FILE = "第三方检查报告-成都衡泰.docx"; private final UserFileService userFileService; public ChengduHengtaiSystemReportBuilder(UserFileService userFileService) { this.userFileService = userFileService; } @Override public XWPFTemplate buildReport(JSONObject data, Map templateConfig) { var modelData = buildModelData(data); var configure = Configure.builder() .bind("data.评估结果.问题清单", new LoopRowTableRenderPolicy()) .addPlugin('%', new ChengduHengtaiPictureRenderPolicy(userFileService)) .build(); var resource = new ClassPathResource("templates/" + TEMPLATE_FILE); try { var template = XWPFTemplate.compile(resource.getInputStream(), configure) .render(Map.of("data", modelData)); compactEmptyPictureRows(template.getXWPFDocument()); applyHazardLevelColors(template.getXWPFDocument()); return template; } catch (IOException e) { throw new RuntimeException(e); } } static void compactEmptyPictureRows(XWPFDocument document) { for (var table : document.getTables()) { var rows = table.getRows(); for (int index = 0; index < rows.size() - 1; index++) { if (isPictureTitleRow(rows.get(index)) && !containsPicture(rows.get(index + 1))) { rows.get(index + 1).setHeight(EMPTY_PICTURE_ROW_HEIGHT); } } } } private static boolean isPictureTitleRow(XWPFTableRow row) { return row.getTableCells().stream() .flatMap(cell -> cell.getParagraphs().stream()) .map(paragraph -> paragraph.getText().trim()) .anyMatch(PICTURE_ROW_TITLE::equals); } private static boolean containsPicture(XWPFTableRow row) { return row.getTableCells().stream() .flatMap(cell -> cell.getParagraphs().stream()) .flatMap(paragraph -> paragraph.getRuns().stream()) .anyMatch(run -> !run.getEmbeddedPictures().isEmpty()); } static void applyHazardLevelColors(XWPFDocument document) { for (var table : document.getTables()) { var rows = table.getRows(); for (int startIndex = 0; startIndex < rows.size(); startIndex++) { var level = findHazardLevel(rows.get(startIndex)); if (level == null) { continue; } var endIndex = findNextHazardStartIndex(rows, startIndex + 1); var color = findHazardLevelColor(level); if (color != null) { colorHazardRows(rows, startIndex, endIndex, color); } } } } private static String findHazardLevel(XWPFTableRow row) { for (var cell : row.getTableCells()) { var text = cell.getText().trim(); if (HAZARD_LEVELS.contains(text)) { return text; } } return null; } private static String findHazardLevelColor(String level) { if (LEVEL_ONE.equals(level)) { return LEVEL_ONE_COLOR; } if (LEVEL_TWO.equals(level)) { return LEVEL_TWO_COLOR; } return null; } private static int findNextHazardStartIndex(java.util.List rows, int startIndex) { for (int index = startIndex; index < rows.size(); index++) { if (findHazardLevel(rows.get(index)) != null) { return index; } } return rows.size(); } private static void colorHazardRows(java.util.List rows, int startIndex, int endIndex, String color) { for (int rowIndex = startIndex; rowIndex < endIndex; rowIndex++) { for (var cell : rows.get(rowIndex).getTableCells()) { if (HAZARD_TABLE_LABELS.contains(cell.getText().trim())) { continue; } cell.getParagraphs().stream() .flatMap(paragraph -> paragraph.getRuns().stream()) .forEach(run -> run.setColor(color)); } } } private JSONObject buildModelData(JSONObject data) { var modelData = new JSONObject(); modelData.putAll(data); modelData.put("设备概况", buildDeviceSummary(data.getJSONArray("设备分组信息"))); var evalInfo = new JSONObject(); evalInfo.putAll(data.getJSONObject("评估结果")); var checkItems = evalInfo.getJSONArray("检查项"); evalInfo.put("项目涉及安全子项", buildSafetySubItemText(checkItems)); evalInfo.put("问题清单", buildHazardItems(checkItems)); modelData.put("评估结果", evalInfo); return modelData; } private String buildDeviceSummary(JSONArray deviceGroups) { if (deviceGroups == null) { return ""; } return deviceGroups.stream() .map(JSONObject.class::cast) .filter(group -> group.getString("设备类型") != null && !group.getString("设备类型").isBlank()) .map(group -> group.getString("设备数量") + "台" + group.getString("设备类型")) .collect(Collectors.joining(";")); } private String buildSafetySubItemText(JSONArray checkItems) { if (checkItems == null || checkItems.isEmpty()) { return ""; } var safetyCheckItems = checkItems.stream() .map(JSONObject.class::cast) .filter(item -> { var name = item.getString("名称"); return name != null && name.contains("安全"); }) .collect(Collectors.toCollection(ArrayList::new)); if (safetyCheckItems.isEmpty()) { safetyCheckItems.add(checkItems.getJSONObject(0)); } var subItemNames = new ArrayList(); for (var checkItem : safetyCheckItems) { collectSubItemNames(checkItem, subItemNames); } return String.join("、", subItemNames); } private void collectSubItemNames(JSONObject checkItem, ArrayList subItemNames) { var categories = checkItem.getJSONArray("分项评估"); if (categories == null) { return; } for (var category : categories) { var subItems = ((JSONObject) category).getJSONArray("分项"); if (subItems == null) { continue; } for (var subItem : subItems) { var name = ((JSONObject) subItem).getString("类别"); if (name != null && !subItemNames.contains(name)) { subItemNames.add(name); } } } } private JSONArray buildHazardItems(JSONArray checkItems) { var hazardItems = new JSONArray(); if (checkItems == null) { return hazardItems; } int sequence = 1; for (var item : checkItems) { var categories = ((JSONObject) item).getJSONArray("分项评估"); if (categories == null) { continue; } for (var category : categories) { var subItems = ((JSONObject) category).getJSONArray("分项"); if (subItems == null) { continue; } for (var subItem : subItems) { var checkInfo = (JSONObject) subItem; var problems = checkInfo.getJSONArray("问题"); if (problems == null) { continue; } for (var problem : problems) { var hazard = new JSONObject(); hazard.putAll((JSONObject) problem); hazard.put("序号", sequence++); hazard.put("检查分项", checkInfo.getString("类别")); hazardItems.add(hazard); } } } } return hazardItems; } }