删除swagger侵入式代码 , 删除demo

This commit is contained in:
2025-06-04 15:08:06 +08:00
parent 8cbbe932f2
commit f9cfaced05
303 changed files with 16491 additions and 49854 deletions

View File

@@ -15,6 +15,11 @@
<description>定义基础 pojo 类、枚举、工具类等等</description>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework</groupId>
@@ -57,12 +62,6 @@
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 -->
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>org.apache.skywalking</groupId>

View File

@@ -38,11 +38,11 @@ public class CommonResult<T> implements Serializable {
/**
* 将传入的 result 对象,转换成另外一个泛型结果的对象
*
* <p>
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
*
* @param result 传入的 result 对象
* @param <T> 返回的泛型
* @param <T> 返回的泛型
* @return 新的 CommonResult 对象
*/
public static <T> CommonResult<T> error(CommonResult<?> result) {

View File

@@ -1,6 +1,6 @@
package com.tashow.cloud.common.pojo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
@@ -8,7 +8,9 @@ import lombok.Data;
import java.io.Serializable;
@Schema(description="分页参数")
/**
* 分页参数
*/
@Data
public class PageParam implements Serializable {
@@ -17,17 +19,21 @@ public class PageParam implements Serializable {
/**
* 每页条数 - 不分页
*
* <p>
* 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。
*/
public static final Integer PAGE_SIZE_NONE = -1;
@Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")
/**
* 页码,从 1 开始", example = "1
*/
@NotNull(message = "页码不能为空")
@Min(value = 1, message = "页码最小值为 1")
private Integer pageNo = PAGE_NO;
@Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
/**
* 每页条数,最大值为 100"
*/
@NotNull(message = "每页条数不能为空")
@Min(value = 1, message = "每页条数最小值为 1")
@Max(value = 100, message = "每页条数最大值为 100")

View File

@@ -1,20 +1,26 @@
package com.tashow.cloud.common.pojo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Schema(description = "分页结果")
/**
* 分页结果
*/
@Data
public final class PageResult<T> implements Serializable {
@Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED)
/**
* 数据
*/
private List<T> list;
@Schema(description = "总量", requiredMode = Schema.RequiredMode.REQUIRED)
/**
* 总量
*/
private Long total;
public PageResult() {

View File

@@ -1,19 +1,23 @@
package com.tashow.cloud.common.pojo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
@Schema(description = "可排序的分页参数")
/**
* 可排序的分页参数
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SortablePageParam extends PageParam {
@Schema(description = "排序字段")
/**
* 排序字段
*/
private List<SortingField> sortingFields;
}

View File

@@ -8,7 +8,7 @@ import java.io.Serializable;
/**
* 排序字段 DTO
*
* <p>
* 类名加了 ing 的原因是,避免和 ES SortField 重名。
*/
@Data

View File

@@ -44,15 +44,6 @@
<scope>provided</scope> <!-- 设置为 provided主要是 GlobalExceptionHandler 使用 -->
</dependency>
<dependency>
<groupId>com.github.xingfudeshi</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>

View File

@@ -22,22 +22,25 @@ public @interface ApiAccessLog {
* 是否记录访问日志
*/
boolean enable() default true;
/**
* 是否记录请求参数
*
* 默认记录,主要考虑请求数据一般不大。可手动设置为 false 进行关闭
* <p>默认记录,主要考虑请求数据一般不大。可手动设置为 false 进行关闭
*/
boolean requestEnable() default true;
/**
* 是否记录响应结果
*
* 默认不记录,主要考虑响应数据可能比较大。可手动设置为 true 进行打开
* <p>默认不记录,主要考虑响应数据可能比较大。可手动设置为 true 进行打开
*/
boolean responseEnable() default false;
/**
* 敏感参数数组
*
* 添加后,请求参数、响应结果不会记录该参数
* <p>添加后,请求参数、响应结果不会记录该参数
*/
String[] sanitizeKeys() default {};
@@ -45,21 +48,18 @@ public @interface ApiAccessLog {
/**
* 操作模块
*
* 为空时,会尝试读取 {@link io.swagger.v3.oas.annotations.tags.Tag#name()} 属性
*/
String operateModule() default "";
/**
* 操作名
*
* 为空时,会尝试读取 {@link io.swagger.v3.oas.annotations.Operation#summary()} 属性
*/
String operateName() default "";
/**
* 操作分类
*
* 实际并不是数组,因为枚举不能设置 null 作为默认值
* <p>实际并不是数组,因为枚举不能设置 null 作为默认值
*/
OperateTypeEnum[] operateType() default {};
}

View File

@@ -7,6 +7,7 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.tashow.cloud.common.exception.enums.GlobalErrorCodeConstants;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.util.json.JsonUtils;
@@ -14,14 +15,11 @@ import com.tashow.cloud.common.util.monitor.TracerUtils;
import com.tashow.cloud.common.util.servlet.ServletUtils;
import com.tashow.cloud.infraapi.api.logger.ApiAccessLogApi;
import com.tashow.cloud.infraapi.api.logger.dto.ApiAccessLogCreateReqDTO;
import com.fasterxml.jackson.databind.JsonNode;
import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
import com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum;
import com.tashow.cloud.web.web.config.WebProperties;
import com.tashow.cloud.web.web.core.filter.ApiRequestFilter;
import com.tashow.cloud.web.web.core.util.WebFrameworkUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@@ -42,7 +40,7 @@ import static com.tashow.cloud.web.apilog.core.interceptor.ApiAccessLogIntercept
/**
* API 访问日志 Filter
*
* 目的:记录 API 访问日志到数据库中
* <p>目的:记录 API 访问日志到数据库中
*
* @author 芋道源码
*/
@@ -63,8 +61,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
@Override
@SuppressWarnings("NullableProblems")
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 获得开始时间
LocalDateTime beginTime = LocalDateTime.now();
// 提前获得参数,避免 XssFilter 过滤处理
@@ -83,8 +80,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
}
}
private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime,
Map<String, String> queryString, String requestBody, Exception ex) {
private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime, Map<String, String> queryString, String requestBody, Exception ex) {
ApiAccessLogCreateReqDTO accessLog = new ApiAccessLogCreateReqDTO();
try {
boolean enable = buildApiAccessLog(accessLog, request, beginTime, queryString, requestBody, ex);
@@ -97,8 +93,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
}
}
private boolean buildApiAccessLog(ApiAccessLogCreateReqDTO accessLog, HttpServletRequest request, LocalDateTime beginTime,
Map<String, String> queryString, String requestBody, Exception ex) {
private boolean buildApiAccessLog(ApiAccessLogCreateReqDTO accessLog, HttpServletRequest request, LocalDateTime beginTime, Map<String, String> queryString, String requestBody, Exception ex) {
// 判断:是否要记录操作日志
HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(ATTRIBUTE_HANDLER_METHOD);
ApiAccessLog accessLogAnnotation = null;
@@ -110,28 +105,22 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
}
// 处理用户信息
accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request))
.setUserType(WebFrameworkUtils.getLoginUserType(request));
accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request)).setUserType(WebFrameworkUtils.getLoginUserType(request));
// 设置访问结果
CommonResult<?> result = WebFrameworkUtils.getCommonResult(request);
if (result != null) {
accessLog.setResultCode(result.getCode()).setResultMsg(result.getMsg());
} else if (ex != null) {
accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode())
.setResultMsg(ExceptionUtil.getRootCauseMessage(ex));
accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode()).setResultMsg(ExceptionUtil.getRootCauseMessage(ex));
} else {
accessLog.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()).setResultMsg("");
}
// 设置请求字段
accessLog.setTraceId(TracerUtils.getTraceId()).setApplicationName(applicationName)
.setRequestUrl(request.getRequestURI()).setRequestMethod(request.getMethod())
.setUserAgent(ServletUtils.getUserAgent(request)).setUserIp(ServletUtils.getClientIP(request));
accessLog.setTraceId(TracerUtils.getTraceId()).setApplicationName(applicationName).setRequestUrl(request.getRequestURI()).setRequestMethod(request.getMethod()).setUserAgent(ServletUtils.getUserAgent(request)).setUserIp(ServletUtils.getClientIP(request));
String[] sanitizeKeys = accessLogAnnotation != null ? accessLogAnnotation.sanitizeKeys() : null;
Boolean requestEnable = accessLogAnnotation != null ? accessLogAnnotation.requestEnable() : Boolean.TRUE;
if (!BooleanUtil.isFalse(requestEnable)) { // 默认记录,所以判断 !false
Map<String, Object> requestParams = MapUtil.<String, Object>builder()
.put("query", sanitizeMap(queryString, sanitizeKeys))
.put("body", sanitizeJson(requestBody, sanitizeKeys)).build();
Map<String, Object> requestParams = MapUtil.<String, Object>builder().put("query", sanitizeMap(queryString, sanitizeKeys)).put("body", sanitizeJson(requestBody, sanitizeKeys)).build();
accessLog.setRequestParams(toJsonString(requestParams));
}
Boolean responseEnable = accessLogAnnotation != null ? accessLogAnnotation.responseEnable() : Boolean.FALSE;
@@ -139,21 +128,13 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
accessLog.setResponseBody(sanitizeJson(result, sanitizeKeys));
}
// 持续时间
accessLog.setBeginTime(beginTime).setEndTime(LocalDateTime.now())
.setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.MILLIS));
accessLog.setBeginTime(beginTime).setEndTime(LocalDateTime.now()).setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.MILLIS));
// 操作模块
if (handlerMethod != null) {
Tag tagAnnotation = handlerMethod.getBeanType().getAnnotation(Tag.class);
Operation operationAnnotation = handlerMethod.getMethodAnnotation(Operation.class);
String operateModule = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateModule()) ?
accessLogAnnotation.operateModule() :
tagAnnotation != null ? StrUtil.nullToDefault(tagAnnotation.name(), tagAnnotation.description()) : null;
String operateName = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateName()) ?
accessLogAnnotation.operateName() :
operationAnnotation != null ? operationAnnotation.summary() : null;
OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ?
accessLogAnnotation.operateType()[0] : parseOperateLogType(request);
String operateModule = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateModule()) ? accessLogAnnotation.operateModule() : null;
String operateName = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateName()) ? accessLogAnnotation.operateName() : null;
OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ? accessLogAnnotation.operateType()[0] : parseOperateLogType(request);
accessLog.setOperateModule(operateModule).setOperateName(operateName).setOperateType(operateType.getType());
}
return true;
@@ -240,13 +221,11 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
Iterator<Map.Entry<String, JsonNode>> iterator = node.properties().iterator();
while (iterator.hasNext()) {
Map.Entry<String, JsonNode> entry = iterator.next();
if (ArrayUtil.contains(sanitizeKeys, entry.getKey())
|| ArrayUtil.contains(SANITIZE_KEYS, entry.getKey())) {
if (ArrayUtil.contains(sanitizeKeys, entry.getKey()) || ArrayUtil.contains(SANITIZE_KEYS, entry.getKey())) {
iterator.remove();
continue;
}
sanitizeJson(entry.getValue(), sanitizeKeys);
}
}
}

