diff --git a/src/main/java/com/sztzjy/linkCommerce/config/TeachingClassTeacherScoreSchemaInitializer.java b/src/main/java/com/sztzjy/linkCommerce/config/TeachingClassTeacherScoreSchemaInitializer.java new file mode 100644 index 0000000..2bbcc68 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/config/TeachingClassTeacherScoreSchemaInitializer.java @@ -0,0 +1,28 @@ +package com.sztzjy.linkCommerce.config; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +/** Keeps existing deployments compatible with teacher-entered scores. */ +@Component +public class TeachingClassTeacherScoreSchemaInitializer { + private final JdbcTemplate jdbcTemplate; + + public TeachingClassTeacherScoreSchemaInitializer(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @PostConstruct + public void initialize() { + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS teaching_class_teacher_score (" + + "teaching_class_id varchar(64) NOT NULL," + + "student_user_id varchar(64) NOT NULL," + + "teacher_score decimal(5,2) NOT NULL," + + "scored_by varchar(64) NOT NULL," + + "update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," + + "PRIMARY KEY (teaching_class_id, student_user_id)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"); + } +} 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 be86b15..285a6d6 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/TeaScoreController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/TeaScoreController.java @@ -97,6 +97,20 @@ public class TeaScoreController { } } + @PostMapping("/saveTeacherScore") + @ApiOperation("保存老师对学生的人工评分") + public ResultEntity saveTeacherScore(@RequestParam String classId, + @RequestParam String studentUserId, + @RequestParam BigDecimal teacherScore, + HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "老师评分保存成功", + teacherScoreService.saveTeacherScore(currentUser(request), classId, studentUserId, teacherScore)); + } catch (com.sztzjy.linkCommerce.config.exception.handler.ServiceException e) { + return new ResultEntity<>(e.getCode(), e.getMessage()); + } + } + @PostMapping("/selectStuRankAndScore") @ApiOperation("查询学生成绩和排名") @AnonymousAccess @@ -334,6 +348,7 @@ public class TeaScoreController { } for (StuRank rank : ranks) { String classId = StringUtils.isNotBlank(requestedClassId) ? requestedClassId : rank.getSchoolClassId(); + rank.setTeacherScore(teacherScoreService.getTeacherScore(classId, rank.getUserId())); Set enabledModules = resolveEnabledTaskModules(classId); int totalTaskCount = enabledModules.size(); rank.setTotalTaskCount(totalTaskCount); diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/StuRank.java b/src/main/java/com/sztzjy/linkCommerce/entity/StuRank.java index 7caf246..7dfe7fe 100644 --- a/src/main/java/com/sztzjy/linkCommerce/entity/StuRank.java +++ b/src/main/java/com/sztzjy/linkCommerce/entity/StuRank.java @@ -49,6 +49,9 @@ public class StuRank { @ApiModelProperty(notes = "实训进度百分比(查询时计算,不落库)") private BigDecimal trainingProgress; + @ApiModelProperty(notes = "老师人工评分(查询时从教学班评分表读取)") + private BigDecimal teacherScore; + @ApiModelProperty(notes = "市场需求挖掘分数") private BigDecimal scqxwjScore; @@ -189,6 +192,14 @@ public class StuRank { this.trainingProgress = trainingProgress; } + public BigDecimal getTeacherScore() { + return teacherScore; + } + + public void setTeacherScore(BigDecimal teacherScore) { + this.teacherScore = teacherScore; + } + public BigDecimal getScqxwjScore() { return scqxwjScore; } diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/TeachingClassTeacherScore.java b/src/main/java/com/sztzjy/linkCommerce/entity/TeachingClassTeacherScore.java new file mode 100644 index 0000000..d275530 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/TeachingClassTeacherScore.java @@ -0,0 +1,23 @@ +package com.sztzjy.linkCommerce.entity; + +import java.math.BigDecimal; +import java.util.Date; + +public class TeachingClassTeacherScore { + private String teachingClassId; + private String studentUserId; + private BigDecimal teacherScore; + private String scoredBy; + private Date updateTime; + + public String getTeachingClassId() { return teachingClassId; } + public void setTeachingClassId(String teachingClassId) { this.teachingClassId = teachingClassId; } + public String getStudentUserId() { return studentUserId; } + public void setStudentUserId(String studentUserId) { this.studentUserId = studentUserId; } + public BigDecimal getTeacherScore() { return teacherScore; } + public void setTeacherScore(BigDecimal teacherScore) { this.teacherScore = teacherScore; } + public String getScoredBy() { return scoredBy; } + public void setScoredBy(String scoredBy) { this.scoredBy = scoredBy; } + public Date getUpdateTime() { return updateTime; } + public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/mapper/TeachingClassTeacherScoreMapper.java b/src/main/java/com/sztzjy/linkCommerce/mapper/TeachingClassTeacherScoreMapper.java new file mode 100644 index 0000000..1d26b85 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/mapper/TeachingClassTeacherScoreMapper.java @@ -0,0 +1,12 @@ +package com.sztzjy.linkCommerce.mapper; + +import com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface TeachingClassTeacherScoreMapper { + TeachingClassTeacherScore selectByClassAndStudent(@Param("teachingClassId") String teachingClassId, + @Param("studentUserId") String studentUserId); + int upsert(TeachingClassTeacherScore record); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/TeacherScoreService.java b/src/main/java/com/sztzjy/linkCommerce/service/TeacherScoreService.java index a71a104..e3b5acd 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/TeacherScoreService.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/TeacherScoreService.java @@ -3,7 +3,9 @@ package com.sztzjy.linkCommerce.service; import com.sztzjy.linkCommerce.config.security.JwtUser; import com.sztzjy.linkCommerce.entity.SchoolClass; import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight; +import com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore; +import java.math.BigDecimal; import java.util.List; public interface TeacherScoreService { @@ -11,4 +13,7 @@ public interface TeacherScoreService { SchoolClass requireOwnedClass(JwtUser teacher, String teachingClassId); TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId); TeachingClassScoreWeight saveWeight(JwtUser teacher, String teachingClassId, TeachingClassScoreWeight weight); + BigDecimal getTeacherScore(String teachingClassId, String studentUserId); + TeachingClassTeacherScore saveTeacherScore(JwtUser teacher, String teachingClassId, + String studentUserId, BigDecimal score); } 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 93ecb03..c94d3ae 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImpl.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImpl.java @@ -5,8 +5,11 @@ import com.sztzjy.linkCommerce.config.security.JwtUser; import com.sztzjy.linkCommerce.entity.SchoolClass; import com.sztzjy.linkCommerce.entity.SchoolClassExample; import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight; +import com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore; import com.sztzjy.linkCommerce.mapper.SchoolClassMapper; import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper; +import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper; +import com.sztzjy.linkCommerce.mapper.TeachingClassTeacherScoreMapper; import com.sztzjy.linkCommerce.service.TeacherScoreService; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -14,6 +17,7 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Collections; import java.util.List; @@ -23,6 +27,10 @@ public class TeacherScoreServiceImpl implements TeacherScoreService { SchoolClassMapper schoolClassMapper; @Autowired(required = false) TeachingClassScoreWeightMapper weightMapper; + @Autowired + TeachingClassTeacherScoreMapper teacherScoreMapper; + @Autowired + TeachingClassStudentMapper teachingClassStudentMapper; @Override public List listOwnedClasses(JwtUser teacher) { @@ -81,6 +89,35 @@ public class TeacherScoreServiceImpl implements TeacherScoreService { return weight; } + @Override + public BigDecimal getTeacherScore(String teachingClassId, String studentUserId) { + if (StringUtils.isBlank(teachingClassId) || StringUtils.isBlank(studentUserId)) { + return null; + } + TeachingClassTeacherScore stored = teacherScoreMapper.selectByClassAndStudent(teachingClassId, studentUserId); + return stored == null ? null : stored.getTeacherScore(); + } + + @Override + public TeachingClassTeacherScore saveTeacherScore(JwtUser teacher, String teachingClassId, + String studentUserId, BigDecimal score) { + requireOwnedClass(teacher, teachingClassId); + if (StringUtils.isBlank(studentUserId) + || teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId(studentUserId, teachingClassId) == 0) { + throw new ServiceException(HttpStatus.BAD_REQUEST, "该学生不在当前教学班"); + } + if (score == null || score.compareTo(BigDecimal.ZERO) < 0 || score.compareTo(new BigDecimal("100")) > 0) { + throw new ServiceException(HttpStatus.BAD_REQUEST, "老师评分必须为0至100之间的数值"); + } + TeachingClassTeacherScore record = new TeachingClassTeacherScore(); + record.setTeachingClassId(teachingClassId); + record.setStudentUserId(studentUserId); + record.setTeacherScore(score.setScale(2, RoundingMode.HALF_UP)); + record.setScoredBy(teacher.getUserId()); + teacherScoreMapper.upsert(record); + return record; + } + private TeachingClassScoreWeight defaultWeight(String teachingClassId) { TeachingClassScoreWeight weight = new TeachingClassScoreWeight(); weight.setTeachingClassId(teachingClassId); diff --git a/src/main/resources/mappers/TeachingClassTeacherScoreMapper.xml b/src/main/resources/mappers/TeachingClassTeacherScoreMapper.xml new file mode 100644 index 0000000..041d1fc --- /dev/null +++ b/src/main/resources/mappers/TeachingClassTeacherScoreMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + insert into teaching_class_teacher_score + (teaching_class_id, student_user_id, teacher_score, scored_by, update_time) + values + (#{teachingClassId}, #{studentUserId}, #{teacherScore}, #{scoredBy}, current_timestamp) + on duplicate key update + teacher_score = values(teacher_score), + scored_by = values(scored_by), + update_time = current_timestamp + + diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImplTest.java index 74f9a42..a500be4 100644 --- a/src/test/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImplTest.java +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/TeacherScoreServiceImplTest.java @@ -4,15 +4,20 @@ import com.sztzjy.linkCommerce.config.exception.handler.ServiceException; import com.sztzjy.linkCommerce.config.security.JwtUser; import com.sztzjy.linkCommerce.entity.SchoolClass; import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight; +import com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore; import com.sztzjy.linkCommerce.mapper.SchoolClassMapper; import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper; +import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper; +import com.sztzjy.linkCommerce.mapper.TeachingClassTeacherScoreMapper; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import org.springframework.test.util.ReflectionTestUtils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class TeacherScoreServiceImplTest { @@ -49,6 +54,42 @@ class TeacherScoreServiceImplTest { assertEquals(HttpStatus.FORBIDDEN, error.getCode()); } + @Test + void savesTeacherScoreForStudentInOwnedTeachingClass() { + TeacherScoreServiceImpl service = new TeacherScoreServiceImpl(); + SchoolClassMapper classMapper = mock(SchoolClassMapper.class); + TeachingClassStudentMapper memberMapper = mock(TeachingClassStudentMapper.class); + TeachingClassTeacherScoreMapper scoreMapper = mock(TeachingClassTeacherScoreMapper.class); + ReflectionTestUtils.setField(service, "schoolClassMapper", classMapper); + ReflectionTestUtils.setField(service, "teachingClassStudentMapper", memberMapper); + ReflectionTestUtils.setField(service, "teacherScoreMapper", scoreMapper); + when(classMapper.selectByPrimaryKey("class-1")).thenReturn(classRow("class-1", "teacher-1", "school-1")); + when(memberMapper.countByStudentUserIdAndTeachingClassId("student-1", "class-1")).thenReturn(1L); + + TeachingClassTeacherScore saved = service.saveTeacherScore( + teacher("teacher-1", "school-1"), "class-1", "student-1", new java.math.BigDecimal("88.126")); + + assertEquals(new java.math.BigDecimal("88.13"), saved.getTeacherScore()); + assertEquals("teacher-1", saved.getScoredBy()); + verify(scoreMapper).upsert(saved); + } + + @Test + void rejectsTeacherScoreOutsideZeroToOneHundred() { + TeacherScoreServiceImpl service = new TeacherScoreServiceImpl(); + SchoolClassMapper classMapper = mock(SchoolClassMapper.class); + TeachingClassStudentMapper memberMapper = mock(TeachingClassStudentMapper.class); + ReflectionTestUtils.setField(service, "schoolClassMapper", classMapper); + ReflectionTestUtils.setField(service, "teachingClassStudentMapper", memberMapper); + when(classMapper.selectByPrimaryKey("class-1")).thenReturn(classRow("class-1", "teacher-1", "school-1")); + when(memberMapper.countByStudentUserIdAndTeachingClassId("student-1", "class-1")).thenReturn(1L); + + ServiceException error = assertThrows(ServiceException.class, () -> service.saveTeacherScore( + teacher("teacher-1", "school-1"), "class-1", "student-1", new java.math.BigDecimal("100.01"))); + + assertTrue(error.getMessage().contains("0至100")); + } + private JwtUser teacher(String userId, String schoolId) { JwtUser user = new JwtUser(); user.setUserId(userId);