diff --git a/src/main/java/com/yunhai/bschclassmanage/controller/api/SchStudentController.java b/src/main/java/com/yunhai/bschclassmanage/controller/api/SchStudentController.java index 0c9fc2d..566e98f 100644 --- a/src/main/java/com/yunhai/bschclassmanage/controller/api/SchStudentController.java +++ b/src/main/java/com/yunhai/bschclassmanage/controller/api/SchStudentController.java @@ -184,6 +184,14 @@ public class SchStudentController extends BaseController { ruleList.add(rule); } + { + ImportRuleExcelColumn rule = new ImportRuleExcelColumn(); + rule.setRuleItem("密码"); + rule.setDescription("验证规则"); + rule.setContent("长度 8-32;必须同时包含字母、数字、符号;不允许出现连续自然数序列(如 1234、6789)。"); + ruleList.add(rule); + } + ExportParams params1 = new ExportParams("学生导入",comSchTreeService.getSchoolName(schId)); Map sheet1 = new HashMap<>(); diff --git a/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncUserController.java b/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncUserController.java index b3cd417..1a5ab1e 100644 --- a/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncUserController.java +++ b/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncUserController.java @@ -47,4 +47,11 @@ public class SyncUserController { List userList = OtherUtil.jsonParseList(jsonString, SyncUserDTO.class); syncUserService.syncAddOrUpdate(userList,true); } + + @SyncOpLog(SyncDataType.SYNC_MOVE_USER) + @PostMapping("moveUser") + public void SyncMoveUser(@RequestBody String jsonString) { + List userList = OtherUtil.jsonParseList(jsonString, SyncUserDTO.class); + syncUserService.syncAddOrUpdate(userList,true); + } } diff --git a/src/main/java/com/yunhai/bschclassmanage/enums/SyncDataType.java b/src/main/java/com/yunhai/bschclassmanage/enums/SyncDataType.java index b3f5cca..4b3fc2c 100644 --- a/src/main/java/com/yunhai/bschclassmanage/enums/SyncDataType.java +++ b/src/main/java/com/yunhai/bschclassmanage/enums/SyncDataType.java @@ -20,6 +20,7 @@ public enum SyncDataType SYNC_ADD_USER("Sync-AddUser","添加用户"), SYNC_EDIT_USER("Sync-UpdateUser","编辑用户"), SYNC_DELETE_USER("Sync-DeleteUser","删除用户"), + SYNC_MOVE_USER("Sync-MoveUser","用户移组"), SYNC_USER("Sync-SyncUser","全量用户"), //推送 @@ -34,7 +35,12 @@ public enum SyncDataType SEND_ADD_STUDENT("Send-AddStudent","添加学生"), SEND_EDIT_STUDENT("Send-UpdateStudent","编辑学生"), - SEND_DELETE_STUDENT("Send-DeleteStudent","删除学生"); + SEND_DELETE_STUDENT("Send-DeleteStudent","删除学生"), + + SEND_MOVE_TEACHER("Send-MoveTeacher","教师移组"), + SEND_MOVE_STUDENT("Send-MoveStudent","学生调班"), + SEND_BATCH_TEACHER("Send-BatchAddTeacher","批量添加教师"), + SEND_BATCH_STUDENT("Send-BatchAddStudent","批量添加学生"); private String key; private String name; diff --git a/src/main/java/com/yunhai/bschclassmanage/model/dto/SchStudentDTO.java b/src/main/java/com/yunhai/bschclassmanage/model/dto/SchStudentDTO.java index fa06a2e..2ef9b46 100644 --- a/src/main/java/com/yunhai/bschclassmanage/model/dto/SchStudentDTO.java +++ b/src/main/java/com/yunhai/bschclassmanage/model/dto/SchStudentDTO.java @@ -1,15 +1,18 @@ package com.yunhai.bschclassmanage.model.dto; +import com.yunhai.bschclassmanage.entity.SchStudent; import com.yunhai.bschclassmanage.model.vo.SchStudentVO; +import com.yunhai.bschclassmanage.validation.PasswordCheckable; +import com.yunhai.bschclassmanage.validation.PasswordValidation; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; @Data -@SuperBuilder @NoArgsConstructor -public class SchStudentDTO extends SchStudentVO { +@PasswordValidation +public class SchStudentDTO extends SchStudent implements PasswordCheckable { - private String pwd; + /** 密码校验见类级 @PasswordValidation:新增(id为null或<=0)必填且校验强度;更新(id>0)时密码为空跳过、非空则同样校验强度 */ + private String pwd; } diff --git a/src/main/java/com/yunhai/bschclassmanage/model/dto/SchTeacherDTO.java b/src/main/java/com/yunhai/bschclassmanage/model/dto/SchTeacherDTO.java index d19fba3..45bf6ab 100644 --- a/src/main/java/com/yunhai/bschclassmanage/model/dto/SchTeacherDTO.java +++ b/src/main/java/com/yunhai/bschclassmanage/model/dto/SchTeacherDTO.java @@ -1,6 +1,8 @@ package com.yunhai.bschclassmanage.model.dto; import com.yunhai.bschclassmanage.entity.SchTeacher; +import com.yunhai.bschclassmanage.validation.PasswordCheckable; +import com.yunhai.bschclassmanage.validation.PasswordValidation; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -8,19 +10,20 @@ import lombok.NoArgsConstructor; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; import java.util.List; @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class SchTeacherDTO extends SchTeacher { +@PasswordValidation +public class SchTeacherDTO extends SchTeacher implements PasswordCheckable { @NotNull(message = "组ID不能为空") @Min(value = 1, message = "至少选择一个组") private Integer groupId; - private String pwd; + /** 密码校验见类级 @PasswordValidation:新增(id为null或<=0)必填且校验强度;更新(id>0)时密码为空跳过、非空则同样校验强度 */ + private String pwd; } diff --git a/src/main/java/com/yunhai/bschclassmanage/model/dto/SendMoveGroupUserDTO.java b/src/main/java/com/yunhai/bschclassmanage/model/dto/SendMoveGroupUserDTO.java new file mode 100644 index 0000000..7fca922 --- /dev/null +++ b/src/main/java/com/yunhai/bschclassmanage/model/dto/SendMoveGroupUserDTO.java @@ -0,0 +1,13 @@ +package com.yunhai.bschclassmanage.model.dto; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class SendMoveGroupUserDTO { + @NotNull(message = "请选择移动教师") + private Integer userId; + @NotNull(message = "请选择移动组") + private Integer groupId; +} diff --git a/src/main/java/com/yunhai/bschclassmanage/service/ISendUserBaseService.java b/src/main/java/com/yunhai/bschclassmanage/service/ISendUserBaseService.java index dec4e3d..0b156d2 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/ISendUserBaseService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/ISendUserBaseService.java @@ -5,8 +5,7 @@ import com.yunhai.bschclassmanage.entity.SchTeacher; import com.yunhai.bschclassmanage.enums.NodeType; import com.yunhai.bschclassmanage.model.dto.SchStudentDTO; import com.yunhai.bschclassmanage.model.dto.SchTeacherDTO; -import com.yunhai.bschclassmanage.model.tool.DeleteUser; -import com.yunhai.bschclassmanage.model.tool.SyncUserDTO; +import com.yunhai.bschclassmanage.model.dto.SendMoveGroupUserDTO; import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.platform.utils.model.BoaUser; import org.springframework.stereotype.Service; @@ -35,4 +34,18 @@ public interface ISendUserBaseService { void deleteTeachers(List userIds,Integer groupId,Integer schId); void deleteStudents(List userIds,Integer groupId,Integer schId); + + /** 导入失败回滚:删除已推送成功的学生平台账号(syncedList 与 groupIds 按下标一一对应) */ + void rollbackSyncedStudents(List syncedList, List groupIds, Integer schId); + + /** 导入失败回滚:删除已推送成功的教师平台账号(syncedList 与 groupIds 按下标一一对应) */ + void rollbackSyncedTeachers(List syncedList, List groupIds, Integer schId); + + void moveTeacherToGroup(List teacherList, Integer schId); + + void moveStudentToGroup(List stuIds,Integer classId,Integer schId); + + List batchAddStudent(List stuList); + + List batchAddTeacher(List teacherList); } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/SchStudentService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/SchStudentService.java index 92dedd0..68ff7fc 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/SchStudentService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/SchStudentService.java @@ -15,7 +15,6 @@ import com.yunhai.bschclassmanage.utils.UserContext; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -90,8 +89,8 @@ public class SchStudentService implements ISchStudentService { @OperationLog(module = LogModule.STUDENT) @Transactional(rollbackFor = Exception.class) @Override - public void saveStudent(UserContext.UserInfo user, SchStudentDTO entityVO) { - infoService.saveStudent(user, entityVO); + public void saveStudent(UserContext.UserInfo user, SchStudentDTO entityDTO) { + infoService.saveStudent(user, entityDTO); } @Transactional(rollbackFor = Exception.class) diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/com/SendUserBaseService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/com/SendUserBaseService.java index 1f9277d..fd6692d 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/com/SendUserBaseService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/com/SendUserBaseService.java @@ -6,13 +6,13 @@ import com.yunhai.bschclassmanage.enums.NodeType; import com.yunhai.bschclassmanage.enums.UserRole; import com.yunhai.bschclassmanage.annotation.SyncOpLog; import com.yunhai.bschclassmanage.enums.SyncDataType; +import com.yunhai.bschclassmanage.model.dto.SchStudentDTO; +import com.yunhai.bschclassmanage.model.dto.SchTeacherDTO; +import com.yunhai.bschclassmanage.model.dto.SendMoveGroupUserDTO; import com.yunhai.bschclassmanage.service.IComSchTreeService; import com.yunhai.bschclassmanage.service.ISendUserBaseService; import com.yunhai.platform.openapi.client.YschBaseOpenApi; -import com.yunhai.platform.openapi.model.SysGroupMoveRequest; -import com.yunhai.platform.openapi.model.SysGroupRequest; -import com.yunhai.platform.openapi.model.SysUserGroupDeleteItem; -import com.yunhai.platform.openapi.model.SysUserRequest; +import com.yunhai.platform.openapi.model.*; import com.yunhai.platform.utils.exception.DException; import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.platform.utils.model.BoaUser; @@ -181,23 +181,120 @@ public class SendUserBaseService implements ISendUserBaseService { } } -// public void moveTeachertToGroup(List teacherList){ -// for (SchTeacherDTO teacher :teacherList) { -// SysUserMoveRequest move=new SysUserMoveRequest(); -// move.setUserId(); -// move.setGroupId(); -// move.setSchoolId(); -// YschBaseOpenApi.moveUserToGroup(teacher.getTeaId(),teacher.getGroupId()); -// } -// } -// -// public void moveStudentToGroup(List teacherList){ -// for (SchTeacherDTO teacher :teacherList) { -// SysUserMoveRequest move=new SysUserMoveRequest(); -// move.setUserId(); -// move.setGroupId(); -// move.setSchoolId(); -// YschBaseOpenApi.moveUserToGroup(teacher.getTeaId(),teacher.getGroupId()); -// } -// } + /** + * 导入失败回滚:删除已成功推送到平台的学生账号,避免孤儿账号。 + * syncedList 与 groupIds 按下标一一对应(groupId 取自主组 classId)。 + */ + public void rollbackSyncedStudents(List syncedList, List groupIds, Integer schId) { + if (syncedList == null || syncedList.isEmpty()) { + return; + } + int n = Math.min(syncedList.size(), groupIds == null ? 0 : groupIds.size()); + for (int i = 0; i < n; i++) { + BoaUser boaUser = syncedList.get(i); + if (boaUser == null) { + continue; + } + Integer groupId = groupIds.get(i); + if (groupId != null) { + deleteStudents(Collections.singletonList(boaUser.getId()), groupId, schId); + } + } + } + + /** + * 导入失败回滚:删除已成功推送到平台的教师账号,避免孤儿账号。 + * syncedList 与 groupIds 按下标一一对应(groupId 取自主组)。 + */ + public void rollbackSyncedTeachers(List syncedList, List groupIds, Integer schId) { + if (syncedList == null || syncedList.isEmpty()) { + return; + } + int n = Math.min(syncedList.size(), groupIds == null ? 0 : groupIds.size()); + for (int i = 0; i < n; i++) { + BoaUser boaUser = syncedList.get(i); + if (boaUser == null) { + continue; + } + Integer groupId = groupIds.get(i); + if (groupId != null) { + deleteTeachers(Collections.singletonList(boaUser.getId()), groupId, schId); + } + } + } + + //移动教师 + @SyncOpLog(SyncDataType.SEND_MOVE_TEACHER) + public void moveTeacherToGroup(List teacherList,Integer schId){ + for (SendMoveGroupUserDTO teacher :teacherList) { + SysUserMoveRequest move=new SysUserMoveRequest(); + move.setUserId(teacher.getUserId()); + move.setGroupId(teacher.getGroupId()); + YschBaseOpenApi.moveUserToGroup(move); + } + } + + //学生调班 + @SyncOpLog(SyncDataType.SEND_MOVE_STUDENT) + public void moveStudentToGroup(List stuIds,Integer classId,Integer schId){ + for (Integer stuId :stuIds) { + SysUserMoveRequest move=new SysUserMoveRequest(); + move.setUserId(stuId); + move.setGroupId(classId); + move.setSchoolId(schId); + YschBaseOpenApi.moveUserToGroup(move); + } + } + + @SyncOpLog(SyncDataType.SEND_BATCH_STUDENT) + public List batchAddStudent(List stuList){ + if (stuList == null || stuList.isEmpty()) { + return new ArrayList<>(); + } + List requests = new ArrayList<>(); + for (SchStudentDTO stu : stuList) { + SysUserRequest sysUser = new SysUserRequest(); + sysUser.setAccount(stu.getAccount()); + sysUser.setRealName(stu.getName()); + sysUser.setGender(stu.getGender()); + sysUser.setIdcard(stu.getIdCard()); + sysUser.setGroupId(stu.getGroupId()); + sysUser.setRoles(Arrays.asList(UserRole.STU_USER.getValue())); + String pwd = stu.getPwd(); + if (StringUtils.isNotBlank(pwd)) { + sysUser.setPassword(pwd); + sysUser.setConfirmPassword(pwd); + } + requests.add(sysUser); + } + return YschBaseOpenApi.batchAddUsers(requests); + } + + @SyncOpLog(SyncDataType.SEND_BATCH_TEACHER) + public List batchAddTeacher(List teacherList) + { + if (teacherList == null || teacherList.isEmpty()) { + return new ArrayList<>(); + } + List requests = new ArrayList<>(); + for (SchTeacherDTO teacher : teacherList) { + SysUserRequest sysUser = new SysUserRequest(); + sysUser.setAccount(teacher.getAccount()); + sysUser.setRealName(teacher.getName()); + sysUser.setGender(teacher.getGender()); + sysUser.setIdcard(teacher.getIdCard()); + sysUser.setPhone(teacher.getTel()); + sysUser.setGroupId(teacher.getGroupId()); + sysUser.setRoles(Arrays.asList(UserRole.TEACHER_USER.getValue())); + String pwd = teacher.getPwd(); + if (StringUtils.isNotBlank(pwd)) { + sysUser.setPassword(pwd); + sysUser.setConfirmPassword(pwd); + } + requests.add(sysUser); + } + return YschBaseOpenApi.batchAddUsers(requests); + } + + } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentImportService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentImportService.java index 6bd16c3..b555fd1 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentImportService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentImportService.java @@ -12,6 +12,7 @@ import com.yunhai.bschclassmanage.enums.StuAbnormalStatus; import com.yunhai.bschclassmanage.enums.TransferStatus; import com.yunhai.bschclassmanage.enums.UserGender; import com.yunhai.bschclassmanage.enums.UserStatus; +import com.yunhai.bschclassmanage.model.dto.SchStudentDTO; import com.yunhai.bschclassmanage.model.excel.ErrorExcelColumn; import com.yunhai.bschclassmanage.model.excel.StudentClassNoExcelColumn; import com.yunhai.bschclassmanage.model.excel.StudentExcelColumn; @@ -20,10 +21,13 @@ import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.service.IBaseSchsystemGradeService; import com.yunhai.bschclassmanage.service.IComSchTreeService; import com.yunhai.bschclassmanage.service.ISchClassService; +import com.yunhai.bschclassmanage.service.ISendUserBaseService; import com.yunhai.bschclassmanage.utils.EasyPoiUtils; import com.yunhai.bschclassmanage.utils.UserContext; import com.yunhai.bschclassmanage.utils.ValidateUtil; import com.yunhai.bschclassmanage.utils.ValidationUtil; +import com.yunhai.platform.openapi.client.YschBaseOpenApi; +import com.yunhai.platform.utils.model.BoaUser; import com.yunhai.platform.utils.exception.DException; import com.yunhai.platform.utils.exception.ErrorCode; import lombok.extern.slf4j.Slf4j; @@ -59,6 +63,10 @@ public class SchStudentImportService { @Autowired private IComSchTreeService comSchTreeService; + + @Autowired + private ISendUserBaseService sendUserBaseService; + @Autowired private SchStudentCacheService cacheService; @@ -77,6 +85,9 @@ public class SchStudentImportService { Integer userId = user.getUserId(); Date now = new Date(); + // 提到 try 外,供 catch 回滚时访问 + List saveStudentList = new ArrayList<>(); + List syncStudentList = null; // 使用 try-with-resources 确保流关闭 try (InputStream is = file.getInputStream()) { // 1. 加载基础数据(放在 try 内,若加载失败也回滚) @@ -109,7 +120,6 @@ public class SchStudentImportService { } // 4. 业务校验与转换 - List saveStudentList = new ArrayList<>(); int currentRow = params.getTitleRows() + params.getHeadRows() + 1; // 从第3行开始 for (StudentExcelColumn item : list) { @@ -122,8 +132,7 @@ public class SchStudentImportService { continue; } } - SchStudent stu = new SchStudent(); - + SchStudentDTO stu = new SchStudentDTO(); // 账号 { Optional accountOpt = schStudentRepository.findByAccount(item.getAccount()); @@ -133,10 +142,12 @@ public class SchStudentImportService { "禁止将" + comSchTreeService.getSchoolName(accountOpt.get().getSchId()) + "正常学生导入!")); continue; } else { - BeanUtils.copyProperties(accountOpt.get(), stu); - stu.setUpdateDate(now); - stu.setUpdateUserId(userId); - oldClassId = accountOpt.get().getClassId(); + errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getStuName(),"学生账号已存在!")); + continue; +// BeanUtils.copyProperties(accountOpt.get(), stu); +// stu.setUpdateDate(now); +// stu.setUpdateUserId(userId); +// oldClassId = accountOpt.get().getClassId(); } } else { stu.setId(0); @@ -190,7 +201,7 @@ public class SchStudentImportService { } // 密码(暂时忽略,或可设置默认密码,但原代码无处理) - + stu.setPwd(item.getPassword()); // 身份证 if (StringUtils.isNotBlank(item.getIdCard())) { if (!ValidateUtil.isValidIdCard(item.getIdCard())) { @@ -250,19 +261,36 @@ public class SchStudentImportService { throw new DException(ErrorCode.CUSTOM,"导入失败,请检查错误列表",errorMsgArr); } - // 6. 批量保存 - schStudentRepository.saveAll(saveStudentList); + // 6. 先推送平台用户(返回平台用户ID),顺序与 saveStudentList 一致 + syncStudentList = sendUserBaseService.batchAddStudent(saveStudentList); + // 按顺序回填平台用户ID(stuId),batchAddUsers 返回顺序与传入一致 + for (int i = 0; i < saveStudentList.size(); i++) { + if (i < syncStudentList.size() && syncStudentList.get(i) != null) { + saveStudentList.get(i).setStuId(syncStudentList.get(i).getId()); + } + } - // 7. 异步记录导入日志 - logService.importLogs(user, saveStudentList, gradeList, schClassList); + // 7. DTO 不能直接传给 saveAll(泛型不变性:List 非 List),转实体并保留已回填的 stuId + List studentEntities = saveStudentList.stream().map(dto -> { + SchStudent s = new SchStudent(); + BeanUtils.copyProperties(dto, s); + return s; + }).collect(Collectors.toList()); + List savedStudentList = schStudentRepository.saveAll(studentEntities); - // 8. 清除受影响的班级缓存 + // 8. 异步记录导入日志 + logService.importLogs(user, savedStudentList, gradeList, schClassList); + + // 9. 清除受影响的班级缓存 cacheService.clearClassStudentCaches(schId, classIds); } catch (DException e) { throw e; } catch (Exception e) { - // 其他所有未预期的异常(包括数据库、IO等) + // 导入失败:回滚已成功推送到平台的学生账号,避免孤儿账号 + List groupIds = saveStudentList.stream() + .map(SchStudentDTO::getGroupId).collect(Collectors.toList()); + sendUserBaseService.rollbackSyncedStudents(syncStudentList, groupIds, schId); log.error("导入班级学生时发生系统异常,操作人:" + user.getUserName(), e); throw new DException(ErrorCode.INTERNAL_SERVER_ERROR,"导入失败,请联系管理员",null,e); } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentLogService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentLogService.java index 9ece00b..a2dfdd6 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentLogService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentLogService.java @@ -1,6 +1,7 @@ package com.yunhai.bschclassmanage.service.impl.student; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SimplePropertyPreFilter; import com.yunhai.bschclassmanage.Repository.SchClassRepository; import com.yunhai.bschclassmanage.entity.OperationLogRecord; import com.yunhai.bschclassmanage.entity.SchClass; @@ -8,6 +9,7 @@ import com.yunhai.bschclassmanage.entity.SchStudent; import com.yunhai.bschclassmanage.enums.StuAbnormalStatus; import com.yunhai.bschclassmanage.enums.TransferStatus; import com.yunhai.bschclassmanage.model.constant.LogModule; +import com.yunhai.bschclassmanage.model.dto.SchStudentDTO; import com.yunhai.bschclassmanage.model.vo.BaseSchClassVO; import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.service.IBaseSchsystemGradeService; @@ -37,6 +39,13 @@ public class SchStudentLogService { @Autowired private ILogService logService; + /** 学生对象序列化时排除敏感字段(密码),防止明文进入日志内容/调试日志 */ + private static final SimplePropertyPreFilter NO_PWD_FILTER = new SimplePropertyPreFilter(); + + static { + NO_PWD_FILTER.getExcludes().add("pwd"); + } + @Autowired private ISchClassService schClassService; @@ -387,7 +396,7 @@ public class SchStudentLogService { if (!logRecords.isEmpty()) { logService.saveAllLogAsync(logRecords); } - log.debug("操作人:{},删除学生日志:{}", user.getUserName(), JSON.toJSONString(students)); + log.debug("操作人:{},删除学生日志:{}", user.getUserName(), JSON.toJSONString(students, NO_PWD_FILTER)); } catch (Exception e) { log.error("记录批量删除学生日志失败", e); } @@ -414,10 +423,10 @@ public class SchStudentLogService { .map(SelectIntVO::getName) .findFirst() .orElse("未知年级"); - content = "导入学生[" + stu.getName() + "]成功,用户信息:" + JSON.toJSONString(stu) + content = "导入学生[" + stu.getName() + "]成功,用户信息:" + JSON.toJSONString(stu, NO_PWD_FILTER) + ",所在年级班级:" + gradeName + schClassOpt.get().getName(); } else { - content = "导入学生[" + stu.getName() + "]成功,用户信息:" + JSON.toJSONString(stu) + ",班级:未知"; + content = "导入学生[" + stu.getName() + "]成功,用户信息:" + JSON.toJSONString(stu, NO_PWD_FILTER) + ",班级:未知"; } OperationLogRecord log = logService.buildLogRecord(user, MODULE_STUDENT, content, now); logs.add(log); @@ -425,7 +434,7 @@ public class SchStudentLogService { if (!logs.isEmpty()) { logService.saveAllLogAsync(logs); } - log.debug("操作人:{},学生导入日志成功: {}", user.getUserName(), JSON.toJSONString(saveStudentList)); + log.debug("操作人:{},学生导入日志成功: {}", user.getUserName(), JSON.toJSONString(saveStudentList, NO_PWD_FILTER)); } catch (Exception e) { log.error("操作人:" + user.getUserName() + ",学生导入日志失败: {}", e.getMessage(), e); } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentTransferService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentTransferService.java index c64347c..366ab45 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentTransferService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/student/SchStudentTransferService.java @@ -5,6 +5,7 @@ import com.yunhai.bschclassmanage.Repository.SchStudentRepository; import com.yunhai.bschclassmanage.entity.SchStudent; import com.yunhai.bschclassmanage.enums.TransferStatus; import com.yunhai.bschclassmanage.model.dto.StuTransferClassDTO; +import com.yunhai.bschclassmanage.service.ISendUserBaseService; import com.yunhai.bschclassmanage.utils.RedisConstants; import com.yunhai.bschclassmanage.utils.RedisUtil; import com.yunhai.bschclassmanage.utils.UserContext; @@ -34,6 +35,9 @@ public class SchStudentTransferService { @Autowired private SchStudentCacheService cacheService; + @Autowired + private ISendUserBaseService sendUserBaseService; + @Autowired private SchStudentLogService logService; @@ -93,6 +97,8 @@ public class SchStudentTransferService { // 清除缓存(新旧班级都要清除) cacheService.clearClassStudentCaches(schId, affectedClassIds); + sendUserBaseService.moveStudentToGroup(model.getStuIds(),model.getTransferClassId(),schId); + // 异步记录操作日志 logService.transferClassLogs(user, students, targetClassId, affectedClassIds); } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/teacher/SchTeacherImportService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/teacher/SchTeacherImportService.java index 3c395a5..2da598c 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/teacher/SchTeacherImportService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/teacher/SchTeacherImportService.java @@ -9,15 +9,18 @@ import com.yunhai.bschclassmanage.entity.SchTeacherGroup; import com.yunhai.bschclassmanage.enums.TransferStatus; import com.yunhai.bschclassmanage.enums.UserGender; import com.yunhai.bschclassmanage.enums.UserStatus; +import com.yunhai.bschclassmanage.model.dto.SchTeacherDTO; import com.yunhai.bschclassmanage.model.excel.ErrorExcelColumn; import com.yunhai.bschclassmanage.model.excel.TeacherExcelColumn; import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.service.IComSchTreeService; +import com.yunhai.bschclassmanage.service.ISendUserBaseService; import com.yunhai.bschclassmanage.utils.UserContext; import com.yunhai.bschclassmanage.utils.ValidateUtil; import com.yunhai.bschclassmanage.utils.ValidationUtil; import com.yunhai.platform.utils.exception.DException; import com.yunhai.platform.utils.exception.ErrorCode; +import com.yunhai.platform.utils.model.BoaUser; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; @@ -44,6 +47,10 @@ public class SchTeacherImportService { @Autowired private IComSchTreeService comSchTreeService; + + @Autowired + private ISendUserBaseService sendUserBaseService; + @Autowired private SchTeacherCacheService cacheService; @@ -59,6 +66,9 @@ public class SchTeacherImportService { Integer userId = user.getUserId(); Date now = new Date(); + // 提到 try 外,供 catch 回滚时访问 + List teachers = new ArrayList<>(); + List syncTeacherList = null; try { List teaGroupNameList = comSchTreeService.getTeaGroupNameList(schId, null); List list = ExcelImportUtil.importExcel(file.getInputStream(), TeacherExcelColumn.class, params); @@ -69,10 +79,9 @@ public class SchTeacherImportService { List teachersByAccounts = getTeachersByAccounts(accountList); List schTeacherList = cacheService.getSchTeacherList(schId); - List teachers = new ArrayList<>(); int rows = 0; for (TeacherExcelColumn item : list) { - SchTeacher teacher = new SchTeacher(); + SchTeacherDTO teacher = new SchTeacherDTO(); List teacherGroupList = new ArrayList<>(); rows += 2; @@ -85,14 +94,17 @@ public class SchTeacherImportService { } Optional schTeacherOpt = teachersByAccounts.stream().filter(s -> s.getAccount().equals(item.getAccount())).findFirst(); if (schTeacherOpt.isPresent()) { - BeanUtils.copyProperties(schTeacherOpt.get(), teacher); - teacher.setUpdateDate(now); - teacher.setUpdateUserId(userId); +// BeanUtils.copyProperties(schTeacherOpt.get(), teacher); +// teacher.setUpdateDate(now); +// teacher.setUpdateUserId(userId); + errorMsgArr.add(new ErrorExcelColumn(rows, item.getAccount(), TeacherExcelColumn.ACCOUNT + "教师已存在!")); + continue; } else { teacher.setCreateDate(now); teacher.setCreateUserId(userId); teacher.setAccount(item.getAccount()); teacher.setStatus(UserStatus.NORMAL.getId()); + teacher.setPwd(item.getPassword()); } } //姓名 @@ -133,6 +145,10 @@ public class SchTeacherImportService { teacherGroupList.add(teacherGroup); } teacher.setGroupList(teacherGroupList); + // 主组取第一个组(DTO.groupId 有 @NotNull/@Min 校验,平台推送 batchAddTeacher 也需要主组) + if (!teacherGroupList.isEmpty()) { + teacher.setGroupId(teacherGroupList.get(0).getGroupId()); + } } //身份证 { @@ -190,13 +206,30 @@ public class SchTeacherImportService { throw new DException(ErrorCode.CUSTOM,"导入失败,请检查错误列表",errorMsgArr); } - List saveList = schTeacherRepository.saveAll(teachers); + // 推送平台用户:必须用原 DTO 列表(含 pwd);saveAll 返回的实体不含密码 + syncTeacherList = sendUserBaseService.batchAddTeacher(teachers); + // 按顺序回填平台用户ID(batchAddUsers 返回顺序与传入一致) + for (int i = 0; i < teachers.size(); i++) { + if (i < syncTeacherList.size() && syncTeacherList.get(i) != null) { + teachers.get(i).setTeaId(syncTeacherList.get(i).getId()); + } + } + + // DTO 不能直接传给 saveAll(泛型不变性:List 非 List),需转实体并保留已回填的 teaId/groupList + List teacherEntities = teachers.stream().map(dto -> { + SchTeacher t = new SchTeacher(); + BeanUtils.copyProperties(dto, t); + return t; + }).collect(Collectors.toList()); + List saveList = schTeacherRepository.saveAll(teacherEntities); + + List schTeacherGroupList = new ArrayList<>(); for (SchTeacher teacher : saveList) { - teacher.setTeaId(teacher.getId()); + // teaId 已是平台推送返回的用户ID(batchAddTeacher 已回填),勿用本地 id 覆盖 for (SchTeacherGroup group : teacher.getGroupList()) { - group.setTeaId(teacher.getId()); + group.setTeaId(teacher.getTeaId()); } schTeacherGroupList.addAll(teacher.getGroupList()); } @@ -213,9 +246,16 @@ public class SchTeacherImportService { // 清除缓存 cacheService.clearTeacherCache(schId); + + + } catch (DException e) { throw e; } catch (Exception e) { + // 导入失败:回滚已成功推送到平台的教师账号,避免孤儿账号 + List groupIds = teachers.stream() + .map(SchTeacherDTO::getGroupId).collect(Collectors.toList()); + sendUserBaseService.rollbackSyncedTeachers(syncTeacherList, groupIds, schId); log.error("操作人:" + user.getUserName() + ",导入教师日志失败: {}", e.getMessage(), e); throw new DException("导入失败,请联系管理员!
错误详情:
" + e.getMessage()); } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/teacher/SchTeacherRelationService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/teacher/SchTeacherRelationService.java index 47964f8..dd0dffb 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/teacher/SchTeacherRelationService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/teacher/SchTeacherRelationService.java @@ -6,9 +6,11 @@ import com.yunhai.bschclassmanage.Repository.SchTeacherRepository; import com.yunhai.bschclassmanage.entity.SchTeacher; import com.yunhai.bschclassmanage.entity.SchTeacherGroup; import com.yunhai.bschclassmanage.enums.TransferStatus; +import com.yunhai.bschclassmanage.model.dto.SendMoveGroupUserDTO; import com.yunhai.bschclassmanage.model.dto.TeaTransferGroupDTO; import com.yunhai.bschclassmanage.model.dto.TeaTransferInGroupDTO; import com.yunhai.bschclassmanage.service.IComSchTreeService; +import com.yunhai.bschclassmanage.service.ISendUserBaseService; import com.yunhai.bschclassmanage.utils.OtherUtil; import com.yunhai.bschclassmanage.utils.UserContext; import com.yunhai.platform.utils.exception.DException; @@ -42,6 +44,9 @@ public class SchTeacherRelationService { @Autowired private SchTeacherCacheService cacheService; + @Autowired + private ISendUserBaseService sendUserBaseService; + @Autowired private SchTeacherLogService logService; // 异步日志 @@ -256,6 +261,17 @@ public class SchTeacherRelationService { schTeacherGroupRepository.saveAll(newRelations); } + List teacherList = newRelations.stream() + .map(rel -> { + SendMoveGroupUserDTO dto = new SendMoveGroupUserDTO(); + dto.setUserId(rel.getTeaId()); + dto.setGroupId(rel.getGroupId()); + return dto; + }) + .collect(Collectors.toList()); + + sendUserBaseService.moveTeacherToGroup(teacherList,schId); + // 清除缓存 cacheService.clearTeacherCache(schId); }