fix(测试跟踪): 用例导入导出国际化问题

This commit is contained in:
chenjianxing 2020-09-27 20:35:24 +08:00
parent 479528d6a7
commit 8770ed47f9
10 changed files with 247 additions and 182 deletions

View File

@ -0,0 +1,5 @@
package io.metersphere.excel.domain;
public interface ExcelDataFactory {
Object getExcelDataByLocal();
}

View File

@ -1,65 +1,31 @@
package io.metersphere.excel.domain;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import com.alibaba.excel.annotation.ExcelIgnore;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@Data
@ColumnWidth(15)
@Getter
@Setter
public class TestCaseExcelData {
@NotBlank(message = "{cannot_be_null}")
@Length(max = 50)
@ExcelProperty("{test_case_name}")
@ExcelIgnore
private String name;
@NotBlank(message = "{cannot_be_null}")
@Length(max = 1000)
@ExcelProperty("{test_case_module}")
@ColumnWidth(30)
@Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}")
@ExcelIgnore
private String nodePath;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("{test_case_type}")
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}")
@ExcelIgnore
private String type;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("{test_case_maintainer}")
@ExcelIgnore
private String maintainer;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("{test_case_priority}")
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
@ExcelIgnore
private String priority;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("{test_case_method}")
@Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
@ExcelIgnore
private String method;
@ColumnWidth(50)
@ExcelProperty("{test_case_prerequisite}")
@Length(min = 0, max = 1000)
@ExcelIgnore
private String prerequisite;
@ColumnWidth(50)
@ExcelProperty("{test_case_remark}")
@Length(max = 1000)
@ExcelIgnore
private String remark;
@ColumnWidth(50)
@ExcelProperty("{test_case_step_desc}")
@Length(max = 1000)
@ExcelIgnore
private String stepDesc;
@ColumnWidth(50)
@ExcelProperty("{test_case_step_result}")
@Length(max = 1000)
@ExcelIgnore
private String stepResult;
}
}

View File

@ -0,0 +1,65 @@
package io.metersphere.excel.domain;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@Data
@ColumnWidth(15)
public class TestCaseExcelDataCn extends TestCaseExcelData {
@NotBlank(message = "{cannot_be_null}")
@Length(max = 50)
@ExcelProperty("用例名称")
private String name;
@NotBlank(message = "{cannot_be_null}")
@Length(max = 1000)
@ExcelProperty("所属模块")
@ColumnWidth(30)
@Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}")
private String nodePath;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("用例类型")
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}")
private String type;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("维护人")
private String maintainer;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("用例等级")
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
private String priority;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("测试方式")
@Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
private String method;
@ColumnWidth(50)
@ExcelProperty("前置条件")
@Length(min = 0, max = 1000)
private String prerequisite;
@ColumnWidth(50)
@ExcelProperty("备注")
@Length(max = 1000)
private String remark;
@ColumnWidth(50)
@ExcelProperty("步骤描述")
@Length(max = 1000)
private String stepDesc;
@ColumnWidth(50)
@ExcelProperty("预期结果")
@Length(max = 1000)
private String stepResult;
}

View File

@ -0,0 +1,18 @@
package io.metersphere.excel.domain;
import org.springframework.context.i18n.LocaleContextHolder;
import java.util.Locale;
public class TestCaseExcelDataFactory implements ExcelDataFactory {
@Override
public Class getExcelDataByLocal() {
Locale locale = LocaleContextHolder.getLocale();
if (Locale.US.toString().equalsIgnoreCase(locale.toString())) {
return TestCaseExcelDataUs.class;
} else if (Locale.TRADITIONAL_CHINESE.toString().equalsIgnoreCase(locale.toString())) {
return TestCaseExcelDataTw.class;
}
return TestCaseExcelDataCn.class;
}
}

View File

@ -0,0 +1,65 @@
package io.metersphere.excel.domain;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@Data
@ColumnWidth(15)
public class TestCaseExcelDataTw extends TestCaseExcelData {
@NotBlank(message = "{cannot_be_null}")
@Length(max = 50)
@ExcelProperty("用例名稱")
private String name;
@NotBlank(message = "{cannot_be_null}")
@Length(max = 1000)
@ExcelProperty("所屬模塊")
@ColumnWidth(30)
@Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}")
private String nodePath;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("用例類型")
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}")
private String type;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("維護人")
private String maintainer;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("用例等級")
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
private String priority;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("測試方式")
@Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
private String method;
@ColumnWidth(50)
@ExcelProperty("前置條件")
@Length(min = 0, max = 1000)
private String prerequisite;
@ColumnWidth(50)
@ExcelProperty("備註")
@Length(max = 1000)
private String remark;
@ColumnWidth(50)
@ExcelProperty("步驟描述")
@Length(max = 1000)
private String stepDesc;
@ColumnWidth(50)
@ExcelProperty("預期結果")
@Length(max = 1000)
private String stepResult;
}

View File

@ -0,0 +1,65 @@
package io.metersphere.excel.domain;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@Data
@ColumnWidth(15)
public class TestCaseExcelDataUs extends TestCaseExcelData {
@NotBlank(message = "{cannot_be_null}")
@Length(max = 50)
@ExcelProperty("Name")
private String name;
@NotBlank(message = "{cannot_be_null}")
@Length(max = 1000)
@ExcelProperty("Module")
@ColumnWidth(30)
@Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}")
private String nodePath;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("Type")
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}")
private String type;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("Maintainer")
private String maintainer;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("Priority")
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
private String priority;
@NotBlank(message = "{cannot_be_null}")
@ExcelProperty("Method")
@Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
private String method;
@ColumnWidth(50)
@ExcelProperty("Prerequisite")
@Length(min = 0, max = 1000)
private String prerequisite;
@ColumnWidth(50)
@ExcelProperty("Remark")
@Length(max = 1000)
private String remark;
@ColumnWidth(50)
@ExcelProperty("Step description")
@Length(max = 1000)
private String stepDesc;
@ColumnWidth(50)
@ExcelProperty("Step result")
@Length(max = 1000)
private String stepResult;
}

