feat: scope teacher scores to owned classes
parent
3591b48db1
commit
f96cb66d02
@ -0,0 +1,10 @@
|
||||
CREATE TABLE IF NOT EXISTS teaching_class_score_weight (
|
||||
teaching_class_id varchar(64) NOT NULL,
|
||||
foundation_weight decimal(8,4) NOT NULL,
|
||||
market_insight_weight decimal(8,4) NOT NULL,
|
||||
planning_design_weight decimal(8,4) NOT NULL,
|
||||
development_validation_weight decimal(8,4) NOT NULL,
|
||||
launch_operation_weight decimal(8,4) NOT NULL,
|
||||
comprehensive_training_weight decimal(8,4) NOT NULL,
|
||||
PRIMARY KEY (teaching_class_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='教学班六模块成绩权重';
|
||||
@ -0,0 +1,28 @@
|
||||
package com.sztzjy.linkCommerce.entity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class TeachingClassScoreWeight {
|
||||
private String teachingClassId;
|
||||
private BigDecimal foundationWeight;
|
||||
private BigDecimal marketInsightWeight;
|
||||
private BigDecimal planningDesignWeight;
|
||||
private BigDecimal developmentValidationWeight;
|
||||
private BigDecimal launchOperationWeight;
|
||||
private BigDecimal comprehensiveTrainingWeight;
|
||||
|
||||
public String getTeachingClassId() { return teachingClassId; }
|
||||
public void setTeachingClassId(String teachingClassId) { this.teachingClassId = teachingClassId == null ? null : teachingClassId.trim(); }
|
||||
public BigDecimal getFoundationWeight() { return foundationWeight; }
|
||||
public void setFoundationWeight(BigDecimal foundationWeight) { this.foundationWeight = foundationWeight; }
|
||||
public BigDecimal getMarketInsightWeight() { return marketInsightWeight; }
|
||||
public void setMarketInsightWeight(BigDecimal marketInsightWeight) { this.marketInsightWeight = marketInsightWeight; }
|
||||
public BigDecimal getPlanningDesignWeight() { return planningDesignWeight; }
|
||||
public void setPlanningDesignWeight(BigDecimal planningDesignWeight) { this.planningDesignWeight = planningDesignWeight; }
|
||||
public BigDecimal getDevelopmentValidationWeight() { return developmentValidationWeight; }
|
||||
public void setDevelopmentValidationWeight(BigDecimal developmentValidationWeight) { this.developmentValidationWeight = developmentValidationWeight; }
|
||||
public BigDecimal getLaunchOperationWeight() { return launchOperationWeight; }
|
||||
public void setLaunchOperationWeight(BigDecimal launchOperationWeight) { this.launchOperationWeight = launchOperationWeight; }
|
||||
public BigDecimal getComprehensiveTrainingWeight() { return comprehensiveTrainingWeight; }
|
||||
public void setComprehensiveTrainingWeight(BigDecimal comprehensiveTrainingWeight) { this.comprehensiveTrainingWeight = comprehensiveTrainingWeight; }
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.sztzjy.linkCommerce.mapper;
|
||||
|
||||
import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface TeachingClassScoreWeightMapper {
|
||||
TeachingClassScoreWeight selectByTeachingClassId(String teachingClassId);
|
||||
int insert(TeachingClassScoreWeight weight);
|
||||
int update(TeachingClassScoreWeight weight);
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
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 java.util.List;
|
||||
|
||||
public interface TeacherScoreService {
|
||||
List<SchoolClass> listOwnedClasses(JwtUser teacher);
|
||||
SchoolClass requireOwnedClass(JwtUser teacher, String teachingClassId);
|
||||
TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId);
|
||||
TeachingClassScoreWeight saveWeight(JwtUser teacher, String teachingClassId, TeachingClassScoreWeight weight);
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
package com.sztzjy.linkCommerce.service.impl;
|
||||
|
||||
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.SchoolClassExample;
|
||||
import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight;
|
||||
import com.sztzjy.linkCommerce.mapper.SchoolClassMapper;
|
||||
import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper;
|
||||
import com.sztzjy.linkCommerce.service.TeacherScoreService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class TeacherScoreServiceImpl implements TeacherScoreService {
|
||||
@Autowired
|
||||
SchoolClassMapper schoolClassMapper;
|
||||
@Autowired(required = false)
|
||||
TeachingClassScoreWeightMapper weightMapper;
|
||||
|
||||
@Override
|
||||
public List<SchoolClass> listOwnedClasses(JwtUser teacher) {
|
||||
requireTeacher(teacher);
|
||||
SchoolClassExample example = new SchoolClassExample();
|
||||
example.createCriteria().andSchoolIdEqualTo(teacher.getSchoolId())
|
||||
.andCreatedByEqualTo(teacher.getUserId()).andClassTypeEqualTo("TEACHING");
|
||||
List<SchoolClass> rows = schoolClassMapper.selectByExample(example);
|
||||
return rows == null ? Collections.emptyList() : rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchoolClass requireOwnedClass(JwtUser teacher, String teachingClassId) {
|
||||
requireTeacher(teacher);
|
||||
if (StringUtils.isBlank(teachingClassId)) {
|
||||
throw new ServiceException(HttpStatus.BAD_REQUEST, "请选择自己的教学班");
|
||||
}
|
||||
SchoolClass schoolClass = schoolClassMapper.selectByPrimaryKey(teachingClassId);
|
||||
if (schoolClass == null || !"TEACHING".equals(schoolClass.getClassType())
|
||||
|| !StringUtils.equals(teacher.getSchoolId(), schoolClass.getSchoolId())
|
||||
|| !StringUtils.equals(teacher.getUserId(), schoolClass.getCreatedBy())) {
|
||||
throw new ServiceException(HttpStatus.FORBIDDEN, "无权访问该教学班成绩");
|
||||
}
|
||||
return schoolClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeachingClassScoreWeight getOrCreateWeight(JwtUser teacher, String teachingClassId) {
|
||||
requireOwnedClass(teacher, teachingClassId);
|
||||
TeachingClassScoreWeight existing = weightMapper == null ? null : weightMapper.selectByTeachingClassId(teachingClassId);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
TeachingClassScoreWeight defaults = defaultWeight(teachingClassId);
|
||||
if (weightMapper != null) {
|
||||
weightMapper.insert(defaults);
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeachingClassScoreWeight saveWeight(JwtUser teacher, String teachingClassId, TeachingClassScoreWeight weight) {
|
||||
requireOwnedClass(teacher, teachingClassId);
|
||||
if (weight == null || !isOneHundredPercent(weight)) {
|
||||
throw new ServiceException(HttpStatus.BAD_REQUEST, "六项权重合计必须为100%");
|
||||
}
|
||||
weight.setTeachingClassId(teachingClassId);
|
||||
TeachingClassScoreWeight current = weightMapper == null ? null : weightMapper.selectByTeachingClassId(teachingClassId);
|
||||
if (weightMapper != null) {
|
||||
if (current == null) {
|
||||
weightMapper.insert(weight);
|
||||
} else {
|
||||
weightMapper.update(weight);
|
||||
}
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
private TeachingClassScoreWeight defaultWeight(String teachingClassId) {
|
||||
TeachingClassScoreWeight weight = new TeachingClassScoreWeight();
|
||||
weight.setTeachingClassId(teachingClassId);
|
||||
weight.setFoundationWeight(new BigDecimal("0.10"));
|
||||
weight.setMarketInsightWeight(new BigDecimal("0.30"));
|
||||
weight.setPlanningDesignWeight(new BigDecimal("0.10"));
|
||||
weight.setDevelopmentValidationWeight(new BigDecimal("0.10"));
|
||||
weight.setLaunchOperationWeight(new BigDecimal("0.20"));
|
||||
weight.setComprehensiveTrainingWeight(new BigDecimal("0.20"));
|
||||
return weight;
|
||||
}
|
||||
|
||||
private boolean isOneHundredPercent(TeachingClassScoreWeight weight) {
|
||||
BigDecimal sum = safe(weight.getFoundationWeight()).add(safe(weight.getMarketInsightWeight()))
|
||||
.add(safe(weight.getPlanningDesignWeight())).add(safe(weight.getDevelopmentValidationWeight()))
|
||||
.add(safe(weight.getLaunchOperationWeight())).add(safe(weight.getComprehensiveTrainingWeight()));
|
||||
return BigDecimal.ONE.compareTo(sum) == 0;
|
||||
}
|
||||
|
||||
private BigDecimal safe(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; }
|
||||
|
||||
private void requireTeacher(JwtUser teacher) {
|
||||
if (teacher == null || StringUtils.isBlank(teacher.getUserId()) || StringUtils.isBlank(teacher.getSchoolId())
|
||||
|| (teacher.getRoleId() != 1 && teacher.getRoleId() != 3)) {
|
||||
throw new ServiceException(HttpStatus.FORBIDDEN, "仅教师可访问成绩中心");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
<?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.TeachingClassScoreWeightMapper">
|
||||
<resultMap id="WeightMap" type="com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight">
|
||||
<id column="teaching_class_id" property="teachingClassId" />
|
||||
<result column="foundation_weight" property="foundationWeight" />
|
||||
<result column="market_insight_weight" property="marketInsightWeight" />
|
||||
<result column="planning_design_weight" property="planningDesignWeight" />
|
||||
<result column="development_validation_weight" property="developmentValidationWeight" />
|
||||
<result column="launch_operation_weight" property="launchOperationWeight" />
|
||||
<result column="comprehensive_training_weight" property="comprehensiveTrainingWeight" />
|
||||
</resultMap>
|
||||
<sql id="Columns">teaching_class_id, foundation_weight, market_insight_weight, planning_design_weight, development_validation_weight, launch_operation_weight, comprehensive_training_weight</sql>
|
||||
<select id="selectByTeachingClassId" resultMap="WeightMap">select <include refid="Columns" /> from teaching_class_score_weight where teaching_class_id = #{teachingClassId}</select>
|
||||
<insert id="insert">insert into teaching_class_score_weight (<include refid="Columns" />) values (#{teachingClassId}, #{foundationWeight}, #{marketInsightWeight}, #{planningDesignWeight}, #{developmentValidationWeight}, #{launchOperationWeight}, #{comprehensiveTrainingWeight})</insert>
|
||||
<update id="update">update teaching_class_score_weight set foundation_weight=#{foundationWeight}, market_insight_weight=#{marketInsightWeight}, planning_design_weight=#{planningDesignWeight}, development_validation_weight=#{developmentValidationWeight}, launch_operation_weight=#{launchOperationWeight}, comprehensive_training_weight=#{comprehensiveTrainingWeight} where teaching_class_id=#{teachingClassId}</update>
|
||||
</mapper>
|
||||
@ -0,0 +1,42 @@
|
||||
package com.sztzjy.linkCommerce.controller.stu;
|
||||
|
||||
import com.sztzjy.linkCommerce.config.security.JwtUser;
|
||||
import com.sztzjy.linkCommerce.entity.SchoolClass;
|
||||
import com.sztzjy.linkCommerce.service.TeacherScoreService;
|
||||
import com.sztzjy.linkCommerce.util.ResultEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class TeaScoreControllerScopeTest {
|
||||
@Test
|
||||
void ownedTeachingClassesUsesAuthenticatedTeacher() {
|
||||
TeaScoreController controller = 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;
|
||||
}
|
||||
};
|
||||
TeacherScoreService service = mock(TeacherScoreService.class);
|
||||
ReflectionTestUtils.setField(controller, "teacherScoreService", service);
|
||||
SchoolClass owned = new SchoolClass();
|
||||
owned.setSchoolClassId("class-1");
|
||||
when(service.listOwnedClasses(any())).thenReturn(Collections.singletonList(owned));
|
||||
|
||||
ResultEntity<?> result = controller.ownedTeachingClasses(new MockHttpServletRequest());
|
||||
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package com.sztzjy.linkCommerce.service.impl;
|
||||
|
||||
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.mapper.SchoolClassMapper;
|
||||
import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper;
|
||||
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.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class TeacherScoreServiceImplTest {
|
||||
|
||||
@Test
|
||||
void createsSixModuleDefaultsOnlyForOwnedTeachingClass() {
|
||||
TeacherScoreServiceImpl service = new TeacherScoreServiceImpl();
|
||||
SchoolClassMapper classMapper = mock(SchoolClassMapper.class);
|
||||
TeachingClassScoreWeightMapper weightMapper = mock(TeachingClassScoreWeightMapper.class);
|
||||
ReflectionTestUtils.setField(service, "schoolClassMapper", classMapper);
|
||||
ReflectionTestUtils.setField(service, "weightMapper", weightMapper);
|
||||
when(classMapper.selectByPrimaryKey("class-1")).thenReturn(classRow("class-1", "teacher-1", "school-1"));
|
||||
|
||||
TeachingClassScoreWeight weight = service.getOrCreateWeight(teacher("teacher-1", "school-1"), "class-1");
|
||||
|
||||
assertEquals(new java.math.BigDecimal("0.10"), weight.getFoundationWeight());
|
||||
assertEquals(new java.math.BigDecimal("0.30"), weight.getMarketInsightWeight());
|
||||
assertEquals(new java.math.BigDecimal("0.10"), weight.getPlanningDesignWeight());
|
||||
assertEquals(new java.math.BigDecimal("0.10"), weight.getDevelopmentValidationWeight());
|
||||
assertEquals(new java.math.BigDecimal("0.20"), weight.getLaunchOperationWeight());
|
||||
assertEquals(new java.math.BigDecimal("0.20"), weight.getComprehensiveTrainingWeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsForeignTeachingClass() {
|
||||
TeacherScoreServiceImpl service = new TeacherScoreServiceImpl();
|
||||
SchoolClassMapper classMapper = mock(SchoolClassMapper.class);
|
||||
ReflectionTestUtils.setField(service, "schoolClassMapper", classMapper);
|
||||
when(classMapper.selectByPrimaryKey("class-2")).thenReturn(classRow("class-2", "teacher-2", "school-1"));
|
||||
|
||||
ServiceException error = assertThrows(ServiceException.class,
|
||||
() -> service.requireOwnedClass(teacher("teacher-1", "school-1"), "class-2"));
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, error.getCode());
|
||||
}
|
||||
|
||||
private JwtUser teacher(String userId, String schoolId) {
|
||||
JwtUser user = new JwtUser();
|
||||
user.setUserId(userId);
|
||||
user.setSchoolId(schoolId);
|
||||
user.setRoleId(3);
|
||||
return user;
|
||||
}
|
||||
|
||||
private SchoolClass classRow(String id, String createdBy, String schoolId) {
|
||||
SchoolClass row = new SchoolClass();
|
||||
row.setSchoolClassId(id);
|
||||
row.setCreatedBy(createdBy);
|
||||
row.setSchoolId(schoolId);
|
||||
row.setClassType("TEACHING");
|
||||
return row;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue