feat:V3.1.1整合remi-comm到remi-cloud中
This commit is contained in:
@@ -0,0 +1,705 @@
|
||||
package ai.remi.comm.util.array;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
|
||||
public class ArrayUtils {
|
||||
/**
|
||||
* 判断数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static boolean isNotEmpty(Object[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数组是否非空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 返回布尔值
|
||||
*/
|
||||
public static boolean isEmpty(Object[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组为null, 或长度为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(boolean[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组为null, 或长度为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(byte[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组为null, 或长度为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(char[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组为null, 或长度为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(double[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组为null, 或长度为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(float[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组为null, 或长度为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(int[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组为null, 或长度为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(long[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组为null, 或长度为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(short[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为不空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组不为null, 而且长度不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(boolean[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为不空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组不为null, 而且长度不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(byte[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为不空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组不为null, 而且长度不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(char[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为不空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组不为null, 而且长度不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(double[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为不空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组不为null, 而且长度不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(float[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为不空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组不为null, 而且长度不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(int[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为不空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组不为null, 而且长度不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(long[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组是否为不空
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 若数组不为null, 而且长度不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(short[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断参数是否是一个数组
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static boolean isArray(Object obj) {
|
||||
return obj != null && obj.getClass().isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将参数组装成数组
|
||||
*
|
||||
* @param obj 参数对象
|
||||
* @return 返回对象数组
|
||||
*/
|
||||
public static <E> E[] asArray(E... obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将参数集合转换为数组表示
|
||||
*
|
||||
* @param collection 集合参数
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static <E> E[] asArray(Collection<E> collection) {
|
||||
if (collection == null || collection.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
int length = collection.size();
|
||||
E e = collection.iterator().next();
|
||||
@SuppressWarnings("unchecked")
|
||||
E[] array = (E[]) Array.newInstance(e.getClass(), length);
|
||||
return collection.toArray(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数组元素的实际数据类型
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static <E> Class<E> getElementType(E[] array) {
|
||||
Class<?> clas = array.getClass();
|
||||
Class<E> type = (Class<E>) clas.getComponentType();
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数组元素的实际数据类型
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static Class<?> getElementType(Object array) {
|
||||
if (!isArray(array)) {
|
||||
throw new IllegalArgumentException("argument must be an array");
|
||||
}
|
||||
return array.getClass().getComponentType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static <E> List<E> toList(E[] array) {
|
||||
return Arrays.asList(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static List<Boolean> toList(boolean[] array) {
|
||||
int length = Array.getLength(array);
|
||||
return asList(new Boolean[length], array, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static List<Byte> toList(byte[] array) {
|
||||
int length = Array.getLength(array);
|
||||
return asList(new Byte[length], array, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static List<Character> toList(char[] array) {
|
||||
int length = Array.getLength(array);
|
||||
return asList(new Character[length], array, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static List<Double> toList(double[] array) {
|
||||
int length = Array.getLength(array);
|
||||
return asList(new Double[length], array, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static List<Float> toList(float[] array) {
|
||||
int length = Array.getLength(array);
|
||||
return asList(new Float[length], array, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static List<Integer> toList(int[] array) {
|
||||
int length = Array.getLength(array);
|
||||
return asList(new Integer[length], array, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static List<Long> toList(long[] array) {
|
||||
int length = Array.getLength(array);
|
||||
return asList(new Long[length], array, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param array 数组
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static List<Short> toList(short[] array) {
|
||||
int length = Array.getLength(array);
|
||||
return asList(new Short[length], array, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return
|
||||
*/
|
||||
public static <E> boolean contains(E[] array, E arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static boolean contains(byte[] array, byte arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static boolean contains(char[] array, char arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static boolean contains(short[] array, short arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return 返回布尔值
|
||||
*/
|
||||
public static boolean contains(int[] array, int arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return 返回布尔值
|
||||
*/
|
||||
public static boolean contains(long[] array, long arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return 返回布尔值
|
||||
*/
|
||||
public static boolean contains(float[] array, float arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return 返回布尔值
|
||||
*/
|
||||
public static boolean contains(double[] array, double arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组元素中是否包含参数对象
|
||||
*
|
||||
* @param array 数组
|
||||
* @param arg 元素参数
|
||||
* @return 返回布尔值
|
||||
*/
|
||||
public static boolean contains(boolean[] array, boolean arg) {
|
||||
return indexOf(array, arg) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static <E> int indexOf(E[] array, E element) {
|
||||
int size = array.length;
|
||||
if (element == null) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (array[i] == null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (element.equals(array[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static int indexOf(boolean[] array, boolean element) {
|
||||
return indexFrom(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static int indexOf(byte[] array, byte element) {
|
||||
return indexFrom(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static int indexOf(char[] array, char element) {
|
||||
return indexFrom(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static int indexOf(double[] array, double element) {
|
||||
return indexFrom(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static int indexOf(float[] array, float element) {
|
||||
return indexFrom(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static int indexOf(int[] array, int element) {
|
||||
return indexFrom(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static int indexOf(long[] array, long element) {
|
||||
return indexFrom(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
public static int indexOf(short[] array, short element) {
|
||||
return indexFrom(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列举
|
||||
*
|
||||
* @param array 数组
|
||||
* @return
|
||||
*/
|
||||
public static <E> Enumeration<E> enumeration(E[] array) {
|
||||
return new ArrayEnumeration<E>(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素所在数组的索引
|
||||
*
|
||||
* @param array 数组
|
||||
* @param element 元素
|
||||
* @return 若数组中不存在该元素, 则返回-1
|
||||
*/
|
||||
private static int indexFrom(Object array, Object element) {
|
||||
int length = Array.getLength(array);
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object e = Array.get(array, i);
|
||||
if (element.equals(e)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转化为List
|
||||
*
|
||||
* @param target 目标数组
|
||||
* @param origin 源数组对象
|
||||
* @param length 数组长度
|
||||
* @return
|
||||
*/
|
||||
private static <E> List<E> asList(E[] target, Object origin, int length) {
|
||||
// java.lang.ArrayStoreException
|
||||
// System.arraycopy(origin, 0, target, 0, length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
target[i] = (E) Array.get(origin, i);
|
||||
}
|
||||
return Arrays.asList(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取int的list中的最大值
|
||||
*
|
||||
* @param list
|
||||
* @return 返回int最大值
|
||||
*/
|
||||
private static int getListMaxValue(List<Integer> list) {
|
||||
return Collections.max(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取int的list中的最大值的下标
|
||||
*
|
||||
* @param list
|
||||
* @return 返回int最大值
|
||||
*/
|
||||
private static int getListMaxValueIndex(List<Integer> list) {
|
||||
int max = list.get(0);
|
||||
// 最大值下标
|
||||
int max_a = 0;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (list.get(i) > list.get(max_a)) {
|
||||
max = list.get(i);
|
||||
max_a = i;
|
||||
}
|
||||
}
|
||||
return max_a;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取int的list中的最小值
|
||||
*
|
||||
* @param list
|
||||
* @return 返回int最小值
|
||||
*/
|
||||
private static int getListMinValue(List<Integer> list) {
|
||||
return Collections.min(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取int的list中的最大值的下标
|
||||
*
|
||||
* @param list list
|
||||
* @return 返回int最下值的下标
|
||||
*/
|
||||
private static int getListMinValueIndex(List<Integer> list) {
|
||||
int min = list.get(0);
|
||||
// 最大值下标
|
||||
int min_a = 0;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (list.get(i) < list.get(min_a)) {
|
||||
min_a = i;
|
||||
}
|
||||
}
|
||||
return min_a;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组列举
|
||||
*/
|
||||
private static class ArrayEnumeration<E> implements Enumeration<E> {
|
||||
|
||||
private E[] es;
|
||||
private int index;
|
||||
private int length;
|
||||
|
||||
private ArrayEnumeration(E[] es) {
|
||||
this.es = es;
|
||||
this.length = es.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return index < length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E nextElement() {
|
||||
return es[index++];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 链表中随机选择一定数量的元素
|
||||
*
|
||||
* @param list 对象链表
|
||||
* @param num 选择链表长度
|
||||
* @return 字符串链表
|
||||
*/
|
||||
public static <T> List<T> getRandomList(List<T> list, int num) {
|
||||
if (CollectionUtil.isEmpty(list)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
if (list.size() <= num) {
|
||||
return list;
|
||||
}
|
||||
Collections.shuffle(list);
|
||||
return list.subList(0, num);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package ai.remi.comm.util.array;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SortUtils {
|
||||
|
||||
/**
|
||||
* 如果找到了直接进行返回,如果没有找到就继续去找
|
||||
*
|
||||
* @param arr 数组
|
||||
* @param key 关键字
|
||||
* @return 位置
|
||||
*/
|
||||
public static int binarySearch(int[] arr, int key) {
|
||||
int i = 0;
|
||||
int j = arr.length - 1;
|
||||
while (i < j) {
|
||||
int middle = i + (j - i) / 2; //防止middle的和超过Integer.MAX_VALUE的值
|
||||
if (key > arr[middle]) {
|
||||
i = middle + 1;
|
||||
} else if (key < arr[middle]) {
|
||||
j = middle - 1;
|
||||
} else {
|
||||
return middle;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择排序
|
||||
*
|
||||
* @param arr 数组
|
||||
* @return int[] 数组
|
||||
*/
|
||||
public static int[] selectSort(int[] arr) {
|
||||
if(arr == null || arr.length < 2) {
|
||||
return arr;
|
||||
}
|
||||
int len = arr.length;
|
||||
for (int i = 0; i < len - 1; i++) {
|
||||
for (int j = i + 1; j < len; j++) {
|
||||
if (arr[i] > arr[j]) {
|
||||
swap(arr, i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速排序
|
||||
*
|
||||
* @param arr 数组
|
||||
*/
|
||||
public static void quickSort(int[] arr) {
|
||||
int low = 0;
|
||||
int high = arr.length - 1;
|
||||
quickSortHelper(arr, low, high);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速排序,递归算法
|
||||
*
|
||||
* @param arr 数组
|
||||
* @param low 低位
|
||||
* @param high 高位
|
||||
*/
|
||||
public static void quickSortHelper(int[] arr, int low, int high) {
|
||||
int start = low;
|
||||
int end = high;
|
||||
int key = arr[start];
|
||||
while (start < end) {
|
||||
// 要从尾到前面进行比较
|
||||
while (start < end && arr[end] >= key) {
|
||||
end--;
|
||||
}
|
||||
if (arr[end] <= key) {
|
||||
swap(arr, start, end);
|
||||
}
|
||||
// 要从前到后进行比较
|
||||
while (start < end && arr[start] <= key) {
|
||||
start++;
|
||||
}
|
||||
if (arr[start] >= key) {
|
||||
swap(arr, start, end);
|
||||
}
|
||||
}
|
||||
if (start > low) {
|
||||
quickSortHelper(arr, low, start - 1);
|
||||
}
|
||||
if (end < high) {
|
||||
quickSortHelper(arr, end + 1, high);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 快速排序,递归算法
|
||||
*
|
||||
* @param arr 输入数组
|
||||
* @return 新数组
|
||||
*/
|
||||
public static int[] quickSort2(int[] arr) {
|
||||
if (arr == null || arr.length < 2) {
|
||||
return arr;
|
||||
}
|
||||
int pivot = arr[arr.length / 2];
|
||||
int[] left = Arrays.stream(arr).boxed().filter(e -> e < pivot).mapToInt(e -> e).toArray();
|
||||
int[] middle = Arrays.stream(arr).boxed().filter(e -> e == pivot).mapToInt(e -> e).toArray();
|
||||
int[] right = Arrays.stream(arr).boxed().filter(e -> e > pivot).mapToInt(e -> e).toArray();
|
||||
return concat(quickSort2(left), middle, quickSort2(right));
|
||||
}
|
||||
|
||||
/**
|
||||
* 冒泡排序
|
||||
*
|
||||
* @param arr 数组
|
||||
*/
|
||||
public static void bubbleSort(int[] arr) {
|
||||
if(arr == null || arr.length < 2) {
|
||||
return;
|
||||
}
|
||||
int len = arr.length;
|
||||
for (int i = 0; i < len - 1; i++) {
|
||||
for (int j = 0; j < len - 1 - i; j++) {
|
||||
if (arr[j] > arr[j + 1]) {
|
||||
swap(arr, j, j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 交换他们数字的值
|
||||
*
|
||||
* @param arr 数组
|
||||
* @param i 位置i
|
||||
* @param j 位置j
|
||||
*/
|
||||
private static void swap(int[] arr, int i, int j) {
|
||||
int temp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = temp;
|
||||
}
|
||||
|
||||
public static int[] concat(int[] left, int[] middle, int[] right) {
|
||||
int totalLength = left.length + middle.length + right.length;
|
||||
int[] result = new int[totalLength];
|
||||
|
||||
System.arraycopy(left, 0, result, 0, left.length);
|
||||
System.arraycopy(middle, 0, result, left.length, middle.length);
|
||||
System.arraycopy(right, 0, result, left.length + middle.length, right.length);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package ai.remi.comm.util.asserts;
|
||||
|
||||
import ai.remi.comm.util.collection.CollectionUtils;
|
||||
import ai.remi.comm.util.object.ObjectUtils;
|
||||
import ai.remi.comm.util.string.StringUtils;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 断言工具类
|
||||
*/
|
||||
public class AssertUtils {
|
||||
/**
|
||||
* 如果不是true,则抛异常
|
||||
* @param expression
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isTrue(boolean expression, String msg) {
|
||||
if (!expression) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不是false,则抛异常
|
||||
* @param expression
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isFalse(boolean expression, String msg) {
|
||||
if (expression) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不是空或者空字符串,则抛异常
|
||||
* @param str
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isEmpty(String str, String msg) {
|
||||
if (StringUtils.hasText(str)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果是空或者空字符串,则抛异常
|
||||
* @param str
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isNotEmpty(String str, String msg) {
|
||||
if (!StringUtils.hasText(str)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果数组为不空或者长度大于等于1,则抛异常
|
||||
* @param str
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isEmpty(Object[] str, String msg) {
|
||||
if (!ObjectUtils.isEmpty(str)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果数组为空或者长度小于1,则抛异常
|
||||
* @param str
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isNotEmpty(Object[] str, String msg) {
|
||||
if (ObjectUtils.isEmpty(str)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果集合不为空或者长度大于等于1,则抛异常
|
||||
* @param collection
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isEmpty(Collection collection, String msg) {
|
||||
if (CollectionUtils.isNotEmpty(collection)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果集合为空或者长度小于1,则抛异常
|
||||
* @param collection
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isNotEmpty(Collection collection, String msg) {
|
||||
if (CollectionUtils.isEmpty(collection)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果集合不为空或者长度大于等于1,则抛异常
|
||||
* @param map
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isEmpty(Map map, String msg) {
|
||||
if (CollectionUtils.isNotEmpty(map)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果集合为空或者长度小于1,则抛异常
|
||||
* @param map
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isNotEmpty(Map map, String msg) {
|
||||
if (CollectionUtils.isEmpty(map)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果对象不为空,则抛异常
|
||||
* @param object
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isNull(Object object, String msg){
|
||||
if (object != null) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果对象空,则抛异常
|
||||
* @param object
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isNotNull(Object object, String msg){
|
||||
if (object == null) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果对象不为空,则抛异常
|
||||
* @param objects
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isNull(Object[] objects, String msg) {
|
||||
if (!ObjectUtils.isEmpty(objects)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果对象空,则抛异常
|
||||
* @param objects
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void isNotNull(Object[] objects, String msg) {
|
||||
if (ObjectUtils.isEmpty(objects)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较对象
|
||||
* @param obj1
|
||||
* @param obj2
|
||||
* @param msg
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void equals(Object obj1, Object obj2, String msg) {
|
||||
if (!equals(obj1, obj2)) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较对象的相等
|
||||
* @param cs1
|
||||
* @param cs2
|
||||
* @return
|
||||
*/
|
||||
public static boolean equals(final Object cs1, final Object cs2) {
|
||||
if (cs1 == cs2) {
|
||||
return true;
|
||||
}
|
||||
if (null == cs1 && null != cs2) {
|
||||
return false;
|
||||
}
|
||||
if (null != cs1 && null == cs2) {
|
||||
return false;
|
||||
}
|
||||
if (cs1 == null || null == cs2) {
|
||||
return true;
|
||||
}
|
||||
if (cs1 instanceof String && cs2 instanceof String) {
|
||||
return cs1.equals(cs2);
|
||||
}
|
||||
return cs1.equals(cs2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ai.remi.comm.util.auth;
|
||||
|
||||
|
||||
import ai.remi.comm.core.enums.IdentityTypeEnum;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc AuthInfo
|
||||
*/
|
||||
public interface AuthInfo {
|
||||
|
||||
/**
|
||||
* 用户系统语言
|
||||
*/
|
||||
String getUserLanguage();
|
||||
|
||||
/**
|
||||
* 用户唯一标识
|
||||
*/
|
||||
String getUniqueId();
|
||||
|
||||
/**
|
||||
* 部门租户号
|
||||
*/
|
||||
String getDeptTenantId();
|
||||
|
||||
/**
|
||||
* 公司租户号
|
||||
*/
|
||||
String getCompanyTenantId();
|
||||
|
||||
/**
|
||||
* 身份类型
|
||||
*/
|
||||
IdentityTypeEnum getIdentityTypeEnum();
|
||||
|
||||
/**
|
||||
* 服务类型
|
||||
*/
|
||||
String getServiceTypeCode();
|
||||
|
||||
/**
|
||||
* 鉴权Token
|
||||
* @return
|
||||
*/
|
||||
String getAccessToken();
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package ai.remi.comm.util.auth;
|
||||
|
||||
|
||||
import ai.remi.comm.core.enums.IdentityTypeEnum;
|
||||
import ai.remi.comm.core.enums.PermissionScopeEnum;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc AuthInfoUtils
|
||||
*/
|
||||
public class AuthInfoUtils {
|
||||
|
||||
/**
|
||||
* 获取认证信息
|
||||
*/
|
||||
public static AuthInfo getAuthInfo() {
|
||||
return RequestHolder.getAuthInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户语言
|
||||
*/
|
||||
public static String getUserLanguage() {
|
||||
return RequestHolder.getAuthInfo().getUserLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 鉴权Token
|
||||
* @return
|
||||
*/
|
||||
public static String getAccessToken() {
|
||||
return RequestHolder.getCompanyAuthInfo().getAccessToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户唯一id 对应 {@link IdentityTypeEnum}
|
||||
*/
|
||||
public static String getUniqueId() {
|
||||
return RequestHolder.getAuthInfo().getUniqueId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门租户号
|
||||
*/
|
||||
public static String getDeptTenantId() {
|
||||
return RequestHolder.getAuthInfo().getDeptTenantId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公司租户号
|
||||
*/
|
||||
public static String getCompanyTenantId() {
|
||||
return RequestHolder.getAuthInfo().getCompanyTenantId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户类型 瑞米官方账户、集团公司账户、访客体验用户
|
||||
*/
|
||||
public static IdentityTypeEnum getIdentityTypeEnum() {
|
||||
return RequestHolder.getAuthInfo().getIdentityTypeEnum();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前服务类型,主要是给feign调用内部服务时使用
|
||||
*/
|
||||
public static String getServiceTypeCode() {
|
||||
return RequestHolder.getAuthInfo().getServiceTypeCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限范围
|
||||
*/
|
||||
public static PermissionScopeEnum getPermissionScopeEnum() {
|
||||
return RequestHolder.getCompanyAuthInfo().getPermissionScope();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当权限范围为 {@link PermissionScopeEnum } 时,
|
||||
* 为当前请求可操作、查询的公司id集合
|
||||
*/
|
||||
public static Set<String> getHasPermissionCompanyIds() {
|
||||
return RequestHolder.getCompanyAuthInfo().getHasPermissionCompanyIds();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取瑞米信息
|
||||
*
|
||||
* @return 瑞米信息
|
||||
* @createTime 2022/1/6 19:35
|
||||
*/
|
||||
//public static CustomerAuthInfo getCustomerAuthInfo() {
|
||||
// return RequestHolder.getCustomerAuthInfo();
|
||||
//}
|
||||
|
||||
/**
|
||||
* 获取公司信息
|
||||
*
|
||||
* @return 客户信息
|
||||
* @createTime 2022/1/6 19:35
|
||||
*/
|
||||
public static CompanyAuthInfo getCompanyAuthInfo() {
|
||||
return RequestHolder.getCompanyAuthInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取信息
|
||||
*
|
||||
* @return 访客信息
|
||||
* @createTime 2022/1/6 19:35
|
||||
*/
|
||||
//public static CompanyAuthInfo getCompanyAuthInfo() {
|
||||
// return RequestHolder.getCompanyAuthInfo();
|
||||
//}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package ai.remi.comm.util.auth;
|
||||
|
||||
import ai.remi.comm.core.enums.IdentityTypeEnum;
|
||||
import ai.remi.comm.core.enums.PermissionScopeEnum;
|
||||
import ai.remi.comm.core.enums.ServiceTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc CompanyAuthInfo
|
||||
*/
|
||||
@Data
|
||||
public class CompanyAuthInfo implements AuthInfo {
|
||||
|
||||
/**
|
||||
* 用户系统语言
|
||||
*/
|
||||
private String userLanguage;
|
||||
|
||||
/**
|
||||
* 根据 {@link IdentityTypeEnum} 不同代表不同的身份
|
||||
* 用户id、不同端代表对应的id
|
||||
*/
|
||||
private String uniqueId;
|
||||
|
||||
/**
|
||||
* 部门租户号
|
||||
*/
|
||||
private String deptTenantId;
|
||||
|
||||
/**
|
||||
* 公司租户号
|
||||
*/
|
||||
private String companyTenantId;
|
||||
|
||||
/**
|
||||
* 鉴权Token
|
||||
*/
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* 权限范围
|
||||
*/
|
||||
private PermissionScopeEnum permissionScope;
|
||||
|
||||
/**
|
||||
* 如果具有集团范围权限时,这个值为拥有这个权限的公司id集合
|
||||
*/
|
||||
private Set<String> hasPermissionCompanyIds;
|
||||
|
||||
/**
|
||||
* 如果具有公司范围权限时,这个值为拥有这个权限的部门id集合
|
||||
*/
|
||||
private Set<String> hasPermissionDeptIds;
|
||||
|
||||
@Override
|
||||
public IdentityTypeEnum getIdentityTypeEnum() {
|
||||
return IdentityTypeEnum.COMPANY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceTypeCode() {
|
||||
return ServiceTypeEnum.WEB_SERVICE.getCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package ai.remi.comm.util.auth;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc RequestHolder
|
||||
*/
|
||||
@Slf4j
|
||||
public class RequestHolder {
|
||||
|
||||
private static final ThreadLocal<AuthInfo> authInfoHolder = new ThreadLocal();
|
||||
|
||||
private static final ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal();
|
||||
|
||||
|
||||
/**
|
||||
* 使用实例:
|
||||
* CompanyAuthInfo companyAuthInfo = new CompanyAuthInfo();
|
||||
* companyAuthInfo.setStoreId(pageRenderRealTimeDTO.getStoreId());
|
||||
* companyAuthInfo.setCompanyId(pageRenderRealTimeDTO.getCompanyId());
|
||||
* companyAuthInfo.setPermissionScope(PermissionScopeEnum.COMPANY);
|
||||
* RequestHolder.add(companyAuthInfo);
|
||||
* @param authInfo
|
||||
*/
|
||||
public static void add(AuthInfo authInfo) {
|
||||
authInfoHolder.set(authInfo);
|
||||
}
|
||||
|
||||
public static void add(HttpServletRequest request) {
|
||||
requestHolder.set(request);
|
||||
}
|
||||
|
||||
public static AuthInfo getAuthInfo() {
|
||||
AuthInfo authInfo = authInfoHolder.get();
|
||||
if (authInfo == null) {
|
||||
log.warn("当前线程AuthInfo为空,请检查");
|
||||
}
|
||||
return authInfo;
|
||||
}
|
||||
|
||||
/*public static RemiAuthInfo getRemiAuthInfo() {
|
||||
AuthInfo authInfo = getAuthInfo();
|
||||
if (authInfo instanceof RemiAuthInfo){
|
||||
return (RemiAuthInfo) authInfo;
|
||||
}
|
||||
throw new RuntimeException("类型转换错误");
|
||||
}*/
|
||||
|
||||
public static CompanyAuthInfo getCompanyAuthInfo() {
|
||||
AuthInfo authInfo = getAuthInfo();
|
||||
if (authInfo instanceof CompanyAuthInfo) {
|
||||
return (CompanyAuthInfo) authInfo;
|
||||
}
|
||||
throw new RuntimeException("类型转换错误");
|
||||
}
|
||||
|
||||
/*public static VisitorAuthInfo getVisitorAuthInfo() {
|
||||
AuthInfo authInfo = getAuthInfo();
|
||||
if (authInfo instanceof VisitorAuthInfo){
|
||||
return (VisitorAuthInfo) authInfo;
|
||||
}
|
||||
throw new RuntimeException("类型转换错误");
|
||||
}*/
|
||||
|
||||
public static HttpServletRequest getCurrentRequest() {
|
||||
return requestHolder.get();
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
authInfoHolder.remove();
|
||||
requestHolder.remove();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package ai.remi.comm.util.auth;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc TenantHolder
|
||||
*/
|
||||
public class TenantHolder {
|
||||
|
||||
/**
|
||||
* 是否是管理员
|
||||
*/
|
||||
private static final ThreadLocal<Boolean> isAdminThreadLocal = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 是否是不拦截的请求
|
||||
*/
|
||||
private static final ThreadLocal<Boolean> urlIgnoreThreadLocal = new ThreadLocal<>();
|
||||
|
||||
|
||||
public static Boolean isAdmin() {
|
||||
return isAdminThreadLocal.get();
|
||||
}
|
||||
|
||||
public static void admin(boolean isAdmin) {
|
||||
isAdminThreadLocal.set(isAdmin);
|
||||
}
|
||||
|
||||
public static Boolean getIgnoredRequest() {
|
||||
return urlIgnoreThreadLocal.get();
|
||||
}
|
||||
|
||||
public static void setIgnoredRequest(boolean ignoredRequest) {
|
||||
urlIgnoreThreadLocal.set(ignoredRequest);
|
||||
}
|
||||
|
||||
|
||||
public static void remove() {
|
||||
isAdminThreadLocal.remove();
|
||||
urlIgnoreThreadLocal.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package ai.remi.comm.util.bean;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc BeanCopyUtils
|
||||
*/
|
||||
public class BeanCopyUtils {
|
||||
|
||||
/**
|
||||
* 浅拷贝list
|
||||
*
|
||||
* @param source 数据源
|
||||
* @param clazz 对象
|
||||
* @return LIST
|
||||
*/
|
||||
public static List copyListProperties(List source, Class clazz) {
|
||||
List target = new ArrayList();
|
||||
source.forEach(o -> {
|
||||
try {
|
||||
target.add(JSONObject.parseObject(JSONObject.toJSONString(o), clazz));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 源对象和目标对象浅拷贝 PS:目标对象和源对象中的属性名称需一至,否则无法copy
|
||||
*
|
||||
* @param sourceObj 源对象
|
||||
* @param targetObj 目标对象
|
||||
*/
|
||||
public static void copyPropertiesThird(Object sourceObj, Object targetObj) {
|
||||
BeanUtils.copyProperties(sourceObj, targetObj);
|
||||
}
|
||||
|
||||
public static <T> T copyPropertiesThird(Object sourceObj, Class<T> calzz) {
|
||||
if (Objects.isNull(sourceObj) || Objects.isNull(calzz)) {
|
||||
return null;
|
||||
}
|
||||
T c = null;
|
||||
try {
|
||||
// c = calzz.newInstance();
|
||||
c = calzz.getDeclaredConstructor().newInstance();
|
||||
BeanCopyUtils.copyPropertiesThird(sourceObj, c);
|
||||
} catch (Exception e) {
|
||||
System.out.println("BeanCopyUtils -> copyPropertiesThird error:" + e);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换list泛型
|
||||
*
|
||||
* @param source 数据源
|
||||
* @param clazz 对象
|
||||
* @return LIST
|
||||
*/
|
||||
public static <T> List<T> coverList(List source, Class<T> clazz) {
|
||||
List<T> res = new ArrayList<>();
|
||||
source.forEach(o -> res.add(BeanCopyUtils.copyPropertiesThird(o, clazz)));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
public static Map<String, Object> entityToMap(Object object) {
|
||||
Map<String, Object> reMap = new HashMap();
|
||||
if (object == null) {
|
||||
return null;
|
||||
} else {
|
||||
List<Field> fields = new ArrayList();
|
||||
List<String> fieldsName = new ArrayList();
|
||||
|
||||
for(Class tempClass = object.getClass(); tempClass != null; tempClass = tempClass.getSuperclass()) {
|
||||
fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
|
||||
}
|
||||
|
||||
List<Field> childFields = Arrays.asList(object.getClass().getDeclaredFields());
|
||||
Iterator var6 = childFields.iterator();
|
||||
|
||||
Field field;
|
||||
while(var6.hasNext()) {
|
||||
field = (Field)var6.next();
|
||||
fieldsName.add(field.getName());
|
||||
}
|
||||
|
||||
try {
|
||||
var6 = fields.iterator();
|
||||
|
||||
while(var6.hasNext()) {
|
||||
field = (Field)var6.next();
|
||||
|
||||
try {
|
||||
Field f;
|
||||
Object o;
|
||||
if (fieldsName.contains(field.getName())) {
|
||||
f = object.getClass().getDeclaredField(field.getName());
|
||||
f.setAccessible(true);
|
||||
o = f.get(object);
|
||||
reMap.put(field.getName(), o);
|
||||
} else {
|
||||
f = object.getClass().getSuperclass().getDeclaredField(field.getName());
|
||||
f.setAccessible(true);
|
||||
o = f.get(object);
|
||||
reMap.put(field.getName(), o);
|
||||
}
|
||||
} catch (Exception var10) {
|
||||
var10.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (SecurityException var11) {
|
||||
var11.printStackTrace();
|
||||
}
|
||||
|
||||
return reMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
package ai.remi.comm.util.collection;
|
||||
|
||||
import ai.remi.comm.util.array.ArrayUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class CollectionUtils {
|
||||
public static final String DESC = "desc";
|
||||
public static final String ASC = "asc";
|
||||
|
||||
/**
|
||||
* 集合是否为空
|
||||
*
|
||||
* @param collection 集合
|
||||
* @return 若集合为null, 或集合大小为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(Collection<?> collection) {
|
||||
return collection == null || collection.size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map是否为空
|
||||
*
|
||||
* @param map
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEmpty(Map<?, ?> map) {
|
||||
return map == null || map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 集合是否不为空
|
||||
*
|
||||
* @param collection 集合
|
||||
* @return 若集合不为null, 而且集合大小不为0, 则返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isNotEmpty(Collection<?> collection) {
|
||||
return !isEmpty(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map是否不为空
|
||||
*
|
||||
* @param map
|
||||
* @return
|
||||
*/
|
||||
public static boolean isNotEmpty(Map<?, ?> map) {
|
||||
return !isEmpty(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数转换为List集合
|
||||
*
|
||||
* @param objs 参数对象
|
||||
* @return 返回泛型
|
||||
*/
|
||||
public static <E> List<E> asList(E... objs) {
|
||||
return Arrays.asList(objs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数集合转换为List集合
|
||||
*
|
||||
* @param collection 集合参数
|
||||
* @return 返回list泛型
|
||||
*/
|
||||
public static <E> List<E> asList(Collection<E> collection) {
|
||||
if (collection == null) {
|
||||
return null;
|
||||
}
|
||||
if (collection instanceof List) {
|
||||
return (List<E>) collection;
|
||||
}
|
||||
return new ArrayList<E>(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数转换为Set集合
|
||||
*
|
||||
* @param objs 参数对象
|
||||
* @return 返回set泛型
|
||||
*/
|
||||
public static <E> Set<E> asSet(E... objs) {
|
||||
return new HashSet<E>(Arrays.asList(objs));
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数转换为Set集合
|
||||
*
|
||||
* @param collection 集合参数
|
||||
* @return 返回set泛型
|
||||
*/
|
||||
public static <E> Set<E> asSet(Collection<E> collection) {
|
||||
if (collection == null) {
|
||||
return null;
|
||||
}
|
||||
if (collection instanceof Set) {
|
||||
return (Set<E>) collection;
|
||||
}
|
||||
return new HashSet<E>(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为数组
|
||||
*
|
||||
* @param collection 集合
|
||||
* @returns 返回数组
|
||||
*/
|
||||
public static <E> E[] toArray(Collection<E> collection) {
|
||||
return ArrayUtils.asArray(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建ArrayList实例
|
||||
*
|
||||
* @return 返回list
|
||||
*/
|
||||
public static <E> List<E> newList() {
|
||||
return new ArrayList<E>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个指定容量的ArrayList实例
|
||||
*
|
||||
* @param size 大小
|
||||
* @return 返回list
|
||||
*/
|
||||
public static <E> List<E> newList(int size) {
|
||||
return new ArrayList<E>(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个HashSet实例
|
||||
*
|
||||
* @return 返回set泛型
|
||||
*/
|
||||
public static <E> Set<E> newSet() {
|
||||
return new HashSet<E>(16, .75f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个指定容量大小的HashSet实例
|
||||
*
|
||||
* @param initialCapacity 初始容量
|
||||
* @param loadFactor 加载因子
|
||||
* @return 返回set
|
||||
*/
|
||||
public static <E> Set<E> newSet(int initialCapacity, double loadFactor) {
|
||||
return new HashSet<E>(initialCapacity, (float) loadFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取map的最小key的value
|
||||
*
|
||||
* @param map 传入map的值
|
||||
* @return String
|
||||
*/
|
||||
public static String getMinKeysValue(Map<Integer, String> map) {
|
||||
if (map == null || map.size() < 1) {
|
||||
return new String();
|
||||
}
|
||||
int key = map.keySet().stream().min(Comparator.comparing(Function.identity())).get();
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对list中的元素按升序排列.
|
||||
*
|
||||
* @param list 排序集合
|
||||
* @param field 排序字段
|
||||
* @return
|
||||
*/
|
||||
public static List<?> sort(List<?> list, final String field) {
|
||||
return sort(list, field, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对list中的元素进行排序.
|
||||
*
|
||||
* @param list 排序集合
|
||||
* @param field 排序字段
|
||||
* @param sort 排序方式: SortList.DESC(降序) SortList.ASC(升序).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<?> sort(List<?> list, final String field,
|
||||
final String sort) {
|
||||
Collections.sort(list, new Comparator() {
|
||||
public int compare(Object a, Object b) {
|
||||
int ret = 0;
|
||||
try {
|
||||
Field f = a.getClass().getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
Class<?> type = f.getType();
|
||||
|
||||
if (type == int.class) {
|
||||
ret = Integer.compare(f.getInt(a), f
|
||||
.getInt(b));
|
||||
} else if (type == double.class) {
|
||||
ret = Double.compare(f.getDouble(a), f
|
||||
.getDouble(b));
|
||||
} else if (type == long.class) {
|
||||
ret = Long.compare(f.getLong(a), f
|
||||
.getLong(b));
|
||||
} else if (type == float.class) {
|
||||
ret = Float.compare(f.getFloat(a), f
|
||||
.getFloat(b));
|
||||
} else if (type == Date.class) {
|
||||
ret = ((Date) f.get(a)).compareTo((Date) f.get(b));
|
||||
} else if (isImplementsOf(type, Comparable.class)) {
|
||||
ret = ((Comparable) f.get(a)).compareTo(f
|
||||
.get(b));
|
||||
} else {
|
||||
ret = String.valueOf(f.get(a)).compareTo(
|
||||
String.valueOf(f.get(b)));
|
||||
}
|
||||
|
||||
} catch (SecurityException | NoSuchFieldException | IllegalAccessException |
|
||||
IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (sort != null && sort.equalsIgnoreCase(DESC)) {
|
||||
return -ret;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对list中的元素按fields和sorts进行排序,
|
||||
* fields[i]指定排序字段,sorts[i]指定排序方式.如果sorts[i]为空则默认按升序排列.
|
||||
*
|
||||
* @param list
|
||||
* @param fields
|
||||
* @param sorts
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<?> sort(List<?> list, String[] fields, String[] sorts) {
|
||||
if (fields != null && fields.length > 0) {
|
||||
for (int i = fields.length - 1; i >= 0; i--) {
|
||||
final String field = fields[i];
|
||||
String tmpSort = ASC;
|
||||
if (sorts != null && sorts.length > i && sorts[i] != null) {
|
||||
tmpSort = sorts[i];
|
||||
}
|
||||
final String sort = tmpSort;
|
||||
Collections.sort(list, new Comparator() {
|
||||
public int compare(Object a, Object b) {
|
||||
int ret = 0;
|
||||
try {
|
||||
Field f = a.getClass().getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
Class<?> type = f.getType();
|
||||
if (type == int.class) {
|
||||
ret = ((Integer) f.getInt(a))
|
||||
.compareTo(f.getInt(b));
|
||||
} else if (type == double.class) {
|
||||
ret = ((Double) f.getDouble(a))
|
||||
.compareTo(f.getDouble(b));
|
||||
} else if (type == long.class) {
|
||||
ret = ((Long) f.getLong(a)).compareTo(f
|
||||
.getLong(b));
|
||||
} else if (type == float.class) {
|
||||
ret = ((Float) f.getFloat(a))
|
||||
.compareTo(f.getFloat(b));
|
||||
} else if (type == Date.class) {
|
||||
ret = ((Date) f.get(a)).compareTo((Date) f
|
||||
.get(b));
|
||||
} else if (isImplementsOf(type, Comparable.class)) {
|
||||
ret = ((Comparable) f.get(a))
|
||||
.compareTo(f.get(b));
|
||||
} else {
|
||||
ret = String.valueOf(f.get(a)).compareTo(
|
||||
String.valueOf(f.get(b)));
|
||||
}
|
||||
|
||||
} catch (SecurityException | NoSuchFieldException | IllegalArgumentException |
|
||||
IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (sort != null && sort.equalsIgnoreCase(DESC)) {
|
||||
return -ret;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认按正序排列
|
||||
*
|
||||
* @param list
|
||||
* @param method
|
||||
*/
|
||||
public static List<?> sortByMethod(List<?> list, final String method) {
|
||||
return sortByMethod(list, method, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<?> sortByMethod(List<?> list, final String method,
|
||||
final String sort) {
|
||||
Collections.sort(list, new Comparator() {
|
||||
public int compare(Object a, Object b) {
|
||||
int ret = 0;
|
||||
try {
|
||||
Method m = a.getClass().getMethod(method, null);
|
||||
m.setAccessible(true);
|
||||
Class<?> type = m.getReturnType();
|
||||
if (type == int.class) {
|
||||
ret = ((Integer) m.invoke(a, null))
|
||||
.compareTo((Integer) m.invoke(b, null));
|
||||
} else if (type == double.class) {
|
||||
ret = ((Double) m.invoke(a, null)).compareTo((Double) m
|
||||
.invoke(b, null));
|
||||
} else if (type == long.class) {
|
||||
ret = ((Long) m.invoke(a, null)).compareTo((Long) m
|
||||
.invoke(b, null));
|
||||
} else if (type == float.class) {
|
||||
ret = ((Float) m.invoke(a, null)).compareTo((Float) m
|
||||
.invoke(b, null));
|
||||
} else if (type == Date.class) {
|
||||
ret = ((Date) m.invoke(a, null)).compareTo((Date) m
|
||||
.invoke(b, null));
|
||||
} else if (isImplementsOf(type, Comparable.class)) {
|
||||
ret = ((Comparable) m.invoke(a, null))
|
||||
.compareTo(m.invoke(b, null));
|
||||
} else {
|
||||
ret = String.valueOf(m.invoke(a, null)).compareTo(
|
||||
String.valueOf(m.invoke(b, null)));
|
||||
}
|
||||
|
||||
if (isImplementsOf(type, Comparable.class)) {
|
||||
ret = ((Comparable) m.invoke(a, null))
|
||||
.compareTo(m.invoke(b, null));
|
||||
} else {
|
||||
ret = String.valueOf(m.invoke(a, null)).compareTo(
|
||||
String.valueOf(m.invoke(b, null)));
|
||||
}
|
||||
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ne) {
|
||||
ne.printStackTrace();
|
||||
}
|
||||
|
||||
if (sort != null && sort.toLowerCase().equals(DESC)) {
|
||||
return -ret;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<?> sortByMethod(List<?> list, final String methods[],
|
||||
final String sorts[]) {
|
||||
if (methods != null && methods.length > 0) {
|
||||
for (int i = methods.length - 1; i >= 0; i--) {
|
||||
final String method = methods[i];
|
||||
String tmpSort = ASC;
|
||||
if (sorts != null && sorts.length > i && sorts[i] != null) {
|
||||
tmpSort = sorts[i];
|
||||
}
|
||||
final String sort = tmpSort;
|
||||
Collections.sort(list, new Comparator() {
|
||||
public int compare(Object a, Object b) {
|
||||
int ret = 0;
|
||||
try {
|
||||
Method m = a.getClass().getMethod(method, null);
|
||||
m.setAccessible(true);
|
||||
Class<?> type = m.getReturnType();
|
||||
if (type == int.class) {
|
||||
ret = ((Integer) m.invoke(a, null))
|
||||
.compareTo((Integer) m.invoke(b, null));
|
||||
} else if (type == double.class) {
|
||||
ret = ((Double) m.invoke(a, null))
|
||||
.compareTo((Double) m.invoke(b, null));
|
||||
} else if (type == long.class) {
|
||||
ret = ((Long) m.invoke(a, null))
|
||||
.compareTo((Long) m.invoke(b, null));
|
||||
} else if (type == float.class) {
|
||||
ret = ((Float) m.invoke(a, null))
|
||||
.compareTo((Float) m.invoke(b, null));
|
||||
} else if (type == Date.class) {
|
||||
ret = ((Date) m.invoke(a, null))
|
||||
.compareTo((Date) m.invoke(b, null));
|
||||
} else if (isImplementsOf(type, Comparable.class)) {
|
||||
ret = ((Comparable) m.invoke(a, null))
|
||||
.compareTo(m.invoke(b,
|
||||
null));
|
||||
} else {
|
||||
ret = String.valueOf(m.invoke(a, null))
|
||||
.compareTo(
|
||||
String.valueOf(m
|
||||
.invoke(b, null)));
|
||||
}
|
||||
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ne) {
|
||||
ne.printStackTrace();
|
||||
}
|
||||
|
||||
if (sort != null && sort.toLowerCase().equals(DESC)) {
|
||||
return -ret;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象实现的所有接口中是否包含szInterface
|
||||
*
|
||||
* @param clazz
|
||||
* @param szInterface
|
||||
*/
|
||||
public static boolean isImplementsOf(Class<?> clazz, Class<?> szInterface) {
|
||||
boolean flag = false;
|
||||
|
||||
Class<?>[] face = clazz.getInterfaces();
|
||||
for (Class<?> c : face) {
|
||||
if (c == szInterface) {
|
||||
flag = true;
|
||||
} else {
|
||||
flag = isImplementsOf(c, szInterface);
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag && null != clazz.getSuperclass()) {
|
||||
return isImplementsOf(clazz.getSuperclass(), szInterface);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据集合的某个指定字段去重
|
||||
* 使用方式:
|
||||
* list.stream().filter(CollectionUtils.distinctByKey(RoleVO::getId)).collect(Collectors.toList());
|
||||
*
|
||||
* @param keyExtractor 需要去重的字段
|
||||
* @param <T>
|
||||
* @return java.util.function.Predicate<T>
|
||||
*/
|
||||
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
|
||||
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
|
||||
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ai.remi.comm.util.cron;
|
||||
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
/**
|
||||
* Quartz Cron 表达式的工具类
|
||||
*/
|
||||
public class CronUtils {
|
||||
/**
|
||||
* 校验 CRON 表达式是否有效
|
||||
*
|
||||
* @param cronExpression CRON 表达式
|
||||
* @return 是否有效
|
||||
*/
|
||||
public static boolean isValid(String cronExpression) {
|
||||
return CronExpression.isValidExpression(cronExpression);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String validCron = "0 0 12 * * ?";
|
||||
String invalidCron = "invalid";
|
||||
|
||||
System.out.println("Valid Cron: " + isValid(validCron)); // 输出 true
|
||||
System.out.println("Invalid Cron: " + isValid(invalidCron)); // 输出 false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,727 @@
|
||||
package ai.remi.comm.util.date;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 时间处理工具类
|
||||
*
|
||||
* @author ws
|
||||
* @mail 1720696548@qq.com
|
||||
* @date 2020/4/24 0024 9:54
|
||||
* @return
|
||||
*/
|
||||
public class LocalDateTimeUtils {
|
||||
|
||||
/**
|
||||
* 时间格式
|
||||
*/
|
||||
private static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
|
||||
private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
private static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
|
||||
private static final String YYYY_MM_DD_HH = "yyyy-MM-dd HH";
|
||||
private static final String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
private static final String YYYY_MM = "yyyy-MM";
|
||||
private static final String YYYY = "yyyy";
|
||||
|
||||
/**
|
||||
* 获取时间类型
|
||||
* 1-包含开始和结束时间(默认)
|
||||
* 2-包含结束-不包含开始时间 // 开始时间+1天
|
||||
* 3-包含开始-不包含结束时间 // 结束时间-1天
|
||||
* 4-不包含开始和结束时间 // 开始时间+1天 or 结束时间-1天
|
||||
*/
|
||||
private static final int BETWEEN_TYPE_ONE = 1;
|
||||
private static final int BETWEEN_TYPE_TWO = 2;
|
||||
private static final int BETWEEN_TYPE_THREE = 3;
|
||||
private static final int BETWEEN_TYPE_FOUR = 4;
|
||||
|
||||
private static Random random;
|
||||
|
||||
static {
|
||||
try {
|
||||
random = SecureRandom.getInstanceStrong();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断时间 小于
|
||||
* <P> t1 < t2 = true (如:2019-10-13 11:11:00 < 2020-11-13 13:13:00 = true) </P>
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static boolean isBefore(LocalDateTime t1, LocalDateTime t2) {
|
||||
return t1.isBefore(t2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断时间 大于
|
||||
* <P> t1 > t2 = true </P>
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static boolean isAfter(LocalDateTime t1, LocalDateTime t2) {
|
||||
return t1.isAfter(t2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自构建 LocalDateTime ==> 年,月,日,时,分
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute) {
|
||||
return LocalDateTime.of(year, month, dayOfMonth, hour, minute);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自构建 LocalDateTime ==> 年,月,日,时,分,秒,毫秒(精确到9位数)
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {
|
||||
return LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);
|
||||
}
|
||||
|
||||
|
||||
//========================================================================================================
|
||||
//========================================================================================================
|
||||
//========================================================================================================
|
||||
//============================================== 时间获取 =================================================
|
||||
//========================================================================================================
|
||||
//========================================================================================================
|
||||
|
||||
/**
|
||||
* 获取指定某一天的开始时间 00:00:00
|
||||
*
|
||||
* @param time
|
||||
* @return java.time.LocalDateTime
|
||||
* @author wangsong
|
||||
* @date 2020/12/24 0024 15:10
|
||||
* @version 1.0.1
|
||||
*/
|
||||
public static LocalDateTime getDayStart(LocalDateTime time) {
|
||||
return time.withHour(0)
|
||||
.withMinute(0)
|
||||
.withSecond(0)
|
||||
.withNano(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定某一天的结束时间 23:59:59.999999
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime getDayEnd(LocalDateTime time) {
|
||||
// 年 月 天 时 分 秒 毫秒(这里精确到6位数)
|
||||
return time.withHour(23)
|
||||
.withMinute(59)
|
||||
.withSecond(59)
|
||||
.withNano(999999);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定时间是周几 1到7
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static int week(LocalDateTime time) {
|
||||
return time.getDayOfWeek().getValue();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定时间之后的日期
|
||||
* <P> 根据field不同加不同值 , field为ChronoUnit.*
|
||||
* 秒 ChronoUnit.SECONDS
|
||||
* 分 ChronoUnit.MINUTES
|
||||
* 时 ChronoUnit.HOURS
|
||||
* 半天 ChronoUnit.HALF_DAYS
|
||||
* 天 ChronoUnit.DAYS
|
||||
* 月 ChronoUnit.MONTHS
|
||||
* 年 ChronoUnit.YEARS
|
||||
* </P>
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
|
||||
return time.plus(number, field);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取两个日期的时间差
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 计算时间
|
||||
* @param field 根据field不同减不同值 , field 为 ChronoUnit.*
|
||||
* @return startTime小 endTime大 返回正数,则反之
|
||||
* @author wangsong
|
||||
* <p>
|
||||
* 秒 ChronoUnit.SECONDS
|
||||
* 分 ChronoUnit.MINUTES
|
||||
* 时 ChronoUnit.HOURS
|
||||
* 半天 ChronoUnit.HALF_DAYS
|
||||
* 天 ChronoUnit.DAYS
|
||||
* 月 ChronoUnit.MONTHS
|
||||
* 年 ChronoUnit.YEARS
|
||||
* </P>
|
||||
*/
|
||||
public static long betweenTwoTime(LocalDateTime startTime, LocalDateTime endTime, ChronoUnit field) {
|
||||
Period period = Period.between(LocalDate.from(startTime), LocalDate.from(endTime));
|
||||
if (field == ChronoUnit.YEARS) {
|
||||
return period.getYears();
|
||||
}
|
||||
if (field == ChronoUnit.MONTHS) {
|
||||
return period.getYears() * 12L + period.getMonths();
|
||||
}
|
||||
return field.between(startTime, endTime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定时间之前的日期
|
||||
*
|
||||
* @author wangsong
|
||||
* <P> 根据field不同减不同值, field 为 ChronoUnit.*
|
||||
* 秒 ChronoUnit.SECONDS
|
||||
* 分 ChronoUnit.MINUTES
|
||||
* 时 ChronoUnit.HOURS
|
||||
* 半天 ChronoUnit.HALF_DAYS
|
||||
* 天 ChronoUnit.DAYS
|
||||
* 月 ChronoUnit.MONTHS
|
||||
* 年 ChronoUnit.YEARS
|
||||
* </P>
|
||||
* @version 1.0.1
|
||||
*/
|
||||
public static LocalDateTime subtract(LocalDateTime time, long number, TemporalUnit field) {
|
||||
return time.minus(number, field);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定时间 加或减N周的第一天 00:00:00
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime weekFirstDay(LocalDateTime time, int num) {
|
||||
int week = week(LocalDateTime.now());
|
||||
LocalDateTime newTime = subtract(LocalDateTime.now(), week - 1L, ChronoUnit.DAYS);
|
||||
newTime = plus(newTime, num * 7L, ChronoUnit.DAYS);
|
||||
return getDayStart(newTime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定时间 加或减N周的最后一天 23:59:59:999999
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime weekLastDay(LocalDateTime time, int num) {
|
||||
int week = week(LocalDateTime.now());
|
||||
LocalDateTime newTime = plus(LocalDateTime.now(), 7L - week, ChronoUnit.DAYS);
|
||||
newTime = plus(newTime, num * 7L, ChronoUnit.DAYS);
|
||||
return getDayEnd(newTime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定月 加或减N月的第一天 00:00:00
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime monthFirstDay(LocalDateTime time, int num) {
|
||||
LocalDateTime newTime = plus(time, num, ChronoUnit.MONTHS);
|
||||
newTime = newTime.with(TemporalAdjusters.firstDayOfMonth());
|
||||
return getDayStart(newTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定月 加或减N月的最后一天 23:59:59:999999
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime monthLastDay(LocalDateTime time, int num) {
|
||||
LocalDateTime newTime = plus(time, num, ChronoUnit.MONTHS);
|
||||
newTime = newTime.with(TemporalAdjusters.lastDayOfMonth());
|
||||
return getDayEnd(newTime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定年 加或减N年的第一天 00:00:00
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime yearFirstDay(LocalDateTime time, int num) {
|
||||
LocalDateTime newTime = plus(time, num, ChronoUnit.YEARS);
|
||||
int year = newTime.getYear();
|
||||
// 年 月 天 时 分 秒 毫秒(这里精确到9位数)
|
||||
return LocalDateTime.of(year, 1, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定年 加或减N年最后一天 23:59:59:999999
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime yearLastDay(LocalDateTime time, int num) {
|
||||
LocalDateTime newTime = subtract(time, num, ChronoUnit.YEARS);
|
||||
int year = newTime.getYear();
|
||||
// 年 月 天 时 分 秒 毫秒(这里精确到6位数)
|
||||
return LocalDateTime.of(year, 12, 31, 23, 59, 59, 999999);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取17位时间戳字符串+3位随机数
|
||||
* <p> 这里增加了线程锁和延时一毫秒,单体项目100%不会重复,可用于生成订单号 </p>
|
||||
* 20200101125959999 2020-01-01 12:59:59:999
|
||||
*
|
||||
* @return
|
||||
* @author wangsong
|
||||
*/
|
||||
public static synchronized String getNo() {
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
|
||||
timeStamp += (random.nextInt(10) + "") + (random.nextInt(10) + "") + (random.nextInt(10) + "");
|
||||
return timeStamp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取整点-- 把指定时间的 分+秒设置为0
|
||||
*
|
||||
* @param time time
|
||||
* @return java.time.LocalDateTime
|
||||
* @author wangsong
|
||||
* @date 2020/12/24 0024 15:10
|
||||
* @version 1.0.1
|
||||
*/
|
||||
public static LocalDateTime getTheHour(LocalDateTime time) {
|
||||
// 分 // 秒 // 毫秒(这里精确到9位数)
|
||||
return time.withMinute(0)
|
||||
.withSecond(0)
|
||||
.withNano(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取整分-- 把指定时间的 秒设置为0
|
||||
* <p>
|
||||
* // * 如:
|
||||
* // * 2020-01-01 12:10 ===> 等于 2020-01-01 12:20
|
||||
* // * 2020-01-01 12:11 ===> 等于 2020-01-01 12:20
|
||||
* // * 2020-01-01 12:19 ===> 等于 2020-01-01 12:20
|
||||
* </P>
|
||||
*
|
||||
* @param time
|
||||
* @return java.time.LocalDateTime
|
||||
* @author wangsong
|
||||
* @date 2020/12/24 0024 15:21
|
||||
* @version 1.0.1
|
||||
*/
|
||||
public static LocalDateTime getTheMinute(LocalDateTime time) {
|
||||
// 秒 // 毫秒(这里精确到9位数)
|
||||
return time.withSecond(0).withNano(0);
|
||||
}
|
||||
|
||||
|
||||
//========================================================================================================
|
||||
//========================================================================================================
|
||||
//========================================================================================================
|
||||
//============================================== 转换相关 =================================================
|
||||
//========================================================================================================
|
||||
//========================================================================================================
|
||||
|
||||
/**
|
||||
* LocalDateTime 转为 天 的字符串,如 1号返回 01
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static Integer parseDayInt(LocalDateTime time) {
|
||||
return Integer.parseInt(parse(time, "dd"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Date 转 LocalDateTime
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static LocalDateTime parseLdt(Date date) {
|
||||
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDateTime 转 Date
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static Date parseDate(LocalDateTime time) {
|
||||
return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDateTime 转 毫秒
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static Long parseMillisecond(LocalDateTime time) {
|
||||
return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDateTime 转 秒
|
||||
*
|
||||
* @author wangsong
|
||||
*/
|
||||
public static Long parseSecond(LocalDateTime time) {
|
||||
return time.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将时间戳转 为 LocalDateTime
|
||||
*
|
||||
* @param timestamp
|
||||
* @return java.lang.String
|
||||
* @author wangsong
|
||||
* @date 2021/5/12 0012 17:13
|
||||
* @version 1.0.1
|
||||
*/
|
||||
public static LocalDateTime parseTimestamp(Long timestamp) {
|
||||
return LocalDateTime.ofEpochSecond(timestamp / 1000, 0, ZoneOffset.ofHours(8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将LocalDateTime 转 为时间戳
|
||||
*
|
||||
* @return java.lang.String
|
||||
* @author wangsong
|
||||
* @date 2021/5/12 0012 17:13
|
||||
* @version 1.0.1
|
||||
*/
|
||||
public static Long parseTimestamp(LocalDateTime time) {
|
||||
return time.toEpochSecond(ZoneOffset.ofHours(8));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* String 类型转成 LocalDateTime ,必须为完整时间,如:2020-01-20 00:00:00
|
||||
*
|
||||
* @param timeStr 时间字符串
|
||||
* @return java.time.LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime parse(String timeStr) {
|
||||
return parse(timeStr, YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* String (2020-01-20 00:00:00)类型转成 LocalDateTime
|
||||
*
|
||||
* @param timeStr timeStr 时间字符串
|
||||
* @param pattern pattern 格式
|
||||
* @return java.time.LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime parse(String timeStr, String pattern) {
|
||||
if (pattern.equals(YYYY)) {
|
||||
timeStr += "-01-01 00:00:00";
|
||||
} else if (pattern.equals(YYYY_MM)) {
|
||||
timeStr += "-01 00:00:00";
|
||||
} else if (pattern.equals(YYYY_MM_DD)) {
|
||||
timeStr += " 00:00:00";
|
||||
} else if (pattern.equals(YYYY_MM_DD_HH)) {
|
||||
timeStr += ":00:00";
|
||||
} else if (pattern.equals(YYYY_MM_DD_HH_MM)) {
|
||||
timeStr += ":00";
|
||||
}
|
||||
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
|
||||
return LocalDateTime.parse(timeStr, dtf);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* LocalDateTime 转完整 String 类型的时间 如:2020-01-20 00:00:00
|
||||
*
|
||||
* @param time time
|
||||
* @return java.lang.String
|
||||
*/
|
||||
public static String parse(LocalDateTime time) {
|
||||
return parse(time, YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDateTime 转指定类型的字符串
|
||||
*
|
||||
* @param time time 时间
|
||||
* @param pattern pattern 格式
|
||||
* @return java.lang.String
|
||||
* @author wangsong
|
||||
*/
|
||||
public static String parse(LocalDateTime time, String pattern) {
|
||||
if (time == null) {
|
||||
return null;
|
||||
}
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
|
||||
return df.format(time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Date 转指定格式的字符串
|
||||
*
|
||||
* @param time
|
||||
* @author wangsong
|
||||
*/
|
||||
public static String parse(Date time, String pattern) {
|
||||
if (time == null) {
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat(pattern);
|
||||
return format.format(time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定天的24小时(0-23) | yyyy-MM-dd HH 格式
|
||||
*
|
||||
* @param t 开始月
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getDay24Hour(LocalDateTime t) {
|
||||
if (t == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<String> times = new ArrayList<>();
|
||||
String time = parse(t, YYYY_MM_DD);
|
||||
int hourNum = 24;
|
||||
for (int i = 0; i < hourNum; i++) {
|
||||
if (i < 10) {
|
||||
times.add(time + " 0" + i);
|
||||
} else {
|
||||
times.add(time + " " + i);
|
||||
}
|
||||
}
|
||||
return times;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取每一天的时间 (指定时间 前n月前的第一天 到 n月后的最后一天的所有时间)
|
||||
* <P> 一天一条数据 List<DateDays> </P>
|
||||
*
|
||||
* @param startNum 前n月,当前月开始为 0
|
||||
* @param endNum 后n月,当前月就是为 0
|
||||
* @return java.util.List<com.lplb.common.utils.LocalDateTimeUtil.DateDays>
|
||||
* @author wangsong
|
||||
*/
|
||||
public static List<DateDays> getBetweenDaysUpListByMonth(LocalDateTime time, Integer startNum, Integer endNum) {
|
||||
// 本月第一天 00:00:00
|
||||
LocalDateTime startTime = monthFirstDay(time, startNum);
|
||||
// n月后的最后一天 23:59:59.999
|
||||
LocalDateTime endTime = monthLastDay(time, endNum);
|
||||
return getBetweenDaysUpList(startTime, endTime, BETWEEN_TYPE_ONE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取每一天的时间 (指定开始时间和结束时间)
|
||||
* <p>
|
||||
* 一天一条数据 List<DateDays>
|
||||
* 返回数据包括 开始时间 和 结束时间 的当天数据
|
||||
* </P>
|
||||
*
|
||||
* @param startTime 开始时间 (时分秒已开始时间位为准)
|
||||
* @param endTime 结束时间
|
||||
* @param type 1-包含开始和结束时间 2-包含结束-不包含开始时间 3-包含开始-不包含结束时间 4-不包含开始和结束时间
|
||||
* @return java.util.List<com.lplb.common.utils.LocalDateTimeUtil.DateDays>
|
||||
* @author wangsong
|
||||
*/
|
||||
public static List<DateDays> getBetweenDaysUpList(LocalDateTime startTime, LocalDateTime endTime, Integer type) {
|
||||
List<DateDays> dateDaysList = new ArrayList<>();
|
||||
List<LocalDateTime> betweenList = getBetweenDaysList(startTime, endTime, type);
|
||||
for (LocalDateTime localDateTime : betweenList) {
|
||||
dateDaysList.add(new DateDays(localDateTime, week(localDateTime)));
|
||||
}
|
||||
return dateDaysList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定开始时间到指定结束时间的每一天, 包括开始时间,不包括结束时间,如:2020-5-16到2020-5-18 获得时间为:[2020-5-16,2020-5-17]
|
||||
*
|
||||
* @param startTime
|
||||
* @param endTime
|
||||
* @param type 1-包含开始和结束时间 2-包含结束-不包含开始时间 3-包含开始-不包含结束时间 4-不包含开始和结束时间
|
||||
* @return java.util.List<java.time.LocalDateTime>
|
||||
* @author wangsong
|
||||
* @date 2020/12/24 0024 15:16
|
||||
* @version 1.0.1
|
||||
*/
|
||||
public static List<LocalDateTime> getBetweenDaysList(LocalDateTime startTime, LocalDateTime endTime, Integer type) {
|
||||
// 指定开始时间 00:00:00 // 指定结束时间 00:00:00
|
||||
LocalDateTime oldStartTime = getDayStart(startTime);
|
||||
LocalDateTime oldEndTime = getDayStart(endTime);
|
||||
// 1-包含开始和结束时间(默认) BetweenType
|
||||
// 2-包含结束-不包含开始时间 // 开始时间+1天
|
||||
// 3-包含开始-不包含结束时间 // 结束时间-1天
|
||||
// 4-不包含开始和结束时间 // 开始时间+1天 or 结束时间-1天
|
||||
if (type == BETWEEN_TYPE_TWO) {
|
||||
oldStartTime = plus(oldStartTime, 1, ChronoUnit.DAYS);
|
||||
} else if (type == BETWEEN_TYPE_THREE) {
|
||||
oldEndTime = subtract(endTime, 1, ChronoUnit.DAYS);
|
||||
} else if (type == BETWEEN_TYPE_FOUR) {
|
||||
oldStartTime = plus(oldStartTime, 1, ChronoUnit.DAYS);
|
||||
oldEndTime = subtract(endTime, 1, ChronoUnit.DAYS);
|
||||
}
|
||||
// 返回数据
|
||||
List<LocalDateTime> everyDays = new ArrayList<>();
|
||||
// 第一天数据
|
||||
everyDays.add(oldStartTime);
|
||||
while (true) {
|
||||
// 获取之后的每一天时间
|
||||
LocalDateTime nextDay = plus(everyDays.get(everyDays.size() - 1), 1, ChronoUnit.DAYS);
|
||||
// 大于最后一天-跳出循环
|
||||
if (isAfter(nextDay, oldEndTime)) {
|
||||
break;
|
||||
}
|
||||
everyDays.add(nextDay);
|
||||
}
|
||||
return everyDays;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取月 (返回每一个月的字串, yyyy-MM 格式)
|
||||
* <p> 包含结束月,不包含开始月 </>
|
||||
*
|
||||
* @param startTime 开始月
|
||||
* @param endTime 结束月
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getBetweenMonthsList(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
List<String> times = new ArrayList<>();
|
||||
if (startTime != null && endTime != null) {
|
||||
// 获取开始月的第一天
|
||||
endTime = monthFirstDay(endTime, 0);
|
||||
times.add(parse(startTime, YYYY_MM));
|
||||
while (isBefore(startTime, endTime)) {
|
||||
startTime = plus(startTime, 1, ChronoUnit.MONTHS);
|
||||
times.add(parse(startTime, YYYY_MM));
|
||||
}
|
||||
}
|
||||
return times;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取日期端的数据保存对象
|
||||
*
|
||||
* @author ws
|
||||
* @mail 1720696548@qq.com
|
||||
* @date 2020/5/7 0007 9:41
|
||||
*/
|
||||
public static class DateDays {
|
||||
// 当天时间- 年月日/00:00:00
|
||||
private LocalDateTime dayTime;
|
||||
// 当天是周几
|
||||
private int week;
|
||||
|
||||
public DateDays(LocalDateTime dayTime, int week) {
|
||||
this.dayTime = dayTime;
|
||||
this.week = week;
|
||||
}
|
||||
|
||||
public LocalDateTime getDayTime() {
|
||||
return dayTime;
|
||||
}
|
||||
|
||||
public void setDayTime(LocalDateTime dayTime) {
|
||||
this.dayTime = dayTime;
|
||||
}
|
||||
|
||||
public int getWeek() {
|
||||
return week;
|
||||
}
|
||||
|
||||
public void setWeek(int week) {
|
||||
this.week = week;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个日期之间的天数
|
||||
*
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 从开始日期到结束日期(包括这两个日期)之间的总天数
|
||||
*/
|
||||
public static long betweenDays(LocalDate startDate, LocalDate endDate) {
|
||||
List<LocalDate> dates = new ArrayList<>();
|
||||
|
||||
while (!startDate.isEqual(endDate)) {
|
||||
dates.add(startDate);
|
||||
startDate = startDate.plusDays(1);
|
||||
}
|
||||
|
||||
dates.add(endDate);
|
||||
|
||||
long totalDays = dates.size();
|
||||
|
||||
return totalDays;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个日期之间的工作日数,移除了星期六和星期日
|
||||
*
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 两个日期之间的工作日数
|
||||
*/
|
||||
public static long removeWeekends(LocalDate startDate, LocalDate endDate) {
|
||||
List<LocalDate> dates = new ArrayList<>();
|
||||
|
||||
while (!startDate.isEqual(endDate)) {
|
||||
dates.add(startDate);
|
||||
startDate = startDate.plusDays(1);
|
||||
}
|
||||
|
||||
dates.add(endDate);
|
||||
|
||||
long totalDays = dates.size();
|
||||
long weekends = 0;
|
||||
|
||||
for (LocalDate date : dates) {
|
||||
if (date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY) {
|
||||
weekends++;
|
||||
}
|
||||
}
|
||||
|
||||
return totalDays - weekends;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
LocalDate startDate = LocalDate.of(2024, 8, 22);
|
||||
LocalDate endDate = LocalDate.of(2024, 8, 26);
|
||||
|
||||
long daysBetween = betweenDays(startDate, endDate);
|
||||
System.out.println("Total days between: " + daysBetween);
|
||||
|
||||
long weekendsRemoved = removeWeekends(startDate, endDate);
|
||||
System.out.println("Days without weekends: " + weekendsRemoved);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package ai.remi.comm.util.file;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc FileTypeUtils
|
||||
*/
|
||||
public class FileTypeUtils {
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: startdis.txt, 返回: txt
|
||||
*
|
||||
* @param file 文件名
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType(File file) {
|
||||
if (null == file) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return getFileType(file.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: startdis.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名的后缀
|
||||
*
|
||||
* @param file 表单文件
|
||||
* @return 后缀名
|
||||
*/
|
||||
public static final String getExtension(MultipartFile file) {
|
||||
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
|
||||
if (StringUtils.isEmpty(extension)) {
|
||||
extension = MediaType.getExtension(Objects.requireNonNull(file.getContentType()));
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
package ai.remi.comm.util.file;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc FileUtils
|
||||
*/
|
||||
public class FileUtils {
|
||||
/**
|
||||
* 字符常量:斜杠 {@code '/'}
|
||||
*/
|
||||
public static final char SLASH = '/';
|
||||
|
||||
/**
|
||||
* 字符常量:反斜杠 {@code '\\'}
|
||||
*/
|
||||
public static final char BACKSLASH = '\\';
|
||||
|
||||
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 {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//路径校验不存在就创建对应的目录
|
||||
public static void filePath(String path) {
|
||||
File file = new File(path);
|
||||
// 判断路径是否存在,不存在创建
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @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(MediaType.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 filePath 文件
|
||||
* @return 文件名
|
||||
*/
|
||||
public static String getName(String filePath) {
|
||||
if (null == filePath) {
|
||||
return null;
|
||||
}
|
||||
int len = filePath.length();
|
||||
if (0 == len) {
|
||||
return filePath;
|
||||
}
|
||||
if (isFileSeparator(filePath.charAt(len - 1))) {
|
||||
// 以分隔符结尾的去掉结尾分隔符
|
||||
len--;
|
||||
}
|
||||
|
||||
int begin = 0;
|
||||
char c;
|
||||
for (int i = len - 1; i > -1; i--) {
|
||||
c = filePath.charAt(i);
|
||||
if (isFileSeparator(c)) {
|
||||
// 查找最后一个路径分隔符(/或者\)
|
||||
begin = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return filePath.substring(begin, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为Windows或者Linux(Unix)文件分隔符<br>
|
||||
* Windows平台下分隔符为\,Linux(Unix)为/
|
||||
*
|
||||
* @param c 字符
|
||||
* @return 是否为Windows或者Linux(Unix)文件分隔符
|
||||
*/
|
||||
public static boolean isFileSeparator(char c) {
|
||||
return SLASH == c || BACKSLASH == c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件名重新编码
|
||||
*
|
||||
* @param response 响应对象
|
||||
* @param realFileName 真实文件名
|
||||
* @return
|
||||
*/
|
||||
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.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");
|
||||
}
|
||||
|
||||
public static String formatSize(long fileS) {
|
||||
DecimalFormat df = new DecimalFormat("#.00");
|
||||
String fileSizeString = "";
|
||||
String wrongSize = "0B";
|
||||
if (fileS == 0) {
|
||||
return wrongSize;
|
||||
}
|
||||
if (fileS < 1024) {
|
||||
fileSizeString = df.format((double) fileS) + "B";
|
||||
} else if (fileS < 1048576) {
|
||||
fileSizeString = df.format((double) fileS / 1024) + "KB";
|
||||
} else if (fileS < 1073741824) {
|
||||
fileSizeString = df.format((double) fileS / 1048576) + "MB";
|
||||
} else {
|
||||
fileSizeString = df.format((double) fileS / 1073741824) + "GB";
|
||||
}
|
||||
return fileSizeString;
|
||||
}
|
||||
|
||||
public static BigDecimal formatKB(Long fileSize) {
|
||||
DecimalFormat df = new DecimalFormat("#.000");
|
||||
if (fileSize == 0) {
|
||||
return new BigDecimal(0.000);
|
||||
}
|
||||
return new BigDecimal(fileSize).divide(new BigDecimal(1024)).setScale(3, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param url 指定文件的位置
|
||||
* @param content 异常输出的内容
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
public static void writeFile(String url, String content) throws Exception {
|
||||
File file = new File(url);
|
||||
FileWriter fw = null;
|
||||
|
||||
//文件不存在创建新文件
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
} else {
|
||||
deleteFile(url);
|
||||
file.createNewFile();
|
||||
}
|
||||
String writeDate = content;
|
||||
try {
|
||||
fw = new FileWriter(file, true);
|
||||
fw.write(writeDate + "\r\n");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (fw != null) {
|
||||
fw.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package ai.remi.comm.util.file;
|
||||
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc ImageUtils
|
||||
*/
|
||||
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 {
|
||||
// 网络地址
|
||||
URL urlObj = new URL(url);
|
||||
URLConnection urlConnection = urlObj.openConnection();
|
||||
urlConnection.setConnectTimeout(30 * 1000);
|
||||
urlConnection.setReadTimeout(60 * 1000);
|
||||
urlConnection.setDoInput(true);
|
||||
in = urlConnection.getInputStream();
|
||||
return IOUtils.toByteArray(in);
|
||||
} catch (Exception e) {
|
||||
log.error("访问文件异常 {}", e);
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package ai.remi.comm.util.file;
|
||||
|
||||
/**
|
||||
* 媒体类型工具类
|
||||
*
|
||||
* @author startdis
|
||||
*/
|
||||
public class MediaType {
|
||||
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,148 @@
|
||||
package ai.remi.comm.util.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.apache.commons.net.ftp.FTPReply;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc FtpUtils
|
||||
*/
|
||||
public class FtpUtils {
|
||||
|
||||
/**
|
||||
* Description: 向FTP服务器上传文件
|
||||
*
|
||||
* @param host FTP服务器hostname
|
||||
* @param port FTP服务器端口
|
||||
* @param username FTP登录账号
|
||||
* @param password FTP登录密码
|
||||
* @param basePath FTP服务器基础目录
|
||||
* @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
|
||||
* @param filename 上传到FTP服务器上的文件名
|
||||
* @param input 输入流
|
||||
* @return 成功返回true,否则返回false
|
||||
*/
|
||||
public static boolean uploadFile(String host, int port, String username, String password, String basePath,
|
||||
String filePath, String filename, InputStream input) {
|
||||
boolean result = false;
|
||||
FTPClient ftp = new FTPClient();
|
||||
try {
|
||||
int reply;
|
||||
ftp.connect(host, port);// 连接FTP服务器
|
||||
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
|
||||
ftp.login(username, password);// 登录
|
||||
reply = ftp.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
ftp.disconnect();
|
||||
return result;
|
||||
}
|
||||
// 切换到上传目录
|
||||
if (!ftp.changeWorkingDirectory(basePath + filePath)) {
|
||||
// 如果目录不存在创建目录
|
||||
String[] dirs = filePath.split("/");
|
||||
String tempPath = basePath;
|
||||
for (String dir : dirs) {
|
||||
if (null == dir || "".equals(dir)) {
|
||||
continue;
|
||||
}
|
||||
tempPath += "/" + dir;
|
||||
if (!ftp.changeWorkingDirectory(tempPath)) {
|
||||
if (!ftp.makeDirectory(tempPath)) {
|
||||
return result;
|
||||
} else {
|
||||
ftp.changeWorkingDirectory(tempPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置上传文件的类型为二进制类型
|
||||
ftp.setFileType(FTP.BINARY_FILE_TYPE);
|
||||
// 上传文件
|
||||
if (!ftp.storeFile(filename, input)) {
|
||||
return result;
|
||||
}
|
||||
input.close();
|
||||
ftp.logout();
|
||||
result = true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (ftp.isConnected()) {
|
||||
try {
|
||||
ftp.disconnect();
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 从FTP服务器下载文件
|
||||
*
|
||||
* @param host FTP服务器hostname
|
||||
* @param port FTP服务器端口
|
||||
* @param username FTP登录账号
|
||||
* @param password FTP登录密码
|
||||
* @param remotePath FTP服务器上的相对路径
|
||||
* @param fileName 要下载的文件名
|
||||
* @param localPath 下载后保存到本地的路径
|
||||
* @return
|
||||
*/
|
||||
public static boolean downloadFile(String host, int port, String username, String password, String remotePath,
|
||||
String fileName, String localPath) {
|
||||
boolean result = false;
|
||||
FTPClient ftp = new FTPClient();
|
||||
try {
|
||||
int reply;
|
||||
ftp.connect(host, port);
|
||||
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
|
||||
ftp.login(username, password);// 登录
|
||||
reply = ftp.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
ftp.disconnect();
|
||||
return result;
|
||||
}
|
||||
ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
|
||||
FTPFile[] fs = ftp.listFiles();
|
||||
for (FTPFile ff : fs) {
|
||||
if (ff.getName().equals(fileName)) {
|
||||
File localFile = new File(localPath + "/" + ff.getName());
|
||||
|
||||
OutputStream is = new FileOutputStream(localFile);
|
||||
ftp.retrieveFile(ff.getName(), is);
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
|
||||
ftp.logout();
|
||||
result = true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (ftp.isConnected()) {
|
||||
try {
|
||||
ftp.disconnect();
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(new File("D:\\temp\\image\\gaigeming.jpg"));
|
||||
boolean flag = uploadFile("192.168.25.133", 21, "ftpuser", "ftpuser", "/app/startdis/excal", "/2022/07/11",
|
||||
"gaigeming.jpg", in);
|
||||
System.out.println(flag);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package ai.remi.comm.util.hash;
|
||||
|
||||
import cn.hutool.core.lang.hash.MurmurHash;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc HashUtils
|
||||
*/
|
||||
public class HashUtils {
|
||||
private static char[] CHARS = new char[]{
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
|
||||
};
|
||||
private static int SIZE = CHARS.length;
|
||||
|
||||
private static String convertDecToBase62(long num) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (num > 0) {
|
||||
int i = (int) (num % SIZE);
|
||||
sb.append(CHARS[i]);
|
||||
num /= SIZE;
|
||||
}
|
||||
return sb.reverse().toString();
|
||||
}
|
||||
|
||||
public static String hashToBase62(String str) {
|
||||
int i = MurmurHash.hash32(str);
|
||||
long num = i < 0 ? Integer.MAX_VALUE - (long) i : i;
|
||||
return convertDecToBase62(num);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package ai.remi.comm.util.html;
|
||||
|
||||
import ai.remi.comm.util.string.StringUtils;
|
||||
|
||||
/**
|
||||
* 转义和反转义工具类
|
||||
*/
|
||||
public class EscapeUtils {
|
||||
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 = EscapeUtils.escape(html);
|
||||
// String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
|
||||
// String html = "<123";
|
||||
// String html = "123>";
|
||||
System.out.println("clean: " + EscapeUtils.clean(html));
|
||||
System.out.println("escape: " + escape);
|
||||
System.out.println("unescape: " + EscapeUtils.unescape(escape));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
package ai.remi.comm.util.html;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* HTML过滤器,用于去除XSS漏洞隐患。
|
||||
*/
|
||||
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,98 @@
|
||||
package ai.remi.comm.util.http;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* cookie操作工具
|
||||
*/
|
||||
public class CookieUtils {
|
||||
/**
|
||||
* 按名称获取cookie
|
||||
*
|
||||
* @param request
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static Cookie getCookie(String name, HttpServletRequest request) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies == null || !StringUtils.hasLength(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Cookie cookie : cookies) {
|
||||
if (name.equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按名称获取cookie中的值
|
||||
*
|
||||
* @param request
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static String getCookieValue(String name, HttpServletRequest request) {
|
||||
Cookie cookie = getCookie(name, request);
|
||||
return cookie == null ? null : cookie.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加cookie
|
||||
*
|
||||
* @param name
|
||||
* @param value
|
||||
* @param path
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public static void addCookie(String name, String value, String path, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
if (path != null) {
|
||||
cookie.setPath(path);
|
||||
}
|
||||
if ("https".equals(request.getScheme())) {
|
||||
cookie.setSecure(true);
|
||||
}
|
||||
cookie.setHttpOnly(true);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新cookie中的值
|
||||
*
|
||||
* @param name
|
||||
* @param value
|
||||
* @param request
|
||||
*/
|
||||
public static void updateCookie(String name, String value, HttpServletRequest request) {
|
||||
Cookie cookie = CookieUtils.getCookie(name, request);
|
||||
cookie.setValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除cookie
|
||||
*
|
||||
* @param name
|
||||
* @param path
|
||||
* @param response
|
||||
*/
|
||||
public static void removeCookie(String name, String path, HttpServletResponse response) {
|
||||
|
||||
Cookie cookie = new Cookie(name, null);
|
||||
|
||||
if (path != null) {
|
||||
cookie.setPath(path);
|
||||
}
|
||||
cookie.setMaxAge(-1000);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package ai.remi.comm.util.http;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc HttpClientUtils
|
||||
*/
|
||||
public class HttpClientUtils {
|
||||
|
||||
public static String doGet(String url, Map<String, String> param) {
|
||||
|
||||
// 创建Httpclient对象
|
||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||
|
||||
String resultString = "";
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
// 创建uri
|
||||
URIBuilder builder = new URIBuilder(url);
|
||||
if (param != null) {
|
||||
for (String key : param.keySet()) {
|
||||
builder.addParameter(key, param.get(key));
|
||||
}
|
||||
}
|
||||
URI uri = builder.build();
|
||||
|
||||
// 创建http GET请求
|
||||
HttpGet httpGet = new HttpGet(uri);
|
||||
|
||||
// 执行请求
|
||||
response = httpclient.execute(httpGet);
|
||||
// 判断返回状态是否为200
|
||||
if (response.getStatusLine().getStatusCode() == 200) {
|
||||
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (response != null) {
|
||||
response.close();
|
||||
}
|
||||
httpclient.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return resultString;
|
||||
}
|
||||
|
||||
public static String doGet(String url) {
|
||||
return doGet(url, null);
|
||||
}
|
||||
|
||||
public static String doPost(String url, Map<String, String> param) {
|
||||
// 创建Httpclient对象
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse response = null;
|
||||
String resultString = "";
|
||||
try {
|
||||
// 创建Http Post请求
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
// 创建参数列表
|
||||
if (param != null) {
|
||||
List<NameValuePair> paramList = new ArrayList<>();
|
||||
for (String key : param.keySet()) {
|
||||
paramList.add(new BasicNameValuePair(key, param.get(key)));
|
||||
}
|
||||
// 模拟表单
|
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
// 执行http请求
|
||||
response = httpClient.execute(httpPost);
|
||||
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
response.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return resultString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post请求
|
||||
* @param url
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
public static String doPost(String url, Map<String, Object> param, Map<String, Object> headData) {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse response = null;
|
||||
String resultString = "";
|
||||
|
||||
try {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
if (param != null) {
|
||||
List<NameValuePair> formParams = new ArrayList<NameValuePair>();
|
||||
for (String key : param.keySet()) {
|
||||
formParams.add(new BasicNameValuePair(key, String.valueOf(param.get(key))));
|
||||
}
|
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams,"UTF-8");
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
//携带head数据
|
||||
if (headData != null && headData.size() > 0) {
|
||||
for (String key : headData.keySet()) {
|
||||
httpPost.setHeader(key, (String) headData.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
//执行post请求
|
||||
response = httpClient.execute(httpPost);
|
||||
|
||||
if (response!=null && response.getStatusLine().getStatusCode() == 200) {
|
||||
resultString = EntityUtils.toString(response.getEntity(),"UTF-8");
|
||||
}
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClientProtocolException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
try {
|
||||
if (response != null) {
|
||||
response.close();
|
||||
}
|
||||
httpClient.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return resultString;
|
||||
}
|
||||
|
||||
public static String doPost(String url) {
|
||||
return doPost(url, null);
|
||||
}
|
||||
|
||||
public static String doPostJson(String url, String json) {
|
||||
// 创建Httpclient对象
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse response = null;
|
||||
String resultString = "";
|
||||
try {
|
||||
// 创建Http Post请求
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
// 创建请求内容
|
||||
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
|
||||
httpPost.setEntity(entity);
|
||||
// 执行http请求
|
||||
response = httpClient.execute(httpPost);
|
||||
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
response.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return resultString;
|
||||
}
|
||||
|
||||
public static String doPostJson(String url, String json, Map<String, String> headData) {
|
||||
// 创建Httpclient对象
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse response = null;
|
||||
String resultString = "";
|
||||
try {
|
||||
// 创建Http Post请求
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
// 创建请求内容
|
||||
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
|
||||
httpPost.setEntity(entity);
|
||||
//携带head数据
|
||||
if (headData != null && headData.size() > 0) {
|
||||
for (String key : headData.keySet()) {
|
||||
httpPost.setHeader(key, (String) headData.get(key));
|
||||
}
|
||||
}
|
||||
// 执行http请求
|
||||
response = httpClient.execute(httpPost);
|
||||
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
response.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return resultString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
package ai.remi.comm.util.http;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.exceptions.UtilException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import ai.remi.comm.core.constant.Constants;
|
||||
import ai.remi.comm.core.constant.TokenConstants;
|
||||
import ai.remi.comm.core.result.ResultBean;
|
||||
import ai.remi.comm.util.string.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
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.io.Writer;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 客户端工具类
|
||||
*/
|
||||
@Slf4j
|
||||
public class ServletUtils {
|
||||
/**
|
||||
* 获取请求token
|
||||
*/
|
||||
public static String getToken(ServerHttpRequest request) {
|
||||
String token = request.getHeaders().getFirst(TokenConstants.SUPPLY_AUTHORIZATION);
|
||||
if (StrUtil.isBlank(token)) {
|
||||
token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
|
||||
}
|
||||
return getToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求token
|
||||
*/
|
||||
public static String getToken(String token) {
|
||||
if (StrUtil.isNotEmpty(token) && StrUtil.startWith(token, TokenConstants.PREFIX)) {
|
||||
token = StringUtils.replaceFirst(token, TokenConstants.PREFIX, StrUtil.EMPTY);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 鉴权失败处理
|
||||
*/
|
||||
public static Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
|
||||
log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
|
||||
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加请求头
|
||||
*/
|
||||
public static void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
String valueStr = value.toString();
|
||||
String valueEncode = ServletUtils.urlEncode(valueStr);
|
||||
mutate.header(name, valueEncode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除请求头
|
||||
*/
|
||||
public static void removeHeader(ServerHttpRequest.Builder mutate, String name) {
|
||||
mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数Map
|
||||
*/
|
||||
public static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
|
||||
parameterMap.forEach((key, values) -> {
|
||||
for (String value : values) {
|
||||
parameters.add(key, value);
|
||||
}
|
||||
});
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @param request 请求对象{@link ServletRequest}
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String[]> getParams(ServletRequest request) {
|
||||
final Map<String, String[]> map = request.getParameterMap();
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @param request 请求对象{@link ServletRequest}
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String> getParamMap(ServletRequest request) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
|
||||
params.put(entry.getKey(), ArrayUtil.join(entry.getValue(), StrUtil.COMMA));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取request
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
try {
|
||||
return getRequestAttributes().getRequest();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取response
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
try {
|
||||
return getRequestAttributes().getResponse();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
public static ServletRequestAttributes getRequestAttributes() {
|
||||
try {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes) attributes;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getHeader(HttpServletRequest request, String name) {
|
||||
String value = request.getHeader(name);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return urlDecode(value);
|
||||
}
|
||||
|
||||
public static Map<String, String> getHeaders(HttpServletRequest request) {
|
||||
Map<String, String> map = new LinkedCaseInsensitiveMap<>();
|
||||
Enumeration<String> enumeration = request.getHeaderNames();
|
||||
if (enumeration != null) {
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String key = enumeration.nextElement();
|
||||
String value = request.getHeader(key);
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串渲染到客户端
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置webflux模型响应
|
||||
*
|
||||
* @param response ServerHttpResponse
|
||||
* @param value 响应内容
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value) {
|
||||
return webFluxResponseWriter(response, HttpStatus.OK, value, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置webflux模型响应
|
||||
*
|
||||
* @param response ServerHttpResponse
|
||||
* @param code 响应状态码
|
||||
* @param value 响应内容
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code) {
|
||||
return webFluxResponseWriter(response, HttpStatus.OK, value, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置webflux模型响应
|
||||
*
|
||||
* @param response ServerHttpResponse
|
||||
* @param status http状态码
|
||||
* @param code 响应状态码
|
||||
* @param value 响应内容
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code) {
|
||||
return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置webflux模型响应
|
||||
*
|
||||
* @param response ServerHttpResponse
|
||||
* @param contentType content-type
|
||||
* @param status http状态码
|
||||
* @param code 响应状态码
|
||||
* @param value 响应内容
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) {
|
||||
response.setStatusCode(status);
|
||||
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
|
||||
ResultBean<?> result = ResultBean.error(String.valueOf(code), value.toString());
|
||||
DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
|
||||
return response.writeWith(Mono.just(dataBuffer));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置servlet响应
|
||||
*
|
||||
* @param response HttpServletResponse
|
||||
* @param value 响应内容
|
||||
*/
|
||||
public static void webResponseWriter(HttpServletResponse response, Object value) {
|
||||
webResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置servlet响应
|
||||
*
|
||||
* @param response HttpServletResponse
|
||||
* @param contentType content-type
|
||||
* @param value 响应内容
|
||||
*/
|
||||
public static void webResponseWriter(HttpServletResponse response, String contentType, Object value) {
|
||||
response.setContentType(contentType);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
Writer writer = null;
|
||||
try {
|
||||
writer = response.getWriter();
|
||||
writer.write(JSON.toJSONString(value));
|
||||
writer.flush();
|
||||
} catch (IOException var8) {
|
||||
throw new UtilException(var8);
|
||||
} finally {
|
||||
IoUtil.close(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package ai.remi.comm.util.http;
|
||||
|
||||
/**
|
||||
* url工具类
|
||||
*/
|
||||
public class UrlUtils {
|
||||
|
||||
/**
|
||||
* @return {@code true} if the url is an http: url.
|
||||
*/
|
||||
public static boolean isHttpUrl(String url) {
|
||||
return (null != url)
|
||||
&& (url.length() > 6)
|
||||
&& url.substring(0, 7).equalsIgnoreCase("http://");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if the url is an https: url.
|
||||
*/
|
||||
public static boolean isHttpsUrl(String url) {
|
||||
return (null != url)
|
||||
&& (url.length() > 7)
|
||||
&& url.substring(0, 8).equalsIgnoreCase("https://");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package ai.remi.comm.util.id;
|
||||
|
||||
|
||||
import java.util.Random;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 随机方法工具类
|
||||
*
|
||||
* @author dianjiu
|
||||
*/
|
||||
public class RandomUtils {
|
||||
static Random random = new Random();
|
||||
static String GREATER_THAN = "x must be greater than or equal 0";
|
||||
static String Y_GREATER_THAN_X = "y must be greater than x";
|
||||
private static final int DISPLAYABLE_MIN_INDEX = 33;
|
||||
private static final int DISPLAYABLE_MAX_INDEX = 127 - 1;
|
||||
private static final String ALL_CHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String LETTER_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String NUMBER_CHAR = "0123456789";
|
||||
private static final ThreadLocal<Random> local = new ThreadLocal<Random>() {
|
||||
@Override
|
||||
protected Random initialValue() {
|
||||
return new Random();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 获取定长的随机数,包含大小写、数字
|
||||
*
|
||||
* @param length 随机数长度
|
||||
* @return 返回字符串
|
||||
*/
|
||||
public static String generateString(int length) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(ALL_CHAR.charAt(random.nextInt(ALL_CHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取定长的随机数,包含大小写字母
|
||||
*
|
||||
* @param length 随机数长度
|
||||
* @return 返回字符串
|
||||
*/
|
||||
public static String generateMixString(int length) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(LETTER_CHAR.charAt(random.nextInt(LETTER_CHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取定长的随机数,只包含小写字母
|
||||
*
|
||||
* @param length 随机数长度
|
||||
* @return 返回字符串
|
||||
*/
|
||||
public static String generateLowerString(int length) {
|
||||
return generateMixString(length).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取定长的随机数,只包含大写字母
|
||||
*
|
||||
* @param length 随机数长度
|
||||
* @return 返回字符串
|
||||
*/
|
||||
public static String generateUpperString(int length) {
|
||||
return generateMixString(length).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取定长的随机数,只包含数字
|
||||
*
|
||||
* @param length 随机数长度
|
||||
* @return 返回字符串
|
||||
*/
|
||||
public static String generateNumberString(int length) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(NUMBER_CHAR.charAt(random.nextInt(NUMBER_CHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Random实例
|
||||
*
|
||||
* @return 返回随机值
|
||||
*/
|
||||
public static Random getRandom() {
|
||||
return local.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生[0, x]区间的随机数
|
||||
*
|
||||
* @param x > 0
|
||||
* @return
|
||||
*/
|
||||
public static int intSeed(int x) {
|
||||
return intSeed(0, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生[x, y]区间的随机数
|
||||
*
|
||||
* @param x >= 0
|
||||
* @param y > x
|
||||
* @return 返回int数
|
||||
*/
|
||||
public static int intSeed(int x, int y) {
|
||||
if (x < 0) { // ensure x >= 0
|
||||
throw new IllegalArgumentException(
|
||||
Y_GREATER_THAN_X);
|
||||
}
|
||||
if (x >= y) { // ensure y > x
|
||||
throw new IllegalArgumentException("");
|
||||
}
|
||||
return x + getRandom().nextInt(y - x + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生[0, x]区间的随机数
|
||||
*
|
||||
* @param x> 0
|
||||
* @return 返回长整型
|
||||
*/
|
||||
public static long longSeed(long x) {
|
||||
return longSeed(0, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生[x, y]区间的随机数
|
||||
*
|
||||
* @param x >= 0
|
||||
* @param y > x
|
||||
* @return 返回长整型
|
||||
*/
|
||||
public static long longSeed(long x, long y) {
|
||||
if (x < 0) { // ensure x >= 0
|
||||
throw new IllegalArgumentException(
|
||||
GREATER_THAN);
|
||||
}
|
||||
if (x >= y) { // ensure y > x
|
||||
throw new IllegalArgumentException(Y_GREATER_THAN_X);
|
||||
}
|
||||
return Math.abs(getRandom().nextLong() % (y - x + 1)) + x;
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生[0, x]区间的随机小数
|
||||
*
|
||||
* @param x > 0
|
||||
* @return 返回浮点数
|
||||
*/
|
||||
public static float floatSeed(float x) {
|
||||
return (float) doubleSeed(0., x);
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生[x, y]区间的随机小数
|
||||
*
|
||||
* @param x >= 0
|
||||
* @param y > x
|
||||
* @return 返回浮点数
|
||||
*/
|
||||
public static float floatSeed(float x, float y) {
|
||||
return (float) doubleSeed(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生[0, x]区间的随机小数
|
||||
*
|
||||
* @param x > 0
|
||||
* @return 返回double类型
|
||||
*/
|
||||
public static double doubleSeed(double x) {
|
||||
return doubleSeed(0., x);
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生[x, y]区间的随机小数
|
||||
*
|
||||
* @param x >= 0
|
||||
* @param y > x
|
||||
* @return 返回double类型
|
||||
*/
|
||||
public static double doubleSeed(double x, double y) {
|
||||
if (x < 0) { // ensure x >= 0
|
||||
throw new IllegalArgumentException(
|
||||
"GREATER_THAN");
|
||||
}
|
||||
if (x >= y) { // ensure y > x
|
||||
throw new IllegalArgumentException("Y_GREATER_THAN_X");
|
||||
}
|
||||
BigDecimal dx = new BigDecimal(String.valueOf(x));
|
||||
BigDecimal dy = new BigDecimal(String.valueOf(y));
|
||||
double diff = dy.subtract(dx).doubleValue();
|
||||
dx = dx.add(new BigDecimal(String.valueOf(getRandom().nextDouble()
|
||||
* diff)));
|
||||
return dx.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机产生true或false
|
||||
*
|
||||
* @return 返回布尔类型
|
||||
*/
|
||||
public static boolean boolSeed() {
|
||||
return intSeed(0, 1) == 0 ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机产生有效的可显示的字符
|
||||
*
|
||||
* @param x >= 33
|
||||
* @param y > x & <= 126
|
||||
* @return 返回char
|
||||
*/
|
||||
public static char charSeed(char x, char y) {
|
||||
if (x < DISPLAYABLE_MIN_INDEX) { // ensure x >= 33 ('!')
|
||||
throw new IllegalArgumentException(
|
||||
"x must be greater than or equal '!'");
|
||||
}
|
||||
if (x >= y) { // ensure y > x
|
||||
throw new IllegalArgumentException("Y_GREATER_THAN_X");
|
||||
}
|
||||
if (y > DISPLAYABLE_MAX_INDEX) { // ensure y <= 126 ('~')
|
||||
throw new IllegalArgumentException(
|
||||
"y must be less than or equal '~'");
|
||||
}
|
||||
return (char) (getRandom().nextInt(y - x + 1) + x);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package ai.remi.comm.util.id;
|
||||
|
||||
import ai.remi.comm.util.ip.MacAddressUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class SequenceUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SequenceUtils.class);
|
||||
|
||||
|
||||
public static String OLD_DATETIME = "";
|
||||
/**
|
||||
* 静态属性
|
||||
*/
|
||||
public static int STATIC_SEQUENCE = 0;
|
||||
|
||||
/**
|
||||
* 默认节点
|
||||
*/
|
||||
public static String DEFAULT_NODE_NUMBER = "01";
|
||||
|
||||
public static String STATIC_NODE_NUMBER = "--";
|
||||
|
||||
/**
|
||||
* 生成20位的流水号
|
||||
*
|
||||
* @return 流水号
|
||||
*/
|
||||
public static synchronized String next() {
|
||||
String currentDateTime = getCurrentSystemDateTime();
|
||||
|
||||
if (null == currentDateTime) {
|
||||
log.error("获取系统日期失败");
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuffer sequenceNumber = new StringBuffer();
|
||||
|
||||
sequenceNumber.append(currentDateTime.substring(0, 8));
|
||||
sequenceNumber.append(getNodeNumber());
|
||||
sequenceNumber.append(currentDateTime.substring(8));
|
||||
sequenceNumber.append(nextSequence());
|
||||
return sequenceNumber.toString();
|
||||
}
|
||||
|
||||
public static final String initNodeNumber(String nodeNumbers) {
|
||||
if (STATIC_NODE_NUMBER.equals("--")) {
|
||||
if (null != nodeNumbers && !nodeNumbers.equals("")) {
|
||||
|
||||
String ipAddressConfigValue = nodeNumbers;
|
||||
log.info("ARE config.node.number : " + ipAddressConfigValue);
|
||||
if (ipAddressConfigValue.indexOf(",") > -1) {
|
||||
|
||||
String hostIpAddress = MacAddressUtils.getAllHostMacAddress();//获得本机IP
|
||||
|
||||
log.info("hostIpAddress : " + hostIpAddress);
|
||||
|
||||
String[] ipAddressValues = ipAddressConfigValue.split(",");
|
||||
for (String ipvalue : ipAddressValues) {
|
||||
String[] ipNode = ipvalue.split("=");
|
||||
if (ipNode != null && ipNode.length > 0 && hostIpAddress.indexOf(ipNode[0]) > -1) {
|
||||
STATIC_NODE_NUMBER = ipNode[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (STATIC_NODE_NUMBER.equals("--")) {
|
||||
log.error("GET MAC BIND NODE ERROR . ");
|
||||
STATIC_NODE_NUMBER = DEFAULT_NODE_NUMBER;
|
||||
}
|
||||
|
||||
} else {
|
||||
STATIC_NODE_NUMBER = nodeNumbers;
|
||||
}
|
||||
log.info("STATIC_NODE_SEQUENCE_NUMBER : " + STATIC_NODE_NUMBER);
|
||||
if (STATIC_NODE_NUMBER.length() != 2) {
|
||||
log.error("系统节点号必须2位");
|
||||
}
|
||||
} else {
|
||||
STATIC_NODE_NUMBER = DEFAULT_NODE_NUMBER;
|
||||
}
|
||||
}
|
||||
return STATIC_NODE_NUMBER;
|
||||
}
|
||||
|
||||
public static final String getNodeNumber() {
|
||||
return STATIC_NODE_NUMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同一时刻只有一个访问
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static final synchronized String nextSequence() {
|
||||
STATIC_SEQUENCE = (STATIC_SEQUENCE + 1) % 10000;
|
||||
return String.format("%04d", STATIC_SEQUENCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统当前日期,格式为yyyyMMddHHmmSS
|
||||
*
|
||||
* @return 当前系统日期
|
||||
*/
|
||||
private static synchronized String getCurrentSystemDateTime() {
|
||||
String currentdatetime = null;
|
||||
synchronized (OLD_DATETIME) {
|
||||
currentdatetime = (new java.text.SimpleDateFormat("yyyyMMddHHmmss")).format(new Date());
|
||||
/**
|
||||
* 判断是否是新的时间,如果是新时间则STATIC_SEQUENCE从0开始计数
|
||||
*/
|
||||
if (!currentdatetime.equals(OLD_DATETIME)) {
|
||||
STATIC_SEQUENCE = 0;
|
||||
OLD_DATETIME = currentdatetime;
|
||||
}
|
||||
}
|
||||
return currentdatetime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package ai.remi.comm.util.id;
|
||||
|
||||
import ai.remi.comm.util.hash.HashUtils;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc SnowflakeIDUtils
|
||||
*/
|
||||
public class SnowflakeUtils {
|
||||
private static final SnowflakeUtils intance = new SnowflakeUtils(1, 1, 1);
|
||||
|
||||
//因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。
|
||||
//下面两个每个5位,加起来就是10位的工作机器id
|
||||
/**
|
||||
* 机器ID 2进制5位 32位减掉1位 31个
|
||||
*/
|
||||
private long workerId;
|
||||
/**
|
||||
* //机房ID 2进制5位 32位减掉1位 31个
|
||||
*/
|
||||
private long datacenterId;
|
||||
/**
|
||||
* 12位的序列号,代表一毫秒内生成的多个id的最新序号 4096 -1 = 4095 个
|
||||
*/
|
||||
private long sequence;
|
||||
/**
|
||||
* 设置一个时间初始值 2^41 - 1 差不多可以用69年
|
||||
*/
|
||||
private long twepoch = 1888888888L;
|
||||
/**
|
||||
* 5位的机器id
|
||||
*/
|
||||
private long workerIdBits = 5L;
|
||||
/**
|
||||
* 5位的机房id
|
||||
*/
|
||||
private long datacenterIdBits = 5L;
|
||||
/**
|
||||
* 每毫秒内产生的id数 2 的 12次方
|
||||
*/
|
||||
private long sequenceBits = 12L;
|
||||
/**
|
||||
* 这个是二进制运算,就是5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
|
||||
*/
|
||||
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
|
||||
/**
|
||||
* 这个是一个意思,就是5 bit最多只能有31个数字,机房id最多只能是32以内
|
||||
*/
|
||||
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
|
||||
|
||||
private long workerIdShift = sequenceBits;
|
||||
private long datacenterIdShift = sequenceBits + workerIdBits;
|
||||
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
|
||||
private long sequenceMask = -1L ^ (-1L << sequenceBits);
|
||||
/**
|
||||
* 记录产生时间毫秒数,判断是否是同1毫秒
|
||||
*/
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
public long getWorkerId() {
|
||||
return workerId;
|
||||
}
|
||||
|
||||
public long getDatacenterId() {
|
||||
return datacenterId;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
public static SnowflakeUtils getInstance() {
|
||||
return intance;
|
||||
}
|
||||
|
||||
public static SnowflakeUtils getInstance(long workerId, long datacenterId) {
|
||||
return new SnowflakeUtils(workerId, datacenterId, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*
|
||||
* @param workerId
|
||||
* @param datacenterId
|
||||
* @param sequence
|
||||
*/
|
||||
public SnowflakeUtils(long workerId, long datacenterId, long sequence) {
|
||||
|
||||
// 检查机房id和机器id是否超过31 不能小于0
|
||||
if (workerId > maxWorkerId || workerId < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
|
||||
}
|
||||
|
||||
if (datacenterId > maxDatacenterId || datacenterId < 0) {
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
|
||||
}
|
||||
this.workerId = workerId;
|
||||
this.datacenterId = datacenterId;
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
public synchronized String nextShortId() {
|
||||
return HashUtils.hashToBase62(this.nextIdStr());
|
||||
}
|
||||
|
||||
public synchronized String nextIdStr() {
|
||||
return Long.toString(this.nextId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个是核心方法,通过调用nextId()方法,让当前这台机器上的snowflake算法程序生成一个全局唯一的id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public synchronized long nextId() {
|
||||
// 这儿就是获取当前时间戳,单位是毫秒
|
||||
long timestamp = timeGen();
|
||||
if (timestamp < lastTimestamp) {
|
||||
|
||||
System.err.printf(
|
||||
"clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
|
||||
throw new RuntimeException(
|
||||
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
|
||||
lastTimestamp - timestamp));
|
||||
}
|
||||
|
||||
// 下面是说假设在同一个毫秒内,又发送了一个请求生成一个id
|
||||
// 这个时候就得把seqence序号给递增1,最多就是4096
|
||||
if (lastTimestamp == timestamp) {
|
||||
|
||||
// 这个意思是说一个毫秒内最多只能有4096个数字,无论你传递多少进来,
|
||||
//这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
|
||||
sequence = (sequence + 1) & sequenceMask;
|
||||
//当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
|
||||
if (sequence == 0) {
|
||||
timestamp = tilNextMillis(lastTimestamp);
|
||||
}
|
||||
|
||||
} else {
|
||||
sequence = 0;
|
||||
}
|
||||
// 这儿记录一下最近一次生成id的时间戳,单位是毫秒
|
||||
lastTimestamp = timestamp;
|
||||
// 这儿就是最核心的二进制位运算操作,生成一个64bit的id
|
||||
// 先将当前时间戳左移,放到41 bit那儿;将机房id左移放到5 bit那儿;将机器id左移放到5 bit那儿;将序号放最后12 bit
|
||||
// 最后拼接起来成一个64 bit的二进制数字,转换成10进制就是个long型
|
||||
return ((timestamp - twepoch) << timestampLeftShift) |
|
||||
(datacenterId << datacenterIdShift) |
|
||||
(workerId << workerIdShift) | sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
|
||||
*
|
||||
* @param lastTimestamp
|
||||
* @return
|
||||
*/
|
||||
private long tilNextMillis(long lastTimestamp) {
|
||||
|
||||
long timestamp = timeGen();
|
||||
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = timeGen();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
//获取当前时间戳
|
||||
private long timeGen() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* main 测试类
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
//SnowflakeIDUtils snowflakeIDUtils = new SnowflakeIDUtils(1, 1, 1);
|
||||
//SnowflakeIDUtils instance = getInstance(1, 1);
|
||||
System.out.println(getInstance().nextId());
|
||||
System.out.println(getInstance(2,1).nextId());
|
||||
//for (int i = 0; i < 9; i++) {
|
||||
// System.out.println(getInstance().nextId());
|
||||
// //System.out.println(instance.nextShortId());
|
||||
// //System.out.println(snowflakeIDUtils.nextId());
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ai.remi.comm.util.id;
|
||||
|
||||
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
|
||||
|
||||
/**
|
||||
* 链路追踪工具类
|
||||
*/
|
||||
public class TracerUtils {
|
||||
|
||||
/**
|
||||
* 私有化构造方法
|
||||
*/
|
||||
private TracerUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得链路追踪编号,直接返回 SkyWalking 的 TraceId。
|
||||
* 如果不存在的话为空字符串!!!
|
||||
*
|
||||
* @return 链路追踪编号
|
||||
*/
|
||||
public static String getTraceId() {
|
||||
return TraceContext.traceId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package ai.remi.comm.util.id;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc UUIDUtils
|
||||
*/
|
||||
public class UUIDUtils {
|
||||
|
||||
/**
|
||||
* All possible chars for representing a number as a String
|
||||
* copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/NumberUtil.java#L113
|
||||
*/
|
||||
private final static byte[] DIGITS = {
|
||||
'0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b',
|
||||
'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'I', 'J', 'K', 'L',
|
||||
'M', 'N', 'O', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z'
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成不带有-的UUID字符串
|
||||
*
|
||||
* @return 不带有-的UUID字符串
|
||||
*/
|
||||
public static String simpleUuid() {
|
||||
return uuid().replaceAll("-", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成带有-的UUID字符串
|
||||
*
|
||||
* @return 带有-的UUID字符串
|
||||
*/
|
||||
public static String uuid() {
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
long lsb = random.nextLong();
|
||||
long msb = random.nextLong();
|
||||
byte[] buf = new byte[32];
|
||||
formatUnsignedLong(lsb, buf, 20, 12);
|
||||
formatUnsignedLong(lsb >>> 48, buf, 16, 4);
|
||||
formatUnsignedLong(msb, buf, 12, 4);
|
||||
formatUnsignedLong(msb >>> 16, buf, 8, 4);
|
||||
formatUnsignedLong(msb >>> 32, buf, 0, 8);
|
||||
return new String(buf, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L348
|
||||
*/
|
||||
private static void formatUnsignedLong(long val, byte[] buf, int offset, int len) {
|
||||
int charPos = offset + len;
|
||||
int radix = 1 << 4;
|
||||
int mask = radix - 1;
|
||||
do {
|
||||
buf[--charPos] = DIGITS[((int) val) & mask];
|
||||
val >>>= 4;
|
||||
} while (charPos > offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
package ai.remi.comm.util.ip;
|
||||
|
||||
import ai.remi.comm.util.string.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc IpUtils IP地址工具类
|
||||
*/
|
||||
public class IpAddrUtils {
|
||||
/**
|
||||
* 获取客户端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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证IP字符串是否合规
|
||||
*
|
||||
* @param ip ip字符串
|
||||
* @return true标识合规
|
||||
*/
|
||||
public static boolean validIp(String ip) {
|
||||
String pattern = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}";
|
||||
Pattern r = Pattern.compile(pattern);
|
||||
Matcher m = r.matcher(ip);
|
||||
boolean matches = m.matches();
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package ai.remi.comm.util.ip;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.lionsoul.ip2region.xdb.Searcher;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc IpUtils IP归属地工具类
|
||||
*/
|
||||
@Slf4j
|
||||
public class IpInfoUtils {
|
||||
|
||||
private static Searcher searcher;
|
||||
|
||||
static {
|
||||
try (InputStream inputStream = IpInfoUtils.class.getClassLoader().getResourceAsStream("ip2region.xdb")) {
|
||||
if (inputStream == null) {
|
||||
throw new IOException("ip2region.xdb loading failed.");
|
||||
}
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, bytesRead);
|
||||
}
|
||||
searcher = Searcher.newWithBuffer(output.toByteArray());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 ip 对应的信息
|
||||
* @param ip
|
||||
* @return 格式: 中国|0|浙江|杭州|电信
|
||||
*/
|
||||
public static IpInfo getInfo(String ip) {
|
||||
|
||||
IpInfo ipInfo = new IpInfo(ip);
|
||||
|
||||
if (!IpAddrUtils.validIp(ip)) {
|
||||
log.warn("========非法 IP 格式========");
|
||||
return ipInfo;
|
||||
}
|
||||
try {
|
||||
convert(searcher.search(ip),ipInfo);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return ipInfo;
|
||||
}
|
||||
|
||||
private static IpInfo convert(String regionStr,IpInfo ipInfo) {
|
||||
if (regionStr == null || regionStr.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
String[] regionSplit = regionStr.split("\\|");
|
||||
if (regionSplit.length != 5) {
|
||||
return null;
|
||||
}
|
||||
ipInfo.setCountry("0".equals(regionSplit[0]) ? null : regionSplit[0]);
|
||||
ipInfo.setRegion("0".equals(regionSplit[1]) ? null : regionSplit[1]);
|
||||
ipInfo.setProvince("0".equals(regionSplit[2]) ? null : regionSplit[2]);
|
||||
ipInfo.setCity("0".equals(regionSplit[3]) ? null : regionSplit[3]);
|
||||
ipInfo.setIsp("0".equals(regionSplit[4]) ? null : regionSplit[4]);
|
||||
return ipInfo;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@Accessors(chain = true)
|
||||
@ToString
|
||||
public static class IpInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 国家
|
||||
*/
|
||||
private String country = "未知";
|
||||
|
||||
/**
|
||||
* 区域
|
||||
*/
|
||||
private String region = "未知";
|
||||
|
||||
/**
|
||||
* 省份
|
||||
*/
|
||||
private String province = "未知";
|
||||
|
||||
/**
|
||||
* 城市
|
||||
*/
|
||||
private String city = "未知";
|
||||
|
||||
/**
|
||||
* 运营商
|
||||
*/
|
||||
private String isp = "未知";
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
public IpInfo(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
IpInfo info = getInfo("14.29.139.251");
|
||||
System.out.println(info.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package ai.remi.comm.util.ip;
|
||||
|
||||
import ai.remi.comm.util.id.SequenceUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MacAddressUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SequenceUtils.class);
|
||||
|
||||
public static String os;
|
||||
|
||||
static {
|
||||
Properties prop = System.getProperties();
|
||||
os = prop.getProperty("os.name");
|
||||
log.info("OS : " + os);
|
||||
}
|
||||
|
||||
public static String getAllHostMacAddress() {
|
||||
String hostIpAddress = "";
|
||||
try {
|
||||
Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
|
||||
InetAddress inetAddress = null;
|
||||
while (netInterfaces.hasMoreElements()) {
|
||||
NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();
|
||||
if (ni.getInetAddresses().hasMoreElements()) {
|
||||
inetAddress = (InetAddress) ni.getInetAddresses().nextElement();
|
||||
if (!inetAddress.isLoopbackAddress()) {
|
||||
hostIpAddress += getMac(inetAddress) + ",";
|
||||
log.info("host MAC : " + getMac(inetAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return hostIpAddress;
|
||||
}
|
||||
|
||||
|
||||
public static String getMac(InetAddress ia) throws SocketException {
|
||||
//获取网卡,获取地址
|
||||
byte[] mac = NetworkInterface.getByInetAddress(ia).getHardwareAddress();
|
||||
//LoggerFactory.getLogger(MacAddress.class).info("mac数组长度:"+mac.length);
|
||||
|
||||
|
||||
StringBuffer sb = new StringBuffer("");
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
if (i != 0) {
|
||||
if (os.startsWith("win") || os.startsWith("Win")) {
|
||||
sb.append("-");//win
|
||||
} else {
|
||||
sb.append(":");//linux
|
||||
}
|
||||
}
|
||||
//字节转换为整数
|
||||
int temp = mac[i] & 0xff;
|
||||
String str = Integer.toHexString(temp);
|
||||
if (str.length() == 1) {
|
||||
sb.append("0" + str);
|
||||
} else {
|
||||
sb.append(str);
|
||||
}
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ai.remi.comm.util.merge;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc MergeUtils
|
||||
*/
|
||||
public class MergeUtils {
|
||||
/**
|
||||
*
|
||||
* @param listA 需要合并的A对象集合
|
||||
* @param listB 需要合并的B对象集合
|
||||
* @param compare 对比A和B对象,如果true 就执行 merge方法
|
||||
* @param merge 合并A和B对象,需要用户自己实现
|
||||
* @param <R> 返回值的对象
|
||||
* @param <T> 被合并的对象
|
||||
*/
|
||||
public static <R,T> void merge(List<R> listA, List<T> listB, Merged.Compare<R,T> compare, Merged<R, T> merge){
|
||||
merge.apply(listA,listB,compare);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package ai.remi.comm.util.merge;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <R> 返回值的对象类型
|
||||
* @param <T> 需要被合并的类型
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Merged<R,T> {
|
||||
|
||||
void merge(R r, T t);
|
||||
|
||||
default void apply(List<R> listA, List<T> listB,Compare<R,T> compare){
|
||||
listA.forEach(a-> listB.forEach(b -> {
|
||||
if(compare.compare(a,b)){
|
||||
merge(a,b);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@FunctionalInterface
|
||||
interface Compare<R,T>{
|
||||
boolean compare(R r,T t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
package ai.remi.comm.util.money;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 金钱处理工具类
|
||||
*
|
||||
* @author dianjiu
|
||||
*/
|
||||
public class MoneyUtils {
|
||||
/**
|
||||
* 汉语中数字大写
|
||||
*/
|
||||
private static final String[] CN_UPPER_NUMBER = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
|
||||
|
||||
/**
|
||||
* 汉语中货币单位大写
|
||||
*/
|
||||
private static final String[] CN_UPPER_MONETRAY_UNIT = {"分", "角", "元", "拾", "佰", "仟", "万", "拾",
|
||||
"佰", "仟", "亿", "拾", "佰", "仟", "兆", "拾",
|
||||
"佰", "仟"};
|
||||
/**
|
||||
* 特殊字符:整
|
||||
*/
|
||||
private static final String CN_FULL = "";
|
||||
|
||||
/**
|
||||
* 特殊字符:负
|
||||
*/
|
||||
private static final String CN_NEGATIVE = "负";
|
||||
/**
|
||||
* 零元整
|
||||
*/
|
||||
private static final String CN_ZEOR_FULL = "零元整";
|
||||
|
||||
/**
|
||||
* 金额的精度,默认值为2
|
||||
*/
|
||||
private static final int MONEY_PRECISION = 2;
|
||||
|
||||
/**
|
||||
* 人民币转换为大写,格式为:x万x千x百x十x元x角x分
|
||||
*
|
||||
* @param numberOfMoney 传入的金额
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String number2CNMontray(String numberOfMoney) {
|
||||
return number2CNMontray(new BigDecimal(numberOfMoney));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 人民币转换为大写,格式为:x万x千x百x十x元x角x分
|
||||
*
|
||||
* @param numberOfMoney 传入的金额
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String number2CNMontray(BigDecimal numberOfMoney) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int signum = numberOfMoney.signum();
|
||||
if (signum == 0) {
|
||||
return CN_ZEOR_FULL;
|
||||
}
|
||||
long number = numberOfMoney.movePointRight(MONEY_PRECISION).setScale(0, 4).abs().longValue();
|
||||
long scale = number % 100;
|
||||
int numUnit = 0;
|
||||
int numIndex = 0;
|
||||
boolean getZero = false;
|
||||
// 判断最后两位数,一共有四中情况:00 = 0, 01 = 1, 10, 11
|
||||
if (!(scale > 0)) {
|
||||
numIndex = 2;
|
||||
number = number / 100;
|
||||
getZero = true;
|
||||
}
|
||||
if ((scale > 0) && (!(scale % 10 > 0))) {
|
||||
numIndex = 1;
|
||||
number = number / 10;
|
||||
getZero = true;
|
||||
}
|
||||
int zeroSize = 0;
|
||||
while (true) {
|
||||
if (number <= 0) {
|
||||
break;
|
||||
}
|
||||
// 每次获取到最后一个数
|
||||
numUnit = (int) (number % 10);
|
||||
if (numUnit > 0) {
|
||||
if ((numIndex == 9) && (zeroSize >= 3)) {
|
||||
sb.insert(0, CN_UPPER_MONETRAY_UNIT[6]);
|
||||
}
|
||||
if ((numIndex == 13) && (zeroSize >= 3)) {
|
||||
sb.insert(0, CN_UPPER_MONETRAY_UNIT[10]);
|
||||
}
|
||||
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
|
||||
sb.insert(0, CN_UPPER_NUMBER[numUnit]);
|
||||
getZero = false;
|
||||
zeroSize = 0;
|
||||
} else {
|
||||
++zeroSize;
|
||||
if (!(getZero)) {
|
||||
sb.insert(0, CN_UPPER_NUMBER[numUnit]);
|
||||
}
|
||||
if (numIndex == 2) {
|
||||
if (number > 0) {
|
||||
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
|
||||
}
|
||||
} else if (((numIndex - 2) % 4 == 0) && (number % 1000 > 0)) {
|
||||
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
|
||||
}
|
||||
getZero = true;
|
||||
}
|
||||
// 让number每次都去掉最后一个数
|
||||
number = number / 10;
|
||||
++numIndex;
|
||||
}
|
||||
// 如果signum == -1,则说明输入的数字为负数,就在最前面追加特殊字符:负
|
||||
if (signum == -1) {
|
||||
sb.insert(0, CN_NEGATIVE);
|
||||
}
|
||||
// 输入的数字小数点后两位为"00"的情况,则要在最后追加特殊字符:整
|
||||
if (!(scale > 0)) {
|
||||
sb.append(CN_FULL);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将人民币转换为会计格式金额(xxxx,xxxx,xxxx.xx),保留两位小数
|
||||
*
|
||||
* @param money 待转换的金额
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String accountantMoney(BigDecimal money) {
|
||||
return accountantMoney(money, 2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化金额,显示为xxx万元,xxx百万,xxx亿
|
||||
*
|
||||
* @param money 待处理的金额
|
||||
* @param scale 小数点后保留的位数
|
||||
* @param divisor 格式化值(10:十元、100:百元,1000千元,10000万元......)
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String getFormatMoney(BigDecimal money, int scale, double divisor) {
|
||||
return formatMoney(money, scale, divisor) + getCellFormat(divisor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会计格式的人民币(格式为:xxxx,xxxx,xxxx.xx)
|
||||
*
|
||||
* @param money 待处理的金额
|
||||
* @param scale 小数点后保留的位数
|
||||
* @param divisor 格式化值(10:十元、100:百元,1000千元,10000万元......)
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String getAccountantMoney(BigDecimal money, int scale, double divisor) {
|
||||
return accountantMoney(money, scale, divisor) + getCellFormat(divisor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将人民币转换为会计格式金额(xxxx,xxxx,xxxx.xx)
|
||||
*
|
||||
* @param money 待处理的金额
|
||||
* @param scale 小数点后保留的位数
|
||||
* @param divisor 格式化值
|
||||
* @return 字符串
|
||||
*/
|
||||
private static String accountantMoney(BigDecimal money, int scale, double divisor) {
|
||||
String disposeMoneyStr = formatMoney(money, scale, divisor);
|
||||
int dotPosition = disposeMoneyStr.indexOf('.');
|
||||
String exceptDotMoeny = null;//小数点之前的字符串
|
||||
String dotMeony = null;//小数点之后的字符串
|
||||
if (dotPosition > 0) {
|
||||
exceptDotMoeny = disposeMoneyStr.substring(0, dotPosition);
|
||||
dotMeony = disposeMoneyStr.substring(dotPosition);
|
||||
} else {
|
||||
exceptDotMoeny = disposeMoneyStr;
|
||||
}
|
||||
//负数处理
|
||||
int negativePosition = exceptDotMoeny.indexOf('-');
|
||||
if (negativePosition == 0) {
|
||||
exceptDotMoeny = exceptDotMoeny.substring(1);
|
||||
}
|
||||
StringBuilder reverseExceptDotMoney = new StringBuilder(exceptDotMoeny);
|
||||
reverseExceptDotMoney.reverse();//字符串倒转
|
||||
char[] moneyChar = reverseExceptDotMoney.toString().toCharArray();
|
||||
StringBuilder returnMeony = new StringBuilder();//返回值
|
||||
for (int i = 0; i < moneyChar.length; i++) {
|
||||
if (i != 0 && i % 3 == 0) {
|
||||
returnMeony.append(",");//每隔3位加','
|
||||
}
|
||||
returnMeony.append(moneyChar[i]);
|
||||
}
|
||||
returnMeony.reverse();//字符串倒转
|
||||
if (dotPosition > 0) {
|
||||
returnMeony.append(dotMeony);
|
||||
}
|
||||
if (negativePosition == 0) {
|
||||
return "-" + returnMeony.toString();
|
||||
} else {
|
||||
return returnMeony.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化金额,显示为xxx万元,xxx百万,xxx亿
|
||||
*
|
||||
* @param money 待处理的金额
|
||||
* @param scale 小数点后保留的位数
|
||||
* @param divisor 格式化值
|
||||
* @return 字符串
|
||||
*/
|
||||
private static String formatMoney(BigDecimal money, int scale, double divisor) {
|
||||
if (divisor == 0) {
|
||||
return "0.00";
|
||||
}
|
||||
if (scale < 0) {
|
||||
return "0.00";
|
||||
}
|
||||
BigDecimal divisorBD = BigDecimal.valueOf(divisor);
|
||||
return money.divide(divisorBD, scale, RoundingMode.HALF_UP).toString();
|
||||
}
|
||||
|
||||
private static String getCellFormat(double divisor) {
|
||||
String str = String.valueOf(divisor);
|
||||
int len = str.substring(0, str.indexOf('.')).length();
|
||||
String cell = "";
|
||||
switch (len) {
|
||||
case 1:
|
||||
cell = "元";
|
||||
break;
|
||||
case 2:
|
||||
cell = "十元";
|
||||
break;
|
||||
case 3:
|
||||
cell = "百元";
|
||||
break;
|
||||
case 4:
|
||||
cell = "千元";
|
||||
break;
|
||||
case 5:
|
||||
cell = "万元";
|
||||
break;
|
||||
case 6:
|
||||
cell = "十万元";
|
||||
break;
|
||||
case 7:
|
||||
cell = "百万元";
|
||||
break;
|
||||
case 8:
|
||||
cell = "千万元";
|
||||
break;
|
||||
case 9:
|
||||
cell = "亿元";
|
||||
break;
|
||||
case 10:
|
||||
cell = "十亿元";
|
||||
break;
|
||||
default:
|
||||
cell = "-1";
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package ai.remi.comm.util.number;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc BigDecimalUtils
|
||||
*/
|
||||
public class BigDecimalUtils {
|
||||
|
||||
/**
|
||||
* 中位数
|
||||
*/
|
||||
public static BigDecimal median(List<BigDecimal> list) {
|
||||
|
||||
if (null == list) {
|
||||
return null;
|
||||
}
|
||||
Collections.sort(list);
|
||||
int size = list.size();
|
||||
if (size % 2 == 1) {
|
||||
|
||||
return list.get((size - 1) / 2);
|
||||
} else {
|
||||
|
||||
return (list.get(size / 2 - 1).add(list.get(size / 2))).divide(new BigDecimal(2), 3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
|
||||
public static List<BigDecimal> sort(List<BigDecimal> list) {
|
||||
|
||||
if (null == list) {
|
||||
return null;
|
||||
}
|
||||
Collections.sort(list);
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 最大值
|
||||
*/
|
||||
public static BigDecimal max(List<BigDecimal> list) {
|
||||
|
||||
if (null == list) {
|
||||
return null;
|
||||
}
|
||||
// BigDecimal max = Collections.max(list);
|
||||
|
||||
BigDecimal sum = list.get(0);
|
||||
for (int i = 1; i < list.size(); i++) {
|
||||
|
||||
sum = sum.max(list.get(i));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最小值
|
||||
*/
|
||||
public static BigDecimal min(List<BigDecimal> list) {
|
||||
|
||||
if (null == list) {
|
||||
return null;
|
||||
}
|
||||
// BigDecimal min = Collections.min(list);
|
||||
BigDecimal sum = list.get(0);
|
||||
for (int i = 1; i < list.size(); i++) {
|
||||
|
||||
sum = sum.min(list.get(i));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 平均值
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal avg(List<BigDecimal> list) {
|
||||
|
||||
if (null == list) {
|
||||
return null;
|
||||
}
|
||||
BigDecimal sum = list.get(0);
|
||||
for (int i = 1; i < list.size(); i++) {
|
||||
|
||||
sum = sum.add(list.get(i));
|
||||
}
|
||||
BigDecimal sum1 = new BigDecimal(list.size());
|
||||
BigDecimal avg = sum.divide(sum1, 2);
|
||||
System.out.println(avg);
|
||||
return avg;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package ai.remi.comm.util.object;
|
||||
|
||||
import ai.remi.comm.util.string.StringUtils;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc ObjectUtils 对象工具箱
|
||||
*/
|
||||
public class ObjectUtils {
|
||||
|
||||
/**
|
||||
* 判断字符串是否为true
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static boolean isStrTrue(String str) {
|
||||
return StringUtils.isNotEmpty(str) && "true".equals(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否不为空
|
||||
*
|
||||
* @param object
|
||||
* @return: boolean
|
||||
*/
|
||||
public static boolean isNotEmpty(Object object) {
|
||||
return !isEmpty(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否为空
|
||||
*
|
||||
* @param object
|
||||
* @return: boolean
|
||||
*/
|
||||
public static boolean isEmpty(Object object) {
|
||||
boolean aNull = isNull(object);
|
||||
if (!aNull) {
|
||||
//普通的类对象
|
||||
Field[] fields = object.getClass().getDeclaredFields();
|
||||
//先假设全部属性都是空的,所以只要出现一个属性不为空的就不需要在循环判断
|
||||
try {
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
if (!"serialVersionUID".equals(field.getName()) && !ObjectUtils.isNull(field.get(object))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否不为空
|
||||
*
|
||||
* @param object
|
||||
* @return: boolean
|
||||
*/
|
||||
public static boolean isNotNull(Object object) {
|
||||
return !isNull(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否为空
|
||||
*
|
||||
* @param object
|
||||
* @return: boolean
|
||||
*/
|
||||
public static boolean isNull(Object object) {
|
||||
//判断对象是否为null
|
||||
if (null == object) {
|
||||
return true;
|
||||
}
|
||||
//判断对象是否为容器对象类型
|
||||
if (object instanceof Optional) {
|
||||
return !((Optional) object).isPresent();
|
||||
}
|
||||
//判断对象是否为字符集合类型
|
||||
if (object instanceof CharSequence) {
|
||||
return ((CharSequence) object).length() == 0;
|
||||
}
|
||||
//判断对象是否为数组类型
|
||||
if (object.getClass().isArray()) {
|
||||
return Array.getLength(object) == 0;
|
||||
}
|
||||
//判断对象是否为集合类型
|
||||
if (object instanceof Collection) {
|
||||
return ((Collection) object).isEmpty();
|
||||
}
|
||||
//判断对象是否为Map
|
||||
return object instanceof Map ? ((Map) object).isEmpty() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断obejct对象中除了names里面的字段,其他字段都为null(已知对象类型)
|
||||
*
|
||||
* @param object
|
||||
* @param names
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEmpty(Object object, String... names) {
|
||||
Field[] fields = object.getClass().getDeclaredFields();
|
||||
//用于判断所有属性是否为空,如果参数为空则不查询
|
||||
boolean flag = true;
|
||||
for (Field field : fields) {
|
||||
//不检查 直接取值
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
String fieldName = field.getName();
|
||||
List<String> nameList = new ArrayList<>();
|
||||
if (null != names && names.length != 0) {
|
||||
nameList = Arrays.asList(names);
|
||||
}
|
||||
if (!nameList.contains(fieldName) && !Objects.isNull(field.get(object))) {
|
||||
//不为空
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断object对象中除了names里面的字段,其他字段都为null或者是""(已知对象类型)
|
||||
*
|
||||
* @param object
|
||||
* @param names
|
||||
* @return
|
||||
*/
|
||||
public static boolean isBlank(Object object, String... names) {
|
||||
Field[] fields = object.getClass().getDeclaredFields();
|
||||
//用于判断所有属性是否为空,如果参数为空则不查询
|
||||
boolean flag = true;
|
||||
for (Field field : fields) {
|
||||
//不检查 直接取值
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
String fieldName = field.getName();
|
||||
List<String> nameList = new ArrayList<>();
|
||||
if (null != names && names.length != 0) {
|
||||
nameList = Arrays.asList(names);
|
||||
}
|
||||
Object value = field.get(object);
|
||||
if (!nameList.contains(fieldName) && !isEmpty(value)) {
|
||||
//不为空
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 利用反射 将Java实体转化为 map
|
||||
*
|
||||
* @param object object
|
||||
* @return map
|
||||
* @version 1.0
|
||||
*/
|
||||
public static Map<String, Object> objectToMap(Object object) {
|
||||
Map<String, Object> reMap = new HashMap<>();
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
List<Field> fields = new ArrayList<>();
|
||||
List<Field> childFields;
|
||||
List<String> fieldsName = new ArrayList<>();
|
||||
Class tempClass = object.getClass();
|
||||
//当父类为null的时候说明到达了最上层的父类(Object类).
|
||||
while (tempClass != null) {
|
||||
fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
|
||||
//得到父类,然后赋给自己
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
childFields = Arrays.asList(object.getClass().getDeclaredFields());
|
||||
for (Field field : childFields) {
|
||||
fieldsName.add(field.getName());
|
||||
}
|
||||
try {
|
||||
for (Field field : fields) {
|
||||
try {
|
||||
if (fieldsName.contains(field.getName())) {
|
||||
Field f = object.getClass().getDeclaredField(field.getName());
|
||||
f.setAccessible(true);
|
||||
Object o = f.get(object);
|
||||
reMap.put(field.getName(), o);
|
||||
} else {
|
||||
Field f = object.getClass().getSuperclass().getDeclaredField(field.getName());
|
||||
f.setAccessible(true);
|
||||
Object o = f.get(object);
|
||||
reMap.put(field.getName(), o);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return reMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 利用反射将map集合封装成bean对象
|
||||
*
|
||||
* @param map
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
public static <T> T mapToObject(Map<Object, Object> map, Class<?> clazz) throws Exception {
|
||||
Object obj = clazz.newInstance();
|
||||
if (map != null && !map.isEmpty() && map.size() > 0) {
|
||||
for (Map.Entry<Object, Object> entry : map.entrySet()) {
|
||||
String propertyName = String.valueOf(entry.getKey()); // 属性名
|
||||
Object value = entry.getValue(); // 属性值
|
||||
String setMethodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
|
||||
Field field = getClassField(clazz, propertyName); //获取和map的key匹配的属性名称
|
||||
if (field == null) {
|
||||
continue;
|
||||
}
|
||||
Class<?> fieldTypeClass = field.getType();
|
||||
value = convertValType(value, fieldTypeClass);
|
||||
try {
|
||||
clazz.getMethod(setMethodName, field.getType()).invoke(obj, value);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定对象类匹配对象中的特定字段
|
||||
*
|
||||
* @param clazz
|
||||
* @param fieldName
|
||||
* @return
|
||||
*/
|
||||
private static Field getClassField(Class<?> clazz, String fieldName) {
|
||||
if (Object.class.getName().equals(clazz.getName())) {
|
||||
return null;
|
||||
}
|
||||
Field[] declaredFields = clazz.getDeclaredFields();
|
||||
for (Field field : declaredFields) {
|
||||
if (field.getName().equals(fieldName)) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
Class<?> superClass = clazz.getSuperclass(); //如果该类还有父类,将父类对象中的字段也取出
|
||||
if (superClass != null) { //递归获取
|
||||
return getClassField(superClass, fieldName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将map的value值转为实体类中字段类型匹配的方法
|
||||
*
|
||||
* @param value
|
||||
* @param fieldTypeClass
|
||||
* @return
|
||||
*/
|
||||
private static Object convertValType(Object value, Class<?> fieldTypeClass) {
|
||||
Object retVal = null;
|
||||
|
||||
if (Long.class.getName().equals(fieldTypeClass.getName())
|
||||
|| long.class.getName().equals(fieldTypeClass.getName())) {
|
||||
retVal = Long.parseLong(value.toString());
|
||||
} else if (Integer.class.getName().equals(fieldTypeClass.getName())
|
||||
|| int.class.getName().equals(fieldTypeClass.getName())) {
|
||||
retVal = Integer.parseInt(value.toString());
|
||||
} else if (Float.class.getName().equals(fieldTypeClass.getName())
|
||||
|| float.class.getName().equals(fieldTypeClass.getName())) {
|
||||
retVal = Float.parseFloat(value.toString());
|
||||
} else if (Double.class.getName().equals(fieldTypeClass.getName())
|
||||
|| double.class.getName().equals(fieldTypeClass.getName())) {
|
||||
retVal = Double.parseDouble(value.toString());
|
||||
} else {
|
||||
retVal = value;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package ai.remi.comm.util.object;
|
||||
|
||||
public class SerializeUtils {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package ai.remi.comm.util.page;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc PageUtils 分页工具箱
|
||||
*/
|
||||
public class PageUtils {
|
||||
/**
|
||||
* 开始分页
|
||||
* @param list
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页多少数据
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> startPage(List<T> list, int pageNum, int pageSize){
|
||||
if(null == list){
|
||||
return new ArrayList<T>();
|
||||
}
|
||||
if(list.size() == 0){
|
||||
return list;
|
||||
}
|
||||
if(pageNum == 0){
|
||||
pageNum = 1;
|
||||
}
|
||||
int count = list.size(); //记录总数
|
||||
int pageCount = 0; //页数
|
||||
if(count % pageSize == 0){
|
||||
pageCount = count / pageSize;
|
||||
}else{
|
||||
pageCount = count / pageSize + 1;
|
||||
}
|
||||
|
||||
int fromIndex = 0; //开始索引
|
||||
int toIndex = 0; //结束索引
|
||||
if(pageNum > pageCount){
|
||||
return new ArrayList<T>();
|
||||
}
|
||||
|
||||
if(pageNum != pageCount){
|
||||
fromIndex = (pageNum -1) * pageSize;
|
||||
toIndex = fromIndex + pageSize;
|
||||
}else {
|
||||
fromIndex = (pageNum - 1) * pageSize;
|
||||
toIndex = count;
|
||||
}
|
||||
return list.subList(fromIndex,toIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
package ai.remi.comm.util.regex;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 正则表达式工具类
|
||||
*/
|
||||
public class RegexUtils {
|
||||
|
||||
/******************** 正则相关常量 ********************/
|
||||
/**
|
||||
* 正则:手机号(简单)
|
||||
*/
|
||||
public static final String REGEX_MOBILE_SIMPLE = "^[1]\\d{10}$";
|
||||
/**
|
||||
* 正则:手机号(精确)
|
||||
* <p>移动:134(0-8)、135、136、137、138、139、147、150、151、152、157、158、159、178、182、183、184、187、188</p>
|
||||
* <p>联通:130、131、132、145、155、156、175、176、185、186</p>
|
||||
* <p>电信:133、153、173、177、180、181、189</p>
|
||||
* <p>全球星:1349</p>
|
||||
* <p>虚拟运营商:170</p>
|
||||
*/
|
||||
public static final String REGEX_MOBILE_EXACT = "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|(147))\\d{8}$";
|
||||
/**
|
||||
* 正则:电话号码
|
||||
*/
|
||||
public static final String REGEX_TEL = "^0\\d{2,3}[- ]?\\d{7,8}";
|
||||
/**
|
||||
* 正则:身份证号码15位
|
||||
*/
|
||||
public static final String REGEX_ID_CARD15 = "^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$";
|
||||
/**
|
||||
* 正则:身份证号码18位
|
||||
*/
|
||||
public static final String REGEX_ID_CARD18 = "^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9Xx])$";
|
||||
/**
|
||||
* 正则:邮箱
|
||||
*/
|
||||
public static final String REGEX_EMAIL = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
|
||||
/**
|
||||
* 正则:URL
|
||||
*/
|
||||
public static final String REGEX_URL = "[a-zA-z]+://[^\\s]*";
|
||||
/**
|
||||
* 正则:汉字
|
||||
*/
|
||||
public static final String REGEX_ZH = "^[\\u4e00-\\u9fa5]+$";
|
||||
/**
|
||||
* 正则:用户名,取值范围为a-z,A-Z,0-9,"_",汉字,不能以"_"结尾,用户名必须是6-20位
|
||||
*/
|
||||
public static final String REGEX_USERNAME = "^[\\w\\u4e00-\\u9fa5]{6,20}(?<!_)$";
|
||||
/**
|
||||
* 正则:yyyy-MM-dd格式的日期校验,已考虑平闰年
|
||||
*/
|
||||
public static final String REGEX_DATE = "^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$";
|
||||
/**
|
||||
* 正则:IP地址
|
||||
*/
|
||||
public static final String REGEX_IP = "((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)";
|
||||
|
||||
/**
|
||||
* 正则:双字节字符(包括汉字在内)
|
||||
*/
|
||||
public static final String REGEX_DOUBLE_BYTE_CHAR = "[^\\x00-\\xff]";
|
||||
/**
|
||||
* 正则:空白行
|
||||
*/
|
||||
public static final String REGEX_BLANK_LINE = "\\n\\s*\\r";
|
||||
/**
|
||||
* 正则:中国邮政编码
|
||||
*/
|
||||
public static final String REGEX_ZIP_CODE = "[1-9]\\d{5}(?!\\d)";
|
||||
/**
|
||||
* 正则:正整数
|
||||
*/
|
||||
public static final String REGEX_POSITIVE_INTEGER = "^[1-9]\\d*$";
|
||||
/**
|
||||
* 正则:负整数
|
||||
*/
|
||||
public static final String REGEX_NEGATIVE_INTEGER = "^-[1-9]\\d*$";
|
||||
/**
|
||||
* 正则:整数
|
||||
*/
|
||||
public static final String REGEX_INTEGER = "^-?[1-9]\\d*$";
|
||||
/**
|
||||
* 正则:非负整数(正整数 + 0)
|
||||
*/
|
||||
public static final String REGEX_NOT_NEGATIVE_INTEGER = "^[1-9]\\d*|0$";
|
||||
/**
|
||||
* 正则:非正整数(负整数 + 0)
|
||||
*/
|
||||
public static final String REGEX_NOT_POSITIVE_INTEGER = "^-[1-9]\\d*|0$";
|
||||
/**
|
||||
* 正则:正浮点数
|
||||
*/
|
||||
public static final String REGEX_POSITIVE_FLOAT = "^[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$";
|
||||
/**
|
||||
* 正则:负浮点数
|
||||
*/
|
||||
public static final String REGEX_NEGATIVE_FLOAT = "^-[1-9]\\d*\\.\\d*|-0\\.\\d*[1-9]\\d*$";
|
||||
/**
|
||||
* 正则:整数或者浮点数
|
||||
*/
|
||||
public static final String REGEX_INTEGER_OR_FLOAT = "^-?(\\d|([1-9]\\d+))(\\.\\d+)?$";
|
||||
|
||||
|
||||
/**
|
||||
* 验证手机号(简单)
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isMobileSimple(CharSequence input) {
|
||||
return isMatch(REGEX_MOBILE_SIMPLE, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证手机号(精确)
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isMobileExact(CharSequence input) {
|
||||
return isMatch(REGEX_MOBILE_EXACT, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证电话号码
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isTel(CharSequence input) {
|
||||
return isMatch(REGEX_TEL, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证身份证号码15位
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isIDCard15(CharSequence input) {
|
||||
return isMatch(REGEX_ID_CARD15, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证身份证号码18位
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isIDCard18(CharSequence input) {
|
||||
return isMatch(REGEX_ID_CARD18, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邮箱
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isEmail(CharSequence input) {
|
||||
return isMatch(REGEX_EMAIL, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证URL
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isURL(CharSequence input) {
|
||||
return isMatch(REGEX_URL, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证汉字
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isZh(CharSequence input) {
|
||||
return isMatch(REGEX_ZH, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户名
|
||||
* <p>取值范围为a-z,A-Z,0-9,"_",汉字,不能以"_"结尾,用户名必须是6-20位</p>
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isUsername(CharSequence input) {
|
||||
return isMatch(REGEX_USERNAME, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证yyyy-MM-dd格式的日期校验,已考虑平闰年
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isDate(CharSequence input) {
|
||||
return isMatch(REGEX_DATE, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证IP地址
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isIP(CharSequence input) {
|
||||
return isMatch(REGEX_IP, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否为双字节字符(包括汉字在内)
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isDoubleByteChar(CharSequence input) {
|
||||
return isMatch(REGEX_DOUBLE_BYTE_CHAR, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否空白行
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isBlankLine(CharSequence input) {
|
||||
return isMatch(REGEX_BLANK_LINE, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否中国邮政编码
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isZipCode(CharSequence input) {
|
||||
return isMatch(REGEX_ZIP_CODE, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否正整数
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isPositiveInteger(CharSequence input) {
|
||||
return isMatch(REGEX_POSITIVE_INTEGER, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否负整数
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isNegativeInteger(CharSequence input) {
|
||||
return isMatch(REGEX_NEGATIVE_INTEGER, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否为整数
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isRegexInteger(CharSequence input) {
|
||||
return isMatch(REGEX_INTEGER, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否为非负整数
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isNotNegativeInteger(CharSequence input) {
|
||||
return isMatch(REGEX_NOT_NEGATIVE_INTEGER, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否为非正整数
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isNotPositiveInteger(CharSequence input) {
|
||||
return isMatch(REGEX_NOT_POSITIVE_INTEGER, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否正浮点数
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isPositiveFloat(CharSequence input) {
|
||||
return isMatch(REGEX_POSITIVE_FLOAT, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否负浮点数
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean NEGATIVE_FLOAT(CharSequence input) {
|
||||
return isMatch(REGEX_NEGATIVE_FLOAT, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否空白行
|
||||
*
|
||||
* @param input 待验证文本
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isIntegerOrFloat(CharSequence input) {
|
||||
return isMatch(REGEX_INTEGER_OR_FLOAT, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否匹配正则
|
||||
*
|
||||
* @param regex 正则表达式
|
||||
* @param input 要匹配的字符串
|
||||
* @return {@code true}: 匹配<br>{@code false}: 不匹配
|
||||
*/
|
||||
public static boolean isMatch(String regex, CharSequence input) {
|
||||
return input != null && input.length() > 0 && Pattern.matches(regex, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正则匹配的部分
|
||||
*
|
||||
* @param regex 正则表达式
|
||||
* @param input 要匹配的字符串
|
||||
* @return 正则匹配的部分
|
||||
*/
|
||||
public static List<String> getMatches(String regex, CharSequence input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
List<String> matches = new ArrayList<>();
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(input);
|
||||
while (matcher.find()) {
|
||||
matches.add(matcher.group());
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正则匹配分组
|
||||
*
|
||||
* @param input 要分组的字符串
|
||||
* @param regex 正则表达式
|
||||
* @return 正则匹配分组
|
||||
*/
|
||||
public static String[] getSplits(String input, String regex) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
return input.split(regex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换正则匹配的第一部分
|
||||
*
|
||||
* @param input 要替换的字符串
|
||||
* @param regex 正则表达式
|
||||
* @param replacement 代替者
|
||||
* @return 替换正则匹配的第一部分
|
||||
*/
|
||||
public static String getReplaceFirst(String input, String regex, String replacement) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
return Pattern.compile(regex).matcher(input).replaceFirst(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换所有正则匹配的部分
|
||||
*
|
||||
* @param input 要替换的字符串
|
||||
* @param regex 正则表达式
|
||||
* @param replacement 代替者
|
||||
* @return 替换所有正则匹配的部分
|
||||
*/
|
||||
public static String getReplaceAll(String input, String regex, String replacement) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
return Pattern.compile(regex).matcher(input).replaceAll(replacement);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ai.remi.comm.util.saml;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
public class SAMLUtils {
|
||||
|
||||
/**
|
||||
* 解析SAMLRequest SAMLResponse
|
||||
*/
|
||||
public static String decodeAndInflateXML(String samlStr) throws Exception {
|
||||
// base64解码
|
||||
byte[] decodedBytes = Base64.decode(java.net.URLDecoder.decode(samlStr, "utf-8"));
|
||||
// 获取输入流
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decodedBytes);
|
||||
InflaterInputStream inflaterInputStream = new InflaterInputStream(byteArrayInputStream, new Inflater(true));
|
||||
byte[] buffer = new byte[decodedBytes.length];
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
// 信息写到输出流
|
||||
for (int i = 0; i != -1; i = inflaterInputStream.read(buffer)) {
|
||||
byteArrayOutputStream.write(buffer, 0, i);
|
||||
}
|
||||
return new String(byteArrayOutputStream.toByteArray(), "UTF-8");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
package ai.remi.comm.util.security;
|
||||
|
||||
import cn.hutool.core.exceptions.UtilException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @date 2022-07-26
|
||||
* @desc AesUtils 加/解密工具类
|
||||
* 使用密钥时请使用 initKey() 方法来生成随机密钥
|
||||
* initKey 方法内部使用 java.crypto.KeyGenerator 密钥生成器来生成特定于 AES 算法参数集的随机密钥
|
||||
* 使用密钥生成器生成的密钥能保证更强的随机性
|
||||
* 生成的二进制密钥建议使用 Hex 进行编码
|
||||
*/
|
||||
public class AesUtils {
|
||||
|
||||
private AesUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 密钥算法类型
|
||||
*/
|
||||
public static final String KEY_ALGORITHM = "AES";
|
||||
|
||||
/**
|
||||
* 密钥的默认位长度
|
||||
*/
|
||||
public static final int DEFAULT_KEY_SIZE = 128;
|
||||
|
||||
/**
|
||||
* 加解密算法/工作模式/填充方式
|
||||
*/
|
||||
private static final String ECB_PKCS_5_PADDING = "AES/ECB/PKCS5Padding";
|
||||
public static final String ECB_NO_PADDING = "AES/ECB/NoPadding";
|
||||
|
||||
public static String base64Encode(byte[] bytes) {
|
||||
return Base64.encodeBase64String(bytes);
|
||||
}
|
||||
|
||||
public static byte[] base64Decode(String base64Code) {
|
||||
return Base64.decodeBase64(base64Code);
|
||||
}
|
||||
|
||||
public static byte[] aesEncryptToBytes(String content, String hexAesKey) throws Exception {
|
||||
return encrypt(content.getBytes(StandardCharsets.UTF_8), Hex.decodeHex(hexAesKey.toCharArray()));
|
||||
}
|
||||
|
||||
public static String aesEncrypt(String content, String hexAesKey) throws Exception {
|
||||
return base64Encode(aesEncryptToBytes(content, hexAesKey));
|
||||
}
|
||||
|
||||
public static String aesDecryptByBytes(byte[] encryptBytes, String hexAesKey) throws Exception {
|
||||
byte[] decrypt = decrypt(encryptBytes, Hex.decodeHex(hexAesKey.toCharArray()));
|
||||
return new String(decrypt, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String aesDecrypt(String encryptStr, String hexAesKey) throws Exception {
|
||||
return aesDecryptByBytes(base64Decode(encryptStr), hexAesKey);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成 Hex 格式默认长度的随机密钥
|
||||
* 32,解二进制后为 16 个字节
|
||||
*
|
||||
* @return String Hex 格式的随机密钥
|
||||
*/
|
||||
public static String initHexKey() {
|
||||
return Hex.encodeHexString(initKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成默认长度的随机密钥
|
||||
* 默认长度为 128
|
||||
*
|
||||
* @return byte[] 二进制密钥
|
||||
*/
|
||||
public static byte[] initKey() {
|
||||
return initKey(DEFAULT_KEY_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密钥
|
||||
* 128、192、256 可选
|
||||
*
|
||||
* @param keySize 密钥长度
|
||||
* @return byte[] 二进制密钥
|
||||
*/
|
||||
public static byte[] initKey(int keySize) {
|
||||
// AES 要求密钥长度为 128 位、192 位或 256 位
|
||||
if (keySize != 128 && keySize != 192 && keySize != 256) {
|
||||
throw new RuntimeException("error keySize: " + keySize);
|
||||
}
|
||||
// 实例化
|
||||
KeyGenerator keyGenerator;
|
||||
try {
|
||||
keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new UtilException("no such algorithm exception: " + KEY_ALGORITHM, e);
|
||||
}
|
||||
keyGenerator.init(keySize);
|
||||
// 生成秘密密钥
|
||||
SecretKey secretKey = keyGenerator.generateKey();
|
||||
// 获得密钥的二进制编码形式
|
||||
return secretKey.getEncoded();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换密钥
|
||||
*
|
||||
* @param key 二进制密钥
|
||||
* @return Key 密钥
|
||||
*/
|
||||
private static Key toKey(byte[] key) {
|
||||
// 实例化 DES 密钥材料
|
||||
return new SecretKeySpec(key, KEY_ALGORITHM);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param data 待加密数据
|
||||
* @param key 密钥
|
||||
* @return byte[] 加密的数据
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, byte[] key) {
|
||||
return encrypt(data, key, ECB_PKCS_5_PADDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param data 待加密数据
|
||||
* @param key 密钥
|
||||
* @param cipherAlgorithm 算法/工作模式/填充模式
|
||||
* @return byte[] 加密的数据
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, byte[] key, final String cipherAlgorithm) {
|
||||
// 还原密钥
|
||||
Key k = toKey(key);
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
|
||||
// 初始化,设置为加密模式
|
||||
cipher.init(Cipher.ENCRYPT_MODE, k);
|
||||
|
||||
// 发现使用 NoPadding 时,使用 ZeroPadding 填充
|
||||
if (ECB_NO_PADDING.equals(cipherAlgorithm)) {
|
||||
return cipher.doFinal(formatWithZeroPadding(data, cipher.getBlockSize()));
|
||||
}
|
||||
|
||||
// 执行操作
|
||||
return cipher.doFinal(data);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("AES encrypt error", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param data 待解密数据
|
||||
* @param key 密钥
|
||||
* @return byte[] 解密的数据
|
||||
*/
|
||||
public static byte[] decrypt(byte[] data, byte[] key) {
|
||||
return decrypt(data, key, ECB_PKCS_5_PADDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param data 待解密数据
|
||||
* @param key 密钥
|
||||
* @param cipherAlgorithm 算法/工作模式/填充模式
|
||||
* @return byte[] 解密的数据
|
||||
*/
|
||||
public static byte[] decrypt(byte[] data, byte[] key, final String cipherAlgorithm) {
|
||||
// 还原密钥
|
||||
Key k = toKey(key);
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
|
||||
// 初始化,设置为解密模式
|
||||
cipher.init(Cipher.DECRYPT_MODE, k);
|
||||
|
||||
// 发现使用 NoPadding 时,使用 ZeroPadding 填充
|
||||
if (ECB_NO_PADDING.equals(cipherAlgorithm)) {
|
||||
return removeZeroPadding(cipher.doFinal(data), cipher.getBlockSize());
|
||||
}
|
||||
|
||||
// 执行操作
|
||||
return cipher.doFinal(data);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("AES decrypt error", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] formatWithZeroPadding(byte[] data, final int blockSize) {
|
||||
final int length = data.length;
|
||||
final int remainLength = length % blockSize;
|
||||
|
||||
if (remainLength > 0) {
|
||||
byte[] inputData = new byte[length + blockSize - remainLength];
|
||||
System.arraycopy(data, 0, inputData, 0, length);
|
||||
return inputData;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private static byte[] removeZeroPadding(byte[] data, final int blockSize) {
|
||||
final int length = data.length;
|
||||
final int remainLength = length % blockSize;
|
||||
if (remainLength == 0) {
|
||||
// 解码后的数据正好是块大小的整数倍,说明可能存在补 0 的情况,去掉末尾所有的 0
|
||||
int i = length - 1;
|
||||
while (i >= 0 && 0 == data[i]) {
|
||||
i--;
|
||||
}
|
||||
byte[] outputData = new byte[i + 1];
|
||||
System.arraycopy(data, 0, outputData, 0, outputData.length);
|
||||
return outputData;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
//long current = Calendar.getInstance().getTimeInMillis();
|
||||
//long current = 1616031425922L;
|
||||
//System.out.println("\n当前时间戳为:" + current);
|
||||
|
||||
|
||||
// 使用密钥生成器 KeyGenerator 生成的 16 字节随机密钥的 hex 字符串,使用时解 hex 得到二进制密钥
|
||||
//byte[] bytes = AesUtils.initKey();
|
||||
//String aesKey = Hex.encodeHexString(bytes);
|
||||
String aesKey = "c39de8d1308e0ad7afbe1b4749c5d3a9";
|
||||
System.out.println("\n当前密钥为" + aesKey);
|
||||
|
||||
|
||||
//String content = "{\"isPhone\":\"0\",\"userid\":\"zhangsan\",\"passwd\":\"123456\",\"timestamp\":1610610307826}";
|
||||
String content = "tya_40hyJF";
|
||||
System.out.println("\nAES加密前:" + content);
|
||||
|
||||
String encrypt = aesEncrypt(content, aesKey);
|
||||
System.out.println("\nAES加密后:" + encrypt);
|
||||
|
||||
String decrypt = aesDecrypt(encrypt, aesKey);
|
||||
System.out.println("\nAES解密后:" + decrypt);
|
||||
|
||||
//String bb = "{\"isPhone\":\"0\",\"userid\":\"zhangsan\",\"passwd\":\"123456\",\"timestamp\":1610610307826}";
|
||||
//String md5 = DigestUtils.md5DigestAsHex(bb.getBytes("UTF-8"));
|
||||
|
||||
//System.out.println("\nmd5加密后:" + md5);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package ai.remi.comm.util.security;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc Base64Kits
|
||||
*/
|
||||
public class Base64Utils {
|
||||
public static final String CHARSET = "utf-8";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Base64Utils.class);
|
||||
|
||||
public static void main(String[] args) throws UnsupportedEncodingException {
|
||||
//测试FileBase64
|
||||
//long start = System.currentTimeMillis();
|
||||
//String imgBase64Str = Base64Utils.convertFileToBase64("C:\\demos\\a3fa6ce0-b9a9-4313-93f0-501d1149596c.jpg");
|
||||
//System.out.println("本地图片转换Base64:" + imgBase64Str);
|
||||
//System.out.println("Base64字符串length=" + imgBase64Str.length());
|
||||
//Base64Utils.convertBase64ToFile(imgBase64Str, "C:\\demos\\", "test.jpg");
|
||||
//System.out.println("duration:" + (System.currentTimeMillis() - start));
|
||||
//测试StringBase64
|
||||
long start = System.currentTimeMillis();
|
||||
//ThisIsMyCustomSecretKey012345678
|
||||
//String base64Str = Base64Utils.encode("RemiAdminNacosCustomSecretKey2024");
|
||||
String base64Str = Base64Utils.encode("RemiAdminNacosCustomSecretKey2024");
|
||||
System.out.println("字符串加密后的Base64为:" + base64Str);
|
||||
System.out.println("Base64字符串length=" + base64Str.length());
|
||||
String decode = Base64Utils.decode(base64Str);
|
||||
//String decode = Base64Utils.decode("VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=");
|
||||
System.out.println("Base64解密后的字符串为:"+decode);
|
||||
System.out.println("duration:" + (System.currentTimeMillis() - start));
|
||||
|
||||
}
|
||||
/**
|
||||
* 字符串解密
|
||||
*/
|
||||
public static String decode(String data) {
|
||||
try {
|
||||
if (null == data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new String(Base64.getDecoder().decode(data.getBytes(CHARSET)), CHARSET);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.error(String.format("字符串:%s,解密异常", data), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串加密
|
||||
*/
|
||||
public static String encode(String data) {
|
||||
try {
|
||||
if (null == data) {
|
||||
return null;
|
||||
}
|
||||
return new String(Base64.getEncoder().encode(data.getBytes(CHARSET)), CHARSET);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.error(String.format("字符串:%s,加密异常", data), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地文件(图片、excel等)转换成Base64字符
|
||||
*/
|
||||
public static String convertFileToBase64(String filePath) throws UnsupportedEncodingException {
|
||||
byte[] data = null;
|
||||
// 读取图片字节数组
|
||||
try {
|
||||
InputStream in = new FileInputStream(filePath);
|
||||
System.out.println("文件大小(字节)=" + in.available());
|
||||
data = new byte[in.available()];
|
||||
in.read(data);
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 对字节数组进行Base64编码,得到Base64编码的字符串
|
||||
return new String(Base64.getEncoder().encode(data), CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将base64字符串,生成文件
|
||||
*/
|
||||
public static File convertBase64ToFile(String fileBase64String, String filePath, String fileName) {
|
||||
|
||||
BufferedOutputStream bos = null;
|
||||
FileOutputStream fos = null;
|
||||
File file = null;
|
||||
try {
|
||||
File dir = new File(filePath);
|
||||
//判断文件目录是否存在
|
||||
if (!dir.exists() && dir.isDirectory()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
byte[] bfile = Base64.getDecoder().decode(fileBase64String);
|
||||
file = new File(filePath + File.separator + fileName);
|
||||
fos = new FileOutputStream(file);
|
||||
bos = new BufferedOutputStream(fos);
|
||||
bos.write(bfile);
|
||||
return file;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (bos != null) {
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package ai.remi.comm.util.security;
|
||||
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.DESKeySpec;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* DES加密介绍 DES是一种对称加密算法,所谓对称加密算法即:加密和解密使用相同密钥的算法。DES加密算法出自IBM的研究,
|
||||
* 后来被美国政府正式采用,之后开始广泛流传,但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,
|
||||
* 24小时内即可被破解。虽然如此,在某些简单应用中,我们还是可以使用DES加密算法,本文简单讲解DES的JAVA实现 。
|
||||
* 注意:DES加密和解密过程中,密钥长度都必须是8的倍数
|
||||
*/
|
||||
|
||||
public class DesUtils {
|
||||
public DesUtils() {
|
||||
}
|
||||
|
||||
// 测试
|
||||
public static void main(String args[]) {
|
||||
// 待加密内容
|
||||
String str = "cryptology";
|
||||
// 密码,长度要是8的倍数
|
||||
String password = "95880288";
|
||||
|
||||
byte[] result;
|
||||
try {
|
||||
result = DesUtils.encrypt(str.getBytes(), password);
|
||||
System.out.println("加密后: " + result);
|
||||
byte[] decryResult = DesUtils.decrypt(result, password);
|
||||
System.out.println("解密后: " + new String(decryResult));
|
||||
} catch (UnsupportedEncodingException e2) {
|
||||
// TODO Auto-generated catch block
|
||||
e2.printStackTrace();
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] encrypt(byte[] datasource, String password) {
|
||||
try {
|
||||
SecureRandom random = new SecureRandom();
|
||||
DESKeySpec desKey = new DESKeySpec(password.getBytes());
|
||||
// 创建一个密匙工厂,然后用它把DESKeySpec转换成
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
|
||||
SecretKey securekey = keyFactory.generateSecret(desKey);
|
||||
// Cipher对象实际完成加密操作
|
||||
Cipher cipher = Cipher.getInstance("DES");
|
||||
// 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
|
||||
cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
|
||||
// 现在,获取数据并加密
|
||||
// 正式执行加密操作
|
||||
return cipher.doFinal(datasource); // 按单部分操作加密或解密数据,或者结束一个多部分操作
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] src, String password) throws Exception {
|
||||
// DES算法要求有一个可信任的随机数源
|
||||
SecureRandom random = new SecureRandom();
|
||||
// 创建一个DESKeySpec对象
|
||||
DESKeySpec desKey = new DESKeySpec(password.getBytes());
|
||||
// 创建一个密匙工厂
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回实现指定转换的
|
||||
// Cipher
|
||||
// 对象
|
||||
// 将DESKeySpec对象转换成SecretKey对象
|
||||
SecretKey securekey = keyFactory.generateSecret(desKey);
|
||||
// Cipher对象实际完成解密操作
|
||||
Cipher cipher = Cipher.getInstance("DES");
|
||||
// 用密匙初始化Cipher对象
|
||||
cipher.init(Cipher.DECRYPT_MODE, securekey, random);
|
||||
// 真正开始解密操作
|
||||
return cipher.doFinal(src);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package ai.remi.comm.util.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* 不可逆加密工具类
|
||||
*/
|
||||
public class DigestUtils {
|
||||
|
||||
private static SecureRandom random = new SecureRandom();
|
||||
|
||||
/**
|
||||
* 生成随机的Byte[]作为salt密钥.
|
||||
*
|
||||
* @param numBytes byte数组的大小
|
||||
*/
|
||||
public static byte[] genSalt(int numBytes) {
|
||||
Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes);
|
||||
byte[] bytes = new byte[numBytes];
|
||||
random.nextBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对字符串进行散列, 支持md5与sha1算法.
|
||||
*
|
||||
* @param input 需要散列的字符串
|
||||
* @param algorithm 散列算法("SHA-1"、"MD5")
|
||||
* @param salt
|
||||
* @param iterations 迭代次数
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
|
||||
MessageDigest digest = MessageDigest.getInstance(algorithm);
|
||||
|
||||
if (salt != null) {
|
||||
digest.update(salt);
|
||||
}
|
||||
|
||||
byte[] result = digest.digest(input);
|
||||
|
||||
for (int i = 1; i < iterations; i++) {
|
||||
digest.reset();
|
||||
result = digest.digest(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对文件进行sha1散列.
|
||||
*
|
||||
* @param input 需要散列的流
|
||||
* @param algorithm 散列算法("SHA-1"、"MD5")
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static byte[] digest(InputStream input, String algorithm) throws IOException {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
|
||||
int bufferLength = 8 * 1024;
|
||||
byte[] buffer = new byte[bufferLength];
|
||||
int read = input.read(buffer, 0, bufferLength);
|
||||
|
||||
while (read > -1) {
|
||||
messageDigest.update(buffer, 0, read);
|
||||
read = input.read(buffer, 0, bufferLength);
|
||||
}
|
||||
|
||||
return messageDigest.digest();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ai.remi.comm.util.security;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc Md5Utils
|
||||
*/
|
||||
public class Md5Utils {
|
||||
/**
|
||||
* 将给定的字符串MD5加密
|
||||
*
|
||||
* @param string 给定的字符串
|
||||
* @return MD5加密后生成的字符串
|
||||
*/
|
||||
public static String encode(String string) {
|
||||
String result = null;
|
||||
try {
|
||||
char[] charArray = string.toCharArray();
|
||||
byte[] byteArray = new byte[charArray.length];
|
||||
for (int i = 0; i < charArray.length; i++) {
|
||||
byteArray[i] = (byte) charArray[i];
|
||||
}
|
||||
|
||||
StringBuffer hexValue = new StringBuffer();
|
||||
byte[] md5Bytes = MessageDigest.getInstance("MD5")
|
||||
.digest(byteArray);
|
||||
for (int i = 0; i < md5Bytes.length; i++) {
|
||||
int val = ((int) md5Bytes[i]) & 0xff;
|
||||
if (val < 16) {
|
||||
hexValue.append("0");
|
||||
}
|
||||
hexValue.append(Integer.toHexString(val));
|
||||
}
|
||||
|
||||
result = hexValue.toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String encode = encode("1122");
|
||||
System.out.println(encode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package ai.remi.comm.util.security;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc PwdUtils 用户密码工具类
|
||||
*/
|
||||
public class PwdUtils {
|
||||
|
||||
//private final static String DEFAULT_PASS="Remi2022";
|
||||
|
||||
private final static String DEFAULT_PASS="Remi@123";
|
||||
|
||||
|
||||
public static boolean isValidPassword(String password,String encodePassword) {
|
||||
return encode(password).equals(encodePassword);
|
||||
}
|
||||
|
||||
|
||||
public static String encode(String s){
|
||||
return Md5Utils.encode(s);
|
||||
}
|
||||
|
||||
public static String getDefaultPassEncryption(){
|
||||
return encode(DEFAULT_PASS);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(getDefaultPassEncryption());
|
||||
System.out.println(Md5Utils.encode(DEFAULT_PASS));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package ai.remi.comm.util.security;
|
||||
|
||||
/**
|
||||
* @author DianJiu 【公众号 点九开源】
|
||||
* @email lidianjiu@njydsz.com
|
||||
* @date 2022-07-26
|
||||
* @desc 公钥加密,私钥解密;私钥加签,公钥验签。
|
||||
*/
|
||||
public class Rsa2Demo {
|
||||
|
||||
public static void main(String[] args) {
|
||||
mookHttp();
|
||||
}
|
||||
|
||||
public static void mookHttp(){
|
||||
Rsa2Utils.initRSAKey(2048);
|
||||
// 初始化A公司的公钥、私钥
|
||||
String privateKeyA= "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC1YbT3O4Rd2F5d7R/+61clUBvGtpnYYzDsUCmVLCYnE1zCNQawi5ga+cpdgyafOLFJcVGfcHEushXgJ3XFp0mriRCKnBiA0OwoNa0sVZyW46eLsYhxx6aNmcsfk04xUNR52FYRD3gvLpfIXHlW4Zib9FtYJThjXo6kSLrZbh2f9gaYNlJzJn8tWljI3sPgzgvvVCtPbwFOW5D7Pt7n/nsL3AUM5ZqItRBvlkejitbdjYlg2WXJA6pDYtAxO9+iwiDsftQqF8QzXiZDTBEr+jqKPk3ZpYpuE8nXSWpx/1c4ujhI6R+/CVvj4PEccfDs9x2dusVLgVdYgXxFY4jyyFhrAgMBAAECggEBAJdxCAaw89jjMg9cUC2o0289DqQAjD1GvG9ikwWRAh34JaelXHuFYi+LxjIDsfiY/jVi3xKvcGDbojYw3D12aqVFYMFKiY7sYXFFN9zfcknttFOHnHPbja2G7jjj5hJ4+Z7uSRYfg2ppjaNlyo4G+EOmZ6oWEZmuNozmiNPIBAYsmNXmxK7jmFKsMr8dN8oTraP2j5rjpWwvcoqoJKacGLY8dV8/miGlaTyJzrJT/JRbvPl1pCe9W5MMv2XhwaIrpM2kPuO1HQjBLgsFXQyYxAP0BvqID7QX+MYK104VcpuuAARLqZZUwEZP2quJUMxT973KUlLkBUdgfrLoElvNXOECgYEA/1x4botRAudqjaWH4UTAqaCy0/WrWluZqGxaZn/aS3BdFYya/zMH8kY6s16yKZ5qErxNamQWrGElpHHR8dz3fqkPp5cjqYRcxztPhtCxwkNO5lPRZPIngD7c4CNN1nKV9rO1+a4ALDZJzOKYZSybwOYWkKfEC9+ZNQz2mk98TNECgYEAtdXcba2ayxb6BypunV8xXgX7e57b7VmwSARJGjGAwJJ+Y9drFTFrn/Ei+Tl6r+lnjB7fDejlzxCKQDtP+ZwEMWKaE+LWiHDMucAC5x77wlaWzLEMSpfBsNp2Sw9PmAoQrteHFKJ6V03jMJR2SB3ihH1uj99+4i6U9svHa+Y0cHsCgYAqSMGYbHFJrdAzDEnnv163t+hkuLVbDPfwr6B4gItBGqYPP20a/PQ/5ekqcm/F/HlMCOo4x8IJYTy+cip1hkVlL9FrytcY5tYGF4gfpscIh+S0twRhVse7sK0aYD4vWGIEq9ViNrHIoGSwDIV3m19fHbp20ZDg9nGEG1VqT+chQQKBgQCukOr2rJUP5jYRDQ52rL3TO0EMlB0lR1ZyPRMFP4MQ3VLTfSrDhNeDDFf8Fc+P9C7BmZ2gV/fQC1v+gDKuOrbXxoaPEtXM1SXmKc1fnyIpl0Fcr5hSQFDl/UVVmBRcrUUI5cc76yQyjobOy6imYXJmqZisLO8+K2Q9brM8BhxP4wKBgQDd0r1LAXXDmfEdtZAz0kfhssWVb2E/w+TSyvSqFsvJh8bCrZ18WgUeiAya47mjXCUqB8dOUTPjtO8HqfUfqI+Qr0agdErt1BvoBD10ntVowbOJrj0RJLfFr8YLrHhGkLPTZzt17upv+l5x+pyMaDPxePXO8jy1Nk3rL2okpHqW6A==";
|
||||
String publicKeyA= "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtWG09zuEXdheXe0f/utXJVAbxraZ2GMw7FAplSwmJxNcwjUGsIuYGvnKXYMmnzixSXFRn3BxLrIV4Cd1xadJq4kQipwYgNDsKDWtLFWcluOni7GIccemjZnLH5NOMVDUedhWEQ94Ly6XyFx5VuGYm/RbWCU4Y16OpEi62W4dn/YGmDZScyZ/LVpYyN7D4M4L71QrT28BTluQ+z7e5/57C9wFDOWaiLUQb5ZHo4rW3Y2JYNllyQOqQ2LQMTvfosIg7H7UKhfEM14mQ0wRK/o6ij5N2aWKbhPJ10lqcf9XOLo4SOkfvwlb4+DxHHHw7PcdnbrFS4FXWIF8RWOI8shYawIDAQAB";
|
||||
// 初始化B公司的公钥、私钥
|
||||
String privateKeyB="MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCD9IqMkrTxcsS/ldiY4ryZzTPY2mPiqjh6bTWCfR1+TJfh7cH9H7TH3cPWfizwa/nb9JaAqpZFEmkn1aprNKkjnZM6F1bW9F5N02mARxxUUL4J+BCnYjNwj/f25b8VQp6XrA37P3mi2Z97m9xBdGSooLCPhsFKFzq99wCJHzBivGCmlvr9IgFbsiYkjdOuMn4EFFhGqubfazECrbpVYQPzq0eeEOOal11ACmMysTinHg9DEG4X73Q9lT5Cwb38sm7mmq6e8PkWUDzKbG+CQxG/IBEio35/OB7zNcJ9G87eUIK0s5OVApxdBGFBfG6gHOjx+iTwJyEpZziZljLZf+07AgMBAAECggEAcHYHax1R54IoGxyr2ElN8ksIiCZBARqQPg87jrXxYAYJYD2RmpjeK4Ve9jzdMh7keUkwyAIbRk0VoCSjNPFRwg/3PYhMs7DberrW7MQuadHS1Y49K1svehzJefx+6bC4l43E8MA9e8S7/s5sXsDDVVB2iiyQKgQ4RVTxeOxDAbJOOSj5QZxqwR4x2JalDGFawhKsX9d2l1fQAV/JdDKKK1xqgRzvWqub9MhZzH9Cf1o+gqQ20paFwAiuHVfJpc1QjgdHI9ExDn45jqAhxe6C+YqeUVHrl0cFaCMnrSkQAcvTUT6ancZY3QW/yJlil3boQojmy+P2RkFESqnEz3EWqQKBgQDL1y7AAfUMsdjYXE+hnADuQznAv01IwcoNYmInXGhGTiAwfpQPNYsi/+u4ia0l1Myu1SVt1QcDdemLEo5YKAQepHHJ6Zi/CZNvfKSfKhbnSzDJzxBX3garSdV65rkLMCPxOEFDZtoF//kgDQg3Pl0fsHORw5isvbHqfFb689sP7wKBgQCluGz0MID2Fh/29Q001toQ1wRHjpuo5FdKn+NkWfDCdaZX3T0XmyF9vNpQM02HsC7Dpipakk/ex/ZJr277DRCWP5WuhjMHgIT6KUannMT4ARj0hmEqdwiAYlZGcK9PKhpERWDrlG4tXO+MsLAiBRA/R0SPZyeuybtoVxZ+XBOrdQKBgBa9T5LN5Rdgc69XRO7jZ1stFrFA5g86xx6NHiQD9AH7zy15wWyc7YOfBx3UBgPVZGwQjRdej2S8b6w7CrQc6rC8aZE7xPVyzQa/4eF7oYh14+BvgAQ9WYMcPntyZ6wu16OeQfGR9Lp8nr8LlSIMi/jwB0R4jHygwMtGybE/KO3zAoGAByqMe5rFWN7oDvPV2F0aHPRPWRDPpmQJak6ztJk3EhGoWdBVXfn1XsyV3DX7ukxiGU/MMRJ2pbmb6t/NOsn716N0Q5HGDyL9nRu/1GuxENBrVdehyAZRtcuK04z358J4VE9zu3w1r3x/V2QkmbQCWdopGtJpIuPSWNa7QEeXCikCgYAri2pTViyR3o98uGpCxVLFp8zLF6YHh8ufjGyhp/l/wk928rmXJM5nSlJYuFJ7MgvOjeJK6u0ebGDX58lvuSN6o6EeFWw3H/WFxEqHAJETpkOl7ya7hbJABCFzL2xG5oCDHLzpykENUrIs2ZpmvND5yiFwqfjqThmKzXcH6oBS7w==";
|
||||
String publicKeyB="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg/SKjJK08XLEv5XYmOK8mc0z2Npj4qo4em01gn0dfkyX4e3B/R+0x93D1n4s8Gv52/SWgKqWRRJpJ9WqazSpI52TOhdW1vReTdNpgEccVFC+CfgQp2IzcI/39uW/FUKel6wN+z95otmfe5vcQXRkqKCwj4bBShc6vfcAiR8wYrxgppb6/SIBW7ImJI3TrjJ+BBRYRqrm32sxAq26VWED86tHnhDjmpddQApjMrE4px4PQxBuF+90PZU+QsG9/LJu5pqunvD5FlA8ymxvgkMRvyARIqN+fzge8zXCfRvO3lCCtLOTlQKcXQRhQXxuoBzo8fok8CchKWc4mZYy2X/tOwIDAQAB";
|
||||
// 初始化请求、响应数据
|
||||
String req ="1111111111111";
|
||||
String resp ="22222222222222";
|
||||
|
||||
/**
|
||||
* (公钥加密,私钥解密;私钥签名,公钥验签。)
|
||||
* 模拟A公司向B公司发送请求
|
||||
* B公司验签后解密请求报文
|
||||
* B公司处理完自己的业务后
|
||||
* 并对响应报文加密和加签
|
||||
* A公司收到B公司响应后
|
||||
* A公司验签后解密响应
|
||||
*/
|
||||
//请求
|
||||
System.out.println("A向B 发送请求开始==========");
|
||||
System.out.println("加密前的请求数据为===>"+req);
|
||||
//用B公司的公钥加密请求
|
||||
String encryptByPublicKeyB = Rsa2Utils.buildRSAEncryptByPublicKey(req, publicKeyB);
|
||||
System.out.println("用B公司的公钥加密请求===>"+encryptByPublicKeyB);
|
||||
//用A公司的私钥生成签名
|
||||
String reqSignature = Rsa2Utils.buildRSASignByPrivateKey(encryptByPublicKeyB, privateKeyA);
|
||||
System.out.println("用A公司的私钥生成签名===>"+reqSignature);
|
||||
|
||||
System.out.println("B收到A的请求开始==========");
|
||||
//用A公司的公钥验证签名
|
||||
boolean SignatureBoolByPublicKeyA = Rsa2Utils.buildRSAverifyByPublicKey(encryptByPublicKeyB, publicKeyA, reqSignature);
|
||||
System.out.println("用A公司的公钥验证签名===>"+SignatureBoolByPublicKeyA);
|
||||
//用B公司的私钥解密请求
|
||||
String decryptByPrivateKeyB = Rsa2Utils.buildRSADecryptByPrivateKey(encryptByPublicKeyB, privateKeyB);
|
||||
System.out.println("用B公司的私钥解密请求===>"+decryptByPrivateKeyB);
|
||||
|
||||
|
||||
//响应
|
||||
System.out.println("B发送A的响应开始==========");
|
||||
System.out.println("加密前的响应数据为===>"+resp);
|
||||
//用A公司的公钥加密响应
|
||||
String encryptByPublicKeyA = Rsa2Utils.buildRSAEncryptByPublicKey(resp, publicKeyA);
|
||||
System.out.println("用A公司的公钥加密请求===>"+encryptByPublicKeyA);
|
||||
//用B公司的私钥生成签名
|
||||
String respSignature = Rsa2Utils.buildRSASignByPrivateKey(encryptByPublicKeyA, privateKeyB);
|
||||
System.out.println("用B公司的私钥生成签名===>"+respSignature);
|
||||
|
||||
System.out.println("A收到B的响应开始==========");
|
||||
//用B公司的公钥验证签名
|
||||
boolean SignatureBoolByPublicKeyB = Rsa2Utils.buildRSAverifyByPublicKey(encryptByPublicKeyA, publicKeyB, respSignature);
|
||||
System.out.println("用B公司的公钥验证签名===>"+SignatureBoolByPublicKeyB);
|
||||
//用A公司的私钥解密响应
|
||||
String decryptByPrivateKeyA = Rsa2Utils.buildRSADecryptByPrivateKey(encryptByPublicKeyA, privateKeyA);
|
||||
System.out.println("用A公司的私钥解密请求===>"+decryptByPrivateKeyA);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
package ai.remi.comm.util.security;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.*;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc Rsa2Utils
|
||||
*/
|
||||
public class Rsa2Utils {
|
||||
public static final String CHARSET = "UTF-8";
|
||||
//密钥算法
|
||||
public static final String ALGORITHM_RSA = "RSA";
|
||||
//RSA 签名算法
|
||||
public static final String ALGORITHM_RSA_SIGN = "SHA256WithRSA";
|
||||
public static final int ALGORITHM_RSA_PRIVATE_KEY_LENGTH = 2048;
|
||||
//用于封装随机产生的公钥与私钥
|
||||
private static Map<String, String> keyPairMap = new HashMap<String, String>();
|
||||
|
||||
private Rsa2Utils() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
//生成公钥私钥
|
||||
initRSAKey(2048);
|
||||
//私钥
|
||||
String privateKey = keyPairMap.get("privateKey");
|
||||
System.out.println("生成私钥:"+privateKey);
|
||||
//String privateKey="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDH4u7Shk55VvrOt7pEcyV4eLETNVgec1hGwtRc/YicYLNS8SSQlozESidoo7rh03QYHrEKswhltPfo63fOydkFWMAbX1M9VbJQin9OR2Xxu/OGBAUrXPArdfzlLpMtG+xsG2iapVBXOHx77E1DX0AehDw/jDGAawlOyqSavdWlVDDLQP2312FLvR9TjfBly9P3oQbbum9JrSwgkJM+rjwvElUC4Rs48w6l0RI+hJIe4RIoTyXefBQI4jNUrcVsJZpJEtp4khlmDJKx632fumg9zi4tctS/3MZTGoJaA1zei/UuzXD33nHsL+6ZAwCSayidLeXjKCn5CCAQO6PGKg2pAgMBAAECggEAA/+fJWj4YucBh2cXe80N39YSPL8hbyKBjpDRLLcJp48rpyoZwCay1C5jkW1NDL1tIhpjEkK4lUhEeU1UjTdudvPqi+GhSaXQNlo+57L38BkBaxnjfJWUXFo03+TJnupjFVqLAiTycXw4yUXV2dYq8zFT/zVvvBDoE8Jr/ohzoKYAb4ziahDPg7D15oaX8S6dAa/qBoSapLSO+rN6eELEqAaH8r4QVCtVpVzDLXcIpF9kgaJmAkJfrb9Cmi6dA7MBbwCjEK/mtyGJTZaw7ZTJR6+CumPmGZCjWppNawX/8ISYbZeAmX9mGSEp6iI17ciYZt79YfQGDOSvRgjBA67vrQKBgQDu4X5AVaxQwLKLFfuhoim9ZmB4/MAQntvCD5Oe7RQM/eepSX9LIuDcoj7tDjqCvPemEosLoR05WHxe8eLNFwcUzkiQFdhB2lNn02rBn8rCHTwZvf1WHwhkNTi0/szRgZexzRIqrUJrNlAiezsbcYxeoOsLqnKzQExNM4mTvPkcDwKBgQDWNgyPHTGXLpXjCPJ8HF92xveUOwXb+H8SVs5AVHwg+kJNvtjqAwASPnaBs9xMCEaXChF/2O04AUB29comjPZ3By0VmWiH3ngOucsIGih/9RDs0JTWaNaMDYvhA9huXKeIRpo8hyTLcLJ8Y0XVHgi8a68tnP+qfA5kANC17kDixwKBgQC9ayLQYt7rfZFWBo1soojUfZyb81ddFoqNfAqDZ9yRXLhYnUYJz1NcwG6DTNgKmUsKbgPBqfThZw/fvZApLkxYLCaP0jKULUobtyIc/NcrS0E/7E44/wyJYUZhBvZkXo1p2IHCQ3a6SuwKG4tH/Kbg1lrq+jJPfvnB8c7yzlL98wKBgBHKbWKZdxvqR5KSlKkQJjGUNs291S++xLb+NKUNHz4R6t3QIu3Uj/Xz8B23lW1PJqW2tVtb5d/H4tW+diqbfIDIPMtVcWQPawM2FRG4MnLmJKC2dhYUoPUCzwJCMoB/+34K9xZd1wV6d6hzCkK3+tvDScpNBsLfCMbvdmYVwpfTAoGBAMv12bQYwCK/EmezCl484AvNJ9/oIjqz1QyK141HXb5QXIXN4DL//TcngslDvqpqvoVXM4TRoc8Ne6aQeRaNgIeOuYeyixWi48x6csN5FC41lnUdEkPYIxqZCrkxglS8J/oLKRiu385gQPxd2EqVE21snajRoY3CiKXQkchxyycB";
|
||||
//公钥
|
||||
String publicKey = keyPairMap.get("publicKey");
|
||||
System.out.println("生成公钥:"+publicKey);
|
||||
//String publicKey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx+Lu0oZOeVb6zre6RHMleHixEzVYHnNYRsLUXP2InGCzUvEkkJaMxEonaKO64dN0GB6xCrMIZbT36Ot3zsnZBVjAG19TPVWyUIp/Tkdl8bvzhgQFK1zwK3X85S6TLRvsbBtomqVQVzh8e+xNQ19AHoQ8P4wxgGsJTsqkmr3VpVQwy0D9t9dhS70fU43wZcvT96EG27pvSa0sIJCTPq48LxJVAuEbOPMOpdESPoSSHuESKE8l3nwUCOIzVK3FbCWaSRLaeJIZZgySset9n7poPc4uLXLUv9zGUxqCWgNc3ov1Ls1w995x7C/umQMAkmsonS3l4ygp+QggEDujxioNqQIDAQAB";
|
||||
//私钥加密解密
|
||||
String encryptByPrivateKey = buildRSAEncryptByPrivateKey("ABCabc123测试", privateKey);
|
||||
System.out.println("私钥加密:"+encryptByPrivateKey);
|
||||
String decryptByPublicKey = buildRSADecryptByPublicKey(encryptByPrivateKey, publicKey);
|
||||
System.out.println("公钥解密:"+decryptByPublicKey);
|
||||
//公钥加密解密
|
||||
//String encryptByPublicKey = buildRSAEncryptByPublicKey("ABCabc123测试", publicKey);
|
||||
//System.out.println("公钥加密:"+encryptByPublicKey);
|
||||
//String decryptByPrivateKey = buildRSADecryptByPrivateKey(encryptByPublicKey, privateKey);
|
||||
//System.out.println("私钥解密:"+decryptByPrivateKey);
|
||||
//使用私钥生成数字签名
|
||||
String privateValue = buildRSASignByPrivateKey("ABCabc123测试", privateKey);
|
||||
System.out.println("私钥加签:"+privateValue);
|
||||
//使用公钥校验数字签名
|
||||
boolean publicValue = buildRSAverifyByPublicKey("ABCabc123测试", publicKey, privateValue);
|
||||
System.out.println("公钥验签:"+publicValue);
|
||||
|
||||
}
|
||||
/**
|
||||
* 初始化RSA算法密钥对
|
||||
*
|
||||
* @param keysize RSA1024已经不安全了,建议2048
|
||||
* @return 经过Base64编码后的公私钥Map, 键名分别为publicKey和privateKey
|
||||
*/
|
||||
public static void initRSAKey(int keysize) {
|
||||
if (keysize != ALGORITHM_RSA_PRIVATE_KEY_LENGTH) {
|
||||
throw new IllegalArgumentException("RSA1024已经不安全了,请使用" + ALGORITHM_RSA_PRIVATE_KEY_LENGTH + "初始化RSA密钥对");
|
||||
}
|
||||
//为RSA算法创建一个KeyPairGenerator对象
|
||||
KeyPairGenerator kpg;
|
||||
try {
|
||||
kpg = KeyPairGenerator.getInstance(ALGORITHM_RSA);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException("No such algorithm-->[" + ALGORITHM_RSA + "]");
|
||||
}
|
||||
//初始化KeyPairGenerator对象,不要被initialize()源码表面上欺骗,其实这里声明的size是生效的
|
||||
kpg.initialize(ALGORITHM_RSA_PRIVATE_KEY_LENGTH);
|
||||
//生成密匙对
|
||||
KeyPair keyPair = kpg.generateKeyPair();
|
||||
//得到公钥
|
||||
Key publicKey = keyPair.getPublic();
|
||||
String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
|
||||
//得到私钥
|
||||
Key privateKey = keyPair.getPrivate();
|
||||
String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
|
||||
keyPairMap.put("publicKey", publicKeyStr);
|
||||
keyPairMap.put("privateKey", privateKeyStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA算法公钥加密数据
|
||||
*
|
||||
* @param data 待加密的明文字符串
|
||||
* @param key RSA公钥字符串
|
||||
* @return RSA公钥加密后的经过Base64编码的密文字符串
|
||||
*/
|
||||
public static String buildRSAEncryptByPublicKey(String data, String key) {
|
||||
try {
|
||||
//通过X509编码的Key指令获得公钥对象
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
|
||||
Key publicKey = keyFactory.generatePublic(x509KeySpec);
|
||||
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
return Base64.getEncoder().encodeToString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET)));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* RSA算法公钥解密数据
|
||||
*
|
||||
* @param data 待解密的经过Base64编码的密文字符串
|
||||
* @param key RSA公钥字符串
|
||||
* @return RSA公钥解密后的明文字符串
|
||||
*/
|
||||
public static String buildRSADecryptByPublicKey(String data, String key) {
|
||||
try {
|
||||
//通过X509编码的Key指令获得公钥对象
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
|
||||
Key publicKey = keyFactory.generatePublic(x509KeySpec);
|
||||
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
|
||||
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
||||
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.getDecoder().decode(data)), CHARSET);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* RSA算法私钥加密数据
|
||||
*
|
||||
* @param data 待加密的明文字符串
|
||||
* @param key RSA私钥字符串
|
||||
* @return RSA私钥加密后的经过Base64编码的密文字符串
|
||||
*/
|
||||
public static String buildRSAEncryptByPrivateKey(String data, String key) {
|
||||
try {
|
||||
//通过PKCS#8编码的Key指令获得私钥对象
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
|
||||
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
|
||||
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
|
||||
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
||||
return Base64.getEncoder().encodeToString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET)));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* RSA算法私钥解密数据
|
||||
* @param data 待解密的经过Base64编码的密文字符串
|
||||
* @param key RSA私钥字符串
|
||||
* @return RSA私钥解密后的明文字符串
|
||||
*/
|
||||
public static String buildRSADecryptByPrivateKey(String data, String key) {
|
||||
try {
|
||||
//通过PKCS#8编码的Key指令获得私钥对象
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
|
||||
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
|
||||
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.getDecoder().decode(data)), CHARSET);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* RSA算法使用私钥对数据生成数字签名
|
||||
*
|
||||
* @param data 待签名的明文字符串
|
||||
* @param key RSA私钥字符串
|
||||
* @return RSA私钥签名后的经过Base64编码的字符串
|
||||
*/
|
||||
public static String buildRSASignByPrivateKey(String data, String key) {
|
||||
try {
|
||||
//通过PKCS#8编码的Key指令获得私钥对象
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
|
||||
Signature signature = Signature.getInstance(ALGORITHM_RSA_SIGN);
|
||||
signature.initSign(privateKey);
|
||||
signature.update(data.getBytes(CHARSET));
|
||||
return Base64.getEncoder().encodeToString(signature.sign());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("签名字符串[" + data + "]时遇到异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA算法使用公钥校验数字签名
|
||||
*
|
||||
* @param data 参与签名的明文字符串
|
||||
* @param key RSA公钥字符串
|
||||
* @param sign RSA签名得到的经过Base64编码的字符串
|
||||
* @return true--验签通过,false--验签未通过
|
||||
*/
|
||||
public static boolean buildRSAverifyByPublicKey(String data, String key, String sign) {
|
||||
try {
|
||||
//通过X509编码的Key指令获得公钥对象
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
|
||||
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
|
||||
Signature signature = Signature.getInstance(ALGORITHM_RSA_SIGN);
|
||||
signature.initVerify(publicKey);
|
||||
signature.update(data.getBytes(CHARSET));
|
||||
return signature.verify(Base64.getDecoder().decode(sign));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("验签字符串[" + data + "]时遇到异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA算法分段加解密数据
|
||||
*
|
||||
* @param cipher 初始化了加解密工作模式后的javax.crypto.Cipher对象
|
||||
* @param opmode 加解密模式,值为javax.crypto.Cipher.ENCRYPT_MODE/DECRYPT_MODE
|
||||
* @return 加密或解密后得到的数据的字节数组
|
||||
*/
|
||||
private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas) {
|
||||
int maxBlock = 0;
|
||||
if (opmode == Cipher.DECRYPT_MODE) {
|
||||
maxBlock = ALGORITHM_RSA_PRIVATE_KEY_LENGTH / 8;
|
||||
} else {
|
||||
maxBlock = ALGORITHM_RSA_PRIVATE_KEY_LENGTH / 8 - 11;
|
||||
}
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int offSet = 0;
|
||||
byte[] buff;
|
||||
int i = 0;
|
||||
try {
|
||||
while (datas.length > offSet) {
|
||||
if (datas.length - offSet > maxBlock) {
|
||||
buff = cipher.doFinal(datas, offSet, maxBlock);
|
||||
} else {
|
||||
buff = cipher.doFinal(datas, offSet, datas.length - offSet);
|
||||
}
|
||||
out.write(buff, 0, buff.length);
|
||||
i++;
|
||||
offSet = i * maxBlock;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
|
||||
}
|
||||
byte[] resultDatas = out.toByteArray();
|
||||
IOUtils.closeQuietly(out);
|
||||
return resultDatas;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package ai.remi.comm.util.string;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 字符集工具类
|
||||
*
|
||||
* @author startdis
|
||||
*/
|
||||
public class CharsetUtils {
|
||||
/**
|
||||
* ISO-8859-1
|
||||
*/
|
||||
public static final String ISO_8859_1 = "ISO-8859-1";
|
||||
/**
|
||||
* UTF-8
|
||||
*/
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
/**
|
||||
* GBK
|
||||
*/
|
||||
public static final String GBK = "GBK";
|
||||
|
||||
/**
|
||||
* ISO-8859-1
|
||||
*/
|
||||
public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
|
||||
/**
|
||||
* UTF-8
|
||||
*/
|
||||
public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
|
||||
/**
|
||||
* GBK
|
||||
*/
|
||||
public static final Charset CHARSET_GBK = Charset.forName(GBK);
|
||||
|
||||
/**
|
||||
* 转换为Charset对象
|
||||
*
|
||||
* @param charset 字符集,为空则返回默认字符集
|
||||
* @return Charset
|
||||
*/
|
||||
public static Charset charset(String charset) {
|
||||
return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换字符串的字符集编码
|
||||
*
|
||||
* @param source 字符串
|
||||
* @param srcCharset 源字符集,默认ISO-8859-1
|
||||
* @param destCharset 目标字符集,默认UTF-8
|
||||
* @return 转换后的字符集
|
||||
*/
|
||||
public static String convert(String source, String srcCharset, String destCharset) {
|
||||
return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换字符串的字符集编码
|
||||
*
|
||||
* @param source 字符串
|
||||
* @param srcCharset 源字符集,默认ISO-8859-1
|
||||
* @param destCharset 目标字符集,默认UTF-8
|
||||
* @return 转换后的字符集
|
||||
*/
|
||||
public static String convert(String source, Charset srcCharset, Charset destCharset) {
|
||||
if (null == srcCharset) {
|
||||
srcCharset = StandardCharsets.ISO_8859_1;
|
||||
}
|
||||
|
||||
if (null == destCharset) {
|
||||
destCharset = StandardCharsets.UTF_8;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) {
|
||||
return source;
|
||||
}
|
||||
return new String(source.getBytes(srcCharset), destCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 系统字符集编码
|
||||
*/
|
||||
public static String systemCharset() {
|
||||
return Charset.defaultCharset().name();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,868 @@
|
||||
package ai.remi.comm.util.string;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 类型转换器
|
||||
*
|
||||
* @author startdis
|
||||
*/
|
||||
public class StringConvertUtils {
|
||||
/**
|
||||
* 转换为字符串<br>
|
||||
* 如果给定的值为null,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static String toStr(Object value, String defaultValue) {
|
||||
if (null == value) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符串<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static String toStr(Object value) {
|
||||
return toStr(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符<br>
|
||||
* 如果给定的值为null,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Character toChar(Object value, Character defaultValue) {
|
||||
if (null == value) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Character) {
|
||||
return (Character) value;
|
||||
}
|
||||
|
||||
final String valueStr = toStr(value, null);
|
||||
return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Character toChar(Object value) {
|
||||
return toChar(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为byte<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Byte toByte(Object value, Byte defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Byte) {
|
||||
return (Byte) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).byteValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Byte.parseByte(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为byte<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Byte toByte(Object value) {
|
||||
return toByte(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Short<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Short toShort(Object value, Short defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Short) {
|
||||
return (Short) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).shortValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Short.parseShort(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Short<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Short toShort(Object value) {
|
||||
return toShort(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Number<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Number toNumber(Object value, Number defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return (Number) value;
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return NumberFormat.getInstance().parse(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Number<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Number toNumber(Object value) {
|
||||
return toNumber(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为int<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer toInt(Object value, Integer defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为int<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer toInt(Object value) {
|
||||
return toInt(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Integer数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer[] toIntArray(String str) {
|
||||
return toIntArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Long数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long[] toLongArray(String str) {
|
||||
return toLongArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Integer数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param split 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer[] toIntArray(String split, String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return new Integer[]{};
|
||||
}
|
||||
String[] arr = str.split(split);
|
||||
final Integer[] ints = new Integer[arr.length];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
final Integer v = toInt(arr[i], 0);
|
||||
ints[i] = v;
|
||||
}
|
||||
return ints;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Long数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long[] toLongArray(String split, String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return new Long[]{};
|
||||
}
|
||||
String[] arr = str.split(split);
|
||||
final Long[] longs = new Long[arr.length];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
final Long v = toLong(arr[i], null);
|
||||
longs[i] = v;
|
||||
}
|
||||
return longs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为String数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static String[] toStrArray(String str) {
|
||||
return toStrArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为String数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param split 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static String[] toStrArray(String split, String str) {
|
||||
return str.split(split);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转数组
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param str 字符串
|
||||
* @return
|
||||
*/
|
||||
public static List<String> strToList(String split, String str) {
|
||||
return StringUtils.isEmpty(str) ? null : Arrays.asList(str.split(split));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为long<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long toLong(Object value, Long defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
// 支持科学计数法
|
||||
return new BigDecimal(valueStr.trim()).longValue();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为long<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long toLong(Object value) {
|
||||
return toLong(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为double<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Double toDouble(Object value, Double defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
return (Double) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
// 支持科学计数法
|
||||
return new BigDecimal(valueStr.trim()).doubleValue();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为double<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Double toDouble(Object value) {
|
||||
return toDouble(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Float<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Float toFloat(Object value, Float defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Float) {
|
||||
return (Float) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).floatValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Float.parseFloat(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Float<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Float toFloat(Object value) {
|
||||
return toFloat(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为boolean<br>
|
||||
* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Boolean toBool(Object value, Boolean defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
valueStr = valueStr.trim().toLowerCase();
|
||||
switch (valueStr) {
|
||||
case "true":
|
||||
case "yes":
|
||||
case "ok":
|
||||
case "1":
|
||||
return true;
|
||||
case "false":
|
||||
case "no":
|
||||
case "0":
|
||||
return false;
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为boolean<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Boolean toBool(Object value) {
|
||||
return toBool(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Enum对象<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
*
|
||||
* @param clazz Enum的Class
|
||||
* @param value 值
|
||||
* @param defaultValue 默认值
|
||||
* @return Enum
|
||||
*/
|
||||
public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (clazz.isAssignableFrom(value.getClass())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
E myE = (E) value;
|
||||
return myE;
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Enum.valueOf(clazz, valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Enum对象<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
*
|
||||
* @param clazz Enum的Class
|
||||
* @param value 值
|
||||
* @return Enum
|
||||
*/
|
||||
public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) {
|
||||
return toEnum(clazz, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigInteger<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigInteger toBigInteger(Object value, BigInteger defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof BigInteger) {
|
||||
return (BigInteger) value;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return BigInteger.valueOf((Long) value);
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return new BigInteger(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigInteger<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigInteger toBigInteger(Object value) {
|
||||
return toBigInteger(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigDecimal<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof BigDecimal) {
|
||||
return (BigDecimal) value;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return new BigDecimal((Long) value);
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
return new BigDecimal((Double) value);
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return new BigDecimal((Integer) value);
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigDecimal<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigDecimal toBigDecimal(Object value) {
|
||||
return toBigDecimal(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为字符串<br>
|
||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String utf8Str(Object obj) {
|
||||
return str(obj, CharsetUtils.CHARSET_UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为字符串<br>
|
||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param charsetName 字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(Object obj, String charsetName) {
|
||||
return str(obj, Charset.forName(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为字符串<br>
|
||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param charset 字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(Object obj, Charset charset) {
|
||||
if (null == obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (obj instanceof String) {
|
||||
return (String) obj;
|
||||
} else if (obj instanceof byte[] || obj instanceof Byte[]) {
|
||||
if (obj instanceof byte[]) {
|
||||
return str((byte[]) obj, charset);
|
||||
} else {
|
||||
Byte[] bytes = (Byte[]) obj;
|
||||
int length = bytes.length;
|
||||
byte[] dest = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
dest[i] = bytes[i];
|
||||
}
|
||||
return str(dest, charset);
|
||||
}
|
||||
} else if (obj instanceof ByteBuffer) {
|
||||
return str((ByteBuffer) obj, charset);
|
||||
}
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将byte数组转为字符串
|
||||
*
|
||||
* @param bytes byte数组
|
||||
* @param charset 字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(byte[] bytes, String charset) {
|
||||
return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码字节码
|
||||
*
|
||||
* @param data 字符串
|
||||
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String str(byte[] data, Charset charset) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null == charset) {
|
||||
return new String(data);
|
||||
}
|
||||
return new String(data, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将编码的byteBuffer数据转换为字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @param charset 字符集,如果为空使用当前系统字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(ByteBuffer data, String charset) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str(data, Charset.forName(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将编码的byteBuffer数据转换为字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @param charset 字符集,如果为空使用当前系统字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(ByteBuffer data, Charset charset) {
|
||||
if (null == charset) {
|
||||
charset = Charset.defaultCharset();
|
||||
}
|
||||
return charset.decode(data).toString();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- 全角半角转换
|
||||
|
||||
/**
|
||||
* 半角转全角
|
||||
*
|
||||
* @param input String.
|
||||
* @return 全角字符串.
|
||||
*/
|
||||
public static String toSBC(String input) {
|
||||
return toSBC(input, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 半角转全角
|
||||
*
|
||||
* @param input String
|
||||
* @param notConvertSet 不替换的字符集合
|
||||
* @return 全角字符串.
|
||||
*/
|
||||
public static String toSBC(String input, Set<Character> notConvertSet) {
|
||||
char[] c = input.toCharArray();
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
if (null != notConvertSet && notConvertSet.contains(c[i])) {
|
||||
// 跳过不替换的字符
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c[i] == ' ') {
|
||||
c[i] = '\u3000';
|
||||
} else if (c[i] < '\177') {
|
||||
c[i] = (char) (c[i] + 65248);
|
||||
|
||||
}
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全角转半角
|
||||
*
|
||||
* @param input String.
|
||||
* @return 半角字符串
|
||||
*/
|
||||
public static String toDBC(String input) {
|
||||
return toDBC(input, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换全角为半角
|
||||
*
|
||||
* @param text 文本
|
||||
* @param notConvertSet 不替换的字符集合
|
||||
* @return 替换后的字符
|
||||
*/
|
||||
public static String toDBC(String text, Set<Character> notConvertSet) {
|
||||
char[] c = text.toCharArray();
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
if (null != notConvertSet && notConvertSet.contains(c[i])) {
|
||||
// 跳过不替换的字符
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c[i] == '\u3000') {
|
||||
c[i] = ' ';
|
||||
} else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {
|
||||
c[i] = (char) (c[i] - 65248);
|
||||
}
|
||||
}
|
||||
String returnString = new String(c);
|
||||
|
||||
return returnString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字金额大写转换 先写个完整的然后将如零拾替换成零
|
||||
*
|
||||
* @param n 数字
|
||||
* @return 中文大写数字
|
||||
*/
|
||||
public static String digitUppercase(double n) {
|
||||
String[] fraction = {"角", "分"};
|
||||
String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
|
||||
String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}};
|
||||
|
||||
String head = n < 0 ? "负" : "";
|
||||
n = Math.abs(n);
|
||||
|
||||
String s = "";
|
||||
for (int i = 0; i < fraction.length; i++) {
|
||||
s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
|
||||
}
|
||||
if (s.length() < 1) {
|
||||
s = "整";
|
||||
}
|
||||
int integerPart = (int) Math.floor(n);
|
||||
|
||||
for (int i = 0; i < unit[0].length && integerPart > 0; i++) {
|
||||
String p = "";
|
||||
for (int j = 0; j < unit[1].length && n > 0; j++) {
|
||||
p = digit[integerPart % 10] + unit[1][j] + p;
|
||||
integerPart = integerPart / 10;
|
||||
}
|
||||
s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
|
||||
}
|
||||
return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ai.remi.comm.util.string;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 字符串格式化
|
||||
*
|
||||
* @author startdis
|
||||
*/
|
||||
public class StringFormatterUtils {
|
||||
public static final String EMPTY_JSON = "{}";
|
||||
public static final char C_BACKSLASH = '\\';
|
||||
public static final char C_DELIM_START = '{';
|
||||
public static final char C_DELIM_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 strPattern 字符串模板
|
||||
* @param argArray 参数列表
|
||||
* @return 结果
|
||||
*/
|
||||
public static String format(final String strPattern, final Object... argArray) {
|
||||
if (StringUtils.isEmpty(strPattern) || ObjectUtils.isEmpty(argArray)) {
|
||||
return strPattern;
|
||||
}
|
||||
final int strPatternLength = strPattern.length();
|
||||
|
||||
// 初始化定义好的长度以获得更好的性能
|
||||
StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
|
||||
|
||||
int handledPosition = 0;
|
||||
int delimIndex;// 占位符所在位置
|
||||
for (int argIndex = 0; argIndex < argArray.length; argIndex++) {
|
||||
delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
|
||||
if (delimIndex == -1) {
|
||||
if (handledPosition == 0) {
|
||||
return strPattern;
|
||||
} else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
|
||||
sbuf.append(strPattern, handledPosition, strPatternLength);
|
||||
return sbuf.toString();
|
||||
}
|
||||
} else {
|
||||
if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {
|
||||
if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {
|
||||
// 转义符之前还有一个转义符,占位符依旧有效
|
||||
sbuf.append(strPattern, handledPosition, delimIndex - 1);
|
||||
sbuf.append(StringConvertUtils.utf8Str(argArray[argIndex]));
|
||||
handledPosition = delimIndex + 2;
|
||||
} else {
|
||||
// 占位符被转义
|
||||
argIndex--;
|
||||
sbuf.append(strPattern, handledPosition, delimIndex - 1);
|
||||
sbuf.append(C_DELIM_START);
|
||||
handledPosition = delimIndex + 1;
|
||||
}
|
||||
} else {
|
||||
// 正常占位符
|
||||
sbuf.append(strPattern, handledPosition, delimIndex);
|
||||
sbuf.append(StringConvertUtils.utf8Str(argArray[argIndex]));
|
||||
handledPosition = delimIndex + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加入最后一个占位符后所有的字符
|
||||
sbuf.append(strPattern, handledPosition, strPattern.length());
|
||||
|
||||
return sbuf.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,521 @@
|
||||
package ai.remi.comm.util.string;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author startdis
|
||||
*/
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/**
|
||||
* 空字符串
|
||||
*/
|
||||
private static final String NULLSTR = "";
|
||||
|
||||
/**
|
||||
* 下划线
|
||||
*/
|
||||
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) || NULLSTR.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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定字符串{@code str}中不包含{@code appendStr},则在{@code str}后追加{@code appendStr};
|
||||
* 如果已包含{@code appendStr},则在{@code str}后追加{@code otherwise}
|
||||
*
|
||||
* @param str 给定的字符串
|
||||
* @param appendStr 需要追加的内容
|
||||
* @param otherwise 当{@code appendStr}不满足时追加到{@code str}后的内容
|
||||
* @return 追加后的字符串
|
||||
*/
|
||||
public static String appendIfNotContain(String str, String appendStr, String otherwise) {
|
||||
if (isEmpty(str) || isEmpty(appendStr)) {
|
||||
return str;
|
||||
}
|
||||
if (str.contains(appendStr)) {
|
||||
return str.concat(otherwise);
|
||||
}
|
||||
return str.concat(appendStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否是数组类型(Java基本型别的数组)
|
||||
*
|
||||
* @param object 对象
|
||||
* @return true:是数组 false:不是数组
|
||||
*/
|
||||
public static boolean isArray(Object object) {
|
||||
return isNotNull(object) && object.getClass().isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 去空格
|
||||
*/
|
||||
public static String trim(String str) {
|
||||
return (str == null ? "" : str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义分割函数,返回第一个
|
||||
*
|
||||
* @param str 待分割的字符串
|
||||
* @param delim 分隔符
|
||||
* @return 分割后的第一个字符串
|
||||
*/
|
||||
public static String splitFirst(final String str, final String delim) {
|
||||
if (null == str || org.apache.commons.lang3.StringUtils.isEmpty(delim)) {
|
||||
return str;
|
||||
}
|
||||
|
||||
int index = str.indexOf(delim);
|
||||
if (index < 0) {
|
||||
return str;
|
||||
}
|
||||
if (index == 0) {
|
||||
// 一开始就是分隔符,返回空串
|
||||
return "";
|
||||
}
|
||||
return str.substring(0, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义分割函数,返回全部
|
||||
*
|
||||
* @param str 待分割的字符串
|
||||
* @param delim 分隔符
|
||||
* @return 分割后的返回结果
|
||||
*/
|
||||
public static List<String> splitStr(String str, final String delim) {
|
||||
if (null == str) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(delim)) {
|
||||
List<String> result = new ArrayList<>(1);
|
||||
result.add(str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
final List<String> stringList = new ArrayList<>();
|
||||
while (true) {
|
||||
int index = str.indexOf(delim);
|
||||
if (index < 0) {
|
||||
stringList.add(str);
|
||||
break;
|
||||
}
|
||||
stringList.add(str.substring(0, index));
|
||||
str = str.substring(index + delim.length());
|
||||
}
|
||||
return stringList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start) {
|
||||
if (str == null) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (start > str.length()) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
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 NULLSTR;
|
||||
}
|
||||
|
||||
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 NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = 0;
|
||||
}
|
||||
|
||||
return str.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为空,并且不是空白字符
|
||||
*
|
||||
* @param str 要判断的value
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean hasText(String str) {
|
||||
return (str != null && !str.isEmpty() && containsText(str));
|
||||
}
|
||||
|
||||
private static boolean containsText(CharSequence str) {
|
||||
int strLen = str.length();
|
||||
for (int i = 0; i < strLen; i++) {
|
||||
if (!Character.isWhitespace(str.charAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文本, {} 表示占位符<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 StringFormatterUtils.format(template, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线命名
|
||||
*/
|
||||
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);
|
||||
}
|
||||
// 用下划线将原始字符串分割
|
||||
List<String> camels = splitStr(name, "_");
|
||||
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 final 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 final 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ai.remi.comm.util.url;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 请求路径忽略工具类
|
||||
*/
|
||||
public class UrlIgnoreUtils {
|
||||
|
||||
private static final AntPathMatcher matcher = new AntPathMatcher();
|
||||
|
||||
public static boolean isIgnoreUrl(Set<String> ignoreUrl, String requestPath) {
|
||||
return ignoreUrl.stream().anyMatch((url) -> {
|
||||
//判断请求的url是否在忽略拦截的url中
|
||||
return matcher.match(url, requestPath);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static boolean isIgnoreUrl(List<String> ignoreUrl, String requestPath) {
|
||||
return ignoreUrl.stream().anyMatch((url) -> {
|
||||
//判断请求的url是否在忽略拦截的url中
|
||||
return matcher.match(url, requestPath);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ai.remi.comm.util.url;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 请求路径匹配工具类
|
||||
*/
|
||||
public class UrlMatchUtils {
|
||||
private static final AntPathMatcher matcher = new AntPathMatcher();
|
||||
|
||||
/**
|
||||
* 集合内所有正则是否包含 请求路径
|
||||
* @param patterns 所有正则
|
||||
* @param requestPath 请求路径
|
||||
* @return 返回是否在包含在内
|
||||
*/
|
||||
public static boolean contain(Collection<String> patterns, String requestPath){
|
||||
if (CollectionUtil.isEmpty(patterns)){
|
||||
return false;
|
||||
}
|
||||
return patterns.stream().anyMatch((pattern) -> {
|
||||
//判断请求的url是否在忽略拦截的url中
|
||||
return matcher.match(pattern, requestPath);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,550 @@
|
||||
package ai.remi.comm.util.xml;
|
||||
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.OutputFormat;
|
||||
import org.dom4j.io.SAXReader;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @date 2022-07-26
|
||||
* @desc DOMUtils
|
||||
*/
|
||||
public class DOMUtils {
|
||||
|
||||
/**
|
||||
* 添加根节点元素
|
||||
*
|
||||
* @param document 根文档
|
||||
* @param rootName 根节点名称
|
||||
* @return 新增节点
|
||||
*/
|
||||
public static Element addRoot(Document document, String rootName) {
|
||||
// 添加子节点元素
|
||||
return document.addElement(rootName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加根节点元素
|
||||
*
|
||||
* @param document 根文档
|
||||
* @param rootName 根节点名称
|
||||
* @param rootValue 根节点值
|
||||
* @return 新增节点
|
||||
*/
|
||||
public static Element addRoot(Document document, String rootName, String rootValue) {
|
||||
// 添加子节点元素
|
||||
Element root = document.addElement(rootName);
|
||||
// 为子节点元素设值
|
||||
root.setText(rootValue == null ? "" : rootValue);
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加根节点元素
|
||||
*
|
||||
* @param document 根文档
|
||||
* @param rootName 根节点名称
|
||||
* @param attributes 根节点属性
|
||||
* @return 新增节点
|
||||
*/
|
||||
public static Element addRoot(Document document, String rootName, Map<String, String> attributes) {
|
||||
// 添加子节点元素
|
||||
Element root = document.addElement(rootName);
|
||||
// 为子节点元素设属性
|
||||
if (MapUtils.isEmpty(attributes)) {
|
||||
return root;
|
||||
}
|
||||
attributes.forEach((key, value) -> {
|
||||
if (key.contains(":")) {
|
||||
//处理冒号属性问题
|
||||
root.addNamespace(key.substring(key.indexOf(":")+1), value);
|
||||
}
|
||||
root.addAttribute(key, value);
|
||||
});
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加根节点元素
|
||||
*
|
||||
* @param document 根文档
|
||||
* @param rootName 根节点名称
|
||||
* @param rootValue 根节点值
|
||||
* @param attributes 根节点属性
|
||||
* @return 新增节点
|
||||
*/
|
||||
public static Element addRoot(Document document, String rootName, String rootValue, Map<String, String> attributes) {
|
||||
// 添加子节点元素
|
||||
Element root = document.addElement(rootName);
|
||||
// 为子节点元素设值
|
||||
root.setText(rootValue == null ? "" : rootValue);
|
||||
// 为子节点元素设属性
|
||||
if (MapUtils.isEmpty(attributes)) {
|
||||
return root;
|
||||
}
|
||||
attributes.forEach((key, value) -> {
|
||||
if (key.contains(":")) {
|
||||
//处理冒号属性问题
|
||||
root.addNamespace(key.substring(key.indexOf(":")+1), value);
|
||||
}
|
||||
root.addAttribute(key, value);
|
||||
});
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加孩子节点元素
|
||||
*
|
||||
* @param parent 父节点
|
||||
* @param childName 孩子节点名称
|
||||
* @return 新增节点
|
||||
*/
|
||||
public static Element addChild(Element parent, String childName) {
|
||||
// 添加子节点元素
|
||||
return parent.addElement(childName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加孩子节点元素
|
||||
*
|
||||
* @param parent 父节点
|
||||
* @param childName 孩子节点名称
|
||||
* @param childValue 孩子节点值
|
||||
* @return 新增节点
|
||||
*/
|
||||
public static Element addChild(Element parent, String childName, String childValue) {
|
||||
// 添加子节点元素
|
||||
Element child = parent.addElement(childName);
|
||||
// 为子节点元素设值
|
||||
child.setText(childValue == null ? "" : childValue);
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加孩子节点元素
|
||||
*
|
||||
* @param parent 父节点
|
||||
* @param childName 孩子节点名称
|
||||
* @param attributes 孩子节点属性
|
||||
* @return 新增节点
|
||||
*/
|
||||
public static Element addChild(Element parent, String childName, Map<String, String> attributes) {
|
||||
// 添加子节点元素
|
||||
Element child = parent.addElement(childName);
|
||||
// 为子节点元素设属性
|
||||
if (MapUtils.isEmpty(attributes)) {
|
||||
return child;
|
||||
}
|
||||
attributes.forEach((key, value) -> {
|
||||
if (key.contains(":")) {
|
||||
//处理冒号属性问题
|
||||
//child.addNamespace(key.substring(key.indexOf(":")+1), value);
|
||||
}
|
||||
child.addAttribute(key, value);
|
||||
});
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加孩子节点元素
|
||||
*
|
||||
* @param parent 父节点
|
||||
* @param childName 孩子节点名称
|
||||
* @param childValue 孩子节点值
|
||||
* @param attributes 孩子节点属性
|
||||
* @return 新增节点
|
||||
*/
|
||||
public static Element addChild(Element parent, String childName, String childValue, Map<String, String> attributes) {
|
||||
// 添加子节点元素
|
||||
Element child = parent.addElement(childName);
|
||||
// 为子节点元素设值
|
||||
child.setText(childValue == null ? "" : childValue);
|
||||
// 为子节点元素设属性
|
||||
if (MapUtils.isEmpty(attributes)) {
|
||||
return child;
|
||||
}
|
||||
attributes.forEach((key, value) -> {
|
||||
if (key.contains(":")) {
|
||||
//处理冒号属性问题
|
||||
//child.addNamespace(key.substring(key.indexOf(":")+1), value);
|
||||
}
|
||||
child.addAttribute(key, value);
|
||||
});
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文档对象写入对应的文件中
|
||||
*
|
||||
* @param xmlstr 字符串
|
||||
* @param path 写入文档的路径
|
||||
* @throws IOException
|
||||
*/
|
||||
public final static void writeXmlStrToFile(String xmlstr, String path) throws Exception {
|
||||
if (StringUtils.isBlank(xmlstr)) {
|
||||
return;
|
||||
}
|
||||
Document document = DocumentHelper.parseText(xmlstr);
|
||||
writeXMLToFile(document, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文档对象写入对应的文件中
|
||||
*
|
||||
* @param document 文档对象
|
||||
* @param path 写入文档的路径
|
||||
* @throws IOException
|
||||
*/
|
||||
public final static void writeXMLToFile(Document document, String path) throws IOException {
|
||||
if (document == null || path == null) {
|
||||
return;
|
||||
}
|
||||
XMLWriter writer = new XMLWriter(new FileWriter(path));
|
||||
writer.write(document);
|
||||
writer.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文档对象写入对应的文件中
|
||||
*
|
||||
* @param document 文档对象
|
||||
* @param path 写入文档的路径
|
||||
* @throws IOException
|
||||
*/
|
||||
public final static void writeXMLToFile(Document document, String path, String encoding) throws IOException {
|
||||
if (document == null || path == null) {
|
||||
return;
|
||||
}
|
||||
// 设置生成xml的格式
|
||||
OutputFormat format = OutputFormat.createPrettyPrint();
|
||||
encoding = StringUtils.isBlank(encoding) ? "UTF-8" : encoding;
|
||||
// 设置编码格式
|
||||
format.setEncoding(encoding);
|
||||
XMLWriter writer = new XMLWriter(new FileWriter(path), format);
|
||||
writer.write(document);
|
||||
writer.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过文件的路径获取xml的document对象
|
||||
*
|
||||
* @param path 文件的路径
|
||||
* @return 返回文档对象
|
||||
*/
|
||||
public static Document getXMLByFilePath(String path) {
|
||||
if (null == path) {
|
||||
return null;
|
||||
}
|
||||
Document document = null;
|
||||
try {
|
||||
SAXReader reader = new SAXReader();
|
||||
document = reader.read(new File(path));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过xml字符获取document文档
|
||||
*
|
||||
* @param xmlstr 要序列化的xml字符
|
||||
* @return 返回文档对象
|
||||
* @throws DocumentException
|
||||
*/
|
||||
public static Document getXMLByString(String xmlstr) throws DocumentException {
|
||||
if (xmlstr == "" || xmlstr == null) {
|
||||
return null;
|
||||
}
|
||||
Document document = DocumentHelper.parseText(xmlstr);
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个元素的所有的子节点
|
||||
*
|
||||
* @param node 制定节点
|
||||
* @return 返回所有的子节点
|
||||
*/
|
||||
public static List<Element> getChildElements(Element node) {
|
||||
if (null == node) {
|
||||
return null;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> lists = node.elements();
|
||||
return lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定节点的子节点
|
||||
*
|
||||
* @param node 父节点
|
||||
* @param childnode 指定名称的子节点
|
||||
* @return 返回指定的子节点
|
||||
*/
|
||||
public static Element getChildElement(Element node, String childnode) {
|
||||
if (null == node || null == childnode || "".equals(childnode)) {
|
||||
return null;
|
||||
}
|
||||
return node.element(childnode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的属性值
|
||||
*
|
||||
* @param node
|
||||
* @param arg
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, String> getAttributes(Element node, String... arg) {
|
||||
if (node == null || arg.length == 0) {
|
||||
return null;
|
||||
}
|
||||
Map<String, String> attrMap = new HashMap<String, String>();
|
||||
for (String attr : arg) {
|
||||
String attrValue = node.attributeValue(attr);
|
||||
attrMap.put(attr, attrValue);
|
||||
}
|
||||
return attrMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取element的单个属性
|
||||
*
|
||||
* @param node 需要获取属性的节点对象
|
||||
* @param attr 需要获取的属性值
|
||||
* @return 返回属性的值
|
||||
*/
|
||||
public static String getAttribute(Element node, String attr) {
|
||||
if (null == node || attr == null || "".equals(attr)) {
|
||||
return "";
|
||||
}
|
||||
return node.attributeValue(attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* DOM4j的Document对象转为XML报文串
|
||||
*
|
||||
* @param document
|
||||
* @param charset
|
||||
* @return 经过解析后的xml字符串
|
||||
*/
|
||||
public static String documentToString(Document document, String charset) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
OutputFormat format = OutputFormat.createPrettyPrint();// 获得格式化输出流
|
||||
format.setEncoding(charset);// 设置字符集,默认为UTF-8
|
||||
XMLWriter xmlWriter = new XMLWriter(stringWriter, format);// 写文件流
|
||||
try {
|
||||
xmlWriter.write(document);
|
||||
xmlWriter.flush();
|
||||
xmlWriter.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉声明头的(即<?xml...?>去掉)
|
||||
*
|
||||
* @param document
|
||||
* @param charset
|
||||
* @return
|
||||
*/
|
||||
public static String documentToStringNoDeclaredHeader(Document document, String charset) {
|
||||
String xml = documentToString(document, charset);
|
||||
return xml.replaceFirst("\\s*<[^<>]+>\\s*", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析XML为Document对象
|
||||
*
|
||||
* @param xml 被解析的XMl
|
||||
* @return Document
|
||||
*/
|
||||
public final static Element parseXml(String xml) {
|
||||
StringReader sr = new StringReader(xml);
|
||||
SAXReader saxReader = new SAXReader();
|
||||
Document document = null;
|
||||
try {
|
||||
document = saxReader.read(sr);
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Element rootElement = document != null ? document.getRootElement() : null;
|
||||
return rootElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取element对象的text的值
|
||||
*
|
||||
* @param e 节点的对象
|
||||
* @param tac 节点的tac
|
||||
* @return
|
||||
*/
|
||||
public final static String getText(Element e, String tac) {
|
||||
Element _e = e.element(tac);
|
||||
if (_e != null) {
|
||||
return _e.getText();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取去除空格的字符串
|
||||
*
|
||||
* @param e
|
||||
* @param tac
|
||||
* @return
|
||||
*/
|
||||
public final static String getTextTrim(Element e, String tac) {
|
||||
Element _e = e.element(tac);
|
||||
if (_e != null) {
|
||||
return _e.getTextTrim();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点值.节点必须不能为空,否则抛错
|
||||
*
|
||||
* @param parent 父节点
|
||||
* @param tac 想要获取的子节点
|
||||
* @return 返回子节点
|
||||
*/
|
||||
public final static String getTextTrimNotNull(Element parent, String tac) {
|
||||
Element e = parent.element(tac);
|
||||
if (e == null) {
|
||||
throw new NullPointerException("节点为空");
|
||||
} else {
|
||||
return e.getTextTrim();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点必须不能为空,否则抛错
|
||||
*
|
||||
* @param parent 父节点
|
||||
* @param tac 想要获取的子节点
|
||||
* @return 子节点
|
||||
*/
|
||||
public final static Element elementNotNull(Element parent, String tac) {
|
||||
Element e = parent.element(tac);
|
||||
if (e == null) {
|
||||
throw new NullPointerException("节点为空");
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象直接转换成String类型的 XML输出
|
||||
*
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
public static String convertToXml(Object obj) {
|
||||
// 创建输出流
|
||||
StringWriter sw = new StringWriter();
|
||||
try {
|
||||
// 利用jdk中自带的转换类实现
|
||||
JAXBContext context = JAXBContext.newInstance(obj.getClass());
|
||||
|
||||
Marshaller marshaller = context.createMarshaller();
|
||||
// 格式化xml输出的格式
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
|
||||
Boolean.TRUE);
|
||||
// 将对象转换成输出流形式的xml
|
||||
marshaller.marshal(obj, sw);
|
||||
} catch (JAXBException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象根据路径转换成xml文件
|
||||
*
|
||||
* @param obj
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public static void convertToXml(Object obj, String path) {
|
||||
try {
|
||||
// 利用jdk中自带的转换类实现
|
||||
JAXBContext context = JAXBContext.newInstance(obj.getClass());
|
||||
|
||||
Marshaller marshaller = context.createMarshaller();
|
||||
// 格式化xml输出的格式
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
|
||||
Boolean.TRUE);
|
||||
// 将对象转换成输出流形式的xml
|
||||
// 创建输出流
|
||||
FileWriter fw = null;
|
||||
try {
|
||||
fw = new FileWriter(path);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
marshaller.marshal(obj, fw);
|
||||
} catch (JAXBException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String类型的xml转换成对象
|
||||
*/
|
||||
public static Object convertXmlStrToObject(Class clazz, String xmlStr) {
|
||||
Object xmlObject = null;
|
||||
try {
|
||||
JAXBContext context = JAXBContext.newInstance(clazz);
|
||||
// 进行将Xml转成对象的核心接口
|
||||
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
StringReader sr = new StringReader(xmlStr);
|
||||
xmlObject = unmarshaller.unmarshal(sr);
|
||||
} catch (JAXBException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return xmlObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将file类型的xml转换成对象
|
||||
*/
|
||||
public static Object convertXmlFileToObject(Class clazz, String xmlPath) {
|
||||
Object xmlObject = null;
|
||||
try {
|
||||
JAXBContext context = JAXBContext.newInstance(clazz);
|
||||
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
FileReader fr = null;
|
||||
try {
|
||||
fr = new FileReader(xmlPath);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
xmlObject = unmarshaller.unmarshal(fr);
|
||||
} catch (JAXBException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return xmlObject;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user