Browse Source

feat:完成作文整体分析与逐句分析接口开发

白白 1 year ago
parent
commit
12eba117f7

+ 2 - 2
src/main/java/com/njuzr/eaibackend/controller/AIController.java

@@ -98,8 +98,8 @@ public class AIController {
             @RequestParam Long assignmentId,
             @RequestParam Long studentId
     ) {
-        AIEntry entry = aiDialogueService.rewrite(assignmentId, studentId);
-        return MyResponse.success(entry);
+//        AIEntry entry = aiDialogueService.rewrite(assignmentId, studentId);
+        return MyResponse.success(aiDialogueService.rewrite(assignmentId, studentId));
     }
 
     @PostMapping("/chatgpt")

+ 64 - 0
src/main/java/com/njuzr/eaibackend/controller/EvaluationController.java

@@ -0,0 +1,64 @@
+package com.njuzr.eaibackend.controller;
+
+import com.njuzr.eaibackend.service.AIEvaluationService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author BaiBai
+ * @date 2025-01-17 10:58
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/correct")
+public class EvaluationController {
+
+    private final AIEvaluationService aiEvaluationService;;
+
+    public EvaluationController(AIEvaluationService aiEvaluationService) {
+        this.aiEvaluationService = aiEvaluationService;
+    }
+
+    @PostMapping("/overall")
+    public MyResponse rewrite(
+            @RequestParam Long assignmentId,
+            @RequestParam Long studentId
+    ) {
+        return MyResponse.success(aiEvaluationService.rewrite(assignmentId, studentId));
+    }
+
+    @PostMapping("/perSentence")
+    public MyResponse rewritePerSentence(
+            @RequestParam Long assignmentId,
+            @RequestParam Long studentId
+    ) {
+        return MyResponse.success(aiEvaluationService.rewritePerSentence(assignmentId, studentId));
+    }
+
+    @GetMapping("/select/overall")
+    public MyResponse selectOverall(
+            @RequestParam Long assignmentId,
+            @RequestParam Long studentId,
+            @RequestParam(value = "version", defaultValue = "-1") Integer version
+    ){
+        return MyResponse.success(aiEvaluationService.getRewrite(assignmentId, studentId, version));
+    }
+
+    @GetMapping("/select/sentence")
+    public MyResponse selectSentence(
+            @RequestParam Long assignmentId,
+            @RequestParam Long studentId,
+            @RequestParam(value = "version", defaultValue = "-1") Integer version
+    ){
+        return MyResponse.success(aiEvaluationService.getRewritePerSentence(assignmentId, studentId, version));
+    }
+
+    @GetMapping("/record")
+    public MyResponse getRewriteVersionList(
+            @RequestParam Long assignmentId,
+            @RequestParam Long studentId,
+            @RequestParam(value = "version", defaultValue = "-1") Integer version
+    ){
+        return MyResponse.success(aiEvaluationService.getVersionList(assignmentId, studentId));
+    }
+}

+ 14 - 0
src/main/java/com/njuzr/eaibackend/mapper/EvaluationMapper.java

@@ -0,0 +1,14 @@
+package com.njuzr.eaibackend.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.njuzr.eaibackend.po.Evaluation;
+import com.njuzr.eaibackend.po.OverallEvaluation;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author BaiBai
+ * @date 2025-01-17 18:46
+ */
+@Mapper
+public interface EvaluationMapper extends BaseMapper<Evaluation> {
+}

+ 13 - 0
src/main/java/com/njuzr/eaibackend/mapper/OverallEvaluationMapper.java

@@ -0,0 +1,13 @@
+package com.njuzr.eaibackend.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.njuzr.eaibackend.po.OverallEvaluation;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author BaiBai
+ * @date 2025-01-17 18:46
+ */
+@Mapper
+public interface OverallEvaluationMapper extends BaseMapper<OverallEvaluation> {
+}

+ 13 - 0
src/main/java/com/njuzr/eaibackend/mapper/SentenceEvaluationMapper.java

