前言

总结记录一下在开发中经常使用到的一些工具类。

MD5加密

用户的密码一般都会进行加密后在存储到数据库当中,所以一般用到MD5这种加密算法。

这种加密算法最大的特点就是不可逆,只能加密不能解密。

这是挺简单的一种加密算法了,之后还可以考虑加盐和哈希散列,进行更复杂的加密手段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Md5Utils {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错+" + e);
}
}
}

雪花算法

对于MySQL的主键问题,一般都需要使用自增的主键。但是只是从1自增就会因为分库分表产生主键相同的情况。

所以这次使用雪花算法生成一个分布式全局唯一ID

首先要知道雪花算法生成的ID是怎么样的。

分布式ID一般都是Long类型,所以会有64位。

  • 第一部分是一个bit位,是一个标识部分,在java中由于long的最高位是符号位,正数是0,负数是1,一般生成的ID为正数,所以固定为0
  • 第二部分是时间戳,该部分占41bit,这个是毫秒级的时间
  • 第三部分是工作机器ID,占10bit
  • 第四部分是序列号,占12bit,支持同一毫秒内同一个节点可以生成4096个ID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* <p>
* 雪花算法,分布式全局唯一ID
* </p>
* @ClassName SnowFlake
* @Author yww
* @date 2021/4/24 18:39
**/
public class SnowFlake {

/**
* 起始的时间戳
*/
private final static long START_STMP = 1480166465631L;
/**
* 序列号占用的位数
*/
private final static long SEQUENCE_BIT = 12;
/**
* 机器标识占用的位数
*/
private final static long MACHINE_BIT = 5;
/**
* 数据中心占用的位数
*/
private final static long DATACENTER_BIT = 5;
/**
* 每一部分的最大值
*/
private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
/**
* 数据中心
*/
private long datacenterId;
/**
* 机器标识
*/
private long machineId;
/**
* 序列号
*/
private long sequence = 0L;
/**
* 上一次时间戳
*/
private long lastStmp = -1L;

public SnowFlake(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}

/**
* 产生下一个ID
*/
public synchronized long nextId() {
long currStmp = getNewstmp();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}

if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
sequence = 0L;
}

lastStmp = currStmp;

return // 时间戳部分
(currStmp - START_STMP) << TIMESTMP_LEFT
// 数据中心部分
| datacenterId << DATACENTER_LEFT
// 机器标识部分
| machineId << MACHINE_LEFT
// 序列号部分
| sequence;
}

private long getNextMill() {
long mill = getNewstmp();
while (mill <= lastStmp) {
mill = getNewstmp();
}
return mill;
}

private long getNewstmp() {
return System.currentTimeMillis();
}

/**
* 测试
*/
public static void main(String[] args) {

SnowFlake snowFlake = new SnowFlake(2, 3);

for (int i = 0; i < (1 << 12); i++) {
System.out.println(snowFlake.nextId());
}

}


}

参考于:https://github.com/beyondfengyu/SnowFlake

JWT工具类

引入依赖

1
2
3
4
5
6
<!--  jwt  -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.yww.management.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
* <p>
* Token工具类
* 1. Header,记录令牌类型和签名算法
* 2. payload,携带用户信息
* (1) iss(issuer), 签发者
* (2) sub(subject), 面向的主体
* (3) aud(audience), 接收方
* (4) nbf(notBefore), 开始生效生效时间戳
* (5) exp(expiresAt), 过期时间戳
* (6) iat(issuedAt ), 签发时间
* (7) jti(jwtId), 唯一标识
* 3. signature, 签名,防止Token被篡改
* </p>
*
* @ClassName TokenUtil
* @Author yww
* @Date 2022/10/15 14:31
*/
public class TokenUtil {

/**
* 生成Token
*
* @return Token
*/
public static String genToken(String secret) {
// 设置Token头部(不设置也会默认有这两个值)
Map<String, Object> header = new HashMap<String, Object>(2) {
private static final long serialVersionUID = 1L;
{
put("alg", "HMAC512");
put("typ", "JWT");
}
};
// 设置需要放在Token的有效载荷
Map<String, Object> payload = new HashMap<String, Object>(3) {
private static final long serialVersionUID = 1L;
{
put("name", "yww");
put("num", 1141950370);
}
};

Date now = new Date();
Date exp = new Date(now.getTime() + 10);

return JWT.create()
// 设置header
.withHeader(header)
// 设置payload
.withIssuer("yww")
.withSubject("management")
.withAudience("vue-management")
.withNotBefore(now)
.withExpiresAt(exp)
.withIssuedAt(now)
.withJWTId("123456")
.withPayload(payload)
// 签名
.sign(Algorithm.HMAC512(secret));
}

/**
* 解析Token
*
* @param token Token
*/
public static void parse(String token) {
try {
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC512("1")).build();
DecodedJWT decode = jwtVerifier.verify(token);
System.out.println(decode.getHeader());
System.out.println(decode.getPayload());
System.out.println(decode.getSignature());
} catch (AlgorithmMismatchException e) {
System.out.println("签名算法不匹配");
} catch (SignatureVerificationException e) {
System.out.println("签名无效");
} catch (TokenExpiredException e) {
System.out.println("令牌已过期");
} catch (MissingClaimException e) {
System.out.println("缺少要验证的声明");
} catch (IncorrectClaimException e) {
System.out.println("声明包含的值和预期不符合");
} catch (JWTVerificationException e) {
System.out.println("验证中的某一个步骤失败");
}
}

}

