diff --git a/src/main/java/com/yunhai/bschclassmanage/service/ISendUserBaseService.java b/src/main/java/com/yunhai/bschclassmanage/service/ISendUserBaseService.java index 0b156d2..a78d89c 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/ISendUserBaseService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/ISendUserBaseService.java @@ -41,6 +41,9 @@ public interface ISendUserBaseService { /** 导入失败回滚:删除已推送成功的教师平台账号(syncedList 与 groupIds 按下标一一对应) */ void rollbackSyncedTeachers(List syncedList, List groupIds, Integer schId); + /** 导入失败回滚:删除已成功推送到平台的组(年级组/班级组/学段组),避免留下孤儿组 */ + void rollbackCreatedGroups(List groupIds, Integer schId); + void moveTeacherToGroup(List teacherList, Integer schId); void moveStudentToGroup(List stuIds,Integer classId,Integer schId); diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/classes/SchClassImportService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/classes/SchClassImportService.java index c699236..d0f3d32 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/classes/SchClassImportService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/classes/SchClassImportService.java @@ -108,6 +108,10 @@ public class SchClassImportService { schsystemIdByAcademicStage.put(schsystemId,stuParentId); } } + + Map> schGradeGroupMap = comSchTreeService.getSchGroupTypeList(schId, NodeType.GRADE.getId()); + Map> schClassGroupMap = comSchTreeService.getSchGroupTypeList(schId, NodeType.CLASS.getId()); + try (InputStream is = file.getInputStream()) { List schTeacherList = schTeacherService.getSchTeacherList(schId); List gradeList = baseSchsystemGradeService.getGradeSelect(schId, null, null); @@ -147,13 +151,15 @@ public class SchClassImportService { Map nowYearGradeMap = baseSchsystemGradeService.getNowYearGradeMap(user.getSchId(), schsystemId); - Map> schGradeGroupMap = comSchTreeService.getSchGroupTypeList(schId, NodeType.GRADE.getId()); - Map> schClassGroupMap = comSchTreeService.getSchGroupTypeList(schId, NodeType.CLASS.getId()); List gradeGroupList = schGradeGroupMap.get(schsystemId); List classGroupList = schClassGroupMap.get(schsystemId); Integer year = nowYearGradeMap.get(gradeId); + if (year == null) { + errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getGradeName(), "该年级未匹配到入学年份,无法导入!")); + continue; + } Integer sort = OtherUtil.extractClassNumber(item.getClazzName()); String clazzName = sort + "班"; @@ -197,25 +203,26 @@ public class SchClassImportService { clazz.setGroupId(parentId); // 班级组推送平台,classId 取平台组 ID(与 addBaseSchClass 一致);同理防重复创建 + // classCacheKey 需带 schsystemId:多学制下不同学段可能同一年份+班号(如初中/高中都有 202501), + // 若只用 code 作 key,会导致第二个学制的班级复用第一个学制的平台组,造成班级组缺失。 + String classCacheKey = schsystemId + "_" + code; Integer existClassId = (classGroupList == null) ? null : classGroupList.stream() .filter(s -> Objects.equals(code, s.getName())) .map(SelectIntVO::getId).findFirst().orElse(null); - if (existClassId == null) { - existClassId = createdClassCache.get(code); + if (existClassId == null) { + existClassId = createdClassCache.get(classCacheKey); + } + if (existClassId != null && existClassId > 0) { + clazz.setClassId(existClassId); + } else { + BoaGroup classGroup = sendUserBaseService.addGroup(code, parentId, sort, NodeType.CLASS, schId); + clazz.setClassId(classGroup.getId()); + createdGroupIds.add(classGroup.getId()); + createdClassCache.put(classCacheKey, classGroup.getId()); + } } - if (existClassId != null && existClassId > 0) { - clazz.setClassId(existClassId); - } else { - BoaGroup classGroup = sendUserBaseService.addGroup(code, parentId, sort, NodeType.CLASS, schId); - clazz.setClassId(classGroup.getId()); - createdGroupIds.add(classGroup.getId()); - createdClassCache.put(code, classGroup.getId()); - } - } - clazz.setCname(item.getClazzCName()); - // 班主任 if (StringUtils.isNotBlank(item.getBzr())) { Optional teacherOpt = schTeacherList.stream() @@ -256,27 +263,17 @@ public class SchClassImportService { throw new DException(ErrorCode.CUSTOM,"导入失败,请检查错误列表",errorMsgArr); } - saveClassList = schClassRepository.saveAll(saveClassList); - for (SchClass clazz : saveClassList) { - if (clazz.getClassId().equals(0)) { - clazz.setClassId(clazz.getId()); - } - } schClassRepository.saveAll(saveClassList); cacheService.clearClassCache(user.getSchId()); logService.importLogs(user, saveClassList); } catch (DException e) { + // 业务/校验异常也要回滚已推送到平台的组,避免孤儿组 + sendUserBaseService.rollbackCreatedGroups(createdGroupIds, schId); throw e; } catch (Exception e) { // 导入失败:回滚已成功推送到平台的班级组/年级组,避免孤儿组 - for (Integer groupId : createdGroupIds) { - try { - sendUserBaseService.deleteGroup(groupId, schId); - } catch (Exception ignore) { - log.error("回滚平台组失败 groupId={}", groupId, ignore); - } - } + sendUserBaseService.rollbackCreatedGroups(createdGroupIds, schId); log.error("导入班级时发生系统异常,操作人:" + user.getUserName(), e); throw new DException(ErrorCode.CUSTOM,"导入失败,请联系管理员",null,e); } @@ -292,12 +289,26 @@ public class SchClassImportService { params.setTitleRows(1); params.setHeadRows(1); Date now = new Date(); + Integer schId = user.getSchId(); + List createdGroupIds = new ArrayList<>(); - Map> schGradeGroupMap = comSchTreeService.getSchGroupTypeList(user.getSchId(), NodeType.GRADE.getId()); + // 单学制:年级组直接挂在学生组下,无需再建学段组 + BoaGroup stuGroup = comSchTreeService.getStuTree(schId, false); + if (stuGroup == null) { + throw new DException("学生组不存在!"); + } + Integer stuParentId = stuGroup.getId(); + + Map> schGradeGroupMap = comSchTreeService.getSchGroupTypeList(schId, NodeType.GRADE.getId()); if (schGradeGroupMap.isEmpty()) { throw new DException("该学制下没有学级,请先同步年级组数据!"); } - //Map> schClassGroupMap = comSchTreeService.getSchGroupTypeList(user.getSchId(), NodeType.CLASS.getId()); + // 构建「年级组ID -> (班级组名 -> 班级组ID)」映射,使同名班级按年级区分,避免跨年级误复用平台组 + Map> gradeClassGroupMap = buildGradeClassGroupMap(schId); + + // 同批导入时缓存已创建的年级组/班级组 ID,避免同一 Excel 多行重复推送平台组 + Map createdGradeCache = new HashMap<>(); + Map createdClassCache = new HashMap<>(); try (InputStream is = file.getInputStream()) { List schTeacherList = schTeacherService.getSchTeacherList(user.getSchId()); @@ -343,14 +354,21 @@ public class SchClassImportService { Integer schsystemId = baseSchsystemGradeService.getSchGradeIdBySchsystemId(user.getSchId(), gradeId); List gradeGroupList = schGradeGroupMap.get(schsystemId); - Optional gradeGroupOpt= gradeGroupList.stream().filter(s->s.getName().equals(gradeId.toString())).findFirst(); - if(gradeGroupOpt.isPresent()) - { - clazz.setGroupId(gradeGroupOpt.get().getId()); - }else { - errorMsgArr.add(new ErrorExcelColumn(currentRow, item.getGradeName(), "年级组不存在!")); - continue; + // 定位年级组(单学制:年级组直接挂在学生组下);缺失则推送平台创建,并缓存避免同批重复创建 + Integer parentId = (gradeGroupList == null) ? null + : gradeGroupList.stream() + .filter(s -> Objects.equals(gradeId.toString(), s.getName())) + .map(SelectIntVO::getId).findFirst().orElse(null); + if (parentId == null) { + parentId = createdGradeCache.get(schsystemId + "_" + gradeId); } + if (parentId == null || parentId == 0) { + BoaGroup gradeGroup = sendUserBaseService.addGroup(gradeId.toString(), stuParentId, gradeId, NodeType.GRADE, schId); + parentId = gradeGroup.getId(); + createdGroupIds.add(parentId); + createdGradeCache.put(schsystemId + "_" + gradeId, parentId); + } + clazz.setGroupId(parentId); clazz.setName(item.getClazzName()); clazz.setCname(item.getClazzCName()); @@ -367,12 +385,35 @@ public class SchClassImportService { } else { clazz.setCreateDate(now); clazz.setCreateUserId(user.getUserId()); - clazz.setSchId(user.getSchId()); + clazz.setSchId(schId); clazz.setCode(item.getClazzCode()); clazz.setGraduate(false); clazz.setEnrollYear(gradeId); - clazz.setGroupId(0); - clazz.setClassId(0); + + // 班级组推送平台(单学制:班级组挂在年级组下);缺失则创建,并缓存避免同批重复创建 + Integer sort = OtherUtil.extractClassNumber(item.getClazzName()); + if (sort == null || sort <= 0) { + sort = 1; + } + clazz.setSort(sort); + // 班级组按年级区分:key 含年级ID,且平台组查找限定在当前年级组(parentId)下,避免跨年级同名班级复用同一平台组 + String classCacheKey = schsystemId + "_" + gradeId + "_" + item.getClazzName(); + Integer existClassId = null; + Map gradeClassMap = gradeClassGroupMap.get(parentId); + if (gradeClassMap != null) { + existClassId = gradeClassMap.get(item.getClazzName()); + } + if (existClassId == null) { + existClassId = createdClassCache.get(classCacheKey); + } + if (existClassId != null && existClassId > 0) { + clazz.setClassId(existClassId); + } else { + BoaGroup classGroup = sendUserBaseService.addGroup(item.getClazzName(), parentId, sort, NodeType.CLASS, schId); + clazz.setClassId(classGroup.getId()); + createdGroupIds.add(classGroup.getId()); + createdClassCache.put(classCacheKey, classGroup.getId()); + } } clazz.setCname(item.getClazzCName()); @@ -430,23 +471,58 @@ public class SchClassImportService { throw new DException(ErrorCode.CUSTOM,"导入失败,请检查错误列表",errorMsgArr); } - saveClassList = schClassRepository.saveAll(saveClassList); - for (SchClass clazz : saveClassList) { - if (clazz.getClassId().equals(0)) { - clazz.setClassId(clazz.getId()); - } - } schClassRepository.saveAll(saveClassList); cacheService.clearClassCache(user.getSchId()); logService.importLogs(user, saveClassList); } catch (DException e) { + // 业务/校验异常也要回滚已推送到平台的组,避免孤儿组 + sendUserBaseService.rollbackCreatedGroups(createdGroupIds, schId); throw e; } catch (Exception e) { + // 导入失败:回滚已成功推送到平台的班级组/年级组,避免孤儿组 + sendUserBaseService.rollbackCreatedGroups(createdGroupIds, schId); log.error("导入班级时发生系统异常,操作人:" + user.getUserName(), e); throw new DException(ErrorCode.CUSTOM,"导入失败,请联系管理员",null,e); } } + /** + * 从平台组树构建「年级组ID -> (班级组名 -> 班级组ID)」映射。 + * 班级组是年级组的直接子节点,按年级隔离,使同名班级跨年级可正确区分, + * 避免单学制下高一/高二/高三都叫"1班"时误复用同一个平台班级组。 + */ + private Map> buildGradeClassGroupMap(Integer schId) { + Map> result = new HashMap<>(); + List tree = comSchTreeService.getSchTree(schId); + if (tree == null) { + return result; + } + for (BoaGroup root : tree) { + collectGradeClassGroups(root, result); + } + return result; + } + + private void collectGradeClassGroups(BoaGroup node, Map> result) { + if (node == null || node.getChildren() == null) { + return; + } + if (node.getType() == NodeType.GRADE.getId()) { + Map classMap = new HashMap<>(); + for (BoaGroup child : node.getChildren()) { + if (child.getType() == NodeType.CLASS.getId() && child.getName() != null) { + classMap.put(child.getName(), child.getId()); + } + } + if (!classMap.isEmpty()) { + result.put(node.getId(), classMap); + } + } + for (BoaGroup child : node.getChildren()) { + collectGradeClassGroups(child, result); + } + } + } \ No newline at end of file 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 0c552b2..8dfc7d9 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 @@ -17,6 +17,8 @@ import com.yunhai.platform.utils.exception.DException; import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.platform.utils.model.BoaUser; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -224,6 +226,25 @@ public class SendUserBaseService implements ISendUserBaseService { } } + private static final Logger log = LoggerFactory.getLogger(SendUserBaseService.class); + + /** + * 导入失败回滚:删除已成功推送到平台的组(年级组/班级组/学段组),避免留下孤儿组。 + * 逐组删除并吞掉单组失败,避免回滚途中二次崩溃。 + */ + public void rollbackCreatedGroups(List groupIds, Integer schId) { + if (groupIds == null) { + return; + } + for (Integer groupId : groupIds) { + try { + deleteGroup(groupId, schId); + } catch (Exception ignore) { + log.error("回滚平台组失败 groupId={}", groupId, ignore); + } + } + } + //移动教师 @SyncOpLog(SyncDataType.SEND_MOVE_TEACHER) public void moveTeacherToGroup(List teacherList,Integer schId){