diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TitleFormatterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TitleFormatterController.java new file mode 100644 index 0000000..6071556 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TitleFormatterController.java @@ -0,0 +1,298 @@ +package com.ruoyi.web.controller.system; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.api.domain.TitleTemplate; +import com.ruoyi.api.service.impl.TitleTemplateServiceImpl; +import com.ruoyi.common.core.domain.AjaxResult; +import org.apache.poi.xwpf.usermodel.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.util.*; + +@RestController +@RequestMapping("/system/formatter") +public class TitleFormatterController { + + @Autowired + private TitleTemplateServiceImpl titleTemplateService; + + @PostMapping + public AjaxResult formatDocument(@RequestParam("file") MultipartFile file, + @RequestParam("template") String templateJson) { + try { + // 解析模板参数 + JSONObject templateObj = JSON.parseObject(templateJson); + Long templateType = Long.parseLong(templateObj.getString("id")); + TitleTemplate titleTemplate = titleTemplateService.selectTitleTemplateById(templateType); + + if (titleTemplate == null) { + return AjaxResult.error("未找到对应的标题模板"); + } + + // 检查文件类型 + if (!file.getOriginalFilename().endsWith(".docx")) { + return AjaxResult.error("仅支持.docx格式的文档"); + } + + // 处理文档 + Map result = processDocumentWithNumbering(file, titleTemplate); + + return AjaxResult.success("文档处理成功", result); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("文档处理失败: " + e.getMessage()); + } + } + + /** + * 处理文档并添加多级标题编号 + */ + private Map processDocumentWithNumbering(MultipartFile file, TitleTemplate titleTemplate) + throws IOException { + Map result = new HashMap<>(); + List> paragraphsInfo = new ArrayList<>(); + + // 初始化各级标题计数器 + int[] levelCounters = new int[5]; // 0不使用,1-4对应各级标题 + Arrays.fill(levelCounters, 0); + + try (InputStream inputStream = file.getInputStream(); + XWPFDocument document = new XWPFDocument(inputStream)) { + + // 获取模板中的标题配置 + String firstTitle = titleTemplate.getFirstTitle(); + String secondTitle = titleTemplate.getSecondTitle(); + String thirdTitle = titleTemplate.getThirdTitle(); + String fourthTitle = titleTemplate.getFourthTitle(); + + System.out.println("=== 开始处理文档 ==="); + System.out.println("模板配置: 一级标题='" + firstTitle + "', 二级标题='" + secondTitle + + "', 三级标题='" + thirdTitle + "', 四级标题='" + fourthTitle + "'"); + + // 遍历文档中的所有段落 + List paragraphs = document.getParagraphs(); + for (int i = 0; i < paragraphs.size(); i++) { + XWPFParagraph paragraph = paragraphs.get(i); + + // 获取段落文本 + String fullText = getParagraphText(paragraph); + if (fullText.isEmpty()) { + continue; + } + + System.out.println("\n--- 处理段落 " + (i + 1) + " ---"); + System.out.println("完整文本: '" + fullText + "'"); + + Map paraInfo = new HashMap<>(); + paraInfo.put("originalText", fullText); + + String numberedText = fullText; + int levelNumber = 0; + String levelName = "正文"; + + // 检查段落是否包含模板字符串并确定标题级别(精确匹配) + if (isExactTemplateMatch(fullText, firstTitle)) { + levelCounters[1]++; + resetLowerLevels(levelCounters, 1); + numberedText = generateLevelNumber(levelCounters, 1) + " " + extractPureContent(fullText, firstTitle); + levelNumber = 1; + levelName = "一级标题"; + setParagraphStyle(paragraph, "Heading1"); + System.out.println("识别为一级标题,新编号: " + numberedText); + } else if (isExactTemplateMatch(fullText, secondTitle)) { + levelCounters[2]++; + resetLowerLevels(levelCounters, 2); + numberedText = generateLevelNumber(levelCounters, 2) + " " + extractPureContent(fullText, secondTitle); + levelNumber = 2; + levelName = "二级标题"; + setParagraphStyle(paragraph, "Heading2"); + System.out.println("识别为二级标题,新编号: " + numberedText); + } else if (isExactTemplateMatch(fullText, thirdTitle)) { + levelCounters[3]++; + resetLowerLevels(levelCounters, 3); + numberedText = generateLevelNumber(levelCounters, 3) + " " + extractPureContent(fullText, thirdTitle); + levelNumber = 3; + levelName = "三级标题"; + setParagraphStyle(paragraph, "Heading3"); + System.out.println("识别为三级标题,新编号: " + numberedText); + } else if (isExactTemplateMatch(fullText, fourthTitle)) { + levelCounters[4]++; + resetLowerLevels(levelCounters, 4); + numberedText = generateLevelNumber(levelCounters, 4) + " " + extractPureContent(fullText, fourthTitle); + levelNumber = 4; + levelName = "四级标题"; + setParagraphStyle(paragraph, "Heading4"); + System.out.println("识别为四级标题,新编号: " + numberedText); + } else { + System.out.println("识别为正文"); + } + + // 设置段落信息 + paraInfo.put("level", levelName); + paraInfo.put("levelNumber", levelNumber); + paraInfo.put("numberedText", numberedText); + paragraphsInfo.add(paraInfo); + + // 更新段落文本 + updateParagraphText(paragraph, numberedText); + } + + } + + result.put("paragraphs", paragraphsInfo); + result.put("summary", String.format( + "文档处理完成:一级标题%d个,二级标题%d个,三级标题%d个,四级标题%d个", + levelCounters[1], levelCounters[2], levelCounters[3], levelCounters[4] + )); + + return result; + } + + /** + * 精确模板匹配 + */ + private boolean isExactTemplateMatch(String text, String template) { + if (text == null || template == null || template.isEmpty()) { + return false; + } + + return text.trim().startsWith(template.trim()); + } + + /** + * 提取纯文本内容 + */ + private String extractPureContent(String fullText, String template) { + if (fullText.startsWith(template)) { + return fullText.substring(template.length()).trim(); + } + return fullText; + } + + /** + * 获取段落文本 + */ + private String getParagraphText(XWPFParagraph paragraph) { + StringBuilder textBuilder = new StringBuilder(); + for (XWPFRun run : paragraph.getRuns()) { + if (run.getText(0) != null) { + textBuilder.append(run.getText(0)); + } + } + return textBuilder.toString().trim(); + } + + /** + * 生成级别编号 + */ + private String generateLevelNumber(int[] levelCounters, int level) { + for (int i = 1; i < levelCounters.length; i++) { + if (levelCounters[i] == 0) { + levelCounters[i] = 1; + } + } + + switch (level) { + case 1: + return generateChineseNumber(levelCounters[1]) + "、"; + case 2: + return "(" + generateChineseNumber(levelCounters[2]) + ")"; + case 3: + return levelCounters[3] + "."; + case 4: + return "(" + levelCounters[4] + ")"; + default: + return ""; + } + } + + /** + * 生成中文数字 + */ + private String generateChineseNumber(int number) { + String[] chineseNumbers = {"", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"}; + if (number <= 10) { + return chineseNumbers[number]; + } else if (number < 20) { + return "十" + (number > 10 ? chineseNumbers[number - 10] : ""); + } else if (number < 100) { + int tens = number / 10; + int units = number % 10; + return chineseNumbers[tens] + "十" + (units > 0 ? chineseNumbers[units] : ""); + } else { + return String.valueOf(number); + } + } + + /** + * 重置下级标题计数器 + */ + private void resetLowerLevels(int[] levelCounters, int currentLevel) { + for (int i = currentLevel + 1; i < levelCounters.length; i++) { + levelCounters[i] = 0; + } + } + + /** + * 更新段落文本 + */ + private void updateParagraphText(XWPFParagraph paragraph, String newText) { + // 清除原有的runs + for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) { + paragraph.removeRun(i); + } + + // 创建新的run并设置文本 + XWPFRun run = paragraph.createRun(); + run.setText(newText); + + // 设置字体样式 + run.setFontSize(12); + if (paragraph.getStyle() != null && paragraph.getStyle().startsWith("Heading")) { + run.setBold(true); + // 根据标题级别设置不同的字体大小 + switch (paragraph.getStyle()) { + case "Heading1": + run.setFontSize(16); + break; + case "Heading2": + run.setFontSize(14); + break; + case "Heading3": + run.setFontSize(13); + break; + case "Heading4": + run.setFontSize(12); + break; + } + } + } + + /** + * 设置段落样式 + */ + private void setParagraphStyle(XWPFParagraph paragraph, String style) { + paragraph.setStyle(style); + if (style.startsWith("Heading")) { + // 设置不同的缩进 + switch (style) { + case "Heading1": + paragraph.setIndentationLeft(0); + break; + case "Heading2": + paragraph.setIndentationLeft(200); + break; + case "Heading3": + paragraph.setIndentationLeft(400); + break; + case "Heading4": + paragraph.setIndentationLeft(600); + break; + } + } + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TitleTemplateController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TitleTemplateController.java new file mode 100644 index 0000000..89d26b2 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TitleTemplateController.java @@ -0,0 +1,64 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import com.ruoyi.api.domain.TitleTemplate; +import com.ruoyi.api.service.ITitleTemplateService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; + + +@RestController +@RequestMapping("/system/template") +public class TitleTemplateController extends BaseController +{ + @Autowired + private ITitleTemplateService titleTemplateService; + + @GetMapping("/list") + public TableDataInfo list(TitleTemplate titleTemplate) + { + startPage(); + List list = titleTemplateService.selectTitleTemplateList(titleTemplate); + return getDataTable(list); + } + + + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(titleTemplateService.selectTitleTemplateById(id)); + } + + + @PostMapping + public AjaxResult add(@RequestBody TitleTemplate titleTemplate) + { + return toAjax(titleTemplateService.insertTitleTemplate(titleTemplate)); + } + + + @PutMapping + public AjaxResult edit(@RequestBody TitleTemplate titleTemplate) + { + return toAjax(titleTemplateService.updateTitleTemplate(titleTemplate)); + } + + + @DeleteMapping("/{id}") + public AjaxResult remove(@PathVariable Long id) + { + return toAjax(titleTemplateService.deleteTitleTemplateById(id)); + } +} \ No newline at end of file diff --git a/ruoyi-api/src/main/java/com/ruoyi/api/domain/TitleTemplate.java b/ruoyi-api/src/main/java/com/ruoyi/api/domain/TitleTemplate.java new file mode 100644 index 0000000..abf2162 --- /dev/null +++ b/ruoyi-api/src/main/java/com/ruoyi/api/domain/TitleTemplate.java @@ -0,0 +1,48 @@ +package com.ruoyi.api.domain; + +public class TitleTemplate { + private String name; + private Long id; + private String firstTitle; + private String secondTitle; + private String thirdTitle; + private String fourthTitle; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getFirstTitle() { + return firstTitle; + } + public void setFirstTitle(String firstTitle) { + this.firstTitle = firstTitle; + } + public String getSecondTitle() { + return secondTitle; + } + public void setSecondTitle(String secondTitle) { + this.secondTitle = secondTitle; + } + public String getThirdTitle() { + return thirdTitle; + } + public void setThirdTitle(String thirdTitle) { + this.thirdTitle = thirdTitle; + } + public String getFourthTitle() { + return fourthTitle; + } + public void setFourthTitle(String fourthTitle) { + this.fourthTitle = fourthTitle; + } + +} diff --git a/ruoyi-api/src/main/java/com/ruoyi/api/mapper/TitleTemplateMapper.java b/ruoyi-api/src/main/java/com/ruoyi/api/mapper/TitleTemplateMapper.java new file mode 100644 index 0000000..628f33d --- /dev/null +++ b/ruoyi-api/src/main/java/com/ruoyi/api/mapper/TitleTemplateMapper.java @@ -0,0 +1,25 @@ +package com.ruoyi.api.mapper; + +import com.ruoyi.api.domain.TitleTemplate; + +import java.util.List; + + +public interface TitleTemplateMapper +{ + + public TitleTemplate selectTitleTemplateById(Long id); + + + public List selectTitleTemplateList(TitleTemplate titleTemplate); + + + public int insertTitleTemplate(TitleTemplate titleTemplate); + + + public int updateTitleTemplate(TitleTemplate titleTemplate); + + + public int deleteTitleTemplateById(Long id); + +} \ No newline at end of file diff --git a/ruoyi-api/src/main/java/com/ruoyi/api/service/ITitleTemplateService.java b/ruoyi-api/src/main/java/com/ruoyi/api/service/ITitleTemplateService.java new file mode 100644 index 0000000..fe81372 --- /dev/null +++ b/ruoyi-api/src/main/java/com/ruoyi/api/service/ITitleTemplateService.java @@ -0,0 +1,24 @@ +package com.ruoyi.api.service; + +import com.ruoyi.api.domain.TitleTemplate; + +import java.util.List; + + + +public interface ITitleTemplateService +{ + public TitleTemplate selectTitleTemplateById(Long id); + + + public List selectTitleTemplateList(TitleTemplate titleTemplate); + + + public int insertTitleTemplate(TitleTemplate titleTemplate); + + + public int updateTitleTemplate(TitleTemplate titleTemplate); + + + public int deleteTitleTemplateById(Long id); +} \ No newline at end of file diff --git a/ruoyi-api/src/main/java/com/ruoyi/api/service/impl/TitleTemplateServiceImpl.java b/ruoyi-api/src/main/java/com/ruoyi/api/service/impl/TitleTemplateServiceImpl.java new file mode 100644 index 0000000..fef6766 --- /dev/null +++ b/ruoyi-api/src/main/java/com/ruoyi/api/service/impl/TitleTemplateServiceImpl.java @@ -0,0 +1,50 @@ +package com.ruoyi.api.service.impl; + +import java.util.List; + +import com.ruoyi.api.domain.TitleTemplate; +import com.ruoyi.api.mapper.TitleTemplateMapper; +import com.ruoyi.api.service.ITitleTemplateService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +@Service +public class TitleTemplateServiceImpl implements ITitleTemplateService +{ + @Autowired + private TitleTemplateMapper titleTemplateMapper; + + @Override + public TitleTemplate selectTitleTemplateById(Long id) + { + return titleTemplateMapper.selectTitleTemplateById(id); + } + + + @Override + public List selectTitleTemplateList(TitleTemplate titleTemplate) + { + return titleTemplateMapper.selectTitleTemplateList(titleTemplate); + } + + + @Override + public int insertTitleTemplate(TitleTemplate titleTemplate) + { + return titleTemplateMapper.insertTitleTemplate(titleTemplate); + } + + + @Override + public int updateTitleTemplate(TitleTemplate titleTemplate) + { + return titleTemplateMapper.updateTitleTemplate(titleTemplate); + } + + @Override + public int deleteTitleTemplateById(Long id) + { + return titleTemplateMapper.deleteTitleTemplateById(id); + } +} \ No newline at end of file diff --git a/ruoyi-api/src/main/resources/mapper/api/TitleTemplateMapper.xml b/ruoyi-api/src/main/resources/mapper/api/TitleTemplateMapper.xml new file mode 100644 index 0000000..e25034e --- /dev/null +++ b/ruoyi-api/src/main/resources/mapper/api/TitleTemplateMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + select id, name, first_title, second_title, third_title, fourth_title from title_template + + + + + + + + insert into title_template + + name, + first_title, + second_title, + third_title, + fourth_title, + + + #{name}, + #{firstTitle}, + #{secondTitle}, + #{thirdTitle}, + #{fourthTitle}, + + + + + update title_template + + name = #{name}, + first_title = #{firstTitle}, + second_title = #{secondTitle}, + third_title = #{thirdTitle}, + fourth_title = #{fourthTitle}, + + where id = #{id} + + + + delete from title_template where id = #{id} + + + + delete from title_template where id in + + #{id} + + + \ No newline at end of file