diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/SchStudentRepository.java b/src/main/java/com/yunhai/bschclassmanage/Repository/SchStudentRepository.java index 3143981..4e8b5b3 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/SchStudentRepository.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/SchStudentRepository.java @@ -45,6 +45,11 @@ public interface SchStudentRepository extends JpaRepository boolean existsBySchIdAndClassIdAndNameAndIdNot(Integer schId,Integer classId, String name, Integer id); + /** + * 判断同一班级内是否已存在相同学号的学生(排除自身,用于学号导入时的重复校验) + */ + boolean existsBySchIdAndClassIdAndStuClassNoAndIdNot(Integer schId, Integer classId, String stuClassNo, Integer id); + @Modifying(clearAutomatically = true) @Query("UPDATE SchStudent s SET s.classId = :newClassId, s.groupId = :newClassId, s.updateDate = :updateDate, s.updateUserId = :updateUserId WHERE s.id IN (:ids) AND s.schId = :schId") int batchUpdateClassId(@Param("ids") List ids, 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 714fd4a..e065522 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 @@ -346,6 +346,47 @@ public class SchStudentImportService { return errors; } + /** + * 校验学号导入数据中的重复项(账号、学号) + * @param importList 导入数据列表 + * @return 错误信息列表 + */ + private List validateClassNoDuplicates(List importList) { + List errors = new ArrayList<>(); + + // 1. 校验 "账号" 在本次导入中是否重复(避免同一学生被重复处理) + Map accountCountMap = importList.stream() + .filter(dto -> StringUtils.isNotBlank(dto.getAccount())) + .collect(Collectors.groupingBy( + dto -> dto.getAccount(), + Collectors.counting() + )); + + accountCountMap.entrySet().stream() + .filter(entry -> entry.getValue() > 1) + .forEach(entry -> errors.add( + String.format("账号 '%s' 在表格中出现了 %d 次,请检查并去重", + entry.getKey(), entry.getValue()) + )); + + // 2. 校验 "学号" 在本次导入中是否重复 + Map classNoCountMap = importList.stream() + .filter(dto -> StringUtils.isNotBlank(dto.getClazzNo())) + .collect(Collectors.groupingBy( + dto -> dto.getClazzNo(), + Collectors.counting() + )); + + classNoCountMap.entrySet().stream() + .filter(entry -> entry.getValue() > 1) + .forEach(entry -> errors.add( + String.format("学号 '%s' 在表格中出现了 %d 次,请检查并去重", + entry.getKey(), entry.getValue()) + )); + + return errors; + } + public void studentClassNoTemplate(HttpServletResponse response, Integer schId,Integer systemId,Integer gradeId,Integer classId){ @@ -419,9 +460,17 @@ public class SchStudentImportService { throw new DException("上传的 Excel 中没有有效数据行,请检查后重新上传!"); } + // Excel数据重复验证(账号、学号) + { + List validateDuplicatesMsgs = validateClassNoDuplicates(list); + if (validateDuplicatesMsgs.size() > 0) { + errorMsgArr.add(new ErrorExcelColumn(0, "数据重复", String.join("\r\n", validateDuplicatesMsgs))); + } + } + // 4. 业务校验与转换 List saveStudentList = new ArrayList<>(); - int currentRow = params.getTitleRows() + params.getHeadRows() ; // 从第2行开始 + int currentRow = params.getTitleRows() + params.getHeadRows() + 1; // 第一行数据从第2行开始 for (StudentClassNoExcelColumn item : list) { // Excel数据验证 @@ -429,6 +478,7 @@ public class SchStudentImportService { List validateMsgs = ValidationUtil.validateMsgs(item); if (validateMsgs.size() > 0) { errorMsgArr.add(new ErrorExcelColumn(currentRow, "导入规则", String.join("\r\n", validateMsgs))); + currentRow++; continue; } } @@ -438,17 +488,22 @@ public class SchStudentImportService { { Optional accountOpt = schStudentRepository.findByAccount(item.getAccount()); if (accountOpt.isPresent()) { - if (!accountOpt.get().getSchId().equals(schId) && accountOpt.get().getTransferStatus().equals(TransferStatus.IN)) { + SchStudent existing = accountOpt.get(); + // 跨校防护(空安全):账号属于其他学校时一律拒绝,避免修改其他学校的学生数据 + if (!schId.equals(existing.getSchId())) { + String schoolName = existing.getSchId() == null ? "未知学校" + : comSchTreeService.getSchoolName(existing.getSchId()); errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getStuName(), - "禁止将" + comSchTreeService.getSchoolName(accountOpt.get().getSchId()) + "正常学生导入!")); + "禁止将" + schoolName + "学生导入!")); + currentRow++; continue; - } else { - BeanUtils.copyProperties(accountOpt.get(), stu); - stu.setUpdateDate(now); - stu.setUpdateUserId(userId); } + BeanUtils.copyProperties(existing, stu); + stu.setUpdateDate(now); + stu.setUpdateUserId(userId); } else { errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getAccount(),item.getStuName()+"学生账号未找到!")); + currentRow++; continue; } } @@ -465,10 +520,12 @@ public class SchStudentImportService { gradeId2 = gradeIdOpt.get(); if(gradeId!=null&&!gradeId2.equals(gradeId)){ errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getGradeName(), StudentExcelColumn.GRADE_NAME + "参数年级与模板年级不一致!")); + currentRow++; continue; } } else { errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getGradeName(), StudentExcelColumn.GRADE_NAME + "不存在!")); + currentRow++; continue; } } @@ -482,20 +539,35 @@ public class SchStudentImportService { if(classId!=null){ if(!schClassOpt.get().getClassId().equals(classId)){ errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getClazzName(), StudentExcelColumn.CLAZZ_NAME + "参数班级与模板班级不一致!")); + currentRow++; continue; } - + } + // 学生当前班级必须与模板班级一致,避免改错其他班级学生的学号 + if (!Objects.equals(stu.getClassId(), schClassOpt.get().getClassId())) { + errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getClazzName(), "学生当前班级与模板班级不一致!")); + currentRow++; + continue; } } else { errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getClazzName(), StudentExcelColumn.CLAZZ_NAME + "不存在!")); + currentRow++; continue; } } - // 学号 + // 学号(为空时保留原学号,避免误清空) { - stu.setStuClassNo(item.getClazzNo()); if (StringUtils.isNotBlank(item.getClazzNo())) { + // 同班级内学号重复校验(排除学生自身) + boolean classNoExists = schStudentRepository.existsBySchIdAndClassIdAndStuClassNoAndIdNot( + schId, stu.getClassId(), item.getClazzNo(), stu.getId()); + if (classNoExists) { + errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getClazzNo(), "该班级已存在相同学号的学生!")); + currentRow++; + continue; + } + stu.setStuClassNo(item.getClazzNo()); try { String digits = item.getClazzNo().replaceAll("\\D+", ""); if (StringUtils.isNotBlank(digits)) { @@ -507,6 +579,8 @@ public class SchStudentImportService { } } saveStudentList.add(stu); + classIds.add(stu.getClassId()); + currentRow++; } // 5. 若有错误,抛出异常(携带明细) 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 a2dfdd6..96b8d79 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 @@ -450,7 +450,7 @@ public class SchStudentLogService { LocalDateTime now = LocalDateTime.now(); List logs = new ArrayList<>(); for (SchStudent stu : saveStudentList) { - String content = ""; + String content; Optional schClassOpt = schClassList.stream() .filter(s -> s.getClassId().equals(stu.getClassId())) .findFirst(); @@ -461,8 +461,12 @@ public class SchStudentLogService { .map(SelectIntVO::getName) .findFirst() .orElse("未知年级"); - content = "导入学生[" + stu.getName() + "]学号["+stu.getStuClassNo()+"]】成功,用户信息:" + content = "导入学生[" + stu.getName() + "]学号[" + stu.getStuClassNo() + "]成功,用户信息:" + + JSON.toJSONString(stu, NO_PWD_FILTER) + ",所在年级班级:" + gradeName + schClassOpt.get().getName(); + } else { + content = "导入学生[" + stu.getName() + "]学号[" + stu.getStuClassNo() + "]成功,用户信息:" + + JSON.toJSONString(stu, NO_PWD_FILTER) + ",班级:未知"; } OperationLogRecord log = logService.buildLogRecord(user, MODULE_STUDENT, content, now); logs.add(log); @@ -470,6 +474,7 @@ public class SchStudentLogService { if (!logs.isEmpty()) { logService.saveAllLogAsync(logs); } + log.debug("操作人:{},学生学号导入日志成功: {}", user.getUserName(), JSON.toJSONString(saveStudentList, NO_PWD_FILTER)); } catch (Exception e) { log.error("操作人:" + user.getUserName() + ",学生学号导入日志失败: {}", e.getMessage(), e); }