v2.0 代码提交

This commit is contained in:
zhuoda
2022-10-27 22:14:48 +08:00
parent 207b949484
commit f7e5f6d539
1851 changed files with 108157 additions and 2231 deletions

View File

@@ -0,0 +1,236 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.1024lab</groupId>
<artifactId>sa-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>sa-common</artifactId>
<version>1.0.0</version>
<name>sa-common</name>
<description>sa-common project</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<exclusions>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
</dependency>
<!-- poi start -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
</dependency>
<!-- poi end -->
<dependency>
<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
<artifactId>concurrentlinkedhashmap-lru</artifactId>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>
<!-- sax 读取时候用到的 -->
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity.tools</groupId>
<artifactId>velocity-tools-generic</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,20 @@
package net.lab1024.sa.common.common.annoation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 不需要登录注解
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NoNeedLogin {
}

View File

@@ -0,0 +1,22 @@
package net.lab1024.sa.common.common.annoation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 校验权限注解
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SaAuth {
String saAuth = "saAuth";
}

View File

@@ -0,0 +1,52 @@
package net.lab1024.sa.common.common.code;
/**
* 错误码<br>
* 一共分为三种: 1系统错误、2用户级别错误、3未预期到的错误
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public interface ErrorCode {
/**
* 系统等级
*/
String LEVEL_SYSTEM = "system";
/**
* 用户等级
*/
String LEVEL_USER = "user";
/**
* 未预期到的等级
*/
String LEVEL_UNEXPECTED = "unexpected";
/**
* 错误码
*
* @return
*/
int getCode();
/**
* 错误消息
*
* @return
*/
String getMsg();
/**
* 错误等级
*
* @return
*/
String getLevel();
}

View File

@@ -0,0 +1,119 @@
package net.lab1024.sa.common.common.code;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 错误码 注册容器
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/09/27 22:09
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
class ErrorCodeRangeContainer {
/**
* 所有的错误码均大于10000
*/
static final int MIN_START_CODE = 10000;
static final Map<Class<? extends ErrorCode>, ImmutablePair<Integer, Integer>> CODE_RANGE_MAP = new ConcurrentHashMap<>();
/**
* 用于统计数量
*/
static int errorCounter = 0;
/**
* 注册状态码
* 校验是否重复 是否越界
*
* @param clazz
* @param start
* @param end
*/
static void register(Class<? extends ErrorCode> clazz, int start, int end) {
String simpleName = clazz.getSimpleName();
if (!clazz.isEnum()) {
throw new ExceptionInInitializerError(String.format("<<ErrorCodeRangeValidator>> error: %s not Enum class !", simpleName));
}
if (start > end) {
throw new ExceptionInInitializerError(String.format("<<ErrorCodeRangeValidator>> error: %s start must be less than the end !", simpleName));
}
if (start <= MIN_START_CODE) {
throw new ExceptionInInitializerError(String.format("<<ErrorCodeRangeValidator>> error: %s start must be more than %s !", simpleName, MIN_START_CODE));
}
// 校验是否重复注册
boolean containsKey = CODE_RANGE_MAP.containsKey(clazz);
if (containsKey) {
throw new ExceptionInInitializerError(String.format("<<ErrorCodeRangeValidator>> error: Enum %s already exist !", simpleName));
}
// 校验 开始结束值 是否越界
CODE_RANGE_MAP.forEach((k, v) -> {
if (isExistOtherRange(start, end, v)) {
throw new IllegalArgumentException(String.format("<<ErrorCodeRangeValidator>> error: %s[%d,%d] has intersection with class:%s[%d,%d]", simpleName, start, end,
k.getSimpleName(), v.getLeft(), v.getRight()));
}
});
// 循环校验code并存储
List<Integer> codeList = Stream.of(clazz.getEnumConstants()).map(codeEnum -> {
Integer code = codeEnum.getCode();
if (code < start || code > end) {
throw new IllegalArgumentException(String.format("<<ErrorCodeRangeValidator>> error: %s[%d,%d] code %d out of range", simpleName, start, end, code));
}
return code;
}).collect(Collectors.toList());
// 校验code是否重复
List<Integer> distinctCodeList = codeList.stream().distinct().collect(Collectors.toList());
Collection<Integer> subtract = CollectionUtils.subtract(codeList, distinctCodeList);
if (CollectionUtils.isNotEmpty(subtract)) {
throw new IllegalArgumentException(String.format("<<ErrorCodeRangeValidator>> error: %s code %s is repeat!", simpleName, subtract));
}
CODE_RANGE_MAP.put(clazz, ImmutablePair.of(start, end));
// 统计
errorCounter = errorCounter + distinctCodeList.size();
}
/**
* 是否存在于其他范围
*
* @param start
* @param end
* @param range
* @return
*/
private static boolean isExistOtherRange(int start, int end, ImmutablePair<Integer, Integer> range) {
if (start >= range.getLeft() && start <= range.getRight()) {
return true;
}
if (end >= range.getLeft() && end <= range.getRight()) {
return true;
}
return false;
}
/**
* 进行初始化
*/
static int initialize() {
return errorCounter;
}
}

View File

@@ -0,0 +1,41 @@
package net.lab1024.sa.common.common.code;
import static net.lab1024.sa.common.common.code.ErrorCodeRangeContainer.register;
/**
* 注册code状态码 <br>
* ps为什么要在此处不那么优雅的手动注册
* 主要是为了能统一、清晰、浏览当前定义的所有状态码
* 方便后续维护
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/09/27 23:09
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class ErrorCodeRegister {
static {
// 系统 错误码
register(SystemErrorCode.class, 10001, 20000);
// 意外 错误码
register(UnexpectedErrorCode.class, 20001, 30000);
// 用户 通用错误码
register(UserErrorCode.class, 30001, 40000);
}
public static int initialize() {
return ErrorCodeRangeContainer.initialize();
}
public static void main(String[] args) {
ErrorCodeRegister.initialize();
}
}

View File

@@ -0,0 +1,36 @@
package net.lab1024.sa.common.common.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 系统错误状态码(此类返回码应该高度重视)
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/10/24 20:09
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Getter
@AllArgsConstructor
public enum SystemErrorCode implements ErrorCode {
SYSTEM_ERROR(10001, "系统似乎出现了点小问题"),
;
private final int code;
private final String msg;
private final String level;
SystemErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
this.level = LEVEL_SYSTEM;
}
}

View File

@@ -0,0 +1,36 @@
package net.lab1024.sa.common.common.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 未预期的错误码(即发生了不可能发生的事情,此类返回码应该高度重视)
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/09/27 22:10:46
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Getter
@AllArgsConstructor
public enum UnexpectedErrorCode implements ErrorCode {
BUSINESS_HANDING(20001, "呃~ 业务繁忙,请稍后重试"),
PAY_ORDER_ID_ERROR(20002, "付款单id发生了异常请联系技术人员排查"),
;
private final int code;
private final String msg;
private final String level;
UnexpectedErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
this.level = LEVEL_UNEXPECTED;
}
}

View File

@@ -0,0 +1,48 @@
package net.lab1024.sa.common.common.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 用户级别的错误码(用户引起的错误返回码,可以不用关注)
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/09/21 22:12:27
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Getter
@AllArgsConstructor
public enum UserErrorCode implements ErrorCode {
PARAM_ERROR(30001, "参数错误"),
DATA_NOT_EXIST(30002, "左翻右翻,数据竟然找不到了~"),
ALREADY_EXIST(30003, "数据已存在了呀~"),
REPEAT_SUBMIT(30004, "亲~您操作的太快了,请稍等下再操作~"),
NO_PERMISSION(30005, "对不起,您无法访问此资源哦~"),
DEVELOPING(30006, "系統正在紧急开发中,敬请期待~"),
LOGIN_STATE_INVALID(30007, "您还未登录或登录失效,请重新登录!"),
USER_STATUS_ERROR(30008, "用户状态异常"),
FORM_REPEAT_SUBMIT(30009, "请勿重复提交");
private final int code;
private final String msg;
private final String level;
UserErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
this.level = LEVEL_USER;
}
}

View File

@@ -0,0 +1,18 @@
package net.lab1024.sa.common.common.constant;
/**
* 请求消息头常量
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-15 20:46:27
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class RequestHeaderConst {
public static final String TOKEN = "x-access-token";
public static final String USER_AGENT = "user-agent";
}

View File

@@ -0,0 +1,44 @@
package net.lab1024.sa.common.common.constant;
/**
* 字符串常量
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-10-14 23:16:47
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class StringConst {
/**
* 全局通用分隔符
*/
public static final String SEPARATOR = ",";
/**
* 全局通用分隔符 下划线
*/
public static final String UNDERLINE = "_";
/**
* 全局通用 横杠
*/
public static final String HORIZONTAL = "-";
/**
* 全局通用分隔符
*/
public static final Character SEPARATOR_CHAR = ',';
/**
* 全局通用分隔符 斜杠
*/
public static final String SEPARATOR_SLASH = "/";
/**
* 空字符串
*/
public static final String EMPTY = "";
}

View File

@@ -0,0 +1,16 @@
package net.lab1024.sa.common.common.controller;
import net.lab1024.sa.common.constant.UrlPrefixConst;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 支撑类业务路由基类
*
* @Author 1024创新实验室: 胡克
* @Date 2022-04-24 20:43:55
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@RequestMapping(UrlPrefixConst.SUPPORT)
public class SupportBaseController {
}

View File

@@ -0,0 +1,15 @@
package net.lab1024.sa.common.common.domain;
import org.apache.ibatis.plugin.Interceptor;
/**
* 数据范围 插件
*
* @Author 1024创新实验室: 罗伊
* @Date 2021-11-15 17:20:04
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public abstract class DataScopePlugin implements Interceptor {
}

View File

@@ -0,0 +1,58 @@
package net.lab1024.sa.common.common.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
* 分页基础参数
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020/04/28 16:19
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Data
public class PageParam {
@ApiModelProperty(value = "页码(不能为空)", required = true, example = "1")
@NotNull(message = "分页参数不能为空")
private Integer pageNum;
@ApiModelProperty(value = "每页数量(不能为空)", required = true, example = "10")
@NotNull(message = "每页数量不能为空")
@Max(value = 200, message = "每页最大为200")
private Integer pageSize;
@ApiModelProperty("是否查询总条数")
protected Boolean searchCount;
@ApiModelProperty("排序字段集合")
@Size(max = 10, message = "排序字段最多10")
@Valid
private List<SortItem> sortItemList;
/**
* 排序DTO类
*/
@Data
public static class SortItem {
@ApiModelProperty("true正序|false倒序")
@NotNull(message = "排序规则不能为空")
private Boolean isAsc;
@ApiModelProperty(value = "排序字段")
@NotBlank(message = "排序字段不能为空")
@Length(max = 30, message = "排序字段最多30")
private String column;
}
}

View File

@@ -0,0 +1,53 @@
package net.lab1024.sa.common.common.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 分页返回对象
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020/04/28 16:19
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Data
public class PageResult<T> {
/**
* 当前页
*/
@ApiModelProperty(value = "当前页")
private Long pageNum;
/**
* 每页的数量
*/
@ApiModelProperty(value = "每页的数量")
private Long pageSize;
/**
* 总记录数
*/
@ApiModelProperty(value = "总记录数")
private Long total;
/**
* 总页数
*/
@ApiModelProperty(value = "总页数")
private Long pages;
/**
* 结果集
*/
@ApiModelProperty(value = "结果集")
private List<T> list;
@ApiModelProperty("是否为空")
private Boolean emptyFlag;
}

View File

@@ -0,0 +1,26 @@
package net.lab1024.sa.common.common.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求url返回对象
*
* @Author 1024创新实验室: 李善逸
* @Date 2021/9/1 20:15
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Data
public class RequestUrlVO {
@ApiModelProperty("注释说明")
private String comment;
@ApiModelProperty("controller.method")
private String name;
@ApiModelProperty("url")
private String url;
}

View File

@@ -0,0 +1,49 @@
package net.lab1024.sa.common.common.domain;
import net.lab1024.sa.common.common.enumeration.UserTypeEnum;
/**
* 请求用户
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-12-21 19:55:07
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public interface RequestUser {
/**
* 请求用户id
*
* @return
*/
Long getUserId();
/**
* 请求用户名称
*
* @return
*/
String getUserName();
/**
* 获取用户类型
*/
UserTypeEnum getUserType();
/**
* 获取请求的IP
*
* @return
*/
String getIp();
/**
* 获取请求 user-agent
*
* @return
*/
String getUserAgent();
}

View File

@@ -0,0 +1,101 @@
package net.lab1024.sa.common.common.domain;
import lombok.Data;
import net.lab1024.sa.common.common.code.ErrorCode;
import net.lab1024.sa.common.common.code.UserErrorCode;
import org.apache.commons.lang3.StringUtils;
/**
* 请求返回对象
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-10-31 21:06:11
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Data
public class ResponseDTO<T> {
public static final int OK_CODE = 0;
public static final String OK_MSG = "success";
private Integer code;
private String level;
private String msg;
private Boolean ok;
private T data;
public ResponseDTO(Integer code, String level, boolean ok, String msg, T data) {
this.code = code;
this.level = level;
this.ok = ok;
this.msg = msg;
this.data = data;
}
public ResponseDTO(ErrorCode errorCode, boolean ok, String msg, T data) {
this.code = errorCode.getCode();
this.level = errorCode.getLevel();
this.ok = ok;
if (StringUtils.isNotBlank(msg)) {
this.msg = msg;
} else {
this.msg = errorCode.getMsg();
}
this.data = data;
}
public static <T> ResponseDTO<T> ok() {
return new ResponseDTO<>(OK_CODE, null, true, OK_MSG, null);
}
public static <T> ResponseDTO<T> ok(T data) {
return new ResponseDTO<>(OK_CODE, null, true, OK_MSG, data);
}
public static <T> ResponseDTO<T> okMsg(String msg) {
return new ResponseDTO<>(OK_CODE, null, true, msg, null);
}
// -------------------------------------------- 最常用的 用户参数 错误码 --------------------------------------------
public static <T> ResponseDTO<T> userErrorParam() {
return new ResponseDTO<>(UserErrorCode.PARAM_ERROR, false, null, null);
}
public static <T> ResponseDTO<T> userErrorParam(String msg) {
return new ResponseDTO<>(UserErrorCode.PARAM_ERROR, false, msg, null);
}
// -------------------------------------------- 错误码 --------------------------------------------
public static <T> ResponseDTO<T> error(ErrorCode errorCode) {
return new ResponseDTO<>(errorCode, false, null, null);
}
public static <T> ResponseDTO<T> error(ErrorCode errorCode, boolean ok) {
return new ResponseDTO<>(errorCode, ok, null, null);
}
public static ResponseDTO error(ResponseDTO responseDTO) {
return new ResponseDTO<>(responseDTO.getCode(), responseDTO.getLevel(), responseDTO.getOk(), responseDTO.getMsg(), responseDTO.getData());
}
public static <T> ResponseDTO<T> error(ErrorCode errorCode, String msg) {
return new ResponseDTO<>(errorCode, false, msg, null);
}
public static <T> ResponseDTO<T> errorData(ErrorCode errorCode, T data) {
return new ResponseDTO<>(errorCode, false, null, data);
}
}

View File

@@ -0,0 +1,35 @@
package net.lab1024.sa.common.common.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.lab1024.sa.common.common.enumeration.SystemEnvironmentEnum;
/**
* 系统环境
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/8/13 21:06:11
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@AllArgsConstructor
@Getter
public class SystemEnvironment {
/**
* 是否位生产环境
*/
private boolean isProd;
/**
* 项目名称
*/
private String projectName;
/**
* 当前环境
*/
private SystemEnvironmentEnum currentEnvironment;
}

View File

@@ -0,0 +1,21 @@
package net.lab1024.sa.common.common.domain;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 校验数据是否为空的包装类
*
* @Author 1024创新实验室: 胡克
* @Date 2020/10/16 21:06:11
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Data
public class ValidateData<T> {
@NotNull(message = "数据不能为空哦")
private T data;
}

View File

