Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
q4speed 2020-05-19 17:48:28 +08:00
commit d94f031a12
21 changed files with 420 additions and 194 deletions

View File

@ -17,15 +17,14 @@ import io.metersphere.controller.request.member.SetAdminRequest;
import io.metersphere.controller.request.organization.AddOrgMemberRequest; import io.metersphere.controller.request.organization.AddOrgMemberRequest;
import io.metersphere.controller.request.organization.QueryOrgMemberRequest; import io.metersphere.controller.request.organization.QueryOrgMemberRequest;
import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserDTO;
import io.metersphere.dto.UserRoleDTO;
import io.metersphere.service.OrganizationService; import io.metersphere.service.OrganizationService;
import io.metersphere.service.UserService; import io.metersphere.service.UserService;
import io.metersphere.service.WorkspaceService; import io.metersphere.service.WorkspaceService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
@ -54,6 +53,12 @@ public class UserController {
return PageUtils.setPageInfo(page, userService.getUserListWithRequest(request)); return PageUtils.setPageInfo(page, userService.getUserListWithRequest(request));
} }
@GetMapping("/special/user/role/{userId}")
@RequiresRoles(RoleConstants.ADMIN)
public UserRoleDTO getUserRole(@PathVariable("userId") String userId) {
return userService.getUserRole(userId);
}
@GetMapping("/special/delete/{userId}") @GetMapping("/special/delete/{userId}")
@RequiresRoles(RoleConstants.ADMIN) @RequiresRoles(RoleConstants.ADMIN)
public void deleteUser(@PathVariable(value = "userId") String userId) { public void deleteUser(@PathVariable(value = "userId") String userId) {

View File

@ -0,0 +1,18 @@
package io.metersphere.dto;
import io.metersphere.base.domain.Role;
import io.metersphere.base.domain.UserRole;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class UserRoleDTO {
private String userId;
private List<Role> roles;
private List<UserRole> userRoles;
private static final long serialVersionUID = 1L;
}

View File

@ -14,52 +14,52 @@ public class TestCaseExcelData {
@NotBlank @NotBlank
@Length(max=50) @Length(max=50)
@ExcelProperty("用例名称") @ExcelProperty("{test_case_name}")
private String name; private String name;
@NotBlank @NotBlank
@Length(max=1000) @Length(max=1000)
@ExcelProperty("所属模块") @ExcelProperty("{test_case_module}")
@ColumnWidth(30) @ColumnWidth(30)
@Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}") @Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}")
private String nodePath; private String nodePath;
@NotBlank @NotBlank
@ExcelProperty("用例类型") @ExcelProperty("{test_case_type}")
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}") @Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}")
private String type; private String type;
@NotBlank @NotBlank
@ExcelProperty("维护人") @ExcelProperty("{test_case_maintainer}")
private String maintainer; private String maintainer;
@NotBlank @NotBlank
@ExcelProperty("优先级") @ExcelProperty("{test_case_priority}")
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}") @Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
private String priority; private String priority;
@NotBlank @NotBlank
@ExcelProperty("测试方式") @ExcelProperty("{test_case_method}")
@Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}") @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
private String method; private String method;
@ColumnWidth(50) @ColumnWidth(50)
@ExcelProperty("前置条件") @ExcelProperty("{test_case_prerequisite}")
@Length(min=0, max=1000) @Length(min=0, max=1000)
private String prerequisite; private String prerequisite;
@ColumnWidth(50) @ColumnWidth(50)
@ExcelProperty("备注") @ExcelProperty("{test_case_remark}")
@Length(max=1000) @Length(max=1000)
private String remark; private String remark;
@ColumnWidth(50) @ColumnWidth(50)
@ExcelProperty("步骤描述") @ExcelProperty("{test_case_step_desc}")
@Length(max=1000) @Length(max=1000)
private String stepDesc; private String stepDesc;
@ColumnWidth(50) @ColumnWidth(50)
@ExcelProperty("预期结果") @ExcelProperty("{test_case_step_result}")
@Length(max=1000) @Length(max=1000)
private String stepResult; private String stepResult;
} }

View File

