|
|
@@ -4,6 +4,8 @@ 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.utils.WordExporter;
|
|
|
+import com.njuzr.eaibackend.utils.ZipExport;
|
|
|
import com.njuzr.eaibackend.vo.AssignmentVO;
|
|
|
import com.njuzr.eaibackend.vo.EngagementVO;
|
|
|
import com.njuzr.eaibackend.vo.UserVO;
|
|
|
@@ -25,9 +27,8 @@ 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;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
@Slf4j
|
|
|
@Service
|
|
|
@@ -41,8 +42,10 @@ public class ExportCompositionService {
|
|
|
private AssignmentService assignmentService;
|
|
|
@Autowired
|
|
|
private UserService userService;
|
|
|
+ @Autowired
|
|
|
+ private WordExporter wordExporter;
|
|
|
|
|
|
- public byte[] exportWord(Long assignmentId, Long studentId) {
|
|
|
+ public byte[] exportWordReal(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);
|
|
|
@@ -54,29 +57,36 @@ public class ExportCompositionService {
|
|
|
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 wordExporter.exportCompositionWord(model);
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- return out.toByteArray();
|
|
|
+ public byte[] exportBatchToZip(List<Long> studentIds, Long assignmentId) {
|
|
|
+ List<ZipExport.ZipItem> zipItems = studentIds.stream()
|
|
|
+ .map(id -> {
|
|
|
+ try {
|
|
|
+ byte[] wordBytes = exportWord(assignmentId, id);
|
|
|
+ String fileName = String.format("%s_%d.docx",
|
|
|
+ "作业", id);
|
|
|
+ log.info("导出成功 studentId={}", id);
|
|
|
+ return new ZipExport.ZipItem(fileName, wordBytes);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("导出失败 studentId={}", id, e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
|
+ try {
|
|
|
+ return ZipExport.packToZip(zipItems);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "ZIP打包失败"+e.getMessage());
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
- public byte[] exportWordTest(Long assignmentId, Long studentId) throws IOException, TemplateException {
|
|
|
+ public byte[] exportWord(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" +
|
|
|
@@ -90,101 +100,11 @@ public class ExportCompositionService {
|
|
|
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();
|
|
|
+ return wordExporter.exportCompositionWord(model);
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
}
|