This commit is contained in:
2026-06-20 00:16:23 +08:00
commit d69b9c940c
844 changed files with 52164 additions and 0 deletions

40
common/README.md Normal file
View File

@@ -0,0 +1,40 @@
# common —— 公共工具库
## 概述
common 是 framework 的公共模块,提供无第三方框架依赖的核心工具类和常量定义,被所有其他模块依赖。
## 核心能力
### 工具类
- **IdWorker** —— 分布式 ID 生成器(雪花算法)
- **Assert** —— 断言工具(参数校验、业务异常抛出)
- **BeanUtil / CollectionUtil / StringUtil / NumberUtil / ArrayUtil / ObjectUtil / DateUtil / FileUtil** —— 常用操作工具
- **RegUtil** —— 正则匹配工具(预置常用正则 PatternPool
- **XmlUtil / ZipUtil** —— XML 解析与 ZIP 压缩
- **MailUtil** —— 邮件发送
- **ReflectUtil** —— 反射操作
- **ThreadUtil** —— 线程工具
- **Locker** — 分布式锁接口定义
### 异常体系
- **BaseException** —— 异常基类
- **ClientException / SysException** —— 客户端异常 / 系统异常
- **AccessDeniedException / AuthExpiredException / InputErrorException / UserLoginException / RepeatRequestException** 等具体异常
### 常量
- **StringPool** —— 字符串常量池
- **PatternPool** —— 正则表达式常量
- **ResponseConstants** —— 响应状态码常量
### 函数式接口
- **SFunction** —— 支持 Lambda 方法引用的函数接口(用于 MyBatis-Plus Lambda 查询)
## 依赖
- Hutool 5.7.17
- SLF4J + Logback
- Lombok
- HttpClient
- JavaMail
- dom4j

77
common/pom.xml Normal file
View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lframework</groupId>
<artifactId>framework</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>common</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,145 @@
package com.lframework.starter.common.constants;
import java.util.regex.Pattern;
/**
* 正则表达式工具类
*
* @author zmj
*/
public class PatternPool extends cn.hutool.core.lang.PatternPool {
/**
* 不包含+86的手机号码
*/
public static final String PATTERN_STR_CN_TEL = "^1[3-9]\\d{9}$";
public static final Pattern PATTERN_CN_TEL = Pattern.compile(PATTERN_STR_CN_TEL);
/**
* 电子邮箱
*/
public static final String PATTERN_STR_EMAIL = "^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$";
public static final Pattern PATTERN_EMAIL = Pattern.compile(PATTERN_STR_EMAIL);
/**
* 5-16位密码 只允许包含大写字母、小写字母、数字、下划线
*/
public static final String PATTERN_STR_PASSWORD = "^[a-zA-Z0-9_]{5,16}$";
public static final Pattern PATTERN_PASSWORD = Pattern.compile(PATTERN_STR_PASSWORD);
/**
* 是否 整数
*/
public static final String PATTERN_STR_IS_INTEGER = "^(-?[1-9]\\d*|[0])$";
public static final Pattern PATTERN_IS_INTEGER = Pattern.compile(PATTERN_STR_IS_INTEGER);
/**
* 是否 正整数
*/
public static final String PATTERN_STR_IS_INTEGER_GT_ZERO = "^[1-9]\\d*$";
public static final Pattern PATTERN_IS_INTEGER_GT_ZERO = Pattern.compile(
PATTERN_STR_IS_INTEGER_GT_ZERO);
/**
* 是否 负整数
*/
public static final String PATTERN_STR_IS_INTEGER_LT_ZERO = "^-[1-9]\\d*$";
public static final Pattern PATTERN_IS_INTEGER_LT_ZERO = Pattern.compile(
PATTERN_STR_IS_INTEGER_LT_ZERO);
/**
* 是否 非正整数 <=0
*/
public static final String PATTERN_STR_IS_INTEGER_LE_ZERO = "^(-[1-9]\\d*|[0]{1})$";
public static final Pattern PATTERN_IS_INTEGER_LE_ZERO = Pattern.compile(
PATTERN_STR_IS_INTEGER_LE_ZERO);
/**
* 是否 非负整数 >=0
*/
public static final String PATTERN_STR_IS_INTEGER_GE_ZERO = "^([1-9]\\d*|[0]{1})$";
public static final Pattern PATTERN_IS_INTEGER_GE_ZERO = Pattern.compile(
PATTERN_STR_IS_INTEGER_GE_ZERO);
/**
* 是否 浮点数
*/
public static final String PATTERN_STR_IS_FLOAT = "^((-?([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0))|((-?[1-9]\\d*|[0])))$";
public static final Pattern PATTERN_IS_FLOAT = Pattern.compile(PATTERN_STR_IS_FLOAT);
/**
* 是否 正浮点数
*/
public static final String PATTERN_STR_IS_FLOAT_GT_ZERO = "^(([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*)|([1-9]\\d*))$";
public static final Pattern PATTERN_IS_FLOAT_GT_ZERO = Pattern.compile(
PATTERN_STR_IS_FLOAT_GT_ZERO);
/**
* 是否 负浮点数
*/
public static final String PATTERN_STR_IS_FLOAT_LT_ZERO = "^((-([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*))|(-[1-9]\\d*))$";
public static final Pattern PATTERN_IS_FLOAT_LT_ZERO = Pattern.compile(
PATTERN_STR_IS_FLOAT_LT_ZERO);
/**
* 是否 非正浮点数 <= 0
*/
public static final String PATTERN_STR_IS_FLOAT_LE_ZERO = "^(((-([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*))|0?\\.0+|0)|(-[1-9]\\d*))$";
public static final Pattern PATTERN_IS_FLOAT_LE_ZERO = Pattern.compile(
PATTERN_STR_IS_FLOAT_LE_ZERO);
/**
* 是否 非负浮点数 >= 0
*/
public static final String PATTERN_STR_IS_FLOAT_GE_ZERO = "^(([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0)|([1-9]\\d*))$";
public static final Pattern PATTERN_IS_FLOAT_GE_ZERO = Pattern.compile(
PATTERN_STR_IS_FLOAT_GE_ZERO);
/**
* 是否 数字组成
*/
public static final String PATTERN_STR_IS_NUMBERIC = "^[0-9]*$";
public static final Pattern PATTERN_IS_NUMBERIC = Pattern.compile(PATTERN_STR_IS_NUMBERIC);
/**
* 是否 价格 大于或等于0的两位小数
*/
public static final String PATTERN_STR_IS_PRICE = "(^[1-9]([0-9]+)?(\\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\\.[0-9]([0-9])?$)";
public static final Pattern PATTERN_IS_PRICE = Pattern.compile(PATTERN_STR_IS_PRICE);
/**
* ip地址
*/
public static final String PATTERN_STR_IP_ADDRESS = "^((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}$";
public static final Pattern PATTERN_IP_ADDRESS = Pattern.compile(PATTERN_STR_IP_ADDRESS);
/**
* Http Url链接
*/
public static final String PATTERN_STR_HTTP_URL = "^(https?|http)://((?!(\\?)).)*$";
public static final Pattern PATTERN_HTTP_URL = Pattern.compile(PATTERN_STR_HTTP_URL);
/**
* 编号
*/
public static final String PATTERN_STR_CODE = "^[-_.A-Za-z0-9]{1,20}$";
public static final Pattern PATTERN_CODE = Pattern.compile(PATTERN_STR_CODE);
}

View File

@@ -0,0 +1,79 @@
package com.lframework.starter.common.constants;
/**
* 响应常量
*
* @author zmj
*/
public class ResponseConstants {
/**
* 响应成功状态码
*/
public static final Integer INVOKE_RESULT_SUCCESS_CODE = 200;
/**
* 响应失败状态码-传入参数错误
*/
public static final Integer INVOKE_RESULT_FAIL_CODE_INPUT_ERROR = 400;
/**
* 响应失败状态码-无登录状态
*/
public static final Integer INVOKE_RESULT_FAIL_CODE_AUTH_EXPIRED = 401;
/**
* 响应失败状态码-无权限
*/
public static final Integer INVOKE_RESULT_FAIL_CODE_ACCESS_DENIED = 403;
/**
* 响应失败状态码-重复请求
*/
public static final Integer INVOKE_RESULT_FAIL_CODE_REPEAT_REQUEST = 410;
/**
* 响应失败状态码-登录失败
*/
public static final Integer INVOKE_RESULT_FAIL_USER_LOGIN_FAIL = 419;
/**
* 响应失败状态码
*/
public static final Integer INVOKE_RESULT_FAIL_CODE = 500;
/**
* 响应成功信息
*/
public static final String INVOKE_RESULT_SUCCESS_MSG = "success";
/**
* 默认响应失败信息
*/
public static final String INVOKE_RESULT_FAIL_MSG = "fail";
/**
* 响应成功信息
*/
public static final String INVOKE_RESULT_ERROR_MSG = "系统出现内部错误,请联系系统管理员!";
/**
* 响应失败信息-传入参数错误
*/
public static final String INVOKE_RESULT_ERROR_MSG_INPUT_ERROR = "传入参数有误!";
/**
* 响应失败信息-无登录状态
*/
public static final String INVOKE_RESULT_ERROR_MSG_AUTH_EXPIRED = "请重新登录!";
/**
* 响应失败信息-无权限
*/
public static final String INVOKE_RESULT_ERROR_MSG_ACCESS_DENIED = "无系统权限!";
/**
* 响应失败信息-重复请求
*/
public static final String INVOKE_RESULT_ERROR_MSG_REPEAT_REQUEST = "请求过于频繁,请稍后再试!";
}

View File

@@ -0,0 +1,108 @@
package com.lframework.starter.common.constants;
/**
* 常量池
*
* @author zmj
*/
public interface StringPool {
/**
* 空格
*/
String SPACE = " ";
/**
* 单元格占位符
*/
String CELL_PLACEHOLDER = "-";
/**
* 空字符串
*/
String EMPTY_STR = "";
/**
* 字符串分隔符
*/
String STR_SPLIT = ",";
/**
* 中文字符串分隔符
*/
String STR_SPLIT_CN = "";
/**
* 城市名称分隔符
*/
String CITY_SPLIT = "/";
/**
* utf-8字符集
*/
String CHARACTER_ENCODING_UTF_8 = "utf-8";
/**
* Excel中的日期格式
*/
String EXCEL_DATE_PATTERN = "yyyy/MM/dd";
/**
* 日期格式
*/
String DATE_PATTERN = "yyyy-MM-dd";
/**
* 时间格式
*/
String TIME_PATTERN = "HH:mm:ss";
/**
* 日期时间格式
*/
String DATE_TIME_PATTERN = DATE_PATTERN + SPACE + TIME_PATTERN;
/**
* 年月日时
*/
String DATE_TIME_HOUR_PATTER = DATE_PATTERN + SPACE + "HH";
/**
* 小数点
*/
String DECIMAL_POINT = ".";
/**
* 零
*/
String ZERO = "0";
/**
* 登录验证码在redis中的key值
*/
String LOGIN_CAPTCHA_KEY = "login_captcha_key_{}";
/**
* 请求ID再Header中的key值
*/
String HEADER_NAME_REQUEST_ID = "Request-Id";
/**
* 数据字典分隔符
*/
String DATA_DIC_SPLIT = "@";
/**
* 加密字符
*/
String ENCRYPT_STR = "*";
/**
* 租户ID在Qrtz中的Key
*/
String TENANT_ID_QRTZ = "__tenantId";
String Y = "Y";
String N = "N";
}

View File

@@ -0,0 +1,37 @@
package com.lframework.starter.common.exceptions;
/**
* 系统内异常基类
*
* @author zmj
*/
public abstract class BaseException extends RuntimeException {
/**
* 响应码
*/
private Integer code;
/**
* 响应信息
*/
private String msg;
public BaseException(Integer code, String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}

View File

@@ -0,0 +1,14 @@
package com.lframework.starter.common.exceptions;
/**
* 由于客户端请求错误导致的异常 用于表示需要返回信息至前端的异常
*
* @author zmj
*/
public abstract class ClientException extends BaseException {
public ClientException(Integer code, String msg) {
super(code, msg);
}
}

View File

@@ -0,0 +1,14 @@
package com.lframework.starter.common.exceptions;
/**
* 由于系统内部错误导致的异常 用于表示程序运行错误或其他情况导致的不能将错误信息返回前端的异常
*
* @author zmj
*/
public abstract class SysException extends BaseException {
public SysException(Integer code, String msg) {
super(code, msg);
}
}

View File

@@ -0,0 +1,23 @@
package com.lframework.starter.common.exceptions.impl;
import com.lframework.starter.common.constants.ResponseConstants;
import com.lframework.starter.common.exceptions.ClientException;
/**
* 由于无权限导致的异常
*
* @author zmj
*/
public class AccessDeniedException extends ClientException {
public AccessDeniedException() {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE_ACCESS_DENIED,
ResponseConstants.INVOKE_RESULT_ERROR_MSG_ACCESS_DENIED);
}
public AccessDeniedException(String msg) {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE_ACCESS_DENIED, msg);
}
}