@@ -0,0 +1,153 @@
package net.lab1024.sa.common.common.domain;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.*;
/**
* 校验集合是否为空的包装类
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020-02-03 17:37
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class ValidateList<E> implements List<E> {
@Valid
@NotEmpty(message = "数据长度不能为空哦")
private List<E> list;
public ValidateList() {
this.list = new ArrayList<>();
}
public ValidateList(List<E> list) {
this.list = list;
}
public List<E> getList() {
return list;
}
public void setList(List<E> list) {
this.list = list;
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Iterator<E> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public boolean add(E e) {
return list.add(e);
}
@Override
public boolean remove(Object o) {
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
return list.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
return list.addAll(index, c);
}
@Override
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
@Override
public void clear() {
list.clear();
}
@Override
public E get(int index) {
return list.get(index);
}
@Override
public E set(int index, E element) {
return list.set(index, element);
}
@Override
public void add(int index, E element) {
list.add(index, element);
}
@Override
public E remove(int index) {
return list.remove(index);
}
@Override
public int indexOf(Object o) {
return list.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
@Override
public ListIterator<E> listIterator() {
return list.listIterator();
}
@Override
public ListIterator<E> listIterator(int index) {
return list.listIterator(index);
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
return list.subList(fromIndex, toIndex);
}
}

View File

@@ -0,0 +1,99 @@
package net.lab1024.sa.common.common.enumeration;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONAware;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.CaseFormat;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Objects;
/**
* 枚举类接口
*
* @Author 1024创新实验室: 胡克
* @Date 2018-07-17 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public interface BaseEnum {
/**
* 获取枚举类的值
*
* @return
*/
Object getValue();
/**
* 获取枚举类的说明
*
* @return String
*/
String getDesc();
/**
* 比较参数是否与枚举类的value相同
*
* @param value
* @return boolean
*/
default boolean equalsValue(Object value) {
return Objects.equals(getValue(), value);
}
/**
* 比较枚举类是否相同
*
* @param baseEnum
* @return boolean
*/
default boolean equals(BaseEnum baseEnum) {
return Objects.equals(getValue(), baseEnum.getValue()) && Objects.equals(getDesc(), baseEnum.getDesc());
}
/**
* 返回枚举类的说明
*
* @param clazz 枚举类类对象
* @return
*/
static String getInfo(Class<? extends BaseEnum> clazz) {
BaseEnum[] enums = clazz.getEnumConstants();
LinkedHashMap<String, JSONObject> json = new LinkedHashMap<>(enums.length);
for (BaseEnum e : enums) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("value", new DeletedQuotationAware(e.getValue()));
jsonObject.put("desc", new DeletedQuotationAware(e.getDesc()));
json.put(e.toString(), jsonObject);
}
String enumJson = JSON.toJSONString(json, true);
enumJson = enumJson.replaceAll("\"", "");
enumJson = enumJson.replaceAll("\t", "&nbsp;&nbsp;");
enumJson = enumJson.replaceAll("\n", "<br>");
String prefix = " <br> export const <br> " + CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, clazz.getSimpleName() + " = <br> ");
return prefix + "" + enumJson + " <br>";
}
@Data
class DeletedQuotationAware implements JSONAware {
private String value;
public DeletedQuotationAware(Object value) {
if (value instanceof String) {
this.value = "'" + value + "'";
} else {
this.value = value.toString();
}
}
@Override
public String toJSONString() {
return value;
}
}
}

View File

@@ -0,0 +1,37 @@
package net.lab1024.sa.common.common.enumeration;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 性别枚举类
*
* @Author 1024创新实验室: 胡克
* @Date 2019/09/24 16:50
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@AllArgsConstructor
@Getter
public enum GenderEnum implements BaseEnum {
/**
* 0 未知
*/
UNKNOWN(0, "未知"),
/**
* 男 1 奇数为阳
*/
MAN(1, ""),
/**
* 女 2 偶数为阴
*/
WOMAN(2, "");
private final Integer value;
private final String desc;
}

View File

@@ -0,0 +1,50 @@
package net.lab1024.sa.common.common.enumeration;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 系统环境枚举类
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020-10-15 22:45:04
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@AllArgsConstructor
@Getter
public enum SystemEnvironmentEnum implements BaseEnum {
/**
* dev
*/
DEV(SystemEnvironmentNameConst.DEV, "开发环境"),
/**
* test
*/
TEST(SystemEnvironmentNameConst.TEST, "测试环境"),
/**
* pre
*/
PRE(SystemEnvironmentNameConst.PRE, "预发布环境"),
/**
* prod
*/
PROD(SystemEnvironmentNameConst.PROD, "生产环境");
private final String value;
private final String desc;
public static final class SystemEnvironmentNameConst {
public static final String DEV = "dev";
public static final String TEST = "test";
public static final String PRE = "pre";
public static final String PROD = "prod";
}
}

View File

@@ -0,0 +1,37 @@
package net.lab1024.sa.common.common.enumeration;
/**
* 用户类型
*
* @Author 1024创新实验室-主任:卓大
* @Date 2022/10/19 21:46:24
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net 2012-2022
*/
public enum UserTypeEnum implements BaseEnum {
/**
* 管理端 员工用户
*/
ADMIN_EMPLOYEE(1, "员工");
private Integer type;
private String desc;
UserTypeEnum(Integer type, String desc) {
this.type = type;
this.desc = desc;
}
@Override
public Integer getValue() {
return type;
}
@Override
public String getDesc() {
return desc;
}
}

View File

@@ -0,0 +1,185 @@
package net.lab1024.sa.common.common.excel;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.params.ExcelForEachParams;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import org.apache.poi.ss.usermodel.*;
/**
* excel样式
*
* @Author 1024创新实验室: 罗伊
* @Date 2020/9/25 19:52
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class ExcelStyle implements IExcelExportStyler {
private static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");
private static final short FONT_SIZE_TEN = 10;
private static final short FONT_SIZE_ELEVEN = 11;
private static final short FONT_SIZE_TWELVE = 12;
/**
* 大标题样式
*/
private CellStyle headerStyle;
/**
* 每列标题样式
*/
private CellStyle titleStyle;
/**
* 数据行样式
*/
private CellStyle styles;
public ExcelStyle(Workbook workbook) {
this.init(workbook);
}
/**
* 初始化样式
*
* @param workbook
*/
private void init(Workbook workbook) {
this.headerStyle = initHeaderStyle(workbook);
this.titleStyle = initTitleStyle(workbook);
this.styles = initStyles(workbook);
}
/**
* 大标题样式
*
* @param color
* @return
*/
@Override
public CellStyle getHeaderStyle(short color) {
return headerStyle;
}
/**
* 每列标题样式
*
* @param color
* @return
*/
@Override
public CellStyle getTitleStyle(short color) {
return titleStyle;
}
/**
* 数据行样式
*
* @param parity 可以用来表示奇偶行
* @param entity 数据内容
* @return 样式
*/
public CellStyle getStyles(boolean parity, ExcelExportEntity entity) {
return styles;
}
/**
* 获取样式方法
*
* @param dataRow 数据行
* @param obj 对象
* @param data 数据
*/
@Override
public CellStyle getStyles(Cell cell, int dataRow, ExcelExportEntity entity, Object obj, Object data) {
return getStyles(true, entity);
}
/**
* 模板使用的样式设置
*/
@Override
public CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {
return null;
}
/**
* 初始化--大标题样式
*
* @param workbook
* @return
*/
private CellStyle initHeaderStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_TWELVE, true));
return style;
}
/**
* 初始化--每列标题样式
*
* @param workbook
* @return
*/
private CellStyle initTitleStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_ELEVEN, false));
//背景色
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
/**
* 初始化--数据行样式
*
* @param workbook
* @return
*/
private CellStyle initStyles(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_TEN, false));
style.setDataFormat(STRING_FORMAT);
style.setAlignment(HorizontalAlignment.LEFT);
return style;
}
/**
* 基础样式
*
* @return
*/
private CellStyle getBaseCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
//下边框
style.setBorderBottom(BorderStyle.THIN);
//左边框
style.setBorderLeft(BorderStyle.THIN);
//上边框
style.setBorderTop(BorderStyle.THIN);
//右边框
style.setBorderRight(BorderStyle.THIN);
//水平居中
style.setAlignment(HorizontalAlignment.CENTER);
//上下居中
style.setVerticalAlignment(VerticalAlignment.CENTER);
//设置自动换行
style.setWrapText(true);
return style;
}
/**
* 字体样式
*
* @param size 字体大小
* @param isBold 是否加粗
* @return
*/
private Font getFont(Workbook workbook, short size, boolean isBold) {
Font font = workbook.createFont();
//字体样式
font.setFontName("宋体");
//是否加粗
font.setBold(isBold);
//字体大小
font.setFontHeightInPoints(size);
return font;
}
}

View File

@@ -0,0 +1,38 @@
package net.lab1024.sa.common.common.exception;
import net.lab1024.sa.common.common.code.ErrorCode;
/**
* 业务逻辑异常,全局异常拦截后统一返回ResponseCodeConst.SYSTEM_ERROR
*
* @Author 1024创新实验室: 罗伊
* @Date 2020/8/25 21:57
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class BusinessException extends RuntimeException {
public BusinessException() {
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMsg());
}
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
public BusinessException(Throwable cause) {
super(cause);
}
public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -0,0 +1,148 @@
package net.lab1024.sa.common.common.interceptor;
import com.alibaba.fastjson.JSONObject;
import net.lab1024.sa.common.common.annoation.NoNeedLogin;
import net.lab1024.sa.common.common.code.UserErrorCode;
import net.lab1024.sa.common.common.constant.RequestHeaderConst;
import net.lab1024.sa.common.common.domain.RequestUser;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.common.util.SmartRequestUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.function.Function;
/**
* 抽象拦截器
*
* @Author 1024创新实验室: 罗伊
* @Date 2021-10-09 20:56:14
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public abstract class AbstractInterceptor implements HandlerInterceptor {
@Autowired
private List<String> ignoreUrlList;
/**
* Token获取用户信息
*
* @return
*/
protected abstract Function<String, RequestUser> userFunction();
/**
* 拦截路径
*
* @return
*/
public abstract String[] pathPatterns();
/**
* 忽略的url集合
*
* @return
*/
protected List<String> getIgnoreUrlList() {
return ignoreUrlList;
}
/**
* 拦截服务器端响应处理ajax请求返回结果
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// OPTIONS请求直接return
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
response.setStatus(HttpStatus.NO_CONTENT.value());
return false;
}
boolean isHandler = handler instanceof HandlerMethod;
if (!isHandler) {
return true;
}
//放行的Uri前缀
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String target = uri.replaceFirst(contextPath, "");
if (this.contain(this.getIgnoreUrlList(), target)) {
return true;
}
//不需要登录
NoNeedLogin noNeedLogin = ((HandlerMethod) handler).getMethodAnnotation(NoNeedLogin.class);
// 检查是否包含 token
String xRequestToken = request.getParameter(RequestHeaderConst.TOKEN);
String xHeaderToken = request.getHeader(RequestHeaderConst.TOKEN);
String xAccessToken = StringUtils.isNotBlank(xRequestToken) ? xRequestToken : xHeaderToken;
// 包含token 则获取用户信息 并保存
if (StringUtils.isNotBlank(xAccessToken)) {
RequestUser requestUser = userFunction().apply(xAccessToken);
if (requestUser != null) {
SmartRequestUtil.setRequestUser(requestUser);
}
// 有token 无需登录
if (null != noNeedLogin) {
return true;
}
}
// 无token 无需登录
if (null != noNeedLogin) {
return true;
}
if (StringUtils.isBlank(xAccessToken)) {
this.outputResult(response, ResponseDTO.error(UserErrorCode.LOGIN_STATE_INVALID));
return false;
}
return true;
}
public Boolean contain(List<String> ignores, String uri) {
if (CollectionUtils.isEmpty(ignores)) {
return false;
}
for (String ignoreUrl : ignores) {
if (uri.startsWith(ignoreUrl)) {
return true;
}
}
return false;
}
/**
* 错误输出
*
* @param response
* @param responseDTO
* @throws IOException
*/
private void outputResult(HttpServletResponse response, ResponseDTO responseDTO) throws IOException {
String msg = JSONObject.toJSONString(responseDTO);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(msg);
response.flushBuffer();
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
SmartRequestUtil.remove();
}
}

View File

@@ -0,0 +1,52 @@
package net.lab1024.sa.common.common.json.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.module.support.dict.domain.vo.DictValueVO;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 字典反序列化
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-08-12 22:17:53
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
public class DictValueVoDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
List<DictValueVO> list = new ArrayList<>();
ObjectCodec objectCodec = jsonParser.getCodec();
JsonNode listOrObjectNode = objectCodec.readTree(jsonParser);
String deserialize = "";
try {
if (listOrObjectNode.isArray()) {
for (JsonNode node : listOrObjectNode) {
list.add(objectCodec.treeToValue(node, DictValueVO.class));
}
} else {
list.add(objectCodec.treeToValue(listOrObjectNode, DictValueVO.class));
}
deserialize = list.stream().map(DictValueVO::getValueCode).collect(Collectors.joining(","));
} catch (Exception e) {
log.error(e.getMessage(), e);
deserialize = listOrObjectNode.asText();
}
return deserialize;
}
}

View File

@@ -0,0 +1,53 @@
package net.lab1024.sa.common.common.json.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.module.support.file.domain.vo.FileVO;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 文件key反序列化<br>
* 由于前端接收到的是序列化过的字段, 这边入库需要进行反序列化操作比较方便处理
*
* @Author 1024创新实验室: 胡克
* @Date 2022-11-24 17:15:23
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
public class FileKeyVoDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
List<FileVO> list = new ArrayList<>();
ObjectCodec objectCodec = jsonParser.getCodec();
JsonNode listOrObjectNode = objectCodec.readTree(jsonParser);
String deserialize = "";
try {
if (listOrObjectNode.isArray()) {
for (JsonNode node : listOrObjectNode) {
list.add(objectCodec.treeToValue(node, FileVO.class));
}
} else {
list.add(objectCodec.treeToValue(listOrObjectNode, FileVO.class));
}
deserialize = list.stream().map(FileVO::getFileKey).collect(Collectors.joining(","));
} catch (Exception e) {
log.error(e.getMessage(), e);
deserialize = listOrObjectNode.asText();
}
return deserialize;
}
}

View File

@@ -0,0 +1,30 @@
package net.lab1024.sa.common.common.json.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
/**
* Long类型序列化
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020-06-02 22:55:07
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class LongJsonDeserializer extends JsonDeserializer<Long> {
@Override
public Long deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String value = jsonParser.getText();
try {
return value == null ? null : Long.parseLong(value);
} catch (NumberFormatException e) {
return null;
}
}
}

View File

@@ -0,0 +1,29 @@
package net.lab1024.sa.common.common.json.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.math.BigDecimal;
/**
* 数字序列化
*
* @Author 1024创新实验室: 罗伊
* @Date 2020/8/20 21:04
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class BigDecimalNullZeroSerializer extends JsonSerializer<BigDecimal> {
@Override
public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (value == null) {
jsonGenerator.writeNumber(BigDecimal.ZERO);
return;
}
jsonGenerator.writeNumber(value);
}
}

View File

@@ -0,0 +1,52 @@
package net.lab1024.sa.common.common.json.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.google.common.collect.Lists;
import net.lab1024.sa.common.module.support.dict.domain.vo.DictValueVO;
import net.lab1024.sa.common.module.support.dict.service.DictCacheService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* 字典序列化
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-08-12 22:17:53
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class DictValueVoSerializer extends JsonSerializer<String> {
@Autowired
private DictCacheService dictCacheService;
@Override
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (StringUtils.isEmpty(value)) {
jsonGenerator.writeObject(Lists.newArrayList());
return;
}
String[] valueCodeArray = value.split(",");
List<String> valueCodeList = Arrays.asList(valueCodeArray);
List<DictValueVO> dictValueVOList = Lists.newArrayList();
valueCodeList.forEach(e->{
if(StringUtils.isNotBlank(e)){
DictValueVO dictValueVO = dictCacheService.selectValueByValueCode(value);
if(dictValueVO != null){
dictValueVOList.add(dictValueVO);
}
}
});
jsonGenerator.writeObject(dictValueVOList);
}
}

View File

@@ -0,0 +1,45 @@
package net.lab1024.sa.common.common.json.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.module.support.file.service.FileService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
/**
* 文件key进行序列化对象
*
* @Author 1024创新实验室: 罗伊
* @Date 2020/8/15 22:06
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class FileKeySerializer extends JsonSerializer<String> {
@Autowired
private FileService fileService;
@Override
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (StringUtils.isEmpty(value)) {
jsonGenerator.writeString(value);
return;
}
if (fileService == null) {
jsonGenerator.writeString(value);
return;
}
ResponseDTO<String> responseDTO = fileService.getFileUrl(value);
if (responseDTO.getOk()) {
jsonGenerator.writeString(responseDTO.getData());
return;
}
jsonGenerator.writeString(value);
}
}

View File

@@ -0,0 +1,46 @@
package net.lab1024.sa.common.common.json.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.google.common.collect.Lists;
import net.lab1024.sa.common.module.support.file.domain.vo.FileVO;
import net.lab1024.sa.common.module.support.file.service.FileService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* 文件key进行序列化对象
*
* @Author 1024创新实验室: 罗伊
* @Date 2020/8/15 22:06
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class FileKeyVoSerializer extends JsonSerializer<String> {
@Autowired
private FileService fileService;
@Override
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (StringUtils.isEmpty(value)) {
jsonGenerator.writeObject(Lists.newArrayList());
return;
}
if(fileService == null){
jsonGenerator.writeString(value);
return;
}
String[] fileKeyArray = value.split(",");
List<String> fileKeyList = Arrays.asList(fileKeyArray);
List<FileVO> fileKeyVOList = fileService.getFileList(fileKeyList);
jsonGenerator.writeObject(fileKeyVOList);
}
}

View File

@@ -0,0 +1,28 @@
package net.lab1024.sa.common.common.json.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**
* Long类型序列化
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020-06-02 22:55:07
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class LongJsonSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
String text = (value == null ? null : String.valueOf(value));
if (text != null) {
jsonGenerator.writeString(text);
}
}
}

View File

@@ -0,0 +1,91 @@
package net.lab1024.sa.common.common.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Spring Security
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/8/3 17:50
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public abstract class AbstractSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CorsFilter corsFilter;
@Autowired
private List<String> noNeedLoginUrlList;
@Autowired
private List<String> ignoreUrlList;
/**
* Token获取用户信息
*
* @return
*/
protected abstract BiFunction<String, HttpServletRequest, UserDetails> userFunction();
/**
* 需要认证的url集合
*
* @return
*/
protected abstract String[] getAuthenticatedUrlPatterns();
/**
* 不需要登录的url集合
*
* @return
*/
protected String[] getNoNeedLoginUrl() {
return noNeedLoginUrlList.toArray(new String[noNeedLoginUrlList.size()]);
}
/**
* 忽略的url集合
*
* @return
*/
protected String[] getIgnoreUrlList() {
return ignoreUrlList.toArray(new String[ignoreUrlList.size()]);
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// CSRF禁用因为不使用session
.csrf().disable()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(new SecurityAuthenticationFailHandler()).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
//忽略的url
.antMatchers(this.getIgnoreUrlList()).permitAll()
// 不需要登陆的url
.antMatchers(this.getNoNeedLoginUrl()).permitAll()
//需要校验权限的url
.antMatchers(getAuthenticatedUrlPatterns()).authenticated();
// token filter 进行校验
httpSecurity.addFilterBefore(new SecurityTokenFilter(this.userFunction()), UsernamePasswordAuthenticationFilter.class);
httpSecurity.addFilterBefore(corsFilter, SecurityTokenFilter.class);
}
}

