|
@@ -0,0 +1,439 @@
|
|
|
|
|
+package com.njuzr.eaibackend.service.impl;
|
|
|
|
|
+
|
|
|
|
|
+import com.njuzr.eaibackend.mapper.BehaviorRecordMapper;
|
|
|
|
|
+import com.njuzr.eaibackend.mapper.BehaviorOverviewMapper;
|
|
|
|
|
+import com.njuzr.eaibackend.po.BehaviorRecord;
|
|
|
|
|
+import com.njuzr.eaibackend.po.BehaviorOverview;
|
|
|
|
|
+import com.njuzr.eaibackend.service.BehaviorOverviewService;
|
|
|
|
|
+import com.njuzr.eaibackend.vo.BehaviorOverviewVO;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.data.domain.Sort;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Date;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Optional;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * BehaviorOverview 业务服务实现类
|
|
|
|
|
+ * @author WuZilong
|
|
|
|
|
+ * @date 2025-08-10
|
|
|
|
|
+ */
|
|
|
|
|
+@Service
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+public class BehaviorOverviewServiceImpl implements BehaviorOverviewService {
|
|
|
|
|
+
|
|
|
|
|
+ private final BehaviorRecordMapper behaviorRecordMapper;
|
|
|
|
|
+ private final BehaviorOverviewMapper behaviorOverviewMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ public BehaviorOverviewServiceImpl(BehaviorRecordMapper behaviorRecordMapper,
|
|
|
|
|
+ BehaviorOverviewMapper behaviorOverviewMapper) {
|
|
|
|
|
+ this.behaviorRecordMapper = behaviorRecordMapper;
|
|
|
|
|
+ this.behaviorOverviewMapper = behaviorOverviewMapper;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 异步更新BehaviorOverview集合
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Async("behaviorOverviewTaskExecutor")
|
|
|
|
|
+ public void updateBehaviorOverviewAsync(Long assignmentId, Long studentId, List<BehaviorRecord.BehaviorEvent> events) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ log.info("开始异步更新BehaviorOverview: studentId={}, assignmentId={}, eventsCount={}",
|
|
|
|
|
+ studentId, assignmentId, events.size());
|
|
|
|
|
+
|
|
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
|
|
+ updateBehaviorOverview(assignmentId, studentId, events);
|
|
|
|
|
+ long endTime = System.currentTimeMillis();
|
|
|
|
|
+
|
|
|
|
|
+ log.info("异步更新BehaviorOverview完成: studentId={}, assignmentId={}, 耗时={}ms",
|
|
|
|
|
+ studentId, assignmentId, (endTime - startTime));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("异步更新BehaviorOverview失败: studentId={}, assignmentId={}, error={}",
|
|
|
|
|
+ studentId, assignmentId, e.getMessage(), e);
|
|
|
|
|
+ // 这里可以添加失败重试逻辑、死信队列等处理方案
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新BehaviorOverview集合
|
|
|
|
|
+ */
|
|
|
|
|
+ private void updateBehaviorOverview(Long assignmentId, Long studentId, List<BehaviorRecord.BehaviorEvent> newEvents) {
|
|
|
|
|
+ // 查找现有的BehaviorOverview记录
|
|
|
|
|
+ Optional<BehaviorOverview> existingOverview = behaviorOverviewMapper
|
|
|
|
|
+ .findByAssignmentIdAndStudentId(assignmentId, studentId);
|
|
|
|
|
+
|
|
|
|
|
+ BehaviorOverview overview;
|
|
|
|
|
+ Long timestampOffset = 0L; // 时间戳偏移量
|
|
|
|
|
+
|
|
|
|
|
+ if (existingOverview.isPresent()) {
|
|
|
|
|
+ // 更新现有记录 - 不修改createdAt字段
|
|
|
|
|
+ overview = existingOverview.get();
|
|
|
|
|
+ // 合并新事件到现有事件列表
|
|
|
|
|
+ if (overview.getEvents() == null) {
|
|
|
|
|
+ overview.setEvents(new ArrayList<>());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算时间戳偏移量
|
|
|
|
|
+ timestampOffset = calculateTimestampOffsetForUpdate(overview.getEvents(), newEvents);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加新事件(应用时间戳偏移量)
|
|
|
|
|
+ overview.getEvents().addAll(convertToOverviewEventsWithOffset(newEvents, timestampOffset));
|
|
|
|
|
+
|
|
|
|
|
+ // 🚀 使用增量更新统计数据(性能优化)
|
|
|
|
|
+ incrementalUpdateStatistics(overview, newEvents);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 创建新记录 - 只有在这里才设置createdAt字段
|
|
|
|
|
+ overview = new BehaviorOverview();
|
|
|
|
|
+ overview.setStudentId(studentId);
|
|
|
|
|
+ overview.setAssignmentId(assignmentId);
|
|
|
|
|
+ overview.setCreatedAt(new Date()); // 记录(student_id,assignment_id)第一条记录的创建时间
|
|
|
|
|
+ overview.setEvents(convertToOverviewEventsWithOffset(newEvents, 0L));
|
|
|
|
|
+
|
|
|
|
|
+ // 💡 新建记录时使用增量更新(从0开始累加)
|
|
|
|
|
+ incrementalUpdateStatistics(overview, newEvents);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 保存或更新
|
|
|
|
|
+ behaviorOverviewMapper.save(overview);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算时间戳偏移量(用于更新现有Overview)
|
|
|
|
|
+ */
|
|
|
|
|
+ private Long calculateTimestampOffsetForUpdate(List<BehaviorOverview.BehaviorEvent> existingEvents,
|
|
|
|
|
+ List<BehaviorRecord.BehaviorEvent> newEvents) {
|
|
|
|
|
+ // 获取现有事件中最后一个非null的endStamp
|
|
|
|
|
+ Long lastEndStamp = getLastEndStampFromOverviewEvents(existingEvents);
|
|
|
|
|
+ if (lastEndStamp == null) {
|
|
|
|
|
+ return 0L; // 如果没有有效的endStamp,不进行偏移
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取新事件中第一个非null的startStamp
|
|
|
|
|
+ Long firstStartStamp = getFirstStartStampFromRecordEvents(newEvents);
|
|
|
|
|
+ if (firstStartStamp == null) {
|
|
|
|
|
+ return 0L; // 如果没有有效的startStamp,不进行偏移
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果新事件的第一个startStamp小于现有事件的最后endStamp,需要偏移
|
|
|
|
|
+ if (firstStartStamp < lastEndStamp) {
|
|
|
|
|
+ return lastEndStamp;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return 0L; // 时间戳连续,无需偏移
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取Overview事件列表中最后一个非null的endStamp
|
|
|
|
|
+ */
|
|
|
|
|
+ private Long getLastEndStampFromOverviewEvents(List<BehaviorOverview.BehaviorEvent> events) {
|
|
|
|
|
+ if (events == null || events.isEmpty()) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = events.size() - 1; i >= 0; i--) {
|
|
|
|
|
+ BehaviorOverview.BehaviorEvent event = events.get(i);
|
|
|
|
|
+ if (event.getEndStamp() != null) {
|
|
|
|
|
+ return event.getEndStamp();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取Record事件列表中第一个非null的startStamp
|
|
|
|
|
+ */
|
|
|
|
|
+ private Long getFirstStartStampFromRecordEvents(List<BehaviorRecord.BehaviorEvent> events) {
|
|
|
|
|
+ if (events == null || events.isEmpty()) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (BehaviorRecord.BehaviorEvent event : events) {
|
|
|
|
|
+ if (event.getStartStamp() != null) {
|
|
|
|
|
+ return event.getStartStamp();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将BehaviorRecord.BehaviorEvent转换为BehaviorOverview.BehaviorEvent(带时间戳偏移量)
|
|
|
|
|
+ */
|
|
|
|
|
+ private List<BehaviorOverview.BehaviorEvent> convertToOverviewEventsWithOffset(List<BehaviorRecord.BehaviorEvent> recordEvents, Long timestampOffset) {
|
|
|
|
|
+ List<BehaviorOverview.BehaviorEvent> overviewEvents = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ for (BehaviorRecord.BehaviorEvent recordEvent : recordEvents) {
|
|
|
|
|
+ BehaviorOverview.BehaviorEvent overviewEvent = new BehaviorOverview.BehaviorEvent();
|
|
|
|
|
+
|
|
|
|
|
+ // 复制基础字段
|
|
|
|
|
+ overviewEvent.setType(recordEvent.getType());
|
|
|
|
|
+ overviewEvent.setRetain(recordEvent.getRetain());
|
|
|
|
|
+ overviewEvent.setLength(recordEvent.getLength());
|
|
|
|
|
+ overviewEvent.setTime(recordEvent.getTime());
|
|
|
|
|
+ overviewEvent.setInsert(recordEvent.getInsert());
|
|
|
|
|
+ overviewEvent.setDocLength(recordEvent.getDocLength());
|
|
|
|
|
+ overviewEvent.setLetterCount(recordEvent.getLetterCount());
|
|
|
|
|
+ overviewEvent.setKey(recordEvent.getKey());
|
|
|
|
|
+ overviewEvent.setTimeKey(recordEvent.getTimeKey());
|
|
|
|
|
+ overviewEvent.setStartClock(recordEvent.getStartClock());
|
|
|
|
|
+ overviewEvent.setStartTime(recordEvent.getStartTime());
|
|
|
|
|
+ overviewEvent.setEndClock(recordEvent.getEndClock());
|
|
|
|
|
+ overviewEvent.setEndTime(recordEvent.getEndTime());
|
|
|
|
|
+ overviewEvent.setActionTime(recordEvent.getActionTime());
|
|
|
|
|
+ overviewEvent.setPauseTime(recordEvent.getPauseTime());
|
|
|
|
|
+ overviewEvent.setDelete(recordEvent.getDelete());
|
|
|
|
|
+ overviewEvent.setOldContent(recordEvent.getOldContent());
|
|
|
|
|
+ overviewEvent.setDeletedContent(recordEvent.getDeletedContent());
|
|
|
|
|
+
|
|
|
|
|
+ // 应用时间戳偏移量
|
|
|
|
|
+ if (recordEvent.getStartStamp() != null) {
|
|
|
|
|
+ overviewEvent.setStartStamp(recordEvent.getStartStamp() + timestampOffset);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ overviewEvent.setStartStamp(null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (recordEvent.getEndStamp() != null) {
|
|
|
|
|
+ overviewEvent.setEndStamp(recordEvent.getEndStamp() + timestampOffset);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ overviewEvent.setEndStamp(null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ overviewEvents.add(overviewEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return overviewEvents;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将BehaviorRecord.BehaviorEvent转换为BehaviorOverview.BehaviorEvent
|
|
|
|
|
+ */
|
|
|
|
|
+ private List<BehaviorOverview.BehaviorEvent> convertToOverviewEvents(List<BehaviorRecord.BehaviorEvent> recordEvents) {
|
|
|
|
|
+ return convertToOverviewEventsWithOffset(recordEvents, 0L);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 增量更新统计数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private void incrementalUpdateStatistics(BehaviorOverview overview, List<BehaviorRecord.BehaviorEvent> newEvents) {
|
|
|
|
|
+ if (newEvents == null || newEvents.isEmpty()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 只计算新增事件的统计
|
|
|
|
|
+ int newInsertCount = 0;
|
|
|
|
|
+ int newDeleteCount = 0;
|
|
|
|
|
+ int newCopyCount = 0;
|
|
|
|
|
+ int newCopyCharacterCount = 0;
|
|
|
|
|
+ int newLongPauseCount = 0;
|
|
|
|
|
+
|
|
|
|
|
+ // 只遍历新增的事件
|
|
|
|
|
+ for (BehaviorRecord.BehaviorEvent event : newEvents) {
|
|
|
|
|
+ String type = event.getType();
|
|
|
|
|
+ if ("insert".equals(type)) {
|
|
|
|
|
+ newInsertCount++;
|
|
|
|
|
+ } else if ("delete".equals(type)) {
|
|
|
|
|
+ newDeleteCount++;
|
|
|
|
|
+ } else if ("copy".equals(type)) {
|
|
|
|
|
+ newCopyCount++;
|
|
|
|
|
+ // 计算copy操作的字符数
|
|
|
|
|
+ if (event.getInsert() != null) {
|
|
|
|
|
+ newCopyCharacterCount += event.getInsert().length();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算pauseTime大于2000ms的次数
|
|
|
|
|
+ if (event.getPauseTime() != null && event.getPauseTime() > 2000) {
|
|
|
|
|
+ newLongPauseCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 累加到现有统计数据上(处理null值)
|
|
|
|
|
+ overview.setInsertCount(safeAdd(overview.getInsertCount(), newInsertCount));
|
|
|
|
|
+ overview.setDeleteCount(safeAdd(overview.getDeleteCount(), newDeleteCount));
|
|
|
|
|
+ overview.setCopyCount(safeAdd(overview.getCopyCount(), newCopyCount));
|
|
|
|
|
+ overview.setCopyCharacterCount(safeAdd(overview.getCopyCharacterCount(), newCopyCharacterCount));
|
|
|
|
|
+ overview.setLongPauseCount(safeAdd(overview.getLongPauseCount(), newLongPauseCount));
|
|
|
|
|
+
|
|
|
|
|
+ log.debug("增量更新统计: +insert={}, +delete={}, +copy={}, +copyChar={}, +longPause={}",
|
|
|
|
|
+ newInsertCount, newDeleteCount, newCopyCount, newCopyCharacterCount, newLongPauseCount);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 安全的数字相加,处理null值
|
|
|
|
|
+ */
|
|
|
|
|
+ private Integer safeAdd(Integer existing, Integer addition) {
|
|
|
|
|
+ return (existing != null ? existing : 0) + (addition != null ? addition : 0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String migrateBehaviorRecordsToOverviews() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ log.info("开始迁移所有behaviorRecords数据到behaviorOverviews集合");
|
|
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
|
|
+
|
|
|
|
|
+ // 获取所有BehaviorRecord记录
|
|
|
|
|
+ List<BehaviorRecord> allRecords = behaviorRecordMapper.findAll();
|
|
|
|
|
+ log.info("找到{}条behaviorRecords记录需要迁移", allRecords.size());
|
|
|
|
|
+
|
|
|
|
|
+ // 按(studentId, assignmentId)分组
|
|
|
|
|
+ Map<String, List<BehaviorRecord>> groupedRecords = allRecords.stream()
|
|
|
|
|
+ .collect(Collectors.groupingBy(record ->
|
|
|
|
|
+ record.getStudentId() + "_" + record.getAssignmentId()));
|
|
|
|
|
+
|
|
|
|
|
+ int processedGroups = 0;
|
|
|
|
|
+ int totalEvents = 0;
|
|
|
|
|
+
|
|
|
|
|
+ for (Map.Entry<String, List<BehaviorRecord>> entry : groupedRecords.entrySet()) {
|
|
|
|
|
+ String key = entry.getKey();
|
|
|
|
|
+ List<BehaviorRecord> records = entry.getValue();
|
|
|
|
|
+
|
|
|
|
|
+ // 解析studentId和assignmentId
|
|
|
|
|
+ String[] parts = key.split("_");
|
|
|
|
|
+ Long studentId = Long.valueOf(parts[0]);
|
|
|
|
|
+ Long assignmentId = Long.valueOf(parts[1]);
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移这一组数据
|
|
|
|
|
+ migrateBehaviorRecordsToOverviews(studentId, assignmentId);
|
|
|
|
|
+ processedGroups++;
|
|
|
|
|
+
|
|
|
|
|
+ // 统计事件数量
|
|
|
|
|
+ int eventsCount = records.stream()
|
|
|
|
|
+ .mapToInt(record -> record.getEvents() != null ? record.getEvents().size() : 0)
|
|
|
|
|
+ .sum();
|
|
|
|
|
+ totalEvents += eventsCount;
|
|
|
|
|
+
|
|
|
|
|
+ log.info("已处理第{}组: studentId={}, assignmentId={}, 记录数={}, 事件数={}",
|
|
|
|
|
+ processedGroups, studentId, assignmentId, records.size(), eventsCount);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ long endTime = System.currentTimeMillis();
|
|
|
|
|
+ String message = String.format("迁移完成!处理了%d组数据,总计%d个事件,耗时%dms",
|
|
|
|
|
+ processedGroups, totalEvents, (endTime - startTime));
|
|
|
|
|
+
|
|
|
|
|
+ log.info(message);
|
|
|
|
|
+ return message;
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("迁移behaviorRecords数据失败: {}", e.getMessage(), e);
|
|
|
|
|
+ return "迁移失败: " + e.getMessage();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String migrateBehaviorRecordsToOverviews(Long studentId, Long assignmentId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ log.info("开始迁移指定数据: studentId={}, assignmentId={}", studentId, assignmentId);
|
|
|
|
|
+
|
|
|
|
|
+ // 检查behaviorOverviews中是否已存在该记录
|
|
|
|
|
+ Optional<BehaviorOverview> existingOverview = behaviorOverviewMapper
|
|
|
|
|
+ .findByAssignmentIdAndStudentId(assignmentId, studentId);
|
|
|
|
|
+
|
|
|
|
|
+ if (existingOverview.isPresent()) {
|
|
|
|
|
+ String message = String.format("BehaviorOverview已存在: studentId=%d, assignmentId=%d,跳过迁移",
|
|
|
|
|
+ studentId, assignmentId);
|
|
|
|
|
+ log.info(message);
|
|
|
|
|
+ return message;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取该(studentId, assignmentId)的所有BehaviorRecord记录,按创建时间排序
|
|
|
|
|
+ Sort sort = Sort.by(Sort.Direction.ASC, "created_at");
|
|
|
|
|
+ List<BehaviorRecord> records = behaviorRecordMapper.findByAssignmentIdAndStudentId(assignmentId, studentId, sort);
|
|
|
|
|
+
|
|
|
|
|
+ if (records.isEmpty()) {
|
|
|
|
|
+ String message = String.format("未找到相关behaviorRecords数据: studentId=%d, assignmentId=%d",
|
|
|
|
|
+ studentId, assignmentId);
|
|
|
|
|
+ log.info(message);
|
|
|
|
|
+ return message;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建新的BehaviorOverview
|
|
|
|
|
+ BehaviorOverview overview = new BehaviorOverview();
|
|
|
|
|
+ overview.setStudentId(studentId);
|
|
|
|
|
+ overview.setAssignmentId(assignmentId);
|
|
|
|
|
+ overview.setCreatedAt(records.get(0).getCreatedAt()); // 使用第一条记录的创建时间
|
|
|
|
|
+ overview.setEvents(new ArrayList<>());
|
|
|
|
|
+
|
|
|
|
|
+ // 依次处理每条记录,确保时间戳连续性
|
|
|
|
|
+ for (BehaviorRecord record : records) {
|
|
|
|
|
+ if (record.getEvents() != null && !record.getEvents().isEmpty()) {
|
|
|
|
|
+ // 计算时间戳偏移量
|
|
|
|
|
+ Long timestampOffset = calculateTimestampOffsetForUpdate(overview.getEvents(), record.getEvents());
|
|
|
|
|
+
|
|
|
|
|
+ // 添加事件(应用时间戳偏移量)
|
|
|
|
|
+ overview.getEvents().addAll(convertToOverviewEventsWithOffset(record.getEvents(), timestampOffset));
|
|
|
|
|
+
|
|
|
|
|
+ // 🚀 使用增量更新统计数据
|
|
|
|
|
+ incrementalUpdateStatistics(overview, record.getEvents());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 保存到behaviorOverviews集合
|
|
|
|
|
+ behaviorOverviewMapper.save(overview);
|
|
|
|
|
+
|
|
|
|
|
+ String message = String.format("迁移成功: studentId=%d, assignmentId=%d, 记录数=%d, 事件数=%d",
|
|
|
|
|
+ studentId, assignmentId, records.size(), overview.getEvents().size());
|
|
|
|
|
+ log.info(message);
|
|
|
|
|
+ return message;
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ String errorMessage = String.format("迁移指定数据失败: studentId=%d, assignmentId=%d, error=%s",
|
|
|
|
|
+ studentId, assignmentId, e.getMessage());
|
|
|
|
|
+ log.error(errorMessage, e);
|
|
|
|
|
+ return errorMessage;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public BehaviorOverviewVO getBehaviorOverview(Long studentId, Long assignmentId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ log.info("获取BehaviorOverview统计数据: studentId={}, assignmentId={}", studentId, assignmentId);
|
|
|
|
|
+
|
|
|
|
|
+ // 查询behaviorOverviews集合中的数据
|
|
|
|
|
+ Optional<BehaviorOverview> overviewOptional = behaviorOverviewMapper
|
|
|
|
|
+ .findByAssignmentIdAndStudentId(assignmentId, studentId);
|
|
|
|
|
+
|
|
|
|
|
+ if (overviewOptional.isPresent()) {
|
|
|
|
|
+ BehaviorOverview overview = overviewOptional.get();
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为VO对象
|
|
|
|
|
+ BehaviorOverviewVO vo = new BehaviorOverviewVO();
|
|
|
|
|
+ vo.setStudentId(overview.getStudentId());
|
|
|
|
|
+ vo.setAssignmentId(overview.getAssignmentId());
|
|
|
|
|
+ vo.setInsertCount(overview.getInsertCount());
|
|
|
|
|
+ vo.setDeleteCount(overview.getDeleteCount());
|
|
|
|
|
+ vo.setCopyCount(overview.getCopyCount());
|
|
|
|
|
+ vo.setCopyCharacterCount(overview.getCopyCharacterCount());
|
|
|
|
|
+ vo.setLongPauseCount(overview.getLongPauseCount());
|
|
|
|
|
+
|
|
|
|
|
+ log.info("成功获取BehaviorOverview数据: studentId={}, assignmentId={}, insertCount={}, deleteCount={}, copyCount={}, copyCharacterCount={}, longPauseCount={}",
|
|
|
|
|
+ studentId, assignmentId, vo.getInsertCount(), vo.getDeleteCount(), vo.getCopyCount(), vo.getCopyCharacterCount(), vo.getLongPauseCount());
|
|
|
|
|
+
|
|
|
|
|
+ return vo;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.info("未找到BehaviorOverview数据: studentId={}, assignmentId={}", studentId, assignmentId);
|
|
|
|
|
+ return createEmptyBehaviorOverviewVO(studentId, assignmentId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("获取BehaviorOverview数据失败: studentId={}, assignmentId={}, error={}",
|
|
|
|
|
+ studentId, assignmentId, e.getMessage(), e);
|
|
|
|
|
+ return createEmptyBehaviorOverviewVO(studentId, assignmentId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建空的BehaviorOverviewVO对象
|
|
|
|
|
+ */
|
|
|
|
|
+ private BehaviorOverviewVO createEmptyBehaviorOverviewVO(Long studentId, Long assignmentId) {
|
|
|
|
|
+ BehaviorOverviewVO vo = new BehaviorOverviewVO();
|
|
|
|
|
+ vo.setStudentId(studentId);
|
|
|
|
|
+ vo.setAssignmentId(assignmentId);
|
|
|
|
|
+ return vo;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|