Jelajahi Sumber

feat: 实现学生写作内容批量下载接口 /api/class/download 和 /api/assignment/download

wuzilong 5 bulan lalu
induk
melakukan
52b41bd890

+ 57 - 0
src/main/java/com/njuzr/eaibackend/controller/ExportController.java

@@ -4,11 +4,13 @@ import com.njuzr.eaibackend.service.ExportCompositionService;
 import com.njuzr.eaibackend.service.ExportBehaviorOverviewService;
 import com.njuzr.eaibackend.service.UserService;
 import com.njuzr.eaibackend.service.AssignmentService;
+import com.njuzr.eaibackend.service.ClassService;
 import com.njuzr.eaibackend.vo.UserVO;
 import com.njuzr.eaibackend.vo.AssignmentVO;
 import freemarker.template.TemplateException;
 import org.apache.commons.lang3.tuple.Pair;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
@@ -17,6 +19,7 @@ import org.springframework.web.bind.annotation.*;
 import java.io.IOException;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
+import java.util.Date;
 import java.util.List;
 
 @RestController
@@ -30,6 +33,8 @@ public class ExportController {
     private UserService userService;
     @Autowired
     private AssignmentService assignmentService;
+    @Autowired
+    private ClassService classService;
 
     @GetMapping("/composition/word")
     public ResponseEntity<byte[]> exportWord(@RequestParam Long assignmentId, @RequestParam Long studentId) throws TemplateException, IOException {
@@ -133,4 +138,56 @@ public class ExportController {
                 .headers(headers)
                 .body(bytes);
     }
+
+    @GetMapping("/class/download")
+    public ResponseEntity<byte[]> downloadClassAssignments(@RequestParam Long classId) {
+        byte[] zipBytes = exportCompositionService.downloadClassAssignments(classId);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
+        String className = null;
+        try {
+            com.njuzr.eaibackend.po.Class cls = classService.getClassById(classId);
+            className = cls != null ? cls.getClassName() : null;
+        } catch (Exception ignored) { }
+
+        String fileName = className == null || className.isEmpty()
+                ? String.format("class_%d_%s.zip", classId, new java.text.SimpleDateFormat("yyyyMMdd").format(new Date()))
+                : String.format("%s_%s.zip", className, new java.text.SimpleDateFormat("yyyyMMdd").format(new Date()));
+
+        ContentDisposition contentDisposition = ContentDisposition.builder("attachment")
+                .filename(fileName, StandardCharsets.UTF_8)
+                .build();
+        headers.setContentDisposition(contentDisposition);
+
+        return ResponseEntity.ok()
+                .headers(headers)
+                .body(zipBytes);
+    }
+
+    @GetMapping("/assignment/download")
+    public ResponseEntity<byte[]> downloadAssignmentSubmissions(@RequestParam Long assignmentId) {
+        byte[] zipBytes = exportCompositionService.downloadAssignmentSubmissions(assignmentId);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
+        String assignmentName = null;
+        try {
+            AssignmentVO vo = assignmentService.findAssignmentById(assignmentId);
+            assignmentName = vo != null ? vo.getAssignmentName() : null;
+        } catch (Exception ignored) { }
+
+        String fileName = assignmentName == null || assignmentName.isEmpty()
+                ? String.format("assignment_%d_%s.zip", assignmentId, new java.text.SimpleDateFormat("yyyyMMdd").format(new Date()))
+                : String.format("%s_%s.zip", assignmentName, new java.text.SimpleDateFormat("yyyyMMdd").format(new Date()));
+
+        ContentDisposition contentDisposition = ContentDisposition.builder("attachment")
+                .filename(fileName, StandardCharsets.UTF_8)
+                .build();
+        headers.setContentDisposition(contentDisposition);
+
+        return ResponseEntity.ok()
+                .headers(headers)
+                .body(zipBytes);
+    }
 }

+ 128 - 0
src/main/java/com/njuzr/eaibackend/service/ExportCompositionService.java

@@ -1,15 +1,20 @@
 package com.njuzr.eaibackend.service;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.njuzr.eaibackend.exception.MyException;
 import com.njuzr.eaibackend.mapper.EngagementMapper;
 import com.njuzr.eaibackend.mapper.serviceIpml.EngagementMapperServiceImpl;
 import com.njuzr.eaibackend.po.Engagement;
+import com.njuzr.eaibackend.po.User;
+import com.njuzr.eaibackend.po.Assignment;
 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;
+import com.njuzr.eaibackend.vo.StudentInfoVO;
+import com.njuzr.eaibackend.vo.EngagementHistoryVO;
 import freemarker.template.Configuration;
 import freemarker.template.TemplateException;
 import lombok.extern.slf4j.Slf4j;
@@ -39,6 +44,10 @@ public class ExportCompositionService {
     private WordExporter wordExporter;
     @Autowired
     private EngagementMapperServiceImpl engagementMapperServiceImpl;
+    @Autowired
+    private com.njuzr.eaibackend.mapper.UserMapper userMapper;
+    @Autowired
+    private com.njuzr.eaibackend.mapper.AssignmentMapper assignmentMapper;
 
     public Pair<UserVO, byte[]> exportWord(Long assignmentId, Long studentId) {
         AssignmentVO assignment = assignmentService.findAssignmentById(assignmentId);
@@ -125,4 +134,123 @@ public class ExportCompositionService {
         return content.replaceAll("\n", "<br/>");
     }
 
+    public byte[] downloadClassAssignments(Long classId) {
+        // 获取班级信息
+        com.njuzr.eaibackend.po.Class cls = classService.getClassById(classId);
+        if (cls == null) {
+            throw MyException.create(HttpStatus.BAD_REQUEST, "班级不存在");
+        }
+
+        // 获取班级所有学生
+        List<StudentInfoVO> students = classService.getStudentsByClassId(classId);
+        if (students.isEmpty()) {
+            throw MyException.create(HttpStatus.BAD_REQUEST, "班级无学生");
+        }
+
+        // 获取班级对应的课程ID
+        Long courseId = cls.getCourseId();
+        if (courseId == null) {
+            throw MyException.create(HttpStatus.BAD_REQUEST, "班级未关联课程");
+        }
+
+        // 获取课程的所有作业
+        List<Assignment> courseAssignments = assignmentMapper.selectByCourseId(courseId);
+        if (courseAssignments.isEmpty()) {
+            throw MyException.create(HttpStatus.BAD_REQUEST, "课程无作业");
+        }
+
+        // 生成ZIP文件
+        try {
+            List<ZipExport.ZipItem> zipItems = new ArrayList<>();
+            for (StudentInfoVO student : students) {
+                // 获取学生ID
+                String officialNumber = student.getOfficialNumber();
+                User user = userMapper.selectOne(new QueryWrapper<User>().eq("official_number", officialNumber));
+                if (user == null) {
+                    continue;
+                }
+                Long studentId = user.getId();
+                String studentDir = user.getOfficialNumber() + "_" + user.getName() + "/";
+                
+                // 对每个作业,获取学生的提交历史
+                for (Assignment assignment : courseAssignments) {
+                    Long assignmentId = assignment.getAssignmentId();
+                    try {
+                        // 获取作业历史记录
+                        List<EngagementHistoryVO> historyList = assignmentService.getEngagementHistory(studentId, assignmentId);
+                        for (EngagementHistoryVO history : historyList) {
+                            // 生成Word文档
+                            Map<String, Object> model = new HashMap<>();
+                            model.put("title", assignment.getAssignmentName());
+                            model.put("studentName", user.getName());
+                            model.put("officialNumber", user.getOfficialNumber());
+                            model.put("assignmentName", assignment.getAssignmentName());
+                            model.put("date", history.getSubmitTime());
+                            model.put("content", processContent(history.getTextContent()));
+                            
+                            byte[] wordBytes = wordExporter.exportCompositionWord(model);
+                            String fileName = studentDir + user.getOfficialNumber() + "_" + user.getName() + "_版本" + history.getVersion() + ".docx";
+                            zipItems.add(new ZipExport.ZipItem(fileName, wordBytes));
+                        }
+                    } catch (Exception e) {
+                        // 学生可能未提交该作业,跳过
+                        log.warn("学生 {} 未提交作业 {},跳过", studentId, assignmentId);
+                    }
+                }
+            }
+            return ZipExport.packToZip(zipItems);
+        } catch (IOException e) {
+            throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "ZIP打包失败" + e.getMessage());
+        }
+    }
+
+    public byte[] downloadAssignmentSubmissions(Long assignmentId) {
+        // 获取作业信息
+        AssignmentVO assignment = assignmentService.findAssignmentById(assignmentId);
+        if (assignment == null) {
+            throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
+        }
+
+        // 获取所有提交该作业的学生
+        List<Engagement> engagements = engagementMapperServiceImpl.getEngagementByAssignmentId(assignmentId);
+        if (engagements.isEmpty()) {
+            throw MyException.create(HttpStatus.BAD_REQUEST, "无学生提交该作业");
+        }
+
+        // 生成ZIP文件
+        try {
+            List<ZipExport.ZipItem> zipItems = new ArrayList<>();
+            for (Engagement engagement : engagements) {
+                Long studentId = engagement.getStudentId();
+                // 获取学生信息
+                UserVO user = userService.searchUsers(new Page<>(1, 1), studentId, null, null, null).getRecords().get(0);
+                String studentDir = user.getOfficialNumber() + "_" + user.getName() + "/";
+                
+                // 获取作业历史记录
+                List<EngagementHistoryVO> historyList = assignmentService.getEngagementHistory(studentId, assignmentId);
+                for (EngagementHistoryVO history : historyList) {
+                    // 生成Word文档
+                    Map<String, Object> model = new HashMap<>();
+                    model.put("title", assignment.getAssignmentName());
+                    model.put("studentName", user.getName());
+                    model.put("officialNumber", user.getOfficialNumber());
+                    model.put("assignmentName", assignment.getAssignmentName());
+                    model.put("date", history.getSubmitTime());
+                    model.put("content", processContent(history.getTextContent()));
+                    
+                    byte[] wordBytes = wordExporter.exportCompositionWord(model);
+                    String fileName = studentDir + user.getOfficialNumber() + "_" + user.getName() + "_版本" + history.getVersion() + ".docx";
+                    zipItems.add(new ZipExport.ZipItem(fileName, wordBytes));
+                }
+            }
+            return ZipExport.packToZip(zipItems);
+        } catch (IOException e) {
+            throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "ZIP打包失败" + e.getMessage());
+        }
+    }
+
+    // 需要注入 ClassService
+    @Autowired
+    private ClassService classService;
+
 }