5 changed files with 283 additions and 1 deletions
@ -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; |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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<NegativeQueryVo> data) { |
||||
if (data.isEmpty()) { |
||||
return new ExportData(Collections.emptyMap(), Collections.emptyList()); |
||||
} |
||||
List<String> negativeIds = data.stream().map(NegativeQueryVo::getId).toList(); |
||||
List<NegativeHistory> histories = negativeHistoryService.list(new LambdaQueryWrapper<NegativeHistory>() |
||||
.in(NegativeHistory::getNegativeId, negativeIds) |
||||
.orderByAsc(NegativeHistory::getNegativeId) |
||||
.orderByAsc(NegativeHistory::getCrtTime)); |
||||
Map<String, List<NegativeHistory>> historyMap = histories.stream() |
||||
.collect(Collectors.groupingBy(NegativeHistory::getNegativeId, LinkedHashMap::new, Collectors.toList())); |
||||
Map<String, Integer> returnCountMap = new HashMap<>(); |
||||
List<ExportNegativeReturnVo> returnRecords = new ArrayList<>(); |
||||
|
||||
for (NegativeQueryVo negative : data) { |
||||
List<NegativeHistory> 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<String> 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<String> 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<String, Integer> returnCountMap, |
||||
List<ExportNegativeReturnVo> 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) { |
||||
} |
||||
} |
||||
Loading…
Reference in new issue