提交
This commit is contained in:
@@ -54,6 +54,16 @@
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
<version>${transmittable-thread-local.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>1.5.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.9.11</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
@@ -11,4 +11,16 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>pom</packaging>
|
||||
<artifactId>aivfo-model-parent</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<artifactId>aivfo-element-common</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -62,12 +62,12 @@
|
||||
<repository>
|
||||
<id>nexus</id>
|
||||
<name>releases</name>
|
||||
<url>https://maven.wylgyx.top/repository/maven-releases/</url>
|
||||
<url>http://192.168.31.90:8081/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>nexus</id>
|
||||
<name>snapshots</name>
|
||||
<url>https://maven.wylgyx.top/repository/maven-snapshots/</url>
|
||||
<url>http://192.168.31.90:8081/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
<build>
|
||||
|
||||
+12
-1
@@ -11,5 +11,16 @@
|
||||
<module>aivfo-supreme</module>
|
||||
</modules>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>nexus</id>
|
||||
<name>releases</name>
|
||||
<url>http://192.168.31.90:8081/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>nexus</id>
|
||||
<name>snapshots</name>
|
||||
<url>http://192.168.31.90:8081/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
</project>
|
||||
@@ -3,10 +3,10 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>aivfo-model-parent</artifactId>
|
||||
<artifactId>aivfo-element</artifactId>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../../../ability/aivfo-framework-build/aivfo-project-build/aivfo-model-parent/pom.xml</relativePath>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>aivfo-element-base</artifactId>
|
||||
@@ -22,7 +22,10 @@
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.9.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
@@ -39,7 +42,36 @@
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>5.7.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package com.aivfo.el.base.constant;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/18
|
||||
* @description: 常用的配置默认值
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class ConfigDefaultValue {
|
||||
/** DEFAULT_TIME_ZONE */
|
||||
public static final String DEFAULT_TIME_ZONE = "GMT+8";
|
||||
/** DEFAULT_DATE_FORMAT */
|
||||
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
/** BASE_PACKAGES */
|
||||
public static final String BASE_PACKAGES = System.getProperty("PARENT_PACKAGE_NAME", "com.aivfo");
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package com.aivfo.el.base.constant;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/18
|
||||
* @description: 所有的配置常量
|
||||
*/
|
||||
@UtilityClass
|
||||
@SuppressWarnings("all")
|
||||
public final class ConfigKey {
|
||||
/** JSON_DATE_FORMAT */
|
||||
public static final String JSON_DATE_FORMAT = "aivfo.rest.json.date-formate";
|
||||
/** JSON_TIME_ZONE */
|
||||
public static final String JSON_TIME_ZONE = "aivfo.rest.json.time-zone";
|
||||
}
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
package com.aivfo.el.base.exception;
|
||||
|
||||
import com.aivfo.el.base.contex.TraceContext;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/14
|
||||
* @description: 统一异常
|
||||
*/
|
||||
@Slf4j
|
||||
public class AivfoBaseException extends RuntimeException {
|
||||
|
||||
|
||||
private static final long serialVersionUID = 8315456934998288154L;
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
@Getter
|
||||
protected String errorCode;
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
protected String message;
|
||||
/**
|
||||
* 请求链路id
|
||||
*/
|
||||
@Getter
|
||||
protected String traceId;
|
||||
|
||||
/**
|
||||
* 默认code
|
||||
*/
|
||||
public static final String DEFAULT_ERROR_CODE = "00000001";
|
||||
/**
|
||||
* 默认错误信息
|
||||
*/
|
||||
public static final String DEFAULT_MESSAGE = "服务内部错误";
|
||||
|
||||
|
||||
public AivfoBaseException() {
|
||||
this(DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
public AivfoBaseException(String errorMsg) {
|
||||
this(DEFAULT_ERROR_CODE, errorMsg);
|
||||
}
|
||||
|
||||
public AivfoBaseException(String errorCode, String errorMsg) {
|
||||
super(errorMsg);
|
||||
this.init(errorCode, errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* @param code code
|
||||
* @param message message
|
||||
* @since 1.9.0
|
||||
*/
|
||||
private void init(String code, String message) {
|
||||
this.errorCode = code;
|
||||
this.message = message;
|
||||
this.traceId = TraceContext.getTraceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常打印到日志
|
||||
*
|
||||
* @param
|
||||
* @return void
|
||||
* @Date 2023/2/20
|
||||
* @Author wangyl
|
||||
*/
|
||||
@Override
|
||||
public void printStackTrace() {
|
||||
log.error("", this);
|
||||
}
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package com.aivfo.el.base.exception;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/14
|
||||
* @description: 错误码接口
|
||||
*/
|
||||
public interface ErrorCodeInterface {
|
||||
/**
|
||||
* @return java.lang.String
|
||||
* @Description 获取错误码
|
||||
* @Date 2020/11/8 15:37
|
||||
* @Author wangyl
|
||||
* @Version V1.0
|
||||
*/
|
||||
String getCode();
|
||||
|
||||
/**
|
||||
* @return java.lang.String
|
||||
* @Description 获取错误信息
|
||||
* @Date 2020/11/8 15:38
|
||||
* @Author wangyl
|
||||
* @Version V1.0
|
||||
*/
|
||||
String getMsg();
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package com.aivfo.el.base.utils;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/14
|
||||
* @description: Collection工具
|
||||
*/
|
||||
public class AivfoCollectionUtils extends CollectionUtils {
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
package com.aivfo.el.base.utils;
|
||||
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/14
|
||||
* @description: String工具类
|
||||
*/
|
||||
public class AivfoStringUtils extends StringUtils {
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package com.aivfo.el.core.function;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 受检的 Callable </p>
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 14:54
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedCallable<T> {
|
||||
|
||||
/**
|
||||
* Run this callable.
|
||||
*
|
||||
* @return result t
|
||||
* @throws Throwable CheckedException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
T call() throws Throwable;
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package com.aivfo.el.core.function;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 受检的 Consumer </p>
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 14:55
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedConsumer<T> {
|
||||
|
||||
/**
|
||||
* Run the Consumer
|
||||
*
|
||||
* @param t T
|
||||
* @throws Throwable UncheckedException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void accept(T t) throws Throwable;
|
||||
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package com.aivfo.el.core.function;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 受检的 runnable </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 14:54
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedRunnable {
|
||||
|
||||
/**
|
||||
* Run this runnable.
|
||||
*
|
||||
* @throws Throwable CheckedException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void run() throws Throwable;
|
||||
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package com.aivfo.el.starter.base;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.SecurityUtils;
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 能够配置需要跳过的 url 的抽象 filter, 子类
|
||||
*/
|
||||
@Slf4j
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public abstract class AbstractSkipFilter extends OncePerRequestFilter {
|
||||
/**
|
||||
* Path matcher
|
||||
*/
|
||||
protected PathMatcher pathMatcher = new AntPathMatcher();
|
||||
/**
|
||||
* 需要执行过滤器逻辑的 url
|
||||
*/
|
||||
@Nullable
|
||||
protected String[] includePatterns;
|
||||
/**
|
||||
* 不需要执行过滤器逻辑的 url
|
||||
*/
|
||||
@Nullable
|
||||
protected String[] excludePatterns;
|
||||
/**
|
||||
* 业务自定义需要忽略的 url, 最终会写入到 {@link AbstractSkipFilter#includePatterns}
|
||||
*/
|
||||
protected String skipUrl;
|
||||
|
||||
/**
|
||||
* Abstract skip filter
|
||||
*
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public AbstractSkipFilter() {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract skip filter
|
||||
*
|
||||
* @param skipUrl skip url
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public AbstractSkipFilter(String skipUrl) {
|
||||
this.skipUrl = skipUrl;
|
||||
merge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
merge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge
|
||||
*
|
||||
* @since 2.1.0
|
||||
*/
|
||||
private void merge() {
|
||||
if (this.includePatterns == null) {
|
||||
this.includePatterns = new String[]{StringPool.ANY_PATH};
|
||||
}
|
||||
|
||||
if (this.excludePatterns == null) {
|
||||
this.excludePatterns = SecurityUtils.mergeSkipPatterns(this.skipUrl).toArray(new String[0]);
|
||||
} else {
|
||||
Set<String> excluded = new HashSet<>(Arrays.asList(this.excludePatterns));
|
||||
this.excludePatterns = SecurityUtils.mergeSkipPatterns(excluded, this.skipUrl).toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 不需要拦截的 url
|
||||
*
|
||||
* @param request request
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Override
|
||||
protected boolean shouldNotFilter(@NotNull HttpServletRequest request) {
|
||||
return SecurityUtils.matches(request.getRequestURI(), this.includePatterns, this.excludePatterns, this.pathMatcher);
|
||||
}
|
||||
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
package com.aivfo.el.starter.base;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>Description: 请求响应返回结构封装, 所有请求都要求返回此类.
|
||||
* 为避免在 v4 中引入过多的 v5 依赖, 我们将 {@link Result} 分为了 2 个子类, 分别是 {@link StandardResult} 和 {@link com.fkhwl.starter.core.api.R},
|
||||
* StandardResult 在 v4 使用, R 在 v5 使用.
|
||||
* {@code
|
||||
* Result<Data> result = StandardResult.success(data);
|
||||
* Result<Data> result = R.success(data);
|
||||
* <p>
|
||||
* 反序列化:
|
||||
* Result<Data> result = JsonUtils.parse(json, new TypeReference<Result<Data>>(){});
|
||||
* Result result = JsonUtils.parse(json, Result.class);
|
||||
* }*
|
||||
* <p>
|
||||
* 由于 jackson 在反序列化抽象类时存在多态问题, 我们使用 {@link Result#TYPE_NAME} 来标识 json 需要被反序列化的 class, 为了避免 v5 的响应 json 数据在 v4 中反序列化失败,
|
||||
* 我们将所有的 json 都添加了 {@link Result#TYPE_NAME}, 全部都将反序列化为 {@link StandardResult}, 业务端在处理 json 结果时, 应该忽略 'type' 字段.
|
||||
* 比较推荐的反序列化方式是根据不同的框架来进行反序列化, 避免使用 {@link Result}:
|
||||
* {@code
|
||||
* v4: Result result = JsonUtils.parse(json, StandardResult.class);
|
||||
* v5: Result result = JsonUtils.parse(json, R.class);
|
||||
* }*
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@SuppressWarnings("all")
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
public abstract class Result<T> implements Serializable {
|
||||
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
public static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* CODE
|
||||
*/
|
||||
public static final String CODE = "code";
|
||||
/**
|
||||
* SUCCESS
|
||||
*/
|
||||
public static final String SUCCESS = "success";
|
||||
/**
|
||||
* DATA
|
||||
*/
|
||||
public static final String DATA = "data";
|
||||
/**
|
||||
* MESSAGE
|
||||
*/
|
||||
public static final String MESSAGE = "message";
|
||||
/**
|
||||
* TRACE_ID
|
||||
*/
|
||||
public static final String TRACE_ID = "traceId";
|
||||
/**
|
||||
* EXTEND
|
||||
*/
|
||||
public static final String EXTEND = "extend";
|
||||
/**
|
||||
* 反序列化时处理多态问题的标识
|
||||
*/
|
||||
public static final String TYPE_NAME = "StandardResult";
|
||||
/**
|
||||
* 请求成功代码
|
||||
*/
|
||||
public static final String SUCCESS_CODE = "2000";
|
||||
/**
|
||||
* 请求成功消息
|
||||
*/
|
||||
public static final String SUCCESS_MESSAGE = "操作成功";
|
||||
/**
|
||||
* 默认的失败代码
|
||||
*/
|
||||
public static final String FAILURE_CODE = "4000";
|
||||
/**
|
||||
* 默认的失败消息
|
||||
*/
|
||||
public static final String FAILURE_MESSAGE = "操作失败: [{}]";
|
||||
|
||||
/**
|
||||
* 请求响应状态码
|
||||
*/
|
||||
@ApiModelProperty(value = "状态码", required = true, example = "2000")
|
||||
protected String code;
|
||||
/**
|
||||
* 请求响应成功标识
|
||||
*/
|
||||
@ApiModelProperty(value = "请求成功的状态", required = true, example = "true")
|
||||
protected boolean success;
|
||||
/**
|
||||
* 请求响应的数据
|
||||
*/
|
||||
@ApiModelProperty(value = "承载的数据", required = true)
|
||||
protected T data;
|
||||
/**
|
||||
* 请求响应的消息
|
||||
*/
|
||||
@ApiModelProperty(value = "返回的消息", required = true, example = "操作成功")
|
||||
protected String message = "";
|
||||
/**
|
||||
* 请求响应的溯源标识
|
||||
*/
|
||||
@ApiModelProperty(value = "溯源标识(业务无需关心此字段)", required = true, example = "1484501823002316800")
|
||||
protected String traceId;
|
||||
/**
|
||||
* 扩展字段
|
||||
*/
|
||||
@ApiModelProperty(value = "扩展字段(业务无需关心此字段, 非生产环境的异常信息会写入到此字段)", required = true, example = "N/A")
|
||||
protected Object extend = StringPool.NULL_STRING;
|
||||
|
||||
/**
|
||||
* Result
|
||||
*
|
||||
* @param code code
|
||||
* @param message message
|
||||
* @param data data
|
||||
* @param traceId trace id
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
protected Result(@NotNull String code, String message, T data, String traceId) {
|
||||
this.code = code;
|
||||
if (!SUCCESS_CODE.equals(code) && !code.contains(StringPool.DASH) && !code.contains(StringPool.DOT)) {
|
||||
this.code = "S.F-" + code;
|
||||
}
|
||||
this.data = data;
|
||||
this.message = message;
|
||||
this.success = (SUCCESS_CODE.equals(code));
|
||||
this.traceId = StringUtils.isBlank(traceId)
|
||||
? StringPool.NULL_STRING
|
||||
: traceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is ok boolean
|
||||
*
|
||||
* @return the boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@JsonIgnore
|
||||
public boolean isOk() {
|
||||
return isOk(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求是否成功
|
||||
*
|
||||
* @param result result
|
||||
* @return the boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("null -> false")
|
||||
public static boolean isOk(@Nullable Result<?> result) {
|
||||
return result != null && (SUCCESS_CODE.equals(result.getCode()) && result.isSuccess());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is fail boolean
|
||||
*
|
||||
* @return the boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@JsonIgnore
|
||||
public boolean isFail() {
|
||||
return isFail(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求是否失败
|
||||
*
|
||||
* @param result result
|
||||
* @return the boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("null -> true")
|
||||
public static boolean isFail(@Nullable Result<?> result) {
|
||||
return !isOk(result);
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.aivfo.el.starter.base.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 错误码来源 业务系统
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface BusinessLevel {
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.aivfo.el.starter.base.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 项目标识
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ModelSerial {
|
||||
/**
|
||||
* DEFAULT
|
||||
*/
|
||||
String DEFAULT = "F";
|
||||
|
||||
/**
|
||||
* Model name
|
||||
*
|
||||
* @return the module name
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String modelName() default DEFAULT;
|
||||
|
||||
/**
|
||||
* Value
|
||||
*
|
||||
* @return the string
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@AliasFor("modelName")
|
||||
String value() default DEFAULT;
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.aivfo.el.starter.base.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 支持普通枚举类字段, 只用在enum类的字段上
|
||||
* <p>当实体类的属性是普通枚举, 且是其中一个字段, 使用该注解来标注枚举类里的那个属性对应字段</p>
|
||||
* {@code
|
||||
* TableName("student")
|
||||
* class Student {
|
||||
* private Integer id;
|
||||
* private String name;
|
||||
* private GradeEnum grade;//数据库grade字段类型为int
|
||||
* }
|
||||
* <p>
|
||||
* public enum GradeEnum {
|
||||
* PRIMARY(1,"小学"),
|
||||
* SECONDORY("2", "中学"),
|
||||
* HIGH(3, "高中");
|
||||
* <p>
|
||||
* @SerializeValue
|
||||
* private final int code;
|
||||
* private final String descp;
|
||||
* }
|
||||
* }*
|
||||
* </p>
|
||||
*
|
||||
* @author yuxiaobin
|
||||
* @version 1.3.0
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.03.23 19:10
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface SerializeValue {
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.aivfo.el.starter.base.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 错误码来源 系统错误
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface SystemLevel {
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.aivfo.el.starter.base.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 错误码来源 三方错误
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ThirdLevel {
|
||||
}
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
+10
-9
@@ -1,9 +1,9 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AivfoBaseException;
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.base.utils.AivfoStringUtils;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.base.exception.BasicException;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@UtilityClass
|
||||
class AssertUtils {
|
||||
|
||||
@@ -79,7 +80,7 @@ class AssertUtils {
|
||||
String className = (obj != null ? obj.getClass().getName() : "null");
|
||||
String result = "";
|
||||
boolean defaultMessage = true;
|
||||
if (AivfoStringUtils.hasLength(message)) {
|
||||
if (StringUtils.hasLength(message)) {
|
||||
if (endsWithSeparator(message)) {
|
||||
result = message + " ";
|
||||
} else {
|
||||
@@ -90,7 +91,7 @@ class AssertUtils {
|
||||
if (defaultMessage) {
|
||||
result = result + ("Object 类型 [" + className + "] 不是: " + type);
|
||||
}
|
||||
throw new AivfoBaseException(result);
|
||||
throw new BasicException(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +105,7 @@ class AssertUtils {
|
||||
static void assignableCheckFailed(Class<?> superType, @Nullable Class<?> subType, @Nullable String message) {
|
||||
String result = "";
|
||||
boolean defaultMessage = true;
|
||||
if (AivfoStringUtils.hasLength(message)) {
|
||||
if (StringUtils.hasLength(message)) {
|
||||
if (endsWithSeparator(message)) {
|
||||
result = message + " ";
|
||||
} else {
|
||||
@@ -115,7 +116,7 @@ class AssertUtils {
|
||||
if (defaultMessage) {
|
||||
result = result + (subType + " 不是 " + superType + "的子类");
|
||||
}
|
||||
throw new AivfoBaseException(result);
|
||||
throw new BasicException(result);
|
||||
}
|
||||
|
||||
/**
|
||||
+11
-11
@@ -1,15 +1,15 @@
|
||||
package com.aivfo.el.base.asserts;
|
||||
package com.aivfo.el.starter.base.asserts;
|
||||
|
||||
import com.aivfo.el.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.base.utils.AivfoCollectionUtils;
|
||||
import com.aivfo.el.base.utils.AivfoStringUtils;
|
||||
import com.aivfo.el.core.function.CheckedCallable;
|
||||
import com.aivfo.el.core.function.CheckedRunnable;
|
||||
import com.aivfo.el.starter.base.exception.AssertionFailedException;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.aivfo.el.starter.core.function.CheckedCallable;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -168,7 +168,7 @@ public class Assertions {
|
||||
public static void notBlank(@Nullable String content,
|
||||
Supplier<? extends RuntimeException> exceptionSupplier,
|
||||
CheckedRunnable runnable) {
|
||||
if (!AivfoStringUtils.hasText(content)) {
|
||||
if (!StringUtils.hasText(content)) {
|
||||
AssertUtils.fail(exceptionSupplier, runnable);
|
||||
}
|
||||
}
|
||||
@@ -459,8 +459,8 @@ public class Assertions {
|
||||
*/
|
||||
public static void doesNotContain(@Nullable String textToSearch, String substring,
|
||||
Supplier<? extends RuntimeException> exceptionSupplier) {
|
||||
if (AivfoStringUtils.hasLength(textToSearch)
|
||||
&& AivfoStringUtils.hasLength(substring)
|
||||
if (StringUtils.hasLength(textToSearch)
|
||||
&& StringUtils.hasLength(substring)
|
||||
&& textToSearch.contains(substring)) {
|
||||
AssertUtils.fail(exceptionSupplier);
|
||||
}
|
||||
@@ -495,7 +495,7 @@ public class Assertions {
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static void notEmpty(@Nullable Map<?, ?> map, Supplier<? extends RuntimeException> exceptionSupplier) {
|
||||
if (AivfoCollectionUtils.isEmpty(map)) {
|
||||
if (CollectionUtils.isEmpty(map)) {
|
||||
AssertUtils.fail(exceptionSupplier);
|
||||
}
|
||||
}
|
||||
@@ -602,7 +602,7 @@ public class Assertions {
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static void notEmpty(@Nullable Collection<?> collection, Supplier<? extends RuntimeException> exceptionSupplier) {
|
||||
if (AivfoCollectionUtils.isEmpty(collection)) {
|
||||
if (CollectionUtils.isEmpty(collection)) {
|
||||
AssertUtils.fail(exceptionSupplier);
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.aivfo.el.starter.base.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 启动成功后将实现指定的提示信息, 主要用于 starter 的自动装配类
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LibraryEnum {
|
||||
|
||||
/**
|
||||
* Rest library enum
|
||||
*/
|
||||
REST("Rest", LibraryEnum.START_URL),
|
||||
/**
|
||||
* Agent library enum
|
||||
*/
|
||||
AGENT("Agent", "/agent/ping"),
|
||||
/**
|
||||
* Dubbo library enum
|
||||
*/
|
||||
DUBBO("Dubbo", ""),
|
||||
/**
|
||||
* Druid library enum
|
||||
*/
|
||||
DRUID("Druid", "/druid/"),
|
||||
/**
|
||||
* Swagger rest default library enum
|
||||
*/
|
||||
SWAGGER_REST_DEFAULT("Swagger(D)", "/swagger-ui.html"),
|
||||
/**
|
||||
* Swagger rest bootstrap library enum
|
||||
*/
|
||||
SWAGGER_REST_BOOTSTRAP("Swagger(B)", "/doc.html"),
|
||||
/**
|
||||
* Swagger dubbo json
|
||||
*/
|
||||
SWAGGER_DUBBO_JSON("Swagger(Dubbo API)", "/swagger-dubbo/api-docs"),
|
||||
/**
|
||||
* Swagger dubbo library enum
|
||||
*/
|
||||
SWAGGER_DUBBO("Swagger(Dubbo)", "/dubbo.html"),
|
||||
/**
|
||||
* Swagger json library enum
|
||||
*/
|
||||
SWAGGER_JSON("Swagger(API)", "/v2/api-docs");
|
||||
|
||||
/**
|
||||
* START_URL
|
||||
*/
|
||||
public static final String START_URL = "/actuator/info";
|
||||
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* Uri
|
||||
*/
|
||||
private final String uri;
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package com.aivfo.el.starter.base.common.util;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 只会输出一次的日志
|
||||
*/
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
public class JustOnceLogger {
|
||||
|
||||
/**
|
||||
* KNOWN_LOGS
|
||||
*/
|
||||
private static final Map<String, Set<String>> KNOWN_LOGS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Info once
|
||||
*
|
||||
* @param loggerName logger name
|
||||
* @param message message
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public static void infoOnce(String loggerName, String message) {
|
||||
if (!log.isInfoEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (check(loggerName, message)) {
|
||||
return;
|
||||
}
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn once *
|
||||
*
|
||||
* @param loggerName logger name
|
||||
* @param message message
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void warnOnce(String loggerName, String message) {
|
||||
if (!log.isWarnEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (check(loggerName, message)) {
|
||||
return;
|
||||
}
|
||||
log.warn(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print once
|
||||
*
|
||||
* @param loggerName logger name
|
||||
* @param message message
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static void printOnce(String loggerName, String message) {
|
||||
if (check(loggerName, message)) {
|
||||
return;
|
||||
}
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check
|
||||
*
|
||||
* @param loggerName logger name
|
||||
* @param message message
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
private static boolean check(String loggerName, String message) {
|
||||
if (!KNOWN_LOGS.containsKey(loggerName)) {
|
||||
KNOWN_LOGS.put(loggerName, new ConcurrentSkipListSet<>(Collections.singleton(message)));
|
||||
} else {
|
||||
|
||||
Set<String> messages = KNOWN_LOGS.get(loggerName);
|
||||
if (messages.contains(message)) {
|
||||
return true;
|
||||
}
|
||||
messages.add(message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.aivfo.el.starter.base.constant;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description:
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class BasicConstant {
|
||||
/**
|
||||
* BOOST_EXECUTOR
|
||||
*/
|
||||
public static final String BOOST_EXECUTOR = "boostExecutor";
|
||||
/**
|
||||
* DYNAMIC_EXECUTOR
|
||||
*/
|
||||
public static final String DYNAMIC_EXECUTOR = "dynamicExecutor";
|
||||
/**
|
||||
* BOOST_EXECUTORSERVICE
|
||||
*/
|
||||
public static final String BOOST_EXECUTORSERVICE = "boostExecutorService";
|
||||
/**
|
||||
* JSON
|
||||
*/
|
||||
public static final String JSON = "json";
|
||||
/**
|
||||
* TRACE_ID
|
||||
*/
|
||||
public static final String TRACE_ID = "traceId";
|
||||
/**
|
||||
* 客户端标识
|
||||
*/
|
||||
public static final String HEADER_CLIENT_ID = "X-Client-Id";
|
||||
/**
|
||||
* 网关写入的原始请求地址
|
||||
*/
|
||||
public static final String X_GATEWAY_ORIGINAL = "X-Gatetay-Original";
|
||||
/**
|
||||
* 网关写入的路由日志
|
||||
*/
|
||||
public static final String X_GATEWAY_ROUTER = "X-Gatetay-Router";
|
||||
/**
|
||||
* 对 rest 和 agent 结果进行解包/装包 的字段名
|
||||
*/
|
||||
public static final String RESULT_WRAPPER_VALUE_KEY = "value";
|
||||
|
||||
/**
|
||||
* REQUEST_EXCEPTION_INFO_ATTR
|
||||
*/
|
||||
public static final String REQUEST_EXCEPTION_INFO_ATTR = "javax.servlet.error.exception";
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.aivfo.el.starter.base.constant;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/18
|
||||
* @description: 常用的配置默认值
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class ConfigDefaultValue {
|
||||
|
||||
/**
|
||||
* DEFAULT_PROPERTY_INCLUSION_VALUE
|
||||
*/
|
||||
public static final String DEFAULT_PROPERTY_INCLUSION_VALUE = "non_null";
|
||||
/**
|
||||
* DEFAULT_ENCRYPTOR_PASSWORD
|
||||
*/
|
||||
public static final String DEFAULT_ENCRYPTOR_PASSWORD = "06020986-3127-40be-9134-90fd033896a1";
|
||||
/**
|
||||
* DEFAULT_TIME_ZONE
|
||||
*/
|
||||
public static final String DEFAULT_TIME_ZONE = "GMT+8";
|
||||
/**
|
||||
* DEFAULT_DATE_FORMAT
|
||||
*/
|
||||
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
/**
|
||||
* TRUE_STRING
|
||||
*/
|
||||
public static final String TRUE_STRING = "true";
|
||||
/**
|
||||
* TRUE
|
||||
*/
|
||||
public static final Boolean TRUE = Boolean.TRUE;
|
||||
/**
|
||||
* FALSE_STRING
|
||||
*/
|
||||
public static final String FALSE_STRING = "false";
|
||||
/**
|
||||
* FALSE
|
||||
*/
|
||||
public static final Boolean FALSE = Boolean.FALSE;
|
||||
/**
|
||||
* EMPTY
|
||||
*/
|
||||
public static final String EMPTY = StringPool.EMPTY;
|
||||
/**
|
||||
* 应用启动成功后, 将在此路径下创建一个 app.pid 文件, 内容是当前应用的 pid
|
||||
*/
|
||||
public static final String PROP_PID_FILE = "./app.pid";
|
||||
|
||||
/**
|
||||
* BASE_PACKAGES
|
||||
*/
|
||||
public static final String BASE_PACKAGES = System.getProperty("PARENT_PACKAGE_NAME", "com.aivfo");
|
||||
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package com.aivfo.el.starter.base.constant;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 所有的配置常量
|
||||
*/
|
||||
@UtilityClass
|
||||
@SuppressWarnings("all")
|
||||
public final class ConfigKey {
|
||||
/**
|
||||
* PARENT_PACKAGE_NAME
|
||||
*/
|
||||
public static final String PARENT_PACKAGE_NAME = "PARENT_PACKAGE_NAME";
|
||||
/**
|
||||
* 框架版本
|
||||
*/
|
||||
public static final String APPLICATION_FRAMEWORK_VERSION = "aivfo.framework.version";
|
||||
/**
|
||||
* EXTEND_ENABLE_AUTOWIRED_IS_NULL
|
||||
*/
|
||||
public static final String EXTEND_ENABLE_AUTOWIRED_IS_NULL = "aivfo.extend.enable-autowired-is-null";
|
||||
/**
|
||||
* EXTEND_ENABLE_RESOURCE_IS_NULL
|
||||
*/
|
||||
public static final String EXTEND_ENABLE_RESOURCE_IS_NULL = "aivfo.extend.enable-resource-is-null";
|
||||
/**
|
||||
* REST_ENABLE_GLOBAL_CACHE_FILTER
|
||||
*/
|
||||
public static final String WEB_ENABLE_GLOBAL_CACHE_FILTER = "aivfo.web.enable-global-cache-filter";
|
||||
/**
|
||||
* REST_ENABLE_EXCEPTION_FILTER
|
||||
*/
|
||||
public static final String WEB_ENABLE_EXCEPTION_FILTER = "aivfo.web.enable-exception-filter";
|
||||
/**
|
||||
* REST_ENABLE_REQUEST_LOG
|
||||
*/
|
||||
public static final String REST_ENABLE_REQUEST_LOG = "aivfo.rest.enable-request-log";
|
||||
/**
|
||||
* REST_ENABLE_GLOBAL_PARAMETER_FILTER
|
||||
*/
|
||||
public static final String REST_ENABLE_GLOBAL_PARAMETER_FILTER = "aivfo.rest.enable-global-parameter-filter";
|
||||
/**
|
||||
* XSS_ENABLE_XSS_FILTER
|
||||
*/
|
||||
public static final String XSS_ENABLE_XSS_FILTER = "aivfo.xss.enable-xss-filter";
|
||||
/**
|
||||
* JSON_TIME_ZONE
|
||||
*/
|
||||
public static final String JSON_TIME_ZONE = "aivfo.rest.json.time-zone";
|
||||
/**
|
||||
* JSON_DATE_FORMAT
|
||||
*/
|
||||
public static final String JSON_DATE_FORMAT = "aivfo.rest.json.date-formate";
|
||||
/**
|
||||
* jackson null 处理配置
|
||||
*/
|
||||
public static final String JSON_SERIALIZE_NULL = "aivfo.rest.json.default-property-inclusion";
|
||||
/**
|
||||
* MYBATIS_ENABLE_ILLEGAL_SQL_INTERCEPTOR
|
||||
*/
|
||||
public static final String MYBATIS_ENABLE_ILLEGAL_SQL_INTERCEPTOR = "aivfo.mybatis.enable-illegal-sql-interceptor";
|
||||
/**
|
||||
* MYBATIS_ENABLE_SQL_EXPLAIN_INTERCEPTOR
|
||||
*/
|
||||
public static final String MYBATIS_ENABLE_SQL_EXPLAIN_INTERCEPTOR = "aivfo.mybatis.enable-sql-explain-interceptor";
|
||||
/**
|
||||
* POM_INFO_VERSION
|
||||
*/
|
||||
public static final String POM_INFO_VERSION = "info.version";
|
||||
/**
|
||||
* POM_INFO_GROUPID
|
||||
*/
|
||||
public static final String POM_INFO_GROUPID = "info.groupId";
|
||||
/**
|
||||
* POM_INFO_ARTIFACTID
|
||||
*/
|
||||
public static final String POM_INFO_ARTIFACTID = "info.artifactId";
|
||||
/**
|
||||
* SERVICE_VERSION
|
||||
*/
|
||||
public static final String SERVICE_VERSION = "aivfo.service.version";
|
||||
/**
|
||||
* JASYPT_ENCRYPTOR_PASSWORD
|
||||
*/
|
||||
public static final String JASYPT_ENCRYPTOR_PASSWORD = "jasypt.encryptor.password";
|
||||
/**
|
||||
* 是否开启 banner
|
||||
*/
|
||||
public static final String AIVFO_ENABLE_BANNER = "aivfo.enable-banner";
|
||||
/**
|
||||
* 应用分组
|
||||
*/
|
||||
public static final String AIVFO_APP_GROUP = "aivfo.app.group";
|
||||
/**
|
||||
* aivfo_APP_CONFIG_GROUP
|
||||
*/
|
||||
public static final String AIVFO_APP_CONFIG_GROUP = "aivfo.app.config-group";
|
||||
/**
|
||||
* DISCOVERY_GROUP
|
||||
*/
|
||||
public static final String AIVFO_APP_DISCOVERY_GROUP = "aivfo.app.discovery-group";
|
||||
|
||||
@UtilityClass
|
||||
public static class SpringConfigKey {
|
||||
/**
|
||||
* SERVER_CONTEXT_PATH
|
||||
*/
|
||||
public static final String SERVER_CONTEXT_PATH = "server.servlet.context-path";
|
||||
/**
|
||||
* SERVER_PORT
|
||||
*/
|
||||
public static final String SERVER_PORT = "server.port";
|
||||
/**
|
||||
* MANAGEMENT_SERVER_PORT
|
||||
*/
|
||||
public static final String MANAGEMENT_SERVER_PORT = "management.server.port";
|
||||
/**
|
||||
* PROP_APPLICATION_NAME
|
||||
*/
|
||||
public static final String APPLICATION_NAME = "spring.application.name";
|
||||
/**
|
||||
* PACKAGE_NAME
|
||||
*/
|
||||
public static final String PACKAGE_NAME = "package.name";
|
||||
/**
|
||||
* PROFILE_ACTIVE
|
||||
*/
|
||||
public static final String PROFILE_ACTIVE = "spring.profiles.active";
|
||||
/**
|
||||
* MAIN_ALLOW_BEAN_DEFINITION_OVERRIDING
|
||||
*/
|
||||
public static final String MAIN_ALLOW_BEAN_DEFINITION_OVERRIDING = "spring.main.allow-bean-definition-overriding";
|
||||
/**
|
||||
* 根据此配置判断是否创建 pid 文件
|
||||
*/
|
||||
public static final String PID_FILE = "spring.pid.file";
|
||||
/**
|
||||
* 排除的自动装配类
|
||||
*/
|
||||
public static final String AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
|
||||
/**
|
||||
* jackson 序列化配置
|
||||
*/
|
||||
public static final String JACKSON_DEFAULT_PROPERTY_INCLUSION = "spring.jackson.default-property-inclusion";
|
||||
/**
|
||||
* DATASOURCE_URL
|
||||
*/
|
||||
public static final String DATASOURCE_URL = "spring.datasource.url";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.aivfo.el.starter.base.contex;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
|
||||
@UtilityClass
|
||||
public class AgentRequestContextHolder {
|
||||
|
||||
/**
|
||||
* inheritableRequestAttributesHolder
|
||||
*/
|
||||
private static final TransmittableThreadLocal<RequestAttributes> INHERITABLE_REQUEST_ATTRIBUTES_HOLDER
|
||||
= new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* Reset the RequestAttributes for the current thread.
|
||||
*
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static void resetRequestAttributes() {
|
||||
INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the given RequestAttributes to the current thread,
|
||||
* <i>not</i> exposing it as inheritable for child threads.
|
||||
*
|
||||
* @param attributes the RequestAttributes to expose
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
|
||||
if (attributes == null) {
|
||||
resetRequestAttributes();
|
||||
} else {
|
||||
INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.set(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RequestAttributes currently bound to the thread.
|
||||
*
|
||||
* @return the RequestAttributes currently bound to the thread, or {@code null} if none bound
|
||||
* @since 1.8.0
|
||||
*/
|
||||
@Nullable
|
||||
public static RequestAttributes getRequestAttributes() {
|
||||
return INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Current request attributes
|
||||
*
|
||||
* @return the request attributes
|
||||
* @throws IllegalStateException illegal state exception
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
|
||||
RequestAttributes attributes = getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
throw new IllegalStateException("当前线程不存在 Request 对象");
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.aivfo.el.starter.base.contex;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 全局上下文
|
||||
*/
|
||||
@Slf4j
|
||||
public final class GlobalContext {
|
||||
|
||||
/**
|
||||
* 公共使用
|
||||
*/
|
||||
private static final TransmittableThreadLocal<Map<String, Object>> COMMON = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* common
|
||||
*
|
||||
* @param key key
|
||||
* @return the transmittable thread local
|
||||
* @since 2022.1.1
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static @Nullable Object get(String key) {
|
||||
Map<String, Object> cache = COMMON.get();
|
||||
if (cache == null) {
|
||||
return null;
|
||||
} else {
|
||||
return cache.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
* @since 2022.1.1
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static void put(String key, Object value) {
|
||||
Map<String, Object> cache = COMMON.get();
|
||||
if (cache == null) {
|
||||
cache = new HashMap<>(8);
|
||||
cache.put(key, value);
|
||||
COMMON.set(cache);
|
||||
} else if (cache.get(key) != null) {
|
||||
// 直接覆盖
|
||||
cache.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear
|
||||
*
|
||||
* @param key key
|
||||
* @since 2022.1.1
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static void clear(String key) {
|
||||
final Map<String, Object> cache = COMMON.get();
|
||||
if (cache != null) {
|
||||
cache.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
package com.aivfo.el.base.contex;
|
||||
package com.aivfo.el.starter.base.contex;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import lombok.experimental.UtilityClass;
|
||||
@@ -6,11 +6,11 @@ import org.jetbrains.annotations.Contract;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/14
|
||||
* @description: 链路id的上下文
|
||||
* @date: 2023/5/16
|
||||
* @description: TraceId 用来存储traceID相关信息
|
||||
*/
|
||||
@UtilityClass
|
||||
public class TraceContext {
|
||||
public class Trace {
|
||||
/**
|
||||
* context
|
||||
*/
|
||||
@@ -23,8 +23,8 @@ public class TraceContext {
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static String getTraceId() {
|
||||
return CONTEXT.get();
|
||||
public static TransmittableThreadLocal<String> context() {
|
||||
return CONTEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
package com.aivfo.el.base.exception;
|
||||
package com.aivfo.el.starter.base.exception;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/14
|
||||
* @description: Assert异常信息
|
||||
*/
|
||||
public class AssertionFailedException extends AivfoBaseException {
|
||||
public class AssertionFailedException extends BasicException {
|
||||
|
||||
/**
|
||||
* DEFAULT_MESSAGE
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package com.aivfo.el.starter.base.exception;
|
||||
|
||||
import com.aivfo.el.starter.base.contex.Trace;
|
||||
import com.aivfo.el.starter.base.support.StrFormatter;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/14
|
||||
* @description: 统一异常
|
||||
*/
|
||||
@Slf4j
|
||||
public class BasicException extends RuntimeException {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = 3076052230646484392L;
|
||||
/**
|
||||
* 异常消息参数
|
||||
*/
|
||||
protected transient Object[] args;
|
||||
/**
|
||||
* Code
|
||||
*/
|
||||
@Getter
|
||||
protected String code;
|
||||
/**
|
||||
* Message
|
||||
*/
|
||||
protected String message;
|
||||
/**
|
||||
* Trace id
|
||||
*/
|
||||
@Getter
|
||||
protected String traceId;
|
||||
/**
|
||||
* DEFAULT_ERROR_CODE
|
||||
*/
|
||||
public static final String DEFAULT_ERROR_CODE = "B.F-5000";
|
||||
/**
|
||||
* DEFAULT_MESSAGE
|
||||
*/
|
||||
public static final String DEFAULT_MESSAGE = "服务内部错误";
|
||||
|
||||
/**
|
||||
* Basic exception
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public BasicException() {
|
||||
this(DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* msg 占位符替换
|
||||
*
|
||||
* @param message message
|
||||
* @param args args
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BasicException(String message, Object... args) {
|
||||
this(StrFormatter.mergeFormat(message, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Base exception.
|
||||
*
|
||||
* @param message message
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BasicException(String message) {
|
||||
this(DEFAULT_ERROR_CODE, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic exception
|
||||
*
|
||||
* @param code code
|
||||
* @param message message
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public BasicException(String code, String message) {
|
||||
super(message);
|
||||
this.init(code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base exception
|
||||
*
|
||||
* @param cause cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BasicException(Throwable cause) {
|
||||
this(DEFAULT_MESSAGE, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base exception
|
||||
*
|
||||
* @param message message
|
||||
* @param cause cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BasicException(String message, Throwable cause) {
|
||||
this(DEFAULT_ERROR_CODE, message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic exception
|
||||
*
|
||||
* @param code code
|
||||
* @param message message
|
||||
* @param cause cause
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public BasicException(String code, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.init(code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* @param code code
|
||||
* @param message message
|
||||
* @since 1.9.0
|
||||
*/
|
||||
private void init(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.traceId = Trace.context().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写打印异常堆栈, 转为日志输出.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
public void printStackTrace() {
|
||||
log.error("", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints this throwable and its backtrace to the specified print stream.
|
||||
*
|
||||
* @param s {@code PrintStream} to use for output
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@Override
|
||||
public void printStackTrace(PrintStream s) {
|
||||
log.error("", this);
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
package com.aivfo.el.starter.base.exception;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 服务内部错误异常, 会输出堆栈信息, traceId, 当前应用名和 ip
|
||||
*/
|
||||
@Slf4j
|
||||
public class ServiceInternalException extends RuntimeException {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = -3466744408568354601L;
|
||||
|
||||
/**
|
||||
* 应用名
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
protected String applicationName;
|
||||
/**
|
||||
* 应用 ip
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
protected String ip;
|
||||
/**
|
||||
* 应用端口
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
protected Integer port;
|
||||
/**
|
||||
* 当前环境
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
protected String env;
|
||||
/**
|
||||
* Rpc
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
protected boolean rpc;
|
||||
/**
|
||||
* Trace id
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
protected String traceId;
|
||||
/**
|
||||
* DEFAULT_ERROR_CODE
|
||||
*/
|
||||
public static final String DEFAULT_ERROR_CODE = "S.I-5000";
|
||||
/**
|
||||
* DEFAULT_MESSAGE
|
||||
*/
|
||||
public static final String DEFAULT_MESSAGE = "服务内部错误";
|
||||
|
||||
/**
|
||||
* Basic exception
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public ServiceInternalException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Service internal exception
|
||||
*
|
||||
* @param throwable throwable
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public ServiceInternalException(Throwable throwable) {
|
||||
super(DEFAULT_MESSAGE, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service internal exception
|
||||
*
|
||||
* @param message message
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public ServiceInternalException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service internal exception
|
||||
*
|
||||
* @param message message
|
||||
* @param throwable throwable
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public ServiceInternalException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写打印异常堆栈, 转为日志输出.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Override
|
||||
public void printStackTrace() {
|
||||
log.error("", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints this throwable and its backtrace to the specified print stream.
|
||||
*
|
||||
* @param s {@code PrintStream} to use for output
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Override
|
||||
public void printStackTrace(PrintStream s) {
|
||||
log.error("", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets message *
|
||||
*
|
||||
* @return the message
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return super.getMessage()
|
||||
+ (StringUtils.isBlank(this.applicationName) ? "" : ": applicationName='" + this.applicationName + '\'')
|
||||
+ (StringUtils.isBlank(this.ip) ? "" : ", ip='" + this.ip + '\'')
|
||||
+ (this.port == null ? "" : ", port='" + this.port + '\'')
|
||||
+ (StringUtils.isBlank(this.env) ? "" : ", env='" + this.env + '\'')
|
||||
+ (StringUtils.isBlank(this.traceId) ? "" : ", traceId='" + this.traceId);
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.aivfo.el.starter.base.exception;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 超时工具异常
|
||||
*/
|
||||
public class TimeoutUtilsExecuteException extends Exception {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = 5896555103722435483L;
|
||||
|
||||
/**
|
||||
* Timeout utils execute exception
|
||||
*
|
||||
* @param message message
|
||||
* @since 1.7.1
|
||||
*/
|
||||
public TimeoutUtilsExecuteException(String message) {
|
||||
super(message);
|
||||
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
package com.aivfo.el.base.serialize;
|
||||
package com.aivfo.el.starter.base.serialize;
|
||||
|
||||
import com.aivfo.el.base.utils.AivfoStringUtils;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
@@ -21,7 +21,7 @@ public class StringTrimmerDeserializer extends JsonDeserializer<String> {
|
||||
@Override
|
||||
@SuppressWarnings("java:S3252")
|
||||
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
||||
return AivfoStringUtils.trimWhitespace(jsonParser.getValueAsString());
|
||||
return StringUtils.trimWhitespace(jsonParser.getValueAsString());
|
||||
}
|
||||
|
||||
}
|
||||
+4
-3
@@ -1,9 +1,10 @@
|
||||
package com.aivfo.el.base.serialize;
|
||||
package com.aivfo.el.starter.base.serialize;
|
||||
|
||||
import com.aivfo.el.base.utils.AivfoStringUtils;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StringTrimmerSerializer extends JsonSerializer<String> {
|
||||
@@ -20,6 +21,6 @@ public class StringTrimmerSerializer extends JsonSerializer<String> {
|
||||
@Override
|
||||
@SuppressWarnings("java:S3252")
|
||||
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
jsonGenerator.writeString(AivfoStringUtils.trimWhitespace(s));
|
||||
jsonGenerator.writeString(StringUtils.trimWhitespace(s));
|
||||
}
|
||||
}
|
||||
+8
-6
@@ -1,6 +1,6 @@
|
||||
package com.aivfo.el.base.support;
|
||||
package com.aivfo.el.starter.base.support;
|
||||
|
||||
import com.aivfo.el.base.utils.AivfoStringPool;
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -12,7 +12,9 @@ import java.io.Writer;
|
||||
* @description: 借助 StringBuilder 提供快读的字符串写出, 相比 jdk 的 StringWriter 非线程安全, 速度更快.
|
||||
*/
|
||||
public class FastStringWriter extends Writer {
|
||||
/** Builder */
|
||||
/**
|
||||
* Builder
|
||||
*/
|
||||
private final StringBuilder builder;
|
||||
|
||||
/**
|
||||
@@ -90,7 +92,7 @@ public class FastStringWriter extends Writer {
|
||||
@Override
|
||||
public void write(char[] cbuilder, int off, int len) {
|
||||
if ((off < 0) || (off > cbuilder.length)
|
||||
|| (len < 0) || ((off + len) > cbuilder.length) || ((off + len) < 0)) {
|
||||
|| (len < 0) || ((off + len) > cbuilder.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return;
|
||||
@@ -132,7 +134,7 @@ public class FastStringWriter extends Writer {
|
||||
@Override
|
||||
public FastStringWriter append(CharSequence csq) {
|
||||
if (csq == null) {
|
||||
this.write(AivfoStringPool.NULL);
|
||||
this.write(StringPool.NULL);
|
||||
} else {
|
||||
this.write(csq.toString());
|
||||
}
|
||||
@@ -150,7 +152,7 @@ public class FastStringWriter extends Writer {
|
||||
*/
|
||||
@Override
|
||||
public FastStringWriter append(CharSequence csq, int start, int end) {
|
||||
CharSequence cs = (csq == null ? AivfoStringPool.NULL : csq);
|
||||
CharSequence cs = (csq == null ? StringPool.NULL : csq);
|
||||
this.write(cs.subSequence(start, end).toString());
|
||||
return this;
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package com.aivfo.el.starter.base.support;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.CharPool;
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 字符串格式化
|
||||
*/
|
||||
@UtilityClass
|
||||
public class StrFormatter {
|
||||
|
||||
/**
|
||||
* 格式化字符串<br>
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||
* 例: <br>
|
||||
* 通常使用: format("this is {} for {}", "a", "b") =》 this is a for b<br>
|
||||
* 转义{}: format("this is \\{} for {}", "a", "b") =》 this is \{} for a<br>
|
||||
* 转义\: format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br>
|
||||
*
|
||||
* @param strPattern 字符串模板
|
||||
* @param argArray 参数列表
|
||||
* @return 结果 string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:ReturnCount")
|
||||
public static String format(String strPattern, Object... argArray) {
|
||||
boolean isEmpty = argArray == null || argArray.length == 0;
|
||||
if (!StringUtils.hasText(strPattern) || isEmpty) {
|
||||
return strPattern;
|
||||
}
|
||||
int strPatternLength = strPattern.length();
|
||||
// 初始化定义好的长度以获得更好的性能
|
||||
StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
|
||||
// 记录已经处理到的位置
|
||||
int handledPosition = 0;
|
||||
// 占位符所在位置
|
||||
int delimIndex;
|
||||
for (int argIndex = 0, length = argArray.length; argIndex < length; argIndex++) {
|
||||
delimIndex = strPattern.indexOf(StringPool.EMPTY_JSON, handledPosition);
|
||||
// 剩余部分无占位符
|
||||
if (delimIndex == -1) {
|
||||
// 不带占位符的模板直接返回
|
||||
if (handledPosition == 0) {
|
||||
return strPattern;
|
||||
} else {
|
||||
sbuf.append(strPattern, handledPosition, strPatternLength);
|
||||
return sbuf.toString();
|
||||
}
|
||||
} else {
|
||||
// 转义符
|
||||
if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == CharPool.BACK_SLASH) {
|
||||
// 双转义符
|
||||
if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == CharPool.BACK_SLASH) {
|
||||
// 转义符之前还有一个转义符,占位符依旧有效
|
||||
sbuf.append(strPattern, handledPosition, delimIndex - 1);
|
||||
sbuf.append(toStr(argArray[argIndex]));
|
||||
handledPosition = delimIndex + 2;
|
||||
} else {
|
||||
//占位符被转义
|
||||
argIndex--;
|
||||
sbuf.append(strPattern, handledPosition, delimIndex - 1);
|
||||
sbuf.append(StringPool.LEFT_BRACE);
|
||||
handledPosition = delimIndex + 1;
|
||||
}
|
||||
} else {
|
||||
// 正常占位符
|
||||
sbuf.append(strPattern, handledPosition, delimIndex);
|
||||
sbuf.append(toStr(argArray[argIndex]));
|
||||
handledPosition = delimIndex + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加入最后一个占位符后所有的字符
|
||||
sbuf.append(strPattern, handledPosition, strPattern.length());
|
||||
return sbuf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To str string
|
||||
*
|
||||
* @param str str
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static String toStr(Object str) {
|
||||
return toStr(str, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 强转string,并去掉多余空格
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param defaultValue 默认值
|
||||
* @return String string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(value = "null, _ -> param2", pure = true)
|
||||
public static String toStr(Object str, String defaultValue) {
|
||||
if (null == str) {
|
||||
return defaultValue;
|
||||
}
|
||||
return String.valueOf(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同时兼容 {} 和 {0} 2 种格式
|
||||
*
|
||||
* @param value value
|
||||
* @param params params
|
||||
* @return the string
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@NotNull
|
||||
public static String mergeFormat(@NotNull String value, @NotNull Object... params) {
|
||||
return params.length > 0 && value.indexOf('{') >= 0
|
||||
? value.contains("{0")
|
||||
? MessageFormat.format(value, params)
|
||||
: format(value, params)
|
||||
: value;
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: char 常量池
|
||||
*/
|
||||
@UtilityClass
|
||||
@SuppressWarnings("all")
|
||||
public class CharPool {
|
||||
/**
|
||||
* UPPER_A
|
||||
*/
|
||||
public static final char UPPER_A = 'A';
|
||||
/**
|
||||
* LOWER_A
|
||||
*/
|
||||
public static final char LOWER_A = 'a';
|
||||
/**
|
||||
* UPPER_Z
|
||||
*/
|
||||
public static final char UPPER_Z = 'Z';
|
||||
/**
|
||||
* LOWER_Z
|
||||
*/
|
||||
public static final char LOWER_Z = 'z';
|
||||
/**
|
||||
* DOT
|
||||
*/
|
||||
public static final char DOT = '.';
|
||||
/**
|
||||
* AT
|
||||
*/
|
||||
public static final char AT = '@';
|
||||
/**
|
||||
* LEFT_BRACE
|
||||
*/
|
||||
public static final char LEFT_BRACE = '{';
|
||||
/**
|
||||
* RIGHT_BRACE
|
||||
*/
|
||||
public static final char RIGHT_BRACE = '}';
|
||||
/**
|
||||
* LEFT_BRACKET
|
||||
*/
|
||||
public static final char LEFT_BRACKET = '(';
|
||||
/**
|
||||
* RIGHT_BRACKET
|
||||
*/
|
||||
public static final char RIGHT_BRACKET = ')';
|
||||
/**
|
||||
* DASH
|
||||
*/
|
||||
public static final char DASH = '-';
|
||||
/**
|
||||
* PERCENT
|
||||
*/
|
||||
public static final char PERCENT = '%';
|
||||
/**
|
||||
* PIPE
|
||||
*/
|
||||
public static final char PIPE = '|';
|
||||
/**
|
||||
* PLUS
|
||||
*/
|
||||
public static final char PLUS = '+';
|
||||
/**
|
||||
* QUESTION_MARK
|
||||
*/
|
||||
public static final char QUESTION_MARK = '?';
|
||||
/**
|
||||
* EXCLAMATION_MARK
|
||||
*/
|
||||
public static final char EXCLAMATION_MARK = '!';
|
||||
/**
|
||||
* EQUALS
|
||||
*/
|
||||
public static final char EQUALS = '=';
|
||||
/**
|
||||
* AMPERSAND
|
||||
*/
|
||||
public static final char AMPERSAND = '&';
|
||||
/**
|
||||
* ASTERISK
|
||||
*/
|
||||
public static final char ASTERISK = '*';
|
||||
/**
|
||||
* STAR
|
||||
*/
|
||||
public static final char STAR = ASTERISK;
|
||||
/**
|
||||
* BACK_SLASH
|
||||
*/
|
||||
public static final char BACK_SLASH = '\\';
|
||||
/**
|
||||
* COLON
|
||||
*/
|
||||
public static final char COLON = ':';
|
||||
/**
|
||||
* COMMA
|
||||
*/
|
||||
public static final char COMMA = ',';
|
||||
/**
|
||||
* DOLLAR
|
||||
*/
|
||||
public static final char DOLLAR = '$';
|
||||
/**
|
||||
* SLASH
|
||||
*/
|
||||
public static final char SLASH = '/';
|
||||
/**
|
||||
* HASH
|
||||
*/
|
||||
public static final char HASH = '#';
|
||||
/**
|
||||
* HAT
|
||||
*/
|
||||
public static final char HAT = '^';
|
||||
/**
|
||||
* LEFT_CHEV
|
||||
*/
|
||||
public static final char LEFT_CHEV = '<';
|
||||
/**
|
||||
* NEWLINE
|
||||
*/
|
||||
public static final char NEWLINE = '\n';
|
||||
/**
|
||||
* N
|
||||
*/
|
||||
public static final char N = 'n';
|
||||
/**
|
||||
* Y
|
||||
*/
|
||||
public static final char Y = 'y';
|
||||
/**
|
||||
* QUOTE
|
||||
*/
|
||||
public static final char QUOTE = '\"';
|
||||
/**
|
||||
* RETURN
|
||||
*/
|
||||
public static final char RETURN = '\r';
|
||||
/**
|
||||
* TAB
|
||||
*/
|
||||
public static final char TAB = '\t';
|
||||
/**
|
||||
* RIGHT_CHEV
|
||||
*/
|
||||
public static final char RIGHT_CHEV = '>';
|
||||
/**
|
||||
* SEMICOLON
|
||||
*/
|
||||
public static final char SEMICOLON = ';';
|
||||
/**
|
||||
* SINGLE_QUOTE
|
||||
*/
|
||||
public static final char SINGLE_QUOTE = '\'';
|
||||
/**
|
||||
* BACKTICK
|
||||
*/
|
||||
public static final char BACKTICK = '`';
|
||||
/**
|
||||
* SPACE
|
||||
*/
|
||||
public static final char SPACE = ' ';
|
||||
/**
|
||||
* TILDA
|
||||
*/
|
||||
public static final char TILDA = '~';
|
||||
/**
|
||||
* LEFT_SQ_BRACKET
|
||||
*/
|
||||
public static final char LEFT_SQ_BRACKET = '[';
|
||||
/**
|
||||
* RIGHT_SQ_BRACKET
|
||||
*/
|
||||
public static final char RIGHT_SQ_BRACKET = ']';
|
||||
/**
|
||||
* UNDERSCORE
|
||||
*/
|
||||
public static final char UNDERSCORE = '_';
|
||||
/**
|
||||
* ONE
|
||||
*/
|
||||
public static final char ONE = '1';
|
||||
/**
|
||||
* ZERO
|
||||
*/
|
||||
public static final char ZERO = '0';
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 字符集工具类
|
||||
*/
|
||||
@UtilityClass
|
||||
public class Charsets {
|
||||
|
||||
/**
|
||||
* 字符集ISO-8859-1
|
||||
*/
|
||||
public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
|
||||
/**
|
||||
* The constant ISO_8859_1_NAME.
|
||||
*/
|
||||
public static final String ISO_8859_1_NAME = ISO_8859_1.name();
|
||||
/**
|
||||
* 字符集GBK
|
||||
*/
|
||||
public static final Charset GBK = of("GBK");
|
||||
/**
|
||||
* The constant GBK_NAME.
|
||||
*/
|
||||
public static final String GBK_NAME = GBK.name();
|
||||
/**
|
||||
* 字符集utf-8
|
||||
*/
|
||||
public static final Charset UTF_8 = StandardCharsets.UTF_8;
|
||||
/**
|
||||
* The constant UTF_8_NAME.
|
||||
*/
|
||||
public static final String UTF_8_NAME = UTF_8.name();
|
||||
|
||||
/**
|
||||
* 转换为Charset对象
|
||||
*
|
||||
* @param charsetName 字符集,为空则返回默认字符集
|
||||
* @return Charsets charset
|
||||
* @throws UnsupportedCharsetException 编码不支持
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static Charset of(String charsetName) throws UnsupportedCharsetException {
|
||||
return StringUtils.hasText(charsetName) ? Charset.forName(charsetName) : Charset.defaultCharset();
|
||||
}
|
||||
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import com.aivfo.el.starter.base.exception.BasicException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 类工具类
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings("all")
|
||||
public abstract class ClassUtils extends org.springframework.util.ClassUtils {
|
||||
|
||||
/**
|
||||
* PARAMETER_NAME_DISCOVERER
|
||||
*/
|
||||
public static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||
/**
|
||||
* PACKAGE_SEPARATOR
|
||||
*/
|
||||
public static final char PACKAGE_SEPARATOR = '.';
|
||||
/**
|
||||
* 代理 class 的名称
|
||||
*/
|
||||
protected static final List<String> PROXY_CLASS_NAMES = Arrays.asList(
|
||||
"net.sf.cglib.proxy.Factory",
|
||||
"org.springframework.cglib.proxy.Factory",
|
||||
"javassist.util.proxy.ProxyObject",
|
||||
"org.apache.ibatis.javassist.util.proxy.ProxyObject");
|
||||
|
||||
/**
|
||||
* 获取方法参数信息
|
||||
*
|
||||
* @param constructor 构造器
|
||||
* @param parameterIndex 参数序号
|
||||
* @return {MethodParameter}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull MethodParameter getMethodParameter(Constructor<?> constructor, int parameterIndex) {
|
||||
MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex);
|
||||
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
|
||||
return methodParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方法参数信息
|
||||
*
|
||||
* @param method 方法
|
||||
* @param parameterIndex 参数序号
|
||||
* @return {MethodParameter}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull MethodParameter getMethodParameter(Method method, int parameterIndex) {
|
||||
MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex);
|
||||
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
|
||||
return methodParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Annotation
|
||||
*
|
||||
* @param <A> 泛型标记
|
||||
* @param method Method
|
||||
* @param annotationType 注解类
|
||||
* @return {Annotation}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static <A extends Annotation> A getAnnotation(@NotNull Method method, Class<A> annotationType) {
|
||||
Class<?> targetClass = method.getDeclaringClass();
|
||||
// The method may be on an interface, but we need attributes from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||
// If we are dealing with method with generic parameters, find the original method.
|
||||
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
// 先找方法,再找方法上的类
|
||||
A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType);
|
||||
if (null != annotation) {
|
||||
return annotation;
|
||||
}
|
||||
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
|
||||
return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Annotation
|
||||
*
|
||||
* @param <A> 泛型标记
|
||||
* @param handlerMethod HandlerMethod
|
||||
* @param annotationType 注解类
|
||||
* @return {Annotation}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static <A extends Annotation> A getAnnotation(@NotNull HandlerMethod handlerMethod, Class<A> annotationType) {
|
||||
// 先找方法,再找方法上的类
|
||||
A annotation = handlerMethod.getMethodAnnotation(annotationType);
|
||||
if (null != annotation) {
|
||||
return annotation;
|
||||
}
|
||||
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
|
||||
Class<?> beanType = handlerMethod.getBeanType();
|
||||
return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口上的泛型T
|
||||
*
|
||||
* @param clazz clazz
|
||||
* @param interfaceClass interface class
|
||||
* @param index 泛型索引
|
||||
* @return the interface t
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Class<?> getInterfaceT(@NotNull Class<?> clazz, Class<?> interfaceClass, int index) {
|
||||
Type[] types = clazz.getGenericInterfaces();
|
||||
Type targetType = Arrays.stream(types).filter(type -> type.toString().contains(interfaceClass.getName())).findAny().orElse(null);
|
||||
if (targetType == null) {
|
||||
throw new BasicException("[{}] 未实现 [{}] 接口", clazz, interfaceClass);
|
||||
}
|
||||
ParameterizedType parameterizedType = (ParameterizedType) targetType;
|
||||
Type type = parameterizedType.getActualTypeArguments()[index];
|
||||
return checkType(type, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取父类上的泛型
|
||||
*
|
||||
* @param clazz clazz
|
||||
* @param index index
|
||||
* @return the class
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Class<?> getSuperClassT(@NotNull Class<?> clazz, @NotNull Integer index) {
|
||||
Type type = clazz.getGenericSuperclass();
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
Type actType = parameterizedType.getActualTypeArguments()[index];
|
||||
return checkType(actType, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归获取泛型类型
|
||||
*
|
||||
* @param type type
|
||||
* @param index index
|
||||
* @return the class
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("null, _ -> fail")
|
||||
private static @NotNull Class<?> checkType(Type type, int index) {
|
||||
if (type instanceof Class<?>) {
|
||||
return (Class<?>) type;
|
||||
} else if (type instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
Type t = parameterizedType.getActualTypeArguments()[index];
|
||||
return checkType(t, index);
|
||||
} else {
|
||||
String className = type == null ? "null" : type.getClass().getName();
|
||||
throw new BasicException("Expected a Class, ParameterizedType" + ", but <" + type + "> is of type " + className);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断传入的类型是否是布尔类型
|
||||
*
|
||||
* @param type 类型
|
||||
* @return 如果是原生布尔或者包装类型布尔 , 均返回 true
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static boolean isBoolean(Class<?> type) {
|
||||
return type == boolean.class || Boolean.class == type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为代理对象
|
||||
*
|
||||
* @param clazz 传入 class 对象
|
||||
* @return 如果对象class是代理 class, 返回 true
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("null -> false")
|
||||
public static boolean isProxy(Class<?> clazz) {
|
||||
if (clazz != null) {
|
||||
for (Class<?> cls : clazz.getInterfaces()) {
|
||||
if (PROXY_CLASS_NAMES.contains(cls.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前对象的 class
|
||||
*
|
||||
* @param clazz 传入
|
||||
* @return 如果是代理的class , 返回父 class, 否则返回自身
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Class<?> getClass(Class<?> clazz) {
|
||||
return isProxy(clazz) ? clazz.getSuperclass() : clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前对象的class
|
||||
*
|
||||
* @param object 对象
|
||||
* @return 返回对象的 user class
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Class<?> getClass(Object object) {
|
||||
Assert.notNull(object, "Error: Instance must not be null");
|
||||
return getClass(object.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的 class , 实例化一个对象, 根据构造参数来实例化
|
||||
* 在 java9 及其之后的版本 Class.newInstance() 方法已被废弃
|
||||
*
|
||||
* @param <T> 类型, 由输入类型决定
|
||||
* @param clazz 需要实例化的对象
|
||||
* @return 返回新的实例 @ not null t
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static <T> @NotNull T newInstance(@NotNull Class<T> clazz) {
|
||||
try {
|
||||
Constructor<T> constructor = clazz.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
throw new BasicException("实例化对象时出现错误,请尝试给 %s 添加无参的构造方法", e, clazz.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请仅在确定类存在的情况下调用该方法
|
||||
*
|
||||
* @param name 类名称
|
||||
* @return 返回转换后的 Class
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Class<?> toClassConfident(String name) {
|
||||
try {
|
||||
return Class.forName(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new BasicException("找不到指定的class!请仅在明确确定会有 class 的时候, 调用该方法", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the name of the package of the given class,
|
||||
* e.g. "java.lang" for the {@code java.lang.String} class.
|
||||
*
|
||||
* @param clazz the class
|
||||
* @return the package name, or the empty String if the class is defined in the default package
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull String getPackageName(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
return getPackageName(clazz.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the name of the package of the given fully-qualified class name,
|
||||
* e.g. "java.lang" for the {@code java.lang.String} class name.
|
||||
*
|
||||
* @param fqClassName the fully-qualified class name
|
||||
* @return the package name, or the empty String if the class is defined in the default package
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull String getPackageName(String fqClassName) {
|
||||
Assert.notNull(fqClassName, "Class name must not be null");
|
||||
int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR);
|
||||
return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : "");
|
||||
}
|
||||
|
||||
}
|
||||
+14
-7
@@ -1,4 +1,4 @@
|
||||
package com.aivfo.el.base.utils;
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -14,19 +14,26 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/18
|
||||
* @date: 2023/5/16
|
||||
* @description: 安全的时间格式化
|
||||
* 参考 tomcat8 中的并发 DateFormat, {@link SimpleDateFormat}的线程安全包装器. 不使用ThreadLocal,创建足够的SimpleDateFormat对象来满足并发性要求.
|
||||
*/
|
||||
public final class ConcurrentDateFormat {
|
||||
/** Format */
|
||||
/**
|
||||
* Format
|
||||
*/
|
||||
@Getter
|
||||
private final String format;
|
||||
/** Locale */
|
||||
/**
|
||||
* Locale
|
||||
*/
|
||||
private final Locale locale;
|
||||
/** Timezone */
|
||||
/**
|
||||
* Timezone
|
||||
*/
|
||||
private final TimeZone timezone;
|
||||
/** Queue */
|
||||
/**
|
||||
* Queue
|
||||
*/
|
||||
private final Queue<SimpleDateFormat> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
/**
|
||||
+14
-9
@@ -1,8 +1,8 @@
|
||||
package com.aivfo.el.base.utils;
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import com.aivfo.el.base.exception.AivfoBaseException;
|
||||
import com.aivfo.el.base.support.FastStringWriter;
|
||||
import com.aivfo.el.starter.base.exception.BasicException;
|
||||
import com.aivfo.el.starter.base.support.FastStringWriter;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -12,9 +12,14 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/18
|
||||
* @description: 异常处理工具类
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 异常处理工具类 </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2019.11.21 09:59
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@UtilityClass
|
||||
public class Exceptions extends ExceptionUtil {
|
||||
@@ -44,12 +49,12 @@ public class Exceptions extends ExceptionUtil {
|
||||
public static RuntimeException unchecked(String message, Throwable e) {
|
||||
RuntimeException runtimeException;
|
||||
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|
||||
|| e instanceof NoSuchMethodException) {
|
||||
|| e instanceof NoSuchMethodException) {
|
||||
runtimeException = new IllegalArgumentException(e);
|
||||
} else if (e instanceof InvocationTargetException) {
|
||||
runtimeException = new RuntimeException(((InvocationTargetException) e).getTargetException());
|
||||
} else if (e instanceof AivfoBaseException) {
|
||||
runtimeException = (AivfoBaseException) e;
|
||||
} else if (e instanceof BasicException) {
|
||||
runtimeException = (BasicException) e;
|
||||
} else if (e instanceof RuntimeException) {
|
||||
runtimeException = (RuntimeException) e;
|
||||
} else {
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.aivfo.el.starter.base.support.StrFormatter;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 修改 hosts 文件
|
||||
*/
|
||||
public abstract class HostUtils {
|
||||
|
||||
/**
|
||||
* SPLITTER
|
||||
*/
|
||||
private static final String SPLITTER = " ";
|
||||
|
||||
/**
|
||||
* 读取 hosts, 排除注释项, 将一行记录解析为 [ip + 空格 + domain}
|
||||
*
|
||||
* @return the set
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static Set<String> read() {
|
||||
return FileUtil.readLines(getHostFile(), Charsets.UTF_8).stream()
|
||||
.filter(it -> !it.trim().matches("(^#.*)|(\\s*)"))
|
||||
.map(it -> it.replaceAll("#.*", "").trim().replaceAll("\\s+", SPLITTER))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine
|
||||
*
|
||||
* @param ip ip
|
||||
* @param domain domain
|
||||
* @return the string
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static @NotNull String combine(String ip, String domain) {
|
||||
if (StringUtils.isBlank(domain)) {
|
||||
throw new IllegalArgumentException("ERROR: domain must be specified");
|
||||
}
|
||||
return (ip + SPLITTER + domain).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exists
|
||||
*
|
||||
* @param domain domain
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static boolean exists(String domain) {
|
||||
return exists(StringPool.EMPTY, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exists
|
||||
*
|
||||
* @param ip ip
|
||||
* @param domain domain
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static boolean exists(String ip, String domain) {
|
||||
return exists(read(), ip, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exists
|
||||
*
|
||||
* @param records records
|
||||
* @param ip ip
|
||||
* @param domain domain
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static boolean exists(@NotNull Set<String> records, String ip, String domain) {
|
||||
String combine = combine(ip, domain);
|
||||
for (String record : records) {
|
||||
if (record.contains(combine)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ips
|
||||
*
|
||||
* @param records records
|
||||
* @param domain domain
|
||||
* @return the set
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static @NotNull Set<String> getIps(@NotNull Set<String> records, String domain) {
|
||||
Set<String> ips = new HashSet<>(4);
|
||||
|
||||
String combine = combine(StringPool.EMPTY, domain);
|
||||
for (String record : records) {
|
||||
if (record.contains(combine)) {
|
||||
ips.add(ip(record));
|
||||
}
|
||||
}
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ip
|
||||
*
|
||||
* @param record record
|
||||
* @return the string
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static String ip(@NotNull String record) {
|
||||
String[] split = check(record);
|
||||
return split[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Doamin
|
||||
*
|
||||
* @param record record
|
||||
* @return the string
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static String doamin(@NotNull String record) {
|
||||
String[] split = check(record);
|
||||
return split[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check
|
||||
*
|
||||
* @param record record
|
||||
* @return the string [ ]
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@NotNull
|
||||
@SuppressWarnings("PMD.UndefineMagicConstantRule")
|
||||
private static String[] check(@NotNull String record) {
|
||||
String[] split = record.split(SPLITTER);
|
||||
if (split.length != 2) {
|
||||
throw new IllegalArgumentException("ERROR: ip & domain must be specified");
|
||||
}
|
||||
return split;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 host 文件路径
|
||||
*
|
||||
* @return host file
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static @NotNull String getHostFile() {
|
||||
String fileName;
|
||||
|
||||
// 判断系统
|
||||
if (SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC) {
|
||||
fileName = "/etc/hosts";
|
||||
} else {
|
||||
fileName = System.getenv("windir") + "\\system32\\drivers\\etc\\hosts";
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据输入 IP 和 Domain, 删除 host 文件中的某个 host 配置
|
||||
*
|
||||
* @param ip ip
|
||||
* @param domain domain
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static synchronized void deleteHost(String ip, String domain) {
|
||||
String record = combine(ip, domain);
|
||||
|
||||
if (exists(ip, domain)) {
|
||||
Set<String> lines = read();
|
||||
lines.removeIf(s -> s.equals(record));
|
||||
FileUtil.writeLines(lines, getHostFile(), Charsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据输入 IP 和 Domain, 更新 host 文件中的某个 host 配置
|
||||
*
|
||||
* @param ip ip
|
||||
* @param domain domain
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static synchronized void updateHost(String ip, String domain) {
|
||||
if (StringUtils.isAnyBlank(ip, domain)) {
|
||||
throw new IllegalArgumentException("ERROR: ip & domain must be specified");
|
||||
}
|
||||
|
||||
String record = combine(ip, domain);
|
||||
// 先删除, 如果不存在则不操作
|
||||
deleteHost(ip, domain);
|
||||
|
||||
Set<String> lines = read();
|
||||
lines.add(record);
|
||||
FileUtil.writeLines(lines, getHostFile(), Charsets.UTF_8);
|
||||
|
||||
System.err.println(StrFormatter.format("更新 hosts: {} {}", ip, domain));
|
||||
}
|
||||
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: IoUtil
|
||||
*/
|
||||
@UtilityClass
|
||||
public class IoUtils extends org.springframework.util.StreamUtils {
|
||||
|
||||
/**
|
||||
* byte[] 转 String, 编码默认 UTF-8
|
||||
*
|
||||
* @param input input
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static String toString(byte[] input) {
|
||||
return toString(input, Charsets.UTF_8_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的字符编码以字符串形式获取 byte[] 的内容
|
||||
*
|
||||
* @param input the byte array to read from
|
||||
* @param encoding the encoding to use, null means platform default
|
||||
* @return the requested String
|
||||
* @throws NullPointerException if the input is null
|
||||
* @throws IOException if an I/O error occurs (never occurs)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract("_, _ -> new")
|
||||
public static String toString(byte[] input, String encoding) {
|
||||
return new String(input, Charsets.of(encoding));
|
||||
}
|
||||
|
||||
/**
|
||||
* InputStream to String utf-8
|
||||
*
|
||||
* @param input the <code>InputStream</code> to read from
|
||||
* @return the requested String
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static String toString(InputStream input) {
|
||||
return toString(input, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* InputStream to String
|
||||
*
|
||||
* @param input the <code>InputStream</code> to read from
|
||||
* @param charset the <code>Charsets</code>
|
||||
* @return the requested String
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static String toString(@Nullable InputStream input, java.nio.charset.Charset charset) {
|
||||
try {
|
||||
return IoUtils.copyToString(input, charset);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* closeQuietly
|
||||
*
|
||||
* @param closeable 自动关闭
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void closeQuietly(@Nullable Closeable closeable) {
|
||||
try {
|
||||
if (closeable != null) {
|
||||
closeable.close();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To byte array byte [ ].
|
||||
*
|
||||
* @param input the input
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static byte[] toByteArray(@Nullable InputStream input) {
|
||||
try {
|
||||
return IoUtils.copyToByteArray(input);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes chars from a <code>String</code> to bytes on an
|
||||
* <code>OutputStream</code> using the specified character encoding.
|
||||
* <p>
|
||||
* This method uses {@link String#getBytes(String)}.
|
||||
*
|
||||
* @param data the <code>String</code> to write, null ignored
|
||||
* @param output the <code>OutputStream</code> to write to
|
||||
* @param encoding the encoding to use, null means platform default
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void write(@Nullable String data, OutputStream output, java.nio.charset.Charset encoding) throws IOException {
|
||||
if (data != null) {
|
||||
output.write(data.getBytes(encoding));
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
-25
@@ -1,10 +1,10 @@
|
||||
package com.aivfo.el.base.utils;
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import com.aivfo.el.base.asserts.Assertions;
|
||||
import com.aivfo.el.base.constant.ConfigDefaultValue;
|
||||
import com.aivfo.el.base.constant.ConfigKey;
|
||||
import com.aivfo.el.base.serialize.StringTrimmerDeserializer;
|
||||
import com.aivfo.el.base.serialize.StringTrimmerSerializer;
|
||||
import com.aivfo.el.starter.base.asserts.Assertions;
|
||||
import com.aivfo.el.starter.base.constant.ConfigDefaultValue;
|
||||
import com.aivfo.el.starter.base.constant.ConfigKey;
|
||||
import com.aivfo.el.starter.base.serialize.StringTrimmerDeserializer;
|
||||
import com.aivfo.el.starter.base.serialize.StringTrimmerSerializer;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.type.CollectionLikeType;
|
||||
import com.fasterxml.jackson.databind.type.MapType;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -32,8 +33,6 @@ import org.reflections.util.ConfigurationBuilder;
|
||||
import org.reflections.util.FilterBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validator;
|
||||
@@ -44,30 +43,32 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* <p>Description: Jackson工具类
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: Jackson工具类
|
||||
* 系统中使用到 {@link ObjectMapper} 的地方定将用此类进行初始化, 提供 2 种方式:
|
||||
* 1. {@link AivfoJsonUtils#getInstance()};
|
||||
* 2. {@link AivfoJsonUtils#getCopyMapper()};
|
||||
* 1. {@link JsonUtils#getInstance()};
|
||||
* 2. {@link JsonUtils#getCopyMapper()};
|
||||
* 第一种方式将返回一个单例对象, 请确保在使用 {@link ObjectMapper} 的过程中不会修改 {@link ObjectMapper} 的配置, 如果需要定制化 {@link ObjectMapper},
|
||||
* 可使用第二种方式, 此方式将每次返回一个新的 {@link ObjectMapper} 对象.
|
||||
* 此类每个工具方法都进行过重载, 如果需要使用自定义 {@link ObjectMapper} 对 json 处理, 请调用具有 {@link ObjectMapper} 参数的工具方法.
|
||||
* </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2019.12.26 21:35
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
@SuppressWarnings("checkstyle:MethodLimit")
|
||||
public class AivfoJsonUtils {
|
||||
/** PATTERN_DATETIME */
|
||||
public class JsonUtils {
|
||||
/**
|
||||
* PATTERN_DATETIME
|
||||
*/
|
||||
public static final String PATTERN_DATETIME = System.getProperty(ConfigKey.JSON_DATE_FORMAT, ConfigDefaultValue.DEFAULT_DATE_FORMAT);
|
||||
/** Empty array */
|
||||
/**
|
||||
* Empty array
|
||||
*/
|
||||
public static final byte[] EMPTY_ARRAY = new byte[0];
|
||||
/** MESSAGE */
|
||||
/**
|
||||
* MESSAGE
|
||||
*/
|
||||
private static final String MESSAGE = "待解析的数据为空";
|
||||
|
||||
/**
|
||||
@@ -81,7 +82,7 @@ public class AivfoJsonUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例对象, 这里只提供全局的基础配置, 让整个应用配置保持一致, 如果需要其他配置, 请使用 {@link AivfoJsonUtils#getCopyMapper()}.
|
||||
* 获取单例对象, 这里只提供全局的基础配置, 让整个应用配置保持一致, 如果需要其他配置, 请使用 {@link JsonUtils#getCopyMapper()}.
|
||||
*
|
||||
* @return the instance
|
||||
* @since 1.0.0
|
||||
@@ -258,7 +259,7 @@ public class AivfoJsonUtils {
|
||||
@SuppressWarnings("java:S3252")
|
||||
public static String toJson(ObjectMapper mapper, Object object, boolean pretty, boolean always) {
|
||||
if (object == null) {
|
||||
return AivfoStringPool.EMPTY_JSON;
|
||||
return StringPool.EMPTY_JSON;
|
||||
}
|
||||
// 如果是 string, 先转为 object 再转为 json, 避免转义字符
|
||||
if (object instanceof String) {
|
||||
@@ -1064,9 +1065,13 @@ public class AivfoJsonUtils {
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
private static class JacksonHolder {
|
||||
/** INSTANCE */
|
||||
/**
|
||||
* INSTANCE
|
||||
*/
|
||||
private static final ObjectMapper INSTANCE;
|
||||
/** CHINA */
|
||||
/**
|
||||
* CHINA
|
||||
*/
|
||||
private static final Locale CHINA = Locale.CHINA;
|
||||
|
||||
static {
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@UtilityClass
|
||||
public class SecurityUtils {
|
||||
/**
|
||||
* 应用默认的忽略 url 列表
|
||||
*/
|
||||
public static final Set<String> DEFAULT_SKIP_URL = Sets.newHashSetWithExpectedSize(32);
|
||||
|
||||
static {
|
||||
// 忽略应用检查接口
|
||||
DEFAULT_SKIP_URL.add("/actuator/**");
|
||||
// 忽略 swagger 相关接口
|
||||
DEFAULT_SKIP_URL.add("/v2/api-docs/**");
|
||||
DEFAULT_SKIP_URL.add("/v2/api-docs-ext/**");
|
||||
DEFAULT_SKIP_URL.add("/swagger-resources/**");
|
||||
DEFAULT_SKIP_URL.add("/swagger-dubbo/**");
|
||||
DEFAULT_SKIP_URL.add("/doc.html");
|
||||
// 忽略验证码相关接口
|
||||
DEFAULT_SKIP_URL.add("/captcha/**");
|
||||
DEFAULT_SKIP_URL.add("/request-urls");
|
||||
// 检查是否需要验证码
|
||||
DEFAULT_SKIP_URL.add("/check");
|
||||
// 忽略 oauth 相关接口
|
||||
DEFAULT_SKIP_URL.add("/oauth/**");
|
||||
// 忽略日志相关接口
|
||||
DEFAULT_SKIP_URL.add("/log/**");
|
||||
// 忽略错误接口
|
||||
DEFAULT_SKIP_URL.add("/error/**");
|
||||
// 忽略静态资源
|
||||
DEFAULT_SKIP_URL.add("/**/*.ico");
|
||||
DEFAULT_SKIP_URL.add("/**/*.css");
|
||||
DEFAULT_SKIP_URL.add("/**/*.js");
|
||||
DEFAULT_SKIP_URL.add("/**/*.html");
|
||||
DEFAULT_SKIP_URL.add("/**/*.map");
|
||||
DEFAULT_SKIP_URL.add("/**/*.svg");
|
||||
DEFAULT_SKIP_URL.add("/**/*.png");
|
||||
DEFAULT_SKIP_URL.add("/**/*.gif");
|
||||
DEFAULT_SKIP_URL.add("/**/*.bmp");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将自定义需要忽略的 url 和默认被忽略的 url 合并
|
||||
*
|
||||
* @param consumterIgnoreUrls 自定义的需要忽略的 url
|
||||
* @return the set 合并后的 url
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static @NotNull Set<String> mergeSkipPatterns(String consumterIgnoreUrls) {
|
||||
return mergeSkipPatterns(SecurityUtils.DEFAULT_SKIP_URL, consumterIgnoreUrls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge skip patterns
|
||||
*
|
||||
* @param defaultSkipUrl default skip url
|
||||
* @param consumterIgnoreUrls consumter ignore urls
|
||||
* @return the set
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static @NotNull Set<String> mergeSkipPatterns(Collection<String> defaultSkipUrl, String consumterIgnoreUrls) {
|
||||
Set<String> ignores = new HashSet<>(defaultSkipUrl);
|
||||
|
||||
if (StringUtils.isNotBlank(consumterIgnoreUrls)) {
|
||||
String[] split = consumterIgnoreUrls.split(StringPool.COMMA);
|
||||
ignores.addAll(Arrays.stream(split).filter(url -> StringUtils.hasText(url.trim())).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
return ignores;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 true 则不进入 filter, 否则进入 filter
|
||||
*
|
||||
* @param lookupPath lookup path
|
||||
* @param includePatterns include patterns
|
||||
* @param excludePatterns exclude patterns
|
||||
* @param pathMatcher path matcher
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static boolean matches(String lookupPath,
|
||||
String[] includePatterns,
|
||||
String[] excludePatterns,
|
||||
PathMatcher pathMatcher) {
|
||||
if (!ObjectUtils.isEmpty(excludePatterns)) {
|
||||
for (String pattern : excludePatterns) {
|
||||
if (pathMatcher.match(pattern, lookupPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ObjectUtils.isEmpty(includePatterns)) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : includePatterns) {
|
||||
if (pathMatcher.match(pattern, lookupPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+183
-65
@@ -1,131 +1,249 @@
|
||||
package com.aivfo.el.base.utils;
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/4/18
|
||||
* @date: 2023/5/16
|
||||
* @description: 静态 String 池
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class AivfoStringPool {
|
||||
public final class StringPool {
|
||||
|
||||
/** AMPERSAND */
|
||||
/**
|
||||
* AMPERSAND
|
||||
*/
|
||||
public static final String AMPERSAND = "&";
|
||||
/** AND */
|
||||
/**
|
||||
* AND
|
||||
*/
|
||||
public static final String AND = "and";
|
||||
/** AT */
|
||||
/**
|
||||
* AT
|
||||
*/
|
||||
public static final String AT = "@";
|
||||
/** ASTERISK */
|
||||
/**
|
||||
* ASTERISK
|
||||
*/
|
||||
public static final String ASTERISK = "*";
|
||||
/** STAR */
|
||||
/**
|
||||
* STAR
|
||||
*/
|
||||
public static final String STAR = ASTERISK;
|
||||
/** SLASH */
|
||||
/**
|
||||
* SLASH
|
||||
*/
|
||||
public static final String SLASH = "/";
|
||||
/** DOUBLE_SLASH */
|
||||
/**
|
||||
* DOUBLE_SLASH
|
||||
*/
|
||||
public static final String DOUBLE_SLASH = "#//";
|
||||
/** COLON */
|
||||
/**
|
||||
* COLON
|
||||
*/
|
||||
public static final String COLON = ":";
|
||||
/** COMMA */
|
||||
/**
|
||||
* COMMA
|
||||
*/
|
||||
public static final String COMMA = ",";
|
||||
/** DASH */
|
||||
/**
|
||||
* DASH
|
||||
*/
|
||||
public static final String DASH = "-";
|
||||
/** DOLLAR */
|
||||
/**
|
||||
* DOLLAR
|
||||
*/
|
||||
public static final String DOLLAR = "$";
|
||||
/** DOT */
|
||||
/**
|
||||
* DOT
|
||||
*/
|
||||
public static final String DOT = ".";
|
||||
/** EMPTY */
|
||||
/**
|
||||
* EMPTY
|
||||
*/
|
||||
public static final String EMPTY = "";
|
||||
/** EMPTY_JSON */
|
||||
/**
|
||||
* EMPTY_JSON
|
||||
*/
|
||||
public static final String EMPTY_JSON = "{}";
|
||||
/** EQUALS */
|
||||
/**
|
||||
* EQUALS
|
||||
*/
|
||||
public static final String EQUALS = "=";
|
||||
/** FALSE */
|
||||
/**
|
||||
* FALSE
|
||||
*/
|
||||
public static final String FALSE = "false";
|
||||
/** HASH */
|
||||
/**
|
||||
* HASH
|
||||
*/
|
||||
public static final String HASH = "#";
|
||||
/** HAT */
|
||||
/**
|
||||
* HAT
|
||||
*/
|
||||
public static final String HAT = "^";
|
||||
/** LEFT_BRACE */
|
||||
/**
|
||||
* LEFT_BRACE
|
||||
*/
|
||||
public static final String LEFT_BRACE = "{";
|
||||
/** LEFT_BRACKET */
|
||||
/**
|
||||
* LEFT_BRACKET
|
||||
*/
|
||||
public static final String LEFT_BRACKET = "(";
|
||||
/** LEFT_CHEV */
|
||||
/**
|
||||
* LEFT_CHEV
|
||||
*/
|
||||
public static final String LEFT_CHEV = "<";
|
||||
/** NEWLINE */
|
||||
/**
|
||||
* NEWLINE
|
||||
*/
|
||||
public static final String NEWLINE = "\n";
|
||||
/** N */
|
||||
/**
|
||||
* N
|
||||
*/
|
||||
public static final String N = "n";
|
||||
/** NO */
|
||||
/**
|
||||
* NO
|
||||
*/
|
||||
public static final String NO = "no";
|
||||
/** NULL */
|
||||
/**
|
||||
* NULL
|
||||
*/
|
||||
public static final String NULL = "null";
|
||||
/** OFF */
|
||||
/**
|
||||
* OFF
|
||||
*/
|
||||
public static final String OFF = "off";
|
||||
/** ON */
|
||||
/**
|
||||
* ON
|
||||
*/
|
||||
public static final String ON = "on";
|
||||
/** PERCENT */
|
||||
/**
|
||||
* PERCENT
|
||||
*/
|
||||
public static final String PERCENT = "%";
|
||||
/** PIPE */
|
||||
/**
|
||||
* PIPE
|
||||
*/
|
||||
public static final String PIPE = "|";
|
||||
/** PLUS */
|
||||
/**
|
||||
* PLUS
|
||||
*/
|
||||
public static final String PLUS = "+";
|
||||
/** QUESTION_MARK */
|
||||
/**
|
||||
* QUESTION_MARK
|
||||
*/
|
||||
public static final String QUESTION_MARK = "?";
|
||||
/** EXCLAMATION_MARK */
|
||||
/**
|
||||
* EXCLAMATION_MARK
|
||||
*/
|
||||
public static final String EXCLAMATION_MARK = "!";
|
||||
/** QUOTE */
|
||||
/**
|
||||
* QUOTE
|
||||
*/
|
||||
public static final String QUOTE = "\"";
|
||||
/** RETURN */
|
||||
/**
|
||||
* RETURN
|
||||
*/
|
||||
public static final String RETURN = "\r";
|
||||
/** TAB */
|
||||
/**
|
||||
* TAB
|
||||
*/
|
||||
public static final String TAB = "\t";
|
||||
/** RIGHT_BRACE */
|
||||
/**
|
||||
* RIGHT_BRACE
|
||||
*/
|
||||
public static final String RIGHT_BRACE = "}";
|
||||
/** RIGHT_BRACKET */
|
||||
/**
|
||||
* RIGHT_BRACKET
|
||||
*/
|
||||
public static final String RIGHT_BRACKET = ")";
|
||||
/** RIGHT_CHEV */
|
||||
/**
|
||||
* RIGHT_CHEV
|
||||
*/
|
||||
public static final String RIGHT_CHEV = ">";
|
||||
/** SEMICOLON */
|
||||
/**
|
||||
* SEMICOLON
|
||||
*/
|
||||
public static final String SEMICOLON = ";";
|
||||
/** SINGLE_QUOTE */
|
||||
/**
|
||||
* SINGLE_QUOTE
|
||||
*/
|
||||
public static final String SINGLE_QUOTE = "'";
|
||||
/** BACKTICK */
|
||||
/**
|
||||
* BACKTICK
|
||||
*/
|
||||
public static final String BACKTICK = "`";
|
||||
/** SPACE */
|
||||
/**
|
||||
* SPACE
|
||||
*/
|
||||
public static final String SPACE = " ";
|
||||
/** TILDA */
|
||||
/**
|
||||
* TILDA
|
||||
*/
|
||||
public static final String TILDA = "~";
|
||||
/** LEFT_SQ_BRACKET */
|
||||
/**
|
||||
* LEFT_SQ_BRACKET
|
||||
*/
|
||||
public static final String LEFT_SQ_BRACKET = "[";
|
||||
/** RIGHT_SQ_BRACKET */
|
||||
/**
|
||||
* RIGHT_SQ_BRACKET
|
||||
*/
|
||||
public static final String RIGHT_SQ_BRACKET = "]";
|
||||
/** TRUE */
|
||||
/**
|
||||
* TRUE
|
||||
*/
|
||||
public static final String TRUE = "true";
|
||||
/** UNDERSCORE */
|
||||
/**
|
||||
* UNDERSCORE
|
||||
*/
|
||||
public static final String UNDERSCORE = "_";
|
||||
// /** UTF_8 */
|
||||
// public static final String UTF_8 = Charsets.UTF_8_NAME;
|
||||
// /** GBK */
|
||||
// public static final String GBK = Charsets.GBK_NAME;
|
||||
// /** ISO_8859_1 */
|
||||
// public static final String ISO_8859_1 = Charsets.ISO_8859_1_NAME;
|
||||
/** Y */
|
||||
/**
|
||||
* UTF_8
|
||||
*/
|
||||
public static final String UTF_8 = Charsets.UTF_8_NAME;
|
||||
/**
|
||||
* GBK
|
||||
*/
|
||||
public static final String GBK = Charsets.GBK_NAME;
|
||||
/**
|
||||
* ISO_8859_1
|
||||
*/
|
||||
public static final String ISO_8859_1 = Charsets.ISO_8859_1_NAME;
|
||||
/**
|
||||
* Y
|
||||
*/
|
||||
public static final String Y = "y";
|
||||
/** YES */
|
||||
/**
|
||||
* YES
|
||||
*/
|
||||
public static final String YES = "yes";
|
||||
/** ONE */
|
||||
/**
|
||||
* ONE
|
||||
*/
|
||||
public static final String ONE = "1";
|
||||
/** ZERO */
|
||||
/**
|
||||
* ZERO
|
||||
*/
|
||||
public static final String ZERO = "0";
|
||||
/** DOLLAR_LEFT_BRACE */
|
||||
/**
|
||||
* DOLLAR_LEFT_BRACE
|
||||
*/
|
||||
public static final String DOLLAR_LEFT_BRACE = "${";
|
||||
/** NULL_STRING */
|
||||
/**
|
||||
* NULL_STRING
|
||||
*/
|
||||
public static final String NULL_STRING = "N/A";
|
||||
/** any_Url_Patterns */
|
||||
/**
|
||||
* any_Url_Patterns
|
||||
*/
|
||||
public static final String ANY_URL_PATTERNS = SLASH + ASTERISK;
|
||||
/** DOUBLE_ASTERISK */
|
||||
/**
|
||||
* DOUBLE_ASTERISK
|
||||
*/
|
||||
public static final String DOUBLE_ASTERISK = ASTERISK + ASTERISK;
|
||||
/** any_PATH */
|
||||
/**
|
||||
* any_PATH
|
||||
*/
|
||||
public static final String ANY_PATH = SLASH + DOUBLE_ASTERISK;
|
||||
}
|
||||
+1826
File diff suppressed because it is too large
Load Diff
+100
@@ -0,0 +1,100 @@
|
||||
package com.aivfo.el.starter.base.utils;
|
||||
|
||||
import com.aivfo.el.starter.base.exception.TimeoutUtilsExecuteException;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 简单超时工具类
|
||||
*/
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
@SuppressWarnings("PMD.ThreadPoolCreationRule")
|
||||
public final class TimeoutUtils {
|
||||
/**
|
||||
* executor
|
||||
*/
|
||||
private static final ExecutorService TIMEOUT_EXECUTOR = Executors.newSingleThreadExecutor();
|
||||
|
||||
/**
|
||||
* Process
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param task task
|
||||
* @param timeout timeout
|
||||
* @return the t
|
||||
* @throws TimeoutUtilsExecuteException timeout utils execute exception
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Contract("null, _ -> null")
|
||||
public static <T> T process(Callable<T> task, long timeout) throws Exception {
|
||||
return process(task, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param task task
|
||||
* @param timeout timeout
|
||||
* @param unit unit
|
||||
* @return the t
|
||||
* @throws TimeoutUtilsExecuteException timeout utils execute exception
|
||||
* @since 1.7.1
|
||||
*/
|
||||
@Contract("null, _, _ -> null")
|
||||
public static <T> T process(Callable<T> task, long timeout, TimeUnit unit) throws Exception {
|
||||
return process(TIMEOUT_EXECUTOR, task, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process
|
||||
* todo-dong4j : (2021.09.18 16:17) [executorService.submit(TtlCallable.get(task))]
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param executorService executor service
|
||||
* @param task task
|
||||
* @param timeout timeout
|
||||
* @param unit unit
|
||||
* @return the t
|
||||
* @throws TimeoutUtilsExecuteException timeout utils execute exception
|
||||
* @since 1.7.1
|
||||
*/
|
||||
@Contract("_, null, _, _ -> null")
|
||||
public static <T> T process(ExecutorService executorService,
|
||||
Callable<T> task,
|
||||
long timeout,
|
||||
TimeUnit unit) throws Exception {
|
||||
if (task == null) {
|
||||
return null;
|
||||
}
|
||||
Future<T> futureRet = executorService.submit(task);
|
||||
try {
|
||||
return futureRet.get(timeout, unit);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("任务被中断: {}", e.getMessage());
|
||||
throw e;
|
||||
} catch (ExecutionException e) {
|
||||
// 任务抛出异常时将被包装为 ExecutionException
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof InvocationTargetException) {
|
||||
// 如果通过反射调用将会抛出 InvocationTargetException, 这里抛出原始异常
|
||||
throw (InvocationTargetException) cause;
|
||||
}
|
||||
// 否则抛出 ExecutionException, 由业务端处理
|
||||
throw e;
|
||||
} catch (TimeoutException e) {
|
||||
if (!futureRet.isCancelled()) {
|
||||
futureRet.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
throw new TimeoutUtilsExecuteException("process timeout: [" + timeout + " " + unit.name().toLowerCase() + "]");
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.aivfo.el.starter.core.function;
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedCallable<T> {
|
||||
|
||||
/**
|
||||
* Run this callable.
|
||||
*
|
||||
* @return result t
|
||||
* @throws Throwable CheckedException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
T call() throws Throwable;
|
||||
}
|
||||
+1
-12
@@ -1,16 +1,5 @@
|
||||
package com.aivfo.el.core.function;
|
||||
package com.aivfo.el.starter.core.function;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 受检的 Comparator </p>
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 14:55
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedComparator<T> {
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.aivfo.el.starter.core.function;
|
||||
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedConsumer<T> {
|
||||
|
||||
/**
|
||||
* Run the Consumer
|
||||
*
|
||||
* @param t T
|
||||
* @throws Throwable UncheckedException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void accept(T t) throws Throwable;
|
||||
|
||||
}
|
||||
+1
-13
@@ -1,19 +1,7 @@
|
||||
package com.aivfo.el.core.function;
|
||||
package com.aivfo.el.starter.core.function;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 受检的 function</p>
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param <R> parameter
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 14:55
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedFunction<T, R> {
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.aivfo.el.starter.core.function;
|
||||
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedRunnable {
|
||||
|
||||
/**
|
||||
* Run this runnable.
|
||||
*
|
||||
* @throws Throwable CheckedException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void run() throws Throwable;
|
||||
|
||||
}
|
||||
+1
-12
@@ -1,18 +1,7 @@
|
||||
package com.aivfo.el.core.function;
|
||||
package com.aivfo.el.starter.core.function;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 受检的 Supplier </p>
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 14:54
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@SuppressWarnings("java:S112")
|
||||
public interface CheckedSupplier<T> {
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.aop.framework.AdvisedSupport;
|
||||
import org.springframework.aop.framework.AopProxy;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
@UtilityClass
|
||||
@SuppressWarnings("all")
|
||||
public class AopTargetUtils {
|
||||
|
||||
/**
|
||||
* 获取 目标对象
|
||||
*
|
||||
* @param proxy 代理对象
|
||||
* @return target
|
||||
* @throws Exception exception
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static Object getTarget(Object proxy) throws Exception {
|
||||
|
||||
if (!AopUtils.isAopProxy(proxy)) {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
if (AopUtils.isJdkDynamicProxy(proxy)) {
|
||||
return getJdkDynamicProxyTargetObject(proxy);
|
||||
} else {
|
||||
return getCglibProxyTargetObject(proxy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets cglib proxy target object *
|
||||
*
|
||||
* @param proxy proxy
|
||||
* @return the cglib proxy target object
|
||||
* @throws Exception exception
|
||||
* @since 1.6.0
|
||||
*/
|
||||
private static Object getCglibProxyTargetObject(@NotNull Object proxy) throws Exception {
|
||||
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
|
||||
h.setAccessible(true);
|
||||
Object dynamicAdvisedInterceptor = h.get(proxy);
|
||||
|
||||
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
|
||||
advised.setAccessible(true);
|
||||
|
||||
return ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets jdk dynamic proxy target object *
|
||||
*
|
||||
* @param proxy proxy
|
||||
* @return the jdk dynamic proxy target object
|
||||
* @throws Exception exception
|
||||
* @since 1.6.0
|
||||
*/
|
||||
private static Object getJdkDynamicProxyTargetObject(@NotNull Object proxy) throws Exception {
|
||||
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
|
||||
h.setAccessible(true);
|
||||
AopProxy aopProxy = (AopProxy) h.get(proxy);
|
||||
|
||||
Field advised = aopProxy.getClass().getDeclaredField("advised");
|
||||
advised.setAccessible(true);
|
||||
|
||||
return ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
|
||||
}
|
||||
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class that enables to get an IP range from CIDR specification. It supports
|
||||
* both IPv4 and IPv6.
|
||||
* <p>
|
||||
* From https://github.com/edazdarevic/CIDRUtils/blob/master/CIDRUtils.java
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public class CIDRUtils {
|
||||
/**
|
||||
* Cidr
|
||||
*/
|
||||
private final String CIDR;
|
||||
/**
|
||||
* Inet address
|
||||
*/
|
||||
private final InetAddress inetAddress;
|
||||
/**
|
||||
* Start address
|
||||
*/
|
||||
private InetAddress startAddress;
|
||||
/**
|
||||
* End address
|
||||
*/
|
||||
private InetAddress endAddress;
|
||||
/**
|
||||
* Prefix length
|
||||
*/
|
||||
private final int prefixLength;
|
||||
|
||||
|
||||
/**
|
||||
* Cidr utils
|
||||
*
|
||||
* @param cidr cidr
|
||||
* @throws UnknownHostException unknown host exception
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public CIDRUtils(String cidr) throws UnknownHostException {
|
||||
|
||||
this.CIDR = cidr;
|
||||
|
||||
/* split CIDR to address and prefix part */
|
||||
if (this.CIDR.contains("/")) {
|
||||
int index = this.CIDR.indexOf("/");
|
||||
String addressPart = this.CIDR.substring(0, index);
|
||||
String networkPart = this.CIDR.substring(index + 1);
|
||||
|
||||
this.inetAddress = InetAddress.getByName(addressPart);
|
||||
this.prefixLength = Integer.parseInt(networkPart);
|
||||
|
||||
this.calculate();
|
||||
} else {
|
||||
throw new IllegalArgumentException("not an valid CIDR format!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate
|
||||
*
|
||||
* @throws UnknownHostException unknown host exception
|
||||
* @since 1.5.0
|
||||
*/
|
||||
private void calculate() throws UnknownHostException {
|
||||
|
||||
ByteBuffer maskBuffer;
|
||||
int targetSize;
|
||||
if (this.inetAddress.getAddress().length == 4) {
|
||||
maskBuffer =
|
||||
ByteBuffer
|
||||
.allocate(4)
|
||||
.putInt(-1);
|
||||
targetSize = 4;
|
||||
} else {
|
||||
maskBuffer = ByteBuffer.allocate(16)
|
||||
.putLong(-1L)
|
||||
.putLong(-1L);
|
||||
targetSize = 16;
|
||||
}
|
||||
|
||||
BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(this.prefixLength);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(this.inetAddress.getAddress());
|
||||
BigInteger ipVal = new BigInteger(1, buffer.array());
|
||||
|
||||
BigInteger startIp = ipVal.and(mask);
|
||||
BigInteger endIp = startIp.add(mask.not());
|
||||
|
||||
byte[] startIpArr = this.toBytes(startIp.toByteArray(), targetSize);
|
||||
byte[] endIpArr = this.toBytes(endIp.toByteArray(), targetSize);
|
||||
|
||||
this.startAddress = InetAddress.getByAddress(startIpArr);
|
||||
this.endAddress = InetAddress.getByAddress(endIpArr);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* To bytes
|
||||
*
|
||||
* @param array array
|
||||
* @param targetSize target size
|
||||
* @return the byte [ ]
|
||||
* @since 1.5.0
|
||||
*/
|
||||
private byte @NotNull [] toBytes(byte[] array, int targetSize) {
|
||||
int counter = 0;
|
||||
List<Byte> newArr = new ArrayList<Byte>();
|
||||
while (counter < targetSize && (array.length - 1 - counter >= 0)) {
|
||||
newArr.add(0, array[array.length - 1 - counter]);
|
||||
counter++;
|
||||
}
|
||||
|
||||
int size = newArr.size();
|
||||
for (int i = 0; i < (targetSize - size); i++) {
|
||||
|
||||
newArr.add(0, (byte) 0);
|
||||
}
|
||||
|
||||
byte[] ret = new byte[newArr.size()];
|
||||
for (int i = 0; i < newArr.size(); i++) {
|
||||
ret[i] = newArr.get(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets network address *
|
||||
*
|
||||
* @return the network address
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public String getNetworkAddress() {
|
||||
|
||||
return this.startAddress.getHostAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets broadcast address *
|
||||
*
|
||||
* @return the broadcast address
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public String getBroadcastAddress() {
|
||||
return this.endAddress.getHostAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is in range
|
||||
*
|
||||
* @param ipAddress ip address
|
||||
* @return the boolean
|
||||
* @throws UnknownHostException unknown host exception
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public boolean isInRange(String ipAddress) throws UnknownHostException {
|
||||
InetAddress address = InetAddress.getByName(ipAddress);
|
||||
BigInteger start = new BigInteger(1, this.startAddress.getAddress());
|
||||
BigInteger end = new BigInteger(1, this.endAddress.getAddress());
|
||||
BigInteger target = new BigInteger(1, address.getAddress());
|
||||
|
||||
int st = start.compareTo(target);
|
||||
int te = target.compareTo(end);
|
||||
|
||||
return (st < 0 || st == 0) && (te < 0 || te == 0);
|
||||
}
|
||||
}
|
||||
+1685
File diff suppressed because it is too large
Load Diff
+79
@@ -0,0 +1,79 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 枚举工具类
|
||||
*/
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
@SuppressWarnings("all")
|
||||
public class EnumUtils {
|
||||
/**
|
||||
* 枚举类缓存
|
||||
*/
|
||||
private static final Map<Class<?>, Object> ENUM_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 根据条件获取枚举对象
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param cla 枚举类
|
||||
* @param predicate 筛选条件
|
||||
* @return enum optional
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static <T> Optional<T> of(@NotNull Class<T> cla, Predicate<T> predicate) {
|
||||
if (!cla.isEnum()) {
|
||||
log.info("Class 不是枚举类 cla ={}", cla);
|
||||
return Optional.empty();
|
||||
}
|
||||
Object obj = ENUM_MAP.get(cla);
|
||||
T[] ts;
|
||||
if (obj == null) {
|
||||
ts = cla.getEnumConstants();
|
||||
ENUM_MAP.put(cla, ts);
|
||||
} else {
|
||||
ts = (T[]) obj;
|
||||
}
|
||||
return Arrays.stream(ts).filter(predicate).findAny();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过枚举的 index 获取枚举
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param clazz the clazz
|
||||
* @param ordinal the ordinal 需要的枚举值在设定的枚举类中的顺序, 以 0 开始
|
||||
* @return t t
|
||||
* @author xiehao
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static <T extends Enum<T>> T indexOf(@NotNull Class<T> clazz, int ordinal) {
|
||||
return clazz.getEnumConstants()[ordinal];
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入的参数 name 指的是枚举值的名称, 一般是大写加下划线的
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param clazz the clazz
|
||||
* @param name the name
|
||||
* @return Enum T
|
||||
* @author xiehao
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static <T extends Enum<T>> T nameOf(Class<T> clazz, String name) {
|
||||
return Enum.valueOf(clazz, name);
|
||||
}
|
||||
|
||||
}
|
||||
+1027
File diff suppressed because it is too large
Load Diff
+72
@@ -0,0 +1,72 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import lombok.Data;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 针对多网卡获取 IP 错误的问题, 使用此类可配置获取 IP 的方式
|
||||
*/
|
||||
@Data
|
||||
class InetUtilsProperties {
|
||||
|
||||
/**
|
||||
* LOCALHOST_KEY
|
||||
*/
|
||||
public static final String LOCALHOST_KEY = "localhost";
|
||||
/**
|
||||
* LOCALHOST_VALUE
|
||||
*/
|
||||
public static final String LOCALHOST_VALUE = "127.0.0.1";
|
||||
|
||||
/**
|
||||
* Default hostname
|
||||
*/
|
||||
private String defaultHostname = LOCALHOST_KEY;
|
||||
/**
|
||||
* Default ip address
|
||||
*/
|
||||
private String defaultIpAddress = LOCALHOST_VALUE;
|
||||
/**
|
||||
* Timeout seconds
|
||||
*/
|
||||
private int timeoutSeconds;
|
||||
/**
|
||||
* 是否只使用带有站点本地地址的接口, {@link InetAddress#isSiteLocalAddress()}
|
||||
*/
|
||||
private boolean useOnlySiteLocalInterfaces;
|
||||
/**
|
||||
* 将被忽略的网络接口的Java正则表达式列表.
|
||||
*/
|
||||
private List<String> ignoredInterfaces = new ArrayList<>();
|
||||
/**
|
||||
* 首选网络地址的Java正则表达式列表
|
||||
*/
|
||||
private List<String> preferredNetworks = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Inet utils properties
|
||||
*
|
||||
* @since 2022.1.1
|
||||
*/
|
||||
InetUtilsProperties() {
|
||||
this.timeoutSeconds = 1;
|
||||
this.useOnlySiteLocalInterfaces = false;
|
||||
|
||||
String ignoredInterfacesStr = "";
|
||||
if (StringUtils.isNotBlank(ignoredInterfacesStr)) {
|
||||
this.ignoredInterfaces = StringUtils.splitTrim(ignoredInterfacesStr, ",");
|
||||
}
|
||||
|
||||
String preferredNetworksStr = "";
|
||||
if (StringUtils.isNotBlank(preferredNetworksStr)) {
|
||||
this.preferredNetworks = StringUtils.splitTrim(preferredNetworksStr, ",");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
|
||||
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = -5167631809472116969L;
|
||||
/**
|
||||
* DEFAULT_LOAD_FACTOR
|
||||
*/
|
||||
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
|
||||
/**
|
||||
* DEFAULT_MAX_CAPACITY
|
||||
*/
|
||||
private static final int DEFAULT_MAX_CAPACITY = 1000;
|
||||
/**
|
||||
* Lock
|
||||
*/
|
||||
private final Lock lock = new ReentrantLock();
|
||||
/**
|
||||
* Max capacity
|
||||
*/
|
||||
private volatile int maxCapacity;
|
||||
|
||||
/**
|
||||
* Lru cache
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public LRUCache() {
|
||||
this(DEFAULT_MAX_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lru cache
|
||||
*
|
||||
* @param maxCapacity max capacity
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public LRUCache(int maxCapacity) {
|
||||
super(16, DEFAULT_LOAD_FACTOR, true);
|
||||
this.maxCapacity = maxCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove eldest entry
|
||||
*
|
||||
* @param eldest eldest
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Override
|
||||
protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
|
||||
return this.size() > this.maxCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains key
|
||||
*
|
||||
* @param key key
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
return super.containsKey(key);
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get
|
||||
*
|
||||
* @param key key
|
||||
* @return the v
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
return super.get(key);
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
* @return the v
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
return super.put(key, value);
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove
|
||||
*
|
||||
* @param key key
|
||||
* @return the v
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
return super.remove(key);
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Size
|
||||
*
|
||||
* @return the int
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
this.lock.lock();
|
||||
try {
|
||||
return super.size();
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
this.lock.lock();
|
||||
try {
|
||||
super.clear();
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets max capacity *
|
||||
*
|
||||
* @return the max capacity
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public int getMaxCapacity() {
|
||||
return this.maxCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets max capacity *
|
||||
*
|
||||
* @param maxCapacity max capacity
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public void setMaxCapacity(int maxCapacity) {
|
||||
this.maxCapacity = maxCapacity;
|
||||
}
|
||||
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: INet 相关工具
|
||||
*/
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
public class NetUtils extends INetUtils {
|
||||
/**
|
||||
* LOCAL_HOST
|
||||
*/
|
||||
public static final String LOCAL_HOST = INetUtils.LOCALHOST_VALUE;
|
||||
/**
|
||||
* MIN_PORT_NUMBER
|
||||
*/
|
||||
public static final int MIN_PORT_NUMBER = INetUtils.MIN_PORT;
|
||||
/**
|
||||
* MAX_PORT_NUMBER
|
||||
*/
|
||||
public static final int MAX_PORT_NUMBER = INetUtils.MAX_PORT;
|
||||
|
||||
/**
|
||||
* Gets local ip addr.
|
||||
*
|
||||
* @return the local ip addr
|
||||
* @since 1.0.0
|
||||
* @deprecated use {@link NetUtils#getLocalHost()}
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public static String getLocalIpAddr() {
|
||||
return getLocalHost();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 服务器 hostname
|
||||
*
|
||||
* @return hostname host name
|
||||
* @since 1.0.0
|
||||
* @deprecated use {@link NetUtils#getLocalAddress()}
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getHostName() {
|
||||
InetAddress localAddress = getLocalAddress();
|
||||
if (localAddress == null) {
|
||||
return LOCALHOST_KEY;
|
||||
}
|
||||
return localAddress.getHostName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试端口时候被占用
|
||||
*
|
||||
* @param port 端口号
|
||||
* @return boolean 没有被占用: true, 被占用: false
|
||||
* @since 1.0.0
|
||||
* @deprecated use {@link NetUtils#available(int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean tryPort(int port) {
|
||||
return available(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查本机 TCP/UDP 端口是否可用, 可用返回 true, 否则返回 false
|
||||
*
|
||||
* @param port port
|
||||
* @return the boolean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static boolean available(int port) {
|
||||
if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
|
||||
throw new IllegalArgumentException("Invalid start port: " + port);
|
||||
}
|
||||
try (ServerSocket ss = new ServerSocket(port);
|
||||
DatagramSocket ds = new DatagramSocket(port)) {
|
||||
ss.setReuseAddress(true);
|
||||
ds.setReuseAddress(true);
|
||||
return true;
|
||||
} catch (IOException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求方 ip
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @return ip string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("PMD.UndefineMagicConstantRule")
|
||||
public static String ip(@NotNull HttpServletRequest request) {
|
||||
String ipAddress;
|
||||
ipAddress = request.getHeader("x-forwarded-for");
|
||||
if (StringUtils.isEmpty(ipAddress)) {
|
||||
ipAddress = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (StringUtils.isEmpty(ipAddress)) {
|
||||
ipAddress = request.getRemoteAddr();
|
||||
if (LOCAL_HOST.equals(ipAddress)) {
|
||||
// 根据网卡取本机配置的IP
|
||||
InetAddress inetAddress = null;
|
||||
try {
|
||||
inetAddress = InetAddress.getLocalHost();
|
||||
} catch (UnknownHostException ignored) {
|
||||
}
|
||||
ipAddress = inetAddress != null ? inetAddress.getHostAddress() : null;
|
||||
}
|
||||
}
|
||||
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
|
||||
if (ipAddress != null && ipAddress.length() > 15) {
|
||||
if (ipAddress.indexOf(StringPool.COMMA) > 0) {
|
||||
ipAddress = ipAddress.substring(0, ipAddress.indexOf(StringPool.COMMA));
|
||||
}
|
||||
}
|
||||
if (StringUtils.isEmpty(ipAddress)) {
|
||||
return "NONE";
|
||||
}
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 数字类型工具类
|
||||
*/
|
||||
@UtilityClass
|
||||
public class NumberUtils extends org.springframework.util.NumberUtils {
|
||||
|
||||
/**
|
||||
* All possible chars for representing a number as a String
|
||||
*/
|
||||
public static final char[] DIGITS = {
|
||||
'0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b',
|
||||
'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'I', 'J', 'K', 'L',
|
||||
'M', 'N', 'O', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z'
|
||||
};
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to an <code>int</code>, returning
|
||||
* <code>zero</code> if the conversion fails.</p>
|
||||
* <p>If the string is <code>null</code>, <code>zero</code> is returned.</p>
|
||||
* <pre>
|
||||
* NumberUtil.toInt(null) = 0
|
||||
* NumberUtil.toInt("") = 0
|
||||
* NumberUtil.toInt("1") = 1
|
||||
* </pre>
|
||||
*
|
||||
* @param str the string to convert, may be null
|
||||
* @return the int represented by the string, or <code>zero</code> if conversion fails
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static int toInt(String str) {
|
||||
return toInt(str, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is numer boolean
|
||||
*
|
||||
* @param str str
|
||||
* @return the boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("null -> false")
|
||||
public static boolean isNumer(String str) {
|
||||
return NumberUtil.isNumber(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to an <code>int</code>, returning a
|
||||
* default value if the conversion fails.</p>
|
||||
* <p>If the string is <code>null</code>, the default value is returned.</p>
|
||||
* <pre>
|
||||
* NumberUtil.toInt(null, 1) = 1
|
||||
* NumberUtil.toInt("", 1) = 1
|
||||
* NumberUtil.toInt("1", 0) = 1
|
||||
* </pre>
|
||||
*
|
||||
* @param str the string to convert, may be null
|
||||
* @param defaultValue the default value
|
||||
* @return the int represented by the string, or the default if conversion fails
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("null, _ -> param2")
|
||||
public static int toInt(@Nullable String str, int defaultValue) {
|
||||
if (str == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(str);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to a <code>long</code>, returning
|
||||
* <code>zero</code> if the conversion fails.</p>
|
||||
* <p>If the string is <code>null</code>, <code>zero</code> is returned.</p>
|
||||
* <pre>
|
||||
* NumberUtil.toLong(null) = 0L
|
||||
* NumberUtil.toLong("") = 0L
|
||||
* NumberUtil.toLong("1") = 1L
|
||||
* </pre>
|
||||
*
|
||||
* @param str the string to convert, may be null
|
||||
* @return the long represented by the string, or <code>0</code> if conversion fails
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static long toLong(String str) {
|
||||
return toLong(str, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to a <code>long</code>, returning a
|
||||
* default value if the conversion fails.</p>
|
||||
* <p>If the string is <code>null</code>, the default value is returned.</p>
|
||||
* <pre>
|
||||
* NumberUtil.toLong(null, 1L) = 1L
|
||||
* NumberUtil.toLong("", 1L) = 1L
|
||||
* NumberUtil.toLong("1", 0L) = 1L
|
||||
* </pre>
|
||||
*
|
||||
* @param str the string to convert, may be null
|
||||
* @param defaultValue the default value
|
||||
* @return the long represented by the string, or the default if conversion fails
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("null, _ -> param2")
|
||||
public static long toLong(@Nullable String str, long defaultValue) {
|
||||
if (str == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(str);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to a <code>Double</code>
|
||||
*
|
||||
* @param value value
|
||||
* @return double value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("!null -> !null")
|
||||
public static Double toDouble(String value) {
|
||||
return toDouble(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to a <code>Double</code>
|
||||
*
|
||||
* @param value value
|
||||
* @param defaultValue 默认值
|
||||
* @return double value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("!null, _ -> !null; null, _ -> param2")
|
||||
public static Double toDouble(@Nullable String value, Double defaultValue) {
|
||||
if (value != null) {
|
||||
return Double.valueOf(value.trim());
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to a <code>Double</code>
|
||||
*
|
||||
* @param value value
|
||||
* @return double value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("!null -> !null")
|
||||
public static Float toFloat(String value) {
|
||||
return toFloat(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to a <code>Double</code>
|
||||
*
|
||||
* @param value value
|
||||
* @param defaultValue 默认值
|
||||
* @return double value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("!null, _ -> !null; null, _ -> param2")
|
||||
public static Float toFloat(@Nullable String value, Float defaultValue) {
|
||||
if (value != null) {
|
||||
return Float.valueOf(value.trim());
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 long 转短字符串 为 62 进制
|
||||
*
|
||||
* @param i 数字
|
||||
* @return 短字符串 string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public static String to62String(long i) {
|
||||
int radix = DIGITS.length;
|
||||
char[] buf = new char[65];
|
||||
int charPos = 64;
|
||||
i = -i;
|
||||
while (i <= -radix) {
|
||||
buf[charPos--] = DIGITS[(int) (-(i % radix))];
|
||||
i = i / radix;
|
||||
}
|
||||
buf[charPos] = DIGITS[(int) (-i)];
|
||||
|
||||
return new String(buf, charPos, (65 - charPos));
|
||||
}
|
||||
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import com.aivfo.el.starter.base.utils.Charsets;
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
public class UrlUtils extends org.springframework.web.util.UriUtils {
|
||||
|
||||
/**
|
||||
* Build url params by map
|
||||
*
|
||||
* @param map map
|
||||
* @return the string
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public static String buildUrlParamsByMap(Map<String, String> map) {
|
||||
if (map == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
sb.append(entry.getKey()).append(StringPool.EQUALS).append(entry.getValue()).append(StringPool.AMPERSAND);
|
||||
}
|
||||
String s = sb.toString();
|
||||
if (s.endsWith(StringPool.AMPERSAND)) {
|
||||
s = StringUtils.subBefore(s, StringPool.AMPERSAND);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build map by url params
|
||||
*
|
||||
* @param param param
|
||||
* @return the map
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public static @NotNull Map<String, String> buildMapByUrlParams(String param) {
|
||||
Map<String, String> map = Maps.newHashMap();
|
||||
if (StringUtils.isBlank(param)) {
|
||||
return map;
|
||||
}
|
||||
if (param.startsWith(StringPool.QUESTION_MARK)) {
|
||||
param = StringUtils.subAfter(param, StringPool.QUESTION_MARK);
|
||||
}
|
||||
String[] params = param.split(StringPool.AMPERSAND);
|
||||
for (String s : params) {
|
||||
String[] p = s.split(StringPool.EQUALS);
|
||||
if (p.length == 2) {
|
||||
map.put(p[0], p[1]);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* url 编码,同js decodeURIComponent
|
||||
*
|
||||
* @param source url
|
||||
* @param charset 字符集
|
||||
* @return 编码后的url string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static String encodeUrl(String source, @NotNull Charset charset) {
|
||||
return UrlUtils.encode(source, charset.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* url 解码
|
||||
*
|
||||
* @param source url
|
||||
* @param charset 字符集
|
||||
* @return 解码url string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static String decodeUrl(String source, @NotNull Charset charset) {
|
||||
return UrlUtils.decode(source, charset.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 url 路径
|
||||
*
|
||||
* @param uriStr 路径
|
||||
* @return url路径 path
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getPath(String uriStr) {
|
||||
URI uri;
|
||||
|
||||
try {
|
||||
uri = new URI(uriStr);
|
||||
} catch (URISyntaxException var3) {
|
||||
throw new RuntimeException(var3);
|
||||
}
|
||||
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* map 转为 url 参数, 默认需要参数编码
|
||||
*
|
||||
* @param source source
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static String asUrlParams(@NotNull Map<String, String> source) {
|
||||
return asUrlParams(source, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* map 转为 url 参数
|
||||
*
|
||||
* @param source source
|
||||
* @param urlEncoder url encoder
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static String asUrlParams(@NotNull Map<String, String> source, boolean urlEncoder) {
|
||||
Map<String, String> tmp = Maps.newHashMap();
|
||||
source.forEach((k, v) -> {
|
||||
if (k != null) {
|
||||
try {
|
||||
if (urlEncoder) {
|
||||
tmp.put(k, URLEncoder.encode(v, Charsets.UTF_8_NAME));
|
||||
} else {
|
||||
tmp.put(k, v);
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.error("url encode error", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
return Joiner.on("&").useForNull("").withKeyValueSeparator("=").join(tmp);
|
||||
}
|
||||
}
|
||||
+840
@@ -0,0 +1,840 @@
|
||||
package com.aivfo.el.starter.core.util;
|
||||
|
||||
import com.aivfo.el.starter.base.asserts.Assertions;
|
||||
import com.aivfo.el.starter.base.contex.AgentRequestContextHolder;
|
||||
import com.aivfo.el.starter.base.utils.*;
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 用于处理HTTP请求的工具类
|
||||
*/
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
@SuppressWarnings("checkstyle:MethodLimit")
|
||||
public class WebUtils extends org.springframework.web.util.WebUtils {
|
||||
|
||||
/**
|
||||
* The constant USER_AGENT_HEADER.
|
||||
*/
|
||||
public static final String USER_AGENT_HEADER = "user-agent";
|
||||
|
||||
/**
|
||||
* The constant UN_KNOWN.
|
||||
*/
|
||||
public static final String UN_KNOWN = "unknown";
|
||||
|
||||
/**
|
||||
* 判断是否ajax请求
|
||||
* spring ajax 返回含有 ResponseBody 或者 RestController注解
|
||||
*
|
||||
* @param handlerMethod HandlerMethod
|
||||
* @return 是否ajax请求 boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static boolean isBody(HandlerMethod handlerMethod) {
|
||||
ResponseBody responseBody = ClassUtils.getAnnotation(handlerMethod, ResponseBody.class);
|
||||
return responseBody != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取cookie
|
||||
*
|
||||
* @param name cookie name
|
||||
* @return cookie value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getCookieVal(String name) {
|
||||
HttpServletRequest request = WebUtils.getRequest();
|
||||
return getCookieVal(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HttpServletRequest
|
||||
*
|
||||
* @return {HttpServletRequest}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static HttpServletRequest getRequest() {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttributes == null) {
|
||||
requestAttributes = AgentRequestContextHolder.getRequestAttributes();
|
||||
}
|
||||
Assertions.notNull(requestAttributes, "当前线程中不存在 RequestAttributes");
|
||||
return ((ServletRequestAttributes) Objects.requireNonNull(requestAttributes)).getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取cookie
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @param name cookie name
|
||||
* @return cookie value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public static String getCookieVal(HttpServletRequest request, String name) {
|
||||
Cookie cookie = getCookie(request, name);
|
||||
return cookie != null ? cookie.getValue() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取COOKIE
|
||||
*
|
||||
* @param request the request
|
||||
* @param name the name
|
||||
* @return the cookie
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @Nullable Cookie getCookie(@NotNull HttpServletRequest request, @NotNull String name) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies == null) {
|
||||
return null;
|
||||
}
|
||||
for (Cookie ck : cookies) {
|
||||
if (StringUtils.equalsIgnoreCase(name, ck.getName())) {
|
||||
return ck;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除 某个指定的cookie
|
||||
*
|
||||
* @param response HttpServletResponse
|
||||
* @param key cookie key
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void removeCookie(HttpServletResponse response, String key) {
|
||||
setCookie(response, key, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置cookie
|
||||
*
|
||||
* @param response HttpServletResponse
|
||||
* @param name cookie name
|
||||
* @param value cookie value
|
||||
* @param maxAgeInSeconds maxage
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void setCookie(@NotNull HttpServletResponse response, String name, @Nullable String value, int maxAgeInSeconds) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
cookie.setPath(StringPool.SLASH);
|
||||
cookie.setMaxAge(maxAgeInSeconds);
|
||||
cookie.setHttpOnly(true);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回json
|
||||
*
|
||||
* @param response HttpServletResponse
|
||||
* @param result 结果对象
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void renderJson(HttpServletResponse response, Object result) {
|
||||
renderJson(response, result, MediaType.APPLICATION_JSON_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回json
|
||||
*
|
||||
* @param response HttpServletResponse
|
||||
* @param result 结果对象
|
||||
* @param contentType contentType
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void renderJson(@NotNull HttpServletResponse response, Object result, String contentType) {
|
||||
response.setCharacterEncoding(StringPool.UTF_8);
|
||||
response.setContentType(contentType);
|
||||
try (PrintWriter out = response.getWriter()) {
|
||||
out.append(JsonUtils.toJson(result));
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ip
|
||||
*
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getIp() {
|
||||
return getIp(WebUtils.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ip
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public static String getIp(HttpServletRequest request) {
|
||||
Assertions.notNull(request, "HttpServletRequest is null");
|
||||
String ip = request.getHeader("X-Requested-For");
|
||||
if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
|
||||
return StringUtils.isBlank(ip) ? NetUtils.ip(request) : ip.split(",")[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求的 url: http[s]://www.xxx.xx[:port]
|
||||
*
|
||||
* @return the string
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static @NotNull String getUrl() {
|
||||
return getUrl(WebUtils.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets url *
|
||||
*
|
||||
* @param request request
|
||||
* @return the url
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static @NotNull String getUrl(@NotNull HttpServletRequest request) {
|
||||
String schemeAndHost = request.getScheme() + "://" + request.getServerName();
|
||||
// 默认端口输出
|
||||
int defaultPort = 80;
|
||||
if (request.getServerPort() != defaultPort) {
|
||||
schemeAndHost += ":" + request.getServerPort();
|
||||
}
|
||||
return schemeAndHost;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取 request 中 json 字符串的内容
|
||||
*
|
||||
* @param request request
|
||||
* @return 字符串内容 request param string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getRequestParamString(HttpServletRequest request) {
|
||||
try {
|
||||
return getRequestStr(request);
|
||||
} catch (Exception ex) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 request 请求内容
|
||||
*
|
||||
* @param request request
|
||||
* @return String request str
|
||||
* @throws IOException IOException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getRequestStr(@NotNull HttpServletRequest request) {
|
||||
String queryString = request.getQueryString();
|
||||
if (StringUtils.isNotBlank(queryString)) {
|
||||
return new String(queryString.getBytes(Charsets.ISO_8859_1), Charsets.UTF_8)
|
||||
.replaceAll("&", StringPool.AMPERSAND)
|
||||
.replaceAll("%22", "\"");
|
||||
}
|
||||
return getRequestStr(request, getRequestBytes(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 request 请求内容
|
||||
*
|
||||
* @param request request
|
||||
* @param buffer buffer
|
||||
* @return String request str
|
||||
* @throws IOException IOException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public static String getRequestStr(@NotNull HttpServletRequest request, byte[] buffer) {
|
||||
String str = new String(buffer, request.getCharacterEncoding()).trim();
|
||||
if (StringUtils.isBlank(str)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Enumeration<String> parameterNames = request.getParameterNames();
|
||||
while (parameterNames.hasMoreElements()) {
|
||||
String key = parameterNames.nextElement();
|
||||
String value = request.getParameter(key);
|
||||
StringUtils.appendBuilder(sb, key, StringPool.EQUALS, value, StringPool.AMPERSAND);
|
||||
}
|
||||
str = StringUtils.removeSuffix(sb.toString(), StringPool.AMPERSAND);
|
||||
}
|
||||
return str.replaceAll("&", StringPool.AMPERSAND);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 request 请求的 byte[] 数组
|
||||
*
|
||||
* @param request request
|
||||
* @return byte[] byte [ ]
|
||||
* @throws IOException IOException
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static byte[] getRequestBytes(@NotNull HttpServletRequest request) {
|
||||
int contentLength = request.getContentLength();
|
||||
if (contentLength < 0) {
|
||||
return null;
|
||||
}
|
||||
byte[] buffer = new byte[contentLength];
|
||||
for (int i = 0; i < contentLength; ) {
|
||||
|
||||
int readlen = request.getInputStream().read(buffer, i, contentLength - i);
|
||||
if (readlen == -1) {
|
||||
break;
|
||||
}
|
||||
i += readlen;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP地址,此方法用在proxy环境中
|
||||
*
|
||||
* @param req the req
|
||||
* @return remote addr
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getRemoteAddr(@NotNull HttpServletRequest req) {
|
||||
String ip = req.getHeader("X-Forwarded-For");
|
||||
if (StringUtils.isNotBlank(ip)) {
|
||||
String[] ips = StringUtils.split(ip, StringPool.COMMA);
|
||||
for (String tmpip : Objects.requireNonNull(ips)) {
|
||||
if (StringUtils.isBlank(tmpip)) {
|
||||
continue;
|
||||
}
|
||||
tmpip = tmpip.trim();
|
||||
if (isIpAddr(tmpip) && !tmpip.startsWith("10.") && !tmpip.startsWith("192.168.")
|
||||
&& !NetUtils.LOCAL_HOST.equals(tmpip)) {
|
||||
return tmpip.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
ip = req.getHeader("x-real-ip");
|
||||
if (isIpAddr(ip)) {
|
||||
return ip;
|
||||
}
|
||||
ip = req.getRemoteAddr();
|
||||
if (ip.indexOf(CharPool.DOT) == -1) {
|
||||
ip = NetUtils.LOCAL_HOST;
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否是一个IP地址
|
||||
*
|
||||
* @param addr the addr
|
||||
* @return boolean boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings(value = {"PMD.UndefineMagicConstantRule", "checkstyle:ReturnCount"})
|
||||
public static boolean isIpAddr(String addr) {
|
||||
if (StringUtils.isEmpty(addr)) {
|
||||
return false;
|
||||
}
|
||||
String[] ips = StringUtils.split(addr, StringPool.DOT);
|
||||
if (Objects.requireNonNull(ips).length != 4) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
int ipa = Integer.parseInt(ips[0]);
|
||||
int ipb = Integer.parseInt(ips[1]);
|
||||
int ipc = Integer.parseInt(ips[2]);
|
||||
int ipd = Integer.parseInt(ips[3]);
|
||||
return ipa >= 0 && ipa <= 255 && ipb >= 0 && ipb <= 255 && ipc >= 0
|
||||
&& ipc <= 255 && ipd >= 0 && ipd <= 255;
|
||||
} catch (Exception e) {
|
||||
log.error("transformation error", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为搜索引擎
|
||||
*
|
||||
* @param req the req
|
||||
* @return boolean boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static boolean isRobot(@NotNull HttpServletRequest req) {
|
||||
String ua = req.getHeader("user-agent");
|
||||
if (StringUtils.isBlank(ua)) {
|
||||
return false;
|
||||
}
|
||||
return ua.contains("Baiduspider")
|
||||
|| ua.contains("Googlebot")
|
||||
|| ua.contains("sogou")
|
||||
|| ua.contains("sina")
|
||||
|| ua.contains("iaskspider")
|
||||
|| ua.contains("ia_archiver")
|
||||
|| ua.contains("Sosospider")
|
||||
|| ua.contains("YoudaoBot")
|
||||
|| ua.contains("yahoo")
|
||||
|| ua.contains("yodao")
|
||||
|| ua.contains("MSNBot")
|
||||
|| ua.contains("Twiceler")
|
||||
|| ua.contains("Sosoimagespider")
|
||||
|| ua.contains("naver.com/robots")
|
||||
|| ua.contains("Nutch")
|
||||
|| ua.contains("spider");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取COOKIE
|
||||
*
|
||||
* @param request the request
|
||||
* @param name the name
|
||||
* @return the cookie value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public static String getCookieValue(@NotNull HttpServletRequest request, String name) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies == null) {
|
||||
return null;
|
||||
}
|
||||
for (Cookie ck : cookies) {
|
||||
if (StringUtils.equalsIgnoreCase(name, ck.getName())) {
|
||||
return ck.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置COOKIE
|
||||
*
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @param name the name
|
||||
* @param value the value
|
||||
* @param maxAge the max age
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String name,
|
||||
String value, int maxAge) {
|
||||
setCookie(request, response, name, value, maxAge, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置COOKIE
|
||||
*
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @param name the name
|
||||
* @param value the value
|
||||
* @param maxAge the max age
|
||||
* @param allSubDomain the all sub domain
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:ParameterNumber")
|
||||
public static void setCookie(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
String name,
|
||||
String value,
|
||||
int maxAge,
|
||||
boolean allSubDomain) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
cookie.setMaxAge(maxAge);
|
||||
if (allSubDomain) {
|
||||
String serverName = request.getServerName();
|
||||
String domain = getDomainOfServerName(serverName);
|
||||
if (domain != null && domain.indexOf(CharPool.DOT) != -1) {
|
||||
cookie.setDomain(CharPool.DOT + domain);
|
||||
}
|
||||
}
|
||||
cookie.setPath("/");
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户访问URL中的根域名
|
||||
* 例如: www.dlog.cn -> dlog.cn
|
||||
*
|
||||
* @param host the host
|
||||
* @return domain of server name
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings(value = {"PMD.UndefineMagicConstantRule", "checkstyle:ReturnCount"})
|
||||
public static String getDomainOfServerName(String host) {
|
||||
if (isIpAddr(host)) {
|
||||
return null;
|
||||
}
|
||||
String[] names = StringUtils.split(host, StringPool.DOT);
|
||||
int len = Objects.requireNonNull(names).length;
|
||||
if (len == 1) {
|
||||
return null;
|
||||
}
|
||||
int www = 3;
|
||||
if (len == www) {
|
||||
return makeup(names[len - 2], names[len - 1]);
|
||||
}
|
||||
if (len > www) {
|
||||
String dp = names[len - 2];
|
||||
if ("com".equalsIgnoreCase(dp)
|
||||
|| "gov".equalsIgnoreCase(dp)
|
||||
|| "net".equalsIgnoreCase(dp)
|
||||
|| "edu".equalsIgnoreCase(dp)
|
||||
|| "org".equalsIgnoreCase(dp)) {
|
||||
return makeup(names[len - 3], names[len - 2], names[len - 1]);
|
||||
} else {
|
||||
return makeup(names[len - 2], names[len - 1]);
|
||||
}
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makeup string
|
||||
*
|
||||
* @param ps ps
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
private static String makeup(@NotNull String... ps) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (int idx = 0; idx < ps.length; idx++) {
|
||||
if (idx > 0) {
|
||||
s.append('.');
|
||||
}
|
||||
s.append(ps[idx]);
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete cookie.
|
||||
*
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @param name the name
|
||||
* @param allSubDomain the all sub domain
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void deleteCookie(HttpServletRequest request,
|
||||
HttpServletResponse response, String name, boolean allSubDomain) {
|
||||
setCookie(request, response, name, "", 0, allSubDomain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HTTP端口
|
||||
*
|
||||
* @param req the req
|
||||
* @return http port
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static int getHttpPort(@NotNull HttpServletRequest req) {
|
||||
try {
|
||||
return new URL(req.getRequestURL().toString()).getPort();
|
||||
} catch (MalformedURLException excp) {
|
||||
return 80;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器提交的整形参数
|
||||
*
|
||||
* @param req the req
|
||||
* @param param the param
|
||||
* @param defaultValue the default value
|
||||
* @return param param
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static int getParam(@NotNull HttpServletRequest req, String param, int defaultValue) {
|
||||
return NumberUtils.toInt(req.getParameter(param), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器提交的整形参数
|
||||
*
|
||||
* @param req the req
|
||||
* @param param the param
|
||||
* @param defaultValue the default value
|
||||
* @return param param
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static long getParam(@NotNull HttpServletRequest req, String param, long defaultValue) {
|
||||
return NumberUtils.toLong(req.getParameter(param), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器提交的字符串参数?
|
||||
*
|
||||
* @param req the req
|
||||
* @param param the param
|
||||
* @param defaultValue the default value
|
||||
* @return param param
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getParam(@NotNull HttpServletRequest req, String param, String defaultValue) {
|
||||
String value = req.getParameter(param);
|
||||
return (StringUtils.isEmpty(value)) ? defaultValue : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*
|
||||
* @param request the request
|
||||
* @return parameters map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Map<String, String> getParameterMap(@NotNull HttpServletRequest request) {
|
||||
ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request);
|
||||
|
||||
Map<String, String> returnMap = Maps.newHashMapWithExpectedSize(16);
|
||||
//request.getParameterMap() 返回的是一个Map类型的值,该返回值记录着前端 (如jsp页面) 所提交请求中的请求参数和请求参数值的映射关系. 这个返回值有个特别之处——只能读.
|
||||
Map<String, String[]> parameterMap = cachingRequestWrapper.getParameterMap();
|
||||
// Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry. 它表示Map中的一个实体 (一个key-value对) . 接口中有getKey(),getValue方法.
|
||||
for (Map.Entry<String, String[]> en : parameterMap.entrySet()) {
|
||||
String value = cachingRequestWrapper.getParameter(en.getKey());
|
||||
try {
|
||||
returnMap.put(en.getKey(), URLDecoder.decode(value, Charsets.UTF_8_NAME));
|
||||
} catch (Exception e) {
|
||||
log.error("url decode error", e);
|
||||
}
|
||||
}
|
||||
return returnMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header map.
|
||||
*
|
||||
* @param request the request
|
||||
* @return the map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Map<String, String> getHeader(@NotNull HttpServletRequest request) {
|
||||
// 获取所有的消息头名称
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
if (headerNames == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, String> headers = Maps.newHashMapWithExpectedSize(16);
|
||||
// 获取获取的消息头名称,获取对应的值,并输出
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String nextElement = headerNames.nextElement();
|
||||
headers.put(nextElement, request.getHeader(nextElement));
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header string
|
||||
*
|
||||
* @param headerName header name
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getHeader(String headerName) {
|
||||
Map<String, String> headers = getHeader(getRequest());
|
||||
return headers.get(headerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否multipart/form-data or application/octet-stream表单提交方式
|
||||
*
|
||||
* @param request the request
|
||||
* @return boolean boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static boolean isMultipartContent(@NotNull HttpServletRequest request) {
|
||||
if (!HttpMethod.POST.name().equalsIgnoreCase(request.getMethod())) {
|
||||
return false;
|
||||
}
|
||||
String contentType = request.getContentType();
|
||||
if (contentType == null) {
|
||||
return false;
|
||||
}
|
||||
contentType = contentType.toLowerCase(Locale.ENGLISH);
|
||||
return contentType.startsWith("multipart/") || MediaType.APPLICATION_OCTET_STREAM_VALUE.equals(contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build query params map map.
|
||||
*
|
||||
* @param request the request
|
||||
* @return the map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Map<String, Object> buildQueryParamsMap(@NotNull HttpServletRequest request) {
|
||||
ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request);
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
Enumeration<String> e = cachingRequestWrapper.getParameterNames();
|
||||
|
||||
StringBuilder tmpbuff = new StringBuilder();
|
||||
if (e.hasMoreElements()) {
|
||||
while (e.hasMoreElements()) {
|
||||
String name = e.nextElement();
|
||||
String[] values = cachingRequestWrapper.getParameterValues(name);
|
||||
if (values.length == 1) {
|
||||
if (StringUtils.isNotBlank(values[0])) {
|
||||
params.put(name, values[0]);
|
||||
}
|
||||
} else {
|
||||
tmpbuff.setLength(0);
|
||||
for (String value : values) {
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
tmpbuff.append(value.trim()).append(",");
|
||||
}
|
||||
}
|
||||
if (tmpbuff.length() > 0) {
|
||||
tmpbuff.deleteCharAt(tmpbuff.length() - 1);
|
||||
params.put(name, tmpbuff.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets typesafe request map *
|
||||
*
|
||||
* @param request request
|
||||
* @return the typesafe request map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private @NotNull Map<String, String> getTypesafeRequestMap(@NotNull HttpServletRequest request) {
|
||||
ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request);
|
||||
Enumeration<?> requestParamNames = cachingRequestWrapper.getParameterNames();
|
||||
Map<String, String> typesafeRequestMap = Maps.newHashMap();
|
||||
|
||||
while (requestParamNames.hasMoreElements()) {
|
||||
String requestParamName = (String) requestParamNames.nextElement();
|
||||
String requestParamValue = cachingRequestWrapper.getParameter(requestParamName);
|
||||
typesafeRequestMap.put(requestParamName, requestParamValue);
|
||||
}
|
||||
|
||||
return typesafeRequestMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 Body, 注意: 调用此方法后还需要二次读取 request时, HttpServletRequest 必须为 CacheRequestWrapper
|
||||
*
|
||||
* @param request the request
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String getBody(@NotNull HttpServletRequest request) {
|
||||
ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request);
|
||||
String bodyInfo = "";
|
||||
try (InputStream is = cachingRequestWrapper.getInputStream()) {
|
||||
bodyInfo = IoUtils.toString(is, StandardCharsets.UTF_8);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return StringUtils.replaceBlank(bodyInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets cache input stream *
|
||||
*
|
||||
* @param body body
|
||||
* @return the cache input stream
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static ServletInputStream getCacheInputStream(byte[] body) {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
||||
|
||||
return new ServletInputStream() {
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
return bais.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 url 参数解析为 map
|
||||
*
|
||||
* @param url the url
|
||||
* @return the query params
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static @NotNull Map<String, Object> converterToMap(@NotNull String url) {
|
||||
try {
|
||||
Map<String, Object> params = new HashMap<>(16);
|
||||
for (String param : url.split(StringPool.AMPERSAND)) {
|
||||
String[] pair = param.split(StringPool.EQUALS);
|
||||
String key = URLDecoder.decode(pair[0], Charsets.UTF_8_NAME);
|
||||
String value = StringPool.EMPTY;
|
||||
if (pair.length > 1) {
|
||||
value = URLDecoder.decode(pair[1], Charsets.UTF_8_NAME);
|
||||
}
|
||||
params.put(key, value);
|
||||
}
|
||||
return params;
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
log.error("{}", ex.getMessage());
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>aivfo-element</artifactId>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>aivfo-element-common</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<artifactId>aivfo-element-core</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package com.aivfo.start.common.enums;
|
||||
|
||||
import com.aivfo.el.start.core.api.IResultCode;
|
||||
import com.aivfo.el.starter.base.annotation.BusinessLevel;
|
||||
import com.aivfo.el.starter.base.annotation.ModelSerial;
|
||||
import com.aivfo.el.starter.base.annotation.SystemLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@ModelSerial(modelName = "R")
|
||||
public enum ServletCodeEnum implements IResultCode {
|
||||
/**
|
||||
* 方法级的参数验证时候后的异常
|
||||
*/
|
||||
@BusinessLevel
|
||||
METHOD_ARGUMENT_NOT_VALID(4400,
|
||||
"请求参数验证失败: [{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "MethodArgumentNotValidException"),
|
||||
/**
|
||||
* Method argument type mismatch exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
METHOD_ARGUMENT_TYPE_MISMATCH(4400,
|
||||
"请求参数格式错误: [{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "MethodArgumentTypeMismatchException"),
|
||||
/**
|
||||
* Missing servlet request part exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
MISSING_SERVLET_REQUEST_PART(4400,
|
||||
"[{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "MissingServletRequestPartException"),
|
||||
/**
|
||||
* Missing path variable exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
MISSING_PATH_VARIABLE(4400,
|
||||
"[{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "MissingPathVariableException"),
|
||||
/**
|
||||
* 参数绑定异常
|
||||
*/
|
||||
@BusinessLevel
|
||||
BIND_ERROR(4400,
|
||||
"请求参数绑定错误: [{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "BindException"),
|
||||
/**
|
||||
* Missing servlet request parameter exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
MISSING_SERVLET_REQUEST_PARAMETER(4400,
|
||||
"缺少请求参数: [{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "MissingServletRequestParameterException"),
|
||||
/**
|
||||
* Type mismatch exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
TYPE_MISMATCH(4400, "[{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "TypeMismatchException"),
|
||||
/**
|
||||
* Servlet request binding exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
SERVLET_REQUEST_BINDING(4400,
|
||||
"[{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "ServletRequestBindingException"),
|
||||
/**
|
||||
* Missing request header exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
MISSING_REQUEST_HEADER(4400,
|
||||
"缺少必要的 header 参数: [{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "MissingServletRequestParameterException"),
|
||||
/**
|
||||
* Http message not readable exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
HTTP_MESSAGE_NOT_READABLE(4400,
|
||||
"消息不能读取: [{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "HttpMessageNotReadableException"),
|
||||
/**
|
||||
* No handler found exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
NO_HANDLER_FOUND(4404,
|
||||
"404 没找到请求!: [{}]",
|
||||
HttpServletResponse.SC_NOT_FOUND, "NoHandlerFoundException"),
|
||||
/**
|
||||
* No such request handling method exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
NO_SUCH_REQUEST_HANDLING_METHOD(4404,
|
||||
"[{}]",
|
||||
HttpServletResponse.SC_NOT_FOUND, "NoSuchRequestHandlingMethodException"),
|
||||
/**
|
||||
* Http request method not supported exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
HTTP_REQUEST_METHOD_NOT_SUPPORTED(4405,
|
||||
"不支持当前请求方法: [{}]",
|
||||
HttpServletResponse.SC_METHOD_NOT_ALLOWED, "HttpRequestMethodNotSupportedException"),
|
||||
/**
|
||||
* Http media type not acceptable exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
HTTP_MEDIA_TYPE_NOT_ACCEPTABLE(4406,
|
||||
"[{}]",
|
||||
HttpServletResponse.SC_NOT_ACCEPTABLE, "HttpMediaTypeNotAcceptableException"),
|
||||
/**
|
||||
* Http media type not supported exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
HTTP_MEDIA_TYPE_NOT_SUPPORTED(4415,
|
||||
"不支持当前媒体类型: [{}]",
|
||||
HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "HttpMediaTypeNotSupportedException"),
|
||||
/**
|
||||
* Conversion not supported exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
CONVERSION_NOT_SUPPORTED(4500,
|
||||
"服务器异常: [{}]",
|
||||
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "ConversionNotSupportedException"),
|
||||
/**
|
||||
* Http message not writable exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
HTTP_MESSAGE_NOT_WRITABLE(4500,
|
||||
"服务器异常: [{}]",
|
||||
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "HttpMessageNotWritableException"),
|
||||
/**
|
||||
* Async request timeout exception servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
ASYNC_REQUEST_TIMEOUT(4503,
|
||||
"异步请求超时: [{}]",
|
||||
HttpServletResponse.SC_SERVICE_UNAVAILABLE, "AsyncRequestTimeoutException"),
|
||||
/**
|
||||
* Missing requestheader servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
MISSING_REQUESTHEADER(4504,
|
||||
"缺少必要的 header 参数: [{}]",
|
||||
HttpServletResponse.SC_BAD_REQUEST, "MissingRequestHeaderException"),
|
||||
|
||||
/**
|
||||
* Inner error servlet code enum
|
||||
*/
|
||||
@SystemLevel
|
||||
INNER_ERROR(5000, "内部错误", HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
"InnerErrorException");
|
||||
|
||||
/**
|
||||
* 返回码,目前与 {@link #statusCode} 相同
|
||||
*/
|
||||
public final Integer code;
|
||||
/**
|
||||
* 返回信息,直接读取异常的 message
|
||||
*/
|
||||
public final String message;
|
||||
/**
|
||||
* HTTP 状态码
|
||||
*/
|
||||
public final int statusCode;
|
||||
/**
|
||||
* 异常名
|
||||
*/
|
||||
public final String exceptionName;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.aivfo.start.common.exception;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 开发环境时输出更多的异常信息
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ExceptionInfo implements Serializable {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = -5072425562316472427L;
|
||||
/**
|
||||
* 请求路径
|
||||
*/
|
||||
private String path;
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
private Object params;
|
||||
/**
|
||||
* 请求方式
|
||||
*/
|
||||
private String method;
|
||||
/**
|
||||
* 请求方地址
|
||||
*/
|
||||
private String remoteAddr;
|
||||
/**
|
||||
* header
|
||||
*/
|
||||
private Object headers;
|
||||
/**
|
||||
* 追踪 id
|
||||
*/
|
||||
private String traceId;
|
||||
/**
|
||||
* 异常类
|
||||
*/
|
||||
private String exceptionClass;
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
/**
|
||||
* 异常堆栈
|
||||
*/
|
||||
private String stackTrace;
|
||||
|
||||
}
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
package com.aivfo.start.common.exception;
|
||||
|
||||
import com.aivfo.el.start.core.api.BaseCodes;
|
||||
import com.aivfo.el.start.core.api.R;
|
||||
import com.aivfo.el.start.core.util.ResultCodeUtils;
|
||||
import com.aivfo.el.starter.base.Result;
|
||||
import com.aivfo.el.starter.base.constant.BasicConstant;
|
||||
import com.aivfo.el.starter.base.contex.Trace;
|
||||
import com.aivfo.el.starter.base.exception.BasicException;
|
||||
import com.aivfo.el.starter.base.utils.Exceptions;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.aivfo.el.starter.core.util.EnumUtils;
|
||||
import com.aivfo.el.starter.core.util.WebUtils;
|
||||
import com.aivfo.start.common.enums.ServletCodeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.ConversionNotSupportedException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.*;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* Build result result
|
||||
*
|
||||
* @param e e
|
||||
* @param request request
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
private Result<?> buildResult(BasicException e, @NotNull HttpServletRequest request) {
|
||||
Result<Object> failed = R.failed(e.getCode(), e.getMessage());
|
||||
failed.setExtend(this.buildExceptionData(e, request));
|
||||
return failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build exception data exception info
|
||||
*
|
||||
* @param throwable throwable
|
||||
* @param request request
|
||||
* @return the exception info
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@SuppressWarnings("checkstyle:Regexp")
|
||||
private ExceptionInfo buildExceptionData(@NotNull Throwable throwable, @NotNull HttpServletRequest request) {
|
||||
ExceptionInfo exceptionEntity = new ExceptionInfo();
|
||||
exceptionEntity.setPath(request.getRequestURI());
|
||||
exceptionEntity.setParams(WebUtils.getRequestParamString(request));
|
||||
exceptionEntity.setMethod(request.getMethod());
|
||||
exceptionEntity.setRemoteAddr(request.getRemoteAddr());
|
||||
exceptionEntity.setHeaders(WebUtils.getHeader(request));
|
||||
exceptionEntity.setExceptionClass(throwable.getClass().getName());
|
||||
exceptionEntity.setTraceId(Trace.context().get());
|
||||
exceptionEntity.setErrorMessage(throwable.getMessage());
|
||||
exceptionEntity.setStackTrace(Exceptions.getStackTraceAsString(throwable));
|
||||
return exceptionEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义异常
|
||||
*
|
||||
* @param e 异常
|
||||
* @param request request
|
||||
* @return 异常结果 result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ExceptionHandler(value = {
|
||||
BasicException.class
|
||||
})
|
||||
public Result<?> handleBasicException(@NotNull BasicException e, @NotNull HttpServletRequest request) {
|
||||
log.error("{} path: {}", e.getMessage(), request.getRequestURI());
|
||||
return this.buildResult(e, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果是正式环境, 将服务器异常转换为对用户友好信息, 不暴露具体异常信息, 而是通过日志输出
|
||||
* 非正式则输出正确异常信息
|
||||
*
|
||||
* @param e 异常
|
||||
* @param request request
|
||||
* @return 异常结果 result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ExceptionHandler(value = {
|
||||
NoHandlerFoundException.class,
|
||||
HttpRequestMethodNotSupportedException.class,
|
||||
HttpMediaTypeNotSupportedException.class,
|
||||
HttpMediaTypeNotAcceptableException.class,
|
||||
MissingPathVariableException.class,
|
||||
MissingRequestHeaderException.class,
|
||||
MissingServletRequestParameterException.class,
|
||||
HttpMessageNotReadableException.class,
|
||||
HttpMessageNotWritableException.class,
|
||||
MethodArgumentTypeMismatchException.class,
|
||||
ServletRequestBindingException.class,
|
||||
ConversionNotSupportedException.class,
|
||||
MissingServletRequestPartException.class,
|
||||
AsyncRequestTimeoutException.class
|
||||
})
|
||||
@SuppressWarnings("all")
|
||||
public Result<?> handleServletException(@NotNull Exception e, HttpServletRequest request) {
|
||||
log.error(e.getMessage());
|
||||
ExceptionInfo exceptionEntity = this.buildExceptionData(e, request);
|
||||
try {
|
||||
String message = e.getMessage();
|
||||
if (e instanceof MissingServletRequestParameterException) {
|
||||
MissingServletRequestParameterException exception = (MissingServletRequestParameterException) e;
|
||||
message = exception.getParameterName()
|
||||
+ " ("
|
||||
+ exception.getParameterType()
|
||||
+ ")";
|
||||
}
|
||||
ServletCodeEnum servletExceptionEnum = EnumUtils.of(ServletCodeEnum.class,
|
||||
servletCodeEnum -> e.getClass().getSimpleName()
|
||||
.equals(servletCodeEnum.exceptionName))
|
||||
.orElse(ServletCodeEnum.INNER_ERROR);
|
||||
if (servletExceptionEnum == ServletCodeEnum.INNER_ERROR) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
final Result<Object> result = R.failed(ResultCodeUtils.generateCode(servletExceptionEnum),
|
||||
StringUtils.format(servletExceptionEnum.getMessage(), message));
|
||||
result.setExtend(exceptionEntity);
|
||||
return result;
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
final Result<Object> failed = R.failed(ResultCodeUtils.generateCode(BaseCodes.FAILURE), e.getMessage());
|
||||
failed.setExtend(exceptionEntity);
|
||||
return failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数绑定异常
|
||||
*
|
||||
* @param e 异常
|
||||
* @return 异常结果 result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ExceptionHandler(value = {
|
||||
BindException.class,
|
||||
})
|
||||
public Result<?> handleBindException(@NotNull BindException e) {
|
||||
log.debug("exception from BindException");
|
||||
return this.wrapperBindingResult(e.getBindingResult());
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装绑定异常结果, 输出全部绑定异常信息
|
||||
*
|
||||
* @param bindingResult 绑定结果
|
||||
* @return 异常结果 result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
private Result<Void> wrapperBindingResult(@NotNull BindingResult bindingResult) {
|
||||
StringBuilder warnMessage = new StringBuilder();
|
||||
String message = "";
|
||||
for (ObjectError error : bindingResult.getAllErrors()) {
|
||||
if (StringUtils.isBlank(message)) {
|
||||
message = bindingResult.getAllErrors().get(0).getDefaultMessage();
|
||||
}
|
||||
warnMessage.append(", ");
|
||||
if (error instanceof FieldError) {
|
||||
warnMessage.append(((FieldError) error).getField()).append(": ");
|
||||
}
|
||||
warnMessage.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());
|
||||
|
||||
}
|
||||
log.warn("参数绑定校验异常: {} params: {}", warnMessage.substring(2), bindingResult.getTarget());
|
||||
return R.failed(ResultCodeUtils.generateCode(BaseCodes.PARAM_VERIFY_ERROR), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 @Validated (spring) 验证异常,将校验失败的所有异常组合成一条错误信息
|
||||
*
|
||||
* @param e 异常
|
||||
* @return 异常结果 result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ExceptionHandler(value = {
|
||||
MethodArgumentNotValidException.class
|
||||
})
|
||||
public Result<Void> handleValidException(@NotNull MethodArgumentNotValidException e) {
|
||||
log.debug("exception from MethodArgumentNotValidException");
|
||||
return this.wrapperBindingResult(e.getBindingResult());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 @Valid (javax.validation [api], hibernate-validator [impl]) 验证异常
|
||||
*
|
||||
* @param e the e
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ExceptionHandler(value = {
|
||||
ConstraintViolationException.class
|
||||
})
|
||||
public Result<Void> handleValidException(@NotNull ConstraintViolationException e) {
|
||||
log.warn("参数校验失败: [{}]", e.getMessage());
|
||||
return R.failed(ResultCodeUtils.generateCode(BaseCodes.PARAM_VERIFY_ERROR), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle resource access exception
|
||||
*
|
||||
* @param e e
|
||||
* @return the result
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@ExceptionHandler(value = {
|
||||
RestClientException.class
|
||||
})
|
||||
public Result<Void> handleResourceAccessException(@NotNull RestClientException e) {
|
||||
log.error("Agent Service 不可用", e);
|
||||
return R.failed(ResultCodeUtils.generateCode(BaseCodes.AGENT_DISABLE_EXCEPTION), BaseCodes.AGENT_DISABLE_EXCEPTION.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 未定义异常
|
||||
*
|
||||
* @param e 异常
|
||||
* @param request request
|
||||
* @return 异常结果 result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ExceptionHandler(value = {
|
||||
Exception.class
|
||||
})
|
||||
public Result<?> handleException(Exception e, HttpServletRequest request) {
|
||||
return this.handleError(e, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获 error 级异常
|
||||
*
|
||||
* @param e the e
|
||||
* @param request request
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ExceptionHandler(Throwable.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result<?> handleError(Throwable e, HttpServletRequest request) {
|
||||
Result<Object> failed = R.failed(ResultCodeUtils.generateCode(BaseCodes.SERVER_INNER_ERROR), e.getMessage());
|
||||
failed.setExtend(this.buildExceptionData(e, request));
|
||||
request.setAttribute(BasicConstant.REQUEST_EXCEPTION_INFO_ATTR, e);
|
||||
return failed;
|
||||
}
|
||||
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.aivfo.start.common.exception;
|
||||
|
||||
import com.aivfo.el.start.core.exception.BaseException;
|
||||
|
||||
public class InstanceException extends BaseException {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = -5426025146601282119L;
|
||||
|
||||
/**
|
||||
* Instantiates a new Instance exception.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public InstanceException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Instance exception.
|
||||
*
|
||||
* @param message the message
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public InstanceException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Instance exception.
|
||||
*
|
||||
* @param cause the cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public InstanceException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Instance exception.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public InstanceException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Instance exception.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the cause
|
||||
* @param enableSuppression the enable suppression
|
||||
* @param writableStackTrace the writable stack traceId
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public InstanceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.aivfo.start.common.exception;
|
||||
|
||||
import com.aivfo.el.start.core.exception.BaseException;
|
||||
|
||||
public class PropertiesException extends BaseException {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = -6498727260647427447L;
|
||||
|
||||
/**
|
||||
* Properties exception
|
||||
*
|
||||
* @param msg msg
|
||||
* @param args args
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public PropertiesException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties exception
|
||||
*
|
||||
* @param cause cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public PropertiesException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package com.aivfo.start.common.exception;
|
||||
|
||||
import com.aivfo.el.start.core.api.BaseCodes;
|
||||
import com.aivfo.el.start.core.exception.BaseException;
|
||||
|
||||
public class StarterException extends BaseException {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = 4076461843028836262L;
|
||||
|
||||
/**
|
||||
* Instantiates a new Json exception.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public StarterException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Json exception.
|
||||
*
|
||||
* @param message the message
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public StarterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Json exception.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public StarterException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* msg 占位符替换
|
||||
*
|
||||
* @param msg msg
|
||||
* @param args args
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public StarterException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
this.resultCode = BaseCodes.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Json exception.
|
||||
*
|
||||
* @param cause the cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public StarterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Json exception.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the cause
|
||||
* @param enableSuppression the enable suppression
|
||||
* @param writableStackTrace the writable stack traceId
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected StarterException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>aivfo-element</artifactId>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>aivfo-element-core</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<artifactId>aivfo-element-base</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.8</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.aivfo.el.start.core.api;
|
||||
|
||||
import com.aivfo.el.start.core.assertion.BaseExceptionAssert;
|
||||
import com.aivfo.el.starter.base.Result;
|
||||
import com.aivfo.el.starter.base.annotation.BusinessLevel;
|
||||
import com.aivfo.el.starter.base.annotation.ModelSerial;
|
||||
import com.aivfo.el.starter.base.annotation.SystemLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 通用返回结果
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@ModelSerial
|
||||
public enum BaseCodes implements BaseExceptionAssert {
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS(Integer.parseInt(Result.SUCCESS_CODE), Result.SUCCESS_MESSAGE),
|
||||
/**
|
||||
* 默认没有数据响应
|
||||
*/
|
||||
@SystemLevel
|
||||
DEFAULT_NULL_DATA(2222, "暂无数据"),
|
||||
/**
|
||||
* 默认的失败响应
|
||||
*/
|
||||
@SystemLevel
|
||||
FAILURE(Integer.parseInt(Result.FAILURE_CODE), Result.FAILURE_MESSAGE),
|
||||
/**
|
||||
* 参数校验失败
|
||||
*/
|
||||
@BusinessLevel
|
||||
PARAM_VERIFY_ERROR(4100, "参数校验失败: [{}]"),
|
||||
/**
|
||||
* 数据不存在
|
||||
*/
|
||||
@SystemLevel
|
||||
DATA_ERROR(4101, "数据不存在"),
|
||||
/**
|
||||
* 操作失败
|
||||
*/
|
||||
@SystemLevel
|
||||
OPTION_FAILURE(4102, "操作失败"),
|
||||
/**
|
||||
* Config error base codes
|
||||
*/
|
||||
@SystemLevel
|
||||
CONFIG_ERROR(7000, "配置错误"),
|
||||
/**
|
||||
* Server inner error base codes.
|
||||
*/
|
||||
@SystemLevel
|
||||
SERVER_INNER_ERROR(5000, "服务内部错误"),
|
||||
/**
|
||||
* Service invoke error base codes
|
||||
*/
|
||||
@SystemLevel
|
||||
SERVICE_INVOKE_ERROR(5001, "服务不可用"),
|
||||
/**
|
||||
* Agent exception
|
||||
*/
|
||||
@SystemLevel
|
||||
AGENT_INVOKE_EXCEPTION(5002, "Rest Client 调用失败"),
|
||||
/**
|
||||
* Agent enable exception base codes
|
||||
*/
|
||||
@SystemLevel
|
||||
AGENT_DISABLE_EXCEPTION(5003, "Agent Service 不可用"),
|
||||
/**
|
||||
* Rpc error base codes
|
||||
*/
|
||||
@SystemLevel
|
||||
RPC_ERROR(5004, "远程服务不可用"),
|
||||
/**
|
||||
* Gateway not fund instances error base codes
|
||||
*/
|
||||
GATEWAY_NOT_FUND_INSTANCES_ERROR(5005, "网关路由失败, 未找到指定服务"),
|
||||
/**
|
||||
* 路由配置错误
|
||||
*/
|
||||
GATEWAY_ROUTER_ERROR(5006, "网关路由配置错误: 1.请检查调用端配置 ; 2.检查路由配置是否正确."),
|
||||
/**
|
||||
* Agent service not found error base codes
|
||||
*/
|
||||
@BusinessLevel
|
||||
AGENT_SERVICE_NOT_FOUND_ERROR(5007, "未找到指定服务"),
|
||||
/**
|
||||
* 服务器繁忙,请稍后重试
|
||||
*/
|
||||
@SystemLevel
|
||||
SERVER_BUSY(9998, "服务暂时不可用,请稍后重试!"),
|
||||
/**
|
||||
* 服务器异常,无法识别的异常,尽可能对通过判断减少未定义异常抛出
|
||||
*/
|
||||
@SystemLevel
|
||||
SERVER_ERROR(9999, "网络异常");
|
||||
|
||||
/**
|
||||
* 返回码
|
||||
*/
|
||||
private final Integer code;
|
||||
/**
|
||||
* 返回消息
|
||||
*/
|
||||
private final String message;
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package com.aivfo.el.start.core.api;
|
||||
|
||||
|
||||
import com.aivfo.el.starter.base.Result;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description:
|
||||
*/
|
||||
public interface GeneralResult {
|
||||
|
||||
/**
|
||||
* 请求成功
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
default <T> Result<T> ok() {
|
||||
return this.ok(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功
|
||||
*
|
||||
* @param <T> 对象泛型
|
||||
* @param data 数据内容
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
default <T> Result<T> ok(T data) {
|
||||
return R.succeed(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求失败
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param msg 提示内容
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
default <T> Result<T> fail(String msg) {
|
||||
return R.failed(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求失败
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param errorCode 请求错误码
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
default <T> Result<T> fail(IResultCode errorCode) {
|
||||
return R.failed(errorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status result.
|
||||
*
|
||||
* @param flag the flag
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
default Result<Boolean> status(boolean flag) {
|
||||
return this.status(flag, BaseCodes.FAILURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status result
|
||||
*
|
||||
* @param flag flag
|
||||
* @param resultCode result code
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
default Result<Boolean> status(boolean flag, IResultCode resultCode) {
|
||||
return R.status(flag, resultCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status result
|
||||
*
|
||||
* @param flag flag
|
||||
* @param message message
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
default Result<Boolean> status(boolean flag, String message) {
|
||||
return R.status(flag, message);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.aivfo.el.start.core.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 请求响应代码接口
|
||||
*/
|
||||
public interface IResultCode extends Serializable {
|
||||
|
||||
/**
|
||||
* 获取返回消息, 可使用占位符
|
||||
*
|
||||
* @return String message
|
||||
* @since 1.0.0
|
||||
*/
|
||||
String getMessage();
|
||||
|
||||
/**
|
||||
* 获取返回状态码
|
||||
*
|
||||
* @return String code
|
||||
* @since 1.0.0
|
||||
*/
|
||||
Integer getCode();
|
||||
|
||||
/**
|
||||
* Name
|
||||
*
|
||||
* @return the string
|
||||
* @since 1.5.0
|
||||
*/
|
||||
default String name() {
|
||||
return "FAILURE";
|
||||
}
|
||||
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
package com.aivfo.el.start.core.api;
|
||||
|
||||
import com.aivfo.el.start.core.support.ChainMap;
|
||||
import com.aivfo.el.start.core.util.CollectionUtils;
|
||||
import com.aivfo.el.start.core.util.ResultCodeUtils;
|
||||
import com.aivfo.el.starter.base.Result;
|
||||
import com.aivfo.el.starter.base.contex.Trace;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 将标准返回的字段全部放在了
|
||||
*/
|
||||
@SuppressWarnings({"PMD.ClassNamingShouldBeCamelRule"})
|
||||
public final class R<T> extends Result<T> {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = 3077918845714343375L;
|
||||
|
||||
/**
|
||||
* R
|
||||
*
|
||||
* @param code code
|
||||
* @param message message
|
||||
* @param data data
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
private R(String code, String message, T data) {
|
||||
super(code, message, data, Trace.context().get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Succeed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Result<T> succeed() {
|
||||
return succeed((T) Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Succeed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param data data
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
@NotNull
|
||||
public static <T> Result<T> succeed(T data) {
|
||||
return succeed(SUCCESS_CODE, SUCCESS_MESSAGE, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Succeed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param code code
|
||||
* @param msg msg
|
||||
* @param data data
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
@NotNull
|
||||
public static <T> Result<T> succeed(String code, String msg, T data) {
|
||||
return build(code, msg, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
@NotNull
|
||||
public static <T> Result<T> failed() {
|
||||
return failed(FAILURE_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param msg msg
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
@NotNull
|
||||
public static <T> Result<T> failed(String msg) {
|
||||
return failed(FAILURE_CODE, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param code code
|
||||
* @param msg msg
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(pure = true)
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Result<T> failed(String code, String msg) {
|
||||
return failed(code, msg, (T) Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param resultCode result code
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static <T> Result<T> failed(@NotNull IResultCode resultCode) {
|
||||
return failed(ResultCodeUtils.generateCode(resultCode), resultCode.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param resultCode result code
|
||||
* @param msg msg
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static <T> Result<T> failed(@NotNull IResultCode resultCode, String msg) {
|
||||
return failed(ResultCodeUtils.generateCode(resultCode), StringUtils.format(resultCode.getMessage(), msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param resultCode result code
|
||||
* @param msg msg
|
||||
* @param data data
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(value = "_, _, _ -> new", pure = true)
|
||||
public static <T> Result<T> failed(@NotNull IResultCode resultCode, String msg, T data) {
|
||||
return failed(ResultCodeUtils.generateCode(resultCode), StringUtils.format(resultCode.getMessage(), msg), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param code code
|
||||
* @param msg msg
|
||||
* @param data data
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(value = "_, _, _ -> new", pure = true)
|
||||
public static <T> Result<T> failed(String code, String msg, T data) {
|
||||
return build(code, msg, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param code code
|
||||
* @param msg msg
|
||||
* @return the result
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Contract("_, _ -> new")
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Result<T> build(String code, String msg) {
|
||||
return build(code, msg, (T) Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param code code
|
||||
* @param msg msg
|
||||
* @param data data
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(value = "_, _, _ -> new", pure = true)
|
||||
@NotNull
|
||||
public static <T> Result<T> build(String code, String msg, T data) {
|
||||
return new R<>(code, msg, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param expression expression
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("_ -> !null")
|
||||
public static <T> Result<T> status(boolean expression) {
|
||||
return status(expression, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Status result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param expression expression
|
||||
* @param resultCode result code
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("_, _ -> !null")
|
||||
public static <T> Result<T> status(boolean expression, @NotNull IResultCode resultCode) {
|
||||
return status(expression, resultCode.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Status result
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param expression expression
|
||||
* @param message message
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("_, _ -> !null")
|
||||
public static <T> Result<T> status(boolean expression, String message) {
|
||||
return expression ? succeed() : failed(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Values result
|
||||
*
|
||||
* @param args 键值对一一对应
|
||||
* @return the result
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static Result<Map<String, Object>> values(@NotNull Object... args) {
|
||||
return succeed(CollectionUtils.toMap(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map map
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param data data
|
||||
* @return the map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static <T> Map<String, Object> map(T data) {
|
||||
return map(ResultCodeUtils.generateCode(BaseCodes.SUCCESS),
|
||||
true,
|
||||
data,
|
||||
BaseCodes.SUCCESS.getMessage(),
|
||||
Trace.context().get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map map
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param code code
|
||||
* @param success success
|
||||
* @param data data
|
||||
* @param message message
|
||||
* @param traceId trace id
|
||||
* @return the map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static <T> Map<String, Object> map(String code,
|
||||
boolean success,
|
||||
T data,
|
||||
String message,
|
||||
String traceId) {
|
||||
|
||||
return ChainMap.build(5)
|
||||
.put(CODE, code)
|
||||
.put(SUCCESS, success)
|
||||
.put(DATA, data)
|
||||
.put(MESSAGE, message)
|
||||
.put(TRACE_ID, traceId);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail map map
|
||||
*
|
||||
* @param resultCode result code
|
||||
* @return the map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static Map<String, Object> failMap(@NotNull IResultCode resultCode) {
|
||||
return map(ResultCodeUtils.generateCode(resultCode), false, null, resultCode.getMessage(), Trace.context().get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map map
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param resultCode result code
|
||||
* @param data data
|
||||
* @return the map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static <T> Map<String, Object> map(@NotNull IResultCode resultCode, T data) {
|
||||
return map(ResultCodeUtils.generateCode(resultCode),
|
||||
BaseCodes.SUCCESS.getCode().equals(resultCode.getCode()),
|
||||
data, resultCode.getMessage(), Trace.context().get());
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.aivfo.el.start.core.assertion;
|
||||
|
||||
import com.aivfo.el.start.core.api.IResultCode;
|
||||
import com.aivfo.el.start.core.exception.BaseException;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 全局错误异常断言
|
||||
*/
|
||||
public interface BaseExceptionAssert extends IResultCode, IAssert {
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
long serialVersionUID = 3077918845714343375L;
|
||||
|
||||
/**
|
||||
* New exceptions base exception.
|
||||
*
|
||||
* @param args the args
|
||||
* @return the base exception
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
default BaseException newException(Object... args) {
|
||||
return new BaseException(this, args, this.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* New exceptions base exception.
|
||||
*
|
||||
* @param t the t
|
||||
* @param args the args
|
||||
* @return the base exception
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
default BaseException newException(Throwable t, Object... args) {
|
||||
return new BaseException(this, args, this.getMessage(), t);
|
||||
}
|
||||
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
package com.aivfo.el.start.core.assertion;
|
||||
|
||||
|
||||
import com.aivfo.el.start.core.exception.BaseException;
|
||||
import com.aivfo.el.starter.base.asserts.Assertions;
|
||||
import com.aivfo.el.starter.core.function.CheckedCallable;
|
||||
import com.aivfo.el.starter.core.function.CheckedRunnable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 枚举类异常断言,提供简便的方式判断条件,并在条件满足时抛出异常
|
||||
* 错误码和错误信息定义在枚举类中,在本断言方法中,传递错误信息需要的参数
|
||||
* 底层会使用 {@link Assertions}
|
||||
* </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.26 20:06
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface IAssert {
|
||||
|
||||
/**
|
||||
* New exception
|
||||
*
|
||||
* @param args args
|
||||
* @return the base exception
|
||||
* @since 1.7.0
|
||||
*/
|
||||
BaseException newException(Object... args);
|
||||
|
||||
/**
|
||||
* New exception
|
||||
*
|
||||
* @param t t
|
||||
* @param args args
|
||||
* @return the base exception
|
||||
* @since 1.7.0
|
||||
*/
|
||||
BaseException newException(Throwable t, Object... args);
|
||||
|
||||
/**
|
||||
* Not blank
|
||||
*
|
||||
* @param str str
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void notBlank(String str, Object... args) {
|
||||
Assertions.notBlank(str, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Not empty
|
||||
*
|
||||
* @param arrays arrays
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void notEmpty(Object[] arrays, Object... args) {
|
||||
Assertions.notEmpty(arrays, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Not empty
|
||||
*
|
||||
* @param c c
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void notEmpty(Collection<?> c, Object... args) {
|
||||
Assertions.notEmpty(c, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Not empty
|
||||
*
|
||||
* @param map map
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void notEmpty(Map<?, ?> map, Object... args) {
|
||||
Assertions.notEmpty(map, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is false
|
||||
*
|
||||
* @param expression expression
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void isFalse(boolean expression, Object... args) {
|
||||
Assertions.isFalse(expression, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is true
|
||||
*
|
||||
* @param expression expression
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void isTrue(boolean expression, Object... args) {
|
||||
Assertions.isTrue(expression, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is null
|
||||
*
|
||||
* @param obj obj
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void isNull(Object obj, Object... args) {
|
||||
Assertions.isNull(obj, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Not null
|
||||
*
|
||||
* @param obj obj
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void notNull(Object obj, Object... args) {
|
||||
Assertions.notNull(obj, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* 适用于没有占位符的错误消息
|
||||
*
|
||||
* @param obj obj
|
||||
* @param runnable runnable
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void notNull(Object obj, CheckedRunnable runnable) {
|
||||
Assertions.notNull(obj, this::newException, runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equals
|
||||
*
|
||||
* @param o1 o 1
|
||||
* @param o2 o 2
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void equals(Object o1, Object o2, Object... args) {
|
||||
Assertions.equals(o1, o2, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Not equals
|
||||
*
|
||||
* @param o1 o 1
|
||||
* @param o2 o 2
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void notEquals(Object o1, Object o2, Object... args) {
|
||||
Assertions.notEquals(o1, o2, () -> this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper
|
||||
*
|
||||
* @param runnable runnable
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void wrapper(CheckedRunnable runnable, Object... args) {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Throwable throwable) {
|
||||
this.fail(throwable, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param callable callable
|
||||
* @param args args
|
||||
* @return the t
|
||||
* @since 1.8.0
|
||||
*/
|
||||
default <T> T wrapper(CheckedCallable<T> callable, Object... args) {
|
||||
try {
|
||||
return callable.call();
|
||||
} catch (Throwable throwable) {
|
||||
this.fail(throwable, args);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail
|
||||
*
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void fail(Object... args) {
|
||||
this.fail(this.newException(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail
|
||||
*
|
||||
* @param t t
|
||||
* @param args args
|
||||
* @since 1.7.0
|
||||
*/
|
||||
default void fail(Throwable t, Object... args) {
|
||||
Assertions.fail(() -> this.newException(t, args));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.aivfo.el.start.core.convert;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 类型 转换 服务,添加了 IEnum 转换
|
||||
*/
|
||||
public final class CustomConversionService extends ApplicationConversionService {
|
||||
|
||||
/**
|
||||
* Custom conversion service
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private CustomConversionService() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom conversion service
|
||||
*
|
||||
* @param embeddedValueResolver the embedded value resolver
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private CustomConversionService(@Nullable StringValueResolver embeddedValueResolver) {
|
||||
super(embeddedValueResolver);
|
||||
super.addConverter(new EnumToStringConverter());
|
||||
super.addConverter(new StringToEnumConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets instance.
|
||||
*
|
||||
* @return the instance
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static GenericConversionService getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 静态内部类实现单例
|
||||
*/
|
||||
private static final class SingletonHolder {
|
||||
/**
|
||||
* INSTANCE
|
||||
*/
|
||||
private static final CustomConversionService INSTANCE = new CustomConversionService();
|
||||
|
||||
/**
|
||||
* Singleton holder
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
private SingletonHolder() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package com.aivfo.el.start.core.convert;
|
||||
|
||||
import com.aivfo.el.start.core.util.ConvertUtils;
|
||||
import com.aivfo.el.start.core.util.ReflectionUtils;
|
||||
import com.aivfo.el.start.core.util.Unchecked;
|
||||
import com.aivfo.el.starter.base.utils.ClassUtils;
|
||||
import com.aivfo.el.starter.core.function.CheckedFunction;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.cglib.core.Converter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 组合 spring cglib Converter 和 spring ConversionService
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class CustomConverter implements Converter {
|
||||
/**
|
||||
* TYPE_CACHE
|
||||
*/
|
||||
private static final ConcurrentMap<String, TypeDescriptor> TYPE_CACHE = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* Source clazz
|
||||
*/
|
||||
private final Class<?> sourceClazz;
|
||||
/**
|
||||
* Target clazz
|
||||
*/
|
||||
private final Class<?> targetClazz;
|
||||
|
||||
/**
|
||||
* cglib convert
|
||||
*
|
||||
* @param value 源对象属性
|
||||
* @param target 目标对象属性类
|
||||
* @param fieldName 目标的field名,原为 set 方法名,MicaBeanCopier 里做了更改
|
||||
* @return {Object}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("checkstyle:ReturnCount")
|
||||
public Object convert(Object value, Class target, Object fieldName) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
// 类型一样,不需要转换
|
||||
if (ClassUtils.isAssignableValue(target, value)) {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
TypeDescriptor targetDescriptor = CustomConverter.getTypeDescriptor(this.targetClazz, (String) fieldName);
|
||||
// 1. 判断 sourceClazz 为 Map
|
||||
if (Map.class.isAssignableFrom(this.sourceClazz)) {
|
||||
return ConvertUtils.convert(value, targetDescriptor);
|
||||
} else {
|
||||
TypeDescriptor sourceDescriptor = CustomConverter.getTypeDescriptor(this.sourceClazz, (String) fieldName);
|
||||
return ConvertUtils.convert(value, sourceDescriptor, targetDescriptor);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Converter error", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets type descriptor *
|
||||
*
|
||||
* @param clazz clazz
|
||||
* @param fieldName field name
|
||||
* @return the type descriptor
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static TypeDescriptor getTypeDescriptor(@NotNull Class<?> clazz, String fieldName) {
|
||||
String srcCacheKey = clazz.getName() + fieldName;
|
||||
// 忽略抛出异常的函数,定义完整泛型,避免编译问题
|
||||
CheckedFunction<String, TypeDescriptor> uncheckedFunction = key -> {
|
||||
// 这里 property 理论上不会为 null
|
||||
Field field = ReflectionUtils.getField(clazz, fieldName);
|
||||
if (field == null) {
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
return new TypeDescriptor(field);
|
||||
};
|
||||
return TYPE_CACHE.computeIfAbsent(srcCacheKey, Unchecked.function(uncheckedFunction));
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package com.aivfo.el.start.core.convert;
|
||||
|
||||
import com.aivfo.el.start.core.util.ConvertUtils;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 接收参数 同 jackson Enum -> String 转换
|
||||
*/
|
||||
@Slf4j
|
||||
public class EnumToStringConverter implements ConditionalGenericConverter {
|
||||
/**
|
||||
* 缓存 Enum 类信息,提供性能
|
||||
*/
|
||||
private static final ConcurrentMap<Class<?>, AccessibleObject> ENUM_CACHE_MAP = Maps.newConcurrentMap();
|
||||
|
||||
/**
|
||||
* Matches boolean
|
||||
*
|
||||
* @param sourceType source type
|
||||
* @param targetType target type
|
||||
* @return the boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(@NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets convertible types *
|
||||
*
|
||||
* @return the convertible types
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
Set<ConvertiblePair> pairSet = new HashSet<>(4);
|
||||
pairSet.add(new ConvertiblePair(Enum.class, String.class));
|
||||
pairSet.add(new ConvertiblePair(Enum.class, Integer.class));
|
||||
pairSet.add(new ConvertiblePair(Enum.class, Long.class));
|
||||
return Collections.unmodifiableSet(pairSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert object
|
||||
*
|
||||
* @param source source
|
||||
* @param sourceType source type
|
||||
* @param targetType target type
|
||||
* @return the object
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public Object convert(@Nullable Object source, @NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?> sourceClazz = sourceType.getType();
|
||||
AccessibleObject accessibleObject = ENUM_CACHE_MAP.computeIfAbsent(sourceClazz, EnumToStringConverter::getAnnotation);
|
||||
Class<?> targetClazz = targetType.getType();
|
||||
// 如果为null,走默认的转换
|
||||
if (accessibleObject == null) {
|
||||
if (String.class == targetClazz) {
|
||||
return ((Enum) source).name();
|
||||
}
|
||||
int ordinal = ((Enum) source).ordinal();
|
||||
return ConvertUtils.convert(ordinal, targetClazz);
|
||||
}
|
||||
try {
|
||||
return EnumToStringConverter.invoke(sourceClazz, accessibleObject, source, targetClazz);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets annotation *
|
||||
*
|
||||
* @param clazz clazz
|
||||
* @return the annotation
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("java:S3011")
|
||||
private static AccessibleObject getAnnotation(@NotNull Class<?> clazz) {
|
||||
Set<AccessibleObject> accessibleObjects = new HashSet<>();
|
||||
// JsonValue METHOD, FIELD
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
Collections.addAll(accessibleObjects, fields);
|
||||
// methods
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
Collections.addAll(accessibleObjects, methods);
|
||||
for (AccessibleObject accessibleObject : accessibleObjects) {
|
||||
// 复用 jackson 的 JsonValue 注解
|
||||
JsonValue jsonValue = accessibleObject.getAnnotation(JsonValue.class);
|
||||
if (jsonValue != null && jsonValue.value()) {
|
||||
accessibleObject.setAccessible(true);
|
||||
return accessibleObject;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke object
|
||||
*
|
||||
* @param clazz clazz
|
||||
* @param accessibleObject accessible object
|
||||
* @param source source
|
||||
* @param targetClazz target clazz
|
||||
* @return the object
|
||||
* @throws IllegalAccessException illegal access exception
|
||||
* @throws InvocationTargetException invocation target exception
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
private static Object invoke(Class<?> clazz, AccessibleObject accessibleObject, Object source, Class<?> targetClazz)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
Object value = null;
|
||||
if (accessibleObject instanceof Field) {
|
||||
Field field = (Field) accessibleObject;
|
||||
value = field.get(source);
|
||||
} else if (accessibleObject instanceof Method) {
|
||||
Method method = (Method) accessibleObject;
|
||||
Class<?> paramType = method.getParameterTypes()[0];
|
||||
// 类型转换
|
||||
Object object = ConvertUtils.convert(source, paramType);
|
||||
value = method.invoke(clazz, object);
|
||||
}
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return ConvertUtils.convert(value, targetClazz);
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
package com.aivfo.el.start.core.convert;
|
||||
|
||||
import com.aivfo.el.start.core.util.ConvertUtils;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 接收参数 同 jackson String -> Enum 转换
|
||||
*/
|
||||
@Slf4j
|
||||
public class StringToEnumConverter implements ConditionalGenericConverter {
|
||||
/**
|
||||
* 缓存 Enum 类信息,提升性能
|
||||
*/
|
||||
private static final ConcurrentMap<Class<?>, AccessibleObject> ENUM_CACHE_MAP = new ConcurrentHashMap<>(8);
|
||||
|
||||
/**
|
||||
* Matches boolean
|
||||
*
|
||||
* @param sourceType source type
|
||||
* @param targetType target type
|
||||
* @return the boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(@NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets convertible types *
|
||||
*
|
||||
* @return the convertible types
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
return Collections.singleton(new ConvertiblePair(String.class, Enum.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert object
|
||||
*
|
||||
* @param source source
|
||||
* @param sourceType source type
|
||||
* @param targetType target type
|
||||
* @return the object
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
@SuppressWarnings("checkstyle:ReturnCount")
|
||||
public Object convert(@Nullable Object source, @NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank((String) source)) {
|
||||
return null;
|
||||
}
|
||||
Class<?> clazz = targetType.getType();
|
||||
AccessibleObject accessibleObject = ENUM_CACHE_MAP.computeIfAbsent(clazz, StringToEnumConverter::getAnnotation);
|
||||
String value = ((String) source).trim();
|
||||
// 如果为null,走默认的转换
|
||||
if (accessibleObject == null) {
|
||||
return valueOf(clazz, value);
|
||||
}
|
||||
try {
|
||||
return StringToEnumConverter.invoke(clazz, accessibleObject, value);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets annotation *
|
||||
*
|
||||
* @param clazz clazz
|
||||
* @return the annotation
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@org.jetbrains.annotations.Nullable
|
||||
@Nullable
|
||||
private static AccessibleObject getAnnotation(@NotNull Class<?> clazz) {
|
||||
Set<AccessibleObject> accessibleObjects = new HashSet<>();
|
||||
// JsonCreator METHOD, CONSTRUCTOR
|
||||
Constructor<?>[] constructors = clazz.getConstructors();
|
||||
Collections.addAll(accessibleObjects, constructors);
|
||||
// methods
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
Collections.addAll(accessibleObjects, methods);
|
||||
for (AccessibleObject accessibleObject : accessibleObjects) {
|
||||
// 复用 jackson 的 JsonCreator注解
|
||||
JsonCreator jsonCreator = accessibleObject.getAnnotation(JsonCreator.class);
|
||||
if (jsonCreator != null && JsonCreator.Mode.DISABLED != jsonCreator.mode()) {
|
||||
accessibleObject.setAccessible(true);
|
||||
return accessibleObject;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value of t
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param clazz clazz
|
||||
* @param value value
|
||||
* @return the t
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Enum<T>> T valueOf(Class<?> clazz, String value) {
|
||||
return Enum.valueOf((Class<T>) clazz, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke object
|
||||
*
|
||||
* @param clazz clazz
|
||||
* @param accessibleObject accessible object
|
||||
* @param value value
|
||||
* @return the object
|
||||
* @throws IllegalAccessException illegal access exception
|
||||
* @throws InvocationTargetException invocation target exception
|
||||
* @throws InstantiationException instantiation exception
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("_, null, _ -> null")
|
||||
@Nullable
|
||||
private static Object invoke(Class<?> clazz, AccessibleObject accessibleObject, String value)
|
||||
throws IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
if (accessibleObject instanceof Constructor) {
|
||||
@SuppressWarnings("rawtypes") Constructor constructor = (Constructor) accessibleObject;
|
||||
Class<?> paramType = constructor.getParameterTypes()[0];
|
||||
// 类型转换
|
||||
Object object = ConvertUtils.convert(value, paramType);
|
||||
return constructor.newInstance(object);
|
||||
}
|
||||
if (accessibleObject instanceof Method) {
|
||||
Method method = (Method) accessibleObject;
|
||||
Class<?> paramType = method.getParameterTypes()[0];
|
||||
// 类型转换
|
||||
Object object = ConvertUtils.convert(value, paramType);
|
||||
return method.invoke(clazz, object);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.aivfo.el.start.core.enums;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 生成的随机数类型
|
||||
*/
|
||||
public enum RandomType {
|
||||
/**
|
||||
* INT STRING ALL
|
||||
*/
|
||||
INT,
|
||||
/**
|
||||
* String random type
|
||||
*/
|
||||
STRING,
|
||||
/**
|
||||
* All random type
|
||||
*/
|
||||
ALL
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
package com.aivfo.el.start.core.exception;
|
||||
|
||||
import com.aivfo.el.start.core.util.ResultCodeUtils;
|
||||
import com.aivfo.el.start.core.api.BaseCodes;
|
||||
import com.aivfo.el.start.core.api.IResultCode;
|
||||
import com.aivfo.el.starter.base.exception.BasicException;
|
||||
import com.aivfo.el.starter.base.support.StrFormatter;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/15
|
||||
* @description: 基础异常类, 所有自定义异常类都需要继承本类
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter
|
||||
public class BaseException extends BasicException {
|
||||
|
||||
/**
|
||||
* serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 返回码
|
||||
*/
|
||||
protected IResultCode resultCode;
|
||||
/**
|
||||
* 异常消息参数
|
||||
*/
|
||||
protected Object[] args;
|
||||
|
||||
/**
|
||||
* Instantiates a new Base exception.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException() {
|
||||
super(BaseCodes.FAILURE.getMessage());
|
||||
this.resultCode = BaseCodes.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Base exception.
|
||||
*
|
||||
* @param msg the msg
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(String msg) {
|
||||
super(msg);
|
||||
this.resultCode = BaseCodes.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Base exception.
|
||||
*
|
||||
* @param code the code
|
||||
* @param msg the msg
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(int code, String msg) {
|
||||
super(msg);
|
||||
this.resultCode = new IResultCode() {
|
||||
private static final long serialVersionUID = 2590640370242410124L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public BaseException(String code, String msg) {
|
||||
super(code, msg);
|
||||
this.resultCode = new IResultCode() {
|
||||
private static final long serialVersionUID = 2590640370242410124L;
|
||||
|
||||
/**
|
||||
* Gets message *
|
||||
*
|
||||
* @return the message
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return ResultCodeUtils.convert(code);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* msg 占位符替换
|
||||
*
|
||||
* @param msg msg
|
||||
* @param args args
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
this.resultCode = BaseCodes.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base exception
|
||||
*
|
||||
* @param cause cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(Throwable cause) {
|
||||
super(cause);
|
||||
this.resultCode = BaseCodes.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base exception
|
||||
*
|
||||
* @param msg msg
|
||||
* @param cause cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.resultCode = BaseCodes.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Base exception.
|
||||
*
|
||||
* @param resultCode the response enum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(@NotNull IResultCode resultCode) {
|
||||
super(resultCode.getMessage());
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base exception
|
||||
*
|
||||
* @param resultCode result code
|
||||
* @param cause cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(@NotNull IResultCode resultCode, Throwable cause) {
|
||||
super(resultCode.getMessage(), cause);
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Base exception.
|
||||
*
|
||||
* @param code the code
|
||||
* @param msg the msg
|
||||
* @param cause cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(int code, String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.resultCode = new IResultCode() {
|
||||
private static final long serialVersionUID = 2590640370242410124L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Base exception.
|
||||
*
|
||||
* @param resultCode the response enum
|
||||
* @param args the args
|
||||
* @param msg msg 替换占位符后的消息
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(IResultCode resultCode, Object[] args, String msg) {
|
||||
super(StrFormatter.mergeFormat(msg, args));
|
||||
this.resultCode = resultCode;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Base exception.
|
||||
*
|
||||
* @param resultCode the response enum
|
||||
* @param args the args
|
||||
* @param msg msg
|
||||
* @param cause the cause
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public BaseException(IResultCode resultCode, Object[] args, String msg, Throwable cause) {
|
||||
super(StrFormatter.mergeFormat(msg, args), cause);
|
||||
this.resultCode = resultCode;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get code
|
||||
*
|
||||
* @return the string
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@Override
|
||||
public String getCode() {
|
||||
return ResultCodeUtils.generateCode(this.resultCode);
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.aivfo.el.start.core.node;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 节点基类
|
||||
*/
|
||||
@Data
|
||||
public class BaseNode implements INode {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
protected Integer id;
|
||||
|
||||
/**
|
||||
* 父节点ID
|
||||
*/
|
||||
protected Integer parentId;
|
||||
|
||||
/**
|
||||
* 子孙节点
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
protected List<INode> children = new ArrayList<>();
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.aivfo.el.start.core.node;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 森林节点类</p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.26 20:42
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ForestNode extends BaseNode {
|
||||
|
||||
/**
|
||||
* 节点内容
|
||||
*/
|
||||
private Object content;
|
||||
|
||||
/**
|
||||
* Instantiates a new Forest node.
|
||||
*
|
||||
* @param id the id
|
||||
* @param parentId the parent id
|
||||
* @param content the content
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public ForestNode(Integer id, Integer parentId, Object content) {
|
||||
this.id = id;
|
||||
this.parentId = parentId;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.aivfo.el.start.core.node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 森林管理类
|
||||
*/
|
||||
public class ForestNodeManager<T extends INode> {
|
||||
|
||||
/**
|
||||
* 森林的所有节点
|
||||
*/
|
||||
private final List<T> list;
|
||||
|
||||
/**
|
||||
* 森林的父节点ID
|
||||
*/
|
||||
private final List<Integer> parentIds = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new Forest node manager.
|
||||
*
|
||||
* @param items the items
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public ForestNodeManager(List<T> items) {
|
||||
list = items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点ID获取一个节点
|
||||
*
|
||||
* @param id 节点ID
|
||||
* @return 对应的节点对象 tree node at
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public INode getTreeNodeAt(int id) {
|
||||
for (INode forestNode : list) {
|
||||
if (forestNode.getId() == id) {
|
||||
return forestNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加父节点ID
|
||||
*
|
||||
* @param parentId 父节点ID
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public void addParentId(Integer parentId) {
|
||||
parentIds.add(parentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树的根节点(一个森林对应多颗树)
|
||||
*
|
||||
* @return 树的根节点集合 root
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public List<T> getRoot() {
|
||||
List<T> roots = new ArrayList<>();
|
||||
for (T forestNode : list) {
|
||||
if (forestNode.getParentId() == 0 || parentIds.contains(forestNode.getId())) {
|
||||
roots.add(forestNode);
|
||||
}
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.aivfo.el.start.core.node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: 森林节点归并类
|
||||
*/
|
||||
public class ForestNodeMerger {
|
||||
|
||||
/**
|
||||
* 将节点数组归并为一个森林 (多棵树) (填充节点的children域)
|
||||
* 时间复杂度为O(n^2)
|
||||
*
|
||||
* @param <T> T 泛型标记
|
||||
* @param items 节点域
|
||||
* @return 多棵树的根节点集合 list
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static <T extends INode> List<T> merge(List<T> items) {
|
||||
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
|
||||
items.forEach(forestNode -> {
|
||||
if (forestNode.getParentId() != 0) {
|
||||
INode node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
|
||||
if (node != null) {
|
||||
node.getChildren().add(forestNode);
|
||||
} else {
|
||||
forestNodeManager.addParentId(forestNode.getId());
|
||||
}
|
||||
}
|
||||
});
|
||||
return forestNodeManager.getRoot();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user