diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/constant/PropertiesConstants.java b/continew-starter-core/src/main/java/top/continew/starter/core/constant/PropertiesConstants.java index edb8d4b0..d973c41d 100644 --- a/continew-starter-core/src/main/java/top/continew/starter/core/constant/PropertiesConstants.java +++ b/continew-starter-core/src/main/java/top/continew/starter/core/constant/PropertiesConstants.java @@ -124,6 +124,11 @@ public class PropertiesConstants { */ public static final String MESSAGING_WEBSOCKET = MESSAGING + StringConstants.DOT + "websocket"; + /** + * 国际化配置 + */ + public static final String I18N = WEB + StringConstants.DOT + "i18n"; + private PropertiesConstants() { } } diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalException.java b/continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalException.java new file mode 100644 index 00000000..9193bb9e --- /dev/null +++ b/continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalException.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.core.exception; + +/** + * 统一错误码异常 + * + * @author Jasmine + * @since 2.2.0 + */ +public class GlobalException extends Exception { + + private ResultInfoInterface resultInfo; + + public GlobalException() { + } + + public GlobalException(ResultInfoInterface resultInfo) { + this.resultInfo = resultInfo; + } + + public ResultInfoInterface getResultInfo() { + return this.resultInfo; + } + + public void setResultInfo(ResultInfoInterface resultInfo) { + this.resultInfo = resultInfo; + } +} diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalResultInfoEnum.java b/continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalResultInfoEnum.java new file mode 100644 index 00000000..266d5d38 --- /dev/null +++ b/continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalResultInfoEnum.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.core.exception; + +/** + * 接口返回码 所有业务异常都要继承该接口 + * + * @author Jasmine + * @since 2.2.0 + */ +public enum GlobalResultInfoEnum implements ResultInfoInterface { + + /** + * 操作成功 + */ + SUCCESS(200, "操作成功"), + + /** + * 操作失败 + */ + FAILED(500, "操作失败"); + + private int code; + private String messageKey; + private String defaultMessage; + + GlobalResultInfoEnum(int code, String defaultMessage) { + this.code = code; + this.defaultMessage = defaultMessage; + } + + GlobalResultInfoEnum(int code, String messageKey, String defaultMessage) { + this.code = code; + this.messageKey = messageKey; + this.defaultMessage = defaultMessage; + } + + @Override + public int getCode() { + return this.code; + } + + @Override + public String getMessageKey() { + return this.messageKey; + } + + @Override + public String getDefaultMessage() { + return this.defaultMessage; + } +} diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/exception/ResultInfoInterface.java b/continew-starter-core/src/main/java/top/continew/starter/core/exception/ResultInfoInterface.java new file mode 100644 index 00000000..d943c4db --- /dev/null +++ b/continew-starter-core/src/main/java/top/continew/starter/core/exception/ResultInfoInterface.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.core.exception; + +/** + * 接口返回码与消息 所有业务异常都要继承该接口 + * + * @author Jasmine + * @since 2.2.0 + */ +public interface ResultInfoInterface { + + /** + * 获取编码 + * + * @return String + */ + int getCode(); + + /** + * 国际化消息key + * + * @return + */ + default String getMessageKey() { + return ""; + } + + /** + * 获取默认消息 若从国际化文件里没有获取到值,就取默认值 + * + * @return String + */ + String getDefaultMessage(); +} diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java index 4c827ff0..792adf6a 100644 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java +++ b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java @@ -19,6 +19,8 @@ package top.continew.starter.web.autoconfigure.exception; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.StrUtil; +import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; @@ -36,7 +38,11 @@ import org.springframework.web.multipart.MultipartException; import top.continew.starter.core.constant.StringConstants; import top.continew.starter.core.exception.BadRequestException; import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.core.exception.GlobalException; +import top.continew.starter.core.exception.ResultInfoInterface; +import top.continew.starter.web.autoconfigure.i18n.I18nProperties; import top.continew.starter.web.model.R; +import top.continew.starter.web.util.MessageSourceUtils; /** * 全局异常处理器 @@ -49,6 +55,8 @@ public class GlobalExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); private static final String PARAM_FAILED = "请求地址 [{}],参数验证失败。"; + @Resource + private I18nProperties i18nProperties; /** * 拦截自定义验证异常-错误请求 @@ -153,6 +161,23 @@ public class GlobalExceptionHandler { return R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); } + /** + * 拦截全局应用异常 + */ + @ExceptionHandler(GlobalException.class) + public R handleGlobalException(GlobalException e, HttpServletRequest request) { + log.error("请求地址 [{}],发生业务异常。", request.getRequestURI(), e); + ResultInfoInterface resultInfo = e.getResultInfo(); + // 未开启,直接返回 + if (!i18nProperties.getEnabled()) { + return R.fail(resultInfo.getCode(), resultInfo.getDefaultMessage()); + } + // 以用户自定的messageKey优先,否则枚举当messageKey + String messageKey = StrUtil.blankToDefault(resultInfo.getMessageKey(), resultInfo.toString()); + String message = MessageSourceUtils.getMessage(messageKey, resultInfo.getDefaultMessage()); + return R.fail(resultInfo.getCode(), message); + } + /** * 拦截未知的运行时异常 */ diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandlerAutoConfiguration.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandlerAutoConfiguration.java index 93bc3899..7b598cb3 100644 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandlerAutoConfiguration.java +++ b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandlerAutoConfiguration.java @@ -26,10 +26,12 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory; +import top.continew.starter.web.autoconfigure.i18n.I18nProperties; /** * 全局异常处理器自动配置 @@ -40,6 +42,7 @@ import org.springframework.validation.beanvalidation.SpringConstraintValidatorFa @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(BasicErrorController.class) @Import({GlobalExceptionHandler.class, GlobalErrorHandler.class}) +@EnableConfigurationProperties(I18nProperties.class) public class GlobalExceptionHandlerAutoConfiguration { private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandlerAutoConfiguration.class); diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/i18n/I18nProperties.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/i18n/I18nProperties.java new file mode 100644 index 00000000..e2e0802c --- /dev/null +++ b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/i18n/I18nProperties.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.web.autoconfigure.i18n; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import top.continew.starter.core.constant.PropertiesConstants; + +/** + * 国际化 配置属性 + * + * @author Jasmine + * @since 2.2.0 + */ +@ConfigurationProperties(prefix = PropertiesConstants.I18N) +public class I18nProperties { + + /** + * 国际化开启 true-开启, false-关闭 + */ + private Boolean enabled; + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } +} diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/util/MessageSourceUtils.java b/continew-starter-web/src/main/java/top/continew/starter/web/util/MessageSourceUtils.java new file mode 100644 index 00000000..66ddb4a7 --- /dev/null +++ b/continew-starter-web/src/main/java/top/continew/starter/web/util/MessageSourceUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.web.util; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * @author Jasmine + * @since 2.2.0 + */ +public class MessageSourceUtils { + + private static final MessageSource messageSource = SpringUtil.getBean(MessageSource.class); + + private static final Object[] emptyArray = new Object[] {}; + + public static String getMessage(String key) { + return getMessage(key, emptyArray); + } + + public static String getMessage(String key, String defaultMessage) { + return getMessage(key, defaultMessage, emptyArray); + } + + public static String getMessage(String msgKey, Object... args) { + return getMessage(msgKey, msgKey, args); + } + + public static String getMessage(String msgKey, String defaultMessage, Object... args) { + try { + return messageSource.getMessage(msgKey, args, LocaleContextHolder.getLocale()); + } catch (Exception e) { + return defaultMessage; + } + } + +}