From af8f337a5d069d1db08ad2671c3dbed887e8b273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=B2=85?= <907037276@qq.com> Date: Thu, 3 Sep 2026 14:27:47 +0800 Subject: [PATCH] fix: sync teacher task grades and training progress --- ...026-09-03-teacher-grade-progress-design.md | 50 ---- ...ngAnswerTeacherScoreSchemaInitializer.java | 22 ++ .../controller/stu/TeaScoreController.java | 232 ++++++++++++++---- .../entity/StudentTrainingAnswer.java | 17 ++ .../entity/dto/TeacherTaskReviewDTO.java | 31 +++ .../mapper/StudentTrainingAnswerMapper.java | 5 + .../service/TeacherScoreService.java | 1 + .../service/impl/TeacherScoreServiceImpl.java | 5 + .../mappers/StudentTrainingAnswerMapper.xml | 23 +- .../stu/TeaScoreControllerProgressTest.java | 44 +++- 10 files changed, 330 insertions(+), 100 deletions(-) delete mode 100644 docs/superpowers/specs/2026-09-03-teacher-grade-progress-design.md create mode 100644 src/main/java/com/sztzjy/linkCommerce/config/StudentTrainingAnswerTeacherScoreSchemaInitializer.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/TeacherTaskReviewDTO.java diff --git a/docs/superpowers/specs/2026-09-03-teacher-grade-progress-design.md b/docs/superpowers/specs/2026-09-03-teacher-grade-progress-design.md deleted file mode 100644 index 9f8dfc8..0000000 --- a/docs/superpowers/specs/2026-09-03-teacher-grade-progress-design.md +++ /dev/null @@ -1,50 +0,0 @@ -# 教师成绩列表进度与评分同步设计 - -## 目标 - -教师端成绩列表展示学生已完成实训任务数与总实训任务数,并实时汇总教师在任务评阅中保存的逐任务评分;成绩列表不再允许直接录入或保存教师总分。 - -## 问题与根因 - -当前进度统计从旧的 `stu_grade` 查询已完成模块,而当前实训完成状态保存在 `student_training_answer`,导致完成实训的学生仍显示为 `0%`。当前教师评分写入单独的 `teaching_class_teacher_score` 汇总表,和逐任务评阅没有数据关联。 - -## 数据模型 - -在 `student_training_answer` 增加以下字段: - -- `teacher_score`:教师对该任务的 0–100 分评分,可为空。 -- `teacher_scored_by`:评分教师用户 ID,可为空。 -- `teacher_scored_at`:评分时间,可为空。 - -每条答卷已经唯一对应学生、教学班与实训任务,因此不新增独立评分表,也不再使用人工汇总评分作为成绩列表的数据源。 - -## 评阅与权限 - -成绩详情弹窗扩展为任务评阅界面,展示当前教学班已启用的全部任务及对应任务状态、AI 评测成绩和教师评分。班级创建教师可对已提交或状态为 `COMPLETED` 的任务录入 0–100 分并保存;其他教师仅可查看。未完成任务不可评分。 - -成绩列表的“老师评分”列只读:尚无任何教师任务评分时显示“待评阅”,存在评分后显示实时计算结果。移除列表中的数字输入框、保存按钮和人工汇总评分接口调用。 - -## 实训进度 - -总任务数取当前教学班启用的已分配任务(使用学校默认任务回退规则,排除发布标记与未参与的综合实训)。完成任务数取同一学生、同一教学班中 `submitted = true` 或 `progress_status = COMPLETED` 的答卷,并按任务去重。 - -列表显示 `已完成数/总数`,并保留百分比提示;没有可用任务时显示 `--`。 - -## 教师评分计算 - -教师总分复用当前参考成绩的六模块算法:按 `TrainingTask.projectName` 把任务归入六个模块;每个模块先计算该模块所有已分配任务教师评分的平均值,未评分任务按 0 分参与平均;再乘以当前教学班的六项权重。综合实训是否参与、参与权重归一化与 `ScoreRankServiceImpl.calculateSixModuleScore` 保持一致。 - -若班级尚未保存权重配置,教师评分计算使用并持久化与参考成绩相同的默认六项权重,不能使用另一套默认值。 - -该值在成绩列表和导出查询时即时计算,不依赖“成绩更新”按钮。任务评分保存后,重新请求列表即可得到新总分。 - -## 接口与兼容性 - -- 成绩详情接口改为返回全部有效任务及答卷、AI 分数和教师评分。 -- 新增受班级创建教师权限保护的逐任务评分保存接口。 -- 现有 `/api/weight/saveTeacherScore` 不再由前端调用;旧汇总数据保留,不迁移、不删除。 - -## 测试 - -- 后端:验证答卷完成状态统计、无任务显示空进度、逐任务评分权限、未完成任务不能评分、按六模块权重汇总及未评分任务按零分计算。 -- 前端:验证列表展示完成数/总数、教师评分只读、详情中存在任务评分控件、没有列表人工评分控件或人工评分 API 调用。 diff --git a/src/main/java/com/sztzjy/linkCommerce/config/StudentTrainingAnswerTeacherScoreSchemaInitializer.java b/src/main/java/com/sztzjy/linkCommerce/config/StudentTrainingAnswerTeacherScoreSchemaInitializer.java new file mode 100644 index 0000000..9844dc9 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/config/StudentTrainingAnswerTeacherScoreSchemaInitializer.java @@ -0,0 +1,22 @@ +package com.sztzjy.linkCommerce.config; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +@Component +public class StudentTrainingAnswerTeacherScoreSchemaInitializer { + private final JdbcTemplate jdbcTemplate; + + public StudentTrainingAnswerTeacherScoreSchemaInitializer(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @PostConstruct + public void initialize() { + jdbcTemplate.execute("ALTER TABLE student_training_answer ADD COLUMN IF NOT EXISTS teacher_score decimal(5,2) NULL"); + jdbcTemplate.execute("ALTER TABLE student_training_answer ADD COLUMN IF NOT EXISTS teacher_scored_by varchar(64) NULL"); + jdbcTemplate.execute("ALTER TABLE student_training_answer ADD COLUMN IF NOT EXISTS teacher_scored_at datetime NULL"); + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/controller/stu/TeaScoreController.java b/src/main/java/com/sztzjy/linkCommerce/controller/stu/TeaScoreController.java index e3174c3..67d3cf9 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/TeaScoreController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/TeaScoreController.java @@ -13,7 +13,9 @@ import com.sztzjy.linkCommerce.mapper.StuRankMapper; import com.sztzjy.linkCommerce.mapper.StudentTrainingAnswerMapper; import com.sztzjy.linkCommerce.mapper.TaskAllocationMapper; import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper; +import com.sztzjy.linkCommerce.mapper.TrainingTaskMapper; import com.sztzjy.linkCommerce.mapper.WeightMapper; +import com.sztzjy.linkCommerce.entity.dto.TeacherTaskReviewDTO; import com.sztzjy.linkCommerce.service.ScoreRankService; import com.sztzjy.linkCommerce.service.SchoolDefaultTaskService; import com.sztzjy.linkCommerce.service.TeacherScoreService; @@ -30,11 +32,7 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; @@ -57,6 +55,8 @@ public class TeaScoreController { @Autowired TaskAllocationMapper taskAllocationMapper; @Autowired(required = false) + TrainingTaskMapper trainingTaskMapper; + @Autowired(required = false) StudentTrainingAnswerMapper studentTrainingAnswerMapper; @Autowired(required = false) SchoolDefaultTaskService schoolDefaultTaskService; @@ -122,6 +122,40 @@ public class TeaScoreController { } } + @PostMapping("/saveTaskTeacherScore") + @ApiOperation("保存教师对单个实训任务的评分") + public ResultEntity saveTaskTeacherScore(@RequestParam String classId, + @RequestParam String studentUserId, + @RequestParam String taskId, + @RequestParam BigDecimal teacherScore, + HttpServletRequest request) { + try { + JwtUser teacher = currentUser(request); + teacherScoreService.requireOwnedClass(teacher, classId); + if (teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId(studentUserId, classId) == 0) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "该学生不在当前教学班"); + } + StudentTrainingAnswer answer = studentTrainingAnswerMapper == null ? null + : studentTrainingAnswerMapper.selectByStudentClassAndTask(studentUserId, classId, taskId); + if (answer == null || !isCompleted(answer)) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "仅已完成的实训任务可以评分"); + } + if (teacherScore == null || teacherScore.compareTo(BigDecimal.ZERO) < 0 + || teacherScore.compareTo(new BigDecimal("100")) > 0) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "教师评分必须为0至100之间的数值"); + } + Date scoredAt = new Date(); + studentTrainingAnswerMapper.updateTeacherScore(answer.getId(), teacherScore.setScale(2, BigDecimal.ROUND_HALF_UP), + teacher.getUserId(), scoredAt); + answer.setTeacherScore(teacherScore.setScale(2, BigDecimal.ROUND_HALF_UP)); + answer.setTeacherScoredBy(teacher.getUserId()); + answer.setTeacherScoredAt(scoredAt); + return new ResultEntity<>(HttpStatus.OK, "任务评分保存成功", answer); + } catch (com.sztzjy.linkCommerce.config.exception.handler.ServiceException e) { + return new ResultEntity<>(e.getCode(), e.getMessage()); + } + } + @PostMapping("/selectStuRankAndScore") @ApiOperation("查询学生成绩和排名") @AnonymousAccess @@ -185,10 +219,9 @@ public class TeaScoreController { if (teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId(userId, teachingClass.getSchoolClassId()) == 0) { return new ResultEntity<>(HttpStatus.FORBIDDEN, "无权查看该学生成绩"); } - if (studentTrainingAnswerMapper != null) { - List answers = studentTrainingAnswerMapper.selectByStudentAndTeachingClass(userId, classId); + if (studentTrainingAnswerMapper != null && trainingTaskMapper != null) { return new ResultEntity<>(HttpStatus.OK, "查看学生成绩详情成功", - answers == null ? Collections.emptyList() : answers); + buildTaskReviews(userId, classId)); } // 定义模块的固定排序顺序 List moduleOrder = Arrays.asList( @@ -357,70 +390,56 @@ public class TeaScoreController { if (ranks == null || ranks.isEmpty()) { return; } - Map classTeacherScores = Collections.emptyMap(); - if (teacherScoreService != null && StringUtils.isNotBlank(requestedClassId)) { - Map loadedScores = teacherScoreService.getTeacherScores(requestedClassId, ranks.stream() - .map(StuRank::getUserId) - .filter(StringUtils::isNotBlank) - .distinct() - .collect(Collectors.toList())); - if (loadedScores != null) { - classTeacherScores = loadedScores; - } - } for (StuRank rank : ranks) { String classId = StringUtils.isNotBlank(requestedClassId) ? requestedClassId : rank.getSchoolClassId(); - rank.setTeacherScore(StringUtils.isNotBlank(requestedClassId) - ? classTeacherScores.get(rank.getUserId()) - : teacherScoreService == null ? null : teacherScoreService.getTeacherScore(classId, rank.getUserId())); - Set enabledModules = resolveEnabledTaskModules(classId); - int totalTaskCount = enabledModules.size(); + List allocations = resolveEnabledTaskAllocations(classId); + int totalTaskCount = (int) allocations.stream().map(TaskAllocation::getModule) + .filter(StringUtils::isNotBlank).distinct().count(); rank.setTotalTaskCount(totalTaskCount); if (totalTaskCount == 0) { rank.setCompletedTaskCount(0); rank.setTrainingProgress(null); + rank.setTeacherScore(null); continue; } - - StuGradeExample gradeExample = new StuGradeExample(); - StuGradeExample.Criteria gradeCriteria = gradeExample.createCriteria().andUserIdEqualTo(rank.getUserId()); - if (StringUtils.isNotBlank(classId)) { - gradeCriteria.andSchoolClassIdEqualTo(classId); - } - List grades = stuGradeMapper == null ? Collections.emptyList() : stuGradeMapper.selectByExample(gradeExample); - int completedTaskCount = (int) (grades == null ? Collections.emptyList() : grades).stream() - .map(StuGrade::getModule) - .filter(enabledModules::contains) - .distinct() - .count(); + List answers = studentTrainingAnswerMapper == null ? Collections.emptyList() + : studentTrainingAnswerMapper.selectByStudentAndTeachingClass(rank.getUserId(), classId); + Map answersByTaskKey = indexAnswersByTaskKey(answers); + int completedTaskCount = (int) allocations.stream().map(TaskAllocation::getModule) + .filter(StringUtils::isNotBlank).distinct() + .filter(taskKey -> isCompleted(answersByTaskKey.get(taskKey))).count(); rank.setCompletedTaskCount(completedTaskCount); rank.setTrainingProgress(BigDecimal.valueOf(completedTaskCount) .multiply(BigDecimal.valueOf(100)) .divide(BigDecimal.valueOf(totalTaskCount), 2, BigDecimal.ROUND_HALF_UP)); + rank.setTeacherScore(calculateTeacherScore(classId, allocations, answersByTaskKey)); } } - private Set resolveEnabledTaskModules(String classId) { + private List resolveEnabledTaskAllocations(String classId) { if (StringUtils.isBlank(classId)) { - return Collections.emptySet(); + return Collections.emptyList(); } + if (taskAllocationMapper == null) { + return Collections.emptyList(); + } + List classAllocations = selectTaskAllocations(classId); + boolean comprehensiveParticipating = hasPublishedComprehensiveTask(classAllocations); List allocations; if (schoolDefaultTaskService != null) { allocations = schoolDefaultTaskService.resolveForTeachingClass(classId); } else { - if (taskAllocationMapper == null) { - return Collections.emptySet(); - } - allocations = selectTaskAllocations(classId); + allocations = classAllocations; if ((allocations == null || allocations.isEmpty()) && !"999999999".equals(classId)) { allocations = selectTaskAllocations("999999999"); } } return (allocations == null ? Collections.emptyList() : allocations).stream() .filter(allocation -> allocation.getDisabledStatus() == null || allocation.getDisabledStatus() != 1) - .map(TaskAllocation::getModule) - .filter(StringUtils::isNotBlank) - .collect(Collectors.toSet()); + .filter(allocation -> !TaskAllocation.PUBLICATION_MARKER.equals(allocation.getModule())) + .filter(allocation -> !"comprehensive-case-training".equals(allocation.getModule()) + || comprehensiveParticipating) + .collect(Collectors.toList()); } private List selectTaskAllocations(String classId) { @@ -429,6 +448,129 @@ public class TeaScoreController { return taskAllocationMapper.selectByExample(example); } + private boolean hasPublishedComprehensiveTask(List allocations) { + boolean published = false; + boolean enabled = false; + for (TaskAllocation allocation : allocations == null ? Collections.emptyList() : allocations) { + if (allocation == null) { + continue; + } + if (TaskAllocation.PUBLICATION_MARKER.equals(allocation.getModule())) { + published = true; + } else if ("comprehensive-case-training".equals(allocation.getModule()) + && (allocation.getDisabledStatus() == null || allocation.getDisabledStatus() == 0)) { + enabled = true; + } + } + return published && enabled; + } + + private Map indexAnswersByTaskKey(List answers) { + Map indexed = new HashMap<>(); + for (StudentTrainingAnswer answer : answers == null ? Collections.emptyList() : answers) { + if (answer != null && StringUtils.isNotBlank(answer.getTaskKey())) { + indexed.put(answer.getTaskKey(), answer); + } + } + return indexed; + } + + private boolean isCompleted(StudentTrainingAnswer answer) { + return answer != null && (Boolean.TRUE.equals(answer.getSubmitted()) + || "COMPLETED".equals(answer.getProgressStatus())); + } + + private BigDecimal calculateTeacherScore(String classId, List allocations, + Map answersByTaskKey) { + if (teacherScoreService == null || trainingTaskMapper == null) { + return null; + } + Map> scoresByProject = new HashMap<>(); + boolean hasTeacherScore = false; + boolean comprehensiveParticipating = false; + for (TaskAllocation allocation : allocations) { + if (allocation == null || StringUtils.isBlank(allocation.getModule())) { + continue; + } + if ("comprehensive-case-training".equals(allocation.getModule())) { + comprehensiveParticipating = true; + } + TrainingTask task = trainingTaskMapper.selectByTaskKey(allocation.getModule()); + if (task == null || StringUtils.isBlank(task.getProjectName())) { + continue; + } + StudentTrainingAnswer answer = answersByTaskKey.get(allocation.getModule()); + BigDecimal taskScore = answer == null ? null : answer.getTeacherScore(); + if (taskScore != null) { + hasTeacherScore = true; + } + scoresByProject.computeIfAbsent(task.getProjectName(), key -> new ArrayList<>()) + .add(taskScore == null ? BigDecimal.ZERO : taskScore); + } + if (!hasTeacherScore) { + return null; + } + TeachingClassScoreWeight weight = teacherScoreService.getOrCreateWeightForCalculation(classId); + if (weight == null) { + return null; + } + BigDecimal weightedTotal = projectAverage(scoresByProject, "互联网产品开发基础认知").multiply(safeWeight(weight.getFoundationWeight())) + .add(projectAverage(scoresByProject, "市场洞察与需求分析").multiply(safeWeight(weight.getMarketInsightWeight()))) + .add(projectAverage(scoresByProject, "产品规划与设计").multiply(safeWeight(weight.getPlanningDesignWeight()))) + .add(projectAverage(scoresByProject, "产品开发与测试验证").multiply(safeWeight(weight.getDevelopmentValidationWeight()))) + .add(projectAverage(scoresByProject, "产品上线与运营推广").multiply(safeWeight(weight.getLaunchOperationWeight()))) + .add(projectAverage(scoresByProject, "综合实训").multiply(safeWeight(weight.getComprehensiveTrainingWeight()))); + BigDecimal participatingWeight = safeWeight(weight.getFoundationWeight()).add(safeWeight(weight.getMarketInsightWeight())) + .add(safeWeight(weight.getPlanningDesignWeight())).add(safeWeight(weight.getDevelopmentValidationWeight())) + .add(safeWeight(weight.getLaunchOperationWeight())); + if (comprehensiveParticipating) { + participatingWeight = participatingWeight.add(safeWeight(weight.getComprehensiveTrainingWeight())); + } + return weightedTotal.divide(participatingWeight.signum() == 0 ? BigDecimal.ONE : participatingWeight, + 2, BigDecimal.ROUND_HALF_UP); + } + + private BigDecimal projectAverage(Map> scoresByProject, String projectName) { + List scores = scoresByProject.get(projectName); + if (scores == null || scores.isEmpty()) { + return BigDecimal.ZERO; + } + BigDecimal total = scores.stream().reduce(BigDecimal.ZERO, BigDecimal::add); + return total.divide(BigDecimal.valueOf(scores.size()), 4, BigDecimal.ROUND_HALF_UP); + } + + private BigDecimal safeWeight(BigDecimal weight) { + return weight == null ? BigDecimal.ZERO : weight; + } + + private List buildTaskReviews(String studentUserId, String classId) { + Map answersByTaskKey = indexAnswersByTaskKey( + studentTrainingAnswerMapper.selectByStudentAndTeachingClass(studentUserId, classId)); + List reviews = new ArrayList<>(); + Set seenTaskKeys = new HashSet<>(); + for (TaskAllocation allocation : resolveEnabledTaskAllocations(classId)) { + if (allocation == null || StringUtils.isBlank(allocation.getModule()) || !seenTaskKeys.add(allocation.getModule())) { + continue; + } + TrainingTask task = trainingTaskMapper.selectByTaskKey(allocation.getModule()); + if (task == null) { + continue; + } + StudentTrainingAnswer answer = answersByTaskKey.get(allocation.getModule()); + TeacherTaskReviewDTO review = new TeacherTaskReviewDTO(); + review.setTaskId(task.getId()); + review.setTaskKey(task.getTaskKey()); + review.setTaskName(task.getTaskName()); + review.setProjectName(task.getProjectName()); + review.setProgressStatus(answer == null ? "NOT_STARTED" : answer.getProgressStatus()); + review.setSubmitted(answer != null && Boolean.TRUE.equals(answer.getSubmitted())); + review.setAiAssessmentScore(answer == null ? null : answer.getAiAssessmentScore()); + review.setTeacherScore(answer == null ? null : answer.getTeacherScore()); + reviews.add(review); + } + return reviews; + } + // Retained for existing direct controller tests; the HTTP route is the JWT-scoped overload above. public void exportStuScore(HttpServletResponse response, String schoolId, String classId, String userName, String name) { StuRankExample example = new StuRankExample(); diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java b/src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java index 82e0bdf..e407f77 100644 --- a/src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java +++ b/src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java @@ -3,6 +3,7 @@ package com.sztzjy.linkCommerce.entity; import io.swagger.annotations.ApiModelProperty; import java.util.Date; +import java.math.BigDecimal; public class StudentTrainingAnswer { @ApiModelProperty("主键ID") @@ -49,6 +50,15 @@ public class StudentTrainingAnswer { private Integer aiAssessmentScore; + @ApiModelProperty("教师任务评分") + private BigDecimal teacherScore; + + @ApiModelProperty("教师评分人") + private String teacherScoredBy; + + @ApiModelProperty("教师评分时间") + private Date teacherScoredAt; + @ApiModelProperty("保存动作:SAVE/SUBMIT/RESET") private String saveAction; @@ -181,6 +191,13 @@ public class StudentTrainingAnswer { this.aiAssessmentScore = aiAssessmentScore; } + public BigDecimal getTeacherScore() { return teacherScore; } + public void setTeacherScore(BigDecimal teacherScore) { this.teacherScore = teacherScore; } + public String getTeacherScoredBy() { return teacherScoredBy; } + public void setTeacherScoredBy(String teacherScoredBy) { this.teacherScoredBy = teacherScoredBy == null ? null : teacherScoredBy.trim(); } + public Date getTeacherScoredAt() { return teacherScoredAt; } + public void setTeacherScoredAt(Date teacherScoredAt) { this.teacherScoredAt = teacherScoredAt; } + public String getSaveAction() { return saveAction; } diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TeacherTaskReviewDTO.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TeacherTaskReviewDTO.java new file mode 100644 index 0000000..e121b97 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TeacherTaskReviewDTO.java @@ -0,0 +1,31 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.math.BigDecimal; + +public class TeacherTaskReviewDTO { + private String taskId; + private String taskKey; + private String taskName; + private String projectName; + private String progressStatus; + private Boolean submitted; + private Integer aiAssessmentScore; + private BigDecimal teacherScore; + + public String getTaskId() { return taskId; } + public void setTaskId(String taskId) { this.taskId = taskId; } + public String getTaskKey() { return taskKey; } + public void setTaskKey(String taskKey) { this.taskKey = taskKey; } + public String getTaskName() { return taskName; } + public void setTaskName(String taskName) { this.taskName = taskName; } + public String getProjectName() { return projectName; } + public void setProjectName(String projectName) { this.projectName = projectName; } + public String getProgressStatus() { return progressStatus; } + public void setProgressStatus(String progressStatus) { this.progressStatus = progressStatus; } + public Boolean getSubmitted() { return submitted; } + public void setSubmitted(Boolean submitted) { this.submitted = submitted; } + public Integer getAiAssessmentScore() { return aiAssessmentScore; } + public void setAiAssessmentScore(Integer aiAssessmentScore) { this.aiAssessmentScore = aiAssessmentScore; } + public BigDecimal getTeacherScore() { return teacherScore; } + public void setTeacherScore(BigDecimal teacherScore) { this.teacherScore = teacherScore; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/mapper/StudentTrainingAnswerMapper.java b/src/main/java/com/sztzjy/linkCommerce/mapper/StudentTrainingAnswerMapper.java index 51d1b42..138e62b 100644 --- a/src/main/java/com/sztzjy/linkCommerce/mapper/StudentTrainingAnswerMapper.java +++ b/src/main/java/com/sztzjy/linkCommerce/mapper/StudentTrainingAnswerMapper.java @@ -27,6 +27,11 @@ public interface StudentTrainingAnswerMapper { @Param("score") Integer score, @Param("updateTime") Date updateTime); + int updateTeacherScore(@Param("id") String id, + @Param("score") java.math.BigDecimal score, + @Param("teacherUserId") String teacherUserId, + @Param("scoredAt") Date scoredAt); + List selectByStudentAndTeachingClass(@Param("studentUserId") String studentUserId, @Param("teachingClassId") String teachingClassId); } diff --git a/src/main/java/com/sztzjy/linkCommerce/service/TeacherScoreService.java b/src/main/java/com/sztzjy/linkCommerce/service/TeacherScoreService.java index b16ae7d..221fea5 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/TeacherScoreService.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/TeacherScoreService.java @@ -15,6 +15,7 @@ public interface TeacherScoreService { SchoolClass requireOwnedClass(JwtUser teacher, String teachingClassId); SchoolClass requireAccessibleClass(JwtUser teacher, String teachingClassId); TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId); + TeachingClassScoreWeight getOrCreateWeightForCalculation(String teachingClassId); TeachingClassScoreWeight saveWeight(JwtUser teacher, String teachingClassId, TeachingClassScoreWeight weight); BigDecimal getTeacherScore(String teachingClassId, String studentUserId); Map getTeacherScores(String teachingClassId, List studentUserIds); diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImpl.java index b2c5280..1310ff7 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImpl.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImpl.java @@ -97,6 +97,11 @@ public class TeacherScoreServiceImpl implements TeacherScoreService { @Override public TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId) { requireOwnedClass(teacher, teachingClassId); + return getOrCreateWeightForCalculation(teachingClassId); + } + + @Override + public TeachingClassScoreWeight getOrCreateWeightForCalculation(String teachingClassId) { TeachingClassScoreWeight existing = weightMapper == null ? null : weightMapper.selectByTeachingClassId(teachingClassId); if (existing != null) { return existing; diff --git a/src/main/resources/mappers/StudentTrainingAnswerMapper.xml b/src/main/resources/mappers/StudentTrainingAnswerMapper.xml index a359573..87c3d75 100644 --- a/src/main/resources/mappers/StudentTrainingAnswerMapper.xml +++ b/src/main/resources/mappers/StudentTrainingAnswerMapper.xml @@ -17,6 +17,9 @@ + + + @@ -24,7 +27,7 @@ id, student_user_id, teaching_class_id, task_id, task_key, task_name, step1_answer, step2_answer, - step3_answer, step4_answer, dynamic_step_answers, current_step, progress_status, submitted, ai_assessment_score, submit_time, create_time, update_time + step3_answer, step4_answer, dynamic_step_answers, current_step, progress_status, submitted, ai_assessment_score, teacher_score, teacher_scored_by, teacher_scored_at, submit_time, create_time, update_time