This commit is contained in:
959814898@qq.com
2023-05-16 17:23:41 +08:00
parent f6bea9b21f
commit f025978c0d
229 changed files with 25045 additions and 8257 deletions
@@ -54,6 +54,16 @@
<artifactId>transmittable-thread-local</artifactId> <artifactId>transmittable-thread-local</artifactId>
<version>${transmittable-thread-local.version}</version> <version>${transmittable-thread-local.version}</version>
</dependency> </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> </dependencies>
</dependencyManagement> </dependencyManagement>
</project> </project>
@@ -11,4 +11,16 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging> <packaging>pom</packaging>
<artifactId>aivfo-model-parent</artifactId> <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> </project>
+2 -2
View File
@@ -62,12 +62,12 @@
<repository> <repository>
<id>nexus</id> <id>nexus</id>
<name>releases</name> <name>releases</name>
<url>https://maven.wylgyx.top/repository/maven-releases/</url> <url>http://192.168.31.90:8081/repository/maven-releases/</url>
</repository> </repository>
<snapshotRepository> <snapshotRepository>
<id>nexus</id> <id>nexus</id>
<name>snapshots</name> <name>snapshots</name>
<url>https://maven.wylgyx.top/repository/maven-snapshots/</url> <url>http://192.168.31.90:8081/repository/maven-snapshots/</url>
</snapshotRepository> </snapshotRepository>
</distributionManagement> </distributionManagement>
<build> <build>
+12 -1
View File
@@ -11,5 +11,16 @@
<module>aivfo-supreme</module> <module>aivfo-supreme</module>
</modules> </modules>
<modelVersion>4.0.0</modelVersion> <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> </project>
+36 -4
View File
@@ -3,10 +3,10 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>aivfo-model-parent</artifactId> <artifactId>aivfo-element</artifactId>
<groupId>com.aivfo</groupId> <groupId>com.aivfo</groupId>
<version>1.0.0-SNAPSHOT</version> <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> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>aivfo-element-base</artifactId> <artifactId>aivfo-element-base</artifactId>
@@ -22,7 +22,10 @@
<dependency> <dependency>
<groupId>org.reflections</groupId> <groupId>org.reflections</groupId>
<artifactId>reflections</artifactId> <artifactId>reflections</artifactId>
<version>0.9.11</version> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
@@ -39,7 +42,36 @@
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId> <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> </dependency>
</dependencies> </dependencies>
@@ -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");
}
@@ -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";
}
@@ -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);
}
}
@@ -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();
}
@@ -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 {
}
@@ -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 {
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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 {
}
@@ -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;
}
@@ -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>
* &#64;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 {
}
@@ -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 {
}
@@ -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 {
}
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.core.function.CheckedRunnable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.core.function.CheckedRunnable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.core.function.CheckedRunnable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@@ -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; import java.util.function.Supplier;
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.core.function.CheckedRunnable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.core.function.CheckedRunnable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.core.function.CheckedRunnable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.core.function.CheckedRunnable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.base.exception.AssertionFailedException; import com.aivfo.el.starter.base.exception.BasicException;
import com.aivfo.el.base.utils.AivfoStringUtils; import com.aivfo.el.starter.base.utils.StringUtils;
import com.aivfo.el.core.function.CheckedRunnable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.function.Supplier; import java.util.function.Supplier;
@UtilityClass @UtilityClass
class AssertUtils { class AssertUtils {
@@ -79,7 +80,7 @@ class AssertUtils {
String className = (obj != null ? obj.getClass().getName() : "null"); String className = (obj != null ? obj.getClass().getName() : "null");
String result = ""; String result = "";
boolean defaultMessage = true; boolean defaultMessage = true;
if (AivfoStringUtils.hasLength(message)) { if (StringUtils.hasLength(message)) {
if (endsWithSeparator(message)) { if (endsWithSeparator(message)) {
result = message + " "; result = message + " ";
} else { } else {
@@ -90,7 +91,7 @@ class AssertUtils {
if (defaultMessage) { if (defaultMessage) {
result = result + ("Object 类型 [" + className + "] 不是: " + type); 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) { static void assignableCheckFailed(Class<?> superType, @Nullable Class<?> subType, @Nullable String message) {
String result = ""; String result = "";
boolean defaultMessage = true; boolean defaultMessage = true;
if (AivfoStringUtils.hasLength(message)) { if (StringUtils.hasLength(message)) {
if (endsWithSeparator(message)) { if (endsWithSeparator(message)) {
result = message + " "; result = message + " ";
} else { } else {
@@ -115,7 +116,7 @@ class AssertUtils {
if (defaultMessage) { if (defaultMessage) {
result = result + (subType + " 不是 " + superType + "的子类"); result = result + (subType + " 不是 " + superType + "的子类");
} }
throw new AivfoBaseException(result); throw new BasicException(result);
} }
/** /**
@@ -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.starter.base.exception.AssertionFailedException;
import com.aivfo.el.base.utils.AivfoCollectionUtils; import com.aivfo.el.starter.base.utils.StringUtils;
import com.aivfo.el.base.utils.AivfoStringUtils; import com.aivfo.el.starter.core.function.CheckedCallable;
import com.aivfo.el.core.function.CheckedCallable; import com.aivfo.el.starter.core.function.CheckedRunnable;
import com.aivfo.el.core.function.CheckedRunnable;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import java.util.Collection; import java.util.Collection;
@@ -168,7 +168,7 @@ public class Assertions {
public static void notBlank(@Nullable String content, public static void notBlank(@Nullable String content,
Supplier<? extends RuntimeException> exceptionSupplier, Supplier<? extends RuntimeException> exceptionSupplier,
CheckedRunnable runnable) { CheckedRunnable runnable) {
if (!AivfoStringUtils.hasText(content)) { if (!StringUtils.hasText(content)) {
AssertUtils.fail(exceptionSupplier, runnable); AssertUtils.fail(exceptionSupplier, runnable);
} }
} }
@@ -459,8 +459,8 @@ public class Assertions {
*/ */
public static void doesNotContain(@Nullable String textToSearch, String substring, public static void doesNotContain(@Nullable String textToSearch, String substring,
Supplier<? extends RuntimeException> exceptionSupplier) { Supplier<? extends RuntimeException> exceptionSupplier) {
if (AivfoStringUtils.hasLength(textToSearch) if (StringUtils.hasLength(textToSearch)
&& AivfoStringUtils.hasLength(substring) && StringUtils.hasLength(substring)
&& textToSearch.contains(substring)) { && textToSearch.contains(substring)) {
AssertUtils.fail(exceptionSupplier); AssertUtils.fail(exceptionSupplier);
} }
@@ -495,7 +495,7 @@ public class Assertions {
* @since 1.6.0 * @since 1.6.0
*/ */
public static void notEmpty(@Nullable Map<?, ?> map, Supplier<? extends RuntimeException> exceptionSupplier) { public static void notEmpty(@Nullable Map<?, ?> map, Supplier<? extends RuntimeException> exceptionSupplier) {
if (AivfoCollectionUtils.isEmpty(map)) { if (CollectionUtils.isEmpty(map)) {
AssertUtils.fail(exceptionSupplier); AssertUtils.fail(exceptionSupplier);
} }
} }
@@ -602,7 +602,7 @@ public class Assertions {
* @since 1.6.0 * @since 1.6.0
*/ */
public static void notEmpty(@Nullable Collection<?> collection, Supplier<? extends RuntimeException> exceptionSupplier) { public static void notEmpty(@Nullable Collection<?> collection, Supplier<? extends RuntimeException> exceptionSupplier) {
if (AivfoCollectionUtils.isEmpty(collection)) { if (CollectionUtils.isEmpty(collection)) {
AssertUtils.fail(exceptionSupplier); AssertUtils.fail(exceptionSupplier);
} }
} }
@@ -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;
}
@@ -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;
}
}
@@ -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";
}
@@ -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");
}
@@ -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";
}
}
@@ -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;
}
}
@@ -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);
}
}
}
@@ -1,4 +1,4 @@
package com.aivfo.el.base.contex; package com.aivfo.el.starter.base.contex;
import com.alibaba.ttl.TransmittableThreadLocal; import com.alibaba.ttl.TransmittableThreadLocal;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
@@ -6,11 +6,11 @@ import org.jetbrains.annotations.Contract;
/** /**
* @author: wangyl * @author: wangyl
* @date: 2023/4/14 * @date: 2023/5/16
* @description: 链路id的上下文 * @description: TraceId 用来存储traceID相关信息
*/ */
@UtilityClass @UtilityClass
public class TraceContext { public class Trace {
/** /**
* context * context
*/ */
@@ -23,8 +23,8 @@ public class TraceContext {
* @since 1.6.0 * @since 1.6.0
*/ */
@Contract(pure = true) @Contract(pure = true)
public static String getTraceId() { public static TransmittableThreadLocal<String> context() {
return CONTEXT.get(); return CONTEXT;
} }
/** /**
@@ -1,11 +1,11 @@
package com.aivfo.el.base.exception; package com.aivfo.el.starter.base.exception;
/** /**
* @author: wangyl * @author: wangyl
* @date: 2023/4/14 * @date: 2023/4/14
* @description: Assert异常信息 * @description: Assert异常信息
*/ */
public class AssertionFailedException extends AivfoBaseException { public class AssertionFailedException extends BasicException {
/** /**
* DEFAULT_MESSAGE * DEFAULT_MESSAGE
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
@@ -21,7 +21,7 @@ public class StringTrimmerDeserializer extends JsonDeserializer<String> {
@Override @Override
@SuppressWarnings("java:S3252") @SuppressWarnings("java:S3252")
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
return AivfoStringUtils.trimWhitespace(jsonParser.getValueAsString()); return StringUtils.trimWhitespace(jsonParser.getValueAsString());
} }
} }
@@ -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.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException; import java.io.IOException;
public class StringTrimmerSerializer extends JsonSerializer<String> { public class StringTrimmerSerializer extends JsonSerializer<String> {
@@ -20,6 +21,6 @@ public class StringTrimmerSerializer extends JsonSerializer<String> {
@Override @Override
@SuppressWarnings("java:S3252") @SuppressWarnings("java:S3252")
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(AivfoStringUtils.trimWhitespace(s)); jsonGenerator.writeString(StringUtils.trimWhitespace(s));
} }
} }
@@ -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.jetbrains.annotations.NotNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@@ -12,7 +12,9 @@ import java.io.Writer;
* @description: 借助 StringBuilder 提供快读的字符串写出, 相比 jdk StringWriter 非线程安全, 速度更快. * @description: 借助 StringBuilder 提供快读的字符串写出, 相比 jdk StringWriter 非线程安全, 速度更快.
*/ */
public class FastStringWriter extends Writer { public class FastStringWriter extends Writer {
/** Builder */ /**
* Builder
*/
private final StringBuilder builder; private final StringBuilder builder;
/** /**
@@ -90,7 +92,7 @@ public class FastStringWriter extends Writer {
@Override @Override
public void write(char[] cbuilder, int off, int len) { public void write(char[] cbuilder, int off, int len) {
if ((off < 0) || (off > cbuilder.length) 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(); throw new IndexOutOfBoundsException();
} else if (len == 0) { } else if (len == 0) {
return; return;
@@ -132,7 +134,7 @@ public class FastStringWriter extends Writer {
@Override @Override
public FastStringWriter append(CharSequence csq) { public FastStringWriter append(CharSequence csq) {
if (csq == null) { if (csq == null) {
this.write(AivfoStringPool.NULL); this.write(StringPool.NULL);
} else { } else {
this.write(csq.toString()); this.write(csq.toString());
} }
@@ -150,7 +152,7 @@ public class FastStringWriter extends Writer {
*/ */
@Override @Override
public FastStringWriter append(CharSequence csq, int start, int end) { 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()); this.write(cs.subSequence(start, end).toString());
return this; return this;
} }
@@ -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;
}
}
@@ -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';
}
@@ -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();
}
}
@@ -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) : "");
}
}
@@ -1,4 +1,4 @@
package com.aivfo.el.base.utils; package com.aivfo.el.starter.base.utils;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
@@ -14,19 +14,26 @@ import java.util.concurrent.ConcurrentLinkedQueue;
/** /**
* @author: wangyl * @author: wangyl
* @date: 2023/4/18 * @date: 2023/5/16
* @description: 安全的时间格式化 * @description: 安全的时间格式化
* 参考 tomcat8 中的并发 DateFormat, {@link SimpleDateFormat}的线程安全包装器. 不使用ThreadLocal,创建足够的SimpleDateFormat对象来满足并发性要求.
*/ */
public final class ConcurrentDateFormat { public final class ConcurrentDateFormat {
/** Format */ /**
* Format
*/
@Getter @Getter
private final String format; private final String format;
/** Locale */ /**
* Locale
*/
private final Locale locale; private final Locale locale;
/** Timezone */ /**
* Timezone
*/
private final TimeZone timezone; private final TimeZone timezone;
/** Queue */ /**
* Queue
*/
private final Queue<SimpleDateFormat> queue = new ConcurrentLinkedQueue<>(); private final Queue<SimpleDateFormat> queue = new ConcurrentLinkedQueue<>();
/** /**
@@ -1,8 +1,8 @@
package com.aivfo.el.base.utils; package com.aivfo.el.starter.base.utils;
import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.exceptions.ExceptionUtil;
import com.aivfo.el.base.exception.AivfoBaseException; import com.aivfo.el.starter.base.exception.BasicException;
import com.aivfo.el.base.support.FastStringWriter; import com.aivfo.el.starter.base.support.FastStringWriter;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -12,9 +12,14 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException; import java.lang.reflect.UndeclaredThrowableException;
/** /**
* @author: wangyl * <p>Company: 成都返空汇网络技术有限公司</p>
* @date: 2023/4/18 * <p>Description: 异常处理工具类 </p>
* @description: 异常处理工具类 *
* @author dong4j
* @version 1.2.3
* @email "mailto:dongshijie@fkhwl.com"
* @date 2019.11.21 09:59
* @since 1.0.0
*/ */
@UtilityClass @UtilityClass
public class Exceptions extends ExceptionUtil { public class Exceptions extends ExceptionUtil {
@@ -44,12 +49,12 @@ public class Exceptions extends ExceptionUtil {
public static RuntimeException unchecked(String message, Throwable e) { public static RuntimeException unchecked(String message, Throwable e) {
RuntimeException runtimeException; RuntimeException runtimeException;
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|| e instanceof NoSuchMethodException) { || e instanceof NoSuchMethodException) {
runtimeException = new IllegalArgumentException(e); runtimeException = new IllegalArgumentException(e);
} else if (e instanceof InvocationTargetException) { } else if (e instanceof InvocationTargetException) {
runtimeException = new RuntimeException(((InvocationTargetException) e).getTargetException()); runtimeException = new RuntimeException(((InvocationTargetException) e).getTargetException());
} else if (e instanceof AivfoBaseException) { } else if (e instanceof BasicException) {
runtimeException = (AivfoBaseException) e; runtimeException = (BasicException) e;
} else if (e instanceof RuntimeException) { } else if (e instanceof RuntimeException) {
runtimeException = (RuntimeException) e; runtimeException = (RuntimeException) e;
} else { } else {
@@ -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));
}
}
@@ -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));
}
}
}
@@ -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.starter.base.asserts.Assertions;
import com.aivfo.el.base.constant.ConfigDefaultValue; import com.aivfo.el.starter.base.constant.ConfigDefaultValue;
import com.aivfo.el.base.constant.ConfigKey; import com.aivfo.el.starter.base.constant.ConfigKey;
import com.aivfo.el.base.serialize.StringTrimmerDeserializer; import com.aivfo.el.starter.base.serialize.StringTrimmerDeserializer;
import com.aivfo.el.base.serialize.StringTrimmerSerializer; import com.aivfo.el.starter.base.serialize.StringTrimmerSerializer;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.core.JsonParser; 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 com.fasterxml.jackson.databind.type.MapType;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -32,8 +33,6 @@ import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder; import org.reflections.util.FilterBuilder;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolation;
import javax.validation.Validator; import javax.validation.Validator;
@@ -44,30 +43,32 @@ import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
/** /**
* <p>Description: Jackson工具类 * @author: wangyl
* @date: 2023/5/16
* @description: Jackson工具类
* 系统中使用到 {@link ObjectMapper} 的地方定将用此类进行初始化, 提供 2 种方式: * 系统中使用到 {@link ObjectMapper} 的地方定将用此类进行初始化, 提供 2 种方式:
* 1. {@link AivfoJsonUtils#getInstance()}; * 1. {@link JsonUtils#getInstance()};
* 2. {@link AivfoJsonUtils#getCopyMapper()}; * 2. {@link JsonUtils#getCopyMapper()};
* 第一种方式将返回一个单例对象, 请确保在使用 {@link ObjectMapper} 的过程中不会修改 {@link ObjectMapper} 的配置, 如果需要定制化 {@link ObjectMapper}, * 第一种方式将返回一个单例对象, 请确保在使用 {@link ObjectMapper} 的过程中不会修改 {@link ObjectMapper} 的配置, 如果需要定制化 {@link ObjectMapper},
* 可使用第二种方式, 此方式将每次返回一个新的 {@link ObjectMapper} 对象. * 可使用第二种方式, 此方式将每次返回一个新的 {@link ObjectMapper} 对象.
* 此类每个工具方法都进行过重载, 如果需要使用自定义 {@link ObjectMapper} json 处理, 请调用具有 {@link ObjectMapper} 参数的工具方法. * 此类每个工具方法都进行过重载, 如果需要使用自定义 {@link ObjectMapper} json 处理, 请调用具有 {@link ObjectMapper} 参数的工具方法.
* </p> * </p>
*
* @author dong4j
* @version 1.2.3
* @email "mailto:dongshijie@fkhwl.com"
* @date 2019.12.26 21:35
* @since 1.0.0
*/ */
@Slf4j @Slf4j
@UtilityClass @UtilityClass
@SuppressWarnings("checkstyle:MethodLimit") @SuppressWarnings("checkstyle:MethodLimit")
public class AivfoJsonUtils { public class JsonUtils {
/** PATTERN_DATETIME */ /**
* PATTERN_DATETIME
*/
public static final String PATTERN_DATETIME = System.getProperty(ConfigKey.JSON_DATE_FORMAT, ConfigDefaultValue.DEFAULT_DATE_FORMAT); 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]; public static final byte[] EMPTY_ARRAY = new byte[0];
/** MESSAGE */ /**
* MESSAGE
*/
private static final String MESSAGE = "待解析的数据为空"; private static final String MESSAGE = "待解析的数据为空";
/** /**
@@ -81,7 +82,7 @@ public class AivfoJsonUtils {
} }
/** /**
* 获取单例对象, 这里只提供全局的基础配置, 让整个应用配置保持一致, 如果需要其他配置, 请使用 {@link AivfoJsonUtils#getCopyMapper()}. * 获取单例对象, 这里只提供全局的基础配置, 让整个应用配置保持一致, 如果需要其他配置, 请使用 {@link JsonUtils#getCopyMapper()}.
* *
* @return the instance * @return the instance
* @since 1.0.0 * @since 1.0.0
@@ -258,7 +259,7 @@ public class AivfoJsonUtils {
@SuppressWarnings("java:S3252") @SuppressWarnings("java:S3252")
public static String toJson(ObjectMapper mapper, Object object, boolean pretty, boolean always) { public static String toJson(ObjectMapper mapper, Object object, boolean pretty, boolean always) {
if (object == null) { if (object == null) {
return AivfoStringPool.EMPTY_JSON; return StringPool.EMPTY_JSON;
} }
// 如果是 string, 先转为 object 再转为 json, 避免转义字符 // 如果是 string, 先转为 object 再转为 json, 避免转义字符
if (object instanceof String) { if (object instanceof String) {
@@ -1064,9 +1065,13 @@ public class AivfoJsonUtils {
*/ */
@SuppressWarnings("all") @SuppressWarnings("all")
private static class JacksonHolder { private static class JacksonHolder {
/** INSTANCE */ /**
* INSTANCE
*/
private static final ObjectMapper INSTANCE; private static final ObjectMapper INSTANCE;
/** CHINA */ /**
* CHINA
*/
private static final Locale CHINA = Locale.CHINA; private static final Locale CHINA = Locale.CHINA;
static { static {
@@ -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;
}
}
@@ -1,131 +1,249 @@
package com.aivfo.el.base.utils; package com.aivfo.el.starter.base.utils;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
/** /**
* @author: wangyl * @author: wangyl
* @date: 2023/4/18 * @date: 2023/5/16
* @description: 静态 String * @description: 静态 String
*/ */
@UtilityClass @UtilityClass
public final class AivfoStringPool { public final class StringPool {
/** AMPERSAND */ /**
* AMPERSAND
*/
public static final String AMPERSAND = "&"; public static final String AMPERSAND = "&";
/** AND */ /**
* AND
*/
public static final String AND = "and"; public static final String AND = "and";
/** AT */ /**
* AT
*/
public static final String AT = "@"; public static final String AT = "@";
/** ASTERISK */ /**
* ASTERISK
*/
public static final String ASTERISK = "*"; public static final String ASTERISK = "*";
/** STAR */ /**
* STAR
*/
public static final String STAR = ASTERISK; public static final String STAR = ASTERISK;
/** SLASH */ /**
* SLASH
*/
public static final String SLASH = "/"; public static final String SLASH = "/";
/** DOUBLE_SLASH */ /**
* DOUBLE_SLASH
*/
public static final String DOUBLE_SLASH = "#//"; public static final String DOUBLE_SLASH = "#//";
/** COLON */ /**
* COLON
*/
public static final String COLON = ":"; public static final String COLON = ":";
/** COMMA */ /**
* COMMA
*/
public static final String COMMA = ","; public static final String COMMA = ",";
/** DASH */ /**
* DASH
*/
public static final String DASH = "-"; public static final String DASH = "-";
/** DOLLAR */ /**
* DOLLAR
*/
public static final String DOLLAR = "$"; public static final String DOLLAR = "$";
/** DOT */ /**
* DOT
*/
public static final String DOT = "."; public static final String DOT = ".";
/** EMPTY */ /**
* EMPTY
*/
public static final String EMPTY = ""; public static final String EMPTY = "";
/** EMPTY_JSON */ /**
* EMPTY_JSON
*/
public static final String EMPTY_JSON = "{}"; public static final String EMPTY_JSON = "{}";
/** EQUALS */ /**
* EQUALS
*/
public static final String EQUALS = "="; public static final String EQUALS = "=";
/** FALSE */ /**
* FALSE
*/
public static final String FALSE = "false"; public static final String FALSE = "false";
/** HASH */ /**
* HASH
*/
public static final String HASH = "#"; public static final String HASH = "#";
/** HAT */ /**
* HAT
*/
public static final String HAT = "^"; public static final String HAT = "^";
/** LEFT_BRACE */ /**
* LEFT_BRACE
*/
public static final String LEFT_BRACE = "{"; public static final String LEFT_BRACE = "{";
/** LEFT_BRACKET */ /**
* LEFT_BRACKET
*/
public static final String LEFT_BRACKET = "("; public static final String LEFT_BRACKET = "(";
/** LEFT_CHEV */ /**
* LEFT_CHEV
*/
public static final String LEFT_CHEV = "<"; public static final String LEFT_CHEV = "<";
/** NEWLINE */ /**
* NEWLINE
*/
public static final String NEWLINE = "\n"; public static final String NEWLINE = "\n";
/** N */ /**
* N
*/
public static final String N = "n"; public static final String N = "n";
/** NO */ /**
* NO
*/
public static final String NO = "no"; public static final String NO = "no";
/** NULL */ /**
* NULL
*/
public static final String NULL = "null"; public static final String NULL = "null";
/** OFF */ /**
* OFF
*/
public static final String OFF = "off"; public static final String OFF = "off";
/** ON */ /**
* ON
*/
public static final String ON = "on"; public static final String ON = "on";
/** PERCENT */ /**
* PERCENT
*/
public static final String PERCENT = "%"; public static final String PERCENT = "%";
/** PIPE */ /**
* PIPE
*/
public static final String PIPE = "|"; public static final String PIPE = "|";
/** PLUS */ /**
* PLUS
*/
public static final String PLUS = "+"; public static final String PLUS = "+";
/** QUESTION_MARK */ /**
* QUESTION_MARK
*/
public static final String QUESTION_MARK = "?"; public static final String QUESTION_MARK = "?";
/** EXCLAMATION_MARK */ /**
* EXCLAMATION_MARK
*/
public static final String EXCLAMATION_MARK = "!"; public static final String EXCLAMATION_MARK = "!";
/** QUOTE */ /**
* QUOTE
*/
public static final String QUOTE = "\""; public static final String QUOTE = "\"";
/** RETURN */ /**
* RETURN
*/
public static final String RETURN = "\r"; public static final String RETURN = "\r";
/** TAB */ /**
* TAB
*/
public static final String TAB = "\t"; public static final String TAB = "\t";
/** RIGHT_BRACE */ /**
* RIGHT_BRACE
*/
public static final String RIGHT_BRACE = "}"; public static final String RIGHT_BRACE = "}";
/** RIGHT_BRACKET */ /**
* RIGHT_BRACKET
*/
public static final String RIGHT_BRACKET = ")"; public static final String RIGHT_BRACKET = ")";
/** RIGHT_CHEV */ /**
* RIGHT_CHEV
*/
public static final String RIGHT_CHEV = ">"; public static final String RIGHT_CHEV = ">";
/** SEMICOLON */ /**
* SEMICOLON
*/
public static final String SEMICOLON = ";"; public static final String SEMICOLON = ";";
/** SINGLE_QUOTE */ /**
* SINGLE_QUOTE
*/
public static final String SINGLE_QUOTE = "'"; public static final String SINGLE_QUOTE = "'";
/** BACKTICK */ /**
* BACKTICK
*/
public static final String BACKTICK = "`"; public static final String BACKTICK = "`";
/** SPACE */ /**
* SPACE
*/
public static final String SPACE = " "; public static final String SPACE = " ";
/** TILDA */ /**
* TILDA
*/
public static final String TILDA = "~"; public static final String TILDA = "~";
/** LEFT_SQ_BRACKET */ /**
* LEFT_SQ_BRACKET
*/
public static final String LEFT_SQ_BRACKET = "["; public static final String LEFT_SQ_BRACKET = "[";
/** RIGHT_SQ_BRACKET */ /**
* RIGHT_SQ_BRACKET
*/
public static final String RIGHT_SQ_BRACKET = "]"; public static final String RIGHT_SQ_BRACKET = "]";
/** TRUE */ /**
* TRUE
*/
public static final String TRUE = "true"; public static final String TRUE = "true";
/** UNDERSCORE */ /**
* UNDERSCORE
*/
public static final String UNDERSCORE = "_"; public static final String UNDERSCORE = "_";
// /** UTF_8 */ /**
// public static final String UTF_8 = Charsets.UTF_8_NAME; * UTF_8
// /** GBK */ */
// public static final String GBK = Charsets.GBK_NAME; public static final String UTF_8 = Charsets.UTF_8_NAME;
// /** ISO_8859_1 */ /**
// public static final String ISO_8859_1 = Charsets.ISO_8859_1_NAME; * GBK
/** Y */ */
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"; public static final String Y = "y";
/** YES */ /**
* YES
*/
public static final String YES = "yes"; public static final String YES = "yes";
/** ONE */ /**
* ONE
*/
public static final String ONE = "1"; public static final String ONE = "1";
/** ZERO */ /**
* ZERO
*/
public static final String ZERO = "0"; public static final String ZERO = "0";
/** DOLLAR_LEFT_BRACE */ /**
* DOLLAR_LEFT_BRACE
*/
public static final String DOLLAR_LEFT_BRACE = "${"; public static final String DOLLAR_LEFT_BRACE = "${";
/** NULL_STRING */ /**
* NULL_STRING
*/
public static final String NULL_STRING = "N/A"; public static final String NULL_STRING = "N/A";
/** any_Url_Patterns */ /**
* any_Url_Patterns
*/
public static final String ANY_URL_PATTERNS = SLASH + ASTERISK; public static final String ANY_URL_PATTERNS = SLASH + ASTERISK;
/** DOUBLE_ASTERISK */ /**
* DOUBLE_ASTERISK
*/
public static final String DOUBLE_ASTERISK = ASTERISK + ASTERISK; public static final String DOUBLE_ASTERISK = ASTERISK + ASTERISK;
/** any_PATH */ /**
* any_PATH
*/
public static final String ANY_PATH = SLASH + DOUBLE_ASTERISK; public static final String ANY_PATH = SLASH + DOUBLE_ASTERISK;
} }
@@ -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() + "]");
}
}
@@ -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,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 @FunctionalInterface
@SuppressWarnings("java:S112") @SuppressWarnings("java:S112")
public interface CheckedComparator<T> { public interface CheckedComparator<T> {
@@ -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,19 +1,7 @@
package com.aivfo.el.core.function; package com.aivfo.el.starter.core.function;
import org.springframework.lang.Nullable; 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 @FunctionalInterface
@SuppressWarnings("java:S112") @SuppressWarnings("java:S112")
public interface CheckedFunction<T, R> { public interface CheckedFunction<T, R> {
@@ -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,18 +1,7 @@
package com.aivfo.el.core.function; package com.aivfo.el.starter.core.function;
import org.springframework.lang.Nullable; 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 @FunctionalInterface
@SuppressWarnings("java:S112") @SuppressWarnings("java:S112")
public interface CheckedSupplier<T> { public interface CheckedSupplier<T> {
@@ -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();
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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, ",");
}
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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));
}
}
@@ -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);
}
}
@@ -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("&amp;", 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("&amp;", 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>
@@ -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;
}
@@ -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;
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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>
@@ -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;
}
@@ -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);
}
}
@@ -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";
}
}
@@ -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());
}
}
@@ -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);
}
}
@@ -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));
}
}
@@ -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() {
}
}
}
@@ -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));
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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
}
@@ -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);
}
}
@@ -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<>();
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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