Przeglądaj źródła

feat:修改行为数据存储方式,改为存到mongodb

wuzilong 1 rok temu
rodzic
commit
cfa66d8540

+ 12 - 7
src/main/java/com/njuzr/eaibackend/mapper/BehaviorRecordMapper.java

@@ -1,13 +1,18 @@
 package com.njuzr.eaibackend.mapper;
 
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.njuzr.eaibackend.po.BehaviorRecord;
-import org.apache.ibatis.annotations.Mapper;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.data.domain.Sort;
+import java.util.List;
 
 /**
- * @author BaiBai
- * @date 2025-02-10 17:02
- */
-@Mapper
-public interface BehaviorRecordMapper extends BaseMapper<BehaviorRecord> {
+ - * @author BaiBai
+ - * @date 2025-02-10 17:02
+ - */
+public interface BehaviorRecordMapper extends MongoRepository<BehaviorRecord, String> {
+    // 按学生+作业组合查询
+    List<BehaviorRecord> findByAssignmentIdAndStudentId(Long assignmentId, Long studentId, Sort sort);
+//    Page<BehaviorRecord> findByStudentIdAndAssignmentId(Long assignmentId, Long userId, Pageable pageable);
 }

+ 73 - 15
src/main/java/com/njuzr/eaibackend/po/BehaviorRecord.java

@@ -1,23 +1,81 @@
 package com.njuzr.eaibackend.po;
 
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.index.CompoundIndex;
+import org.springframework.data.mongodb.core.index.CompoundIndexes;
+import org.springframework.data.mongodb.core.mapping.Field;
 
 import java.util.Date;
+import java.util.List;
 
-/**
- * @author BaiBai
- * @date 2025-02-10 16:55
- */
 @Data
-@TableName("behavior_record")
+@Document(collection = "behaviorRecords")
+@CompoundIndexes({
+        // 复合索引:assignment_id + student_id + created_at (降序)
+        @CompoundIndex(
+                name = "assignment_student_created_idx",
+                def = "{'assignment_id': 1, 'student_id': 1, 'created_at': -1}",
+                background = true    // 后台构建,避免阻塞操作
+        )
+})
 public class BehaviorRecord {
-    @TableId(type = IdType.AUTO)
-    private Long id;
-    private Long StudentId;
-    private Long assignmentId;
-    private String recordFileUrl;
-    private Date time;
-}
+
+    @Id
+    private String id; // MongoDB自动生成的ObjectId
+
+    @Field("student_id")
+    private Long studentId; // 学生ID
+
+    @Field("assignment_id")
+    private Long assignmentId; // 作业ID
+
+    @Field("created_at")
+//    @Indexed
+    private Date createdAt = new Date(); // 记录创建时间
+
+    private List<BehaviorEvent> events; // 行为事件集合
+
+    /**
+     * 嵌套行为事件类
+     */
+    @Data
+    public static class BehaviorEvent {
+        // 基础事件信息(按JSON顺序排列)
+        private String type;           // 事件类型(cursor_move/insert/delete等)
+        private Integer retain;        // 光标位置
+        private Integer length;        // 操作长度
+        private String time;           // 事件时间(字符串格式)
+
+        // 文本操作相关
+        private String insert;         // 插入的文本内容
+        private Integer docLength;     // 文档总长度
+        private Integer letterCount;   // 字母计数
+
+        // 键盘输入相关
+        private String key;            // 按键值
+        private String timeKey;        // 按键时间
+
+        // 时间戳信息
+        private Long startStamp;     // 开始时间戳
+        private String startClock;     // 开始时钟时间
+        private String startTime;      // 开始时间字符串
+
+        // 结束时间信息
+        private Long endStamp;       // 结束时间戳
+        private String endClock;       // 结束时钟时间
+        private String endTime;        // 结束时间字符串
+
+        // 时间统计
+        private Long actionTime;     // 操作耗时(毫秒)
+        private Long pauseTime;      // 停留时间(毫秒)
+
+        // 删除操作相关
+        private String delete;         // 删除操作类型
+        private String oldContent;     // 旧内容
+        private String deletedContent; // 删除的内容
+    }
+
+}

+ 53 - 21
src/main/java/com/njuzr/eaibackend/service/impl/BehaviorRecordServiceImpl.java

@@ -15,7 +15,8 @@ import org.springframework.web.multipart.MultipartFile;
 import java.io.IOException;
 import java.util.Date;
 import java.util.List;
