Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
d94f031a12
|
@ -17,15 +17,14 @@ import io.metersphere.controller.request.member.SetAdminRequest;
|
|||
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.service.OrganizationService;
|
||||
import io.metersphere.service.UserService;
|
||||
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.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -54,6 +53,12 @@ public class UserController {
|
|||
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}")
|
||||
@RequiresRoles(RoleConstants.ADMIN)
|
||||
public void deleteUser(@PathVariable(value = "userId") String userId) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -14,52 +14,52 @@ public class TestCaseExcelData {
|
|||
|
||||
@NotBlank
|
||||
@Length(max=50)
|
||||
@ExcelProperty("用例名称")
|
||||
@ExcelProperty("{test_case_name}")
|
||||
private String name;
|
||||
|
||||
@NotBlank
|
||||
@Length(max=1000)
|
||||
@ExcelProperty("所属模块")
|
||||
@ExcelProperty("{test_case_module}")
|
||||
@ColumnWidth(30)
|
||||
@Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}")
|
||||
private String nodePath;
|
||||
|
||||
@NotBlank
|
||||
@ExcelProperty("用例类型")
|
||||
@ExcelProperty("{test_case_type}")
|
||||
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}")
|
||||
private String type;
|
||||
|
||||
@NotBlank
|
||||
@ExcelProperty("维护人")
|
||||
@ExcelProperty("{test_case_maintainer}")
|
||||
private String maintainer;
|
||||
|
||||
@NotBlank
|
||||
@ExcelProperty("优先级")
|
||||
@ExcelProperty("{test_case_priority}")
|
||||
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
|
||||
private String priority;
|
||||
|
||||
@NotBlank
|
||||
@ExcelProperty("测试方式")
|
||||
@ExcelProperty("{test_case_method}")
|
||||
@Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
|
||||
private String method;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("前置条件")
|
||||
@ExcelProperty("{test_case_prerequisite}")
|
||||
@Length(min=0, max=1000)
|
||||
private String prerequisite;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("备注")
|
||||
@ExcelProperty("{test_case_remark}")
|
||||
@Length(max=1000)
|
||||
private String remark;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("步骤描述")
|
||||
@ExcelProperty("{test_case_step_desc}")
|
||||
@Length(max=1000)
|
||||
private String stepDesc;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("预期结果")
|
||||
@ExcelProperty("{test_case_step_result}")
|
||||
@Length(max=1000)
|
||||
private String stepResult;
|
||||
}
|
||||
|
|
|
@ -6,24 +6,22 @@ import com.alibaba.excel.event.AnalysisEventListener;
|
|||
import com.alibaba.excel.exception.ExcelAnalysisException;
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.excel.utils.ExcelValidateHelper;
|
||||
import io.metersphere.excel.domain.ExcelErrData;
|
||||
import io.metersphere.excel.utils.EasyExcelI18nTranslator;
|
||||
import io.metersphere.excel.utils.ExcelValidateHelper;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
@Component
|
||||
public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
||||
|
||||
protected List<ExcelErrData<T>> errList = new ArrayList<>();
|
||||
|
||||
protected List<T> list = new ArrayList<>();
|
||||
|
||||
protected EasyExcelI18nTranslator easyExcelI18nTranslator;
|
||||
|
||||
/**
|
||||
* 每隔2000条存储数据库,然后清理list ,方便内存回收
|
||||
*/
|
||||
|
@ -31,16 +29,15 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
|
||||
protected Class<T> clazz;
|
||||
|
||||
@Resource
|
||||
ExcelValidateHelper excelValidateHelper;
|
||||
|
||||
public EasyExcelListener(){
|
||||
Type type = getClass().getGenericSuperclass();
|
||||
this.clazz = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
this.easyExcelI18nTranslator = new EasyExcelI18nTranslator(this.clazz);
|
||||
this.easyExcelI18nTranslator.translateExcelProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个每一条数据解析都会来调用
|
||||
* 每条数据解析都会调用
|
||||
*
|
||||
* @param t
|
||||
* @param analysisContext
|
||||
|
@ -51,7 +48,7 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
|
||||
try {
|
||||
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
|
||||
errMsg = excelValidateHelper.validateEntity(t);
|
||||
errMsg = ExcelValidateHelper.validateEntity(t);
|
||||
//自定义校验规则
|
||||
errMsg = validate(t, errMsg);
|
||||
} catch (NoSuchFieldException e) {
|
||||
|
@ -104,7 +101,6 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
*/
|
||||
@Override
|
||||
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
|
||||
super.invokeHeadMap(headMap, context);
|
||||
if (clazz != null){
|
||||
try {
|
||||
Set<String> fieldNameSet = getFieldNameSet(clazz);
|
||||
|
@ -118,6 +114,7 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
super.invokeHeadMap(headMap, context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,11 +139,11 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
public List<ExcelErrData<T>> getAndClearErrList() {
|
||||
List<ExcelErrData<T>> tmp = this.errList;
|
||||
this.errList = new ArrayList<>();
|
||||
return tmp;
|
||||
public List<ExcelErrData<T>> getErrList() {
|
||||
return errList;
|
||||
}
|
||||
|
||||
public void close () {
|
||||
this.easyExcelI18nTranslator.resetExcelProperty();
|
||||
}
|
||||
}
|
|
@ -2,26 +2,23 @@ package io.metersphere.excel.listener;
|
|||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||
import io.metersphere.commons.constants.TestCaseConstants;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@Component
|
||||
|
||||
public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
||||
|
||||
@Resource
|
||||
private TestCaseService testCaseService;
|
||||
|
||||
private String projectId;
|
||||
|
@ -30,13 +27,11 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
|
||||
Set<String> userIds;
|
||||
|
||||
public TestCaseDataListener() {}
|
||||
|
||||
public TestCaseDataListener init(String projectId, Set<String> testCaseNames, Set<String> userIds) {
|
||||
public TestCaseDataListener(TestCaseService testCaseService, String projectId, Set<String> testCaseNames, Set<String> userIds) {
|
||||
this.testCaseService = testCaseService;
|
||||
this.projectId = projectId;
|
||||
this.testCaseNames = testCaseNames;
|
||||
this.userIds = userIds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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异常");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
|
|||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.groups.Default;
|
||||
|
@ -13,14 +14,14 @@ import java.util.Set;
|
|||
@Component
|
||||
public class ExcelValidateHelper {
|
||||
|
||||
private ExcelValidateHelper(){}
|
||||
private static ExcelValidateHelper excelValidateHelper;
|
||||
|
||||
@Resource
|
||||
LocalValidatorFactoryBean localValidatorFactoryBean;
|
||||
|
||||
public <T> String validateEntity(T obj) throws NoSuchFieldException {
|
||||
public static <T> String validateEntity(T obj) throws NoSuchFieldException {
|
||||
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()) {
|
||||
for (ConstraintViolation<T> cv : set) {
|
||||
Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString());
|
||||
|
@ -31,4 +32,10 @@ public class ExcelValidateHelper {
|
|||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initialize() {
|
||||
excelValidateHelper = this;
|
||||
excelValidateHelper.localValidatorFactoryBean = this.localValidatorFactoryBean;
|
||||
}
|
||||
}
|
|
@ -17,13 +17,13 @@ import io.metersphere.controller.request.member.SetAdminRequest;
|
|||
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.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
@ -100,16 +100,24 @@ public class UserService {
|
|||
}
|
||||
UserDTO userDTO = new 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.createCriteria().andUserIdEqualTo(userId);
|
||||
List<UserRole> userRoleList = userRoleMapper.selectByExample(userRoleExample);
|
||||
|
||||
if (CollectionUtils.isEmpty(userRoleList)) {
|
||||
return userDTO;
|
||||
return userRoleDTO;
|
||||
}
|
||||
// 设置 user_role
|
||||
userDTO.setUserRoles(userRoleList);
|
||||
userRoleDTO.setUserRoles(userRoleList);
|
||||
|
||||
List<String> roleIds = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList());
|
||||
|
||||
|
@ -117,9 +125,9 @@ public class UserService {
|
|||
roleExample.createCriteria().andIdIn(roleIds);
|
||||
|
||||
List<Role> roleList = roleMapper.selectByExample(roleExample);
|
||||
userDTO.setRoles(roleList);
|
||||
userRoleDTO.setRoles(roleList);
|
||||
|
||||
return userDTO;
|
||||
return userRoleDTO;
|
||||
}
|
||||
|
||||
public List<User> getUserList() {
|
||||
|
|
|
@ -77,7 +77,7 @@ public class TestCaseController {
|
|||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,14 +9,13 @@ import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
|||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.excel.domain.ExcelErrData;
|
||||
import io.metersphere.excel.domain.ExcelResponse;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.excel.listener.EasyExcelListener;
|
||||
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.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
|
@ -31,7 +30,6 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -60,9 +58,6 @@ public class TestCaseService {
|
|||
@Resource
|
||||
TestCaseNodeService testCaseNodeService;
|
||||
|
||||
@Resource
|
||||
TestCaseDataListener testCaseDataListener;
|
||||
|
||||
@Resource
|
||||
UserMapper userMapper;
|
||||
|
||||
|
@ -173,8 +168,6 @@ public class TestCaseService {
|
|||
|
||||
public ExcelResponse testCaseImport(MultipartFile file, String projectId) {
|
||||
|
||||
try {
|
||||
|
||||
ExcelResponse excelResponse = new ExcelResponse();
|
||||
|
||||
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||
|
@ -190,10 +183,17 @@ public class TestCaseService {
|
|||
List<User> users = userMapper.selectByExample(userExample);
|
||||
Set<String> userIds = users.stream().map(User::getId).collect(Collectors.toSet());
|
||||
|
||||
EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class,
|
||||
testCaseDataListener.init(projectId, testCaseNames, userIds)).sheet().doRead();
|
||||
|
||||
List<ExcelErrData<TestCaseExcelData>> errList = testCaseDataListener.getAndClearErrList();
|
||||
EasyExcelListener easyExcelListener = null;
|
||||
List<ExcelErrData<TestCaseExcelData>> errList = null;
|
||||
try {
|
||||
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()) {
|
||||
excelResponse.setSuccess(false);
|
||||
|
@ -202,13 +202,6 @@ public class TestCaseService {
|
|||
excelResponse.setSuccess(true);
|
||||
}
|
||||
return excelResponse;
|
||||
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void saveImportData(List<TestCaseWithBLOBs> testCases, String projectId) {
|
||||
|
@ -226,8 +219,16 @@ public class TestCaseService {
|
|||
}
|
||||
|
||||
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"));
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e);
|
||||
} finally {
|
||||
easyExcelExporter.close();
|
||||
}
|
||||
}
|
||||
|
||||
private List<TestCaseExcelData> generateExportTemplate() {
|
||||
|
@ -238,28 +239,29 @@ public class TestCaseService {
|
|||
SessionUser user = SessionUtils.getUser();
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
TestCaseExcelData data = new TestCaseExcelData();
|
||||
data.setName("测试用例" + i);
|
||||
path.append("/" + "模块" + i);
|
||||
data.setName(Translator.get("test_case") + i);
|
||||
path.append("/" + Translator.get("module") + i);
|
||||
data.setNodePath(path.toString());
|
||||
data.setPriority("P" + i%4);
|
||||
data.setType(types.get(i%3));
|
||||
data.setMethod(methods.get(i%2));
|
||||
data.setPrerequisite("前置条件选填");
|
||||
data.setStepDesc("1. 每个步骤以换行分隔\n2. 步骤前可标序号\n3. 测试步骤和结果选填");
|
||||
data.setStepResult("1. 每条结果以换行分隔\n2. 结果前可标序号\n3. 测试步骤和结果选填");
|
||||
data.setPrerequisite(Translator.get("preconditions_optional"));
|
||||
data.setStepDesc("1. " + Translator.get("step_tip_separate") +
|
||||
"\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.setRemark("备注选填");
|
||||
data.setRemark(Translator.get("remark_optional"));
|
||||
list.add(data);
|
||||
}
|
||||
|
||||
list.add(new TestCaseExcelData());
|
||||
TestCaseExcelData explain = new TestCaseExcelData();
|
||||
explain.setName("同一项目下测试用例名称不能重复!");
|
||||
explain.setNodePath("模块名称请按照'/模块1/模块2'的格式书写; 错误格式示例:('/', '/tes//test'); 若无该模块,则自动创建模块");
|
||||
explain.setType("用例类型必须为:functional、performance、api");
|
||||
explain.setMethod("测试方式必须为:manual、auto");
|
||||
explain.setPriority("优先级必须为:P0、P1、P2、P3");
|
||||
explain.setMaintainer("维护人必须为该工作空间相关人员");
|
||||
explain.setName(Translator.get("do_not_modify_header_order"));
|
||||
explain.setNodePath(Translator.get("module_created_automatically"));
|
||||
explain.setType(Translator.get("options") + "(functional、performance、api)");
|
||||
explain.setMethod(Translator.get("options") + "(manual、auto)");
|
||||
explain.setPriority(Translator.get("options") + "(P0、P1、P2、P3)");
|
||||
explain.setMaintainer(Translator.get("please_input_workspace_member"));
|
||||
|
||||
list.add(explain);
|
||||
return list;
|
||||
|
|
|
@ -18,3 +18,27 @@ incorrect_format=
|
|||
test_case_type_validate=
|
||||
test_case_priority_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=
|
|
@ -59,3 +59,26 @@ incorrect_format=Incorrect format
|
|||
test_case_type_validate=must be functional, performance, api
|
||||
test_case_priority_validate=must be P0, P1, P2, P3
|
||||
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
|
|
@ -51,11 +51,35 @@ parse_data_error=解析数据出错
|
|||
missing_header_information=缺少头部信息
|
||||
number=第
|
||||
row=行
|
||||
incorrect_format=格式不正确
|
||||
incorrect_format=所属模块
|
||||
test_case_type_validate=必须为functional、performance、api
|
||||
test_case_priority_validate=必须为P0、P1、P2、P3
|
||||
test_case_method_validate=必须为manual、auto
|
||||
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=该项目下已存在用例:
|
||||
node_deep_limit=节点深度不超过5层!
|
||||
before_delete_plan=该计划下存在关联测试用例,请先取消关联!
|
||||
|
|
|
@ -51,11 +51,35 @@ parse_data_error=解析數據出錯
|
|||
missing_header_information=缺少頭部信息
|
||||
number=第
|
||||
row=行
|
||||
incorrect_format=格式不正確
|
||||
incorrect_format=所屬模塊
|
||||
test_case_type_validate=必須為functional、performance、api
|
||||
test_case_priority_validate=必須為P0、P1、P2、P3
|
||||
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=該項目下已存在用例:
|
||||
node_deep_limit=節點深度不超過5層!
|
||||
before_delete_plan=該計劃下存在關聯測試用例,請先取消關聯!
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
<!--Change personal password-->
|
||||
<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-input v-model="ruleForm.password" autocomplete="off" show-password/>
|
||||
</el-form-item>
|
||||
|
|
|
@ -9,9 +9,13 @@
|
|||
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<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="phone" :label="$t('commons.phone')"/>
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="scope">
|
||||
<el-switch v-model="scope.row.status"
|
||||
|
@ -23,7 +27,7 @@
|
|||
/>
|
||||
</template>
|
||||
</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">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
|
@ -34,8 +38,6 @@
|
|||
<template v-slot:behind>
|
||||
<ms-table-operator-button :tip="$t('member.edit_password')" icon="el-icon-s-tools"
|
||||
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>
|
||||
</ms-table-operator>
|
||||
</template>
|
||||
|
@ -46,21 +48,6 @@
|
|||
:total="total"/>
|
||||
</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-->
|
||||
<el-dialog :title="$t('user.create')" :visible.sync="createVisible" width="30%" @closed="handleClose"
|
||||
:destroy-on-close="true">
|
||||
|
@ -113,7 +100,7 @@
|
|||
</el-dialog>
|
||||
<!--Changing user password in system settings-->
|
||||
<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">
|
||||
<el-form-item :label="$t('member.new_password')" prop="newpassword">
|
||||
<el-input type="password" v-model="ruleForm.newpassword" autocomplete="off" show-password></el-input>
|
||||
|
@ -140,10 +127,19 @@
|
|||
import MsDialogFooter from "../../common/components/MsDialogFooter";
|
||||
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
|
||||
import {getCurrentUser} from "../../../../common/js/utils";
|
||||
import MsRolesTag from "../../common/components/MsRolesTag";
|
||||
|
||||
export default {
|
||||
name: "MsUser",
|
||||
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsTableOperator, MsDialogFooter, MsTableOperatorButton},
|
||||
components: {
|
||||
MsCreateBox,
|
||||
MsTablePagination,
|
||||
MsTableHeader,
|
||||
MsTableOperator,
|
||||
MsDialogFooter,
|
||||
MsTableOperatorButton,
|
||||
MsRolesTag
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
queryPath: '/user/special/list',
|
||||
|
@ -155,7 +151,6 @@
|
|||
createVisible: false,
|
||||
updateVisible: false,
|
||||
editPasswordVisible: false,
|
||||
checkPasswordVisible: false,
|
||||
multipleSelection: [],
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
|
@ -293,6 +288,15 @@
|
|||
let data = response.data;
|
||||
this.total = data.itemCount;
|
||||
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() {
|
||||
|
@ -308,28 +312,6 @@
|
|||
},
|
||||
handleSelectionChange(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;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ export default {
|
|||
'personal_information': 'Personal Information',
|
||||
'exit_system': 'Exit System',
|
||||
'verification': 'Verification',
|
||||
'set_admin': 'Set Admin',
|
||||
'title': 'Title',
|
||||
'custom': 'Custom',
|
||||
'select_date': 'Select date',
|
||||
|
@ -124,8 +123,8 @@ export default {
|
|||
'please_choose_member': 'Please Choose Member',
|
||||
'search_by_name': 'Search by name',
|
||||
'modify_personal_info': 'Modify Personal Information',
|
||||
'edit_password': 'Edit_Password',
|
||||
'edit_information': 'Edit_Information',
|
||||
'edit_password': 'Edit Password',
|
||||
'edit_information': 'Edit Information',
|
||||
'input_name': 'Please enter a user name',
|
||||
'input_email': 'Please enter a email',
|
||||
'special_characters_are_not_supported': 'Special characters are not supported',
|
||||
|
|
|
@ -56,7 +56,6 @@ export default {
|
|||
'personal_information': '个人信息',
|
||||
'exit_system': '退出系统',
|
||||
'verification': '验证',
|
||||
'set_admin': '设置为管理员',
|
||||
'title': '标题',
|
||||
'custom': '自定义',
|
||||
'select_date': '选择日期',
|
||||
|
|
|
@ -56,7 +56,6 @@ export default {
|
|||
'personal_information': '個人信息',
|
||||
'exit_system': '退出系統',
|
||||
'verification': '驗證',
|
||||
'set_admin': '設置為管理員',
|
||||
'title': '標題',
|
||||
'custom': '自定義',
|
||||
'select_date': '選擇日期',
|
||||
|
|
Loading…
Reference in New Issue