View File

@@ -1,162 +0,0 @@
package com.tashow.cloud.web.swagger.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.models.GroupedOpenApi;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.providers.JavadocProvider;
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static com.tashow.cloud.web.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
/**
* Swagger 自动配置类,基于 OpenAPI + Springdoc 实现。
*
* 友情提示:
* 1. Springdoc 文档地址:<a href="https://github.com/springdoc/springdoc-openapi">仓库</a>
* 2. Swagger 规范,于 2015 更名为 OpenAPI 规范,本质是一个东西
*
* @author 芋道源码
*/
@AutoConfiguration
@ConditionalOnClass({OpenAPI.class})
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用
public class SwaggerAutoConfiguration {
// ========== 全局 OpenAPI 配置 ==========
@Bean
public OpenAPI createApi(SwaggerProperties properties) {
Map<String, SecurityScheme> securitySchemas = buildSecuritySchemes();
OpenAPI openAPI = new OpenAPI()
// 接口信息
.info(buildInfo(properties))
// 接口安全配置
.components(new Components().securitySchemes(securitySchemas))
.addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION));
securitySchemas.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key)));
return openAPI;
}
/**
* API 摘要信息
*/
private Info buildInfo(SwaggerProperties properties) {
return new Info()
.title(properties.getTitle())
.description(properties.getDescription())
.version(properties.getVersion())
.contact(new Contact().name(properties.getAuthor()).url(properties.getUrl()).email(properties.getEmail()))
.license(new License().name(properties.getLicense()).url(properties.getLicenseUrl()));
}
/**
* 安全模式,这里配置通过请求头 Authorization 传递 token 参数
*/
private Map<String, SecurityScheme> buildSecuritySchemes() {
Map<String, SecurityScheme> securitySchemes = new HashMap<>();
SecurityScheme securityScheme = new SecurityScheme()
.type(SecurityScheme.Type.APIKEY) // 类型
.name(HttpHeaders.AUTHORIZATION) // 请求头的 name
.in(SecurityScheme.In.HEADER); // token 所在位置
securitySchemes.put(HttpHeaders.AUTHORIZATION, securityScheme);
return securitySchemes;
}
/**
* 自定义 OpenAPI 处理器
*/
@Bean
@Primary // 目的:以我们创建的 OpenAPIService Bean 为主,避免一键改包后,启动报错!
public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties,
PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
Optional<JavadocProvider> javadocProvider) {
return new OpenAPIService(openAPI, securityParser, springDocConfigProperties,
propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
}
// ========== 分组 OpenAPI 配置 ==========
/**
* 所有模块的 API 分组
*/
@Bean
public GroupedOpenApi allGroupedOpenApi() {
return buildGroupedOpenApi("all", "");
}
public static GroupedOpenApi buildGroupedOpenApi(String group) {
return buildGroupedOpenApi(group, group);
}
public static GroupedOpenApi buildGroupedOpenApi(String group, String path) {
return GroupedOpenApi.builder()
.group(group)
.pathsToMatch("/admin-api/" + path + "/**", "/app-api/" + path + "/**")
.addOperationCustomizer((operation, handlerMethod) -> operation
.addParametersItem(buildTenantHeaderParameter())
.addParametersItem(buildSecurityHeaderParameter()))
.build();
}
/**
* 构建 Tenant 租户编号请求头参数
*
* @return 多租户参数
*/
private static Parameter buildTenantHeaderParameter() {
return new Parameter()
.name(HEADER_TENANT_ID) // header 名
.description("租户编号") // 描述
.in(String.valueOf(SecurityScheme.In.HEADER)) // 请求 header
.schema(new IntegerSchema()._default(1L).name(HEADER_TENANT_ID).description("租户编号")); // 默认:使用租户编号为 1
}
/**
* 构建 Authorization 认证请求头参数
*
* 解决 Knife4j <a href="https://gitee.com/xiaoym/knife4j/issues/I69QBU">Authorize 未生效请求header里未包含参数</a>
*
* @return 认证参数
*/
private static Parameter buildSecurityHeaderParameter() {
return new Parameter()
.name(HttpHeaders.AUTHORIZATION) // header 名
.description("认证 Token") // 描述
.in(String.valueOf(SecurityScheme.In.HEADER)) // 请求 header
.schema(new StringSchema()._default("Bearer test1").name(HEADER_TENANT_ID).description("认证 Token")); // 默认:使用用户编号为 1
}
}

