diff --git a/src/main/java/com/yunhai/bschclassmanage/controller/oapi/oa/SchGroupController.java b/src/main/java/com/yunhai/bschclassmanage/controller/oapi/oa/SchGroupController.java index b793444..6d3204c 100644 --- a/src/main/java/com/yunhai/bschclassmanage/controller/oapi/oa/SchGroupController.java +++ b/src/main/java/com/yunhai/bschclassmanage/controller/oapi/oa/SchGroupController.java @@ -4,12 +4,14 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import com.yunhai.bschclassmanage.config.Config; +import com.yunhai.bschclassmanage.model.oa.TeaTreeVO; import com.yunhai.bschclassmanage.model.vo.oa.SchInfoVO; +import com.yunhai.bschclassmanage.service.IComSchTreeService; import com.yunhai.bschclassmanage.service.ISchInfoService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.Collections; import java.util.List; @Tag(name = "SchGroup", description = "SchGroup related APIs") @@ -18,7 +20,38 @@ import java.util.List; @CrossOrigin(originPatterns = {"*"}, allowCredentials = "true") public class SchGroupController { + @Autowired + private IComSchTreeService comSchTreeService; + @Autowired + private ISchInfoService schInfoService; + @GetMapping("GetTeaTree") + @Operation(summary = "获取学校教师组树+教师(SchSerID 与 schId 二选一)") + public List getTeaTree(@RequestParam(value = "SchSerID", required = false) String SchSerID, + @RequestParam(value = "schId", required = false) Integer schId) { + Integer resolvedSchId = resolveSchId(schId, SchSerID); + if (resolvedSchId == null) { + return Collections.emptyList(); + } + return comSchTreeService.getSchTeaTree(resolvedSchId); + } + /** + * 解析学校ID:支持直接传 schId,或传 SchSerID 反查 schId,二者至少提供一个。 + * + * @param schId 学校内部ID(优先使用) + * @param schSerID 学校外部序列号 + * @return 学校内部ID,参数非法或查不到时返回 null + */ + private Integer resolveSchId(Integer schId, String schSerID) { + if (schId != null) { + return schId; + } + if (schSerID == null || schSerID.trim().isEmpty()) { + return null; + } + SchInfoVO schInfoVO = schInfoService.GetSchFirst(schSerID); + return schInfoVO == null ? null : schInfoVO.getUserSchSerId(); + } } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/IComSchTreeService.java b/src/main/java/com/yunhai/bschclassmanage/service/IComSchTreeService.java index e8795b4..71bad36 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/IComSchTreeService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/IComSchTreeService.java @@ -1,6 +1,7 @@ package com.yunhai.bschclassmanage.service; import com.yunhai.bschclassmanage.entity.BaseDicGrade; +import com.yunhai.bschclassmanage.model.oa.TeaTreeVO; import com.yunhai.bschclassmanage.model.vo.SchSystemTreeVO; import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.model.vo.SelectStrVO; @@ -28,6 +29,8 @@ public interface IComSchTreeService { BoaGroup getTeacherTree(Integer schId); + List getSchTeaTree(Integer schId); + List getSchoolList(); BoaGroup getTeaTree(Integer schId, Boolean withSub); diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/com/ComSchTreeService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/com/ComSchTreeService.java index 1a977da..4f3c230 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/com/ComSchTreeService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/com/ComSchTreeService.java @@ -8,6 +8,7 @@ import com.yunhai.bschclassmanage.entity.*; import com.yunhai.bschclassmanage.enums.AcademicStage; import com.yunhai.bschclassmanage.enums.NodeType; import com.yunhai.bschclassmanage.enums.SchType; +import com.yunhai.bschclassmanage.model.oa.TeaTreeVO; import com.yunhai.bschclassmanage.model.vo.SchSystemTreeVO; import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.service.*; @@ -433,6 +434,150 @@ public class ComSchTreeService implements IComSchTreeService { return teaTree; } + /** 教师叶子的"未分组"节点 ID(与 getTeacherTree 的默认组保持一致) */ + private static final int UNGROUPED_ID = -1; + + /** + * 教师组树 + 教师(SchGroup/GetTeaTree 输出)。 + * 结构:学校根(type=1) → 教师组(type=1) → 子组递归 + 教师叶子(type=2)。 + * 节点字段:pid=父节点id(根为0)、no=组取 BoaGroup.sort / 教师固定 0、 + * isParent=组true/教师false、photo 固定空串(本库无头像数据)。 + * 返回单元素列表(学校根节点)。 + */ + public List getSchTeaTree(Integer schId) { + // 1. 学校根节点(远程组树的根即学校节点,取不到时用学校名兜底) + List schTree = getSchTree(schId); + BoaGroup schRoot = (schTree == null || schTree.isEmpty()) ? null : schTree.get(0); + TeaTreeVO rootVo = new TeaTreeVO(); + if (schRoot != null && schRoot.getId() != null) { + rootVo.setId(schRoot.getId()); + rootVo.setName(schRoot.getName()); + rootVo.setNo(schRoot.getSort()); + } else { + rootVo.setId(schId); + rootVo.setName(getSchoolName(schId)); + rootVo.setNo(0); + } + rootVo.setPid(0); + rootVo.setType(1); + rootVo.setIsParent(true); + rootVo.setChildren(new ArrayList<>()); + + // 2. 教师组骨架(type=7 子树)转 VO,同时建立 groupId -> 节点 映射供挂教师用 + Map groupVoMap = new HashMap<>(); + BoaGroup teaTree = getTeaTree(schId, true); + TeaTreeVO teaRootVo; + if (teaTree == null || teaTree.getId() == null) { + teaRootVo = newGroupNode(0, "教师组", rootVo.getId(), 1); + } else { + teaRootVo = convertGroupTree(teaTree, rootVo.getId(), groupVoMap); + } + groupVoMap.put(teaRootVo.getId(), teaRootVo); + rootVo.getChildren().add(teaRootVo); + + // 3. 挂教师:多组各挂一份;无组或组不在树中的落到"未分组" + List teacherList = schTeacherService.getSchTeacherList(schId); + if (CollectionUtils.isNotEmpty(teacherList)) { + List teaIds = teacherList.stream() + .map(SchTeacher::getTeaId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + Map> teaIdToGroupIds = schTeacherGroupRepository.findByTeaIdInAndSchId(teaIds, schId) + .stream() + .collect(Collectors.groupingBy(SchTeacherGroup::getTeaId, + Collectors.mapping(SchTeacherGroup::getGroupId, Collectors.toList()))); + + for (SchTeacher teacher : teacherList) { + if (teacher.getTeaId() == null) { + continue; + } + List groupIds = teaIdToGroupIds.getOrDefault(teacher.getTeaId(), Collections.emptyList()); + if (groupIds.isEmpty()) { + TeaTreeVO ungrouped = getOrCreateUngrouped(teaRootVo, groupVoMap); + ungrouped.getChildren().add(newTeacherNode(teacher, ungrouped.getId())); + } else { + for (Integer groupId : groupIds) { + TeaTreeVO groupVo = groupVoMap.get(groupId); + if (groupVo == null) { + groupVo = getOrCreateUngrouped(teaRootVo, groupVoMap); + } + // 同一节点下按 teaId 去重(防脏数据中同一组出现多条关联) + boolean exists = groupVo.getChildren().stream() + .anyMatch(c -> Integer.valueOf(2).equals(c.getType()) && c.getId().equals(teacher.getTeaId())); + if (!exists) { + groupVo.getChildren().add(newTeacherNode(teacher, groupVo.getId())); + } + } + } + } + } + + // 4. 每个节点 children 按 no 升序(nullsLast、稳定排序):教师 no=0 排在子组之前, + // 同 no 的教师保持 getSchTeacherList 的 sort 顺序 + sortChildrenByNo(rootVo); + + return Collections.singletonList(rootVo); + } + + /** 组节点 VO(type=1,isParent=true,children 初始化为空列表) */ + private TeaTreeVO newGroupNode(Integer id, String name, Integer pid, Integer no) { + TeaTreeVO vo = new TeaTreeVO(); + vo.setId(id); + vo.setName(name); + vo.setPid(pid); + vo.setNo(no); + vo.setType(1); + vo.setIsParent(true); + vo.setChildren(new ArrayList<>()); + return vo; + } + + /** 教师叶子 VO(type=2,isParent=false,no 固定 0,children=null → JSON 省略) */ + private TeaTreeVO newTeacherNode(SchTeacher teacher, Integer pid) { + TeaTreeVO vo = new TeaTreeVO(); + vo.setId(teacher.getTeaId()); + vo.setName(teacher.getName()); + vo.setPid(pid); + vo.setNo(0); + vo.setType(2); + vo.setIsParent(false); + return vo; + } + + /** 递归把 BoaGroup 骨架转成 TeaTreeVO(type=1),pid 记录父节点 id,并收集 groupId -> 节点 */ + private TeaTreeVO convertGroupTree(BoaGroup node, Integer pid, Map groupVoMap) { + TeaTreeVO vo = newGroupNode(node.getId(), node.getName(), pid, node.getSort()); + groupVoMap.put(node.getId(), vo); + if (node.getChildren() != null) { + for (BoaGroup child : node.getChildren()) { + vo.getChildren().add(convertGroupTree(child, node.getId(), groupVoMap)); + } + } + return vo; + } + + /** 获取或惰性创建"未分组"节点(挂在教师组根下) */ + private TeaTreeVO getOrCreateUngrouped(TeaTreeVO teaRootVo, Map groupVoMap) { + TeaTreeVO ungrouped = groupVoMap.get(UNGROUPED_ID); + if (ungrouped == null) { + ungrouped = newGroupNode(UNGROUPED_ID, "未分组", teaRootVo.getId(), null); + teaRootVo.getChildren().add(ungrouped); + groupVoMap.put(UNGROUPED_ID, ungrouped); + } + return ungrouped; + } + + /** 递归按 no 升序排序每个节点的 children(nullsLast、稳定排序) */ + private void sortChildrenByNo(TeaTreeVO node) { + if (node == null || node.getChildren() == null) { + return; + } + node.getChildren().sort(Comparator.comparing(TeaTreeVO::getNo, Comparator.nullsLast(Integer::compareTo))); + for (TeaTreeVO child : node.getChildren()) { + sortChildrenByNo(child); + } + } + /** * 递归确保所有节点的 children 不为 null,若为 null 则设置为空列表 [] */