feat(系统): 新增系统组织与项目中组织添加成员可选用户组

This commit is contained in:
WangXu10 2024-08-30 17:42:17 +08:00 committed by Craftsman
parent 1560478751
commit 45eea8dd75
16 changed files with 198 additions and 15 deletions

View File

@ -255,6 +255,7 @@ permission.organization_member.name=User
permission.service_integration.name=Service Integration
permission.system_auth=Authorization
permission.system_organization_project_member.add=Add member
permission.system_organization_project_member.update=Update member
permission.system_organization_project_member.delete=Delete member
permission.system_operation_log.name=Operation log
permission.organization_operation_log.name=Operation log

View File

@ -256,6 +256,7 @@ permission.organization_member.name=成员
permission.service_integration.name=服务集成
permission.system_auth=授权
permission.system_organization_project_member.add=添加成员
permission.system_organization_project_member.update=编辑成员
permission.system_organization_project_member.delete=移除成员
permission.system_operation_log.name=日志
permission.organization_operation_log.name=日志

View File

@ -255,6 +255,7 @@ permission.organization_member.name=成員
permission.service_integration.name=服務集成
permission.system_auth=授權
permission.system_organization_project_member.add=添加成员
permission.system_organization_project_member.update=編輯成員
permission.system_organization_project_member.delete=移除成員
permission.system_operation_log.name=日志
permission.organization_operation_log.name=日志

View File

