diff --git a/backend/src/main/java/io/metersphere/config/I18nConfig.java b/backend/src/main/java/io/metersphere/config/I18nConfig.java index 5467268924..f4a8d108de 100644 --- a/backend/src/main/java/io/metersphere/config/I18nConfig.java +++ b/backend/src/main/java/io/metersphere/config/I18nConfig.java @@ -3,8 +3,12 @@ package io.metersphere.config; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.i18n.Translator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +import javax.validation.Validator; @Configuration public class I18nConfig { @@ -20,4 +24,16 @@ public class I18nConfig { public CommonBeanFactory commonBeanFactory() { return new CommonBeanFactory(); } + + /** + * JSR-303校验国际化 + * @param messageSource + * @return + */ + @Bean + public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) { + LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); + localValidatorFactoryBean.setValidationMessageSource(messageSource); + return localValidatorFactoryBean; + } } diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java index c04bea2fc6..04e124c695 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java @@ -2,12 +2,13 @@ package io.metersphere.excel.domain; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; -import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; +@Data @ColumnWidth(15) public class TestCaseExcelData { @@ -20,12 +21,12 @@ public class TestCaseExcelData { @Length(max=1000) @ExcelProperty("所属模块") @ColumnWidth(30) - @Pattern(regexp = "^(?!.*//).*$", message = "格式不正确") + @Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}") private String nodePath; @NotBlank @ExcelProperty("用例类型") - @Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "必须为functional、performance、api") + @Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}") private String type; @NotBlank @@ -34,12 +35,12 @@ public class TestCaseExcelData { @NotBlank @ExcelProperty("优先级") - @Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "必须为P0、P1、P2、P3") + @Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}") private String priority; @NotBlank @ExcelProperty("测试方式") - @Pattern(regexp = "(^manual$)|(^auto$)", message = "必须为manual、auto") + @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}") private String method; @ColumnWidth(50) @@ -61,85 +62,4 @@ public class TestCaseExcelData { @ExcelProperty("预期结果") @Length(max=1000) private String stepResult; - - public String getNodePath() { - return nodePath; - } - - public void setNodePath(String nodePath) { - this.nodePath = nodePath; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getMaintainer() { - return maintainer; - } - - public void setMaintainer(String maintainer) { - this.maintainer = maintainer; - } - - public String getPriority() { - return priority; - } - - public void setPriority(String priority) { - this.priority = priority; - } - - public String getMethod() { - return method; - } - - public void setMethod(String method) { - this.method = method; - } - - public String getPrerequisite() { - return prerequisite; - } - - public void setPrerequisite(String prerequisite) { - this.prerequisite = prerequisite; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public String getStepDesc() { - return stepDesc; - } - - public void setStepDesc(String stepDesc) { - this.stepDesc = stepDesc; - } - - public String getStepResult() { - return stepResult; - } - - public void setStepResult(String stepResult) { - this.stepResult = stepResult; - } - } diff --git a/backend/src/main/java/io/metersphere/excel/listener/EasyExcelListener.java b/backend/src/main/java/io/metersphere/excel/listener/EasyExcelListener.java index 1d95d382ba..acfd96c6ae 100644 --- a/backend/src/main/java/io/metersphere/excel/listener/EasyExcelListener.java +++ b/backend/src/main/java/io/metersphere/excel/listener/EasyExcelListener.java @@ -8,11 +8,16 @@ 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.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.util.*; - +@Component public abstract class EasyExcelListener extends AnalysisEventListener { protected List> errList = new ArrayList<>(); @@ -26,9 +31,12 @@ public abstract class EasyExcelListener extends AnalysisEventListener { protected Class clazz; + @Resource + ExcelValidateHelper excelValidateHelper; - public EasyExcelListener(Class clazz){ - this.clazz = clazz; + public EasyExcelListener(){ + Type type = getClass().getGenericSuperclass(); + this.clazz = (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; } /** @@ -43,16 +51,18 @@ public abstract class EasyExcelListener extends AnalysisEventListener { 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) { - errMsg = "解析数据出错"; + errMsg = Translator.get("parse_data_error"); LogUtil.error(e.getMessage(), e); } if (!StringUtils.isEmpty(errMsg)) { - ExcelErrData excelErrData = new ExcelErrData(t, rowIndex, "第" + rowIndex + "行出错:" + errMsg); + ExcelErrData excelErrData = new ExcelErrData(t, rowIndex, + Translator.get("number") + rowIndex + Translator.get("row") + Translator.get("error") + + ":" + errMsg); errList.add(excelErrData); } else { list.add(t); @@ -101,7 +111,7 @@ public abstract class EasyExcelListener extends AnalysisEventListener { Collection values = headMap.values(); for (String key : fieldNameSet) { if (!values.contains(key)){ - throw new ExcelAnalysisException("缺少头部信息:" + key); + throw new ExcelAnalysisException(Translator.get("missing_header_information") + ":" + key); } } } catch (NoSuchFieldException e) { @@ -133,8 +143,10 @@ public abstract class EasyExcelListener extends AnalysisEventListener { } - public List> getErrList() { - return errList; + public List> getAndClearErrList() { + List> tmp = this.errList; + this.errList = new ArrayList<>(); + return tmp; } } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java b/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java index c9df3b95e2..350c9b6fae 100644 --- a/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java +++ b/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java @@ -6,18 +6,22 @@ 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.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 { + @Resource private TestCaseService testCaseService; private String projectId; @@ -26,13 +30,13 @@ public class TestCaseDataListener extends EasyExcelListener { Set userIds; - public TestCaseDataListener(TestCaseService testCaseService, String projectId, - Set testCaseNames, Set userIds, Class clazz) { - super(clazz); - this.testCaseService = testCaseService; + public TestCaseDataListener() {} + + public TestCaseDataListener init(String projectId, Set testCaseNames, Set userIds) { this.projectId = projectId; this.testCaseNames = testCaseNames; this.userIds = userIds; + return this; } @Override @@ -43,21 +47,22 @@ public class TestCaseDataListener extends EasyExcelListener { if (nodePath != null) { String[] nodes = nodePath.split("/"); if ( nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) { - stringBuilder.append("节点最多为" + TestCaseConstants.MAX_NODE_DEPTH + "层;"); + stringBuilder.append(Translator.get("test_case_node_level_tip") + + TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level")); } for (int i = 0; i < nodes.length; i++) { if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) { - stringBuilder.append("所属模块不能为空格"); + stringBuilder.append(Translator.get("module_not_null")); break; } } } if (!userIds.contains(data.getMaintainer())) { - stringBuilder.append("该工作空间下无该用户:" + data.getMaintainer() + ";"); + stringBuilder.append(Translator.get("user_not_exists") + ":" + data.getMaintainer() + "; "); } if (testCaseNames.contains(data.getName())) { - stringBuilder.append("该项目下已存在该测试用例:" + data.getName() + ";"); + stringBuilder.append(Translator.get("test_case_already_exists") + ":" + data.getName() + "; "); } return stringBuilder.toString(); } diff --git a/backend/src/main/java/io/metersphere/excel/utils/ExcelValidateHelper.java b/backend/src/main/java/io/metersphere/excel/utils/ExcelValidateHelper.java index e4e29fa144..6bacd4a925 100644 --- a/backend/src/main/java/io/metersphere/excel/utils/ExcelValidateHelper.java +++ b/backend/src/main/java/io/metersphere/excel/utils/ExcelValidateHelper.java @@ -1,30 +1,32 @@ package io.metersphere.excel.utils; import com.alibaba.excel.annotation.ExcelProperty; +import org.springframework.stereotype.Component; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import javax.annotation.Resource; import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; import javax.validation.groups.Default; import java.lang.reflect.Field; import java.util.Set; - +@Component public class ExcelValidateHelper { private ExcelValidateHelper(){} - private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + @Resource + LocalValidatorFactoryBean localValidatorFactoryBean; - public static String validateEntity(T obj) throws NoSuchFieldException { + public String validateEntity(T obj) throws NoSuchFieldException { StringBuilder result = new StringBuilder(); - Set> set = validator.validate(obj, Default.class); + Set> set = localValidatorFactoryBean.getValidator().validate(obj, Default.class); if (set != null && !set.isEmpty()) { for (ConstraintViolation cv : set) { Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString()); ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class); //拼接错误信息,包含当前出错数据的标题名字+错误信息 - result.append(annotation.value()[0]+cv.getMessage()).append(";"); + result.append(annotation.value()[0]+cv.getMessage()).append("; "); } } return result.toString(); diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java index c8ef98ce14..9faa743a84 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java @@ -260,7 +260,7 @@ public class TestCaseNodeService { String rootNodeName = null; if (nodeNameList.size() <= 1) { - throw new ExcelException(Translator.get("test_case_module_not_null") + ":" + path); + throw new ExcelException(Translator.get("test_case_create_module_fail") + ":" + path); } else { pathIterator.next(); pathIterator.remove(); diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index e1a3f74450..a509c1441a 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -60,6 +60,9 @@ public class TestCaseService { @Resource TestCaseNodeService testCaseNodeService; + @Resource + TestCaseDataListener testCaseDataListener; + @Resource UserMapper userMapper; @@ -187,11 +190,10 @@ public class TestCaseService { List users = userMapper.selectByExample(userExample); Set userIds = users.stream().map(User::getId).collect(Collectors.toSet()); - EasyExcelListener easyExcelListener = new TestCaseDataListener(this, projectId, - testCaseNames, userIds, TestCaseExcelData.class); - EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead(); + EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, + testCaseDataListener.init(projectId, testCaseNames, userIds)).sheet().doRead(); - List> errList = easyExcelListener.getErrList(); + List> errList = testCaseDataListener.getAndClearErrList(); //如果包含错误信息就导出错误信息 if (!errList.isEmpty()) { excelResponse.setSuccess(false); diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 9ed63647c2..195a2618d7 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -3,6 +3,18 @@ before_delete_plan= test_case_node_level_tip= test_case_node_level= test_case_module_not_null= -test_case_create_moule_fail= +test_case_create_module_fail= test_case_import_template_name= -test_case_import_template_sheet= \ No newline at end of file +test_case_import_template_sheet= +module_not_null= +user_not_exists= +test_case_already_exists= +parse_data_error= +missing_header_information= +number= +row= +error= +incorrect_format= +test_case_type_validate= +test_case_priority_validate= +test_case_method_validate= \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 2c259c5861..ad60394485 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -37,6 +37,18 @@ password_modification_failed=Password modification failed cannot_delete_current_user=Cannot delete the user currently logged in test_case_node_level=level test_case_module_not_null=The owned module cannot be empty -test_case_create_moule_fail=Failed to create module +test_case_create_module_fail=Failed to create module test_case_import_template_name=Test case templates -test_case_import_template_sheet=Template \ No newline at end of file +test_case_import_template_sheet=Template +module_not_null=The module must not be blank +user_not_exists=The user in this workspace is not exists +test_case_already_exists=The test case in this project is exists +parse_data_error=Parse data error +missing_header_information=Missing header information +number=Number +row=row +error=error +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 \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 4272998a62..89954c4501 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -38,7 +38,19 @@ cannot_delete_current_user=无法删除当前登录用户 test_case_node_level=层 test_case_node_level_tip=模块树最大深度为 test_case_module_not_null=所属模块不能为空 -test_case_create_moule_fail=创建模块失败 +test_case_create_module_fail=创建模块失败 test_case_import_template_name=测试用例模版 test_case_import_template_sheet=模版 +module_not_null=所属模块不能为空格 +user_not_exists=该工作空间下无该用户 +test_case_already_exists=该项目下已存在该测试用例 +parse_data_error=解析数据出错 +missing_header_information=缺少头部信息 +number=第 +row=行 +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 end \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 777656a854..0fc4492dca 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -1,6 +1,6 @@ test_case_exist=該項目下已存在用例: error_lang_invalid=語言參數錯誤 -load_test_already_exists=測試名稱不能重複 +load_test_already_exists=測試名稱不能重復 project_name_is_null=項目名稱不能為空 project_name_already_exists=項目名稱已存在 workspace_name_is_null=工作空間名不能為空 @@ -16,10 +16,10 @@ run_load_test_file_init_error=無法運行測試,初始化運行環境失敗 load_test_is_running=測試正在運行, 請等待 node_deep_limit=節點深度不超過5層! no_nodes_message=沒有節點信息 -duplicate_node_ip=節點 IP 重複 -only_one_k8s=只能添加一個 K8s +duplicate_node_ip=節點 IP 重復 +only_one_k8s=只能添加壹個 K8s organization_id_is_null=組織 ID 不能為空 -max_thread_insufficient=並髮用戶數超額 +max_thread_insufficient=並發用戶數超額 cannot_edit_load_test_running=不能修改正在運行的測試 test_not_found=測試不存在: test_not_running=測試未運行 @@ -38,7 +38,19 @@ cannot_delete_current_user=無法刪除當前登錄用戶 test_case_node_level=層 test_case_node_level_tip=模塊樹最大深度為 test_case_module_not_null=所屬模塊不能為空 -test_case_create_moule_fail=創建模塊失敗 +test_case_create_module_fail=創建模塊失敗 test_case_import_template_name=測試用例模版 test_case_import_template_sheet=模版 +module_not_null=所屬模塊不能為空格 +user_not_exists=該工作空間下無該用戶 +test_case_already_exists=該項目下已存在該測試用例 +parse_data_error=解析數據出錯 +missing_header_information=缺少頭部信息 +number=第 +row=行 +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 end \ No newline at end of file diff --git a/frontend/src/business/components/common/components/MsTestHeatmap.vue b/frontend/src/business/components/common/components/MsTestHeatmap.vue index fd5e921786..df843f1e74 100644 --- a/frontend/src/business/components/common/components/MsTestHeatmap.vue +++ b/frontend/src/business/components/common/components/MsTestHeatmap.vue @@ -16,46 +16,45 @@ data() { return { endDate: new Date(), - unit: 'tests', colorRange: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'], - locale: { - // 一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月 - months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], - // 星期日 Sun. 星期一 Mon. 星期二 Tues. 星期三 Wed. 星期四 Thur. 星期五 Fri. 星期六 Sat. - days: ['Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'], + } + }, + computed: { + locale() { + return { + months: [ + this.$t('commons.months_1'), + this.$t('commons.months_2'), + this.$t('commons.months_3'), + this.$t('commons.months_4'), + this.$t('commons.months_5'), + this.$t('commons.months_6'), + this.$t('commons.months_7'), + this.$t('commons.months_8'), + this.$t('commons.months_9'), + this.$t('commons.months_10'), + this.$t('commons.months_11'), + this.$t('commons.months_12') + ], + days: [ + this.$t('commons.weeks_0'), + this.$t('commons.weeks_1'), + this.$t('commons.weeks_2'), + this.$t('commons.weeks_3'), + this.$t('commons.weeks_4'), + this.$t('commons.weeks_5'), + this.$t('commons.weeks_6') + ], No: 'No', on: ',', less: 'Less', more: 'More' - }, + } + }, + unit() { + return this.$t('commons.test_unit') } }, - mounted() { - this.locale.months = [ - this.$t('commons.months_1'), - this.$t('commons.months_2'), - this.$t('commons.months_3'), - this.$t('commons.months_4'), - this.$t('commons.months_5'), - this.$t('commons.months_6'), - this.$t('commons.months_7'), - this.$t('commons.months_8'), - this.$t('commons.months_9'), - this.$t('commons.months_10'), - this.$t('commons.months_11'), - this.$t('commons.months_12') - ]; - this.locale.days = [ - this.$t('commons.weeks_0'), - this.$t('commons.weeks_1'), - this.$t('commons.weeks_2'), - this.$t('commons.weeks_3'), - this.$t('commons.weeks_4'), - this.$t('commons.weeks_5'), - this.$t('commons.weeks_6') - ]; - this.unit = this.$t('commons.test_unit') - } } diff --git a/frontend/src/business/components/track/case/components/TestCaseImport.vue b/frontend/src/business/components/track/case/components/TestCaseImport.vue index fad096aa9f..f988218fa2 100644 --- a/frontend/src/business/components/track/case/components/TestCaseImport.vue +++ b/frontend/src/business/components/track/case/components/TestCaseImport.vue @@ -1,7 +1,4 @@ - .el-dialog__body { - padding-top: 10px; - padding-bottom: 10px; - } + - -