From 0b41f2d10c5dfb309585b36bde7008cdff6c912a Mon Sep 17 00:00:00 2001 From: Charles7c Date: Tue, 6 Aug 2024 23:54:06 +0800 Subject: [PATCH] =?UTF-8?q?refactor(web):=20=E9=87=8D=E6=9E=84=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E5=93=8D=E5=BA=94=E5=A4=84=E7=90=86=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 引入 Graceful Response(一个Spring Boot技术栈下的优雅响应处理组件,可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程,提高开发效率,提高代码质量) --- .../SaTokenAutoConfiguration.java | 1 - .../GlobalSaTokenExceptionHandler.java | 72 ----- .../core/constant/PropertiesConstants.java | 10 +- .../core/exception/GlobalException.java | 43 --- .../core/exception/GlobalResultInfoEnum.java | 66 ----- .../core/exception/ResultInfoInterface.java | 49 ---- continew-starter-dependencies/pom.xml | 10 +- .../extension/crud/model/resp/BaseIdResp.java | 68 +++++ .../crud/controller/BaseController.java | 36 ++- .../crud/controller/BaseController.java | 32 ++- continew-starter-web/pom.xml | 6 + ...Handler.java => EnableGlobalResponse.java} | 9 +- .../exception/GlobalErrorHandler.java | 94 ------- .../exception/GlobalExceptionHandler.java | 199 -------------- ...obalExceptionHandlerAutoConfiguration.java | 47 ---- .../mvc/WebMvcAutoConfiguration.java | 4 +- .../GlobalResponseAutoConfiguration.java | 150 +++++++++++ .../GlobalResponseProperties.java} | 26 +- .../top/continew/starter/web/model/R.java | 251 ++++++++---------- .../src/main/resources/default-web.yml | 23 ++ 20 files changed, 419 insertions(+), 777 deletions(-) delete mode 100644 continew-starter-auth/continew-starter-auth-satoken/src/main/java/top/continew/starter/auth/satoken/exception/GlobalSaTokenExceptionHandler.java delete mode 100644 continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalException.java delete mode 100644 continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalResultInfoEnum.java delete mode 100644 continew-starter-core/src/main/java/top/continew/starter/core/exception/ResultInfoInterface.java create mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/model/resp/BaseIdResp.java rename continew-starter-web/src/main/java/top/continew/starter/web/annotation/{EnableGlobalExceptionHandler.java => EnableGlobalResponse.java} (78%) delete mode 100644 continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalErrorHandler.java delete mode 100644 continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java delete mode 100644 continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandlerAutoConfiguration.java create mode 100644 continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/response/GlobalResponseAutoConfiguration.java rename continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/{i18n/I18nProperties.java => response/GlobalResponseProperties.java} (63%) create mode 100644 continew-starter-web/src/main/resources/default-web.yml diff --git a/continew-starter-auth/continew-starter-auth-satoken/src/main/java/top/continew/starter/auth/satoken/autoconfigure/SaTokenAutoConfiguration.java b/continew-starter-auth/continew-starter-auth-satoken/src/main/java/top/continew/starter/auth/satoken/autoconfigure/SaTokenAutoConfiguration.java index aae34dca..5a58924d 100644 --- a/continew-starter-auth/continew-starter-auth-satoken/src/main/java/top/continew/starter/auth/satoken/autoconfigure/SaTokenAutoConfiguration.java +++ b/continew-starter-auth/continew-starter-auth-satoken/src/main/java/top/continew/starter/auth/satoken/autoconfigure/SaTokenAutoConfiguration.java @@ -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) diff --git a/continew-starter-auth/continew-starter-auth-satoken/src/main/java/top/continew/starter/auth/satoken/exception/GlobalSaTokenExceptionHandler.java b/continew-starter-auth/continew-starter-auth-satoken/src/main/java/top/continew/starter/auth/satoken/exception/GlobalSaTokenExceptionHandler.java deleted file mode 100644 index 00eb22b9..00000000 --- a/continew-starter-auth/continew-starter-auth-satoken/src/main/java/top/continew/starter/auth/satoken/exception/GlobalSaTokenExceptionHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.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 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 handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { - log.error("请求地址 [{}],权限码校验失败。", request.getRequestURI(), e); - return R.fail(HttpStatus.FORBIDDEN.value(), "没有访问权限,请联系管理员授权"); - } - - /** - * 认证异常-角色认证 - */ - @ExceptionHandler(NotRoleException.class) - public R handleNotRoleException(NotRoleException e, HttpServletRequest request) { - log.error("请求地址 [{}],角色权限校验失败。", request.getRequestURI(), e); - return R.fail(HttpStatus.FORBIDDEN.value(), "没有访问权限,请联系管理员授权"); - } -} \ No newline at end of file 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 0d0f942a..e297b018 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 @@ -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"; - /** * 日志配置 */ 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 deleted file mode 100644 index 9193bb9e..00000000 --- a/continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalException.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 deleted file mode 100644 index 266d5d38..00000000 --- a/continew-starter-core/src/main/java/top/continew/starter/core/exception/GlobalResultInfoEnum.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 deleted file mode 100644 index d943c4db..00000000 --- a/continew-starter-core/src/main/java/top/continew/starter/core/exception/ResultInfoInterface.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index 1b9de7ee..01a03148 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -43,7 +43,7 @@ - 2.4.0 + 2.5.0-SNAPSHOT 1.1.0 1.38.0 1.16.6 @@ -61,6 +61,7 @@ 15.4 2.2.0 1.12.761 + 4.0.1-boot3 2.9.0 4.5.0 1.5.2 @@ -261,6 +262,13 @@ ${aws-s3.version} + + + com.feiniaojin + graceful-response + ${graceful-response.version} + + cn.crane4j diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/model/resp/BaseIdResp.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/model/resp/BaseIdResp.java new file mode 100644 index 00000000..04a856fe --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/model/resp/BaseIdResp.java @@ -0,0 +1,68 @@ +/* + * 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.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 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 BaseIdRespBuilder builder() { + return new BaseIdRespBuilder(); + } + + public static class BaseIdRespBuilder { + private T id; + + BaseIdRespBuilder() { + } + + public BaseIdRespBuilder id(final T id) { + this.id = id; + return this; + } + + public BaseIdResp build() { + return new BaseIdResp(this.id); + } + } +} diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java index b2c66ca5..0aaa0916 100644 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java @@ -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, L, D, Q, @Operation(summary = "分页查询列表", description = "分页查询列表") @ResponseBody @GetMapping - public R> page(Q query, @Validated PageQuery pageQuery) { + public PageResp 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, L, D, Q, @Operation(summary = "查询树列表", description = "查询树列表") @ResponseBody @GetMapping("/tree") - public R>> tree(Q query, SortQuery sortQuery) { + public List> 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, L, D, Q, @Operation(summary = "查询列表", description = "查询列表") @ResponseBody @GetMapping("/list") - public R> list(Q query, SortQuery sortQuery) { + public List 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, L, D, Q, @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) @ResponseBody @GetMapping("/{id}") - public R 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, L, D, Q, @Operation(summary = "新增数据", description = "新增数据") @ResponseBody @PostMapping - public R add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { + public BaseIdResp add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { this.checkPermission(Api.ADD); - return R.ok("新增成功", baseService.add(req)); + return BaseIdResp.builder().id(baseService.add(req)).build(); } /** @@ -134,32 +135,28 @@ public abstract class BaseController, 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 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 delete(@PathVariable("ids") List ids) { + public void delete(@PathVariable("ids") List ids) { this.checkPermission(Api.DELETE); baseService.delete(ids); - return R.ok("删除成功"); } /** @@ -169,6 +166,7 @@ public abstract class BaseController, L, D, Q, * @param sortQuery 排序查询条件 * @param response 响应对象 */ + @ExcludeFromGracefulResponse @Operation(summary = "导出数据", description = "导出数据") @GetMapping("/export") public void export(Q query, SortQuery sortQuery, HttpServletResponse response) { diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java index 6fc31e13..cc626cd6 100644 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java @@ -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, L, D, Q, @Operation(summary = "分页查询列表", description = "分页查询列表") @ResponseBody @GetMapping - public R> page(Q query, @Validated PageQuery pageQuery) { + public PageResp 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, L, D, Q, @Operation(summary = "查询树列表", description = "查询树列表") @ResponseBody @GetMapping("/tree") - public R>> tree(Q query, SortQuery sortQuery) { + public List> 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, L, D, Q, @Operation(summary = "查询列表", description = "查询列表") @ResponseBody @GetMapping("/list") - public R> list(Q query, SortQuery sortQuery) { + public List 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, L, D, Q, @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) @ResponseBody @GetMapping("/{id}") - public R 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, L, D, Q, @Operation(summary = "新增数据", description = "新增数据") @ResponseBody @PostMapping - public R add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { + public BaseIdResp add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { this.checkPermission(Api.ADD); - return R.ok("新增成功", baseService.add(req)); + return BaseIdResp.builder().id(baseService.add(req)).build(); } /** @@ -134,32 +135,28 @@ public abstract class BaseController, 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 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 delete(@PathVariable("ids") List ids) { + public void delete(@PathVariable("ids") List ids) { this.checkPermission(Api.DELETE); baseService.delete(ids); - return R.ok("删除成功"); } /** @@ -169,6 +166,7 @@ public abstract class BaseController, L, D, Q, * @param sortQuery 排序查询条件 * @param response 响应对象 */ + @ExcludeFromGracefulResponse @Operation(summary = "导出数据", description = "导出数据") @GetMapping("/export") public void export(Q query, SortQuery sortQuery, HttpServletResponse response) { diff --git a/continew-starter-web/pom.xml b/continew-starter-web/pom.xml index 3a97f379..da290899 100644 --- a/continew-starter-web/pom.xml +++ b/continew-starter-web/pom.xml @@ -44,6 +44,12 @@ tlog-web-spring-boot-starter + + + com.feiniaojin + graceful-response + + top.continew diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/annotation/EnableGlobalExceptionHandler.java b/continew-starter-web/src/main/java/top/continew/starter/web/annotation/EnableGlobalResponse.java similarity index 78% rename from continew-starter-web/src/main/java/top/continew/starter/web/annotation/EnableGlobalExceptionHandler.java rename to continew-starter-web/src/main/java/top/continew/starter/web/annotation/EnableGlobalResponse.java index 78371cbc..6dad596b 100644 --- a/continew-starter-web/src/main/java/top/continew/starter/web/annotation/EnableGlobalExceptionHandler.java +++ b/continew-starter-web/src/main/java/top/continew/starter/web/annotation/EnableGlobalResponse.java @@ -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 { } diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalErrorHandler.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalErrorHandler.java deleted file mode 100644 index 6a1b0ffc..00000000 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalErrorHandler.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.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 errorViewResolvers) { - super(errorAttributes, serverProperties.getError(), errorViewResolvers); - } - - @Override - public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { - Map errorAttributeMap = super.getErrorAttributes(request, super.getErrorAttributeOptions(request, MediaType.TEXT_HTML)); - String path = (String)errorAttributeMap.get("path"); - HttpStatus status = super.getStatus(request); - R 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> error(HttpServletRequest request) { - Map errorAttributeMap = super.getErrorAttributes(request, super.getErrorAttributeOptions(request, MediaType.ALL)); - String path = (String)errorAttributeMap.get("path"); - HttpStatus status = super.getStatus(request); - R 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); - } -} 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 deleted file mode 100644 index 2a2b05a0..00000000 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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.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 handleBadRequestException(BadRequestException e, HttpServletRequest request) { - log.warn("请求地址 [{}],自定义验证失败。", request.getRequestURI(), e); - return R.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage()); - } - - /** - * 拦截校验异常-违反约束异常 - */ - @ExceptionHandler(ConstraintViolationException.class) - public R 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 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 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 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 handleRequestTooBigException(MultipartException e, HttpServletRequest request) { - String msg = e.getMessage(); - R 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 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 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 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 handleRuntimeException(RuntimeException e, HttpServletRequest request) { - log.error("请求地址 [{}],发生系统异常。", request.getRequestURI(), e); - return R.fail(e.getMessage()); - } - - /** - * 拦截未知的系统异常 - */ - @ExceptionHandler(Throwable.class) - public R handleException(Throwable e, HttpServletRequest request) { - log.error("请求地址 [{}],发生未知异常。", request.getRequestURI(), e); - return R.fail(e.getMessage()); - } - -} \ No newline at end of file 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 deleted file mode 100644 index 8b08a109..00000000 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandlerAutoConfiguration.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.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."); - } -} diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/mvc/WebMvcAutoConfiguration.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/mvc/WebMvcAutoConfiguration.java index e83cc04f..ad1e434f 100644 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/mvc/WebMvcAutoConfiguration.java +++ b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/mvc/WebMvcAutoConfiguration.java @@ -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; } diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/response/GlobalResponseAutoConfiguration.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/response/GlobalResponseAutoConfiguration.java new file mode 100644 index 00000000..c04fb94a --- /dev/null +++ b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/response/GlobalResponseAutoConfiguration.java @@ -0,0 +1,150 @@ +/* + * 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.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."); + } +} 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/response/GlobalResponseProperties.java similarity index 63% rename from continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/i18n/I18nProperties.java rename to continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/response/GlobalResponseProperties.java index ca35baf2..89024d18 100644 --- 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/response/GlobalResponseProperties.java @@ -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 { } diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/model/R.java b/continew-starter-web/src/main/java/top/continew/starter/web/model/R.java index 55903429..30b64b59 100644 --- a/continew-starter-web/src/main/java/top/continew/starter/web/model/R.java +++ b/continew-starter-web/src/main/java/top/continew/starter/web/model/R.java @@ -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 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 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 响应数据类型 - * @return R / - */ - public static R 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 响应数据类型 - * @return R / - */ - public static R ok(T data) { - return new R<>(true, SUCCESS_CODE, "操作成功", data); + @Override + @JsonIgnore + public ResponseStatus getStatus() { + return null; } - /** - * 操作成功 - * - * @param msg 业务状态信息 - * @param 响应数据类型 - * @return R / - */ - public static R 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 响应数据类型 - * @return R / - */ - public static R ok(String msg, T data) { - return new R<>(true, SUCCESS_CODE, msg, data); + @Override + @JsonIgnore + public Object getPayload() { + return null; } - /** - * 操作失败 - * - * @param 响应数据类型 - * @return R / - */ - public static R fail() { - return new R<>(false, FAIL_CODE, "操作失败", null); - } - - /** - * 操作失败 - * - * @param msg 业务状态信息 - * @param 响应数据类型 - * @return R / - */ - public static R fail(String msg) { - return new R<>(false, FAIL_CODE, msg, null); - } - - /** - * 操作失败 - * - * @param data 响应数据 - * @param 响应数据类型 - * @return R / - */ - public static R fail(T data) { - return new R<>(false, FAIL_CODE, "操作失败", data); - } - - /** - * 操作失败 - * - * @param msg 业务状态信息 - * @param data 响应数据 - * @param 响应数据类型 - * @return R / - */ - public static R fail(String msg, T data) { - return new R<>(false, FAIL_CODE, msg, data); - } - - /** - * 操作失败 - * - * @param code 业务状态码 - * @param msg 业务状态信息 - * @param 响应数据类型 - * @return R / - */ - public static R 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 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); + } +} \ No newline at end of file diff --git a/continew-starter-web/src/main/resources/default-web.yml b/continew-starter-web/src/main/resources/default-web.yml new file mode 100644 index 00000000..111325ce --- /dev/null +++ b/continew-starter-web/src/main/resources/default-web.yml @@ -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.* \ No newline at end of file