@ -6,24 +6,22 @@ import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.StringUtils;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.excel.utils.ExcelValidateHelper;
import io.metersphere.excel.domain.ExcelErrData; import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.utils.EasyExcelI18nTranslator;
import io.metersphere.excel.utils.ExcelValidateHelper;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import org.springframework.stereotype.Component;
import javax.annotation.Resource; import java.lang.reflect.*;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*; import java.util.*;
@Component
public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> { public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
protected List<ExcelErrData<T>> errList = new ArrayList<>(); protected List<ExcelErrData<T>> errList = new ArrayList<>();
protected List<T> list = new ArrayList<>(); protected List<T> list = new ArrayList<>();
protected EasyExcelI18nTranslator easyExcelI18nTranslator;
/** /**
* 每隔2000条存储数据库然后清理list 方便内存回收 * 每隔2000条存储数据库然后清理list 方便内存回收
*/ */
@ -31,16 +29,15 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
protected Class<T> clazz; protected Class<T> clazz;
@Resource
ExcelValidateHelper excelValidateHelper;
public EasyExcelListener(){ public EasyExcelListener(){
Type type = getClass().getGenericSuperclass(); Type type = getClass().getGenericSuperclass();
this.clazz = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0]; this.clazz = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
this.easyExcelI18nTranslator = new EasyExcelI18nTranslator(this.clazz);
this.easyExcelI18nTranslator.translateExcelProperty();
} }
/** /**
* 这个条数据解析都会调用 * 每条数据解析都会调用
* *
* @param t * @param t
* @param analysisContext * @param analysisContext
@ -51,7 +48,7 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
Integer rowIndex = analysisContext.readRowHolder().getRowIndex(); Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
try { try {
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据 //根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
errMsg = excelValidateHelper.validateEntity(t); errMsg = ExcelValidateHelper.validateEntity(t);
//自定义校验规则 //自定义校验规则
errMsg = validate(t, errMsg); errMsg = validate(t, errMsg);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
@ -104,7 +101,6 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
*/ */
@Override @Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
super.invokeHeadMap(headMap, context);
if (clazz != null){ if (clazz != null){
try { try {
Set<String> fieldNameSet = getFieldNameSet(clazz); Set<String> fieldNameSet = getFieldNameSet(clazz);
@ -118,6 +114,7 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
e.printStackTrace(); e.printStackTrace();
} }
} }
super.invokeHeadMap(headMap, context);
} }
/** /**
@ -142,11 +139,11 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
return result; return result;
} }
public List<ExcelErrData<T>> getErrList() {
public List<ExcelErrData<T>> getAndClearErrList() { return errList;
List<ExcelErrData<T>> tmp = this.errList;
this.errList = new ArrayList<>();
return tmp;
} }
public void close () {
this.easyExcelI18nTranslator.resetExcelProperty();
}
} }

View File

@ -2,26 +2,23 @@ package io.metersphere.excel.listener;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.base.domain.TestCaseWithBLOBs; import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.commons.constants.TestCaseConstants; import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.utils.BeanUtils; import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import io.metersphere.track.service.TestCaseService; import io.metersphere.track.service.TestCaseService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Component
public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> { public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
@Resource
private TestCaseService testCaseService; private TestCaseService testCaseService;
private String projectId; private String projectId;
@ -30,13 +27,11 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
Set<String> userIds; Set<String> userIds;
public TestCaseDataListener() {} public TestCaseDataListener(TestCaseService testCaseService, String projectId, Set<String> testCaseNames, Set<String> userIds) {
this.testCaseService = testCaseService;
public TestCaseDataListener init(String projectId, Set<String> testCaseNames, Set<String> userIds) {
this.projectId = projectId; this.projectId = projectId;
this.testCaseNames = testCaseNames; this.testCaseNames = testCaseNames;
this.userIds = userIds; this.userIds = userIds;
return this;
} }
@Override @Override

View File

@ -0,0 +1,52 @@
package io.metersphere.excel.utils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.exception.ExcelException;
import io.metersphere.i18n.Translator;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public class EasyExcelExporter {
EasyExcelI18nTranslator easyExcelI18nTranslator;
public EasyExcelExporter() {
easyExcelI18nTranslator = new EasyExcelI18nTranslator(TestCaseExcelData.class);
easyExcelI18nTranslator.translateExcelProperty();
}
public void export(HttpServletResponse response, Class clazz, List data, String fileName, String sheetName) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
try {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
} catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("Utf-8 encoding is not supported");
} catch (IOException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("IO exception");
}
}
public void close() {
easyExcelI18nTranslator.resetExcelProperty();
}
}

View File

@ -0,0 +1,98 @@
package io.metersphere.excel.utils;
import com.alibaba.excel.annotation.ExcelProperty;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.exception.ExcelException;
import io.metersphere.i18n.Translator;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;
/**
* 表头国际化
* 先调用 saveOriginalExcelProperty 存储原表头注解值
* 再调用 translateExcelProperty 获取国际化的值
* 最后调用 resetExcelProperty 重置为原来值防止切换语言后无法国际化
*/
public class EasyExcelI18nTranslator {
private Map<String, List<String>> excelPropertyMap = new HashMap<>();
private Class clazz;
public EasyExcelI18nTranslator(Class clazz) {
this.clazz = clazz;
saveOriginalExcelProperty();
}
private void readExcelProperty(Class clazz, BiConsumer<String, Map<String, Object>> operate) {
Field field;
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length ; i++) {
try {
field = clazz.getDeclaredField(fields[i].getName());
field.setAccessible(true);
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
if(excelProperty != null){
InvocationHandler invocationHandler = Proxy.getInvocationHandler(excelProperty);
Field fieldValue = invocationHandler.getClass().getDeclaredField("memberValues");
fieldValue.setAccessible(true);
Map<String, Object> memberValues = null;
try {
memberValues = (Map<String, Object>) fieldValue.get(invocationHandler);
} catch (IllegalAccessException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException(e.getMessage());
}
operate.accept(field.getName(), memberValues);
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
}
public void saveOriginalExcelProperty() {
readExcelProperty(clazz, (fieldName, memberValues) -> {
List<String> values = Arrays.asList((String [])memberValues.get("value"));
List<String> copyValues = new ArrayList<>();
values.forEach(value -> {
copyValues.add(value);
});
excelPropertyMap.put(fieldName, copyValues);
});
}
public void translateExcelProperty() {
readExcelProperty(TestCaseExcelData.class, (fieldName, memberValues) -> {
String [] values = (String[]) memberValues.get("value");
for (int j = 0; j < values.length; j++) {
if (Pattern.matches("^\\{.+\\}$", values[j])) {
values[j] = Translator.get(values[j].substring(1, values[j].length() - 1));
}
}
memberValues.put("value", values);
});
}
public void resetExcelProperty() {
readExcelProperty(clazz, (fieldName, memberValues) -> {
String [] values = (String[]) memberValues.get("value");
List<String> list = excelPropertyMap.get(fieldName);
for (int j = 0; j < values.length; j++) {
values[j] = list.get(j);
}
memberValues.put("value", values);
});
}
}

View File

@ -1,30 +0,0 @@
package io.metersphere.excel.utils;
import com.alibaba.excel.EasyExcel;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.exception.ExcelException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
public class EasyExcelUtil {
public static void export(HttpServletResponse response, Class clazz, List data, String fileName, String sheetName) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
try {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
} catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("不支持UTF-8编码");
} catch (IOException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("IO异常");
}
}
}

