ref 脚手架同步为最新版本

This commit is contained in:
bootx
2024-10-04 21:55:03 +08:00
parent 8c2d860326
commit 380757016c
1516 changed files with 10762 additions and 65322 deletions

View File

@@ -0,0 +1,61 @@
<?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>
<groupId>cn.bootx.platform</groupId>
<artifactId>bootx-platform</artifactId>
<version>3.0.0.beta1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>bootx-platform-core</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- swagger 依赖 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-common</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- hutool工具类 核心包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- hutool工具类 json包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- hutool工具类 加解密 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- 国密扩展-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- TTL容器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>${ttl.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,24 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 大字段注解
* 配合 {@linkplain cn.hutool.core.map.MapUtil} 中 excludeBigField 方法使用使用
*
* 使用Mp条件构造器器分页查询时样例
* {@snippet :
* wrapper.select(this.getEntityClass (), MpUtil::excludeBigField);
* }
*
*
* @author xxm
* @since 2021/10/24
*/
@Target({ ElementType.PARAMETER, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface BigField {
}

View File

@@ -0,0 +1,22 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 忽略鉴权, 可以放在controller控制器类和方法上同时使用时以方法上的为准
* @author xxm
* @since 2024/6/25
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface IgnoreAuth {
/**
* 只要登录就可以访问,忽略权限校验
*/
boolean login() default false;
}

View File

@@ -0,0 +1,16 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 组件忽略注解
* @author xxm
* @since 2024/6/25
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreTenant {
}

View File

@@ -0,0 +1,15 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 内部请求接口, 只允许超级管理员访问
* @author xxm
* @since 2024/6/25
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface InternalPath {
}

View File

@@ -0,0 +1,91 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 操作日志注解(支持重复注解)
*
* @author xxm
* @since 2021/8/13
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(value = OperateLogs.class)
@Inherited
public @interface OperateLog {
/**
* 模块
*/
String title() default "";
/**
* 业务操作类型
*/
BusinessType businessType() default BusinessType.OTHER;
/**
* 是否保存请求参数
*/
boolean saveParam() default false;
/**
* 是否保存返回参数
*/
boolean saverReturn() default false;
/**
* 业务操作类型
* 字典: log_business_type
*/
enum BusinessType {
/**
* 其它
*/
OTHER,
/**
* 新增
*/
ADD,
/**
* 修改
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 授权
*/
GRANT,
/**
* 导出
*/
EXPORT,
/**
* 导入
*/
IMPORT,
/**
* 强退
*/
FORCE,
/**
* 清空数据
*/
CLEAN,
}
}

View File

@@ -0,0 +1,20 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 操作日志注解组
*
* @author xxm
* @since 2021/12/20
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OperateLogs {
/** 操作日志注解组 */
OperateLog[] value();
}

View File

@@ -0,0 +1,20 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 权限码
* @author xxm
* @since 2024/7/7
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PermCode {
/**
* 权限码,支持配置多个
*/
String[] value();
}

View File

@@ -0,0 +1,84 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 查询参数 生效顺序 QueryParams 查询参数字段 > Entity 数据库实体字段 > QueryParams 查询类 > Entity 数据库实体类
*
* @author xxm
* @since 2022/12/12
*/
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface QueryParam {
/**
* 匹配条件类型
*/
CompareTypeEnum type() default CompareTypeEnum.EQ;
/**
* 生成查询条件时是否忽略
*/
boolean ignore() default false;
/**
* 名称转换类型, 默认为下划线
*/
NamingCaseEnum namingCase() default NamingCaseEnum.UNDER_LINE;
/**
* 自定义查询字段对应的数据库字段名称. 只可以在字段上标注时使用,标注在类上不生效 配置后namingCase配置将会无效会以这项配置数据库字段名称为准
*/
String fieldName() default "";
/**
* 匹配类型
*/
enum CompareTypeEnum {
/** 大于 */
GT,
/** 大于等于 */
GE,
/** 小于 */
LT,
/** 小于等于 */
LE,
/** 等于 */
EQ,
/** 范围查询, 需要放在对象上, */
BETWEEN,
/** 模糊匹配 */
LIKE,
/** 左模糊 */
LIKE_LEFT,
/** 右模糊 */
LIKE_RIGHT,
/** 是否为空, 只作用在布尔类型上, true 代表 is null, false 代表 not null */
IS_NULL,
/** 排序 */
SORT
}
/**
* 名称转换类型
*/
enum NamingCaseEnum {
/** 转换为下划线 */
UNDER_LINE,
/** 不进行处理 */
NONE
}
}

View File

@@ -0,0 +1,46 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 请求分组注解
* @see RequestPath
*
* <br/>
* 分为三级: 模块-->分组-->路径
* <br/>
* 模块: 通常对应一个的功能模块, 如用户管理、认证服务、权限管理等
* <br/>
* 分组: 通常对应多个请求组成的一个功能点分组, 如用户管理下的用户管理、用户管理下的角色管理等,多数时候会对对应少数几个或者一个控制器(Controller)
* <br/>
* 路径: 对应具体的一个具体请求方式(GET/POST)的请求路径
*
* @author xxm
* @since 2024/7/4
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RequestGroup {
/**
* 模块编码
*/
String moduleCode();
/**
* 模块名称, 多次标注注解时, 同样的模块编码, 可以只写一个模块名称, 其他未写的会自动使用这个
*/
String moduleName() default "";
/**
* 分组编码
*/
String groupCode();
/**
* 分组名称, 多次标注注解时, 同样的分组编码, 可以只写一个分组名称, 其他未写的会自动使用这个
*/
String groupName() default "";
}

View File

@@ -0,0 +1,30 @@
package cn.bootx.platform.core.annotation;
import java.lang.annotation.*;
/**
* 请求路径注解
* @see RequestGroup
* <br/>
* 分为三级: 模块-->分组-->路径
* <br/>
* 模块: 通常对应一个的功能模块, 如用户管理、认证服务、权限管理等
* <br/>
* 分组: 通常对应多个请求组成的一个功能点分组, 如用户管理下的用户管理、用户管理下的角色管理等,多数时候会对对应少数几个或者一个控制器
* <br/>
* 路径: 对应具体的一个具体请求方式(GET/POST)的请求路径
*
* @author xxm
* @since 2024/7/4
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RequestPath {
/**
* 请求路径名称
*/
String value();
}

View File

@@ -0,0 +1,83 @@
package cn.bootx.platform.core.code;
/**
* 公共常量
*
* @author xxm
* @since 2020/4/8 10:58
*/
public interface CommonCode {
/** 服务器地址 */
String SERVER_URL = "ServerUrl";
/** 开发环境 */
String ENV_DEV = "dev";
/** 测试环境 */
String ENV_TEST = "test";
/** 生产环境 */
String ENV_PROD = "prod";
/**
* 实体类删除标记
*/
String DELETE_FLAG = "1";
/**
* 实体类正常标记
*/
String NORMAL_FLAG = "0";
/**
* 系统默认用户的 userId便于定时任务和异步任务时使用
*/
Long SYSTEM_DEFAULT_USERID = 1L;
/**
* 系统默认用户的 userId便于定时任务和异步任务时使用
*/
String SYSTEM_DEFAULT_USERNAME = "系统";
/**
* 响应成功码
*/
int SUCCESS_CODE = 0;
/**
* 响应失败码
*/
int FAIL_CODE = 1;
/** 追踪Id */
String TRACE_ID = "traceId";
/** 用户 */
String USER = "user";
/** 用户id */
String USER_ID = "userId";
/** 主键字段 */
String ID = "id";
/** 创建人字段 */
String CREATOR = "creator";
/** 创建时间字段 */
String CREATE_TIME = "createTime";
/** 最后更新人字段 */
String LAST_MODIFIER = "lastModifier";
/** 最后更新时间字段 */
String LAST_MODIFIED_TIME = "lastModifiedTime";
/** 数据版本号字段 */
String VERSION = "version";
/** 数据软删除标识字段 */
String DELETED = "deleted";
}

View File

@@ -0,0 +1,35 @@
package cn.bootx.platform.core.code;
/**
* 公用错误码常量 10000-19999
*/
public interface CommonErrorCode {
/** 系统错误 */
int SYSTEM_ERROR = 10500;
/** 认证失败 */
int AUTHENTICATION_FAIL = 10401;
/** 参数处理失败 */
int PARSE_PARAMETERS_ERROR = 10505;
/** 参数验证失败 */
int VALIDATE_PARAMETERS_ERROR = 10506;
/** 重复操作异常 */
int REPETITIVE_OPERATION_ERROR = 10507;
/** 资源不存在 */
int SOURCES_NOT_EXIST = 10404;
/** 数据不存在 */
int DATA_NOT_EXIST = 10405;
/** 不支持的操作 */
int UN_SUPPORTED_OPERATE = 10415;
/** 危险SQL异常, */
int DANGER_SQL = 10512;
}

View File

@@ -0,0 +1,19 @@
package cn.bootx.platform.core.code;
/**
* Servlet常量
*
* @author xxm
* @since 2022/3/10
*/
public interface ServletCode {
String METHOD = "Method";
String CONTEXT_PATH = "ContextPath";
String REQUEST_URI = "RequestURI";
String REQUEST_URL = "RequestURL";
}

View File

@@ -0,0 +1,49 @@
package cn.bootx.platform.core.code;
/**
* web请求头常量
*
* @author network
*/
public interface WebHeaderCode {
/** ip */
String X_FORWARDED_FOR = "X-Forwarded-For";
/** 用户代理 */
String USER_AGENT = "User-Agent";
/** 请求头中 operatorId 的参数名 */
String OPERATOR_ID = "OperatorId";
/** 请求头中 ACCESS_TOKEN 的参数名 */
String ACCESS_TOKEN = "AccessToken";
/** 请求头中 Jwt_Token 的参数名 */
String JWT_TOKEN = "JwtToken";
/** 请求头中 Bearer 的参数名 */
String BEARER = "Bearer";
/** 请求头中 Authorization 的参数名 */
String AUTH = "Authorization";
/** 幂等请求token */
String IDEMPOTENT_TOKEN = "IdempotentToken";
/** 请求头 tid 的参数名 */
String TID = "Tid";
/** 请求头 channelId 的参数名 */
String CHANNEL_ID = "ChannelId";
/** 请求头中 appId 的参数名 */
String APP_ID = "AppId";
/** 请求头中 appName 的参数名 */
String APP_NAME = "AppName";
/** 请求头中 timezone 的参数名 */
String TIMEZONE_NAME = "Timezone";
}

View File

@@ -0,0 +1,52 @@
package cn.bootx.platform.core.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Map;
/**
* 用户信息类
*
* @author xxm
* @since 2021/7/30
*/
@Getter
@Setter
@ToString(callSuper = true)
@Accessors(chain = true)
@NoArgsConstructor
public class UserDetail {
/** 用户id */
private Long id;
/** 用户名称 */
private String name;
/** 账号 */
private String account;
/** 不进行持久化 */
@JsonIgnore
private transient String password;
/** 是否管理员 */
private boolean admin;
/**
* 账号状态
* @see cn.bootx.platform.iam.code.UserStatusCode
*/
private String status;
/**
* 扩展信息
*/
private Map<String,String> ext;
}

View File

@@ -0,0 +1,16 @@
package cn.bootx.platform.core.exception;
/**
* 致命异常
* @author xxm
* @since 2024/7/14
*/
public class BizErrorException extends BizException {
public BizErrorException(int code, String message) {
super(code,message);
}
public BizErrorException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,29 @@
package cn.bootx.platform.core.exception;
import cn.bootx.platform.core.code.CommonCode;
import lombok.Getter;
/**
* 业务异常基类
* @see BizErrorException 致命异常 error级别警告
* @see BizWarnException 业务异常 warn级别
* @see BizInfoException 哦月异常 info级别
*/
@Getter
public class BizException extends RuntimeException {
private int code = CommonCode.FAIL_CODE;
public BizException(int code, String message) {
super(message);
this.code = code;
}
public BizException(String message) {
super(message);
}
public BizException() {
}
}

View File

@@ -0,0 +1,16 @@
package cn.bootx.platform.core.exception;
/**
* 普通异常, 不需要显示异常栈
* @author xxm
* @since 2024/7/14
*/
public class BizInfoException extends BizException{
public BizInfoException(int code, String message) {
super(code,message);
}
public BizInfoException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,17 @@
package cn.bootx.platform.core.exception;
/**
* 警告异常
* @author xxm
* @since 2024/7/14
*/
public class BizWarnException extends BizException{
public BizWarnException(int code, String message) {
super(code,message);
}
public BizWarnException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,22 @@
package cn.bootx.platform.core.exception;
import cn.bootx.platform.core.code.CommonErrorCode;
/**
* SQL相关异常
*
* @author xxm
* @since 2023/3/9
*/
public class DangerSqlException extends BizException {
public DangerSqlException(String msg) {
super(CommonErrorCode.DANGER_SQL, msg);
}
public DangerSqlException() {
super(CommonErrorCode.DANGER_SQL, "危险SQL异常");
}
}

View File

@@ -0,0 +1,22 @@
package cn.bootx.platform.core.exception;
import cn.bootx.platform.core.code.CommonErrorCode;
/**
* 数据不存在异常
*
* @author xxm
* @since 2022/1/10
*/
public class DataNotExistException extends BizInfoException {
public DataNotExistException(String msg) {
super(CommonErrorCode.DATA_NOT_EXIST, msg);
}
public DataNotExistException() {
super(CommonErrorCode.DATA_NOT_EXIST, "数据不存在");
}
}

View File

@@ -0,0 +1,24 @@
package cn.bootx.platform.core.exception;
import java.io.Serializable;
import static cn.bootx.platform.core.code.CommonErrorCode.REPETITIVE_OPERATION_ERROR;
/**
* 重复操作异常
*
* @author xxm
* @since 2021/1/2
*/
public class RepetitiveOperationException extends BizInfoException implements Serializable {
public RepetitiveOperationException() {
super(REPETITIVE_OPERATION_ERROR, "重复操作异常");
}
public RepetitiveOperationException(String msg) {
super(REPETITIVE_OPERATION_ERROR, msg);
}
}

View File

@@ -0,0 +1,23 @@
package cn.bootx.platform.core.exception;
import java.io.Serializable;
import static cn.bootx.platform.core.code.CommonErrorCode.UN_SUPPORTED_OPERATE;
/**
* 不支持的操作异常
*
* @author xxm
* @since 2022/7/27
*/
public class UnSupportOperateException extends BizInfoException implements Serializable {
public UnSupportOperateException(String message) {
super(UN_SUPPORTED_OPERATE, message);
}
public UnSupportOperateException() {
super(UN_SUPPORTED_OPERATE, "不支持的操作异常");
}
}

View File

@@ -0,0 +1,18 @@
package cn.bootx.platform.core.exception;
import cn.bootx.platform.core.code.CommonErrorCode;
/**
* 验证失败异常
*/
public class ValidationFailedException extends BizInfoException {
public ValidationFailedException(String detail) {
super(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "验证参数错误" + System.lineSeparator() + detail);
}
public ValidationFailedException() {
super(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,"验证参数错误");
}
}

View File

@@ -0,0 +1,53 @@
package cn.bootx.platform.core.rest;
import cn.bootx.platform.core.rest.result.ErrorResult;
import cn.bootx.platform.core.rest.result.Result;
import static cn.bootx.platform.core.code.CommonCode.FAIL_CODE;
import static cn.bootx.platform.core.code.CommonCode.SUCCESS_CODE;
/**
* 返回工具类
*
* @author xxm
* @since 2020/1/22 15:29
*/
public class Res {
private final static String SUCCESS = "success";
private final static String FAILED = "failed";
public static <T> Result<T> ok() {
return new Result<>(SUCCESS_CODE, SUCCESS);
}
public static <T> Result<T> okAndMsg(String message) {
return new Result<>(SUCCESS_CODE, message);
}
public static <T> Result<T> ok(T data) {
return new Result<>(SUCCESS_CODE, data, SUCCESS);
}
public static <T> Result<T> error() {
return new Result<>(FAIL_CODE, FAILED);
}
public static <T> Result<T> error(String message) {
return new Result<>(FAIL_CODE, message);
}
public static <T> Result<T> response(int code, String msg) {
return new Result<>(code, msg);
}
public static <T> Result<T> response(int code, String msg, String traceId) {
return new ErrorResult<>(code, msg, traceId);
}
public static <T> Result<T> response(int code, String msg, T data) {
return new Result<>(code, data, msg);
}
}

View File

@@ -0,0 +1,29 @@
package cn.bootx.platform.core.rest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.experimental.Accessors;
/**
* kv键值对象
*
* @author xxm
* @since 2021/5/18
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Accessors(chain = true)
@Schema(title = "kv键值对象")
public class KeyValue {
@Schema(description = "")
private String key;
@Schema(description = "")
private String value;
}

View File

@@ -0,0 +1,34 @@
package cn.bootx.platform.core.rest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
/**
* LabelValue
*
* @author xxm
* @since 2022/5/4
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Schema(title = "LabelValue键值对象")
public class LabelValue {
@Schema(description = "label")
private String label;
@Schema(description = "")
private String value;
public LabelValue(String label, Number value) {
this.label = label;
this.value = String.valueOf(value);
}
}

View File

@@ -0,0 +1,48 @@
package cn.bootx.platform.core.rest.param;
import cn.hutool.core.util.PageUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* 分页查询参数
*/
@Getter
@Setter
@Schema(title = "分页查询参数")
@NoArgsConstructor
@AllArgsConstructor
public class PageParam {
/**
* 当前页
*/
@Schema(description = "当前页", defaultValue = "1")
private int current = 1;
/**
* 每页显示条数,默认 10
*/
@Schema(description = "每页显示条数,默认 10", defaultValue = "10")
private int size = 10;
/**
* 开始条数
*/
public int start() {
// hutool 的下标从0开始
return PageUtil.transToStartEnd(current - 1, size)[0];
}
/**
* 结束条数
*/
public int end() {
// hutool 的下标从0开始
return PageUtil.transToStartEnd(current - 1, size)[1];
}
}

View File

@@ -0,0 +1,27 @@
package cn.bootx.platform.core.rest.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* 错误响应类,携带链路追踪标示 trackId
*
* @author xxm
* @since 2021/9/9
*/
@Getter
@Setter
@Schema(title = "错误响应类,携带链路追踪标示 trackId")
public class ErrorResult<T> extends Result<T> {
/** 链路追踪标示 */
@Schema(description = "链路追踪标示")
private String traceId;
public ErrorResult(int code, String msg, String traceId) {
super(code, msg);
this.traceId = traceId;
}
}

View File

@@ -0,0 +1,56 @@
package cn.bootx.platform.core.rest.result;
import lombok.Getter;
import java.util.Collections;
import java.util.List;
/**
* 分页包装类
*
* @author xxm
* @since 2020/4/21 14:37
*/
@Getter
public class PageResult<T> {
/**
* 查询数据列表
*/
private List<T> records = Collections.emptyList();
/**
* 总数
*/
private long total = 0;
/**
* 每页显示条数,默认 10
*/
private long size = 10;
/**
* 当前页
*/
private long current = 1;
public PageResult<T> setRecords(List<T> records) {
this.records = records;
return this;
}
public PageResult<T> setTotal(long total) {
this.total = total;
return this;
}
public PageResult<T> setSize(long size) {
this.size = size;
return this;
}
public PageResult<T> setCurrent(long current) {
this.current = current;
return this;
}
}

View File

@@ -0,0 +1,52 @@
package cn.bootx.platform.core.rest.result;
import cn.bootx.platform.core.code.CommonCode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* 响应包装类
*
* @author xxm
* @since 2020/1/22 15:26
*/
@Getter
@Setter
@ToString
public class Result<T> {
@Schema(description = "响应提示")
private String msg = "success";
@Schema(description = "响应码")
private int code = CommonCode.SUCCESS_CODE;
@Schema(description = "响应数据")
private T data;
public Result() {
super();
}
public Result(int code) {
this.code = code;
}
public Result(int code, T data) {
this(code);
this.data = data;
}
public Result(int code, String msg) {
this(code);
this.msg = msg;
}
public Result(int code, T data, String msg) {
this(code, msg);
this.data = data;
}
}

View File

@@ -0,0 +1,23 @@
package cn.bootx.platform.core.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
/**
* 基础返回类
* @author xxm
* @since 2024/8/12
*/
@Getter
@Setter
public class BaseResult {
@Schema(description = "主键")
private Long id;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,62 @@
package cn.bootx.platform.core.util;
import lombok.experimental.UtilityClass;
import java.math.BigDecimal;
/**
* BigDecimal相关的工具类
*
* @author network
*/
@UtilityClass
public class BigDecimalUtil {
/**
* 比较大小
* @param first 数字1
* @param last 数字2
* @return first > last =1 / first == last = 0 / first < last = -1
*/
public int compareTo(BigDecimal first, BigDecimal last) {
BigDecimal newFirst = BigDecimal.ZERO;
BigDecimal newLast = BigDecimal.ZERO;
if (first != null) {
newFirst = first;
}
if (last != null) {
newLast = last;
}
return newFirst.compareTo(newLast);
}
/**
* 是否大于
*/
public boolean isGreaterThan(BigDecimal first, BigDecimal last) {
return compareTo(first, last) > 0;
}
/**
* 是否大于或等于
*/
public boolean isGreaterAndEqualThan(BigDecimal first, BigDecimal last) {
return compareTo(first, last) >= 0;
}
/**
* 是否小于
*/
public boolean isLessThan(BigDecimal first, BigDecimal last) {
return compareTo(first, last) < 0;
}
/**
* 是否等于
*/
public boolean isEqual(BigDecimal first, BigDecimal last) {
return compareTo(first, last) == 0;
}
}

View File

@@ -0,0 +1,34 @@
package cn.bootx.platform.core.util;
import cn.hutool.core.codec.Base64Encoder;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
/**
* 证书工具类
*
* @author xxm
* @since 2022/2/24
*/
@UtilityClass
public class CertUtil {
/**
* 根据证书文本获取证书
* @param certContent 证书字符串
*/
@SneakyThrows
public String getCertByContent(String certContent) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(new ByteArrayInputStream(certContent.getBytes(StandardCharsets.UTF_8)));
PublicKey publicKey = cert.getPublicKey();
return Base64Encoder.encode(publicKey.getEncoded());
}
}

View File

@@ -0,0 +1,67 @@
package cn.bootx.platform.core.util;
import cn.hutool.core.util.ClassUtil;
import lombok.experimental.UtilityClass;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 类工具类
* @author xxm
* @since 2023/5/30
*/
@UtilityClass
public class ClassUtils {
/**
* 获取字段属性
*/
public Field getField(Class<?> clas, String fieldName){
if (Objects.isNull(clas)){
throw new IllegalArgumentException("类为空");
}
// 查询是否有该属性
Field field = ClassUtil.getDeclaredField(clas, fieldName);
if (Objects.nonNull(field)){
return field;
}
// 递归查询父类中的字段值
if (Objects.nonNull(clas.getSuperclass())){
return getField(clas.getSuperclass(),fieldName);
}
return null;
}
/**
* 递归扫描父类的fields
* @param clas 类
* @param fields 属性
*/
public Field[] recursionParents(Class<?> clas, Field[] fields) {
if (Objects.nonNull(clas.getSuperclass())) {
Class<?> clsSup = clas.getSuperclass();
List<Field> fieldList = new ArrayList<>(List.of(fields));
// 获取当前class的所有fields的name列表
List<String> fdNames = fieldList.stream().map(Field::getName).toList();
for (Field pfd : clsSup.getDeclaredFields()) {
// 避免重载属性
if (fdNames.contains(pfd.getName())) {
continue;
}
fieldList.add(pfd);
}
fields = new Field[fieldList.size()];
int i = 0;
for (Object field : fieldList.toArray()) {
fields[i] = (Field) field;
i++;
}
fields = recursionParents(clsSup, fields);
}
return fields;
}
}

View File

@@ -0,0 +1,85 @@
package cn.bootx.platform.core.util;
import lombok.experimental.UtilityClass;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* java8 时间工具类
*
* @author xxm
* @since 2020/11/10
*/
@UtilityClass
public class DateTimeUtil {
/**
* 大于
*/
public boolean gt(LocalDateTime now, LocalDateTime next) {
long mills = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long epochMilli = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
return mills > epochMilli;
}
/**
* 小于
*/
public boolean lt(LocalDateTime now, LocalDateTime next) {
long mills = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long epochMilli = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
return mills < epochMilli;
}
/**
* 大于等于
*/
public boolean ge(LocalDateTime now, LocalDateTime next) {
long mills = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long epochMilli = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
return mills >= epochMilli;
}
/**
* 小于等于
*/
public boolean le(LocalDateTime now, LocalDateTime next) {
long mills = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long epochMilli = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
return mills <= epochMilli;
}
/**
* 将localDate转换成localDateTime
*/
public LocalDateTime date2DateTime(LocalDate localDate) {
return localDate.atTime(0, 0);
}
/**
* 将long类型的timestamp转为LocalDateTime
* @param timestamp 时间戳
* @return LocalDateTime
*/
public LocalDateTime parse(long timestamp) {
Instant instant = Instant.ofEpochMilli(timestamp);
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone);
}
/**
* LocalDateTime转为long类型的timestamp
* @param localDateTime 日期时间
* @return timestamp
*/
public long timestamp(LocalDateTime localDateTime) {
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
return instant.toEpochMilli();
}
}

View File

@@ -0,0 +1,61 @@
package cn.bootx.platform.core.util;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.experimental.UtilityClass;
/**
* json工具类, 基于hutool的进行封装, 对java8的LocalDateTime时间格式进行转换, 但无法处理LocalDate, LocalTime格式, 需要使用JacksonUtil进行处理
* @author xxm
* @since 2024/6/28
*/
@UtilityClass
public class JsonUtil {
private final JSONConfig JSON_CONFIG = JSONConfig.create().setDateFormat(DatePattern.NORM_DATETIME_PATTERN);
/**
* 转换为实体
*/
public <T> T toBean(String json, Class<T> clazz){
JSONObject jsonObject = new JSONObject(json, JSON_CONFIG);
return JSONUtil.toBean(jsonObject, clazz);
}
/**
* 转换为实体
*/
public <T> T toBean(String json, TypeReference<T> reference){
JSON parse = JSONUtil.parse(json, JSON_CONFIG);
return parse.toBean(reference);
}
/**
* 转换为实体
*/
public <T> T toBean(String json, TypeReference<T> reference, boolean ignoreError){
JSONConfig jsonConfig = JSONConfig.create()
.setDateFormat(DatePattern.NORM_DATETIME_PATTERN)
.setIgnoreError(ignoreError);
JSON parse = JSONUtil.parse(json, jsonConfig);
return parse.toBean(reference);
}
/**
* 序列化为字符串
*/
public String toJsonStr(Object object){
JSONObject jsonObject = new JSONObject(object, JSON_CONFIG);
return JSONUtil.toJsonStr(jsonObject);
}
/**
* JSON字符串转JSONObject对象
*/
public JSONObject parseObj(String jsonStr){
return JSONUtil.parseObj(jsonStr, JSON_CONFIG);
}
}

View File

@@ -0,0 +1,137 @@
package cn.bootx.platform.core.util;
import cn.bootx.platform.core.exception.DangerSqlException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* sql注入处理工具类 参考自 Jeecg Boot
*
* @author zhoujf
*/
@Slf4j
@UtilityClass
public class SqlInjectionUtil {
private final String XSS_STR = "and |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()";
/**
* 正则 user() 匹配更严谨
*/
private final String REGULAR_EXPRE_USER = "user[\\s]*\\([\\s]*\\)";
/** 正则 show tables */
private final String SHOW_TABLES = "show\\s+tables";
/**
* sql注释的正则
*/
private final Pattern SQL_ANNOTATION = Pattern.compile("/\\*[\\s\\S]*\\*/");
/**
* sql注入过滤处理遇到注入关键字抛异常
*/
public void filterContent(String value) {
filterContent(value, null);
}
/**
* sql注入过滤处理遇到注入关键字抛异常
*
*/
public void filterContent(String value, String customXssString) {
if (value == null || value.isEmpty()) {
return;
}
// 校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 统一转为小写
value = value.toLowerCase();
// SQL注入检测存在绕过风险
String[] xssArr = XSS_STR.split("\\|");
for (String s : xssArr) {
if (value.contains(s)) {
log.error("请注意存在SQL注入关键词---> {}", s);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new DangerSqlException("请注意值可能存在SQL注入风险!--->" + value);
}
}
// 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (customXssString != null) {
String[] xssArr2 = customXssString.split("\\|");
for (String s : xssArr2) {
if (value.contains(s)) {
log.error("请注意存在SQL注入关键词---> {}", s);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new DangerSqlException("请注意值可能存在SQL注入风险!--->" + value);
}
}
}
// 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)) {
throw new DangerSqlException("请注意值可能存在SQL注入风险!--->" + value);
}
}
/**
* sql注入过滤处理遇到注入关键字抛异常
*/
public void filterContent(String[] values) {
filterContent(values, null);
}
/**
* sql注入过滤处理遇到注入关键字抛异常
*/
public void filterContent(String[] values, String customXssString) {
String[] xssArr = XSS_STR.split("\\|");
for (String value : values) {
if (value == null || value.isEmpty()) {
return;
}
// 校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 统一转为小写
value = value.toLowerCase();
for (String s : xssArr) {
if (value.contains(s)) {
log.error("请注意存在SQL注入关键词---> {}", s);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new DangerSqlException("请注意值可能存在SQL注入风险!--->" + value);
}
}
// 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (customXssString != null) {
String[] xssArr2 = customXssString.split("\\|");
for (String s : xssArr2) {
if (value.contains(s)) {
log.error("请注意存在SQL注入关键词---> {}", s);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new DangerSqlException("请注意值可能存在SQL注入风险!--->" + value);
}
}
}
// 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)) {
throw new DangerSqlException("请注意值可能存在SQL注入风险!--->" + value);
}
}
}
/**
* 校验是否有sql注释
*/
public void checkSqlAnnotation(String str) {
Matcher matcher = SQL_ANNOTATION.matcher(str);
if (matcher.find()) {
String error = "请注意值可能存在SQL注入风险---> \\*.*\\";
log.error(error);
throw new DangerSqlException(error);
}
}
}