View File

@@ -1,59 +0,0 @@
package com.tashow.cloud.web.swagger.config;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Swagger 配置属性
*
* @author 芋道源码
*/
@ConfigurationProperties("yudao.swagger")
@Data
public class SwaggerProperties {
/**
* 标题
*/
@NotEmpty(message = "标题不能为空")
private String title;
/**
* 描述
*/
@NotEmpty(message = "描述不能为空")
private String description;
/**
* 作者
*/
@NotEmpty(message = "作者不能为空")
private String author;
/**
* 版本
*/
@NotEmpty(message = "版本不能为空")
private String version;
/**
* url
*/
@NotEmpty(message = "扫描的 package 不能为空")
private String url;
/**
* email
*/
@NotEmpty(message = "扫描的 email 不能为空")
private String email;
/**
* license
*/
@NotEmpty(message = "扫描的 license 不能为空")
private String license;
/**
* license-url
*/
@NotEmpty(message = "扫描的 license-url 不能为空")
private String licenseUrl;
}

View File

@@ -1,6 +0,0 @@
/**
* 基于 Swagger + Knife4j 实现 API 接口文档
*
* @author 芋道源码
*/
package com.tashow.cloud.web.swagger;

View File

@@ -1,6 +1,5 @@
com.tashow.cloud.web.apilog.config.ApiLogAutoConfiguration
com.tashow.cloud.web.jackson.config.JacksonAutoConfiguration
com.tashow.cloud.web.swagger.config.SwaggerAutoConfiguration
com.tashow.cloud.web.web.config.WebAutoConfiguration
com.tashow.cloud.web.apilog.config.ApiLogRpcAutoConfiguration
com.tashow.cloud.web.banner.config.BannerAutoConfiguration