oa审批方案插入模板逻辑调整

This commit is contained in:
zhaolei 2025-10-30 09:11:00 +08:00
parent 2a613a8fcf
commit 462c434eb3
2 changed files with 291 additions and 142 deletions

View File

@ -423,145 +423,183 @@ public class OutputWordUtil {
FormFile formFile = processfile.get(0);
DCContext fileDCContext = SDK.getBOAPI().getFileDCContext(formFile);
// 加载目标文件和待合并文档2
Document docs1 = new Document();
docs1.loadFromFile(outFile.getPath());
Document docs2 = new Document();
docs2.loadFromFile(fileDCContext.getFilePath());
try {
// 删除docs1的最后一个节保留原有逻辑
Section lastSection1 = docs1.getLastSection();
docs1.getSections().remove(lastSection1);
Section lastSection1 = docs1.getLastSection();
// 在docs2中查找"三、流程图"的位置
TextSelection[] selectionsInDocs2 = docs2.findAllString("三、流程图", true, true);
// 删除最后一个节
docs1.getSections().remove(lastSection1);
if (selectionsInDocs2.length > 0) {
// 获取第一个匹配的位置
TextSelection selectionInDocs2 = selectionsInDocs2[0];
// 提取所有表格
List<Table> allTables = extractAllTables(docs2);
// 获取包含文本的段落 - 通过遍历节和段落来查找
Paragraph targetParagraphInDocs2 = findParagraphContainingSelection(docs2, selectionInDocs2);
if (allTables.size() >= 4) {
// 拆分表格前2个表格后2个表格
List<Table> firstTwoTables = allTables.subList(0, 1);
if (targetParagraphInDocs2 != null) {
// 提取docs2中"三、流程图"前面和后面的内容
List<DocumentObject> contentBeforeFlowChart = extractContentBeforeParagraph(docs2, targetParagraphInDocs2);
List<DocumentObject> contentAfterFlowChart = extractContentAfterParagraph(docs2, targetParagraphInDocs2);
TextPosition position = findTextPosition(docs1, "流程图");
// 在docs1中查找"流程图"文本位置
Paragraph targetParagraphInDocs1= findAutoNumberedFlowchartParagraph(docs1);
if (position != null) {
// 在前面插入前2个表格
insertTablesBeforeText(docs1, firstTwoTables, position);
// 获取docs2中剩余的内容除了前两个表格
List<DocumentObject> remainingContent = extractRemainingContent(docs2, firstTwoTables.size());
// 将剩余内容插入到docs1的最后一节
insertContentToLastSection(docs1, remainingContent);
if (targetParagraphInDocs1 != null) {
System.out.println("成功找到自动编号的流程图段落: '" + targetParagraphInDocs1.getText().trim() + "'");
// 保存合并后的文档
docs1.saveToFile(outFile.getPath(), FileFormat.Docx);
if (targetParagraphInDocs1 != null) {
// 在docs1的"流程图"前面插入docs2中"三、流程图"前面的内容
insertContentBeforeParagraph(docs1, contentBeforeFlowChart, targetParagraphInDocs1);
docs1.dispose();
docs2.dispose();
// 在docs1的"流程图"后面插入docs2中"三、流程图"后面的内容
insertContentAfterParagraph(docs1, contentAfterFlowChart, targetParagraphInDocs1);
System.out.println("文档合并成功!");
// 保存合并后的文档
docs1.saveToFile(outFile.getPath(), FileFormat.Docx);
System.out.println("文档合并成功!");
} else {
System.out.println("在目标文档中未找到包含'流程图'的段落");
}
} else {
System.out.println("在目标文档中未找到文本'流程图'");
}
} else {
System.out.println("在源文档中未找到包含'三、流程图'的段落");
}
} else {
System.out.println("未找到文本'流程图'");
System.out.println("在源文档中未找到文本'三、流程图'");
}
} else {
System.out.println("源文档中的表格数量不足4个当前只有: " + allTables.size());
} finally {
docs1.dispose();
docs2.dispose();
}
}
/**
* 提取剩余内容除了前n个表格
* 查找自动编号的"1.流程图"段落
*/
private static List<DocumentObject> extractRemainingContent(Document document, int tablesToSkip) {
List<DocumentObject> remainingContent = new ArrayList<>();
int skippedTables = 0;
private static Paragraph findAutoNumberedFlowchartParagraph(Document document) {
System.out.println("开始查找自动编号的流程图段落...");
for (int i = 0; i < document.getSections().getCount(); i++) {
Section section = document.getSections().get(i);
for (int j = 0; j < section.getBody().getChildObjects().getCount(); j++) {
DocumentObject obj = section.getBody().getChildObjects().get(j);
for (Section section : (Iterable<Section>) document.getSections()) {
for (Paragraph paragraph : (Iterable<Paragraph>) section.getParagraphs()) {
String paragraphText = paragraph.getText().trim();
if (obj instanceof Table) {
if (skippedTables < tablesToSkip) {
skippedTables++;
continue; // 跳过前n个表格
// 检查段落是否包含"流程图"
if (paragraphText.contains("流程图")) {
System.out.println("找到包含'流程图'的段落: '" + paragraphText + "'");
// 检查是否是自动编号段落
if (isAutoNumberedParagraph(paragraph)) {
System.out.println("这是一个自动编号段落");
// 进一步验证是否符合"1.流程图"的格式
if (isNumberedFlowchartFormat(paragraph, paragraphText)) {
System.out.println("符合编号流程图格式");
return paragraph;
}
} else {
System.out.println("这不是自动编号段落");
}
}
// 克隆对象以避免重复引用问题
remainingContent.add(obj.deepClone());
}
}
return remainingContent;
}
/**
* 将内容插入到最后一节
*/
private static void insertContentToLastSection(Document docs1, List<DocumentObject> content) {
int lastSectionIndex = docs1.getSections().getCount() - 1;
Section lastSection = docs1.getSections().get(lastSectionIndex);
// 在插入前添加分页符可选
Paragraph pageBreak = new Paragraph(docs1);
pageBreak.appendBreak(BreakType.Page_Break);
lastSection.getBody().getChildObjects().add(pageBreak);
// 插入剩余内容
for (DocumentObject obj : content) {
lastSection.getBody().getChildObjects().add(obj);
}
}
/**
* 提取文档中的所有表格
*/
private static List<Table> extractAllTables(Document doc) {
List<Table> tables = new ArrayList<>();
for (Object sectionObj : doc.getSections()) {
Section section = (Section) sectionObj;
for (Object obj : section.getBody().getChildObjects()) {
if (obj instanceof Table) {
tables.add((Table) obj);
}
}
}
return tables;
System.out.println("未找到符合条件的自动编号流程图段落");
return null;
}
/**
* 查找文本位置
* 验证是否符合编号流程图的格式
*/
private static TextPosition findTextPosition(Document doc, String targetText) {
for (int s = 0; s < doc.getSections().getCount(); s++) {
Section section = doc.getSections().get(s);
private static boolean isNumberedFlowchartFormat(Paragraph paragraph, String paragraphText) {
// 检查文本是否包含"流程图"
if (!paragraphText.contains("流程图")) {
return false;
}
for (int p = 0; p < section.getParagraphs().getCount(); p++) {
Paragraph paragraph = section.getParagraphs().get(p);
String paragraphText = paragraph.getText();
// 检查格式数字 + + 文本
// 匹配 "1.流程图", "1. 流程图", "1.流程图说明" 等格式
if (paragraphText.matches("^\\d+\\..*流程图.*") ||
paragraphText.matches("^\\d+\\.\\s*流程图.*")) {
return true;
}
if (paragraphText.contains(targetText)) {
return new TextPosition(s, p, paragraphText.indexOf(targetText));
// 对于复杂的自动编号可能数字和文本是分开的
// 这种情况下主要依靠 isAutoNumberedParagraph 的判断
return true;
}
/**
* 判断段落是否是自动编号段落
*/
private static boolean isAutoNumberedParagraph(Paragraph paragraph) {
// 方法1检查列表格式
if (paragraph.getListFormat() != null) {
// 检查是否有列表级别
if (paragraph.getListFormat().getListLevelNumber() >= 0) {
System.out.println(" 通过列表级别判断为自动编号");
return true;
}
}
// 方法2检查格式特征
if (paragraph.getFormat() != null) {
// 自动编号通常有特定的缩进
if (paragraph.getFormat().getLeftIndent() > 0 ||
paragraph.getFormat().getFirstLineIndent() != 0) {
System.out.println(" 通过缩进格式判断为自动编号");
return true;
}
}
// 方法3检查文本模式数字. 开头
String text = paragraph.getText().trim();
if (text.matches("^\\d+\\..*")) {
System.out.println(" 通过文本模式判断为自动编号");
return true;
}
return false;
}
/**
* 查找包含文本选择的段落
*/
private static Paragraph findParagraphContainingSelection(Document doc, TextSelection selection) {
for (int i = 0; i < doc.getSections().getCount(); i++) {
Section section = doc.getSections().get(i);
for (int j = 0; j < section.getParagraphs().getCount(); j++) {
Paragraph paragraph = section.getParagraphs().get(j);
// 检查这个段落是否包含选中的文本
if (paragraph.getText().contains(selection.getSelectedText())) {
return paragraph;
}
}
}
@ -569,65 +607,154 @@ public class OutputWordUtil {
}
/**
* 在指定文本前插入表格
* 提取文档中指定段落之前的所有内容
*/
private static void insertTablesBeforeText(Document mainDoc, List<Table> tables, TextPosition position) {
Section mainSection = mainDoc.getSections().get(position.getSectionIndex());
Paragraph targetParagraph = mainSection.getParagraphs().get(position.getParagraphIndex());
int insertIndex = mainSection.getBody().getChildObjects().indexOf(targetParagraph);
private static List<DocumentObject> extractContentBeforeParagraph(Document doc, Paragraph targetParagraph) {
List<DocumentObject> content = new ArrayList<>();
// 在前方插入表格反向插入以保持顺序
for (int i = tables.size() - 1; i >= 0; i--) {
Table clonedTable = tables.get(i).deepClone();
mainSection.getBody().getChildObjects().insert(insertIndex, clonedTable);
boolean foundTarget = false;
// 在每个表格前添加一个空段落作为间隔
Paragraph spacingPara = new Paragraph(mainDoc);
mainSection.getBody().getChildObjects().insert(insertIndex, spacingPara);
// 遍历所有节
for (int i = 0; i < doc.getSections().getCount(); i++) {
Section section = doc.getSections().get(i);
// 遍历节中的所有子对象
for (int j = 0; j < section.getBody().getChildObjects().getCount(); j++) {
DocumentObject obj = section.getBody().getChildObjects().get(j);
if (obj == targetParagraph) {
foundTarget = true;
break;
}
// 克隆并添加对象
DocumentObject clonedObj = cloneDocumentObject(obj);
if (clonedObj != null) {
content.add(clonedObj);
}
}
if (foundTarget) {
break;
}
}
return content;
}
/**
* 在指定文本后插入表格
* 提取文档中指定段落之后的所有内容
*/
private static void insertTablesAfterText(Document mainDoc, List<Table> tables, TextPosition position) {
Section mainSection = mainDoc.getSections().get(position.getSectionIndex());
Paragraph targetParagraph = mainSection.getParagraphs().get(position.getParagraphIndex());
int insertIndex = mainSection.getBody().getChildObjects().indexOf(targetParagraph) + 1;
private static List<DocumentObject> extractContentAfterParagraph(Document doc, Paragraph targetParagraph) {
List<DocumentObject> content = new ArrayList<>();
// 在后方插入表格
for (Table table : tables) {
Table clonedTable = table.deepClone();
mainSection.getBody().getChildObjects().insert(insertIndex, clonedTable);
insertIndex++;
boolean foundTarget = false;
// 在每个表格后添加一个空段落作为间隔
Paragraph spacingPara = new Paragraph(mainDoc);
mainSection.getBody().getChildObjects().insert(insertIndex, spacingPara);
insertIndex++;
// 遍历所有节
for (int i = 0; i < doc.getSections().getCount(); i++) {
Section section = doc.getSections().get(i);
// 遍历节中的所有子对象
for (int j = 0; j < section.getBody().getChildObjects().getCount(); j++) {
DocumentObject obj = section.getBody().getChildObjects().get(j);
if (foundTarget) {
// 克隆并添加目标之后的对象
DocumentObject clonedObj = cloneDocumentObject(obj);
if (clonedObj != null) {
content.add(clonedObj);
}
}
if (obj == targetParagraph) {
foundTarget = true;
}
}
}
return content;
}
/**
* 文本位置信息类
* 在指定段落前插入内容
*/
static class TextPosition {
private int sectionIndex;
private int paragraphIndex;
private int textOffset;
private static void insertContentBeforeParagraph(Document doc, List<DocumentObject> content, Paragraph targetParagraph) {
// 找到目标段落所在的节和索引
int targetIndex = -1;
Section targetSection = null;
public TextPosition(int sectionIndex, int paragraphIndex, int textOffset) {
this.sectionIndex = sectionIndex;
this.paragraphIndex = paragraphIndex;
this.textOffset = textOffset;
for (int i = 0; i < doc.getSections().getCount(); i++) {
Section section = doc.getSections().get(i);
for (int j = 0; j < section.getBody().getChildObjects().getCount(); j++) {
if (section.getBody().getChildObjects().get(j) == targetParagraph) {
targetSection = section;
targetIndex = j;
break;
}
}
if (targetSection != null) break;
}
public int getSectionIndex() { return sectionIndex; }
public int getParagraphIndex() { return paragraphIndex; }
public int getTextOffset() { return textOffset; }
if (targetSection != null && targetIndex != -1) {
// 在目标位置之前插入内容逆序插入以保证顺序正确
for (int i = content.size() - 1; i >= 0; i--) {
DocumentObject obj = content.get(i);
targetSection.getBody().getChildObjects().insert(targetIndex, obj);
}
}
}
/**
* 在指定段落之后插入内容
*/
private static void insertContentAfterParagraph(Document document, List<DocumentObject> content, Paragraph targetParagraph) {
int lastSectionIndex = document.getSections().getCount() - 1;
Section lastSection = document.getSections().get(lastSectionIndex);
// 在插入前添加分页符可选
Paragraph pageBreak = new Paragraph(document);
pageBreak.appendBreak(BreakType.Page_Break);
lastSection.getBody().getChildObjects().add(pageBreak);
// 插入剩余内容
for (DocumentObject obj : content) {
lastSection.getBody().getChildObjects().add(obj);
}
}
/**
* 克隆文档对象
*/
private static DocumentObject cloneDocumentObject(DocumentObject obj) {
try {
if (obj instanceof Paragraph) {
return ((Paragraph) obj).deepClone();
} else if (obj instanceof Table) {
return ((Table) obj).deepClone();
}
// 可以添加其他类型的处理
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 生成word文件
*
@ -1073,13 +1200,17 @@ public class OutputWordUtil {
for (int i = 2; i < doc2.getSections().getCount(); i++) {
Section targetSection = doc2.getSections().get(i);
PageSetup pageSetup = targetSection.getPageSetup();
PageOrientation orientation = pageSetup.getOrientation();
// 根据合并文档节的页面方向选择模板
PageOrientation mergeOrientation = pageSetup.getOrientation();
Document templateDoc = (mergeOrientation == PageOrientation.Landscape) ? doc3 : doc1;
double width = targetSection.getPageSetup().getPageSize().getWidth();
double height = targetSection.getPageSetup().getPageSize().getHeight();
// 实际方向根据尺寸
boolean actualLandscape = isLandscapeBySize(width, height);
Document templateDoc = actualLandscape ? doc3 : doc1;
// 复制模板文档的页眉和页脚到新文档
HeaderFooter sourceHeader = templateDoc.getSections().get(0).getHeadersFooters().getHeader();
@ -1114,10 +1245,16 @@ public class OutputWordUtil {
Section targetSection = doc2.getSections().get(i);
HeaderFooter targetFooter = targetSection.getHeadersFooters().getFooter();
PageSetup pageSetup = targetSection.getPageSetup();
double width = targetSection.getPageSetup().getPageSize().getWidth();
double height = targetSection.getPageSetup().getPageSize().getHeight();
// 实际方向根据尺寸
boolean actualLandscape = isLandscapeBySize(width, height);
Document templateDoc = actualLandscape ? doc3 : doc1;
PageOrientation mergeOrientation = pageSetup.getOrientation();
Document templateDoc = (mergeOrientation == PageOrientation.Landscape) ? doc3 : doc1;
HeaderFooter sourceFooter = templateDoc.getSections().get(0).getHeadersFooters().getFooter();
targetFooter.getChildObjects().clear();
@ -1859,6 +1996,18 @@ public class OutputWordUtil {
}
/**
* 判断是否为横向
* @param width
* @param height
* @return
*/
public static boolean isLandscapeBySize(double width, double height) {
return width > height;
}
/**
* 生成word文件
*