View File

@@ -0,0 +1,91 @@
package cn.bootx.platform.core.util;
import cn.hutool.core.collection.CollectionUtil;
import lombok.experimental.UtilityClass;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 构建数据树工具类
*
* @author xxm
* @since 2022/12/24
*/
@UtilityClass
public class TreeBuildUtil {
/**
* 构建成树
* @param list 要进行转换的列表
* @param pid 一级节点的父级id通常为null
* @param getId 获取主键的方法方法引用
* @param getPid 获取关联父级节点主键的方法引用
* @param setChildren 设置子节点列表的方法引用
*/
public <T> List<T> build(List<T> list, Object pid, Function<T, Object> getId, Function<T, Object> getPid,
BiConsumer<T, List<T>> setChildren) {
return build(list, pid, getId, getPid, setChildren, null);
}
/**
* 构建成树 (带排序)
* @param list 要进行转换的列表
* @param pid 一级节点的父级id通常为null
* @param getId 获取主键的方法方法引用
* @param getPid 获取关联父级节点主键的方法引用
* @param setChildren 设置子节点列表的方法引用
* @param comparator 节点顺序的排序规则
*/
public <T> List<T> build(List<T> list, Object pid, Function<T, Object> getId, Function<T, Object> getPid,
BiConsumer<T, List<T>> setChildren, Comparator<? super T> comparator) {
List<T> children = list.stream().filter(m -> Objects.equals(getPid.apply(m), pid)).collect(Collectors.toList());
if (CollectionUtil.isEmpty(children)) {
return new ArrayList<>(0);
}
for (T region : children) {
List<T> childrenList = build(list, getId.apply(region), getId, getPid, setChildren,comparator);
setChildren.accept(region, childrenList);
}
// 排序
if (Objects.nonNull(comparator)) {
children.sort(comparator);
}
return children;
}
/**
* 展开树为list列表
*
* @param list 要进行展开的列表
* @param getChildren 获取子节点列表的方法引用
* @return 展开后的结果列表
*/
public <T> List<T> unfold(List<T> list, Function<T, List<T>> getChildren){
return unfold(list,getChildren, new ArrayList<>());
}
/**
* 展开树为list列表
*
* @param list 要进行展开的列表
* @param getChildren 获取子节点列表的方法引用
* @param result 用于存储展开后的列表对象
* @return 展开后的结果列表
*/
private <T> List<T> unfold(List<T> list, Function<T, List<T>> getChildren, List<T> result){
if (CollectionUtil.isEmpty(list)) {
return new ArrayList<>(0);
}
for (T region : list) {
unfold(getChildren.apply(region), getChildren, result);
result.add(region);
}
return result;
}
}

