mirror of
				https://github.com/continew-org/continew-starter.git
				synced 2025-10-31 22:57:19 +08:00 
			
		
		
		
	refactor(web): 重构全局响应处理方案
引入 Graceful Response(一个Spring Boot技术栈下的优雅响应处理组件,可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程,提高开发效率,提高代码质量)
This commit is contained in:
		| @@ -44,7 +44,6 @@ import top.continew.starter.core.util.GeneralPropertySourceFactory; | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @AutoConfiguration | ||||
| @ComponentScan("top.continew.starter.auth.satoken.exception") | ||||
| @EnableConfigurationProperties(SaTokenExtensionProperties.class) | ||||
| @ConditionalOnProperty(prefix = "sa-token.extension", name = PropertiesConstants.ENABLED, havingValue = "true") | ||||
| @PropertySource(value = "classpath:default-auth-satoken.yml", factory = GeneralPropertySourceFactory.class) | ||||
|   | ||||
| @@ -1,72 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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.auth.satoken.exception; | ||||
|  | ||||
| import cn.dev33.satoken.exception.NotLoginException; | ||||
| import cn.dev33.satoken.exception.NotPermissionException; | ||||
| import cn.dev33.satoken.exception.NotRoleException; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||||
| import top.continew.starter.web.model.R; | ||||
|  | ||||
| /** | ||||
|  * 全局 SaToken 异常处理器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 1.2.0 | ||||
|  */ | ||||
| @RestControllerAdvice | ||||
| public class GlobalSaTokenExceptionHandler { | ||||
|  | ||||
|     private static final Logger log = LoggerFactory.getLogger(GlobalSaTokenExceptionHandler.class); | ||||
|  | ||||
|     /** | ||||
|      * 认证异常-登录认证 | ||||
|      */ | ||||
|     @ExceptionHandler(NotLoginException.class) | ||||
|     public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],认证失败,无法访问系统资源。", request.getRequestURI(), e); | ||||
|         String errorMsg = switch (e.getType()) { | ||||
|             case NotLoginException.KICK_OUT -> "您已被踢下线。"; | ||||
|             case NotLoginException.BE_REPLACED_MESSAGE -> "您已被顶下线。"; | ||||
|             default -> "您的登录状态已过期,请重新登录。"; | ||||
|         }; | ||||
|         return R.fail(HttpStatus.UNAUTHORIZED.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 认证异常-权限认证 | ||||
|      */ | ||||
|     @ExceptionHandler(NotPermissionException.class) | ||||
|     public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],权限码校验失败。", request.getRequestURI(), e); | ||||
|         return R.fail(HttpStatus.FORBIDDEN.value(), "没有访问权限,请联系管理员授权"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 认证异常-角色认证 | ||||
|      */ | ||||
|     @ExceptionHandler(NotRoleException.class) | ||||
|     public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],角色权限校验失败。", request.getRequestURI(), e); | ||||
|         return R.fail(HttpStatus.FORBIDDEN.value(), "没有访问权限,请联系管理员授权"); | ||||
|     } | ||||
| } | ||||
| @@ -64,6 +64,11 @@ public class PropertiesConstants { | ||||
|      */ | ||||
|     public static final String WEB_CORS = WEB + StringConstants.DOT + "cors"; | ||||
|  | ||||
|     /** | ||||
|      * 响应配置 | ||||
|      */ | ||||
|     public static final String WEB_RESPONSE = WEB + StringConstants.DOT + "response"; | ||||
|  | ||||
|     /** | ||||
|      * 链路配置 | ||||
|      */ | ||||
| @@ -74,11 +79,6 @@ public class PropertiesConstants { | ||||
|      */ | ||||
|     public static final String WEB_XSS = WEB + StringConstants.DOT + "xss"; | ||||
|  | ||||
|     /** | ||||
|      * 国际化配置 | ||||
|      */ | ||||
|     public static final String WEB_I18N = WEB + StringConstants.DOT + "i18n"; | ||||
|  | ||||
|     /** | ||||
|      * 日志配置 | ||||
|      */ | ||||
|   | ||||
| @@ -1,43 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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; | ||||
|     } | ||||
| } | ||||
| @@ -1,66 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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; | ||||
|     } | ||||
| } | ||||
| @@ -1,49 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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(); | ||||
| } | ||||
| @@ -43,7 +43,7 @@ | ||||
|  | ||||
|     <properties> | ||||
|         <!-- 项目版本号 --> | ||||
|         <revision>2.4.0</revision> | ||||
|         <revision>2.5.0-SNAPSHOT</revision> | ||||
|         <snail-job.version>1.1.0</snail-job.version> | ||||
|         <sa-token.version>1.38.0</sa-token.version> | ||||
|         <just-auth.version>1.16.6</just-auth.version> | ||||
| @@ -61,6 +61,7 @@ | ||||
|         <nashorn.version>15.4</nashorn.version> | ||||
|         <x-file-storage.version>2.2.0</x-file-storage.version> | ||||
|         <aws-s3.version>1.12.761</aws-s3.version> | ||||
|         <graceful-response.version>4.0.1-boot3</graceful-response.version> | ||||
|         <crane4j.version>2.9.0</crane4j.version> | ||||
|         <knife4j.version>4.5.0</knife4j.version> | ||||
|         <tlog.version>1.5.2</tlog.version> | ||||
| @@ -261,6 +262,13 @@ | ||||
|                 <version>${aws-s3.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- Graceful Response(一个Spring Boot技术栈下的优雅响应处理组件,可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程,提高开发效率,提高代码质量) --> | ||||
|             <dependency> | ||||
|                 <groupId>com.feiniaojin</groupId> | ||||
|                 <artifactId>graceful-response</artifactId> | ||||
|                 <version>${graceful-response.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- Crane4j(一个基于注解的,用于完成一切 “根据 A 的 key 值拿到 B,再把 B 的属性映射到 A” 这类需求的字段填充框架) --> | ||||
|             <dependency> | ||||
|                 <groupId>cn.crane4j</groupId> | ||||
|   | ||||
| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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.extension.crud.model.resp; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * ID 响应信息 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2.5.0 | ||||
|  */ | ||||
| public class BaseIdResp<T extends Serializable> implements Serializable { | ||||
|  | ||||
|     /** | ||||
|      * ID | ||||
|      */ | ||||
|     @Schema(description = "ID", example = "1") | ||||
|     private T id; | ||||
|  | ||||
|     public T getId() { | ||||
|         return id; | ||||
|     } | ||||
|  | ||||
|     public void setId(T id) { | ||||
|         this.id = id; | ||||
|     } | ||||
|  | ||||
|     BaseIdResp(final T id) { | ||||
|         this.id = id; | ||||
|     } | ||||
|  | ||||
|     public static <T extends Serializable> BaseIdRespBuilder<T> builder() { | ||||
|         return new BaseIdRespBuilder(); | ||||
|     } | ||||
|  | ||||
|     public static class BaseIdRespBuilder<T extends Serializable> { | ||||
|         private T id; | ||||
|  | ||||
|         BaseIdRespBuilder() { | ||||
|         } | ||||
|  | ||||
|         public BaseIdRespBuilder<T> id(final T id) { | ||||
|             this.id = id; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public BaseIdResp<T> build() { | ||||
|             return new BaseIdResp(this.id); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -19,6 +19,7 @@ package top.continew.starter.extension.crud.controller; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.lang.tree.Tree; | ||||
| import cn.hutool.core.text.CharSequenceUtil; | ||||
| import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| import io.swagger.v3.oas.annotations.enums.ParameterIn; | ||||
| @@ -29,13 +30,13 @@ import org.springframework.web.bind.annotation.*; | ||||
| import top.continew.starter.core.constant.StringConstants; | ||||
| import top.continew.starter.extension.crud.annotation.CrudRequestMapping; | ||||
| import top.continew.starter.extension.crud.enums.Api; | ||||
| import top.continew.starter.extension.crud.model.query.PageQuery; | ||||
| import top.continew.starter.extension.crud.model.query.SortQuery; | ||||
| import top.continew.starter.extension.crud.model.req.BaseReq; | ||||
| import top.continew.starter.extension.crud.model.resp.BaseIdResp; | ||||
| import top.continew.starter.extension.crud.model.resp.PageResp; | ||||
| import top.continew.starter.extension.crud.service.BaseService; | ||||
| import top.continew.starter.extension.crud.util.ValidateGroup; | ||||
| import top.continew.starter.extension.crud.model.query.PageQuery; | ||||
| import top.continew.starter.extension.crud.model.resp.PageResp; | ||||
| import top.continew.starter.web.model.R; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| @@ -65,9 +66,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Operation(summary = "分页查询列表", description = "分页查询列表") | ||||
|     @ResponseBody | ||||
|     @GetMapping | ||||
|     public R<PageResp<L>> page(Q query, @Validated PageQuery pageQuery) { | ||||
|     public PageResp<L> page(Q query, @Validated PageQuery pageQuery) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return R.ok(baseService.page(query, pageQuery)); | ||||
|         return baseService.page(query, pageQuery); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -80,9 +81,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Operation(summary = "查询树列表", description = "查询树列表") | ||||
|     @ResponseBody | ||||
|     @GetMapping("/tree") | ||||
|     public R<List<Tree<Long>>> tree(Q query, SortQuery sortQuery) { | ||||
|     public List<Tree<Long>> tree(Q query, SortQuery sortQuery) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return R.ok(baseService.tree(query, sortQuery, false)); | ||||
|         return baseService.tree(query, sortQuery, false); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -95,9 +96,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Operation(summary = "查询列表", description = "查询列表") | ||||
|     @ResponseBody | ||||
|     @GetMapping("/list") | ||||
|     public R<List<L>> list(Q query, SortQuery sortQuery) { | ||||
|     public List<L> list(Q query, SortQuery sortQuery) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return R.ok(baseService.list(query, sortQuery)); | ||||
|         return baseService.list(query, sortQuery); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -110,9 +111,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) | ||||
|     @ResponseBody | ||||
|     @GetMapping("/{id}") | ||||
|     public R<D> get(@PathVariable("id") Long id) { | ||||
|     public D get(@PathVariable("id") Long id) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return R.ok(baseService.get(id)); | ||||
|         return baseService.get(id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -124,9 +125,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Operation(summary = "新增数据", description = "新增数据") | ||||
|     @ResponseBody | ||||
|     @PostMapping | ||||
|     public R<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { | ||||
|     public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { | ||||
|         this.checkPermission(Api.ADD); | ||||
|         return R.ok("新增成功", baseService.add(req)); | ||||
|         return BaseIdResp.<Long>builder().id(baseService.add(req)).build(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -134,32 +135,28 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|      * | ||||
|      * @param req 修改信息 | ||||
|      * @param id  ID | ||||
|      * @return / | ||||
|      */ | ||||
|     @Operation(summary = "修改数据", description = "修改数据") | ||||
|     @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) | ||||
|     @ResponseBody | ||||
|     @PutMapping("/{id}") | ||||
|     public R<Void> update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) { | ||||
|     public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) { | ||||
|         this.checkPermission(Api.UPDATE); | ||||
|         baseService.update(req, id); | ||||
|         return R.ok("修改成功"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 删除 | ||||
|      * | ||||
|      * @param ids ID 列表 | ||||
|      * @return / | ||||
|      */ | ||||
|     @Operation(summary = "删除数据", description = "删除数据") | ||||
|     @Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH) | ||||
|     @ResponseBody | ||||
|     @DeleteMapping("/{ids}") | ||||
|     public R<Void> delete(@PathVariable("ids") List<Long> ids) { | ||||
|     public void delete(@PathVariable("ids") List<Long> ids) { | ||||
|         this.checkPermission(Api.DELETE); | ||||
|         baseService.delete(ids); | ||||
|         return R.ok("删除成功"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -169,6 +166,7 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|      * @param sortQuery 排序查询条件 | ||||
|      * @param response  响应对象 | ||||
|      */ | ||||
|     @ExcludeFromGracefulResponse | ||||
|     @Operation(summary = "导出数据", description = "导出数据") | ||||
|     @GetMapping("/export") | ||||
|     public void export(Q query, SortQuery sortQuery, HttpServletResponse response) { | ||||
|   | ||||
| @@ -19,6 +19,7 @@ package top.continew.starter.extension.crud.controller; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.lang.tree.Tree; | ||||
| import cn.hutool.core.text.CharSequenceUtil; | ||||
| import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| import io.swagger.v3.oas.annotations.enums.ParameterIn; | ||||
| @@ -31,11 +32,11 @@ import top.continew.starter.extension.crud.annotation.CrudRequestMapping; | ||||
| import top.continew.starter.extension.crud.enums.Api; | ||||
| import top.continew.starter.extension.crud.model.query.SortQuery; | ||||
| import top.continew.starter.extension.crud.model.req.BaseReq; | ||||
| import top.continew.starter.extension.crud.model.resp.BaseIdResp; | ||||
| import top.continew.starter.extension.crud.util.ValidateGroup; | ||||
| import top.continew.starter.extension.crud.model.query.PageQuery; | ||||
| import top.continew.starter.extension.crud.model.resp.PageResp; | ||||
| import top.continew.starter.extension.crud.service.BaseService; | ||||
| import top.continew.starter.web.model.R; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| @@ -65,9 +66,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Operation(summary = "分页查询列表", description = "分页查询列表") | ||||
|     @ResponseBody | ||||
|     @GetMapping | ||||
|     public R<PageResp<L>> page(Q query, @Validated PageQuery pageQuery) { | ||||
|     public PageResp<L> page(Q query, @Validated PageQuery pageQuery) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return R.ok(baseService.page(query, pageQuery)); | ||||
|         return baseService.page(query, pageQuery); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -80,9 +81,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Operation(summary = "查询树列表", description = "查询树列表") | ||||
|     @ResponseBody | ||||
|     @GetMapping("/tree") | ||||
|     public R<List<Tree<Long>>> tree(Q query, SortQuery sortQuery) { | ||||
|     public List<Tree<Long>> tree(Q query, SortQuery sortQuery) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return R.ok(baseService.tree(query, sortQuery, false)); | ||||
|         return baseService.tree(query, sortQuery, false); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -95,9 +96,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Operation(summary = "查询列表", description = "查询列表") | ||||
|     @ResponseBody | ||||
|     @GetMapping("/list") | ||||
|     public R<List<L>> list(Q query, SortQuery sortQuery) { | ||||
|     public List<L> list(Q query, SortQuery sortQuery) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return R.ok(baseService.list(query, sortQuery)); | ||||
|         return baseService.list(query, sortQuery); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -110,9 +111,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) | ||||
|     @ResponseBody | ||||
|     @GetMapping("/{id}") | ||||
|     public R<D> get(@PathVariable("id") Long id) { | ||||
|     public D get(@PathVariable("id") Long id) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return R.ok(baseService.get(id)); | ||||
|         return baseService.get(id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -124,9 +125,9 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|     @Operation(summary = "新增数据", description = "新增数据") | ||||
|     @ResponseBody | ||||
|     @PostMapping | ||||
|     public R<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { | ||||
|     public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { | ||||
|         this.checkPermission(Api.ADD); | ||||
|         return R.ok("新增成功", baseService.add(req)); | ||||
|         return BaseIdResp.<Long>builder().id(baseService.add(req)).build(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -134,32 +135,28 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|      * | ||||
|      * @param req 修改信息 | ||||
|      * @param id  ID | ||||
|      * @return / | ||||
|      */ | ||||
|     @Operation(summary = "修改数据", description = "修改数据") | ||||
|     @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) | ||||
|     @ResponseBody | ||||
|     @PutMapping("/{id}") | ||||
|     public R<Void> update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) { | ||||
|     public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) { | ||||
|         this.checkPermission(Api.UPDATE); | ||||
|         baseService.update(req, id); | ||||
|         return R.ok("修改成功"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 删除 | ||||
|      * | ||||
|      * @param ids ID 列表 | ||||
|      * @return / | ||||
|      */ | ||||
|     @Operation(summary = "删除数据", description = "删除数据") | ||||
|     @Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH) | ||||
|     @ResponseBody | ||||
|     @DeleteMapping("/{ids}") | ||||
|     public R<Void> delete(@PathVariable("ids") List<Long> ids) { | ||||
|     public void delete(@PathVariable("ids") List<Long> ids) { | ||||
|         this.checkPermission(Api.DELETE); | ||||
|         baseService.delete(ids); | ||||
|         return R.ok("删除成功"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -169,6 +166,7 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, | ||||
|      * @param sortQuery 排序查询条件 | ||||
|      * @param response  响应对象 | ||||
|      */ | ||||
|     @ExcludeFromGracefulResponse | ||||
|     @Operation(summary = "导出数据", description = "导出数据") | ||||
|     @GetMapping("/export") | ||||
|     public void export(Q query, SortQuery sortQuery, HttpServletResponse response) { | ||||
|   | ||||
| @@ -44,6 +44,12 @@ | ||||
|             <artifactId>tlog-web-spring-boot-starter</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- Graceful Response(一个Spring Boot技术栈下的优雅响应处理组件,可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程,提高开发效率,提高代码质量) --> | ||||
|         <dependency> | ||||
|             <groupId>com.feiniaojin</groupId> | ||||
|             <artifactId>graceful-response</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- API 文档模块 --> | ||||
|         <dependency> | ||||
|             <groupId>top.continew</groupId> | ||||
|   | ||||
| @@ -17,12 +17,12 @@ | ||||
| package top.continew.starter.web.annotation; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Import; | ||||
| import top.continew.starter.web.autoconfigure.exception.GlobalExceptionHandlerAutoConfiguration; | ||||
| import top.continew.starter.web.autoconfigure.response.GlobalResponseAutoConfiguration; | ||||
| 
 | ||||
| import java.lang.annotation.*; | ||||
| 
 | ||||
| /** | ||||
|  * 全局异常、错误处理器启用注解 | ||||
|  * 全局响应启用注解 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 1.2.0 | ||||
| @@ -30,6 +30,7 @@ import java.lang.annotation.*; | ||||
| @Target({ElementType.TYPE}) | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Documented | ||||
| @Import({GlobalExceptionHandlerAutoConfiguration.class}) | ||||
| public @interface EnableGlobalExceptionHandler { | ||||
| @Inherited | ||||
| @Import({GlobalResponseAutoConfiguration.class}) | ||||
| public @interface EnableGlobalResponse { | ||||
| } | ||||
| @@ -1,94 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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.exception; | ||||
|  | ||||
| import cn.hutool.core.bean.BeanUtil; | ||||
| import cn.hutool.json.JSONUtil; | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
| import jakarta.annotation.Resource; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.boot.autoconfigure.web.ServerProperties; | ||||
| import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; | ||||
| import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver; | ||||
| import org.springframework.boot.web.servlet.error.ErrorAttributes; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.http.ResponseEntity; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| import org.springframework.web.servlet.ModelAndView; | ||||
| import top.continew.starter.web.model.R; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| /** | ||||
|  * 全局错误处理器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @RestController | ||||
| public class GlobalErrorHandler extends BasicErrorController { | ||||
|  | ||||
|     private static final Logger log = LoggerFactory.getLogger(GlobalErrorHandler.class); | ||||
|  | ||||
|     @Resource | ||||
|     private ObjectMapper objectMapper; | ||||
|  | ||||
|     public GlobalErrorHandler(ErrorAttributes errorAttributes, | ||||
|                               ServerProperties serverProperties, | ||||
|                               List<ErrorViewResolver> errorViewResolvers) { | ||||
|         super(errorAttributes, serverProperties.getError(), errorViewResolvers); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { | ||||
|         Map<String, Object> errorAttributeMap = super.getErrorAttributes(request, super.getErrorAttributeOptions(request, MediaType.TEXT_HTML)); | ||||
|         String path = (String)errorAttributeMap.get("path"); | ||||
|         HttpStatus status = super.getStatus(request); | ||||
|         R<Object> result = R.fail(status.value(), (String)errorAttributeMap.get("error")); | ||||
|         result.setData(path); | ||||
|         try { | ||||
|             response.setStatus(HttpStatus.OK.value()); | ||||
|             response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||||
|             objectMapper.writeValue(response.getWriter(), result); | ||||
|         } catch (IOException e) { | ||||
|             log.error("请求地址 [{}],默认错误处理时发生 IO 异常。", path, e); | ||||
|         } | ||||
|         if (log.isErrorEnabled()) { | ||||
|             log.error("请求地址 [{}],发生错误,错误信息:{}。", path, JSONUtil.toJsonStr(errorAttributeMap)); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { | ||||
|         Map<String, Object> errorAttributeMap = super.getErrorAttributes(request, super.getErrorAttributeOptions(request, MediaType.ALL)); | ||||
|         String path = (String)errorAttributeMap.get("path"); | ||||
|         HttpStatus status = super.getStatus(request); | ||||
|         R<Object> result = R.fail(status.value(), (String)errorAttributeMap.get("error")); | ||||
|         result.setData(path); | ||||
|         if (log.isErrorEnabled()) { | ||||
|             log.error("请求地址 [{}],发生错误,错误信息:{}。", path, JSONUtil.toJsonStr(errorAttributeMap)); | ||||
|         } | ||||
|         return new ResponseEntity<>(BeanUtil.beanToMap(result), HttpStatus.OK); | ||||
|     } | ||||
| } | ||||
| @@ -1,199 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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.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; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.context.support.DefaultMessageSourceResolvable; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.validation.BindException; | ||||
| import org.springframework.web.HttpRequestMethodNotSupportedException; | ||||
| import org.springframework.web.bind.MethodArgumentNotValidException; | ||||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||||
| import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; | ||||
| 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.core.util.MessageSourceUtils; | ||||
|  | ||||
| /** | ||||
|  * 全局异常处理器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 1.1.0 | ||||
|  */ | ||||
| @RestControllerAdvice | ||||
| public class GlobalExceptionHandler { | ||||
|     private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); | ||||
|  | ||||
|     private static final String PARAM_FAILED = "请求地址 [{}],参数验证失败。"; | ||||
|     @Resource | ||||
|     private I18nProperties i18nProperties; | ||||
|  | ||||
|     /** | ||||
|      * 拦截自定义验证异常-错误请求 | ||||
|      */ | ||||
|     @ExceptionHandler(BadRequestException.class) | ||||
|     public R<Void> handleBadRequestException(BadRequestException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],自定义验证失败。", request.getRequestURI(), e); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-违反约束异常 | ||||
|      */ | ||||
|     @ExceptionHandler(ConstraintViolationException.class) | ||||
|     public R<Void> constraintViolationException(ConstraintViolationException e, HttpServletRequest request) { | ||||
|         log.warn(PARAM_FAILED, request.getRequestURI(), e); | ||||
|         String errorMsg = CollUtil.join(e | ||||
|             .getConstraintViolations(), StringConstants.CHINESE_COMMA, ConstraintViolation::getMessage); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-绑定异常 | ||||
|      */ | ||||
|     @ExceptionHandler(BindException.class) | ||||
|     public R<Void> handleBindException(BindException e, HttpServletRequest request) { | ||||
|         log.warn(PARAM_FAILED, request.getRequestURI(), e); | ||||
|         String errorMsg = CollUtil.join(e | ||||
|             .getAllErrors(), StringConstants.CHINESE_COMMA, DefaultMessageSourceResolvable::getDefaultMessage); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-方法参数无效异常 | ||||
|      */ | ||||
|     @ExceptionHandler(MethodArgumentNotValidException.class) | ||||
|     public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, | ||||
|                                                          HttpServletRequest request) { | ||||
|         log.warn(PARAM_FAILED, request.getRequestURI(), e); | ||||
|         String errorMsg = CollUtil.join(e | ||||
|             .getAllErrors(), StringConstants.CHINESE_COMMA, DefaultMessageSourceResolvable::getDefaultMessage); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-方法参数类型不匹配异常 | ||||
|      */ | ||||
|     @ExceptionHandler(MethodArgumentTypeMismatchException.class) | ||||
|     public R<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, | ||||
|                                                              HttpServletRequest request) { | ||||
|         String errorMsg = CharSequenceUtil.format("参数名:[{}],期望参数类型:[{}]", e.getName(), e.getParameter() | ||||
|             .getParameterType()); | ||||
|         log.warn("请求地址 [{}],参数转换失败,{}。", request.getRequestURI(), errorMsg, e); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截文件上传异常-超过上传大小限制 | ||||
|      */ | ||||
|     @ExceptionHandler(MultipartException.class) | ||||
|     public R<Void> handleRequestTooBigException(MultipartException e, HttpServletRequest request) { | ||||
|         String msg = e.getMessage(); | ||||
|         R<Void> defaultFail = R.fail(HttpStatus.BAD_REQUEST.value(), msg); | ||||
|         if (CharSequenceUtil.isBlank(msg)) { | ||||
|             return defaultFail; | ||||
|         } | ||||
|  | ||||
|         String sizeLimit; | ||||
|         Throwable cause = e.getCause(); | ||||
|         if (null != cause) { | ||||
|             msg = msg.concat(cause.getMessage().toLowerCase()); | ||||
|         } | ||||
|         if (msg.contains("size") && msg.contains("exceed")) { | ||||
|             sizeLimit = CharSequenceUtil.subBetween(msg, "the maximum size ", " for"); | ||||
|         } else if (msg.contains("larger than")) { | ||||
|             sizeLimit = CharSequenceUtil.subAfter(msg, "larger than ", true); | ||||
|         } else { | ||||
|             return defaultFail; | ||||
|         } | ||||
|  | ||||
|         String errorMsg = "请上传小于 %sKB 的文件".formatted(NumberUtil.parseLong(sizeLimit) / 1024); | ||||
|         log.warn("请求地址 [{}],上传文件失败,文件大小超过限制。", request.getRequestURI(), e); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-请求方式不支持异常 | ||||
|      */ | ||||
|     @ExceptionHandler(HttpRequestMethodNotSupportedException.class) | ||||
|     public R<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, | ||||
|                                                        HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],不支持 [{}] 请求。", request.getRequestURI(), e.getMethod()); | ||||
|         return R.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), e.getMessage()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截业务异常 | ||||
|      */ | ||||
|     @ExceptionHandler(BusinessException.class) | ||||
|     public R<Void> handleServiceException(BusinessException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生业务异常。", request.getRequestURI(), e); | ||||
|         return R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截全局应用异常 | ||||
|      */ | ||||
|     @ExceptionHandler(GlobalException.class) | ||||
|     public R<Void> 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); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截未知的运行时异常 | ||||
|      */ | ||||
|     @ExceptionHandler(RuntimeException.class) | ||||
|     public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生系统异常。", request.getRequestURI(), e); | ||||
|         return R.fail(e.getMessage()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截未知的系统异常 | ||||
|      */ | ||||
|     @ExceptionHandler(Throwable.class) | ||||
|     public R<Void> handleException(Throwable e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生未知异常。", request.getRequestURI(), e); | ||||
|         return R.fail(e.getMessage()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,47 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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.exception; | ||||
|  | ||||
| import jakarta.annotation.PostConstruct; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 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.Configuration; | ||||
| import org.springframework.context.annotation.Import; | ||||
| import top.continew.starter.web.autoconfigure.i18n.I18nProperties; | ||||
|  | ||||
| /** | ||||
|  * 全局异常处理器自动配置 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @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); | ||||
|  | ||||
|     @PostConstruct | ||||
|     public void postConstruct() { | ||||
|         log.debug("[ContiNew Starter] - Auto Configuration 'Web-Global Exception Handler' completed initialization."); | ||||
|     } | ||||
| } | ||||
| @@ -24,7 +24,6 @@ import org.springframework.format.FormatterRegistry; | ||||
| import org.springframework.http.converter.ByteArrayHttpMessageConverter; | ||||
| import org.springframework.http.converter.HttpMessageConverter; | ||||
| import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | ||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||||
| import org.springframework.web.servlet.config.annotation.EnableWebMvc; | ||||
| import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||||
|  | ||||
| @@ -44,8 +43,7 @@ public class WebMvcAutoConfiguration implements WebMvcConfigurer { | ||||
|     private static final Logger log = LoggerFactory.getLogger(WebMvcAutoConfiguration.class); | ||||
|     private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter; | ||||
|  | ||||
|     public WebMvcAutoConfiguration(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter, | ||||
|                                    ThreadPoolTaskExecutor threadPoolTaskExecutor) { | ||||
|     public WebMvcAutoConfiguration(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) { | ||||
|         this.mappingJackson2HttpMessageConverter = mappingJackson2HttpMessageConverter; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,150 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * 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 | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * 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.response; | ||||
|  | ||||
| import com.feiniaojin.gracefulresponse.ExceptionAliasRegister; | ||||
| import com.feiniaojin.gracefulresponse.advice.*; | ||||
| import com.feiniaojin.gracefulresponse.api.ResponseFactory; | ||||
| import com.feiniaojin.gracefulresponse.api.ResponseStatusFactory; | ||||
| import com.feiniaojin.gracefulresponse.defaults.DefaultResponseFactory; | ||||
| import com.feiniaojin.gracefulresponse.defaults.DefaultResponseStatusFactoryImpl; | ||||
| import jakarta.annotation.PostConstruct; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||
| import org.springframework.context.MessageSource; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.context.annotation.PropertySource; | ||||
| import org.springframework.context.support.ResourceBundleMessageSource; | ||||
| import top.continew.starter.core.constant.PropertiesConstants; | ||||
| import top.continew.starter.core.util.GeneralPropertySourceFactory; | ||||
|  | ||||
| import java.util.Locale; | ||||
|  | ||||
| /** | ||||
|  * 全局响应自动配置 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Configuration(proxyBeanMethods = false) | ||||
| @EnableConfigurationProperties(GlobalResponseProperties.class) | ||||
| @PropertySource(value = "classpath:default-web.yml", factory = GeneralPropertySourceFactory.class) | ||||
| public class GlobalResponseAutoConfiguration { | ||||
|  | ||||
|     private static final Logger log = LoggerFactory.getLogger(GlobalResponseAutoConfiguration.class); | ||||
|  | ||||
|     /** | ||||
|      * 全局异常处理 | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnMissingBean | ||||
|     public GrGlobalExceptionAdvice globalExceptionAdvice() { | ||||
|         return new GrGlobalExceptionAdvice(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 全局校验异常处理 | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnMissingBean | ||||
|     public GrValidationExceptionAdvice validationExceptionAdvice() { | ||||
|         return new GrValidationExceptionAdvice(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 全局响应体处理(非 void) | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnMissingBean | ||||
|     public GrNotVoidResponseBodyAdvice notVoidResponseBodyAdvice() { | ||||
|         return new GrNotVoidResponseBodyAdvice(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 全局响应体处理(void) | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnMissingBean | ||||
|     public GrVoidResponseBodyAdvice voidResponseBodyAdvice() { | ||||
|         return new GrVoidResponseBodyAdvice(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 响应工厂 | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnMissingBean | ||||
|     public ResponseFactory responseBeanFactory() { | ||||
|         return new DefaultResponseFactory(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 响应状态工厂 | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnMissingBean | ||||
|     public ResponseStatusFactory responseStatusFactory() { | ||||
|         return new DefaultResponseStatusFactoryImpl(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 异常别名注册 | ||||
|      */ | ||||
|     @Bean | ||||
|     public ExceptionAliasRegister exceptionAliasRegister() { | ||||
|         return new ExceptionAliasRegister(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 响应支持 | ||||
|      */ | ||||
|     @Bean | ||||
|     public AdviceSupport adviceSupport() { | ||||
|         return new AdviceSupport(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 国际化支持 | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnProperty(prefix = PropertiesConstants.WEB_RESPONSE, name = "i18n", havingValue = "true") | ||||
|     public GrI18nAdvice i18nAdvice() { | ||||
|         return new GrI18nAdvice(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 国际化配置 | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnProperty(prefix = PropertiesConstants.WEB_RESPONSE, name = "i18n", havingValue = "true") | ||||
|     public MessageSource messageSource() { | ||||
|         ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); | ||||
|         messageSource.setBasenames("i18n", "i18n/empty-messages"); | ||||
|         messageSource.setDefaultEncoding("UTF-8"); | ||||
|         messageSource.setDefaultLocale(Locale.CHINA); | ||||
|         return messageSource; | ||||
|     } | ||||
|  | ||||
|     @PostConstruct | ||||
|     public void postConstruct() { | ||||
|         log.debug("[ContiNew Starter] - Auto Configuration 'Web-Global Response' completed initialization."); | ||||
|     } | ||||
| } | ||||
| @@ -14,30 +14,18 @@ | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package top.continew.starter.web.autoconfigure.i18n; | ||||
| package top.continew.starter.web.autoconfigure.response; | ||||
| 
 | ||||
| import com.feiniaojin.gracefulresponse.GracefulResponseProperties; | ||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
| import top.continew.starter.core.constant.PropertiesConstants; | ||||
| 
 | ||||
| /** | ||||
|  * 国际化配置属性 | ||||
|  * 全局响应配置属性 | ||||
|  * | ||||
|  * @author Jasmine | ||||
|  * @since 2.2.0 | ||||
|  * @author Charles7c | ||||
|  * @since 2.5.0 | ||||
|  */ | ||||
| @ConfigurationProperties(PropertiesConstants.WEB_I18N) | ||||
| public class I18nProperties { | ||||
| 
 | ||||
|     /** | ||||
|      * 国际化开启 true-开启, false-关闭 | ||||
|      */ | ||||
|     private Boolean enabled; | ||||
| 
 | ||||
|     public Boolean getEnabled() { | ||||
|         return enabled; | ||||
|     } | ||||
| 
 | ||||
|     public void setEnabled(Boolean enabled) { | ||||
|         this.enabled = enabled; | ||||
|     } | ||||
| @ConfigurationProperties(PropertiesConstants.WEB_RESPONSE) | ||||
| public class GlobalResponseProperties extends GracefulResponseProperties { | ||||
| } | ||||
| @@ -16,12 +16,14 @@ | ||||
|  | ||||
| package top.continew.starter.web.model; | ||||
|  | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
| import com.fasterxml.jackson.annotation.JsonIgnore; | ||||
| import com.feiniaojin.gracefulresponse.data.Response; | ||||
| import com.feiniaojin.gracefulresponse.data.ResponseStatus; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import top.continew.starter.web.autoconfigure.response.GlobalResponseProperties; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.util.Collections; | ||||
|  | ||||
| /** | ||||
|  * 响应信息 | ||||
| @@ -30,13 +32,25 @@ import java.io.Serializable; | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Schema(description = "响应信息") | ||||
| public class R<T> implements Serializable { | ||||
| public class R implements Response { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|     private static final GlobalResponseProperties PROPERTIES = SpringUtil.getBean(GlobalResponseProperties.class); | ||||
|     private static final String DEFAULT_SUCCESS_CODE = PROPERTIES.getDefaultSuccessCode(); | ||||
|     private static final String DEFAULT_SUCCESS_MSG = PROPERTIES.getDefaultSuccessMsg(); | ||||
|     private static final String DEFAULT_ERROR_CODE = PROPERTIES.getDefaultErrorCode(); | ||||
|     private static final String DEFAULT_ERROR_MSG = PROPERTIES.getDefaultErrorMsg(); | ||||
|  | ||||
|     private static final int SUCCESS_CODE = HttpStatus.OK.value(); | ||||
|     private static final int FAIL_CODE = HttpStatus.INTERNAL_SERVER_ERROR.value(); | ||||
|     /** | ||||
|      * 状态码 | ||||
|      */ | ||||
|     @Schema(description = "状态码", example = "1") | ||||
|     private String code; | ||||
|  | ||||
|     /** | ||||
|      * 状态信息 | ||||
|      */ | ||||
|     @Schema(description = "状态信息", example = "操作成功") | ||||
|     private String msg; | ||||
|  | ||||
|     /** | ||||
|      * 是否成功 | ||||
| @@ -45,153 +59,60 @@ public class R<T> implements Serializable { | ||||
|     private boolean success; | ||||
|  | ||||
|     /** | ||||
|      * 业务状态码 | ||||
|      * 时间戳 | ||||
|      */ | ||||
|     @Schema(description = "业务状态码", example = "200") | ||||
|     private int code; | ||||
|  | ||||
|     /** | ||||
|      * 业务状态信息 | ||||
|      */ | ||||
|     @Schema(description = "业务状态信息", example = "操作成功") | ||||
|     private String msg; | ||||
|     @Schema(description = "时间戳", example = "1691453288000") | ||||
|     private final Long timestamp = System.currentTimeMillis(); | ||||
|  | ||||
|     /** | ||||
|      * 响应数据 | ||||
|      */ | ||||
|     @Schema(description = "响应数据") | ||||
|     private T data; | ||||
|     private Object data = Collections.emptyMap(); | ||||
|  | ||||
|     /** | ||||
|      * 时间戳 | ||||
|      */ | ||||
|     @Schema(description = "时间戳", example = "1691453288") | ||||
|     private long timestamp = DateUtil.currentSeconds(); | ||||
|  | ||||
|     private R() { | ||||
|     public R() { | ||||
|     } | ||||
|  | ||||
|     private R(boolean success, int code, String msg, T data) { | ||||
|         this.success = success; | ||||
|         this.code = code; | ||||
|         this.msg = msg; | ||||
|     public R(String code, String msg) { | ||||
|         this.setCode(code); | ||||
|         this.setMsg(msg); | ||||
|     } | ||||
|  | ||||
|     public R(String code, String msg, Object data) { | ||||
|         this(code, msg); | ||||
|         this.data = data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作成功 | ||||
|      * | ||||
|      * @param <T> 响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> ok() { | ||||
|         return new R<>(true, SUCCESS_CODE, "操作成功", null); | ||||
|     @Override | ||||
|     public void setStatus(ResponseStatus status) { | ||||
|         this.setCode(status.getCode()); | ||||
|         this.setMsg(status.getMsg()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作成功 | ||||
|      * | ||||
|      * @param data 响应数据 | ||||
|      * @param <T>  响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> ok(T data) { | ||||
|         return new R<>(true, SUCCESS_CODE, "操作成功", data); | ||||
|     @Override | ||||
|     @JsonIgnore | ||||
|     public ResponseStatus getStatus() { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作成功 | ||||
|      * | ||||
|      * @param msg 业务状态信息 | ||||
|      * @param <T> 响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> ok(String msg) { | ||||
|         return new R<>(true, SUCCESS_CODE, msg, null); | ||||
|     @Override | ||||
|     public void setPayload(Object payload) { | ||||
|         this.data = payload; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作成功 | ||||
|      * | ||||
|      * @param msg  业务状态信息 | ||||
|      * @param data 响应数据 | ||||
|      * @param <T>  响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> ok(String msg, T data) { | ||||
|         return new R<>(true, SUCCESS_CODE, msg, data); | ||||
|     @Override | ||||
|     @JsonIgnore | ||||
|     public Object getPayload() { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作失败 | ||||
|      * | ||||
|      * @param <T> 响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> fail() { | ||||
|         return new R<>(false, FAIL_CODE, "操作失败", null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作失败 | ||||
|      * | ||||
|      * @param msg 业务状态信息 | ||||
|      * @param <T> 响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> fail(String msg) { | ||||
|         return new R<>(false, FAIL_CODE, msg, null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作失败 | ||||
|      * | ||||
|      * @param data 响应数据 | ||||
|      * @param <T>  响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> fail(T data) { | ||||
|         return new R<>(false, FAIL_CODE, "操作失败", data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作失败 | ||||
|      * | ||||
|      * @param msg  业务状态信息 | ||||
|      * @param data 响应数据 | ||||
|      * @param <T>  响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> fail(String msg, T data) { | ||||
|         return new R<>(false, FAIL_CODE, msg, data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作失败 | ||||
|      * | ||||
|      * @param code 业务状态码 | ||||
|      * @param msg  业务状态信息 | ||||
|      * @param <T>  响应数据类型 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static <T> R<T> fail(int code, String msg) { | ||||
|         return new R<>(false, code, msg, null); | ||||
|     } | ||||
|  | ||||
|     public boolean isSuccess() { | ||||
|         return success; | ||||
|     } | ||||
|  | ||||
|     public void setSuccess(boolean success) { | ||||
|         this.success = success; | ||||
|     } | ||||
|  | ||||
|     public int getCode() { | ||||
|     public String getCode() { | ||||
|         return code; | ||||
|     } | ||||
|  | ||||
|     public void setCode(int code) { | ||||
|     public void setCode(String code) { | ||||
|         this.code = code; | ||||
|         this.success = DEFAULT_SUCCESS_CODE.equals(code); | ||||
|     } | ||||
|  | ||||
|     public String getMsg() { | ||||
| @@ -202,19 +123,73 @@ public class R<T> implements Serializable { | ||||
|         this.msg = msg; | ||||
|     } | ||||
|  | ||||
|     public T getData() { | ||||
|     public Object getData() { | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
|     public void setData(T data) { | ||||
|     public void setData(Object data) { | ||||
|         this.data = data; | ||||
|     } | ||||
|  | ||||
|     public long getTimestamp() { | ||||
|     public boolean isSuccess() { | ||||
|         return success; | ||||
|     } | ||||
|  | ||||
|     public void setSuccess(boolean success) { | ||||
|         this.success = success; | ||||
|     } | ||||
|  | ||||
|     public Long getTimestamp() { | ||||
|         return timestamp; | ||||
|     } | ||||
|  | ||||
|     public void setTimestamp(long timestamp) { | ||||
|         this.timestamp = timestamp; | ||||
|     /** | ||||
|      * 操作成功 | ||||
|      * | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static R ok() { | ||||
|         return new R(DEFAULT_SUCCESS_CODE, DEFAULT_SUCCESS_MSG); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作成功 | ||||
|      * | ||||
|      * @param data 响应数据 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static R ok(Object data) { | ||||
|         return new R(DEFAULT_SUCCESS_CODE, DEFAULT_SUCCESS_MSG, data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作成功 | ||||
|      * | ||||
|      * @param msg  业务状态信息 | ||||
|      * @param data 响应数据 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static R ok(String msg, Object data) { | ||||
|         return new R(DEFAULT_SUCCESS_CODE, msg, data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作失败 | ||||
|      * | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static R fail() { | ||||
|         return new R(DEFAULT_ERROR_CODE, DEFAULT_ERROR_MSG); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 操作失败 | ||||
|      * | ||||
|      * @param code 业务状态码 | ||||
|      * @param msg  业务状态信息 | ||||
|      * @return R / | ||||
|      */ | ||||
|     public static R fail(String code, String msg) { | ||||
|         return new R(code, msg); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										23
									
								
								continew-starter-web/src/main/resources/default-web.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								continew-starter-web/src/main/resources/default-web.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| --- ### 响应配置 | ||||
| continew-starter.web.response: | ||||
|   # 是否开启国际化(默认:false) | ||||
|   i18n: false | ||||
|   # 响应类全名(配置后 response-style 将不再生效) | ||||
|   response-class-full-name: top.continew.starter.web.model.R | ||||
|   # 自定义成功响应码(默认:0) | ||||
|   default-success-code: 0 | ||||
|   # 自定义成功提示(默认:ok) | ||||
|   default-success-msg: ok | ||||
|   # 自定义失败响应码(默认:1) | ||||
|   default-error-code: 1 | ||||
|   # 自定义失败提示(默认:error) | ||||
|   default-error-msg: error | ||||
|   # 是否打印异常日志(默认:false) | ||||
|   print-exception-in-global-advice: true | ||||
|   # 是否将原生异常错误信息填充到状态信息中(默认:false) | ||||
|   origin-exception-using-detail-message: true | ||||
|   # 例外包路径(支持数字, * 和 ** 通配符匹配),该包路径下的 Controller 将被忽略处理 | ||||
|   exclude-packages: | ||||
|     - io.swagger.** | ||||
|     - org.springdoc.** | ||||
|     - org.springframework.boot.actuate.* | ||||
		Reference in New Issue
	
	Block a user