View File

@@ -0,0 +1,43 @@
package net.lab1024.sa.common.common.security;
import com.alibaba.fastjson.JSONObject;
import net.lab1024.sa.common.common.code.ErrorCode;
import net.lab1024.sa.common.common.code.UserErrorCode;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登录认证失败处理
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-08-26 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SecurityAuthenticationFailHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {
this.outputResult(response, UserErrorCode.LOGIN_STATE_INVALID);
}
/**
* 输出
*
* @param response
* @param errorCode
* @throws IOException
*/
private void outputResult(HttpServletResponse response, ErrorCode errorCode) throws IOException {
String msg = JSONObject.toJSONString(ResponseDTO.error(errorCode));
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(msg);
response.flushBuffer();
}
}

View File

@@ -0,0 +1,66 @@
package net.lab1024.sa.common.common.security;
import net.lab1024.sa.common.common.annoation.SaAuth;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.prepost.PreInvocationAttribute;
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
import org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
/**
* 此类用于默认给所有接口添加权限 @saAuth.checkPermission('%s')
* %s 为类名.方法名
* 和使用@PreAuthorize("@saAuth.checkPermission('%s')") 效果一致
* 避免所有接口都添加一遍 减轻工作量
*
* @Author 1024创新实验室: 罗伊
* @Date 2021-08-30 23:08
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SecurityMethodSource extends PrePostAnnotationSecurityMetadataSource {
private static String EXPRESSION_FORMAT = "@%s.checkPermission('%s')";
private final PrePostInvocationAttributeFactory attributeFactory;
private String beanName;
public SecurityMethodSource(PrePostInvocationAttributeFactory attributeFactory, String beanName) {
super(attributeFactory);
this.attributeFactory = attributeFactory;
this.beanName = beanName;
}
@Override
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
//如果不存在SaAuth采用security认证模式
SaAuth saAuth = method.getAnnotation(SaAuth.class);
if (saAuth == null) {
return super.getAttributes(method, targetClass);
}
//存在添加以URL为权限字符串的校验模式
ArrayList<ConfigAttribute> configAttributes = new ArrayList(1);
String classFullName = targetClass.getName();
String methodName = method.getName();
String[] classNameArray = StringUtils.split(classFullName, "\\.");
String controllerName = classNameArray[classNameArray.length - 1];
String privilegeName = controllerName + "." + methodName;
String preAuthorizeAttribute = String.format(EXPRESSION_FORMAT, beanName, privilegeName);
PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(null, null, preAuthorizeAttribute);
if (pre != null) {
configAttributes.add(pre);
}
return configAttributes;
}
}

View File

@@ -0,0 +1,74 @@
package net.lab1024.sa.common.common.security;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 校验权限
*
* @Author 1024创新实验室: 罗伊
* @Date 2022/5/12 21:50
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public abstract class SecurityPermissionCheckService {
/**
* 校验是否有权限
*
* @param permission
* @return
*/
public boolean checkPermission(String permission) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
return checkPermission(authentication, permission);
}
/**
* 校验是否有权限
*
* @param authentication
* @param permission
* @return
*/
public abstract boolean checkPermission(Authentication authentication, String permission);
/**
* 判断
*
* @param userDetails
* @param permissionStr
* @return
*/
protected boolean permissionJudge(UserDetails userDetails, String permissionStr) {
if (CollectionUtils.isEmpty(userDetails.getAuthorities())) {
return false;
}
if (StringUtils.isBlank(permissionStr)) {
return false;
}
String[] permissionArray = permissionStr.split(",");
for (String permission : permissionArray) {
if(userDetails.getAuthorities().contains(new SimpleGrantedAuthority(permission))){
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,64 @@
package net.lab1024.sa.common.common.security;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.constant.RequestHeaderConst;
import net.lab1024.sa.common.common.domain.RequestUser;
import net.lab1024.sa.common.common.util.SmartRequestUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* 注意此处不能 加入@Component否则对应ignoreUrl的相关请求 将会进入此Filter并会覆盖CorsFilter
*
* @Author 1024创新实验室: 罗伊
* @Date 2022/5/12 21:50
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
public class SecurityTokenFilter extends OncePerRequestFilter {
private BiFunction<String,HttpServletRequest, UserDetails> userFunction;
public SecurityTokenFilter(BiFunction<String,HttpServletRequest, UserDetails> userFunction) {
this.userFunction = userFunction;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
//需要做token校验, 消息头的token优先于请求query参数的token
String xHeaderToken = request.getHeader(RequestHeaderConst.TOKEN);
String xRequestToken = request.getParameter(RequestHeaderConst.TOKEN);
String xAccessToken = null != xHeaderToken ? xHeaderToken : xRequestToken;
if (StringUtils.isBlank(xAccessToken)) {
chain.doFilter(request, response);
return;
}
//清理spring security
SecurityContextHolder.clearContext();
UserDetails loginUserDetail = userFunction.apply(xAccessToken,request);
if (null != loginUserDetail) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUserDetail, null, loginUserDetail.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
SmartRequestUtil.setRequestUser((RequestUser) loginUserDetail);
}
// 若未给予spring security上下文用户授权 则会授权失败 进入AuthenticationEntryPointImpl
chain.doFilter(request, response);
}
}

View File

@@ -0,0 +1,40 @@
package net.lab1024.sa.common.common.swagger;
import net.lab1024.sa.common.common.enumeration.BaseEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 枚举类字段属性的 自定义 swagger 注解
*
* @Author 1024创新实验室: 胡克
* @Date 2019/05/16 23:18
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiModelPropertyEnum {
/**
* 枚举类对象
*
* @return
*/
Class<? extends BaseEnum> value();
String example() default "";
boolean hidden() default false;
boolean required() default true;
String dataType() default "";
String desc() default "";
}

View File

@@ -0,0 +1,91 @@
package net.lab1024.sa.common.common.swagger;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import io.swagger.models.Operation;
import io.swagger.models.Path;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.service.ApiDescription;
import springfox.documentation.service.ApiListing;
import springfox.documentation.swagger2.mappers.ModelMapper;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import static springfox.documentation.builders.BuilderDefaults.nullToEmptyList;
/**
* 修改 api 顺序
*
* @Author 1024创新实验室: 胡克
* @Date 2021/8/11 22:05
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@ConditionalOnBean(ModelMapper.class)
@Component
@Primary
public class Swagger2MapperImplExtension extends ServiceModelToSwagger2MapperImpl {
@Override
protected Map<String, Path> mapApiListings(Multimap<String, ApiListing> apiListings) {
Map<String, Path> paths = new LinkedHashMap<>();
Multimap<String, ApiListing> apiListingMap = LinkedListMultimap.create();
Iterator iter = apiListings.entries().iterator();
while (iter.hasNext()) {
Map.Entry<String, ApiListing> entry = (Map.Entry<String, ApiListing>) iter.next();
ApiListing apis = entry.getValue();
List<ApiDescription> apiList = apis.getApis();
apiList.sort((left, right) -> {
int position1 = left.getOperations().get(0).getPosition();
int position2 = right.getOperations().get(0).getPosition();
if (position1 == position2) {
return String.CASE_INSENSITIVE_ORDER.compare(left.getPath(), right.getPath());
}
return Integer.compare(position1, position2);
});
try {
// 因ApiListing的属性都是final故需要通过反射来修改值
modify(apis, "apis", apiList);
} catch (Exception e) {
e.printStackTrace();
}
apiListingMap.put(entry.getKey(), apis);
}
for (ApiListing each : apiListingMap.values()) {
for (ApiDescription api : each.getApis()) {
paths.put(api.getPath(), mapOperations(api, Optional.ofNullable(paths.get(api.getPath()))));
}
}
return paths;
}
private Path mapOperations(ApiDescription api, Optional<Path> existingPath) {
Path path = existingPath.orElse(new Path());
for (springfox.documentation.service.Operation each : nullToEmptyList(api.getOperations())) {
Operation operation = mapOperation(each);
path.set(each.getMethod().toString().toLowerCase(), operation);
}
return path;
}
public static void modify(Object object, String fieldName, Object newFieldValue) throws Exception {
Field field = object.getClass().getDeclaredField(fieldName);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(object, newFieldValue);
}
}

View File

@@ -0,0 +1,77 @@
package net.lab1024.sa.common.common.swagger;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import net.lab1024.sa.common.common.enumeration.BaseEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import java.lang.reflect.AnnotatedElement;
import static springfox.documentation.schema.Annotations.findPropertyAnnotation;
/**
* swagger 用于说明枚举类字段说明
* * SWAGGER_PLUGIN_ORDER+1 是将此配置放在原来的后面执行
*
* @Author 1024创新实验室: 胡克
* @Date 2019/8/11 15:36:56
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
public class SwaggerApiModelPropertyEnumPlugin implements ModelPropertyBuilderPlugin {
@Override
public void apply(ModelPropertyContext context) {
Optional<ApiModelPropertyEnum> annotation = Optional.absent();
if (context.getAnnotatedElement().isPresent()) {
annotation = annotation.or(findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
}
if (context.getBeanPropertyDefinition().isPresent()) {
annotation = annotation.or(findPropertyAnnotation(context.getBeanPropertyDefinition().get(), ApiModelPropertyEnum.class));
}
if (annotation.isPresent()) {
ApiModelPropertyEnum anEnum = annotation.get();
String enumInfo = BaseEnum.getInfo(anEnum.value());
context.getBuilder()
.required(annotation.transform(toIsRequired()).or(false))
.description(anEnum.desc() + ":" + enumInfo)
.example(annotation.transform(toExample()).orNull())
.isHidden(anEnum.hidden());
}
}
@Override
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
static Function<ApiModelPropertyEnum, Boolean> toIsRequired() {
return annotation -> annotation.required();
}
public static Optional<ApiModelPropertyEnum> findApiModePropertyAnnotation(AnnotatedElement annotated) {
return Optional.fromNullable(AnnotationUtils.getAnnotation(annotated, ApiModelPropertyEnum.class));
}
static Function<ApiModelPropertyEnum, String> toExample() {
return annotation -> {
String example = annotation.example();
if (StringUtils.isBlank(example)) {
return "";
}
return example;
};
}
}

View File

@@ -0,0 +1,94 @@
package net.lab1024.sa.common.common.util;
import org.springframework.beans.BeanUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* bean相关工具类
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2018-01-15 10:48:23
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SmartBeanUtil {
/**
* 验证器
*/
private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
/**
* 复制bean的属性
*
* @param source 源 要复制的对象
* @param target 目标 复制到此对象
*/
public static void copyProperties(Object source, Object target) {
BeanUtils.copyProperties(source, target);
}
/**
* 复制对象
*
* @param source 源 要复制的对象
* @param target 目标 复制到此对象
* @param <T>
* @return
*/
public static <T> T copy(Object source, Class<T> target) {
if (source == null || target == null) {
return null;
}
try {
T newInstance = target.newInstance();
BeanUtils.copyProperties(source, newInstance);
return newInstance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 复制list
*
* @param source
* @param target
* @param <T>
* @param <K>
* @return
*/
public static <T, K> List<K> copyList(List<T> source, Class<K> target) {
if (null == source || source.isEmpty()) {
return Collections.emptyList();
}
return source.stream().map(e -> copy(e, target)).collect(Collectors.toList());
}
/**
* 手动验证对象 Model的属性
* 需要配合 hibernate-validator 校验注解
*
* @param t
* @return String 返回null代表验证通过否则返回错误的信息
*/
public static <T> String verify(T t) {
// 获取验证结果
Set<ConstraintViolation<T>> validate = VALIDATOR.validate(t);
if (validate.isEmpty()) {
// 验证通过
return null;
}
// 返回错误信息
List<String> messageList = validate.stream().map(ConstraintViolation::getMessage).collect(Collectors.toList());
return messageList.toString();
}
}

View File

@@ -0,0 +1,247 @@
package net.lab1024.sa.common.common.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* BigDecimal 工具类
*
* @Author 1024创新实验室: 胡克
* @Date 2018/01/17 13:54
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SmartBigDecimalUtil {
/**
* 金额 保留小数点 2
*/
public static final int AMOUNT_DECIMAL_POINT = 2;
public static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
/**
* 金额相关计算方法:四舍五入 保留2位小数点
*/
public static class Amount {
public static BigDecimal add(BigDecimal num1, BigDecimal num2) {
return setScale(num1.add(num2), AMOUNT_DECIMAL_POINT);
}
public static BigDecimal multiply(BigDecimal num1, BigDecimal num2) {
return setScale(num1.multiply(num2), AMOUNT_DECIMAL_POINT);
}
public static BigDecimal subtract(BigDecimal num1, BigDecimal num2) {
return setScale(num1.subtract(num2), AMOUNT_DECIMAL_POINT);
}
public static BigDecimal divide(BigDecimal num1, BigDecimal num2) {
return setScale(num1.divide(num2, RoundingMode.HALF_UP), AMOUNT_DECIMAL_POINT);
}
}
/**
* BigDecimal 加法 num1 + num2
* 未做非空校验
*
* @param num1
* @param num2
* @param point 请使用BigDecimalUtils.PRICE_DECIMAL_POINT | BigDecimalUtils.WEIGHT_DECIMAL_POINT
* @return BigDecimal
*/
public static BigDecimal add(BigDecimal num1, BigDecimal num2, int point) {
return setScale(num1.add(num2), point);
}
/**
* 累加
*
* @param point
* @param array
* @return
*/
public static BigDecimal add(int point, BigDecimal... array) {
BigDecimal res = new BigDecimal(0);
for (BigDecimal item : array) {
if (item == null) {
res = res.add(BigDecimal.ZERO);
} else {
res = res.add(item);
}
}
return setScale(res, point);
}
/**
* BigDecimal 乘法 num1 x num2
* 未做非空校验
*
* @param num1
* @param num2
* @param point 请使用BigDecimalUtils.PRICE_DECIMAL_POINT | BigDecimalUtils.WEIGHT_DECIMAL_POINT
* @return BigDecimal
*/
public static BigDecimal multiply(BigDecimal num1, BigDecimal num2, int point) {
return setScale(num1.multiply(num2), point);
}
/**
* BigDecimal 减法 num1 - num2
* 未做非空校验
*
* @param num1
* @param num2
* @param point 请使用BigDecimalUtils.PRICE_DECIMAL_POINT | BigDecimalUtils.WEIGHT_DECIMAL_POINT
* @return BigDecimal
*/
public static BigDecimal subtract(BigDecimal num1, BigDecimal num2, int point) {
return setScale(num1.subtract(num2), point);
}
/**
* BigDecimal 除法 num1/num2
* 未做非空校验
*
* @param num1
* @param num2
* @param point 请使用BigDecimalUtils.PRICE_DECIMAL_POINT | BigDecimalUtils.WEIGHT_DECIMAL_POINT
* @return BigDecimal
*/
public static BigDecimal divide(BigDecimal num1, BigDecimal num2, int point) {
return num1.divide(num2, point, RoundingMode.HALF_UP);
}
/**
* 设置小数点类型为 四舍五入
*
* @param num
* @param point
* @return BigDecimal
*/
public static BigDecimal setScale(BigDecimal num, int point) {
return num.setScale(point, RoundingMode.HALF_UP);
}
/**
* 比较 num1 是否大于 num2
*
* @param num1
* @param num2
* @return boolean
*/
public static boolean isGreaterThan(BigDecimal num1, BigDecimal num2) {
return num1.compareTo(num2) > 0;
}
/**
* 比较 num1 是否大于等于 num2
*
* @param num1
* @param num2
* @return boolean
*/
public static boolean isGreaterOrEqual(BigDecimal num1, BigDecimal num2) {
return isGreaterThan(num1, num2) || equals(num1, num2);
}
/**
* 比较 num1 是否小于 num2
*
* @param num1
* @param num2
* @return boolean
*/
public static boolean isLessThan(BigDecimal num1, BigDecimal num2) {
return num1.compareTo(num2) < 0;
}
/**
* 比较 num1 是否小于等于 num2
*
* @param num1
* @param num2
* @return boolean
*/
public static boolean isLessOrEqual(BigDecimal num1, BigDecimal num2) {
return isLessThan(num1, num2) || equals(num1, num2);
}
/**
* 比较 num1 是否等于 num2
*
* @param num1
* @param num2
* @return
*/
public static boolean equals(BigDecimal num1, BigDecimal num2) {
return num1.compareTo(num2) == 0;
}
/**
* 计算 num1 / num2 的百分比
*
* @param num1
* @param num2
* @param point 保留几位小数
* @return String
*/
public static BigDecimal percent(Integer num1, Integer num2, int point) {
if (num1 == null || num2 == null) {
return BigDecimal.ZERO;
}
if (num2.equals(0)) {
return BigDecimal.ZERO;
}
return percent(new BigDecimal(num1), new BigDecimal(num2), point);
}
/**
* 计算 num1 / num2 的百分比
*
* @param num1
* @param num2
* @param point 保留几位小数
* @return String
*/
public static BigDecimal percent(BigDecimal num1, BigDecimal num2, int point) {
if (num1 == null || num2 == null) {
return BigDecimal.ZERO;
}
if (equals(BigDecimal.ZERO, num2)) {
return BigDecimal.ZERO;
}
BigDecimal percent = num1.divide(num2, point + 2, RoundingMode.HALF_UP);
return percent.multiply(ONE_HUNDRED).setScale(point);
}
/**
* 比较 num1num2 返回最大的值
*
* @param num1
* @param num2
* @return BigDecimal
*/
public static BigDecimal max(BigDecimal num1, BigDecimal num2) {
return num1.compareTo(num2) > 0 ? num1 : num2;
}
/**
* 比较 num1num2 返回最小的值
*
* @param num1
* @param num2
* @return BigDecimal
*/
public static BigDecimal min(BigDecimal num1, BigDecimal num2) {
return num1.compareTo(num2) < 0 ? num1 : num2;
}
public static void main(String[] args) {
System.out.println(percent(new BigDecimal("3"), new BigDecimal("11"), 2));
System.out.println(percent(new BigDecimal("8"), new BigDecimal("11"), 2));
}
}

View File

@@ -0,0 +1,114 @@
package net.lab1024.sa.common.common.util;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* excel 工具类
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2018/01/17 13:54
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
public class SmartEasyPoiExcelUtil {
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass,
String fileName, boolean isCreateHeader, HttpServletResponse response) throws IOException {
ExportParams exportParams = new ExportParams(title, sheetName);
exportParams.setCreateHeadRows(isCreateHeader);
defaultExport(list, pojoClass, fileName, response, exportParams);
}
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName,
HttpServletResponse response) throws IOException {
defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName));
}
public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
defaultExport(list, fileName, response);
}
private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName,
HttpServletResponse response, ExportParams exportParams) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
downloadExcel(fileName, workbook, response);
}
public static void downloadExcel(String fileName, Workbook workbook, HttpServletResponse response) {
try {
fileName = URLEncoder.encode(fileName, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error("", e);
}
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xls");
try {
workbook.write(response.getOutputStream());
workbook.close();
} catch (IOException e) {
log.error("", e);
}
}
private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
downloadExcel(fileName, workbook, response);
}
public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
if (StringUtils.isBlank(filePath)) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
List<T> list = null;
try {
list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
} catch (NoSuchElementException e) {
//throw new NormalException("模板不能为空");
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
if (file == null) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
List<T> list = null;
try {
list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);
} catch (NoSuchElementException e) {
// throw new NormalException("excel文件不能为空");
} catch (Exception e) {
//throw new NormalException(e.getMessage());
System.out.println(e.getMessage());
}
return list;
}
}