View File

@@ -0,0 +1,53 @@
package cn.bootx.platform.core.util;
import cn.bootx.platform.core.exception.ValidationFailedException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import lombok.experimental.UtilityClass;
import java.util.Set;
/**
* BeanValidation 校验工具类
*
* @author xxm
* @since 2020/5/26 18:14
*/
@UtilityClass
public class ValidationUtil {
private static final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
/**
* 验证参数对象,如果验证失败则抛出异常
*/
public void validateParam(Object paramObject, Class<?>... groups) {
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<Object>> violations = validator.validate(paramObject, groups);
if (!violations.isEmpty()) {
throw new ValidationFailedException(extractMessages(violations));
}
}
/**
* 验证参数对象,如果验证失败则返回所有失败信息
*/
public String validate(Object paramObject, Class<?>... groups) {
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<Object>> violations = validator.validate(paramObject, groups);
return extractMessages(violations);
}
/**
* 提取校验失败的消息
*/
private String extractMessages(Set<ConstraintViolation<Object>> violations) {
StringBuilder message = new StringBuilder();
for (ConstraintViolation<Object> violation : violations) {
message.append(violation.getMessage()).append(System.lineSeparator());
}
return message.toString();
}
}

View File

@@ -0,0 +1,27 @@
package cn.bootx.platform.core.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author xxm
* @since 2024/6/11
*/
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
// 标记由哪个类来执行校验逻辑该类需要实现ConstraintValidator接口
@Constraint(validatedBy = IpAddressValidator.class)
public @interface IpAddress {
String message() default "IP地址不合法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,25 @@
package cn.bootx.platform.core.validation;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* ip地址校验器
* @author xxm
* @since 2024/6/11
*/
public class IpAddressValidator implements ConstraintValidator<IpAddress, String> {
/**
* 校验是否为IP地址。成功返回true失败返回false
*/
@Override
public boolean isValid(String str, ConstraintValidatorContext constraintValidatorContext) {
if (StrUtil.isNotBlank(str)){
return Validator.isIpv4(str) || Validator.isIpv6(str);
}
return true;
}
}

View File

@@ -0,0 +1,39 @@
package cn.bootx.platform.core.validation;
/**
* 校验分组
*
* @author xxm
* @since 2021/5/7
*/
public interface ValidationGroup {
/**
* 参数校验分组:增加
*/
@interface add {
}
/**
* 参数校验分组:编辑
*/
@interface edit {
}
/**
* 参数校验分组:删除
*/
@interface delete {
}
/**
* 参数校验分组:查询
*/
@interface query {
}
}