diff --git a/.superpowers/sdd/task-1-report.md b/.superpowers/sdd/task-1-report.md new file mode 100644 index 0000000..cb3e4a8 --- /dev/null +++ b/.superpowers/sdd/task-1-report.md @@ -0,0 +1,28 @@ +# Task 1: AI Training Evaluation Persistence Report + +## Delivered + +- Added `docs/sql/2026-07-16-ai-training-evaluation.sql`. + - Creates `ai_training_evaluation` with a unique `(student_user_id, teaching_class_id, task_id)` key. + - Persists independent help and assessment statuses, task/answer snapshots, reports, raw model responses, model names, completion times, errors, and assessment score. + - Adds nullable `student_training_answer.ai_assessment_score`. +- Added `AiTrainingEvaluation`, `AiTrainingEvaluationMapper`, and its MyBatis XML mapping. + - `findByStudentClassAndTask` retrieves the sole evaluation record for a student, teaching class, and task. + - `insertIgnore` safely creates the record under concurrent requests. + - `claimHelp` and `claimAssessment` transition only `NOT_STARTED` or `FAILED` rows to `PROCESSING`. + - Completion and failure operations update only rows currently in `PROCESSING`. +- Added `aiAssessmentScore` to `StudentTrainingAnswer`, its XML result/column/dynamic insert/dynamic update mappings, and `updateAiAssessmentScore` for a targeted score write-back. +- Added the assessment score column to the fresh `student_training_answer` table DDL in `StudentTrainingAnswerServiceImpl`. + +## Test-first evidence + +1. Added `AiTrainingEvaluationServiceImplTest` before implementation. +2. Ran `mvn -Dtest=AiTrainingEvaluationServiceImplTest test` before production changes. + - Expected RED result: test compilation failed because `AiTrainingEvaluation` and `AiTrainingEvaluationMapper` did not exist. +3. Implemented the persistence files and re-ran the same targeted Maven test. + - GREEN result: 3 tests run; 0 failures; 0 errors; 0 skipped. + +## Verification notes + +- `git diff --check` completed without whitespace errors. +- Maven emitted existing project-model warnings about local Aspose `systemPath` dependencies and the relocated MySQL connector artifact. They do not originate from this task and did not prevent compilation or the targeted test from succeeding. diff --git a/docs/sql/2026-07-16-ai-training-evaluation.sql b/docs/sql/2026-07-16-ai-training-evaluation.sql new file mode 100644 index 0000000..1904472 --- /dev/null +++ b/docs/sql/2026-07-16-ai-training-evaluation.sql @@ -0,0 +1,31 @@ +CREATE TABLE IF NOT EXISTS ai_training_evaluation ( + id varchar(64) NOT NULL, + student_user_id varchar(64) NOT NULL, + teaching_class_id varchar(64) NOT NULL, + task_id varchar(64) NOT NULL, + task_key varchar(128) NOT NULL, + help_status varchar(16) NOT NULL DEFAULT 'NOT_STARTED', + help_task_snapshot longtext NULL, + help_answer_snapshot longtext NULL, + help_report_json longtext NULL, + help_raw_response longtext NULL, + help_model varchar(128) NULL, + help_completed_at datetime NULL, + help_error_message varchar(1000) NULL, + assessment_status varchar(16) NOT NULL DEFAULT 'NOT_STARTED', + assessment_task_snapshot longtext NULL, + assessment_answer_snapshot longtext NULL, + assessment_score int NULL, + assessment_report_json longtext NULL, + assessment_raw_response longtext NULL, + assessment_model varchar(128) NULL, + assessment_completed_at datetime NULL, + assessment_error_message varchar(1000) NULL, + create_time datetime NOT NULL, + update_time datetime NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY uk_ai_training_evaluation (student_user_id, teaching_class_id, task_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +ALTER TABLE student_training_answer + ADD COLUMN IF NOT EXISTS ai_assessment_score int NULL; diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/AiTrainingEvaluation.java b/src/main/java/com/sztzjy/linkCommerce/entity/AiTrainingEvaluation.java new file mode 100644 index 0000000..fb01571 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/AiTrainingEvaluation.java @@ -0,0 +1,83 @@ +package com.sztzjy.linkCommerce.entity; + +import java.util.Date; + +public class AiTrainingEvaluation { + private String id; + private String studentUserId; + private String teachingClassId; + private String taskId; + private String taskKey; + private String helpStatus; + private String helpTaskSnapshot; + private String helpAnswerSnapshot; + private String helpReportJson; + private String helpRawResponse; + private String helpModel; + private Date helpCompletedAt; + private String helpErrorMessage; + private String assessmentStatus; + private String assessmentTaskSnapshot; + private String assessmentAnswerSnapshot; + private Integer assessmentScore; + private String assessmentReportJson; + private String assessmentRawResponse; + private String assessmentModel; + private Date assessmentCompletedAt; + private String assessmentErrorMessage; + private Date createTime; + private Date updateTime; + + public String getId() { return id; } + public void setId(String id) { this.id = trim(id); } + public String getStudentUserId() { return studentUserId; } + public void setStudentUserId(String studentUserId) { this.studentUserId = trim(studentUserId); } + public String getTeachingClassId() { return teachingClassId; } + public void setTeachingClassId(String teachingClassId) { this.teachingClassId = trim(teachingClassId); } + public String getTaskId() { return taskId; } + public void setTaskId(String taskId) { this.taskId = trim(taskId); } + public String getTaskKey() { return taskKey; } + public void setTaskKey(String taskKey) { this.taskKey = trim(taskKey); } + public String getHelpStatus() { return helpStatus; } + public void setHelpStatus(String helpStatus) { this.helpStatus = trim(helpStatus); } + public String getHelpTaskSnapshot() { return helpTaskSnapshot; } + public void setHelpTaskSnapshot(String helpTaskSnapshot) { this.helpTaskSnapshot = trim(helpTaskSnapshot); } + public String getHelpAnswerSnapshot() { return helpAnswerSnapshot; } + public void setHelpAnswerSnapshot(String helpAnswerSnapshot) { this.helpAnswerSnapshot = trim(helpAnswerSnapshot); } + public String getHelpReportJson() { return helpReportJson; } + public void setHelpReportJson(String helpReportJson) { this.helpReportJson = trim(helpReportJson); } + public String getHelpRawResponse() { return helpRawResponse; } + public void setHelpRawResponse(String helpRawResponse) { this.helpRawResponse = trim(helpRawResponse); } + public String getHelpModel() { return helpModel; } + public void setHelpModel(String helpModel) { this.helpModel = trim(helpModel); } + public Date getHelpCompletedAt() { return helpCompletedAt; } + public void setHelpCompletedAt(Date helpCompletedAt) { this.helpCompletedAt = helpCompletedAt; } + public String getHelpErrorMessage() { return helpErrorMessage; } + public void setHelpErrorMessage(String helpErrorMessage) { this.helpErrorMessage = trim(helpErrorMessage); } + public String getAssessmentStatus() { return assessmentStatus; } + public void setAssessmentStatus(String assessmentStatus) { this.assessmentStatus = trim(assessmentStatus); } + public String getAssessmentTaskSnapshot() { return assessmentTaskSnapshot; } + public void setAssessmentTaskSnapshot(String assessmentTaskSnapshot) { this.assessmentTaskSnapshot = trim(assessmentTaskSnapshot); } + public String getAssessmentAnswerSnapshot() { return assessmentAnswerSnapshot; } + public void setAssessmentAnswerSnapshot(String assessmentAnswerSnapshot) { this.assessmentAnswerSnapshot = trim(assessmentAnswerSnapshot); } + public Integer getAssessmentScore() { return assessmentScore; } + public void setAssessmentScore(Integer assessmentScore) { this.assessmentScore = assessmentScore; } + public String getAssessmentReportJson() { return assessmentReportJson; } + public void setAssessmentReportJson(String assessmentReportJson) { this.assessmentReportJson = trim(assessmentReportJson); } + public String getAssessmentRawResponse() { return assessmentRawResponse; } + public void setAssessmentRawResponse(String assessmentRawResponse) { this.assessmentRawResponse = trim(assessmentRawResponse); } + public String getAssessmentModel() { return assessmentModel; } + public void setAssessmentModel(String assessmentModel) { this.assessmentModel = trim(assessmentModel); } + public Date getAssessmentCompletedAt() { return assessmentCompletedAt; } + public void setAssessmentCompletedAt(Date assessmentCompletedAt) { this.assessmentCompletedAt = assessmentCompletedAt; } + public String getAssessmentErrorMessage() { return assessmentErrorMessage; } + public void setAssessmentErrorMessage(String assessmentErrorMessage) { this.assessmentErrorMessage = trim(assessmentErrorMessage); } + public Date getCreateTime() { return createTime; } + public void setCreateTime(Date createTime) { this.createTime = createTime; } + public Date getUpdateTime() { return updateTime; } + public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } + + private String trim(String value) { + return value == null ? null : value.trim(); + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java b/src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java index 3c8b1a0..6d15dda 100644 --- a/src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java +++ b/src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java @@ -44,6 +44,8 @@ public class StudentTrainingAnswer { @ApiModelProperty("是否提交") private Boolean submitted; + private Integer aiAssessmentScore; + @ApiModelProperty("保存动作:SAVE/SUBMIT/RESET") private String saveAction; @@ -160,6 +162,14 @@ public class StudentTrainingAnswer { this.submitted = submitted; } + public Integer getAiAssessmentScore() { + return aiAssessmentScore; + } + + public void setAiAssessmentScore(Integer aiAssessmentScore) { + this.aiAssessmentScore = aiAssessmentScore; + } + public String getSaveAction() { return saveAction; } diff --git a/src/main/java/com/sztzjy/linkCommerce/mapper/AiTrainingEvaluationMapper.java b/src/main/java/com/sztzjy/linkCommerce/mapper/AiTrainingEvaluationMapper.java new file mode 100644 index 0000000..172d804 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/mapper/AiTrainingEvaluationMapper.java @@ -0,0 +1,30 @@ +package com.sztzjy.linkCommerce.mapper; + +import com.sztzjy.linkCommerce.entity.AiTrainingEvaluation; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; + +@Mapper +public interface AiTrainingEvaluationMapper { + AiTrainingEvaluation findByStudentClassAndTask(@Param("studentUserId") String studentUserId, + @Param("teachingClassId") String teachingClassId, + @Param("taskId") String taskId); + + int insertIgnore(AiTrainingEvaluation record); + + int claimHelp(@Param("id") String id, @Param("updateTime") Date updateTime); + + int claimAssessment(@Param("id") String id, @Param("updateTime") Date updateTime); + + int completeHelp(AiTrainingEvaluation record); + + int completeAssessment(AiTrainingEvaluation record); + + int failHelp(@Param("id") String id, @Param("errorMessage") String errorMessage, + @Param("updateTime") Date updateTime); + + int failAssessment(@Param("id") String id, @Param("errorMessage") String errorMessage, + @Param("updateTime") Date updateTime); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/mapper/StudentTrainingAnswerMapper.java b/src/main/java/com/sztzjy/linkCommerce/mapper/StudentTrainingAnswerMapper.java index 2e712fc..87f5c8e 100644 --- a/src/main/java/com/sztzjy/linkCommerce/mapper/StudentTrainingAnswerMapper.java +++ b/src/main/java/com/sztzjy/linkCommerce/mapper/StudentTrainingAnswerMapper.java @@ -4,6 +4,8 @@ import com.sztzjy.linkCommerce.entity.StudentTrainingAnswer; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import java.util.Date; + @Mapper public interface StudentTrainingAnswerMapper { int insertSelective(StudentTrainingAnswer record); @@ -19,4 +21,8 @@ public interface StudentTrainingAnswerMapper { int deleteByStudentClassAndTask(@Param("studentUserId") String studentUserId, @Param("teachingClassId") String teachingClassId, @Param("taskId") String taskId); + + int updateAiAssessmentScore(@Param("id") String id, + @Param("score") Integer score, + @Param("updateTime") Date updateTime); } diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/StudentTrainingAnswerServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/StudentTrainingAnswerServiceImpl.java index 941741e..1fb8e40 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/impl/StudentTrainingAnswerServiceImpl.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/StudentTrainingAnswerServiceImpl.java @@ -242,6 +242,7 @@ public class StudentTrainingAnswerServiceImpl implements StudentTrainingAnswerSe "current_step int DEFAULT 1 COMMENT 'current step'," + "progress_status varchar(32) DEFAULT 'NOT_STARTED' COMMENT 'progress status'," + "submitted bit(1) DEFAULT b'0' COMMENT 'submitted'," + + "ai_assessment_score int NULL COMMENT 'AI assessment score'," + "submit_time datetime NULL COMMENT 'submit time'," + "create_time datetime NULL COMMENT 'create time'," + "update_time datetime NULL COMMENT 'update time'," + diff --git a/src/main/resources/mappers/AiTrainingEvaluationMapper.xml b/src/main/resources/mappers/AiTrainingEvaluationMapper.xml new file mode 100644 index 0000000..f4d3b9d --- /dev/null +++ b/src/main/resources/mappers/AiTrainingEvaluationMapper.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, student_user_id, teaching_class_id, task_id, task_key, + help_status, help_task_snapshot, help_answer_snapshot, help_report_json, help_raw_response, + help_model, help_completed_at, help_error_message, + assessment_status, assessment_task_snapshot, assessment_answer_snapshot, assessment_score, + assessment_report_json, assessment_raw_response, assessment_model, assessment_completed_at, + assessment_error_message, create_time, update_time + + + + + + INSERT IGNORE INTO ai_training_evaluation ( + id, student_user_id, teaching_class_id, task_id, task_key, + help_status, assessment_status, create_time, update_time + ) VALUES ( + #{id,jdbcType=VARCHAR}, #{studentUserId,jdbcType=VARCHAR}, #{teachingClassId,jdbcType=VARCHAR}, + #{taskId,jdbcType=VARCHAR}, #{taskKey,jdbcType=VARCHAR}, + #{helpStatus,jdbcType=VARCHAR}, #{assessmentStatus,jdbcType=VARCHAR}, + #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP} + ) + + + + UPDATE ai_training_evaluation + SET help_status = 'PROCESSING', help_error_message = NULL, update_time = #{updateTime,jdbcType=TIMESTAMP} + WHERE id = #{id,jdbcType=VARCHAR} + AND help_status IN ('NOT_STARTED', 'FAILED') + + + + UPDATE ai_training_evaluation + SET assessment_status = 'PROCESSING', assessment_error_message = NULL, update_time = #{updateTime,jdbcType=TIMESTAMP} + WHERE id = #{id,jdbcType=VARCHAR} + AND assessment_status IN ('NOT_STARTED', 'FAILED') + + + + UPDATE ai_training_evaluation + SET help_status = 'SUCCEEDED', + help_task_snapshot = #{helpTaskSnapshot}, + help_answer_snapshot = #{helpAnswerSnapshot}, + help_report_json = #{helpReportJson}, + help_raw_response = #{helpRawResponse}, + help_model = #{helpModel,jdbcType=VARCHAR}, + help_completed_at = #{helpCompletedAt,jdbcType=TIMESTAMP}, + help_error_message = NULL, + update_time = #{updateTime,jdbcType=TIMESTAMP} + WHERE id = #{id,jdbcType=VARCHAR} AND help_status = 'PROCESSING' + + + + UPDATE ai_training_evaluation + SET assessment_status = 'SUCCEEDED', + assessment_task_snapshot = #{assessmentTaskSnapshot}, + assessment_answer_snapshot = #{assessmentAnswerSnapshot}, + assessment_score = #{assessmentScore,jdbcType=INTEGER}, + assessment_report_json = #{assessmentReportJson}, + assessment_raw_response = #{assessmentRawResponse}, + assessment_model = #{assessmentModel,jdbcType=VARCHAR}, + assessment_completed_at = #{assessmentCompletedAt,jdbcType=TIMESTAMP}, + assessment_error_message = NULL, + update_time = #{updateTime,jdbcType=TIMESTAMP} + WHERE id = #{id,jdbcType=VARCHAR} AND assessment_status = 'PROCESSING' + + + + UPDATE ai_training_evaluation + SET help_status = 'FAILED', help_error_message = #{errorMessage,jdbcType=VARCHAR}, + update_time = #{updateTime,jdbcType=TIMESTAMP} + WHERE id = #{id,jdbcType=VARCHAR} AND help_status = 'PROCESSING' + + + + UPDATE ai_training_evaluation + SET assessment_status = 'FAILED', assessment_error_message = #{errorMessage,jdbcType=VARCHAR}, + update_time = #{updateTime,jdbcType=TIMESTAMP} + WHERE id = #{id,jdbcType=VARCHAR} AND assessment_status = 'PROCESSING' + + diff --git a/src/main/resources/mappers/StudentTrainingAnswerMapper.xml b/src/main/resources/mappers/StudentTrainingAnswerMapper.xml index 789d404..570704e 100644 --- a/src/main/resources/mappers/StudentTrainingAnswerMapper.xml +++ b/src/main/resources/mappers/StudentTrainingAnswerMapper.xml @@ -15,6 +15,7 @@ + @@ -22,7 +23,7 @@ id, student_user_id, teaching_class_id, task_id, task_key, task_name, step1_answer, step2_answer, - step3_answer, step4_answer, current_step, progress_status, submitted, submit_time, create_time, update_time + step3_answer, step4_answer, current_step, progress_status, submitted, ai_assessment_score, submit_time, create_time, update_time