@ -171,7 +171,7 @@ public class SystemOrganizationController {
@GetMapping("/total")
@Operation(summary = "系统设置-系统-组织与项目-组织-获取组织和项目总数")
@RequiresPermissions(PermissionConstants.SYSTEM_ORGANIZATION_PROJECT_READ)
public Map<String, Long> getTotal(@RequestParam(value = "organizationId",required = false) String organizationId) {
public Map<String, Long> getTotal(@RequestParam(value = "organizationId", required = false) String organizationId) {
return organizationService.getTotal(organizationId);
}
@ -181,7 +181,16 @@ public class SystemOrganizationController {
@Parameter(name = "sourceId", description = "组织ID或项目ID", schema = @Schema(requiredMode = Schema.RequiredMode.REQUIRED))
public List<UserExtendDTO> getMemberOption(@PathVariable String sourceId,
@Schema(description = "查询关键字,根据邮箱和用户名查询")
@RequestParam(value = "keyword", required = false) String keyword) {
@RequestParam(value = "keyword", required = false) String keyword) {
return simpleUserService.getMemberOption(sourceId, keyword);
}
@PostMapping("/member-list")
@Operation(summary = "系统设置-系统-组织与项目-获取添加成员列表")
@RequiresPermissions(PermissionConstants.SYSTEM_ORGANIZATION_PROJECT_READ)
public Pager<List<UserExtendDTO>> getMemberOptionList(@Validated @RequestBody MemberRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize());
return PageUtils.setPageInfo(page, simpleUserService.getMemberList(request));
}
}

View File

@ -0,0 +1,17 @@
package io.metersphere.system.dto.request;
import io.metersphere.system.dto.sdk.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author wx
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class MemberRequest extends BasePageRequest {
@Schema(description = "组织ID或项目ID",requiredMode = Schema.RequiredMode.REQUIRED)
private String sourceId;
}

View File

@ -19,14 +19,19 @@ public class OrganizationMemberRequest implements Serializable {
/**
* 组织ID
*/
@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{organization.id.not_blank}")
private String organizationId;
/**
* 成员ID集合
*/
@Schema(description = "成员ID集合", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "成员ID集合", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{user.id.not_blank}")
private List<String> userIds;
@Schema(description = "用户组ID集合", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{user_role.id.not_blank}")
private List<String> userRoleIds;
}

View File

@ -6,6 +6,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author song-cc-rock
@ -19,7 +21,7 @@ public class UserExtendDTO extends User implements Serializable {
/**
* 是否管理员(组织, 项目)
*/
@Schema(description = "是否组织/项目管理员, 是: 展示管理员标识, 否: 不展示管理员标识")
@Schema(description = "是否组织/项目管理员, 是: 展示管理员标识, 否: 不展示管理员标识")
private boolean adminFlag;
/**
@ -36,4 +38,8 @@ public class UserExtendDTO extends User implements Serializable {
@Schema(description = "组织ID")
private String sourceId;
@Schema(description = "用户所属用户组")
private List<UserRoleOptionDto> userRoleList = new ArrayList<>();
}

View File

@ -0,0 +1,23 @@
package io.metersphere.system.dto.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* @author wx
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class UserRoleOptionDto implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "ID")
private String id;
@Schema(description = "名称")
private String name;
@Schema(description = "用户id")
private String userId;
}

View File

@ -1,6 +1,7 @@
package io.metersphere.system.mapper;
import io.metersphere.system.domain.User;
import io.metersphere.system.dto.request.MemberRequest;
import io.metersphere.system.dto.user.UserDTO;
import io.metersphere.system.dto.user.UserExtendDTO;
import org.apache.ibatis.annotations.Param;
@ -47,4 +48,6 @@ public interface ExtUserMapper {
Long gaInstalledTime();
void updateInstalled();
List<UserExtendDTO> getMemberList(@Param("request") MemberRequest request);
}

View File

@ -109,4 +109,15 @@
update metersphere_version SET description ='ga'
WHERE version ='3.0.1.2' and description = 'ga ddl'
</select>
<select id="getMemberList" resultType="io.metersphere.system.dto.user.UserExtendDTO" parameterType="io.metersphere.system.dto.request.MemberRequest">
select distinct u.*, count(urr.id) > 0 as memberFlag
from `user` u left join user_role_relation urr on urr.user_id = u.id and urr.source_id = #{request.sourceId}
where u.deleted = 0
<if test="request.keyword != null and request.keyword != ''">
and (u.name like concat('%', #{request.keyword}, '%') or u.email like concat('%', #{request.keyword}, '%'))
</if>
group by u.id
</select>
</mapper>

View File

@ -2,6 +2,7 @@ package io.metersphere.system.mapper;
import io.metersphere.system.domain.UserRoleRelation;
import io.metersphere.system.dto.request.GlobalUserRoleRelationQueryRequest;
import io.metersphere.system.dto.user.UserRoleOptionDto;
import io.metersphere.system.dto.user.UserRoleRelationUserDTO;
import org.apache.ibatis.annotations.Param;
@ -15,4 +16,6 @@ public interface ExtUserRoleRelationMapper {
List<UserRoleRelation> selectGlobalRoleByUserId(String userId);
List<UserRoleRelationUserDTO> listGlobal(@Param("request") GlobalUserRoleRelationQueryRequest request);
List<UserRoleOptionDto> selectUserRoleByUserIds(@Param("userIds") List<String> userIds, @Param("orgId") String orgId);
}

View File

@ -46,4 +46,20 @@
</if>
order by urr.create_time desc
</select>
<select id="selectUserRoleByUserIds" resultType="io.metersphere.system.dto.user.UserRoleOptionDto">
SELECT DISTINCT
user_role_relation.role_id as id,
user_role.`name` as name,
user_role_relation.user_id as userId
FROM
user_role_relation
LEFT JOIN user_role ON user_role_relation.role_id = user_role.id
where user_role_relation.user_id in
<foreach collection="userIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
and organization_id = #{orgId}
and source_id = #{orgId}
</select>
</mapper>

View File

@ -14,6 +14,7 @@ import io.metersphere.system.dto.*;
import io.metersphere.system.dto.request.*;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.dto.user.UserExtendDTO;
import io.metersphere.system.dto.user.UserRoleOptionDto;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.dto.LogDTO;
@ -81,6 +82,8 @@ public class OrganizationService {
private static final String REMOVE_MEMBER_PATH = "/system/organization/remove-member";
public static final Integer DEFAULT_REMAIN_DAY_COUNT = 30;
private static final Long DEFAULT_ORGANIZATION_NUM = 100001L;
@Resource
private ExtUserRoleRelationMapper extUserRoleRelationMapper;
/**
* 分页获取系统下组织列表
@ -207,7 +210,19 @@ public class OrganizationService {
* @return 组织成员集合
*/
public List<UserExtendDTO> getMemberListBySystem(OrganizationRequest request) {
return extOrganizationMapper.listMember(request);
List<UserExtendDTO> userExtendDTOS = extOrganizationMapper.listMember(request);
if (CollectionUtils.isNotEmpty(userExtendDTOS)) {
List<String> userIds = userExtendDTOS.stream().map(UserExtendDTO::getId).toList();
List<UserRoleOptionDto> userRole = extUserRoleRelationMapper.selectUserRoleByUserIds(userIds, request.getOrganizationId());
Map<String, List<UserRoleOptionDto>> roleMap = userRole.stream().collect(Collectors.groupingBy(UserRoleOptionDto::getUserId));
userExtendDTOS.forEach(user -> {
if (roleMap.containsKey(user.getId())) {
user.setUserRoleList(roleMap.get(user.getId()));
}
});
}
return userExtendDTOS;
}
public void deleteOrganization(String organizationId) {
@ -258,19 +273,53 @@ public class OrganizationService {
*/
public void addMemberBySystem(OrganizationMemberRequest organizationMemberRequest, String createUserId) {
List<LogDTO> logs = new ArrayList<>();
OrganizationMemberBatchRequest batchRequest = new OrganizationMemberBatchRequest();
batchRequest.setOrganizationIds(List.of(organizationMemberRequest.getOrganizationId()));
batchRequest.setUserIds(organizationMemberRequest.getUserIds());
addMemberBySystem(batchRequest, createUserId);
addMemberAndGroup(organizationMemberRequest, createUserId);
// 添加日志
UserExample example = new UserExample();
example.createCriteria().andIdIn(batchRequest.getUserIds());
example.createCriteria().andIdIn(organizationMemberRequest.getUserIds());
List<User> users = userMapper.selectByExample(example);
List<String> nameList = users.stream().map(User::getName).collect(Collectors.toList());
setLog(organizationMemberRequest.getOrganizationId(), createUserId, OperationLogType.ADD.name(), Translator.get("add") + Translator.get("organization_member_log") + ": " + StringUtils.join(nameList, ","), ADD_MEMBER_PATH, null, null, logs);
operationLogService.batchAdd(logs);
}
/**
* 系统-组织与项目-组织-添加成员用户+用户组
*
* @param request
* @param createUserId
*/
private void addMemberAndGroup(OrganizationMemberRequest request, String createUserId) {
checkOrgExistByIds(List.of(request.getOrganizationId()));
Map<String, User> userMap = checkUserExist(request.getUserIds());
List<UserRoleRelation> userRoleRelations = new ArrayList<>();
for (String userId : request.getUserIds()) {
if (userMap.get(userId) == null) {
throw new MSException(Translator.get("user.not.exist") + ", id: " + userId);
}
//组织用户关系已存在, 不再重复添加
UserRoleRelationExample example = new UserRoleRelationExample();
example.createCriteria().andSourceIdEqualTo(request.getOrganizationId()).andUserIdEqualTo(userId);
if (userRoleRelationMapper.countByExample(example) > 0) {
continue;
}
request.getUserRoleIds().forEach(userRoleId -> {
UserRoleRelation userRoleRelation = new UserRoleRelation();
userRoleRelation.setId(IDGenerator.nextStr());
userRoleRelation.setUserId(userId);
userRoleRelation.setSourceId(request.getOrganizationId());
userRoleRelation.setRoleId(userRoleId);
userRoleRelation.setCreateTime(System.currentTimeMillis());
userRoleRelation.setCreateUser(createUserId);
userRoleRelation.setOrganizationId(request.getOrganizationId());
userRoleRelations.add(userRoleRelation);
});
}
if (CollectionUtils.isNotEmpty(userRoleRelations)) {
userRoleRelationMapper.batchInsert(userRoleRelations);
}
}
/**
* 组织添加成员公共方法(N个组织添加N个成员)
*
@ -460,13 +509,13 @@ public class OrganizationService {
private Map<String, User> getUserMap(OrganizationMemberExtendRequest organizationMemberExtendRequest) {
Map<String, User> userMap;
if(organizationMemberExtendRequest.isSelectAll()) {
if (organizationMemberExtendRequest.isSelectAll()) {
OrganizationRequest organizationRequest = new OrganizationRequest();
BeanUtils.copyBean(organizationRequest, organizationMemberExtendRequest);
List<OrgUserExtend> orgUserExtends = extOrganizationMapper.listMemberByOrg(organizationRequest);
List<String> excludeIds = organizationMemberExtendRequest.getExcludeIds();
if (CollectionUtils.isNotEmpty(excludeIds)) {
userMap = orgUserExtends.stream().filter(user->!excludeIds.contains(user.getId())).collect(Collectors.toMap(User::getId, user -> user));
userMap = orgUserExtends.stream().filter(user -> !excludeIds.contains(user.getId())).collect(Collectors.toMap(User::getId, user -> user));
} else {
userMap = orgUserExtends.stream().collect(Collectors.toMap(User::getId, user -> user));
}
@ -550,13 +599,13 @@ public class OrganizationService {
List<String> projectIds = orgMemberExtendProjectRequest.getProjectIds();
//用户不在当前组织内过掉
Map<String, User> userMap;
if(orgMemberExtendProjectRequest.isSelectAll()) {
if (orgMemberExtendProjectRequest.isSelectAll()) {
OrganizationRequest organizationRequest = new OrganizationRequest();
BeanUtils.copyBean(organizationRequest, orgMemberExtendProjectRequest);
List<OrgUserExtend> orgUserExtends = extOrganizationMapper.listMemberByOrg(organizationRequest);
List<String> excludeIds = orgMemberExtendProjectRequest.getExcludeIds();
if (CollectionUtils.isNotEmpty(excludeIds)) {
userMap = orgUserExtends.stream().filter(user->!excludeIds.contains(user.getId())).collect(Collectors.toMap(User::getId, user -> user));
userMap = orgUserExtends.stream().filter(user -> !excludeIds.contains(user.getId())).collect(Collectors.toMap(User::getId, user -> user));
} else {
userMap = orgUserExtends.stream().collect(Collectors.toMap(User::getId, user -> user));
}
@ -1142,6 +1191,7 @@ public class OrganizationService {
/**
* 获取当前用户所拥有的组织
*
* @param userId 用户ID
* @return 组织下拉选项
*/

View File

@ -12,6 +12,7 @@ import io.metersphere.system.controller.result.SystemResultCode;
import io.metersphere.system.domain.*;
import io.metersphere.system.dto.excel.UserExcel;
import io.metersphere.system.dto.excel.UserExcelRowDTO;
import io.metersphere.system.dto.request.MemberRequest;
import io.metersphere.system.dto.request.UserInviteRequest;
import io.metersphere.system.dto.request.UserRegisterRequest;
import io.metersphere.system.dto.request.user.*;
@ -561,6 +562,7 @@ public class SimpleUserService {
editUser.setLanguage(request.getLanguage());
userMapper.updateByPrimaryKeySelective(editUser);
}
public boolean updateAccount(PersonalUpdateRequest request, String operator) {
this.checkUserEmail(request.getId(), request.getEmail());
User editUser = new User();
@ -599,4 +601,8 @@ public class SimpleUserService {
List<OptionDTO> userOptions = baseUserMapper.selectUserOptionByIds(userIds);
return userOptions.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
}
public List<UserExtendDTO> getMemberList(MemberRequest request) {
return extUserMapper.getMemberList(request);
}
}

View File

@ -52,6 +52,10 @@
"id": "SYSTEM_ORGANIZATION_PROJECT:READ+ADD_MEMBER",
"name": "permission.system_organization_project_member.add"
},
{
"id": "SYSTEM_ORGANIZATION_PROJECT:READ+UPDATE_MEMBER",
"name": "permission.system_organization_project_member.update"
},
{
"id": "SYSTEM_ORGANIZATION_PROJECT:READ+DELETE_MEMBER",
"name": "permission.system_organization_project_member.delete"

View File

@ -53,6 +53,7 @@ public class SystemOrganizationControllerTests extends BaseTest {
public static final String ORGANIZATION_LIST_PROJECT = "/system/organization/list-project";
public static final String ORGANIZATION_MEMBER_OPTION = "/system/organization/get-option";
public static final String ORGANIZATION_TOTAL = "/system/organization/total";
public static final String MEMBER_LIST = "/system/organization/member-list";
@Test
@Order(0)
@ -288,6 +289,7 @@ public class SystemOrganizationControllerTests extends BaseTest {
OrganizationMemberRequest organizationMemberRequest = new OrganizationMemberRequest();
organizationMemberRequest.setOrganizationId("default-organization-3");
organizationMemberRequest.setUserIds(List.of("admin", "default-admin"));
organizationMemberRequest.setUserRoleIds(List.of("default-role-1"));
this.requestPost(ORGANIZATION_ADD_MEMBER, organizationMemberRequest, status().isOk());
// 日志校验
checkLog(organizationMemberRequest.getOrganizationId(), OperationLogType.ADD);
@ -326,6 +328,7 @@ public class SystemOrganizationControllerTests extends BaseTest {
OrganizationMemberRequest organizationMemberRequest = new OrganizationMemberRequest();
organizationMemberRequest.setOrganizationId("default-organization-3");
organizationMemberRequest.setUserIds(List.of("admin"));
organizationMemberRequest.setUserRoleIds(List.of("default-role-1"));
this.requestPost(ORGANIZATION_ADD_MEMBER, organizationMemberRequest, status().isOk());
// 批量添加成员成功后, 验证是否添加成功
OrganizationRequest organizationRequest = new OrganizationRequest();
@ -360,21 +363,25 @@ public class SystemOrganizationControllerTests extends BaseTest {
OrganizationMemberRequest organizationMemberRequest = new OrganizationMemberRequest();
organizationMemberRequest.setOrganizationId("default-organization-3");
organizationMemberRequest.setUserIds(Collections.emptyList());
organizationMemberRequest.setUserRoleIds(List.of("default-role-1"));
this.requestPost(ORGANIZATION_ADD_MEMBER, organizationMemberRequest, status().isBadRequest());
// 成员都不存在
organizationMemberRequest = new OrganizationMemberRequest();
organizationMemberRequest.setOrganizationId("default-organization-3");
organizationMemberRequest.setUserIds(Arrays.asList("SccNotExistOne", "SccNotExistTwo"));
organizationMemberRequest.setUserRoleIds(List.of("default-role-1"));
this.requestPost(ORGANIZATION_ADD_MEMBER, organizationMemberRequest, status().is5xxServerError());
// 成员有一个不存在
organizationMemberRequest = new OrganizationMemberRequest();
organizationMemberRequest.setOrganizationId("default-organization-3");
organizationMemberRequest.setUserIds(Arrays.asList("SccNotExistOne", "default-admin"));
organizationMemberRequest.setUserRoleIds(List.of("default-role-1"));
this.requestPost(ORGANIZATION_ADD_MEMBER, organizationMemberRequest, status().is5xxServerError());
// 组织不存在
organizationMemberRequest = new OrganizationMemberRequest();
organizationMemberRequest.setOrganizationId("default-organization-x");
organizationMemberRequest.setUserIds(Arrays.asList("admin", "default-admin"));
organizationMemberRequest.setUserRoleIds(List.of("default-role-1"));
this.requestPost(ORGANIZATION_ADD_MEMBER, organizationMemberRequest, status().is5xxServerError());
}
@ -545,4 +552,24 @@ public class SystemOrganizationControllerTests extends BaseTest {
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
}
@Test
@Order(24)
public void getMemberList() throws Exception {
MemberRequest request = new MemberRequest();
request.setSourceId("default-organization-2");
request.setCurrent(1);
request.setPageSize(10);
this.requestPostWithOkAndReturn(MEMBER_LIST, request);
MvcResult pageResult = this.requestPostWithOkAndReturn(MEMBER_LIST, request);
String returnData = pageResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
Pager<List<UserExtendDTO>> result = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
//返回值的页码和当前页码相同
Assertions.assertEquals(result.getCurrent(), request.getCurrent());
//返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(result.getList())).size() <= request.getPageSize());
}
}