From 260c2c79f1809af403bfa246df33e262003ac761 Mon Sep 17 00:00:00 2001 From: lbw <1192299468@qq.com> Date: Wed, 24 Dec 2025 15:22:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(student):=20=E5=AE=9E=E7=8E=B0=E5=AD=A6?= =?UTF-8?q?=E7=94=9F=E5=AD=A6=E4=B9=A0=E5=88=86=E6=9E=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增AnalyzeStudentStudyReqVO用于分析请求参数封装 - StudentService接口新增analyzeStudentStudy方法及其实现 - 实现分析逻辑,查询最近7天学生考试及单词掌握记录,构造分析数据 - 通过DifyArticleClient调用外部AI服务生成学习分析结果 - 使用Redis缓存分析结果,设置3天过期 - 新增ExamWordsJudgeResultDetail和WordMasteryDetail数据模型 - Mapper新增支持根据学生ID和时间范围查询考试结果和单词掌握日志 - DifyArticleClient新增sendStudentAnalyze方法调用分析接口 - 前端学生页面新增学习分析面板及调用接口,支持超时设置 - 修改路由权限配置,允许访问学习分析接口 - 添加markdown-it库支持分析结果富文本渲染 - 移除RoleServiceImpl中redis设置过期时间,改为永久保存 --- .../service/config/SaTokenConfigure.java | 1 + .../service/constant/StudentConstant.java | 10 +++ .../service/controller/StudentController.java | 7 ++ .../mapper/ExamWordsJudgeResultDOMapper.java | 2 + .../domain/mapper/WordMasteryLogDOMapper.java | 2 + .../bo/exam/ExamWordsJudgeResultDetail.java | 28 +++++++ .../model/bo/exam/WordMasteryDetail.java | 23 +++++ .../vo/student/AnalyzeStudentStudyReqVO.java | 15 ++++ .../service/service/StudentService.java | 2 + .../service/service/role/RoleServiceImpl.java | 2 +- .../service/student/StudentServiceImpl.java | 83 +++++++++++++++++-- .../service/utils/DifyArticleClient.java | 37 ++++++++- .../mapper/ExamWordsJudgeResultDOMapper.xml | 6 ++ .../mapper/WordMasteryLogDOMapper.xml | 6 ++ .../exam/ExamWordsJudgeServiceTest.java | 20 ++++- enlish-vue/package-lock.json | 54 ++++++++++++ enlish-vue/package.json | 1 + enlish-vue/src/api/student.js | 9 +- enlish-vue/src/pages/student.vue | 45 +++++++++- 19 files changed, 342 insertions(+), 11 deletions(-) create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/StudentConstant.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/exam/ExamWordsJudgeResultDetail.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/exam/WordMasteryDetail.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/student/AnalyzeStudentStudyReqVO.java diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/config/SaTokenConfigure.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/config/SaTokenConfigure.java index 4922573..c186508 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/config/SaTokenConfigure.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/config/SaTokenConfigure.java @@ -33,6 +33,7 @@ public class SaTokenConfigure implements WebMvcConfigurer { .notMatch("/student/detail") .notMatch("/studentLessonPlans/list") .notMatch("/studentLessonPlans/history") + .notMatch("/student/analyze") .notMatch("/unit/list") .notMatch("/vocabulary/list") .notMatch("/plan/download") diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/StudentConstant.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/StudentConstant.java new file mode 100644 index 0000000..bcb435b --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/StudentConstant.java @@ -0,0 +1,10 @@ +package com.yinlihupo.enlish.service.constant; + +public class StudentConstant { + + public static final String ANALYZE_STUDENT_STUDY = "analyzeStudentStudy"; + + public static String buildAnalyzeStudentStudyKey(Integer studentId) { + return ANALYZE_STUDENT_STUDY + ":" + studentId; + } +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/StudentController.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/StudentController.java index 9c1c9d7..580db9a 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/StudentController.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/StudentController.java @@ -85,4 +85,11 @@ public class StudentController { studentService.deleteStudent(deleteStudentReqVO.getStudentId()); return Response.success(); } + + @PostMapping("analyze") + @ApiOperationLog(description = "学生学习分析") + public Response analyzeStudentStudy(@RequestBody AnalyzeStudentStudyReqVO analyzeStudentStudyReqVO) { + String analyzeStudentStudy = studentService.analyzeStudentStudy(analyzeStudentStudyReqVO.getStudentId()); + return Response.success(analyzeStudentStudy); + } } diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/ExamWordsJudgeResultDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/ExamWordsJudgeResultDOMapper.java index a8b0bba..6eeef66 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/ExamWordsJudgeResultDOMapper.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/ExamWordsJudgeResultDOMapper.java @@ -22,4 +22,6 @@ public interface ExamWordsJudgeResultDOMapper { ExamWordsJudgeResultDO selectDetailById(@Param("id") Integer id); List selectByStudentId(@Param("studentId") Integer studentId); + + List selectByStudentIdAndLimitTime(@Param("studentId") Integer studentId); } \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/WordMasteryLogDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/WordMasteryLogDOMapper.java index 46eecc3..434e19c 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/WordMasteryLogDOMapper.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/WordMasteryLogDOMapper.java @@ -16,4 +16,6 @@ public interface WordMasteryLogDOMapper { int batchUpdateStudentMastery(@Param("wordMasteryLogDOs") List wordMasteryLogDOs); int selectStudentStrengthCount(@Param("studentId") Integer studentId); + + List selectByStudentIdAndLimitTime(@Param("studentId") Integer studentId); } \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/exam/ExamWordsJudgeResultDetail.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/exam/ExamWordsJudgeResultDetail.java new file mode 100644 index 0000000..e6c1444 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/exam/ExamWordsJudgeResultDetail.java @@ -0,0 +1,28 @@ +package com.yinlihupo.enlish.service.model.bo.exam; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class ExamWordsJudgeResultDetail { + + private Integer correctWordCount; + + private Integer wrongWordCount; + + private LocalDateTime startDate; + + private List correctWords; + + private List wrongWords; + + private String msg; +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/exam/WordMasteryDetail.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/exam/WordMasteryDetail.java new file mode 100644 index 0000000..fee0b2f --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/exam/WordMasteryDetail.java @@ -0,0 +1,23 @@ +package com.yinlihupo.enlish.service.model.bo.exam; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class WordMasteryDetail { + + private String word; + + private Integer reviewCount; + + private Double memoryStrength; + + private LocalDateTime update_time; +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/student/AnalyzeStudentStudyReqVO.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/student/AnalyzeStudentStudyReqVO.java new file mode 100644 index 0000000..8665a6a --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/student/AnalyzeStudentStudyReqVO.java @@ -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 AnalyzeStudentStudyReqVO { + + private Integer studentId; +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/StudentService.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/StudentService.java index 7e44e23..58605ad 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/StudentService.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/StudentService.java @@ -20,4 +20,6 @@ public interface StudentService { void addStudent(AddStudentReqVO addStudentReqVO); void deleteStudent(Integer studentId); + + String analyzeStudentStudy(Integer studentId); } diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/role/RoleServiceImpl.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/role/RoleServiceImpl.java index 414eb58..31cdef3 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/role/RoleServiceImpl.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/role/RoleServiceImpl.java @@ -46,7 +46,7 @@ public class RoleServiceImpl implements RoleService { List roleDOs = roleIds.stream().map(roleId2RoleDO::get).toList(); List user2RoleKeys = roleDOs.stream().map(RoleDO::getRoleKey).toList(); log.info("将用户 {} 的角色同步到 redis 中, {}", userId, roleKeys); - stringRedisTemplate.opsForValue().set(RoleConstants.buildUserRoleKey(userId), JsonUtils.toJsonString(user2RoleKeys), 60 * 60 * 24); + stringRedisTemplate.opsForValue().set(RoleConstants.buildUserRoleKey(userId), JsonUtils.toJsonString(user2RoleKeys)); }); } diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/student/StudentServiceImpl.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/student/StudentServiceImpl.java index 9948f9a..b2eab3f 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/student/StudentServiceImpl.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/student/StudentServiceImpl.java @@ -1,19 +1,24 @@ package com.yinlihupo.enlish.service.service.student; -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.constant.StudentConstant; +import com.yinlihupo.enlish.service.domain.dataobject.*; import com.yinlihupo.enlish.service.domain.mapper.*; import com.yinlihupo.enlish.service.model.bo.StudentDetail; +import com.yinlihupo.enlish.service.model.bo.exam.ExamWordsJudgeResultDetail; +import com.yinlihupo.enlish.service.model.bo.exam.WordMasteryDetail; import com.yinlihupo.enlish.service.model.vo.student.AddStudentReqVO; import com.yinlihupo.enlish.service.service.StudentService; +import com.yinlihupo.enlish.service.utils.DifyArticleClient; +import com.yinlihupo.framework.common.util.JsonUtils; import jakarta.annotation.Resource; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Service @@ -29,6 +34,12 @@ public class StudentServiceImpl implements StudentService { private VocabularyBankDOMapper vocabularyBankMapper; @Resource private WordMasteryLogDOMapper wordMasteryLogDOMapper; + @Resource + private ExamWordsJudgeResultDOMapper examWordsJudgeResultDOMapper; + @Resource + private DifyArticleClient difyArticleClient; + @Resource + private RedisTemplate redisTemplate; @Override public List getStudentsByClassIdAndGradeId(Integer classId, Integer gradeId, String name, Integer pageNo, Integer pageSize) { @@ -94,4 +105,66 @@ public class StudentServiceImpl implements StudentService { public void deleteStudent(Integer studentId) { studentDOMapper.deleteById(studentId); } + + @Override + public String analyzeStudentStudy(Integer studentId) { + + String key = StudentConstant.buildAnalyzeStudentStudyKey(studentId); + if (redisTemplate.hasKey(key)) { + Object ans = redisTemplate.opsForValue().get(key); + return JsonUtils.toJsonString(ans); + } + + List examWordsJudgeResultDOS = examWordsJudgeResultDOMapper.selectByStudentIdAndLimitTime(studentId); + + List wordIds = new java.util.ArrayList<>(examWordsJudgeResultDOS.stream().map(ExamWordsJudgeResultDO::getCorrectWordIds).flatMap(List::stream).toList()); + wordIds.addAll(examWordsJudgeResultDOS.stream().map(ExamWordsJudgeResultDO::getWrongWordIds).flatMap(List::stream).toList()); + List vocabularyBankDOS = vocabularyBankMapper.selectVocabularyBankDOListByIds(wordIds); + Map id2Word = vocabularyBankDOS.stream().collect(Collectors.toMap(VocabularyBankDO::getId, vocabularyBankDO -> vocabularyBankDO)); + + List examWordsJudgeResultDetails = new ArrayList<>(); + for (ExamWordsJudgeResultDO examWordsJudgeResultDO : examWordsJudgeResultDOS) { + List correctWordIds = examWordsJudgeResultDO.getCorrectWordIds(); + List correctWords = correctWordIds.stream().map(id2Word::get).map(VocabularyBankDO::getWord).toList(); + List wrongWordIds = examWordsJudgeResultDO.getWrongWordIds(); + List wrongWords = wrongWordIds.stream().map(id2Word::get).map(VocabularyBankDO::getWord).toList(); + + examWordsJudgeResultDetails.add(ExamWordsJudgeResultDetail.builder() + .correctWordCount(examWordsJudgeResultDO.getCorrectWordCount()) + .wrongWordCount(examWordsJudgeResultDO.getWrongWordCount()) + .startDate(examWordsJudgeResultDO.getStartDate()) + .correctWords(correctWords) + .wrongWords(wrongWords) + .msg(examWordsJudgeResultDO.getMsg()).build() + ); + } + + Map studentStudyInfo = new HashMap<>(); + studentStudyInfo.put("考试记录", examWordsJudgeResultDetails); + + List wordMasteryLogDOS = wordMasteryLogDOMapper.selectByStudentIdAndLimitTime(studentId); + List masteredWords = vocabularyBankMapper.selectVocabularyBankDOListByIds(wordMasteryLogDOS.stream().map(WordMasteryLogDO::getWordId).toList()); + Map id2MasteryWord = masteredWords.stream().collect(Collectors.toMap(VocabularyBankDO::getId, vocabularyBankDO -> vocabularyBankDO)); + List 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()); + } + studentStudyInfo.put("单词掌握情况", wordMasteryDetails); + + try { + String analyze = difyArticleClient.sendStudentAnalyze(JsonUtils.toJsonString(studentStudyInfo)).getAnswer(); + // 设置过期时间 3 天 + redisTemplate.opsForValue().set(key, analyze); + redisTemplate.expire(key, 3, TimeUnit.DAYS); + return analyze; + } catch (Exception e) { + throw new RuntimeException(e); + } + + } } diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/utils/DifyArticleClient.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/utils/DifyArticleClient.java index 160fa94..997444a 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/utils/DifyArticleClient.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/utils/DifyArticleClient.java @@ -21,6 +21,7 @@ public class DifyArticleClient { @Value("${ai.key}") private String apiKey; + private String anaKey = "app-hrUFcopdcpnflsvpHWRuBfCp"; @Value("${ai.url}") private String baseUrl; private final HttpClient httpClient; @@ -35,6 +36,40 @@ public class DifyArticleClient { this.objectMapper = new ObjectMapper(); } + public DifyResponse sendStudentAnalyze(String query) throws Exception { + String endpoint = this.baseUrl; + + // 1. 构建请求体对象 + ChatRequest payload = new ChatRequest(); + payload.setQuery(query); + payload.setUser(String.valueOf(1)); + payload.setResponseMode("blocking"); // 使用阻塞模式,一次性返回 + payload.setInputs(new HashMap<>()); + + // 2. 序列化为 JSON 字符串 + String jsonBody = objectMapper.writeValueAsString(payload); + + // 3. 构建 HTTP 请求 + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(endpoint)) + .header("Authorization", "Bearer " + anaKey) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) + .timeout(Duration.ofSeconds(30)) // 读取超时 + .build(); + + // 4. 发送请求 + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + // 5. 检查状态码 + if (response.statusCode() != 200) { + throw new RuntimeException("Dify 请求失败: HTTP " + response.statusCode() + " | Body: " + response.body()); + } + + // 6. 反序列化响应体 + return objectMapper.readValue(response.body(), DifyResponse.class); + } + /** * 发送对话请求 (阻塞模式) * @@ -64,7 +99,7 @@ public class DifyArticleClient { // 3. 构建 HTTP 请求 HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(endpoint)) - .header("Authorization", "Bearer " + this.apiKey) + .header("Authorization", "Bearer " + apiKey) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .timeout(Duration.ofSeconds(30)) // 读取超时 diff --git a/enlish-service/src/main/resources/mapper/ExamWordsJudgeResultDOMapper.xml b/enlish-service/src/main/resources/mapper/ExamWordsJudgeResultDOMapper.xml index 1922adb..5e7acd9 100644 --- a/enlish-service/src/main/resources/mapper/ExamWordsJudgeResultDOMapper.xml +++ b/enlish-service/src/main/resources/mapper/ExamWordsJudgeResultDOMapper.xml @@ -74,5 +74,11 @@ order by start_date desc limit 500; + \ No newline at end of file diff --git a/enlish-service/src/main/resources/mapper/WordMasteryLogDOMapper.xml b/enlish-service/src/main/resources/mapper/WordMasteryLogDOMapper.xml index 4961b70..83992ec 100644 --- a/enlish-service/src/main/resources/mapper/WordMasteryLogDOMapper.xml +++ b/enlish-service/src/main/resources/mapper/WordMasteryLogDOMapper.xml @@ -52,4 +52,10 @@ + \ No newline at end of file diff --git a/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/exam/ExamWordsJudgeServiceTest.java b/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/exam/ExamWordsJudgeServiceTest.java index 4a9b8cd..07ff7a5 100644 --- a/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/exam/ExamWordsJudgeServiceTest.java +++ b/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/exam/ExamWordsJudgeServiceTest.java @@ -2,6 +2,8 @@ package com.yinlihupo.enlish.service.service.exam; import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsJudgeResultDO; import com.yinlihupo.enlish.service.service.ExamWordsJudgeService; +import com.yinlihupo.enlish.service.service.StudentService; +import com.yinlihupo.enlish.service.utils.DifyArticleClient; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; @@ -16,7 +18,10 @@ public class ExamWordsJudgeServiceTest { @Resource private ExamWordsJudgeService examWordsJudgeService; - + @Resource + private StudentService studentService; + @Resource + private DifyArticleClient difyArticleClient; @Test public void judgeExamWords() { examWordsJudgeService.judgeExamWords(1); @@ -27,4 +32,17 @@ public class ExamWordsJudgeServiceTest { List examWordsJudgeResult = examWordsJudgeService.getExamWordsJudgeResult(1, 10); log.info("examWordsJudgeResult:{}", examWordsJudgeResult); } + + @Test + public void selectExamWordsJudgeResult2() { + String s = studentService.analyzeStudentStudy(1); + try { + DifyArticleClient.DifyResponse difyResponse = difyArticleClient.sendStudentAnalyze(s); + String answer = difyResponse.getAnswer(); + log.info("answer:{}", answer); + } catch (Exception e) { + throw new RuntimeException(e); + } + log.info("s:{}", s); + } } diff --git a/enlish-vue/package-lock.json b/enlish-vue/package-lock.json index 6a009a6..c558281 100644 --- a/enlish-vue/package-lock.json +++ b/enlish-vue/package-lock.json @@ -13,6 +13,7 @@ "echarts": "^6.0.0", "element-plus": "^2.12.0", "flowbite": "^1.8.1", + "markdown-it": "^14.1.0", "nprogress": "^0.2.0", "pinia": "^3.0.4", "universal-cookie": "^8.0.1", @@ -1413,6 +1414,12 @@ "dev": true, "license": "MIT" }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/async-validator": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", @@ -2326,6 +2333,15 @@ "dev": true, "license": "MIT" }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/local-pkg": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", @@ -2378,6 +2394,23 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -2387,6 +2420,12 @@ "node": ">= 0.4" } }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", @@ -2865,6 +2904,15 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/quansync": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", @@ -3233,6 +3281,12 @@ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "license": "0BSD" }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/ufo": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", diff --git a/enlish-vue/package.json b/enlish-vue/package.json index 859f16a..de90a65 100644 --- a/enlish-vue/package.json +++ b/enlish-vue/package.json @@ -14,6 +14,7 @@ "echarts": "^6.0.0", "element-plus": "^2.12.0", "flowbite": "^1.8.1", + "markdown-it": "^14.1.0", "nprogress": "^0.2.0", "pinia": "^3.0.4", "universal-cookie": "^8.0.1", diff --git a/enlish-vue/src/api/student.js b/enlish-vue/src/api/student.js index cdbcd1c..546d0c9 100644 --- a/enlish-vue/src/api/student.js +++ b/enlish-vue/src/api/student.js @@ -23,4 +23,11 @@ export function deleteStudent(id) { return axios.post('/student/delete', { studentId: id }) -} \ No newline at end of file +} + +// 加一个最大响应时间 20 秒 +export function getStudentStudyAnalyze(data) { + return axios.post('/student/analyze', data, { + timeout: 20000 + }) +} diff --git a/enlish-vue/src/pages/student.vue b/enlish-vue/src/pages/student.vue index 41aad3c..fbdf53b 100644 --- a/enlish-vue/src/pages/student.vue +++ b/enlish-vue/src/pages/student.vue @@ -32,6 +32,20 @@
学生学案记录
+
+
+
学习分析
+ + 生成学习分析 + +
+ + +
@@ -41,17 +55,28 @@