mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-24 13:05:11 +08:00
commit
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import sun.misc.BASE64Decoder;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2019-present ShenBao
|
||||
*
|
||||
* @author ShenBao <shenbaoone@gmail.com>
|
||||
* @homepage https://github.com/ShenBao/rsa-aes-utils
|
||||
*/
|
||||
public class AESUtil {
|
||||
/**
|
||||
* 随机生成秘钥
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String createAesKey() {
|
||||
try {
|
||||
KeyGenerator kg = KeyGenerator.getInstance("AES");
|
||||
kg.init(128);
|
||||
//要生成多少位,只需要修改这里即可 128, 192 或 256
|
||||
SecretKey sk = kg.generateKey();
|
||||
byte[] b = sk.getEncoded();
|
||||
String k = byteToHexString(b);
|
||||
return k;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成 IV
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String createAesIv() {
|
||||
String iv = RandomStringUtils.randomAlphanumeric(16);
|
||||
return iv.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* byte 数组转化为 16 进制字符串
|
||||
*
|
||||
* @param bytes
|
||||
* @return String
|
||||
*/
|
||||
public static String byteToHexString(byte[] bytes) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
String strHex = Integer.toHexString(bytes[i]);
|
||||
if (strHex.length() > 3) {
|
||||
sb.append(strHex.substring(6));
|
||||
} else {
|
||||
if (strHex.length() < 2) {
|
||||
sb.append("0" + strHex);
|
||||
} else {
|
||||
sb.append(strHex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密 CBC
|
||||
*
|
||||
* @param data String
|
||||
* @param key String
|
||||
* @param iv String
|
||||
* @return String
|
||||
*/
|
||||
public static String encryptByCBC(String data, String key, String iv) throws Exception {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
int blockSize = cipher.getBlockSize();
|
||||
byte[] dataBytes = data.getBytes();
|
||||
int plaintextLength = dataBytes.length;
|
||||
if (plaintextLength % blockSize != 0) {
|
||||
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
|
||||
}
|
||||
byte[] plaintext = new byte[plaintextLength];
|
||||
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
|
||||
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
|
||||
byte[] encrypted = cipher.doFinal(plaintext);
|
||||
return new sun.misc.BASE64Encoder().encode(encrypted);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密 CBC
|
||||
*
|
||||
* @param data String
|
||||
* @param key String
|
||||
* @param iv String
|
||||
* @return String
|
||||
*/
|
||||
public static String decryptByCBC(String data, String key, String iv) throws Exception {
|
||||
try {
|
||||
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
|
||||
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
|
||||
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
|
||||
byte[] original = cipher.doFinal(encrypted1);
|
||||
String originalString = new String(original);
|
||||
return originalString.trim();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密 ECB
|
||||
*
|
||||
* @param data String
|
||||
* @param key String
|
||||
* @return String
|
||||
*/
|
||||
public static String encryptByECB(String data, String key) throws Exception {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||
int blockSize = cipher.getBlockSize();
|
||||
byte[] dataBytes = data.getBytes();
|
||||
int plaintextLength = dataBytes.length;
|
||||
if (plaintextLength % blockSize != 0) {
|
||||
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
|
||||
}
|
||||
byte[] plaintext = new byte[plaintextLength];
|
||||
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyspec);
|
||||
byte[] encrypted = cipher.doFinal(plaintext);
|
||||
return new sun.misc.BASE64Encoder().encode(encrypted);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密 ECB
|
||||
*
|
||||
* @param data String
|
||||
* @param key String
|
||||
* @return String
|
||||
*/
|
||||
public static String decryptByECB(String data, String key) throws Exception {
|
||||
try {
|
||||
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, keyspec);
|
||||
byte[] original = cipher.doFinal(encrypted1);
|
||||
String originalString = new String(original);
|
||||
return originalString.trim();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
113
jsowell-common/src/main/java/com/jsowell/common/util/Arith.java
Normal file
113
jsowell-common/src/main/java/com/jsowell/common/util/Arith.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 精确的浮点数运算
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class Arith {
|
||||
|
||||
/**
|
||||
* 默认除法运算精度
|
||||
*/
|
||||
private static final int DEF_DIV_SCALE = 10;
|
||||
|
||||
/**
|
||||
* 这个类不能实例化
|
||||
*/
|
||||
private Arith() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算。
|
||||
*
|
||||
* @param v1 被加数
|
||||
* @param v2 加数
|
||||
* @return 两个参数的和
|
||||
*/
|
||||
public static double add(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.add(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的减法运算。
|
||||
*
|
||||
* @param v1 被减数
|
||||
* @param v2 减数
|
||||
* @return 两个参数的差
|
||||
*/
|
||||
public static double sub(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.subtract(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算。
|
||||
*
|
||||
* @param v1 被乘数
|
||||
* @param v2 乘数
|
||||
* @return 两个参数的积
|
||||
*/
|
||||
public static double mul(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.multiply(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
|
||||
* 小数点以后10位,以后的数字四舍五入。
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2) {
|
||||
return div(v1, v2, DEF_DIV_SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
|
||||
* 定精度,以后的数字四舍五入。
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @param scale 表示表示需要精确到小数点以后几位。
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
if (b1.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO.doubleValue();
|
||||
}
|
||||
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的小数位四舍五入处理。
|
||||
*
|
||||
* @param v 需要四舍五入的数字
|
||||
* @param scale 小数点后保留几位
|
||||
* @return 四舍五入后的结果
|
||||
*/
|
||||
public static double round(double v, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
BigDecimal b = new BigDecimal(Double.toString(v));
|
||||
BigDecimal one = BigDecimal.ONE;
|
||||
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,706 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.Stack;
|
||||
|
||||
public class BytesUtil {
|
||||
|
||||
static final long fx = 0xffl;
|
||||
|
||||
/**
|
||||
* 将int数值转换为占两个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。
|
||||
*/
|
||||
public static byte[] intToBytes(int value) {
|
||||
//limit 传入2
|
||||
return intToBytes(value, 2);
|
||||
}
|
||||
|
||||
public static byte[] intToBytes(int value, int limit) {
|
||||
byte[] src = new byte[limit];
|
||||
for (int i = 0; i < limit; i++) {
|
||||
int x = 8 * (limit - i - 1);
|
||||
if (x == 0) {
|
||||
src[i] = (byte) (value & 0xFF);
|
||||
} else {
|
||||
src[i] = (byte) ((value >> x) & 0xFF);
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
public static int bytesToInt(byte[] src) {
|
||||
return bytesToInt(src, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取int数值,本方法适用于(低位在后,高位在前)的顺序。
|
||||
*/
|
||||
public static int bytesToInt(byte[] src, int offset) {
|
||||
if (src == null) {
|
||||
return 0;
|
||||
}
|
||||
while (src.length > 0 && src[0] == 0x00) {
|
||||
src = BytesUtil.copyBytes(src, 1, src.length - 1);
|
||||
}
|
||||
if (src.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
int len = src.length;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
int value = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (i == (len - 1)) {
|
||||
value = value | ((src[i] & 0xFF));
|
||||
}
|
||||
value = value | ((src[i] & 0xFF) << (8 * (len - i - 1)));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* long转字节,大端模式
|
||||
*
|
||||
* @param number
|
||||
* @return
|
||||
*/
|
||||
public static byte[] long2Byte(long number) {
|
||||
long temp = number;
|
||||
byte[] b = new byte[8];
|
||||
for (int i = (b.length - 1); i >= 0; i--) {
|
||||
b[i] = new Long(temp & 0xff).byteValue();//
|
||||
//将最低位保存在最低位
|
||||
temp = temp >> 8;// 向右移8位
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节转long 大端模式
|
||||
*
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public static long byte2Long(byte[] b) {
|
||||
long s = 0;
|
||||
long s0 = b[7] & 0xff;
|
||||
long s1 = b[6] & 0xff;
|
||||
long s2 = b[5] & 0xff;
|
||||
long s3 = b[4] & 0xff;
|
||||
long s4 = b[3] & 0xff;
|
||||
long s5 = b[2] & 0xff;
|
||||
long s6 = b[1] & 0xff;
|
||||
long s7 = b[0] & 0xff;
|
||||
|
||||
// s0不变
|
||||
s1 <<= 8;
|
||||
s2 <<= 16;
|
||||
s3 <<= 24;
|
||||
s4 <<= 8 * 4;
|
||||
s5 <<= 8 * 5;
|
||||
s6 <<= 8 * 6;
|
||||
s7 <<= 8 * 7;
|
||||
s = s0 | s1 | s2 | s3 | s4 | s5 | s6 | s7;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个byte数组中拷贝一部分出来
|
||||
*
|
||||
* @param oriBytes
|
||||
* @param startIndex
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public static byte[] copyBytes(byte[] oriBytes, int startIndex, int length) {
|
||||
int endIndex = startIndex + length;
|
||||
byte[] bts = new byte[length];
|
||||
int index = 0;
|
||||
for (int i = 0; i < oriBytes.length; i++) {
|
||||
if (i >= startIndex && i < endIndex) {
|
||||
bts[index] = oriBytes[i];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return bts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将byte[]转为各种进制的字符串
|
||||
*
|
||||
* @param bytes byte[]
|
||||
* @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制
|
||||
* @return 转换后的字符串
|
||||
*/
|
||||
public static String binary(byte[] bytes, int radix) {
|
||||
return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
|
||||
}
|
||||
|
||||
/**
|
||||
* @函数功能: BCD码转为10进制串(阿拉伯数据)
|
||||
* @输入参数: BCD码
|
||||
* @输出结果: 10进制串
|
||||
*/
|
||||
public static String bcd2Str(byte[] bytes) {
|
||||
StringBuffer temp = new StringBuffer(bytes.length * 2);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
temp.append((byte) ((bytes[i] & 0xf0) >>> 4));
|
||||
temp.append((byte) (bytes[i] & 0x0f));
|
||||
}
|
||||
return temp.toString();
|
||||
}
|
||||
|
||||
public static String bcd2StrContainA(byte[] bytes) {
|
||||
char temp[] = new char[bytes.length * 2], val;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
|
||||
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
|
||||
val = (char) (bytes[i] & 0x0f);
|
||||
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
|
||||
}
|
||||
return new String(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @函数功能: BCD码转为10进制串(阿拉伯数据) 小端模式
|
||||
* @输入参数: BCD码
|
||||
* @输出结果: 10进制串
|
||||
*/
|
||||
public static String bcd2StrLittle(byte[] bytes) {
|
||||
Stack<String> strings = new Stack<>();
|
||||
String temp = bcd2Str(bytes);
|
||||
for (int i = 0; i < temp.length(); i = i + 2) {
|
||||
strings.push(temp.substring(i, i + 2));
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
while (!strings.isEmpty()) {
|
||||
stringBuilder.append(strings.pop());
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @函数功能: BCD码转为10进制串(阿拉伯数据) 小端模式
|
||||
* @输入参数: BCD码
|
||||
* @输出结果: 10进制串
|
||||
*/
|
||||
public static String bcd2StrLittleContainA(byte[] bytes) {
|
||||
Stack<String> strings = new Stack<>();
|
||||
String temp = bcd2StrContainA(bytes);
|
||||
for (int i = 0; i < temp.length(); i = i + 2) {
|
||||
strings.push(temp.substring(i, i + 2));
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
while (!strings.isEmpty()) {
|
||||
stringBuilder.append(strings.pop());
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @函数功能: 10进制串转为BCD码
|
||||
* @输入参数: 10进制串
|
||||
* @输出结果: BCD码
|
||||
*/
|
||||
public static byte[] str2Bcd(String asc) {
|
||||
if (asc == null) {
|
||||
asc = "";
|
||||
}
|
||||
int len = asc.length();
|
||||
int mod = len % 2;
|
||||
if (mod != 0) {
|
||||
asc = "0" + asc;
|
||||
len = asc.length();
|
||||
}
|
||||
byte abt[] = new byte[len];
|
||||
if (len >= 2) {
|
||||
len = len / 2;
|
||||
}
|
||||
byte bbt[] = new byte[len];
|
||||
abt = asc.getBytes();
|
||||
int j, k;
|
||||
for (int p = 0; p < asc.length() / 2; p++) {
|
||||
if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
|
||||
j = abt[2 * p] - '0';
|
||||
} else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
|
||||
j = abt[2 * p] - 'a' + 0x0a;
|
||||
} else {
|
||||
j = abt[2 * p] - 'A' + 0x0a;
|
||||
}
|
||||
if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
|
||||
k = abt[2 * p + 1] - '0';
|
||||
} else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
|
||||
k = abt[2 * p + 1] - 'a' + 0x0a;
|
||||
} else {
|
||||
k = abt[2 * p + 1] - 'A' + 0x0a;
|
||||
}
|
||||
int a = (j << 4) + k;
|
||||
byte b = (byte) a;
|
||||
bbt[p] = b;
|
||||
}
|
||||
return bbt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @函数功能: 10进制串转为BCD码
|
||||
* @输入参数: 10进制串
|
||||
* @输出结果: BCD码
|
||||
*/
|
||||
public static byte[] str2BcdLittle(String asc) {
|
||||
byte[] temp = str2Bcd(asc);
|
||||
return revert(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int转为byte 小端模式
|
||||
*
|
||||
* @param value int值
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToBytesLittle(int value) {
|
||||
//limit 传入2
|
||||
return intToBytes(value, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int转换byte 小端模式
|
||||
*
|
||||
* @param value int值
|
||||
* @param limit 保留长度
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToBytesLittle(int value, int limit) {
|
||||
byte[] src = new byte[limit];
|
||||
for (int i = 0; i < limit; i++) {
|
||||
int x = 8 * i;
|
||||
if (x == 0) {
|
||||
src[i] = (byte) (value & 0xFF);
|
||||
} else {
|
||||
src[i] = (byte) ((value >> x) & 0xFF);
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取int数值,本方法适用于(低位在前,高位在后 )的顺序。小端模式
|
||||
*/
|
||||
public static int bytesToIntLittle(byte[] src) {
|
||||
if (src == null) {
|
||||
return 0;
|
||||
}
|
||||
int len = src.length;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
int value = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
value = value | ((src[i] & 0xFF) << (8 * i));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* long转字节,小端模式
|
||||
*
|
||||
* @param number
|
||||
* @param limit 保留字节位
|
||||
* @return
|
||||
*/
|
||||
public static byte[] long2ByteLittle(long number, int limit) {
|
||||
long temp = number;
|
||||
byte[] b = new byte[limit];
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
b[i] = new Long(temp & 0xff).byteValue();//
|
||||
//将最低位保存在最前面
|
||||
temp = temp >> 8;// 向右移8位
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 字节转long 小端模式
|
||||
*
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static long byte2LongLittle(byte[] src) {
|
||||
long s = 0;
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
//防止转为int
|
||||
long si = src[i] & 0xFF;
|
||||
si = si << (8 * i);
|
||||
s = s | si;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用字节数组替换目标数组从指定位置开始替换字节
|
||||
*
|
||||
* @param target 被替换的数组
|
||||
* @param startIndex 开始位置
|
||||
* @param replace 用于替换的数组
|
||||
*/
|
||||
public static void replaceBytes(byte[] target, int startIndex, byte[] replace) {
|
||||
// 暂时由外界保证不会出数组越界的异常
|
||||
for (int i = 0; i < replace.length; i++) {
|
||||
int targetIndex = startIndex + i;
|
||||
target[targetIndex] = replace[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数组
|
||||
*
|
||||
* @param bytes 用于替换的数组
|
||||
*/
|
||||
public static byte[] createByteArray(byte... bytes) {
|
||||
byte[] temp = new byte[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
temp[i] = bytes[i];
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
public static byte[] rightPadBytes(byte[] target, int len, byte b) {
|
||||
int length = target.length;
|
||||
if (len <= length) {
|
||||
return target;
|
||||
}
|
||||
int addedLen = len - length;
|
||||
byte[] added = new byte[addedLen];
|
||||
for (int i = 0; i < addedLen; i++) {
|
||||
added[i] = b;
|
||||
}
|
||||
return Bytes.concat(target, added);
|
||||
}
|
||||
|
||||
public static byte[] rightPadBytes(String targetStr, int len, byte b) {
|
||||
if (targetStr == null) {
|
||||
targetStr = "";
|
||||
}
|
||||
byte[] target = targetStr.getBytes();
|
||||
return rightPadBytes(target, len, b);
|
||||
}
|
||||
|
||||
public static String ascii2StrLittle(byte[] ascs) {
|
||||
byte[] data = revert(ascs);
|
||||
String asciiStr = null;
|
||||
try {
|
||||
asciiStr = new String(data, "ISO8859-1");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return asciiStr;
|
||||
}
|
||||
|
||||
public static String ascii2Str(byte[] ascs) {
|
||||
byte[] data = ascs;
|
||||
String asciiStr = null;
|
||||
try {
|
||||
asciiStr = new String(data, "ISO8859-1");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return asciiStr;
|
||||
}
|
||||
|
||||
public static byte[] str2AscLittle(String str) {
|
||||
return revert(str2Asc(str));
|
||||
}
|
||||
|
||||
public static byte[] str2Asc(String str) {
|
||||
byte[] bytes = null;
|
||||
try {
|
||||
bytes = str.getBytes("ISO8859-1");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反转byte数组
|
||||
*
|
||||
* @param temp
|
||||
* @return
|
||||
*/
|
||||
public static byte[] revert(byte[] temp) {
|
||||
byte[] ret = new byte[temp.length];
|
||||
for (int i = 0; i < temp.length; i++) {
|
||||
ret[temp.length - i - 1] = temp[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cp56time2a 格式转date格式
|
||||
*/
|
||||
public static Date byteCp2Date(byte[] bytes) {
|
||||
if (bytes == null || bytes.length != 7) {
|
||||
return null;
|
||||
}
|
||||
int ms = bytesToIntLittle(copyBytes(bytes, 0, 2));
|
||||
int min = bytesToIntLittle(copyBytes(bytes, 2, 1));
|
||||
int hour = bytesToIntLittle(copyBytes(bytes, 3, 1));
|
||||
int day = bytesToIntLittle(copyBytes(bytes, 4, 1));
|
||||
int month = bytesToIntLittle(copyBytes(bytes, 5, 1));
|
||||
int year = bytesToIntLittle(copyBytes(bytes, 6, 1));
|
||||
if (month == 0 || day == 0 || year == 0) {
|
||||
return null;
|
||||
}
|
||||
LocalDateTime localDateTime = LocalDateTime.of(year + 2000, month, day, hour, min, ms / 1000);
|
||||
Date date = DateUtils.localDateTime2Date(localDateTime);
|
||||
return date;
|
||||
}
|
||||
|
||||
private static String hexStr = "0123456789ABCDEF";
|
||||
private static String[] binaryArray = {
|
||||
"0000", "0001", "0010", "0011",
|
||||
"0100", "0101", "0110", "0111",
|
||||
"1000", "1001", "1010", "1011",
|
||||
"1100", "1101", "1110", "1111"
|
||||
};
|
||||
|
||||
/**
|
||||
* @param bArray
|
||||
* @return 二进制数组转换为二进制字符串 2-2
|
||||
*/
|
||||
public static String bytes2BinStr(byte[] bArray) {
|
||||
String outStr = "";
|
||||
int pos = 0;
|
||||
for (byte b : bArray) {
|
||||
//高四位
|
||||
pos = (b & 0xF0) >> 4;
|
||||
outStr += binaryArray[pos];
|
||||
//低四位
|
||||
pos = b & 0x0F;
|
||||
outStr += binaryArray[pos];
|
||||
}
|
||||
return outStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bytes
|
||||
* @return 将二进制数组转换为十六进制字符串 2-16
|
||||
*/
|
||||
public static String bin2HexStr(byte[] bytes) {
|
||||
String result = "";
|
||||
String hex = "";
|
||||
for (byte aByte : bytes) {
|
||||
//字节高4位
|
||||
hex = String.valueOf(hexStr.charAt((aByte & 0xF0) >> 4));
|
||||
//字节低4位
|
||||
hex += String.valueOf(hexStr.charAt(aByte & 0x0F));
|
||||
result += hex; //+" "
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// byte[] a = new byte[] {0x0C, 0x00, 0x00, 0x00, 0x02, 0x20, 0x22, 0x12, 0x14, 0x00, 0x00, 0x01, 0x00};
|
||||
// byte[] bytes = intToBytes(CRC16Util.calcCrc16(a));
|
||||
// String binary = binary(bytes, 16);
|
||||
// System.out.println(binary);
|
||||
|
||||
// String a = "10";
|
||||
// byte[] bytes = bigDecimal2Bcd(new BigDecimal(a));
|
||||
//
|
||||
// System.out.println(bytes);
|
||||
|
||||
// byte[] startTimeByteArr = new byte[] {(byte) 0x98, (byte) 0xB7, 0x0E, 0x11, 0x10, 0x03, 0x14};
|
||||
// String binary = binary(startTimeByteArr, 16);
|
||||
// String s = DateUtils.decodeCP56Time2a(binary);
|
||||
// System.out.println(s);
|
||||
|
||||
BigDecimal chargeAmount = new BigDecimal("10.5").setScale(2, BigDecimal.ROUND_HALF_UP);
|
||||
byte[] accountBalanceByteArr = BytesUtil.getFloatBytes(chargeAmount.floatValue());
|
||||
|
||||
float aFloat = BytesUtil.getFloat(accountBalanceByteArr);
|
||||
|
||||
System.out.println(aFloat);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param hexString
|
||||
* @return 将十六进制转换为二进制字节数组 16-2
|
||||
*/
|
||||
public static byte[] hexStr2BinArr(String hexString) {
|
||||
//hexString的长度对2取整,作为bytes的长度
|
||||
int len = hexString.length() / 2;
|
||||
byte[] bytes = new byte[len];
|
||||
byte high = 0;//字节高四位
|
||||
byte low = 0;//字节低四位
|
||||
for (int i = 0; i < len; i++) {
|
||||
//右移四位得到高位
|
||||
high = (byte) ((hexStr.indexOf(hexString.charAt(2 * i))) << 4);
|
||||
low = (byte) hexStr.indexOf(hexString.charAt(2 * i + 1));
|
||||
bytes[i] = (byte) (high | low);//高地位做或运算
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param src 16进制字符串
|
||||
* @return 字节数组
|
||||
* @Title hexString2Bytes
|
||||
* @Description 16进制字符串转字节数组
|
||||
*/
|
||||
public static byte[] hexString2Bytes(String src) {
|
||||
int l = src.length() / 2;
|
||||
byte[] ret = new byte[l];
|
||||
for (int i = 0; i < l; i++) {
|
||||
ret[i] = (byte) Integer.valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param hexString
|
||||
* @return 将十六进制转换为二进制字符串 16-2
|
||||
*/
|
||||
public static String hexStr2BinStr(String hexString) {
|
||||
return bytes2BinStr(hexStr2BinArr(hexString));
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验数据长度是否达到要求,如果够长,就直接返回,如果不够,则在数据 后补0 直至到达该长度
|
||||
*
|
||||
* @param msg byte类型数组
|
||||
* @parm length 需要检验的数据长度(为实际字节数)
|
||||
*/
|
||||
public static byte[] checkLengthAndBehindAppendZero(byte[] msg, int length) {
|
||||
String s = BytesUtil.binary(msg, 16);
|
||||
int msgLen = msg.length;
|
||||
if (msgLen < length) {
|
||||
while (msgLen < length) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// 后补零
|
||||
sb.append(s).append("0");
|
||||
s = sb.toString();
|
||||
msgLen = s.length();
|
||||
}
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
return BytesUtil.str2Bcd(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验数据长度是否达到要求,如果够长,就直接返回,如果不够,则在数据 前补0 直至到达该长度
|
||||
*
|
||||
* @param msg byte类型数组
|
||||
* @parm length 需要检验的数据长度(为实际字节数)
|
||||
*/
|
||||
public static byte[] checkLengthAndFrontAppendZero(byte[] msg, int length) {
|
||||
String s = BytesUtil.binary(msg, 16);
|
||||
int msgLen = msg.length;
|
||||
if (msgLen < length) {
|
||||
while (msgLen < length) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// 前补零
|
||||
sb.append("0").append(s);
|
||||
s = sb.toString();
|
||||
msgLen = s.length();
|
||||
}
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
return BytesUtil.str2Bcd(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将金额转换成BCD数组
|
||||
*
|
||||
* @param bigDecimal
|
||||
* @return
|
||||
*/
|
||||
public static byte[] bigDecimal2Bcd(BigDecimal bigDecimal) {
|
||||
int i = Float.floatToIntBits(bigDecimal.floatValue());
|
||||
String hexString = Integer.toHexString(i);
|
||||
return hexString2Bytes(hexString);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* int 转 byte[]
|
||||
* 小端
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static byte[] getIntBytes(int data) {
|
||||
int length = 4;
|
||||
byte[] bytes = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
bytes[i] = (byte) ((data >> (i * 8)) & fx);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* float 转 byte[]
|
||||
* 小端
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static byte[] getFloatBytes(float data) {
|
||||
int intBits = Float.floatToIntBits(data);
|
||||
|
||||
byte[] bytes = getIntBytes(intBits);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* byte[] 转 int
|
||||
*
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static int getInt(byte[] bytes) {
|
||||
int result = (int) ((fx & bytes[0])
|
||||
| ((fx & bytes[1]) << 8)
|
||||
| ((fx & bytes[2]) << 16)
|
||||
| ((fx & bytes[3]) << 24));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* byte[] 转 float
|
||||
*
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public static float getFloat(byte[] b) {
|
||||
int l = getInt(b);
|
||||
return Float.intBitsToFloat(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String转换为byte[]
|
||||
*
|
||||
* @param s String
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] hexStringToByteArray(String s) {
|
||||
int len = s.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
/**
|
||||
* CRC16相关计算
|
||||
* <p>
|
||||
* encode: utf-8
|
||||
*/
|
||||
public class CRC16Util {
|
||||
|
||||
private static byte[] crc16_tab_h = {
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41,
|
||||
(byte) 0x01, (byte) 0xc0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xc1, (byte) 0x81, (byte) 0x40
|
||||
|
||||
};
|
||||
|
||||
private static byte[] crc16_tab_l = {
|
||||
0x00, (byte) 0xc0, (byte) 0xc1, (byte) 0x01, (byte) 0xc3, (byte) 0x03, (byte) 0x02, (byte) 0xc2, (byte) 0xc6, (byte) 0x06, (byte)
|
||||
0x07, (byte) 0xc7, (byte) 0x05, (byte) 0xc5, (byte) 0xc4, (byte) 0x04, (byte) 0xcc, (byte) 0x0c, (byte) 0x0d, (byte) 0xcd, (byte)
|
||||
0x0f, (byte) 0xcf, (byte) 0xce, (byte) 0x0e, (byte) 0x0a, (byte) 0xca, (byte) 0xcb, (byte) 0x0b, (byte) 0xc9, (byte) 0x09, (byte)
|
||||
0x08, (byte) 0xc8, (byte) 0xd8, (byte) 0x18, (byte) 0x19, (byte) 0xd9, (byte) 0x1b, (byte) 0xdb, (byte) 0xda, (byte) 0x1a, (byte)
|
||||
0x1e, (byte) 0xde, (byte) 0xdf, (byte) 0x1f, (byte) 0xdd, (byte) 0x1d, (byte) 0x1c, (byte) 0xdc, (byte) 0x14, (byte) 0xd4, (byte)
|
||||
0xd5, (byte) 0x15, (byte) 0xd7, (byte) 0x17, (byte) 0x16, (byte) 0xd6, (byte) 0xd2, (byte) 0x12, (byte) 0x13, (byte) 0xd3, (byte)
|
||||
0x11, (byte) 0xd1, (byte) 0xd0, (byte) 0x10, (byte) 0xf0, (byte) 0x30, (byte) 0x31, (byte) 0xf1, (byte) 0x33, (byte) 0xf3, (byte)
|
||||
0xf2, (byte) 0x32, (byte) 0x36, (byte) 0xf6, (byte) 0xf7, (byte) 0x37, (byte) 0xf5, (byte) 0x35, (byte) 0x34, (byte) 0xf4, (byte)
|
||||
0x3c, (byte) 0xfc, (byte) 0xfd, (byte) 0x3d, (byte) 0xff, (byte) 0x3f, (byte) 0x3e, (byte) 0xfe, (byte) 0xfa, (byte) 0x3a, (byte)
|
||||
0x3b, (byte) 0xfb, (byte) 0x39, (byte) 0xf9, (byte) 0xf8, (byte) 0x38, (byte) 0x28, (byte) 0xe8, (byte) 0xe9, (byte) 0x29, (byte)
|
||||
0xeb, (byte) 0x2b, (byte) 0x2a, (byte) 0xea, (byte) 0xee, (byte) 0x2e, (byte) 0x2f, (byte) 0xef, (byte) 0x2d, (byte) 0xed, (byte)
|
||||
0xec, (byte) 0x2c, (byte) 0xe4, (byte) 0x24, (byte) 0x25, (byte) 0xe5, (byte) 0x27, (byte) 0xe7, (byte) 0xe6, (byte) 0x26, (byte)
|
||||
0x22, (byte) 0xe2, (byte) 0xe3, (byte) 0x23, (byte) 0xe1, (byte) 0x21, (byte) 0x20, (byte) 0xe0, (byte) 0xa0, (byte) 0x60, (byte)
|
||||
0x61, (byte) 0xa1, (byte) 0x63, (byte) 0xa3, (byte) 0xa2, (byte) 0x62, (byte) 0x66, (byte) 0xa6, (byte) 0xa7, (byte) 0x67, (byte)
|
||||
0xa5, (byte) 0x65, (byte) 0x64, (byte) 0xa4, (byte) 0x6c, (byte) 0xac, (byte) 0xad, (byte) 0x6d, (byte) 0xaf, (byte) 0x6f, (byte)
|
||||
0x6e, (byte) 0xae, (byte) 0xaa, (byte) 0x6a, (byte) 0x6b, (byte) 0xab, (byte) 0x69, (byte) 0xa9, (byte) 0xa8, (byte) 0x68, (byte)
|
||||
0x78, (byte) 0xb8, (byte) 0xb9, (byte) 0x79, (byte) 0xbb, (byte) 0x7b, (byte) 0x7a, (byte) 0xba, (byte) 0xbe, (byte) 0x7e, (byte)
|
||||
0x7f, (byte) 0xbf, (byte) 0x7d, (byte) 0xbd, (byte) 0xbc, (byte) 0x7c, (byte) 0xb4, (byte) 0x74, (byte) 0x75, (byte) 0xb5, (byte)
|
||||
0x77, (byte) 0xb7, (byte) 0xb6, (byte) 0x76, (byte) 0x72, (byte) 0xb2, (byte) 0xb3, (byte) 0x73, (byte) 0xb1, (byte) 0x71, (byte)
|
||||
0x70, (byte) 0xb0, (byte) 0x50, (byte) 0x90, (byte) 0x91, (byte) 0x51, (byte) 0x93, (byte) 0x53, (byte) 0x52, (byte) 0x92, (byte)
|
||||
0x96, (byte) 0x56, (byte) 0x57, (byte) 0x97, (byte) 0x55, (byte) 0x95, (byte) 0x94, (byte) 0x54, (byte) 0x9c, (byte) 0x5c, (byte)
|
||||
0x5d, (byte) 0x9d, (byte) 0x5f, (byte) 0x9f, (byte) 0x9e, (byte) 0x5e, (byte) 0x5a, (byte) 0x9a, (byte) 0x9b, (byte) 0x5b, (byte)
|
||||
0x99, (byte) 0x59, (byte) 0x58, (byte) 0x98, (byte) 0x88, (byte) 0x48, (byte) 0x49, (byte) 0x89, (byte) 0x4b, (byte) 0x8b, (byte)
|
||||
0x8a, (byte) 0x4a, (byte) 0x4e, (byte) 0x8e, (byte) 0x8f, (byte) 0x4f, (byte) 0x8d, (byte) 0x4d, (byte) 0x4c, (byte) 0x8c, (byte)
|
||||
0x44, (byte) 0x84, (byte) 0x85, (byte) 0x45, (byte) 0x87, (byte) 0x47, (byte) 0x46, (byte) 0x86, (byte) 0x82, (byte) 0x42, (byte)
|
||||
0x43, (byte) 0x83, (byte) 0x41, (byte) 0x81, (byte) 0x80, (byte) 0x40
|
||||
};
|
||||
|
||||
private static byte[] crc16_tab_l_old = {
|
||||
0x00, (byte) 0xc0, (byte) 0xc1, (byte) 0x01, (byte) 0xc3, (byte) 0x03, (byte) 0x02, (byte) 0xc2, (byte) 0xc6, (byte) 0x06, (byte)
|
||||
0x07, (byte) 0xc7, (byte) 0x05, (byte) 0xc5, (byte) 0xc4, (byte) 0x04, (byte) 0xcc, (byte) 0x0c, (byte) 0x0d, (byte) 0xcd, (byte)
|
||||
0x0f, (byte) 0xcf, (byte) 0xce, (byte) 0x0e, (byte) 0x0a, (byte) 0xca, (byte) 0xcb, (byte) 0x0b, (byte) 0xc9, (byte) 0x09, (byte)
|
||||
0x08, (byte) 0xc8, (byte) 0xd8, (byte) 0x18, (byte) 0x19, (byte) 0xd9, (byte) 0x1b, (byte) 0xdb, (byte) 0xda, (byte) 0x1a, (byte)
|
||||
0x1e, (byte) 0xde, (byte) 0xdf, (byte) 0x1f, (byte) 0xdd, (byte) 0x1d, (byte) 0x1c, (byte) 0xdc, (byte) 0x14, (byte) 0xd4, (byte)
|
||||
0xd5, (byte) 0x15, (byte) 0xd7, (byte) 0x17, (byte) 0x16, (byte) 0xd6, (byte) 0xd2, (byte) 0x12, (byte) 0x13, (byte) 0xd3, (byte)
|
||||
0x11, (byte) 0xd1, (byte) 0xd0, (byte) 0x10, (byte) 0xf0, (byte) 0x30, (byte) 0x31, (byte) 0xf1, (byte) 0x33, (byte) 0xf3, (byte)
|
||||
0xf2, (byte) 0x32, (byte) 0x36, (byte) 0xf6, (byte) 0xf7, (byte) 0x37, (byte) 0xf5, (byte) 0x35, (byte) 0x34, (byte) 0xf4, (byte)
|
||||
0x3c, (byte) 0xfc, (byte) 0xfd, (byte) 0x3d, (byte) 0xff, (byte) 0x3f, (byte) 0x3e, (byte) 0xfe, (byte) 0xfa, (byte) 0x3a, (byte)
|
||||
0x3b, (byte) 0xfb, (byte) 0x39, (byte) 0xf9, (byte) 0xf8, (byte) 0x40, (byte) 0x28, (byte) 0xe8, (byte) 0xe9, (byte) 0x29, (byte)
|
||||
0xeb, (byte) 0x2b, (byte) 0x2a, (byte) 0xea, (byte) 0xee, (byte) 0x2e, (byte) 0x2f, (byte) 0xef, (byte) 0x2d, (byte) 0xed, (byte)
|
||||
0xec, (byte) 0x2c, (byte) 0xe4, (byte) 0x24, (byte) 0x25, (byte) 0xe5, (byte) 0x27, (byte) 0xe7, (byte) 0xe6, (byte) 0x26, (byte)
|
||||
0x22, (byte) 0xe2, (byte) 0xe3, (byte) 0x23, (byte) 0xe1, (byte) 0x21, (byte) 0x20, (byte) 0xe0, (byte) 0xa0, (byte) 0x60, (byte)
|
||||
0x61, (byte) 0xa1, (byte) 0x63, (byte) 0xa3, (byte) 0xa2, (byte) 0x62, (byte) 0x66, (byte) 0xa6, (byte) 0xa7, (byte) 0x67, (byte)
|
||||
0xa5, (byte) 0x65, (byte) 0x64, (byte) 0xa4, (byte) 0x6c, (byte) 0xac, (byte) 0xad, (byte) 0x6d, (byte) 0xaf, (byte) 0x6f, (byte)
|
||||
0x6e, (byte) 0xae, (byte) 0xaa, (byte) 0x6a, (byte) 0x6b, (byte) 0xab, (byte) 0x69, (byte) 0xa9, (byte) 0xa8, (byte) 0x68, (byte)
|
||||
0x78, (byte) 0xb8, (byte) 0xb9, (byte) 0x79, (byte) 0xbb, (byte) 0x7b, (byte) 0x7a, (byte) 0xba, (byte) 0xbe, (byte) 0x7e, (byte)
|
||||
0x7f, (byte) 0xbf, (byte) 0x7d, (byte) 0xbd, (byte) 0xbc, (byte) 0x7c, (byte) 0xb4, (byte) 0x74, (byte) 0x75, (byte) 0xb5, (byte)
|
||||
0x77, (byte) 0xb7, (byte) 0xb6, (byte) 0x76, (byte) 0x72, (byte) 0xb2, (byte) 0xb3, (byte) 0x73, (byte) 0xb1, (byte) 0x71, (byte)
|
||||
0x70, (byte) 0xb0, (byte) 0x50, (byte) 0x90, (byte) 0x91, (byte) 0x51, (byte) 0x93, (byte) 0x53, (byte) 0x52, (byte) 0x92, (byte)
|
||||
0x96, (byte) 0x56, (byte) 0x57, (byte) 0x97, (byte) 0x55, (byte) 0x95, (byte) 0x94, (byte) 0x54, (byte) 0x9c, (byte) 0x5c, (byte)
|
||||
0x5d, (byte) 0x9d, (byte) 0x5f, (byte) 0x9f, (byte) 0x9e, (byte) 0x5e, (byte) 0x5a, (byte) 0x9a, (byte) 0x9b, (byte) 0x5b, (byte)
|
||||
0x99, (byte) 0x59, (byte) 0x58, (byte) 0x98, (byte) 0x88, (byte) 0x48, (byte) 0x49, (byte) 0x89, (byte) 0x4b, (byte) 0x8b, (byte)
|
||||
0x8a, (byte) 0x4a, (byte) 0x4e, (byte) 0x8e, (byte) 0x8f, (byte) 0x4f, (byte) 0x8d, (byte) 0x4d, (byte) 0x4c, (byte) 0x8c, (byte)
|
||||
0x44, (byte) 0x84, (byte) 0x85, (byte) 0x45, (byte) 0x87, (byte) 0x47, (byte) 0x46, (byte) 0x86, (byte) 0x82, (byte) 0x42, (byte)
|
||||
0x43, (byte) 0x83, (byte) 0x41, (byte) 0x81, (byte) 0x80, (byte) 0x40
|
||||
};
|
||||
|
||||
/**
|
||||
* 一个字节包含位的数量 8
|
||||
*/
|
||||
private static final int BITS_OF_BYTE = 8;
|
||||
|
||||
/**
|
||||
* 多项式
|
||||
*/
|
||||
private static final int POLYNOMIAL = 0x180D;
|
||||
|
||||
/**
|
||||
* 初始值
|
||||
*/
|
||||
private static final int INITIAL_VALUE = 0xFFFF;
|
||||
|
||||
// 测试
|
||||
public static void main(String[] args) {
|
||||
// 序列号域
|
||||
byte[] serialNumber = BytesUtil.str2Bcd("3c40");
|
||||
|
||||
// 加密标志
|
||||
byte[] encryptFlag = BytesUtil.str2Bcd("00");
|
||||
|
||||
// 帧类型标志
|
||||
byte[] frameType = BytesUtil.str2Bcd("03");
|
||||
|
||||
// 消息体
|
||||
byte[] msgBody = BytesUtil.str2Bcd("880000000000270100");
|
||||
|
||||
byte[] data = Bytes.concat(serialNumber, encryptFlag, frameType, msgBody);
|
||||
|
||||
String old_crc = String.format("%04x", CRC16Util.calcCrc16Old(data));
|
||||
String crc = String.format("%04x", CRC16Util.calcCrc16(data));
|
||||
|
||||
System.out.println("old_低位在前,高位在后:" + old_crc);
|
||||
System.out.println("new_低位在前,高位在后:" + crc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bytes 编码内容
|
||||
* @return 编码结果
|
||||
* @function CRC16 编码
|
||||
*/
|
||||
public static int crc16(int[] bytes) {
|
||||
int res = INITIAL_VALUE;
|
||||
for (int data : bytes) {
|
||||
res = res ^ data;
|
||||
for (int i = 0; i < BITS_OF_BYTE; i++) {
|
||||
res = (res & 0x0001) == 1 ? (res >> 1) ^ POLYNOMIAL : res >> 1;
|
||||
}
|
||||
}
|
||||
return revert(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param src 翻转数字
|
||||
* @return 翻转结果
|
||||
* @function 翻转16位的高八位和低八位字节,低位在前,高位在后
|
||||
*/
|
||||
public static int revert(int src) {
|
||||
int lowByte = (src & 0xFF00) >> 8;
|
||||
int highByte = (src & 0x00FF) << 8;
|
||||
return lowByte | highByte;
|
||||
}
|
||||
|
||||
/*以下方法得出的校验位:低位在前,高位在后*/
|
||||
|
||||
/**
|
||||
* @param data 需要计算的数组
|
||||
* @return CRC16校验值
|
||||
* @function 计算CRC16校验
|
||||
*/
|
||||
public static int calcCrc16(byte[] data) {
|
||||
return calcCrc16(data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data 需要计算的数组
|
||||
* @param offset 起始位置
|
||||
* @param len 长度
|
||||
* @return CRC16校验值
|
||||
* @function 计算CRC16校验
|
||||
*/
|
||||
public static int calcCrc16(byte[] data, int offset, int len) {
|
||||
return calcCrc16(data, offset, len, 0xffff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data 需要计算的数组
|
||||
* @param offset 起始位置
|
||||
* @param len 长度
|
||||
* @param preval 之前的校验值
|
||||
* @return CRC16校验值
|
||||
* @function 计算CRC16校验
|
||||
*/
|
||||
public static int calcCrc16(byte[] data, int offset, int len, int preval) {
|
||||
int ucCRCHi = (preval & 0xff00) >> 8;
|
||||
int ucCRCLo = preval & 0x00ff;
|
||||
int iIndex;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
iIndex = (ucCRCLo ^ data[offset + i]) & 0x00ff;
|
||||
ucCRCLo = ucCRCHi ^ crc16_tab_h[iIndex];
|
||||
ucCRCHi = crc16_tab_l[iIndex];
|
||||
}
|
||||
return revert(((ucCRCHi & 0x00ff) << 8) | (ucCRCLo & 0x00ff) & 0xffff);
|
||||
}
|
||||
|
||||
/////////////////////////使用老协议中的低位码表////////////////////////////////////////////////////////
|
||||
|
||||
public static int calcCrc16Old(byte[] data) {
|
||||
return calcCrc16Old(data, 0, data.length);
|
||||
}
|
||||
|
||||
public static int calcCrc16Old(byte[] data, int offset, int len) {
|
||||
return calcCrc16Old(data, offset, len, 0xffff);
|
||||
}
|
||||
|
||||
public static int calcCrc16Old(byte[] data, int offset, int len, int preval) {
|
||||
int ucCRCHi = (preval & 0xff00) >> 8;
|
||||
int ucCRCLo = preval & 0x00ff;
|
||||
int iIndex;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
iIndex = (ucCRCLo ^ data[offset + i]) & 0x00ff;
|
||||
ucCRCLo = ucCRCHi ^ crc16_tab_h[iIndex];
|
||||
ucCRCHi = crc16_tab_l_old[iIndex];
|
||||
}
|
||||
return revert(((ucCRCHi & 0x00ff) << 8) | (ucCRCLo & 0x00ff) & 0xffff);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.jsowell.common.util.Cp56Time2a;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2023/2/28 16:50
|
||||
*/
|
||||
public class Cp56Time2aUtil {
|
||||
|
||||
/**
|
||||
* int 转换成 byte数组
|
||||
*/
|
||||
public static byte[] intToByteArray(int i) {
|
||||
byte[] result = new byte[4];
|
||||
result[0] = (byte) ((i >> 24) & 0xFF);
|
||||
result[1] = (byte) ((i >> 16) & 0xFF);
|
||||
result[2] = (byte) ((i >> 8) & 0xFF);
|
||||
result[3] = (byte) (i & 0xFF);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期转换成 CP56Time2a
|
||||
*
|
||||
* @param date Date类型日期
|
||||
* @return {@link Byte}
|
||||
*/
|
||||
public static byte[] date2Hbyte(Date date) {
|
||||
ByteArrayOutputStream bOutput = new ByteArrayOutputStream();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
// 毫秒需要转换成两个字节其中 低位在前高位在后
|
||||
// 先转换成short
|
||||
int millisecond = calendar.get(Calendar.SECOND) * 1000 + calendar.get(Calendar.MILLISECOND);
|
||||
|
||||
// 默认的高位在前
|
||||
byte[] millisecondByte = intToByteArray(millisecond);
|
||||
bOutput.write(millisecondByte[3]);
|
||||
bOutput.write(millisecondByte[2]);
|
||||
|
||||
// 分钟 只占6个比特位 需要把前两位置为零
|
||||
bOutput.write((byte) calendar.get(Calendar.MINUTE));
|
||||
// 小时需要把前三位置零
|
||||
bOutput.write((byte) calendar.get(Calendar.HOUR_OF_DAY));
|
||||
// 星期日的时候 week 是0
|
||||
int week = 0;
|
||||
// if (week == Calendar.SUNDAY) {
|
||||
// week = 7;
|
||||
// } else {
|
||||
// week--;
|
||||
// }
|
||||
// 前三个字节是 星期 因此需要将星期向左移5位 后五个字节是日期 需要将两个数字相加 相加之前需要先将前三位置零
|
||||
bOutput.write((byte) (week << 5) + (calendar.get(Calendar.DAY_OF_MONTH)));
|
||||
// 前四字节置零
|
||||
bOutput.write((byte) ((byte) calendar.get(Calendar.MONTH) + 1));
|
||||
bOutput.write((byte) (calendar.get(Calendar.YEAR) - 2000));
|
||||
return bOutput.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* CP56Time2a转换成 时间
|
||||
*
|
||||
* @param dataByte 数据报文
|
||||
*/
|
||||
public static Date byte2Hdate(byte[] dataByte) {
|
||||
int year = (dataByte[6] & 0x7F) + 2000;
|
||||
int month = dataByte[5] & 0x0F;
|
||||
int day = dataByte[4] & 0x1F;
|
||||
int hour = dataByte[3] & 0x1F;
|
||||
int minute = dataByte[2] & 0x3F;
|
||||
int second = dataByte[1] > 0 ? dataByte[1] : (dataByte[1] & 0xff);
|
||||
int millisecond = dataByte[0] > 0 ? dataByte[0] : (dataByte[0] & 0xff);
|
||||
millisecond = (second << 8) + millisecond;
|
||||
second = millisecond / 1000;
|
||||
millisecond = millisecond % 1000;
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.YEAR, year);
|
||||
calendar.set(Calendar.MONTH, month);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, day);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||
calendar.set(Calendar.MINUTE, minute);
|
||||
calendar.set(Calendar.SECOND, second);
|
||||
calendar.set(Calendar.MILLISECOND, millisecond);
|
||||
return calendar.getTime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,813 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
||||
static Logger log = LoggerFactory.getLogger(DateUtils.class);
|
||||
|
||||
public static String YYYY = "yyyy";
|
||||
|
||||
public static String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
|
||||
public static String YYYYMMDDHHMM = "yyyyMMddHHmm";
|
||||
|
||||
public static String YYMMDDHHMMSS = "yyMMddHHmmss";
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static String RFC3339 = "yyyy-MM-dd'T'HH:mm:ssXXX";
|
||||
|
||||
private static String[] parsePatterns = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
|
||||
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
|
||||
|
||||
/**
|
||||
* 获取当前Date型日期
|
||||
*
|
||||
* @return Date() 当前日期
|
||||
*/
|
||||
public static Date getNowDate() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期, 默认格式为yyyy-MM-dd
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String getDate() {
|
||||
return dateTimeNow(YYYY_MM_DD);
|
||||
}
|
||||
|
||||
public static String getTime() {
|
||||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
public static String dateTimeNow() {
|
||||
return dateTimeNow(YYYYMMDDHHMMSS);
|
||||
}
|
||||
|
||||
public static String dateTimeNow(final String format) {
|
||||
return parseDateToStr(format, new Date());
|
||||
}
|
||||
|
||||
public static String dateTime(final Date date) {
|
||||
return parseDateToStr(YYYY_MM_DD, date);
|
||||
}
|
||||
|
||||
public static String parseDateToStr(final String format, final Date date) {
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
|
||||
public static String timeStampToRfc3339(long timeStamp) {
|
||||
Date date = new Date(timeStamp);
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(RFC3339);
|
||||
String formatDate = simpleDateFormat.format(date);
|
||||
return formatDate;
|
||||
}
|
||||
|
||||
public static LocalDateTime toLocalDateTime(String str, String format) {
|
||||
if (StringUtils.isBlank(str)) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(format)) {
|
||||
format = YYYY_MM_DD_HH_MM_SS;
|
||||
}
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern(format);
|
||||
return LocalDateTime.parse(str, df);
|
||||
}
|
||||
|
||||
public static Date dateTime(final String format, final String ts) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(ts);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如2018/08/08
|
||||
*/
|
||||
public static String datePath() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyy/MM/dd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如20180808
|
||||
*/
|
||||
public static String dateTime() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyyMMdd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期型字符串转化为日期 格式
|
||||
*/
|
||||
public static Date parseDate(Object str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return parseDate(str.toString(), parsePatterns);
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// String str = "2023-01-07 11:17:12";
|
||||
// Date date = parseDate(str);
|
||||
// String str1 = parseDateToStr(YYYY_MM_DD_HH_MM_SS, date);
|
||||
// System.out.println(str1);
|
||||
//
|
||||
//
|
||||
// Date date1 = addMinute(new Date(), -15);
|
||||
// String s = parseDateToStr(YYYY_MM_DD_HH_MM_SS, date1);
|
||||
// System.out.println(s);
|
||||
//
|
||||
// String time = getDate();
|
||||
// System.out.println(time);
|
||||
//
|
||||
// System.out.println(dateTimeNow("yyyy-MM-dd HH:mm"));
|
||||
|
||||
// String s2 = formatDateTime(new Date());
|
||||
// System.out.println(s2);
|
||||
|
||||
String time = DateUtils.getTime();
|
||||
System.out.println(time);
|
||||
|
||||
System.out.println(DateUtils.getDate());
|
||||
// String s = date2HexStr(new Date());
|
||||
// String s = BytesUtil.binary(bytes, 16);
|
||||
// System.out.println(s);
|
||||
|
||||
String hexString = "9401270a1b0217";
|
||||
byte[] bytes1 = BytesUtil.hexStringToByteArray(hexString);
|
||||
// String s1 = toDateString(bytes1);
|
||||
// Date date = CP56Time2aToDate(bytes1);
|
||||
// System.out.println(s1);
|
||||
|
||||
// String encodeCP56Time2a = DateUtils.encodeCP56Time2a(new Date());
|
||||
// byte[] bytes = BytesUtil.hexString2Bytes(encodeCP56Time2a);
|
||||
// String s4 = CP56Time2aToDateStr(bytes);
|
||||
// // byte[] msg = BytesUtil.str2Bcd("88000000000021" + encodeCP56Time2a);
|
||||
// // Date date1 = toDate(bytes);
|
||||
// // String s3 = formatDateTime(date1);
|
||||
// // System.out.println(s3);
|
||||
// System.out.println(s4);
|
||||
long chargingTime = DateUtils.intervalTime("2023-02-24 16:00:00", "2023-02-24 17:03:06");
|
||||
System.out.println(chargingTime);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器启动时间
|
||||
*/
|
||||
public static Date getServerStartDate() {
|
||||
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
|
||||
return new Date(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算相差天数
|
||||
*/
|
||||
public static int differentDaysByMillisecond(Date date1, Date date2) {
|
||||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间差
|
||||
*/
|
||||
public static String getDatePoor(Date endDate, Date nowDate) {
|
||||
long nd = 1000 * 24 * 60 * 60;
|
||||
long nh = 1000 * 60 * 60;
|
||||
long nm = 1000 * 60;
|
||||
long ns = 1000;
|
||||
// 获得两个时间的毫秒时间差异
|
||||
long diff = endDate.getTime() - nowDate.getTime();
|
||||
// 计算差多少天
|
||||
long day = diff / nd;
|
||||
// 计算差多少小时
|
||||
long hour = diff % nd / nh;
|
||||
// 计算差多少分钟
|
||||
long min = diff % nd % nh / nm;
|
||||
// 计算差多少秒//输出结果
|
||||
long sec = diff % nd % nh % nm / ns;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (day != 0) {
|
||||
sb.append(day).append("天");
|
||||
}
|
||||
if (hour != 0) {
|
||||
sb.append(hour).append("小时");
|
||||
}
|
||||
if (min != 0) {
|
||||
sb.append(min).append("分");
|
||||
}
|
||||
if (sec != 0) {
|
||||
sb.append(sec).append("秒");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDateTime转Date
|
||||
*
|
||||
* @param localDateTime
|
||||
* @return
|
||||
*/
|
||||
public static Date localDateTime2Date(LocalDateTime localDateTime) {
|
||||
if (localDateTime == null) {
|
||||
return null;
|
||||
}
|
||||
ZoneId zone = ZoneId.systemDefault();
|
||||
Instant instant = localDateTime.atZone(zone).toInstant();
|
||||
return Date.from(instant);
|
||||
}
|
||||
|
||||
public static Date localDate2Date(LocalDate localDate) {
|
||||
if (localDate == null) {
|
||||
return null;
|
||||
}
|
||||
LocalDateTime localDateTime = LocalDateTime.of(localDate, LocalTime.of(0, 0, 0));
|
||||
return localDateTime2Date(localDateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Date转LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime date2LocalDateTime(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取云快充协议所需时间段
|
||||
* 0: 00~0: 30 时段费率号
|
||||
* 0: 30~1: 00 时段费率号
|
||||
* ……
|
||||
* 23: 00~23: 30 时段费率号
|
||||
* 23: 30~0: 00 时段费率号
|
||||
*/
|
||||
public static List<String> getPeriodOfTime() {
|
||||
int intervalMinutes = 30;
|
||||
// 结果集
|
||||
List<String> resultList = Lists.newArrayList();
|
||||
LocalTime startTime = LocalTime.of(0, 0);
|
||||
|
||||
LocalTime tempStartDateTime = startTime;
|
||||
LocalTime tempEndDateTime = null;
|
||||
while (true) {
|
||||
// 获取加intervalMinutes分钟后的时间
|
||||
tempEndDateTime = tempStartDateTime.plusMinutes(intervalMinutes);
|
||||
resultList.add(tempStartDateTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")) +
|
||||
"-" + tempEndDateTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
|
||||
// 下次的开始时间,为上次的结束时间
|
||||
tempStartDateTime = tempEndDateTime;
|
||||
// 当tempStartDateTime等于startTime时,停止
|
||||
if (tempStartDateTime.compareTo(startTime) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 特殊处理
|
||||
resultList = resultList.subList(0, resultList.size() - 1);
|
||||
// 每天的最后半小时
|
||||
resultList.add("23:30:00-23:59:59");
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时分秒转LocalTime
|
||||
* 可以是 10:10:23 或者 10:10
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static LocalTime getLocalTime(String time) {
|
||||
if (StringUtils.equals("24:00", time)) {
|
||||
// System.out.println("time为24:00, 转换为23:59");
|
||||
time = "23:59";
|
||||
}
|
||||
List<String> list = Lists.newArrayList(time.split(":"));
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
if (list.size() == 2) {
|
||||
return LocalTime.of(Integer.parseInt(list.get(0)), Integer.parseInt(list.get(1)));
|
||||
} else if (list.size() == 3) {
|
||||
return LocalTime.of(Integer.parseInt(list.get(0)), Integer.parseInt(list.get(1)), Integer.parseInt(list.get(2)));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public enum IntervalType {
|
||||
DAY,
|
||||
HOUR,
|
||||
MINUTE,
|
||||
SECOND,
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间切割
|
||||
*
|
||||
* @param startTime 被切割的开始时间
|
||||
* @param endTime 被切割的结束时间
|
||||
* @param intervalType
|
||||
* @param interval >0
|
||||
* @return
|
||||
*/
|
||||
public static List<DateSplit> splitDate(Date startTime, Date endTime, IntervalType intervalType, int interval) {
|
||||
if (interval < 0) {
|
||||
return null;
|
||||
}
|
||||
if (endTime.getTime() <= startTime.getTime()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (intervalType == IntervalType.DAY) {
|
||||
return splitByDay(startTime, endTime, interval);
|
||||
}
|
||||
if (intervalType == IntervalType.HOUR) {
|
||||
return splitByHour(startTime, endTime, interval);
|
||||
}
|
||||
if (intervalType == IntervalType.MINUTE) {
|
||||
return splitByMinute(startTime, endTime, interval);
|
||||
}
|
||||
if (intervalType == IntervalType.SECOND) {
|
||||
return splitBySecond(startTime, endTime, interval);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照小时切割时间区间
|
||||
*/
|
||||
public static List<DateSplit> splitByHour(Date startTime, Date endTime, int intervalHours) {
|
||||
if (endTime.getTime() <= startTime.getTime()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<DateSplit> dateSplits = new ArrayList<>(256);
|
||||
|
||||
DateSplit param = new DateSplit();
|
||||
param.setStartDateTime(startTime);
|
||||
param.setEndDateTime(endTime);
|
||||
param.setEndDateTime(addHour(startTime, intervalHours));
|
||||
while (true) {
|
||||
param.setStartDateTime(startTime);
|
||||
Date tempEndTime = addHour(startTime, intervalHours);
|
||||
if (tempEndTime.getTime() >= endTime.getTime()) {
|
||||
tempEndTime = endTime;
|
||||
}
|
||||
param.setEndDateTime(tempEndTime);
|
||||
|
||||
dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime()));
|
||||
|
||||
startTime = addHour(startTime, intervalHours);
|
||||
if (startTime.getTime() >= endTime.getTime()) {
|
||||
break;
|
||||
}
|
||||
if (param.getEndDateTime().getTime() >= endTime.getTime()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dateSplits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照秒切割时间区间
|
||||
* getRealTimeDetectionData
|
||||
*/
|
||||
public static List<DateSplit> splitBySecond(Date startTime, Date endTime, int intervalSeconds) {
|
||||
if (endTime.getTime() <= startTime.getTime()) {
|
||||
return null;
|
||||
}
|
||||
List<DateSplit> dateSplits = new ArrayList<>(256);
|
||||
|
||||
DateSplit param = new DateSplit();
|
||||
param.setStartDateTime(startTime);
|
||||
param.setEndDateTime(endTime);
|
||||
param.setEndDateTime(addSecond(startTime, intervalSeconds));
|
||||
while (true) {
|
||||
param.setStartDateTime(startTime);
|
||||
Date tempEndTime = addSecond(startTime, intervalSeconds);
|
||||
if (tempEndTime.getTime() >= endTime.getTime()) {
|
||||
tempEndTime = endTime;
|
||||
}
|
||||
param.setEndDateTime(tempEndTime);
|
||||
|
||||
dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime()));
|
||||
|
||||
startTime = addSecond(startTime, intervalSeconds);
|
||||
if (startTime.getTime() >= endTime.getTime()) {
|
||||
break;
|
||||
}
|
||||
if (param.getEndDateTime().getTime() >= endTime.getTime()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dateSplits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照天切割时间区间
|
||||
*/
|
||||
public static List<DateSplit> splitByDay(Date startTime, Date endTime, int intervalDays) {
|
||||
if (endTime.getTime() <= startTime.getTime()) {
|
||||
return null;
|
||||
}
|
||||
List<DateSplit> dateSplits = new ArrayList<>(256);
|
||||
|
||||
DateSplit param = new DateSplit();
|
||||
param.setStartDateTime(startTime);
|
||||
param.setEndDateTime(endTime);
|
||||
param.setEndDateTime(addDay(startTime, intervalDays));
|
||||
while (true) {
|
||||
param.setStartDateTime(startTime);
|
||||
Date tempEndTime = addDay(startTime, intervalDays);
|
||||
if (tempEndTime.getTime() >= endTime.getTime()) {
|
||||
tempEndTime = endTime;
|
||||
}
|
||||
param.setEndDateTime(tempEndTime);
|
||||
|
||||
dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime()));
|
||||
|
||||
startTime = addDay(startTime, intervalDays);
|
||||
if (startTime.getTime() >= endTime.getTime()) {
|
||||
break;
|
||||
}
|
||||
if (param.getEndDateTime().getTime() >= endTime.getTime()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dateSplits;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 按照分钟切割时间区间
|
||||
*
|
||||
* @param startTime
|
||||
* @param endTime
|
||||
* @param intervalMinutes
|
||||
* @return
|
||||
*/
|
||||
public static List<DateSplit> splitByMinute(Date startTime, Date endTime, int intervalMinutes) {
|
||||
if (endTime.getTime() <= startTime.getTime()) {
|
||||
return null;
|
||||
}
|
||||
List<DateSplit> dateSplits = new ArrayList<>(256);
|
||||
|
||||
DateSplit param = new DateSplit();
|
||||
param.setStartDateTime(startTime);
|
||||
param.setEndDateTime(endTime);
|
||||
param.setEndDateTime(addMinute(startTime, intervalMinutes));
|
||||
while (true) {
|
||||
param.setStartDateTime(startTime);
|
||||
Date tempEndTime = addMinute(startTime, intervalMinutes);
|
||||
if (tempEndTime.getTime() >= endTime.getTime()) {
|
||||
tempEndTime = endTime;
|
||||
}
|
||||
param.setEndDateTime(tempEndTime);
|
||||
|
||||
dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime()));
|
||||
|
||||
startTime = addMinute(startTime, intervalMinutes);
|
||||
if (startTime.getTime() >= endTime.getTime()) {
|
||||
break;
|
||||
}
|
||||
if (param.getEndDateTime().getTime() >= endTime.getTime()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dateSplits;
|
||||
}
|
||||
|
||||
private static Date addDay(Date date, int days) {
|
||||
return add(date, Calendar.DAY_OF_MONTH, days);
|
||||
}
|
||||
|
||||
private static Date addHour(Date date, int hours) {
|
||||
return add(date, Calendar.HOUR_OF_DAY, hours);
|
||||
}
|
||||
|
||||
public static Date addMinute(Date date, int minute) {
|
||||
return add(date, Calendar.MINUTE, minute);
|
||||
}
|
||||
|
||||
private static Date addSecond(Date date, int second) {
|
||||
return add(date, Calendar.SECOND, second);
|
||||
}
|
||||
|
||||
private static Date add(final Date date, final int calendarField, final int amount) {
|
||||
final Calendar c = Calendar.getInstance();
|
||||
c.setTime(date);
|
||||
c.add(calendarField, amount);
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
public static String formatDateTime(Date date) {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
|
||||
return simpleDateFormat.format(date);
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class DateSplit {
|
||||
private Date startDateTime;
|
||||
private Date endDateTime;
|
||||
|
||||
public String getStartDateTimeStr() {
|
||||
return formatDateTime(startDateTime);
|
||||
}
|
||||
|
||||
public String getEndDateTimeStr() {
|
||||
return formatDateTime(endDateTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CP56Time2a 转 日期字符串
|
||||
* 0x3b交易记录 会使用这个方法,解析开始时间 结束时间 交易时间
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
// public static String CP56Time2aToDateStr(byte[] bytes) {
|
||||
// Date date = CP56Time2aToDate(bytes);
|
||||
// if (date == null) {
|
||||
// return null;
|
||||
// }
|
||||
// return DateUtils.formatDateTime(date);
|
||||
// }
|
||||
// public static String toDateString(byte[] bytes) {
|
||||
// return Cp56Time2aUtil.toDateString(bytes);
|
||||
// }
|
||||
|
||||
/**
|
||||
* cp56time2a 格式转date格式
|
||||
*/
|
||||
// public static Date CP56Time2aToDate(byte[] bytes) {
|
||||
// if (bytes == null || bytes.length != 7) {
|
||||
// return null;
|
||||
// }
|
||||
// int ms = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(bytes, 0, 2));
|
||||
// int min = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(bytes, 2, 1));
|
||||
// int hour = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(bytes, 3, 1));
|
||||
// int day = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(bytes, 4, 1));
|
||||
// int month = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(bytes, 5, 1));
|
||||
// int year = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(bytes, 6, 1));
|
||||
// if (month == 0 || day == 0 || year == 0) {
|
||||
// return null;
|
||||
// }
|
||||
// LocalDateTime localDateTime = LocalDateTime.of(year + 2000, month, day, hour, min, ms / 1000);
|
||||
// Date date = DateUtils.localDateTime2Date(localDateTime);
|
||||
// return date;
|
||||
// }
|
||||
|
||||
// public static Date CP56Time2aToDate(byte[] bytes) {
|
||||
// return Cp56Time2aUtil.toDate(bytes);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 时间转16进制字符串
|
||||
* Date转成CP56Time2a
|
||||
* 发送对时请求,会用到这个方法
|
||||
*/
|
||||
// public static String encodeCP56Time2a(Date date) {
|
||||
// // Calendar calendar = Calendar.getInstance();
|
||||
// // calendar.setTime(date);
|
||||
// // StringBuilder builder = new StringBuilder();
|
||||
// // String milliSecond = String.format("%04X", (calendar.get(Calendar.SECOND) * 1000) + calendar.get(Calendar.MILLISECOND));
|
||||
// // builder.append(milliSecond.substring(2, 4));
|
||||
// // builder.append(milliSecond.substring(0, 2));
|
||||
// // builder.append(String.format("%02X", calendar.get(Calendar.MINUTE) & 0x3F));
|
||||
// // builder.append(String.format("%02X", calendar.get(Calendar.HOUR_OF_DAY) & 0x1F));
|
||||
// // int week = calendar.get(Calendar.DAY_OF_WEEK);
|
||||
// // if (week == Calendar.SUNDAY)
|
||||
// // week = 7;
|
||||
// // else week--;
|
||||
// // builder.append(String.format("%02X", (week << 5) + (calendar.get(Calendar.DAY_OF_MONTH) & 0x1F)));
|
||||
// // builder.append(String.format("%02X", calendar.get(Calendar.MONTH) + 1));
|
||||
// // builder.append(String.format("%02X", calendar.get(Calendar.YEAR) - 2000));
|
||||
// // return builder.toString();
|
||||
//
|
||||
// byte[] result = new byte[7];
|
||||
// final Calendar aTime = Calendar.getInstance();
|
||||
// aTime.setTime(date);
|
||||
// final int milliseconds = aTime.get(Calendar.MILLISECOND);
|
||||
// result[0] = (byte) (milliseconds % 256);
|
||||
// result[1] = (byte) (milliseconds / 256);
|
||||
// result[2] = (byte) aTime.get(Calendar.MINUTE);
|
||||
// result[3] = (byte) aTime.get(Calendar.HOUR_OF_DAY);
|
||||
// result[4] = (byte) aTime.get(Calendar.DAY_OF_MONTH);
|
||||
// result[5] = (byte) (aTime.get(Calendar.MONTH) + 1);
|
||||
// result[6] = (byte) (aTime.get(Calendar.YEAR) % 100);
|
||||
// System.out.println("Year->" + aTime.get(Calendar.YEAR));
|
||||
// return BytesUtil.binary(result, 16);
|
||||
// }
|
||||
// public static String date2HexStr(Date date) {
|
||||
// return Cp56Time2aUtil.date2HexStr(date);
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 获取两个时间的间隔时间
|
||||
*
|
||||
* @return 间隔时间 单位:分钟
|
||||
*/
|
||||
public static long intervalTime(String begin, String end) {
|
||||
return intervalTime(parseDate(begin), parseDate(end));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取两个时间的间隔时间
|
||||
*
|
||||
* @return 间隔时间 单位:分钟
|
||||
*/
|
||||
public static long intervalTime(Date begin, Date end) {
|
||||
return intervalTime(date2LocalDateTime(begin), date2LocalDateTime(end));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取两个时间的间隔时间
|
||||
*
|
||||
* @return 间隔时间 单位:分钟
|
||||
*/
|
||||
public static long intervalTime(LocalDateTime begin, LocalDateTime end) {
|
||||
return ChronoUnit.MINUTES.between(begin, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断2个时间段是否有重叠(交集)
|
||||
*
|
||||
* @param startDate1 时间段1开始时间戳
|
||||
* @param endDate1 时间段1结束时间戳
|
||||
* @param startDate2 时间段2开始时间戳
|
||||
* @param endDate2 时间段2结束时间戳
|
||||
* @param isStrict 是否严格重叠,true 严格,没有任何相交或相等;false 不严格,可以首尾相等,比如2021/5/29-2021/5/31和2021/5/31-2021/6/1,不重叠。
|
||||
* @return 返回是否重叠
|
||||
*/
|
||||
public static boolean isOverlap(long startDate1, long endDate1, long startDate2, long endDate2, boolean isStrict) {
|
||||
if (isStrict) {
|
||||
if (!(endDate1 < startDate2 || startDate1 > endDate2)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!(endDate1 <= startDate2 || startDate1 >= endDate2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param time1 17:02:00 - 17:02:00
|
||||
* @param time2 00:00-06:30
|
||||
* @return
|
||||
*/
|
||||
public static boolean checkTime(String time1, String time2) {
|
||||
String[] split = time1.split("-");
|
||||
LocalTime startTime1 = DateUtils.getLocalTime(split[0]);
|
||||
LocalTime endTime1 = DateUtils.getLocalTime(split[1]);
|
||||
String[] split2 = time2.split("-");
|
||||
LocalTime startTime2 = DateUtils.getLocalTime(split2[0]);
|
||||
LocalTime endTime2 = DateUtils.getLocalTime(split2[1]);
|
||||
return DateUtils.isOverlap(startTime1, endTime1, startTime2, endTime2, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断时间段是否重叠
|
||||
*
|
||||
* @param startDate1
|
||||
* @param endDate1
|
||||
* @param startDate2
|
||||
* @param endDate2
|
||||
* @param isStrict
|
||||
* @return
|
||||
*/
|
||||
public static boolean isOverlap(LocalTime startDate1, LocalTime endDate1, LocalTime startDate2, LocalTime endDate2, boolean isStrict) {
|
||||
if (startDate1 == null || endDate1 == null || startDate2 == null || endDate2 == null) {
|
||||
log.warn("判断时间段是否重叠缺少参数,返回false");
|
||||
return false;
|
||||
}
|
||||
boolean result = false;
|
||||
if (isStrict) {
|
||||
if (!(endDate1.isBefore(startDate2) || startDate1.isAfter(endDate2))) {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
if (!((endDate1.isBefore(startDate2) || endDate1.equals(startDate2)) || (startDate1.isAfter(endDate2) || startDate1.equals(endDate2)))) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
// log.info("时间段1={}, 时间段2={}, 结果:{}", startDate1 + "-" + endDate1, startDate2 + "-" + endDate2, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断2个时间段是否有重叠(交集)
|
||||
*
|
||||
* @param startDate1 时间段1开始时间
|
||||
* @param endDate1 时间段1结束时间
|
||||
* @param startDate2 时间段2开始时间
|
||||
* @param endDate2 时间段2结束时间
|
||||
* @param isStrict 是否严格重叠,true 严格,没有任何相交或相等;false 不严格,可以首尾相等,比如2021-05-29到2021-05-31和2021-05-31到2021-06-01,不重叠。
|
||||
* @return 返回是否重叠
|
||||
*/
|
||||
public static boolean isOverlap(Date startDate1, Date endDate1, Date startDate2, Date endDate2, boolean isStrict) {
|
||||
Objects.requireNonNull(startDate1, "startDate1");
|
||||
Objects.requireNonNull(endDate1, "endDate1");
|
||||
Objects.requireNonNull(startDate2, "startDate2");
|
||||
Objects.requireNonNull(endDate2, "endDate2");
|
||||
return isOverlap(startDate1.getTime(), endDate1.getTime(), startDate2.getTime(), endDate2.getTime(), isStrict);
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒 转 天时分秒
|
||||
*
|
||||
* @param mss 秒数
|
||||
* @return xx天xx小时xx分钟xx秒
|
||||
*/
|
||||
public static String formatDateTime(long mss) {
|
||||
String DateTimes = null;
|
||||
long days = mss / (60 * 60 * 24);
|
||||
long hours = (mss % (60 * 60 * 24)) / (60 * 60);
|
||||
long minutes = (mss % (60 * 60)) / 60;
|
||||
long seconds = mss % 60;
|
||||
if (days > 0) {
|
||||
DateTimes = days + "天" + hours + "小时" + minutes + "分钟"
|
||||
+ seconds + "秒";
|
||||
} else if (hours > 0) {
|
||||
DateTimes = hours + "小时" + minutes + "分钟"
|
||||
+ seconds + "秒";
|
||||
} else if (minutes > 0) {
|
||||
DateTimes = minutes + "分钟"
|
||||
+ seconds + "秒";
|
||||
} else {
|
||||
DateTimes = seconds + "秒";
|
||||
}
|
||||
|
||||
return DateTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间是否在时间指定范围内<br>
|
||||
*
|
||||
* @param time 被检查的时间
|
||||
* @param beginTime 起始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 是否在范围内
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static boolean isIn(LocalTime time, LocalTime beginTime, LocalTime endTime) {
|
||||
// 判断是否在范围内 包含开始结束时间
|
||||
if (time.compareTo(beginTime) == 0 || time.compareTo(endTime) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (beginTime.isBefore(time) && endTime.isAfter(time)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.core.domain.entity.SysDictData;
|
||||
import com.jsowell.common.core.redis.RedisCache;
|
||||
import com.jsowell.common.util.spring.SpringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class DictUtils {
|
||||
/**
|
||||
* 分隔符
|
||||
*/
|
||||
public static final String SEPARATOR = ",";
|
||||
|
||||
/**
|
||||
* 设置字典缓存
|
||||
*
|
||||
* @param key 参数键
|
||||
* @param dictDatas 字典数据列表
|
||||
*/
|
||||
public static void setDictCache(String key, List<SysDictData> dictDatas) {
|
||||
SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典缓存
|
||||
*
|
||||
* @param key 参数键
|
||||
* @return dictDatas 字典数据列表
|
||||
*/
|
||||
public static List<SysDictData> getDictCache(String key) {
|
||||
JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
||||
if (StringUtils.isNotNull(arrayCache)) {
|
||||
return arrayCache.toList(SysDictData.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典值获取字典标签
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictValue 字典值
|
||||
* @return 字典标签
|
||||
*/
|
||||
public static String getDictLabel(String dictType, String dictValue) {
|
||||
return getDictLabel(dictType, dictValue, SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典标签获取字典值
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictLabel 字典标签
|
||||
* @return 字典值
|
||||
*/
|
||||
public static String getDictValue(String dictType, String dictLabel) {
|
||||
return getDictValue(dictType, dictLabel, SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典值获取字典标签
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictValue 字典值
|
||||
* @param separator 分隔符
|
||||
* @return 字典标签
|
||||
*/
|
||||
public static String getDictLabel(String dictType, String dictValue, String separator) {
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = getDictCache(dictType);
|
||||
|
||||
if (StringUtils.isNotNull(datas)) {
|
||||
if (StringUtils.containsAny(separator, dictValue)) {
|
||||
for (SysDictData dict : datas) {
|
||||
for (String value : dictValue.split(separator)) {
|
||||
if (value.equals(dict.getDictValue())) {
|
||||
propertyString.append(dict.getDictLabel()).append(separator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (SysDictData dict : datas) {
|
||||
if (dictValue.equals(dict.getDictValue())) {
|
||||
return dict.getDictLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return StringUtils.stripEnd(propertyString.toString(), separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典标签获取字典值
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictLabel 字典标签
|
||||
* @param separator 分隔符
|
||||
* @return 字典值
|
||||
*/
|
||||
public static String getDictValue(String dictType, String dictLabel, String separator) {
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = getDictCache(dictType);
|
||||
|
||||
if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) {
|
||||
for (SysDictData dict : datas) {
|
||||
for (String label : dictLabel.split(separator)) {
|
||||
if (label.equals(dict.getDictLabel())) {
|
||||
propertyString.append(dict.getDictValue()).append(separator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (SysDictData dict : datas) {
|
||||
if (dictLabel.equals(dict.getDictLabel())) {
|
||||
return dict.getDictValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return StringUtils.stripEnd(propertyString.toString(), separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定字典缓存
|
||||
*
|
||||
* @param key 字典键
|
||||
*/
|
||||
public static void removeDictCache(String key) {
|
||||
SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空字典缓存
|
||||
*/
|
||||
public static void clearDictCache() {
|
||||
Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*");
|
||||
SpringUtils.getBean(RedisCache.class).deleteObject(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置cache key
|
||||
*
|
||||
* @param configKey 参数键
|
||||
* @return 缓存键key
|
||||
*/
|
||||
public static String getCacheKey(String configKey) {
|
||||
return CacheConstants.SYS_DICT_KEY + configKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
public final class DistanceUtils {
|
||||
|
||||
/**
|
||||
* 地球半径,单位 km
|
||||
*/
|
||||
private static final double EARTH_RADIUS = 6378.137;
|
||||
|
||||
/**
|
||||
* 根据经纬度,计算两点间的距离
|
||||
*
|
||||
* @param longitude1 第一个点的经度
|
||||
* @param latitude1 第一个点的纬度
|
||||
* @param longitude2 第二个点的经度
|
||||
* @param latitude2 第二个点的纬度
|
||||
* @return 返回距离 单位千米
|
||||
*/
|
||||
public static double getDistance(double longitude1, double latitude1, double longitude2, double latitude2) {
|
||||
// 纬度
|
||||
double lat1 = Math.toRadians(latitude1);
|
||||
double lat2 = Math.toRadians(latitude2);
|
||||
// 经度
|
||||
double lng1 = Math.toRadians(longitude1);
|
||||
double lng2 = Math.toRadians(longitude2);
|
||||
// 纬度之差
|
||||
double a = lat1 - lat2;
|
||||
// 经度之差
|
||||
double b = lng1 - lng2;
|
||||
// 计算两点距离的公式
|
||||
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
|
||||
Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
|
||||
// 弧长乘地球半径, 返回单位: 千米
|
||||
s = s * EARTH_RADIUS;
|
||||
return s;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
double d = getDistance(116.308479, 39.983171, 116.353454, 39.996059);
|
||||
System.out.println(d);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* 错误信息处理类。
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class ExceptionUtil {
|
||||
/**
|
||||
* 获取exception的详细错误信息。
|
||||
*/
|
||||
public static String getExceptionMessage(Throwable e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw, true));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
public static String getRootErrorMessage(Exception e) {
|
||||
Throwable root = org.apache.commons.lang3.exception.ExceptionUtils.getRootCause(e);
|
||||
root = (root == null ? e : root);
|
||||
if (root == null) {
|
||||
return "";
|
||||
}
|
||||
String msg = root.getMessage();
|
||||
if (msg == null) {
|
||||
return "null";
|
||||
}
|
||||
return StringUtils.defaultString(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
|
||||
@Component
|
||||
public class JWTUtils {
|
||||
// 令牌自定义标识
|
||||
private static String header;
|
||||
|
||||
@Value("${token.header}")
|
||||
public void setHeader(String header) {
|
||||
JWTUtils.header = header;
|
||||
}
|
||||
|
||||
// 令牌秘钥
|
||||
private static String secret;
|
||||
|
||||
@Value("${token.secret}")
|
||||
public void setSecret(String secret) {
|
||||
JWTUtils.secret = secret;
|
||||
}
|
||||
|
||||
// 接口服务 令牌有效期
|
||||
private static int serviceExpireTime;
|
||||
|
||||
@Value("${token.serviceExpireTime}")
|
||||
public void setServiceExpireTime(int serviceExpireTime) {
|
||||
JWTUtils.serviceExpireTime = serviceExpireTime;
|
||||
}
|
||||
|
||||
protected static final long MILLIS_SECOND = 1000;
|
||||
|
||||
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
|
||||
|
||||
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
|
||||
|
||||
/**
|
||||
* 生成Jwt的方法
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @param subject 用户昵称
|
||||
* @param ttlMillis 过期时间 毫秒
|
||||
* @return Token String 凭证
|
||||
*/
|
||||
private static String createToken(String id, String subject, long ttlMillis) {
|
||||
// 签名方法 HS256
|
||||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
|
||||
|
||||
// 生成Jwt的时间
|
||||
long nowMillis = System.currentTimeMillis();
|
||||
Date now = new Date(nowMillis);
|
||||
|
||||
// 生成秘钥
|
||||
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(secret);
|
||||
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
|
||||
|
||||
// 设置JWT所存储的信息
|
||||
JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now).setSubject(subject).signWith(signatureAlgorithm, signingKey);
|
||||
|
||||
//builder.claim("name", "value"); //存储自定义信息
|
||||
|
||||
// 设置过期时间
|
||||
if (ttlMillis >= 0) {
|
||||
long expMillis = nowMillis + ttlMillis * MILLIS_MINUTE;
|
||||
Date exp = new Date(expMillis);
|
||||
builder.setExpiration(exp);
|
||||
}
|
||||
|
||||
// 构建JWT并将其序列化为紧凑的URL安全字符串
|
||||
return builder.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取数据声明
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 数据声明
|
||||
*/
|
||||
private static Claims parseToken(String token) {
|
||||
return Jwts.parser()
|
||||
.setSigningKey(secret)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员token
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String createMemberToken(String memberId, String nickName) {
|
||||
return createToken(memberId, nickName, serviceExpireTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员id
|
||||
*
|
||||
* @param memberToken
|
||||
* @return
|
||||
*/
|
||||
public static String getMemberId(String memberToken) {
|
||||
memberToken = getToken(memberToken);
|
||||
if (StringUtils.isBlank(memberToken)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_TOKEN_ERROR);
|
||||
}
|
||||
Claims claims = parseToken(memberToken);
|
||||
return claims.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换Bearer
|
||||
*
|
||||
* @param memberToken 会员token
|
||||
* @return
|
||||
*/
|
||||
private static String getToken(String memberToken) {
|
||||
if (StringUtils.isNotEmpty(memberToken) && memberToken.startsWith(Constants.TOKEN_PREFIX)) {
|
||||
memberToken = memberToken.replace(Constants.TOKEN_PREFIX, "").trim();
|
||||
}
|
||||
return memberToken;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
/**
|
||||
* 处理并记录日志文件
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class LogUtils {
|
||||
public static String getBlock(Object msg) {
|
||||
if (msg == null) {
|
||||
msg = "";
|
||||
}
|
||||
return "[" + msg.toString() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import com.jsowell.common.util.spring.SpringUtils;
|
||||
|
||||
/**
|
||||
* 获取i18n资源文件
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class MessageUtils {
|
||||
/**
|
||||
* 根据消息键和参数 获取消息 委托给spring messageSource
|
||||
*
|
||||
* @param code 消息键
|
||||
* @param args 参数
|
||||
* @return 获取国际化翻译值
|
||||
*/
|
||||
public static String message(String code, Object... args) {
|
||||
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
|
||||
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.jsowell.common.core.page.PageDomain;
|
||||
import com.jsowell.common.core.page.TableSupport;
|
||||
import com.jsowell.common.util.sql.SqlUtil;
|
||||
|
||||
/**
|
||||
* 分页工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class PageUtils extends PageHelper {
|
||||
/**
|
||||
* 设置请求分页数据
|
||||
*/
|
||||
public static void startPage() {
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
Integer pageNum = pageDomain.getPageNum();
|
||||
Integer pageSize = pageDomain.getPageSize();
|
||||
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
|
||||
Boolean reasonable = pageDomain.getReasonable();
|
||||
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理分页的线程变量
|
||||
*/
|
||||
public static void clearPage() {
|
||||
PageHelper.clearPage();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 获取随机数的工具类
|
||||
*/
|
||||
public class RandomUtil {
|
||||
private static final String SYMBOLS_NUM = "0123456789"; // 纯数字
|
||||
//如果需加入字母就改成0123456789abcdefg...........
|
||||
private static final String SYMBOLS_NUM_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
private static final Random RANDOM = new SecureRandom();
|
||||
|
||||
/**
|
||||
* 获取6位的短信验证码 纯数字
|
||||
*
|
||||
* @return 随机数字
|
||||
*/
|
||||
public static String getSMSVerificationCode() {
|
||||
return getRandomNumber(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定长度随机数
|
||||
*
|
||||
* @param length 需要的长度
|
||||
* @return 随机数
|
||||
*/
|
||||
public static String getRandomNumber(int length) {
|
||||
char[] nonceChars = new char[length]; //指定长度,自己可以设置
|
||||
for (int index = 0; index < nonceChars.length; ++index) {
|
||||
nonceChars[index] = SYMBOLS_NUM.charAt(RANDOM.nextInt(SYMBOLS_NUM.length()));
|
||||
}
|
||||
return new String(nonceChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一组不重复的6位数随机数
|
||||
*
|
||||
* @param num 数量
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getRandomNumberList(int num) {
|
||||
List<String> list = Lists.newArrayList();
|
||||
for (int i = 0; i < num; i++) {
|
||||
String code = getNotRepeatCode(list);
|
||||
// System.out.println(code);
|
||||
list.add(code);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取一组不重复的6位数随机数
|
||||
*/
|
||||
private static String getNotRepeatCode(List<String> list) {
|
||||
// 获取随机验证码
|
||||
String code = RandomUtil.getSMSVerificationCode();
|
||||
// 判断list中是否存在
|
||||
boolean contains = list.contains(code);
|
||||
if (contains) {
|
||||
// 已经存在,重新获取一个
|
||||
code = getNotRepeatCode(list);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> randomNumberList = getRandomNumberList(100000);
|
||||
Set<String> set = Sets.newHashSet(randomNumberList);
|
||||
System.out.println("list长度:" + randomNumberList.size());
|
||||
System.out.println("set长度:" + set.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.github.qcloudsms.SmsSingleSender;
|
||||
import com.github.qcloudsms.SmsSingleSenderResult;
|
||||
import com.github.qcloudsms.httpclient.HTTPException;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.core.redis.RedisCache;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 发送短信验证码工具类
|
||||
*/
|
||||
@Component
|
||||
public class SMSUtil {
|
||||
|
||||
private static RedisCache redisCache;
|
||||
|
||||
@Autowired
|
||||
public void setRedisCache(RedisCache redisCache) {
|
||||
SMSUtil.redisCache = redisCache;
|
||||
}
|
||||
|
||||
|
||||
// 短信应用SDK AppKey
|
||||
private static final String APP_KEY = "8ebcf52de98416814b440891350cd594";
|
||||
|
||||
// 短信模板ID,需要在短信应用中申请
|
||||
private static final int TEMPLATE_ID = 1002460;
|
||||
|
||||
// 短信应用SDK AppID 1400开头
|
||||
private static final int APP_ID = 1400536771;
|
||||
|
||||
// 签名,使用的是签名内容,而不是签名ID
|
||||
private static final String SMS_SIGN = "举视新能源";
|
||||
|
||||
// 国家码 如 86 为中国
|
||||
private static final String NATION_CODE = "86";
|
||||
|
||||
public static String sendSMS(HttpServletRequest request, String phoneNumber) throws HTTPException, IOException {
|
||||
String reStr = "success"; //定义返回值
|
||||
//随机生成六位验证码
|
||||
String code = RandomUtil.getSMSVerificationCode();
|
||||
//参数,一定要对应短信模板中的参数顺序和个数,
|
||||
String[] params = {code};
|
||||
//创建ssender对象
|
||||
SmsSingleSender ssender = new SmsSingleSender(APP_ID, APP_KEY);
|
||||
//发送
|
||||
SmsSingleSenderResult result = ssender.sendWithParam(NATION_CODE, phoneNumber, TEMPLATE_ID, params, SMS_SIGN, "", "");
|
||||
if (result.result != 0) {
|
||||
reStr = "error";
|
||||
} else {
|
||||
// 改为保存redis
|
||||
String redisKey = CacheConstants.SMS_VERIFICATION_CODE_KEY + phoneNumber;
|
||||
redisCache.setCacheObject(redisKey, code, CacheConstants.cache_expire_time_10m);
|
||||
}
|
||||
return reStr;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import com.jsowell.common.constant.HttpStatus;
|
||||
import com.jsowell.common.core.domain.model.LoginUser;
|
||||
import com.jsowell.common.exception.ServiceException;
|
||||
|
||||
/**
|
||||
* 安全服务工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class SecurityUtils {
|
||||
/**
|
||||
* 用户ID
|
||||
**/
|
||||
public static Long getUserId() {
|
||||
try {
|
||||
return getLoginUser().getUserId();
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门ID
|
||||
**/
|
||||
public static Long getDeptId() {
|
||||
try {
|
||||
return getLoginUser().getDeptId();
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户账户
|
||||
**/
|
||||
public static String getUsername() {
|
||||
try {
|
||||
return getLoginUser().getUsername();
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
**/
|
||||
public static LoginUser getLoginUser() {
|
||||
try {
|
||||
return (LoginUser) getAuthentication().getPrincipal();
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Authentication
|
||||
*/
|
||||
public static Authentication getAuthentication() {
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成BCryptPasswordEncoder密码
|
||||
*
|
||||
* @param password 密码
|
||||
* @return 加密字符串
|
||||
*/
|
||||
public static String encryptPassword(String password) {
|
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
return passwordEncoder.encode(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断密码是否相同
|
||||
*
|
||||
* @param rawPassword 真实密码
|
||||
* @param encodedPassword 加密后字符
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean matchesPassword(String rawPassword, String encodedPassword) {
|
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isAdmin(Long userId) {
|
||||
return userId != null && 1L == userId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.text.Convert;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* 客户端工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class ServletUtils {
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name) {
|
||||
return getRequest().getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name, String defaultValue) {
|
||||
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name) {
|
||||
return Convert.toInt(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name, Integer defaultValue) {
|
||||
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name) {
|
||||
return Convert.toBool(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name, Boolean defaultValue) {
|
||||
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取request
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
return getRequestAttributes().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取response
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
return getRequestAttributes().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
public static ServletRequestAttributes getRequestAttributes() {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes) attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串渲染到客户端
|
||||
*
|
||||
* @param response 渲染对象
|
||||
* @param string 待渲染的字符串
|
||||
*/
|
||||
public static void renderString(HttpServletResponse response, String string) {
|
||||
try {
|
||||
response.setStatus(200);
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.getWriter().print(string);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是Ajax异步请求
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public static boolean isAjaxRequest(HttpServletRequest request) {
|
||||
String accept = request.getHeader("accept");
|
||||
if (accept != null && accept.contains("application/json")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String ajax = request.getParameter("__ajax");
|
||||
return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容编码
|
||||
*
|
||||
* @param str 内容
|
||||
* @return 编码后的内容
|
||||
*/
|
||||
public static String urlEncode(String str) {
|
||||
try {
|
||||
return URLEncoder.encode(str, Constants.UTF8);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容解码
|
||||
*
|
||||
* @param str 内容
|
||||
* @return 解码后的内容
|
||||
*/
|
||||
public static String urlDecode(String str) {
|
||||
try {
|
||||
return URLDecoder.decode(str, Constants.UTF8);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,561 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.text.StrFormatter;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/**
|
||||
* 空字符串
|
||||
*/
|
||||
private static final String NULL_STR = "";
|
||||
|
||||
/**
|
||||
* 下划线
|
||||
*/
|
||||
private static final char SEPARATOR = '_';
|
||||
|
||||
/**
|
||||
* 获取参数不为空值
|
||||
*
|
||||
* @param value defaultValue 要判断的value
|
||||
* @return value 返回值
|
||||
*/
|
||||
public static <T> T nvl(T value, T defaultValue) {
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否为空, 包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Collection<?> coll) {
|
||||
return isNull(coll) || coll.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否非空,包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Collection<?> coll) {
|
||||
return !isEmpty(coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否为空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
* * @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Object[] objects) {
|
||||
return isNull(objects) || (objects.length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否非空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Object[] objects) {
|
||||
return !isEmpty(objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Map<?, ?> map) {
|
||||
return isNull(map) || map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Map<?, ?> map) {
|
||||
return !isEmpty(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(String str) {
|
||||
return isNull(str) || NULL_STR.equals(str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为非空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:非空串 false:空串
|
||||
*/
|
||||
public static boolean isNotEmpty(String str) {
|
||||
return !isEmpty(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否为空
|
||||
*
|
||||
* @param object Object
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isNull(Object object) {
|
||||
return object == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否非空
|
||||
*
|
||||
* @param object Object
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotNull(Object object) {
|
||||
return !isNull(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* 去空格
|
||||
*/
|
||||
public static String trim(String str) {
|
||||
return (str == null ? "" : str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start) {
|
||||
if (str == null) {
|
||||
return NULL_STR;
|
||||
}
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (start > str.length()) {
|
||||
return NULL_STR;
|
||||
}
|
||||
return str.substring(start);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @param end 结束
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start, int end) {
|
||||
if (str == null) {
|
||||
return NULL_STR;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = str.length() + end;
|
||||
}
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
if (end > str.length()) {
|
||||
end = str.length();
|
||||
}
|
||||
if (start > end) {
|
||||
return NULL_STR;
|
||||
}
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = 0;
|
||||
}
|
||||
return str.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文本, {} 表示占位符<br>
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||
* 例:<br>
|
||||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||
*
|
||||
* @param template 文本模板,被替换的部分用 {} 表示
|
||||
* @param params 参数值
|
||||
* @return 格式化后的文本
|
||||
*/
|
||||
public static String format(String template, Object... params) {
|
||||
if (isEmpty(params) || isEmpty(template)) {
|
||||
return template;
|
||||
}
|
||||
return StrFormatter.format(template, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为http(s)://开头
|
||||
*
|
||||
* @param link 链接
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isHttp(String link) {
|
||||
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转set
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 分隔符
|
||||
* @return set集合
|
||||
*/
|
||||
public static Set<String> str2Set(String str, String sep) {
|
||||
return new HashSet<String>(str2List(str, sep, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转list
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 分隔符
|
||||
* @param filterBlank 过滤纯空白
|
||||
* @param trim 去掉首尾空白
|
||||
* @return list集合
|
||||
*/
|
||||
public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// 过滤空白字符串
|
||||
if (filterBlank && StringUtils.isBlank(str)) {
|
||||
return list;
|
||||
}
|
||||
String[] split = str.split(sep);
|
||||
for (String string : split) {
|
||||
if (filterBlank && StringUtils.isBlank(string)) {
|
||||
continue;
|
||||
}
|
||||
if (trim) {
|
||||
string = string.trim();
|
||||
}
|
||||
list.add(string);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
|
||||
*
|
||||
* @param cs 指定字符串
|
||||
* @param searchCharSequences 需要检查的字符串数组
|
||||
* @return 是否包含任意一个字符串
|
||||
*/
|
||||
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
|
||||
if (isEmpty(cs) || isEmpty(searchCharSequences)) {
|
||||
return false;
|
||||
}
|
||||
for (CharSequence testStr : searchCharSequences) {
|
||||
if (containsIgnoreCase(cs, testStr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线命名
|
||||
*/
|
||||
public static String toUnderScoreCase(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 前置字符是否大写
|
||||
boolean preCharIsUpperCase = true;
|
||||
// 当前字符是否大写
|
||||
boolean curreCharIsUpperCase = true;
|
||||
// 下一字符是否大写
|
||||
boolean nexteCharIsUpperCase = true;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char c = str.charAt(i);
|
||||
if (i > 0) {
|
||||
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
|
||||
} else {
|
||||
preCharIsUpperCase = false;
|
||||
}
|
||||
curreCharIsUpperCase = Character.isUpperCase(c);
|
||||
if (i < (str.length() - 1)) {
|
||||
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
|
||||
}
|
||||
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
|
||||
sb.append(SEPARATOR);
|
||||
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
|
||||
sb.append(SEPARATOR);
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含字符串
|
||||
*
|
||||
* @param str 验证字符串
|
||||
* @param strs 字符串组
|
||||
* @return 包含返回true
|
||||
*/
|
||||
public static boolean inStringIgnoreCase(String str, String... strs) {
|
||||
if (str != null && strs != null) {
|
||||
for (String s : strs) {
|
||||
if (str.equalsIgnoreCase(trim(s))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
|
||||
*
|
||||
* @param name 转换前的下划线大写方式命名的字符串
|
||||
* @return 转换后的驼峰式命名的字符串
|
||||
*/
|
||||
public static String convertToCamelCase(String name) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
// 快速检查
|
||||
if (name == null || name.isEmpty()) {
|
||||
// 没必要转换
|
||||
return "";
|
||||
} else if (!name.contains("_")) {
|
||||
// 不含下划线,仅将首字母大写
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
// 用下划线将原始字符串分割
|
||||
String[] camels = name.split("_");
|
||||
for (String camel : camels) {
|
||||
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
||||
if (camel.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// 首字母大写
|
||||
result.append(camel.substring(0, 1).toUpperCase());
|
||||
result.append(camel.substring(1).toLowerCase());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰式命名法 例如:user_name->userName
|
||||
*/
|
||||
public static String toCamelCase(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
s = s.toLowerCase();
|
||||
StringBuilder sb = new StringBuilder(s.length());
|
||||
boolean upperCase = false;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
|
||||
if (c == SEPARATOR) {
|
||||
upperCase = true;
|
||||
} else if (upperCase) {
|
||||
sb.append(Character.toUpperCase(c));
|
||||
upperCase = false;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
|
||||
*
|
||||
* @param str 指定字符串
|
||||
* @param strs 需要检查的字符串数组
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean matches(String str, List<String> strs) {
|
||||
if (isEmpty(str) || isEmpty(strs)) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : strs) {
|
||||
if (isMatch(pattern, str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断url是否与规则配置:
|
||||
* ? 表示单个字符;
|
||||
* * 表示一层路径内的任意字符串,不可跨层级;
|
||||
* ** 表示任意层路径;
|
||||
*
|
||||
* @param pattern 匹配规则
|
||||
* @param url 需要匹配的url
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String url) {
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
return matcher.match(pattern, url);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T cast(Object obj) {
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||
*
|
||||
* @param num 数字对象
|
||||
* @param size 字符串指定长度
|
||||
* @return 返回数字的字符串格式,该字符串为指定长度。
|
||||
*/
|
||||
public static String padl(final Number num, final int size) {
|
||||
return padl(num.toString(), size, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
||||
*
|
||||
* @param s 原始字符串
|
||||
* @param size 字符串指定长度
|
||||
* @param c 用于补齐的字符
|
||||
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
|
||||
*/
|
||||
public static String padl(final String s, final int size, final char c) {
|
||||
final StringBuilder sb = new StringBuilder(size);
|
||||
if (s != null) {
|
||||
final int len = s.length();
|
||||
if (s.length() <= size) {
|
||||
for (int i = size - len; i > 0; i--) {
|
||||
sb.append(c);
|
||||
}
|
||||
sb.append(s);
|
||||
} else {
|
||||
return s.substring(len - size, len);
|
||||
}
|
||||
} else {
|
||||
for (int i = size; i > 0; i--) {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 16进制表示的字符串转换为字节数组
|
||||
*
|
||||
* @param s 16进制表示的字符串
|
||||
* @return byte[] 字节数组
|
||||
*/
|
||||
public static int[] hexStringToIntArray(String s) {
|
||||
int len = s.length();
|
||||
int[] b = new int[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
// 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节
|
||||
b[i / 2] = (int) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* int转换成16进制字符串
|
||||
*
|
||||
* @param b 需要转换的int值
|
||||
* @return 16进制的String
|
||||
*/
|
||||
public static String toHexString(int b) {
|
||||
String hex = Integer.toHexString(b & 0xFF);
|
||||
if (hex.length() == 1) {
|
||||
hex = '0' + hex;
|
||||
}
|
||||
return "0x" + hex.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转换成String,指定长度转换长度
|
||||
*
|
||||
* @param arrBytes
|
||||
* @param count 转换长度
|
||||
* @param blank 要不要空格(每个byte字节,最是否用一个“ ”隔开)
|
||||
* @return "" | arrBytes换成的字符串(不存在null)
|
||||
*/
|
||||
public static String byteArray2HexString(byte[] arrBytes, int count, boolean blank) {
|
||||
String ret = "";
|
||||
if (arrBytes == null || arrBytes.length < 1)
|
||||
return ret;
|
||||
if (count > arrBytes.length)
|
||||
count = arrBytes.length;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
ret = Integer.toHexString(arrBytes[i] & 0xFF).toUpperCase();
|
||||
if (ret.length() == 1)
|
||||
builder.append("0").append(ret);
|
||||
else
|
||||
builder.append(ret);
|
||||
if (blank)
|
||||
builder.append(" ");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成16进制字符串
|
||||
*
|
||||
* @param array 需要转换的字符串(字节间没有分隔符)
|
||||
* @return 转换完成的字符串
|
||||
*/
|
||||
public static String byteArrayToHexString(byte[] array) {
|
||||
return byteArray2HexString(array, Integer.MAX_VALUE, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将两个ASCII字符合成一个字节; 如:"EF"--> 0xEF
|
||||
*
|
||||
* @param src0 byte
|
||||
* @param src1 byte
|
||||
* @return byte
|
||||
*/
|
||||
public static byte uniteBytes(byte src0, byte src1) {
|
||||
byte _b0 = Byte.decode("0x" + new String(new byte[]{src0}));
|
||||
_b0 = (byte) (_b0 << 4);
|
||||
byte _b1 = Byte.decode("0x" + new String(new byte[]{src1}));
|
||||
byte ret = (byte) (_b0 ^ _b1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 线程相关工具类.
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class Threads {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Threads.class);
|
||||
|
||||
/**
|
||||
* sleep等待,单位为毫秒
|
||||
*/
|
||||
public static void sleep(long milliseconds) {
|
||||
try {
|
||||
Thread.sleep(milliseconds);
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止线程池
|
||||
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
|
||||
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
|
||||
* 如果仍然超時,則強制退出.
|
||||
* 另对在shutdown时线程本身被调用中断做了处理.
|
||||
*/
|
||||
public static void shutdownAndAwaitTermination(ExecutorService pool) {
|
||||
if (pool != null && !pool.isShutdown()) {
|
||||
pool.shutdown();
|
||||
try {
|
||||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
|
||||
pool.shutdownNow();
|
||||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
|
||||
logger.info("Pool did not terminate");
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
pool.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印线程异常信息
|
||||
*/
|
||||
public static void printException(Runnable r, Throwable t) {
|
||||
if (t == null && r instanceof Future<?>) {
|
||||
try {
|
||||
Future<?> future = (Future<?>) r;
|
||||
if (future.isDone()) {
|
||||
future.get();
|
||||
}
|
||||
} catch (CancellationException ce) {
|
||||
t = ce;
|
||||
} catch (ExecutionException ee) {
|
||||
t = ee.getCause();
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (t != null) {
|
||||
logger.error(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Slf4j
|
||||
public class YKCUtils {
|
||||
|
||||
private static final BigDecimal multiple = new BigDecimal("100000");
|
||||
|
||||
/**
|
||||
* 校验云快充请求数据格式
|
||||
*
|
||||
* @param msg 请求报文
|
||||
* @return
|
||||
*/
|
||||
public static boolean checkMsg(byte[] msg) {
|
||||
// 起始标志
|
||||
byte[] head = BytesUtil.copyBytes(msg, 0, 1);
|
||||
// 数据长度
|
||||
byte[] length = BytesUtil.copyBytes(msg, 1, 1);
|
||||
// 序列号域
|
||||
byte[] serialNumber = BytesUtil.copyBytes(msg, 2, 2);
|
||||
// 加密标志
|
||||
byte[] encryptFlag = BytesUtil.copyBytes(msg, 4, 1);
|
||||
// 帧类型标志
|
||||
byte[] frameType = BytesUtil.copyBytes(msg, 5, 1);
|
||||
// 消息体
|
||||
byte[] msgBody = BytesUtil.copyBytes(msg, 6, msg.length - 8);
|
||||
// 帧校验域
|
||||
byte[] crcByte = new byte[]{msg[msg.length - 2], msg[msg.length - 1]};
|
||||
|
||||
//起始位必须是0x68
|
||||
if (0x68 != head[0]) {
|
||||
log.error("起始位必须是0x68");
|
||||
return false;
|
||||
}
|
||||
// 序列号域+加密标志+帧类型标志+消息体
|
||||
byte[] data = Bytes.concat(serialNumber, encryptFlag, frameType, msgBody);
|
||||
// 校验长度
|
||||
if (data.length != BytesUtil.bytesToIntLittle(length)) {
|
||||
log.error("数据长度不正确");
|
||||
return false;
|
||||
}
|
||||
// CRC校验 source target
|
||||
String sourceCRC = String.format("%04x", BytesUtil.bytesToInt(crcByte, 0));
|
||||
String targetCRC = String.format("%04x", CRC16Util.calcCrc16(data));
|
||||
String oldTargetCRC = String.format("%04x", CRC16Util.calcCrc16Old(data));
|
||||
if (!sourceCRC.equalsIgnoreCase(targetCRC)) {
|
||||
log.error("CRC校验不通过, 源crc:{}, 计算得出crc:{}, 老crc计算:{}, 帧类型:{}, 报文:{}, msg:{}"
|
||||
, sourceCRC, targetCRC, oldTargetCRC, YKCUtils.frameType2Str(frameType), BytesUtil.binary(msg, 16), Arrays.toString(msg));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换电压电流以及起始soc
|
||||
* 精确到小数点后一位;待机置零
|
||||
*
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static String convertVoltageCurrent(byte[] bytes) {
|
||||
// 转换为int 最后一位是小数位
|
||||
int i = BytesUtil.bytesToIntLittle(bytes);
|
||||
// 使用BigDecimal
|
||||
BigDecimal bigDecimal = new BigDecimal(i);
|
||||
BigDecimal divide = bigDecimal.divide(new BigDecimal(10), 1, BigDecimal.ROUND_UP);
|
||||
return divide.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换小数点
|
||||
*
|
||||
* @param bytes
|
||||
* @param scale 小数位
|
||||
* @return
|
||||
*/
|
||||
public static String convertDecimalPoint(byte[] bytes, int scale) {
|
||||
// 转换为int
|
||||
int i = BytesUtil.bytesToIntLittle(bytes);
|
||||
// 使用BigDecimal
|
||||
BigDecimal bigDecimal = new BigDecimal(i);
|
||||
BigDecimal divide = bigDecimal.divide(BigDecimal.valueOf(Math.pow(10d, scale)), scale, BigDecimal.ROUND_UP);
|
||||
return divide.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取价格byte数组
|
||||
* 价格转换为byte数组
|
||||
* @param price 实际价格,单位元
|
||||
* @param scale 保留小数位
|
||||
* @return
|
||||
*/
|
||||
public static byte[] getPriceByte(String price, int scale) {
|
||||
// 保留小数位 例:保留5位小数,需要乘以10的5次方
|
||||
BigDecimal value = BigDecimal.valueOf(Math.pow(10d, scale));
|
||||
int i = new BigDecimal(price).multiply(value).intValue();
|
||||
return BytesUtil.intToBytesLittle(i, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* byte转帧类型字符串
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static String frameType2Str(byte[] bytes) {
|
||||
String s = BytesUtil.bin2HexStr(bytes);
|
||||
return "0x" + s;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// String hexString = "681E0000003388000000000027012302081602434533880000000000270101008361";
|
||||
// byte[] byteArray = new byte[hexString.length() / 2];
|
||||
// for (int i = 0; i < byteArray.length; i++) {
|
||||
// int index = i * 2;
|
||||
// int j = Integer.parseInt(hexString.substring(index, index + 2), 16);
|
||||
// byteArray[i] = (byte) j;
|
||||
// }
|
||||
// System.out.println(byteArray);
|
||||
// String binary = BytesUtil.binary(byteArray, 16);
|
||||
// String aaa = DatatypeConverter.printHexBinary(byteArray);
|
||||
// System.out.println(binary);
|
||||
// System.out.println(aaa);
|
||||
|
||||
String hexString = "680d3c4000038800000000002701001568";
|
||||
byte[] bytes = BytesUtil.hexStringToByteArray(hexString);
|
||||
System.out.println(checkMsg(bytes));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.jsowell.common.util.bean;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Bean 工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class BeanUtils extends org.springframework.beans.BeanUtils {
|
||||
/**
|
||||
* Bean方法名中属性名开始的下标
|
||||
*/
|
||||
private static final int BEAN_METHOD_PROP_INDEX = 3;
|
||||
|
||||
/**
|
||||
* 匹配getter方法的正则表达式
|
||||
*/
|
||||
private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
|
||||
|
||||
/**
|
||||
* 匹配setter方法的正则表达式
|
||||
*/
|
||||
private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
|
||||
|
||||
/**
|
||||
* Bean属性复制工具方法。
|
||||
*
|
||||
* @param dest 目标对象
|
||||
* @param src 源对象
|
||||
*/
|
||||
public static void copyBeanProp(Object dest, Object src) {
|
||||
try {
|
||||
copyProperties(src, dest);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的setter方法。
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 对象的setter方法列表
|
||||
*/
|
||||
public static List<Method> getSetterMethods(Object obj) {
|
||||
// setter方法列表
|
||||
List<Method> setterMethods = new ArrayList<Method>();
|
||||
|
||||
// 获取所有方法
|
||||
Method[] methods = obj.getClass().getMethods();
|
||||
|
||||
// 查找setter方法
|
||||
|
||||
for (Method method : methods) {
|
||||
Matcher m = SET_PATTERN.matcher(method.getName());
|
||||
if (m.matches() && (method.getParameterTypes().length == 1)) {
|
||||
setterMethods.add(method);
|
||||
}
|
||||
}
|
||||
// 返回setter方法列表
|
||||
return setterMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的getter方法。
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 对象的getter方法列表
|
||||
*/
|
||||
|
||||
public static List<Method> getGetterMethods(Object obj) {
|
||||
// getter方法列表
|
||||
List<Method> getterMethods = new ArrayList<Method>();
|
||||
// 获取所有方法
|
||||
Method[] methods = obj.getClass().getMethods();
|
||||
// 查找getter方法
|
||||
for (Method method : methods) {
|
||||
Matcher m = GET_PATTERN.matcher(method.getName());
|
||||
if (m.matches() && (method.getParameterTypes().length == 0)) {
|
||||
getterMethods.add(method);
|
||||
}
|
||||
}
|
||||
// 返回getter方法列表
|
||||
return getterMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Bean方法名中的属性名是否相等。<br>
|
||||
* 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
|
||||
*
|
||||
* @param m1 方法名1
|
||||
* @param m2 方法名2
|
||||
* @return 属性名一样返回true,否则返回false
|
||||
*/
|
||||
|
||||
public static boolean isMethodPropEquals(String m1, String m2) {
|
||||
return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.jsowell.common.util.bean;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* bean对象属性验证
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class BeanValidators {
|
||||
public static void validateWithException(Validator validator, Object object, Class<?>... groups)
|
||||
throws ConstraintViolationException {
|
||||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||
if (!constraintViolations.isEmpty()) {
|
||||
throw new ConstraintViolationException(constraintViolations);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.jsowell.common.util.file;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 文件类型工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class FileTypeUtils {
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: jsowell.txt, 返回: txt
|
||||
*
|
||||
* @param file 文件名
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType(File file) {
|
||||
if (null == file) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return getFileType(file.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: jsowell.txt, 返回: txt
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType(String fileName) {
|
||||
int separatorIndex = fileName.lastIndexOf(".");
|
||||
if (separatorIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return fileName.substring(separatorIndex + 1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
*
|
||||
* @param photoByte 文件字节码
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileExtendName(byte[] photoByte) {
|
||||
String strFileExtendName = "JPG";
|
||||
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
|
||||
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
|
||||
strFileExtendName = "GIF";
|
||||
} else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
|
||||
strFileExtendName = "JPG";
|
||||
} else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
|
||||
strFileExtendName = "BMP";
|
||||
} else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
|
||||
strFileExtendName = "PNG";
|
||||
}
|
||||
return strFileExtendName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package com.jsowell.common.util.file;
|
||||
|
||||
import com.jsowell.common.config.JsowellConfig;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.exception.file.FileNameLengthLimitExceededException;
|
||||
import com.jsowell.common.exception.file.FileSizeLimitExceededException;
|
||||
import com.jsowell.common.exception.file.InvalidExtensionException;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.id.Seq;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 文件上传工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class FileUploadUtils {
|
||||
/**
|
||||
* 默认大小 50M
|
||||
*/
|
||||
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* 默认的文件名最大长度 100
|
||||
*/
|
||||
public static final int DEFAULT_FILE_NAME_LENGTH = 100;
|
||||
|
||||
/**
|
||||
* 默认上传的地址
|
||||
*/
|
||||
private static String defaultBaseDir = JsowellConfig.getProfile();
|
||||
|
||||
public static void setDefaultBaseDir(String defaultBaseDir) {
|
||||
FileUploadUtils.defaultBaseDir = defaultBaseDir;
|
||||
}
|
||||
|
||||
public static String getDefaultBaseDir() {
|
||||
return defaultBaseDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以默认配置进行文件上传
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return 文件名称
|
||||
* @throws Exception
|
||||
*/
|
||||
public static final String upload(MultipartFile file) throws IOException {
|
||||
try {
|
||||
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件路径上传
|
||||
*
|
||||
* @param baseDir 相对应用的基目录
|
||||
* @param file 上传的文件
|
||||
* @return 文件名称
|
||||
* @throws IOException
|
||||
*/
|
||||
public static final String upload(String baseDir, MultipartFile file) throws IOException {
|
||||
try {
|
||||
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param baseDir 相对应用的基目录
|
||||
* @param file 上传的文件
|
||||
* @param allowedExtension 上传文件类型
|
||||
* @return 返回上传成功的文件名
|
||||
* @throws FileSizeLimitExceededException 如果超出最大大小
|
||||
* @throws FileNameLengthLimitExceededException 文件名太长
|
||||
* @throws IOException 比如读写文件出错时
|
||||
* @throws InvalidExtensionException 文件校验异常
|
||||
*/
|
||||
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
|
||||
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
|
||||
InvalidExtensionException {
|
||||
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
|
||||
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
|
||||
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
|
||||
}
|
||||
|
||||
assertAllowed(file, allowedExtension);
|
||||
|
||||
String fileName = extractFilename(file);
|
||||
|
||||
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
|
||||
file.transferTo(Paths.get(absPath));
|
||||
return getPathFileName(baseDir, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码文件名
|
||||
*/
|
||||
public static final String extractFilename(MultipartFile file) {
|
||||
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
|
||||
FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
|
||||
}
|
||||
|
||||
public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
|
||||
File desc = new File(uploadDir + File.separator + fileName);
|
||||
|
||||
if (!desc.exists()) {
|
||||
if (!desc.getParentFile().exists()) {
|
||||
desc.getParentFile().mkdirs();
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
public static final String getPathFileName(String uploadDir, String fileName) throws IOException {
|
||||
int dirLastIndex = JsowellConfig.getProfile().length() + 1;
|
||||
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
|
||||
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件大小校验
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return
|
||||
* @throws FileSizeLimitExceededException 如果超出最大大小
|
||||
* @throws InvalidExtensionException
|
||||
*/
|
||||
public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
|
||||
throws FileSizeLimitExceededException, InvalidExtensionException {
|
||||
long size = file.getSize();
|
||||
if (size > DEFAULT_MAX_SIZE) {
|
||||
throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
|
||||
}
|
||||
|
||||
String fileName = file.getOriginalFilename();
|
||||
String extension = getExtension(file);
|
||||
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
|
||||
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
|
||||
throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
|
||||
fileName);
|
||||
} else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
|
||||
throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
|
||||
fileName);
|
||||
} else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
|
||||
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
|
||||
fileName);
|
||||
} else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
|
||||
throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
|
||||
fileName);
|
||||
} else {
|
||||
throw new InvalidExtensionException(allowedExtension, extension, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断MIME类型是否是允许的MIME类型
|
||||
*
|
||||
* @param extension
|
||||
* @param allowedExtension
|
||||
* @return
|
||||
*/
|
||||
public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
|
||||
for (String str : allowedExtension) {
|
||||
if (str.equalsIgnoreCase(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名的后缀
|
||||
*
|
||||
* @param file 表单文件
|
||||
* @return 后缀名
|
||||
*/
|
||||
public static final String getExtension(MultipartFile file) {
|
||||
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
|
||||
if (StringUtils.isEmpty(extension)) {
|
||||
extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package com.jsowell.common.util.file;
|
||||
|
||||
import com.jsowell.common.config.JsowellConfig;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.id.IdUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 文件处理工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class FileUtils {
|
||||
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
|
||||
|
||||
/**
|
||||
* 输出指定文件的byte数组
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* @param os 输出流
|
||||
* @return
|
||||
*/
|
||||
public static void writeBytes(String filePath, OutputStream os) throws IOException {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(filePath);
|
||||
}
|
||||
fis = new FileInputStream(file);
|
||||
byte[] b = new byte[1024];
|
||||
int length;
|
||||
while ((length = fis.read(b)) > 0) {
|
||||
os.write(b, 0, length);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} finally {
|
||||
IOUtils.close(os);
|
||||
IOUtils.close(fis);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写数据到文件中
|
||||
*
|
||||
* @param data 数据
|
||||
* @return 目标文件
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public static String writeImportBytes(byte[] data) throws IOException {
|
||||
return writeBytes(data, JsowellConfig.getImportPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 写数据到文件中
|
||||
*
|
||||
* @param data 数据
|
||||
* @param uploadDir 目标文件
|
||||
* @return 目标文件
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public static String writeBytes(byte[] data, String uploadDir) throws IOException {
|
||||
FileOutputStream fos = null;
|
||||
String pathName = "";
|
||||
try {
|
||||
String extension = getFileExtendName(data);
|
||||
pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
|
||||
File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
|
||||
fos = new FileOutputStream(file);
|
||||
fos.write(data);
|
||||
} finally {
|
||||
IOUtils.close(fos);
|
||||
}
|
||||
return FileUploadUtils.getPathFileName(uploadDir, pathName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param filePath 文件
|
||||
* @return
|
||||
*/
|
||||
public static boolean deleteFile(String filePath) {
|
||||
boolean flag = false;
|
||||
File file = new File(filePath);
|
||||
// 路径为文件且不为空则进行删除
|
||||
if (file.isFile() && file.exists()) {
|
||||
file.delete();
|
||||
flag = true;
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件名称验证
|
||||
*
|
||||
* @param filename 文件名称
|
||||
* @return true 正常 false 非法
|
||||
*/
|
||||
public static boolean isValidFilename(String filename) {
|
||||
return filename.matches(FILENAME_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否可下载
|
||||
*
|
||||
* @param resource 需要下载的文件
|
||||
* @return true 正常 false 非法
|
||||
*/
|
||||
public static boolean checkAllowDownload(String resource) {
|
||||
// 禁止目录上跳级别
|
||||
if (StringUtils.contains(resource, "..")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查允许下载的文件规则
|
||||
if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 不在允许下载的文件规则
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件名重新编码
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @param fileName 文件名
|
||||
* @return 编码后的文件名
|
||||
*/
|
||||
public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
|
||||
final String agent = request.getHeader("USER-AGENT");
|
||||
String filename = fileName;
|
||||
if (agent.contains("MSIE")) {
|
||||
// IE浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
filename = filename.replace("+", " ");
|
||||
} else if (agent.contains("Firefox")) {
|
||||
// 火狐浏览器
|
||||
filename = new String(fileName.getBytes(), "ISO8859-1");
|
||||
} else if (agent.contains("Chrome")) {
|
||||
// google浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
} else {
|
||||
// 其它浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件名重新编码
|
||||
*
|
||||
* @param response 响应对象
|
||||
* @param realFileName 真实文件名
|
||||
*/
|
||||
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
|
||||
String percentEncodedFileName = percentEncode(realFileName);
|
||||
|
||||
StringBuilder contentDispositionValue = new StringBuilder();
|
||||
contentDispositionValue.append("attachment; filename=")
|
||||
.append(percentEncodedFileName)
|
||||
.append(";")
|
||||
.append("filename*=")
|
||||
.append("utf-8''")
|
||||
.append(percentEncodedFileName);
|
||||
|
||||
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
|
||||
response.setHeader("Content-disposition", contentDispositionValue.toString());
|
||||
response.setHeader("download-filename", percentEncodedFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 百分号编码工具方法
|
||||
*
|
||||
* @param s 需要百分号编码的字符串
|
||||
* @return 百分号编码后的字符串
|
||||
*/
|
||||
public static String percentEncode(String s) throws UnsupportedEncodingException {
|
||||
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
|
||||
return encode.replaceAll("\\+", "%20");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像后缀
|
||||
*
|
||||
* @param photoByte 图像数据
|
||||
* @return 后缀名
|
||||
*/
|
||||
public static String getFileExtendName(byte[] photoByte) {
|
||||
String strFileExtendName = "jpg";
|
||||
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
|
||||
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
|
||||
strFileExtendName = "gif";
|
||||
} else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
|
||||
strFileExtendName = "jpg";
|
||||
} else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
|
||||
strFileExtendName = "bmp";
|
||||
} else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
|
||||
strFileExtendName = "png";
|
||||
}
|
||||
return strFileExtendName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名称 /profile/upload/2022/04/16/jsowell.png -- jsowell.png
|
||||
*
|
||||
* @param fileName 路径名称
|
||||
* @return 没有文件路径的名称
|
||||
*/
|
||||
public static String getName(String fileName) {
|
||||
if (fileName == null) {
|
||||
return null;
|
||||
}
|
||||
int lastUnixPos = fileName.lastIndexOf('/');
|
||||
int lastWindowsPos = fileName.lastIndexOf('\\');
|
||||
int index = Math.max(lastUnixPos, lastWindowsPos);
|
||||
return fileName.substring(index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取不带后缀文件名称 /profile/upload/2022/04/16/jsowell.png -- jsowell
|
||||
*
|
||||
* @param fileName 路径名称
|
||||
* @return 没有文件路径和后缀的名称
|
||||
*/
|
||||
public static String getNameNotSuffix(String fileName) {
|
||||
if (fileName == null) {
|
||||
return null;
|
||||
}
|
||||
String baseName = FilenameUtils.getBaseName(fileName);
|
||||
return baseName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.jsowell.common.util.file;
|
||||
|
||||
import com.jsowell.common.config.JsowellConfig;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 图片处理工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class ImageUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
|
||||
|
||||
public static byte[] getImage(String imagePath) {
|
||||
InputStream is = getFile(imagePath);
|
||||
try {
|
||||
return IOUtils.toByteArray(is);
|
||||
} catch (Exception e) {
|
||||
log.error("图片加载异常 {}", e);
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream getFile(String imagePath) {
|
||||
try {
|
||||
byte[] result = readFile(imagePath);
|
||||
result = Arrays.copyOf(result, result.length);
|
||||
return new ByteArrayInputStream(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取图片异常 {}", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件为字节数据
|
||||
*
|
||||
* @param url 地址
|
||||
* @return 字节数据
|
||||
*/
|
||||
public static byte[] readFile(String url) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
if (url.startsWith("http")) {
|
||||
// 网络地址
|
||||
URL urlObj = new URL(url);
|
||||
URLConnection urlConnection = urlObj.openConnection();
|
||||
urlConnection.setConnectTimeout(30 * 1000);
|
||||
urlConnection.setReadTimeout(60 * 1000);
|
||||
urlConnection.setDoInput(true);
|
||||
in = urlConnection.getInputStream();
|
||||
} else {
|
||||
// 本机地址
|
||||
String localPath = JsowellConfig.getProfile();
|
||||
String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
|
||||
in = new FileInputStream(downloadPath);
|
||||
}
|
||||
return IOUtils.toByteArray(in);
|
||||
} catch (Exception e) {
|
||||
log.error("获取文件路径异常 {}", e);
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.jsowell.common.util.file;
|
||||
|
||||
/**
|
||||
* 媒体类型工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class MimeTypeUtils {
|
||||
public static final String IMAGE_PNG = "image/png";
|
||||
|
||||
public static final String IMAGE_JPG = "image/jpg";
|
||||
|
||||
public static final String IMAGE_JPEG = "image/jpeg";
|
||||
|
||||
public static final String IMAGE_BMP = "image/bmp";
|
||||
|
||||
public static final String IMAGE_GIF = "image/gif";
|
||||
|
||||
public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
|
||||
|
||||
public static final String[] FLASH_EXTENSION = {"swf", "flv"};
|
||||
|
||||
public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
|
||||
"asf", "rm", "rmvb"};
|
||||
|
||||
public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
|
||||
|
||||
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
|
||||
// 图片
|
||||
"bmp", "gif", "jpg", "jpeg", "png",
|
||||
// word excel powerpoint
|
||||
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
|
||||
// 压缩文件
|
||||
"rar", "zip", "gz", "bz2",
|
||||
// 视频格式
|
||||
"mp4", "avi", "rmvb",
|
||||
// pdf
|
||||
"pdf"};
|
||||
|
||||
public static String getExtension(String prefix) {
|
||||
switch (prefix) {
|
||||
case IMAGE_PNG:
|
||||
return "png";
|
||||
case IMAGE_JPG:
|
||||
return "jpg";
|
||||
case IMAGE_JPEG:
|
||||
return "jpeg";
|
||||
case IMAGE_BMP:
|
||||
return "bmp";
|
||||
case IMAGE_GIF:
|
||||
return "gif";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.jsowell.common.util.html;
|
||||
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 转义和反转义工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class EscapeUtil {
|
||||
public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
|
||||
|
||||
private static final char[][] TEXT = new char[64][];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
TEXT[i] = new char[]{(char) i};
|
||||
}
|
||||
|
||||
// special HTML characters
|
||||
TEXT['\''] = "'".toCharArray(); // 单引号
|
||||
TEXT['"'] = """.toCharArray(); // 双引号
|
||||
TEXT['&'] = "&".toCharArray(); // &符
|
||||
TEXT['<'] = "<".toCharArray(); // 小于号
|
||||
TEXT['>'] = ">".toCharArray(); // 大于号
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义文本中的HTML字符为安全的字符
|
||||
*
|
||||
* @param text 被转义的文本
|
||||
* @return 转义后的文本
|
||||
*/
|
||||
public static String escape(String text) {
|
||||
return encode(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原被转义的HTML特殊字符
|
||||
*
|
||||
* @param content 包含转义符的HTML内容
|
||||
* @return 转换后的字符串
|
||||
*/
|
||||
public static String unescape(String content) {
|
||||
return decode(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有HTML标签,但是不删除标签内的内容
|
||||
*
|
||||
* @param content 文本
|
||||
* @return 清除标签后的文本
|
||||
*/
|
||||
public static String clean(String content) {
|
||||
return new HTMLFilter().filter(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape编码
|
||||
*
|
||||
* @param text 被编码的文本
|
||||
* @return 编码后的字符
|
||||
*/
|
||||
private static String encode(String text) {
|
||||
if (StringUtils.isEmpty(text)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
final StringBuilder tmp = new StringBuilder(text.length() * 6);
|
||||
char c;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
c = text.charAt(i);
|
||||
if (c < 256) {
|
||||
tmp.append("%");
|
||||
if (c < 16) {
|
||||
tmp.append("0");
|
||||
}
|
||||
tmp.append(Integer.toString(c, 16));
|
||||
} else {
|
||||
tmp.append("%u");
|
||||
if (c <= 0xfff) {
|
||||
// issue#I49JU8@Gitee
|
||||
tmp.append("0");
|
||||
}
|
||||
tmp.append(Integer.toString(c, 16));
|
||||
}
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape解码
|
||||
*
|
||||
* @param content 被转义的内容
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String decode(String content) {
|
||||
if (StringUtils.isEmpty(content)) {
|
||||
return content;
|
||||
}
|
||||
|
||||
StringBuilder tmp = new StringBuilder(content.length());
|
||||
int lastPos = 0, pos = 0;
|
||||
char ch;
|
||||
while (lastPos < content.length()) {
|
||||
pos = content.indexOf("%", lastPos);
|
||||
if (pos == lastPos) {
|
||||
if (content.charAt(pos + 1) == 'u') {
|
||||
ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
|
||||
tmp.append(ch);
|
||||
lastPos = pos + 6;
|
||||
} else {
|
||||
ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
|
||||
tmp.append(ch);
|
||||
lastPos = pos + 3;
|
||||
}
|
||||
} else {
|
||||
if (pos == -1) {
|
||||
tmp.append(content.substring(lastPos));
|
||||
lastPos = content.length();
|
||||
} else {
|
||||
tmp.append(content.substring(lastPos, pos));
|
||||
lastPos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String html = "<script>alert(1);</script>";
|
||||
String escape = EscapeUtil.escape(html);
|
||||
// String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
|
||||
// String html = "<123";
|
||||
// String html = "123>";
|
||||
System.out.println("clean: " + EscapeUtil.clean(html));
|
||||
System.out.println("escape: " + escape);
|
||||
System.out.println("unescape: " + EscapeUtil.unescape(escape));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,501 @@
|
||||
package com.jsowell.common.util.html;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* HTML过滤器,用于去除XSS漏洞隐患。
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public final class HTMLFilter {
|
||||
/**
|
||||
* regex flag union representing /si modifiers in php
|
||||
**/
|
||||
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
|
||||
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
|
||||
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
|
||||
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
|
||||
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
|
||||
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
|
||||
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
|
||||
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
|
||||
private static final Pattern P_END_ARROW = Pattern.compile("^>");
|
||||
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||
private static final Pattern P_AMP = Pattern.compile("&");
|
||||
private static final Pattern P_QUOTE = Pattern.compile("\"");
|
||||
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
|
||||
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
|
||||
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
|
||||
|
||||
// @xxx could grow large... maybe use sesat's ReferenceMap
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* set of allowed html elements, along with allowed attributes for each element
|
||||
**/
|
||||
private final Map<String, List<String>> vAllowed;
|
||||
/**
|
||||
* counts of open tags for each (allowable) html element
|
||||
**/
|
||||
private final Map<String, Integer> vTagCounts = new HashMap<>();
|
||||
|
||||
/**
|
||||
* html elements which must always be self-closing (e.g. "<img />")
|
||||
**/
|
||||
private final String[] vSelfClosingTags;
|
||||
/**
|
||||
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
|
||||
**/
|
||||
private final String[] vNeedClosingTags;
|
||||
/**
|
||||
* set of disallowed html elements
|
||||
**/
|
||||
private final String[] vDisallowed;
|
||||
/**
|
||||
* attributes which should be checked for valid protocols
|
||||
**/
|
||||
private final String[] vProtocolAtts;
|
||||
/**
|
||||
* allowed protocols
|
||||
**/
|
||||
private final String[] vAllowedProtocols;
|
||||
/**
|
||||
* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
|
||||
**/
|
||||
private final String[] vRemoveBlanks;
|
||||
/**
|
||||
* entities allowed within html markup
|
||||
**/
|
||||
private final String[] vAllowedEntities;
|
||||
/**
|
||||
* flag determining whether comments are allowed in input String.
|
||||
*/
|
||||
private final boolean stripComment;
|
||||
private final boolean encodeQuotes;
|
||||
/**
|
||||
* flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
|
||||
* becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
|
||||
*/
|
||||
private final boolean alwaysMakeTags;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public HTMLFilter() {
|
||||
vAllowed = new HashMap<>();
|
||||
|
||||
final ArrayList<String> a_atts = new ArrayList<>();
|
||||
a_atts.add("href");
|
||||
a_atts.add("target");
|
||||
vAllowed.put("a", a_atts);
|
||||
|
||||
final ArrayList<String> img_atts = new ArrayList<>();
|
||||
img_atts.add("src");
|
||||
img_atts.add("width");
|
||||
img_atts.add("height");
|
||||
img_atts.add("alt");
|
||||
vAllowed.put("img", img_atts);
|
||||
|
||||
final ArrayList<String> no_atts = new ArrayList<>();
|
||||
vAllowed.put("b", no_atts);
|
||||
vAllowed.put("strong", no_atts);
|
||||
vAllowed.put("i", no_atts);
|
||||
vAllowed.put("em", no_atts);
|
||||
|
||||
vSelfClosingTags = new String[]{"img"};
|
||||
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
|
||||
vDisallowed = new String[]{};
|
||||
vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
|
||||
vProtocolAtts = new String[]{"src", "href"};
|
||||
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
|
||||
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
|
||||
stripComment = true;
|
||||
encodeQuotes = true;
|
||||
alwaysMakeTags = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map-parameter configurable constructor.
|
||||
*
|
||||
* @param conf map containing configuration. keys match field names.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public HTMLFilter(final Map<String, Object> conf) {
|
||||
|
||||
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
|
||||
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
|
||||
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
|
||||
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
|
||||
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
|
||||
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
|
||||
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
|
||||
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
|
||||
|
||||
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
|
||||
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
|
||||
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
|
||||
vDisallowed = (String[]) conf.get("vDisallowed");
|
||||
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
|
||||
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
|
||||
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
|
||||
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
|
||||
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
|
||||
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
|
||||
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
vTagCounts.clear();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// my versions of some PHP library functions
|
||||
public static String chr(final int decimal) {
|
||||
return String.valueOf((char) decimal);
|
||||
}
|
||||
|
||||
public static String htmlSpecialChars(final String s) {
|
||||
String result = s;
|
||||
result = regexReplace(P_AMP, "&", result);
|
||||
result = regexReplace(P_QUOTE, """, result);
|
||||
result = regexReplace(P_LEFT_ARROW, "<", result);
|
||||
result = regexReplace(P_RIGHT_ARROW, ">", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* given a user submitted input String, filter out any invalid or restricted html.
|
||||
*
|
||||
* @param input text (i.e. submitted by a user) than may contain html
|
||||
* @return "clean" version of input, with only valid, whitelisted html elements allowed
|
||||
*/
|
||||
public String filter(final String input) {
|
||||
reset();
|
||||
String s = input;
|
||||
|
||||
s = escapeComments(s);
|
||||
|
||||
s = balanceHTML(s);
|
||||
|
||||
s = checkTags(s);
|
||||
|
||||
s = processRemoveBlanks(s);
|
||||
|
||||
// s = validateEntities(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public boolean isAlwaysMakeTags() {
|
||||
return alwaysMakeTags;
|
||||
}
|
||||
|
||||
public boolean isStripComments() {
|
||||
return stripComment;
|
||||
}
|
||||
|
||||
private String escapeComments(final String s) {
|
||||
final Matcher m = P_COMMENTS.matcher(s);
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
if (m.find()) {
|
||||
final String match = m.group(1); // (.*?)
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private String balanceHTML(String s) {
|
||||
if (alwaysMakeTags) {
|
||||
//
|
||||
// try and form html
|
||||
//
|
||||
s = regexReplace(P_END_ARROW, "", s);
|
||||
// 不追加结束标签
|
||||
s = regexReplace(P_BODY_TO_END, "<$1>", s);
|
||||
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
|
||||
|
||||
} else {
|
||||
//
|
||||
// escape stray brackets
|
||||
//
|
||||
s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
|
||||
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
|
||||
|
||||
//
|
||||
// the last regexp causes '<>' entities to appear
|
||||
// (we need to do a lookahead assertion so that the last bracket can
|
||||
// be used in the next pass of the regexp)
|
||||
//
|
||||
s = regexReplace(P_BOTH_ARROWS, "", s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String checkTags(String s) {
|
||||
Matcher m = P_TAGS.matcher(s);
|
||||
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
while (m.find()) {
|
||||
String replaceStr = m.group(1);
|
||||
replaceStr = processTag(replaceStr);
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
// these get tallied in processTag
|
||||
// (remember to reset before subsequent calls to filter method)
|
||||
final StringBuilder sBuilder = new StringBuilder(buf.toString());
|
||||
for (String key : vTagCounts.keySet()) {
|
||||
for (int ii = 0; ii < vTagCounts.get(key); ii++) {
|
||||
sBuilder.append("</").append(key).append(">");
|
||||
}
|
||||
}
|
||||
s = sBuilder.toString();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String processRemoveBlanks(final String s) {
|
||||
String result = s;
|
||||
for (String tag : vRemoveBlanks) {
|
||||
if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
|
||||
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
|
||||
}
|
||||
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
|
||||
if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
|
||||
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
|
||||
}
|
||||
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
|
||||
Matcher m = regex_pattern.matcher(s);
|
||||
return m.replaceAll(replacement);
|
||||
}
|
||||
|
||||
private String processTag(final String s) {
|
||||
// ending tags
|
||||
Matcher m = P_END_TAG.matcher(s);
|
||||
if (m.find()) {
|
||||
final String name = m.group(1).toLowerCase();
|
||||
if (allowed(name)) {
|
||||
if (!inArray(name, vSelfClosingTags)) {
|
||||
if (vTagCounts.containsKey(name)) {
|
||||
vTagCounts.put(name, vTagCounts.get(name) - 1);
|
||||
return "</" + name + ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// starting tags
|
||||
m = P_START_TAG.matcher(s);
|
||||
if (m.find()) {
|
||||
final String name = m.group(1).toLowerCase();
|
||||
final String body = m.group(2);
|
||||
String ending = m.group(3);
|
||||
|
||||
// debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
|
||||
if (allowed(name)) {
|
||||
final StringBuilder params = new StringBuilder();
|
||||
|
||||
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
|
||||
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
|
||||
final List<String> paramNames = new ArrayList<>();
|
||||
final List<String> paramValues = new ArrayList<>();
|
||||
while (m2.find()) {
|
||||
paramNames.add(m2.group(1)); // ([a-z0-9]+)
|
||||
paramValues.add(m2.group(3)); // (.*?)
|
||||
}
|
||||
while (m3.find()) {
|
||||
paramNames.add(m3.group(1)); // ([a-z0-9]+)
|
||||
paramValues.add(m3.group(3)); // ([^\"\\s']+)
|
||||
}
|
||||
|
||||
String paramName, paramValue;
|
||||
for (int ii = 0; ii < paramNames.size(); ii++) {
|
||||
paramName = paramNames.get(ii).toLowerCase();
|
||||
paramValue = paramValues.get(ii);
|
||||
|
||||
// debug( "paramName='" + paramName + "'" );
|
||||
// debug( "paramValue='" + paramValue + "'" );
|
||||
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
|
||||
|
||||
if (allowedAttribute(name, paramName)) {
|
||||
if (inArray(paramName, vProtocolAtts)) {
|
||||
paramValue = processParamProtocol(paramValue);
|
||||
}
|
||||
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (inArray(name, vSelfClosingTags)) {
|
||||
ending = " /";
|
||||
}
|
||||
|
||||
if (inArray(name, vNeedClosingTags)) {
|
||||
ending = "";
|
||||
}
|
||||
|
||||
if (ending == null || ending.length() < 1) {
|
||||
if (vTagCounts.containsKey(name)) {
|
||||
vTagCounts.put(name, vTagCounts.get(name) + 1);
|
||||
} else {
|
||||
vTagCounts.put(name, 1);
|
||||
}
|
||||
} else {
|
||||
ending = " /";
|
||||
}
|
||||
return "<" + name + params + ending + ">";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// comments
|
||||
m = P_COMMENT.matcher(s);
|
||||
if (!stripComment && m.find()) {
|
||||
return "<" + m.group() + ">";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private String processParamProtocol(String s) {
|
||||
s = decodeEntities(s);
|
||||
final Matcher m = P_PROTOCOL.matcher(s);
|
||||
if (m.find()) {
|
||||
final String protocol = m.group(1);
|
||||
if (!inArray(protocol, vAllowedProtocols)) {
|
||||
// bad protocol, turn into local anchor link instead
|
||||
s = "#" + s.substring(protocol.length() + 1);
|
||||
if (s.startsWith("#//")) {
|
||||
s = "#" + s.substring(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String decodeEntities(String s) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
Matcher m = P_ENTITY.matcher(s);
|
||||
while (m.find()) {
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.decode(match).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
buf = new StringBuffer();
|
||||
m = P_ENTITY_UNICODE.matcher(s);
|
||||
while (m.find()) {
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.valueOf(match, 16).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
buf = new StringBuffer();
|
||||
m = P_ENCODE.matcher(s);
|
||||
while (m.find()) {
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.valueOf(match, 16).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
s = validateEntities(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
private String validateEntities(final String s) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
// validate entities throughout the string
|
||||
Matcher m = P_VALID_ENTITIES.matcher(s);
|
||||
while (m.find()) {
|
||||
final String one = m.group(1); // ([^&;]*)
|
||||
final String two = m.group(2); // (?=(;|&|$))
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
return encodeQuotes(buf.toString());
|
||||
}
|
||||
|
||||
private String encodeQuotes(final String s) {
|
||||
if (encodeQuotes) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
Matcher m = P_VALID_QUOTES.matcher(s);
|
||||
while (m.find()) {
|
||||
final String one = m.group(1); // (>|^)
|
||||
final String two = m.group(2); // ([^<]+?)
|
||||
final String three = m.group(3); // (<|$)
|
||||
// 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two)
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
return buf.toString();
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private String checkEntity(final String preamble, final String term) {
|
||||
|
||||
return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble;
|
||||
}
|
||||
|
||||
private boolean isValidEntity(final String entity) {
|
||||
return inArray(entity, vAllowedEntities);
|
||||
}
|
||||
|
||||
private static boolean inArray(final String s, final String[] array) {
|
||||
for (String item : array) {
|
||||
if (item != null && item.equals(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean allowed(final String name) {
|
||||
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
|
||||
}
|
||||
|
||||
private boolean allowedAttribute(final String name, final String paramName) {
|
||||
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.jsowell.common.util.http;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 通用http工具封装
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class HttpHelper {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
|
||||
|
||||
public static String getBodyString(ServletRequest request) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
BufferedReader reader = null;
|
||||
try (InputStream inputStream = request.getInputStream()) {
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||
String line = "";
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("getBodyString出现问题!");
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(ExceptionUtils.getMessage(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
package com.jsowell.common.util.http;
|
||||
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* 通用http发送方法
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class HttpUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送GET方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendGet(String url) {
|
||||
return sendGet(url, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送GET方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendGet(String url, String param) {
|
||||
return sendGet(url, param, Constants.UTF8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送GET方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||
* @param contentType 编码类型
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendGet(String url, String param, String contentType) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
|
||||
log.info("sendGet - {}", urlNameString);
|
||||
URL realUrl = new URL(urlNameString);
|
||||
URLConnection connection = realUrl.openConnection();
|
||||
connection.setRequestProperty("accept", "*/*");
|
||||
connection.setRequestProperty("connection", "Keep-Alive");
|
||||
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
connection.connect();
|
||||
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
result.append(line);
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
} catch (IOException e) {
|
||||
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
|
||||
} catch (Exception e) {
|
||||
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
|
||||
} finally {
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送POST方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendPost(String url, String param) {
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
StringBuilder result = new StringBuilder();
|
||||
try {
|
||||
log.info("sendPost - {}", url);
|
||||
URL realUrl = new URL(url);
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
conn.setRequestProperty("accept", "*/*");
|
||||
conn.setRequestProperty("connection", "Keep-Alive");
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||
conn.setRequestProperty("contentType", "utf-8");
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
out.print(param);
|
||||
out.flush();
|
||||
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
result.append(line);
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
} catch (IOException e) {
|
||||
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
|
||||
} catch (Exception e) {
|
||||
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送POST方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||
* @param contentType header中的contentType
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendPostContentType(String url, String param, String contentType) {
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
StringBuilder result = new StringBuilder();
|
||||
try {
|
||||
log.info("sendPost - {}", url);
|
||||
URL realUrl = new URL(url);
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
conn.setRequestProperty("accept", "*/*");
|
||||
conn.setRequestProperty("connection", "Keep-Alive");
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||
conn.setRequestProperty("Content-Type", contentType);
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
out.print(param);
|
||||
out.flush();
|
||||
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
result.append(line);
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
} catch (IOException e) {
|
||||
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
|
||||
} catch (Exception e) {
|
||||
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static String sendSSLPost(String url, String param) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
String urlNameString = url + "?" + param;
|
||||
try {
|
||||
log.info("sendSSLPost - {}", urlNameString);
|
||||
SSLContext sc = SSLContext.getInstance("SSL");
|
||||
sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());
|
||||
URL console = new URL(urlNameString);
|
||||
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
|
||||
conn.setRequestProperty("accept", "*/*");
|
||||
conn.setRequestProperty("connection", "Keep-Alive");
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||
conn.setRequestProperty("contentType", "utf-8");
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
|
||||
conn.setSSLSocketFactory(sc.getSocketFactory());
|
||||
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
|
||||
conn.connect();
|
||||
InputStream is = conn.getInputStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
String ret = "";
|
||||
while ((ret = br.readLine()) != null) {
|
||||
if (ret != null && !"".equals(ret.trim())) {
|
||||
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
conn.disconnect();
|
||||
br.close();
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
} catch (IOException e) {
|
||||
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
|
||||
} catch (Exception e) {
|
||||
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static class TrustAnyTrustManager implements X509TrustManager {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[]{};
|
||||
}
|
||||
}
|
||||
|
||||
private static class TrustAnyHostnameVerifier implements HostnameVerifier {
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.jsowell.common.util.id;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.RandomUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* ID生成器工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class IdUtils {
|
||||
/**
|
||||
* 获取随机UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String randomUUID() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String simpleUUID() {
|
||||
return UUID.randomUUID().toString(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String fastUUID() {
|
||||
return UUID.fastUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String fastSimpleUUID() {
|
||||
return UUID.fastUUID().toString(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成交易流水号
|
||||
*
|
||||
* @param pileSn 桩编号 例如:32010600019236
|
||||
* @param connectorCode 枪口号 例如:01
|
||||
*/
|
||||
public static String generateOrderCode(String pileSn, String connectorCode) {
|
||||
return generateOrderCode(pileSn + connectorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成交易流水号
|
||||
* 生成规则为 格式桩号(7bytes) +枪号(1byte) +年月日时分秒(6bytes) +自 增序号(2bytes);
|
||||
* @param pileConnectorCode 为已经拼好的充电桩枪口号 例如:3201060001923601
|
||||
* @return 交易流水号 例如:32010600019236012001061803423060
|
||||
*/
|
||||
public static String generateOrderCode(String pileConnectorCode) {
|
||||
String timeNow = DateUtils.dateTimeNow(DateUtils.YYMMDDHHMMSS);
|
||||
//随机生成一个四位整数
|
||||
String randomNumber = RandomUtil.getRandomNumber(4);
|
||||
return pileConnectorCode + timeNow + randomNumber;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Set<String> set = Sets.newHashSet();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
// String s = System.currentTimeMillis() + RandomUtil.getRandomNumber(6);
|
||||
String id = SnowflakeIdWorker.getSnowflakeId();
|
||||
set.add(id);
|
||||
System.out.println(id);
|
||||
}
|
||||
System.out.println("set size = " + set.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成八位会员id
|
||||
*/
|
||||
public static String getMemberId() {
|
||||
long id = Long.parseLong(SnowflakeIdWorker.getSnowflakeId());
|
||||
StringBuilder sb = new StringBuilder(id + "");
|
||||
StringBuilder reverse = sb.reverse();//将id翻转:我们发现id很长,且高位很长部分是一样的数
|
||||
id = new Long(reverse.toString()) / 1000;//切去部分长度
|
||||
while (id > 100000000) {
|
||||
id /= 10;
|
||||
}
|
||||
Integer num = Integer.parseInt(id + "");
|
||||
return String.valueOf(num);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.jsowell.common.util.id;
|
||||
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author jsowell 序列生成类
|
||||
*/
|
||||
public class Seq {
|
||||
// 通用序列类型
|
||||
public static final String commSeqType = "COMMON";
|
||||
|
||||
// 上传序列类型
|
||||
public static final String uploadSeqType = "UPLOAD";
|
||||
|
||||
// 通用接口序列数
|
||||
private static AtomicInteger commSeq = new AtomicInteger(1);
|
||||
|
||||
// 上传接口序列数
|
||||
private static AtomicInteger uploadSeq = new AtomicInteger(1);
|
||||
|
||||
// 机器标识
|
||||
private static String machineCode = "A";
|
||||
|
||||
/**
|
||||
* 获取通用序列号
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId() {
|
||||
return getId(commSeqType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId(String type) {
|
||||
AtomicInteger atomicInt = commSeq;
|
||||
if (uploadSeqType.equals(type)) {
|
||||
atomicInt = uploadSeq;
|
||||
}
|
||||
return getId(atomicInt, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
|
||||
*
|
||||
* @param atomicInt 序列数
|
||||
* @param length 数值长度
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId(AtomicInteger atomicInt, int length) {
|
||||
String result = DateUtils.dateTimeNow();
|
||||
result += machineCode;
|
||||
result += getSeq(atomicInt, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
private synchronized static String getSeq(AtomicInteger atomicInt, int length) {
|
||||
// 先取值再+1
|
||||
int value = atomicInt.getAndIncrement();
|
||||
|
||||
// 如果更新后值>=10 的 (length)幂次方则重置为1
|
||||
int maxSeq = (int) Math.pow(10, length);
|
||||
if (atomicInt.get() >= maxSeq) {
|
||||
atomicInt.set(1);
|
||||
}
|
||||
// 转字符串,用0左补齐
|
||||
return StringUtils.padl(value, length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.jsowell.common.util.id;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.core.redis.RedisCache;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生成sn号Util
|
||||
*/
|
||||
@Component
|
||||
public class SnUtils {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SnUtils.class);
|
||||
|
||||
@Autowired
|
||||
public RedisCache redisCache;
|
||||
|
||||
private String prefix = "88";
|
||||
|
||||
/**
|
||||
* 获取递增的序列号
|
||||
* 充电桩编号定义为14位: 固定位88 + 年份23 + 10位自增数字不足补0
|
||||
* @param prefix 生成序列号的前缀
|
||||
* @return
|
||||
*/
|
||||
private String getPileSn(String prefix) {
|
||||
//序列号前缀加特定标识,如系统模块名之类的 防止重复
|
||||
String key = CacheConstants.PILE_SN_GENERATE_KEY + prefix;
|
||||
String increResult = null;
|
||||
try {
|
||||
Long increNum = redisCache.increment(key, 1);
|
||||
// 年份
|
||||
int year = LocalDate.now().getYear() - 2000;
|
||||
//不足补位
|
||||
increResult = prefix + year + String.format("%1$010d", increNum);
|
||||
} catch (Exception e) {
|
||||
logger.error("获取序列号失败", e);
|
||||
}
|
||||
return increResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 生成sn号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> generateSN() {
|
||||
return generateSN(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成sn号
|
||||
*
|
||||
* @param size
|
||||
* @return
|
||||
*/
|
||||
public List<String> generateSN(int size) {
|
||||
List<String> resultList = Lists.newArrayList();
|
||||
if (size <= 0) {
|
||||
return resultList;
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
resultList.add(getPileSn(prefix));
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.jsowell.common.util.id;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Twitter_Snowflake<br>
|
||||
* SnowFlake的结构如下(每部分用-分开):<br>
|
||||
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
|
||||
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
|
||||
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
|
||||
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
|
||||
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
|
||||
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
|
||||
* 加起来刚好64位,为一个Long型。<br>
|
||||
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
|
||||
*/
|
||||
@Slf4j
|
||||
public class SnowflakeIdWorker {
|
||||
|
||||
// ==============================Fields===========================================
|
||||
|
||||
/**
|
||||
* 机器id所占的位数
|
||||
*/
|
||||
private final long workerIdBits = 5L;
|
||||
|
||||
/**
|
||||
* 数据标识id所占的位数
|
||||
*/
|
||||
private final long dataCenterIdBits = 5L;
|
||||
|
||||
/**
|
||||
* 工作机器ID(0~31)
|
||||
*/
|
||||
private final long workerId;
|
||||
|
||||
/**
|
||||
* 数据中心ID(0~31)
|
||||
*/
|
||||
private final long dataCenterId;
|
||||
|
||||
/**
|
||||
* 毫秒内序列(0~4095)
|
||||
*/
|
||||
private long sequence = 0L;
|
||||
|
||||
/**
|
||||
* 上次生成ID的时间截
|
||||
*/
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
private static final SnowflakeIdWorker idWorker;
|
||||
|
||||
static {
|
||||
idWorker = new SnowflakeIdWorker(getWorkId(), getDataCenterId());
|
||||
}
|
||||
|
||||
//==============================Constructors=====================================
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param workerId 工作ID (0~31)
|
||||
* @param dataCenterId 数据中心ID (0~31)
|
||||
*/
|
||||
public SnowflakeIdWorker(long workerId, long dataCenterId) {
|
||||
/*
|
||||
* 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
|
||||
*/
|
||||
long maxWorkerId = ~(-1L << workerIdBits);
|
||||
if (workerId > maxWorkerId || workerId < 0) {
|
||||
throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
|
||||
}
|
||||
/*
|
||||
* 支持的最大数据标识id,结果是31
|
||||
*/
|
||||
long maxDataCenterId = ~(-1L << dataCenterIdBits);
|
||||
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
|
||||
throw new IllegalArgumentException(String.format("dataCenterId can't be greater than %d or less than 0", maxDataCenterId));
|
||||
}
|
||||
this.workerId = workerId;
|
||||
this.dataCenterId = dataCenterId;
|
||||
}
|
||||
|
||||
// ==============================Methods==========================================
|
||||
|
||||
/**
|
||||
* 获得下一个ID (该方法是线程安全的)
|
||||
*
|
||||
* @return SnowflakeId
|
||||
*/
|
||||
public synchronized long nextId() {
|
||||
long timestamp = timeGen();
|
||||
|
||||
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
|
||||
if (timestamp < lastTimestamp) {
|
||||
throw new RuntimeException(
|
||||
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
|
||||
}
|
||||
|
||||
//如果是同一时间生成的,则进行毫秒内序列
|
||||
/*
|
||||
* 序列在id中占的位数
|
||||
*/
|
||||
long sequenceBits = 12L;
|
||||
if (lastTimestamp == timestamp) {
|
||||
/*
|
||||
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
|
||||
*/
|
||||
long sequenceMask = ~(-1L << sequenceBits);
|
||||
sequence = (sequence + 1) & sequenceMask;
|
||||
//毫秒内序列溢出
|
||||
if (sequence == 0) {
|
||||
//阻塞到下一个毫秒,获得新的时间戳
|
||||
timestamp = tilNextMillis(lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
//时间戳改变,毫秒内序列重置
|
||||
sequence = 0L;
|
||||
}
|
||||
|
||||
//上次生成ID的时间截
|
||||
lastTimestamp = timestamp;
|
||||
|
||||
//移位并通过或运算拼到一起组成64位的ID
|
||||
/*
|
||||
* 时间截向左移22位(5+5+12)
|
||||
*/
|
||||
long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
|
||||
/*
|
||||
* 数据标识id向左移17位(12+5)
|
||||
*/
|
||||
long dataCenterIdShift = sequenceBits + workerIdBits;
|
||||
/*
|
||||
* 机器ID向左移12位
|
||||
*/
|
||||
/*
|
||||
* 开始时间截 (2015-01-01)
|
||||
*/
|
||||
long twepoch = 1489111610226L;
|
||||
return ((timestamp - twepoch) << timestampLeftShift)
|
||||
| (dataCenterId << dataCenterIdShift)
|
||||
| (workerId << sequenceBits)
|
||||
| sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞到下一个毫秒,直到获得新的时间戳
|
||||
*
|
||||
* @param lastTimestamp 上次生成ID的时间截
|
||||
* @return 当前时间戳
|
||||
*/
|
||||
protected long tilNextMillis(long lastTimestamp) {
|
||||
long timestamp = timeGen();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = timeGen();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回以毫秒为单位的当前时间
|
||||
*
|
||||
* @return 当前时间(毫秒)
|
||||
*/
|
||||
protected long timeGen() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static Long getWorkId() {
|
||||
try {
|
||||
String hostAddress = Inet4Address.getLocalHost().getHostAddress();
|
||||
int[] ints = StringUtils.toCodePoints(hostAddress);
|
||||
int sums = 0;
|
||||
for (int b : ints) {
|
||||
sums += b;
|
||||
}
|
||||
return (long) (sums % 32);
|
||||
} catch (UnknownHostException e) {
|
||||
// 如果获取失败,则使用随机数备用
|
||||
return RandomUtils.nextLong(0, 31);
|
||||
}
|
||||
}
|
||||
|
||||
private static Long getDataCenterId() {
|
||||
int[] ints = StringUtils.toCodePoints(SystemUtils.getHostName());
|
||||
int sums = 0;
|
||||
for (int i : ints) {
|
||||
sums += i;
|
||||
}
|
||||
return (long) (sums % 32);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 静态工具类
|
||||
* 生成18位id
|
||||
*/
|
||||
public static String getSnowflakeId() {
|
||||
return String.valueOf(idWorker.nextId());
|
||||
}
|
||||
|
||||
//==============================Test=============================================
|
||||
|
||||
/**
|
||||
* 测试
|
||||
*/
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
//判断生成的记录是否有重复记录
|
||||
long beginTime = System.currentTimeMillis();
|
||||
Map<String, String> map = new ConcurrentHashMap<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
new Thread(() -> {
|
||||
for (int s = 0; s < 20000; s++) {
|
||||
String snowFlakeId = SnowflakeIdWorker.getSnowflakeId();
|
||||
if (map.containsKey(snowFlakeId)) {
|
||||
log.error("主键重复:" + snowFlakeId);
|
||||
} else {
|
||||
map.put(snowFlakeId, snowFlakeId);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
Thread.sleep(3000);
|
||||
log.info("map.size():{}", map.size());
|
||||
log.info("耗时:" + (System.currentTimeMillis() - beginTime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
package com.jsowell.common.util.id;
|
||||
|
||||
import com.jsowell.common.exception.UtilException;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 提供通用唯一识别码(universally unique identifier)(UUID)实现
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public final class UUID implements java.io.Serializable, Comparable<UUID> {
|
||||
private static final long serialVersionUID = -1185015143654744140L;
|
||||
|
||||
/**
|
||||
* SecureRandom 的单例
|
||||
*/
|
||||
private static class Holder {
|
||||
static final SecureRandom numberGenerator = getSecureRandom();
|
||||
}
|
||||
|
||||
/**
|
||||
* 此UUID的最高64有效位
|
||||
*/
|
||||
private final long mostSigBits;
|
||||
|
||||
/**
|
||||
* 此UUID的最低64有效位
|
||||
*/
|
||||
private final long leastSigBits;
|
||||
|
||||
/**
|
||||
* 私有构造
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
private UUID(byte[] data) {
|
||||
long msb = 0;
|
||||
long lsb = 0;
|
||||
assert data.length == 16 : "data must be 16 bytes in length";
|
||||
for (int i = 0; i < 8; i++) {
|
||||
msb = (msb << 8) | (data[i] & 0xff);
|
||||
}
|
||||
for (int i = 8; i < 16; i++) {
|
||||
lsb = (lsb << 8) | (data[i] & 0xff);
|
||||
}
|
||||
this.mostSigBits = msb;
|
||||
this.leastSigBits = lsb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的数据构造新的 UUID。
|
||||
*
|
||||
* @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
|
||||
* @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
|
||||
*/
|
||||
public UUID(long mostSigBits, long leastSigBits) {
|
||||
this.mostSigBits = mostSigBits;
|
||||
this.leastSigBits = leastSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID fastUUID() {
|
||||
return randomUUID(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID randomUUID() {
|
||||
return randomUUID(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID randomUUID(boolean isSecure) {
|
||||
final Random ng = isSecure ? Holder.numberGenerator : getRandom();
|
||||
|
||||
byte[] randomBytes = new byte[16];
|
||||
ng.nextBytes(randomBytes);
|
||||
randomBytes[6] &= 0x0f; /* clear version */
|
||||
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new UUID(randomBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
|
||||
*
|
||||
* @param name 用于构造 UUID 的字节数组。
|
||||
* @return 根据指定数组生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID nameUUIDFromBytes(byte[] name) {
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
throw new InternalError("MD5 not supported");
|
||||
}
|
||||
byte[] md5Bytes = md.digest(name);
|
||||
md5Bytes[6] &= 0x0f; /* clear version */
|
||||
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new UUID(md5Bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
|
||||
*
|
||||
* @param name 指定 {@code UUID} 字符串
|
||||
* @return 具有指定值的 {@code UUID}
|
||||
* @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
|
||||
*/
|
||||
public static UUID fromString(String name) {
|
||||
String[] components = name.split("-");
|
||||
if (components.length != 5) {
|
||||
throw new IllegalArgumentException("Invalid UUID string: " + name);
|
||||
}
|
||||
for (int i = 0; i < 5; i++) {
|
||||
components[i] = "0x" + components[i];
|
||||
}
|
||||
|
||||
long mostSigBits = Long.decode(components[0]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[1]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[2]).longValue();
|
||||
|
||||
long leastSigBits = Long.decode(components[3]).longValue();
|
||||
leastSigBits <<= 48;
|
||||
leastSigBits |= Long.decode(components[4]).longValue();
|
||||
|
||||
return new UUID(mostSigBits, leastSigBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的 128 位值中的最低有效 64 位。
|
||||
*
|
||||
* @return 此 UUID 的 128 位值中的最低有效 64 位。
|
||||
*/
|
||||
public long getLeastSignificantBits() {
|
||||
return leastSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的 128 位值中的最高有效 64 位。
|
||||
*
|
||||
* @return 此 UUID 的 128 位值中最高有效 64 位。
|
||||
*/
|
||||
public long getMostSignificantBits() {
|
||||
return mostSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
|
||||
* <p>
|
||||
* 版本号具有以下含意:
|
||||
* <ul>
|
||||
* <li>1 基于时间的 UUID
|
||||
* <li>2 DCE 安全 UUID
|
||||
* <li>3 基于名称的 UUID
|
||||
* <li>4 随机生成的 UUID
|
||||
* </ul>
|
||||
*
|
||||
* @return 此 {@code UUID} 的版本号
|
||||
*/
|
||||
public int version() {
|
||||
// Version is bits masked by 0x000000000000F000 in MS long
|
||||
return (int) ((mostSigBits >> 12) & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
|
||||
* <p>
|
||||
* 变体号具有以下含意:
|
||||
* <ul>
|
||||
* <li>0 为 NCS 向后兼容保留
|
||||
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类
|
||||
* <li>6 保留,微软向后兼容
|
||||
* <li>7 保留供以后定义使用
|
||||
* </ul>
|
||||
*
|
||||
* @return 此 {@code UUID} 相关联的变体号
|
||||
*/
|
||||
public int variant() {
|
||||
// This field is composed of a varying number of bits.
|
||||
// 0 - - Reserved for NCS backward compatibility
|
||||
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||
// 1 1 1 Reserved for future definition.
|
||||
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关联的时间戳值。
|
||||
*
|
||||
* <p>
|
||||
* 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
|
||||
* 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
|
||||
*
|
||||
* <p>
|
||||
* 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||
* 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
|
||||
*/
|
||||
public long timestamp() throws UnsupportedOperationException {
|
||||
checkTimeBase();
|
||||
return (mostSigBits & 0x0FFFL) << 48//
|
||||
| ((mostSigBits >> 16) & 0x0FFFFL) << 32//
|
||||
| mostSigBits >>> 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关联的时钟序列值。
|
||||
*
|
||||
* <p>
|
||||
* 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
|
||||
* <p>
|
||||
* {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
|
||||
* UnsupportedOperationException。
|
||||
*
|
||||
* @return 此 {@code UUID} 的时钟序列
|
||||
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||
*/
|
||||
public int clockSequence() throws UnsupportedOperationException {
|
||||
checkTimeBase();
|
||||
return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关的节点值。
|
||||
*
|
||||
* <p>
|
||||
* 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
|
||||
* <p>
|
||||
* 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||
* 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||
*
|
||||
* @return 此 {@code UUID} 的节点值
|
||||
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||
*/
|
||||
public long node() throws UnsupportedOperationException {
|
||||
checkTimeBase();
|
||||
return leastSigBits & 0x0000FFFFFFFFFFFFL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此{@code UUID} 的字符串表现形式。
|
||||
*
|
||||
* <p>
|
||||
* UUID 的字符串表示形式由此 BNF 描述:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||
* time_low = 4*<hexOctet>
|
||||
* time_mid = 2*<hexOctet>
|
||||
* time_high_and_version = 2*<hexOctet>
|
||||
* variant_and_sequence = 2*<hexOctet>
|
||||
* node = 6*<hexOctet>
|
||||
* hexOctet = <hexDigit><hexDigit>
|
||||
* hexDigit = [0-9a-fA-F]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @return 此{@code UUID} 的字符串表现形式
|
||||
* @see #toString(boolean)
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此{@code UUID} 的字符串表现形式。
|
||||
*
|
||||
* <p>
|
||||
* UUID 的字符串表示形式由此 BNF 描述:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||
* time_low = 4*<hexOctet>
|
||||
* time_mid = 2*<hexOctet>
|
||||
* time_high_and_version = 2*<hexOctet>
|
||||
* variant_and_sequence = 2*<hexOctet>
|
||||
* node = 6*<hexOctet>
|
||||
* hexOctet = <hexDigit><hexDigit>
|
||||
* hexDigit = [0-9a-fA-F]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
|
||||
* @return 此{@code UUID} 的字符串表现形式
|
||||
*/
|
||||
public String toString(boolean isSimple) {
|
||||
final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
|
||||
// time_low
|
||||
builder.append(digits(mostSigBits >> 32, 8));
|
||||
if (!isSimple) {
|
||||
builder.append('-');
|
||||
}
|
||||
// time_mid
|
||||
builder.append(digits(mostSigBits >> 16, 4));
|
||||
if (!isSimple) {
|
||||
builder.append('-');
|
||||
}
|
||||
// time_high_and_version
|
||||
builder.append(digits(mostSigBits, 4));
|
||||
if (!isSimple) {
|
||||
builder.append('-');
|
||||
}
|
||||
// variant_and_sequence
|
||||
builder.append(digits(leastSigBits >> 48, 4));
|
||||
if (!isSimple) {
|
||||
builder.append('-');
|
||||
}
|
||||
// node
|
||||
builder.append(digits(leastSigBits, 12));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的哈希码。
|
||||
*
|
||||
* @return UUID 的哈希码值。
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
long hilo = mostSigBits ^ leastSigBits;
|
||||
return ((int) (hilo >> 32)) ^ (int) hilo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将此对象与指定对象比较。
|
||||
* <p>
|
||||
* 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
|
||||
*
|
||||
* @param obj 要与之比较的对象
|
||||
* @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ((null == obj) || (obj.getClass() != UUID.class)) {
|
||||
return false;
|
||||
}
|
||||
UUID id = (UUID) obj;
|
||||
return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
|
||||
}
|
||||
|
||||
// Comparison Operations
|
||||
|
||||
/**
|
||||
* 将此 UUID 与指定的 UUID 比较。
|
||||
*
|
||||
* <p>
|
||||
* 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
|
||||
*
|
||||
* @param val 与此 UUID 比较的 UUID
|
||||
* @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(UUID val) {
|
||||
// The ordering is intentionally set up so that the UUIDs
|
||||
// can simply be numerically compared as two numbers
|
||||
return (this.mostSigBits < val.mostSigBits ? -1 : //
|
||||
(this.mostSigBits > val.mostSigBits ? 1 : //
|
||||
(this.leastSigBits < val.leastSigBits ? -1 : //
|
||||
(this.leastSigBits > val.leastSigBits ? 1 : //
|
||||
0))));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
// Private method start
|
||||
|
||||
/**
|
||||
* 返回指定数字对应的hex值
|
||||
*
|
||||
* @param val 值
|
||||
* @param digits 位
|
||||
* @return 值
|
||||
*/
|
||||
private static String digits(long val, int digits) {
|
||||
long hi = 1L << (digits * 4);
|
||||
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为time-based版本UUID
|
||||
*/
|
||||
private void checkTimeBase() {
|
||||
if (version() != 1) {
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
|
||||
*
|
||||
* @return {@link SecureRandom}
|
||||
*/
|
||||
public static SecureRandom getSecureRandom() {
|
||||
try {
|
||||
return SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new UtilException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机数生成器对象<br>
|
||||
* ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
|
||||
*
|
||||
* @return {@link ThreadLocalRandom}
|
||||
*/
|
||||
public static ThreadLocalRandom getRandom() {
|
||||
return ThreadLocalRandom.current();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.jsowell.common.util.ip;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.jsowell.common.config.JsowellConfig;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.http.HttpUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 获取地址类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class AddressUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
|
||||
|
||||
// IP地址查询
|
||||
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
|
||||
|
||||
// 高德查询地址api
|
||||
public static final String AMAP_GET_LOCATION_URL = "https://restapi.amap.com/v3/geocode/geo";
|
||||
|
||||
// 高德应用key
|
||||
private static final String AMAP_GET_LOCATION_KEY = "51e155ceb3ab2d4f3b6bd81ebaab20b3";
|
||||
|
||||
// 未知地址
|
||||
public static final String UNKNOWN = "XX XX";
|
||||
|
||||
public static String getRealAddressByIP(String ip) {
|
||||
// 内网不查询
|
||||
if (IpUtils.internalIp(ip)) {
|
||||
return "内网IP";
|
||||
}
|
||||
if (JsowellConfig.isAddressEnabled()) {
|
||||
try {
|
||||
String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK);
|
||||
if (StringUtils.isEmpty(rspStr)) {
|
||||
log.error("获取地理位置异常 {}", ip);
|
||||
return UNKNOWN;
|
||||
}
|
||||
JSONObject obj = JSON.parseObject(rspStr);
|
||||
String region = obj.getString("pro");
|
||||
String city = obj.getString("city");
|
||||
return String.format("%s %s", region, city);
|
||||
} catch (Exception e) {
|
||||
log.error("获取地理位置异常 {}", ip);
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 地址转换经纬度
|
||||
* areaCode和address必传参数,任意一个为空则返回null
|
||||
* 高德api地址 https://lbs.amap.com/api/webservice/guide/api/georegeo
|
||||
*
|
||||
* @param areaCode 前端地址选择器获取到的区域编码 例如:"320000,320500,320583"
|
||||
* @param address 详细地址
|
||||
* @return 逗号分割的经纬度字符串
|
||||
*/
|
||||
public static Map<String, String> getLongitudeAndLatitude(String areaCode, String address) {
|
||||
if (StringUtils.isBlank(areaCode) || StringUtils.isBlank(address)) {
|
||||
return null;
|
||||
}
|
||||
String[] split = StringUtils.split(areaCode, ",");
|
||||
String param = "key=" + AMAP_GET_LOCATION_KEY + "&city=" + split[2] + "&address=" + address;
|
||||
String resultStr = HttpUtils.sendGet(AMAP_GET_LOCATION_URL, param, Constants.UTF8);
|
||||
if (StringUtils.isNotBlank(resultStr)) {
|
||||
JSONObject resultObj = JSON.parseObject(resultStr);
|
||||
// 状态为1 查询成功
|
||||
if (StringUtils.equals(resultObj.getString("status"), Constants.ONE)) {
|
||||
JSONArray geocodes = resultObj.getJSONArray("geocodes");
|
||||
if (geocodes != null && geocodes.size() > 0) {
|
||||
Map<String, String> map = Maps.newHashMap();
|
||||
JSONObject jsonObject = geocodes.getJSONObject(0);
|
||||
String location = jsonObject.getString("location");
|
||||
String[] strArr = StringUtils.split(location, ",");
|
||||
map.put("lng", strArr[0]);
|
||||
map.put("lat", strArr[1]);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package com.jsowell.common.util.ip;
|
||||
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* 获取IP方法
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class IpUtils {
|
||||
/**
|
||||
* 获取客户端IP
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return IP地址
|
||||
*/
|
||||
public static String getIpAddr(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return "unknown";
|
||||
}
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
|
||||
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为内部IP地址
|
||||
*
|
||||
* @param ip IP地址
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean internalIp(String ip) {
|
||||
byte[] addr = textToNumericFormatV4(ip);
|
||||
return internalIp(addr) || "127.0.0.1".equals(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为内部IP地址
|
||||
*
|
||||
* @param addr byte地址
|
||||
* @return 结果
|
||||
*/
|
||||
private static boolean internalIp(byte[] addr) {
|
||||
if (StringUtils.isNull(addr) || addr.length < 2) {
|
||||
return true;
|
||||
}
|
||||
final byte b0 = addr[0];
|
||||
final byte b1 = addr[1];
|
||||
// 10.x.x.x/8
|
||||
final byte SECTION_1 = 0x0A;
|
||||
// 172.16.x.x/12
|
||||
final byte SECTION_2 = (byte) 0xAC;
|
||||
final byte SECTION_3 = (byte) 0x10;
|
||||
final byte SECTION_4 = (byte) 0x1F;
|
||||
// 192.168.x.x/16
|
||||
final byte SECTION_5 = (byte) 0xC0;
|
||||
final byte SECTION_6 = (byte) 0xA8;
|
||||
switch (b0) {
|
||||
case SECTION_1:
|
||||
return true;
|
||||
case SECTION_2:
|
||||
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
|
||||
return true;
|
||||
}
|
||||
case SECTION_5:
|
||||
switch (b1) {
|
||||
case SECTION_6:
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将IPv4地址转换成字节
|
||||
*
|
||||
* @param text IPv4地址
|
||||
* @return byte 字节
|
||||
*/
|
||||
public static byte[] textToNumericFormatV4(String text) {
|
||||
if (text.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[4];
|
||||
String[] elements = text.split("\\.", -1);
|
||||
try {
|
||||
long l;
|
||||
int i;
|
||||
switch (elements.length) {
|
||||
case 1:
|
||||
l = Long.parseLong(elements[0]);
|
||||
if ((l < 0L) || (l > 4294967295L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
|
||||
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 2:
|
||||
l = Integer.parseInt(elements[0]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l & 0xFF);
|
||||
l = Integer.parseInt(elements[1]);
|
||||
if ((l < 0L) || (l > 16777215L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 3:
|
||||
for (i = 0; i < 2; ++i) {
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
l = Integer.parseInt(elements[2]);
|
||||
if ((l < 0L) || (l > 65535L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 4:
|
||||
for (i = 0; i < 4; ++i) {
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取IP地址
|
||||
*
|
||||
* @return 本地IP地址
|
||||
*/
|
||||
public static String getHostIp() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
}
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机名
|
||||
*
|
||||
* @return 本地主机名
|
||||
*/
|
||||
public static String getHostName() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从多级反向代理中获得第一个非unknown IP地址
|
||||
*
|
||||
* @param ip 获得的IP地址
|
||||
* @return 第一个非unknown IP地址
|
||||
*/
|
||||
public static String getMultistageReverseProxyIp(String ip) {
|
||||
// 多级反向代理检测
|
||||
if (ip != null && ip.indexOf(",") > 0) {
|
||||
final String[] ips = ip.trim().split(",");
|
||||
for (String subIp : ips) {
|
||||
if (false == isUnknown(subIp)) {
|
||||
ip = subIp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测给定字符串是否为未知,多用于检测HTTP请求相关
|
||||
*
|
||||
* @param checkString 被检测的字符串
|
||||
* @return 是否未知
|
||||
*/
|
||||
public static boolean isUnknown(String checkString) {
|
||||
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.jsowell.common.util.poi;
|
||||
|
||||
/**
|
||||
* Excel数据格式处理适配器
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public interface ExcelHandlerAdapter {
|
||||
/**
|
||||
* 格式化
|
||||
*
|
||||
* @param value 单元格数据值
|
||||
* @param args excel注解args参数组
|
||||
* @return 处理后的值
|
||||
*/
|
||||
Object format(Object value, String[] args);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,329 @@
|
||||
package com.jsowell.common.util.reflect;
|
||||
|
||||
import com.jsowell.common.core.text.Convert;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ReflectUtils {
|
||||
private static final String SETTER_PREFIX = "set";
|
||||
|
||||
private static final String GETTER_PREFIX = "get";
|
||||
|
||||
private static final String CGLIB_CLASS_SEPARATOR = "$$";
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
|
||||
|
||||
/**
|
||||
* 调用Getter方法.
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeGetter(Object obj, String propertyName) {
|
||||
Object object = obj;
|
||||
for (String name : StringUtils.split(propertyName, ".")) {
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
|
||||
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
|
||||
}
|
||||
return (E) object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用Setter方法, 仅匹配方法名。
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
public static <E> void invokeSetter(Object obj, String propertyName, E value) {
|
||||
Object object = obj;
|
||||
String[] names = StringUtils.split(propertyName, ".");
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
if (i < names.length - 1) {
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
|
||||
} else {
|
||||
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
invokeMethodByName(object, setterMethodName, new Object[]{value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E getFieldValue(final Object obj, final String fieldName) {
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null) {
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return null;
|
||||
}
|
||||
E result = null;
|
||||
try {
|
||||
result = (E) field.get(obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.error("不可能抛出的异常{}", e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
|
||||
*/
|
||||
public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) {
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null) {
|
||||
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
field.set(obj, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.error("不可能抛出的异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符.
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
|
||||
* 同时匹配方法名+参数类型,
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
|
||||
final Object[] args) {
|
||||
if (obj == null || methodName == null) {
|
||||
return null;
|
||||
}
|
||||
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
|
||||
if (method == null) {
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (E) method.invoke(obj, args);
|
||||
} catch (Exception e) {
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符,
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
|
||||
* 只匹配函数名,如果有多个同名函数调用第一个。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
|
||||
Method method = getAccessibleMethodByName(obj, methodName, args.length);
|
||||
if (method == null) {
|
||||
// 如果为空不报错,直接返回空。
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 类型转换(将参数数据类型转换为目标方法参数类型)
|
||||
Class<?>[] cs = method.getParameterTypes();
|
||||
for (int i = 0; i < cs.length; i++) {
|
||||
if (args[i] != null && !args[i].getClass().equals(cs[i])) {
|
||||
if (cs[i] == String.class) {
|
||||
args[i] = Convert.toStr(args[i]);
|
||||
if (StringUtils.endsWith((String) args[i], ".0")) {
|
||||
args[i] = StringUtils.substringBefore((String) args[i], ".0");
|
||||
}
|
||||
} else if (cs[i] == Integer.class) {
|
||||
args[i] = Convert.toInt(args[i]);
|
||||
} else if (cs[i] == Long.class) {
|
||||
args[i] = Convert.toLong(args[i]);
|
||||
} else if (cs[i] == Double.class) {
|
||||
args[i] = Convert.toDouble(args[i]);
|
||||
} else if (cs[i] == Float.class) {
|
||||
args[i] = Convert.toFloat(args[i]);
|
||||
} else if (cs[i] == Date.class) {
|
||||
if (args[i] instanceof String) {
|
||||
args[i] = DateUtils.parseDate(args[i]);
|
||||
} else {
|
||||
args[i] = DateUtil.getJavaDate((Double) args[i]);
|
||||
}
|
||||
} else if (cs[i] == boolean.class || cs[i] == Boolean.class) {
|
||||
args[i] = Convert.toBool(args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (E) method.invoke(obj, args);
|
||||
} catch (Exception e) {
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
*/
|
||||
public static Field getAccessibleField(final Object obj, final String fieldName) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(fieldName, "fieldName can't be blank");
|
||||
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
|
||||
try {
|
||||
Field field = superClass.getDeclaredField(fieldName);
|
||||
makeAccessible(field);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 匹配函数名+参数类型。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethod(final Object obj, final String methodName,
|
||||
final Class<?>... parameterTypes) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
|
||||
try {
|
||||
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
} catch (NoSuchMethodException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 只匹配函数名。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
|
||||
Method[] methods = searchType.getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) {
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible(Method method) {
|
||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible()) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible(Field field) {
|
||||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|
||||
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
|
||||
* 如无法找到, 返回Object.class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> getClassGenricType(final Class clazz) {
|
||||
return getClassGenricType(clazz, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
|
||||
* 如无法找到, 返回Object.class.
|
||||
*/
|
||||
public static Class getClassGenricType(final Class clazz, final int index) {
|
||||
Type genType = clazz.getGenericSuperclass();
|
||||
|
||||
if (!(genType instanceof ParameterizedType)) {
|
||||
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
|
||||
|
||||
if (index >= params.length || index < 0) {
|
||||
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
|
||||
+ params.length);
|
||||
return Object.class;
|
||||
}
|
||||
if (!(params[index] instanceof Class)) {
|
||||
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
return (Class) params[index];
|
||||
}
|
||||
|
||||
public static Class<?> getUserClass(Object instance) {
|
||||
if (instance == null) {
|
||||
throw new RuntimeException("Instance must not be null");
|
||||
}
|
||||
Class clazz = instance.getClass();
|
||||
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass != null && !Object.class.equals(superClass)) {
|
||||
return superClass;
|
||||
}
|
||||
}
|
||||
return clazz;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将反射时的checked exception转换为unchecked exception.
|
||||
*/
|
||||
public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) {
|
||||
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|
||||
|| e instanceof NoSuchMethodException) {
|
||||
return new IllegalArgumentException(msg, e);
|
||||
} else if (e instanceof InvocationTargetException) {
|
||||
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
|
||||
}
|
||||
return new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package com.jsowell.common.util.sign;
|
||||
|
||||
/**
|
||||
* Base64工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public final class Base64 {
|
||||
static private final int BASELENGTH = 128;
|
||||
static private final int LOOKUPLENGTH = 64;
|
||||
static private final int TWENTYFOURBITGROUP = 24;
|
||||
static private final int EIGHTBIT = 8;
|
||||
static private final int SIXTEENBIT = 16;
|
||||
static private final int FOURBYTE = 4;
|
||||
static private final int SIGN = -128;
|
||||
static private final char PAD = '=';
|
||||
static final private byte[] base64Alphabet = new byte[BASELENGTH];
|
||||
static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < BASELENGTH; ++i) {
|
||||
base64Alphabet[i] = -1;
|
||||
}
|
||||
for (int i = 'Z'; i >= 'A'; i--) {
|
||||
base64Alphabet[i] = (byte) (i - 'A');
|
||||
}
|
||||
for (int i = 'z'; i >= 'a'; i--) {
|
||||
base64Alphabet[i] = (byte) (i - 'a' + 26);
|
||||
}
|
||||
|
||||
for (int i = '9'; i >= '0'; i--) {
|
||||
base64Alphabet[i] = (byte) (i - '0' + 52);
|
||||
}
|
||||
|
||||
base64Alphabet['+'] = 62;
|
||||
base64Alphabet['/'] = 63;
|
||||
|
||||
for (int i = 0; i <= 25; i++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('A' + i);
|
||||
}
|
||||
|
||||
for (int i = 26, j = 0; i <= 51; i++, j++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('a' + j);
|
||||
}
|
||||
|
||||
for (int i = 52, j = 0; i <= 61; i++, j++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('0' + j);
|
||||
}
|
||||
lookUpBase64Alphabet[62] = (char) '+';
|
||||
lookUpBase64Alphabet[63] = (char) '/';
|
||||
}
|
||||
|
||||
private static boolean isWhiteSpace(char octect) {
|
||||
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
|
||||
}
|
||||
|
||||
private static boolean isPad(char octect) {
|
||||
return (octect == PAD);
|
||||
}
|
||||
|
||||
private static boolean isData(char octect) {
|
||||
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes hex octects into Base64
|
||||
*
|
||||
* @param binaryData Array containing binaryData
|
||||
* @return Encoded Base64 array
|
||||
*/
|
||||
public static String encode(byte[] binaryData) {
|
||||
if (binaryData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int lengthDataBits = binaryData.length * EIGHTBIT;
|
||||
if (lengthDataBits == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
|
||||
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
|
||||
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
|
||||
char encodedData[] = null;
|
||||
|
||||
encodedData = new char[numberQuartet * 4];
|
||||
|
||||
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
|
||||
|
||||
int encodedIndex = 0;
|
||||
int dataIndex = 0;
|
||||
|
||||
for (int i = 0; i < numberTriplets; i++) {
|
||||
b1 = binaryData[dataIndex++];
|
||||
b2 = binaryData[dataIndex++];
|
||||
b3 = binaryData[dataIndex++];
|
||||
|
||||
l = (byte) (b2 & 0x0f);
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
|
||||
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
|
||||
}
|
||||
|
||||
// form integral number of 6-bit groups
|
||||
if (fewerThan24bits == EIGHTBIT) {
|
||||
b1 = binaryData[dataIndex];
|
||||
k = (byte) (b1 & 0x03);
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
} else if (fewerThan24bits == SIXTEENBIT) {
|
||||
b1 = binaryData[dataIndex];
|
||||
b2 = binaryData[dataIndex + 1];
|
||||
l = (byte) (b2 & 0x0f);
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
}
|
||||
return new String(encodedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Base64 data into octects
|
||||
*
|
||||
* @param encoded string containing Base64 data
|
||||
* @return Array containind decoded data.
|
||||
*/
|
||||
public static byte[] decode(String encoded) {
|
||||
if (encoded == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
char[] base64Data = encoded.toCharArray();
|
||||
// remove white spaces
|
||||
int len = removeWhiteSpace(base64Data);
|
||||
|
||||
if (len % FOURBYTE != 0) {
|
||||
return null;// should be divisible by four
|
||||
}
|
||||
|
||||
int numberQuadruple = (len / FOURBYTE);
|
||||
|
||||
if (numberQuadruple == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
byte decodedData[] = null;
|
||||
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
|
||||
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
|
||||
|
||||
int i = 0;
|
||||
int encodedIndex = 0;
|
||||
int dataIndex = 0;
|
||||
decodedData = new byte[(numberQuadruple) * 3];
|
||||
|
||||
for (; i < numberQuadruple - 1; i++) {
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
|
||||
|| !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) {
|
||||
return null;
|
||||
} // if found "no data" just return null
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||
}
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
|
||||
return null;// if found "no data" just return null
|
||||
}
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
|
||||
d3 = base64Data[dataIndex++];
|
||||
d4 = base64Data[dataIndex++];
|
||||
if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
|
||||
if (isPad(d3) && isPad(d4)) {
|
||||
if ((b2 & 0xf) != 0)// last 4 bits should be zero
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] tmp = new byte[i * 3 + 1];
|
||||
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
|
||||
return tmp;
|
||||
} else if (!isPad(d3) && isPad(d4)) {
|
||||
b3 = base64Alphabet[d3];
|
||||
if ((b3 & 0x3) != 0)// last 2 bits should be zero
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] tmp = new byte[i * 3 + 2];
|
||||
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
return tmp;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else { // No PAD e.g 3cQl
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||
|
||||
}
|
||||
return decodedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove WhiteSpace from MIME containing encoded Base64 data.
|
||||
*
|
||||
* @param data the byte array of base64 data (with WS)
|
||||
* @return the new length
|
||||
*/
|
||||
private static int removeWhiteSpace(char[] data) {
|
||||
if (data == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// count characters that's not whitespace
|
||||
int newSize = 0;
|
||||
int len = data.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!isWhiteSpace(data[i])) {
|
||||
data[newSize++] = data[i];
|
||||
}
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.jsowell.common.util.sign;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class MD5Util {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MD5Util.class);
|
||||
|
||||
private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
|
||||
|
||||
private static String byteArrayToHexString(byte b[]) {
|
||||
StringBuffer resultSb = new StringBuffer();
|
||||
for (int i = 0; i < b.length; i++)
|
||||
resultSb.append(byteToHexString(b[i]));
|
||||
|
||||
return resultSb.toString();
|
||||
}
|
||||
|
||||
private static String byteToHexString(byte b) {
|
||||
int n = b;
|
||||
if (n < 0)
|
||||
n += 256;
|
||||
int d1 = n / 16;
|
||||
int d2 = n % 16;
|
||||
return hexDigits[d1] + hexDigits[d2];
|
||||
}
|
||||
|
||||
public static String MD5Encode(String origin, String charsetname) {
|
||||
String resultString = null;
|
||||
try {
|
||||
resultString = new String(origin);
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
if (charsetname == null || "".equals(charsetname))
|
||||
resultString = byteArrayToHexString(md.digest(resultString
|
||||
.getBytes()));
|
||||
else
|
||||
resultString = byteArrayToHexString(md.digest(resultString
|
||||
.getBytes(charsetname)));
|
||||
} catch (Exception exception) {
|
||||
}
|
||||
return resultString;
|
||||
}
|
||||
|
||||
/**
|
||||
* MD5编码
|
||||
*
|
||||
* @param origin 原始字符串
|
||||
* @return 经过MD5加密之后的结果
|
||||
*/
|
||||
public static String MD5Encode(String origin) {
|
||||
String resultString = null;
|
||||
try {
|
||||
resultString = origin;
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
md.update(resultString.getBytes("UTF-8"));
|
||||
resultString = byteArrayToHexString(md.digest());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return resultString;
|
||||
}
|
||||
|
||||
private static byte[] md5(String s) {
|
||||
MessageDigest algorithm;
|
||||
try {
|
||||
algorithm = MessageDigest.getInstance("MD5");
|
||||
algorithm.reset();
|
||||
algorithm.update(s.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] messageDigest = algorithm.digest();
|
||||
return messageDigest;
|
||||
} catch (Exception e) {
|
||||
log.error("MD5 Error...", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String toHex(byte[] hash) {
|
||||
if (hash == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder buf = new StringBuilder(hash.length * 2);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hash.length; i++) {
|
||||
if ((hash[i] & 0xff) < 0x10) {
|
||||
buf.append("0");
|
||||
}
|
||||
buf.append(Long.toString(hash[i] & 0xff, 16));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String hash(String s) {
|
||||
try {
|
||||
return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
log.error("not supported charset...", e);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.jsowell.common.util.sim;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
* sim卡相关工具类
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/12/7 10:25
|
||||
*/
|
||||
public class SimCardUtils {
|
||||
|
||||
/**
|
||||
* 将KB转为对应的MB或GB
|
||||
*
|
||||
* @param size 需进行转化的值(KB)
|
||||
* @return 转换后的值,自带单位
|
||||
*/
|
||||
public static String kb2MbOrGb(int size) {
|
||||
int GB = 1024 * 1024; // 定义GB的计算常量
|
||||
int MB = 1024; // 定义MB的计算常量
|
||||
DecimalFormat df = new DecimalFormat("0.00");//格式化小数
|
||||
String resultSize = "";
|
||||
if (size / GB >= 1) {
|
||||
//如果当前Byte的值大于等于1GB
|
||||
resultSize = df.format(size / (float) GB);
|
||||
} else if (size / MB >= 1) {
|
||||
//如果当前Byte的值大于等于1MB
|
||||
resultSize = df.format(size / (float) MB);
|
||||
} else {
|
||||
resultSize = String.valueOf(size);
|
||||
}
|
||||
return resultSize;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int a = 13320;
|
||||
String str = "11874.95";
|
||||
BigDecimal bigDecimal = new BigDecimal(str);
|
||||
System.out.println(kb2MbOrGb(bigDecimal.intValue()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.jsowell.common.util.sim;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 讯众物联Sim卡商工具类
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/12/6 15:47
|
||||
*/
|
||||
@Component
|
||||
public class XunZhongSimUtils {
|
||||
|
||||
private static String API_SECRET;
|
||||
|
||||
@Value("${xunzhong.apiSecret}")
|
||||
public void setAPI_SECRET(String API_SECRET) {
|
||||
XunZhongSimUtils.API_SECRET = API_SECRET;
|
||||
}
|
||||
|
||||
// Base 64 加密
|
||||
private static String encode(final byte[] bytes) {
|
||||
return new String(Base64.encodeBase64(bytes));
|
||||
}
|
||||
|
||||
// SHA 256 加密
|
||||
private static String SHA256(final String strText) {
|
||||
return SHA(strText, "SHA-256");
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA算法加密
|
||||
* @param strText
|
||||
* @param strType
|
||||
* @return
|
||||
*/
|
||||
private static String SHA(final String strText, final String strType) {
|
||||
// 返回值
|
||||
String strResult = null;
|
||||
// 是否是有效字符串
|
||||
if (strText != null && strText.length() > 0) {
|
||||
try {
|
||||
// SHA 加密开始
|
||||
MessageDigest messageDigest = MessageDigest.getInstance(strType);
|
||||
// 传⼊要加密的字符串
|
||||
messageDigest.update(strText.getBytes());
|
||||
// 得到 byte 结果
|
||||
byte[] byteBuffer = messageDigest.digest();
|
||||
// 將 byte 转换 string
|
||||
StringBuilder strHexString = new StringBuilder();
|
||||
// 遍历 byte buffer
|
||||
for (byte b : byteBuffer) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
strHexString.append('0');
|
||||
}
|
||||
strHexString.append(hex);
|
||||
}
|
||||
strResult = strHexString.toString();
|
||||
}
|
||||
// 得到返回結果
|
||||
catch(NoSuchAlgorithmException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return strResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成signStr
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
public static String getSignStr(Hashtable<String, Object> params) {
|
||||
// 参数按 key 排序
|
||||
List<String> keys = new ArrayList<>(params.keySet());
|
||||
Collections.sort(keys);
|
||||
|
||||
StringBuilder sign = new StringBuilder();
|
||||
|
||||
for (String key : keys) {
|
||||
Object obj = params.get(key);
|
||||
if (!sign.toString().equals("")) {
|
||||
sign.append("&");
|
||||
}
|
||||
sign.append(key).append("=").append(obj);
|
||||
}
|
||||
// ⽣成 sign
|
||||
sign.append(API_SECRET);
|
||||
// 添加加密的 sign
|
||||
String signStr = SHA256(encode(sign.toString().getBytes()));
|
||||
|
||||
return signStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 链式构建请求,引入hutool
|
||||
* @param url
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
public static String sendPost(String url, Hashtable<String, Object> params) {
|
||||
String postResult = HttpRequest.post(url)
|
||||
.header("Content-type", "application/x-www-form-urlencoded;charset=utf-8")
|
||||
.form(params)
|
||||
.timeout(20000)//超时,毫秒
|
||||
.execute().body();
|
||||
|
||||
return postResult;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.jsowell.common.util.spring;
|
||||
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
|
||||
/**
|
||||
* spring工具类 方便在非spring管理环境中获取bean
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
@Component
|
||||
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
|
||||
/**
|
||||
* Spring应用上下文环境
|
||||
*/
|
||||
private static ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
SpringUtils.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
SpringUtils.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param name
|
||||
* @return Object 一个以所给名字注册的bean的实例
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String name) throws BeansException {
|
||||
return (T) beanFactory.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型为requiredType的对象
|
||||
*
|
||||
* @param clz
|
||||
* @return
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clz) throws BeansException {
|
||||
T result = (T) beanFactory.getBean(clz);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean containsBean(String name) {
|
||||
return beanFactory.containsBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*/
|
||||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
|
||||
return beanFactory.isSingleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return Class 注册对象的类型
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*/
|
||||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
|
||||
return beanFactory.getType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*/
|
||||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
|
||||
return beanFactory.getAliases(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取aop代理对象
|
||||
*
|
||||
* @param invoker
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy(T invoker) {
|
||||
return (T) AopContext.currentProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的环境配置,无配置返回null
|
||||
*
|
||||
* @return 当前的环境配置
|
||||
*/
|
||||
public static String[] getActiveProfiles() {
|
||||
return applicationContext.getEnvironment().getActiveProfiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
|
||||
*
|
||||
* @return 当前的环境配置
|
||||
*/
|
||||
public static String getActiveProfile() {
|
||||
final String[] activeProfiles = getActiveProfiles();
|
||||
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置文件中的值
|
||||
*
|
||||
* @param key 配置文件的key
|
||||
* @return 当前的配置文件的值
|
||||
*/
|
||||
public static String getRequiredProperty(String key) {
|
||||
return applicationContext.getEnvironment().getRequiredProperty(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.jsowell.common.util.sql;
|
||||
|
||||
import com.jsowell.common.exception.UtilException;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
|
||||
/**
|
||||
* sql操作工具类
|
||||
*
|
||||
* @author jsowell
|
||||
*/
|
||||
public class SqlUtil {
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
|
||||
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
*/
|
||||
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
|
||||
|
||||
/**
|
||||
* 检查字符,防止注入绕过
|
||||
*/
|
||||
public static String escapeOrderBySql(String value) {
|
||||
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
|
||||
throw new UtilException("参数不符合规范,不能进行查询");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 order by 语法是否符合规范
|
||||
*/
|
||||
public static boolean isValidOrderBySql(String value) {
|
||||
return value.matches(SQL_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL关键字检查
|
||||
*/
|
||||
public static void filterKeyword(String value) {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||
for (String sqlKeyword : sqlKeywords) {
|
||||
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
|
||||
throw new UtilException("参数存在SQL注入风险");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user