@@ -0,0 +1,13 @@
+package com.njuzr.eaibackend.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.njuzr.eaibackend.po.SentenceEvaluation;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author BaiBai
+ * @date 2025-01-17 18:46
+ */
+@Mapper
+public interface SentenceEvaluationMapper extends BaseMapper<SentenceEvaluation> {
+}

+ 20 - 0
src/main/java/com/njuzr/eaibackend/po/Evaluation.java

@@ -0,0 +1,20 @@
+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;
+
+/**
+ * @author BaiBai
+ * @date 2025-01-18 12:23
+ */
+@Data
+@TableName("evaluation")
+public class Evaluation {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Long studentId;
+    private Long assignmentId;
+    private Integer version;
+}

+ 22 - 0
src/main/java/com/njuzr/eaibackend/po/OverallEvaluation.java

@@ -0,0 +1,22 @@
+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;
+
+/**
+ * @author BaiBai
+ * @date 2025-01-17 18:41
+ */
+@Data
+@TableName("overall_evaluation")
+public class OverallEvaluation {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Long studentId;
+    private Long assignmentId;
+    private String content;
+    private Integer version; // 保存后才能进行批改,批改版本跟随作业版本
+    private String evaluation;
+}

+ 25 - 0
src/main/java/com/njuzr/eaibackend/po/SentenceEvaluation.java

@@ -0,0 +1,25 @@
+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;
+
+/**
+ * @author BaiBai
+ * @date 2025-01-17 18:41
+ */
+@Data
+@TableName("sentence_evaluation")
+public class SentenceEvaluation {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Long studentId;
+    private Long assignmentId;
+    private Integer no;
+    private String content;
+    private String sentence;
+    private String evaluation;
+    private Integer version;
+    private String type;
+}

+ 1 - 1
src/main/java/com/njuzr/eaibackend/service/AIDialogueService.java

@@ -308,4 +308,4 @@ public class AIDialogueService {
             throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"数据库更新失败");
         }
     }
-}
+}

+ 332 - 0
src/main/java/com/njuzr/eaibackend/service/AIEvaluationService.java

