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<String> 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<String> 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<String, Integer> 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<XWPFTableRow> 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<XWPFTableRow> 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<String>();
|
for (var checkItem : safetyCheckItems) {
|
collectSubItemNames(checkItem, subItemNames);
|
}
|
return String.join("、", subItemNames);
|
}
|
|
private void collectSubItemNames(JSONObject checkItem, ArrayList<String> 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;
|
}
|
}
|