View File

@@ -0,0 +1,165 @@
package net.lab1024.sa.common.common.util;
import net.lab1024.sa.common.common.enumeration.BaseEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 枚举工具类
*
* @Author 1024创新实验室: 胡克
* @Date 2017/10/10 18:17
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SmartEnumUtil {
/**
* 校验参数与枚举类比较是否合法
*
* @param value 参数
* @param enumClass 枚举类必须实现BaseEnum接口
* @return boolean
* @Author 胡克
*/
public static boolean checkEnum(Object value, Class<? extends BaseEnum> enumClass) {
if (null == value) {
return false;
}
return Stream.of(enumClass.getEnumConstants()).anyMatch(e -> e.equalsValue(value));
}
/**
* 创建一个具有唯一array值的数组每个值不包含在其他给定的数组中。
*
* @param enumClass
* @param exclude
* @param <T>
* @return
*/
public static <T extends BaseEnum> List<Object> differenceValueList(Class<? extends BaseEnum> enumClass, T... exclude) {
HashSet<Object> valueSet = new HashSet<>();
if (exclude != null) {
valueSet.addAll(Stream.of(exclude).map(BaseEnum::getValue).collect(Collectors.toSet()));
}
return Stream.of(enumClass.getEnumConstants())
.filter(e -> !valueSet.contains(e.getValue()))
.map(BaseEnum::getValue)
.collect(Collectors.toList());
}
/**
* 获取枚举类的说明 value : info 的形式
*
* @param enumClass
* @return String
*/
public static String getEnumDesc(Class<? extends BaseEnum> enumClass) {
BaseEnum[] enums = enumClass.getEnumConstants();
// value : info 的形式
StringBuilder sb = new StringBuilder();
for (BaseEnum baseEnum : enums) {
sb.append(baseEnum.getValue()).append("").append(baseEnum.getDesc()).append("");
}
return sb.toString();
}
/**
* 获取与参数相匹配的枚举类实例的 说明
*
* @param value 参数
* @param enumClass 枚举类必须实现BaseEnum接口
* @return String 如无匹配枚举则返回null
*/
public static String getEnumDescByValue(Object value, Class<? extends BaseEnum> enumClass) {
if (null == value) {
return null;
}
return Stream.of(enumClass.getEnumConstants())
.filter(e -> e.equalsValue(value))
.findFirst()
.map(BaseEnum::getDesc)
.orElse(null);
}
public static <T> String getEnumDescByValueList(Collection<T> values, Class<? extends BaseEnum> enumClass) {
if (CollectionUtils.isEmpty(values)) {
return "";
}
return Stream.of(enumClass.getEnumConstants()).filter(e -> values.contains(e.getValue())).map(BaseEnum::getDesc).collect(Collectors.joining(","));
}
/**
* 根据参数获取枚举类的实例
*
* @param value 参数
* @param enumClass 枚举类必须实现BaseEnum接口
* @return BaseEnum 无匹配值返回null
* @Author 胡克
*/
public static <T extends BaseEnum> T getEnumByValue(Object value, Class<T> enumClass) {
if (null == value) {
return null;
}
return Stream.of(enumClass.getEnumConstants())
.filter(e -> e.equalsValue(value))
.findFirst()
.orElse(null);
}
/**
* 根据实例描述与获取枚举类的实例
*
* @param desc 参数描述
* @param enumClass 枚举类必须实现BaseEnum接口
* @return BaseEnum 无匹配值返回null
* @Author 胡克
*/
public static <T extends BaseEnum> T getEnumByDesc(String desc, Class<T> enumClass) {
return Stream.of(enumClass.getEnumConstants())
.filter(e -> Objects.equals(e.getDesc(), desc))
.findFirst()
.orElse(null);
}
public static <T extends BaseEnum> T getEnumByName(String name, Class<T> enumClass) {
return Stream.of(enumClass.getEnumConstants())
.filter(e -> StringUtils.equalsIgnoreCase(e.toString(), name))
.findFirst()
.orElse(null);
}
/**
* 根据lambda getter/setter 注入
*
* @param list
* @param getter
* @param setter
* @param enumClass
* @param <T>
*/
public static <T> void inject(List<T> list, Function<T, Integer> getter, BiConsumer<T, String> setter, Class<? extends BaseEnum> enumClass) {
if (list == null || list.isEmpty()) {
return;
}
for (T t : list) {
Integer enumValue = getter.apply(t);
if (enumValue != null) {
setter.accept(t, getEnumDescByValue(enumValue, enumClass));
}
}
}
}

View File

@@ -0,0 +1,111 @@
package net.lab1024.sa.common.common.util;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import net.lab1024.sa.common.common.domain.PageParam;
import net.lab1024.sa.common.common.domain.PageResult;
import org.apache.commons.collections4.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
/**
* 分页工具类
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020-04-23 20:51:40
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SmartPageUtil {
/**
* 转换为查询参数
*
* @param baseDTO
* @return
*/
public static Page<?> convert2PageQuery(PageParam baseDTO) {
Page<?> page = new Page<>(baseDTO.getPageNum(), baseDTO.getPageSize());
// 设置排序字段
List<PageParam.SortItem> sortItemList = baseDTO.getSortItemList();
if (CollectionUtils.isNotEmpty(sortItemList)) {
List<OrderItem> orderItemList = sortItemList.stream().map(e -> new OrderItem(e.getColumn(), e.getIsAsc())).collect(Collectors.toList());
page.setOrders(orderItemList);
}
return page;
}
/**
* 转换为 PageResultDTO 对象
*
* @param page
* @param sourceList 原list
* @param targetClazz 目标类
* @return
*/
public static <T, E> PageResult<T> convert2PageResult(Page<?> page, List<E> sourceList, Class<T> targetClazz) {
return convert2PageResult(page, SmartBeanUtil.copyList(sourceList, targetClazz));
}
/**
* 转换为 PageResultDTO 对象
*
* @param page
* @param sourceList list
* @return
*/
public static <E> PageResult<E> convert2PageResult(Page<?> page, List<E> sourceList) {
PageResult<E> pageResult = new PageResult<>();
pageResult.setPageNum(page.getCurrent());
pageResult.setPageSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setList(sourceList);
pageResult.setEmptyFlag(CollectionUtils.isEmpty(sourceList));
return pageResult;
}
/**
* 转换分页结果对象
*
* @param pageResult
* @param targetClazz
* @return
*/
public static <E, T> PageResult<T> convert2PageResult(PageResult<E> pageResult, Class<T> targetClazz) {
PageResult<T> newPageResult = new PageResult<>();
newPageResult.setPageNum(pageResult.getPageNum());
newPageResult.setPageSize(pageResult.getPageSize());
newPageResult.setTotal(pageResult.getTotal());
newPageResult.setPages(pageResult.getPages());
newPageResult.setEmptyFlag(pageResult.getEmptyFlag());
newPageResult.setList(SmartBeanUtil.copyList(pageResult.getList(), targetClazz));
return newPageResult;
}
public static <T> PageResult subListPage(Integer pageNum, Integer pageSize, List<T> list) {
PageResult<T> pageRet = new PageResult<T>();
//总条数
int count = list.size();
int pages = count % pageSize == 0 ? count / pageSize : (count / pageSize + 1);
int fromIndex = (pageNum - 1) * pageSize;
int toIndex = pageNum * pageSize > count ? count : pageNum * pageSize;
if (pageNum > pages) {
pageRet.setList(Lists.newLinkedList());
pageRet.setPageNum(pageNum.longValue());
pageRet.setPages(Long.valueOf(pages));
pageRet.setTotal(Long.valueOf(count));
return pageRet;
}
List<T> pageList = list.subList(fromIndex, toIndex);
pageRet.setList(pageList);
pageRet.setPageNum(pageNum.longValue());
pageRet.setPages(Long.valueOf(pages));
pageRet.setTotal(Long.valueOf(count));
return pageRet;
}
}

View File

