|
|
@@ -0,0 +1,190 @@
|
|
|
+package com.njuzr.eaibackend.service;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.njuzr.eaibackend.exception.MyException;
|
|
|
+import com.njuzr.eaibackend.mapper.EngagementMapper;
|
|
|
+import com.njuzr.eaibackend.po.Engagement;
|
|
|
+import com.njuzr.eaibackend.vo.AssignmentVO;
|
|
|
+import com.njuzr.eaibackend.vo.EngagementVO;
|
|
|
+import com.njuzr.eaibackend.vo.UserVO;
|
|
|
+import freemarker.template.Configuration;
|
|
|
+import freemarker.template.Template;
|
|
|
+import freemarker.template.TemplateException;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFRun;
|
|
|
+import org.jsoup.Jsoup;
|
|
|
+import org.jsoup.nodes.Element;
|
|
|
+import org.jsoup.nodes.Node;
|
|
|
+import org.jsoup.nodes.TextNode;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.http.HttpStatus;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
|
|
+
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class ExportCompositionService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private Configuration freemarkerConfig;
|
|
|
+ @Autowired
|
|
|
+ private EngagementMapper engagementMapper;
|
|
|
+ @Autowired
|
|
|
+ private AssignmentService assignmentService;
|
|
|
+ @Autowired
|
|
|
+ private UserService userService;
|
|
|
+
|
|
|
+ public byte[] exportWord(Long assignmentId, Long studentId) {
|
|
|
+ AssignmentVO assignment = assignmentService.findAssignmentById(assignmentId);
|
|
|
+ EngagementVO engagement = assignmentService.getEngagement(studentId, assignmentId);
|
|
|
+ UserVO user = userService.searchUsers(new Page<>(1,1), studentId, null, null, null).getRecords().get(0);
|
|
|
+
|
|
|
+ Map<String, Object> model = new HashMap<>();
|
|
|
+ model.put("title", assignment.getAssignmentName());
|
|
|
+ model.put("studentName", user.getName());
|
|
|
+ model.put("assignmentName", assignment.getAssignmentName());
|
|
|
+ model.put("date", new Date());
|
|
|
+ model.put("content", processContent(engagement.getTextContent()));
|
|
|
+
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ try {
|
|
|
+ // 加载模板并渲染
|
|
|
+ Template template = freemarkerConfig.getTemplate("composition.ftl");
|
|
|
+ String htmlContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
|
|
+
|
|
|
+ // 创建Word文档
|
|
|
+ XWPFDocument document = new XWPFDocument();
|
|
|
+ parseHtmlToWord(document, htmlContent); // 关键改造点
|
|
|
+
|
|
|
+ document.write(out);
|
|
|
+ document.close();
|
|
|
+ } catch (Exception e){
|
|
|
+ log.error("exportWord failed. reason: {}", e.getMessage());
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "作业导出失败,请重试");
|
|
|
+ }
|
|
|
+
|
|
|
+ return out.toByteArray();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public byte[] exportWordTest(Long assignmentId, Long studentId) throws IOException, TemplateException {
|
|
|
+
|
|
|
+ String assignmentName = "My Favorite Season";
|
|
|
+ String content = "My favorite season is autumn. The weather is cool and comfortable, and the leaves turn golden, making the scenery beautiful. I enjoy walking in the park and listening to the sound of rustling leaves. Autumn is also a harvest season, with fruits like apples and pears ripe for picking.\n" +
|
|
|
+ "During this season, my family often goes hiking or has picnics. The Mid-Autumn Festival, when we eat mooncakes and admire the full moon, is my favorite holiday. Autumn reminds me of warmth and joy.\n" +
|
|
|
+ "In short, autumn is not only pleasant but also full of happy memories. That’s why I love it most.";
|
|
|
+
|
|
|
+ Map<String, Object> model = new HashMap<>();
|
|
|
+ model.put("title", "title_"+assignmentName);
|
|
|
+ model.put("studentName", "柏琪");
|
|
|
+ model.put("assignmentName", assignmentName);
|
|
|
+ model.put("date", new Date());
|
|
|
+ model.put("content", content.replaceAll("\n", "<br/>"));
|
|
|
+
|
|
|
+ // 3. 加载模板并渲染
|
|
|
+ Template template = freemarkerConfig.getTemplate("composition.ftl");
|
|
|
+ String htmlContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
|
|
+
|
|
|
+ // 3. 创建Word文档
|
|
|
+ XWPFDocument document = new XWPFDocument();
|
|
|
+ parseHtmlToWord(document, htmlContent); // 关键改造点
|
|
|
+
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ document.write(out);
|
|
|
+ document.close();
|
|
|
+
|
|
|
+ return out.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String processContent(String content) {
|
|
|
+ return content.replaceAll("\n", "<br/>");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将HTML内容解析为Word格式(支持基础HTML标签)
|
|
|
+ */
|
|
|
+ private void parseHtmlToWord(XWPFDocument document, String html) {
|
|
|
+ // 使用JSoup解析HTML
|
|
|
+ Element body = Jsoup.parse(html).body();
|
|
|
+
|
|
|
+ // 递归处理所有子节点
|
|
|
+ for (Node node : body.childNodes()) {
|
|
|
+ processNode(document, node);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归处理HTML节点
|
|
|
+ */
|
|
|
+ private void processNode(XWPFDocument doc, Node node) {
|
|
|
+ if (node instanceof TextNode) {
|
|
|
+ // 文本节点直接添加
|
|
|
+ XWPFParagraph para = doc.createParagraph();
|
|
|
+ XWPFRun run = para.createRun();
|
|
|
+ run.setText(((TextNode) node).text());
|
|
|
+ } else if (node instanceof Element) {
|
|
|
+ Element element = (Element) node;
|
|
|
+ switch (element.tagName().toLowerCase()) {
|
|
|
+ case "h1":
|
|
|
+ case "h2":
|
|
|
+ case "h3":
|
|
|
+ case "h4":
|
|
|
+ case "h5":
|
|
|
+ case "h6":
|
|
|
+ // 处理标题
|
|
|
+ XWPFParagraph heading = doc.createParagraph();
|
|
|
+ heading.setStyle("Heading" + element.tagName().charAt(1));
|
|
|
+ XWPFRun headingRun = heading.createRun();
|
|
|
+ headingRun.setText(element.text());
|
|
|
+ headingRun.setBold(true);
|
|
|
+ break;
|
|
|
+ case "p":
|
|
|
+ // 处理段落
|
|
|
+ XWPFParagraph para = doc.createParagraph();
|
|
|
+ XWPFRun run = para.createRun();
|
|
|
+ run.setText(element.text());
|
|
|
+ break;
|
|
|
+ case "b":
|
|
|
+ case "strong":
|
|
|
+ // 加粗文本
|
|
|
+ XWPFParagraph boldPara = doc.createParagraph();
|
|
|
+ XWPFRun boldRun = boldPara.createRun();
|
|
|
+ boldRun.setText(element.text());
|
|
|
+ boldRun.setBold(true);
|
|
|
+ break;
|
|
|
+ case "i":
|
|
|
+ case "em":
|
|
|
+ // 斜体文本
|
|
|
+ XWPFParagraph italicPara = doc.createParagraph();
|
|
|
+ XWPFRun italicRun = italicPara.createRun();
|
|
|
+ italicRun.setText(element.text());
|
|
|
+ italicRun.setItalic(true);
|
|
|
+ break;
|
|
|
+ case "ul":
|
|
|
+ case "ol":
|
|
|
+ // 处理列表
|
|
|
+ for (Element li : element.children()) {
|
|
|
+ XWPFParagraph listPara = doc.createParagraph();
|
|
|
+ XWPFRun listRun = listPara.createRun();
|
|
|
+ listRun.setText("• " + li.text()); // 简单列表符号
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ // 默认处理(递归子节点)
|
|
|
+ for (Node child : node.childNodes()) {
|
|
|
+ processNode(doc, child);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|