Compare commits
3 Commits
912644f02f
...
d55394e5a1
| Author | SHA1 | Date |
|---|---|---|
|
|
d55394e5a1 | 1 month ago |
|
|
e0c20371de | 1 month ago |
|
|
4f63c060ad | 1 month ago |
6 changed files with 346 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,79 @@ |
|||||||
|
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 Font arrowFont; |
||||||
|
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)); |
||||||
|
} |
||||||
|
int arrowIndex = line.indexOf('→', separatorIndex + 1); |
||||||
|
if (arrowIndex >= 0) { |
||||||
|
richText.applyFont(offset + arrowIndex, offset + arrowIndex + 1, getArrowFont(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 Font getArrowFont(Workbook workbook) { |
||||||
|
if (arrowFont == null) { |
||||||
|
arrowFont = workbook.createFont(); |
||||||
|
arrowFont.setBold(true); |
||||||
|
arrowFont.setColor(IndexedColors.RED.getIndex()); |
||||||
|
} |
||||||
|
return arrowFont; |
||||||
|
} |
||||||
|
|
||||||
|
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