View File

@ -8,7 +8,6 @@ import com.alibaba.excel.util.StringUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.utils.EasyExcelI18nTranslator;
import io.metersphere.excel.utils.ExcelValidateHelper;
import io.metersphere.i18n.Translator;
@ -17,14 +16,12 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
public abstract class EasyExcelListener<T> extends AnalysisEventListener<T> implements AutoCloseable {
public abstract class EasyExcelListener<T> extends AnalysisEventListener<T> {
protected List<ExcelErrData<T>> errList = new ArrayList<>();
protected List<T> list = new ArrayList<>();
protected EasyExcelI18nTranslator easyExcelI18nTranslator;
protected List<TestCaseExcelData> excelDataList = new ArrayList<>();
/**
@ -37,11 +34,6 @@ public abstract class EasyExcelListener<T> extends AnalysisEventListener<T> impl
public EasyExcelListener() {
Type type = getClass().getGenericSuperclass();
this.clazz = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
//防止多线程修改运行时类注解后saveOriginalExcelProperty保存的是修改后的值
synchronized (EasyExcelI18nTranslator.class) {
this.easyExcelI18nTranslator = new EasyExcelI18nTranslator(this.clazz);
this.easyExcelI18nTranslator.translateExcelProperty();
}
}
/**
@ -153,9 +145,4 @@ public abstract class EasyExcelListener<T> extends AnalysisEventListener<T> impl
return errList;
}
@Override
public void close() {
this.easyExcelI18nTranslator.resetExcelProperty();
}
}

View File

@ -12,20 +12,12 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
public class EasyExcelExporter implements AutoCloseable {
EasyExcelI18nTranslator easyExcelI18nTranslator;
public class EasyExcelExporter {
private Class clazz;
public EasyExcelExporter(Class clazz) {
this.clazz = clazz;
//防止多线程修改运行时类注解后saveOriginalExcelProperty保存的是修改后的值
synchronized (EasyExcelI18nTranslator.class) {
easyExcelI18nTranslator = new EasyExcelI18nTranslator(clazz);
easyExcelI18nTranslator.translateExcelProperty();
}
}
public void export(HttpServletResponse response, List data, String fileName, String sheetName) {
@ -46,9 +38,4 @@ public class EasyExcelExporter implements AutoCloseable {
}
}
@Override
public void close() {
easyExcelI18nTranslator.resetExcelProperty();
}
}

View File

@ -1,98 +0,0 @@
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

@ -20,6 +20,7 @@ import io.metersphere.controller.request.OrderRequest;
import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.domain.TestCaseExcelDataFactory;
import io.metersphere.excel.listener.EasyExcelListener;
import io.metersphere.excel.listener.TestCaseDataListener;
import io.metersphere.excel.utils.EasyExcelExporter;
@ -302,8 +303,9 @@ public class TestCaseService {
Set<String> userIds = userRoleMapper.selectByExample(userRoleExample).stream().map(UserRole::getUserId).collect(Collectors.toSet());
try (EasyExcelListener easyExcelListener = new TestCaseDataListener(this, projectId, testCaseNames, userIds)) {
EasyExcelFactory.read(multipartFile.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead();
try {
EasyExcelListener easyExcelListener = new TestCaseDataListener(this, projectId, testCaseNames, userIds);
EasyExcelFactory.read(multipartFile.getInputStream(), new TestCaseExcelDataFactory().getExcelDataByLocal(), easyExcelListener).sheet().doRead();
errList = easyExcelListener.getErrList();
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
@ -341,7 +343,8 @@ public class TestCaseService {
}
public void testCaseTemplateExport(HttpServletResponse response) {
try (EasyExcelExporter easyExcelExporter = new EasyExcelExporter(TestCaseExcelData.class)) {
try {
EasyExcelExporter easyExcelExporter = new EasyExcelExporter(new TestCaseExcelDataFactory().getExcelDataByLocal());
easyExcelExporter.export(response, generateExportTemplate(),
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"));
} catch (Exception e) {
@ -419,10 +422,12 @@ public class TestCaseService {
}
public void testCaseExport(HttpServletResponse response, TestCaseBatchRequest request) {
try (EasyExcelExporter easyExcelExporter = new EasyExcelExporter(TestCaseExcelData.class)) {
try {
EasyExcelExporter easyExcelExporter = new EasyExcelExporter(new TestCaseExcelDataFactory().getExcelDataByLocal());
easyExcelExporter.export(response, generateTestCaseExcel(request),
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"));
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException(e);
}
}
@ -470,7 +475,7 @@ public class TestCaseService {
} else if (t.getMethod().equals("auto") && t.getType().equals("api")) {
data.setStepDesc("");
data.setStepResult("");
if (t.getTestId().equals("other")) {
if (t.getTestId() != null && t.getTestId().equals("other")) {
data.setRemark(t.getOtherTestName());
} else {
data.setRemark(t.getApiName());
@ -479,7 +484,7 @@ public class TestCaseService {
} else if (t.getMethod().equals("auto") && t.getType().equals("performance")) {
data.setStepDesc("");
data.setStepResult("");
if (t.getTestId().equals("other")) {
if (t.getTestId() != null && t.getTestId().equals("other")) {
data.setRemark(t.getOtherTestName());
} else {
data.setRemark(t.getPerformName());