feat(项目管理): 项目成员功能
This commit is contained in:
parent
ddf4611586
commit
0ba8e85b45
|
@ -18,6 +18,7 @@
|
|||
<artifactId>metersphere-sdk</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.metersphere</groupId>
|
||||
<artifactId>metersphere-sdk</artifactId>
|
||||
|
@ -26,6 +27,12 @@
|
|||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.metersphere</groupId>
|
||||
<artifactId>metersphere-system-setting</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package io.metersphere.project.controller;
|
||||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.project.dto.ProjectUserDTO;
|
||||
import io.metersphere.project.request.ProjectMemberAddRequest;
|
||||
import io.metersphere.project.request.ProjectMemberBatchDeleteRequest;
|
||||
import io.metersphere.project.request.ProjectMemberEditRequest;
|
||||
import io.metersphere.project.request.ProjectMemberRequest;
|
||||
import io.metersphere.project.service.ProjectMemberService;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
import io.metersphere.sdk.dto.OptionDTO;
|
||||
import io.metersphere.sdk.util.PageUtils;
|
||||
import io.metersphere.sdk.util.Pager;
|
||||
import io.metersphere.sdk.util.SessionUtils;
|
||||
import io.metersphere.system.dto.UserExtend;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song-cc-rock
|
||||
*/
|
||||
@Tag(name = "项目管理-成员")
|
||||
@RestController
|
||||
@RequestMapping("/project/member")
|
||||
public class ProjectMemberController {
|
||||
|
||||
@Resource
|
||||
private ProjectMemberService projectMemberService;
|
||||
|
||||
@PostMapping("/list")
|
||||
@Operation(summary = "项目管理-成员-列表查询")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_MEMBER_READ)
|
||||
public Pager<List<ProjectUserDTO>> listMember(@Validated @RequestBody ProjectMemberRequest request) {
|
||||
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(), true);
|
||||
return PageUtils.setPageInfo(page, projectMemberService.listMember(request));
|
||||
}
|
||||
|
||||
@GetMapping("/get-member/option/{projectId}")
|
||||
@Operation(summary = "项目管理-成员-获取成员下拉选项")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_MEMBER_ADD)
|
||||
public List<UserExtend> getMemberOption(@PathVariable String projectId) {
|
||||
return projectMemberService.getMemberOption(projectId);
|
||||
}
|
||||
|
||||
@GetMapping("/get-role/option/{projectId}")
|
||||
@Operation(summary = "项目管理-成员-获取用户组下拉选项")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_MEMBER_ADD)
|
||||
public List<OptionDTO> getRoleOption(@PathVariable String projectId) {
|
||||
return projectMemberService.getRoleOption(projectId);
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "项目管理-成员-添加成员")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_MEMBER_ADD)
|
||||
public void addMember(@RequestBody ProjectMemberAddRequest request) {
|
||||
projectMemberService.addMember(request, SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
@PostMapping("/update")
|
||||
@Operation(summary = "项目管理-成员-编辑成员")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_MEMBER_UPDATE)
|
||||
public void updateMember(@RequestBody ProjectMemberEditRequest request) {
|
||||
projectMemberService.updateMember(request, SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
@GetMapping("/remove/{projectId}/{userId}")
|
||||
@Operation(summary = "项目管理-成员-移除成员")
|
||||
@Parameters({
|
||||
@Parameter(name = "projectId", description = "项目ID", schema = @Schema(requiredMode = Schema.RequiredMode.REQUIRED)),
|
||||
@Parameter(name = "userId", description = "成员ID", schema = @Schema(requiredMode = Schema.RequiredMode.REQUIRED))
|
||||
})
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_MEMBER_DELETE)
|
||||
public void removeMember(@PathVariable String projectId, @PathVariable String userId) {
|
||||
projectMemberService.removeMember(projectId, userId);
|
||||
}
|
||||
|
||||
@PostMapping("/add-role")
|
||||
@Operation(summary = "项目管理-成员-批量添加至用户组")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_MEMBER_UPDATE)
|
||||
public void addMemberRole(@RequestBody ProjectMemberAddRequest request) {
|
||||
projectMemberService.addMemberRole(request, SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
@PostMapping("/batch/remove")
|
||||
@Operation(summary = "项目管理-成员-批量从项目移除")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_MEMBER_DELETE)
|
||||
public void batchRemove(@RequestBody ProjectMemberBatchDeleteRequest request) {
|
||||
projectMemberService.batchRemove(request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.project.dto;
|
||||
|
||||
import io.metersphere.system.domain.User;
|
||||
import io.metersphere.system.domain.UserRole;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song-cc-rock
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ProjectUserDTO extends User {
|
||||
|
||||
@Schema(description = "用户组集合")
|
||||
private List<UserRole> userRoles;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.project.mapper;
|
||||
|
||||
import io.metersphere.project.request.ProjectMemberRequest;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song-cc-rock
|
||||
*/
|
||||
public interface ExtProjectMemberMapper {
|
||||
|
||||
/**
|
||||
* 获取项目成员列表
|
||||
*
|
||||
* @param request 请求参数
|
||||
* @return 成员列表及用户组关联信息
|
||||
*/
|
||||
List<String> listMember(@Param("request") ProjectMemberRequest request);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.metersphere.project.mapper.ExtProjectMemberMapper">
|
||||
<select id="listMember" resultType="java.lang.String">
|
||||
select distinct u.id
|
||||
from user_role_relation urr
|
||||
join `user` u on urr.user_id = u.id
|
||||
<where>
|
||||
urr.source_id = #{request.projectId} and 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},'%')
|
||||
or u.phone like CONCAT('%', #{request.keyword},'%'))
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
</mapper>
|
|
@ -0,0 +1,30 @@
|
|||
package io.metersphere.project.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song-cc-rock
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ProjectMemberAddRequest implements Serializable {
|
||||
|
||||
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{project.id.not_blank}")
|
||||
private String projectId;
|
||||
|
||||
@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> roleIds;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package io.metersphere.project.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song-cc-rock
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ProjectMemberBatchDeleteRequest implements Serializable {
|
||||
|
||||
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{project.id.not_blank}")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "用户ID集合", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "{user.id.not_blank}")
|
||||
private List<String> userIds;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package io.metersphere.project.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song-cc-rock
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ProjectMemberEditRequest implements Serializable {
|
||||
|
||||
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{project.id.not_blank}")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "{user.id.not_blank}")
|
||||
private String userId;
|
||||
|
||||
@Schema(description = "用户组ID集合", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "{user_role.id.not_blank}")
|
||||
private List<String> roleIds;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.project.request;
|
||||
|
||||
import io.metersphere.sdk.dto.BasePageRequest;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ProjectMemberRequest extends BasePageRequest {
|
||||
|
||||
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{project.id.not_blank}")
|
||||
private String projectId;
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
package io.metersphere.project.service;
|
||||
|
||||
import io.metersphere.project.domain.Project;
|
||||
import io.metersphere.project.dto.ProjectUserDTO;
|
||||
import io.metersphere.project.mapper.ExtProjectMemberMapper;
|
||||
import io.metersphere.project.mapper.ProjectMapper;
|
||||
import io.metersphere.project.request.ProjectMemberAddRequest;
|
||||
import io.metersphere.project.request.ProjectMemberBatchDeleteRequest;
|
||||
import io.metersphere.project.request.ProjectMemberEditRequest;
|
||||
import io.metersphere.project.request.ProjectMemberRequest;
|
||||
import io.metersphere.sdk.constants.UserRoleEnum;
|
||||
import io.metersphere.sdk.constants.UserRoleType;
|
||||
import io.metersphere.sdk.dto.OptionDTO;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.mapper.BaseUserMapper;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.domain.*;
|
||||
import io.metersphere.system.dto.UserExtend;
|
||||
import io.metersphere.system.mapper.ExtOrganizationMapper;
|
||||
import io.metersphere.system.mapper.UserMapper;
|
||||
import io.metersphere.system.mapper.UserRoleMapper;
|
||||
import io.metersphere.system.mapper.UserRoleRelationMapper;
|
||||
import io.metersphere.system.request.OrganizationRequest;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author song-cc-rock
|
||||
*/
|
||||
@Service
|
||||
@Transactional
|
||||
public class ProjectMemberService {
|
||||
|
||||
@Resource
|
||||
private UserRoleMapper userRoleMapper;
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
@Resource
|
||||
private UserRoleRelationMapper userRoleRelationMapper;
|
||||
@Resource
|
||||
private BaseUserMapper baseUserMapper;
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private ExtOrganizationMapper extOrganizationMapper;
|
||||
@Resource
|
||||
private ExtProjectMemberMapper extProjectMemberMapper;
|
||||
|
||||
/**
|
||||
* 获取成员列表
|
||||
*
|
||||
* @param request 请求参数
|
||||
* @return 成员列表
|
||||
*/
|
||||
public List<ProjectUserDTO> listMember(ProjectMemberRequest request) {
|
||||
// 查询当前项目成员
|
||||
List<String> members = extProjectMemberMapper.listMember(request);
|
||||
UserRoleRelationExample relationExample = new UserRoleRelationExample();
|
||||
relationExample.createCriteria().andSourceIdEqualTo(request.getProjectId()).andUserIdIn(members);
|
||||
List<UserRoleRelation> userRoleRelates = userRoleRelationMapper.selectByExample(relationExample);
|
||||
Map<String, List<String>> userRoleRelateMap = userRoleRelates.stream().collect(Collectors.groupingBy(UserRoleRelation::getUserId,
|
||||
Collectors.mapping(UserRoleRelation::getRoleId, Collectors.toList())));
|
||||
// 查询所有用户
|
||||
List<User> users = baseUserMapper.findAll();
|
||||
Map<String, User> userMap = users.stream().collect(Collectors.toMap(User::getId, user -> user));
|
||||
// 查询所有项目类型用户组
|
||||
UserRoleExample example = new UserRoleExample();
|
||||
example.createCriteria().andTypeEqualTo(UserRoleType.PROJECT.name());
|
||||
List<UserRole> roles = userRoleMapper.selectByExample(example);
|
||||
Map<String, UserRole> roleMap = roles.stream().collect(Collectors.toMap(UserRole::getId, role -> role));
|
||||
List<ProjectUserDTO> projectUsers = new ArrayList<>();
|
||||
userRoleRelateMap.forEach((k, v) -> {
|
||||
ProjectUserDTO projectUser = new ProjectUserDTO();
|
||||
User user = userMap.get(k);
|
||||
BeanUtils.copyBean(projectUser, user);
|
||||
List<UserRole> userRoles = new ArrayList<>();
|
||||
v.forEach(roleId -> {
|
||||
UserRole role = roleMap.get(roleId);
|
||||
userRoles.add(role);
|
||||
});
|
||||
projectUser.setUserRoles(userRoles);
|
||||
projectUsers.add(projectUser);
|
||||
});
|
||||
return projectUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组织成员下拉选项
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 项目成员下拉选项
|
||||
*/
|
||||
public List<UserExtend> getMemberOption(String projectId) {
|
||||
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||
if (project == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return extOrganizationMapper.getMemberByOrg(project.getOrganizationId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目用户组下拉选项
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 用户组下拉选项
|
||||
*/
|
||||
public List<OptionDTO> getRoleOption(String projectId) {
|
||||
UserRoleExample example = new UserRoleExample();
|
||||
example.createCriteria().andTypeEqualTo(UserRoleType.PROJECT.name())
|
||||
.andScopeIdIn(Arrays.asList(projectId, UserRoleEnum.GLOBAL.toString()));
|
||||
List<UserRole> userRoles = userRoleMapper.selectByExample(example);
|
||||
return userRoles.stream().map(userRole -> new OptionDTO(userRole.getId(), userRole.getName())).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加成员(项目)
|
||||
*
|
||||
* @param request 请求参数
|
||||
* @param currentUserId 当前用户ID
|
||||
*/
|
||||
public void addMember(ProjectMemberAddRequest request, String currentUserId) {
|
||||
// 项目不存在, 则不添加
|
||||
checkProjectExist(request.getProjectId());
|
||||
// 获取已经存在的用户组
|
||||
UserRoleRelationExample example = new UserRoleRelationExample();
|
||||
example.createCriteria().andSourceIdEqualTo(request.getProjectId())
|
||||
.andUserIdIn(request.getUserIds()).andRoleIdIn(request.getRoleIds());
|
||||
List<UserRoleRelation> userRoleRelations = userRoleRelationMapper.selectByExample(example);
|
||||
Map<String, List<String>> existUserRelations = userRoleRelations.stream().collect(
|
||||
Collectors.groupingBy(UserRoleRelation::getUserId, Collectors.mapping(UserRoleRelation::getRoleId, Collectors.toList())));
|
||||
// 比较用户组是否已经存在, 如果不存在则添加
|
||||
List<UserRoleRelation> relations = new ArrayList<>();
|
||||
request.getUserIds().forEach(userId -> request.getRoleIds().forEach(roleId -> {
|
||||
// 用户不存在或用户组不存在, 则不添加
|
||||
if (isUserOrRoleNotExist(userId, roleId)) {
|
||||
return;
|
||||
}
|
||||
// 如果该用户已经添加至该用户组, 则不再添加
|
||||
if (existUserRelations.containsKey(userId) && existUserRelations.get(userId).contains(roleId)) {
|
||||
return;
|
||||
}
|
||||
UserRoleRelation relation = new UserRoleRelation();
|
||||
relation.setId(UUID.randomUUID().toString());
|
||||
relation.setUserId(userId);
|
||||
relation.setRoleId(roleId);
|
||||
relation.setSourceId(request.getProjectId());
|
||||
relation.setCreateTime(System.currentTimeMillis());
|
||||
relation.setCreateUser(currentUserId);
|
||||
relations.add(relation);
|
||||
}));
|
||||
if (!CollectionUtils.isEmpty(relations)) {
|
||||
userRoleRelationMapper.batchInsert(relations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新成员(项目)
|
||||
*
|
||||
* @param request 请求参数
|
||||
* @param currentUserId 当前用户ID
|
||||
*/
|
||||
public void updateMember(ProjectMemberEditRequest request, String currentUserId) {
|
||||
// 项目不存在
|
||||
checkProjectExist(request.getProjectId());
|
||||
// 移除已经存在的用户组
|
||||
UserRoleRelationExample example = new UserRoleRelationExample();
|
||||
example.createCriteria().andSourceIdEqualTo(request.getProjectId())
|
||||
.andUserIdEqualTo(request.getUserId());
|
||||
userRoleRelationMapper.deleteByExample(example);
|
||||
// 添加新的用户组
|
||||
List<UserRoleRelation> relations = new ArrayList<>();
|
||||
request.getRoleIds().forEach(roleId -> {
|
||||
// 用户不存在或用户组不存在, 则不添加
|
||||
if (isUserOrRoleNotExist(request.getUserId(), roleId)) {
|
||||
return;
|
||||
}
|
||||
UserRoleRelation relation = new UserRoleRelation();
|
||||
relation.setId(UUID.randomUUID().toString());
|
||||
relation.setUserId(request.getUserId());
|
||||
relation.setRoleId(roleId);
|
||||
relation.setSourceId(request.getProjectId());
|
||||
relation.setCreateTime(System.currentTimeMillis());
|
||||
relation.setCreateUser(currentUserId);
|
||||
relations.add(relation);
|
||||
});
|
||||
if (!CollectionUtils.isEmpty(relations)) {
|
||||
userRoleRelationMapper.batchInsert(relations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除成员(项目)
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
public void removeMember(String projectId, String userId) {
|
||||
// 项目不存在, 则不移除
|
||||
checkProjectExist(projectId);
|
||||
// 移除成员, 则移除该成员在该项目下的所有用户组
|
||||
UserRoleRelationExample example = new UserRoleRelationExample();
|
||||
example.createCriteria().andSourceIdEqualTo(projectId).andUserIdEqualTo(userId);
|
||||
userRoleRelationMapper.deleteByExample(example);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加成员至用户组(项目)
|
||||
* @param request 请求参数
|
||||
* @param currentUserId 当前用户ID
|
||||
*/
|
||||
public void addMemberRole(ProjectMemberAddRequest request, String currentUserId) {
|
||||
// 添加用户用户组(已经添加的用户组不再添加)
|
||||
addMember(request, currentUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量移除成员(项目)
|
||||
* @param request 请求参数
|
||||
*/
|
||||
public void batchRemove(ProjectMemberBatchDeleteRequest request) {
|
||||
// 项目不存在, 则不移除
|
||||
checkProjectExist(request.getProjectId());
|
||||
// 批量移除成员, 则移除该成员在该项目下的所有用户组
|
||||
UserRoleRelationExample example = new UserRoleRelationExample();
|
||||
example.createCriteria().andSourceIdEqualTo(request.getProjectId())
|
||||
.andUserIdIn(request.getUserIds());
|
||||
userRoleRelationMapper.deleteByExample(example);
|
||||
}
|
||||
|
||||
private void checkProjectExist(String projectId) {
|
||||
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||
if (project == null) {
|
||||
throw new MSException(Translator.get("project_not_exist"));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUserOrRoleNotExist(String userId, String roleId) {
|
||||
UserExample example = new UserExample();
|
||||
example.createCriteria().andIdEqualTo(userId).andDeletedEqualTo(false);
|
||||
return userMapper.selectByExample(example) == null || userRoleMapper.selectByPrimaryKey(roleId) == null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
package io.metersphere.project.controller;
|
||||
|
||||
import io.metersphere.project.dto.ProjectUserDTO;
|
||||
import io.metersphere.project.request.ProjectMemberAddRequest;
|
||||
import io.metersphere.project.request.ProjectMemberBatchDeleteRequest;
|
||||
import io.metersphere.project.request.ProjectMemberEditRequest;
|
||||
import io.metersphere.project.request.ProjectMemberRequest;
|
||||
import io.metersphere.sdk.base.BaseTest;
|
||||
import io.metersphere.sdk.constants.SessionConstants;
|
||||
import io.metersphere.sdk.controller.handler.ResultHolder;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.Pager;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class ProjectMemberControllerTests extends BaseTest {
|
||||
|
||||
public static final String LIST_MEMBER = "/project/member/list";
|
||||
public static final String GET_MEMBER = "/project/member/get-member/option";
|
||||
public static final String GET_ROLE = "/project/member/get-role/option";
|
||||
public static final String ADD_MEMBER = "/project/member/add";
|
||||
public static final String UPDATE_MEMBER = "/project/member/update";
|
||||
public static final String REMOVE_MEMBER = "/project/member/remove";
|
||||
public static final String ADD_ROLE = "/project/member/add-role";
|
||||
public static final String BATCH_REMOVE_MEMBER = "/project/member/batch/remove";
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@Sql(scripts = {"/dml/init_project_member.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
|
||||
public void testListMemberSuccess() throws Exception{
|
||||
ProjectMemberRequest request = new ProjectMemberRequest();
|
||||
request.setProjectId("default-project-member-test");
|
||||
request.setCurrent(1);
|
||||
request.setPageSize(10);
|
||||
request.setKeyword("default");
|
||||
MvcResult mvcResult = this.responsePost(LIST_MEMBER, request);
|
||||
// 获取返回值
|
||||
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
|
||||
// 返回请求正常
|
||||
Assertions.assertNotNull(resultHolder);
|
||||
Pager<?> pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
|
||||
// 返回值不为空
|
||||
Assertions.assertNotNull(pageData);
|
||||
// 返回值的页码和当前页码相同
|
||||
Assertions.assertEquals(pageData.getCurrent(), request.getCurrent());
|
||||
// 返回的数据量不超过规定要返回的数据量相同
|
||||
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize());
|
||||
// 返回值中取出第一条数据, 并判断name, email, phone是否包含关键字default
|
||||
ProjectUserDTO projectUserDTO = JSON.parseArray(JSON.toJSONString(pageData.getList()), ProjectUserDTO.class).get(0);
|
||||
Assertions.assertTrue(StringUtils.contains(projectUserDTO.getName(), request.getKeyword())
|
||||
|| StringUtils.contains(projectUserDTO.getEmail(), request.getKeyword())
|
||||
|| StringUtils.contains(projectUserDTO.getPhone(), request.getKeyword()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testListMemberError() throws Exception {
|
||||
// 页码有误
|
||||
ProjectMemberRequest request = new ProjectMemberRequest();
|
||||
request.setCurrent(0);
|
||||
request.setPageSize(10);
|
||||
this.requestPost(LIST_MEMBER, request, status().isBadRequest());
|
||||
// 页数有误
|
||||
request = new ProjectMemberRequest();
|
||||
request.setCurrent(1);
|
||||
request.setPageSize(1);
|
||||
this.requestPost(LIST_MEMBER, request, status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testGetMemberOption() throws Exception {
|
||||
this.requestGet(GET_MEMBER + "/default-project-member-test", status().isOk());
|
||||
// 覆盖空数据
|
||||
this.requestGet(GET_MEMBER + "/default-project-member-x", status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testGetRoleOption() throws Exception {
|
||||
this.requestGet(GET_ROLE + "/default-project-member-test", status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testAddMemberSuccess() throws Exception {
|
||||
ProjectMemberAddRequest request = new ProjectMemberAddRequest();
|
||||
request.setProjectId("default-project-member-test");
|
||||
request.setUserIds(List.of("default-project-member-user-1", "default-project-member-user-del"));
|
||||
request.setRoleIds(List.of("project_admin", "project_admin_x", "project_member"));
|
||||
this.requestPost(ADD_MEMBER, request, status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void testAddMemberRepeat() throws Exception {
|
||||
ProjectMemberAddRequest request = new ProjectMemberAddRequest();
|
||||
request.setProjectId("default-project-member-test");
|
||||
request.setUserIds(List.of("default-project-member-user-1"));
|
||||
request.setRoleIds(List.of("project_admin"));
|
||||
this.requestPost(ADD_MEMBER, request, status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
public void testAddMemberError() throws Exception {
|
||||
ProjectMemberAddRequest request = new ProjectMemberAddRequest();
|
||||
request.setProjectId("default-project-member-x");
|
||||
request.setUserIds(List.of("default-project-member-user-1", "default-project-member-user-del"));
|
||||
request.setRoleIds(List.of("project_admin", "project_admin_x", "project_member"));
|
||||
this.requestPost(ADD_MEMBER, request, status().is5xxServerError());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(8)
|
||||
public void testUpdateMemberSuccess() throws Exception {
|
||||
// 不存在的用户组
|
||||
ProjectMemberEditRequest request = new ProjectMemberEditRequest();
|
||||
request.setProjectId("default-project-member-test");
|
||||
request.setUserId("default-project-member-user-1");
|
||||
request.setRoleIds(List.of("project_admin_x"));
|
||||
this.requestPost(UPDATE_MEMBER, request, status().isOk());
|
||||
// 存在的用户组
|
||||
request.setRoleIds(List.of("project_admin", "project_member"));
|
||||
this.requestPost(UPDATE_MEMBER, request, status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(9)
|
||||
public void testUpdateMemberError() throws Exception {
|
||||
ProjectMemberEditRequest request = new ProjectMemberEditRequest();
|
||||
request.setProjectId("default-project-member-x");
|
||||
request.setUserId("default-project-member-user-1");
|
||||
request.setRoleIds(List.of("project_admin", "project_admin_x", "project_member"));
|
||||
this.requestPost(UPDATE_MEMBER, request, status().is5xxServerError());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(10)
|
||||
public void testRemoveMemberSuccess() throws Exception {
|
||||
this.requestGet(REMOVE_MEMBER + "/default-project-member-test/default-project-member-user-1", status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(11)
|
||||
public void testRemoveMemberError() throws Exception {
|
||||
this.requestGet(REMOVE_MEMBER + "/default-project-member-x/default-project-member-user-1", status().is5xxServerError());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(12)
|
||||
public void testAddMemberRoleSuccess() throws Exception {
|
||||
ProjectMemberAddRequest request = new ProjectMemberAddRequest();
|
||||
request.setProjectId("default-project-member-test");
|
||||
request.setUserIds(List.of("default-project-member-user-1", "default-project-member-user-2"));
|
||||
request.setRoleIds(List.of("project_admin", "project_member"));
|
||||
this.requestPost(ADD_ROLE, request, status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(13)
|
||||
public void testAddMemberRoleError() throws Exception {
|
||||
ProjectMemberAddRequest request = new ProjectMemberAddRequest();
|
||||
request.setProjectId("default-project-member-x");
|
||||
request.setUserIds(List.of("default-project-member-user-1", "default-project-member-user-2"));
|
||||
request.setRoleIds(List.of("project_admin", "project_member"));
|
||||
this.requestPost(ADD_ROLE, request, status().is5xxServerError());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(14)
|
||||
public void testBatchRemoveMemberSuccess() throws Exception {
|
||||
ProjectMemberBatchDeleteRequest request = new ProjectMemberBatchDeleteRequest();
|
||||
request.setProjectId("default-project-member-test");
|
||||
request.setUserIds(List.of("default-project-member-user-1", "default-project-member-user-2"));
|
||||
this.requestPost(BATCH_REMOVE_MEMBER, request, status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(15)
|
||||
public void testBatchRemoveMember() throws Exception {
|
||||
ProjectMemberBatchDeleteRequest request = new ProjectMemberBatchDeleteRequest();
|
||||
request.setProjectId("default-project-member-x");
|
||||
request.setUserIds(List.of("default-project-member-user-1", "default-project-member-user-2"));
|
||||
this.requestPost(BATCH_REMOVE_MEMBER, request, status().is5xxServerError());
|
||||
}
|
||||
|
||||
private void requestPost(String url, Object param, ResultMatcher resultMatcher) throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post(url)
|
||||
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||
.content(JSON.toJSONString(param))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(resultMatcher)
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
private MvcResult responsePost(String url, Object param) throws Exception {
|
||||
return mockMvc.perform(MockMvcRequestBuilders.post(url)
|
||||
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||
.content(JSON.toJSONString(param))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
private void requestGet(String url, ResultMatcher resultMatcher) throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get(url)
|
||||
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(resultMatcher)
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
# 1. 准备组织, 项目, 用户数据
|
||||
# 2. 准备组织成员数据
|
||||
# 3. 准备项目成员数据
|
||||
INSERT INTO organization(id, num, name, description, create_time, update_time, create_user, update_user, deleted, delete_user, delete_time) VALUE
|
||||
('default-organization-member-test', null, 'efault-organization-member-test', 'efault-organization-member-test', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'admin', 0, null, null);
|
||||
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE
|
||||
('default-project-member-test', null, 'efault-organization-member-test', '默认项目', '系统默认创建的项目', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
|
||||
INSERT INTO user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source, last_project_id, create_user, update_user, deleted) VALUES
|
||||
('default-project-member-user-1', 'default-project-member-user1', 'project-member1@metersphere.io', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', 0),
|
||||
('default-project-member-user-2', 'default-project-member-user2', 'project-member2@metersphere.io', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', 0),
|
||||
('default-project-member-user-del', 'default-project-member-userDel', 'project-member-del@metersphere.io', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', 1);
|
||||
|
||||
INSERT INTO user_role_relation (id, user_id, role_id, source_id, create_time, create_user) VALUES
|
||||
(UUID(), 'default-project-member-user-1', 'org_member', 'default-organization-member-test', UNIX_TIMESTAMP() * 1000, 'admin'),
|
||||
(UUID(), 'default-project-member-user-2', 'org_member', 'default-organization-member-test', UNIX_TIMESTAMP() * 1000, 'admin');
|
||||
|
||||
INSERT INTO user_role_relation (id, user_id, role_id, source_id, create_time, create_user) VALUES
|
||||
(UUID(), 'default-project-member-user-1', 'project_admin', 'default-project-member-test', UNIX_TIMESTAMP() * 1000, 'admin'),
|
||||
(UUID(), 'default-project-member-user-2', 'project_admin', 'default-project-member-test', UNIX_TIMESTAMP() * 1000, 'admin'),
|
||||
(UUID(), 'default-project-member-user-del', 'project_admin', 'default-project-member-test', UNIX_TIMESTAMP() * 1000, 'admin');
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -77,5 +77,17 @@ public interface ExtOrganizationMapper {
|
|||
*/
|
||||
List<OrganizationProjectOptionsDTO> selectOrganizationOptions();
|
||||
|
||||
/**
|
||||
* 获取组织下拉选项
|
||||
* @param ids 组织ID集合
|
||||
* @return 组织下拉选项
|
||||
*/
|
||||
List<OptionDTO> getOptionsByIds(@Param("ids") List<String> ids);
|
||||
|
||||
/**
|
||||
* 获取所有组织成员
|
||||
* @param organizationId 组织ID
|
||||
* @return 成员
|
||||
*/
|
||||
List<UserExtend> getMemberByOrg(String organizationId);
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
<select id="selectOrganizationOptions" resultType="io.metersphere.system.dto.OrganizationProjectOptionsDTO">
|
||||
select id, name from organization order by create_time desc
|
||||
</select>
|
||||
|
||||
<select id="getOptionsByIds" resultType="io.metersphere.sdk.dto.OptionDTO">
|
||||
select id, name from organization where id in
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
|
@ -87,6 +88,17 @@
|
|||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="getMemberByOrg" resultType="io.metersphere.system.dto.UserExtend">
|
||||
select distinct u.* from user_role_relation urr join `user` u on urr.user_id = u.id
|
||||
<where>
|
||||
u.deleted = 0
|
||||
<if test="organizationId != null and organizationId != ''">
|
||||
and urr.source_id = #{organizationId}
|
||||
</if>
|
||||
</where>
|
||||
order by u.create_time desc
|
||||
</select>
|
||||
|
||||
<sql id="queryWhereCondition">
|
||||
<where>
|
||||
<if test="request.keyword != null">
|
||||
|
|
Loading…
Reference in New Issue