View File

@@ -0,0 +1,18 @@
package com.lframework.starter.common.exceptions.impl;
import com.lframework.starter.common.constants.ResponseConstants;
import com.lframework.starter.common.exceptions.ClientException;
/**
* 登录状态过期导致的异常
*
* @author zmj
*/
public class AuthExpiredException extends ClientException {
public AuthExpiredException() {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE_AUTH_EXPIRED,
ResponseConstants.INVOKE_RESULT_ERROR_MSG_AUTH_EXPIRED);
}
}

View File

@@ -0,0 +1,17 @@
package com.lframework.starter.common.exceptions.impl;
import com.lframework.starter.common.constants.ResponseConstants;
import com.lframework.starter.common.exceptions.ClientException;
/**
* 自定义消息的异常
*
* @author zmj
*/
public class DefaultClientException extends ClientException {
public DefaultClientException(String msg) {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE, msg);
}
}

View File

@@ -0,0 +1,22 @@
package com.lframework.starter.common.exceptions.impl;
import com.lframework.starter.common.constants.ResponseConstants;
import com.lframework.starter.common.exceptions.SysException;
/**
* 自定义消息的系统异常
*
* @author zmj
*/
public class DefaultSysException extends SysException {
public DefaultSysException() {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE, ResponseConstants.INVOKE_RESULT_ERROR_MSG);
}
public DefaultSysException(String msg) {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE, msg);
}
}

View File

@@ -0,0 +1,23 @@
package com.lframework.starter.common.exceptions.impl;
import com.lframework.starter.common.constants.ResponseConstants;
import com.lframework.starter.common.exceptions.ClientException;
/**
* 由于客户端传入参数错误导致的异常
*
* @author zmj
*/
public class InputErrorException extends ClientException {
public InputErrorException() {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE_INPUT_ERROR,
ResponseConstants.INVOKE_RESULT_ERROR_MSG_INPUT_ERROR);
}
public InputErrorException(String msg) {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE_INPUT_ERROR, msg);
}
}

View File

