From 4eb0348e6ca60f83b7ec17e9faece4c2da3c6c69 Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Sat, 20 Aug 2022 15:15:47 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=B5=8B=E8=AF=95=E8=B7=9F=E8=B8=AA):?= =?UTF-8?q?=20=E4=BC=98=E5=8C=96=E5=8A=9F=E8=83=BD=E7=94=A8=E4=BE=8B?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --story=1008224 --user=陈建星 用例导出/导入支持自定义字段 https://www.tapd.cn/55049933/s/1225482 --- .../commons/constants/CustomFieldType.java | 21 ++- .../commons/constants/TestCaseStatus.java | 5 - .../commons/constants/TestCaseTestStatus.java | 5 + .../CustomFieldValidateException.java | 13 ++ .../io/metersphere/dto/CustomFieldOption.java | 11 +- .../excel/constants/TestCaseImportFiled.java | 40 ++--- .../excel/domain/TestCaseExcelData.java | 2 +- .../excel/domain/TestCaseExcelDataCn.java | 12 -- .../excel/domain/TestCaseExcelDataTw.java | 12 -- .../excel/domain/TestCaseExcelDataUs.java | 11 -- .../FunctionCaseTemplateWriteHandler.java | 2 +- .../listener/TestCaseNoModelDataListener.java | 134 +++++------------ .../track/constants/TestCaseStatus.java | 17 +++ .../track/service/TestCaseService.java | 4 +- .../track/service/TestPlanService.java | 6 +- .../AbstractCustomFieldValidator.java | 50 +++++++ .../CustomFieldDateTimeValidator.java | 22 +++ .../validate/CustomFieldDateValidator.java | 21 +++ .../validate/CustomFieldFloatValidator.java | 20 +++ .../validate/CustomFieldIntegerValidator.java | 20 +++ .../validate/CustomFieldMemberValidator.java | 48 ++++++ .../CustomFieldMultipleMemberValidator.java | 39 +++++ .../CustomFieldMultipleSelectValidator.java | 42 ++++++ .../CustomFieldMultipleTextValidator.java | 21 +++ .../validate/CustomFieldSelectValidator.java | 140 ++++++++++++++++++ .../validate/CustomFieldTextValidator.java | 11 ++ .../validate/CustomFieldValidatorFactory.java | 33 +++++ .../resources/i18n/messages_zh_CN.properties | 8 + 28 files changed, 602 insertions(+), 168 deletions(-) delete mode 100644 backend/src/main/java/io/metersphere/commons/constants/TestCaseStatus.java create mode 100644 backend/src/main/java/io/metersphere/commons/constants/TestCaseTestStatus.java create mode 100644 backend/src/main/java/io/metersphere/commons/exception/CustomFieldValidateException.java create mode 100644 backend/src/main/java/io/metersphere/track/constants/TestCaseStatus.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/AbstractCustomFieldValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldDateTimeValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldDateValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldFloatValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldIntegerValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldMemberValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleMemberValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleSelectValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleTextValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldSelectValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldTextValidator.java create mode 100644 backend/src/main/java/io/metersphere/track/validate/CustomFieldValidatorFactory.java diff --git a/backend/src/main/java/io/metersphere/commons/constants/CustomFieldType.java b/backend/src/main/java/io/metersphere/commons/constants/CustomFieldType.java index 080c65e286..6493686d4f 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/CustomFieldType.java +++ b/backend/src/main/java/io/metersphere/commons/constants/CustomFieldType.java @@ -1,13 +1,20 @@ package io.metersphere.commons.constants; public enum CustomFieldType { - INPUT("input"),TEXTAREA("textarea"), - SELECT("select"),MULTIPLE_SELECT("multipleSelect"), - RADIO("radio"),CHECKBOX("checkbox"), - MEMBER("member"), MULTIPLE_MEMBER("multipleMember"), - DATE("date"),DATETIME("datetime"), - INT("int"),FLOAT("float"), - MULTIPLE_INPUT("multipleInput"),RICH_TEXT("richText"); + INPUT("input"), + TEXTAREA("textarea"), + SELECT("select"), + MULTIPLE_SELECT("multipleSelect"), + RADIO("radio"), + CHECKBOX("checkbox"), + MEMBER("member"), + MULTIPLE_MEMBER("multipleMember"), + DATE("date"), + DATETIME("datetime"), + INT("int"), + FLOAT("float"), + MULTIPLE_INPUT("multipleInput"), + RICH_TEXT("richText"); String value; diff --git a/backend/src/main/java/io/metersphere/commons/constants/TestCaseStatus.java b/backend/src/main/java/io/metersphere/commons/constants/TestCaseStatus.java deleted file mode 100644 index 9b14b52f92..0000000000 --- a/backend/src/main/java/io/metersphere/commons/constants/TestCaseStatus.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.metersphere.commons.constants; - -public enum TestCaseStatus { - performance,api,testcase,automation -} diff --git a/backend/src/main/java/io/metersphere/commons/constants/TestCaseTestStatus.java b/backend/src/main/java/io/metersphere/commons/constants/TestCaseTestStatus.java new file mode 100644 index 0000000000..bc557523d0 --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/constants/TestCaseTestStatus.java @@ -0,0 +1,5 @@ +package io.metersphere.commons.constants; + +public enum TestCaseTestStatus { + performance, api, testcase, automation +} diff --git a/backend/src/main/java/io/metersphere/commons/exception/CustomFieldValidateException.java b/backend/src/main/java/io/metersphere/commons/exception/CustomFieldValidateException.java new file mode 100644 index 0000000000..8240c8848b --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/exception/CustomFieldValidateException.java @@ -0,0 +1,13 @@ +package io.metersphere.commons.exception; + +public class CustomFieldValidateException extends Exception { + private static final long serialVersionUID = 1L; + + public CustomFieldValidateException(String message) { + super(message); + } + + public static void throwException(String message) throws CustomFieldValidateException { + throw new CustomFieldValidateException(message); + } +} diff --git a/backend/src/main/java/io/metersphere/dto/CustomFieldOption.java b/backend/src/main/java/io/metersphere/dto/CustomFieldOption.java index d3d9b2ef35..f3f3daa42e 100644 --- a/backend/src/main/java/io/metersphere/dto/CustomFieldOption.java +++ b/backend/src/main/java/io/metersphere/dto/CustomFieldOption.java @@ -6,6 +6,15 @@ import lombok.Setter; @Setter @Getter public class CustomFieldOption { - private String text; private String value; + private String text; + private Boolean system = false; + + public CustomFieldOption(String value, String text, Boolean system) { + this.text = text; + this.value = value; + this.system = system; + } + + public CustomFieldOption() {} } diff --git a/backend/src/main/java/io/metersphere/excel/constants/TestCaseImportFiled.java b/backend/src/main/java/io/metersphere/excel/constants/TestCaseImportFiled.java index c9f553ad76..e55bc6d6fa 100644 --- a/backend/src/main/java/io/metersphere/excel/constants/TestCaseImportFiled.java +++ b/backend/src/main/java/io/metersphere/excel/constants/TestCaseImportFiled.java @@ -14,35 +14,41 @@ import java.util.function.Function; public enum TestCaseImportFiled { - ID("ID", "ID", "ID", TestCaseExcelData::getCustomNum), - NAME("用例名称", "用例名稱", "Name", TestCaseExcelData::getName), - MODULE("所属模块", "所屬模塊", "Module", TestCaseExcelData::getNodePath), - TAG("标签", "標簽", "Tag", TestCaseImportFiled::parseTags), - PREREQUISITE("前置条件", "前置條件", "Prerequisite", TestCaseExcelData::getPrerequisite), - REMARK("备注", "備註", "Remark", TestCaseExcelData::getRemark), - STEP_DESC("步骤描述", "步驟描述", "Step description", TestCaseExcelData::getStepDesc), - STEP_RESULT("预期结果", "預期結果", "Step result", TestCaseExcelData::getStepResult), - STEP_MODEL("编辑模式", "編輯模式", "Edit Model", TestCaseExcelData::getStepModel), - STATUS("用例状态", "用例狀態", "Case status", TestCaseExcelData::getStatus), - MAINTAINER("责任人", "責任人", "Maintainer", TestCaseExcelData::getMaintainer), - PRIORITY("用例等级", "用例等級", "Priority", TestCaseExcelData::getPriority); + ID("id", "ID", "ID", "ID", TestCaseExcelData::getCustomNum), + NAME("name", "用例名称", "用例名稱", "Name", TestCaseExcelData::getName), + MODULE("module","所属模块", "所屬模塊", "Module", TestCaseExcelData::getNodePath), + TAGS("tags","标签", "標簽", "Tag", TestCaseImportFiled::parseTags), + PREREQUISITE("prerequisite","前置条件", "前置條件", "Prerequisite", TestCaseExcelData::getPrerequisite), + REMARK("remark","备注", "備註", "Remark", TestCaseExcelData::getRemark), + STEP_DESC("stepDesc","步骤描述", "步驟描述", "Step description", TestCaseExcelData::getStepDesc), + STEP_RESULT("stepResult","预期结果", "預期結果", "Step result", TestCaseExcelData::getStepResult), + STEP_MODEL("stepModel","编辑模式", "編輯模式", "Edit Model", TestCaseExcelData::getStepModel), + STATUS("status","用例状态", "用例狀態", "Case status", TestCaseExcelData::getStatus), + MAINTAINER("maintainer","责任人", "責任人", "Maintainer", TestCaseExcelData::getMaintainer), + PRIORITY("priority","用例等级", "用例等級", "Priority", TestCaseExcelData::getPriority); private Map filedLangMap; private Function parseFunc; + private String value; - public Map getFiledLangMap() { - return this.filedLangMap; - } - - TestCaseImportFiled(String zn, String chineseTw, String us, Function parseFunc) { + TestCaseImportFiled(String value, String zn, String chineseTw, String us, Function parseFunc) { this.filedLangMap = new HashMap<>() {{ put(Locale.SIMPLIFIED_CHINESE, zn); put(Locale.TRADITIONAL_CHINESE, chineseTw); put(Locale.US, us); }}; + this.value = value; this.parseFunc = parseFunc; } + public Map getFiledLangMap() { + return this.filedLangMap; + } + + public String getValue() { + return value; + } + public String parseExcelDataValue(TestCaseExcelData excelData) { return parseFunc.apply(excelData); } 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 ebc1c02459..c67ac5e8e1 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java @@ -48,7 +48,7 @@ public class TestCaseExcelData { @ExcelIgnore private String priority; @ExcelIgnore - Map customDatas = new LinkedHashMap<>(); + Map customData = new LinkedHashMap<>(); @ExcelIgnore List mergeStepDesc; diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataCn.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataCn.java index 83b34eec04..f35750686d 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataCn.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataCn.java @@ -61,18 +61,6 @@ public class TestCaseExcelDataCn extends TestCaseExcelData { @Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}") private String stepModel; - @ColumnWidth(50) - @ExcelProperty("用例状态") - private String status; - - @ExcelProperty(value = "责任人(ID)") - private String maintainer; - - @NotBlank(message = "{cannot_be_null}") - @ExcelProperty("用例等级") - @Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}") - private String priority; - @Override public List> getHead(boolean needNum, List customFields) { return super.getHead(needNum, customFields, Locale.SIMPLIFIED_CHINESE); diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataTw.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataTw.java index c7503033f8..fa31c7f905 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataTw.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataTw.java @@ -61,18 +61,6 @@ public class TestCaseExcelDataTw extends TestCaseExcelData { @Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}") private String stepModel; - @ColumnWidth(50) - @ExcelProperty("用例狀態") - private String status; - - @NotBlank(message = "{cannot_be_null}") - @ExcelProperty("用例等級") - @Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}") - private String priority; - - @ExcelProperty("責任人(ID)") - private String maintainer; - @Override public List> getHead(boolean needNum, List customFields) { return super.getHead(needNum, customFields, Locale.TRADITIONAL_CHINESE); diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java index 961c58a109..c322d19047 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java @@ -62,17 +62,6 @@ public class TestCaseExcelDataUs extends TestCaseExcelData { @Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}") private String stepModel; - @ColumnWidth(50) - @ExcelProperty("Case status") - private String status; - - @ExcelProperty("Priority") - @Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}") - private String priority; - - @ExcelProperty("Maintainer(ID)") - private String maintainer; - @Override public List> getHead(boolean needNum, List customFields) { return super.getHead(needNum, customFields, Locale.US); diff --git a/backend/src/main/java/io/metersphere/excel/handler/FunctionCaseTemplateWriteHandler.java b/backend/src/main/java/io/metersphere/excel/handler/FunctionCaseTemplateWriteHandler.java index 447b8466c9..207478db86 100644 --- a/backend/src/main/java/io/metersphere/excel/handler/FunctionCaseTemplateWriteHandler.java +++ b/backend/src/main/java/io/metersphere/excel/handler/FunctionCaseTemplateWriteHandler.java @@ -58,7 +58,7 @@ public class FunctionCaseTemplateWriteHandler implements RowWriteHandler { maintainerIndex = index; } else if (TestCaseImportFiled.PRIORITY.containsHead(head)) { priorityIndex = index; - } else if (TestCaseImportFiled.TAG.containsHead(head)) { + } else if (TestCaseImportFiled.TAGS.containsHead(head)) { tagIndex = index; } else if (TestCaseImportFiled.STATUS.containsHead(head)) { statusIndex = index; diff --git a/backend/src/main/java/io/metersphere/excel/listener/TestCaseNoModelDataListener.java b/backend/src/main/java/io/metersphere/excel/listener/TestCaseNoModelDataListener.java index c36d986898..572a58bdaa 100644 --- a/backend/src/main/java/io/metersphere/excel/listener/TestCaseNoModelDataListener.java +++ b/backend/src/main/java/io/metersphere/excel/listener/TestCaseNoModelDataListener.java @@ -8,13 +8,12 @@ import com.alibaba.fastjson.JSONObject; import io.metersphere.base.domain.TestCase; import io.metersphere.base.domain.TestCaseWithBLOBs; import io.metersphere.commons.constants.TestCaseConstants; +import io.metersphere.commons.exception.CustomFieldValidateException; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.BeanUtils; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.LogUtil; -import io.metersphere.commons.utils.SessionUtils; import io.metersphere.dto.CustomFieldDao; -import io.metersphere.dto.CustomFieldOption; import io.metersphere.excel.annotation.NotRequired; import io.metersphere.excel.constants.TestCaseImportFiled; import io.metersphere.excel.domain.ExcelErrData; @@ -26,6 +25,8 @@ import io.metersphere.excel.utils.FunctionCaseImportEnum; import io.metersphere.i18n.Translator; import io.metersphere.track.request.testcase.TestCaseImportRequest; import io.metersphere.track.service.TestCaseService; +import io.metersphere.track.validate.AbstractCustomFieldValidator; +import io.metersphere.track.validate.CustomFieldValidatorFactory; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; @@ -97,6 +98,8 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener mergeCellDataMap = new HashMap<>(); + private HashMap customFieldValidatorMap; + public boolean isUpdated() { return isUpdated; } @@ -109,23 +112,11 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener customFields = request.getCustomFields(); if (CollectionUtils.isNotEmpty(customFields)) { - for (CustomFieldDao dto : customFields) { - String name = dto.getName(); - if (StringUtils.isNotEmpty(name)) { - name = name.trim(); - } - if (TestCaseImportFiled.MAINTAINER.getFiledLangMap().values().contains(name)) { - customFieldsMap.put("maintainer", dto); - } else if (TestCaseImportFiled.PRIORITY.getFiledLangMap().values().contains(name)) { - customFieldsMap.put("priority", dto); - } else if (TestCaseImportFiled.STATUS.getFiledLangMap().values().contains(name)) { - customFieldsMap.put("status", dto); - } else { - customFieldsMap.put(name, dto); - } - } + customFieldsMap = customFields.stream().collect(Collectors.toMap(CustomFieldDao::getName, i -> i)); } } @@ -377,48 +368,36 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener customEntry : customFieldsMap.entrySet()) { - String customName = customEntry.getKey(); - CustomFieldDao field = customEntry.getValue(); - - if (field.getRequired()) { - String value; - if (StringUtils.equals(customName, "status")) { - Map valueMap = new HashMap<>() {{ - put("Prepare", Translator.get("test_case_status_prepare")); - put("Underway", Translator.get("test_case_status_running")); - put("Completed", Translator.get("test_case_status_finished")); - }}; - value = getCustomFieldValue(Translator.get("test_case_status"), data.getStatus(), field.getOptions(), - stringBuilder, valueMap); - data.setStatus(value); - } else if (StringUtils.equals(customName, "priority")) { - value = data.getPriority(); - } else if (StringUtils.equals(customName, "maintainer")) { - value = data.getMaintainer(); - //校验维护人 - if (StringUtils.isBlank(value)) { - data.setMaintainer(SessionUtils.getUserId()); - } else { - if (!request.getUserIds().contains(value)) { - stringBuilder.append(Translator.get("user_not_exists")) - .append( ":") - .append(value) - .append(ERROR_MSG_SEPARATOR); - } - } - continue; - } else { - value = data.getCustomDatas().get(customName); - } - if (StringUtils.isEmpty(value)) { - stringBuilder.append(field.getName()) - .append(" ") - .append(Translator.get("required")) - .append(ERROR_MSG_SEPARATOR); - } + Map customData = data.getCustomData(); + for (String fieldName : customData.keySet()) { + String value = customData.get(fieldName); + CustomFieldDao customField = customFieldsMap.get(fieldName); + if (customField == null) { + continue; + } + AbstractCustomFieldValidator customFieldValidator = customFieldValidatorMap.get(customField.getType()); + try { + customFieldValidator.validate(customField, value); + } catch (CustomFieldValidateException e) { + stringBuilder.append(e.getMessage().concat(ERROR_MSG_SEPARATOR)); + } + if (customFieldValidator.isKVOption) { + // 这里如果填的是选项值,替换成选项ID,保存 + customData.put(fieldName, customFieldValidator.parse2Key(value, customField)); + } + if (StringUtils.equals(fieldName, TestCaseImportFiled.STATUS.getValue())) { + data.setStatus(value); + } else if (StringUtils.equals(fieldName, TestCaseImportFiled.PRIORITY.getValue())) { + data.setPriority(value); + } else if (StringUtils.equals(fieldName, TestCaseImportFiled.MAINTAINER.getValue())) { + data.setMaintainer(value); } } } @@ -485,37 +464,6 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener systemValueMap) { - List textList = new ArrayList<>(); - if (StringUtils.isNotEmpty(optionsStr)) { - List options = JSONObject.parseArray(optionsStr, CustomFieldOption.class); - for (CustomFieldOption option : options) { - // 系统字段需要翻译 - String i18nText = systemValueMap.get(option.getValue()); - if (i18nText != null) { - option.setText(i18nText); - } - if (StringUtils.equals(option.getValue(), text) || - // 系统字段填写对应内容,而不是key的情况,比如用例状态填未开始而不是 Prepare - StringUtils.equals(option.getText(), text)) { - return option.getValue(); - } - textList.add(option.getText()); - } - } - error.append(String.format(Translator.get("custom_field_option_not_exist"), name, textList.toString())); - return text; - } - public List getNames() { return this.names; } @@ -820,14 +768,8 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener fields = new ArrayList<>(); - Map customDataMaps = Optional.ofNullable(model.getCustomDatas()) + Map customDataMaps = Optional.ofNullable(model.getCustomData()) .orElse(new HashMap<>()); Map otherFieldMaps = Optional.ofNullable(model.getOtherFields()) .orElse(new HashMap<>()); @@ -1626,7 +1626,7 @@ public class TestCaseService { map.put(customNameMap.get(id), value); } } - data.setCustomDatas(map); + data.setCustomData(map); } catch (Exception e) { LogUtil.error(e); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index c20c5372b3..b63312f37a 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -627,7 +627,7 @@ public class TestPlanService { Long nextScenarioOrder = ServiceUtils.getNextOrder(request.getPlanId(), extTestPlanScenarioCaseMapper::getLastOrder); for (TestCaseTest l : list) { - if (StringUtils.equals(l.getTestType(), TestCaseStatus.performance.name())) { + if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.performance.name())) { String id = l.getTestId(); LoadTestWithBLOBs loadTest = loadTestMapper.selectByPrimaryKey(id); TestPlanLoadCaseWithBLOBs t = new TestPlanLoadCaseWithBLOBs(); @@ -650,7 +650,7 @@ public class TestPlanService { } } - if (StringUtils.equals(l.getTestType(), TestCaseStatus.testcase.name())) { + if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.testcase.name())) { TestPlanApiCase t = new TestPlanApiCase(); ApiTestCaseWithBLOBs apitest = apiTestCaseMapper.selectByPrimaryKey(l.getTestId()); if (null != apitest) { @@ -674,7 +674,7 @@ public class TestPlanService { } - if (StringUtils.equals(l.getTestType(), TestCaseStatus.automation.name())) { + if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.automation.name())) { TestPlanApiScenario t = new TestPlanApiScenario(); ApiScenarioWithBLOBs testPlanApiScenario = apiScenarioMapper.selectByPrimaryKey(l.getTestId()); if (testPlanApiScenario != null) { diff --git a/backend/src/main/java/io/metersphere/track/validate/AbstractCustomFieldValidator.java b/backend/src/main/java/io/metersphere/track/validate/AbstractCustomFieldValidator.java new file mode 100644 index 0000000000..19e6702982 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/AbstractCustomFieldValidator.java @@ -0,0 +1,50 @@ +package io.metersphere.track.validate; + +import com.alibaba.fastjson.JSONArray; +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractCustomFieldValidator { + + /** + * 标记是否是键值对的选项 + * 需要校验时可以填键也可以填值 + */ + public Boolean isKVOption = false; + + /** + * 校验参数是否合法 + * @param customField + * @param value + */ + abstract public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException; + + /** + * 将选项的值转化为对应的key + * @param keyOrValue + * @return + */ + public String parse2Key(String keyOrValue, CustomFieldDao customField) { + return keyOrValue; + } + + protected void validateRequired(CustomFieldDao customField, String value) throws CustomFieldValidateException { + if (customField.getRequired() && StringUtils.isBlank(value)) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_required_tip"), customField.getName())); + } + } + + protected List parse2Array(String name, String value) throws CustomFieldValidateException { + try { + return JSONArray.parseArray(value, String.class); + } catch (Exception e) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_required_tip"), name)); + } + return new ArrayList<>(); + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldDateTimeValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldDateTimeValidator.java new file mode 100644 index 0000000000..b8c18c14f7 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldDateTimeValidator.java @@ -0,0 +1,22 @@ +package io.metersphere.track.validate; + +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.commons.utils.DateUtils; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; + +public class CustomFieldDateTimeValidator extends AbstractCustomFieldValidator { + + @Override + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + try { + if (StringUtils.isNotBlank(value)) { + DateUtils.getTime(value); + } + } catch (Exception e) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_datetime_tip"), customField.getName(), DateUtils.TIME_PATTERN)); + } + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldDateValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldDateValidator.java new file mode 100644 index 0000000000..ac200fb570 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldDateValidator.java @@ -0,0 +1,21 @@ +package io.metersphere.track.validate; + +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.commons.utils.DateUtils; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; + +public class CustomFieldDateValidator extends AbstractCustomFieldValidator { + + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + try { + if (StringUtils.isNotBlank(value)) { + DateUtils.getDate(value); + } + } catch (Exception e) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_date_tip"), customField.getName(), DateUtils.DATE_PATTERM)); + } + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldFloatValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldFloatValidator.java new file mode 100644 index 0000000000..59d4f31d47 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldFloatValidator.java @@ -0,0 +1,20 @@ +package io.metersphere.track.validate; + +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; + +public class CustomFieldFloatValidator extends AbstractCustomFieldValidator { + + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + try { + if (StringUtils.isNotBlank(value)) { + Float.parseFloat(value); + } + } catch (Exception e) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_float_tip"), customField.getName())); + } + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldIntegerValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldIntegerValidator.java new file mode 100644 index 0000000000..5c44760baf --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldIntegerValidator.java @@ -0,0 +1,20 @@ +package io.metersphere.track.validate; + +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; + +public class CustomFieldIntegerValidator extends AbstractCustomFieldValidator { + + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + try { + if (StringUtils.isNotBlank(value)) { + Integer.parseInt(value); + } + } catch (Exception e) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_int_tip"), customField.getName())); + } + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldMemberValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldMemberValidator.java new file mode 100644 index 0000000000..5df1a9ec79 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldMemberValidator.java @@ -0,0 +1,48 @@ +package io.metersphere.track.validate; + +import io.metersphere.base.domain.User; +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import io.metersphere.service.UserService; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class CustomFieldMemberValidator extends AbstractCustomFieldValidator { + + protected Map userIdMap; + protected Map userNameMap; + + public CustomFieldMemberValidator() { + this.isKVOption = true; + UserService userService = CommonBeanFactory.getBean(UserService.class); + List memberOption = userService.getProjectMemberOption(SessionUtils.getCurrentProjectId()); + userIdMap = memberOption.stream().collect(Collectors.toMap(User::getId, User::getName)); + userNameMap = memberOption.stream().collect(Collectors.toMap(User::getName, User::getId)); + } + + @Override + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + if (StringUtils.isBlank(value)) { + return; + } + if (userIdMap.containsKey(value) || userNameMap.containsKey(value)) { + return; + } + throw new CustomFieldValidateException(String.format(Translator.get("custom_field_member_tip"), customField.getName())); + } + + @Override + public String parse2Key(String keyOrValue, CustomFieldDao customField) { + if (userNameMap.containsKey(keyOrValue)) { + return userNameMap.get(keyOrValue); + } + return keyOrValue; + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleMemberValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleMemberValidator.java new file mode 100644 index 0000000000..1517b109dd --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleMemberValidator.java @@ -0,0 +1,39 @@ +package io.metersphere.track.validate; + +import com.alibaba.fastjson.JSONArray; +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +public class CustomFieldMultipleMemberValidator extends CustomFieldMemberValidator { + + @Override + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + if (StringUtils.isBlank(value)) { + return; + } + + for (String item : parse2Array(customField.getName(), value)) { + if (!userIdMap.containsKey(item) && !userNameMap.containsKey(item)) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_member_tip"), customField.getName())); + } + } + } + + @Override + public String parse2Key(String keyOrValuesStr, CustomFieldDao customField) { + List keyOrValues = JSONArray.parseArray(keyOrValuesStr, String.class); + + for (int i = 0; i < keyOrValues.size(); i++) { + String item = keyOrValues.get(i); + if (userNameMap.containsKey(item)) { + keyOrValues.set(i, userNameMap.get(item)); + } + } + return JSONArray.toJSONString(keyOrValues); + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleSelectValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleSelectValidator.java new file mode 100644 index 0000000000..8eece8d939 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleSelectValidator.java @@ -0,0 +1,42 @@ +package io.metersphere.track.validate; + +import com.alibaba.fastjson.JSONArray; +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class CustomFieldMultipleSelectValidator extends CustomFieldSelectValidator { + + @Override + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + if (StringUtils.isBlank(value)) { + return; + } + Set idSet = optionValueSetCache.get(customField.getId()); + Set textSet = optionTextSetCache.get(customField.getId()); + for (String item : parse2Array(customField.getName(), value)) { + if (!idSet.contains(item) && !textSet.contains(item)) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_select_tip"), customField.getName(), textSet)); + } + } + } + + @Override + public String parse2Key(String keyOrValuesStr, CustomFieldDao customField) { + List keyOrValues = JSONArray.parseArray(keyOrValuesStr, String.class); + Map nameMap = optionTextMapCache.get(customField.getId()); + for (int i = 0; i < keyOrValues.size(); i++) { + String item = keyOrValues.get(i); + if (nameMap.containsKey(item)) { + keyOrValues.set(i, nameMap.get(item)); + } + } + return JSONArray.toJSONString(keyOrValues); + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleTextValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleTextValidator.java new file mode 100644 index 0000000000..acf1a815eb --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldMultipleTextValidator.java @@ -0,0 +1,21 @@ +package io.metersphere.track.validate; + +import com.alibaba.fastjson.JSONArray; +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; + +public class CustomFieldMultipleTextValidator extends AbstractCustomFieldValidator { + + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + if (StringUtils.isNotBlank(value)) { + try { + JSONArray.parseArray(value); + } catch (Exception e) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_array_tip"), customField.getName())); + } + } + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldSelectValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldSelectValidator.java new file mode 100644 index 0000000000..5517010f2c --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldSelectValidator.java @@ -0,0 +1,140 @@ +package io.metersphere.track.validate; + +import com.alibaba.fastjson.JSONArray; +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.dto.CustomFieldDao; +import io.metersphere.dto.CustomFieldOption; +import io.metersphere.excel.constants.TestCaseImportFiled; +import io.metersphere.i18n.Translator; +import io.metersphere.track.constants.TestCaseStatus; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.stream.Collectors; + +public class CustomFieldSelectValidator extends AbstractCustomFieldValidator { + + /** + * 缓存每个字段对应的选项值 + */ + Map> optionCache = new HashMap<>(); + Map> optionValueSetCache = new HashMap<>(); + Map> optionTextSetCache = new HashMap<>(); + Map> optionTextMapCache = new HashMap<>(); + + /** + * 保存系统字段中选项翻译后的值 + * key 为字段名称,value 为选项value,和选项值的映射 + */ + Map> i18nMap = new HashMap<>(); + + public CustomFieldSelectValidator() { + Map statusI18nMap = new HashMap<>(); + for (TestCaseStatus status : TestCaseStatus.values()) { + statusI18nMap.put(status.name(), Translator.get(status.getI18nKey())); + } + i18nMap.put(TestCaseImportFiled.STATUS.getFiledLangMap().get(Locale.SIMPLIFIED_CHINESE), statusI18nMap); + this.isKVOption = true; + } + + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + if (StringUtils.isBlank(value)) { + return; + } + prepareCatch(customField); + Set idSet = optionValueSetCache.get(customField.getId()); + Set textSet = optionTextSetCache.get(customField.getId()); + if (!idSet.contains(value) && !textSet.contains(value)) { + CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_select_tip"), customField.getName(), textSet)); + } + } + + @Override + public String parse2Key(String keyOrValuesStr, CustomFieldDao customField) { + Map textMap = optionTextMapCache.get(customField.getId()); + if (MapUtils.isNotEmpty(textMap) && textMap.containsKey(keyOrValuesStr)) { + return textMap.get(keyOrValuesStr); + } + return keyOrValuesStr; + } + + /** + * 获取自定义字段的选项值和key + * 存储到缓存中,增强导入时性能 + * + * @param customField + */ + private void prepareCatch(CustomFieldDao customField) { + if (optionValueSetCache.get(customField.getId()) == null) { + List options = getOptions(customField.getId(), customField.getOptions()); + + translateSystemOption(customField, options); + + optionValueSetCache.put(customField.getId(), getIdSet(options)); + optionTextSetCache.put(customField.getId(), getNameSet(options)); + optionTextMapCache.put(customField.getId(), getTextMap(options)); + } + } + + /** + * 翻译系统字段的选项名称 + * @param customField + * @param options + */ + private void translateSystemOption(CustomFieldDao customField, List options) { + Map fieldI18nMap = i18nMap.get(customField.getName()); + if (fieldI18nMap != null) { + // 不为空,说明需要翻译 + Iterator iterator = options.iterator(); + // 先将系统字段删掉 + while (iterator.hasNext()) { + CustomFieldOption option = iterator.next(); + if (option.getSystem()) { + iterator.remove(); + } + } + // 再填充翻译后的值 + for (String optionValue : fieldI18nMap.keySet()) { + CustomFieldOption option = new CustomFieldOption(optionValue, fieldI18nMap.get(optionValue), true); + options.add(option); + } + } + } + + @NotNull + private Map getTextMap(List options) { + return options.stream() + .collect(Collectors.toMap(CustomFieldOption::getValue, CustomFieldOption::getText)); + } + + @NotNull + protected Set getNameSet(List options) { + return options.stream() + .map(CustomFieldOption::getText) + .collect(Collectors.toSet()); + } + + @NotNull + protected Set getIdSet(List options) { + return options.stream() + .map(CustomFieldOption::getValue) + .collect(Collectors.toSet()); + } + + protected List getOptions(String id, String optionsStr) { + List options = optionCache.get(id); + if (options != null) { + return options; + } + try { + return JSONArray.parseArray(optionsStr, CustomFieldOption.class); + } catch (Exception e) { + LogUtil.error(e); + } + return new ArrayList<>(); + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldTextValidator.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldTextValidator.java new file mode 100644 index 0000000000..731d40f015 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldTextValidator.java @@ -0,0 +1,11 @@ +package io.metersphere.track.validate; + +import io.metersphere.commons.exception.CustomFieldValidateException; +import io.metersphere.dto.CustomFieldDao; + +public class CustomFieldTextValidator extends AbstractCustomFieldValidator { + + public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException { + validateRequired(customField, value); + } +} diff --git a/backend/src/main/java/io/metersphere/track/validate/CustomFieldValidatorFactory.java b/backend/src/main/java/io/metersphere/track/validate/CustomFieldValidatorFactory.java new file mode 100644 index 0000000000..5bd7717e91 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/validate/CustomFieldValidatorFactory.java @@ -0,0 +1,33 @@ +package io.metersphere.track.validate; + +import io.metersphere.commons.constants.CustomFieldType; + +import java.util.HashMap; + +public class CustomFieldValidatorFactory { + + public static HashMap getValidatorMap() { + return new HashMap<>() {{ + put(CustomFieldType.SELECT.getValue(), new CustomFieldSelectValidator()); + put(CustomFieldType.RADIO.getValue(), new CustomFieldSelectValidator()); + + put(CustomFieldType.MULTIPLE_SELECT.getValue(), new CustomFieldMultipleSelectValidator()); + put(CustomFieldType.CHECKBOX.getValue(), new CustomFieldMultipleSelectValidator()); + + put(CustomFieldType.INPUT.getValue(), new CustomFieldTextValidator()); + put(CustomFieldType.RICH_TEXT.getValue(), new CustomFieldTextValidator()); + put(CustomFieldType.TEXTAREA.getValue(), new CustomFieldTextValidator()); + + put(CustomFieldType.MULTIPLE_INPUT.getValue(), new CustomFieldMultipleTextValidator()); + + put(CustomFieldType.DATE.getValue(), new CustomFieldDateValidator()); + put(CustomFieldType.DATETIME.getValue(), new CustomFieldDateTimeValidator()); + + put(CustomFieldType.MEMBER.getValue(), new CustomFieldMemberValidator()); + put(CustomFieldType.MULTIPLE_MEMBER.getValue(), new CustomFieldMultipleMemberValidator()); + + put(CustomFieldType.INT.getValue(), new CustomFieldIntegerValidator()); + put(CustomFieldType.FLOAT.getValue(), new CustomFieldFloatValidator()); + }}; + } +} diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index b178c6f810..69a4a7e1b6 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -323,6 +323,14 @@ test_case_plan_comment=执行 test_case_review_comment=评审 plan_case_status_blocking=阻塞 plan_case_status_skip=跳过 +custom_field_required_tip=[%s]为必填项 +custom_field_array_tip=[%s]必须是数组 +custom_field_datetime_tip=[%s]必须为时间日期格式[%s] +custom_field_date_tip=[%s]必须为日期格式[%s] +custom_field_float_tip=[%s]必须为数字 +custom_field_int_tip=[%s]必须为整型 +custom_field_member_tip=[%s]必须当前项目成员 +custom_field_select_tip=[%s]必须为%s # mock mock_warning=未找到匹配的Mock期望 zentao_test_type_error=无效的 Zentao 请求