@@ -0,0 +1,38 @@
package net.lab1024.sa.common.common.util;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.domain.RequestUser;
/**
* 请求用户 工具类
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
public class SmartRequestUtil {
private static final ThreadLocal<RequestUser> requestThreadLocal = new ThreadLocal<>();
public static void setRequestUser(RequestUser requestUser) {
requestThreadLocal.set(requestUser);
}
public static RequestUser getRequestUser() {
return requestThreadLocal.get();
}
public static Long getRequestUserId() {
RequestUser requestUser = getRequestUser();
return null == requestUser ? null : requestUser.getUserId();
}
public static void remove() {
requestThreadLocal.remove();
}
}

View File

@@ -0,0 +1,334 @@
package net.lab1024.sa.common.common.util;
import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 独有的字符串工具类
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SmartStringUtil extends StrUtil {
// ===============split =======================
public static Set<String> splitConvertToSet(String str, String split) {
if (isEmpty(str)) {
return new HashSet<String>();
}
String[] splitArr = str.split(split);
HashSet<String> set = new HashSet<String>(splitArr.length);
for (String string : splitArr) {
set.add(string);
}
return set;
}
public static List<String> splitConvertToList(String str, String split) {
if (isEmpty(str)) {
return new ArrayList<String>();
}
String[] splitArr = str.split(split);
ArrayList<String> list = new ArrayList<String>(splitArr.length);
for (String string : splitArr) {
list.add(string);
}
return list;
}
// ===============split Integer=======================
public static List<Integer> splitConvertToIntList(String str, String split, int defaultVal) {
if (isEmpty(str)) {
return new ArrayList<Integer>();
}
String[] strArr = str.split(split);
List<Integer> list = new ArrayList<Integer>(strArr.length);
for (int i = 0; i < strArr.length; i++) {
try {
int parseInt = Integer.parseInt(strArr[i]);
list.add(parseInt);
} catch (NumberFormatException e) {
list.add(defaultVal);
continue;
}
}
return list;
}
public static Set<Integer> splitConvertToIntSet(String str, String split, int defaultVal) {
if (isEmpty(str)) {
return new HashSet<Integer>();
}
String[] strArr = str.split(split);
HashSet<Integer> set = new HashSet<Integer>(strArr.length);
for (int i = 0; i < strArr.length; i++) {
try {
int parseInt = Integer.parseInt(strArr[i]);
set.add(parseInt);
} catch (NumberFormatException e) {
set.add(defaultVal);
continue;
}
}
return set;
}
public static Set<Integer> splitConvertToIntSet(String str, String split) {
return splitConvertToIntSet(str, split, 0);
}
public static List<Integer> splitConvertToIntList(String str, String split) {
return splitConvertToIntList(str, split, 0);
}
public static int[] splitConvertToIntArray(String str, String split, int defaultVal) {
if (isEmpty(str)) {
return new int[0];
}
String[] strArr = str.split(split);
int[] result = new int[strArr.length];
for (int i = 0; i < strArr.length; i++) {
try {
result[i] = Integer.parseInt(strArr[i]);
} catch (NumberFormatException e) {
result[i] = defaultVal;
continue;
}
}
return result;
}
public static int[] splitConvertToIntArray(String str, String split) {
return splitConvertToIntArray(str, split, 0);
}
// ===============split 2 Long=======================
public static List<Long> splitConvertToLongList(String str, String split, long defaultVal) {
if (isEmpty(str)) {
return new ArrayList<Long>();
}
String[] strArr = str.split(split);
List<Long> list = new ArrayList<Long>(strArr.length);
for (int i = 0; i < strArr.length; i++) {
try {
long parseLong = Long.parseLong(strArr[i]);
list.add(parseLong);
} catch (NumberFormatException e) {
list.add(defaultVal);
continue;
}
}
return list;
}
public static List<Long> splitConvertToLongList(String str, String split) {
return splitConvertToLongList(str, split, 0L);
}
public static long[] splitConvertToLongArray(String str, String split, long defaultVal) {
if (isEmpty(str)) {
return new long[0];
}
String[] strArr = str.split(split);
long[] result = new long[strArr.length];
for (int i = 0; i < strArr.length; i++) {
try {
result[i] = Long.parseLong(strArr[i]);
} catch (NumberFormatException e) {
result[i] = defaultVal;
continue;
}
}
return result;
}
public static long[] splitConvertToLongArray(String str, String split) {
return splitConvertToLongArray(str, split, 0L);
}
// ===============split convert byte=======================
public static List<Byte> splitConvertToByteList(String str, String split, byte defaultVal) {
if (isEmpty(str)) {
return new ArrayList<Byte>();
}
String[] strArr = str.split(split);
List<Byte> list = new ArrayList<Byte>(strArr.length);
for (int i = 0; i < strArr.length; i++) {
try {
byte parseByte = Byte.parseByte(strArr[i]);
list.add(parseByte);
} catch (NumberFormatException e) {
list.add(defaultVal);
continue;
}
}
return list;
}
public static List<Byte> splitConvertToByteList(String str, String split) {
return splitConvertToByteList(str, split, (byte) 0);
}
public static byte[] splitConvertToByteArray(String str, String split, byte defaultVal) {
if (isEmpty(str)) {
return new byte[0];
}
String[] strArr = str.split(split);
byte[] result = new byte[strArr.length];
for (int i = 0; i < strArr.length; i++) {
try {
result[i] = Byte.parseByte(strArr[i]);
} catch (NumberFormatException e) {
result[i] = defaultVal;
continue;
}
}
return result;
}
public static byte[] splitConvertToByteArray(String str, String split) {
return splitConvertToByteArray(str, split, (byte) 0);
}
// ===============split convert double=======================
public static List<Double> splitConvertToDoubleList(String str, String split, double defaultVal) {
if (isEmpty(str)) {
return new ArrayList<Double>();
}
String[] strArr = str.split(split);
List<Double> list = new ArrayList<Double>(strArr.length);
for (int i = 0; i < strArr.length; i++) {
try {
double parseByte = Double.parseDouble(strArr[i]);
list.add(parseByte);
} catch (NumberFormatException e) {
list.add(defaultVal);
continue;
}
}
return list;
}
public static List<Double> splitConvertToDoubleList(String str, String split) {
return splitConvertToDoubleList(str, split, 0);
}
public static double[] splitConvertToDoubleArray(String str, String split, double defaultVal) {
if (isEmpty(str)) {
return new double[0];
}
String[] strArr = str.split(split);
double[] result = new double[strArr.length];
for (int i = 0; i < strArr.length; i++) {
try {
result[i] = Double.parseDouble(strArr[i]);
} catch (NumberFormatException e) {
result[i] = defaultVal;
continue;
}
}
return result;
}
public static double[] splitConvertToDoubleArray(String str, String split) {
return splitConvertToDoubleArray(str, split, 0);
}
// ===============split convert float=======================
public static List<Float> splitConvertToFloatList(String str, String split, float defaultVal) {
if (isEmpty(str)) {
return new ArrayList<Float>();
}
String[] strArr = str.split(split);
List<Float> list = new ArrayList<Float>(strArr.length);
for (int i = 0; i < strArr.length; i++) {
try {
float parseByte = Float.parseFloat(strArr[i]);
list.add(parseByte);
} catch (NumberFormatException e) {
list.add(defaultVal);
continue;
}
}
return list;
}
public static List<Float> splitConvertToFloatList(String str, String split) {
return splitConvertToFloatList(str, split, 0f);
}
public static float[] splitConvertToFloatArray(String str, String split, float defaultVal) {
if (isEmpty(str)) {
return new float[0];
}
String[] strArr = str.split(split);
float[] result = new float[strArr.length];
for (int i = 0; i < strArr.length; i++) {
try {
result[i] = Float.parseFloat(strArr[i]);
} catch (NumberFormatException e) {
result[i] = defaultVal;
continue;
}
}
return result;
}
public static float[] splitConvertToFloatArray(String str, String split) {
return splitConvertToFloatArray(str, split, 0f);
}
public static String upperCaseFirstChar(String str) {
if (str != null && !str.isEmpty()) {
char firstChar = str.charAt(0);
if (Character.isUpperCase(firstChar)) {
return str;
} else {
char[] values = str.toCharArray();
values[0] = Character.toUpperCase(firstChar);
return new String(values);
}
} else {
return str;
}
}
public static String replace(String content, int begin, int end, String newStr) {
if (begin < content.length() && begin >= 0) {
if (end <= content.length() && end >= 0) {
if (begin > end) {
return content;
} else {
StringBuilder starStr = new StringBuilder();
for (int i = begin; i < end; ++i) {
starStr.append(newStr);
}
return content.substring(0, begin) + starStr + content.substring(end);
}
} else {
return content;
}
} else {
return content;
}
}
}

View File

@@ -0,0 +1,98 @@
package net.lab1024.sa.common.common.util;
import java.util.regex.Pattern;
/**
* 验证工具类
*
* @Author 1024创新实验室: 胡克
* @Date 2017/11/06 10:54
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SmartVerificationUtil {
/**
* 手机号码验证规则
*/
public static final String PHONE_REGEXP = "^1[0-9]{10}";
/**
* 固定号码验证规则
*/
public static final String FIXED_PHONE_REGEXP = "^0\\d{2,3}-[1-9]\\d{6,7}$";
/**
* 密码正则校验
*/
public static final String PWD_REGEXP = "^[A-Za-z0-9.]{6,15}$";
/**
* 车牌号
*/
public static final String CAR_NUMBER =
"([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}(([A-HJ-Z]{1}[A-HJ-NP-Z0-9]{5})|([A-HJ-Z]{1}(([DF]{1}[A-HJ-NP-Z0-9]{1}[0-9]{4})|([0-9]{5}[DF]{1})))|" + "([A-HJ-Z" + "]{1}[A-D0-9]{1}[0-9]{3}警)))|" +
"([0-9]{6}使)|((([沪粤川云桂鄂陕蒙藏黑辽渝]{1}A)|鲁B|闽D|蒙E|蒙H)[0-9]{4}领)|(WJ[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼·•]{1}[0-9]{4}[TDSHBXJ0-9]{1})|" + "([VKHBSLJNGCE]{1}[A-DJ-PR" + "-TVY]{1}[0-9]{5})";
/**
* 日期年月日校验 yyyy-MM-dd HH:mm:ss
*/
public static final String DATE_TIME = "^((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9" +
"]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))\\s+([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$";
/**
* 日期校验 yyyy-MM-dd
*/
public static final String DATE = "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))"
+ "|(02-(0[1-9]|[1][0-9]|2[0-8])))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)" + "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9"
+ "][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|(" + "(([0-9]{2})(0[48]|[2468][048]|[13579][26])|(" + "(0[48" + "]|[2468][048]|[3579][26])00))-02-29)";
public static final String DATE_TIME_HM = "^((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))\\s+([0-1]?[0-9]|2[0-3]):([0-5][0-9])$";
/**
* 年月校验 例: 2019-10
*/
public static final String YEAR_MONTH = "^\\d{4}-((0([1-9]))|(1(0|1|2)))$";
/**
* 时间区间验证 10:23-19:00
*/
public static final String TIME_SECTION = "^(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9])-(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9])$";
/**
* 时间验证 10:23
*/
public static final String TIME = "^(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9])$";
/**
* 身份证号
*/
public static final String ID_CARD = "(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)";
/**
* URL
*/
public static final String URL = "[a-zA-z]+://[^\\s]*";
/**
* 邮箱
*/
public static final String EMAIL = "[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?";
/**
* 整数
*/
public static final String INTEGER = "^-?[1-9]\\d*$";
/**
* 小数
*/
public static final String DOUBLE = "^-?[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$";
public static void main(String[] args) {
boolean matches = Pattern.matches(INTEGER, "1");
System.out.println(matches);
}
}

View File

@@ -0,0 +1,52 @@
package net.lab1024.sa.common.common.validator.enumeration;
import net.lab1024.sa.common.common.enumeration.BaseEnum;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义的属性校验注解,为了方便与校验属性的值是否为合法的枚举值
*
* @Author 1024创新实验室: 胡克
* @Date 2017/11/11 15:31
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)// 自定义验证的处理类
public @interface CheckEnum {
/**
* 默认的错误提示信息
*
* @return String
*/
String message();
/**
* 枚举类对象 必须实现BaseEnum接口
*
* @return
*/
Class<? extends BaseEnum> value();
/**
* 是否必须
*
* @return boolean
*/
boolean required() default false;
//下面这两个属性必须添加 :不然会报错
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,75 @@
package net.lab1024.sa.common.common.validator.enumeration;
import net.lab1024.sa.common.common.enumeration.BaseEnum;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 枚举类校验器
*
* @Author 1024创新实验室: 胡克
* @Date 2017/11/11 15:34
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class EnumValidator implements ConstraintValidator<CheckEnum, Object> {
/**
* 枚举类实例集合
*/
private List<Object> enumValList;
/**
* 是否必须
*/
private boolean required;
@Override
public void initialize(CheckEnum constraintAnnotation) {
// 获取注解传入的枚举类对象
required = constraintAnnotation.required();
Class<? extends BaseEnum> enumClass = constraintAnnotation.value();
enumValList = Stream.of(enumClass.getEnumConstants()).map(BaseEnum::getValue).collect(Collectors.toList());
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
// 判断是否必须
if (null == value) {
return !required;
}
if (value instanceof List) {
// 如果为 List 集合数据
return this.checkList((List<Object>) value);
}
// 校验是否为合法的枚举值
return enumValList.contains(value);
}
/**
* 校验集合类型
*
* @param list
* @return
*/
private boolean checkList(List<Object> list) {
if (required && list.isEmpty()) {
// 必须的情况下 list 不能为空
return false;
}
// 校验是否重复
long count = list.stream().distinct().count();
if (count != list.size()) {
return false;
}
return enumValList.containsAll(list);
}
}

View File

@@ -0,0 +1,71 @@
package net.lab1024.sa.common.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 异步调用线程配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
@Configuration
public class AsyncConfig {
/**
* 线程池 配置bean名称
*/
public static final String ASYNC_EXECUTOR_THREAD_NAME = "smart-admin-async-executor";
/**
* 配置线程池
*
* @return
*/
@Bean(name = ASYNC_EXECUTOR_THREAD_NAME)
public AsyncTaskExecutor executor() {
int processors = Runtime.getRuntime().availableProcessors();
int threadCount = Math.max(1, processors - 1);
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// 核心线程数量
taskExecutor.setCorePoolSize(threadCount);
// 最大线程数量
taskExecutor.setMaxPoolSize(threadCount);
taskExecutor.setThreadNamePrefix(ASYNC_EXECUTOR_THREAD_NAME);
taskExecutor.initialize();
return taskExecutor;
}
/**
* spring 异步任务 异常配置
*/
@Configuration
public static class AsyncExceptionConfig implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
/**
* 自定义异常处理
*/
public static class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("异步任务发生异常:{}, 参数:{}, ", method.getDeclaringClass().getSimpleName() + "." + method.getName(), Arrays.toString(objects), throwable);
}
}
}

View File

@@ -0,0 +1,45 @@
package net.lab1024.sa.common.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 跨域配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2021/11/15 20:38
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class CorsFilterConfig {
@Value("${access-control-allow-origin}")
private String accessControlAllowOrigin;
/**
* 跨域配置
*
* @return
*/
@Bean
public CorsFilter corsFilter () {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern(accessControlAllowOrigin);
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 对接口配置跨域设置
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}

View File

@@ -0,0 +1,197 @@
package net.lab1024.sa.common.config;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.support.spring.stat.DruidStatInterceptor;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.domain.DataScopePlugin;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 数据源配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2017-11-28 15:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
@Configuration
public class DataSourceConfig {
@Value("${spring.datasource.driver-class-name}")
String driver;
@Value("${spring.datasource.url}")
String url;
@Value("${spring.datasource.username}")
String username;
@Value("${spring.datasource.password}")
String password;
@Value("${spring.datasource.initial-size}")
int initialSize;
@Value("${spring.datasource.min-idle}")
int minIdle;
@Value("${spring.datasource.max-active}")
int maxActive;
@Value("${spring.datasource.max-wait}")
long maxWait;
@Value("${spring.datasource.time-between-eviction-runs-millis}")
long timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.min-evictable-idle-time-millis}")
long minEvictableIdleTimeMillis;
@Value("${spring.datasource.filters}")
String filters;
@Value("${spring.datasource.druid.username}")
String druidUserName;
@Value("${spring.datasource.druid.password}")
String druidPassword;
@Value("${spring.datasource.druid.login.enabled}")
boolean druidLoginEnable;
@Value("${spring.datasource.druid.method.pointcut}")
String methodPointcut;
@Autowired
private MybatisPlusInterceptor paginationInterceptor;
@Autowired(required = false)
private DataScopePlugin dataScopePlugin;
@Bean
@Primary
public DataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDbType(DbType.MYSQL.getDb());
druidDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
druidDataSource.setInitialSize(initialSize);
druidDataSource.setMinIdle(minIdle);
druidDataSource.setMaxActive(maxActive);
druidDataSource.setMaxWait(maxWait);
druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
druidDataSource.setValidationQuery("SELECT 1");
try {
druidDataSource.setFilters(filters);
ArrayList<Filter> arrayList = new ArrayList<>();
StatFilter statFilter = new StatFilter();
statFilter.setMergeSql(true);
statFilter.setSlowSqlMillis(500);
statFilter.setLogSlowSql(true);
arrayList.add(statFilter);
druidDataSource.setProxyFilters(arrayList);
druidDataSource.init();
} catch (SQLException e) {
log.error("初始化数据源出错", e);
}
return druidDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(druidDataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:/mapper/**/*.xml");
factoryBean.setMapperLocations(resources);
// 设置 MyBatis-Plus 分页插件 注意此处myBatisPlugin一定要放在后面
List<Interceptor> pluginsList = new ArrayList<>();
pluginsList.add(paginationInterceptor);
if (dataScopePlugin != null) {
pluginsList.add(dataScopePlugin);
}
factoryBean.setPlugins(pluginsList.toArray(new Interceptor[pluginsList.size()]));
return factoryBean.getObject();
}
/**
* 非正式环境 才加载
*
* @return
*/
@Conditional(SystemEnvironmentConfig.class)
@Bean
public ServletRegistrationBean<StatViewServlet> druidServlet() {
ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<>();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
Map<String, String> initParameters = new HashMap<String, String>();
//不设置用户名密码可以直接通过druid/index.html访问
if (druidLoginEnable) {
initParameters.put("loginUsername", druidUserName);
initParameters.put("loginPassword", druidPassword);
}
initParameters.put("resetEnable", "false");
servletRegistrationBean.setInitParameters(initParameters);
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<WebStatFilter>();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/*");
return filterRegistrationBean;
}
@Bean
public JdkRegexpMethodPointcut jdkRegexpMethodPointcut() {
JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut();
jdkRegexpMethodPointcut.setPatterns(methodPointcut);
return jdkRegexpMethodPointcut;
}
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor() {
DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor();
pointcutAdvisor.setPointcut(jdkRegexpMethodPointcut());
pointcutAdvisor.setAdvice(new DruidStatInterceptor());
return pointcutAdvisor;
}
}

View File

@@ -0,0 +1,88 @@
package net.lab1024.sa.common.config;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
/**
* java8 localDate 时间类格式化配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2017-11-28 15:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class DateConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
builder.deserializers(new LocalDateDeserializer(DatePattern.NORM_DATE_FORMAT.getDateTimeFormatter()));
builder.deserializers(new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMAT.getDateTimeFormatter()));
builder.serializers(new LocalDateSerializer(DatePattern.NORM_DATE_FORMAT.getDateTimeFormatter()));
builder.serializers(new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMAT.getDateTimeFormatter()));
};
}
/**
* string 转为 LocalDateTime 配置类
*
* @author 卓大
*/
@Configuration
public static class StringToLocalDateTime implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String str) {
if (StringUtils.isBlank(str)) {
return null;
}
LocalDateTime localDateTime;
try {
localDateTime = LocalDateTimeUtil.parse(str, DatePattern.NORM_DATETIME_FORMAT.getDateTimeFormatter());
} catch (DateTimeParseException e) {
throw new RuntimeException("请输入正确的日期格式yyyy-MM-dd HH:mm:ss");
}
return localDateTime;
}
}
/**
* string 转为 LocalDate 配置类
*
* @author 卓大
*/
@Configuration
public static class StringToLocalDate implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String str) {
if (StringUtils.isBlank(str)) {
return null;
}
LocalDate localDate;
try {
localDate = LocalDateTimeUtil.parseDate(str, DatePattern.NORM_DATE_FORMAT.getDateTimeFormatter());
} catch (DateTimeParseException e) {
throw new RuntimeException("请输入正确的日期格式yyyy-MM-dd");
}
return localDate;
}
}
}