@@ -0,0 +1,11 @@
package com.lframework.starter.common.exceptions.impl;
public class ParameterNotFoundException extends DefaultSysException {
public ParameterNotFoundException() {
}
public ParameterNotFoundException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,18 @@
package com.lframework.starter.common.exceptions.impl;
import com.lframework.starter.common.constants.ResponseConstants;
import com.lframework.starter.common.exceptions.ClientException;
/**
* 重复请求异常
*
* @author zmj
*/
public class RepeatRequestException extends ClientException {
public RepeatRequestException() {
super(ResponseConstants.INVOKE_RESULT_FAIL_CODE_REPEAT_REQUEST,
ResponseConstants.INVOKE_RESULT_ERROR_MSG_REPEAT_REQUEST);
}
}

View File

@@ -0,0 +1,12 @@
package com.lframework.starter.common.exceptions.impl;
import com.lframework.starter.common.constants.ResponseConstants;
import com.lframework.starter.common.exceptions.ClientException;
public class UserLoginException extends ClientException {
public UserLoginException(String msg) {
super(ResponseConstants.INVOKE_RESULT_FAIL_USER_LOGIN_FAIL, msg);
}
}

View File

@@ -0,0 +1,17 @@
package com.lframework.starter.common.functions;
import com.lframework.starter.common.utils.ReflectUtil;
import java.io.Serializable;
import java.util.function.Function;
/**
* 主要用于根据方法名获取对应的属性名
*
* @author zmj
* @see ReflectUtil
*/
@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}

View File

@@ -0,0 +1,14 @@
package com.lframework.starter.common.locker;
public interface LockBuilder {
/**
* 构建锁
*
* @param lockName 锁名称
* @param expireTime 过期时间毫秒只有Redis锁会生效
* @param waitTime 等待时间毫秒只有Redis锁会生效
* @return
*/
Locker buildLocker(String lockName, long expireTime, long waitTime);
}

View File

@@ -0,0 +1,8 @@
package com.lframework.starter.common.locker;
public interface Locker extends AutoCloseable {
boolean lock();
boolean unLock();
}

View File

@@ -0,0 +1,12 @@
package com.lframework.starter.common.utils;
/**
* 数组工具类
* 基于HuTool的ArrayUtil进行扩展提供数组操作相关的工具方法
* 包括数组判空、转换、查找、排序等功能
*
* @author lframework@163.com
*/
public class ArrayUtil extends cn.hutool.core.util.ArrayUtil {
}

View File

@@ -0,0 +1,39 @@
package com.lframework.starter.common.utils;
/**
* 断言工具类
* 基于HuTool的Assert进行扩展提供参数验证和断言相关的工具方法
* 包括参数非空验证、数值范围验证、条件断言等功能
*
* @author lframework@163.com
*/
public class Assert extends cn.hutool.core.lang.Assert {
/**
* 断言数字大于0
* 验证数字必须大于0否则抛出异常
*
* @param number 要验证的数字不能为null
* @throws IllegalArgumentException 当数字为null或小于等于0时抛出
*/
public static void greaterThanZero(Number number) {
notNull(number);
isTrue(number.doubleValue() > 0D);
}
/**
* 断言数字大于或等于0
* 验证数字必须大于或等于0否则抛出异常
*
* @param number 要验证的数字不能为null
* @throws IllegalArgumentException 当数字为null或小于0时抛出
*/
public static void greaterThanOrEqualToZero(Number number) {
notNull(number);
isTrue(number.doubleValue() >= 0D);
}
}

View File

@@ -0,0 +1,12 @@
package com.lframework.starter.common.utils;
/**
* JavaBean工具类
* 基于HuTool的BeanUtil进行扩展提供JavaBean操作相关的工具方法
* 包括对象属性复制、类型转换、反射操作等功能
*
* @author lframework@163.com
*/
public class BeanUtil extends cn.hutool.core.bean.BeanUtil {
}

View File

@@ -0,0 +1,39 @@
package com.lframework.starter.common.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 集合工具类
* 基于HuTool的CollectionUtil进行扩展提供集合操作相关的工具方法
* 包括集合判空、创建空集合、集合转换等功能
*
* @author lframework@163.com
*/
public class CollectionUtil extends cn.hutool.core.collection.CollectionUtil {
/**
* 创建空的List集合
* 返回一个容量为0的ArrayList实例
*
* @param <T> 集合元素类型
* @return 空的List集合
*/
public static <T> List<T> emptyList() {
return new ArrayList<>(0);
}
/**
* 创建空的Map集合
* 返回一个容量为0的HashMap实例
*
* @param <K> Map键类型
* @param <V> Map值类型
* @return 空的Map集合
*/
public static <K, V> Map<K, V> emptyMap() {
return new HashMap<>(0);
}
}

View File