@@ -0,0 +1,332 @@
+package com.njuzr.eaibackend.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.njuzr.eaibackend.exception.MyException;
+import com.njuzr.eaibackend.mapper.*;
+import com.njuzr.eaibackend.po.*;
+import com.njuzr.eaibackend.utils.FileUtil;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author BaiBai
+ * @date 2025-01-17 18:58
+ */
+@Slf4j
+@Service
+public class AIEvaluationService {
+    // 整体评价
+    // 两个输入 1.作文描述 2.作文内容
+    private static final String REWRITE_PROMPT_TEMPLATE = "假设你现在是一位英文专业的教师,正在批阅一位同学的写作作业,作业描述是:%s"
+            + "请做如下工作:\n"
+            + "1. 满分100分,请综合英文用词、句式等方面,给出评分和评分原因\n"
+            + "2. 请对以下英语作文进行详细分析,从词汇、句法、内容结构、逻辑连贯性、语言风格以及整体写作水平等方面展开。\n" +
+            "    词汇层面,评估词汇的丰富度,是否存在重复词汇,是否运用了适合文章主题和难度级别的词汇,有无高级词汇或专业术语的恰当使用。检查词汇拼写与词性搭配是否准确,以及词汇的感情色彩是否契合文章基调。\n" +
+            "    句法层面,剖析句子结构的多样性,是否合理运用了简单句、并列句、复合句等。判断句子语法是否正确,包括主谓一致、时态一致、语态使用是否恰当等。关注句子的长度和复杂度是否适中,是否存在句式过于冗长或过于简单的问题。\n" +
+            "    内容结构方面,分析文章是否紧扣主题,开头是否能够有效引入主题,中间论述是否条理清晰、层次分明,结尾是否有力且能升华主题。检查段落之间的过渡是否自然,每个段落是否有明确的中心思想,段落内部的内容是否紧密围绕该中心思想展开。\n" +
+            "    逻辑连贯性上,判断文章整体逻辑是否清晰,观点之间的衔接是否自然流畅,是否使用了合适的连接词、过渡语来增强逻辑关系。\n" +
+            "    语言风格上,考量文章是正式、半正式还是口语化风格,风格是否与文章类型和受众相匹配。\n" +
+            "    整体写作水平方面,综合上述各项因素,给出一个整体评价,指出文章的优点与不足,并提出具体的改进建议。\n"
+            + "格式举例:\n"
+            + "评分:95分\n" +
+            "\n" +
+            "评分原因:\n" +
+            "\n" +
+            "1. 词汇层面:文章词汇丰富,运用了多种词汇,如\"palpable sense of community\"、\"isolationist world\"、\"raw creativity\"、\"immense patience\"、\"academic rigor\"等,体现了作者对词汇的恰当运用。文章中未出现重复词汇,高级词汇和术语的使用也恰到好处,如\"algorithm design\"。\n" +
+            "\n" +
+            "2. 句法层面:文章句式多样,合理运用了简单句、并列句和复合句。句子结构清晰,语法正确,如主谓一致和时态一致等。句子的长度和复杂度适中,既不过于冗长也不过于简单。\n" +
+            "\n" +
+            "3. 内容结构方面:文章紧扣主题,开头通过激活时光机器的方式引人入胜。中间部分分别从校园氛围、学术氛围、社交氛围等方面展开,条理清晰,层次分明。结尾升华主题,表达对那段时光的感激之情,使文章更具感染力。段落之间的过渡自然,每个段落都有明确的中心思想。\n" +
+            "\n" +
+            "4. 逻辑连贯性方面:文章整体逻辑清晰,观点之间的衔接自然流畅,使用了合适的连接词和过渡语来增强逻辑关系。\n" +
+            "\n" +
+            "5. 语言风格方面:文章采用正式风格,适合文章类型和受众,既庄重又富有感染力。\n" +
+            "\n" +
+            "6. 整体写作水平方面:文章结构完整,内容丰富,语言流畅,表达清晰,具有很高的写作水平。\n" +
+            "\n" +
+            "改进建议:\n" +
+            "1. 可以适当增加一些形容词和副词,使文章更加生动形象。\n" +
+            "2. 在描述校园氛围和学术氛围时,可以加入一些具体的事例来增强说服力。\n" +
+            "3. 结尾部分可以进一步强调作者对那段时光的感激之情,使文章更具感染力。"
+            + "其余的任何描述都不需要,也不需要任何交互!\n"
+            + "以下是学生作业内容:\n\n"
+            + "%s";
+    // 逐句评价
+    private static final String SENTENCE_PROMPT_TEMPLATE = "假设你是一位英文专业的教师,正在批改一份写作作业,以下是作业相关信息:\n" +
+            "- 作业描述:%s\n" +
+            "- 学生作业内容:%s\n" +
+            "- 待分析的句子:%s\n" +
+            "\n" + "请从以下三个角度对该句子进行评价,并选择最需要修改的一个角度给出意见。若句子合理,则回复“合理”;若句子不合理,则按照以下格式回复:\n" +
+            "{角度小标题}\n" +
+            "{具体修改建议}\n" +
+            "\n" +
+            "例如\n" +
+            "\n" +
+            "1.合理\n" +
+            "\n" +
+            "则回复\n" +
+            "\n" +
+            "修改建议:合理\n" +
+            "\n" +
+            "2.词汇最不合理\n" +
+            "\n" +
+            "则回复\n" +
+            "\n" +
+            "词汇表达意思不够生动\n" +
+            "\n" +
+            "可以使用“the world outside the time machine swirled into obscurity”或“the world outside the time machine dissolved into a whirl of blurs”来替代,这样既能保持句子的流畅性,又能增加描述的生动性。" +
+            "评判标准如下:\n" +
+            "- **词汇分析**:\n" +
+            "  - **词汇丰富度**:查看句子中词汇的多样性,避免词汇重复,确保词汇的难度符合作文要求。如高中英语作文应使用一定比例的高级词汇,大学英语四六级作文则对词汇丰富度和复杂度有更高要求。检查是否有简单词汇可被更精准、更具表现力的词汇替换,例如“good”可改为“excellent”“marvelous”“superb”等。\n" +
+            "  - **词汇准确性**:确保词汇拼写正确,词性使用恰当,注意词汇搭配的合理性和符合英语表达习惯,例如“affect”(动词,影响)和“effect”(名词,影响)不可混淆,“make a decision”正确,“do a decision”错误。\n" +
+            "- **句法分析**:\n" +
+            "  - **句型结构**:分析使用的基本句型(如主谓宾、主系表等),检查是否运用多种句型以丰富表达,避免单一。例如,应灵活运用简单句、并列句和复合句。确保句子结构完整,无成分缺失或多余,如“Because he is ill, so he didn't come to school.”应去掉“because”或“so”其中一个。\n" +
+            "  - **语法正确性**:检查时态、语态是否正确且一致,例如描述过去事件用一般过去时,强调动作承受者用被动语态。同时,检查主谓是否一致,名词单复数是否正确,如“The book on the desk are mine.”应改为“The book on the desk is mine.”。\n" +
+            "- **内容结构分析**:\n" +
+            "  - **与主题相关性**:判断句子是否紧密围绕作文主题展开,是否对主题表达有积极贡献。例如,若主题是“环境保护”,句子应涉及环境污染现状、原因、解决措施等相关内容。同时,检查句子在上下文中的连贯性,是否能自然衔接前后句,使文章逻辑清晰,如使用过渡句引导读者理解文章思路。\n" +
+            "  - **逻辑连贯性**:分析句子内部及句子间的逻辑关系是否清晰,使用恰当连接词(如“and”“but”“however”“therefore”等)表达并列、转折、因果等逻辑关系。检查句子能否有效支持段落主旨,段落能否服务于文章整体结构,例如在论述观点时,句子应提供具体论据或例子支持论点。";
+
+    private final AIRequestService aiRequestService;
+
+    private final StudentAssignmentMapper studentAssignmentMapper;
+
+    private final AssignmentMapper assignmentMapper;
+
+    private final OverallEvaluationMapper overallEvaluationMapper;
+
+    private final SentenceEvaluationMapper sentenceEvaluationMapper;
+
+    private final EvaluationMapper evaluationMapper;
+
+    private final FileUtil fileUtil = new FileUtil();
+
+    @Autowired
+    public AIEvaluationService(AIRequestService aiRequestService, StudentAssignmentMapper studentAssignmentMapper, AssignmentMapper assignmentMapper, OverallEvaluationMapper overallEvaluationMapper, SentenceEvaluationMapper sentenceEvaluationMapper, EvaluationMapper evaluationMapper) {
+        this.aiRequestService = aiRequestService;
+        this.studentAssignmentMapper = studentAssignmentMapper;
+        this.assignmentMapper = assignmentMapper;
+        this.overallEvaluationMapper = overallEvaluationMapper;
+        this.sentenceEvaluationMapper = sentenceEvaluationMapper;
+        this.evaluationMapper = evaluationMapper;
+    }
+    /**
+     * AI作文整体智能批改
+     * @param assignmentId
+     * @param studentId
+     * @return
+     */
+    public AIEntry rewrite(Long assignmentId, Long studentId) {
+        // 1 通过assignmentId和studentId找到engagement,获取当前文件链接
+        Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
+        if(engagement == null)
+            throw MyException.create(HttpStatus.BAD_REQUEST, "作业参与不存在");
+
+        // 通过assignmentId找到作业,获取作业描述
+        Assignment assignment = assignmentMapper.selectById(assignmentId);
+        if (assignment == null)
+            throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
+
+        // 确定当前批改没有进行过
+        Evaluation evaluationJudge = evaluationMapper.selectOne(new QueryWrapper<Evaluation>()
+                .eq("assignment_id", assignmentId)
+                .eq("student_id", studentId)
+                .eq("version", engagement.getVersion()));
+        if(evaluationJudge != null){
+            throw MyException.create(HttpStatus.BAD_REQUEST, "作业已进行过AI分析");
+        }
+
+        String assignmentDescription = assignment.getDescription();
+
+        // 2 找到学生作业内容
+        String content = engagement.getTextContent();
+        log.info("作文内容为:" + content);
+
+        // 3 构造提示词
+        String prompt = String.format(REWRITE_PROMPT_TEMPLATE, assignmentDescription, content);
+        log.info("内置提示词为:"+ prompt);
+
+        // 4 请求AI
+        List<AIEntry> entry = new ArrayList<>();
+        entry.add(new AIEntry("user", prompt));
+        AIRequestService.AIResponse response = aiRequestService.requestChatGLM4(entry);
+
+        // 5 获取返回数据
+        String retContent;
+        String role;
+        Long timestamp;
+        try {
+            retContent = response.getChoices().get(0).getMessage().getContent();
+            role = response.getChoices().get(0).getMessage().getRole();
+            timestamp = response.getCreated();
+            log.info("AI返回的数据为:"+retContent);
+        } catch (Exception e) {
+            log.error("AI请求返回的数据内容有误,数据为{}", response);
+            throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"请求失败");
+        }
+
+
+        // 6 存储数据,已修改为MySQL存储。历史版本使用MangoDB,可前往仓库查看。
+        // 6.1 存储评价
+        OverallEvaluation overallEvaluation = new OverallEvaluation();
+        overallEvaluation.setStudentId(studentId);
+        overallEvaluation.setAssignmentId(assignmentId);
+        overallEvaluation.setContent(content);
+        overallEvaluation.setEvaluation(retContent);
+        // 评价的版本跟随作业版本
+        overallEvaluation.setVersion(engagement.getVersion());
+        overallEvaluationMapper.insert(overallEvaluation);
+
+        // 6.2 存储版本信息
+        Evaluation evaluation = new Evaluation();
+        evaluation.setAssignmentId(assignmentId);
+        evaluation.setStudentId(studentId);
+        evaluation.setVersion(engagement.getVersion());
+        evaluationMapper.insert(evaluation);
+
+        return new AIEntry(role, retContent);
+
+    }
+
+    /**
+     * AI作业逐句智能批改
+     * @param assignmentId
+     * @param studentId
+     * @return
+     */
+    public List<SentenceEvaluation> rewritePerSentence(Long assignmentId, Long studentId){
+        // 1 通过assignmentId和studentId找到engagement,获取当前文件链接
+        Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
+        if(engagement == null)
+            throw MyException.create(HttpStatus.BAD_REQUEST, "作业参与不存在");
+
+        // 通过assignmentId找到作业,获取作业描述
+        Assignment assignment = assignmentMapper.selectById(assignmentId);
+        if (assignment == null)
+            throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
+
+        // 确定当前批改没有进行过
+        Evaluation evaluationJudge = evaluationMapper.selectOne(new QueryWrapper<Evaluation>()
+                .eq("assignment_id", assignmentId)
+                .eq("student_id", studentId)
+                .eq("version", engagement.getVersion()));
+        if(evaluationJudge != null){
+            throw MyException.create(HttpStatus.BAD_REQUEST, "作业已进行过AI分析");
+        }
+
+        String assignmentDescription = assignment.getDescription();
+
+        // 2 找到学生作业内容
+        String content = engagement.getTextContent();
+        log.info("作文内容为:" + content);
+
+        // 3 分割作文的句子
+        // TODO: 句子分割需要详细考虑和判断
+        String[] res = content.split("\\.");
+
+        // 4 初始化AI请求变量
+        List<AIEntry> entry = new ArrayList<>();
+        AIRequestService.AIResponse response;
+
+        // 5 初始化获取返回数据
+        String retContent = "";
+        List<SentenceEvaluation> responses = new ArrayList<>();
+
+        // 6 逐句进行处理
+        for(int i = 0; i < res.length; i++) {
+            SentenceEvaluation sentenceEvaluation = new SentenceEvaluation();
+
+            // 1 构造提示词
+            String prompt2 = String.format(SENTENCE_PROMPT_TEMPLATE, assignmentDescription, content, res[i]);
+            log.info("内置提示词为:"+ prompt2);
+
+            // 2 请求AI
+            entry.clear();
+            entry.add(new AIEntry("user", prompt2));
+            response = aiRequestService.requestChatGLM4(entry);
+
+            // 3 获取返回数据
+            try {
+                sentenceEvaluation.setEvaluation(response.getChoices().get(0).getMessage().getContent());
+                log.info("AI返回的数据为:"+retContent);
+            } catch (Exception e) {
+                log.error("AI请求返回的数据内容有误,数据为{}", response);
+                throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"请求失败");
+            }
+
+            // 4 存储数据
+            sentenceEvaluation.setAssignmentId(assignmentId);
+            sentenceEvaluation.setStudentId(studentId);
+            sentenceEvaluation.setContent(content);
+            sentenceEvaluation.setNo(i+1);
+            sentenceEvaluation.setSentence(res[i] + "\n");
+            sentenceEvaluation.setVersion(engagement.getVersion());
+            sentenceEvaluationMapper.insert(sentenceEvaluation);
+
+            // 5 填入返回信息
+            responses.add(sentenceEvaluation);
+        }
+        return responses;
+    }
+
+    /**
+     * 获得AI作业整体评价记录详情
+     * @param assignmentId
+     * @param studentId
+     * @param version
+     * @return
+     */
+    public String getRewrite(Long assignmentId, Long studentId, int version){
+        QueryWrapper<SentenceEvaluation> queryWrapper =  new QueryWrapper<>();
+        queryWrapper.eq("assignment_id", assignmentId);
+        queryWrapper.eq("student_id", studentId);
+        queryWrapper.eq("version", version);
+        SentenceEvaluation sentenceEvaluation = sentenceEvaluationMapper.selectOne(queryWrapper);
+        if(sentenceEvaluation == null){
+            throw MyException.create(HttpStatus.BAD_REQUEST, "AI批改不存在");
+        }
+        return sentenceEvaluation.getContent();
+    }
+
+    /**
+     * 获得AI作业逐句评价记录详情
+     * @param assignmentId
+     * @param studentId
+     * @param version
+     * @return
+     */
+    public List<SentenceEvaluation> getRewritePerSentence(Long assignmentId, Long studentId, int version){
+        QueryWrapper<SentenceEvaluation> queryWrapper =  new QueryWrapper<>();
+        queryWrapper.eq("assignment_id", assignmentId);
+        queryWrapper.eq("student_id", studentId);
+        queryWrapper.eq("version", version);
+        List<SentenceEvaluation> sentenceEvaluations = sentenceEvaluationMapper.selectList(queryWrapper);
+
+        // 按no属性进行排序
+        return sentenceEvaluations.stream().sorted(Comparator.comparing(SentenceEvaluation::getNo)).toList();
+    }
+
+    /**
+     * 获取所有AI评价版本记录
+     * @param assignmentId
+     * @param studentId
+     * @return
+     */
+    public List<Evaluation> getVersionList(Long assignmentId, Long studentId){
+        QueryWrapper<Evaluation> queryWrapper =  new QueryWrapper<>();
+        queryWrapper.eq("assignment_id", assignmentId);
+        queryWrapper.eq("student_id", studentId);
+
+        return evaluationMapper.selectList(queryWrapper);
+    }
+
+}

+ 6 - 0
src/test/java/com/njuzr/eaibackend/EaiBackendApplicationTests.java

@@ -1,13 +1,19 @@
 package com.njuzr.eaibackend;
 
+import com.njuzr.eaibackend.service.impl.CourseServiceImpl;
 import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 
 @SpringBootTest
 class EaiBackendApplicationTests {
 
+    @Autowired
+    private CourseServiceImpl courseServiceImpl;
+
     @Test
     void contextLoads() {
+        System.out.println(courseServiceImpl.getEnrollCodeByCourseId(111112L));
     }
 
 }