diff --git a/src/main/java/com/biutag/supervision/pojo/vo/ExportNegativeReturnVo.java b/src/main/java/com/biutag/supervision/pojo/vo/ExportNegativeReturnVo.java new file mode 100644 index 0000000..d07979e --- /dev/null +++ b/src/main/java/com/biutag/supervision/pojo/vo/ExportNegativeReturnVo.java @@ -0,0 +1,35 @@ +package com.biutag.supervision.pojo.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Setter +@Getter +public class ExportNegativeReturnVo { + + @ExcelProperty("问题编号") + private String id; + + @ExcelProperty("样本源头编号") + private String originId; + + @ExcelProperty("退回次数") + private Integer returnCount; + + @ExcelProperty(value = "退回时间", format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime returnTime; + + @ExcelProperty("退回人员") + private String returnUser; + + @ExcelProperty("退回意见") + private String returnOpinion; + + @ExcelProperty("修改核查内容") + @ColumnWidth(80) + private String modifiedVerifyContent; +} diff --git a/src/main/java/com/biutag/supervision/pojo/vo/ExportNegativeVo.java b/src/main/java/com/biutag/supervision/pojo/vo/ExportNegativeVo.java index b80359f..b755f52 100644 --- a/src/main/java/com/biutag/supervision/pojo/vo/ExportNegativeVo.java +++ b/src/main/java/com/biutag/supervision/pojo/vo/ExportNegativeVo.java @@ -144,5 +144,8 @@ public class ExportNegativeVo { @ExcelProperty({"核办情况","市局审批时长"}) private String firstApproveTime; + @ExcelProperty({"核办情况","退回次数"}) + private Integer returnCount; + } diff --git a/src/main/java/com/biutag/supervision/service/ModifiedVerifyContentCellWriteHandler.java b/src/main/java/com/biutag/supervision/service/ModifiedVerifyContentCellWriteHandler.java new file mode 100644 index 0000000..42930f4 --- /dev/null +++ b/src/main/java/com/biutag/supervision/service/ModifiedVerifyContentCellWriteHandler.java @@ -0,0 +1,65 @@ +package com.biutag.supervision.service; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.RichTextString; +import org.apache.poi.ss.usermodel.Workbook; + +public class ModifiedVerifyContentCellWriteHandler implements CellWriteHandler { + + private static final int MODIFIED_VERIFY_CONTENT_COLUMN_INDEX = 6; + + private Font labelFont; + private CellStyle wrappedStyle; + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + if (Boolean.TRUE.equals(context.getHead()) + || !Integer.valueOf(MODIFIED_VERIFY_CONTENT_COLUMN_INDEX).equals(context.getColumnIndex())) { + return; + } + Cell cell = context.getCell(); + String content = cell.getStringCellValue(); + if (StrUtil.isBlank(content)) { + return; + } + Workbook workbook = cell.getSheet().getWorkbook(); + applyWrappedStyle(workbook, cell); + RichTextString richText = workbook.getCreationHelper().createRichTextString(content); + String[] lines = content.split("\\n", -1); + int offset = 0; + for (String line : lines) { + int separatorIndex = line.indexOf(':'); + if (separatorIndex > 0) { + richText.applyFont(offset, offset + separatorIndex, getLabelFont(workbook)); + } + offset += line.length() + 1; + } + float requiredHeight = Math.max(cell.getRow().getHeightInPoints(), lines.length * 18F); + cell.getRow().setHeightInPoints(requiredHeight); + cell.setCellValue(richText); + } + + private Font getLabelFont(Workbook workbook) { + if (labelFont == null) { + labelFont = workbook.createFont(); + labelFont.setBold(true); + labelFont.setColor(IndexedColors.BLACK.getIndex()); + } + return labelFont; + } + + private void applyWrappedStyle(Workbook workbook, Cell cell) { + if (wrappedStyle == null) { + wrappedStyle = workbook.createCellStyle(); + wrappedStyle.cloneStyleFrom(cell.getCellStyle()); + wrappedStyle.setWrapText(true); + } + cell.setCellStyle(wrappedStyle); + } +} diff --git a/src/main/java/com/biutag/supervision/service/NegativeReturnExportService.java b/src/main/java/com/biutag/supervision/service/NegativeReturnExportService.java new file mode 100644 index 0000000..169a8d3 --- /dev/null +++ b/src/main/java/com/biutag/supervision/service/NegativeReturnExportService.java @@ -0,0 +1,165 @@ +package com.biutag.supervision.service; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.biutag.supervision.pojo.entity.NegativeHistory; +import com.biutag.supervision.pojo.vo.ExportNegativeReturnVo; +import com.biutag.supervision.pojo.vo.NegativeQueryVo; +import com.biutag.supervision.util.JSON; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class NegativeReturnExportService { + + private final NegativeHistoryService negativeHistoryService; + + public ExportData build(List data) { + if (data.isEmpty()) { + return new ExportData(Collections.emptyMap(), Collections.emptyList()); + } + List negativeIds = data.stream().map(NegativeQueryVo::getId).toList(); + List histories = negativeHistoryService.list(new LambdaQueryWrapper() + .in(NegativeHistory::getNegativeId, negativeIds) + .orderByAsc(NegativeHistory::getNegativeId) + .orderByAsc(NegativeHistory::getCrtTime)); + Map> historyMap = histories.stream() + .collect(Collectors.groupingBy(NegativeHistory::getNegativeId, LinkedHashMap::new, Collectors.toList())); + Map returnCountMap = new HashMap<>(); + List returnRecords = new ArrayList<>(); + + for (NegativeQueryVo negative : data) { + List negativeHistories = historyMap.getOrDefault(negative.getId(), Collections.emptyList()); + VerifySnapshot latestSnapshot = null; + PendingReturn pendingReturn = null; + int returnCount = 0; + for (NegativeHistory history : negativeHistories) { + ParsedHistory parsedHistory = parseHistory(history); + if (parsedHistory.snapshot() != null) { + latestSnapshot = parsedHistory.snapshot(); + } + if (isReturnAction(parsedHistory.actionKey())) { + returnCount++; + ExportNegativeReturnVo returnVo = new ExportNegativeReturnVo(); + returnVo.setId(negative.getId()); + returnVo.setOriginId(negative.getOriginId()); + returnVo.setReturnCount(returnCount); + returnVo.setReturnTime(history.getCrtTime()); + returnVo.setReturnUser(history.getCrtName()); + returnVo.setReturnOpinion(parsedHistory.comments()); + returnVo.setModifiedVerifyContent("尚未再次提交"); + returnRecords.add(returnVo); + pendingReturn = new PendingReturn(parsedHistory.actionKey(), latestSnapshot, returnVo); + } else if (pendingReturn != null && parsedHistory.snapshot() != null) { + pendingReturn.returnVo().setModifiedVerifyContent( + buildVerifyDiff(pendingReturn.beforeSnapshot(), latestSnapshot)); + } else if (pendingReturn != null && isResubmitAction(pendingReturn.returnActionKey(), parsedHistory.actionKey())) { + pendingReturn.returnVo().setModifiedVerifyContent(buildVerifyDiff(pendingReturn.beforeSnapshot(), latestSnapshot)); + } + } + returnCountMap.put(negative.getId(), returnCount); + } + return new ExportData(returnCountMap, returnRecords); + } + + private ParsedHistory parseHistory(NegativeHistory history) { + if (StrUtil.isBlank(history.getDataJson())) { + return new ParsedHistory(null, null, null); + } + try { + JsonNode root = JSON.readTree(history.getDataJson()); + String actionKey = textValue(root, "actionKey"); + JsonNode actionData = root.path("data"); + String comments = actionData.isObject() ? textValue(actionData, "comments") : null; + VerifySnapshot snapshot = hasVerifyData(actionData) + ? new VerifySnapshot( + textValue(actionData, "checkStatusName"), + textValue(actionData, "isRectifyName"), + textValue(actionData, "checkStatusDesc"), + textValue(actionData, "rectifyDesc")) + : null; + return new ParsedHistory(actionKey, comments, snapshot); + } catch (RuntimeException ignored) { + return new ParsedHistory(null, null, null); + } + } + + private boolean hasVerifyData(JsonNode actionData) { + return actionData.isObject() && (actionData.has("checkStatusName") + || actionData.has("isRectifyName") + || actionData.has("checkStatusDesc") + || actionData.has("rectifyDesc")); + } + + private String textValue(JsonNode node, String fieldName) { + JsonNode value = node.path(fieldName); + return value.isMissingNode() || value.isNull() ? null : value.asText(); + } + + private boolean isReturnAction(String actionKey) { + return "second_approve_return".equals(actionKey) || "first_approve_return".equals(actionKey); + } + + private boolean isResubmitAction(String returnActionKey, String actionKey) { + if ("second_approve_return".equals(returnActionKey)) { + return "apply_completion".equals(actionKey); + } + if ("first_approve_return".equals(returnActionKey)) { + return "second_approve".equals(actionKey) || "apply_completion".equals(actionKey); + } + return false; + } + + private String buildVerifyDiff(VerifySnapshot before, VerifySnapshot after) { + if (before == null || after == null) { + return "无法获取核查办理内容"; + } + List diffs = new ArrayList<>(); + addVerifyDiff(diffs, "核查结论", before.checkStatusName(), after.checkStatusName()); + addVerifyDiff(diffs, "是否整改", before.isRectifyName(), after.isRectifyName()); + addVerifyDiff(diffs, "问题核查情况", before.checkStatusDesc(), after.checkStatusDesc()); + addVerifyDiff(diffs, "问题整改情况", before.rectifyDesc(), after.rectifyDesc()); + return diffs.isEmpty() ? null : String.join("\n", diffs); + } + + private void addVerifyDiff(List diffs, String label, String before, String after) { + String beforeText = normalizeCellText(before); + String afterText = normalizeCellText(after); + if (!Objects.equals(beforeText, afterText)) { + diffs.add(String.format("%s:%s → %s", label, beforeText, afterText)); + } + } + + private String normalizeCellText(String text) { + if (StrUtil.isBlank(text)) { + return "无"; + } + return text.replaceAll("[\\r\\n]+", " ").trim(); + } + + public record ExportData(Map returnCountMap, + List returnRecords) { + } + + private record ParsedHistory(String actionKey, String comments, VerifySnapshot snapshot) { + } + + private record VerifySnapshot(String checkStatusName, String isRectifyName, + String checkStatusDesc, String rectifyDesc) { + } + + private record PendingReturn(String returnActionKey, VerifySnapshot beforeSnapshot, + ExportNegativeReturnVo returnVo) { + } +} diff --git a/src/main/java/com/biutag/supervision/service/NegativeTaskService.java b/src/main/java/com/biutag/supervision/service/NegativeTaskService.java index 827f689..8e24132 100644 --- a/src/main/java/com/biutag/supervision/service/NegativeTaskService.java +++ b/src/main/java/com/biutag/supervision/service/NegativeTaskService.java @@ -23,6 +23,7 @@ import com.biutag.supervision.pojo.param.NegativeQueryParam; import com.biutag.supervision.pojo.param.NegativeTaskQueryParam; import com.biutag.supervision.pojo.vo.ExportNegativeBlameLeaderVo; import com.biutag.supervision.pojo.vo.ExportNegativeBlameVo; +import com.biutag.supervision.pojo.vo.ExportNegativeReturnVo; import com.biutag.supervision.pojo.vo.ExportNegativeVo; import com.biutag.supervision.pojo.vo.NegativeQueryVo; import com.biutag.supervision.util.TimeUtil; @@ -66,6 +67,8 @@ public class NegativeTaskService extends ServiceImpl page(NegativeTaskQueryParam param) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() @@ -135,6 +138,7 @@ public class NegativeTaskService extends ServiceImpl specialSupervisionDict = dictDataService.listByDictType("specialSupervision"); List list = new ArrayList<>(); List blameVoList = new ArrayList<>(); + NegativeReturnExportService.ExportData returnHistoryExportData = negativeReturnExportService.build(data); if (!data.isEmpty()) { List negativeProblemRelations = negativeProblemRelationService.list(new LambdaQueryWrapper().in(NegativeProblemRelation::getNegativeId, data.stream().map(NegativeQueryVo::getId).toList())); List blames = negativeBlameService.list(new LambdaQueryWrapper().in(NegativeBlame::getNegativeId, data.stream().map(NegativeQueryVo::getId).toList())); @@ -142,6 +146,7 @@ public class NegativeTaskService extends ServiceImpl { ExportNegativeVo vo = new ExportNegativeVo(); BeanUtils.copyProperties(item, vo); + vo.setReturnCount(returnHistoryExportData.returnCountMap().getOrDefault(item.getId(), 0)); if (StrUtil.isNotBlank(item.getInvolveProblem())) { String involveProblem = Arrays.stream(item.getInvolveProblem().split(",")) .map(val -> suspectProblem.stream().filter(problem -> val.equals(problem.getDictValue())).findFirst().map(SupDictData::getDictLabel).orElse("")) @@ -265,10 +270,15 @@ public class NegativeTaskService extends ServiceImpl