@@ -0,0 +1,385 @@
package com.lframework.starter.common.utils;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.lframework.starter.common.constants.StringPool;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 时间工具类
* 基于HuTool的LocalDateTimeUtil进行扩展提供时间操作相关的工具方法
* 包括时间格式化、类型转换、时间范围计算等功能
*
* @author lframework@163.com
*/
public class DateUtil extends LocalDateTimeUtil {
/**
* 格式化日期为标准格式
* 使用默认的日期格式yyyy-MM-dd格式化LocalDate
*
* @param localDate 要格式化的日期不能为null
* @return 格式化后的日期字符串
*/
public static String formatDate(LocalDate localDate) {
return LocalDateTimeUtil.formatNormal(localDate);
}
/**
* 格式化日期为指定格式
* 使用指定的日期格式格式化LocalDate
*
* @param localDate 要格式化的日期不能为null
* @param format 日期格式不能为null或空
* @return 格式化后的日期字符串
*/
public static String formatDate(LocalDate localDate, String format) {
return LocalDateTimeUtil.format(localDate, format);
}
/**
* 将Date转换为LocalTime
* 提取Date中的时间部分转换为LocalTime
*
* @param date 要转换的Date对象可以为null
* @return 转换后的LocalTime对象如果输入为null则返回null
*/
public static LocalTime toLocalTime(Date date) {
if (date == null) {
return null;
}
return LocalDateTimeUtil.of(date).toLocalTime();
}
/**
* 将Date转换为LocalDate
* 提取Date中的日期部分转换为LocalDate
*
* @param date 要转换的Date对象可以为null
* @return 转换后的LocalDate对象如果输入为null则返回null
*/
public static LocalDate toLocalDate(Date date) {
if (date == null) {
return null;
}
return LocalDateTimeUtil.of(date).toLocalDate();
}
/**
* 将Date转换为LocalDateTime
* 将Date对象转换为LocalDateTime对象
*
* @param date 要转换的Date对象不能为null
* @return 转换后的LocalDateTime对象
*/
public static LocalDateTime toLocalDateTime(Date date) {
return LocalDateTimeUtil.of(date);
}
/**
* 将LocalDateTime转换为Date
* 将LocalDateTime对象转换为Date对象
*
* @param dateTime 要转换的LocalDateTime对象可以为null
* @return 转换后的Date对象如果输入为null则返回null
*/
public static Date toDate(LocalDateTime dateTime) {
if (dateTime == null) {
return null;
}
return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
}
/**
* 将LocalDate转换为Date
* 将LocalDate对象转换为Date对象时间部分为00:00:00
*
* @param date 要转换的LocalDate对象可以为null
* @return 转换后的Date对象如果输入为null则返回null
*/
public static Date toDate(LocalDate date) {
if (date == null) {
return null;
}
return Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}
/**
* 将LocalDate转换为LocalDateTime最小时间
* 将LocalDate转换为当天的00:00:00时间
*
* @param localDate 要转换的LocalDate对象不能为null
* @return 转换后的LocalDateTime对象
*/
public static LocalDateTime toLocalDateTime(LocalDate localDate) {
return LocalDateTime.of(localDate, LocalTime.MIN);
}
/**
* 将LocalDate转换为LocalDateTime最大时间
* 将LocalDate转换为当天的23:59:59.999999999时间
*
* @param localDate 要转换的LocalDate对象不能为null
* @return 转换后的LocalDateTime对象
*/
public static LocalDateTime toLocalDateTimeMax(LocalDate localDate) {
return LocalDateTime.of(localDate, LocalTime.MAX.withNano(0));
}
/**
* 格式化日期时间为标准格式
* 使用默认的日期时间格式yyyy-MM-dd HH:mm:ss格式化LocalDateTime
*
* @param localDateTime 要格式化的日期时间不能为null
* @return 格式化后的日期时间字符串
*/
public static String formatDateTime(LocalDateTime localDateTime) {
return formatDateTime(localDateTime, StringPool.DATE_TIME_PATTERN);
}
/**
* 格式化日期时间为指定格式
* 使用指定的日期时间格式格式化LocalDateTime
*
* @param localDateTime 要格式化的日期时间不能为null
* @param pattern 日期时间格式不能为null或空
* @return 格式化后的日期时间字符串
*/
public static String formatDateTime(LocalDateTime localDateTime, String pattern) {
return format(localDateTime, pattern);
}
/**
* 获取LocalDateTime的时间戳
* 将LocalDateTime转换为毫秒时间戳
*
* @param localDateTime 要转换的LocalDateTime对象不能为null
* @return 毫秒时间戳
*/
public static long getTime(LocalDateTime localDateTime) {
return Timestamp.valueOf(localDateTime).getTime();
}
/**
* 获取LocalDate的时间戳
* 将LocalDate转换为毫秒时间戳当天00:00:00
*
* @param localDate 要转换的LocalDate对象不能为null
* @return 毫秒时间戳
*/
public static long getTime(LocalDate localDate) {
return Timestamp.valueOf(toLocalDateTime(localDate)).getTime();
}
/**
* 获取Date的时间戳
* 获取Date对象的毫秒时间戳
*
* @param date 要获取时间戳的Date对象不能为null
* @return 毫秒时间戳
*/
public static long getTime(Date date) {
return date.getTime();
}
/**
* 获取本周的所有日期
* 返回当前周从周一到周日的所有日期
*
* @return 本周的日期列表包含7个日期
*/
public static List<LocalDate> getCurrentWeekDates() {
return getWeekDates(LocalDate.now());
}
/**
* 获取指定日期所在周的所有日期
* 返回指定日期所在周从周一到周日的所有日期
*
* @param date 指定日期不能为null
* @return 该周的日期列表包含7个日期
*/
public static List<LocalDate> getWeekDates(LocalDate date) {
LocalDate firstDate = date.with(WeekFields.ISO.dayOfWeek(), 1L);
List<LocalDate> results = new ArrayList<>();
for (int i = 0; i < 7; i++) {
results.add(firstDate.plusDays(i));
}
return results;
}
/**
* 获取本月的所有日期
* 返回当前月份的所有日期
*
* @return 本月的日期列表
*/
public static List<LocalDate> getCurrentMonthDates() {
LocalDate now = LocalDate.now();
return getMonthDates(LocalDate.now());
}
/**
* 获取指定日期所在月份的所有日期
* 返回指定日期所在月份的所有日期
*
* @param date 指定日期不能为null
* @return 该月的日期列表
*/
public static List<LocalDate> getMonthDates(LocalDate date) {
LocalDate firstDate = date.withDayOfMonth(1);
List<LocalDate> results = new ArrayList<>();
for (int i = 0; i < 31; i++) {
LocalDate tmp = firstDate.plusDays(i);
if (tmp.getMonthValue() != firstDate.getMonthValue()) {
break;
}
results.add(tmp);
}
return results;
}
/**
* 获取本季度的所有日期
* 返回当前季度的所有日期
*
* @return 本季度的日期列表
*/
public static List<LocalDate> getCurrentQuarterDates() {
return getQuarterDates(LocalDate.now());
}
/**
* 获取指定日期所在季度的所有日期
* 返回指定日期所在季度的所有日期
*
* @param date 指定日期不能为null
* @return 该季度的日期列表
*/
public static List<LocalDate> getQuarterDates(LocalDate date) {
int quarter = getQuarter(date);
LocalDate firstDate = date.withMonth((quarter - 1) * 3 + 1).withDayOfMonth(1);
List<LocalDate> results = new ArrayList<>();
for (int i = 0; i < 3; i++) {
results.addAll(getMonthDates(firstDate.plusMonths(i)));
}
return results;
}
/**
* 获取当前半年的所有日期
* 返回当前半年的所有日期
*
* @param date 指定日期不能为null
* @return 当前半年的日期列表
*/
public static List<LocalDate> getCurrentHalfYearDates(LocalDate date) {
return getHalfYearDates(LocalDate.now());
}
/**
* 获取指定日期所在半年的所有日期
* 返回指定日期所在半年的所有日期
*
* @param date 指定日期不能为null
* @return 该半年的日期列表
*/
public static List<LocalDate> getHalfYearDates(LocalDate date) {
LocalDate firstDate = date.getMonthValue() > 6 ? date.withMonth(7).withDayOfMonth(1)
: date.withMonth(1).withDayOfMonth(1);
LocalDate lastDate = date.getMonthValue() > 6 ? date.withMonth(12).withDayOfMonth(31)
: date.withMonth(6).withDayOfMonth(30);
List<LocalDate> results = new ArrayList<>();
int i = 0;
while (true) {
LocalDate tmp = firstDate.plusDays(i);
if (tmp.isAfter(lastDate)) {
break;
}
results.add(tmp);
i++;
}
return results;
}
/**
* 获取当前年的所有日期
* 返回当前年份的所有日期
*
* @return 当前年的日期列表
*/
public static List<LocalDate> getCurrentYearDates() {
return getYearDates(LocalDate.now());
}
/**
* 获取指定日期所在年份的所有日期
* 返回指定日期所在年份的所有日期
*
* @param date 指定日期不能为null
* @return 该年的日期列表
*/
public static List<LocalDate> getYearDates(LocalDate date) {
LocalDate firstDate = date.withMonth(1).withDayOfMonth(1);
LocalDate lastDate = date.withMonth(12).withDayOfMonth(31);
List<LocalDate> results = new ArrayList<>();
int i = 0;
while (true) {
LocalDate tmp = firstDate.plusDays(i);
if (tmp.isAfter(lastDate)) {
break;
}
results.add(tmp);
i++;
}
return results;
}
/**
* 获取指定日期属于第几季度
* 根据月份计算季度1-3月为第1季度4-6月为第2季度7-9月为第3季度10-12月为第4季度
*
* @param date 指定日期不能为null
* @return 季度数1-4
*/
public static int getQuarter(LocalDate date) {
return (date.getMonthValue() - 1) / 3 + 1;
}
}

View File

@@ -0,0 +1,33 @@
package com.lframework.starter.common.utils;
import java.util.Arrays;
import java.util.List;
/**
* 文件工具类
* 基于HuTool的FileUtil进行扩展提供文件操作相关的工具方法
* 包括文件类型判断、文件操作、路径处理等功能
*
* @author lframework@163.com
*/
public class FileUtil extends cn.hutool.core.io.FileUtil {
/**
* 图片文件后缀名列表
* 支持常见的图片格式jpg、jpeg、bmp、png、gif
*/
public static final List<String> IMG_SUFFIX = Arrays.asList("jpg", "jpeg", "bmp", "png", "gif");
/**
* Excel文件后缀名列表
* 支持Excel格式xls、xlsx
*/
public static final List<String> EXCEL_SUFFIX = Arrays.asList("xls", "xlsx");
/**
* 视频文件后缀名列表
* 支持常见的视频格式avi、wmv、mpeg、mp4、m4v、mov、asf、flv、f4v、rmvb、rm、3gp、vob
*/
public static final List<String> VIDEO_SUFFIX = Arrays.asList("avi", "wmv", "mpeg", "mp4", "m4v",
"mov", "asf", "flv", "f4v", "rmvb", "rm", "3gp", "vob");
}

