From 58d8054d67d92ba956fa89436f4f48ef6a26708b Mon Sep 17 00:00:00 2001 From: CaptainB Date: Fri, 12 May 2023 17:45:36 +0800 Subject: [PATCH] =?UTF-8?q?build:=20=E5=8F=82=E6=95=B0=E6=A0=A1=E9=AA=8Cde?= =?UTF-8?q?mo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/metersphere/system/domain/User.java | 2 +- .../io/metersphere/sdk/config/I18nConfig.java | 41 +++++ .../RestControllerExceptionHandler.java | 29 ++++ .../sdk/controller/handler/ResultHolder.java | 73 +++++++++ .../handler/ResultResponseBodyAdvice.java | 47 ++++++ .../handler/annotation/NoResultHolder.java | 10 ++ .../java/io/metersphere/sdk/util/JSON.java | 99 ++++++++++++ .../io/metersphere/sdk/util/Translator.java | 21 +++ .../resources/i18n/system_en_US.properties | 153 ++++++++++++++++++ .../resources/i18n/system_zh_CN.properties | 143 ++++++++++++++++ .../resources/i18n/system_zh_TW.properties | 141 ++++++++++++++++ .../system/controller/UserController.java | 14 +- .../controller/UserControllerTests.java | 14 ++ .../src/test/resources/application.properties | 2 +- 14 files changed, 782 insertions(+), 7 deletions(-) create mode 100644 backend/framework/sdk/src/main/java/io/metersphere/sdk/config/I18nConfig.java create mode 100644 backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/RestControllerExceptionHandler.java create mode 100644 backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/ResultHolder.java create mode 100644 backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/ResultResponseBodyAdvice.java create mode 100644 backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/annotation/NoResultHolder.java create mode 100644 backend/framework/sdk/src/main/java/io/metersphere/sdk/util/JSON.java create mode 100644 backend/framework/sdk/src/main/java/io/metersphere/sdk/util/Translator.java diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/User.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/User.java index 76204f9140..2a71b45b4c 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/User.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/User.java @@ -19,7 +19,7 @@ public class User implements Serializable { private static final long serialVersionUID = 1L; @TableId - @NotBlank(message = "{user.id.not_blank}", groups = {Updated.class}) + @NotBlank(message = "{user.id.not_blank}", groups = {Created.class, Updated.class}) @ApiModelProperty(name = "用户ID", required = true, allowableValues = "range[1, 50]") private String id; diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/config/I18nConfig.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/config/I18nConfig.java new file mode 100644 index 0000000000..37b9251b9b --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/config/I18nConfig.java @@ -0,0 +1,41 @@ +package io.metersphere.sdk.config; + +import io.metersphere.sdk.util.Translator; +import jakarta.validation.Validator; +import org.hibernate.validator.HibernateValidator; +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; + +@Configuration +public class I18nConfig { + + @Bean + @ConditionalOnMissingBean + public Translator translator() { + return new Translator(); + } + + + /** + * JSR-303校验国际化 + * + * @param messageSource + * @return + */ + @Bean + public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) { + LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); + localValidatorFactoryBean.setProviderClass(HibernateValidator.class); + localValidatorFactoryBean.setValidationMessageSource(messageSource); + return localValidatorFactoryBean; + } + + @Bean + public Validator validator(LocalValidatorFactoryBean localValidatorFactoryBean) { + return localValidatorFactoryBean.getValidator(); + } + +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/RestControllerExceptionHandler.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/RestControllerExceptionHandler.java new file mode 100644 index 0000000000..f5a849e50b --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/RestControllerExceptionHandler.java @@ -0,0 +1,29 @@ +package io.metersphere.sdk.controller.handler; + + +import org.springframework.http.HttpStatus; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.HashMap; +import java.util.Map; + + +@RestControllerAdvice +public class RestControllerExceptionHandler { + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(MethodArgumentNotValidException.class) + public Map handleValidationExceptions(MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + ex.getBindingResult().getAllErrors().forEach((error) -> { + String fieldName = ((FieldError) error).getField(); + String errorMessage = error.getDefaultMessage(); + errors.put(fieldName, errorMessage); + }); + return errors; + } +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/ResultHolder.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/ResultHolder.java new file mode 100644 index 0000000000..a282ba4ff2 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/ResultHolder.java @@ -0,0 +1,73 @@ +package io.metersphere.sdk.controller.handler; + +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class ResultHolder { + public ResultHolder() { + this.code = 0; + } + + public ResultHolder(Object data) { + this.data = data; + this.code = 0; + } + + public ResultHolder(int code, String msg) { + this.code = code; + this.message = msg; + } + + public ResultHolder(int code, String msg, Object data) { + this.code = code; + this.message = msg; + this.data = data; + } + + // 请求是否成功 + private int code = 0; + // 描述信息 + private String message; + // 返回数据 + private Object data = ""; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + + public static ResultHolder success(Object obj) { + return new ResultHolder(obj); + } + + public static ResultHolder error(int code, String message) { + return new ResultHolder(code, message, null); + } + + public static ResultHolder error(int code, String message, Object object) { + return new ResultHolder(code, message, object); + } + + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/ResultResponseBodyAdvice.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/ResultResponseBodyAdvice.java new file mode 100644 index 0000000000..60a825f1a9 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/ResultResponseBodyAdvice.java @@ -0,0 +1,47 @@ +package io.metersphere.sdk.controller.handler; + +import io.metersphere.sdk.controller.handler.annotation.NoResultHolder; +import io.metersphere.sdk.util.JSON; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +/** + * 统一处理返回结果集 + */ +@RestControllerAdvice(value = {"io.metersphere"}) +public class ResultResponseBodyAdvice implements ResponseBodyAdvice { + + + @Override + public boolean supports(MethodParameter methodParameter, Class> converterType) { + return MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType) || StringHttpMessageConverter.class.isAssignableFrom(converterType); + } + + @Override + public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class> converterType, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { + // 处理空值 + if (o == null && StringHttpMessageConverter.class.isAssignableFrom(converterType)) { + return null; + } + + if (methodParameter.hasMethodAnnotation(NoResultHolder.class)) { + return o; + } + + if (!(o instanceof ResultHolder)) { + if (o instanceof String) { + return JSON.toJSONString(ResultHolder.success(o)); + } + return ResultHolder.success(o); + } + return o; + } + +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/annotation/NoResultHolder.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/annotation/NoResultHolder.java new file mode 100644 index 0000000000..41db712e98 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/annotation/NoResultHolder.java @@ -0,0 +1,10 @@ +package io.metersphere.sdk.controller.handler.annotation; + +import java.lang.annotation.*; + +@Documented +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface NoResultHolder { +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/JSON.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/JSON.java new file mode 100644 index 0000000000..fcfd44f1f4 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/JSON.java @@ -0,0 +1,99 @@ +package io.metersphere.sdk.util; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +public class JSON { + private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final TypeFactory typeFactory = objectMapper.getTypeFactory(); + + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + // 自动检测所有类的全部属性 + objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + // 如果一个对象中没有任何的属性,那么在序列化的时候就会报错 + objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + } + + public static String toJSONString(Object value) { + try { + return objectMapper.writeValueAsString(value); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Object parseObject(String content) { + return parseObject(content, Object.class); + } + + public static T parseObject(String content, Class valueType) { + try { + return objectMapper.readValue(content, valueType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String content, TypeReference valueType) { + try { + return objectMapper.readValue(content, valueType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(InputStream src, Class valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List parseArray(String content) { + return parseArray(content, Object.class); + } + + public static List parseArray(String content, Class valueType) { + CollectionType javaType = typeFactory.constructCollectionType(List.class, valueType); + try { + return objectMapper.readValue(content, javaType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List parseArray(String content, TypeReference valueType) { + try { + JavaType subType = typeFactory.constructType(valueType); + CollectionType javaType = typeFactory.constructCollectionType(List.class, subType); + return objectMapper.readValue(content, javaType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Map parseMap(String jsonObject) { + try { + return objectMapper.readValue(jsonObject, new TypeReference<>() { + }); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/Translator.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/Translator.java new file mode 100644 index 0000000000..7978cbf719 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/Translator.java @@ -0,0 +1,21 @@ +package io.metersphere.sdk.util; + +import jakarta.annotation.Resource; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +public class Translator { + private static MessageSource messageSource; + + @Resource + public void setMessageSource(MessageSource messageSource) { + Translator.messageSource = messageSource; + } + + /** + * 单Key翻译 + */ + public static String get(String key) { + return messageSource.getMessage(key, null, "Not Support Key: " + key, LocaleContextHolder.getLocale()); + } +} diff --git a/backend/framework/sdk/src/main/resources/i18n/system_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/system_en_US.properties index e69de29bb2..5b3ce4fa7f 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system_en_US.properties @@ -0,0 +1,153 @@ +auth_source.id.not_blank=Auth source id must not be blank +auth_source.status.length_range=Auth source status must be between {min} and {max} characters long +auth_source.status.not_blank=Auth source status must not be blank +license.id.not_blank=License id must not be blank +message_task.id.not_blank=Message task id must not be blank +message_task.type.not_blank=Message task type must not be blank +message_task.type.length_range=Message task type must be between {min} and {max} characters long +message_task.event.not_blank=Message task event must not be blank +message_task.event.length_range=Message task event must be between {min} and {max} characters long +message_task.receiver.not_blank=Message task receiver must not be blank +message_task.receiver.length_range=Message task receiver must be between {min} and {max} characters long +message_task.task_type.not_blank=Message task task type must not be blank +message_task.task_type.length_range=Message task task type must be between {min} and {max} characters long +message_task.test_id.not_blank=Message task test id must not be blank +message_task.test_id.length_range=Message task test id must be between {min} and {max} characters long +message_task.project_id.not_blank=Message task project id must not be blank +message_task.project_id.length_range=Message task project id must be between {min} and {max} characters long +message_task_blob.message_task_id.not_blank=Message task blob message task id must not be blank +notification.id.not_blank=Notification id must not be blank +notification.type.not_blank=Notification type must not be blank +notification.type.length_range=Notification type must be between {min} and {max} characters long +notification.receiver.not_blank=Notification receiver must not be blank +notification.receiver.length_range=Notification receiver must be between {min} and {max} characters long +notification.title.not_blank=Notification title must not be blank +notification.title.length_range=Notification title must be between {min} and {max} characters long +notification.status.not_blank=Notification status must not be blank +notification.status.length_range=Notification status must be between {min} and {max} characters long +notification.operator.not_blank=Notification operator must not be blank +notification.operator.length_range=Notification operator must be between {min} and {max} characters long +notification.operation.not_blank=Notification operation must not be blank +notification.operation.length_range=Notification operation must be between {min} and {max} characters long +notification.resource_id.not_blank=Notification resource id must not be blank +notification.resource_id.length_range=Notification resource id must be between {min} and {max} characters long +notification.resource_type.not_blank=Notification resource type must not be blank +notification.resource_type.length_range=Notification resource type must be between {min} and {max} characters long +notification.resource_name.not_blank=Notification resource name must not be blank +notification.resource_name.length_range=Notification resource name must be between {min} and {max} characters long +novice_statistics.id.not_blank=Novice statistics id must not be blank +novice_statistics.guide_step.not_blank=Novice statistics guide step must not be blank +novice_statistics.guide_step.length_range=Novice statistics guide step must be between {min} and {max} characters long +operating_log.id.not_blank=Operating log id must not be blank +operating_log.project_id.not_blank=Operating log project id must not be blank +operating_log.project_id.length_range=Operating log project id must be between {min} and {max} characters long +operating_log_resource.id.not_blank=Operating log resource id must not be blank +operating_log_resource.operating_log_id.not_blank=Operating log resource operating log id must not be blank +operating_log_resource.operating_log_id.length_range=Operating log resource operating log id must be between {min} and {max} characters long +operating_log_resource.source_id.not_blank=Operating log resource source id must not be blank +operating_log_resource.source_id.length_range=Operating log resource source id must be between {min} and {max} characters long +plugin.id.not_blank=Plugin id must not be blank +plugin.plugin_id.not_blank=Plugin plugin id must not be blank +plugin.plugin_id.length_range=Plugin plugin id must be between {min} and {max} characters long +plugin.script_id.not_blank=Plugin script id must not be blank +plugin.script_id.length_range=Plugin script id must be between {min} and {max} characters long +plugin.clazz_name.not_blank=Plugin clazz name must not be blank +plugin.clazz_name.length_range=Plugin clazz name must be between {min} and {max} characters long +plugin.jmeter_clazz.not_blank=Plugin jmeter clazz must not be blank +plugin.jmeter_clazz.length_range=Plugin jmeter clazz must be between {min} and {max} characters long +plugin.source_path.not_blank=Plugin source path must not be blank +plugin.source_path.length_range=Plugin source path must be between {min} and {max} characters long +plugin.source_name.not_blank=Plugin source name must not be blank +plugin.source_name.length_range=Plugin source name must be between {min} and {max} characters long +plugin.scenario.not_blank=Plugin scenario must not be blank +plugin.scenario.length_range=Plugin scenario must be between {min} and {max} characters long +plugin_blob.plugin_id.not_blank=Plugin blob plugin id must not be blank +quota.id.not_blank=Quota id must not be blank +schedule.id.not_blank=Schedule id must not be blank +schedule.type.not_blank=Schedule type must not be blank +schedule.type.length_range=Schedule type must be between {min} and {max} characters long +schedule.value.not_blank=Schedule value must not be blank +schedule.value.length_range=Schedule value must be between {min} and {max} characters long +schedule.job.not_blank=Schedule job must not be blank +schedule.job.length_range=Schedule job must be between {min} and {max} characters long +schedule.create_user.not_blank=Schedule create user must not be blank +schedule.create_user.length_range=Schedule create user must be between {min} and {max} characters long +service_integration.id.not_blank=Service integration id must not be blank +service_integration.platform.not_blank=Service integration platform must not be blank +service_integration.platform.length_range=Service integration platform must be between {min} and {max} characters long +system_parameter.param_key.not_blank=System parameter param key must not be blank +system_parameter.type.not_blank=System parameter type must not be blank +system_parameter.type.length_range=System parameter type must be between {min} and {max} characters long +test_resource.id.not_blank=Test resource id must not be blank +test_resource.test_resource_pool_id.not_blank=Test resource test resource pool id must not be blank +test_resource.test_resource_pool_id.length_range=Test resource test resource pool id must be between {min} and {max} characters long +test_resource.status.not_blank=Test resource status must not be blank +test_resource.status.length_range=Test resource status must be between {min} and {max} characters long +test_resource_pool.id.not_blank=Test resource pool id must not be blank +test_resource_pool.name.not_blank=Test resource pool name must not be blank +test_resource_pool.name.length_range=Test resource pool name must be between {min} and {max} characters long +test_resource_pool.type.not_blank=Test resource pool type must not be blank +test_resource_pool.type.length_range=Test resource pool type must be between {min} and {max} characters long +test_resource_pool.status.not_blank=Test resource pool status must not be blank +test_resource_pool.status.length_range=Test resource pool status must be between {min} and {max} characters long +user.id.not_blank=User id must not be blank +user.name.not_blank=User name must not be blank +user.name.length_range=User name must be between {min} and {max} characters long +user.email.not_blank=User email must not be blank +user.email.length_range=User email must be between {min} and {max} characters long +user.status.not_blank=User status must not be blank +user.status.length_range=User status must be between {min} and {max} characters long +user.source.not_blank=User source must not be blank +user.source.length_range=User source must be between {min} and {max} characters long +user.create_user.not_blank=User create user must not be blank +user.create_user.length_range=User create user must be between {min} and {max} characters long +user_extend.user_id.not_blank=User extend user id must not be blank +user_key.id.not_blank=User key id must not be blank +user_key.create_user.not_blank=User key create user must not be blank +user_key.create_user.length_range=User key create user must be between {min} and {max} characters long +user_key.access_key.not_blank=User key access key must not be blank +user_key.access_key.length_range=User key access key must be between {min} and {max} characters long +user_key.secret_key.not_blank=User key secret key must not be blank +user_key.secret_key.length_range=User key secret key must be between {min} and {max} characters long +user_role.id.not_blank=User role id must not be blank +user_role.name.not_blank=User role name must not be blank +user_role.name.length_range=User role name must be between {min} and {max} characters long +user_role.system.not_blank=User role system must not be blank +user_role.system.length_range=User role system must be between {min} and {max} characters long +user_role.type.not_blank=User role type must not be blank +user_role.type.length_range=User role type must be between {min} and {max} characters long +user_role.create_user.not_blank=User role create user must not be blank +user_role.create_user.length_range=User role create user must be between {min} and {max} characters long +user_role.scope_id.not_blank=User role scope id must not be blank +user_role.scope_id.length_range=User role scope id must be between {min} and {max} characters long +user_role_permission.id.not_blank=User role permission id must not be blank +user_role_permission.role_id.not_blank=User role permission role id must not be blank +user_role_permission.role_id.length_range=User role permission role id must be between {min} and {max} characters long +user_role_permission.permission_id.not_blank=User role permission permission id must not be blank +user_role_permission.permission_id.length_range=User role permission permission id must be between {min} and {max} characters long +user_role_permission.module_id.not_blank=User role permission module id must not be blank +user_role_permission.module_id.length_range=User role permission module id must be between {min} and {max} characters long +user_role_relation.id.not_blank=User role relation id must not be blank +user_role_relation.user_id.not_blank=User role relation user id must not be blank +user_role_relation.user_id.length_range=User role relation user id must be between {min} and {max} characters long +user_role_relation.role_id.not_blank=User role relation role id must not be blank +user_role_relation.role_id.length_range=User role relation role id must be between {min} and {max} characters long +user_role_relation.source_id.not_blank=User role relation source id must not be blank +user_role_relation.source_id.length_range=User role relation source id must be between {min} and {max} characters long +workspace.id.not_blank=Workspace id must not be blank +workspace.name.not_blank=Workspace name must not be blank +workspace.name.length_range=Workspace name must be between {min} and {max} characters long +workspace.create_user.not_blank=Workspace create user must not be blank +workspace.create_user.length_range=Workspace create user must be between {min} and {max} characters long + + + + + + + + + + + + diff --git a/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties index e69de29bb2..e1b7480cf1 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties @@ -0,0 +1,143 @@ +auth_source.id.not_blank=认证源ID不能为空 +auth_source.status.length_range=认证源状态长度必须在{min}和{max}之间 +auth_source.status.not_blank=认证源状态不能为空 +license.id.not_blank=License ID不能为空 +message_task.id.not_blank=消息通知任务ID不能为空 +message_task.type.not_blank=消息通知任务类型不能为空 +message_task.type.length_range=消息通知任务类型长度必须在{min}和{max}之间 +message_task.event.not_blank=消息通知任务事件不能为空 +message_task.event.length_range=消息通知任务事件长度必须在{min}和{max}之间 +message_task.receiver.not_blank=消息通知任务接收者不能为空 +message_task.receiver.length_range=消息通知任务接收者长度必须在{min}和{max}之间 +message_task.task_type.not_blank=消息通知任务任务类型不能为空 +message_task.task_type.length_range=消息通知任务任务类型长度必须在{min}和{max}之间 +message_task.test_id.not_blank=消息通知任务测试ID不能为空 +message_task.test_id.length_range=消息通知任务测试ID长度必须在{min}和{max}之间 +message_task.project_id.not_blank=消息通知任务项目ID不能为空 +message_task.project_id.length_range=消息通知任务项目ID长度必须在{min}和{max}之间 +message_task_blob.message_task_id.not_blank=消息通知任务ID不能为空 +notification.id.not_blank=消息通知ID不能为空 +notification.type.not_blank=消息通知类型不能为空 +notification.type.length_range=消息通知类型长度必须在{min}和{max}之间 +notification.receiver.not_blank=消息通知接收者不能为空 +notification.receiver.length_range=消息通知接收者长度必须在{min}和{max}之间 +notification.title.not_blank=消息通知标题不能为空 +notification.title.length_range=消息通知标题长度必须在{min}和{max}之间 +notification.status.not_blank=消息通知状态不能为空 +notification.status.length_range=消息通知状态长度必须在{min}和{max}之间 +notification.operator.not_blank=消息通知操作者不能为空 +notification.operator.length_range=消息通知操作者长度必须在{min}和{max}之间 +notification.operation.not_blank=消息通知操作不能为空 +notification.operation.length_range=消息通知操作长度必须在{min}和{max}之间 +notification.resource_id.not_blank=消息通知资源ID不能为空 +notification.resource_id.length_range=消息通知资源ID长度必须在{min}和{max}之间 +notification.resource_type.not_blank=消息通知资源类型不能为空 +notification.resource_type.length_range=消息通知资源类型长度必须在{min}和{max}之间 +notification.resource_name.not_blank=消息通知资源名称不能为空 +notification.resource_name.length_range=消息通知资源名称长度必须在{min}和{max}之间 +novice_statistics.id.not_blank=新手村ID不能为空 +novice_statistics.guide_step.not_blank=新手村步骤不能为空 +novice_statistics.guide_step.length_range=新手村步骤长度必须在{min}和{max}之间 +operating_log.id.not_blank=操作日志ID不能为空 +operating_log.project_id.not_blank=操作日志项目ID不能为空 +operating_log.project_id.length_range=操作日志项目ID长度必须在{min}和{max}之间 +operating_log_resource.id.not_blank=操作日志资源ID不能为空 +operating_log_resource.operating_log_id.not_blank=操作日志资源操作日志ID不能为空 +operating_log_resource.operating_log_id.length_range=操作日志资源操作日志ID长度必须在{min}和{max}之间 +operating_log_resource.source_id.not_blank=操作日志资源来源ID不能为空 +operating_log_resource.source_id.length_range=操作日志资源来源ID长度必须在{min}和{max}之间 +plugin.id.not_blank=插件主键不能为空 +plugin.plugin_id.not_blank=插件ID不能为空 +plugin.plugin_id.length_range=插件ID长度必须在{min}和{max}之间 +plugin.script_id.not_blank=插件脚本ID不能为空 +plugin.script_id.length_range=插件脚本ID长度必须在{min}和{max}之间 +plugin.clazz_name.not_blank=插件类名不能为空 +plugin.clazz_name.length_range=插件类名长度必须在{min}和{max}之间 +plugin.jmeter_clazz.not_blank=插件jmeter类名不能为空 +plugin.jmeter_clazz.length_range=插件jmeter类名长度必须在{min}和{max}之间 +plugin.source_path.not_blank=插件源路径不能为空 +plugin.source_path.length_range=插件源路径长度必须在{min}和{max}之间 +plugin.source_name.not_blank=插件源名称不能为空 +plugin.source_name.length_range=插件源名称长度必须在{min}和{max}之间 +plugin.scenario.not_blank=插件场景不能为空 +plugin.scenario.length_range=插件场景长度必须在{min}和{max}之间 +plugin_blob.plugin_id.not_blank=插件ID不能为空 +quota.id.not_blank=配额ID不能为空 +schedule.id.not_blank=定时任务ID不能为空 +schedule.type.not_blank=定时任务类型不能为空 +schedule.type.length_range=定时任务类型长度必须在{min}和{max}之间 +schedule.value.not_blank=定时任务值不能为空 +schedule.value.length_range=定时任务值长度必须在{min}和{max}之间 +schedule.job.not_blank=定时任务不能为空 +schedule.job.length_range=定时任务长度必须在{min}和{max}之间 +schedule.create_user.not_blank=定时任务创建人不能为空 +schedule.create_user.length_range=定时任务创建人长度必须在{min}和{max}之间 +service_integration.id.not_blank=服务集成ID不能为空 +service_integration.platform.not_blank=服务集成平台不能为空 +service_integration.platform.length_range=服务集成平台长度必须在{min}和{max}之间 +system_parameter.param_key.not_blank=系统参数Key不能为空 +system_parameter.type.not_blank=系统参数类型不能为空 +system_parameter.type.length_range=系统参数类型长度必须在{min}和{max}之间 +test_resource.id.not_blank=资源池节点ID不能为空 +test_resource.test_resource_pool_id.not_blank=资源池ID不能为空 +test_resource.test_resource_pool_id.length_range=资源池ID长度必须在{min}和{max}之间 +test_resource.status.not_blank=资源池节点状态不能为空 +test_resource.status.length_range=资源池节点状态长度必须在{min}和{max}之间 +test_resource_pool.id.not_blank=资源池ID不能为空 +test_resource_pool.name.not_blank=资源池名称不能为空 +test_resource_pool.name.length_range=资源池名称长度必须在{min}和{max}之间 +test_resource_pool.type.not_blank=资源池类型不能为空 +test_resource_pool.type.length_range=资源池类型长度必须在{min}和{max}之间 +test_resource_pool.status.not_blank=资源池状态不能为空 +test_resource_pool.status.length_range=资源池状态长度必须在{min}和{max}之间 +user.id.not_blank=用户ID不能为空 +user.name.not_blank=用户名称不能为空 +user.name.length_range=用户名称长度必须在{min}和{max}之间 +user.email.not_blank=用户email不能为空 +user.email.length_range=用户email长度必须在{min}和{max}之间 +user.status.not_blank=用户状态不能为空 +user.status.length_range=用户状态长度必须在{min}和{max}之间 +user.source.not_blank=用户来源不能为空 +user.source.length_range=用户来源长度必须在{min}和{max}之间 +user.create_user.not_blank=用户创建人不能为空 +user.create_user.length_range=用户创建人长度必须在{min}和{max}之间 +user_extend.user_id.not_blank=用户ID不能为空 +user_key.id.not_blank=用户ApiKey ID不能为空 +user_key.create_user.not_blank=用户ApiKey创建人不能为空 +user_key.create_user.length_range=用户ApiKey创建人长度必须在{min}和{max}之间 +user_key.access_key.not_blank=用户ApiKey access key不能为空 +user_key.access_key.length_range=用户ApiKey access key长度必须在{min}和{max}之间 +user_key.secret_key.not_blank=用户ApiKey secret key不能为空 +user_key.secret_key.length_range=用户ApiKey secret key长度必须在{min}和{max}之间 +user_role.id.not_blank=用户组ID不能为空 +user_role.name.not_blank=用户组名称不能为空 +user_role.name.length_range=用户组名称长度必须在{min}和{max}之间 +user_role.system.not_blank=是否是系统用户组不能为空 +user_role.system.length_range=是否是系统用户组长度必须在{min}和{max}之间 +user_role.type.not_blank=用户组类型不能为空 +user_role.type.length_range=用户组类型长度必须在{min}和{max}之间 +user_role.create_user.not_blank=用户组创建人不能为空 +user_role.create_user.length_range=用户组创建人长度必须在{min}和{max}之间 +user_role.scope_id.not_blank=用户组应用范围不能为空 +user_role.scope_id.length_range=用户组应用范围长度必须在{min}和{max}之间 +user_role_permission.id.not_blank=用户组权限ID不能为空 +user_role_permission.role_id.not_blank=用户组权限用户组ID不能为空 +user_role_permission.role_id.length_range=用户组权限用户组ID长度必须在{min}和{max}之间 +user_role_permission.permission_id.not_blank=用户组权限权限ID不能为空 +user_role_permission.permission_id.length_range=用户组权限权限ID长度必须在{min}和{max}之间 +user_role_permission.module_id.not_blank=用户组权限模块ID不能为空 +user_role_permission.module_id.length_range=用户组权限模块ID长度必须在{min}和{max}之间 +user_role_relation.id.not_blank=用户组关系ID不能为空 +user_role_relation.user_id.not_blank=用户组关系用户ID不能为空 +user_role_relation.user_id.length_range=用户组关系用户ID长度必须在{min}和{max}之间 +user_role_relation.role_id.not_blank=用户组关系用户组ID不能为空 +user_role_relation.role_id.length_range=用户组关系用户组ID长度必须在{min}和{max}之间 +user_role_relation.source_id.not_blank=用户组关系来源ID不能为空 +user_role_relation.source_id.length_range=用户组关系来源ID长度必须在{min}和{max}之间 +workspace.id.not_blank=工作空间ID不能为空 +workspace.name.not_blank=工作空间名称不能为空 +workspace.name.length_range=工作空间名称长度必须在{min}和{max}之间 +workspace.create_user.not_blank=工作空间创建人不能为空 +workspace.create_user.length_range=工作空间创建人长度必须在{min}和{max}之间 + + diff --git a/backend/framework/sdk/src/main/resources/i18n/system_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/system_zh_TW.properties index e69de29bb2..bb50e320be 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system_zh_TW.properties @@ -0,0 +1,141 @@ +auth_source.id.not_blank=認證源ID不能為空 +auth_source.status.length_range=認證源狀態長度必須在{min}和{max}之間 +auth_source.status.not_blank=認證源狀態不能為空 +license.id.not_blank=License ID不能為空 +message_task.id.not_blank=消息通知任務ID不能為空 +message_task.type.not_blank=消息通知任務類型不能為空 +message_task.type.length_range=消息通知任務類型長度必須在{min}和{max}之間 +message_task.event.not_blank=消息通知任務事件不能為空 +message_task.event.length_range=消息通知任務事件長度必須在{min}和{max}之間 +message_task.receiver.not_blank=消息通知任務接收者不能為空 +message_task.receiver.length_range=消息通知任務接收者長度必須在{min}和{max}之間 +message_task.task_type.not_blank=消息通知任務任務類型不能為空 +message_task.task_type.length_range=消息通知任務任務類型長度必須在{min}和{max}之間 +message_task.test_id.not_blank=消息通知任務測試ID不能為空 +message_task.test_id.length_range=消息通知任務測試ID長度必須在{min}和{max}之間 +message_task.project_id.not_blank=消息通知任務項目ID不能為空 +message_task.project_id.length_range=消息通知任務項目ID長度必須在{min}和{max}之間 +message_task_blob.message_task_id.not_blank=消息通知任務ID不能為空 +notification.id.not_blank=消息通知ID不能為空 +notification.type.not_blank=消息通知類型不能為空 +notification.type.length_range=消息通知類型長度必須在{min}和{max}之間 +notification.receiver.not_blank=消息通知接收者不能為空 +notification.receiver.length_range=消息通知接收者長度必須在{min}和{max}之間 +notification.title.not_blank=消息通知標題不能為空 +notification.title.length_range=消息通知標題長度必須在{min}和{max}之間 +notification.status.not_blank=消息通知狀態不能為空 +notification.status.length_range=消息通知狀態長度必須在{min}和{max}之間 +notification.operator.not_blank=消息通知操作者不能為空 +notification.operator.length_range=消息通知操作者長度必須在{min}和{max}之間 +notification.operation.not_blank=消息通知操作不能為空 +notification.operation.length_range=消息通知操作長度必須在{min}和{max}之間 +notification.resource_id.not_blank=消息通知資源ID不能為空 +notification.resource_id.length_range=消息通知資源ID長度必須在{min}和{max}之間 +notification.resource_type.not_blank=消息通知資源類型不能為空 +notification.resource_type.length_range=消息通知資源類型長度必須在{min}和{max}之間 +notification.resource_name.not_blank=消息通知資源名稱不能為空 +notification.resource_name.length_range=消息通知資源名稱長度必須在{min}和{max}之間 +novice_statistics.id.not_blank=新手村ID不能為空 +novice_statistics.guide_step.not_blank=新手村步驟不能為空 +novice_statistics.guide_step.length_range=新手村步驟長度必須在{min}和{max}之間 +operating_log.id.not_blank=操作日誌ID不能為空 +operating_log.project_id.not_blank=操作日誌項目ID不能為空 +operating_log.project_id.length_range=操作日誌項目ID長度必須在{min}和{max}之間 +operating_log_resource.id.not_blank=操作日誌資源ID不能為空 +operating_log_resource.operating_log_id.not_blank=操作日誌資源操作日誌ID不能為空 +operating_log_resource.operating_log_id.length_range=操作日誌資源操作日誌ID長度必須在{min}和{max}之間 +operating_log_resource.source_id.not_blank=操作日誌資源來源ID不能為空 +operating_log_resource.source_id.length_range=操作日誌資源來源ID長度必須在{min}和{max}之間 +plugin.id.not_blank=插件主鍵不能為空 +plugin.plugin_id.not_blank=插件ID不能為空 +plugin.plugin_id.length_range=插件ID長度必須在{min}和{max}之間 +plugin.script_id.not_blank=插件腳本ID不能為空 +plugin.script_id.length_range=插件腳本ID長度必須在{min}和{max}之間 +plugin.clazz_name.not_blank=插件類名不能為空 +plugin.clazz_name.length_range=插件類名長度必須在{min}和{max}之間 +plugin.jmeter_clazz.not_blank=插件jmeter類名不能為空 +plugin.jmeter_clazz.length_range=插件jmeter類名長度必須在{min}和{max}之間 +plugin.source_path.not_blank=插件源路徑不能為空 +plugin.source_path.length_range=插件源路徑長度必須在{min}和{max}之間 +plugin.source_name.not_blank=插件源名稱不能為空 +plugin.source_name.length_range=插件源名稱長度必須在{min}和{max}之間 +plugin.scenario.not_blank=插件場景不能為空 +plugin.scenario.length_range=插件場景長度必須在{min}和{max}之間 +plugin_blob.plugin_id.not_blank=插件ID不能為空 +quota.id.not_blank=配額ID不能為空 +schedule.id.not_blank=定時任務ID不能為空 +schedule.type.not_blank=定時任務類型不能為空 +schedule.type.length_range=定時任務類型長度必須在{min}和{max}之間 +schedule.value.not_blank=定時任務值不能為空 +schedule.value.length_range=定時任務值長度必須在{min}和{max}之間 +schedule.job.not_blank=定時任務不能為空 +schedule.job.length_range=定時任務長度必須在{min}和{max}之間 +schedule.create_user.not_blank=定時任務創建人不能為空 +schedule.create_user.length_range=定時任務創建人長度必須在{min}和{max}之間 +service_integration.id.not_blank=服務集成ID不能為空 +service_integration.platform.not_blank=服務集成平台不能為空 +service_integration.platform.length_range=服務集成平台長度必須在{min}和{max}之間 +system_parameter.param_key.not_blank=系統參數Key不能為空 +system_parameter.type.not_blank=系統參數類型不能為空 +system_parameter.type.length_range=系統參數類型長度必須在{min}和{max}之間 +test_resource.id.not_blank=資源池節點ID不能為空 +test_resource.test_resource_pool_id.not_blank=資源池ID不能為空 +test_resource.test_resource_pool_id.length_range=資源池ID長度必須在{min}和{max}之間 +test_resource.status.not_blank=資源池節點狀態不能為空 +test_resource.status.length_range=資源池節點狀態長度必須在{min}和{max}之間 +test_resource_pool.id.not_blank=資源池ID不能為空 +test_resource_pool.name.not_blank=資源池名稱不能為空 +test_resource_pool.name.length_range=資源池名稱長度必須在{min}和{max}之間 +test_resource_pool.type.not_blank=資源池類型不能為空 +test_resource_pool.type.length_range=資源池類型長度必須在{min}和{max}之間 +test_resource_pool.status.not_blank=資源池狀態不能為空 +test_resource_pool.status.length_range=資源池狀態長度必須在{min}和{max}之間 +user.id.not_blank=用戶ID不能為空 +user.name.not_blank=用戶名稱不能為空 +user.name.length_range=用戶名稱長度必須在{min}和{max}之間 +user.email.not_blank=用戶email不能為空 +user.email.length_range=用戶email長度必須在{min}和{max}之間 +user.status.not_blank=用戶狀態不能為空 +user.status.length_range=用戶狀態長度必須在{min}和{max}之間 +user.source.not_blank=用戶來源不能為空 +user.source.length_range=用戶來源長度必須在{min}和{max}之間 +user.create_user.not_blank=用戶創建人不能為空 +user.create_user.length_range=用戶創建人長度必須在{min}和{max}之間 +user_extend.user_id.not_blank=用戶ID不能為空 +user_key.id.not_blank=用戶ApiKey ID不能為空 +user_key.create_user.not_blank=用戶ApiKey創建人不能為空 +user_key.create_user.length_range=用戶ApiKey創建人長度必須在{min}和{max}之間 +user_key.access_key.not_blank=用戶ApiKey access key不能為空 +user_key.access_key.length_range=用戶ApiKey access key長度必須在{min}和{max}之間 +user_key.secret_key.not_blank=用戶ApiKey secret key不能為空 +user_key.secret_key.length_range=用戶ApiKey secret key長度必須在{min}和{max}之間 +user_role.id.not_blank=用戶組ID不能為空 +user_role.name.not_blank=用戶組名稱不能為空 +user_role.name.length_range=用戶組名稱長度必須在{min}和{max}之間 +user_role.system.not_blank=是否是系統用戶組不能為空 +user_role.system.length_range=是否是系統用戶組長度必須在{min}和{max}之間 +user_role.type.not_blank=用戶組類型不能為空 +user_role.type.length_range=用戶組類型長度必須在{min}和{max}之間 +user_role.create_user.not_blank=用戶組創建人不能為空 +user_role.create_user.length_range=用戶組創建人長度必須在{min}和{max}之間 +user_role.scope_id.not_blank=用戶組應用範圍不能為空 +user_role.scope_id.length_range=用戶組應用範圍長度必須在{min}和{max}之間 +user_role_permission.id.not_blank=用戶組權限ID不能為空 +user_role_permission.role_id.not_blank=用戶組權限用戶組ID不能為空 +user_role_permission.role_id.length_range=用戶組權限用戶組ID長度必須在{min}和{max}之間 +user_role_permission.permission_id.not_blank=用戶組權限權限ID不能為空 +user_role_permission.permission_id.length_range=用戶組權限權限ID長度必須在{min}和{max}之間 +user_role_permission.module_id.not_blank=用戶組權限模塊ID不能為空 +user_role_permission.module_id.length_range=用戶組權限模塊ID長度必須在{min}和{max}之間 +user_role_relation.id.not_blank=用戶組關係ID不能為空 +user_role_relation.user_id.not_blank=用戶組關係用戶ID不能為空 +user_role_relation.user_id.length_range=用戶組關係用戶ID長度必須在{min}和{max}之間 +user_role_relation.role_id.not_blank=用戶組關係用戶組ID不能為空 +user_role_relation.role_id.length_range=用戶組關係用戶組ID長度必須在{min}和{max}之間 +user_role_relation.source_id.not_blank=用戶組關係來源ID不能為空 +user_role_relation.source_id.length_range=用戶組關係來源ID長度必須在{min}和{max}之間 +workspace.id.not_blank=工作空間ID不能為空 +workspace.name.not_blank=工作空間名稱不能為空 +workspace.name.length_range=工作空間名稱長度必須在{min}和{max}之間 +workspace.create_user.not_blank=工作空間創建人不能為空 +workspace.create_user.length_range=工作空間創建人長度必須在{min}和{max}之間 \ No newline at end of file diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/controller/UserController.java b/backend/services/system-setting/src/main/java/io/metersphere/system/controller/UserController.java index 858a4fae1b..f27b939bb6 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/controller/UserController.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/controller/UserController.java @@ -1,13 +1,12 @@ package io.metersphere.system.controller; -import io.metersphere.system.domain.User; import io.metersphere.sdk.service.UserService; +import io.metersphere.system.domain.User; +import io.metersphere.validation.groups.Created; import jakarta.annotation.Resource; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; import java.util.List; @@ -26,4 +25,9 @@ public class UserController { public User getUser(@PathVariable String userId) { return userService.getById(userId); } + + @PostMapping("/add") + public boolean addUser(@Validated({Created.class}) @RequestBody User user) { + return userService.save(user); + } } diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/UserControllerTests.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/UserControllerTests.java index cb9282493b..285a3461b7 100644 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/UserControllerTests.java +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/UserControllerTests.java @@ -1,5 +1,7 @@ package io.metersphere.system.controller; +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.domain.User; import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -33,6 +35,18 @@ public class UserControllerTests { mockMvc.perform(MockMvcRequestBuilders.get("/user/get/admin")) .andExpect(status().isOk()) // .andExpect(content().contentType(MediaType.APPLICATION_JSON)) +// .andExpect(jsonPath("$.person.name").value("Jason")) + .andDo(print()); + } + + @Test + public void testAddUser() throws Exception { + User user = new User(); + user.setId("admin"); + user.setName("admin"); + mockMvc.perform(MockMvcRequestBuilders.post("/user/add").content(JSON.toJSONString(user)).contentType(MediaType.APPLICATION_JSON)) +// .andExpect(status().isOk()) +// .andExpect(content().contentType(MediaType.APPLICATION_JSON)) // .andExpect(jsonPath("$.person.name").value("Jason")) .andDo(print()); } diff --git a/backend/services/system-setting/src/test/resources/application.properties b/backend/services/system-setting/src/test/resources/application.properties index efef6fcdcc..2997228575 100644 --- a/backend/services/system-setting/src/test/resources/application.properties +++ b/backend/services/system-setting/src/test/resources/application.properties @@ -60,7 +60,7 @@ jmeter.home=/opt/jmeter spring.servlet.multipart.max-file-size=500MB spring.servlet.multipart.max-request-size=500MB # i18n -spring.messages.basename=i18n/messages,i18n/api,i18n/load,i18n/project,i18n/system,i18n/plan,i18n/track,i18n/ui,i18n/workstation +spring.messages.basename=i18n/commons,i18n/api,i18n/issue,i18n/load,i18n/project,i18n/system,i18n/plan,i18n/track,i18n/ui,i18n/workstation # actuator management.endpoints.web.exposure.include=* management.endpoints.enabled-by-default=false