From 7454357e580554fb6ae2d2a6e7c8509cd40fd96e Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Tue, 24 Oct 2023 13:37:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E9=A1=B9=E7=9B=AE=E8=AE=BE=E7=BD=AE):=20?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E6=A8=A1=E6=9D=BF=E8=A7=A3=E6=9E=90=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=80=BC=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/constants/CustomFieldType.java | 7 +- .../sdk/dto/TemplateCustomFieldDTO.java | 2 +- .../request/TemplateCustomFieldRequest.java | 2 +- .../main/resources/i18n/commons.properties | 3 +- .../resources/i18n/commons_en_US.properties | 3 +- .../resources/i18n/commons_zh_CN.properties | 3 +- .../resources/i18n/commons_zh_TW.properties | 3 +- .../ProjectTemplateControllerTests.java | 2 +- .../handler/result/CommonResultCode.java | 3 +- .../system/dto/CustomFieldDao.java | 18 ++ .../field/AbstractCustomFieldResolver.java | 77 +++++++++ .../field/CustomFieldDateResolver.java | 22 +++ .../field/CustomFieldDateTimeResolver.java | 22 +++ .../field/CustomFieldFloatResolver.java | 20 +++ .../field/CustomFieldIntegerResolver.java | 18 ++ .../field/CustomFieldMemberResolver.java | 13 ++ .../CustomFieldMultipleMemberResolver.java | 24 +++ .../CustomFieldMultipleSelectResolver.java | 36 ++++ .../CustomFieldMultipleTextResolver.java | 25 +++ .../field/CustomFieldResolverFactory.java | 36 ++++ .../field/CustomFieldSelectResolver.java | 33 ++++ .../field/CustomFieldTextResolver.java | 13 ++ .../BaseTemplateCustomFieldService.java | 29 +++- .../system/service/BaseTemplateService.java | 25 ++- .../base/BaseCustomFieldTestService.java | 158 ++++++++++++++++++ .../OrganizationTemplateControllerTests.java | 19 ++- 26 files changed, 589 insertions(+), 27 deletions(-) create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/dto/CustomFieldDao.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/AbstractCustomFieldResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldDateResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldDateTimeResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldFloatResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldIntegerResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMemberResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleMemberResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleSelectResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleTextResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldResolverFactory.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldSelectResolver.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldTextResolver.java create mode 100644 backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseCustomFieldTestService.java diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/CustomFieldType.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/CustomFieldType.java index d5b7b6fc81..d2d94a1d70 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/CustomFieldType.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/CustomFieldType.java @@ -56,12 +56,7 @@ public enum CustomFieldType { /** * 多值输入框(标签输入框) */ - MULTIPLE_INPUT(false), - /** - * 级联下拉框 - * 第三方平台可能会使用 - */ - CASCADING_SELECT(false); + MULTIPLE_INPUT(false); private final Boolean hasOption; diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/TemplateCustomFieldDTO.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/TemplateCustomFieldDTO.java index e84d9f1e77..f662914293 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/TemplateCustomFieldDTO.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/TemplateCustomFieldDTO.java @@ -19,6 +19,6 @@ public class TemplateCustomFieldDTO { private String apiFieldId; @Schema(title = "默认值") - private String defaultValue; + private Object defaultValue; } diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/request/TemplateCustomFieldRequest.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/request/TemplateCustomFieldRequest.java index d4e7c98ea9..8c74eda1dc 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/request/TemplateCustomFieldRequest.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/request/TemplateCustomFieldRequest.java @@ -22,5 +22,5 @@ public class TemplateCustomFieldRequest { private String apiFieldId; @Schema(title = "默认值") - private String defaultValue; + private Object defaultValue; } diff --git a/backend/framework/sdk/src/main/resources/i18n/commons.properties b/backend/framework/sdk/src/main/resources/i18n/commons.properties index 0c2075ef04..653487e9ac 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons.properties @@ -406,6 +406,7 @@ user_role_relation_remove_admin_user_permission_error=无法将 admin 用户将 internal_custom_field_permission_error=系统字段或模板无法删除! internal_template_permission_error=系统模板无法删除! default_template_permission_error=默认模板无法删除! +field_validate_error={0}字段参数值不合法 #result message http_result_success=操作成功 @@ -444,7 +445,7 @@ plugin_permission_error=没有该插件的访问权限 template_scene_illegal_error=使用场景不合法 # 内置的模板或字段 -custom_field.functional_priority=优先级 +custom_field.functional_priority=用例等级 template.default=默认模板 parent.node.not_blank=父节点不能为空 diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties index c6828a2e52..476d1e287a 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties @@ -409,6 +409,7 @@ user_role_relation_remove_admin_user_permission_error=Unable to delete the admin internal_custom_field_permission_error=System fields cannot be deleted! internal_template_permission_error=System template cannot be deleted! default_template_permission_error=Default templates cannot be deleted! +field_validate_error=The field {0} value is invalid #result message http_result_success=operate success @@ -447,7 +448,7 @@ plugin_permission_error=No access to this plugin scheduled_tasks=Scheduled Tasks template_scene_illegal_error=Scene is illegal # 内置的模板或字段 -custom_field.functional_priority=Priority +custom_field.functional_priority=Case Priority template.default=Default set_default_template=Set the default template diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties index 2c631f562c..33c1ad4a66 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties @@ -406,6 +406,7 @@ user_role_relation_remove_admin_user_permission_error=无法将 admin 用户将 internal_custom_field_permission_error=系统字段或模板无法删除! internal_template_permission_error=系统模板无法删除! default_template_permission_error=默认模板无法删除! +field_validate_error={0}字段参数值不合法 #result message http_result_success=操作成功 @@ -445,7 +446,7 @@ plugin_permission_error=没有该插件的访问权限 scheduled_tasks=定时任务 template_scene_illegal_error=使用场景不合法 # 内置的模板或字段 -custom_field.functional_priority=优先级 +custom_field.functional_priority=用例等级 template.default=默认模板 set_default_template=设置默认模板 diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties index 4df26d9859..b205b14d0b 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties @@ -405,6 +405,7 @@ user_role_relation_remove_admin_user_permission_error=無法將 admin 用戶將 internal_custom_field_permission_error=系統字段或模板無法刪除! internal_template_permission_error=系統模板無法刪除! default_template_permission_error=默认模板无法删除! +field_validate_error={0}字段參數值不合法 #result message http_result_success=操作成功 @@ -444,7 +445,7 @@ scheduled_tasks=定時任務 template_scene_illegal_error=使用場景不合法 # 内置的模板或字段 -custom_field.functional_priority=優先級 +custom_field.functional_priority=用例等級 template.default=默認模板 set_default_template=設置默認模板 diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectTemplateControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectTemplateControllerTests.java index b6149b47a9..f63b725780 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectTemplateControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectTemplateControllerTests.java @@ -350,7 +350,7 @@ public class ProjectTemplateControllerTests extends BaseTest { Assertions.assertEquals(customFieldDTO.getApiFieldId(), templateCustomField.getApiFieldId()); Assertions.assertEquals(customFieldDTO.getRequired(), templateCustomField.getRequired()); Assertions.assertEquals(templateCustomField.getTemplateId(), template.getId()); - Assertions.assertEquals(customFieldDTO.getFieldName(), "优先级"); + Assertions.assertEquals(customFieldDTO.getFieldName(), "用例等级"); } // @@校验权限 diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/controller/handler/result/CommonResultCode.java b/backend/services/system-setting/src/main/java/io/metersphere/system/controller/handler/result/CommonResultCode.java index 1b6442d06f..0dccf7297e 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/controller/handler/result/CommonResultCode.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/controller/handler/result/CommonResultCode.java @@ -26,7 +26,8 @@ public enum CommonResultCode implements IResultCode { TEMPLATE_EXIST(100013, "template.exist"), DEFAULT_TEMPLATE_PERMISSION(100014, "default_template_permission_error"), STATUS_ITEM_NOT_EXIST(100015, "status_item.not.exist"), - STATUS_ITEM_EXIST(100016, "status_item.exist"); + STATUS_ITEM_EXIST(100016, "status_item.exist"), + FIELD_VALIDATE_ERROR(100017, "field_validate_error"); private int code; diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/CustomFieldDao.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/CustomFieldDao.java new file mode 100644 index 0000000000..1a2652646c --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/CustomFieldDao.java @@ -0,0 +1,18 @@ +package io.metersphere.system.dto; + +import io.metersphere.system.domain.CustomField; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class CustomFieldDao extends CustomField implements Serializable { + + private static final long serialVersionUID = 1L; + + private Boolean required; + + private String defaultValue; + + private Object value; +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/AbstractCustomFieldResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/AbstractCustomFieldResolver.java new file mode 100644 index 0000000000..6080411bbf --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/AbstractCustomFieldResolver.java @@ -0,0 +1,77 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.JSON; +import io.metersphere.sdk.util.Translator; +import io.metersphere.system.dto.CustomFieldDao; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +import static io.metersphere.system.controller.handler.result.CommonResultCode.FIELD_VALIDATE_ERROR; + +public abstract class AbstractCustomFieldResolver { + + /** + * 校验参数是否合法 + * + * @param customField + * @param value + */ + abstract public void validate(CustomFieldDao customField, Object value); + + protected void throwValidateException(String name) { + throw new MSException(FIELD_VALIDATE_ERROR, Translator.get(FIELD_VALIDATE_ERROR.getMessage(), name)); + } + + protected void validateRequired(CustomFieldDao customField, Object value) { + if (!customField.getRequired()) { + return; + } + if (value == null) { + throwValidateException(customField.getName()); + } else if (value instanceof String && StringUtils.isBlank(value.toString())) { + throwValidateException(customField.getName()); + } + } + + protected void validateArrayRequired(CustomFieldDao customField, Object value) { + if (!customField.getRequired()) { + return; + } + if (value == null || (value instanceof List && CollectionUtils.isEmpty((List) value))) { + throwValidateException(customField.getName()); + } + } + + protected void validateArray(String name, Object value) { + if (value == null) { + return; + } + if (value instanceof List) { + ((List) value).forEach(v -> validateString(name, v)); + } else { + throwValidateException(name); + } + } + + protected void validateString(String name, Object v) { + if (!(v instanceof String)) { + throwValidateException(name); + } + } + + public Object parse2Value(String value) { + return value; + } + + public String parse2String(Object value) { + return value == null ? null : value.toString(); + } + + protected Object parse2Array(String value) { + return value == null ? null : JSON.parseArray(value); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldDateResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldDateResolver.java new file mode 100644 index 0000000000..7d86454c95 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldDateResolver.java @@ -0,0 +1,22 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.sdk.util.DateUtils; +import io.metersphere.system.dto.CustomFieldDao; +import org.apache.commons.lang3.StringUtils; + +public class CustomFieldDateResolver extends AbstractCustomFieldResolver { + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateRequired(customField, value); + validateString(customField.getName(), value); + try { + if (value != null && StringUtils.isNotBlank(value.toString())) { + DateUtils.getDate(value.toString()); + } + } catch (Exception e) { + throwValidateException(customField.getName()); + } + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldDateTimeResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldDateTimeResolver.java new file mode 100644 index 0000000000..44d52a5288 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldDateTimeResolver.java @@ -0,0 +1,22 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.sdk.util.DateUtils; +import io.metersphere.system.dto.CustomFieldDao; +import org.apache.commons.lang3.StringUtils; + +public class CustomFieldDateTimeResolver extends AbstractCustomFieldResolver { + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateRequired(customField, value); + try { + if (value != null && StringUtils.isNotBlank(value.toString())) { + DateUtils.getTime(value.toString()); + } + } catch (Exception e) { + throwValidateException(customField.getName()); + } + } + +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldFloatResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldFloatResolver.java new file mode 100644 index 0000000000..6bf5b1acde --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldFloatResolver.java @@ -0,0 +1,20 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.system.dto.CustomFieldDao; + +public class CustomFieldFloatResolver extends AbstractCustomFieldResolver { + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateRequired(customField, value); + if (value != null && !(value instanceof Number)) { + throwValidateException(customField.getName()); + } + } + + @Override + public Object parse2Value(String value) { + return value == null ? null : Float.parseFloat(value); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldIntegerResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldIntegerResolver.java new file mode 100644 index 0000000000..e59b7bfc07 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldIntegerResolver.java @@ -0,0 +1,18 @@ +package io.metersphere.system.resolver.field; + +import io.metersphere.system.dto.CustomFieldDao; + +public class CustomFieldIntegerResolver extends AbstractCustomFieldResolver { + @Override + public void validate(CustomFieldDao customField, Object value) { + validateRequired(customField, value); + if (value != null && !(value instanceof Integer)) { + throwValidateException(customField.getName()); + } + } + + @Override + public Object parse2Value(String value) { + return value == null ? null : Integer.parseInt(value); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMemberResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMemberResolver.java new file mode 100644 index 0000000000..e2792baa05 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMemberResolver.java @@ -0,0 +1,13 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.system.dto.CustomFieldDao; + +public class CustomFieldMemberResolver extends AbstractCustomFieldResolver { + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateRequired(customField, value); + validateString(customField.getName(), value); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleMemberResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleMemberResolver.java new file mode 100644 index 0000000000..9cc2d5eee8 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleMemberResolver.java @@ -0,0 +1,24 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.dto.CustomFieldDao; + +public class CustomFieldMultipleMemberResolver extends CustomFieldMemberResolver { + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateArrayRequired(customField, value); + validateArray(customField.getName(), value); + } + + @Override + public String parse2String(Object value) { + return JSON.toJSONString(value); + } + + @Override + public Object parse2Value(String value) { + return parse2Array(value); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleSelectResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleSelectResolver.java new file mode 100644 index 0000000000..31571b26af --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleSelectResolver.java @@ -0,0 +1,36 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.domain.CustomFieldOption; +import io.metersphere.system.dto.CustomFieldDao; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class CustomFieldMultipleSelectResolver extends CustomFieldSelectResolver { + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateArrayRequired(customField, value); + validateArray(customField.getName(), value); + List options = getOptions(customField.getId()); + Set values = options.stream().map(CustomFieldOption::getValue).collect(Collectors.toSet()); + for (String item : (List)value) { + if (!values.contains(item)) { + throwValidateException(customField.getName()); + } + } + } + + @Override + public String parse2String(Object value) { + return JSON.toJSONString(value); + } + + @Override + public Object parse2Value(String value) { + return parse2Array(value); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleTextResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleTextResolver.java new file mode 100644 index 0000000000..202cd8bd8a --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldMultipleTextResolver.java @@ -0,0 +1,25 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.dto.CustomFieldDao; + +public class CustomFieldMultipleTextResolver extends AbstractCustomFieldResolver { + + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateArrayRequired(customField, value); + validateArray(customField.getName(), value); + } + + @Override + public String parse2String(Object value) { + return JSON.toJSONString(value); + } + + @Override + public Object parse2Value(String value) { + return parse2Array(value); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldResolverFactory.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldResolverFactory.java new file mode 100644 index 0000000000..4c3753e1c1 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldResolverFactory.java @@ -0,0 +1,36 @@ +package io.metersphere.system.resolver.field; + +import io.metersphere.sdk.constants.CustomFieldType; + +import java.util.HashMap; + +public class CustomFieldResolverFactory { + + private static final HashMap resolverMap = new HashMap<>(); + + static { + resolverMap.put(CustomFieldType.SELECT.name(), new CustomFieldSelectResolver()); + resolverMap.put(CustomFieldType.RADIO.name(), new CustomFieldSelectResolver()); + + resolverMap.put(CustomFieldType.MULTIPLE_SELECT.name(), new CustomFieldMultipleSelectResolver()); + resolverMap.put(CustomFieldType.CHECKBOX.name(), new CustomFieldMultipleSelectResolver()); + + resolverMap.put(CustomFieldType.INPUT.name(), new CustomFieldTextResolver()); + resolverMap.put(CustomFieldType.TEXTAREA.name(), new CustomFieldTextResolver()); + + resolverMap.put(CustomFieldType.MULTIPLE_INPUT.name(), new CustomFieldMultipleTextResolver()); + + resolverMap.put(CustomFieldType.DATE.name(), new CustomFieldDateResolver()); + resolverMap.put(CustomFieldType.DATETIME.name(), new CustomFieldDateTimeResolver()); + + resolverMap.put(CustomFieldType.MEMBER.name(), new CustomFieldMemberResolver()); + resolverMap.put(CustomFieldType.MULTIPLE_MEMBER.name(), new CustomFieldMultipleMemberResolver()); + + resolverMap.put(CustomFieldType.INT.name(), new CustomFieldIntegerResolver()); + resolverMap.put(CustomFieldType.FLOAT.name(), new CustomFieldFloatResolver()); + } + + public static AbstractCustomFieldResolver getResolver(String type) { + return resolverMap.get(type); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldSelectResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldSelectResolver.java new file mode 100644 index 0000000000..bf1da73afa --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldSelectResolver.java @@ -0,0 +1,33 @@ +package io.metersphere.system.resolver.field; + + +import io.metersphere.sdk.util.CommonBeanFactory; +import io.metersphere.system.domain.CustomFieldOption; +import io.metersphere.system.dto.CustomFieldDao; +import io.metersphere.system.service.BaseCustomFieldOptionService; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class CustomFieldSelectResolver extends AbstractCustomFieldResolver { + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateRequired(customField, value); + if (value == null) { + return; + } + validateString(customField.getName(), value); + List options = getOptions(customField.getId()); + Set values = options.stream().map(CustomFieldOption::getValue).collect(Collectors.toSet()); + if (!values.contains(value)) { + throwValidateException(customField.getName()); + } + } + + protected List getOptions(String id) { + BaseCustomFieldOptionService customFieldOptionService = CommonBeanFactory.getBean(BaseCustomFieldOptionService.class); + return customFieldOptionService.getByFieldId(id); + } +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldTextResolver.java b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldTextResolver.java new file mode 100644 index 0000000000..a45a155843 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/resolver/field/CustomFieldTextResolver.java @@ -0,0 +1,13 @@ +package io.metersphere.system.resolver.field; + +import io.metersphere.system.dto.CustomFieldDao; + +public class CustomFieldTextResolver extends AbstractCustomFieldResolver { + + @Override + public void validate(CustomFieldDao customField, Object value) { + validateRequired(customField, value); + validateString(customField.getName(), value); + } + +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateCustomFieldService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateCustomFieldService.java index 12d0431b75..65a01cb324 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateCustomFieldService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateCustomFieldService.java @@ -5,7 +5,10 @@ import io.metersphere.sdk.util.BeanUtils; import io.metersphere.system.domain.CustomField; import io.metersphere.system.domain.TemplateCustomField; import io.metersphere.system.domain.TemplateCustomFieldExample; +import io.metersphere.system.dto.CustomFieldDao; import io.metersphere.system.mapper.TemplateCustomFieldMapper; +import io.metersphere.system.resolver.field.AbstractCustomFieldResolver; +import io.metersphere.system.resolver.field.CustomFieldResolverFactory; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Service; @@ -49,6 +52,17 @@ public class BaseTemplateCustomFieldService { if (CollectionUtils.isEmpty(customFieldRequests)) { return; } + + // 过滤下不存在的字段 + List ids = customFieldRequests.stream().map(TemplateCustomFieldRequest::getFieldId).toList(); + Set fieldIdSet = baseCustomFieldService.getByIds(ids) + .stream() + .map(CustomField::getId) + .collect(Collectors.toSet()); + customFieldRequests = customFieldRequests.stream() + .filter(item -> fieldIdSet.contains(item.getFieldId())) + .toList(); + AtomicReference pos = new AtomicReference<>(0); List templateCustomFields = customFieldRequests.stream().map(field -> { TemplateCustomField templateCustomField = new TemplateCustomField(); @@ -56,19 +70,24 @@ public class BaseTemplateCustomFieldService { BeanUtils.copyBean(templateCustomField, field); templateCustomField.setTemplateId(id); templateCustomField.setPos(pos.getAndSet(pos.get() + 1)); + templateCustomField.setDefaultValue(parseDefaultValue(field)); return templateCustomField; }).toList(); - // 过滤下不存在的字段 - List ids = templateCustomFields.stream().map(TemplateCustomField::getFieldId).toList(); - Set fieldIdSet = baseCustomFieldService.getByIds(ids).stream().map(CustomField::getId).collect(Collectors.toSet()); - templateCustomFields = templateCustomFields.stream().filter(item -> fieldIdSet.contains(item.getFieldId())).toList(); - if (templateCustomFields.size() > 0) { templateCustomFieldMapper.batchInsert(templateCustomFields); } } + private String parseDefaultValue(TemplateCustomFieldRequest field) { + CustomField customField = baseCustomFieldService.getWithCheck(field.getFieldId()); + AbstractCustomFieldResolver customFieldResolver = CustomFieldResolverFactory.getResolver(customField.getType()); + CustomFieldDao customFieldDao = BeanUtils.copyBean(new CustomFieldDao(), customField); + customFieldDao.setRequired(false); + customFieldResolver.validate(customFieldDao, field.getDefaultValue()); + return customFieldResolver.parse2String(field.getDefaultValue()); + } + public List getByTemplateId(String id) { TemplateCustomFieldExample example = new TemplateCustomFieldExample(); example.createCriteria().andTemplateIdEqualTo(id); diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateService.java index d5756eebb7..294bf15fa5 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateService.java @@ -7,6 +7,9 @@ import io.metersphere.sdk.dto.TemplateDTO; import io.metersphere.sdk.dto.request.TemplateCustomFieldRequest; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.resolver.field.AbstractCustomFieldResolver; +import io.metersphere.system.resolver.field.CustomFieldResolverFactory; import io.metersphere.system.utils.ServiceUtils; import io.metersphere.sdk.util.Translator; import io.metersphere.system.domain.CustomField; @@ -96,20 +99,30 @@ public class BaseTemplateService { // 查找字段名称 List fieldIds = templateCustomFields.stream().map(TemplateCustomField::getFieldId).toList(); - Map fieldNameMap = baseCustomFieldService.getByIds(fieldIds) + List customFields = baseCustomFieldService.getByIds(fieldIds); + Map fieldMap = customFields .stream() - .collect(Collectors.toMap(CustomField::getId, i -> { - if (i.getInternal()) { - return baseCustomFieldService.translateInternalField(i.getName()); + .collect(Collectors.toMap(CustomField::getId, customField -> { + if (customField.getInternal()) { + customField.setName(baseCustomFieldService.translateInternalField(customField.getName())); } - return i.getName(); + return customField; })); // 封装字段信息 List fieldDTOS = templateCustomFields.stream().map(i -> { + CustomField customField = fieldMap.get(i.getFieldId()); TemplateCustomFieldDTO templateCustomFieldDTO = new TemplateCustomFieldDTO(); BeanUtils.copyBean(templateCustomFieldDTO, i); - templateCustomFieldDTO.setFieldName(fieldNameMap.get(i.getFieldId())); + templateCustomFieldDTO.setFieldName(customField.getName()); + AbstractCustomFieldResolver customFieldResolver = CustomFieldResolverFactory.getResolver(customField.getType()); + Object defaultValue = null; + try { + defaultValue = customFieldResolver.parse2Value(i.getDefaultValue()); + } catch (Exception e) { + LogUtils.error(e); + } + templateCustomFieldDTO.setDefaultValue(defaultValue); return templateCustomFieldDTO; }).toList(); diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseCustomFieldTestService.java b/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseCustomFieldTestService.java new file mode 100644 index 0000000000..2be00538a7 --- /dev/null +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseCustomFieldTestService.java @@ -0,0 +1,158 @@ +package io.metersphere.system.base; + +import io.metersphere.sdk.constants.CustomFieldType; +import io.metersphere.sdk.constants.InternalUser; +import io.metersphere.sdk.constants.TemplateScene; +import io.metersphere.sdk.dto.request.CustomFieldOptionRequest; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.system.domain.CustomField; +import io.metersphere.system.dto.CustomFieldDao; +import io.metersphere.system.resolver.field.AbstractCustomFieldResolver; +import io.metersphere.system.resolver.field.CustomFieldResolverFactory; +import io.metersphere.system.service.OrganizationCustomFieldService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static io.metersphere.system.controller.handler.result.CommonResultCode.FIELD_VALIDATE_ERROR; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-20 11:32 + */ +@Service +public class BaseCustomFieldTestService { + + + @Resource + private OrganizationCustomFieldService organizationCustomFieldService; + + private static Map customFields; + + public static final String OPTION_VALUE = "OPTION_VALUE"; + + + public Map addOrgTestCustomFields() { + if (customFields != null) { + return customFields; + } + customFields = new HashMap<>(); + for (CustomFieldType customFieldType : CustomFieldType.values()) { + CustomFieldOptionRequest customFieldOptionRequest = new CustomFieldOptionRequest(); + customFieldOptionRequest.setValue(OPTION_VALUE); + customFieldOptionRequest.setText("test"); + List optionRequests = Arrays.asList(customFieldOptionRequest); + CustomField customField = new CustomField(); + customField.setType(customFieldType.name()); + customField.setName(customFieldType.name()); + customField.setCreateUser(InternalUser.ADMIN.getValue()); + customField.setScene(TemplateScene.FUNCTIONAL.name()); + customField.setScopeId(BaseTest.DEFAULT_ORGANIZATION_ID); + customField = organizationCustomFieldService.add(customField, optionRequests); + customFields.put(customFieldType, customField); + } + return customFields; + } + + public Map getCustomFields() { + return customFields; + } + + public void testResolverEmptyValidate() { + Map emptyValueMap = new HashMap<>(); + Arrays.stream(CustomFieldType.values()).forEach(i -> emptyValueMap.put(i, StringUtils.EMPTY)); + emptyValueMap.put(CustomFieldType.MULTIPLE_SELECT, List.of()); + emptyValueMap.put(CustomFieldType.MULTIPLE_INPUT, List.of()); + emptyValueMap.put(CustomFieldType.CHECKBOX, List.of()); + emptyValueMap.put(CustomFieldType.MULTIPLE_MEMBER, List.of()); + emptyValueMap.put(CustomFieldType.INT, null); + emptyValueMap.put(CustomFieldType.FLOAT, null); + assertValidateError(emptyValueMap); + } + + public void testResolverErrorValidate() { + Map errorValueMap = new HashMap<>(); + Arrays.stream(CustomFieldType.values()).forEach(i -> errorValueMap.put(i, 1)); + errorValueMap.put(CustomFieldType.MULTIPLE_SELECT, StringUtils.EMPTY); + errorValueMap.put(CustomFieldType.MULTIPLE_INPUT, StringUtils.EMPTY); + errorValueMap.put(CustomFieldType.CHECKBOX, StringUtils.EMPTY); + errorValueMap.put(CustomFieldType.MULTIPLE_MEMBER, StringUtils.EMPTY); + errorValueMap.put(CustomFieldType.INT, StringUtils.EMPTY); + errorValueMap.put(CustomFieldType.FLOAT, StringUtils.EMPTY); + assertValidateError(errorValueMap); + + errorValueMap.clear(); + Arrays.stream(CustomFieldType.values()).forEach(i -> errorValueMap.put(i, 1)); + errorValueMap.put(CustomFieldType.DATE, "eeee"); + errorValueMap.put(CustomFieldType.DATETIME, "eeee"); + errorValueMap.put(CustomFieldType.DATETIME, "eeee"); + errorValueMap.put(CustomFieldType.MULTIPLE_SELECT, List.of(1)); + errorValueMap.put(CustomFieldType.MULTIPLE_INPUT, List.of(1)); + errorValueMap.put(CustomFieldType.CHECKBOX, List.of(1)); + errorValueMap.put(CustomFieldType.MULTIPLE_MEMBER, List.of(1)); + errorValueMap.put(CustomFieldType.INT, "dd"); + errorValueMap.put(CustomFieldType.FLOAT, "dd"); + assertValidateError(errorValueMap); + } + + public void testResolverParse() { + Map objectValueMap = new HashMap<>(); + Arrays.stream(CustomFieldType.values()).forEach(i -> objectValueMap.put(i, "1")); + objectValueMap.put(CustomFieldType.SELECT, OPTION_VALUE); + objectValueMap.put(CustomFieldType.RADIO, OPTION_VALUE); + objectValueMap.put(CustomFieldType.DATE, "2021-10-10"); + objectValueMap.put(CustomFieldType.DATETIME, "2021-10-10 10:10:10"); + objectValueMap.put(CustomFieldType.RADIO, OPTION_VALUE); + objectValueMap.put(CustomFieldType.MULTIPLE_SELECT, List.of(OPTION_VALUE)); + objectValueMap.put(CustomFieldType.CHECKBOX, List.of(OPTION_VALUE)); + objectValueMap.put(CustomFieldType.MULTIPLE_INPUT, List.of(OPTION_VALUE)); + objectValueMap.put(CustomFieldType.MULTIPLE_MEMBER, List.of(OPTION_VALUE)); + objectValueMap.put(CustomFieldType.INT, 1); + objectValueMap.put(CustomFieldType.FLOAT, 1.2f); + + for (CustomFieldType customFieldType : CustomFieldType.values()) { + // 校验 validate 调用成功 + invokeValidate(objectValueMap, customFieldType); + + AbstractCustomFieldResolver customFieldResolver = CustomFieldResolverFactory.getResolver(customFieldType.name()); + CustomField customField = getCustomFields().get(customFieldType); + CustomFieldDao customFieldDao = BeanUtils.copyBean(new CustomFieldDao(), customField); + customFieldDao.setRequired(true); + String valueStr = customFieldResolver.parse2String(objectValueMap.get(customFieldType)); + Object objectValue = customFieldResolver.parse2Value(valueStr); + // 校验 parse2String 和 parse2Value 是否正确 + if (objectValue instanceof List) { + Assertions.assertEquals(objectValue.toString(), objectValueMap.get(customFieldType).toString()); + } else { + Assertions.assertEquals(objectValue, objectValueMap.get(customFieldType)); + } + } + } + + private void assertValidateError(Map errorValueMap) { + for (CustomFieldType customFieldType : CustomFieldType.values()) { + try { + invokeValidate(errorValueMap, customFieldType); + } catch (MSException e) { + Assertions.assertEquals(e.getErrorCode(), FIELD_VALIDATE_ERROR); + continue; + } + throw new MSException("自定义字段校验失败"); + } + } + + private void invokeValidate(Map valueMap, CustomFieldType customFieldType) { + AbstractCustomFieldResolver customFieldResolver = CustomFieldResolverFactory.getResolver(customFieldType.name()); + CustomField customField = getCustomFields().get(customFieldType); + CustomFieldDao customFieldDao = BeanUtils.copyBean(new CustomFieldDao(), customField); + customFieldDao.setRequired(true); + customFieldResolver.validate(customFieldDao, valueMap.get(customFieldType)); + } +} diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/OrganizationTemplateControllerTests.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/OrganizationTemplateControllerTests.java index e1ceb60497..358e42db70 100644 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/OrganizationTemplateControllerTests.java +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/OrganizationTemplateControllerTests.java @@ -9,6 +9,7 @@ import io.metersphere.sdk.dto.TemplateDTO; import io.metersphere.sdk.dto.request.TemplateCustomFieldRequest; import io.metersphere.sdk.dto.request.TemplateUpdateRequest; import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.system.base.BaseCustomFieldTestService; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.param.TemplateUpdateRequestDefinition; import io.metersphere.system.domain.*; @@ -37,7 +38,7 @@ import static io.metersphere.system.controller.result.SystemResultCode.ORGANIZAT * @author jianxing * @date : 2023-8-30 */ -@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class OrganizationTemplateControllerTests extends BaseTest { @@ -62,6 +63,8 @@ public class OrganizationTemplateControllerTests extends BaseTest { private BaseOrganizationParameterService organizationParameterService; @Resource private OrganizationTemplateService organizationTemplateService; + @Resource + private BaseCustomFieldTestService baseCustomFieldTestService; private static Template addTemplate; private static Template anotherTemplateField; @@ -320,7 +323,7 @@ public class OrganizationTemplateControllerTests extends BaseTest { Assertions.assertEquals(customFieldDTO.getApiFieldId(), templateCustomField.getApiFieldId()); Assertions.assertEquals(customFieldDTO.getRequired(), templateCustomField.getRequired()); Assertions.assertEquals(templateCustomField.getTemplateId(), template.getId()); - Assertions.assertEquals(customFieldDTO.getFieldName(), "优先级"); + Assertions.assertEquals(customFieldDTO.getFieldName(), "用例等级"); } // @@校验权限 @@ -395,4 +398,16 @@ public class OrganizationTemplateControllerTests extends BaseTest { // @@校验权限 requestGetPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_READ, IS_ORGANIZATION_TEMPLATE_ENABLE, DEFAULT_ORGANIZATION_ID, TemplateScene.FUNCTIONAL.name()); } + + /** + * 测试自定义字段解析器 + */ + @Test + @Order(9) + public void testCustomFiledResolver() { + baseCustomFieldTestService.addOrgTestCustomFields(); + baseCustomFieldTestService.testResolverEmptyValidate(); + baseCustomFieldTestService.testResolverErrorValidate(); + baseCustomFieldTestService.testResolverParse(); + } } \ No newline at end of file