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