View File

@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolation;
import javax.validation.groups.Default; import javax.validation.groups.Default;
@ -13,14 +14,14 @@ import java.util.Set;
@Component @Component
public class ExcelValidateHelper { public class ExcelValidateHelper {
private ExcelValidateHelper(){} private static ExcelValidateHelper excelValidateHelper;
@Resource @Resource
LocalValidatorFactoryBean localValidatorFactoryBean; LocalValidatorFactoryBean localValidatorFactoryBean;
public <T> String validateEntity(T obj) throws NoSuchFieldException { public static <T> String validateEntity(T obj) throws NoSuchFieldException {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
Set<ConstraintViolation<T>> set = localValidatorFactoryBean.getValidator().validate(obj, Default.class); Set<ConstraintViolation<T>> set = excelValidateHelper.localValidatorFactoryBean.getValidator().validate(obj, Default.class);
if (set != null && !set.isEmpty()) { if (set != null && !set.isEmpty()) {
for (ConstraintViolation<T> cv : set) { for (ConstraintViolation<T> cv : set) {
Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString()); Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString());
@ -31,4 +32,10 @@ public class ExcelValidateHelper {
} }
return result.toString(); return result.toString();
} }
@PostConstruct
public void initialize() {
excelValidateHelper = this;
excelValidateHelper.localValidatorFactoryBean = this.localValidatorFactoryBean;
}
} }

View File

@ -17,13 +17,13 @@ import io.metersphere.controller.request.member.SetAdminRequest;
import io.metersphere.controller.request.organization.AddOrgMemberRequest; import io.metersphere.controller.request.organization.AddOrgMemberRequest;
import io.metersphere.controller.request.organization.QueryOrgMemberRequest; import io.metersphere.controller.request.organization.QueryOrgMemberRequest;
import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserDTO;
import io.metersphere.dto.UserRoleDTO;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -100,16 +100,24 @@ public class UserService {
} }
UserDTO userDTO = new UserDTO(); UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(user, userDTO); BeanUtils.copyProperties(user, userDTO);
UserRoleDTO userRole = getUserRole(userId);
userDTO.setUserRoles(userRole.getUserRoles());
userDTO.setRoles(userRole.getRoles());
return userDTO;
}
public UserRoleDTO getUserRole(String userId) {
UserRoleDTO userRoleDTO = new UserRoleDTO();
// //
UserRoleExample userRoleExample = new UserRoleExample(); UserRoleExample userRoleExample = new UserRoleExample();
userRoleExample.createCriteria().andUserIdEqualTo(userId); userRoleExample.createCriteria().andUserIdEqualTo(userId);
List<UserRole> userRoleList = userRoleMapper.selectByExample(userRoleExample); List<UserRole> userRoleList = userRoleMapper.selectByExample(userRoleExample);
if (CollectionUtils.isEmpty(userRoleList)) { if (CollectionUtils.isEmpty(userRoleList)) {
return userDTO; return userRoleDTO;
} }
// 设置 user_role // 设置 user_role
userDTO.setUserRoles(userRoleList); userRoleDTO.setUserRoles(userRoleList);
List<String> roleIds = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList()); List<String> roleIds = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList());
@ -117,9 +125,9 @@ public class UserService {
roleExample.createCriteria().andIdIn(roleIds); roleExample.createCriteria().andIdIn(roleIds);
List<Role> roleList = roleMapper.selectByExample(roleExample); List<Role> roleList = roleMapper.selectByExample(roleExample);
userDTO.setRoles(roleList); userRoleDTO.setRoles(roleList);
return userDTO; return userRoleDTO;
} }
public List<User> getUserList() { public List<User> getUserList() {

View File

@ -77,7 +77,7 @@ public class TestCaseController {
} }
@PostMapping("/import/{projectId}") @PostMapping("/import/{projectId}")
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId){ public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId) throws NoSuchFieldException {
return testCaseService.testCaseImport(file, projectId); return testCaseService.testCaseImport(file, projectId);
} }

