Просмотр исходного кода

Merge branch 'feat/behavior_record' of FanYanPeng/EAI-Backend into refactor

LiuLuLin 1 год назад
Родитель
Сommit
8115e49190

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

@@ -107,4 +107,30 @@ public class ExportController {
                 .headers(headers)
                 .body(bytes);
     }
+
+    @GetMapping("/pauseStatistics/excel/assignment")
+    public ResponseEntity<byte[]> exportAssignmentPauseStatisticsExcel(
+            @RequestParam Long assignmentId,
+            @RequestParam(defaultValue = "2000") Integer threshold) {
+        byte[] bytes = exportBehaviorOverviewService.exportAssignmentPauseStatisticsExcel(assignmentId, threshold);
+
+        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("pause_statistics_assignment_%d_threshold_%d.xlsx", assignmentId, threshold)
+                : String.format("pause_statistics_assignment_%d_%s_threshold_%d.xlsx", assignmentId, assignmentName, threshold);
+
+        headers.setContentDispositionFormData("attachment",
+                URLEncoder.encode(fileName, StandardCharsets.UTF_8));
+
+        return ResponseEntity.ok()
+                .headers(headers)
+                .body(bytes);
+    }
 }

+ 62 - 0
src/main/java/com/njuzr/eaibackend/service/ExportBehaviorOverviewService.java

@@ -18,6 +18,7 @@ import org.springframework.stereotype.Service;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import com.njuzr.eaibackend.vo.PauseStatisticsVO;
 
 /**
  * 将 behaviorOverviews 的数据导出为 Excel
@@ -117,6 +118,67 @@ public class ExportBehaviorOverviewService {
         }
     }
 
+    /**
+     * 导出指定 assignmentId 下所有学生的停顿行为统计数据为 Excel
+     * Sheet1: PauseStatistics(每行一个学生的停顿统计)
+     */
+    public byte[] exportAssignmentPauseStatisticsExcel(Long assignmentId, Integer threshold) {
+        try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            // PauseStatistics 汇总
+            Sheet summary = workbook.createSheet("PauseStatistics");
+            int rowIdx = 0;
+            Row header = summary.createRow(rowIdx++);
+            String[] headers = new String[]{
+                    "name", "studentId", "assignmentId", "totalPauseTime(ms)", "pauseCount", 
+                    "averagePauseTime(ms)", "medianPauseTime(ms)", "standardDeviation(ms)", "threshold(ms)"
+            };
+            for (int i = 0; i < headers.length; i++) {
+                header.createCell(i).setCellValue(headers[i]);
+            }
+
+            // 查询该作业下所有概览
+            java.util.List<BehaviorOverview> overviews = behaviorOverviewMapper.findByAssignmentId(assignmentId);
+            if (overviews != null) {
+                // 先批量拉取 studentId -> name 映射,避免 N 次查询
+                Set<Long> studentIds = overviews.stream()
+                        .map(BehaviorOverview::getStudentId)
+                        .filter(java.util.Objects::nonNull)
+                        .collect(Collectors.toSet());
+                Map<Long, String> idToName = userService.getNamesByIds(studentIds);
+                
+                for (BehaviorOverview ov : overviews) {
+                    Long studentId = ov.getStudentId();
+                    if (studentId == null) continue;
+                    
+                    // 获取停顿统计数据(使用传入的阈值参数)
+                    PauseStatisticsVO pauseStats = behaviorOverviewService.getPauseStatistics(studentId, assignmentId, threshold);
+                    String name = idToName != null ? idToName.get(studentId) : null;
+                    
+                    Row row = summary.createRow(rowIdx++);
+                    int col = 0;
+                    row.createCell(col++).setCellValue(name == null ? "" : name);
+                    row.createCell(col++).setCellValue(pauseStats.getStudentId() == null ? 0 : pauseStats.getStudentId());
+                    row.createCell(col++).setCellValue(pauseStats.getAssignmentId() == null ? 0 : pauseStats.getAssignmentId());
+                    row.createCell(col++).setCellValue(pauseStats.getTotalPauseTime() == null ? 0 : pauseStats.getTotalPauseTime());
+                    row.createCell(col++).setCellValue(pauseStats.getPauseCount() == null ? 0 : pauseStats.getPauseCount());
+                    row.createCell(col++).setCellValue(pauseStats.getAveragePauseTime() == null ? 0.0 : pauseStats.getAveragePauseTime());
+                    row.createCell(col++).setCellValue(pauseStats.getMedianPauseTime() == null ? 0.0 : pauseStats.getMedianPauseTime());
+                    row.createCell(col++).setCellValue(pauseStats.getStandardDeviation() == null ? 0.0 : pauseStats.getStandardDeviation());
+                    row.createCell(col).setCellValue(pauseStats.getThreshold() == null ? 0 : pauseStats.getThreshold());
+                }
+            }
+
+            for (int i = 0; i < headers.length; i++) {
+                summary.autoSizeColumn(i);
+            }
+
+            workbook.write(baos);
+            return baos.toByteArray();
+        } catch (IOException e) {
+            throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "导出停顿统计Excel失败" + e.getMessage());
+        }
+    }
+
     private String safeSheetName(String name) {
         // Excel sheet 名称长度限制31,且不允许某些字符
         String n = name.replaceAll("[\\\\/:*?\\n\\r\\[\\]]", "-");