From d3cff4553c563e631da2447300d1072cbefe5607 Mon Sep 17 00:00:00 2001 From: chenyuan Date: Tue, 4 Aug 2026 13:43:03 +0800 Subject: [PATCH] feat: expand teacher admin student roster scope --- .../controller/stu/UserController.java | 55 ++++++---- .../stu/UserControllerTeacherAdminTest.java | 100 ++++++++++++++++++ 2 files changed, 136 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java b/src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java index 5d9404a..7cc5d39 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java @@ -893,8 +893,8 @@ public class UserController { return new ResultEntity<>(HttpStatus.BAD_REQUEST, "无权维护学生名单"); } SchoolClass teachingClass = schoolClassMapper.selectByPrimaryKey(teachingClassId); - if (!isOwnedTeachingClass(teachingClass, operatorId)) { - return new ResultEntity<>(HttpStatus.BAD_REQUEST, "只能选择自己创建的教学班"); + if (!canManageTeachingClass(operator, teachingClass)) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "只能选择可维护的教学班"); } if (StringUtils.isAnyBlank(StringUtils.trimToNull(name), StringUtils.trimToNull(userName))) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, "学生姓名和学号不能为空"); @@ -938,8 +938,8 @@ public class UserController { return new ResultEntity<>(HttpStatus.BAD_REQUEST, "无权维护该学生名单"); } SchoolClass teachingClass = schoolClassMapper.selectByPrimaryKey(teachingClassId); - if (!isOwnedTeachingClass(teachingClass, operatorId)) { - return new ResultEntity<>(HttpStatus.BAD_REQUEST, "只能选择自己创建的教学班"); + if (!canManageTeachingClass(operator, teachingClass)) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "只能选择可维护的教学班"); } if (StringUtils.isAnyBlank(StringUtils.trimToNull(name), StringUtils.trimToNull(userName))) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, "学生姓名和学号不能为空"); @@ -1002,11 +1002,11 @@ public class UserController { criteria.andUsernameEqualTo(userName); } if (teacherRosterScope) { - List ownedStudentIds = resolveOwnedRosterStudentIds(operator, schoolClassId); - if (ownedStudentIds == null) { - return new ResultEntity<>(HttpStatus.BAD_REQUEST, "只能查看自己教学班内的学生"); + List managedStudentIds = resolveManagedRosterStudentIds(operator, schoolClassId); + if (managedStudentIds == null) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "无权查看该教学班内的学生"); } - criteria.andUserIdIn(ownedStudentIds.isEmpty() ? Collections.singletonList("__empty__") : ownedStudentIds); + criteria.andUserIdIn(managedStudentIds.isEmpty() ? Collections.singletonList("__empty__") : managedStudentIds); } if (StringUtils.isNotBlank(schoolClassId)) { schoolClass = schoolClassMapper.selectByPrimaryKey(schoolClassId); @@ -1053,21 +1053,25 @@ public class UserController { } //编辑 - private List resolveOwnedRosterStudentIds(Userinfo operator, String teachingClassId) { + private List resolveManagedRosterStudentIds(Userinfo operator, String teachingClassId) { if (!isSchoolTeacher(operator) || teachingClassStudentMapper == null) { return Collections.emptyList(); } List teachingClasses; if (StringUtils.isNotBlank(teachingClassId)) { SchoolClass teachingClass = schoolClassMapper.selectByPrimaryKey(teachingClassId); - if (!isOwnedTeachingClass(teachingClass, operator.getUserId())) { + if (!canManageTeachingClass(operator, teachingClass)) { return null; } teachingClasses = Collections.singletonList(teachingClass); } else { SchoolClassExample example = new SchoolClassExample(); - example.createCriteria().andSchoolIdEqualTo(operator.getSchoolId()) - .andCreatedByEqualTo(operator.getUserId()).andClassTypeEqualTo("TEACHING"); + SchoolClassExample.Criteria criteria = example.createCriteria() + .andSchoolIdEqualTo(operator.getSchoolId()) + .andClassTypeEqualTo("TEACHING"); + if (!isTeacherAdmin(operator)) { + criteria.andCreatedByEqualTo(operator.getUserId()); + } teachingClasses = schoolClassMapper.selectByExample(example); } Set studentIds = new LinkedHashSet<>(); @@ -1157,8 +1161,9 @@ public class UserController { public ResultEntity initializeTeachingClassTrainingData(@RequestParam String teachingClassId, @RequestParam String operatorId) { SchoolClass schoolClass = schoolClassMapper.selectByPrimaryKey(teachingClassId); - if (!isOwnedTeachingClass(schoolClass, operatorId)) { - return new ResultEntity<>(HttpStatus.BAD_REQUEST, "只能初始化自己创建的教学班"); + Userinfo operator = userinfoMapper.selectByPrimaryKey(operatorId); + if (!canManageTeachingClass(operator, schoolClass)) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "无权初始化该教学班"); } List members = teachingClassStudentMapper.selectByTeachingClassId(teachingClassId); if (members == null) { @@ -1179,8 +1184,9 @@ public class UserController { @RequestParam String studentUserId, @RequestParam String operatorId) { SchoolClass schoolClass = schoolClassMapper.selectByPrimaryKey(teachingClassId); - if (!isOwnedTeachingClass(schoolClass, operatorId)) { - return new ResultEntity<>(HttpStatus.BAD_REQUEST, "只能初始化自己教学班内的学生"); + Userinfo operator = userinfoMapper.selectByPrimaryKey(operatorId); + if (!canManageTeachingClass(operator, schoolClass)) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "无权初始化该教学班内的学生"); } if (teachingClassStudentMapper != null && teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId(studentUserId, teachingClassId) <= 0) { @@ -1569,13 +1575,13 @@ public class UserController { UserinfoExample userInfoExample = new UserinfoExample(); userInfoExample.createCriteria().andSchoolIdEqualTo(schoolId).andRoleEqualTo(4); if (teacherRosterScope) { - List ownedStudentIds = resolveOwnedRosterStudentIds(operator, schoolClassId); - if (ownedStudentIds == null) { + List managedStudentIds = resolveManagedRosterStudentIds(operator, schoolClassId); + if (managedStudentIds == null) { response.setStatus(HttpStatus.BAD_REQUEST.value()); return; } userInfoExample.getOredCriteria().get(0) - .andUserIdIn(ownedStudentIds.isEmpty() ? Collections.singletonList("__empty__") : ownedStudentIds); + .andUserIdIn(managedStudentIds.isEmpty() ? Collections.singletonList("__empty__") : managedStudentIds); } List list = userinfoMapper.selectByExample(userInfoExample); //导出的表名 @@ -1726,6 +1732,14 @@ public class UserController { && isClassOwner(schoolClass, operatorId); } + private boolean canManageTeachingClass(Userinfo operator, SchoolClass teachingClass) { + return isSchoolTeacher(operator) + && teachingClass != null + && "TEACHING".equals(teachingClass.getClassType()) + && StringUtils.equals(operator.getSchoolId(), teachingClass.getSchoolId()) + && (isTeacherAdmin(operator) || isClassOwner(teachingClass, operator.getUserId())); + } + private boolean isClassOwner(SchoolClass schoolClass, String userId) { return schoolClass != null && StringUtils.isNotBlank(userId) @@ -2345,6 +2359,9 @@ public class UserController { return false; } SchoolClass schoolClass = schoolClassMapper.selectByPrimaryKey(schoolClassId); + if (canManageTeachingClass(operator, schoolClass)) { + return true; + } if (canManageAdminClass(operator, schoolClass)) { return true; } diff --git a/src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java b/src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java index 23a0ef7..e5bd8b4 100644 --- a/src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java +++ b/src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java @@ -201,6 +201,98 @@ class UserControllerTeacherAdminTest { verify(controller.userinfoMapper, never()).selectByExample(any(UserinfoExample.class)); } + @Test + void teacherAdminRosterListIncludesStudentsFromEveryTeachingClassInSchool() { + UserController controller = controllerWithCommonMocks(); + SchoolClass ownedClass = classCreatedBy("teacher-admin"); + ownedClass.setSchoolClassId("owned-class"); + SchoolClass otherClass = classCreatedBy("teacher-2"); + otherClass.setSchoolClassId("other-class"); + TeachingClassStudent ownedMember = activeMember("owned-class", "student-owned"); + TeachingClassStudent otherMember = activeMember("other-class", "student-other"); + when(controller.schoolProductConfigService.isTeacherRosterManaged("school-1")).thenReturn(true); + when(controller.schoolClassMapper.selectByExample(any(SchoolClassExample.class))) + .thenReturn(Arrays.asList(ownedClass, otherClass)); + when(controller.teachingClassStudentMapper.selectByTeachingClassId("owned-class")) + .thenReturn(Collections.singletonList(ownedMember)); + when(controller.teachingClassStudentMapper.selectByTeachingClassId("other-class")) + .thenReturn(Collections.singletonList(otherMember)); + when(controller.userinfoMapper.selectByExample(any(UserinfoExample.class))) + .thenReturn(Arrays.asList(student("student-owned", "owned", "s001", null), + student("student-other", "other", "s002", null))); + + ResultEntity> result = controller.selectStudent( + "other-school", null, null, null, 1, 10, "teacher-admin"); + + ArgumentCaptor exampleCaptor = ArgumentCaptor.forClass(UserinfoExample.class); + verify(controller.userinfoMapper).selectByExample(exampleCaptor.capture()); + verify(controller.schoolClassMapper).selectByExample(argThat(example -> + hasCondition(example, "school_id =") + && hasCondition(example, "class_type =") + && !hasCondition(example, "created_by ="))); + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertTrue(hasUserCondition(exampleCaptor.getValue(), "school_id =", "school-1")); + assertTrue(hasUserCondition(exampleCaptor.getValue(), "user_id in", + Arrays.asList("student-owned", "student-other"))); + } + + @Test + void teacherAdminCanUpdateStudentInAnotherTeachersTeachingClass() { + UserController controller = controllerWithCommonMocks(); + SchoolClass otherClass = classCreatedBy("teacher-2"); + otherClass.setSchoolClassId("other-class"); + Userinfo existingStudent = student("student-other", "before", "s002", null); + when(controller.schoolProductConfigService.isTeacherRosterManaged("school-1")).thenReturn(true); + when(controller.userinfoMapper.selectByPrimaryKey("student-other")).thenReturn(existingStudent); + when(controller.schoolClassMapper.selectByPrimaryKey("other-class")).thenReturn(otherClass); + when(controller.userinfoMapper.updateByPrimaryKeySelective(any(Userinfo.class))).thenReturn(1); + + ResultEntity result = controller.updateTeacherRosterStudent( + "student-other", "after", "s002", "other-class", "teacher-admin"); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + verify(controller.userinfoMapper).updateByPrimaryKeySelective(any(Userinfo.class)); + } + + @Test + void teacherAdminCanDeleteAndResetPasswordForStudentInAnotherTeachersTeachingClass() { + UserController controller = controllerWithCommonMocks(); + SchoolClass otherClass = classCreatedBy("teacher-2"); + otherClass.setSchoolClassId("other-class"); + Userinfo existingStudent = student("student-other", "other", "s002", null); + when(controller.userinfoMapper.selectByPrimaryKey("student-other")).thenReturn(existingStudent); + when(controller.schoolClassMapper.selectByPrimaryKey("other-class")).thenReturn(otherClass); + when(controller.teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId("student-other", "other-class")) + .thenReturn(1L); + when(controller.teachingClassStudentMapper.deleteByStudentUserIdAndTeachingClassId("student-other", "other-class")) + .thenReturn(1); + when(controller.userinfoMapper.updateByPrimaryKey(existingStudent)).thenReturn(1); + + ResultEntity deleteResult = controller.deleteStudent("student-other", "teacher-admin", "other-class"); + ResultEntity passwordResult = controller.updateStudentPassword("student-other", "teacher-admin", "other-class"); + + assertEquals(HttpStatus.OK, deleteResult.getStatusCode()); + assertEquals(HttpStatus.OK, passwordResult.getStatusCode()); + verify(controller.teachingClassStudentMapper).deleteByStudentUserIdAndTeachingClassId("student-other", "other-class"); + verify(controller.userinfoMapper).updateByPrimaryKey(existingStudent); + } + + @Test + void teacherAdminCanInitializeStudentDataInAnotherTeachersTeachingClass() { + UserController controller = controllerWithCommonMocks(); + SchoolClass otherClass = classCreatedBy("teacher-2"); + otherClass.setSchoolClassId("other-class"); + when(controller.schoolClassMapper.selectByPrimaryKey("other-class")).thenReturn(otherClass); + when(controller.teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId("student-other", "other-class")) + .thenReturn(1L); + + ResultEntity result = controller.initializeTeachingClassStudentTrainingData( + "other-class", "student-other", "teacher-admin"); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + verify(controller.cptfController).restartByUserId("student-other"); + } + @Test void addSchoolClassStoresCreatorTeacher() { UserController controller = controllerWithCommonMocks(); @@ -1234,6 +1326,14 @@ class UserControllerTeacherAdminTest { return row; } + private TeachingClassStudent activeMember(String teachingClassId, String studentUserId) { + TeachingClassStudent member = new TeachingClassStudent(); + member.setTeachingClassId(teachingClassId); + member.setStudentUserId(studentUserId); + member.setStatus("IN_PROGRESS"); + return member; + } + private Userinfo student(String userId, String name, String userName, String adminClassId) { Userinfo student = new Userinfo(); student.setUserId(userId);