View File

@@ -0,0 +1,82 @@
package com.lframework.starter.common.utils;
import cn.hutool.core.lang.Snowflake;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
/**
* 分布式ID生成器
* 基于HuTool的Snowflake雪花算法实现生成全局唯一的分布式ID
* 支持高并发场景下的ID生成保证ID的唯一性和有序性
*
* @author lframework@163.com
*/
@Slf4j
public class IdWorker extends Snowflake {
/**
* 默认构造函数
* 使用默认的workerId和dataCenterId创建ID生成器
*/
public IdWorker() {
super();
}
/**
* 指定workerId的构造函数
*
* @param workerId 工作机器ID范围0-31
*/
public IdWorker(long workerId) {
super(workerId);
}
/**
* 指定workerId和dataCenterId的构造函数
*
* @param workerId 工作机器ID范围0-31
* @param dataCenterId 数据中心ID范围0-31
*/
public IdWorker(long workerId, long dataCenterId) {
super(workerId, dataCenterId);
}
/**
* 指定workerId、dataCenterId和时钟使用方式的构造函数
*
* @param workerId 工作机器ID范围0-31
* @param dataCenterId 数据中心ID范围0-31
* @param isUseSystemClock 是否使用系统时钟
*/
public IdWorker(long workerId, long dataCenterId, boolean isUseSystemClock) {
super(workerId, dataCenterId, isUseSystemClock);
}
/**
* 指定纪元时间、workerId、dataCenterId和时钟使用方式的构造函数
*
* @param epochDate 纪元时间不能为null
* @param workerId 工作机器ID范围0-31
* @param dataCenterId 数据中心ID范围0-31
* @param isUseSystemClock 是否使用系统时钟
*/
public IdWorker(Date epochDate, long workerId, long dataCenterId,
boolean isUseSystemClock) {
super(epochDate, workerId, dataCenterId, isUseSystemClock);
}
/**
* 完整参数的构造函数
*
* @param epochDate 纪元时间不能为null
* @param workerId 工作机器ID范围0-31
* @param dataCenterId 数据中心ID范围0-31
* @param isUseSystemClock 是否使用系统时钟
* @param timeOffset 时间偏移量
*/
public IdWorker(Date epochDate, long workerId, long dataCenterId,
boolean isUseSystemClock, long timeOffset) {
super(epochDate, workerId, dataCenterId, isUseSystemClock, timeOffset);
}
}

View File

@@ -0,0 +1,12 @@
package com.lframework.starter.common.utils;
/**
* 邮件工具类
* 基于HuTool的MailUtil进行扩展提供邮件发送相关的工具方法
* 包括邮件发送、附件处理、模板邮件等功能
*
* @author lframework@163.com
*/
public class MailUtil extends cn.hutool.extra.mail.MailUtil {
}

View File

