Browse Source

20261019

master
han\hanst 3 weeks ago
parent
commit
5808505dab
  1. 20
      src/main/java/com/xujie/sys/modules/sampletracking/controller/SampleProofTrackingController.java
  2. 4
      src/main/java/com/xujie/sys/modules/sampletracking/service/SampleProofTrackingService.java
  3. 324
      src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java

20
src/main/java/com/xujie/sys/modules/sampletracking/controller/SampleProofTrackingController.java

@ -67,6 +67,26 @@ public class SampleProofTrackingController {
}
}
@PostMapping("/exportPdf")
public void exportProofTrackingPdf(@RequestBody ProofTrackingData data, HttpServletResponse response) {
try {
sampleProofTrackingService.exportProofTrackingPdf(data, response);
} catch (Exception e) {
log.error("导出产品打样记录跟踪PDF失败: {}", e.getMessage(), e);
throw new RuntimeException("导出产品打样记录跟踪PDF失败: " + e.getMessage());
}
}
@PostMapping("/exportImage")
public void exportProofTrackingImage(@RequestBody ProofTrackingData data, HttpServletResponse response) {
try {
sampleProofTrackingService.exportProofTrackingImage(data, response);
} catch (Exception e) {
log.error("导出产品打样记录跟踪图片失败: {}", e.getMessage(), e);
throw new RuntimeException("导出产品打样记录跟踪图片失败: " + e.getMessage());
}
}
@PostMapping("/oneKeyCreate")
public R oneKeyCreate(@RequestBody ProofTrackingData data) {
try {

4
src/main/java/com/xujie/sys/modules/sampletracking/service/SampleProofTrackingService.java

@ -44,6 +44,10 @@ public interface SampleProofTrackingService {
void exportProofTracking(ProofTrackingData data, HttpServletResponse response);
void exportProofTrackingPdf(ProofTrackingData data, HttpServletResponse response);
void exportProofTrackingImage(ProofTrackingData data, HttpServletResponse response);
List<ProofTrackingProcessLogData> getProcessHistory(ProofTrackingData data);
List<ProofTrackingBuProcessConfigData> queryBuProcessConfig(ProofTrackingData data);

324
src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java

@ -37,6 +37,14 @@ import com.xujie.sys.modules.sampletracking.service.SampleProofTrackingService;
import com.xujie.sys.modules.sampletracking.service.SampleTrackingService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
@ -87,6 +95,11 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic
private static final String PROOFING_STATUS_SCRAPPED = "报废";
private static final String NPI_PROOFING_STATUS_FINISHED = "完成";
private static final int NPI_ID_THRESHOLD = 100000;
private static final String PROOF_TRACKING_PDF_FONT = "Microsoft YaHei";
/** 列按内容放宽,超过这个宽度就换行,Comments 与物料描述使用同一规则。 */
private static final double PDF_MAX_COLUMN_WIDTH = 36D;
private static final double PDF_COLUMN_WIDTH_PADDING = 2D;
private static volatile boolean proofTrackingPdfFontsRegistered;
/**
* 工序定义兜底:当配置表未初始化或查询异常时,仍保持页面可用。
@ -131,6 +144,51 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic
@Override
public void exportProofTracking(ProofTrackingData data, HttpServletResponse response) {
ProofTrackingExportSheet sheet = buildProofTrackingExportSheet(data);
String fileName = buildProofTrackingExportFileName();
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
writeProofTrackingWorkbook(response.getOutputStream(), sheet, false);
response.flushBuffer();
} catch (Exception e) {
log.error("导出产品打样记录跟踪失败: {}", e.getMessage(), e);
throw new RuntimeException("导出产品打样记录跟踪失败: " + e.getMessage());
}
}
@Override
public void exportProofTrackingPdf(ProofTrackingData data, HttpServletResponse response) {
// PDF 直接转换同一份工作簿,列、单元格文字和颜色与 Excel 导出保持一致。
ProofTrackingExportSheet sheet = buildProofTrackingExportSheet(data);
String fileName = buildProofTrackingExportFileName();
try {
ByteArrayOutputStream excelBuffer = new ByteArrayOutputStream();
writeProofTrackingWorkbook(excelBuffer, sheet, true);
writeProofTrackingPdfResponse(response, excelBuffer.toByteArray(), fileName);
} catch (Exception e) {
log.error("导出产品打样记录跟踪PDF失败: {}", e.getMessage(), e);
throw new RuntimeException("导出产品打样记录跟踪PDF失败: " + e.getMessage());
}
}
@Override
public void exportProofTrackingImage(ProofTrackingData data, HttpServletResponse response) {
// 图片使用和 PDF 相同的工作簿与列宽,避免两套导出内容不一致。
ProofTrackingExportSheet sheet = buildProofTrackingExportSheet(data);
String fileName = buildProofTrackingExportFileName();
try {
ByteArrayOutputStream excelBuffer = new ByteArrayOutputStream();
writeProofTrackingWorkbook(excelBuffer, sheet, true);
writeProofTrackingImageResponse(response, excelBuffer.toByteArray(), fileName);
} catch (Exception e) {
log.error("导出产品打样记录跟踪图片失败: {}", e.getMessage(), e);
throw new RuntimeException("导出产品打样记录跟踪图片失败: " + e.getMessage());
}
}
private ProofTrackingExportSheet buildProofTrackingExportSheet(ProofTrackingData data) {
if (data == null) {
throw new RuntimeException("导出参数不能为空");
}
@ -173,53 +231,225 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic
);
Map<Long, ExportCellAppearance> 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())
.replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 业务要求:表头不使用底色,避免默认灰底影响业务二次标注。
WriteCellStyle headCellStyle = new WriteCellStyle();
// 表头字体大小固定为 12,确保与业务模板一致。
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 12);
headCellStyle.setWriteFont(headWriteFont);
// 表头背景色:无填充
headCellStyle.setFillPatternType(FillPatternType.NO_FILL);
// 边框:全部取消
headCellStyle.setBorderLeft(BorderStyle.NONE);
headCellStyle.setBorderRight(BorderStyle.NONE);
headCellStyle.setBorderTop(BorderStyle.NONE);
headCellStyle.setBorderBottom(BorderStyle.NONE);
WriteCellStyle contentCellStyle = new WriteCellStyle();
contentCellStyle.setBorderLeft(BorderStyle.NONE);
contentCellStyle.setBorderRight(BorderStyle.NONE);
contentCellStyle.setBorderTop(BorderStyle.NONE);
contentCellStyle.setBorderBottom(BorderStyle.NONE);
HorizontalCellStyleStrategy noHeaderBackgroundStyle =
new HorizontalCellStyleStrategy(headCellStyle, contentCellStyle);
ExcelWriterBuilder writerBuilder = EasyExcel.write(response.getOutputStream())
.autoCloseStream(false)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(noHeaderBackgroundStyle);
if (!cellAppearances.isEmpty()) {
writerBuilder.registerWriteHandler(new ProofTrackingCellStyleHandler(cellAppearances));
}
writerBuilder
.head(headerRows)
.sheet("项目打样跟踪")
.doWrite(bodyRows);
response.flushBuffer();
} catch (Exception e) {
log.error("导出产品打样记录跟踪失败: {}", e.getMessage(), e);
throw new RuntimeException("导出产品打样记录跟踪失败: " + e.getMessage());
return new ProofTrackingExportSheet(headerRows, bodyRows, cellAppearances);
}
private String buildProofTrackingExportFileName() {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
return URLEncoder.encode("项目打样跟踪_" + timestamp, StandardCharsets.UTF_8)
.replaceAll("\\+", "%20");
}
private void writeProofTrackingWorkbook(OutputStream outputStream,
ProofTrackingExportSheet sheet,
boolean cjkFont) {
// 业务要求:表头不使用底色,避免默认灰底影响业务二次标注。
WriteCellStyle headCellStyle = new WriteCellStyle();
// 表头字体大小固定为 12,确保与业务模板一致。
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 12);
if (cjkFont) {
headWriteFont.setFontName(PROOF_TRACKING_PDF_FONT);
}
headCellStyle.setWriteFont(headWriteFont);
// 表头背景色:无填充
headCellStyle.setFillPatternType(FillPatternType.NO_FILL);
// 边框:全部取消
headCellStyle.setBorderLeft(BorderStyle.NONE);
headCellStyle.setBorderRight(BorderStyle.NONE);
headCellStyle.setBorderTop(BorderStyle.NONE);
headCellStyle.setBorderBottom(BorderStyle.NONE);
WriteCellStyle contentCellStyle = new WriteCellStyle();
contentCellStyle.setBorderLeft(BorderStyle.NONE);
contentCellStyle.setBorderRight(BorderStyle.NONE);
contentCellStyle.setBorderTop(BorderStyle.NONE);
contentCellStyle.setBorderBottom(BorderStyle.NONE);
if (cjkFont) {
// 默认 Calibri 没有中文字形,PDF 转换会变成方框;只在 PDF 这条链路指定中文字体。
WriteFont contentWriteFont = new WriteFont();
contentWriteFont.setFontName(PROOF_TRACKING_PDF_FONT);
contentCellStyle.setWriteFont(contentWriteFont);
}
HorizontalCellStyleStrategy noHeaderBackgroundStyle =
new HorizontalCellStyleStrategy(headCellStyle, contentCellStyle);
ExcelWriterBuilder writerBuilder = EasyExcel.write(outputStream)
.autoCloseStream(false)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(noHeaderBackgroundStyle);
if (sheet.cellAppearances != null && !sheet.cellAppearances.isEmpty()) {
writerBuilder.registerWriteHandler(new ProofTrackingCellStyleHandler(sheet.cellAppearances));
}
writerBuilder
.head(sheet.headerRows)
.sheet("项目打样跟踪")
.doWrite(sheet.bodyRows);
}
private void writeProofTrackingPdfResponse(HttpServletResponse response, byte[] excelBytes, String fileName) throws Exception {
registerProofTrackingPdfFonts();
com.aspose.cells.Workbook workbook = new com.aspose.cells.Workbook(new ByteArrayInputStream(excelBytes));
com.aspose.cells.Worksheet worksheet = workbook.getWorksheets().get(0);
layoutProofTrackingPdfColumns(worksheet);
com.aspose.cells.PageSetup pageSetup = worksheet.getPageSetup();
pageSetup.setOrientation(com.aspose.cells.PageOrientationType.LANDSCAPE);
pageSetup.setPaperSize(com.aspose.cells.PaperSizeType.PAPER_A_3);
// 高度仍按 A3 横向分页;列宽不在这里等比缩小,避免文字被裁切。
pageSetup.setFitToPages(0, 0);
pageSetup.setLeftMargin(0.3);
pageSetup.setRightMargin(0.3);
pageSetup.setTopMargin(0.4);
pageSetup.setBottomMargin(0.4);
com.aspose.cells.PdfSaveOptions saveOptions = new com.aspose.cells.PdfSaveOptions();
saveOptions.setDefaultFont(PROOF_TRACKING_PDF_FONT);
// 所有列放进同一页宽,页宽随列展开;行数超出页高时再向下分页。
saveOptions.setOnePagePerSheet(false);
saveOptions.setAllColumnsInOnePagePerSheet(true);
ByteArrayOutputStream pdfBuffer = new ByteArrayOutputStream();
workbook.save(pdfBuffer, saveOptions);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName + ".pdf");
response.getOutputStream().write(pdfBuffer.toByteArray());
response.flushBuffer();
}
private void writeProofTrackingImageResponse(HttpServletResponse response, byte[] excelBytes, String fileName) throws Exception {
registerProofTrackingPdfFonts();
com.aspose.cells.Workbook workbook = new com.aspose.cells.Workbook(new ByteArrayInputStream(excelBytes));
com.aspose.cells.Worksheet worksheet = workbook.getWorksheets().get(0);
layoutProofTrackingPdfColumns(worksheet);
com.aspose.cells.ImageOrPrintOptions options = new com.aspose.cells.ImageOrPrintOptions();
options.setImageType(com.aspose.cells.ImageType.PNG);
options.setDefaultFont(PROOF_TRACKING_PDF_FONT);
options.setOnlyArea(true);
// 96 DPI 放大后发虚;按 2 倍分辨率绘制,文字边缘更清楚。
options.setHorizontalResolution(192);
options.setVerticalResolution(192);
options.setRenderingHint(java.awt.RenderingHints.KEY_RENDERING, java.awt.RenderingHints.VALUE_RENDER_QUALITY);
options.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
options.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
options.setRenderingHint(java.awt.RenderingHints.KEY_INTERPOLATION, java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC);
// 整表画成一张图,列和换行规则与 PDF 相同,不再按页拆开。
options.setOnePagePerSheet(true);
options.setAllColumnsInOnePagePerSheet(true);
byte[] imageBytes = renderProofTrackingSheetPng(worksheet, options);
response.setContentType("image/png");
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName + ".png");
response.getOutputStream().write(imageBytes);
response.flushBuffer();
}
private byte[] renderProofTrackingSheetPng(com.aspose.cells.Worksheet worksheet,
com.aspose.cells.ImageOrPrintOptions options) throws Exception {
com.aspose.cells.SheetRender render = new com.aspose.cells.SheetRender(worksheet, options);
int pageCount = render.getPageCount();
if (pageCount <= 0) {
throw new RuntimeException("没有可导出的图片内容");
}
if (pageCount == 1) {
ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream();
render.toImage(0, imageBuffer);
return imageBuffer.toByteArray();
}
// 行数过多被拆成多页时,按原顺序纵向拼成一张图,内容和 PDF 保持完整。
List<BufferedImage> pageImages = new ArrayList<>();
int imageWidth = 0;
int imageHeight = 0;
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
ByteArrayOutputStream pageBuffer = new ByteArrayOutputStream();
render.toImage(pageIndex, pageBuffer);
BufferedImage pageImage = ImageIO.read(new ByteArrayInputStream(pageBuffer.toByteArray()));
if (pageImage == null) {
throw new RuntimeException("导出图片失败");
}
pageImages.add(pageImage);
imageWidth = Math.max(imageWidth, pageImage.getWidth());
imageHeight += pageImage.getHeight();
}
BufferedImage combinedImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = combinedImage.createGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, imageWidth, imageHeight);
int offsetY = 0;
for (BufferedImage pageImage : pageImages) {
graphics.drawImage(pageImage, 0, offsetY, null);
offsetY += pageImage.getHeight();
}
graphics.dispose();
ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream();
if (!ImageIO.write(combinedImage, "png", imageBuffer)) {
throw new RuntimeException("导出图片失败");
}
return imageBuffer.toByteArray();
}
private void layoutProofTrackingPdfColumns(com.aspose.cells.Worksheet worksheet) throws Exception {
com.aspose.cells.Cells cells = worksheet.getCells();
int maxRow = cells.getMaxDataRow();
int maxCol = cells.getMaxDataColumn();
if (maxRow < 0 || maxCol < 0) {
return;
}
com.aspose.cells.Style wrapStyle = worksheet.getWorkbook().createStyle();
wrapStyle.setTextWrapped(true);
wrapStyle.setVerticalAlignment(com.aspose.cells.TextAlignmentType.TOP);
com.aspose.cells.StyleFlag wrapFlag = new com.aspose.cells.StyleFlag();
wrapFlag.setWrapText(true);
wrapFlag.setVerticalAlignment(true);
cells.createRange(0, 0, maxRow + 1, maxCol + 1).applyStyle(wrapStyle, wrapFlag);
worksheet.autoFitColumns();
for (int columnIndex = 0; columnIndex <= maxCol; columnIndex++) {
double fittedWidth = cells.getColumnWidth(columnIndex) + PDF_COLUMN_WIDTH_PADDING;
if (fittedWidth > PDF_MAX_COLUMN_WIDTH) {
fittedWidth = PDF_MAX_COLUMN_WIDTH;
}
cells.setColumnWidth(columnIndex, fittedWidth);
}
// 列宽封顶后必须重算行高,换行后的文字才会完整落在单元格里。
worksheet.autoFitRows();
}
private void registerProofTrackingPdfFonts() {
if (proofTrackingPdfFontsRegistered) {
return;
}
synchronized (SampleProofTrackingServiceImpl.class) {
if (proofTrackingPdfFontsRegistered) {
return;
}
String windir = System.getenv("WINDIR");
if (windir != null) {
File windowsFonts = new File(windir, "Fonts");
if (windowsFonts.isDirectory()) {
com.aspose.cells.FontConfigs.setFontFolder(windowsFonts.getAbsolutePath(), true);
}
}
File linuxFonts = new File("/usr/share/fonts");
if (linuxFonts.isDirectory()) {
com.aspose.cells.FontConfigs.setFontFolder(linuxFonts.getAbsolutePath(), true);
}
com.aspose.cells.FontConfigs.setFontSubstitutes(PROOF_TRACKING_PDF_FONT,
new String[]{"微软雅黑", "SimSun", "Noto Sans CJK SC", "WenQuanYi Micro Hei"});
proofTrackingPdfFontsRegistered = true;
}
}
private static final class ProofTrackingExportSheet {
private final List<List<String>> headerRows;
private final List<List<String>> bodyRows;
private final Map<Long, ExportCellAppearance> cellAppearances;
private ProofTrackingExportSheet(List<List<String>> headerRows,
List<List<String>> bodyRows,
Map<Long, ExportCellAppearance> cellAppearances) {
this.headerRows = headerRows;
this.bodyRows = bodyRows;
this.cellAppearances = cellAppearances;
}
}

Loading…
Cancel
Save