From 31f435f23912734a5851c17bbaba1d5400ceb5fd Mon Sep 17 00:00:00 2001 From: "han\\hanst" Date: Fri, 18 Sep 2026 14:32:12 +0800 Subject: [PATCH] Trial prepare --- .../impl/SampleProofTrackingServiceImpl.java | 165 +++++++++++++++--- 1 file changed, 140 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java b/src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java index 6c69a6e3..b31cee8b 100644 --- a/src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java +++ b/src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java @@ -60,8 +60,11 @@ import java.util.Objects; import java.util.Set; import org.apache.poi.ss.usermodel.FillPatternType; -import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFColor; +import org.apache.poi.xssf.usermodel.XSSFFont; /** * 产品打样记录跟踪 Service 实现 @@ -157,6 +160,8 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic buProcessConfigMap, exportHeaderRowCount ); + Map cellAppearances = collectExportCellAppearances( + exportRows, trackingColumns, bodyRows, completedProcessCellKeySet, exportHeaderRowCount); try { String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); String fileName = URLEncoder.encode("项目打样跟踪_" + timestamp, StandardCharsets.UTF_8.name()) @@ -193,9 +198,8 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic .autoCloseStream(false) .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) .registerWriteHandler(noHeaderBackgroundStyle); - if (!completedProcessCellKeySet.isEmpty()) { - // 已完成工序单元格灰底高亮,导出视觉与前端表格“完成态”保持一致。 - writerBuilder.registerWriteHandler(new CompletedProcessCellStyleHandler(completedProcessCellKeySet)); + if (!cellAppearances.isEmpty()) { + writerBuilder.registerWriteHandler(new ProofTrackingCellStyleHandler(cellAppearances)); } writerBuilder .head(headerRows) @@ -398,6 +402,67 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic return rows; } + private Map collectExportCellAppearances( + List exportRows, + List trackingColumns, + List> bodyRows, + Set completedProcessCellKeySet, + int headerRowCount) { + Map appearances = new HashMap<>(); + for (Long cellKey : completedProcessCellKeySet) { + appearances.put(cellKey, ExportCellAppearance.COMPLETED_PROCESS); + } + for (int rowIndex = 0; rowIndex < exportRows.size(); rowIndex++) { + ProofTrackingData row = exportRows.get(rowIndex); + if (row == null) { + continue; + } + for (int columnIndex = 0; columnIndex < trackingColumns.size(); columnIndex++) { + String columnProp = trackingColumns.get(columnIndex).getColumnProp(); + String displayValue = bodyRows.get(rowIndex).get(columnIndex); + ExportCellAppearance appearance = resolveExportCellAppearance(row, columnProp, displayValue); + if (appearance != null) { + appearances.put(buildExportCellCoordinateKey(headerRowCount + rowIndex, columnIndex), appearance); + } + } + } + return appearances; + } + + private ExportCellAppearance resolveExportCellAppearance(ProofTrackingData row, + String columnProp, + String displayValue) { + // 与 projectProofTracking.vue 的链接、状态和预警样式保持一致,不给未同步的编码着色。 + switch (columnProp) { + case "projectNo": + return !isBlank(displayValue) && isNpiAlignedId(row.getProjectId()) + ? ExportCellAppearance.NPI_LINK : null; + case "testPartNo": + return !isBlank(displayValue) && isNpiAlignedId(row.getProjectPartId()) + ? ExportCellAppearance.NPI_LINK : null; + case "proofingStatus": + return TRACKING_PROOFING_STATUS_FINISHED.equals(row.getProofingStatus()) + ? ExportCellAppearance.PROOF_FINISHED : ExportCellAppearance.PROOF_IN_PROGRESS; + case "trackingStatus": + // 复用已生成的导出文案,避免颜色与日期回退、完成态优先级的判断不一致。 + if ("Complete".equals(displayValue)) { + return ExportCellAppearance.TRACKING_COMPLETE; + } + if ("Trial prepare".equals(displayValue) || (displayValue != null && displayValue.startsWith("Delay-"))) { + return ExportCellAppearance.TRACKING_DELAY; + } + if (displayValue != null && displayValue.startsWith("On schedule-")) { + return ExportCellAppearance.TRACKING_ON_SCHEDULE; + } + return ExportCellAppearance.TRACKING_UNKNOWN; + case "deliveryVariance": + return row.getDeliveryVariance() != null && row.getDeliveryVariance() < -7 + ? ExportCellAppearance.DELIVERY_ALERT : null; + default: + return null; + } + } + private static long buildExportCellCoordinateKey(int rowIndex, int columnIndex) { return (((long) rowIndex) << 32) | (columnIndex & 0xffffffffL); } @@ -457,11 +522,15 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic if (rowData == null) { return ""; } + String packageDate = resolveTrackingPackageDateForExport(rowData); + // 与页面一致:打样单号为空且 Delivery&Package 有值时,才显示准备阶段。 + if (isBlank(rowData.getProofingNo()) && !isBlank(packageDate)) { + return "Trial prepare"; + } // 业务口径:打样状态为“打样完成”时,Tracking Status 固定展示 Complete,不再展示 Delay/On schedule。 if (isTrackingProofingCompleted(rowData.getProofingStatus())) { return "Complete"; } - String packageDate = resolveTrackingPackageDateForExport(rowData); String baseline = toDateString(rowData.getBaseline()); if (isBlank(packageDate) || isBlank(baseline)) { return ""; @@ -2987,17 +3056,42 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic data.setProofingStatusList(normalizedStatusList.isEmpty() ? null : normalizedStatusList); } - private static class CompletedProcessCellStyleHandler implements CellWriteHandler { + /** 页面使用的 RGB 色值;不使用近似的 Excel 索引色。 */ + private enum ExportCellAppearance { + NPI_LINK(null, "0C4DBB", false), + PROOF_FINISHED(null, "67C23A", false), + PROOF_IN_PROGRESS(null, "046E97", false), + TRACKING_ON_SCHEDULE("E8F5E9", "303133", false), + TRACKING_DELAY("FDE2E2", "303133", false), + TRACKING_UNKNOWN("F4F4F5", "303133", false), + TRACKING_COMPLETE("F4F4F5", "909399", false), + DELIVERY_ALERT(null, "F56C6C", true), + COMPLETED_PROCESS("ECECEC", "8C8C8C", false); + + private final String background; + private final String foreground; + private final boolean bold; + + ExportCellAppearance(String background, String foreground, boolean bold) { + this.background = background; + this.foreground = foreground; + this.bold = bold; + } + } + + private static class ProofTrackingCellStyleHandler implements CellWriteHandler { - private final Set completedCellKeySet; - private final Map completedStyleCache = new HashMap<>(); + private final Map cellAppearances; + private final Map styleCache = new HashMap<>(); + private final Map fontCache = new HashMap<>(); - private CompletedProcessCellStyleHandler(Set completedCellKeySet) { - this.completedCellKeySet = completedCellKeySet == null ? new LinkedHashSet<>() : completedCellKeySet; + private ProofTrackingCellStyleHandler(Map cellAppearances) { + this.cellAppearances = cellAppearances; } + @Override public int order() { - // 晚于默认样式策略执行,避免灰底被后续样式覆盖。 + // 晚于默认样式策略执行,避免颜色被后续样式覆盖。 return Integer.MAX_VALUE; } @@ -3009,28 +3103,49 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic Head head, Integer relativeRowIndex, Boolean isHead) { - if (cell == null || Boolean.TRUE.equals(isHead) || completedCellKeySet.isEmpty()) { + if (cell == null || Boolean.TRUE.equals(isHead)) { return; } - long cellKey = buildExportCellCoordinateKey(cell.getRowIndex(), cell.getColumnIndex()); - if (!completedCellKeySet.contains(cellKey)) { + ExportCellAppearance appearance = cellAppearances.get( + buildExportCellCoordinateKey(cell.getRowIndex(), cell.getColumnIndex())); + if (appearance == null) { return; } CellStyle sourceStyle = cell.getCellStyle(); Workbook workbook = cell.getSheet().getWorkbook(); - int sourceStyleKey = sourceStyle == null ? -1 : sourceStyle.getIndex(); - CellStyle completedStyle = completedStyleCache.get(sourceStyleKey); - if (completedStyle == null) { - completedStyle = workbook.createCellStyle(); - if (sourceStyle != null) { - completedStyle.cloneStyleFrom(sourceStyle); + String styleKey = sourceStyle.getIndex() + ":" + appearance.name(); + CellStyle styledCell = styleCache.get(styleKey); + if (styledCell == null) { + // 导出固定为 xlsx;流式 SXSSF 工作簿也使用 XSSF 样式和字体。 + XSSFCellStyle style = (XSSFCellStyle) workbook.createCellStyle(); + style.cloneStyleFrom(sourceStyle); + if (appearance.background != null) { + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(toExcelColor(appearance.background)); } - completedStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); - completedStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); - completedStyle.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); - completedStyleCache.put(sourceStyleKey, completedStyle); + String fontKey = sourceStyle.getFontIndex() + ":" + appearance.foreground + ":" + appearance.bold; + XSSFFont font = fontCache.get(fontKey); + if (font == null) { + XSSFFont sourceFont = (XSSFFont) workbook.getFontAt(sourceStyle.getFontIndex()); + font = (XSSFFont) workbook.createFont(); + font.getCTFont().set(sourceFont.getCTFont()); + font.setColor(toExcelColor(appearance.foreground)); + if (appearance.bold) { + font.setBold(true); + } + fontCache.put(fontKey, font); + } + style.setFont(font); + styledCell = style; + styleCache.put(styleKey, styledCell); } - cell.setCellStyle(completedStyle); + cell.setCellStyle(styledCell); + } + + private static XSSFColor toExcelColor(String rgb) { + int color = Integer.parseInt(rgb, 16); + return new XSSFColor(new byte[]{(byte) (color >> 16), (byte) (color >> 8), (byte) color}, + new DefaultIndexedColorMap()); } }