feat: add class-scoped teacher scores

bugfix/ai-assessment-evidence-rescore
chenyuan 1 month ago
parent 615eb1cc93
commit a5866cf8b0

@ -0,0 +1,27 @@
package com.sztzjy.linkCommerce.config;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@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");
}
}

@ -33,6 +33,7 @@ import java.math.BigDecimal;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -72,6 +73,16 @@ public class TeaScoreController {
} }
} }
@GetMapping("/accessibleTeachingClasses")
@ApiOperation("成绩中心可查看的教学班列表")
public ResultEntity<List<SchoolClass>> accessibleTeachingClasses(HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "查询成功", teacherScoreService.listAccessibleClasses(currentUser(request)));
} catch (com.sztzjy.linkCommerce.config.exception.handler.ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/selectTeachingClassWeight") @PostMapping("/selectTeachingClassWeight")
@ApiOperation("查询本人教学班六模块权重") @ApiOperation("查询本人教学班六模块权重")
public ResultEntity<TeachingClassScoreWeight> selectTeachingClassWeight(@RequestParam String teachingClassId, public ResultEntity<TeachingClassScoreWeight> selectTeachingClassWeight(@RequestParam String teachingClassId,
@ -107,13 +118,12 @@ public class TeaScoreController {
@RequestParam Integer index, @RequestParam Integer size, @RequestParam Integer index, @RequestParam Integer size,
HttpServletRequest request) { HttpServletRequest request) {
JwtUser teacher = currentUser(request); JwtUser teacher = currentUser(request);
PageHelper.startPage(index, size);
StuRankExample stuRankExample = new StuRankExample(); StuRankExample stuRankExample = new StuRankExample();
StuRankExample.Criteria criteria = stuRankExample.createCriteria(); StuRankExample.Criteria criteria = stuRankExample.createCriteria();
criteria.andSchoolIdEqualTo(teacher.getSchoolId()); criteria.andSchoolIdEqualTo(teacher.getSchoolId());
String nowDate = scoreRankService.getNowDate(); String nowDate = scoreRankService.getNowDate();
criteria.andUpdateTimeEqualTo(nowDate); criteria.andUpdateTimeEqualTo(nowDate);
applyOwnedTeacherClassCriteria(criteria, teacher, classId); applyAccessibleTeacherClassCriteria(criteria, teacher, classId);
if(StringUtil.isNotEmpty(userName)){ if(StringUtil.isNotEmpty(userName)){
criteria.andUsernameEqualTo(userName); criteria.andUsernameEqualTo(userName);
} }
@ -121,6 +131,7 @@ public class TeaScoreController {
criteria.andNameEqualTo(name); criteria.andNameEqualTo(name);
} }
stuRankExample.setOrderByClause("stu_rank asc"); stuRankExample.setOrderByClause("stu_rank asc");
PageHelper.startPage(index, size);
List<StuRank> stuRankList = stuRankMapper.selectByExample(stuRankExample); List<StuRank> stuRankList = stuRankMapper.selectByExample(stuRankExample);
enrichTrainingProgress(stuRankList, classId); enrichTrainingProgress(stuRankList, classId);
PageInfo<StuRank> rankPageInfo = new PageInfo<>(stuRankList); PageInfo<StuRank> rankPageInfo = new PageInfo<>(stuRankList);
@ -130,7 +141,6 @@ public class TeaScoreController {
// Retained for existing direct controller tests; HTTP requests always use the JWT-scoped overload above. // Retained for existing direct controller tests; HTTP requests always use the JWT-scoped overload above.
public ResultEntity<PageInfo<StuRank>> selectStuRankAndScore(String classId, String userName, String name, public ResultEntity<PageInfo<StuRank>> selectStuRankAndScore(String classId, String userName, String name,
String schoolId, Integer index, Integer size) { String schoolId, Integer index, Integer size) {
PageHelper.startPage(index, size);
StuRankExample example = new StuRankExample(); StuRankExample example = new StuRankExample();
StuRankExample.Criteria criteria = example.createCriteria(); StuRankExample.Criteria criteria = example.createCriteria();
criteria.andSchoolIdEqualTo(schoolId); criteria.andSchoolIdEqualTo(schoolId);
@ -143,6 +153,7 @@ public class TeaScoreController {
criteria.andNameEqualTo(name); criteria.andNameEqualTo(name);
} }
example.setOrderByClause("stu_rank asc"); example.setOrderByClause("stu_rank asc");
PageHelper.startPage(index, size);
List<StuRank> rows = stuRankMapper.selectByExample(example); List<StuRank> rows = stuRankMapper.selectByExample(example);
enrichTrainingProgress(rows, classId); enrichTrainingProgress(rows, classId);
return new ResultEntity<>(HttpStatus.OK, "查询学生成绩和排名成功", new PageInfo<>(rows)); return new ResultEntity<>(HttpStatus.OK, "查询学生成绩和排名成功", new PageInfo<>(rows));
@ -156,7 +167,7 @@ public class TeaScoreController {
@RequestParam(required = false) String classId, @RequestParam(required = false) String classId,
HttpServletRequest request) { HttpServletRequest request) {
JwtUser teacher = currentUser(request); JwtUser teacher = currentUser(request);
SchoolClass teachingClass = teacherScoreService.requireOwnedClass(teacher, classId); SchoolClass teachingClass = teacherScoreService.requireAccessibleClass(teacher, classId);
if (teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId(userId, teachingClass.getSchoolClassId()) == 0) { if (teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId(userId, teachingClass.getSchoolClassId()) == 0) {
return new ResultEntity<>(HttpStatus.FORBIDDEN, "无权查看该学生成绩"); return new ResultEntity<>(HttpStatus.FORBIDDEN, "无权查看该学生成绩");
} }
@ -225,6 +236,20 @@ public class TeaScoreController {
return new ResultEntity<>(HttpStatus.OK, "查看学生成绩详情成功", stuGradeList); return new ResultEntity<>(HttpStatus.OK, "查看学生成绩详情成功", stuGradeList);
} }
@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("/selectWeight") @PostMapping("/selectWeight")
@ApiOperation("查询权重") @ApiOperation("查询权重")
@ -285,7 +310,7 @@ public class TeaScoreController {
criteria.andSchoolIdEqualTo(teacher.getSchoolId()); criteria.andSchoolIdEqualTo(teacher.getSchoolId());
String nowDate = scoreRankService.getNowDate(); String nowDate = scoreRankService.getNowDate();
criteria.andUpdateTimeEqualTo(nowDate); criteria.andUpdateTimeEqualTo(nowDate);
applyOwnedTeacherClassCriteria(criteria, teacher, classId); applyAccessibleTeacherClassCriteria(criteria, teacher, classId);
if(StringUtil.isNotEmpty(userName)){ if(StringUtil.isNotEmpty(userName)){
criteria.andUsernameEqualTo(userName); criteria.andUsernameEqualTo(userName);
} }
@ -298,10 +323,10 @@ public class TeaScoreController {
//导出的表名 //导出的表名
String title = "学生成绩"; String title = "学生成绩";
//表中第一行表头字段 //表中第一行表头字段
String[] headers = {"学号", "学生姓名", "班级名称", "排名", "参考成绩", "实训进度"}; String[] headers = {"学号", "学生姓名", "班级名称", "排名", "参考成绩", "老师评分", "实训进度"};
//具体需要写入excel需要哪些字段,这些字段取自UserReward类,也就是上面的实际数据结果集的泛型 //具体需要写入excel需要哪些字段,这些字段取自UserReward类,也就是上面的实际数据结果集的泛型
List<String> listColumn = Arrays.asList("username", "name", "className", "stuRank", "score", "trainingProgress"); List<String> listColumn = Arrays.asList("username", "name", "className", "stuRank", "score", "teacherScore", "trainingProgress");
try { try {
FilePortUtil.exportExcel(response, title, headers, stuRankList, listColumn); FilePortUtil.exportExcel(response, title, headers, stuRankList, listColumn);
} catch (Exception e) { } catch (Exception e) {
@ -332,8 +357,22 @@ public class TeaScoreController {
if (ranks == null || ranks.isEmpty()) { if (ranks == null || ranks.isEmpty()) {
return; return;
} }
Map<String, BigDecimal> classTeacherScores = Collections.emptyMap();
if (teacherScoreService != null && StringUtils.isNotBlank(requestedClassId)) {
Map<String, BigDecimal> 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) { for (StuRank rank : ranks) {
String classId = StringUtils.isNotBlank(requestedClassId) ? requestedClassId : rank.getSchoolClassId(); 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<String> enabledModules = resolveEnabledTaskModules(classId); Set<String> enabledModules = resolveEnabledTaskModules(classId);
int totalTaskCount = enabledModules.size(); int totalTaskCount = enabledModules.size();
rank.setTotalTaskCount(totalTaskCount); rank.setTotalTaskCount(totalTaskCount);
@ -414,14 +453,14 @@ public class TeaScoreController {
} }
} }
private void applyOwnedTeacherClassCriteria(StuRankExample.Criteria criteria, JwtUser teacher, String classId) { private void applyAccessibleTeacherClassCriteria(StuRankExample.Criteria criteria, JwtUser teacher, String classId) {
List<SchoolClass> ownedClasses; List<SchoolClass> accessibleClasses;
if (StringUtils.isNotBlank(classId)) { if (StringUtils.isNotBlank(classId)) {
ownedClasses = Collections.singletonList(teacherScoreService.requireOwnedClass(teacher, classId)); accessibleClasses = Collections.singletonList(teacherScoreService.requireAccessibleClass(teacher, classId));
} else { } else {
ownedClasses = teacherScoreService.listOwnedClasses(teacher); accessibleClasses = teacherScoreService.listAccessibleClasses(teacher);
} }
List<String> studentIds = ownedClasses.stream() List<String> studentIds = accessibleClasses.stream()
.flatMap(schoolClass -> teachingClassStudentMapper.selectByTeachingClassId(schoolClass.getSchoolClassId()).stream()) .flatMap(schoolClass -> teachingClassStudentMapper.selectByTeachingClassId(schoolClass.getSchoolClassId()).stream())
.map(TeachingClassStudent::getStudentUserId) .map(TeachingClassStudent::getStudentUserId)
.filter(StringUtils::isNotBlank) .filter(StringUtils::isNotBlank)

@ -49,6 +49,9 @@ public class StuRank {
@ApiModelProperty(notes = "实训进度百分比(查询时计算,不落库)") @ApiModelProperty(notes = "实训进度百分比(查询时计算,不落库)")
private BigDecimal trainingProgress; private BigDecimal trainingProgress;
@ApiModelProperty(notes = "老师人工评分(查询时从教学班评分表读取)")
private BigDecimal teacherScore;
@ApiModelProperty(notes = "市场需求挖掘分数") @ApiModelProperty(notes = "市场需求挖掘分数")
private BigDecimal scqxwjScore; private BigDecimal scqxwjScore;
@ -189,6 +192,14 @@ public class StuRank {
this.trainingProgress = trainingProgress; this.trainingProgress = trainingProgress;
} }
public BigDecimal getTeacherScore() {
return teacherScore;
}
public void setTeacherScore(BigDecimal teacherScore) {
this.teacherScore = teacherScore;
}
public BigDecimal getScqxwjScore() { public BigDecimal getScqxwjScore() {
return scqxwjScore; return scqxwjScore;
} }

@ -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; }
}

@ -0,0 +1,16 @@
package com.sztzjy.linkCommerce.mapper;
import com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface TeachingClassTeacherScoreMapper {
TeachingClassTeacherScore selectByClassAndStudent(@Param("teachingClassId") String teachingClassId,
@Param("studentUserId") String studentUserId);
List<TeachingClassTeacherScore> selectByClassAndStudents(@Param("teachingClassId") String teachingClassId,
@Param("studentUserIds") List<String> studentUserIds);
int upsert(TeachingClassTeacherScore record);
}

@ -3,12 +3,21 @@ package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser; import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.SchoolClass; import com.sztzjy.linkCommerce.entity.SchoolClass;
import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight; import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight;
import com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore;
import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Map;
public interface TeacherScoreService { public interface TeacherScoreService {
List<SchoolClass> listOwnedClasses(JwtUser teacher); List<SchoolClass> listOwnedClasses(JwtUser teacher);
List<SchoolClass> listAccessibleClasses(JwtUser teacher);
SchoolClass requireOwnedClass(JwtUser teacher, String teachingClassId); SchoolClass requireOwnedClass(JwtUser teacher, String teachingClassId);
SchoolClass requireAccessibleClass(JwtUser teacher, String teachingClassId);
TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId); TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId);
TeachingClassScoreWeight saveWeight(JwtUser teacher, String teachingClassId, TeachingClassScoreWeight weight); TeachingClassScoreWeight saveWeight(JwtUser teacher, String teachingClassId, TeachingClassScoreWeight weight);
BigDecimal getTeacherScore(String teachingClassId, String studentUserId);
Map<String, BigDecimal> getTeacherScores(String teachingClassId, List<String> studentUserIds);
TeachingClassTeacherScore saveTeacherScore(JwtUser teacher, String teachingClassId,
String studentUserId, BigDecimal score);
} }

@ -5,8 +5,13 @@ import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.SchoolClass; import com.sztzjy.linkCommerce.entity.SchoolClass;
import com.sztzjy.linkCommerce.entity.SchoolClassExample; import com.sztzjy.linkCommerce.entity.SchoolClassExample;
import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight; import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight;
import com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore;
import com.sztzjy.linkCommerce.entity.Userinfo;
import com.sztzjy.linkCommerce.mapper.SchoolClassMapper; import com.sztzjy.linkCommerce.mapper.SchoolClassMapper;
import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper; import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper;
import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper;
import com.sztzjy.linkCommerce.mapper.TeachingClassTeacherScoreMapper;
import com.sztzjy.linkCommerce.mapper.UserinfoMapper;
import com.sztzjy.linkCommerce.service.TeacherScoreService; import com.sztzjy.linkCommerce.service.TeacherScoreService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -15,7 +20,9 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
@Service @Service
public class TeacherScoreServiceImpl implements TeacherScoreService { public class TeacherScoreServiceImpl implements TeacherScoreService {
@ -23,6 +30,12 @@ public class TeacherScoreServiceImpl implements TeacherScoreService {
SchoolClassMapper schoolClassMapper; SchoolClassMapper schoolClassMapper;
@Autowired(required = false) @Autowired(required = false)
TeachingClassScoreWeightMapper weightMapper; TeachingClassScoreWeightMapper weightMapper;
@Autowired(required = false)
TeachingClassTeacherScoreMapper teacherScoreMapper;
@Autowired(required = false)
TeachingClassStudentMapper teachingClassStudentMapper;
@Autowired(required = false)
UserinfoMapper userinfoMapper;
@Override @Override
public List<SchoolClass> listOwnedClasses(JwtUser teacher) { public List<SchoolClass> listOwnedClasses(JwtUser teacher) {
@ -30,6 +43,20 @@ public class TeacherScoreServiceImpl implements TeacherScoreService {
SchoolClassExample example = new SchoolClassExample(); SchoolClassExample example = new SchoolClassExample();
example.createCriteria().andSchoolIdEqualTo(teacher.getSchoolId()) example.createCriteria().andSchoolIdEqualTo(teacher.getSchoolId())
.andCreatedByEqualTo(teacher.getUserId()).andClassTypeEqualTo("TEACHING"); .andCreatedByEqualTo(teacher.getUserId()).andClassTypeEqualTo("TEACHING");
example.setOrderByClause("create_time desc");
List<SchoolClass> rows = schoolClassMapper.selectByExample(example);
return rows == null ? Collections.emptyList() : rows;
}
@Override
public List<SchoolClass> listAccessibleClasses(JwtUser teacher) {
requireTeacher(teacher);
if (!isTeacherAdmin(teacher)) {
return listOwnedClasses(teacher);
}
SchoolClassExample example = new SchoolClassExample();
example.createCriteria().andSchoolIdEqualTo(teacher.getSchoolId()).andClassTypeEqualTo("TEACHING");
example.setOrderByClause("create_time desc");
List<SchoolClass> rows = schoolClassMapper.selectByExample(example); List<SchoolClass> rows = schoolClassMapper.selectByExample(example);
return rows == null ? Collections.emptyList() : rows; return rows == null ? Collections.emptyList() : rows;
} }
@ -49,6 +76,23 @@ public class TeacherScoreServiceImpl implements TeacherScoreService {
return schoolClass; return schoolClass;
} }
@Override
public SchoolClass requireAccessibleClass(JwtUser teacher, String teachingClassId) {
requireTeacher(teacher);
if (StringUtils.isBlank(teachingClassId)) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请选择教学班");
}
SchoolClass schoolClass = schoolClassMapper.selectByPrimaryKey(teachingClassId);
boolean sameSchoolTeachingClass = schoolClass != null
&& "TEACHING".equals(schoolClass.getClassType())
&& StringUtils.equals(teacher.getSchoolId(), schoolClass.getSchoolId());
if (!sameSchoolTeachingClass || (!isTeacherAdmin(teacher)
&& !StringUtils.equals(teacher.getUserId(), schoolClass.getCreatedBy()))) {
throw new ServiceException(HttpStatus.FORBIDDEN, "无权访问该教学班成绩");
}
return schoolClass;
}
@Override @Override
public TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId) { public TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId) {
requireOwnedClass(teacher, teachingClassId); requireOwnedClass(teacher, teachingClassId);
@ -81,6 +125,53 @@ public class TeacherScoreServiceImpl implements TeacherScoreService {
return weight; return weight;
} }
@Override
public BigDecimal getTeacherScore(String teachingClassId, String studentUserId) {
if (teacherScoreMapper == null || StringUtils.isBlank(teachingClassId) || StringUtils.isBlank(studentUserId)) {
return null;
}
TeachingClassTeacherScore stored = teacherScoreMapper.selectByClassAndStudent(teachingClassId, studentUserId);
return stored == null ? null : stored.getTeacherScore();
}
@Override
public Map<String, BigDecimal> getTeacherScores(String teachingClassId, List<String> studentUserIds) {
if (teacherScoreMapper == null || StringUtils.isBlank(teachingClassId)
|| studentUserIds == null || studentUserIds.isEmpty()) {
return Collections.emptyMap();
}
List<TeachingClassTeacherScore> rows = teacherScoreMapper
.selectByClassAndStudents(teachingClassId, studentUserIds);
Map<String, BigDecimal> scores = new LinkedHashMap<>();
for (TeachingClassTeacherScore row : rows == null
? Collections.<TeachingClassTeacherScore>emptyList() : rows) {
scores.put(row.getStudentUserId(), row.getTeacherScore());
}
return scores;
}
@Override
public TeachingClassTeacherScore saveTeacherScore(JwtUser teacher, String teachingClassId,
String studentUserId, BigDecimal score) {
requireOwnedClass(teacher, teachingClassId);
if (StringUtils.isBlank(studentUserId) || teachingClassStudentMapper == null
|| 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, BigDecimal.ROUND_HALF_UP));
record.setScoredBy(teacher.getUserId());
if (teacherScoreMapper != null) {
teacherScoreMapper.upsert(record);
}
return record;
}
private TeachingClassScoreWeight defaultWeight(String teachingClassId) { private TeachingClassScoreWeight defaultWeight(String teachingClassId) {
TeachingClassScoreWeight weight = new TeachingClassScoreWeight(); TeachingClassScoreWeight weight = new TeachingClassScoreWeight();
weight.setTeachingClassId(teachingClassId); weight.setTeachingClassId(teachingClassId);
@ -108,4 +199,13 @@ public class TeacherScoreServiceImpl implements TeacherScoreService {
throw new ServiceException(HttpStatus.FORBIDDEN, "仅教师可访问成绩中心"); throw new ServiceException(HttpStatus.FORBIDDEN, "仅教师可访问成绩中心");
} }
} }
private boolean isTeacherAdmin(JwtUser teacher) {
if (teacher == null || userinfoMapper == null) {
return false;
}
Userinfo userinfo = userinfoMapper.selectByPrimaryKey(teacher.getUserId());
return userinfo != null && Boolean.TRUE.equals(userinfo.getTeacherAdmin())
&& StringUtils.equals(teacher.getSchoolId(), userinfo.getSchoolId());
}
} }

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sztzjy.linkCommerce.mapper.TeachingClassTeacherScoreMapper">
<resultMap id="BaseResultMap" type="com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore">
<id column="teaching_class_id" property="teachingClassId" />
<id column="student_user_id" property="studentUserId" />
<result column="teacher_score" property="teacherScore" />
<result column="scored_by" property="scoredBy" />
<result column="update_time" property="updateTime" />
</resultMap>
<select id="selectByClassAndStudent" resultMap="BaseResultMap">
select teaching_class_id, student_user_id, teacher_score, scored_by, update_time
from teaching_class_teacher_score
where teaching_class_id = #{teachingClassId}
and student_user_id = #{studentUserId}
</select>
<select id="selectByClassAndStudents" resultMap="BaseResultMap">
select teaching_class_id, student_user_id, teacher_score, scored_by, update_time
from teaching_class_teacher_score
where teaching_class_id = #{teachingClassId}
and student_user_id in
<foreach collection="studentUserIds" item="studentUserId" open="(" separator="," close=")">
#{studentUserId}
</foreach>
</select>
<insert id="upsert" parameterType="com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore">
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
</insert>
</mapper>

@ -0,0 +1,27 @@
package com.sztzjy.linkCommerce.config;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.jdbc.core.JdbcTemplate;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
class TeachingClassTeacherScoreSchemaInitializerTest {
@Test
void createsClassStudentTeacherScoreTable() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
TeachingClassTeacherScoreSchemaInitializer initializer =
new TeachingClassTeacherScoreSchemaInitializer(jdbcTemplate);
initializer.initialize();
ArgumentCaptor<String> statement = ArgumentCaptor.forClass(String.class);
verify(jdbcTemplate).execute(statement.capture());
String ddl = statement.getValue();
assertTrue(ddl.startsWith("CREATE TABLE IF NOT EXISTS teaching_class_teacher_score"));
assertTrue(ddl.contains("teacher_score decimal(5,2) NOT NULL"));
assertTrue(ddl.contains("PRIMARY KEY (teaching_class_id, student_user_id)"));
}
}

@ -13,8 +13,7 @@ import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.*;
import static org.mockito.Mockito.when;
class TeaScoreControllerScopeTest { class TeaScoreControllerScopeTest {
@Test @Test
@ -38,5 +37,32 @@ class TeaScoreControllerScopeTest {
ResultEntity<?> result = controller.ownedTeachingClasses(new MockHttpServletRequest()); ResultEntity<?> result = controller.ownedTeachingClasses(new MockHttpServletRequest());
assertEquals(HttpStatus.OK, result.getStatusCode()); assertEquals(HttpStatus.OK, result.getStatusCode());
verify(service).listOwnedClasses(any());
}
@Test
void accessibleTeachingClassesUsesAuthenticatedTeacherScope() {
TeaScoreController controller = controllerForTeacher();
TeacherScoreService service = mock(TeacherScoreService.class);
ReflectionTestUtils.setField(controller, "teacherScoreService", service);
when(service.listAccessibleClasses(any())).thenReturn(Collections.emptyList());
ResultEntity<?> result = controller.accessibleTeachingClasses(new MockHttpServletRequest());
assertEquals(HttpStatus.OK, result.getStatusCode());
verify(service).listAccessibleClasses(any());
}
private TeaScoreController controllerForTeacher() {
return new TeaScoreController() {
@Override
protected JwtUser currentUser(javax.servlet.http.HttpServletRequest request) {
JwtUser teacher = new JwtUser();
teacher.setUserId("teacher-1");
teacher.setSchoolId("school-1");
teacher.setRoleId(3);
return teacher;
}
};
} }
} }

@ -4,15 +4,23 @@ import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser; import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.SchoolClass; import com.sztzjy.linkCommerce.entity.SchoolClass;
import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight; import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight;
import com.sztzjy.linkCommerce.entity.TeachingClassTeacherScore;
import com.sztzjy.linkCommerce.entity.Userinfo;
import com.sztzjy.linkCommerce.mapper.SchoolClassMapper; import com.sztzjy.linkCommerce.mapper.SchoolClassMapper;
import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper; import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper;
import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper;
import com.sztzjy.linkCommerce.mapper.TeachingClassTeacherScoreMapper;
import com.sztzjy.linkCommerce.mapper.UserinfoMapper;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
class TeacherScoreServiceImplTest { class TeacherScoreServiceImplTest {
@ -49,6 +57,74 @@ class TeacherScoreServiceImplTest {
assertEquals(HttpStatus.FORBIDDEN, error.getCode()); assertEquals(HttpStatus.FORBIDDEN, error.getCode());
} }
@Test
void teacherAdminListsAndCanReadAllTeachingClassesInSameSchool() {
TeacherScoreServiceImpl service = new TeacherScoreServiceImpl();
SchoolClassMapper classMapper = mock(SchoolClassMapper.class);
UserinfoMapper userinfoMapper = mock(UserinfoMapper.class);
ReflectionTestUtils.setField(service, "schoolClassMapper", classMapper);
ReflectionTestUtils.setField(service, "userinfoMapper", userinfoMapper);
JwtUser manager = teacher("teacher-admin", "school-1");
Userinfo managerInfo = new Userinfo();
managerInfo.setUserId("teacher-admin");
managerInfo.setSchoolId("school-1");
managerInfo.setTeacherAdmin(true);
SchoolClass otherTeacherClass = classRow("class-2", "teacher-2", "school-1");
when(userinfoMapper.selectByPrimaryKey("teacher-admin")).thenReturn(managerInfo);
when(classMapper.selectByExample(any())).thenReturn(java.util.Collections.singletonList(otherTeacherClass));
when(classMapper.selectByPrimaryKey("class-2")).thenReturn(otherTeacherClass);
assertEquals(1, service.listAccessibleClasses(manager).size());
assertEquals("class-2", service.requireAccessibleClass(manager, "class-2").getSchoolClassId());
}
@Test
void classOwnerCanSaveTeacherScoreButManagerCannotScoreAnotherTeachersClass() {
TeacherScoreServiceImpl service = new TeacherScoreServiceImpl();
SchoolClassMapper classMapper = mock(SchoolClassMapper.class);
TeachingClassStudentMapper memberMapper = mock(TeachingClassStudentMapper.class);
TeachingClassTeacherScoreMapper scoreMapper = mock(TeachingClassTeacherScoreMapper.class);
UserinfoMapper userinfoMapper = mock(UserinfoMapper.class);
ReflectionTestUtils.setField(service, "schoolClassMapper", classMapper);
ReflectionTestUtils.setField(service, "teachingClassStudentMapper", memberMapper);
ReflectionTestUtils.setField(service, "teacherScoreMapper", scoreMapper);
ReflectionTestUtils.setField(service, "userinfoMapper", userinfoMapper);
SchoolClass ownerClass = classRow("class-1", "teacher-1", "school-1");
when(classMapper.selectByPrimaryKey("class-1")).thenReturn(ownerClass);
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());
verify(scoreMapper).upsert(saved);
Userinfo managerInfo = new Userinfo();
managerInfo.setUserId("teacher-admin");
managerInfo.setSchoolId("school-1");
managerInfo.setTeacherAdmin(true);
when(userinfoMapper.selectByPrimaryKey("teacher-admin")).thenReturn(managerInfo);
ServiceException error = assertThrows(ServiceException.class, () -> service.saveTeacherScore(
teacher("teacher-admin", "school-1"), "class-1", "student-1", new java.math.BigDecimal("90")));
assertEquals(HttpStatus.FORBIDDEN, error.getCode());
}
@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) { private JwtUser teacher(String userId, String schoolId) {
JwtUser user = new JwtUser(); JwtUser user = new JwtUser();
user.setUserId(userId); user.setUserId(userId);

Loading…
Cancel
Save