View File

@@ -0,0 +1,85 @@
package net.lab1024.sa.common.config;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import lombok.Data;
import net.lab1024.sa.common.module.support.file.service.FileStorageCloudServiceImpl;
import net.lab1024.sa.common.module.support.file.service.FileStorageLocalServiceImpl;
import net.lab1024.sa.common.module.support.file.service.IFileStorageService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 文件上传 配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2019-09-02 23:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Data
@Configuration
public class FileCloudConfig {
@Value("${file.storage.cloud.region}")
private String region;
@Value("${file.storage.cloud.endpoint}")
private String endpoint;
@Value("${file.storage.cloud.bucket-name}")
private String bucketName;
@Value("${file.storage.cloud.access-key}")
private String accessKey;
@Value("${file.storage.cloud.secret-key}")
private String secretKey;
@Value("${file.storage.cloud.url.expire}")
private Long urlExpire;
@Value("${file.storage.cloud.url.public}")
private String publicUrl;
/**
* 初始化 云oss client 配置
*
* @return
*/
@Bean
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "cloud")
public AmazonS3 initAmazonS3() {
ClientConfiguration clientConfig = new ClientConfiguration();
clientConfig.setProtocol(Protocol.HTTPS);
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.withClientConfiguration(clientConfig)
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withPathStyleAccessEnabled(false)
.build();
return s3Client;
}
@Bean
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "cloud")
public IFileStorageService initCloudFileService() {
return new FileStorageCloudServiceImpl();
}
@Bean
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "local")
public IFileStorageService initLocalFileService() {
return new FileStorageLocalServiceImpl();
}
}

View File

@@ -0,0 +1,37 @@
package net.lab1024.sa.common.config;
import net.lab1024.sa.common.module.support.heartbeat.core.HeartBeatManager;
import net.lab1024.sa.common.module.support.heartbeat.core.IHeartBeatRecordHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 心跳配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2018/10/9 18:47
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class HeartBeatConfig {
/**
* 间隔时间
*/
@Value("${heart-beat.interval-seconds}")
private Long intervalSeconds;
@Autowired
private IHeartBeatRecordHandler heartBeatRecordHandler;
@Bean
public HeartBeatManager heartBeatManager() {
return new HeartBeatManager(intervalSeconds * 1000L, heartBeatRecordHandler);
}
}

View File

@@ -0,0 +1,50 @@
package net.lab1024.sa.common.config;
import net.lab1024.sa.common.common.interceptor.AbstractInterceptor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* web相关配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired(required = false)
private List<HandlerInterceptor> interceptorList;
@Override
public void addInterceptors (InterceptorRegistry registry) {
if (CollectionUtils.isEmpty(interceptorList)) {
return;
}
interceptorList.forEach(e->{
registry.addInterceptor(e).addPathPatterns("/**");
});
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/preview/**");
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/druidMonitor").setViewName("redirect:druid/index.html");
registry.addViewController("/swaggerApi").setViewName("redirect:swagger-ui.html");
}
}

View File

@@ -0,0 +1,33 @@
package net.lab1024.sa.common.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* mp 插件
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

View File

@@ -0,0 +1,56 @@
package net.lab1024.sa.common.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.IOException;
import java.util.List;
/**
* yaml 读取配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
@Slf4j
public class PostProcessorConfig implements EnvironmentPostProcessor {
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
this.loadProperty(propertySources);
}
private void loadProperty(MutablePropertySources propertySources) {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
Resource[] resources = resolver.getResources("classpath*:sa-*.yaml");
if (resources.length < 1) {
return;
}
for (Resource resource : resources) {
log.info("初始化系统配置:{}", resource.getFilename());
List<PropertySource<?>> load = loader.load(resource.getFilename(), resource);
load.forEach(propertySources::addLast);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,72 @@
package net.lab1024.sa.common.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class RedisConfig {
@Autowired
private RedisConnectionFactory factory;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setDefaultSerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
@Bean
public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
return redisTemplate.opsForValue();
}
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}

View File

@@ -0,0 +1,38 @@
package net.lab1024.sa.common.config;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.module.support.reload.ReloadCommand;
import net.lab1024.sa.common.module.support.reload.core.SmartReloadManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* reload配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/9/1 21:40
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
@Configuration
public class ReloadConfig {
/**
* 间隔时间
*/
@Value("${reload.interval-seconds}")
private Integer intervalSeconds;
@Autowired
private ReloadCommand reloadCommand;
@Bean
public SmartReloadManager initSmartReloadManager() {
// 创建 Reload Manager 调度器
return new SmartReloadManager(reloadCommand,intervalSeconds);
}
}

View File

@@ -0,0 +1,40 @@
package net.lab1024.sa.common.config;
import net.lab1024.sa.common.common.constant.StringConst;
import net.lab1024.sa.common.common.util.SmartRequestUtil;
import net.lab1024.sa.common.module.support.repeatsubmit.RepeatSubmitAspect;
import net.lab1024.sa.common.module.support.repeatsubmit.ticket.RepeatSubmitCaffeineTicket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 重复提交配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2021/10/9 18:47
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class RepeatSubmitConfig {
@Bean
public RepeatSubmitAspect repeatSubmitAspect() {
RepeatSubmitCaffeineTicket caffeineTicket = new RepeatSubmitCaffeineTicket(this::ticket);
return new RepeatSubmitAspect(caffeineTicket);
}
/**
* 获取指明某个用户的凭证
*
* @return
*/
private String ticket(String servletPath) {
Long userId = SmartRequestUtil.getRequestUserId();
if (null == userId) {
return StringConst.EMPTY;
}
return servletPath + "_" + userId;
}
}

View File

@@ -0,0 +1,130 @@
package net.lab1024.sa.common.config;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* http请求配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class RestTemplateConfig {
@Value("${http.pool.max-total}")
private Integer maxTotal;
@Value("${http.pool.connect-timeout}")
private Integer connectTimeout;
@Value("${http.pool.read-timeout}")
private Integer readTimeout;
@Value("${http.pool.write-timeout}")
private Integer writeTimeout;
@Value("${http.pool.keep-alive}")
private Integer keepAlive;
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(this.clientHttpRequestFactory());
List<HttpMessageConverter<?>> messageConverterList = restTemplate.getMessageConverters();
messageConverterList.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
messageConverterList.addAll(this.converters());
return restTemplate;
}
public List<HttpMessageConverter<?>> converters() {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
fastMediaTypes.add(MediaType.APPLICATION_JSON);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
converters.add(converter);
converters.add(fastConverter);
return converters;
}
public OkHttp3ClientHttpRequestFactory clientHttpRequestFactory() {
return new OkHttp3ClientHttpRequestFactory(httpClientBuilder());
}
public OkHttpClient httpClientBuilder() {
return new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.connectionPool(this.pool())
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.readTimeout(readTimeout, TimeUnit.MILLISECONDS)
.writeTimeout(writeTimeout, TimeUnit.MILLISECONDS)
.build();
}
public ConnectionPool pool() {
return new ConnectionPool(maxTotal, keepAlive, TimeUnit.MILLISECONDS);
}
@Bean
public X509TrustManager x509TrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
@Bean
public SSLSocketFactory sslSocketFactory() {
try {
//信任任何链接
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,47 @@
package net.lab1024.sa.common.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 定时任务调度 配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
private ScheduledTaskRegistrar taskRegistrar;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
}
public String destroy() {
List<Task> taskList = new ArrayList<>();
taskList.addAll(taskRegistrar.getCronTaskList());
taskList.addAll(taskRegistrar.getTriggerTaskList());
taskList.addAll(taskRegistrar.getFixedDelayTaskList());
taskList.addAll(taskRegistrar.getFixedRateTaskList());
taskRegistrar.destroy();
List<String> taskNameList = taskList.stream().map(Task::toString).collect(Collectors.toList());
return "已关闭 @Scheduled定时任务" + taskNameList.size() + "个!";
}
}

View File

@@ -0,0 +1,206 @@
package net.lab1024.sa.common.config;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import io.swagger.annotations.Api;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.constant.RequestHeaderConst;
import net.lab1024.sa.common.common.swagger.SwaggerApiModelPropertyEnumPlugin;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 根据SwaggerTagConst内部类动态生成Swagger group
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020-03-25 22:54:46
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
@EnableSwagger2
@Configuration
@Conditional(SystemEnvironmentConfig.class)
public class SwaggerConfig implements EnvironmentAware, BeanDefinitionRegistryPostProcessor {
/**
* 文档标题
*/
private String title;
/**
* 文档描述
*/
private String description;
/**
* api版本
*/
private String version;
/**
* service url
*/
private String teamUrl;
/**
* host
*/
private String host;
private String tagClass;
@Bean
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
public SwaggerApiModelPropertyEnumPlugin swaggerEnum() {
return new SwaggerApiModelPropertyEnumPlugin();
}
@Override
public void setEnvironment(Environment environment) {
this.title = environment.getProperty("swagger.title");
this.description = environment.getProperty("swagger.description");
this.version = environment.getProperty("swagger.version");
this.host = environment.getProperty("swagger.host");
this.tagClass = environment.getProperty("swagger.tag-class");
this.teamUrl = environment.getProperty("swagger.team-url");
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
Map<String, List<String>> groupMap = this.buildGroup();
for (Map.Entry<String, List<String>> entry : groupMap.entrySet()) {
String group = entry.getKey();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Docket.class, () -> this.baseDocket(group, entry.getValue()));
BeanDefinition beanDefinition = builder.getRawBeanDefinition();
registry.registerBeanDefinition(group + "Api", beanDefinition);
}
}
@SneakyThrows
private Map<String, List<String>> buildGroup() {
Class<?> clazz = Class.forName(tagClass);
Class<?>[] innerClazz = clazz.getClasses();
Map<String, List<String>> groupMap = new HashMap<>(16);
for (Class<?> cls : innerClazz) {
String group = cls.getSimpleName();
List<String> apiTags = Lists.newArrayList();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
boolean isFinal = Modifier.isFinal(field.getModifiers());
if (isFinal) {
apiTags.add(field.get(null).toString());
}
}
groupMap.put(group, apiTags);
}
return groupMap;
}
private Docket baseDocket(String groupName, List<String> apiTagList) {
// 配置全局参数
List<Parameter> parameterList = this.generateParameter();
Docket docket = new Docket(DocumentationType.SWAGGER_2).groupName(groupName)
.forCodeGeneration(true)
.select()
// 过滤规则
.apis(this.getControllerPredicate(apiTagList))
// 与 过滤规则 controller 包路径 二选一
// .apis(RequestHandlerSelectors.basePackage(packAge))
.paths(PathSelectors.any())
.build().apiInfo(this.apiInfo())
.globalOperationParameters(parameterList);
if (StringUtils.isNotBlank(host)) {
docket = docket.host(host);
}
return docket;
}
private Predicate<RequestHandler> getControllerPredicate(List<String> apiTagList) {
Predicate<RequestHandler> methodPredicate = (input) -> {
Api api = null;
Optional<Api> apiOptional = input.findControllerAnnotation(Api.class);
if (apiOptional.isPresent()) {
api = apiOptional.get();
}
if (api == null) {
return false;
}
List<String> tags = Arrays.asList(api.tags());
if (apiTagList.containsAll(tags)) {
return true;
}
return false;
};
Predicate controllerPredicate = Predicates.or(RequestHandlerSelectors.withClassAnnotation(RestController.class), RequestHandlerSelectors.withClassAnnotation(Controller.class));
return Predicates.and(controllerPredicate, methodPredicate);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title(title)
.description(description)
.version(version)
.termsOfServiceUrl(teamUrl)
.contact(new Contact("1024lab", teamUrl, "1024lab@sina.com"))
.build();
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
/**
* 生成共用请求参数
*
* @return
*/
private List<Parameter> generateParameter() {
// 配置全局参数 token
Parameter token = new ParameterBuilder().name(RequestHeaderConst.TOKEN)
.description("token")
.modelRef(new ModelRef("string"))
.parameterType("header").defaultValue("1")
.required(false)
.build();
return Lists.newArrayList(token);
}
}

View File

@@ -0,0 +1,49 @@
package net.lab1024.sa.common.config;
import net.lab1024.sa.common.common.domain.SystemEnvironment;
import net.lab1024.sa.common.common.enumeration.SystemEnvironmentEnum;
import net.lab1024.sa.common.common.util.SmartEnumUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 系统环境
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/08/13 18:56
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class SystemEnvironmentConfig implements Condition {
@Value("${spring.profiles.active}")
private String systemEnvironment;
@Value("${project.name}")
private String projectName;
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String property = conditionContext.getEnvironment().getProperty("spring.profiles.active");
return StringUtils.isNotBlank(property) && !SystemEnvironmentEnum.PROD.equalsValue(property);
}
@Bean
public SystemEnvironment initEnvironment() {
SystemEnvironmentEnum currentEnvironment = SmartEnumUtil.getEnumByValue(systemEnvironment, SystemEnvironmentEnum.class);
if (currentEnvironment == null) {
throw new ExceptionInInitializerError("无法获取当前环境!请在 application.yaml 配置参数spring.profiles.active");
}
if (StringUtils.isBlank(projectName)) {
throw new ExceptionInInitializerError("无法获取当前项目名称!请在 application.yaml 配置参数project.name");
}
return new SystemEnvironment(currentEnvironment == SystemEnvironmentEnum.PROD, projectName, currentEnvironment);
}
}

View File

@@ -0,0 +1,150 @@
package net.lab1024.sa.common.config;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.annoation.NoNeedLogin;
import net.lab1024.sa.common.common.annoation.SaAuth;
import net.lab1024.sa.common.common.domain.RequestUrlVO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* url配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
@Slf4j
public class UrlConfig {
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
/**
* 获取每个方法的请求路径
*
* @return
*/
@Bean
public Map<Method, Set<String>> methodUrlMap() {
Map<Method, Set<String>> methodUrlMap = Maps.newHashMap();
//获取url与类和方法的对应信息
Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
RequestMappingInfo requestMappingInfo = entry.getKey();
Set<String> urls = requestMappingInfo.getPatternsCondition().getPatterns();
if (CollectionUtils.isEmpty(urls)) {
continue;
}
HandlerMethod handlerMethod = entry.getValue();
methodUrlMap.put(handlerMethod.getMethod(), urls);
}
return methodUrlMap;
}
/**
* 需要进行url权限校验的方法
*
* @param methodUrlMap
* @return
*/
@Bean
public List<RequestUrlVO> authUrl(Map<Method, Set<String>> methodUrlMap) {
List<RequestUrlVO> authUrlList = Lists.newArrayList();
for (Map.Entry<Method, Set<String>> entry : methodUrlMap.entrySet()) {
Method method = entry.getKey();
SaAuth saAuth = method.getAnnotation(SaAuth.class);
if (null == saAuth) {
continue;
}
List<RequestUrlVO> requestUrlList = this.buildRequestUrl(method, entry.getValue());
authUrlList.addAll(requestUrlList);
}
log.info("需要权限校验的URL{}", authUrlList.stream().map(e -> e.getUrl()).collect(Collectors.toList()));
return authUrlList;
}
private List<RequestUrlVO> buildRequestUrl(Method method, Set<String> urlSet) {
List<RequestUrlVO> requestUrlList = Lists.newArrayList();
if (CollectionUtils.isEmpty(urlSet)) {
return requestUrlList;
}
//url对应的方法名称
String className = method.getDeclaringClass().getName();
String methodName = method.getName();
List<String> list = StrUtil.split(className, ".");
String controllerName = list.get(list.size() - 1);
String name = controllerName + "." + methodName;
//swagger 说明信息
String methodComment = null;
ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
if (apiOperation != null) {
methodComment = apiOperation.value();
}
for (String url : urlSet) {
RequestUrlVO requestUrlVO = new RequestUrlVO();
requestUrlVO.setUrl(url);
requestUrlVO.setName(name);
requestUrlVO.setComment(methodComment);
requestUrlList.add(requestUrlVO);
}
return requestUrlList;
}
/**
* 获取无需登录可以匿名访问的url信息
*
* @return
*/
@Bean
public List<String> noNeedLoginUrlList(Map<Method, Set<String>> methodUrlMap) {
List<String> noNeedLoginUrlList = Lists.newArrayList();
for (Map.Entry<Method, Set<String>> entry : methodUrlMap.entrySet()) {
Method method = entry.getKey();
NoNeedLogin noNeedLogin = method.getAnnotation(NoNeedLogin.class);
if (null == noNeedLogin) {
continue;
}
noNeedLoginUrlList.addAll(entry.getValue());
}
log.info("不需要登录的URL{}", noNeedLoginUrlList);
return noNeedLoginUrlList;
}
/**
* 获取忽略的url信息
*
* @return
*/
@Bean
public List<String> ignoreUrlList() {
List<String> ignoreUrlList = Lists.newArrayList();
ignoreUrlList.add("/swagger-ui.html");
ignoreUrlList.add("/swagger-resources/**");
ignoreUrlList.add("/webjars/**");
ignoreUrlList.add("/druid/**");
ignoreUrlList.add("/*/api-docs");
return ignoreUrlList;
}
}

