package com.yunhai.notice.service.impl; import com.yunhai.notice.model.SchTeaList; import com.yunhai.notice.service.IClassService; import com.yunhai.notice.service.IUserCenterService; import com.yunhai.platform.openapi.client.YschBaseOpenApi; import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.platform.utils.model.BoaModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; /** * 用户中心门面服务实现。 *

原 BaseApiController.getSchoolAll 的组树组装、教师挂组、递归建树、排序逻辑整体下沉至此; * GetGroupUser 空桩改为显式报错(未接入用户中心对应开放接口时提示,而非静默返回空列表)。 * 跨仓调用统一收敛到 {@link YschBaseOpenApi} 门面,Controller 不再直接静态调用。

*/ @Service public class UserCenterService implements IUserCenterService { private static final Logger log = LoggerFactory.getLogger(UserCenterService.class); /** 学生组(UserBase GroupTypeEnum.STUDENT_GROUP),组树中剔除 */ private static final int GROUP_TYPE_STUDENT = 8; @Autowired private IClassService classService; // ============================ 学校组织树 ============================ @Override public List> getSchoolAllTree(Integer schId) { if (schId == null) { throw new BizException(ErrorCode.PARAM_ERROR, "获取学校信息失败"); } // 1. userbase 组树(整棵学校子树),剔除学生组 List> flatNodes = new ArrayList<>(); BoaGroup root = YschBaseOpenApi.getGroupById(schId, true); if (root == null) { log.warn("[getSchoolAll] 用户中心未找到学校组, schId={}", schId); return Collections.emptyList(); } flattenGroup(root, 0, flatNodes); // 2. userclass(班级管理)全部教师,按 GroupIds 挂到对应组下;无有效组的兜底挂学校根节点 Set treeGroupIds = new HashSet<>(); for (Map node : flatNodes) { treeGroupIds.add((Integer) node.get("id")); } Map teacherMap = new LinkedHashMap<>(); Map> teacherPids = new LinkedHashMap<>(); List teachers = classService.getSchTeaList(schId); for (SchTeaList tea : teachers) { if (tea.getUserTeaSerID() == null) { continue; } // 同一教师多行(多组/接口重复数据)合并,按 (教师, 组) 去重 teacherMap.putIfAbsent(tea.getUserTeaSerID(), tea); // 接口现行字段为单个 GroupID,兼容旧字段 GroupIds List teaGroupIds = tea.getGroupIds(); if ((teaGroupIds == null || teaGroupIds.isEmpty()) && tea.getGroupId() != null) { teaGroupIds = Collections.singletonList(tea.getGroupId()); } if (teaGroupIds != null) { for (Integer gid : teaGroupIds) { if (gid != null && treeGroupIds.contains(gid)) { teacherPids.computeIfAbsent(tea.getUserTeaSerID(), k -> new HashSet<>()).add(gid); } } } } for (Map.Entry entry : teacherMap.entrySet()) { int teaId = entry.getKey(); SchTeaList tea = entry.getValue(); Set pids = teacherPids.get(teaId); if (pids == null || pids.isEmpty()) { // 教师所属组均被剔除(如学生组)或无组信息,挂学校根节点 pids = new HashSet<>(Collections.singletonList(treeGroupIds.contains(schId) ? schId : 0)); } for (Integer pid : pids) { Map node = new LinkedHashMap<>(); node.put("id", teaId); node.put("name", tea.getTeaName() != null ? tea.getTeaName() : ""); node.put("pid", pid); node.put("no", tea.getTeaNo() != null ? tea.getTeaNo() : 0); node.put("type", 2); node.put("isParent", false); node.put("photo", ""); flatNodes.add(node); } } // 3. 扁平节点递归组装为树 return formatUserTree(flatNodes, 0); } /** * 将 BoaGroup 子树扁平化为组节点列表(type=1);学生组(type=8)整棵剔除。 * isParent=true 标识组节点,前端 ztree 据此渲染为文件夹。 */ private void flattenGroup(BoaGroup group, int pid, List> nodes) { if (group == null || group.getId() == null) { return; } if (group.getType() != null && group.getType() == GROUP_TYPE_STUDENT) { return; } Map node = new LinkedHashMap<>(); node.put("id", group.getId()); node.put("name", group.getName() != null ? group.getName() : ""); node.put("pid", pid); node.put("no", group.getSort() != null ? group.getSort() : 0); node.put("type", 1); node.put("isParent", true); node.put("photo", ""); nodes.add(node); if (group.getChildren() != null) { for (BoaGroup child : group.getChildren()) { flattenGroup(child, group.getId(), nodes); } } } /** * 递归构建用户树(对应 C# HttpUserApi.FormatUserTree)。 * 根层取 pid=0 或父节点不在列表中的节点;type=1 且无子节点的空组剔除。 */ private List> formatUserTree(List> treeList, int pid) { List> data; if (pid == 0) { Set allIds = new HashSet<>(); for (Map n : treeList) { allIds.add((Integer) n.get("id")); } data = new ArrayList<>(); for (Map n : treeList) { int p = (Integer) n.get("pid"); if (p == 0 || !allIds.contains(p)) { data.add(n); } } } else { data = new ArrayList<>(); for (Map n : treeList) { if ((Integer) n.get("pid") == pid) { data.add(n); } } } List> result = new ArrayList<>(); for (Map item : data) { int itemId = (Integer) item.get("id"); int itemType = (Integer) item.get("type"); List> children = formatUserTree(treeList, itemId); // type=1(组) 且无子节点 → 不加入(C# 逻辑) if (itemType == 1 && children.isEmpty()) { continue; } Map node = new LinkedHashMap<>(item); // 仅组节点携带 children;叶子不带该字段,避免 ztree 误判 isParent if (itemType == 1) { node.put("children", children); } result.add(node); } // 按 no 排序(数字优先,无法解析的排最后) result.sort(Comparator.comparingInt(n -> { Object no = n.get("no"); if (no instanceof Integer) { return (Integer) no; } if (no instanceof String) { try { return Integer.parseInt((String) no); } catch (NumberFormatException e) { return 9999; } } return 9999; })); return result; } // ============================ 组内用户 ============================ @Override public List> getGroupUser(Integer gid, Integer role) { if (gid == null) { throw new BizException(ErrorCode.PARAM_ERROR, "组ID不得为空"); } log.info("[getGroupUser] gid={}, role={}, 用户中心组内用户开放接口尚未接入", gid, role); // 显式报错而非静默返回空列表:原空桩会让前端误以为组内无用户 throw new BizException(ErrorCode.INTERNAL_SERVER_ERROR, "组内用户查询接口暂未接入用户中心,请联系管理员配置后重试"); } // ============================ 用户模块树 ============================ @Override public List> getUserModuleTree(Integer userId, Integer schId) { if (userId == null) { throw new BizException(ErrorCode.PARAM_ERROR, "用户ID不得为空"); } List modules = YschBaseOpenApi.getUserModule(userId, schId); if (modules == null || modules.isEmpty()) { return Collections.emptyList(); } // 用户中心已按 BoaModule.children 下发模块树:本地不再重组层级,仅做前端字段改名映射 return toFrontendTree(modules); } /** * BoaModule 树 → 前端 layout 期望的字段形状(递归,只改名不组树)。
* 层级与兄弟顺序均由用户中心 {@code BoaModule.children} + customId 升序决定,本侧不重排; * 叶子节点 children 为空列表(用户中心保证),前端可无判空递归。 */ private List> toFrontendTree(List modules) { List> result = new ArrayList<>(); if (modules == null || modules.isEmpty()) { return result; } for (BoaModule m : modules) { Map node = new LinkedHashMap<>(); node.put("id", m.getId()); node.put("modId", m.getId()); node.put("name", m.getName()); node.put("parentId", m.getParentId() != null ? m.getParentId() : 0); node.put("page", m.getPath() != null ? m.getPath() : ""); node.put("level", m.getLevel() != null ? m.getLevel() : 0); node.put("icon", m.getIcon() != null ? m.getIcon() : ""); node.put("orderNumber", m.getSortNo() != null ? m.getSortNo() : 0); node.put("usable", true); node.put("children", toFrontendTree(m.getChildren())); result.add(node); } return result; } }