mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-11 06:57:14 +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
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@ComponentScan("top.continew.starter.auth.satoken.exception")
|
|
||||||
@EnableConfigurationProperties(SaTokenExtensionProperties.class)
|
@EnableConfigurationProperties(SaTokenExtensionProperties.class)
|
||||||
@ConditionalOnProperty(prefix = "sa-token.extension", name = PropertiesConstants.ENABLED, havingValue = "true")
|
@ConditionalOnProperty(prefix = "sa-token.extension", name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||||
@PropertySource(value = "classpath:default-auth-satoken.yml", factory = GeneralPropertySourceFactory.class)
|
@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_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_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>
|
<properties>
|
||||||
<!-- 项目版本号 -->
|
<!-- 项目版本号 -->
|
||||||
<revision>2.4.0</revision>
|
<revision>2.5.0-SNAPSHOT</revision>
|
||||||
<snail-job.version>1.1.0</snail-job.version>
|
<snail-job.version>1.1.0</snail-job.version>
|
||||||
<sa-token.version>1.38.0</sa-token.version>
|
<sa-token.version>1.38.0</sa-token.version>
|
||||||
<just-auth.version>1.16.6</just-auth.version>
|
<just-auth.version>1.16.6</just-auth.version>
|
||||||
@@ -61,6 +61,7 @@
|
|||||||
<nashorn.version>15.4</nashorn.version>
|
<nashorn.version>15.4</nashorn.version>
|
||||||
<x-file-storage.version>2.2.0</x-file-storage.version>
|
<x-file-storage.version>2.2.0</x-file-storage.version>
|
||||||
<aws-s3.version>1.12.761</aws-s3.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>
|
<crane4j.version>2.9.0</crane4j.version>
|
||||||
<knife4j.version>4.5.0</knife4j.version>
|
<knife4j.version>4.5.0</knife4j.version>
|
||||||
<tlog.version>1.5.2</tlog.version>
|
<tlog.version>1.5.2</tlog.version>
|
||||||
@@ -261,6 +262,13 @@
|
|||||||
<version>${aws-s3.version}</version>
|
<version>${aws-s3.version}</version>
|
||||||
</dependency>
|
</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” 这类需求的字段填充框架) -->
|
<!-- Crane4j(一个基于注解的,用于完成一切 “根据 A 的 key 值拿到 B,再把 B 的属性映射到 A” 这类需求的字段填充框架) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.crane4j</groupId>
|
<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.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.lang.tree.Tree;
|
import cn.hutool.core.lang.tree.Tree;
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
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.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
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.core.constant.StringConstants;
|
||||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||||
import top.continew.starter.extension.crud.enums.Api;
|
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.query.SortQuery;
|
||||||
import top.continew.starter.extension.crud.model.req.BaseReq;
|
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.service.BaseService;
|
||||||
import top.continew.starter.extension.crud.util.ValidateGroup;
|
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;
|
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 = "分页查询列表")
|
@Operation(summary = "分页查询列表", description = "分页查询列表")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public R<PageResp<L>> page(Q query, @Validated PageQuery pageQuery) {
|
public PageResp<L> page(Q query, @Validated PageQuery pageQuery) {
|
||||||
this.checkPermission(Api.LIST);
|
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 = "查询树列表")
|
@Operation(summary = "查询树列表", description = "查询树列表")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@GetMapping("/tree")
|
@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);
|
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 = "查询列表")
|
@Operation(summary = "查询列表", description = "查询列表")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public R<List<L>> list(Q query, SortQuery sortQuery) {
|
public List<L> list(Q query, SortQuery sortQuery) {
|
||||||
this.checkPermission(Api.LIST);
|
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)
|
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public R<D> get(@PathVariable("id") Long id) {
|
public D get(@PathVariable("id") Long id) {
|
||||||
this.checkPermission(Api.LIST);
|
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 = "新增数据")
|
@Operation(summary = "新增数据", description = "新增数据")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@PostMapping
|
@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);
|
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 req 修改信息
|
||||||
* @param id ID
|
* @param id ID
|
||||||
* @return /
|
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "修改数据", description = "修改数据")
|
@Operation(summary = "修改数据", description = "修改数据")
|
||||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@PutMapping("/{id}")
|
@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);
|
this.checkPermission(Api.UPDATE);
|
||||||
baseService.update(req, id);
|
baseService.update(req, id);
|
||||||
return R.ok("修改成功");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除
|
* 删除
|
||||||
*
|
*
|
||||||
* @param ids ID 列表
|
* @param ids ID 列表
|
||||||
* @return /
|
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "删除数据", description = "删除数据")
|
@Operation(summary = "删除数据", description = "删除数据")
|
||||||
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
|
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@DeleteMapping("/{ids}")
|
@DeleteMapping("/{ids}")
|
||||||
public R<Void> delete(@PathVariable("ids") List<Long> ids) {
|
public void delete(@PathVariable("ids") List<Long> ids) {
|
||||||
this.checkPermission(Api.DELETE);
|
this.checkPermission(Api.DELETE);
|
||||||
baseService.delete(ids);
|
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 sortQuery 排序查询条件
|
||||||
* @param response 响应对象
|
* @param response 响应对象
|
||||||
*/
|
*/
|
||||||
|
@ExcludeFromGracefulResponse
|
||||||
@Operation(summary = "导出数据", description = "导出数据")
|
@Operation(summary = "导出数据", description = "导出数据")
|
||||||
@GetMapping("/export")
|
@GetMapping("/export")
|
||||||
public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
|
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.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.lang.tree.Tree;
|
import cn.hutool.core.lang.tree.Tree;
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
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.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
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.enums.Api;
|
||||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
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.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.util.ValidateGroup;
|
||||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||||
import top.continew.starter.extension.crud.service.BaseService;
|
import top.continew.starter.extension.crud.service.BaseService;
|
||||||
import top.continew.starter.web.model.R;
|
|
||||||
|
|
||||||
import java.util.List;
|
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 = "分页查询列表")
|
@Operation(summary = "分页查询列表", description = "分页查询列表")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public R<PageResp<L>> page(Q query, @Validated PageQuery pageQuery) {
|
public PageResp<L> page(Q query, @Validated PageQuery pageQuery) {
|
||||||
this.checkPermission(Api.LIST);
|
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 = "查询树列表")
|
@Operation(summary = "查询树列表", description = "查询树列表")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@GetMapping("/tree")
|
@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);
|
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 = "查询列表")
|
@Operation(summary = "查询列表", description = "查询列表")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public R<List<L>> list(Q query, SortQuery sortQuery) {
|
public List<L> list(Q query, SortQuery sortQuery) {
|
||||||
this.checkPermission(Api.LIST);
|
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)
|
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public R<D> get(@PathVariable("id") Long id) {
|
public D get(@PathVariable("id") Long id) {
|
||||||
this.checkPermission(Api.LIST);
|
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 = "新增数据")
|
@Operation(summary = "新增数据", description = "新增数据")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@PostMapping
|
@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);
|
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 req 修改信息
|
||||||
* @param id ID
|
* @param id ID
|
||||||
* @return /
|
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "修改数据", description = "修改数据")
|
@Operation(summary = "修改数据", description = "修改数据")
|
||||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@PutMapping("/{id}")
|
@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);
|
this.checkPermission(Api.UPDATE);
|
||||||
baseService.update(req, id);
|
baseService.update(req, id);
|
||||||
return R.ok("修改成功");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除
|
* 删除
|
||||||
*
|
*
|
||||||
* @param ids ID 列表
|
* @param ids ID 列表
|
||||||
* @return /
|
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "删除数据", description = "删除数据")
|
@Operation(summary = "删除数据", description = "删除数据")
|
||||||
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
|
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@DeleteMapping("/{ids}")
|
@DeleteMapping("/{ids}")
|
||||||
public R<Void> delete(@PathVariable("ids") List<Long> ids) {
|
public void delete(@PathVariable("ids") List<Long> ids) {
|
||||||
this.checkPermission(Api.DELETE);
|
this.checkPermission(Api.DELETE);
|
||||||
baseService.delete(ids);
|
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 sortQuery 排序查询条件
|
||||||
* @param response 响应对象
|
* @param response 响应对象
|
||||||
*/
|
*/
|
||||||
|
@ExcludeFromGracefulResponse
|
||||||
@Operation(summary = "导出数据", description = "导出数据")
|
@Operation(summary = "导出数据", description = "导出数据")
|
||||||
@GetMapping("/export")
|
@GetMapping("/export")
|
||||||
public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
|
public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
|
||||||
|
@@ -44,6 +44,12 @@
|
|||||||
<artifactId>tlog-web-spring-boot-starter</artifactId>
|
<artifactId>tlog-web-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Graceful Response(一个Spring Boot技术栈下的优雅响应处理组件,可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程,提高开发效率,提高代码质量) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.feiniaojin</groupId>
|
||||||
|
<artifactId>graceful-response</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- API 文档模块 -->
|
<!-- API 文档模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.continew</groupId>
|
<groupId>top.continew</groupId>
|
||||||
|
@@ -17,12 +17,12 @@
|
|||||||
package top.continew.starter.web.annotation;
|
package top.continew.starter.web.annotation;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Import;
|
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.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局异常、错误处理器启用注解
|
* 全局响应启用注解
|
||||||
*
|
*
|
||||||
* @author Charles7c
|
* @author Charles7c
|
||||||
* @since 1.2.0
|
* @since 1.2.0
|
||||||
@@ -30,6 +30,7 @@ import java.lang.annotation.*;
|
|||||||
@Target({ElementType.TYPE})
|
@Target({ElementType.TYPE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@Import({GlobalExceptionHandlerAutoConfiguration.class})
|
@Inherited
|
||||||
public @interface EnableGlobalExceptionHandler {
|
@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.ByteArrayHttpMessageConverter;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
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.EnableWebMvc;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
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 static final Logger log = LoggerFactory.getLogger(WebMvcAutoConfiguration.class);
|
||||||
private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;
|
private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;
|
||||||
|
|
||||||
public WebMvcAutoConfiguration(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter,
|
public WebMvcAutoConfiguration(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
|
||||||
ThreadPoolTaskExecutor threadPoolTaskExecutor) {
|
|
||||||
this.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.
|
* 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 org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import top.continew.starter.core.constant.PropertiesConstants;
|
import top.continew.starter.core.constant.PropertiesConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际化配置属性
|
* 全局响应配置属性
|
||||||
*
|
*
|
||||||
* @author Jasmine
|
* @author Charles7c
|
||||||
* @since 2.2.0
|
* @since 2.5.0
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(PropertiesConstants.WEB_I18N)
|
@ConfigurationProperties(PropertiesConstants.WEB_RESPONSE)
|
||||||
public class I18nProperties {
|
public class GlobalResponseProperties extends GracefulResponseProperties {
|
||||||
|
|
||||||
/**
|
|
||||||
* 国际化开启 true-开启, false-关闭
|
|
||||||
*/
|
|
||||||
private Boolean enabled;
|
|
||||||
|
|
||||||
public Boolean getEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(Boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
package top.continew.starter.web.model;
|
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 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.util.Collections;
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 响应信息
|
* 响应信息
|
||||||
@@ -30,13 +32,25 @@ import java.io.Serializable;
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Schema(description = "响应信息")
|
@Schema(description = "响应信息")
|
||||||
public class R<T> implements Serializable {
|
public class R implements Response {
|
||||||
|
|
||||||
@Serial
|
private static final GlobalResponseProperties PROPERTIES = SpringUtil.getBean(GlobalResponseProperties.class);
|
||||||
private static final long serialVersionUID = 1L;
|
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;
|
private boolean success;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务状态码
|
* 时间戳
|
||||||
*/
|
*/
|
||||||
@Schema(description = "业务状态码", example = "200")
|
@Schema(description = "时间戳", example = "1691453288000")
|
||||||
private int code;
|
private final Long timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
/**
|
|
||||||
* 业务状态信息
|
|
||||||
*/
|
|
||||||
@Schema(description = "业务状态信息", example = "操作成功")
|
|
||||||
private String msg;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 响应数据
|
* 响应数据
|
||||||
*/
|
*/
|
||||||
@Schema(description = "响应数据")
|
@Schema(description = "响应数据")
|
||||||
private T data;
|
private Object data = Collections.emptyMap();
|
||||||
|
|
||||||
/**
|
public R() {
|
||||||
* 时间戳
|
|
||||||
*/
|
|
||||||
@Schema(description = "时间戳", example = "1691453288")
|
|
||||||
private long timestamp = DateUtil.currentSeconds();
|
|
||||||
|
|
||||||
private R() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private R(boolean success, int code, String msg, T data) {
|
public R(String code, String msg) {
|
||||||
this.success = success;
|
this.setCode(code);
|
||||||
this.code = code;
|
this.setMsg(msg);
|
||||||
this.msg = msg;
|
}
|
||||||
|
|
||||||
|
public R(String code, String msg, Object data) {
|
||||||
|
this(code, msg);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 操作成功
|
public void setStatus(ResponseStatus status) {
|
||||||
*
|
this.setCode(status.getCode());
|
||||||
* @param <T> 响应数据类型
|
this.setMsg(status.getMsg());
|
||||||
* @return R /
|
|
||||||
*/
|
|
||||||
public static <T> R<T> ok() {
|
|
||||||
return new R<>(true, SUCCESS_CODE, "操作成功", null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 操作成功
|
@JsonIgnore
|
||||||
*
|
public ResponseStatus getStatus() {
|
||||||
* @param data 响应数据
|
return null;
|
||||||
* @param <T> 响应数据类型
|
|
||||||
* @return R /
|
|
||||||
*/
|
|
||||||
public static <T> R<T> ok(T data) {
|
|
||||||
return new R<>(true, SUCCESS_CODE, "操作成功", data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 操作成功
|
public void setPayload(Object payload) {
|
||||||
*
|
this.data = payload;
|
||||||
* @param msg 业务状态信息
|
|
||||||
* @param <T> 响应数据类型
|
|
||||||
* @return R /
|
|
||||||
*/
|
|
||||||
public static <T> R<T> ok(String msg) {
|
|
||||||
return new R<>(true, SUCCESS_CODE, msg, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 操作成功
|
@JsonIgnore
|
||||||
*
|
public Object getPayload() {
|
||||||
* @param msg 业务状态信息
|
return null;
|
||||||
* @param data 响应数据
|
|
||||||
* @param <T> 响应数据类型
|
|
||||||
* @return R /
|
|
||||||
*/
|
|
||||||
public static <T> R<T> ok(String msg, T data) {
|
|
||||||
return new R<>(true, SUCCESS_CODE, msg, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public String getCode() {
|
||||||
* 操作失败
|
|
||||||
*
|
|
||||||
* @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() {
|
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCode(int code) {
|
public void setCode(String code) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
|
this.success = DEFAULT_SUCCESS_CODE.equals(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMsg() {
|
public String getMsg() {
|
||||||
@@ -202,19 +123,73 @@ public class R<T> implements Serializable {
|
|||||||
this.msg = msg;
|
this.msg = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getData() {
|
public Object getData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setData(T data) {
|
public void setData(Object data) {
|
||||||
this.data = 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;
|
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