View File

@@ -0,0 +1,14 @@
package net.lab1024.sa.common.constant;
/**
* 缓存key常量
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class CacheKeyConst {
}

View File

@@ -0,0 +1,31 @@
package net.lab1024.sa.common.constant;
/**
* redis key 常量类
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class RedisKeyConst {
public static final String SEPARATOR = ":";
public static class Support {
public static final String FILE_URL = "file:";
public static final String FILE_VO = "file-vo:";
public static final String SERIAL_NUMBER_LAST_INFO = "serial-number:last-info";
public static final String SERIAL_NUMBER = "serial-number:";
public static final String CAPTCHA = "captcha:";
public static final String TOKEN = "token:";
}
}

View File

@@ -0,0 +1,18 @@
package net.lab1024.sa.common.constant;
/**
* reload 项目
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class ReloadConst {
public static final String CONFIG_RELOAD = "config";
public static final String CACHE_SERVICE = "cache_service";
}

View File

@@ -0,0 +1,49 @@
package net.lab1024.sa.common.constant;
/**
* swagger
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class SwaggerTagConst {
public static class Support {
public static final String CACHE = "业务支撑-缓存";
public static final String CAPTCHA = "业务支撑-验证码";
public static final String OPERATE_LOG = "业务支撑-用户操作记录";
public static final String LOGIN_LOG = "业务支撑-登录日志";
public static final String RELOAD = "业务支撑-reload";
public static final String SERIAL_NUMBER = "业务支撑-id生成器";
public static final String HEART_BEAT = "业务支撑-服务心跳";
public static final String FILE = "业务支撑-文件服务";
public static final String CONFIG = "业务支撑-系统参数";
public static final String DATA_TRACER = "业务支撑-数据变动记录";
public static final String DICT = "业务支撑-数据字典";
public static final String CODE_GENERATOR = "业务支撑-代码生成";
public static final String CHANGE_LOG = "业务支撑-更新日志";
public static final String HELP_DOC = "业务支撑-帮助文档";
public static final String FEEDBACK = "业务支撑-意见反馈";
public static final String TABLE_COLUMN = "业务支撑-列自定义";
}
}

View File

@@ -0,0 +1,18 @@
package net.lab1024.sa.common.constant;
/**
* url前缀
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/10/03 20:48
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class UrlPrefixConst {
public static final String SUPPORT = "/support";
public static final String THIRD = "/third";
}

View File

@@ -0,0 +1,126 @@
package net.lab1024.sa.common.handler;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.code.SystemErrorCode;
import net.lab1024.sa.common.common.code.UserErrorCode;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.common.domain.SystemEnvironment;
import net.lab1024.sa.common.common.exception.BusinessException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
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 java.util.List;
import java.util.stream.Collectors;
/**
* 全局异常拦截
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020/8/25 21:57
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@Autowired
private SystemEnvironment systemEnvironment;
/**
* json 格式错误 缺少请求体
*/
@ResponseBody
@ExceptionHandler({HttpMessageNotReadableException.class})
public ResponseDTO<?> jsonFormatExceptionHandler(Exception e) {
if (!systemEnvironment.isProd()) {
log.error("全局JSON格式错误异常,URL:{}", getCurrentRequestUrl(), e);
}
return ResponseDTO.error(UserErrorCode.PARAM_ERROR, "参数JSON格式错误");
}
/**
* json 格式错误 缺少请求体
*/
@ResponseBody
@ExceptionHandler({TypeMismatchException.class, BindException.class})
public ResponseDTO<?> paramExceptionHandler(Exception e) {
if (!systemEnvironment.isProd()) {
log.error("全局参数异常,URL:{}", getCurrentRequestUrl(), e);
}
if (e instanceof BindException) {
if (e instanceof MethodArgumentNotValidException) {
List<FieldError> fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
List<String> msgList = fieldErrors.stream().map(FieldError::getDefaultMessage).collect(Collectors.toList());
return ResponseDTO.error(UserErrorCode.PARAM_ERROR, String.join(",", msgList));
}
List<FieldError> fieldErrors = ((BindException) e).getFieldErrors();
List<String> error = fieldErrors.stream().map(field -> field.getField() + ":" + field.getRejectedValue()).collect(Collectors.toList());
String errorMsg = UserErrorCode.PARAM_ERROR.getMsg() + ":" + error;
return ResponseDTO.error(UserErrorCode.PARAM_ERROR, errorMsg);
}
return ResponseDTO.error(UserErrorCode.PARAM_ERROR);
}
/**
* 权限异常
*/
@ResponseBody
@ExceptionHandler({AccessDeniedException.class})
public ResponseDTO<?> permissionExceptionHandler(AccessDeniedException e) {
return ResponseDTO.error(UserErrorCode.NO_PERMISSION);
}
/**
* 业务异常
*/
@ResponseBody
@ExceptionHandler(BusinessException.class)
public ResponseDTO<?> businessExceptionHandler(BusinessException e) {
if (!systemEnvironment.isProd()) {
log.error("全局业务异常,URL:{}", getCurrentRequestUrl(), e);
}
return ResponseDTO.error(SystemErrorCode.SYSTEM_ERROR, e.getMessage());
}
/**
* 其他全部异常
*
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler(Throwable.class)
public ResponseDTO<?> errorHandler(Throwable e) {
log.error("捕获全局异常,URL:{}", getCurrentRequestUrl(), e);
return ResponseDTO.error(SystemErrorCode.SYSTEM_ERROR, systemEnvironment.isProd() ? null : e.toString());
}
/**
* 获取当前请求url
*/
private String getCurrentRequestUrl() {
RequestAttributes request = RequestContextHolder.getRequestAttributes();
if (null == request) {
return null;
}
ServletRequestAttributes servletRequest = (ServletRequestAttributes) request;
return servletRequest.getRequest().getRequestURI();
}
}

View File

@@ -0,0 +1,54 @@
package net.lab1024.sa.common.listener;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.URLUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.boot.web.server.WebServer;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* 启动监听器
*
* @Author 1024创新实验室: 罗伊
* @Date 2021-12-23 23:45:26
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
@Component
@Order(value = 1024)
public class SmartApplicationListener implements ApplicationListener<WebServerInitializedEvent> {
@Override
public void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) {
WebServer server = webServerInitializedEvent.getWebServer();
WebServerApplicationContext context = webServerInitializedEvent.getApplicationContext();
Environment env = context.getEnvironment();
//获取服务信息
String ip = NetUtil.getLocalhost().getHostAddress();
Integer port = server.getPort();
String contextPath = env.getProperty("server.servlet.context-path");
if (contextPath == null) {
contextPath = "";
}
String profile = env.getProperty("spring.profiles.active");
String projectName = env.getProperty("project.name");
//拼接服务地址
String title = String.format("-------------【%s】 service is runningcurrent profile is 【%s】-------------", projectName, profile);
String localhostUrl = URLUtil.normalize(String.format("http://localhost:%d%s", port, contextPath), false, true);
String externalUrl = URLUtil.normalize(String.format("http://%s:%d%s", ip, port, contextPath), false, true);
String swaggerUrl = URLUtil.normalize(String.format("http://localhost:%d%s/swagger-ui.html", port, contextPath), false, true);
log.info("\n{}\n" +
"\tLocal:\t\t{}" +
"\n\tExternal:\t{}" +
"\n\tSwagger:\t{}" +
"\n-------------------------------------------------------------------------------------\n",
title, localhostUrl, externalUrl, swaggerUrl);
}
}

View File

@@ -0,0 +1,67 @@
package net.lab1024.sa.common.module.support.cache;
import com.google.common.collect.Lists;
import net.lab1024.sa.common.constant.ReloadConst;
import net.lab1024.sa.common.module.support.reload.core.annoation.SmartReload;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 缓存操作
*
* @Author 1024创新实验室: 罗伊
* @Date 2021/10/11 20:07
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Service
public class CacheService {
@Resource
private CaffeineCacheManager caffeineCacheManager;
/**
* 获取所有缓存名称
*
* @return
*/
public List<String> cacheNames() {
return Lists.newArrayList(caffeineCacheManager.getCacheNames());
}
/**
* 移除某个key
*
* @param cacheName
*/
@SmartReload(ReloadConst.CACHE_SERVICE)
public void removeCache(String cacheName) {
CaffeineCache cache = (CaffeineCache) caffeineCacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
}
}
/**
* 某个缓存下的所有key
*
* @param cacheName
* @return
*/
public List<String> cacheKey(String cacheName) {
CaffeineCache cache = (CaffeineCache) caffeineCacheManager.getCache(cacheName);
if (cache == null) {
return Lists.newArrayList();
}
Set<Object> cacheKey = cache.getNativeCache().asMap().keySet();
return cacheKey.stream().map(e -> e.toString()).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,36 @@
package net.lab1024.sa.common.module.support.captcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.lab1024.sa.common.common.controller.SupportBaseController;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.constant.SwaggerTagConst;
import net.lab1024.sa.common.module.support.captcha.domain.CaptchaVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 图形验证码业务
*
* @Author 1024创新实验室: 胡克
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Api(tags = SwaggerTagConst.Support.CAPTCHA)
@RestController
public class CaptchaController extends SupportBaseController {
@Autowired
private CaptchaService captchaService;
@ApiOperation("获取图形验证码 @author 胡克")
@GetMapping("/captcha")
public ResponseDTO<CaptchaVO> generateCaptcha() {
return ResponseDTO.ok(captchaService.generateCaptcha());
}
}

View File

@@ -0,0 +1,115 @@
package net.lab1024.sa.common.module.support.captcha;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.code.UserErrorCode;
import net.lab1024.sa.common.common.constant.StringConst;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.common.domain.SystemEnvironment;
import net.lab1024.sa.common.common.exception.BusinessException;
import net.lab1024.sa.common.constant.RedisKeyConst;
import net.lab1024.sa.common.module.support.captcha.domain.CaptchaForm;
import net.lab1024.sa.common.module.support.captcha.domain.CaptchaVO;
import net.lab1024.sa.common.module.support.redis.RedisService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Objects;
import java.util.UUID;
/**
* 图形验证码 服务
*
* @Author 1024创新实验室: 胡克
* @Date 2021/8/31 20:52
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Slf4j
@Service
public class CaptchaService {
/**
* 过期时间65秒
*/
private static final long EXPIRE_SECOND = 65L;
@Autowired
private DefaultKaptcha defaultKaptcha;
@Autowired
private SystemEnvironment systemEnvironment;
@Autowired
private RedisService redisService;
/**
* 生成图形验证码
* 默认 1 分钟有效期
*
* @return
*/
public CaptchaVO generateCaptcha() {
String captchaText = defaultKaptcha.createText();
BufferedImage image = defaultKaptcha.createImage(captchaText);
String base64Code;
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
ImageIO.write(image, "jpg", os);
base64Code = Base64Utils.encodeToString(os.toByteArray());
} catch (Exception e) {
log.error("generateCaptcha error:", e);
throw new BusinessException("生成验证码错误");
}
/**
* 返回验证码对象
* 图片 base64格式
*/
// uuid 唯一标识
String uuid = UUID.randomUUID().toString().replace("-", StringConst.EMPTY);
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaUuid(uuid);
captchaVO.setCaptchaBase64Image("data:image/png;base64," + base64Code);
captchaVO.setExpireSeconds(EXPIRE_SECOND);
if (!systemEnvironment.isProd()) {
captchaVO.setCaptchaText(captchaText);
}
String redisCaptchaKey = redisService.generateRedisKey(RedisKeyConst.Support.CAPTCHA, uuid);
redisService.set(redisCaptchaKey, captchaText, EXPIRE_SECOND);
return captchaVO;
}
/**
* 校验图形验证码
*
* @param captchaForm
* @return
*/
public ResponseDTO<String> checkCaptcha(CaptchaForm captchaForm) {
if (StringUtils.isBlank(captchaForm.getCaptchaUuid()) || StringUtils.isBlank(captchaForm.getCaptchaCode())) {
return ResponseDTO.userErrorParam("请输入正确验证码");
}
/**
* 1、校验redis里的验证码
* 2、校验成功后删除redis
*/
String redisCaptchaKey = redisService.generateRedisKey(RedisKeyConst.Support.CAPTCHA, captchaForm.getCaptchaUuid());
String redisCaptchaCode = redisService.get(redisCaptchaKey);
if (StringUtils.isBlank(redisCaptchaCode)) {
return ResponseDTO.userErrorParam("验证码已过期,请刷新重试");
}
if (!Objects.equals(redisCaptchaCode, captchaForm.getCaptchaCode())) {
return ResponseDTO.userErrorParam("验证码错误,请输入正确的验证码");
}
// 删除已使用的验证码
redisService.delete(redisCaptchaKey);
return ResponseDTO.ok();
}
}

View File

