Merge branch 'master' of github.com:metersphere/metersphere

This commit is contained in:
Captain.B 2021-03-05 13:29:47 +08:00
commit 2dd0d94276
40 changed files with 1102 additions and 102 deletions

View File

@ -84,8 +84,7 @@ public class MsAuthManager extends MsTestElement {
authManager.setProperty(TestElement.TEST_CLASS, AuthManager.class.getName());
authManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("AuthPanel"));
Authorization auth = new Authorization();
auth.setURL(samplerProxy.getUrl().toString());
auth.setDomain(samplerProxy.getDomain());
auth.setURL(samplerProxy.getProtocol() + "://" + samplerProxy.getDomain());
auth.setUser(msAuthManager.getUsername());
auth.setPass(msAuthManager.getPassword());
auth.setMechanism(AuthManager.Mechanism.DIGEST);

View File

@ -10,5 +10,5 @@ public interface ExtOrganizationMapper {
int checkSourceRole(@Param("sourceId") String sourceId,@Param("userId") String userId,@Param("roleId") String roleId);
List<OrganizationMemberDTO> findAllIdAndName();
List<OrganizationMemberDTO> findIdAndNameByOrganizationId(@Param("organizationId")String organizationID);
}

View File

@ -11,7 +11,12 @@
</select>
<select id="findAllIdAndName" resultType="io.metersphere.dto.OrganizationMemberDTO">
<select id="findIdAndNameByOrganizationId" resultType="io.metersphere.dto.OrganizationMemberDTO">
select id,name from Organization
<where>
<if test="organizationId != 'All'">
AND id = #{organizationId}
</if>
</where>
</select>
</mapper>

View File

@ -1,7 +1,9 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.base.domain.User;
import io.metersphere.controller.request.UserRequest;
import io.metersphere.controller.request.resourcepool.UserBatchProcessRequest;
import io.metersphere.notice.domain.UserDetail;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
@ -25,4 +27,6 @@ public interface ExtUserMapper {
Map<String, User> queryNameByIds(List<String> userIds);
List<String> selectAllId();
List<String> selectIdsByQuery(@Param("request") UserRequest request);
}

View File