-
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.data.domain.Sort;
 /**
  * @author BaiBai
  * @date 2025-02-10 17:03
@@ -25,39 +26,70 @@ import java.util.List;
 public class BehaviorRecordServiceImpl implements BehaviorRecordService {
 
     private final BehaviorRecordMapper behaviorRecordMapper;
-
-    private final OssUtil ossUtil;
+    private final ObjectMapper objectMapper;
 
     @Autowired
     public BehaviorRecordServiceImpl(BehaviorRecordMapper behaviorRecordMapper, OssUtil ossUtil) {
         this.behaviorRecordMapper = behaviorRecordMapper;
-        this.ossUtil = ossUtil;
+        this.objectMapper = new ObjectMapper();
     }
 
+    @Override
     public List<BehaviorRecord> find(Long assignmentId, Long studentId) {
-        QueryWrapper<BehaviorRecord> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq("assignment_id", assignmentId);
-        queryWrapper.eq("student_id", studentId);
-        queryWrapper.orderByDesc("time");
-        return behaviorRecordMapper.selectList(queryWrapper);
+        // 创建一个按 created_at 降序排序的 Sort 对象,它不会引起额外的排序开销(因为索引已经支持);显式指定排序确保代码的兼容性
+        Sort sort = Sort.by(Sort.Direction.DESC, "created_at");
+        return behaviorRecordMapper.findByAssignmentIdAndStudentId(assignmentId, studentId, sort);
     }
 
+//    public String upload(Long assignmentId, Long studentId, MultipartFile file) {
+//        try {
+//            Date now = new Date();
+//            String filePath = "uploads/" + assignmentId + "_" + studentId + "_" + now.getTime() + file.getOriginalFilename();
+//            String urlStr = ossUtil.uploadFile(file.getInputStream(), filePath);
+//            String url = urlStr.split("\\?")[0];
+//            BehaviorRecord behaviorRecord = new BehaviorRecord();
+//            behaviorRecord.setAssignmentId(assignmentId);
+//            behaviorRecord.setStudentId(studentId);
+//            behaviorRecord.setRecordFileUrl(url);
+//            behaviorRecord.setTime(now);
+//            behaviorRecordMapper.insert(behaviorRecord);
+//            return url;
+//        } catch (IOException e) {
+//            log.error(e.getMessage());
+//            throw MyException.create(HttpStatus.BAD_REQUEST, "文件上传失败");
+//        }
+//    }
+
+
     public String upload(Long assignmentId, Long studentId, MultipartFile file) {
         try {
-            Date now = new Date();
-            String filePath = "uploads/" + assignmentId + "_" + studentId + "_" + now.getTime() + file.getOriginalFilename();
-            String urlStr = ossUtil.uploadFile(file.getInputStream(), filePath);
-            String url = urlStr.split("\\?")[0];
-            BehaviorRecord behaviorRecord = new BehaviorRecord();
-            behaviorRecord.setAssignmentId(assignmentId);
-            behaviorRecord.setStudentId(studentId);
-            behaviorRecord.setRecordFileUrl(url);
-            behaviorRecord.setTime(now);
-            behaviorRecordMapper.insert(behaviorRecord);
-            return url;
+            // 解析JSON文件
+            List<BehaviorRecord.BehaviorEvent> events = parseBehaviorEvents(file);
+
+            // 创建行为记录对象
+            BehaviorRecord record = new BehaviorRecord();
+            record.setStudentId(studentId);
+            record.setAssignmentId(assignmentId);
+            record.setEvents(events);
+
+            // 保存到MongoDB
+            behaviorRecordMapper.save(record);
+            return "行为数据保存成功";
         } catch (IOException e) {
             log.error(e.getMessage());
-            throw MyException.create(HttpStatus.BAD_REQUEST, "文件上传失败");
+            throw MyException.create(HttpStatus.BAD_REQUEST, "行为数据保存失败");
         }
     }
+
+    private List<BehaviorRecord.BehaviorEvent> parseBehaviorEvents(MultipartFile file)
+            throws IOException {
+        // 使用Jackson解析JSON数组
+        return objectMapper.readValue(
+                file.getInputStream(),
+                objectMapper.getTypeFactory().constructCollectionType(
+                        List.class,
+                        BehaviorRecord.BehaviorEvent.class
+                )
+        );
+    }
 }