@@ -0,0 +1,38 @@
package net.lab1024.sa.common.module.support.captcha.config;
import com.google.common.collect.Lists;
import java.awt.*;
import java.util.List;
import java.util.Random;
/**
* 验证码颜色
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class CaptchaColor {
public static Color getColor() {
List<Color> colors = Lists.newArrayList();
colors.add(new Color(0, 135, 255));
colors.add(new Color(51, 153, 51));
colors.add(new Color(255, 102, 102));
colors.add(new Color(255, 153, 0));
colors.add(new Color(153, 102, 0));
colors.add(new Color(153, 102, 153));
colors.add(new Color(51, 153, 153));
colors.add(new Color(102, 102, 255));
colors.add(new Color(0, 102, 204));
colors.add(new Color(204, 51, 51));
colors.add(new Color(128, 153, 65));
Random random = new Random();
int colorIndex = random.nextInt(10);
return colors.get(colorIndex);
}
}

View File

@@ -0,0 +1,46 @@
package net.lab1024.sa.common.module.support.captcha.config;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
/**
* 验证码配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Configuration
public class CaptchaConfig {
@Bean
public DefaultKaptcha getDefaultKaptcha() {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty("kaptcha.border", "no");
properties.setProperty("kaptcha.border.color", "34,114,200");
properties.setProperty("kaptcha.image.width", "125");
properties.setProperty("kaptcha.image.height", "45");
properties.setProperty("kaptcha.textproducer.char.string", "123456789");
properties.setProperty("kaptcha.textproducer.char.length", "4");
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Arial Narrow,Serif,Helvetica,Tahoma,Times New Roman,Verdana");
properties.setProperty("kaptcha.textproducer.font.size", "38");
properties.setProperty("kaptcha.background.clear.from", "white");
properties.setProperty("kaptcha.background.clear.to", "white");
properties.setProperty("kaptcha.word.impl", CaptchaWordRenderer.class.getName());
properties.setProperty("kaptcha.noise.impl", CaptchaNoise.class.getName());
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}

View File

@@ -0,0 +1,44 @@
package net.lab1024.sa.common.module.support.captcha.config;
import com.google.code.kaptcha.NoiseProducer;
import com.google.code.kaptcha.util.Configurable;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* 验证码加噪处理
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class CaptchaNoise extends Configurable implements NoiseProducer {
public CaptchaNoise() {
}
@Override
public void makeNoise(BufferedImage image, float factorOne, float factorTwo, float factorThree, float factorFour) {
int width = image.getWidth();
int height = image.getHeight();
Graphics2D graph = (Graphics2D) image.getGraphics();
graph.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
graph.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
Random random = new Random();
int noiseLineNum = random.nextInt(3);
if (noiseLineNum == 0) {
noiseLineNum = 1;
}
for (int i = 0; i < noiseLineNum; i++) {
graph.setColor(CaptchaColor.getColor());
graph.drawLine(random.nextInt(width), random.nextInt(height), 10 + random.nextInt(20), 10 + random.nextInt(20));
}
graph.dispose();
}
}

View File

@@ -0,0 +1,74 @@
package net.lab1024.sa.common.module.support.captcha.config;
import com.google.code.kaptcha.text.WordRenderer;
import com.google.code.kaptcha.util.Configurable;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* 验证码字体生成
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class CaptchaWordRenderer extends Configurable implements WordRenderer {
public CaptchaWordRenderer() {
}
@Override
public BufferedImage renderWord(String word, int width, int height) {
int fontSize = this.getConfig().getTextProducerFontSize();
Font[] fonts = this.getConfig().getTextProducerFonts(fontSize);
int charSpace = this.getConfig().getTextProducerCharSpace();
BufferedImage image = new BufferedImage(width, height, 2);
Graphics2D g2D = image.createGraphics();
g2D.setColor(Color.WHITE);
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
hints.add(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2D.setRenderingHints(hints);
g2D.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
FontRenderContext frc = g2D.getFontRenderContext();
Random random = new Random();
int startPosY = (height - fontSize) / 5 + fontSize;
char[] wordChars = word.toCharArray();
Font[] chosenFonts = new Font[wordChars.length];
int[] charWidths = new int[wordChars.length];
int widthNeeded = 0;
int startPosX;
for (startPosX = 0; startPosX < wordChars.length; ++startPosX) {
chosenFonts[startPosX] = fonts[random.nextInt(fonts.length)];
char[] charToDraw = new char[]{wordChars[startPosX]};
GlyphVector gv = chosenFonts[startPosX].createGlyphVector(frc, charToDraw);
charWidths[startPosX] = (int) gv.getVisualBounds().getWidth();
if (startPosX > 0) {
widthNeeded += 2;
}
widthNeeded += charWidths[startPosX];
}
startPosX = (width - widthNeeded) / 2;
for (int i = 0; i < wordChars.length; ++i) {
g2D.setColor(CaptchaColor.getColor());
g2D.setFont(chosenFonts[i].deriveFont(Font.PLAIN));
char[] charToDraw = new char[]{wordChars[i]};
g2D.drawChars(charToDraw, 0, charToDraw.length, startPosX, startPosY);
startPosX = startPosX + charWidths[i] + charSpace;
}
return image;
}
}

View File

@@ -0,0 +1,28 @@
package net.lab1024.sa.common.module.support.captcha.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 图形验证码 表单
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Data
public class CaptchaForm {
@ApiModelProperty(value = "验证码")
@NotBlank(message = "验证码不能为空")
private String captchaCode;
@ApiModelProperty(value = "验证码uuid标识")
@NotBlank(message = "验证码uuid标识不能为空")
private String captchaUuid;
}

View File

@@ -0,0 +1,29 @@
package net.lab1024.sa.common.module.support.captcha.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 图形验证码 VO
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/8/31 20:52
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
@Data
public class CaptchaVO {
@ApiModelProperty("验证码唯一标识")
private String captchaUuid;
@ApiModelProperty("验证码图片内容-生产环境无效")
private String captchaText;
@ApiModelProperty("验证码Base64图片")
private String captchaBase64Image;
@ApiModelProperty("过期时间(秒)")
private Long expireSeconds;
}

View File

@@ -0,0 +1,28 @@
package net.lab1024.sa.common.module.support.changelog.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.lab1024.sa.common.common.enumeration.BaseEnum;
/**
* 更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]
*
* @Author 卓大
* @Date 2022-09-26T14:53:50
* @Copyright 1024创新实验室
*/
@AllArgsConstructor
@Getter
public enum ChangeLogTypeEnum implements BaseEnum {
MAJOR_UPDATE(1, "重大更新"),
FUNCTION_UPDATE(2, "功能更新"),
BUG_FIX(3, "Bug修复"),
;
private final Integer value;
private final String desc;
}

View File

@@ -0,0 +1,38 @@
package net.lab1024.sa.common.module.support.changelog.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.lab1024.sa.common.common.domain.PageResult;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.constant.SwaggerTagConst;
import net.lab1024.sa.common.module.support.changelog.domain.form.ChangeLogQueryForm;
import net.lab1024.sa.common.module.support.changelog.domain.vo.ChangeLogVO;
import net.lab1024.sa.common.module.support.changelog.service.ChangeLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* 系统更新日志 Controller
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@RestController
@Api(tags = SwaggerTagConst.Support.CHANGE_LOG)
public class ChangeLogController {
@Autowired
private ChangeLogService changeLogService;
@ApiOperation("分页查询 @author 卓大")
@PostMapping("/changeLog/queryPage")
public ResponseDTO<PageResult<ChangeLogVO>> queryPage(@RequestBody @Valid ChangeLogQueryForm queryForm) {
return ResponseDTO.ok(changeLogService.queryPage(queryForm));
}
}

View File

@@ -0,0 +1,44 @@
package net.lab1024.sa.common.module.support.changelog.dao;
import java.util.List;
import net.lab1024.sa.common.module.support.changelog.domain.form.ChangeLogQueryForm;
import net.lab1024.sa.common.module.support.changelog.domain.vo.ChangeLogVO;
import net.lab1024.sa.common.module.support.changelog.domain.entity.ChangeLogEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
/**
* 系统更新日志 Dao
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@Mapper
@Component
public interface ChangeLogDao extends BaseMapper<ChangeLogEntity> {
/**
* 分页 查询
*
* @param page
* @param queryForm
* @return
*/
List<ChangeLogVO> queryPage(Page page, @Param("queryForm") ChangeLogQueryForm queryForm);
/**
* 根据版本查询 ChangeLog
*
* @param version
* @return
*/
ChangeLogEntity selectByVersion(@Param("version") String version);
}

View File

@@ -0,0 +1,68 @@
package net.lab1024.sa.common.module.support.changelog.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.Data;
/**
* 系统更新日志
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@Data
@TableName("t_change_log")
public class ChangeLogEntity {
/**
* 更新日志id
*/
@TableId(type = IdType.AUTO)
private Long changeLogId;
/**
* 版本
*/
private String version;
/**
* 更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]
*/
private Integer type;
/**
* 发布人
*/
private String publishAuthor;
/**
* 发布日期
*/
private LocalDate publicDate;
/**
* 更新内容
*/
private String content;
/**
* 跳转链接
*/
private String link;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,46 @@
package net.lab1024.sa.common.module.support.changelog.domain.form;
import io.swagger.annotations.ApiModelProperty;
import java.time.LocalDate;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
import net.lab1024.sa.common.common.swagger.ApiModelPropertyEnum;
import net.lab1024.sa.common.common.validator.enumeration.CheckEnum;
import net.lab1024.sa.common.module.support.changelog.constant.ChangeLogTypeEnum;
/**
* 系统更新日志 新建表单
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@Data
public class ChangeLogAddForm {
@ApiModelProperty(value = "版本", required = true)
@NotBlank(message = "版本 不能为空")
private String version;
@ApiModelPropertyEnum(value = ChangeLogTypeEnum.class, desc = "更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]")
@CheckEnum(value = ChangeLogTypeEnum.class, message = "更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复] 错误", required = true)
private Integer type;
@ApiModelProperty(value = "发布人", required = true)
@NotBlank(message = "发布人 不能为空")
private String publishAuthor;
@ApiModelProperty(value = "发布日期", required = true)
@NotNull(message = "发布日期 不能为空")
private LocalDate publicDate;
@ApiModelProperty(value = "更新内容", required = true)
@NotBlank(message = "更新内容 不能为空")
private String content;
@ApiModelProperty(value = "跳转链接")
private String link;
}

View File

@@ -0,0 +1,42 @@
package net.lab1024.sa.common.module.support.changelog.domain.form;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import net.lab1024.sa.common.common.domain.PageParam;
import net.lab1024.sa.common.common.swagger.ApiModelPropertyEnum;
import net.lab1024.sa.common.common.validator.enumeration.CheckEnum;
import net.lab1024.sa.common.module.support.changelog.constant.ChangeLogTypeEnum;
import java.time.LocalDate;
/**
* 系统更新日志 查询
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@Data
public class ChangeLogQueryForm extends PageParam{
@ApiModelPropertyEnum(value = ChangeLogTypeEnum.class, desc = "更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]")
@CheckEnum(value = ChangeLogTypeEnum.class, message = "更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复] 错误")
private Integer type;
@ApiModelProperty(value = "关键字")
private String keyword;
@ApiModelProperty(value = "发布日期")
private LocalDate publicDateBegin;
@ApiModelProperty(value = "发布日期")
private LocalDate publicDateEnd;
@ApiModelProperty(value = "创建时间")
private LocalDate createTime;
@ApiModelProperty(value = "跳转链接")
private String link;
}

View File

@@ -0,0 +1,50 @@
package net.lab1024.sa.common.module.support.changelog.domain.form;
import io.swagger.annotations.ApiModelProperty;
import java.time.LocalDate;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
import net.lab1024.sa.common.common.swagger.ApiModelPropertyEnum;
import net.lab1024.sa.common.common.validator.enumeration.CheckEnum;
import net.lab1024.sa.common.module.support.changelog.constant.ChangeLogTypeEnum;
/**
* 系统更新日志 更新表单
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@Data
public class ChangeLogUpdateForm {
@ApiModelProperty(value = "更新日志id", required = true)
@NotNull(message = "更新日志id 不能为空")
private Long changeLogId;
@ApiModelProperty(value = "版本", required = true)
@NotBlank(message = "版本 不能为空")
private String version;
@ApiModelPropertyEnum(value = ChangeLogTypeEnum.class, desc = "更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]")
@CheckEnum(value = ChangeLogTypeEnum.class, message = "更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复] 错误", required = true)
private Integer type;
@ApiModelProperty(value = "发布人", required = true)
@NotBlank(message = "发布人 不能为空")
private String publishAuthor;
@ApiModelProperty(value = "发布日期", required = true)
@NotNull(message = "发布日期 不能为空")
private LocalDate publicDate;
@ApiModelProperty(value = "更新内容", required = true)
@NotBlank(message = "更新内容 不能为空")
private String content;
@ApiModelProperty(value = "跳转链接")
private String link;
}

View File

@@ -0,0 +1,49 @@
package net.lab1024.sa.common.module.support.changelog.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.Data;
import net.lab1024.sa.common.common.swagger.ApiModelPropertyEnum;
import net.lab1024.sa.common.module.support.changelog.constant.ChangeLogTypeEnum;
/**
* 系统更新日志 列表VO
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@Data
public class ChangeLogVO {
private Long changeLogId;
@ApiModelProperty(value = "版本")
private String version;
@ApiModelPropertyEnum(value = ChangeLogTypeEnum.class, desc = "更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]")
private Integer type;
@ApiModelProperty(value = "发布人")
private String publishAuthor;
@ApiModelProperty(value = "发布日期")
private LocalDate publicDate;
@ApiModelProperty(value = "更新内容")
private String content;
@ApiModelProperty(value = "跳转链接")
private String link;
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新时间")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,20 @@
package net.lab1024.sa.common.module.support.changelog.manager;
import net.lab1024.sa.common.module.support.changelog.dao.ChangeLogDao;
import net.lab1024.sa.common.module.support.changelog.domain.entity.ChangeLogEntity;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* 系统更新日志 Manager
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@Service
public class ChangeLogManager extends ServiceImpl<ChangeLogDao, ChangeLogEntity> {
}

View File

@@ -0,0 +1,103 @@
package net.lab1024.sa.common.module.support.changelog.service;
import java.util.List;
import net.lab1024.sa.common.module.support.changelog.dao.ChangeLogDao;
import net.lab1024.sa.common.module.support.changelog.domain.entity.ChangeLogEntity;
import net.lab1024.sa.common.module.support.changelog.domain.form.ChangeLogAddForm;
import net.lab1024.sa.common.module.support.changelog.domain.form.ChangeLogQueryForm;
import net.lab1024.sa.common.module.support.changelog.domain.form.ChangeLogUpdateForm;
import net.lab1024.sa.common.module.support.changelog.domain.vo.ChangeLogVO;
import net.lab1024.sa.common.common.util.SmartBeanUtil;
import net.lab1024.sa.common.common.util.SmartPageUtil;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.common.domain.PageResult;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 系统更新日志 Service
*
* @Author 卓大
* @Date 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
@Service
public class ChangeLogService {
@Autowired
private ChangeLogDao changeLogDao;
/**
* 分页查询
*
* @param queryForm
* @return
*/
public PageResult<ChangeLogVO> queryPage(ChangeLogQueryForm queryForm) {
Page<?> page = SmartPageUtil.convert2PageQuery(queryForm);
List<ChangeLogVO> list = changeLogDao.queryPage(page, queryForm);
PageResult<ChangeLogVO> pageResult = SmartPageUtil.convert2PageResult(page, list);
return pageResult;
}
/**
* 添加
*/
public synchronized ResponseDTO<String> add(ChangeLogAddForm addForm) {
ChangeLogEntity existVersion = changeLogDao.selectByVersion(addForm.getVersion());
if (existVersion != null) {
return ResponseDTO.userErrorParam("此版本已经存在");
}
ChangeLogEntity changeLogEntity = SmartBeanUtil.copy(addForm, ChangeLogEntity.class);
changeLogDao.insert(changeLogEntity);
return ResponseDTO.ok();
}
/**
* 更新
*
* @param updateForm
* @return
*/
public synchronized ResponseDTO<String> update(ChangeLogUpdateForm updateForm) {
ChangeLogEntity existVersion = changeLogDao.selectByVersion(updateForm.getVersion());
if (existVersion != null && !updateForm.getChangeLogId().equals(existVersion.getChangeLogId())) {
return ResponseDTO.userErrorParam("此版本已经存在");
}
ChangeLogEntity changeLogEntity = SmartBeanUtil.copy(updateForm, ChangeLogEntity.class);
changeLogDao.updateById(changeLogEntity);
return ResponseDTO.ok();
}
/**
* 批量删除
*
* @param idList
* @return
*/
public synchronized ResponseDTO<String> batchDelete(List<Long> idList) {
if (CollectionUtils.isEmpty(idList)) {
return ResponseDTO.ok();
}
changeLogDao.deleteBatchIds(idList);
return ResponseDTO.ok();
}
/**
* 单个删除
*/
public synchronized ResponseDTO<String> delete(Long changeLogId) {
if (null == changeLogId) {
return ResponseDTO.ok();
}
changeLogDao.deleteById(changeLogId);
return ResponseDTO.ok();
}
}

View File

@@ -0,0 +1,38 @@
package net.lab1024.sa.common.module.support.codegenerator.constant;
import net.lab1024.sa.common.common.enumeration.BaseEnum;
/**
* 删除类型
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2022-06-30 22:15:38
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public enum CodeDeleteEnum implements BaseEnum {
SINGLE("Single", "单个删除"),
BATCH("Batch", "批量删除"),
SINGLE_AND_BATCH("SingleAndBatch", "单个和批量删除");
private String value;
private String desc;
CodeDeleteEnum(String value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Object getValue() {
return value;
}
@Override
public String getDesc() {
return desc;
}
}

View File

@@ -0,0 +1,52 @@
package net.lab1024.sa.common.module.support.codegenerator.constant;
import net.lab1024.sa.common.common.enumeration.BaseEnum;
/**
* 前端组件类型
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2022-06-30 20:15:38
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public enum CodeFrontComponentEnum implements BaseEnum {
INPUT("Input", "输入框"),
INPUT_NUMBER("InputNumber", "数字输入框"),
TEXTAREA("Textarea", " 文本"),
BOOLEAN_SELECT("BooleanSelect", "布尔下拉框"),
ENUM_SELECT("SmartEnumSelect", "枚举下拉"),
DICT_SELECT("DictSelect", "字典下拉"),
DATE("Date", "日期选择"),
DATE_TIME("DateTime", "时间选择"),
FILE_UPLOAD("FileUpload", "文件上传");
private String value;
private String desc;
CodeFrontComponentEnum(String value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Object getValue() {
return value;
}
@Override
public String getDesc() {
return desc;
}
}

View File

@@ -0,0 +1,30 @@
package net.lab1024.sa.common.module.support.codegenerator.constant;
/**
* 常量
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-06-30 22:15:38
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net
*/
public class CodeGeneratorConstant {
/**
* 主键
*/
public final static String PRIMARY_KEY = "PRI";
/**
* 自增
*/
public final static String AUTO_INCREMENT = "auto_increment";
/**
* 默认逻辑删除字段名称
*/
public static String DELETED_FLAG = "deleted_flag";
}

Some files were not shown because too many files have changed in this diff Show More