|
|
@@ -2,13 +2,20 @@ package com.njuzr.eaibackend.service;
|
|
|
|
|
|
import com.njuzr.eaibackend.exception.MyException;
|
|
|
import com.njuzr.eaibackend.mapper.BehaviorOverviewMapper;
|
|
|
+import com.njuzr.eaibackend.mapper.AssignmentMapper;
|
|
|
+import com.njuzr.eaibackend.mapper.ClassMapper;
|
|
|
import com.njuzr.eaibackend.po.BehaviorOverview;
|
|
|
+import com.njuzr.eaibackend.po.Assignment;
|
|
|
+import com.njuzr.eaibackend.po.Class;
|
|
|
import com.njuzr.eaibackend.service.ClassService;
|
|
|
import com.njuzr.eaibackend.vo.BehaviorOverviewVO;
|
|
|
import com.njuzr.eaibackend.vo.StudentInfoVO;
|
|
|
+import com.njuzr.eaibackend.utils.ZipExport;
|
|
|
import java.util.stream.Collectors;
|
|
|
import java.util.Set;
|
|
|
import java.util.Map;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
import org.apache.poi.ss.usermodel.Cell;
|
|
|
import org.apache.poi.ss.usermodel.Row;
|
|
|
import org.apache.poi.ss.usermodel.Sheet;
|
|
|
@@ -20,7 +27,6 @@ import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
import java.io.IOException;
|
|
|
-import java.util.List;
|
|
|
import com.njuzr.eaibackend.vo.PauseStatisticsVO;
|
|
|
|
|
|
/**
|
|
|
@@ -33,13 +39,17 @@ public class ExportBehaviorOverviewService {
|
|
|
private final BehaviorOverviewMapper behaviorOverviewMapper;
|
|
|
private final UserService userService;
|
|
|
private final ClassService classService;
|
|
|
+ private final AssignmentMapper assignmentMapper;
|
|
|
+ private final ClassMapper classMapper;
|
|
|
|
|
|
@Autowired
|
|
|
- public ExportBehaviorOverviewService(BehaviorOverviewService behaviorOverviewService, BehaviorOverviewMapper behaviorOverviewMapper, UserService userService, ClassService classService) {
|
|
|
+ public ExportBehaviorOverviewService(BehaviorOverviewService behaviorOverviewService, BehaviorOverviewMapper behaviorOverviewMapper, UserService userService, ClassService classService, AssignmentMapper assignmentMapper, ClassMapper classMapper) {
|
|
|
this.behaviorOverviewService = behaviorOverviewService;
|
|
|
this.behaviorOverviewMapper = behaviorOverviewMapper;
|
|
|
this.userService = userService;
|
|
|
this.classService = classService;
|
|
|
+ this.assignmentMapper = assignmentMapper;
|
|
|
+ this.classMapper = classMapper;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -64,65 +74,6 @@ public class ExportBehaviorOverviewService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 导出指定 assignmentId 下所有学生的数据到一个 Excel
|
|
|
- * Sheet1: Summary(每行一个学生概览)
|
|
|
- * Sheet2..N: 每个学生一个 Events 明细表(按 studentId 命名,避免超长或重复名冲突)
|
|
|
- */
|
|
|
- public byte[] exportAssignmentBehaviorOverviewExcel(Long assignmentId) {
|
|
|
- try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
|
|
- // Summary 汇总
|
|
|
- Sheet summary = workbook.createSheet("Summary");
|
|
|
- int rowIdx = 0;
|
|
|
- Row header = summary.createRow(rowIdx++);
|
|
|
- String[] headers = new String[]{
|
|
|
- "name", "studentId", "assignmentId", "insertCount", "deleteCount", "copyCount", "copyCharacterCount", "longPauseCount"
|
|
|
- };
|
|
|
- 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) {
|
|
|
- BehaviorOverviewVO vo = behaviorOverviewService.getBehaviorOverview(ov.getStudentId(), assignmentId);
|
|
|
- Row row = summary.createRow(rowIdx++);
|
|
|
- int col = 0;
|
|
|
- String name = idToName != null ? idToName.get(vo.getStudentId()) : null;
|
|
|
- row.createCell(col++).setCellValue(name == null ? "" : name);
|
|
|
- row.createCell(col++).setCellValue(vo.getStudentId() == null ? 0 : vo.getStudentId());
|
|
|
- row.createCell(col++).setCellValue(vo.getAssignmentId() == null ? 0 : vo.getAssignmentId());
|
|
|
- row.createCell(col++).setCellValue(vo.getInsertCount() == null ? 0 : vo.getInsertCount());
|
|
|
- row.createCell(col++).setCellValue(vo.getDeleteCount() == null ? 0 : vo.getDeleteCount());
|
|
|
- row.createCell(col++).setCellValue(vo.getCopyCount() == null ? 0 : vo.getCopyCount());
|
|
|
- row.createCell(col++).setCellValue(vo.getCopyCharacterCount() == null ? 0 : vo.getCopyCharacterCount());
|
|
|
- row.createCell(col).setCellValue(vo.getLongPauseCount() == null ? 0 : vo.getLongPauseCount());
|
|
|
-
|
|
|
- // 为每个学生创建一个事件明细sheet
|
|
|
- String sheetName = safeSheetName("S-" + vo.getStudentId() + "-" + (name != null ? name : "Unknown"));
|
|
|
- Sheet eventsSheet = workbook.createSheet(sheetName);
|
|
|
- createEventsSheet(eventsSheet, vo.getStudentId(), assignmentId);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- 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());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 导出指定 assignmentId 下所有学生的停顿行为统计数据为 Excel
|
|
|
* Sheet1: PauseStatistics(每行一个学生的停顿统计)
|
|
|
@@ -185,18 +136,18 @@ public class ExportBehaviorOverviewService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 导出指定 classId 下所有学生的行为概览数据为 Excel
|
|
|
+ * 导出指定班级和作业的所有学生行为概览数据为 Excel
|
|
|
* Sheet1: Summary(每行一个学生概览)
|
|
|
* Sheet2..N: 每个学生一个 Events 明细表(按 studentId 命名,避免超长或重复名冲突)
|
|
|
*/
|
|
|
- public byte[] exportClassBehaviorOverviewExcel(Long classId) {
|
|
|
+ public byte[] exportClassAssignmentBehaviorOverviewExcel(Long classId, Long assignmentId) {
|
|
|
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
|
|
// Summary 汇总
|
|
|
Sheet summary = workbook.createSheet("Summary");
|
|
|
int rowIdx = 0;
|
|
|
Row header = summary.createRow(rowIdx++);
|
|
|
String[] headers = new String[]{
|
|
|
- "name", "studentId", "insertCount", "deleteCount", "copyCount", "copyCharacterCount", "longPauseCount"
|
|
|
+ "name", "studentId", "assignmentId", "insertCount", "deleteCount", "copyCount", "copyCharacterCount", "longPauseCount"
|
|
|
};
|
|
|
for (int i = 0; i < headers.length; i++) {
|
|
|
header.createCell(i).setCellValue(headers[i]);
|
|
|
@@ -216,27 +167,25 @@ public class ExportBehaviorOverviewService {
|
|
|
Long studentId = student.getStudentId();
|
|
|
if (studentId == null) continue;
|
|
|
|
|
|
- // 查询该学生的所有行为概览
|
|
|
- java.util.List<BehaviorOverview> overviews = behaviorOverviewMapper.findByStudentId(studentId);
|
|
|
- if (overviews != null && !overviews.isEmpty()) {
|
|
|
- for (BehaviorOverview ov : overviews) {
|
|
|
- BehaviorOverviewVO vo = behaviorOverviewService.getBehaviorOverview(studentId, ov.getAssignmentId());
|
|
|
- Row row = summary.createRow(rowIdx++);
|
|
|
- int col = 0;
|
|
|
- String name = idToName != null ? idToName.get(studentId) : student.getStuName();
|
|
|
- row.createCell(col++).setCellValue(name == null ? "" : name);
|
|
|
- row.createCell(col++).setCellValue(vo.getStudentId() == null ? 0 : vo.getStudentId());
|
|
|
- row.createCell(col++).setCellValue(vo.getInsertCount() == null ? 0 : vo.getInsertCount());
|
|
|
- row.createCell(col++).setCellValue(vo.getDeleteCount() == null ? 0 : vo.getDeleteCount());
|
|
|
- row.createCell(col++).setCellValue(vo.getCopyCount() == null ? 0 : vo.getCopyCount());
|
|
|
- row.createCell(col++).setCellValue(vo.getCopyCharacterCount() == null ? 0 : vo.getCopyCharacterCount());
|
|
|
- row.createCell(col).setCellValue(vo.getLongPauseCount() == null ? 0 : vo.getLongPauseCount());
|
|
|
-
|
|
|
- // 为每个学生的每个作业创建一个事件明细sheet,添加作业ID确保名称唯一
|
|
|
- String sheetName = safeSheetName("S-" + vo.getStudentId() + "-" + (name != null ? name : "Unknown") + "-A" + vo.getAssignmentId());
|
|
|
- Sheet eventsSheet = workbook.createSheet(sheetName);
|
|
|
- createEventsSheet(eventsSheet, vo.getStudentId(), vo.getAssignmentId());
|
|
|
- }
|
|
|
+ // 查询该学生在指定作业的行为概览
|
|
|
+ BehaviorOverviewVO vo = behaviorOverviewService.getBehaviorOverview(studentId, assignmentId);
|
|
|
+ if (vo != null) {
|
|
|
+ Row row = summary.createRow(rowIdx++);
|
|
|
+ int col = 0;
|
|
|
+ String name = idToName != null ? idToName.get(studentId) : student.getStuName();
|
|
|
+ row.createCell(col++).setCellValue(name == null ? "" : name);
|
|
|
+ row.createCell(col++).setCellValue(vo.getStudentId() == null ? 0 : vo.getStudentId());
|
|
|
+ row.createCell(col++).setCellValue(vo.getAssignmentId() == null ? 0 : vo.getAssignmentId());
|
|
|
+ row.createCell(col++).setCellValue(vo.getInsertCount() == null ? 0 : vo.getInsertCount());
|
|
|
+ row.createCell(col++).setCellValue(vo.getDeleteCount() == null ? 0 : vo.getDeleteCount());
|
|
|
+ row.createCell(col++).setCellValue(vo.getCopyCount() == null ? 0 : vo.getCopyCount());
|
|
|
+ row.createCell(col++).setCellValue(vo.getCopyCharacterCount() == null ? 0 : vo.getCopyCharacterCount());
|
|
|
+ row.createCell(col).setCellValue(vo.getLongPauseCount() == null ? 0 : vo.getLongPauseCount());
|
|
|
+
|
|
|
+ // 为每个学生创建一个事件明细sheet
|
|
|
+ String sheetName = safeSheetName(name != null ? name : "Unknown");
|
|
|
+ Sheet eventsSheet = workbook.createSheet(sheetName);
|
|
|
+ createEventsSheet(eventsSheet, vo.getStudentId(), assignmentId);
|
|
|
} else {
|
|
|
// 没有行为概览数据的学生
|
|
|
Row row = summary.createRow(rowIdx++);
|
|
|
@@ -244,6 +193,7 @@ public class ExportBehaviorOverviewService {
|
|
|
String name = idToName != null ? idToName.get(studentId) : student.getStuName();
|
|
|
row.createCell(col++).setCellValue(name == null ? "" : name);
|
|
|
row.createCell(col++).setCellValue(studentId);
|
|
|
+ row.createCell(col++).setCellValue(assignmentId);
|
|
|
row.createCell(col++).setCellValue(0);
|
|
|
row.createCell(col++).setCellValue(0);
|
|
|
row.createCell(col++).setCellValue(0);
|
|
|
@@ -260,7 +210,95 @@ public class ExportBehaviorOverviewService {
|
|
|
workbook.write(baos);
|
|
|
return baos.toByteArray();
|
|
|
} catch (IOException e) {
|
|
|
- throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "导出班级行为概览Excel失败" + e.getMessage());
|
|
|
+ throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "导出班级作业行为概览Excel失败" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出指定班级的所有作业数据为 ZIP
|
|
|
+ * 每个作业一个 Excel 文件,打包成 ZIP
|
|
|
+ */
|
|
|
+ public byte[] exportClassBehaviorOverviewZip(Long classId) {
|
|
|
+ try {
|
|
|
+ // 获取班级信息
|
|
|
+ Class cls = classMapper.selectById(classId);
|
|
|
+ if (cls == null) {
|
|
|
+ throw MyException.create(HttpStatus.NOT_FOUND, "班级不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取该班级对应课程的所有作业
|
|
|
+ List<Assignment> assignments = assignmentMapper.selectByCourseId(cls.getCourseId());
|
|
|
+ if (assignments == null || assignments.isEmpty()) {
|
|
|
+ throw MyException.create(HttpStatus.NOT_FOUND, "该班级没有作业数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 为每个作业生成 Excel 文件
|
|
|
+ List<ZipExport.ZipItem> zipItems = new ArrayList<>();
|
|
|
+ for (Assignment assignment : assignments) {
|
|
|
+ try {
|
|
|
+ byte[] excelBytes = exportClassAssignmentBehaviorOverviewExcel(classId, assignment.getAssignmentId());
|
|
|
+ String fileName = safeFileName(assignment.getAssignmentName()) + ".xlsx";
|
|
|
+ zipItems.add(new ZipExport.ZipItem(fileName, excelBytes));
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 跳过生成失败的作业,继续处理其他作业
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (zipItems.isEmpty()) {
|
|
|
+ throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "没有可导出的作业数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 打包成 ZIP
|
|
|
+ return ZipExport.packToZip(zipItems);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "导出班级行为概览ZIP失败" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出指定作业的所有班级数据为 ZIP
|
|
|
+ * 每个班级一个 Excel 文件,打包成 ZIP
|
|
|
+ */
|
|
|
+ public byte[] exportAssignmentBehaviorOverviewZip(Long assignmentId) {
|
|
|
+ try {
|
|
|
+ // 获取作业信息
|
|
|
+ Assignment assignment = assignmentMapper.selectById(assignmentId);
|
|
|
+ if (assignment == null) {
|
|
|
+ throw MyException.create(HttpStatus.NOT_FOUND, "作业不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取该作业对应课程的所有班级
|
|
|
+ List<Class> classes = classMapper.selectList(
|
|
|
+ new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<Class>()
|
|
|
+ .eq("course_id", assignment.getCourseId())
|
|
|
+ );
|
|
|
+
|
|
|
+ if (classes == null || classes.isEmpty()) {
|
|
|
+ throw MyException.create(HttpStatus.NOT_FOUND, "该作业没有班级数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 为每个班级生成 Excel 文件
|
|
|
+ List<ZipExport.ZipItem> zipItems = new ArrayList<>();
|
|
|
+ for (Class cls : classes) {
|
|
|
+ try {
|
|
|
+ byte[] excelBytes = exportClassAssignmentBehaviorOverviewExcel(cls.getClassId(), assignmentId);
|
|
|
+ String fileName = safeFileName(cls.getClassName()) + ".xlsx";
|
|
|
+ zipItems.add(new ZipExport.ZipItem(fileName, excelBytes));
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 跳过生成失败的班级,继续处理其他班级
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (zipItems.isEmpty()) {
|
|
|
+ throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "没有可导出的班级数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 打包成 ZIP
|
|
|
+ return ZipExport.packToZip(zipItems);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "导出作业行为概览ZIP失败" + e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -276,6 +314,14 @@ public class ExportBehaviorOverviewService {
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
+ private String safeFileName(String name) {
|
|
|
+ // 文件名不允许某些字符
|
|
|
+ if (name == null || name.isEmpty()) {
|
|
|
+ return "file";
|
|
|
+ }
|
|
|
+ return name.replaceAll("[\\/:*?\"<>|]", "-");
|
|
|
+ }
|
|
|
+
|
|
|
private void createSummarySheet(Sheet sheet, BehaviorOverviewVO overview) {
|
|
|
int rowIdx = 0;
|
|
|
Row header = sheet.createRow(rowIdx++);
|
|
|
@@ -363,6 +409,4 @@ public class ExportBehaviorOverviewService {
|
|
|
}
|
|
|
|
|
|
// 已改为批量映射方式,不再需要单查姓名方法
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
+}
|