响应工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import cn.hutool.json.JSONUtil;
import com.yww.management.common.Result;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* <p>
* 用于接口外响应JSON的响应工具类
* </p>
*
* @ClassName ResponseUtil
* @Author yww
* @Date 2022/10/12 21:30
*/
public class ResponseUtil {

/**
* 用于返回JSON数据
* @param response 请求响应
* @param result 响应封装
*/
public static void response(HttpServletResponse response, Result<Object> result) throws IOException {
// 设置响应的Header
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Cache-Control","no-cache");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
// 设置响应码
response.setStatus(result.getCode());
// 设置响应内容
response.getWriter().write(JSONUtil.toJsonStr(result));
// 将缓存信息刷新到页面
response.getWriter().flush();
}

}

IP工具类

这个工具类主要是用于获取请求的IP地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import cn.hutool.core.util.StrUtil;

import javax.servlet.http.HttpServletRequest;

/**
* <p>
* IP相关工具类
* </p>
*
* @ClassName IpUtil
* @Author yww
* @Date 2022/10/16 14:27
*/
public class IpUtil {

/**
* 获取客户端IP
*
* @param request 请求
* @return IP
*/
public static String getIpAddr(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
// 判断x-forwarded-for是否有IP
String ip = request.getHeader("x-forwarded-for");
if (isNotUnknown(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
// 判断Proxy-Client-IP是否有IP
if (isNotUnknown(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
// 判断X-Forwarded-For是否有IP
if (isNotUnknown(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
// 判断WL-Proxy-Client-IP是否有IP
if (isNotUnknown(ip)) {
ip = request.getHeader("X-Real-IP");
}
// 判断X-Real-IP是否有IP
if (isNotUnknown(ip)) {
ip = request.getRemoteAddr();
}
// 若是IP最后为0:0:0:0:0:0:0:1,就是本地回环地址,否则第一个非unknown的IP地址就是真实地址
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
}

/**
* 从多级反向代理中获得第一个非unknown IP地址
*
* @param ip 获得的IP地址
* @return 第一个非unknown IP地址
*/
@SuppressWarnings("all")
public static String getMultistageReverseProxyIp(String ip) {
// 多级反向代理检测
if (ip != null && ip.indexOf(",") > 0) {
final String[] ips = ip.trim().split(",");
for (String subIp : ips) {
if (isNotUnknown(subIp)) {
ip = subIp;
break;
}
}
}
return ip;
}

/**
* 检测给定字符串是否为未知,多用于检测HTTP请求相关
*
* @param checkString 被检测的字符串
* @return 是否未知
*/
public static boolean isNotUnknown(String checkString) {
return StrUtil.isNotBlank(checkString) && StrUtil.isNotEmpty(checkString) && !"unknown".equalsIgnoreCase(checkString);
}

}

BigDecimal工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
* <p>
* BigDecimal工具类
* 用来提供浮点数的精确运算的工具类
* </p>
*
* @ClassName BigDecimalUtil
* @Author yww
* @Date 2022/10/25 16:47
*/
public class BigDecimalUtil {

/**
* 除法运算默认精度
*/
private static final int DEFAULT_PRECISION = 10;

/**
* 私有化无参构造
*/
private BigDecimalUtil() {}

/**
* 用于浮点数的精确加法
*
* @param value1 浮点数1
* @param value2 浮点数2
* @return 浮点数1 + 浮点数2
*/
public static double addUp(double value1, double value2) {
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.add(b2).doubleValue();
}

/**
* 精确减法
*
* @param value1 浮点数1
* @param value2 浮点数2
* @return 浮点数1 - 浮点数2
*/
public static double subtract(double value1, double value2) {
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.subtract(b2).doubleValue();
}

/**
* 精确乘法
*
* @param value1 浮点数1
* @param value2 浮点数2
* @return 浮点数1 * 浮点数2
*/
public static double multiply(double value1, double value2) {
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.multiply(b2).doubleValue();
}

/**
* 精确除法(使用默认精度)
*
* @param value1 浮点数1
* @param value2 浮点数2
* @return 浮点数1 / 浮点数2
*/
public static double div(double value1, double value2) throws IllegalAccessException {
return div(value1, value2, DEFAULT_PRECISION);
}

/**
* 精确除法(指定精度)
* 四舍五入
*
* @param value1 浮点数1
* @param value2 浮点数2
* @param precision 精度
* @return 浮点数1 / 浮点数2
*/
public static double div(double value1, double value2, int precision) throws IllegalAccessException {
if (precision < 0) {
throw new IllegalAccessException("除法的精度不能小于0");
}
return div(value1, value2, precision, RoundingMode.HALF_UP);
}

/**
* 精确除法(指定精度)
* 指定舍入的策略
*
* @param value1 浮点数1
* @param value2 浮点数2
* @param precision 精度
* @param mode 舍入策略
* @return 浮点数1 / 浮点数2
*/
public static double div(double value1, double value2, int precision, RoundingMode mode) throws IllegalAccessException {
if (precision < 0) {
throw new IllegalAccessException("除法的精度不能小于0");
}
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.divide(b2, precision, mode).doubleValue();
}

}

断言工具类

hutool里也有很多断言的工具类,但是抛出的异常不是自己定义的,所以为了指定抛出的异常,需要自己在定义一个断言工具类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

import cn.hutool.core.util.StrUtil;
import com.yww.management.common.exception.GlobalException;

/**
* <p>
* 断言工具类
* </p>
*
* @ClassName AssertUtils
* @Author yww
* @Date 2022/10/20 11:27
*/
public class AssertUtils {

/**
* 私有化无参构造器
*/
private AssertUtils() {}

/**
* 如果对象为{@code null}, 则抛出异常
*
* @param object 要判断的对象
* @throws GlobalException 全局异常类
*/
public static void notNull(Object object) throws GlobalException {
notNull(object, "不能处理空对象");
}

/**
* 如果对象为{@code null}, 则抛出异常
*
* @param object 要判断的对象
* @param message 断言失败时的错误信息
* @throws GlobalException 全局异常类
*/
public static void notNull(Object object, String message) throws GlobalException {
if (object == null) {
throw new GlobalException(message);
}
}

/**
* 如果字符串为{@code null}、空字符串或仅包含空白字符, 则抛出异常
*
* @param text 要进行检查的字符串
* @throws GlobalException 全局异常类
*/
public static void hasText(String text) throws GlobalException {
hasText(text, "此参数不能为空字符串");
}

/**
* 如果字符串为{@code null}、空字符串或仅包含空白字符, 则抛出异常
*
* @param text 要进行检查的字符串
* @param message 断言失败时的错误信息
* @throws GlobalException 全局异常类
*/
public static void hasText(String text, String message) throws GlobalException {
if (StrUtil.isBlank(text)) {
throw new GlobalException(message);
}
}

}

ThreadLocal工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

import java.util.HashMap;
import java.util.Map;

/**
* <p>
* ThreadLocal工具类
* </p>
*
* @ClassName ThreadLocalUtil
* @Author yww
* @Date 2022/10/21 14:27
*/
@SuppressWarnings("unchecked")
public class ThreadLocalUtil {

/**
* ThreadLocalMap
*/
private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL_MAP = ThreadLocal.withInitial(HashMap::new);

/**
* 私有化无参构造器
*/
private ThreadLocalUtil() {}

/**
* 根据指定的key获取当前线程相关的变量值
*
* @param key 变量的key
* @param <T> 变量值的具体类型
* @return 若无此key对应的变量值, 则返回{@code null}
* @throws ClassCastException 若接收此返回值的变量类型与上下文保存的值的实际类型不匹配, 则抛出异常
*/
public static <T> T get(String key) {
return (T) THREAD_LOCAL_MAP.get().get(key);
}

/**
* 根据指定的key获取当前线程相关的变量值, 若为{@code null}则返回指定的默认值
*
* @param key 变量的key
* @param defaultValue 默认值
* @param <T> 变量值的具体类型
* @return 若无此key对应的变量值, 则返回defaultValue
* @throws ClassCastException 若接收此返回值的变量类型与上下文保存的值的实际类型不匹配, 则抛出异常
*/
public static <T> T get(String key, T defaultValue) {
T value = get(key);
return value == null ? defaultValue : value;
}

/**
* 设置线程相关上下文的变量值
*
* @param key 变量的key
* @param value 变量值
*/
public static void set(String key, Object value) {
THREAD_LOCAL_MAP.get().put(key, value);
}

/**
* 删除指定key的变量
*
* @param key 变量的key
*/
public static void remove(String key) {
THREAD_LOCAL_MAP.get().remove(key);
}

/**
* 清除当前线程相关的上下文
*/
public static void close() {
THREAD_LOCAL_MAP.remove();
}

}

分页工具类

封装分页请求对象

如果需要添加其他的属性筛选,只需要继承该封装对象即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

/**
* <p>
* 分页查询请求对象
* </p>
*
* @ClassName PageVo
* @Author yww
* @Date 2022/10/24 21:18
*/
@SuppressWarnings("all")
@JsonIgnoreProperties(ignoreUnknown = true)
public class PageVo {

/**
* 每页显示行数默认为10
*/
private static final int DEFAULT_SIZE = 10;

/**
* 当前页码, 首页为1
*/
private int page = 1;

/**
* 每页记录条数
*/
private int size = DEFAULT_SIZE;

/**
* 排序字段名,asc,desc
*/
private String sort;

/**
* 排序方向
*/
private String dir;

public static PageVo of(int page, int size) {
PageVo pageReqVo = new PageVo();
pageReqVo.setPage(page);
pageReqVo.setSize(size);
return pageReqVo;
}

public PageVo() {}

@JsonIgnore
public int getOffset() {
return (getPage() - 1) * getSize();
}

public int getPage() {
return page > 0 ? page : 1;
}

public void setPage(int page) {
this.page = page;
}

public int getSize() {
return size > 0 ? size : DEFAULT_SIZE;
}

public void setSize(int size) {
this.size = size;
}

public String getSort() {
return sort;
}

public void setSort(String sort) {
this.sort = sort;
}

public String getDir() {
return dir;
}

public void setDir(String dir) {
this.dir = dir;
}

}

分页结果类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import java.util.List;

/**
* <p>
* 分页查询结果
* </p>
*
* @ClassName PageResultVo
* @Author yww
* @Date 2022/10/24 21:15
*/
@JsonPropertyOrder({"start", "size", "total", "rows"})
public class PageResultVo<T> {

/**
* 本页记录在所有记录中的起始位置
*/
private int start;

/**
* 每页记录条数
*/
private int size;

/**
* 总记录条数
*/
private int total;

/**
* 当前页数据
*/
private List<T> rows;

public static <E> PageResultVo<E> ofReqVo(PageVo reqVo, List<E> rows, int total) {
PageResultVo<E> pageResultVo = new PageResultVo<>();
pageResultVo.setSize(reqVo.getSize());
pageResultVo.setStart(reqVo.getOffset());
pageResultVo.setTotal(total);
pageResultVo.setRows(rows);
return pageResultVo;
}

public PageResultVo() {
}

public int getStart() {
return start;
}

public void setStart(int start) {
this.start = start;
}

public int getSize() {
return size;
}

public void setSize(int size) {
this.size = size;
}

public int getTotal() {
return total;
}

public void setTotal(int total) {
this.total = total;
}

public List<T> getRows() {
return rows;
}

public void setRows(List<T> rows) {
this.rows = rows;
}

}

分页工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.conditions.segments.NormalSegmentList;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yww.management.common.exception.GlobalException;

import java.util.ArrayList;
import java.util.List;

/**
* <p>
* 分页工具类
* </p>
*
* @ClassName PageUtil
* @Author yww
* @Date 2022/10/24 21:21
*/
@SuppressWarnings("all")
public class PageUtil {

/**
* mybatis-plus自带分页插件
*
* @param pageVo
* @param mapper
* @param clazz
* @param <R>
* @param <Q>
* @return
*/
public static <R, Q extends PageVo> PageResultVo<R> paging(Q pageVo, BaseMapper<R> mapper, Class<R> clazz) {
R entity;
try {
entity = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new GlobalException("分页类型转换错误");
}
BeanUtil.copyProperties(pageVo, entity);
IPage<R> page = new Page<>(pageVo.getPage(), pageVo.getSize());
page = mapper.selectPage(page, Wrappers.lambdaQuery(entity));
return PageResultVo.ofReqVo(pageVo, page.getRecords(), Long.valueOf(page.getTotal()).intValue());
}

/**
* mybatis-plus自带分页插件
*
* @param pageVo
* @param mapper
* @param <R>
* @param <Q>
* @return
*/
public static <R, Q extends PageVo> PageResultVo<R> paging(Q pageVo, BaseMapper<R> mapper, LambdaQueryWrapper<R> lambdaQuery, Class<R> clazz) {
R entity;
try {
entity = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new GlobalException("分页类型转换错误");
}
// 已设置在lambdaQuery的字段实体类中不应该重复设置
String[] whereFields = getWhereFields(lambdaQuery);
BeanUtil.copyProperties(pageVo, entity, whereFields);
lambdaQuery.setEntity(entity);
IPage<R> page = new Page<>(pageVo.getPage(), pageVo.getSize());
page = mapper.selectPage(page, lambdaQuery);
return PageResultVo.ofReqVo(pageVo, page.getRecords(), Long.valueOf(page.getTotal()).intValue());
}

/**
* 获取设置的条件字段名
*
* @param lambdaQuery
* @param <R>
* @return
*/
public static <R> String[] getWhereFields(LambdaQueryWrapper<R> lambdaQuery) {
List<String> fields = new ArrayList<>();
MergeSegments expression = lambdaQuery.getExpression();
NormalSegmentList normal = expression.getNormal();
int index = 0;
for (ISqlSegment iSqlSegment : normal) {
if (index % 4 == 0) {
String field = StrUtil.toCamelCase(iSqlSegment.getSqlSegment());
fields.add(field);
}
index++;
}
return fields.toArray(new String[]{});
}

}

文件工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.yww.filebackend.utils;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.yww.filebackend.entity.SysFile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
* <p>
* 文件工具类
* </p>
*
* @author yww
*/
@Slf4j
public class FileUtil {

/**
* 文件保存路径(需要注意)
*/
private final static String FILE_SAVE_PATH = "D:\\file";

/**
* 文件日期部分路径格式
*/
public final static String DATE_FORMAT = "/yyyy/MM/dd/";
/**
* windows下文件日期部分路径格式
*/
private final static String DATE_FORMAT_WIN = "\\yyyy\\MM\\dd\\";

/**
* 保存文件
*
* @param file 文件
* @return 文件信息
*/
public static SysFile saveFile(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
// 获取文件扩展名
String ext = getExtName(originalFilename);
// 生成新的文件名称
String newName = IdUtil.fastSimpleUUID() + "." + ext;
String dateFormat = new SimpleDateFormat(getDateFormat()).format(new Date());
String path = FILE_SAVE_PATH + dateFormat + newName;
File dest = new File(path);
// 若是路径不存在就先创建父级目录
if (!dest.exists()) {
cn.hutool.core.io.FileUtil.touch(dest);
}
// 保存文件
try {
file.transferTo(dest);
} catch (IOException e) {
log.warn("保存文件出错,保存的文件名称为:" + originalFilename);
log.warn("保存文件出错,保存的路径为:" + path);
throw new RuntimeException(e);
}
return SysFile.builder()
.fileName(originalFilename)
.path(path)
.size(file.getSize())
.build();
}

/**
* 获取文件后缀
* 参考自hutool的FileUtil.extName()方法
*
* @param fileName 文件名称
* @return 文件后缀
*/
public static String getExtName(String fileName) {
if (fileName == null) {
return null;
}
int index = fileName.lastIndexOf(".");
if (index == -1) {
return "";
}
// 可能会有两个.符号
int secondToLastIndex = fileName.substring(0, index).lastIndexOf(".");
return fileName.substring(secondToLastIndex == -1 ? index + 1 : secondToLastIndex + 1);
}

/**
* 获取日期格式
* 由于操作系统不一样,所以可能会有不同文件路径格式
*
* @return 日期格式
*/
public static String getDateFormat() {
String osName = System.getProperty("os.name").toLowerCase();
if (StrUtil.isNotBlank(osName) && osName.contains("win")) {
return DATE_FORMAT_WIN;
}
return DATE_FORMAT;
}

}