@ -16,6 +16,27 @@
<result column="phone" jdbcType="VARCHAR" property="phone"/>
</resultMap>
<sql id="queryWhereCondition">
<where>
<if test="request.id != null">
AND user.id like CONCAT('%', #{request.id},'%')
</if>
<if test="request.name != null">
AND user.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.email != null">
AND user.email like CONCAT('%', #{request.email},'%')
</if>
<if test="request.unSelectIds != null and request.unSelectIds.size() > 0">
AND user.id not in
<foreach collection="request.unSelectIds" item="itemId" separator="," open="(" close=")">
#{itemId}
</foreach>
</if>
</where>
</sql>
<select id="getUserList" resultMap="BaseResultMap">
select u.id, u.name, u.email, u.phone, u.language, u.status, u.source,
u.last_organization_id, u.last_workspace_id, u.language, u.create_time, u.update_time
@ -76,4 +97,9 @@
select id from `user`
</select>
<select id="selectIdsByQuery" resultType="java.lang.String">
select user.id
from user
<include refid="queryWhereCondition"/>
</select>
</mapper>

View File

@ -2,6 +2,7 @@ package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.Role;
import io.metersphere.base.domain.User;
import io.metersphere.controller.request.UserRequest;
import io.metersphere.controller.request.member.QueryMemberRequest;
import io.metersphere.controller.request.organization.QueryOrgMemberRequest;
import io.metersphere.dto.OrganizationMemberDTO;
@ -28,4 +29,6 @@ public interface ExtUserRoleMapper {
List<User> getTestManagerAndTestUserList(@Param("request") QueryMemberRequest request);
List<String> selectIdsByQuery(@Param("organizationId") String organizationId, @Param("orgMember")UserRequest condition);
}

View File

@ -108,4 +108,22 @@
</if>
order by user_role.update_time desc) temp
</select>
<select id="selectIdsByQuery" resultType="java.lang.String">
SELECT DISTINCT temp.id FROM (
SELECT `user`.* FROM user_role
JOIN `user` ON user_role.user_id = `user`.id
WHERE user_role.source_id in
(
SELECT id FROM workspace w
WHERE w.organization_id = #{organizationId}
UNION
SELECT #{organizationId} AS id FROM dual
)
<if test="orgMember.name != null">
AND `user`.name like CONCAT('%', #{orgMember.name},'%')
</if>
order by user_role.update_time desc
) temp
</select>
</mapper>

View File

@ -11,5 +11,7 @@ public interface ExtWorkspaceMapper {
List<WorkspaceDTO> getWorkspaceWithOrg(@Param("request") WorkspaceRequest request);
List<String> getWorkspaceIdsByOrgId(@Param("orgId") String orgId);
List<WorkspaceDTO> findAllIdAndName();
String getOrganizationIdById(String resourceID);
List<WorkspaceDTO> findIdAndNameByOrganizationId(@Param("organizationId") String organizationId);
}

View File

@ -18,8 +18,18 @@
where organization_id = #{orgId}
</select>
<select id="findAllIdAndName" resultType="io.metersphere.dto.WorkspaceDTO">
select id,name from workspace
<select id="findIdAndNameByOrganizationId" resultType="io.metersphere.dto.WorkspaceDTO">
select id,name,organization_id AS organizationId from workspace
<where>
<if test="organizationId != 'All'">
AND organization_id = #{organizationId}
</if>
</where>
</select>
<select id="getOrganizationIdById" resultType="java.lang.String">
select organization_id from workspace
where id = #{orgId}
</select>
</mapper>

View File

@ -0,0 +1,5 @@
package io.metersphere.commons.constants;
public enum BatchProcessUserInfoType {
ADD_WORKSPACE,ADD_USER_ROLE
}

View File

@ -45,6 +45,10 @@ public class DateUtils {
SimpleDateFormat dateFormat = new SimpleDateFormat(TIME_PATTERN);
return dateFormat.format(timeStamp);
}
public static String getDataStr(long timeStamp) {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERM);
return dateFormat.format(timeStamp);
}
public static Date dateSum (Date date,int countDays){

View File

@ -1,8 +1,11 @@
package io.metersphere.controller;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.Organization;
import io.metersphere.base.domain.User;
import io.metersphere.base.domain.Workspace;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
@ -15,8 +18,8 @@ import io.metersphere.controller.request.member.QueryMemberRequest;
import io.metersphere.controller.request.member.UserRequest;
import io.metersphere.controller.request.organization.AddOrgMemberRequest;
import io.metersphere.controller.request.organization.QueryOrgMemberRequest;
import io.metersphere.dto.UserDTO;
import io.metersphere.dto.UserRoleDTO;
import io.metersphere.controller.request.resourcepool.UserBatchProcessRequest;
import io.metersphere.dto.*;
import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.i18n.Translator;
import io.metersphere.service.CheckPermissionService;
@ -26,12 +29,16 @@ import io.metersphere.service.WorkspaceService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.checkerframework.checker.units.qual.C;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RequestMapping("user")
@RestController
@ -315,4 +322,53 @@ public class UserController {
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String userId) {
return userService.userImport(file, userId);
}
@PostMapping("/special/batchProcessUserInfo")
@RequiresRoles(value = {RoleConstants.ADMIN, RoleConstants.ORG_ADMIN,RoleConstants.TEST_MANAGER})
public String batchProcessUserInfo(@RequestBody UserBatchProcessRequest request) {
String returnString = "success";
userService.batchProcessUserInfo(request);
return returnString;
}
@GetMapping("/getWorkspaceDataStruct/{organizationId}")
public List<CascaderDTO> getWorkspaceDataStruct(@PathVariable String organizationId) {
List<OrganizationMemberDTO> organizationList = organizationService.findIdAndNameByOrganizationId(organizationId);
List<WorkspaceDTO> workspaceDTOList = workspaceService.findIdAndNameByOrganizationId(organizationId);
if(!workspaceDTOList.isEmpty()){
Map<String, List<WorkspaceDTO>> orgIdWorkspaceMap = workspaceDTOList.stream().collect(Collectors.groupingBy(WorkspaceDTO::getOrganizationId));
List<CascaderDTO> returnList = CascaderParse.parseWorkspaceDataStruct(organizationList,orgIdWorkspaceMap);
return returnList;
}else {
return new ArrayList<>();
}
}
@GetMapping("/getUserRoleDataStruct/{organizationId}")
public List<CascaderDTO> getUserRoleDataStruct(@PathVariable String organizationId) {
List<OrganizationMemberDTO> organizationList = organizationService.findIdAndNameByOrganizationId(organizationId);
List<WorkspaceDTO> workspaceDTOList = workspaceService.findIdAndNameByOrganizationId(organizationId);
if(!workspaceDTOList.isEmpty()){
Map<String, List<WorkspaceDTO>> orgIdWorkspaceMap = workspaceDTOList.stream().collect(Collectors.groupingBy(WorkspaceDTO::getOrganizationId));
List<CascaderDTO> returnList = CascaderParse.parseUserRoleDataStruct(organizationList,orgIdWorkspaceMap,false);
return returnList;
}else {
return new ArrayList<>();
}
}
@GetMapping("/getWorkspaceUserRoleDataStruct/{organizationId}")
public List<CascaderDTO> getWorkspaceUserRoleDataStruct(@PathVariable String organizationId) {
List<OrganizationMemberDTO> organizationList = organizationService.findIdAndNameByOrganizationId(organizationId);
List<WorkspaceDTO> workspaceDTOList = workspaceService.findIdAndNameByOrganizationId(organizationId);
if(!workspaceDTOList.isEmpty()){
Map<String, List<WorkspaceDTO>> orgIdWorkspaceMap = workspaceDTOList.stream().collect(Collectors.groupingBy(WorkspaceDTO::getOrganizationId));
List<CascaderDTO> returnList = CascaderParse.parseUserRoleDataStruct(organizationList,orgIdWorkspaceMap,true);
return returnList;
}else {
return new ArrayList<>();
}
}
}

View File

@ -3,10 +3,15 @@ package io.metersphere.controller.request;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class UserRequest {
private String id;
private String name;
private String email;
boolean selectAll;
List<String> unSelectIds;
}

View File

@ -0,0 +1,25 @@
package io.metersphere.controller.request.resourcepool;
import io.metersphere.controller.request.UserRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.concurrent.locks.Condition;
/**
* @author song.tianyang
* @Date 2021/3/3 5:21 下午
* @Description
*/
@Getter
@Setter
public class UserBatchProcessRequest {
List<String> ids;
String projectId;
String batchType;
List<String> batchProcessValue;
String organizationId;
UserRequest condition;
}

View File

@ -0,0 +1,22 @@
package io.metersphere.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
/**
* 级联选择器-数据格式
*
* @author song.tianyang
* @Date 2021/3/4 10:47 上午
* @Description
*/
@Getter
@Setter
public class CascaderDTO {
private String value;
private String label;
private List<CascaderDTO> children;
}

View File

@ -0,0 +1,108 @@
package io.metersphere.dto;
import bsh.StringUtil;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.i18n.Translator;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author song.tianyang
* @Date 2021/3/4 2:47 下午
* @Description
*/
public class CascaderParse {
public static List<CascaderDTO> parseUserRoleDataStruct(List<OrganizationMemberDTO> organizationList, Map<String, List<WorkspaceDTO>> orgIdWorkspaceMap,boolean hideOrgRole) {
List<CascaderDTO> returnList = new ArrayList<>();
for (OrganizationMemberDTO orgDTO : organizationList) {
String orgId = orgDTO.getId();
List<WorkspaceDTO> workspaceDTOList = orgIdWorkspaceMap.get(orgId);
CascaderDTO orgCascader = generateCascaderDTO(orgDTO.getId(), orgDTO.getName(), null);
if (workspaceDTOList != null) {
List<CascaderDTO> children = new ArrayList<>();
for (WorkspaceDTO workspace : workspaceDTOList) {
String parentCascaderType = "workspace";
if(hideOrgRole){
parentCascaderType = "hideOrg";
}
List<CascaderDTO> cascaderDTOList = getUserRoleCascaderDTO(parentCascaderType, workspace.getId());
CascaderDTO workspaceCascader = generateCascaderDTO(workspace.getId(), workspace.getName(), cascaderDTOList);
children.add(workspaceCascader);
}
orgCascader.setChildren(children);
} else {
List<CascaderDTO> cascaderDTOList = getUserRoleCascaderDTO("org", orgDTO.getId());
orgCascader.setChildren(cascaderDTOList);
}
returnList.add(orgCascader);
}
return returnList;
}
private static List<CascaderDTO> getUserRoleCascaderDTO(String parentCascaderType, String parentID) {
String idPrefix = "";
String idSuffix = "<->" + parentCascaderType;
if (!StringUtils.isEmpty(parentID)) {
idPrefix = parentID + "<->";
}
CascaderDTO orgAdminCascasder = generateCascaderDTO(idPrefix + RoleConstants.ORG_ADMIN + idSuffix, Translator.get("org_admin"), null);
CascaderDTO orgMemberCascasder = generateCascaderDTO(idPrefix + RoleConstants.ORG_MEMBER + idSuffix, Translator.get("org_member"), null);
CascaderDTO testManagerCascasder = generateCascaderDTO(idPrefix + RoleConstants.TEST_MANAGER + idSuffix, Translator.get("test_manager"), null);
CascaderDTO testerCascasder = generateCascaderDTO(idPrefix + RoleConstants.TEST_USER + idSuffix, Translator.get("tester"), null);
CascaderDTO readOnlyUserCascasder = generateCascaderDTO(idPrefix + RoleConstants.TEST_VIEWER + idSuffix, Translator.get("read_only_user"), null);
//默认都要添加这两种类型
List<CascaderDTO> returnList = new ArrayList<>();
switch (parentCascaderType) {
case "workspace":
returnList.add(orgAdminCascasder);
returnList.add(orgMemberCascasder);
returnList.add(testManagerCascasder);
returnList.add(testerCascasder);
returnList.add(readOnlyUserCascasder);
break;
case "org":
returnList.add(orgAdminCascasder);
returnList.add(orgMemberCascasder);
break;
case "hideOrg":
returnList.add(testManagerCascasder);
returnList.add(testerCascasder);
returnList.add(readOnlyUserCascasder);
break;
}
return returnList;
}
public static List<CascaderDTO> parseWorkspaceDataStruct(List<OrganizationMemberDTO> organizationList, Map<String, List<WorkspaceDTO>> orgIdWorkspaceMap) {
List<CascaderDTO> returnList = new ArrayList<>();
for (OrganizationMemberDTO orgDTO : organizationList) {
String orgId = orgDTO.getId();
List<WorkspaceDTO> workspaceDTOList = orgIdWorkspaceMap.get(orgId);
if (workspaceDTOList != null) {
List<CascaderDTO> children = new ArrayList<>();
for (WorkspaceDTO workspace : workspaceDTOList) {
CascaderDTO workspaceCascader = generateCascaderDTO(workspace.getId(), workspace.getName(), null);
children.add(workspaceCascader);
}
CascaderDTO orgCascader = generateCascaderDTO(orgDTO.getId(), orgDTO.getName(), children);
returnList.add(orgCascader);
}
}
return returnList;
}
private static CascaderDTO generateCascaderDTO(String value, String lable, List<CascaderDTO> children) {
CascaderDTO cascaderDTO = new CascaderDTO();
cascaderDTO.setLabel(lable);
cascaderDTO.setValue(value);
if (children != null && !children.isEmpty()) {
cascaderDTO.setChildren(children);
}
return cascaderDTO;
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.excel.listener;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.controller.request.member.UserRequest;
@ -176,37 +177,37 @@ public class UserDataListener extends EasyExcelListener<UserExcelData> {
if (StringUtils.equalsIgnoreCase(Translator.get("options_yes"), data.getUserIsAdmin())) {
List<String> adminIdList = new ArrayList<>();
adminIdList.add("adminSourceId");
Map<String, Object> adminRoleMap = this.genRoleMap("admin", adminIdList);
Map<String, Object> adminRoleMap = this.genRoleMap(RoleConstants.ADMIN, adminIdList);
roleMapList.add(adminRoleMap);
}
//判断组织管理员
List<String> orgManagerOrdIdList = this.getIdByExcelInfoAndIdDic(data.getUserIsOrgAdmin(), data.getOrgAdminOrganization(), orgNameMap);
if (!orgManagerOrdIdList.isEmpty()) {
Map<String, Object> orgAdminRoleMap = this.genRoleMap("org_admin", orgManagerOrdIdList);
Map<String, Object> orgAdminRoleMap = this.genRoleMap(RoleConstants.ORG_ADMIN, orgManagerOrdIdList);
roleMapList.add(orgAdminRoleMap);
}
//判断组织成员
List<String> orgMemberOrdIdList = this.getIdByExcelInfoAndIdDic(data.getUserIsOrgMember(), data.getOrgMemberOrganization(), orgNameMap);
if (!orgMemberOrdIdList.isEmpty()) {
Map<String, Object> orgMemberRoleMap = this.genRoleMap("org_member", orgMemberOrdIdList);
Map<String, Object> orgMemberRoleMap = this.genRoleMap(RoleConstants.ORG_MEMBER, orgMemberOrdIdList);
roleMapList.add(orgMemberRoleMap);
}
//判断测试经理
List<String> testManagerWorkspaceIdList = this.getIdByExcelInfoAndIdDic(data.getUserIsTestManager(), data.getTestManagerWorkspace(), workspaceNameMap);
if (!testManagerWorkspaceIdList.isEmpty()) {
Map<String, Object> testManagerRoleMap = this.genRoleMap("test_manager", testManagerWorkspaceIdList);
Map<String, Object> testManagerRoleMap = this.genRoleMap(RoleConstants.TEST_MANAGER, testManagerWorkspaceIdList);
roleMapList.add(testManagerRoleMap);
}
//判断测试人员
List<String> testgerWorkspaceIdList = this.getIdByExcelInfoAndIdDic(data.getUserIsTester(), data.getTesterWorkspace(), workspaceNameMap);
if (!testgerWorkspaceIdList.isEmpty()) {
Map<String, Object> testerRoleMap = this.genRoleMap("test_user", testgerWorkspaceIdList);
Map<String, Object> testerRoleMap = this.genRoleMap(RoleConstants.TEST_USER, testgerWorkspaceIdList);
roleMapList.add(testerRoleMap);
}
//判断只读用户
List<String> viewerWorkspaceIdList = this.getIdByExcelInfoAndIdDic(data.getUserIsViewer(), data.getViewerWorkspace(), workspaceNameMap);
if (!viewerWorkspaceIdList.isEmpty()) {
Map<String, Object> testViewerRoleMap = this.genRoleMap("test_viewer", viewerWorkspaceIdList);
Map<String, Object> testViewerRoleMap = this.genRoleMap(RoleConstants.TEST_VIEWER, viewerWorkspaceIdList);
roleMapList.add(testViewerRoleMap);
}
request.setRoles(roleMapList);

View File

@ -179,7 +179,7 @@ public class OrganizationService {
}
}
public List<OrganizationMemberDTO> findAllIdAndName(){
return extOrganizationMapper.findAllIdAndName();
public List<OrganizationMemberDTO> findIdAndNameByOrganizationId(String OrganizationID){
return extOrganizationMapper.findIdAndNameByOrganizationId(OrganizationID);
}
}

View File

@ -1,21 +1,16 @@
package io.metersphere.service;
import com.alibaba.excel.EasyExcelFactory;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtOrganizationMapper;
import io.metersphere.base.mapper.ext.ExtUserMapper;
import io.metersphere.base.mapper.ext.ExtUserRoleMapper;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.constants.UserSource;
import io.metersphere.commons.constants.UserStatus;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.CodingUtil;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.controller.ResultHolder;
import io.metersphere.controller.request.LoginRequest;
import io.metersphere.controller.request.member.AddMemberRequest;
@ -24,6 +19,7 @@ import io.metersphere.controller.request.member.QueryMemberRequest;
import io.metersphere.controller.request.member.UserRequest;
import io.metersphere.controller.request.organization.AddOrgMemberRequest;
import io.metersphere.controller.request.organization.QueryOrgMemberRequest;
import io.metersphere.controller.request.resourcepool.UserBatchProcessRequest;
import io.metersphere.dto.OrganizationMemberDTO;
import io.metersphere.dto.UserDTO;
import io.metersphere.dto.UserRoleDTO;
@ -43,6 +39,7 @@ import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.python.antlr.ast.Str;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -313,7 +310,6 @@ public class UserService {
userRoleExample.createCriteria().andUserIdEqualTo(userId);
List<UserRole> userRoles = userRoleMapper.selectByExample(userRoleExample);
List<String> list = userRoles.stream().map(UserRole::getSourceId).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(list)) {
if (list.contains(user.getLastWorkspaceId()) || list.contains(user.getLastOrganizationId())) {
user.setLastOrganizationId("");
@ -335,7 +331,6 @@ public class UserService {
if (userMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("user_email_already_exists"));
}
user.setUpdateTime(System.currentTimeMillis());
userMapper.updateByPrimaryKeySelective(user);
}
@ -651,7 +646,7 @@ public class UserService {
SessionUser user = SessionUtils.getUser();
for (int i = 1; i <= 2; i++) {
UserExcelData data = new UserExcelData();
data.setId("user_id_"+i);
data.setId("user_id_" + i);
data.setName(Translator.get("user") + i);
String workspace = "";
for (int workspaceIndex = 1; workspaceIndex <= i; workspaceIndex++) {
@ -690,18 +685,18 @@ public class UserService {
try {
Class clazz = new UserExcelDataFactory().getExcelDataByLocal();
Map<String,String> orgNameMap = new HashMap<>();
Map<String,String> workspaceNameMap = new HashMap<>();
Map<String, String> orgNameMap = new HashMap<>();
Map<String, String> workspaceNameMap = new HashMap<>();
List<OrganizationMemberDTO> organizationList = extOrganizationMapper.findAllIdAndName();
List<OrganizationMemberDTO> organizationList = extOrganizationMapper.findIdAndNameByOrganizationId("All");
for (OrganizationMemberDTO model : organizationList) {
orgNameMap.put(model.getName(),model.getId());
orgNameMap.put(model.getName(), model.getId());
}
List<WorkspaceDTO> workspaceList = workspaceService.findAllIdAndName();
List<WorkspaceDTO> workspaceList = workspaceService.findIdAndNameByOrganizationId("All");
for (WorkspaceDTO model : workspaceList) {
workspaceNameMap.put(model.getName(),model.getId());
workspaceNameMap.put(model.getName(), model.getId());
}
EasyExcelListener easyExcelListener = new UserDataListener(clazz,workspaceNameMap,orgNameMap);
EasyExcelListener easyExcelListener = new UserDataListener(clazz, workspaceNameMap, orgNameMap);
EasyExcelFactory.read(multipartFile.getInputStream(), clazz, easyExcelListener).sheet().doRead();
errList = easyExcelListener.getErrList();
} catch (Exception e) {
@ -722,4 +717,134 @@ public class UserService {
public List<String> selectAllId() {
return extUserMapper.selectAllId();
}
/**
* 批量处理用户信息:
* 添加用户到工作空间
* 添加用户权限
*
* @param request
*/
public void batchProcessUserInfo(UserBatchProcessRequest request) {
List<String> userIdList = this.selectIdByUserRequest(request);
String batchType = request.getBatchType();
for (String userID : userIdList) {
Map<String, List<String>> roleResourceIdMap = new HashMap<>();
if (StringUtils.equals(BatchProcessUserInfoType.ADD_WORKSPACE.name(), batchType)) {
//添加工作空间时默认赋予只读用户权限
String userRole = RoleConstants.TEST_VIEWER;
List<String> workspaceID = request.getBatchProcessValue();
if (workspaceID != null && !workspaceID.isEmpty()) {
roleResourceIdMap.put(userRole, workspaceID);
}
} else if (StringUtils.equals(BatchProcessUserInfoType.ADD_USER_ROLE.name(), batchType)) {
roleResourceIdMap = this.genRoleResourceMap(request.getBatchProcessValue());
}
if (!roleResourceIdMap.isEmpty()) {
UserRoleExample userRoleExample = new UserRoleExample();
userRoleExample.createCriteria().andUserIdEqualTo(userID);
List<UserRole> userRoles = userRoleMapper.selectByExample(userRoleExample);
UserRequest user = this.convert2UserRequest(userID, roleResourceIdMap, userRoles);
this.addUserWorkspaceAndRole(user, userRoles);
}
}
}
private List<String> selectIdByUserRequest(UserBatchProcessRequest request) {
if (request.getCondition() != null && request.getCondition().isSelectAll()) {
List<String> userIdList = new ArrayList<>();
if(StringUtils.isEmpty(request.getOrganizationId())){
userIdList = extUserMapper.selectIdsByQuery(request.getCondition());
}else{
//组织->成员 页面发起的请求
userIdList = extUserRoleMapper.selectIdsByQuery(request.getOrganizationId(),request.getCondition());
}
return userIdList;
} else {
return request.getIds();
}
}
private Map<String, List<String>> genRoleResourceMap(List<String> batchProcessValue) {
Map<String, List<String>> returnMap = new HashMap<>();
Map<String, String> workspaceToOrgMap = new HashMap<>();
for (String string : batchProcessValue) {
String[] stringArr = string.split("<->");
// string格式 资源ID<->权限<->workspace/org
if (stringArr.length == 3) {
String resourceID = stringArr[0];
String role = stringArr[1];
String sourceType = stringArr[2];
String finalResourceId = resourceID;
if (StringUtils.equalsIgnoreCase(sourceType, "workspace")) {
if (StringUtils.equalsAnyIgnoreCase(role, RoleConstants.ORG_ADMIN, RoleConstants.ORG_MEMBER)) {
finalResourceId = workspaceToOrgMap.get(resourceID);
if (finalResourceId == null) {
finalResourceId = workspaceService.getOrganizationIdById(resourceID);
workspaceToOrgMap.put(resourceID, finalResourceId);
}
}
}
if (StringUtils.isNotEmpty(finalResourceId)) {
if (returnMap.containsKey(role)) {
if (!returnMap.get(role).contains(finalResourceId)) {
returnMap.get(role).add(finalResourceId);
}
} else {
List<String> list = new ArrayList<>();
list.add(finalResourceId);
returnMap.put(role, list);
}
}
}
}
return returnMap;
}
private UserRequest convert2UserRequest(String userID, Map<String, List<String>> roleIdMap, List<UserRole> userRoles) {
Map<String, List<String>> userRoleAndResourceMap = userRoles.stream().collect(
Collectors.groupingBy(UserRole::getRoleId, Collectors.mapping(UserRole::getSourceId, Collectors.toList())));
List<Map<String, Object>> roles = new ArrayList<>();
for (Map.Entry<String, List<String>> entry : roleIdMap.entrySet()) {
String role = entry.getKey();
List<String> rawResourceIDList = entry.getValue();
List<String> resourceIDList = new ArrayList<>();
for (String resourceID : rawResourceIDList) {
if (userRoleAndResourceMap.containsKey(role) && userRoleAndResourceMap.get(role).contains(resourceID)) {
continue;
}
resourceIDList.add(resourceID);
}
if (resourceIDList.isEmpty()) {
continue;
}
Map<String, Object> roleMap = new HashMap<>();
roleMap.put("id", role);
roleMap.put("ids", resourceIDList);
roles.add(roleMap);
}
UserRequest request = new UserRequest();
request.setId(userID);
request.setRoles(roles);
return request;
}
public void addUserWorkspaceAndRole(UserRequest user, List<UserRole> userRoles) {
List<Map<String, Object>> roles = user.getRoles();
if (!roles.isEmpty()) {
insertUserRole(roles, user.getId());
}
List<String> list = userRoles.stream().map(UserRole::getSourceId).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(list)) {
if (list.contains(user.getLastWorkspaceId()) || list.contains(user.getLastOrganizationId())) {
user.setLastOrganizationId("");
user.setLastWorkspaceId("");
userMapper.updateByPrimaryKeySelective(user);
}
}
}
}

View File

@ -286,7 +286,11 @@ public class WorkspaceService {
return projectMapper.selectByExample(projectExample);
}
public List<WorkspaceDTO> findAllIdAndName(){
return extWorkspaceMapper.findAllIdAndName();
public String getOrganizationIdById(String resourceID) {
return extWorkspaceMapper.getOrganizationIdById(resourceID);
}
public List<WorkspaceDTO> findIdAndNameByOrganizationId(String organizationId) {
return extWorkspaceMapper.findIdAndNameByOrganizationId(organizationId);
}
}

View File

@ -50,4 +50,10 @@ public class TestPlanApiCaseController {
testPlanApiCaseService.deleteApiCaseBath(request);
}
@PostMapping("/batch/update/env")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void batchUpdateEnv(@RequestBody TestPlanApiCaseBatchRequest request) {
testPlanApiCaseService.batchUpdateEnv(request);
}
}

View File

@ -5,9 +5,20 @@ import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class TestPlanApiCaseBatchRequest extends TestPlanTestCase {
private List<String> ids;
/**
* 批量修改选中的数据
*/
private Map<String, String> selectRows;
/**
* 项目ID环境ID对应关系
*/
private Map<String, String> projectEnvMap;
}

View File

@ -154,11 +154,15 @@ public class TestCaseService {
// 如果上边字段全部相同去检查 steps remark
boolean isExt = false;
String caseRemark = testCase.getRemark();
if (StringUtils.isBlank(caseRemark)) {
caseRemark = "";
}
if (!CollectionUtils.isEmpty(caseList)) {
for (TestCaseWithBLOBs tc : caseList) {
String steps = tc.getSteps();
String remark = tc.getRemark();
if (StringUtils.equals(steps, testCase.getSteps()) && StringUtils.equals(remark, testCase.getRemark())) {
if (StringUtils.equals(steps, testCase.getSteps()) && StringUtils.equals(remark, caseRemark)) {
// MSException.throwException(Translator.get("test_case_already_exists"));
isExt = true;
}

View File

@ -12,6 +12,7 @@ import io.metersphere.api.dto.definition.request.MsThreadGroup;
import io.metersphere.api.service.ApiDefinitionExecResultService;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.ApiTestCaseExample;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.base.domain.TestPlanApiCaseExample;
@ -27,9 +28,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.*;
@Service
@Transactional(rollbackFor = Exception.class)
@ -129,4 +128,19 @@ public class TestPlanApiCaseService {
request.setIds(extTestPlanApiCaseMapper.getNotRelevanceCaseIds(planId, relevanceProjectIds));
deleteApiCaseBath(request);
}
public void batchUpdateEnv(TestPlanApiCaseBatchRequest request) {
// 批量修改用例环境
Map<String, String> rows = request.getSelectRows();
Set<String> ids = rows.keySet();
Map<String, String> env = request.getProjectEnvMap();
if (env != null && !env.isEmpty()) {
ids.forEach(id -> {
TestPlanApiCase apiCase = new TestPlanApiCase();
apiCase.setId(id);
apiCase.setEnvironmentId(env.get(rows.get(id)));
testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
});
}
}
}

View File

@ -40,6 +40,11 @@ user_import_phone_format_wrong=
user_import_email_format_wrong=
user_import_organization_not_fond=
user_import_workspace_not_fond=
org_admin=
org_member=
test_manager=
tester=
read_only_user=
module=
preconditions_optional=
step_tip_separate=

View File

@ -116,6 +116,11 @@ user_import_phone_format_wrong=Wrong phone format
user_import_email_format_wrong=Wrong email format
user_import_organization_not_fond=Organization is not found
user_import_workspace_not_fond=Workspace is not found
org_admin=Organization manager
org_member=Organization member
test_manager=Test manager
tester=Tester
read_only_user=Read-only user
module=Module
preconditions_optional=Preconditions optional
step_tip_separate=Each step is separated by a new line

View File

@ -116,6 +116,11 @@ user_import_phone_format_wrong=手机号码格式错误
user_import_email_format_wrong=电子邮箱格式错误
user_import_organization_not_fond=组织未找到
user_import_workspace_not_fond=工作空间未找到
org_admin=组织管理员
org_member=组织成员
test_manager=测试经理
tester=测试成员
read_only_user=只读用户
module=模块
preconditions_optional=前置条件选填
step_tip_separate=每个步骤以换行分隔

View File

@ -116,6 +116,11 @@ user_import_phone_format_wrong=手機號碼格式錯誤
user_import_email_format_wrong=電子郵箱格式錯誤
user_import_organization_not_fond=組織未找到
user_import_workspace_not_fond=工作空間未找到
org_admin=組織管理員
org_member=組織成員
test_manager=測試經理
tester=測試成員
read_only_user=只讀用戶
module=模塊
preconditions_optional=前置條件選填
step_tip_separate=每個步驟以換行分隔

View File

@ -391,7 +391,7 @@
},
{
title: this.$t('api_test.automation.scenario_import'),
show: this.operatingElements && this.operatingElements.indexOf('scenario') === 0,
show:this.showButton("scenario"),
titleColor: "#606266",
titleBgColor: "#F4F4F5",
icon: "movie",
@ -919,6 +919,7 @@
}
this.enableCookieShare = obj.enableCookieShare;
this.scenarioDefinition = obj.hashTree;
this.initProjectIds();
}
}
if (this.currentScenario.copy) {
@ -999,6 +1000,7 @@
this.reload();
},
initProjectIds() {
//
this.projectIds.clear();
this.scenarioDefinition.forEach(data=>{
let arr = jsonPath.query(data, "$..projectId");

View File

@ -6,7 +6,7 @@ export const ELEMENTS = new Map([
['JDBCSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
['TCPSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
['OT_IMPORT', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
['IfController', ["IfController", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
['IfController', ["IfController","scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
['LoopController', ["IfController", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
['ConstantTimer', []],
['JSR223Processor', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],

View File

@ -5,7 +5,22 @@
<ms-table-header :condition.sync="condition" @search="initTableData" @create="create"
:create-tip="$t('member.create')" :title="$t('commons.member')"/>
</template>
<el-table border class="adjust-table" :data="tableData" style="width: 100%">
<el-table border class="adjust-table ms-select-all-fixed" :data="tableData" style="width: 100%"
@select-all="handleSelectAll"
@select="handleSelect"
ref="userTable">
<el-table-column type="selection" width="50"/>
<ms-table-header-select-popover v-show="total>0"
:page-size="pageSize>total?total:pageSize"
:total="total"
@selectPageAll="isSelectDataAll(false)"
@selectAll="isSelectDataAll(true)"/>
<el-table-column v-if="!referenced" width="30" min-width="30" :resizable="false" align="center">
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
</template>
</el-table-column>
<el-table-column prop="id" label="ID"/>
<el-table-column prop="name" :label="$t('commons.username')"/>
<el-table-column prop="email" :label="$t('commons.email')"/>
@ -106,6 +121,7 @@
@confirm="updateOrgMember('updateUserForm')"/>
</template>
</el-dialog>
<user-cascader :lable="batchAddLable" :title="batchAddTitle" @confirm="cascaderConfirm" ref="cascaderDialog"></user-cascader>
</div>
</template>
@ -116,11 +132,22 @@
import MsRolesTag from "../../common/components/MsRolesTag";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {getCurrentUser, listenGoBack, removeGoBackListener} from "../../../../common/js/utils";
import {getCurrentProjectID, getCurrentOrganizationId,getCurrentUser, listenGoBack, removeGoBackListener} from "../../../../common/js/utils";
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
import {
_handleSelect,
_handleSelectAll,
getSelectDataCounts,
setUnSelectIds,
toggleAllSelection
} from "@/common/js/tableUtils";
import UserCascader from "@/business/components/settings/system/components/UserCascader";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
export default {
name: "MsOrganizationMember",
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter,
MsTableHeaderSelectPopover,UserCascader,ShowMoreBtn},
activated() {
this.initTableData();
},
@ -147,6 +174,21 @@
total: 0,
options: [],
loading: false,
selectDataCounts: 0,
batchAddLable: this.$t('project.please_choose_workspace'),
batchAddTitle: this.$t('project.batch_choose_workspace'),
selectRows: new Set(),
referenced: false,
batchAddWorkspaceOptions:[],
batchAddUserRoleOptions:[],
buttons: [
{
name: this.$t('user.button.add_workspace_batch'), handleClick: this.addWorkspaceBatch
},
{
name: this.$t('user.button.add_user_role_batch'), handleClick: this.addUserRoleBatch
}
],
}
},
methods: {
@ -275,7 +317,78 @@
} else {
this.options = [];
}
}
},
initWorkspaceBatchProcessDataStruct(isShow){
let organizationId = getCurrentOrganizationId();
this.$get("/user/getWorkspaceDataStruct/"+organizationId, response => {
this.batchAddWorkspaceOptions = response.data;
if(isShow){
this.$refs.cascaderDialog.open('ADD_WORKSPACE',this.batchAddWorkspaceOptions);
}
});
},
initRoleBatchProcessDataStruct(isShow){
let organizationId = getCurrentOrganizationId();
this.$get("/user/getUserRoleDataStruct/"+organizationId, response => {
this.batchAddUserRoleOptions = response.data;
if(isShow){
this.$refs.cascaderDialog.open('ADD_USER_ROLE',this.batchAddUserRoleOptions);
}
});
},
handleSelectAll(selection) {
_handleSelectAll(this, selection, this.tableData, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.$emit('selection', selection);
},
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.$emit('selection', selection);
},
isSelectDataAll(data) {
this.condition.selectAll = data;
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
toggleAllSelection(this.$refs.userTable, this.tableData, this.selectRows);
},
addWorkspaceBatch(){
if(this.batchAddWorkspaceOptions.length == 0){
this.initWorkspaceBatchProcessDataStruct(true);
}else{
this.$refs.cascaderDialog.open('ADD_WORKSPACE',this.batchAddWorkspaceOptions);
}
},
addUserRoleBatch(){
if(this.batchAddUserRoleOptions.length == 0){
this.initRoleBatchProcessDataStruct(true);
}else{
this.$refs.cascaderDialog.open('ADD_USER_ROLE',this.batchAddUserRoleOptions);
}
},
cascaderConfirm(batchProcessTypeParam,selectValueArr){
if(selectValueArr.length == 0){
this.$success(this.$t('commons.modify_success'));
}
let params = {};
params = this.buildBatchParam(params);
params.organizationId = getCurrentOrganizationId();
params.batchType = batchProcessTypeParam;
params.batchProcessValue = selectValueArr;
this.$post('/user/special/batchProcessUserInfo', params, () => {
this.$success(this.$t('commons.modify_success'));
this.initTableData();
this.$refs.cascaderDialog.close();
});
},
buildBatchParam(param) {
param.ids = Array.from(this.selectRows).map(row => row.id);
param.projectId = getCurrentProjectID();
param.condition = this.condition;
return param;
},
},
}
</script>
@ -296,4 +409,7 @@
width: 100%;
}
/deep/ .ms-select-all-fixed th:nth-child(2) .el-icon-arrow-down {
top: -5px;
}
</style>

View File

@ -8,9 +8,25 @@
</template>
<el-table border class="adjust-table" :data="tableData" style="width: 100%">
<el-table border class="adjust-table ms-select-all-fixed" :data="tableData" style="width: 100%"
@select-all="handleSelectAll"
@select="handleSelect"
ref="userTable">
<el-table-column type="selection" width="50"/>
<ms-table-header-select-popover v-show="total>0"
:page-size="pageSize>total?total:pageSize"
:total="total"
@selectPageAll="isSelectDataAll(false)"
@selectAll="isSelectDataAll(true)"/>
<el-table-column v-if="!referenced" width="30" min-width="30" :resizable="false" align="center">
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
</template>
</el-table-column>
<el-table-column prop="id" label="ID"/>
<el-table-column prop="name" :label="$t('commons.name')" width="200"/>
<el-table-column :label="$t('commons.role')" width="120">
<template v-slot:default="scope">
<ms-roles-tag :roles="scope.row.roles"/>
@ -325,6 +341,7 @@
</span>
</el-dialog>
<user-import ref="userImportDialog" @refreshAll="search"></user-import>
<user-cascader :lable="batchAddLable" :title="batchAddTitle" @confirm="cascaderConfirm" ref="cascaderDialog"></user-cascader>
</div>
</template>
@ -335,12 +352,22 @@ import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import {hasRole, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import {getCurrentProjectID, getUUID, hasRole, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import MsRolesTag from "../../common/components/MsRolesTag";
import {ROLE_ADMIN} from "@/common/js/constants";
import {getCurrentUser} from "../../../../common/js/utils";
import {PHONE_REGEX} from "@/common/js/regex";
import UserImport from "@/business/components/settings/system/components/UserImport";
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
import {
_handleSelect,
_handleSelectAll,
getSelectDataCounts,
setUnSelectIds,
toggleAllSelection
} from "@/common/js/tableUtils";
import UserCascader from "@/business/components/settings/system/components/UserCascader";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
export default {
name: "MsUser",
@ -352,19 +379,28 @@ export default {
MsDialogFooter,
MsTableOperatorButton,
MsRolesTag,
UserImport
UserImport,
MsTableHeaderSelectPopover,
UserCascader,
ShowMoreBtn
},
data() {
return {
referenced: false,
queryPath: '/user/special/list',
deletePath: '/user/special/delete/',
createPath: '/user/special/add',
updatePath: '/user/special/update',
editPasswordPath: '/user/special/password',
batchAddLable: this.$t('project.please_choose_workspace'),
batchAddTitle: this.$t('project.batch_choose_workspace'),
batchAddWorkspaceOptions:[],
batchAddUserRoleOptions:[],
result: {},
currentUserId: '',
createVisible: false,
updateVisible: false,
selectDataCounts: 0,
editPasswordVisible: false,
btnAddRole: false,
multipleSelection: [],
@ -373,6 +409,7 @@ export default {
pageSize: 10,
total: 0,
condition: {},
selectRows: new Set(),
tableData: [],
form: {
roles: [{
@ -381,6 +418,14 @@ export default {
},
checkPasswordForm: {},
ruleForm: {},
buttons: [
{
name: this.$t('user.button.add_workspace_batch'), handleClick: this.addWorkspaceBatch
},
{
name: this.$t('user.button.add_user_role_batch'), handleClick: this.addUserRoleBatch
}
],
rule: {
id: [
{required: true, message: this.$t('user.input_id'), trigger: 'blur'},
@ -530,6 +575,8 @@ export default {
if (!hasRole(ROLE_ADMIN)) {
return;
}
this.selectRows = new Set();
this.condition.selectAll = false;
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
@ -629,10 +676,89 @@ export default {
}
return value;
})
}
},
initWorkspaceBatchProcessDataStruct(isShow){
this.$get("/user/getWorkspaceDataStruct/All", response => {
this.batchAddWorkspaceOptions = response.data;
if(isShow){
this.$refs.cascaderDialog.open('ADD_WORKSPACE',this.batchAddWorkspaceOptions);
}
});
},
initRoleBatchProcessDataStruct(isShow){
this.$get("/user/getUserRoleDataStruct/All", response => {
this.batchAddUserRoleOptions = response.data;
if(isShow){
this.$refs.cascaderDialog.open('ADD_USER_ROLE',this.batchAddUserRoleOptions);
}
});
},
handleSelectAll(selection) {
_handleSelectAll(this, selection, this.tableData, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.$emit('selection', selection);
},
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.$emit('selection', selection);
},
isSelectDataAll(data) {
this.condition.selectAll = data;
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
toggleAllSelection(this.$refs.userTable, this.tableData, this.selectRows);
},
addWorkspaceBatch(){
if(this.batchAddWorkspaceOptions.length == 0){
this.initWorkspaceBatchProcessDataStruct(true);
}else{
this.$refs.cascaderDialog.open('ADD_WORKSPACE',this.batchAddWorkspaceOptions);
}
},
addUserRoleBatch(){
if(this.batchAddUserRoleOptions.length == 0){
this.initRoleBatchProcessDataStruct(true);
}else{
this.$refs.cascaderDialog.open('ADD_USER_ROLE',this.batchAddUserRoleOptions);
}
},
cascaderConfirm(batchProcessTypeParam,selectValueArr){
if(selectValueArr.length == 0){
this.$success(this.$t('commons.modify_success'));
}
let params = {};
params = this.buildBatchParam(params);
params.batchType = batchProcessTypeParam;
params.batchProcessValue = selectValueArr;
this.$post('/user/special/batchProcessUserInfo', params, () => {
this.$success(this.$t('commons.modify_success'));
this.search();
this.$refs.cascaderDialog.close();
});
},
buildBatchParam(param) {
param.ids = Array.from(this.selectRows).map(row => row.id);
param.projectId = getCurrentProjectID();
param.condition = this.condition;
return param;
},
}
}
</script>
<style scoped>
/deep/ .el-table__fixed-right {
height: 100% !important;
}
/deep/ .el-table__fixed {
height: 110px !important;
}
/deep/ .ms-select-all-fixed th:nth-child(2) .el-icon-arrow-down {
top: -5px;
}
</style>

View File

@ -0,0 +1,124 @@
<template>
<el-dialog class="user-casecader" :title="title" :visible.sync="dialogVisible"
@close="close">
<div class="block" >
<!-- <el-row>-->
<!-- <span class="demonstration" v-html="lable"></span>-->
<!-- </el-row>-->
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item prop="workspace" label-width="0px">
<el-cascader
:options="options"
:props="props"
v-model="selectedIds"
ref="cascaderSelector"
style="width:100%"
:key="isResouceShow"
clearable></el-cascader>
</el-form-item>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<ms-dialog-footer
@cancel="close()"
@confirm="confirm()"/>
</span>
</el-dialog>
</template>
<script>
import ElUploadList from "element-ui/packages/upload/src/upload-list";
import MsTableButton from '../../../../components/common/components/MsTableButton';
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
export default {
name: "UserImport",
components: {ElUploadList, MsTableButton,MsDialogFooter},
data() {
var validateSelect = (rule, value, callback) => {
let checkNodes = this.$refs.cascaderSelector.checkedNodes;
if(checkNodes.length==0){
callback(new Error(this.$t('workspace.select')));
}
callback();
};
return {
ruleForm: {
workspace: '',
},
rules: {
workspace: [
{ validator: validateSelect, message: this.$t('workspace.select'), trigger: 'change' }
],
},
selectedIds:[],
isResouceShow:0,
props: { multiple: true },
dialogVisible: false,
isLoading: false,
batchProcessType:'',
options:[],
};
},
props: {
title: {
type: String,
default: ''
},
lable: {
type: String,
default: ''
},
},
methods: {
close() {
removeGoBackListener(this.close);
this.dialogVisible = false;
this.selectedIds=[];
++ this.isResouceShow;
this.options = [];
this.$refs['ruleForm'].resetFields();
},
open(batchProcessType,optionsParam) {
listenGoBack(this.close);
this.dialogVisible = true;
this.batchProcessType = batchProcessType;
this.options = optionsParam;
if(this.batchProcessType == 'ADD_WORKSPACE'){
this.rules.workspace[0].message = this.$t('workspace.select');
}else{
this.rules.workspace[0].message = this.$t('role.please_choose_role');
}
},
confirm(){
this.$refs.ruleForm.validate((valid) => {
if (valid) {
let checkNodes = this.$refs.cascaderSelector.checkedNodes;
let selectValueArr = [];
for (let i = 0; i < checkNodes.length; i++) {
selectValueArr.push(checkNodes[i].value);
}
this.$emit('confirm',this.batchProcessType,selectValueArr)
} else {
return false;
}
});
}
}
}
</script>
<style>
</style>
<style scoped>
.user-casecader >>> .el-dialog {
width: 400px;
}
/deep/ .el-form-item__content{
margin-left: 0px;
}
</style>

View File

@ -5,7 +5,23 @@
<ms-table-header :condition.sync="condition" @search="initTableData" @create="create"
:create-tip="$t('member.create')" :title="$t('commons.member')"/>
</template>
<el-table border class="adjust-table" :data="tableData" style="width: 100%">
<el-table border class="adjust-table ms-select-all-fixed" :data="tableData" style="width: 100%"
@select-all="handleSelectAll"
@select="handleSelect"
ref="userTable">
<el-table-column type="selection" width="50"/>
<ms-table-header-select-popover v-show="total>0"
:page-size="pageSize>total?total:pageSize"
:total="total"
@selectPageAll="isSelectDataAll(false)"
@selectAll="isSelectDataAll(true)"/>
<el-table-column v-if="!referenced" width="30" min-width="30" :resizable="false" align="center">
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
</template>
</el-table-column>
<el-table-column prop="id" label="ID"/>
<el-table-column prop="name" :label="$t('commons.username')"/>
<el-table-column prop="email" :label="$t('commons.email')"/>
@ -97,7 +113,7 @@
@confirm="updateWorkspaceMember('updateUserForm')"/>
</template>
</el-dialog>
<user-cascader :lable="batchAddLable" :title="batchAddTitle" @confirm="cascaderConfirm" ref="cascaderDialog"></user-cascader>
</div>
</template>
@ -108,11 +124,27 @@
import MsRolesTag from "../../common/components/MsRolesTag";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {getCurrentUser, listenGoBack, removeGoBackListener} from "../../../../common/js/utils";
import {
getCurrentOrganizationId, getCurrentProjectID,
getCurrentUser,
listenGoBack,
removeGoBackListener
} from "../../../../common/js/utils";
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
import {
_handleSelect,
_handleSelectAll,
getSelectDataCounts,
setUnSelectIds,
toggleAllSelection
} from "@/common/js/tableUtils";
import UserCascader from "@/business/components/settings/system/components/UserCascader";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
export default {
name: "MsMember",
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter,
MsTableHeaderSelectPopover,UserCascader,ShowMoreBtn},
data() {
return {
result: {},
@ -135,6 +167,17 @@
currentPage: 1,
pageSize: 10,
total: 0,
selectDataCounts: 0,
batchAddLable: this.$t('project.please_choose_workspace'),
batchAddTitle: this.$t('project.batch_choose_workspace'),
selectRows: new Set(),
referenced: false,
batchAddUserRoleOptions:[],
buttons: [
{
name: this.$t('user.button.add_user_role_batch'), handleClick: this.addUserRoleBatch
}
],
}
},
activated: function () {
@ -283,9 +326,62 @@
return (user.email.indexOf(queryString.toLowerCase()) === 0 || user.id.indexOf(queryString.toLowerCase()) === 0);
};
},
handleSelect(item) {
this.$set(this.form, "userId", item.id);
}
initRoleBatchProcessDataStruct(isShow){
let organizationId = getCurrentOrganizationId();
this.$get("/user/getWorkspaceUserRoleDataStruct/"+organizationId, response => {
this.batchAddUserRoleOptions = response.data;
if(isShow){
this.$refs.cascaderDialog.open('ADD_USER_ROLE',this.batchAddUserRoleOptions);
}
});
},
handleSelectAll(selection) {
_handleSelectAll(this, selection, this.tableData, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.$emit('selection', selection);
},
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.$emit('selection', selection);
this.$set(this.form, "userId", selection.id);
},
isSelectDataAll(data) {
this.condition.selectAll = data;
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
toggleAllSelection(this.$refs.userTable, this.tableData, this.selectRows);
},
addUserRoleBatch(){
if(this.batchAddUserRoleOptions.length == 0){
this.initRoleBatchProcessDataStruct(true);
}else{
this.$refs.cascaderDialog.open('ADD_USER_ROLE',this.batchAddUserRoleOptions);
}
},
cascaderConfirm(batchProcessTypeParam,selectValueArr){
if(selectValueArr.length == 0){
this.$success(this.$t('commons.modify_success'));
}
let params = {};
params = this.buildBatchParam(params);
params.organizationId = getCurrentOrganizationId();
params.batchType = batchProcessTypeParam;
params.batchProcessValue = selectValueArr;
this.$post('/user/special/batchProcessUserInfo', params, () => {
this.$success(this.$t('commons.modify_success'));
this.initTableData();
this.$refs.cascaderDialog.close();
});
},
buildBatchParam(param) {
param.ids = Array.from(this.selectRows).map(row => row.id);
param.projectId = getCurrentProjectID();
param.condition = this.condition;
return param;
},
}
}
</script>
@ -314,4 +410,7 @@
width: 100%;
}
/deep/ .ms-select-all-fixed th:nth-child(2) .el-icon-arrow-down {
top: -5px;
}
</style>

View File

@ -14,7 +14,11 @@
<el-option v-for="(type, index) in typeArr" :key="index" :value="type.id" :label="type.name"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('test_track.case.updated_attr_value')" prop="value">
<el-form-item v-if="form.type === 'projectEnv'" :label="$t('test_track.case.updated_attr_value')">
<env-popover :env-map="projectEnvMap" :project-ids="projectIds" @setProjectEnvMap="setProjectEnvMap"
:project-list="projectList" ref="envPopover"/>
</el-form-item>
<el-form-item v-else :label="$t('test_track.case.updated_attr_value')" prop="value">
<el-select v-model="form.value" style="width: 80%" :filterable="filterable">
<el-option v-for="(option, index) in options" :key="index" :value="option.id" :label="option.name">
<div v-if="option.email">
@ -35,11 +39,13 @@
<script>
import MsDialogFooter from "../../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover";
export default {
name: "BatchEdit",
components: {
EnvPopover,
MsDialogFooter
},
props: {
@ -50,7 +56,7 @@
default() {
return this.$t('test_track.case.batch_operate')
}
}
},
},
data() {
return {
@ -63,12 +69,22 @@
},
options: [],
filterable: false,
projectList: [],
projectIds: new Set(),
selectRows: new Set(),
projectEnvMap: new Map()
}
},
methods: {
submit(form) {
this.$refs[form].validate((valid) => {
if (valid) {
this.form.projectEnvMap = this.projectEnvMap;
if (this.form.type === 'projectEnv') {
if (!this.$refs.envPopover.checkEnv()) {
return false;
}
}
this.$emit("batchEdit", this.form);
this.dialogVisible = false;
} else {
@ -76,15 +92,25 @@
}
});
},
setProjectEnvMap(projectEnvMap) {
this.projectEnvMap = projectEnvMap;
},
open(size) {
this.dialogVisible = true;
if (size) {
this.size = size;
} else {
// this.size = this.$parent.selectRows.size;
this.size = this.$parent.selectDataCounts;
}
listenGoBack(this.handleClose);
this.getWsProjects();
},
setSelectRows(rows) {
this.selectRows = rows;
this.projectIds.clear();
this.selectRows.forEach(row => {
this.projectIds.add(row.projectId)
})
},
handleClose() {
this.form = {};
@ -104,7 +130,12 @@
return;
}
});
}
},
getWsProjects() {
this.$get("/project/listAll", res => {
this.projectList = res.data;
})
},
}
}
</script>

View File

@ -129,6 +129,10 @@
<ms-run :debug="false" :type="'API_PLAN'" :reportId="reportId" :run-data="runData"
@runRefresh="runRefresh" ref="runTest"/>
<!-- 批量编辑 -->
<batch-edit :dialog-title="$t('test_track.case.batch_edit_case')" :type-arr="typeArr" :value-arr="valueArr"
:select-row="selectRows" ref="batchEdit" @batchEdit="batchEdit"/>
</el-card>
</div>
@ -145,9 +149,9 @@ import ApiCaseList from "../../../../../api/definition/components/case/ApiCaseLi
import MsContainer from "../../../../../common/components/MsContainer";
import MsBottomContainer from "../../../../../api/definition/components/BottomContainer";
import ShowMoreBtn from "../../../../case/components/ShowMoreBtn";
import MsBatchEdit from "../../../../../api/definition/components/basis/BatchEdit";
import BatchEdit from "@/business/components/track/case/components/BatchEdit";
import {API_METHOD_COLOUR, CASE_PRIORITY, RESULT_MAP} from "../../../../../api/definition/model/JsonData";
import {getCurrentProjectID, getCurrentUser} from "@/common/js/utils";
import {getCurrentProjectID, strMapToObj} from "@/common/js/utils";
import ApiListContainer from "../../../../../api/definition/components/list/ApiListContainer";
import PriorityTableItem from "../../../../common/tableItems/planview/PriorityTableItem";
import {getBodyUploadFiles, getUUID} from "../../../../../../../common/js/utils";
@ -156,16 +160,17 @@ import MsRun from "../../../../../api/definition/components/Run";
import TestPlanApiCaseResult from "./TestPlanApiCaseResult";
import TestPlan from "../../../../../api/definition/components/jmeter/components/test-plan";
import ThreadGroup from "../../../../../api/definition/components/jmeter/components/thread-group";
import {TEST_CASE_LIST, TEST_PLAN_API_CASE, WORKSPACE_ID} from "@/common/js/constants";
import {TEST_PLAN_API_CASE, WORKSPACE_ID} from "@/common/js/constants";
import {_filter, _sort, getLabel} from "@/common/js/tableUtils";
import HeaderCustom from "@/business/components/common/head/HeaderCustom";
import {Test_Plan_Api_Case, Track_Test_Case} from "@/business/components/common/model/JsonData";
import {Test_Plan_Api_Case} from "@/business/components/common/model/JsonData";
import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate";
export default {
name: "TestPlanApiCaseList",
components: {
BatchEdit,
HeaderLabelOperate,
HeaderCustom,
TestPlanApiCaseResult,
@ -182,7 +187,6 @@ export default {
MsContainer,
MsBottomContainer,
ShowMoreBtn,
MsBatchEdit
},
data() {
return {
@ -198,10 +202,11 @@ export default {
selectRows: new Set(),
buttons: [
{name: this.$t('test_track.case.batch_unlink'), handleClick: this.handleDeleteBatch},
{name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute}
{name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute},
{name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleBatchEdit}
],
typeArr: [
{id: 'priority', name: this.$t('test_track.case.priority')},
{id: 'projectEnv', name: this.$t('api_test.definition.request.run_env')},
],
priorityFilters: [
{text: 'P0', value: 'P0'},
@ -212,6 +217,7 @@ export default {
valueArr: {
priority: CASE_PRIORITY,
userId: [],
projectEnv: []
},
methodColorMap: new Map(API_METHOD_COLOUR),
tableData: [],
@ -225,7 +231,9 @@ export default {
reportId: "",
response: {},
rowLoading: "",
userFilters: []
userFilters: [],
projectIds: [],
projectList: []
}
},
props: {
@ -342,15 +350,6 @@ export default {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
let arr = Array.from(this.selectRows);
// 1
if (this.selectRows.size === 1) {
this.$set(arr[0], "showMore", false);
} else if (this.selectRows.size === 2) {
arr.forEach(row => {
this.$set(row, "showMore", true);
})
}
},
showExecResult(row) {
this.$emit('showExecResult', row);
@ -369,16 +368,10 @@ export default {
},
handleSelectAll(selection) {
if (selection.length > 0) {
if (selection.length === 1) {
selection.hashTree = [];
this.selectRows.add(selection[0]);
} else {
this.tableData.forEach(item => {
item.hashTree = [];
this.$set(item, "showMore", true);
this.selectRows.add(item);
});
}
this.tableData.forEach(item => {
this.$set(item, "showMore", true);
this.selectRows.add(item);
});
} else {
this.selectRows.clear();
this.tableData.forEach(row => {
@ -449,16 +442,27 @@ export default {
this.reportId = getUUID().substring(0, 8);
});
},
handleBatchEdit() {
this.$refs.batchEdit.open(this.selectRows.size);
this.$refs.batchEdit.setSelectRows(this.selectRows);
},
batchEdit(form) {
let arr = Array.from(this.selectRows);
let ids = arr.map(row => row.id);
let param = {};
param[form.type] = form.value;
param.ids = ids;
this.$post('/api/testcase/batch/edit', param, () => {
this.$success(this.$t('commons.save_success'));
this.initTable();
});
//
if (form.type === 'projectEnv') {
let map = new Map();
param.projectEnvMap = strMapToObj(form.projectEnvMap);
this.selectRows.forEach(row => {
map[row.id] = row.projectId;
})
param.selectRows = map;
this.$post('/test/plan/api/case/batch/update/env', param, () => {
this.$success(this.$t('commons.save_success'));
this.initTable();
});
} else {
//
}
},
handleBatchExecute() {
this.selectRows.forEach(row => {

@ -1 +1 @@
Subproject commit e8f074b11af55c44a43a413e239def2ddf58bd3c
Subproject commit 46f1ad6670d023844e7f8f09f89ef9fe9b466bbf

View File

@ -194,6 +194,9 @@ export default {
module: {
select_module: "Select module",
default_module: "Default module",
},
report_statistics: {
title: "Report statistics"
}
},
license: {
@ -237,6 +240,7 @@ export default {
search_by_name: 'Search by name',
organization_name: 'Organization Name',
please_choose_organization: 'Please Choose Organization',
batch_choose_workspace: 'Please choose organizations',
please_select_a_workspace_first: 'Please select a workspace first!',
none: 'None Workspace',
select: 'Select Workspace',
@ -372,7 +376,11 @@ export default {
delete_confirm: 'Are you sure you want to delete this User?',
apikey_delete_confirm: 'Are you sure you want to delete this API Key?',
input_id_placeholder: 'Please enter ID (Chinese characters are not supported)',
source: 'Source'
source: 'Source',
button:{
add_workspace_batch: 'Batch add user to workspace',
add_user_role_batch: 'Batch add user role',
}
},
role: {
please_choose_role: 'Please Choose Role',

View File

@ -326,6 +326,7 @@ export default {
input_name: '请输入项目名称',
owning_workspace: '所属工作空间',
please_choose_workspace: '请选择工作空间',
batch_choose_workspace: '批量选择工作空间',
special_characters_are_not_supported: '格式错误(不支持特殊字符,且不能以\'-\'开头结尾)',
tapd_id: 'TAPD项目ID',
jira_key: 'JIRA项目key',
@ -373,7 +374,11 @@ export default {
delete_confirm: '这个用户确定要删除吗?',
apikey_delete_confirm: '这个 API Key 确定要删除吗?',
input_id_placeholder: '请输入ID (不支持中文)',
source: '用户来源'
source: '用户来源',
button:{
add_workspace_batch: '批量添加到工作空间',
add_user_role_batch: '批量添加角色',
}
},
role: {
please_choose_role: '请选择角色',

View File

@ -195,6 +195,9 @@ export default {
module: {
select_module: "選擇模塊",
default_module: "默認模塊",
},
report_statistics: {
title: "報表統計"
}
},
license: {
@ -323,6 +326,7 @@ export default {
input_name: '請輸入項目名稱',
owning_workspace: '所屬工作空間',
please_choose_workspace: '請選擇工作空間',
batch_choose_workspace: '批量選擇工作空間',
special_characters_are_not_supported: '格式錯誤(不支持特殊字符,且不能以\'-\'開頭結尾)',
tapd_id: 'TAPD項目ID',
jira_key: 'JIRA項目key',
@ -370,7 +374,11 @@ export default {
delete_confirm: '這個用戶確定要刪除嗎?',
apikey_delete_confirm: '這個 API Key 確定要刪除嗎?',
input_id_placeholder: '請輸入ID (不支持中文)',
source: '用戶來源'
source: '用戶來源',
button:{
add_workspace_batch: '批量添加到工作空間',
add_user_role_batch: '批量添加角色',
}
},
role: {
please_choose_role: '請選擇角色',