|
@@ -16,6 +16,7 @@ import com.hotent.uc.util.ContextUtil;
|
|
|
import com.hotent.util.FileUploadUtil;
|
|
import com.hotent.util.FileUploadUtil;
|
|
|
import com.hotent.util.wordexcelutils.CompleteTemplateProcessor;
|
|
import com.hotent.util.wordexcelutils.CompleteTemplateProcessor;
|
|
|
import com.hotent.util.wordexcelutils.SmartTemplateWriter;
|
|
import com.hotent.util.wordexcelutils.SmartTemplateWriter;
|
|
|
|
|
+import org.apache.pdfbox.pdmodel.PDDocument;
|
|
|
import org.apache.poi.xwpf.usermodel.*;
|
|
import org.apache.poi.xwpf.usermodel.*;
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
@@ -899,7 +900,7 @@ public class AsyncMaterialSummaryService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 合并单个文档文件到主文档(保持原始样式)
|
|
|
|
|
|
|
+ * 合并单个文档文件到主文档(支持Word、PDF、Excel等多种格式)
|
|
|
*
|
|
*
|
|
|
* @param document 主文档
|
|
* @param document 主文档
|
|
|
* @param fileUrl 文件URL(如:/profile/upload/20251116/xxx.docx)
|
|
* @param fileUrl 文件URL(如:/profile/upload/20251116/xxx.docx)
|
|
@@ -920,6 +921,8 @@ public class AsyncMaterialSummaryService {
|
|
|
return isFirstDocument;
|
|
return isFirstDocument;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ String lowerCasePath = filePath.toLowerCase();
|
|
|
|
|
+
|
|
|
// 添加分页符(非第一个文档)
|
|
// 添加分页符(非第一个文档)
|
|
|
if (!isFirstDocument) {
|
|
if (!isFirstDocument) {
|
|
|
XWPFParagraph pageBreakPara = document.createParagraph();
|
|
XWPFParagraph pageBreakPara = document.createParagraph();
|
|
@@ -927,30 +930,18 @@ public class AsyncMaterialSummaryService {
|
|
|
pageBreakRun.addBreak(BreakType.PAGE);
|
|
pageBreakRun.addBreak(BreakType.PAGE);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 读取Word文件
|
|
|
|
|
- java.io.FileInputStream fis = new java.io.FileInputStream(filePath);
|
|
|
|
|
- XWPFDocument sourceDoc = new XWPFDocument(fis);
|
|
|
|
|
-
|
|
|
|
|
- // 使用XML级别复制,保持原始样式
|
|
|
|
|
- org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody srcBody = sourceDoc.getDocument().getBody();
|
|
|
|
|
- org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody destBody = document.getDocument().getBody();
|
|
|
|
|
-
|
|
|
|
|
- // 复制所有元素(段落、表格等)
|
|
|
|
|
- for (org.apache.xmlbeans.XmlObject obj : srcBody.selectPath("./*")) {
|
|
|
|
|
- if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP) {
|
|
|
|
|
- // 复制段落
|
|
|
|
|
- org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP newP = destBody.addNewP();
|
|
|
|
|
- newP.set(obj.copy());
|
|
|
|
|
- } else if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl) {
|
|
|
|
|
- // 复制表格
|
|
|
|
|
- org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl newTbl = destBody.addNewTbl();
|
|
|
|
|
- newTbl.set(obj.copy());
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 根据文件类型处理
|
|
|
|
|
+ if (lowerCasePath.endsWith(".docx") || lowerCasePath.endsWith(".doc")) {
|
|
|
|
|
+ mergeWordDocument(document, filePath);
|
|
|
|
|
+ } else if (lowerCasePath.endsWith(".pdf")) {
|
|
|
|
|
+ mergePdfDocument(document, filePath);
|
|
|
|
|
+ } else if (lowerCasePath.endsWith(".xlsx") || lowerCasePath.endsWith(".xls")) {
|
|
|
|
|
+ mergeExcelDocument(document, filePath);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.warn("不支持的文件格式:{}", fileUrl);
|
|
|
|
|
+ return isFirstDocument;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- sourceDoc.close();
|
|
|
|
|
- fis.close();
|
|
|
|
|
-
|
|
|
|
|
return false;
|
|
return false;
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
@@ -960,6 +951,130 @@ public class AsyncMaterialSummaryService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
|
+ * 合并Word文档
|
|
|
|
|
+ */
|
|
|
|
|
+ private void mergeWordDocument(XWPFDocument document, String filePath) throws Exception {
|
|
|
|
|
+ java.io.FileInputStream fis = new java.io.FileInputStream(filePath);
|
|
|
|
|
+ XWPFDocument sourceDoc = new XWPFDocument(fis);
|
|
|
|
|
+
|
|
|
|
|
+ org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody srcBody = sourceDoc.getDocument().getBody();
|
|
|
|
|
+ org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody destBody = document.getDocument().getBody();
|
|
|
|
|
+
|
|
|
|
|
+ for (org.apache.xmlbeans.XmlObject obj : srcBody.selectPath("./*")) {
|
|
|
|
|
+ if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP) {
|
|
|
|
|
+ org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP newP = destBody.addNewP();
|
|
|
|
|
+ newP.set(obj.copy());
|
|
|
|
|
+ } else if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl) {
|
|
|
|
|
+ org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl newTbl = destBody.addNewTbl();
|
|
|
|
|
+ newTbl.set(obj.copy());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ sourceDoc.close();
|
|
|
|
|
+ fis.close();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 合并PDF文档(转换为图片后插入)
|
|
|
|
|
+ */
|
|
|
|
|
+ private void mergePdfDocument(XWPFDocument document, String filePath) throws Exception {
|
|
|
|
|
+ try {
|
|
|
|
|
+ PDDocument pdfDoc = PDDocument.load(new java.io.File(filePath));
|
|
|
|
|
+ org.apache.pdfbox.rendering.PDFRenderer renderer = new org.apache.pdfbox.rendering.PDFRenderer(pdfDoc);
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < pdfDoc.getNumberOfPages(); i++) {
|
|
|
|
|
+ java.awt.image.BufferedImage image = renderer.renderImageWithDPI(i, 200);
|
|
|
|
|
+
|
|
|
|
|
+ java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
|
|
|
|
+ javax.imageio.ImageIO.write(image, "png", baos);
|
|
|
|
|
+ byte[] imageBytes = baos.toByteArray();
|
|
|
|
|
+ baos.close();
|
|
|
|
|
+
|
|
|
|
|
+ XWPFParagraph para = document.createParagraph();
|
|
|
|
|
+ XWPFRun run = para.createRun();
|
|
|
|
|
+ java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(imageBytes);
|
|
|
|
|
+ run.addPicture(bais, org.apache.poi.xwpf.usermodel.Document.PICTURE_TYPE_PNG,
|
|
|
|
|
+ "pdf_page_" + i + ".png", org.apache.poi.util.Units.toEMU(410), org.apache.poi.util.Units.toEMU(600));
|
|
|
|
|
+ bais.close();
|
|
|
|
|
+
|
|
|
|
|
+ para.createRun().addBreak();
|
|
|
|
|
+
|
|
|
|
|
+ if (i < pdfDoc.getNumberOfPages() - 1) {
|
|
|
|
|
+ XWPFParagraph pageBreak = document.createParagraph();
|
|
|
|
|
+ XWPFRun pageBreakRun = pageBreak.createRun();
|
|
|
|
|
+ pageBreakRun.addBreak(BreakType.PAGE);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pdfDoc.close();
|
|
|
|
|
+ logger.info("PDF文件已合并:{}", filePath);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ logger.error("合并PDF失败:{},错误:{}", filePath, e.getMessage());
|
|
|
|
|
+ XWPFParagraph para = document.createParagraph();
|
|
|
|
|
+ XWPFRun run = para.createRun();
|
|
|
|
|
+ run.setText("[PDF文件:" + new java.io.File(filePath).getName() + " - 无法嵌入]");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 合并Excel文档(转换为表格后插入)
|
|
|
|
|
+ */
|
|
|
|
|
+ private void mergeExcelDocument(XWPFDocument document, String filePath) throws Exception {
|
|
|
|
|
+ try {
|
|
|
|
|
+ org.apache.poi.ss.usermodel.Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(new java.io.File(filePath));
|
|
|
|
|
+
|
|
|
|
|
+ for (int sheetIndex = 0; sheetIndex < workbook.getNumberOfSheets(); sheetIndex++) {
|
|
|
|
|
+ org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(sheetIndex);
|
|
|
|
|
+
|
|
|
|
|
+ if (sheet.getPhysicalNumberOfRows() == 0) continue;
|
|
|
|
|
+
|
|
|
|
|
+ int rows = sheet.getPhysicalNumberOfRows();
|
|
|
|
|
+ int cols = 0;
|
|
|
|
|
+ for (int i = 0; i < rows; i++) {
|
|
|
|
|
+ org.apache.poi.ss.usermodel.Row row = sheet.getRow(i);
|
|
|
|
|
+ if (row != null && row.getPhysicalNumberOfCells() > cols) {
|
|
|
|
|
+ cols = row.getPhysicalNumberOfCells();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (cols == 0) cols = 1;
|
|
|
|
|
+
|
|
|
|
|
+ XWPFTable table = document.createTable(rows, cols);
|
|
|
|
|
+ table.setStyleID("TableGrid");
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < rows; i++) {
|
|
|
|
|
+ org.apache.poi.ss.usermodel.Row excelRow = sheet.getRow(i);
|
|
|
|
|
+ XWPFTableRow wordRow = table.getRow(i);
|
|
|
|
|
+
|
|
|
|
|
+ if (excelRow != null) {
|
|
|
|
|
+ for (int j = 0; j < cols; j++) {
|
|
|
|
|
+ org.apache.poi.ss.usermodel.Cell cell = excelRow.getCell(j);
|
|
|
|
|
+ String cellValue = cell != null ? cell.toString() : "";
|
|
|
|
|
+
|
|
|
|
|
+ XWPFTableCell wordCell = wordRow.getCell(j);
|
|
|
|
|
+ wordCell.setText(cellValue);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (sheetIndex < workbook.getNumberOfSheets() - 1) {
|
|
|
|
|
+ XWPFParagraph pageBreak = document.createParagraph();
|
|
|
|
|
+ XWPFRun pageBreakRun = pageBreak.createRun();
|
|
|
|
|
+ pageBreakRun.addBreak(BreakType.PAGE);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ workbook.close();
|
|
|
|
|
+ logger.info("Excel文件已合并:{}", filePath);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ logger.error("合并Excel失败:{},错误:{}", filePath, e.getMessage());
|
|
|
|
|
+ XWPFParagraph para = document.createParagraph();
|
|
|
|
|
+ XWPFRun run = para.createRun();
|
|
|
|
|
+ run.setText("[Excel文件:" + new java.io.File(filePath).getName() + " - 无法嵌入,请查看原文件]");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
* 更新单个资料归纳主表的总页数
|
|
* 更新单个资料归纳主表的总页数
|
|
|
*
|
|
*
|
|
|
* @param masterId 主表ID
|
|
* @param masterId 主表ID
|
|
@@ -1054,23 +1169,17 @@ public class AsyncMaterialSummaryService {
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 判断文件类型
|
|
|
|
|
String lowerCaseUrl = fileUrl.toLowerCase();
|
|
String lowerCaseUrl = fileUrl.toLowerCase();
|
|
|
|
|
|
|
|
- if (lowerCaseUrl.endsWith(".xls") || lowerCaseUrl.endsWith(".xlsx")) {
|
|
|
|
|
- // Excel文件算1页
|
|
|
|
|
- return 1;
|
|
|
|
|
- } else if (lowerCaseUrl.endsWith(".doc") || lowerCaseUrl.endsWith(".docx")) {
|
|
|
|
|
- // Word文件需要读取实际页数
|
|
|
|
|
|
|
+ if (lowerCaseUrl.endsWith(".doc") || lowerCaseUrl.endsWith(".docx")) {
|
|
|
return getWordPageCount(fileUrl);
|
|
return getWordPageCount(fileUrl);
|
|
|
} else {
|
|
} else {
|
|
|
- // 其他格式默认1页
|
|
|
|
|
return 1;
|
|
return 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
logger.error("计算文件页数失败,文件:{},错误:{}", fileUrl, e.getMessage(), e);
|
|
logger.error("计算文件页数失败,文件:{},错误:{}", fileUrl, e.getMessage(), e);
|
|
|
- return 0;
|
|
|
|
|
|
|
+ return 1;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1093,17 +1202,27 @@ public class AsyncMaterialSummaryService {
|
|
|
fis = new java.io.FileInputStream(filePath);
|
|
fis = new java.io.FileInputStream(filePath);
|
|
|
document = new XWPFDocument(fis);
|
|
document = new XWPFDocument(fis);
|
|
|
|
|
|
|
|
- // 获取页数
|
|
|
|
|
- int pageCount = document.getProperties().getExtendedProperties().getUnderlyingProperties().getPages();
|
|
|
|
|
|
|
+ // 获取页数(可能为null或0)
|
|
|
|
|
+ try {
|
|
|
|
|
+ int pageCount = document.getProperties().getExtendedProperties().getUnderlyingProperties().getPages();
|
|
|
|
|
+ if (pageCount > 0) {
|
|
|
|
|
+ return pageCount;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ logger.debug("无法读取Word文档页数属性:{}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果页数属性不可用,通过段落数估算(每页约50行)
|
|
|
|
|
+ int paragraphCount = document.getParagraphs().size();
|
|
|
|
|
+ int tableCount = document.getTables().size();
|
|
|
|
|
+ int estimatedPages = Math.max(1, (paragraphCount + tableCount * 10) / 50);
|
|
|
|
|
|
|
|
- return pageCount > 0 ? pageCount : 1;
|
|
|
|
|
|
|
+ return estimatedPages;
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
logger.error("读取Word页数失败,文件:{},错误:{}", fileUrl, e.getMessage(), e);
|
|
logger.error("读取Word页数失败,文件:{},错误:{}", fileUrl, e.getMessage(), e);
|
|
|
- // 读取失败默认返回1页
|
|
|
|
|
return 1;
|
|
return 1;
|
|
|
} finally {
|
|
} finally {
|
|
|
- // 关闭资源
|
|
|
|
|
try {
|
|
try {
|
|
|
if (document != null) {
|
|
if (document != null) {
|
|
|
document.close();
|
|
document.close();
|