diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/group/service/GroupMemberService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/group/service/GroupMemberService.java index 813628d064..9f95a4d676 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/group/service/GroupMemberService.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/group/service/GroupMemberService.java @@ -41,4 +41,6 @@ public interface GroupMemberService { Mono bulkRemoveMember(String groupId, Collection userIds); + Mono> getGroupMembersByIdAndRole(String groupId, String role); + } diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/group/service/GroupMemberServiceImpl.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/group/service/GroupMemberServiceImpl.java index 82de01adf4..a5c8f21b1b 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/group/service/GroupMemberServiceImpl.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/group/service/GroupMemberServiceImpl.java @@ -35,6 +35,13 @@ public Mono> getGroupMembers(String groupId) { .collectList(); } + @Override + public Mono> getGroupMembersByIdAndRole(String groupId, String role) { + return biRelationService.getBySourceIdAndRelation(GROUP_MEMBER, groupId, role) + .map(GroupMember::from) + .collectList(); + } + @Override public Mono addMember(String orgId, String groupId, String userId, MemberRole memberRole) { return biRelationService.addBiRelation(GROUP_MEMBER, groupId, diff --git a/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/birelation/BiRelationServiceImpl.java b/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/birelation/BiRelationServiceImpl.java index ad98de0f95..56592e1d29 100644 --- a/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/birelation/BiRelationServiceImpl.java +++ b/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/birelation/BiRelationServiceImpl.java @@ -185,6 +185,9 @@ public Mono updateState(BiRelationBizType bizType, String sourceId, Str @Override public Flux getBySourceIdAndRelation(BiRelationBizType bizType, String sourceId, String relation) { + if (relation == null || relation.isBlank()) { + return biRelationRepository.findByBizTypeAndSourceId(bizType, sourceId); + } Query query = new Query(); query.addCriteria(where(BIZ_TYPE).is(bizType)); query.addCriteria(where(SOURCE_ID).is(sourceId)); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiService.java index e227e5d117..7319309842 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiService.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiService.java @@ -10,6 +10,8 @@ public interface GroupApiService { Mono getGroupMembers(String groupId, int page, int count); + Mono getGroupMembersForSearch(String groupId, String search, String role, String sort, String order, Integer pageNum, Integer pageSize); + Mono addGroupMember(String groupId, String newUserId, String roleName); Mono updateRoleForMember(String groupId, UpdateRoleRequest updateRoleRequest); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java index 3dbd8b6dd1..8b7fc20c94 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java @@ -111,6 +111,91 @@ public Mono getGroupMembers(String groupId, int page, }); } + @Override + public Mono getGroupMembersForSearch(String groupId, String search, String role, String sort, String order, Integer pageNum, Integer pageSize) { + Mono> groupAndOrgMemberInfo = getGroupAndOrgMemberInfo(groupId).cache(); + + Mono visitorRoleMono = groupAndOrgMemberInfo.flatMap(tuple -> { + GroupMember groupMember = tuple.getT1(); + OrgMember orgMember = tuple.getT2(); + if (groupMember.isSuperAdmin() || orgMember.isSuperAdmin()) { + return Mono.just(MemberRole.SUPER_ADMIN); + } + if (groupMember.isAdmin() || orgMember.isAdmin()) { + return Mono.just(MemberRole.ADMIN); + } + if (groupMember.isValid()) { + return Mono.just(MemberRole.MEMBER); + } + return ofError(BizError.NOT_AUTHORIZED, NOT_AUTHORIZED); + }); + + return groupAndOrgMemberInfo + .filter(this::hasReadPermission) + .switchIfEmpty(deferredError(BizError.NOT_AUTHORIZED, NOT_AUTHORIZED)) + .flatMap(groupMember -> groupMemberService.getGroupMembersByIdAndRole(groupId, role)) + ., Integer>> flatMap(members -> { + if (members.isEmpty()) { + return Mono.just(Pair.of(emptyList(), 0)); + } + + List userIds = collectList(members, GroupMember::getUserId); + Mono> userMapMono = userService.getByIds(userIds); + return userMapMono.map(map -> { + var list = members.stream() + .map(orgMember -> { + User user = map.get(orgMember.getUserId()); + if (user == null) { + return null; + } + return new GroupMemberView(orgMember, user); + }) + .filter(Objects::nonNull) + .filter(view -> { + if (search == null || search.isBlank()) return true; + return view.getUserName() != null && + view.getUserName().toLowerCase().contains(search.toLowerCase()); + }) + .toList(); + List mutableList = new ArrayList<>(list); + if (sort != null && !sort.isBlank()) { + Comparator comparator = null; + if ("userName".equalsIgnoreCase(sort)) { + comparator = Comparator.comparing(GroupMemberView::getUserName, Comparator.nullsLast(String::compareToIgnoreCase)); + } else if ("role".equalsIgnoreCase(sort)) { + comparator = Comparator.comparing(GroupMemberView::getRole, Comparator.nullsLast(String::compareToIgnoreCase)); + } else if ("joinTime".equalsIgnoreCase(sort)) { + comparator = Comparator.comparing(GroupMemberView::getJoinTime, Comparator.nullsLast(Long::compareTo)); + } + if (comparator != null && "desc".equalsIgnoreCase(order)) { + comparator = comparator.reversed(); + } + if (comparator != null) { + mutableList.sort(comparator); + } + } + + int pageTotal = mutableList.size(); + int fromIndex = Math.max(0, (pageNum - 1) * pageSize); + int toIndex = pageSize == 0 ? pageTotal : Math.min(pageNum * pageSize, pageTotal); + List pagedList = fromIndex < toIndex ? mutableList.subList(fromIndex, toIndex) : emptyList(); + + return Pair.of(pagedList, pageTotal); + }); + }) + .zipWith(visitorRoleMono) + .map(tuple -> { + Pair, Integer> t1 = tuple.getT1(); + return GroupMemberAggregateView.builder() + .members(t1.getLeft()) + .total(t1.getRight()) + .pageNum(pageNum) + .pageSize(pageSize) + .visitorRole(tuple.getT2().getValue()) + .build(); + }); + } + private boolean hasReadPermission(Tuple2 tuple) { GroupMember groupMember = tuple.getT1(); OrgMember orgMember = tuple.getT2(); @@ -366,4 +451,5 @@ public Mono getPotentialGroupMembers(String groupId, String s }); }); } + } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java index 992bba1fe0..0252bd4707 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java @@ -116,11 +116,16 @@ public Mono>> getOrgGroups(@RequestParam(r @Override - public Mono> getGroupMembers(@PathVariable String groupId, - @RequestParam(required = false, defaultValue = "1") int pageNum, - @RequestParam(required = false, defaultValue = "100") int pageSize) { + public Mono> getGroupMembers( + @PathVariable String groupId, + @RequestParam(required = false) String search, + @RequestParam(required = false) String role, + @RequestParam(required = false) String sort, + @RequestParam(required = false) String order, + @RequestParam(required = false, defaultValue = "1") Integer pageNum, + @RequestParam(required = false, defaultValue = "100") Integer pageSize) { return gidService.convertGroupIdToObjectId(groupId).flatMap(objectId -> - groupApiService.getGroupMembers(objectId, pageNum, pageSize) + groupApiService.getGroupMembersForSearch(objectId, search, role, sort, order, pageNum, pageSize) .map(ResponseView::success)); } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java index 3a4b90d567..b4530c1f61 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java @@ -68,14 +68,20 @@ public Mono>> getOrgGroups(@RequestParam(r @Operation( tags = TAG_GROUP_MEMBERS, - operationId = "listGroupMembers", - summary = "List User Group Members", - description = "Retrieve a list of Users / Members within a specific User Group in Lowcoder, showing the group's composition." + operationId = "listGroupMembersWithSearchAndSort", + summary = "List User Group Members with Search and Sort", + description = "Retrieve a paginated, searchable, and sortable list of Users / Members within a specific User Group in Lowcoder." ) - @GetMapping("/{groupId}/members") - public Mono> getGroupMembers(@PathVariable String groupId, - @RequestParam(required = false, defaultValue = "1") int pageNum, - @RequestParam(required = false, defaultValue = "100") int pageSize); + @GetMapping("/{groupId}/members") + public Mono> getGroupMembers( + @PathVariable String groupId, + @RequestParam(required = false) String search, + @RequestParam(required = false) String role, + @RequestParam(required = false) String sort, + @RequestParam(required = false) String order, + @RequestParam(required = false, defaultValue = "1") Integer pageNum, + @RequestParam(required = false, defaultValue = "100") Integer pageSize + ); @Operation( tags = TAG_GROUP_MEMBERS,