feat(dept): SysGroupVo 补 memberCount/memberNames,getTree 回填部门成员数与姓名预览
build / build-push (push) Successful in 2m23s

- SysGroupVo 新增 memberCount(直属成员数)与 memberNames(前 50 个姓名预览)
- SysGroupService 新增 fillMemberStats:整棵树只发两次查询(关联表 in + 用户表 in),
  getTree()/getTree(scope) 均回填,口径同 oapi 部门目录(sys_user_group_rel 按 groupId 计数,不排除转出)
- 预览上限 MEMBER_PREVIEW_LIMIT=50,避免大部门把名单塞进 getTree;完整名单走 /api/sysuser/List?groupId=
This commit is contained in:
chenxing
2026-10-23 10:30:52 +08:00
parent 72fe0f218a
commit 809d88c838
2 changed files with 94 additions and 2 deletions
@@ -20,6 +20,13 @@ public class SysGroupVo {
private Integer status;
/** 包含子组(2026-10-21):1=部门列表含下级部门 / 0=仅本级;BeanUtils 同名拷贝,null 视为 1 由前端兜底 */
private Integer includeChildren;
/** 直属成员数(部门表格「人员」列);口径同 oapi 部门目录:sys_user_group_rel 按 groupId 计数,不排除转出。 */
private Long memberCount;
/**
* 直属成员姓名预览,最多 {@code SysGroupService.MEMBER_PREVIEW_LIMIT} 个。<br>
* 完整名单不走这里(部门可能上百人),前端点「查看全部」调 /api/sysuser/List?groupId=xxx。
*/
private List<String> memberNames = new ArrayList<>();
/** 子组(递归);默认空列表,避免 JSON 序列化出现 null。 */
private List<SysGroupVo> children = new ArrayList<>();
}
@@ -63,6 +63,13 @@ public class SysGroupService implements ISysGroupService {
/** 开放接口/oapi 写操作无登录态时使用的审计人哨兵值(非真实用户ID),用于兜底 create_user/update_user 的 NOT NULL 约束。 */
private static final int SYSTEM_USER_ID = 0;
/**
* 部门表格「人员」列的姓名预览上限。<br>
* 表格最多显示 3 个名字 + 计数,tooltip 展示这份预览;部门上百人时不把这些名字塞进 getTree 响应,
* 完整名单由前端点「查看全部」走 /api/sysuser/List?groupId=xxx 分页取。
*/
static final int MEMBER_PREVIEW_LIMIT = 50;
/**
* 层级类型约束:父类型 -> 允许直接创建的子类型集合。
* key 为 {@code null} 表示「根节点(教育云中心)」。
@@ -363,7 +370,9 @@ public class SysGroupService implements ISysGroupService {
Map<Integer, List<SysGroup>> childrenMap = snapshot.getChildrenMap();
// 从根(pid=0)开始组装树
List<SysGroup> roots = childrenMap.getOrDefault(ROOT_PARENT_ID, new ArrayList<>());
return roots.stream().map(r -> buildNode(r, childrenMap)).collect(Collectors.toList());
List<SysGroupVo> tree = roots.stream().map(r -> buildNode(r, childrenMap)).collect(Collectors.toList());
fillMemberStats(tree);
return tree;
}
@Override
@@ -375,7 +384,83 @@ public class SysGroupService implements ISysGroupService {
if (scope == null || scope.isSuper()) {
return getTree();
}
return buildTrimmedTree(scope, snapshot.getAll(), false);
List<SysGroupVo> tree = buildTrimmedTree(scope, snapshot.getAll(), false);
fillMemberStats(tree);
return tree;
}
/**
* 给树节点补直属成员数与前 N 个姓名预览(部门表格「人员」列)。<br>
* 口径与 oapi 部门目录一致:sys_user_group_rel 按 groupId 计数,不排除转出;
* 整棵树只发两次查询(关联表 in + 用户表 in),不受节点数影响。
*/
private void fillMemberStats(List<SysGroupVo> tree) {
List<Integer> groupIds = new ArrayList<>();
collectGroupIds(tree, groupIds);
if (groupIds.isEmpty()) {
return;
}
List<SysUserGroupRel> rels = sysUserGroupRelRepository.findByGroupIdIn(groupIds);
if (rels == null || rels.isEmpty()) {
return;
}
Map<Integer, List<Integer>> relByGroup = new HashMap<>();
Set<Integer> userIds = new HashSet<>();
for (SysUserGroupRel rel : rels) {
if (rel.getGroupId() == null || rel.getUserId() == null) {
continue;
}
relByGroup.computeIfAbsent(rel.getGroupId(), k -> new ArrayList<>()).add(rel.getUserId());
userIds.add(rel.getUserId());
}
Map<Integer, String> nameById = new HashMap<>();
for (SysUser user : sysUserRepository.findAllById(userIds)) {
nameById.put(user.getId(),
StringUtils.hasText(user.getRealName()) ? user.getRealName() : user.getAccount());
}
Map<Integer, SysGroupVo> voById = new HashMap<>();
collectVoById(tree, voById);
for (Map.Entry<Integer, List<Integer>> entry : relByGroup.entrySet()) {
SysGroupVo vo = voById.get(entry.getKey());
if (vo == null) {
continue;
}
List<Integer> members = entry.getValue();
vo.setMemberCount((long) members.size());
List<String> preview = new ArrayList<>();
for (Integer uid : members) {
if (preview.size() >= MEMBER_PREVIEW_LIMIT) {
break;
}
String name = nameById.get(uid);
if (name != null) {
preview.add(name);
}
}
vo.setMemberNames(preview);
}
}
private void collectGroupIds(List<SysGroupVo> nodes, List<Integer> out) {
for (SysGroupVo vo : nodes) {
if (vo.getId() != null) {
out.add(vo.getId());
}
if (vo.getChildren() != null) {
collectGroupIds(vo.getChildren(), out);
}
}
}
private void collectVoById(List<SysGroupVo> nodes, Map<Integer, SysGroupVo> out) {
for (SysGroupVo vo : nodes) {
if (vo.getId() != null) {
out.put(vo.getId(), vo);
}
if (vo.getChildren() != null) {
collectVoById(vo.getChildren(), out);
}
}
}
@Override