feat(student): 新增学生词汇掌握详情及热力图展示功能

- 新增FindStudentMasteryDetailReqVO和FindStudentMasteryDetailRspVO数据类
- 学生接口新增/ student/mastery/detail,用于查询学生词汇掌握详情
- StudentService及实现类添加查询词汇掌握详情的方法
- WordMasteryLogDOMapper新增selectAllByStudentId方法支持查询
- SaTokenConfigure增加对新接口的免认证配置
- 前端api新增getStudentWordMastery方法
- 学生页面新增WordMasteryHeatmap组件并展示词汇掌握热力图
- 创建WordMasteryHeatmap组件,支持动态请求数据及Echarts热力图渲染
- 热力图按记忆强度排序,提供丰富的鼠标悬停提示信息
This commit is contained in:
lbw
2025-12-24 16:26:22 +08:00
parent 15e909c318
commit aff862d161
11 changed files with 311 additions and 0 deletions

View File

@@ -34,6 +34,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
.notMatch("/studentLessonPlans/list")
.notMatch("/studentLessonPlans/history")
.notMatch("/student/analyze")
.notMatch("/student/mastery/detail")
.notMatch("/unit/list")
.notMatch("/vocabulary/list")
.notMatch("/plan/download")

View File

@@ -4,6 +4,8 @@ package com.yinlihupo.enlish.service.controller;
import com.yinlihupo.enlish.service.domain.dataobject.ClassDO;
import com.yinlihupo.enlish.service.domain.dataobject.GradeDO;
import com.yinlihupo.enlish.service.domain.dataobject.StudentDO;
import com.yinlihupo.enlish.service.model.bo.StudentDetail;
import com.yinlihupo.enlish.service.model.bo.exam.WordMasteryDetail;
import com.yinlihupo.enlish.service.model.vo.student.*;
import com.yinlihupo.enlish.service.service.ClassService;
import com.yinlihupo.enlish.service.service.GradeService;
@@ -92,4 +94,21 @@ public class StudentController {
String analyzeStudentStudy = studentService.analyzeStudentStudy(analyzeStudentStudyReqVO.getStudentId());
return Response.success(analyzeStudentStudy);
}
@PostMapping("mastery/detail")
@ApiOperationLog(description = "查询学生单词掌握详情")
public Response<List<FindStudentMasteryDetailRspVO>> findStudentMasteryDetail(@RequestBody FindStudentMasteryDetailReqVO findStudentMasteryDetailReqVO) {
Integer studentId = findStudentMasteryDetailReqVO.getStudentId();
List<WordMasteryDetail> studentWordMasteryDetail = studentService.findStudentWordMasteryDetail(studentId);
List<FindStudentMasteryDetailRspVO> list = studentWordMasteryDetail.stream().map(wordMasteryDetail -> FindStudentMasteryDetailRspVO.builder()
.word(wordMasteryDetail.getWord())
.reviewCount(wordMasteryDetail.getReviewCount())
.memoryStrength(wordMasteryDetail.getMemoryStrength())
.updateTime(wordMasteryDetail.getUpdate_time())
.build()
).toList();
return Response.success(list);
}
}

View File

@@ -18,4 +18,6 @@ public interface WordMasteryLogDOMapper {
int selectStudentStrengthCount(@Param("studentId") Integer studentId);
List<WordMasteryLogDO> selectByStudentIdAndLimitTime(@Param("studentId") Integer studentId);
List<WordMasteryLogDO> selectAllByStudentId(@Param("studentId") Integer studentId);
}

View File

@@ -0,0 +1,15 @@
package com.yinlihupo.enlish.service.model.vo.student;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class FindStudentMasteryDetailReqVO {
private Integer studentId;
}

View File

@@ -0,0 +1,22 @@
package com.yinlihupo.enlish.service.model.vo.student;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class FindStudentMasteryDetailRspVO {
private String word;
private Integer reviewCount;
private Double memoryStrength;
private LocalDateTime updateTime;
}

View File

@@ -3,6 +3,7 @@ package com.yinlihupo.enlish.service.service;
import com.yinlihupo.enlish.service.domain.dataobject.StudentDO;
import com.yinlihupo.enlish.service.model.bo.StudentDetail;
import com.yinlihupo.enlish.service.model.bo.exam.WordMasteryDetail;
import com.yinlihupo.enlish.service.model.vo.student.AddStudentReqVO;
import java.util.List;
@@ -22,4 +23,6 @@ public interface StudentService {
void deleteStudent(Integer studentId);
String analyzeStudentStudy(Integer studentId);
List<WordMasteryDetail> findStudentWordMasteryDetail(Integer studentId);
}

View File

@@ -167,4 +167,22 @@ public class StudentServiceImpl implements StudentService {
}
}
@Override
public List<WordMasteryDetail> findStudentWordMasteryDetail(Integer studentId) {
List<WordMasteryLogDO> wordMasteryLogDOS = wordMasteryLogDOMapper.selectAllByStudentId(studentId);
List<VocabularyBankDO> masteredWords = vocabularyBankMapper.selectVocabularyBankDOListByIds(wordMasteryLogDOS.stream().map(WordMasteryLogDO::getWordId).toList());
Map<Integer, VocabularyBankDO> id2MasteryWord = masteredWords.stream().collect(Collectors.toMap(VocabularyBankDO::getId, vocabularyBankDO -> vocabularyBankDO));
List<WordMasteryDetail> wordMasteryDetails = new ArrayList<>();
for (WordMasteryLogDO wordMasteryLogDO : wordMasteryLogDOS) {
wordMasteryDetails.add(WordMasteryDetail.builder()
.word(id2MasteryWord.get(wordMasteryLogDO.getWordId()).getWord())
.reviewCount(wordMasteryLogDO.getReviewCount())
.memoryStrength(wordMasteryLogDO.getMemoryStrength())
.update_time(wordMasteryLogDO.getUpdate_time())
.build());
}
return wordMasteryDetails;
}
}

View File

@@ -58,4 +58,9 @@
where student_id = #{studentId}
and update_time between date_sub(now(), interval 7 day) and now()
</select>
<select id="selectAllByStudentId" resultMap="BaseResultMap">
select *
from word_mastery_log
where student_id = #{studentId}
</select>
</mapper>