From e4c5093466448c18ffada9b620fcadd71cf2fe23 Mon Sep 17 00:00:00 2001 From: chenyuan Date: Thu, 30 Jul 2026 09:51:30 +0800 Subject: [PATCH] feat: import teacher teaching-class rosters --- .../controller/stu/UserController.java | 67 +++++++++++++++++ .../TeacherRosterStudentImportDTO.java | 27 +++++++ .../stu/TeacherRosterImportTest.java | 72 +++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/importDto/TeacherRosterStudentImportDTO.java create mode 100644 src/test/java/com/sztzjy/linkCommerce/controller/stu/TeacherRosterImportTest.java 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 a43d4ce..49b9ec2 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java @@ -6,6 +6,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.sztzjy.linkCommerce.annotation.AnonymousAccess; +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.config.security.TokenProvider; import com.sztzjy.linkCommerce.entity.*; import com.sztzjy.linkCommerce.entity.dto.SchoolClassDto; import com.sztzjy.linkCommerce.entity.dto.SchoolFacultyDto; @@ -13,10 +15,12 @@ import com.sztzjy.linkCommerce.entity.dto.SchoolMajorDto; import com.sztzjy.linkCommerce.entity.importDto.SchoolAdminStudentImportDTO; import com.sztzjy.linkCommerce.entity.importDto.StudentImportDTO; import com.sztzjy.linkCommerce.entity.importDto.TeachingClassStudentImportDTO; +import com.sztzjy.linkCommerce.entity.importDto.TeacherRosterStudentImportDTO; import com.sztzjy.linkCommerce.entity.importDto.TeacherImportDTO; import com.sztzjy.linkCommerce.mapper.*; import com.sztzjy.linkCommerce.service.ImportService; import com.sztzjy.linkCommerce.service.SchoolService; +import com.sztzjy.linkCommerce.service.SchoolProductConfigService; import com.sztzjy.linkCommerce.service.UserInfoService; import com.sztzjy.linkCommerce.service.ValidateService; import com.sztzjy.linkCommerce.util.*; @@ -32,6 +36,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletRequest; import java.io.InputStream; import java.util.*; @@ -43,6 +48,8 @@ public class UserController { @Autowired SchoolService schoolService; @Autowired + SchoolProductConfigService schoolProductConfigService; + @Autowired SchoolMapper schoolMapper; @Autowired UserinfoMapper userinfoMapper; @@ -1093,6 +1100,66 @@ public class UserController { return new ResultEntity<>(HttpStatus.OK, "导入成功,共 " + students.size() + " 条"); } + ResultEntity importTeacherRosterRows(List rows, + String teachingClassId, + String operatorId) { + Userinfo operator = userinfoMapper.selectByPrimaryKey(operatorId); + SchoolClass teachingClass = schoolClassMapper.selectByPrimaryKey(teachingClassId); + if (!isSchoolTeacher(operator) || !isOwnedTeachingClass(teachingClass, operatorId) + || !schoolProductConfigService.isTeacherRosterManaged(operator.getSchoolId())) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "无权维护该教学班名单"); + } + if (rows == null || rows.isEmpty()) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "未读取到可导入的学生数据"); + } + for (TeacherRosterStudentImportDTO row : rows) { + String name = row == null ? "" : StringUtils.trimToEmpty(row.getName()); + String username = row == null ? "" : StringUtils.trimToEmpty(row.getUsername()); + if (StringUtils.isBlank(name) || StringUtils.isBlank(username) || userInfoService.existsByUserName(username)) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "学生姓名或学号无效"); + } + Userinfo student = new Userinfo(); + student.setUserId(UUID.randomUUID().toString()); + student.setName(name); + student.setUsername(username); + student.setPassword("123qwe"); + student.setSex(StringUtils.trimToEmpty(row.getSex())); + student.setPhone(StringUtils.trimToEmpty(row.getPhone())); + student.setEmail(StringUtils.trimToEmpty(row.getEmail())); + student.setRole(4); + student.setTeacherAdmin(false); + student.setSchoolId(operator.getSchoolId()); + student.setCreateTime(new Date()); + student.setCodeFrom("教师教学班导入"); + if (userinfoMapper.insertSelective(student) <= 0) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "学生导入失败"); + } + upsertActiveTeachingClassMember(teachingClassId, student, null, "TEACHER_ROSTER_IMPORT"); + } + return new ResultEntity<>(HttpStatus.OK, "导入成功"); + } + + @PostMapping("/teaching-classes/{teachingClassId}/students/import") + @ApiOperation("教师教学班-导入学生名单") + public ResultEntity importTeacherRoster(@PathVariable String teachingClassId, + @RequestParam MultipartFile file, + HttpServletRequest request) { + JwtUser user = TokenProvider.getJWTUser(request); + if (user == null || StringUtils.isBlank(user.getUserId())) { + return new ResultEntity<>(HttpStatus.UNAUTHORIZED, "请先登录"); + } + if (file == null || file.isEmpty()) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "请选择要导入的Excel文件"); + } + try (InputStream inputStream = file.getInputStream()) { + List rows = EasyExcel.read(inputStream) + .head(TeacherRosterStudentImportDTO.class).sheet().doReadSync(); + return importTeacherRosterRows(rows, teachingClassId, user.getUserId()); + } catch (Exception exception) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "数据校验失败", exception.getMessage()); + } + } + ResultEntity addSchoolClass(String schoolId, String schoolFacultyId, String schoolMajorId, diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/importDto/TeacherRosterStudentImportDTO.java b/src/main/java/com/sztzjy/linkCommerce/entity/importDto/TeacherRosterStudentImportDTO.java new file mode 100644 index 0000000..e140ba7 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/importDto/TeacherRosterStudentImportDTO.java @@ -0,0 +1,27 @@ +package com.sztzjy.linkCommerce.entity.importDto; + +import com.alibaba.excel.annotation.ExcelProperty; + +public class TeacherRosterStudentImportDTO { + @ExcelProperty("姓名") + private String name; + @ExcelProperty("学号") + private String username; + @ExcelProperty("性别") + private String sex; + @ExcelProperty("手机") + private String phone; + @ExcelProperty("邮箱") + private String email; + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getUsername() { return username; } + public void setUsername(String username) { this.username = username; } + public String getSex() { return sex; } + public void setSex(String sex) { this.sex = sex; } + public String getPhone() { return phone; } + public void setPhone(String phone) { this.phone = phone; } + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/controller/stu/TeacherRosterImportTest.java b/src/test/java/com/sztzjy/linkCommerce/controller/stu/TeacherRosterImportTest.java new file mode 100644 index 0000000..8c70ea3 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/controller/stu/TeacherRosterImportTest.java @@ -0,0 +1,72 @@ +package com.sztzjy.linkCommerce.controller.stu; + +import com.sztzjy.linkCommerce.entity.SchoolClass; +import com.sztzjy.linkCommerce.entity.TeachingClassStudent; +import com.sztzjy.linkCommerce.entity.Userinfo; +import com.sztzjy.linkCommerce.entity.importDto.TeacherRosterStudentImportDTO; +import com.sztzjy.linkCommerce.mapper.SchoolClassMapper; +import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper; +import com.sztzjy.linkCommerce.mapper.UserinfoMapper; +import com.sztzjy.linkCommerce.service.SchoolProductConfigService; +import com.sztzjy.linkCommerce.service.UserInfoService; +import com.sztzjy.linkCommerce.util.ResultEntity; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.Collections; +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +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.verify; +import static org.mockito.Mockito.when; + +class TeacherRosterImportTest { + @Test + void exposesTeacherRosterImportEndpoint() { + assertTrue(Arrays.stream(UserController.class.getDeclaredMethods()) + .anyMatch(method -> method.isAnnotationPresent(PostMapping.class) + && Arrays.asList(method.getAnnotation(PostMapping.class).value()) + .contains("/teaching-classes/{teachingClassId}/students/import"))); + } + + @Test + void teacherRosterImportCreatesStudentAndMembershipWithoutAdminClass() { + UserController controller = new UserController(); + controller.userinfoMapper = mock(UserinfoMapper.class); + controller.schoolClassMapper = mock(SchoolClassMapper.class); + controller.teachingClassStudentMapper = mock(TeachingClassStudentMapper.class); + controller.userInfoService = mock(UserInfoService.class); + controller.schoolProductConfigService = mock(SchoolProductConfigService.class); + + Userinfo teacher = new Userinfo(); + teacher.setUserId("teacher-1"); + teacher.setRole(3); + teacher.setSchoolId("school-zj"); + SchoolClass teachingClass = new SchoolClass(); + teachingClass.setSchoolClassId("teach-1"); + teachingClass.setSchoolId("school-zj"); + teachingClass.setClassType("TEACHING"); + teachingClass.setCreatedBy("teacher-1"); + when(controller.userinfoMapper.selectByPrimaryKey("teacher-1")).thenReturn(teacher); + when(controller.schoolClassMapper.selectByPrimaryKey("teach-1")).thenReturn(teachingClass); + when(controller.schoolProductConfigService.isTeacherRosterManaged("school-zj")).thenReturn(true); + when(controller.userInfoService.existsByUserName("20260001")).thenReturn(false); + when(controller.userinfoMapper.insertSelective(any(Userinfo.class))).thenReturn(1); + + TeacherRosterStudentImportDTO row = new TeacherRosterStudentImportDTO(); + row.setName("张三"); + row.setUsername("20260001"); + ResultEntity result = controller.importTeacherRosterRows(Collections.singletonList(row), "teach-1", "teacher-1"); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + ArgumentCaptor membership = ArgumentCaptor.forClass(TeachingClassStudent.class); + verify(controller.teachingClassStudentMapper).insertSelective(membership.capture()); + assertEquals("teach-1", membership.getValue().getTeachingClassId()); + assertEquals(null, membership.getValue().getAdminClassId()); + } +}