View File

@ -9,14 +9,13 @@ import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser; import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.BeanUtils; import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.excel.domain.ExcelErrData; import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.ExcelResponse; import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.excel.domain.TestCaseExcelData; import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.listener.EasyExcelListener; import io.metersphere.excel.listener.EasyExcelListener;
import io.metersphere.excel.listener.TestCaseDataListener; import io.metersphere.excel.listener.TestCaseDataListener;
import io.metersphere.excel.utils.EasyExcelUtil; import io.metersphere.excel.utils.EasyExcelExporter;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import io.metersphere.track.dto.TestCaseDTO; import io.metersphere.track.dto.TestCaseDTO;
import io.metersphere.track.request.testcase.QueryTestCaseRequest; import io.metersphere.track.request.testcase.QueryTestCaseRequest;
@ -31,7 +30,6 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -60,9 +58,6 @@ public class TestCaseService {
@Resource @Resource
TestCaseNodeService testCaseNodeService; TestCaseNodeService testCaseNodeService;
@Resource
TestCaseDataListener testCaseDataListener;
@Resource @Resource
UserMapper userMapper; UserMapper userMapper;
@ -173,8 +168,6 @@ public class TestCaseService {
public ExcelResponse testCaseImport(MultipartFile file, String projectId) { public ExcelResponse testCaseImport(MultipartFile file, String projectId) {
try {
ExcelResponse excelResponse = new ExcelResponse(); ExcelResponse excelResponse = new ExcelResponse();
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId(); String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
@ -190,10 +183,17 @@ public class TestCaseService {
List<User> users = userMapper.selectByExample(userExample); List<User> users = userMapper.selectByExample(userExample);
Set<String> userIds = users.stream().map(User::getId).collect(Collectors.toSet()); Set<String> userIds = users.stream().map(User::getId).collect(Collectors.toSet());
EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, EasyExcelListener easyExcelListener = null;
testCaseDataListener.init(projectId, testCaseNames, userIds)).sheet().doRead(); List<ExcelErrData<TestCaseExcelData>> errList = null;
try {
List<ExcelErrData<TestCaseExcelData>> errList = testCaseDataListener.getAndClearErrList(); easyExcelListener = new TestCaseDataListener(this, projectId, testCaseNames, userIds);
EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead();
errList = easyExcelListener.getErrList();
} catch (Exception e) {
MSException.throwException(e.getMessage());
} finally {
easyExcelListener.close();
}
//如果包含错误信息就导出错误信息 //如果包含错误信息就导出错误信息
if (!errList.isEmpty()) { if (!errList.isEmpty()) {
excelResponse.setSuccess(false); excelResponse.setSuccess(false);
@ -202,13 +202,6 @@ public class TestCaseService {
excelResponse.setSuccess(true); excelResponse.setSuccess(true);
} }
return excelResponse; return excelResponse;
} catch (IOException e) {
LogUtil.error(e.getMessage(), e);
e.printStackTrace();
}
return null;
} }
public void saveImportData(List<TestCaseWithBLOBs> testCases, String projectId) { public void saveImportData(List<TestCaseWithBLOBs> testCases, String projectId) {
@ -226,8 +219,16 @@ public class TestCaseService {
} }
public void testCaseTemplateExport(HttpServletResponse response) { public void testCaseTemplateExport(HttpServletResponse response) {
EasyExcelUtil.export(response, TestCaseExcelData.class, generateExportTemplate(), EasyExcelExporter easyExcelExporter = null;
try {
easyExcelExporter = new EasyExcelExporter();
easyExcelExporter.export(response, TestCaseExcelData.class, generateExportTemplate(),
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet")); Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"));
} catch (Exception e) {
MSException.throwException(e);
} finally {
easyExcelExporter.close();
}
} }
private List<TestCaseExcelData> generateExportTemplate() { private List<TestCaseExcelData> generateExportTemplate() {
@ -238,28 +239,29 @@ public class TestCaseService {
SessionUser user = SessionUtils.getUser(); SessionUser user = SessionUtils.getUser();
for (int i = 1; i <= 5; i++) { for (int i = 1; i <= 5; i++) {
TestCaseExcelData data = new TestCaseExcelData(); TestCaseExcelData data = new TestCaseExcelData();
data.setName("测试用例" + i); data.setName(Translator.get("test_case") + i);
path.append("/" + "模块" + i); path.append("/" + Translator.get("module") + i);
data.setNodePath(path.toString()); data.setNodePath(path.toString());
data.setPriority("P" + i%4); data.setPriority("P" + i%4);
data.setType(types.get(i%3)); data.setType(types.get(i%3));
data.setMethod(methods.get(i%2)); data.setMethod(methods.get(i%2));
data.setPrerequisite("前置条件选填"); data.setPrerequisite(Translator.get("preconditions_optional"));
data.setStepDesc("1. 每个步骤以换行分隔\n2. 步骤前可标序号\n3. 测试步骤和结果选填"); data.setStepDesc("1. " + Translator.get("step_tip_separate") +
data.setStepResult("1. 每条结果以换行分隔\n2. 结果前可标序号\n3. 测试步骤和结果选填"); "\n2. " + Translator.get("step_tip_order") + "\n3. " + Translator.get("step_tip_optional"));
data.setStepResult("1. " + Translator.get("step_tip_order") + "\n2. " + Translator.get("result_tip_order") + "\n3. " + Translator.get("result_tip_optional"));
data.setMaintainer(user.getId()); data.setMaintainer(user.getId());
data.setRemark("备注选填"); data.setRemark(Translator.get("remark_optional"));
list.add(data); list.add(data);
} }
list.add(new TestCaseExcelData()); list.add(new TestCaseExcelData());
TestCaseExcelData explain = new TestCaseExcelData(); TestCaseExcelData explain = new TestCaseExcelData();
explain.setName("同一项目下测试用例名称不能重复!"); explain.setName(Translator.get("do_not_modify_header_order"));
explain.setNodePath("模块名称请按照'/模块1/模块2'的格式书写; 错误格式示例:('/', '/tes//test'); 若无该模块,则自动创建模块"); explain.setNodePath(Translator.get("module_created_automatically"));
explain.setType("用例类型必须为functional、performance、api"); explain.setType(Translator.get("options") + "functional、performance、api");
explain.setMethod("测试方式必须为manual、auto"); explain.setMethod(Translator.get("options") + "manual、auto");
explain.setPriority("优先级必须为P0、P1、P2、P3"); explain.setPriority(Translator.get("options") + "P0、P1、P2、P3");
explain.setMaintainer("维护人必须为该工作空间相关人员"); explain.setMaintainer(Translator.get("please_input_workspace_member"));
list.add(explain); list.add(explain);
return list; return list;

View File

@ -18,3 +18,27 @@ incorrect_format=
test_case_type_validate= test_case_type_validate=
test_case_priority_validate= test_case_priority_validate=
test_case_method_validate= test_case_method_validate=
test_case_name=
test_case_module=
test_case_type=
test_case_maintainer=
test_case_priority=
test_case_method=
test_case_prerequisite=
test_case_remark=
test_case_step_desc=
test_case_step_result=
test_case=
module=
preconditions_optional=
step_tip_separate=
step_tip_order=
step_tip_optional=
result_tip_separate=
result_tip_order=
result_tip_optional=
remark_optional=
do_not_modify_header_order=
module_created_automatically=
options=
please_input_workspace_member=

View File

@ -59,3 +59,26 @@ incorrect_format=Incorrect format
test_case_type_validate=must be functional, performance, api test_case_type_validate=must be functional, performance, api
test_case_priority_validate=must be P0, P1, P2, P3 test_case_priority_validate=must be P0, P1, P2, P3
test_case_method_validate=\ must be manual, auto test_case_method_validate=\ must be manual, auto
test_case_name=Name
test_case_type=Type
test_case_priority=Priority
test_case_method=method
test_case_prerequisite=Prerequisite
test_case_remark=Remark
test_case_step_desc=Step description
test_case_step_result=Step result
test_case_module=module
test_case=Test case
module=Module
preconditions_optional=Preconditions optional
step_tip_separate=Each step is separated by a new line
step_tip_order=The serial number can be marked before the step
step_tip_optional=Test steps and results optional
result_tip_separate=Each result is separated by a new line
result_tip_order=The sequence number can be marked before the result
result_tip_optional=Test steps and results optional
remark_optional=Remark optional
do_not_modify_header_order=Do not modify the header order
module_created_automatically=If there is no such module, will be created automatically
options=options
please_input_workspace_member=Please input workspace merber

View File

@ -51,11 +51,35 @@ parse_data_error=解析数据出错
missing_header_information=缺少头部信息 missing_header_information=缺少头部信息
number= number=
row= row=
incorrect_format=格式不正确 incorrect_format=所属模块
test_case_type_validate=必须为functional、performance、api test_case_type_validate=必须为functional、performance、api
test_case_priority_validate=必须为P0、P1、P2、P3 test_case_priority_validate=必须为P0、P1、P2、P3
test_case_method_validate=必须为manual、auto test_case_method_validate=必须为manual、auto
error=出错 error=出错
test_case_name=用例名称
test_case_type=用例类型
test_case_maintainer=维护人
test_case_priority=优先级
test_case_method=测试方式
test_case_prerequisite=前置条件
test_case_remark=备注
test_case_step_desc=步骤描述
test_case_step_result=预期结果
test_case_module=所属模块
test_case=测试用例
module=模块
preconditions_optional=前置条件选填
step_tip_separate=每个步骤以换行分隔
step_tip_order=步骤前可标序号
step_tip_optional=步骤前可标序号
result_tip_separate=每条结果以换行分隔
result_tip_order=结果前可标序号
result_tip_optional=测试步骤和结果选填
remark_optional=备注选填
do_not_modify_header_order=请勿修改表头顺序
module_created_automatically=若无该模块将自动创建
options=选项
please_input_workspace_member=请填写该工作空间相关人员
test_case_exist=该项目下已存在用例: test_case_exist=该项目下已存在用例:
node_deep_limit=节点深度不超过5层 node_deep_limit=节点深度不超过5层
before_delete_plan=该计划下存在关联测试用例,请先取消关联! before_delete_plan=该计划下存在关联测试用例,请先取消关联!

View File

@ -51,11 +51,35 @@ parse_data_error=解析數據出錯
missing_header_information=缺少頭部信息 missing_header_information=缺少頭部信息
number= number=
row= row=
incorrect_format=格式不正確 incorrect_format=所屬模塊
test_case_type_validate=必須為functional、performance、api test_case_type_validate=必須為functional、performance、api
test_case_priority_validate=必須為P0、P1、P2、P3 test_case_priority_validate=必須為P0、P1、P2、P3
test_case_method_validate=必須為manual、auto test_case_method_validate=必須為manual、auto
error=出错 error=出錯
test_case_name=用例名稱
test_case_type=用例類型
test_case_maintainer=維護人
test_case_priority=優先級
test_case_method=測試方式
test_case_prerequisite=前置條件
test_case_remark=備註
test_case_step_desc=步驟描述
test_case_step_result=預期結果
test_case_module=所屬模塊
test_case=測試用例
module=模塊
preconditions_optional=前置條件選填
step_tip_separate=每個步驟以換行分隔
step_tip_order=步驟前可標序號
step_tip_optional=步驟前可標序號
result_tip_separate=每條結果以換行分隔
result_tip_order=結果前可標序號
result_tip_optional=測試步驟和結果選填
remark_optional=備註選填
do_not_modify_header_order=請勿修改表頭順序
module_created_automatically=若無該模塊將自動創建
options=選項
please_input_workspace_member=請填寫該工作空間相關人員
test_case_exist=該項目下已存在用例: test_case_exist=該項目下已存在用例:
node_deep_limit=節點深度不超過5層 node_deep_limit=節點深度不超過5層
before_delete_plan=該計劃下存在關聯測試用例,請先取消關聯! before_delete_plan=該計劃下存在關聯測試用例,請先取消關聯!

View File

@ -58,7 +58,7 @@
<!--Change personal password--> <!--Change personal password-->
<el-dialog :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="30%" left> <el-dialog :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="30%" left>
<el-form :model="ruleForm" :rules="rules" ref="editPasswordForm" label-width="100px" class="demo-ruleForm"> <el-form :model="ruleForm" :rules="rules" ref="editPasswordForm" label-width="120px" class="demo-ruleForm">
<el-form-item :label="$t('member.old_password')" prop="password"> <el-form-item :label="$t('member.old_password')" prop="password">
<el-input v-model="ruleForm.password" autocomplete="off" show-password/> <el-input v-model="ruleForm.password" autocomplete="off" show-password/>
</el-form-item> </el-form-item>

View File

@ -9,9 +9,13 @@
<el-table :data="tableData" style="width: 100%"> <el-table :data="tableData" style="width: 100%">
<el-table-column prop="id" label="ID"/> <el-table-column prop="id" label="ID"/>
<el-table-column prop="name" :label="$t('commons.username')"/> <el-table-column prop="name" :label="$t('commons.username')" width="200"/>
<el-table-column :label="$t('commons.role')" width="120">
<template v-slot:default="scope">
<ms-roles-tag :roles="scope.row.roles"/>
</template>
</el-table-column>
<el-table-column prop="email" :label="$t('commons.email')"/> <el-table-column prop="email" :label="$t('commons.email')"/>
<el-table-column prop="phone" :label="$t('commons.phone')"/>
<el-table-column prop="status" :label="$t('commons.status')"> <el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<el-switch v-model="scope.row.status" <el-switch v-model="scope.row.status"
@ -23,7 +27,7 @@
/> />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="createTime" :label="$t('commons.create_time')" width="180"> <el-table-column prop="createTime" :label="$t('commons.create_time')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span> <span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template> </template>
@ -34,8 +38,6 @@
<template v-slot:behind> <template v-slot:behind>
<ms-table-operator-button :tip="$t('member.edit_password')" icon="el-icon-s-tools" <ms-table-operator-button :tip="$t('member.edit_password')" icon="el-icon-s-tools"
type="success" @exec="editPassword(scope.row)"/> type="success" @exec="editPassword(scope.row)"/>
<ms-table-operator-button :tip="$t('commons.set_admin')" icon="el-icon-user-solid"
type="danger" @exec="openCheckDialog(scope.row)"/>
</template> </template>
</ms-table-operator> </ms-table-operator>
</template> </template>
@ -46,21 +48,6 @@
:total="total"/> :total="total"/>
</el-card> </el-card>
<el-dialog :title="$t('commons.verification')" :visible.sync="checkPasswordVisible" width="30%"
@close="closeCheckPassword" :destroy-on-close="true">
<el-form :model="checkPasswordForm" label-position="right" label-width="100px" size="small" :rules="rule"
ref="checkPasswordForm">
<el-form-item :label="$t('commons.password')" prop="password">
<el-input type="password" v-model="checkPasswordForm.password" autocomplete="off" show-password></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<ms-dialog-footer
@cancel="checkPasswordVisible = false"
@confirm="setAdmin('checkPasswordForm')"/>
</template>
</el-dialog>
<!--Create user--> <!--Create user-->
<el-dialog :title="$t('user.create')" :visible.sync="createVisible" width="30%" @closed="handleClose" <el-dialog :title="$t('user.create')" :visible.sync="createVisible" width="30%" @closed="handleClose"
:destroy-on-close="true"> :destroy-on-close="true">
@ -113,12 +100,12 @@
</el-dialog> </el-dialog>
<!--Changing user password in system settings--> <!--Changing user password in system settings-->
<el-dialog :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="30%" left> <el-dialog :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="30%" left>
<el-form :model="ruleForm" label-position="right" label-width="100px" size="small" :rules="rule" <el-form :model="ruleForm" label-position="right" label-width="120px" size="small" :rules="rule"
ref="editPasswordForm" class="demo-ruleForm"> ref="editPasswordForm" class="demo-ruleForm">
<el-form-item :label="$t('member.new_password')" prop="newpassword"> <el-form-item :label="$t('member.new_password')" prop="newpassword">
<el-input type="password" v-model="ruleForm.newpassword" autocomplete="off" show-password></el-input> <el-input type="password" v-model="ruleForm.newpassword" autocomplete="off" show-password></el-input>
</el-form-item> </el-form-item>
<el-form-item > <el-form-item>
<el-input v-model="ruleForm.id" autocomplete="off" :disabled="true" style="display:none"/> <el-input v-model="ruleForm.id" autocomplete="off" :disabled="true" style="display:none"/>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -140,10 +127,19 @@
import MsDialogFooter from "../../common/components/MsDialogFooter"; import MsDialogFooter from "../../common/components/MsDialogFooter";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton"; import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import {getCurrentUser} from "../../../../common/js/utils"; import {getCurrentUser} from "../../../../common/js/utils";
import MsRolesTag from "../../common/components/MsRolesTag";
export default { export default {
name: "MsUser", name: "MsUser",
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsTableOperator, MsDialogFooter, MsTableOperatorButton}, components: {
MsCreateBox,
MsTablePagination,
MsTableHeader,
MsTableOperator,
MsDialogFooter,
MsTableOperatorButton,
MsRolesTag
},
data() { data() {
return { return {
queryPath: '/user/special/list', queryPath: '/user/special/list',
@ -155,7 +151,6 @@
createVisible: false, createVisible: false,
updateVisible: false, updateVisible: false,
editPasswordVisible: false, editPasswordVisible: false,
checkPasswordVisible: false,
multipleSelection: [], multipleSelection: [],
currentPage: 1, currentPage: 1,
pageSize: 5, pageSize: 5,
@ -274,9 +269,9 @@
} }
}) })
}, },
editUserPassword(editPasswordForm){ editUserPassword(editPasswordForm) {
this.$refs[editPasswordForm].validate(valid=>{ this.$refs[editPasswordForm].validate(valid => {
if(valid){ if (valid) {
this.result = this.$post(this.editPasswordPath, this.ruleForm, response => { this.result = this.$post(this.editPasswordPath, this.ruleForm, response => {
this.$success(this.$t('commons.modify_success')); this.$success(this.$t('commons.modify_success'));
this.editPasswordVisible = false; this.editPasswordVisible = false;
@ -293,6 +288,15 @@
let data = response.data; let data = response.data;
this.total = data.itemCount; this.total = data.itemCount;
this.tableData = data.listObject; this.tableData = data.listObject;
let url = "/user/special/user/role";
for (let i = 0; i < this.tableData.length; i++) {
this.$get(url + '/' + this.tableData[i].id, result => {
let data = result.data;
let roles = data.roles;
// let userRoles = result.userRoles;
this.$set(this.tableData[i], "roles", roles);
})
}
}) })
}, },
handleClose() { handleClose() {
@ -308,28 +312,6 @@
}, },
handleSelectionChange(val) { handleSelectionChange(val) {
this.multipleSelection = val; this.multipleSelection = val;
},
closeCheckPassword() {
this.checkPasswordForm = {};
},
openCheckDialog(row) {
this.$set(this.setAdminParam, 'id', row.id);
this.checkPasswordVisible = true;
},
setAdmin(checkPasswordForm) {
let user = getCurrentUser();
this.$set(this.setAdminParam, 'adminId', user.id);
this.$set(this.setAdminParam, 'password', this.checkPasswordForm.password);
this.$refs[checkPasswordForm].validate(valid => {
if (valid) {
this.$post("/user/set/admin", this.setAdminParam, () => {
this.$success(this.$t('commons.modify_success'));
this.checkPasswordVisible = false;
})
} else {
return false;
}
})
} }
} }
} }

View File

@ -56,7 +56,6 @@ export default {
'personal_information': 'Personal Information', 'personal_information': 'Personal Information',
'exit_system': 'Exit System', 'exit_system': 'Exit System',
'verification': 'Verification', 'verification': 'Verification',
'set_admin': 'Set Admin',
'title': 'Title', 'title': 'Title',
'custom': 'Custom', 'custom': 'Custom',
'select_date': 'Select date', 'select_date': 'Select date',
@ -124,8 +123,8 @@ export default {
'please_choose_member': 'Please Choose Member', 'please_choose_member': 'Please Choose Member',
'search_by_name': 'Search by name', 'search_by_name': 'Search by name',
'modify_personal_info': 'Modify Personal Information', 'modify_personal_info': 'Modify Personal Information',
'edit_password': 'Edit_Password', 'edit_password': 'Edit Password',
'edit_information': 'Edit_Information', 'edit_information': 'Edit Information',
'input_name': 'Please enter a user name', 'input_name': 'Please enter a user name',
'input_email': 'Please enter a email', 'input_email': 'Please enter a email',
'special_characters_are_not_supported': 'Special characters are not supported', 'special_characters_are_not_supported': 'Special characters are not supported',

View File

@ -56,7 +56,6 @@ export default {
'personal_information': '个人信息', 'personal_information': '个人信息',
'exit_system': '退出系统', 'exit_system': '退出系统',
'verification': '验证', 'verification': '验证',
'set_admin': '设置为管理员',
'title': '标题', 'title': '标题',
'custom': '自定义', 'custom': '自定义',
'select_date': '选择日期', 'select_date': '选择日期',

View File

@ -56,7 +56,6 @@ export default {
'personal_information': '個人信息', 'personal_information': '個人信息',
'exit_system': '退出系統', 'exit_system': '退出系統',
'verification': '驗證', 'verification': '驗證',
'set_admin': '設置為管理員',
'title': '標題', 'title': '標題',
'custom': '自定義', 'custom': '自定義',
'select_date': '選擇日期', 'select_date': '選擇日期',