@@ -0,0 +1,381 @@
package com.lframework.starter.common.utils;
import com.lframework.starter.common.constants.StringPool;
import com.lframework.starter.common.exceptions.impl.DefaultSysException;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 数字工具类
* 提供精确的数字计算、比较、精度控制等功能,避免浮点数计算精度问题
* 支持加减乘除运算、大小比较、税率计算、精度验证等常用数字操作
*
* @author lframework@163.com
*/
public class NumberUtil {
/**
* 判断数字的小数位数是否符合指定精度要求
* 用于验证数字的小数位数是否在允许范围内
*
* @param value 待验证的数字不能为null
* @param precision 允许的最大小数位数必须大于等于0
* @return true-符合精度要求false-不符合精度要求或参数无效
*/
public static boolean isNumberPrecision(Number value, int precision) {
if (value == null) {
return false;
}
if (precision < 0) {
return false;
}
String str = BigDecimal.valueOf(value.doubleValue()).toPlainString();
if (str.contains(StringPool.DECIMAL_POINT)) {
while (StringPool.ZERO.equals(str.substring(str.length() - 1))) {
// 将数字末尾为0的字符去除
str = str.substring(0, str.length() - 1);
}
if (StringPool.DECIMAL_POINT.equals(str.substring(str.length() - 1))) {
return true;
}
return str.substring(str.indexOf(StringPool.DECIMAL_POINT)).length() - 1 <= precision;
}
return true;
}
/**
* 判断数字是否为整数
* 检查Number对象是否表示一个整数值没有小数部分
*
* @param number 待检查的数字不能为null
* @return true-是整数false-不是整数或参数为null
*/
public static boolean isInteger(Number number) {
if (number == null) {
return false;
}
if (number instanceof Integer || number instanceof Long || number instanceof Short
|| number instanceof Byte) {
return true;
}
BigDecimal bigDecimal = getNumber(number);
return bigDecimal.compareTo(new BigDecimal(bigDecimal.toBigInteger())) == 0;
}
/**
* 精确加法运算
* 对多个数字进行精确的加法计算,避免浮点数精度问题
*
* @param numbers 参与加法运算的数字,支持可变参数
* @return 所有数字相加的结果使用BigDecimal保证精度
*/
public static BigDecimal add(Number... numbers) {
BigDecimal result = new BigDecimal(0);
for (Number number : numbers) {
result = result.add(getNumber(number));
}
return result;
}
/**
* 精确减法运算
* 从被减数中依次减去所有减数,进行精确的减法计算
*
* @param n1 被减数不能为null
* @param numbers 减数数组,不能为空
* @return 减法运算的结果使用BigDecimal保证精度
* @throws IllegalArgumentException 当被减数为null或减数数组为空时抛出
*/
public static BigDecimal sub(Number n1, Number... numbers) {
Assert.notNull(n1);
Assert.notEmpty(numbers);
BigDecimal result = getNumber(n1);
for (Number number : numbers) {
BigDecimal tmp = getNumber(number);
result = result.subtract(tmp);
}
return result;
}
/**
* 精确乘法运算
* 对多个数字进行精确的乘法计算,避免浮点数精度问题
*
* @param numbers 参与乘法运算的数字,支持可变参数
* @return 所有数字相乘的结果使用BigDecimal保证精度
*/
public static BigDecimal mul(Number... numbers) {
BigDecimal result = new BigDecimal(1);
for (Number number : numbers) {
result = result.multiply(getNumber(number));
}
return result;
}
/**
* 精确除法运算(四舍五入)
* 使用四舍五入模式进行精确的除法计算
*
* @param n1 被除数不能为null
* @param numbers 除数数组不能为空且不能包含0
* @return 除法运算的结果使用BigDecimal保证精度
* @throws IllegalArgumentException 当被除数为null或除数数组为空时抛出
* @throws DefaultSysException 当除数为0时抛出
*/
public static BigDecimal div(Number n1, Number... numbers) {
return div(RoundingMode.HALF_UP, n1, numbers);
}
/**
* 精确除法运算(指定舍入模式)
* 使用指定的舍入模式进行精确的除法计算
*
* @param mode 小数位处理方式不能为null
* @param n1 被除数不能为null
* @param numbers 除数数组不能为空且不能包含0
* @return 除法运算的结果使用BigDecimal保证精度
* @throws IllegalArgumentException 当被除数为null或除数数组为空时抛出
* @throws DefaultSysException 当除数为0时抛出
*/
public static BigDecimal div(RoundingMode mode, Number n1, Number... numbers) {
Assert.notNull(n1);
Assert.notEmpty(numbers);
BigDecimal result = getNumber(n1);
for (Number number : numbers) {
BigDecimal tmp = getNumber(number);
if (equal(tmp, 0)) {
throw new DefaultSysException("除数不能等于0");
}
result = new BigDecimal(result.divide(tmp, 16, mode).stripTrailingZeros().toPlainString());
}
return result;
}
/**
* 判断第一个数字是否大于第二个数字
* 使用BigDecimal进行精确比较避免浮点数比较问题
*
* @param n1 第一个数字不能为null
* @param n2 第二个数字不能为null
* @return true-第一个数字大于第二个数字false-否则
*/
public static boolean gt(Number n1, Number n2) {
return getNumber(n1).compareTo(getNumber(n2)) > 0;
}
/**
* 判断第一个数字是否小于第二个数字
* 使用BigDecimal进行精确比较避免浮点数比较问题
*
* @param n1 第一个数字不能为null
* @param n2 第二个数字不能为null
* @return true-第一个数字小于第二个数字false-否则
*/
public static boolean lt(Number n1, Number n2) {
return getNumber(n1).compareTo(getNumber(n2)) < 0;
}
/**
* 判断第一个数字是否大于或等于第二个数字
* 使用BigDecimal进行精确比较避免浮点数比较问题
*
* @param n1 第一个数字不能为null
* @param n2 第二个数字不能为null
* @return true-第一个数字大于或等于第二个数字false-否则
*/
public static boolean ge(Number n1, Number n2) {
return getNumber(n1).compareTo(getNumber(n2)) >= 0;
}
/**
* 判断第一个数字是否小于或等于第二个数字
* 使用BigDecimal进行精确比较避免浮点数比较问题
*
* @param n1 第一个数字不能为null
* @param n2 第二个数字不能为null
* @return true-第一个数字小于或等于第二个数字false-否则
*/
public static boolean le(Number n1, Number n2) {
return getNumber(n1).compareTo(getNumber(n2)) <= 0;
}
/**
* 判断两个数字是否相等
* 使用BigDecimal进行精确比较避免浮点数比较问题
*
* @param n1 第一个数字不能为null
* @param n2 第二个数字不能为null
* @return true-两个数字相等false-否则
*/
public static boolean equal(Number n1, Number n2) {
return getNumber(n1).compareTo(getNumber(n2)) == 0;
}
/**
* 根据无税价格计算含税价格
* 计算公式:含税价格 = 无税价格 × (1 + 税率/100)
*
* @param unTaxPrice 无税价格不能为null
* @param taxRate 税率百分比不能为null如13表示13%
* @return 计算后的含税价格使用BigDecimal保证精度
*/
public static BigDecimal calcTaxPrice(Number unTaxPrice, Number taxRate) {
return mul(unTaxPrice, add(div(taxRate, 100), BigDecimal.ONE));
}
/**
* 根据含税价格计算无税价格
* 计算公式:无税价格 = 含税价格 ÷ (1 + 税率/100)
*
* @param taxPrice 含税价格不能为null
* @param taxRate 税率百分比不能为null如13表示13%
* @return 计算后的无税价格使用BigDecimal保证精度
*/
public static BigDecimal calcUnTaxPrice(Number taxPrice, Number taxRate) {
return div(taxPrice, add(div(taxRate, 100), BigDecimal.ONE));
}
/**
* 根据含税价格和无税价格计算税率
* 计算公式:税率 = (含税价格 ÷ 无税价格 - 1) × 100
*
* @param tax 含税价格或含税金额不能为null
* @param unTax 无税价格或无税金额不能为null且不能为0
* @return 计算后的税率百分比使用BigDecimal保证精度
* @throws DefaultSysException 当无税价格为0时抛出
*/
public static BigDecimal calcTaxRate(Number tax, Number unTax) {
return mul(sub(div(tax, unTax), BigDecimal.ONE), 100);
}
/**
* 获取多个数字中的最小值
* 使用BigDecimal进行精确比较避免浮点数比较问题
*
* @param numbers 参与比较的数字不能为null且至少包含一个元素
* @return 所有数字中的最小值使用BigDecimal保证精度
* @throws IllegalArgumentException 当数字数组为null或为空时抛出
*/
public static BigDecimal min(Number... numbers) {
Assert.notEmpty(numbers);
BigDecimal min = getNumber(numbers[0]);
for (int i = 1; i < numbers.length; i++) {
BigDecimal current = getNumber(numbers[i]);
if (current.compareTo(min) < 0) {
min = current;
}
}
return min;
}
/**
* 获取多个数字中的最大值
* 使用BigDecimal进行精确比较避免浮点数比较问题
*
* @param numbers 参与比较的数字不能为null且至少包含一个元素
* @return 所有数字中的最大值使用BigDecimal保证精度
* @throws IllegalArgumentException 当数字数组为null或为空时抛出
*/
public static BigDecimal max(Number... numbers) {
Assert.notEmpty(numbers);
BigDecimal max = getNumber(numbers[0]);
for (int i = 1; i < numbers.length; i++) {
BigDecimal current = getNumber(numbers[i]);
if (current.compareTo(max) > 0) {
max = current;
}
}
return max;
}
/**
* 获取数字的绝对值
* 使用BigDecimal进行精确计算避免浮点数精度问题
*
* @param number 待计算绝对值的数字不能为null
* @return 数字的绝对值使用BigDecimal保证精度
* @throws IllegalArgumentException 当数字为null时抛出
*/
public static BigDecimal abs(Number number) {
Assert.notNull(number);
if (number instanceof BigDecimal) {
return ((BigDecimal) number).abs();
} else {
return BigDecimal.valueOf(number.doubleValue()).abs();
}
}
/**
* 将数字格式化为指定精度的小数
* 使用四舍五入模式保留指定的小数位数
*
* @param number 待格式化的数字不能为null
* @param precision 保留的小数位数必须大于等于0
* @return 格式化后的数字使用BigDecimal保证精度
*/
public static BigDecimal getNumber(Number number, int precision) {
precision = Math.max(0, precision);
BigDecimal result = getNumber(number).setScale(precision, BigDecimal.ROUND_HALF_UP);
return result.stripTrailingZeros();
}
/**
* 将Number类型转换为BigDecimal类型
* 内部工具方法,用于统一数字类型转换
*
* @param number 待转换的数字不能为null
* @return 转换后的BigDecimal对象
* @throws IllegalArgumentException 当数字为null时抛出
*/
private static BigDecimal getNumber(Number number) {
Assert.notNull(number);
if (number instanceof BigDecimal) {
return (BigDecimal) number;
} else {
return BigDecimal.valueOf(number.doubleValue());
}
}
}

View File

@@ -0,0 +1,12 @@
package com.lframework.starter.common.utils;
/**
* 对象工具类
* 基于HuTool的ObjectUtil进行扩展提供对象操作相关的工具方法
* 包括对象判空、类型转换、克隆、序列化等功能
*
* @author lframework@163.com
*/
public class ObjectUtil extends cn.hutool.core.util.ObjectUtil {
}

View File

@@ -0,0 +1,70 @@
package com.lframework.starter.common.utils;
import com.lframework.starter.common.functions.SFunction;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 反射工具类
* 基于HuTool的ReflectUtil进行扩展提供反射操作相关的工具方法
* 包括字段获取、方法调用、Lambda表达式字段名提取等功能
*
* @author lframework@163.com
*/
public class ReflectUtil extends cn.hutool.core.util.ReflectUtil {
/**
* 获取实体类的字段名称(实体声明的字段名称)
* 通过Lambda表达式获取对应的字段名称支持getter方法
*
* @param fn Lambda表达式如 User::getName
* @param <T> 实体类型
* @return 字段名称,如 "name"
* @throws RuntimeException 当获取字段名失败时抛出
*/
public static <T> String getFieldName(SFunction<T, ?> fn) {
SerializedLambda serializedLambda = getSerializedLambda(fn);
// 从lambda信息取出method、field、class等
String fieldName = serializedLambda.getImplMethodName().substring("get".length());
fieldName = fieldName.replaceFirst(fieldName.charAt(0) + "",
(fieldName.charAt(0) + "").toLowerCase());
// 从field取出字段名可以根据实际情况调整
return fieldName.replaceAll("[A-Z]", "$0");
}
/**
* 获取Lambda表达式的序列化信息
* 通过反射获取Lambda表达式的序列化Lambda对象
*
* @param fn Lambda表达式不能为null
* @param <T> 实体类型
* @return 序列化Lambda对象
* @throws RuntimeException 当获取序列化Lambda失败时抛出
*/
private static <T> SerializedLambda getSerializedLambda(SFunction<T, ?> fn) {
// 从function取出序列化方法
Method writeReplaceMethod;
try {
writeReplaceMethod = fn.getClass().getDeclaredMethod("writeReplace");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// 从序列化方法取出序列化的lambda信息
boolean isAccessible = writeReplaceMethod.isAccessible();
writeReplaceMethod.setAccessible(true);
SerializedLambda serializedLambda;
try {
serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(fn);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
writeReplaceMethod.setAccessible(isAccessible);
return serializedLambda;
}
}

View File

@@ -0,0 +1,28 @@
package com.lframework.starter.common.utils;
import cn.hutool.core.util.ReUtil;
import java.util.regex.Pattern;
/**
* 正则表达式工具类
* 基于HuTool的ReUtil进行扩展提供正则表达式相关的工具方法
* 包括模式匹配、字符串替换、提取等功能
*
* @author lframework@163.com
*/
public class RegUtil {
/**
* 判断字符串是否匹配指定的正则表达式模式
* 使用预编译的Pattern对象进行匹配提高性能
*
* @param pattern 预编译的正则表达式模式不能为null
* @param str 要匹配的字符串不能为null
* @return true-匹配成功false-匹配失败
*/
public static boolean isMatch(Pattern pattern, String str) {
return ReUtil.isMatch(pattern, str);
}
}

View File

@@ -0,0 +1,76 @@
package com.lframework.starter.common.utils;
import cn.hutool.core.util.StrUtil;
import com.lframework.starter.common.constants.PatternPool;
/**
* 字符串工具类
* 基于HuTool的StrUtil进行扩展提供字符串操作相关的工具方法
* 包括字符串验证、脱敏处理、模式匹配等功能
*
* @author lframework@163.com
*/
public class StringUtil extends StrUtil {
/**
* 邮箱地址脱敏处理
* 将邮箱地址进行脱敏,保留首字符和域名部分
*
* @param email 邮箱地址不能为null
* @return 脱敏后的邮箱地址如果格式不正确则返回null
*/
public static String encodeEmail(String email) {
if (!RegUtil.isMatch(PatternPool.PATTERN_EMAIL, email)) {
return null;
}
return email.charAt(0) + "******" + "@" + email.split("@")[1];
}
/**
* 手机号码脱敏处理
* 将手机号码进行脱敏保留前3位和后4位
*
* @param telephone 手机号码不能为null
* @return 脱敏后的手机号码如果格式不正确则返回null
*/
public static String encodeTelephone(String telephone) {
if (!RegUtil.isMatch(PatternPool.PATTERN_CN_TEL, telephone)) {
return null;
}
return telephone.substring(0, 3) + "****" + telephone.substring(7);
}
/**
* 字符串模式匹配,支持*和?通配符
* 支持*匹配任意字符,?匹配单个字符的通配符匹配
*
* @param str 要匹配的字符串不能为null
* @param pattern 匹配模式,支持*和?通配符不能为null
* @return true-匹配成功false-匹配失败
*/
public static boolean strMatch(String str, String pattern) {
if (StringUtil.isEmpty(str) && StringUtil.isEmpty(pattern)) {
return true;
}
if ("*".equals(str)) {
return true;
}
if (StringUtil.isEmpty(str) || StringUtil.isEmpty(pattern)) {
return false;
}
if ("?".equals(str.substring(0, 1))) {
return strMatch(str.substring(1), pattern.substring(1));
} else if ("*".equals(str.substring(0, 1))) {
return strMatch(str.substring(1), pattern) || strMatch(str.substring(1), pattern.substring(1))
|| strMatch(str, pattern.substring(1));
} else if (pattern.substring(0, 1).equals(str.substring(0, 1))) {
return strMatch(str.substring(1), pattern.substring(1));
} else {
return false;
}
}
}

View File

@@ -0,0 +1,12 @@
package com.lframework.starter.common.utils;
/**
* 线程工具类
* 基于HuTool的ThreadUtil进行扩展提供线程相关的工具方法
* 包括线程池创建、线程管理、异步任务执行等功能
*
* @author lframework@163.com
*/
public class ThreadUtil extends cn.hutool.core.thread.ThreadUtil {
}

View File

@@ -0,0 +1,361 @@
package com.lframework.starter.common.utils;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
/**
* XML工具类
* 提供XML与Map之间的相互转换、XML格式化等功能
* 支持带属性和不带属性的XML解析以及XML文档的格式化输出
*
* @author lframework@163.com
*/
public class XmlUtil {
/**
* 将XML字符串转换为Map不包含根节点键
* 默认不包含根节点键,直接返回子节点内容
*
* @param xmlStr XML字符串不能为null或空
* @return 转换后的Map对象
* @throws DocumentException 当XML格式错误时抛出
*/
public static Map<String, Object> xml2map(String xmlStr) throws DocumentException {
return xml2map(xmlStr, false);
}
/**
* 将XML字符串转换为Map不带属性
* 解析XML字符串并转换为Map结构不包含XML属性信息
*
* @param xmlStr XML字符串不能为null或空
* @param needRootKey 是否需要在返回的Map中包含根节点键
* @return 转换后的Map对象包含XML的层次结构
* @throws DocumentException 当XML格式错误时抛出
*/
public static Map<String, Object> xml2map(String xmlStr, boolean needRootKey)
throws DocumentException {
Document doc = DocumentHelper.parseText(xmlStr);
Element root = doc.getRootElement();
Map<String, Object> map = (Map<String, Object>) xml2map(root);
if (root.elements().size() == 0 && root.attributes().size() == 0) {
return map;
}
if (needRootKey) {
//在返回的map里加根节点键如果需要
Map<String, Object> rootMap = new HashMap<String, Object>();
rootMap.put(root.getName(), map);
return rootMap;
}
return map;
}
/**
* 将XML字符串转换为Map带属性
* 解析XML字符串并转换为Map结构包含XML属性信息
* 属性以"@"前缀标识,文本内容以"#text"标识
*
* @param xmlStr XML字符串不能为null或空
* @param needRootKey 是否需要在返回的Map中包含根节点键
* @return 转换后的Map对象包含XML的层次结构和属性信息
* @throws DocumentException 当XML格式错误时抛出
*/
public static Map xml2mapWithAttr(String xmlStr, boolean needRootKey) throws DocumentException {
Document doc = DocumentHelper.parseText(xmlStr);
Element root = doc.getRootElement();
Map<String, Object> map = (Map<String, Object>) xml2mapWithAttr(root);
if (root.elements().size() == 0 && root.attributes().size() == 0) {
return map; //根节点只有一个文本内容
}
if (needRootKey) {
//在返回的map里加根节点键如果需要
Map<String, Object> rootMap = new HashMap<String, Object>();
rootMap.put(root.getName(), map);
return rootMap;
}
return map;
}
/**
* 将XML元素转换为Map不带属性
* 递归处理XML元素将子元素转换为Map结构
*
* @param e XML元素不能为null
* @return 转换后的Map对象
*/
private static Map xml2map(Element e) {
Map map = new LinkedHashMap();
List list = e.elements();
if (list.size() > 0) {
for (int i = 0; i < list.size(); i++) {
Element iter = (Element) list.get(i);
List mapList = new ArrayList();
if (iter.elements().size() > 0) {
Map m = xml2map(iter);
if (map.get(iter.getName()) != null) {
Object obj = map.get(iter.getName());
if (!(obj instanceof List)) {
mapList = new ArrayList();
mapList.add(obj);
mapList.add(m);
}
if (obj instanceof List) {
mapList = (List) obj;
mapList.add(m);
}
map.put(iter.getName(), mapList);
} else {
map.put(iter.getName(), m);
}
} else {
if (map.get(iter.getName()) != null) {
Object obj = map.get(iter.getName());
if (!(obj instanceof List)) {
mapList = new ArrayList();
mapList.add(obj);
mapList.add(iter.getText());
}
if (obj instanceof List) {
mapList = (List) obj;
mapList.add(iter.getText());
}
map.put(iter.getName(), mapList);
} else {
map.put(iter.getName(), iter.getText());
}
}
}
} else {
map.put(e.getName(), e.getText());
}
return map;
}
/**
* 将XML元素转换为Map带属性
* 递归处理XML元素将子元素和属性转换为Map结构
* 属性以"@"前缀标识,文本内容以"#text"标识
*
* @param element XML元素不能为null
* @return 转换后的Map对象包含属性和文本内容
*/
private static Map xml2mapWithAttr(Element element) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
List<Element> list = element.elements();
List<Attribute> listAttr0 = element.attributes(); // 当前节点的所有属性的list
for (Attribute attr : listAttr0) {
map.put("@" + attr.getName(), attr.getValue());
}
if (list.size() > 0) {
for (int i = 0; i < list.size(); i++) {
Element iter = list.get(i);
List mapList = new ArrayList();
if (iter.elements().size() > 0) {
Map m = xml2mapWithAttr(iter);
if (map.get(iter.getName()) != null) {
Object obj = map.get(iter.getName());
if (!(obj instanceof List)) {
mapList = new ArrayList();
mapList.add(obj);
mapList.add(m);
}
if (obj instanceof List) {
mapList = (List) obj;
mapList.add(m);
}
map.put(iter.getName(), mapList);
} else {
map.put(iter.getName(), m);
}
} else {
List<Attribute> listAttr = iter.attributes(); // 当前节点的所有属性的list
Map<String, Object> attrMap = null;
boolean hasAttributes = false;
if (listAttr.size() > 0) {
hasAttributes = true;
attrMap = new LinkedHashMap<String, Object>();
for (Attribute attr : listAttr) {
attrMap.put("@" + attr.getName(), attr.getValue());
}
}
if (map.get(iter.getName()) != null) {
Object obj = map.get(iter.getName());
if (!(obj instanceof List)) {
mapList = new ArrayList();
mapList.add(obj);
// mapList.add(iter.getText());
if (hasAttributes) {
attrMap.put("#text", iter.getText());
mapList.add(attrMap);
} else {
mapList.add(iter.getText());
}
}
if (obj instanceof List) {
mapList = (List) obj;
// mapList.add(iter.getText());
if (hasAttributes) {
attrMap.put("#text", iter.getText());
mapList.add(attrMap);
} else {
mapList.add(iter.getText());
}
}
map.put(iter.getName(), mapList);
} else {
// map.put(iter.getName(), iter.getText());
if (hasAttributes) {
attrMap.put("#text", iter.getText());
map.put(iter.getName(), attrMap);
} else {
map.put(iter.getName(), iter.getText());
}
}
}
}
} else {
// 根节点的
if (listAttr0.size() > 0) {
map.put("#text", element.getText());
} else {
map.put(element.getName(), element.getText());
}
}
return map;
}
/**
* 将Map转换为XML文档指定根节点名称
* 当Map中没有根节点键时使用指定的根节点名称创建XML文档
*
* @param map 要转换的Map对象不能为null
* @param rootName 根节点名称不能为null或空
* @return 转换后的XML文档对象
*/
public static Document map2xml(Map<String, Object> map, String rootName) {
Document doc = DocumentHelper.createDocument();
Element root = DocumentHelper.createElement(rootName);
doc.add(root);
map2xml(map, root);
return doc;
}
/**
* 将Map转换为XML文档包含根节点键
* 当Map中包含根节点键时使用第一个键作为根节点名称创建XML文档
*
* @param map 要转换的Map对象不能为null且不能为空
* @return 转换后的XML文档对象如果Map为空则返回null
*/
public static Document map2xml(Map<String, Object> map) {
Iterator<Map.Entry<String, Object>> entries = map.entrySet().iterator();
if (entries.hasNext()) { //获取第一个键创建根节点
Map.Entry<String, Object> entry = entries.next();
Document doc = DocumentHelper.createDocument();
Element root = DocumentHelper.createElement(entry.getKey());
doc.add(root);
map2xml((Map) entry.getValue(), root);
return doc;
}
return null;
}
/**
* 将Map转换为XML元素
* 递归处理Map将键值对转换为XML元素和属性
* 支持List、Map等复杂数据结构的转换
*
* @param map 要转换的Map对象不能为null
* @param body 父级XML元素不能为null
* @return 转换后的XML元素
*/
private static Element map2xml(Map<String, Object> map, Element body) {
Iterator<Map.Entry<String, Object>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, Object> entry = entries.next();
String key = entry.getKey();
Object value = entry.getValue();
if (key.startsWith("@")) { //属性
body.addAttribute(key.substring(1, key.length()), value.toString());
} else if (key.equals("#text")) { //有属性时的文本
body.setText(value.toString());
} else {
if (value instanceof List) {
List list = (List) value;
Object obj;
for (int i = 0; i < list.size(); i++) {
obj = list.get(i);
//list里是map或String不会存在list里直接是list的
if (obj instanceof Map) {
Element subElement = body.addElement(key);
map2xml((Map) list.get(i), subElement);
} else {
body.addElement(key).setText((String) list.get(i));
}
}
} else if (value instanceof Map) {
Element subElement = body.addElement(key);
map2xml((Map) value, subElement);
} else {
body.addElement(key).setText(value.toString());
}
}
}
return body;
}
/**
* 格式化输出XML字符串
* 将XML字符串格式化为易读的格式包含缩进和换行
*
* @param xmlStr 要格式化的XML字符串不能为null或空
* @return 格式化后的XML字符串
* @throws DocumentException 当XML格式错误时抛出
* @throws IOException 当IO操作失败时抛出
*/
public static String formatXml(String xmlStr) throws DocumentException, IOException {
Document document = DocumentHelper.parseText(xmlStr);
return formatXml(document);
}
/**
* 格式化输出XML文档
* 将XML文档格式化为易读的格式包含缩进和换行
*
* @param document 要格式化的XML文档不能为null
* @return 格式化后的XML字符串
* @throws IOException 当IO操作失败时抛出
*/
public static String formatXml(Document document) throws IOException {
// 格式化输出格式
OutputFormat format = OutputFormat.createPrettyPrint();
//format.setEncoding("UTF-8");
StringWriter writer = new StringWriter();
// 格式化输出流
XMLWriter xmlWriter = new XMLWriter(writer, format);
// 将document写入到输出流
xmlWriter.write(document);
xmlWriter.close();
return writer.toString();
}
}

View File

@@ -0,0 +1,12 @@
package com.lframework.starter.common.utils;
/**
* 压缩工具类
* 基于HuTool的ZipUtil进行扩展提供ZIP文件压缩和解压缩功能
* 支持文件和目录的压缩、解压缩、流式处理等操作
*
* @author lframework@163.com
*/
public class ZipUtil extends cn.hutool.core.util.ZipUtil {
}

View File

@@ -0,0 +1,2 @@
config.stopBubbling=true
lombok.equalsAndHashCode.callSuper=call