2026-01-09 16:58:04 +08:00

535 lines
18 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.czlis.interfaceCommon.utils;
import cn.hutool.core.codec.Base64;
import com.czlis.common.utils.DateUtils;
import com.lowagie.text.Document;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfReader;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
public class LisUtil {
// 换行符常量(跨平台)
public static final String LINE_SEPARATOR = System.lineSeparator();
/**
* 生日格式为:yyyy-MM-dd
* 根据出生日期计算年龄
*
* @param birthday
* @return
*/
public static Map<String, String> getAgeFromBirthDay(String birthday) {
return AgeUtils.getAgeFromBirthDay(birthday);
}
/**
* 根据年龄获取出生日期
*
* @param nl
* @return
*/
public static Date getBirthDayFromAge(int nl, String nldw) {
return AgeUtils.getBirthDayFromAge(nl, nldw);
}
/**
* 按GBK编码的字节宽度截取字符串,最多取cutlens个字节(避免截取半个汉字)
*
* @param str 原始字符串
* @param beginidx 起始位置
* @param cutlens 截取长度
* @return 截取后的字符串
*/
public static String substringByGbkWidth(String str, int beginidx, int cutlens) {
if (str == null || str.isEmpty()) {
return "";
}
int targetWidth = cutlens; // 目标字节宽度
int currentWidth = beginidx; // 当前累计字节宽度
StringBuilder result = new StringBuilder();
for (char c : str.toCharArray()) {
// 计算当前字符在GBK编码下的字节数
int charWidth;
try {
// GBK编码中,汉字占2字节,英文等占1字节
charWidth = String.valueOf(c).getBytes("GBK").length;
} catch (UnsupportedEncodingException e) {
// 理论上GBK是Java默认支持的编码,不会走到这里
throw new RuntimeException("不支持GBK编码", e);
}
// 判断加上当前字符后是否超过目标宽度
if (currentWidth + charWidth > targetWidth) {
break; // 超过则停止,不截取当前字符
}
// 未超过则累加宽度并添加字符
currentWidth += charWidth;
result.append(c);
// 如果刚好达到目标宽度,提前结束
if (currentWidth == targetWidth) {
break;
}
}
return result.toString();
}
/**
* 查询字符串GBK编码的长度
*
* @param str
* @return
*/
public static int getGbkByteLength(String str) {
if (str == null) {
return 0;
}
// 将字符串按GBK编码转换为字节数组,数组长度即为GBK编码下的宽度
try {
return str.getBytes("GBK").length;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* 字符串转ASCII值
* 中文:返回GBK编码第一个字节的ASCII值(无符号)
* 英文及其他单字节字符:直接返回其ASCII值
*
* @param strdata 要处理的字符串
* @return 对应的ASCII值
*/
public static int[] getStringAscii(String strdata) {
if (strdata == null || strdata.isEmpty()) {
return new int[0]; // 处理空字符串
}
// 用List暂存结果,避免提前计算数组长度
List<Integer> asciiList = new ArrayList<>();
for (int i = 0; i < strdata.length(); i++) {
char c = strdata.charAt(i);
try {
if (isChinese(c)) {
// 中文字符:添加GBK编码的所有字节(通常是2个)
byte[] gbkBytes = String.valueOf(c).getBytes("GBK");
for (byte b : gbkBytes) {
asciiList.add(b & 0xFF); // 转换为无符号值
}
} else {
// 非中文字符:直接添加ASCII值
asciiList.add((int) c);
}
} catch (UnsupportedEncodingException e) {
System.out.println("处理字符 '" + c + "' 时发生编码错误: " + e.getMessage());
}
}
// 转换List为int数组并返回
int[] ascValue = new int[asciiList.size()];
for (int i = 0; i < asciiList.size(); i++) {
ascValue[i] = asciiList.get(i);
}
return ascValue;
}
/**
* 计算以10为底的对数
*
* @param x 输入值(必须大于0)
* @return 以10为底的对数值
*/
public static double logten(double x) {
if (x <= 0) {
throw new IllegalArgumentException("输入值必须大于0");
}
// 利用换底公式:log10(x) = ln(x) / ln(10)
return Math.log(x) / Math.log(10);
}
/**
* 判断字符是否为中文字符
*
* @param c 要判断的字符
* @return 是中文字符返回true,否则返回false
*/
private static boolean isChinese(char c) {
// 中文字符的Unicode范围
return (c >= 0x4E00 && c <= 0x9FA5)
|| (c >= 0x3400 && c <= 0x4DBF) // 扩展A
|| (c >= 0x20000 && c <= 0x2A6DF); // 扩展B
}
/**
* 使用正则表达式判断字符串是否为数字
* 支持:整数(123)、小数(123.45)、正负号(-123、+45.6)
*/
public static boolean isNumber(String str) {
if (str == null || str.trim().isEmpty()) {
return false;
}
// 正则表达式:匹配正负整数、正负小数
String regex = "^[+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)$";
return str.matches(regex);
}
/**
* 使用正则表达式判断字符串是否为数字
* 支持:整数(123)、小数(123.45),不包括带正负号的情况
*/
public static boolean isTureNumber(String str) {
if (str == null || str.trim().isEmpty()) {
return false;
}
// 正则表达式:仅匹配正整数、正小数(不包含正负号)
// 说明:
// ^[0-9]+(\.[0-9]*)?$ 匹配整数(如123)或带整数部分的小数(如123.45)
// |\.([0-9]+)$ 匹配纯小数(如.67)
String regex = "^[0-9]+(\\.[0-9]*)?|\\.[0-9]+$";
return str.matches(regex);
}
/**字符串转小数数值**/
public static Double getDoublevalue(String str) {
try {
Double value = Double.valueOf(str);
return value;
} catch (NumberFormatException e) {
e.printStackTrace();
}
return null;
}
/**
* 将包含比较运算符的字符串转换为特定格式的数值字符串
* 不支持的格式或非数字内容将返回原值
*
* @param input 输入字符串,如">=123", ">123", "<=123", "<123"
* @return 转换后的字符串或原值
*/
public static String getOverNumber(String input) {
// 输入为空时返回原值
if (input == null || input.trim().isEmpty()) {
return input;
}
// 定义支持的运算符,按长度排序以便正确识别(先长后短)
String[] operators = {">=", "<=", ">", "<"};
String operator = null;
String numberPart = null;
// 识别运算符和数字部分
for (String op : operators) {
if (input.startsWith(op)) {
operator = op;
numberPart = input.substring(op.length());
break;
}
}
// 未识别到支持的运算符,返回原值
if (operator == null) {
return input;
}
// 尝试将数字部分转换为double
double number;
try {
number = Double.parseDouble(numberPart);
} catch (NumberFormatException e) {
// 数字部分格式不正确,返回原值
return input;
}
// 根据运算符进行转换
switch (operator) {
case ">=":
case ">":
return String.format("%.4f", number + 0.0001);
case "<=":
case "<":
return String.format("%.4f", number - 0.0001);
default:
return input;
}
}
/**
* 保留指定小数位数(四舍五入)
* 支持比较符号 (<=、>=、<、>) 和负号 (-) 同时存在的情况
*
* @param input 输入字符串,如 "123.1234"、">123.252"、"<=123.252"、"-45.67"、">=-89.01"
* @param decimalPlaces 要保留的小数位数,需为非负整数
* @return 格式化后的字符串,无法处理时返回原值
*/
public static String round(String input, int decimalPlaces) {
// 处理空输入或无效小数位数
if (input == null || input.trim().isEmpty() || decimalPlaces < 0) {
return input;
}
// 定义比较运算符前缀(按长度排序,确保正确识别)
String[] comparisonPrefixes = {"<=", ">=", "<", ">"};
String comparisonPrefix = "";
String remainingPart = input.trim(); // 去除首尾空格
// 提取比较运算符前缀
for (String prefix : comparisonPrefixes) {
if (remainingPart.startsWith(prefix)) {
comparisonPrefix = prefix;
remainingPart = remainingPart.substring(prefix.length()).trim(); // 去除前缀后的空格
break;
}
}
// 尝试解析剩余部分为数字(使用 BigDecimal 避免精度问题)
try {
// 解析为 BigDecimal(支持正负号和小数)
BigDecimal number = new BigDecimal(remainingPart);
// 设置四舍五入模式并保留指定小数位数
BigDecimal rounded = number.setScale(decimalPlaces, RoundingMode.HALF_UP);
// 拼接比较前缀和格式化后的数字
return comparisonPrefix + rounded.toPlainString(); //toPlainString 避免科学计数法
} catch (NumberFormatException e) {
// 数字部分无法解析时返回原值
return input;
}
}
/**
* 根据天数返回日期,正数往后推,负数往前推
*
* @param day
* @return
*/
public static Date relativeDate(int day) {
// 1. 获取当前时间(基于Calendar)
Calendar calendar = Calendar.getInstance(); // 当前时间
calendar.add(Calendar.DAY_OF_MONTH, day);
// 2重置时间为00:00:00
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
// 3. 返回推算的出生日期
return calendar.getTime();
}
public static Date relativeDate(Date date, int day) {
// 1. 获取当前时间(基于Calendar)
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, day);
// 2重置时间为00:00:00
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
// 3. 返回推算的出生日期
return calendar.getTime();
}
/**
* 根据天数返回日期时间,正数往后推,负数往前推
*
* @param day
* @return
*/
public static Date relativeDateTime(int day) {
// 1. 获取当前时间(基于Calendar)
Calendar calendar = Calendar.getInstance(); // 当前时间
// 2天:忽略小时,按整天推算
calendar.add(Calendar.DAY_OF_MONTH, day);
// 3. 返回推算的时间
return calendar.getTime();
}
public static Date relativeDateTime(Date date, int day) {
// 1. 获取当前时间(基于Calendar)
Calendar calendar = Calendar.getInstance(); // 当前时间
calendar.setTime(date);
// 2天:忽略小时,按整天推算
calendar.add(Calendar.DAY_OF_MONTH, day);
// 3. 返回推算的时间
return calendar.getTime();
}
/**
* 判断前一个时间与后面时间间隔几分钟
*
* @param pastDate
* @param datenow
* @return
*/
public static long relativeMinute(Date pastDate, Date datenow) {
LocalDateTime pastTime = pastDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
LocalDateTime now = datenow.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
long minutesDiff = Duration.between(pastTime, now).toMinutes();
return minutesDiff;
}
/**
* 判断一个时间与当前时间间隔几分钟
*
* @param pastDate
* @return
*/
public static long relativeMinute(Date pastDate) {
return relativeMinute(pastDate, new Date());
}
/**
* 获取实体类中指定字段的属性和值
*
* @param obj 实体类对象
* @param fieldName 要查询的字段名
* @return 字段信息字符串
*/
public static String getFieldValue(Object obj, String fieldName) {
if (obj == null || fieldName == null || fieldName.isEmpty()) {
return "参数不能为空";
}
Class<?> clazz = obj.getClass();
try {
// 获取指定字段(包括私有字段)
Field field = clazz.getDeclaredField(fieldName);
// 设置访问权限(私有字段需要开启)
field.setAccessible(true);
// 1. 获取字段属性信息
String fieldType = field.getType().getSimpleName(); // 字段类型(如String、Integer)
// 2. 获取字段值
Object fieldValue = field.get(obj); // 获取字段值
String valueStr;
// 处理特殊类型(如Date格式化)
if (fieldValue instanceof Date) {
valueStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) fieldValue);
} else {
valueStr = (fieldValue != null) ? fieldValue.toString() : "null";
}
return valueStr;
} catch (NoSuchFieldException e) {
return "字段不存在:" + fieldName;
} catch (IllegalAccessException e) {
return "无法访问字段:" + fieldName + ",原因:" + e.getMessage();
}
}
public static String getFieldType(Object obj, String fieldName) {
if (obj == null || fieldName == null || fieldName.isEmpty()) {
return "参数不能为空";
}
Class<?> clazz = obj.getClass();
try {
// 获取指定字段(包括私有字段)
Field field = clazz.getDeclaredField(fieldName);
// 设置访问权限(私有字段需要开启)
field.setAccessible(true);
// 1. 获取字段属性信息
return field.getType().getSimpleName(); // 字段类型(如String、Integer)
} catch (NoSuchFieldException e) {
return "字段不存在:" + fieldName;
}
}
/**
* 合并多个Base64编码的PDF为字节数组
*
* @param base64PdfList Base64字符串列表
* @return 合并后的PDF字节数组
* @throws Exception 处理异常
*/
private static byte[] mergeBase64Pdfs(List<String> base64PdfList) throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Document document = null;
PdfCopy pdfCopy = null;
try {
for (int i = 0; i < base64PdfList.size(); i++) {
// 解码Base64为PDF字节数组
String base64Str = base64PdfList.get(i);
byte[] pdfBytes = Base64.decode(base64Str); // 使用Hutool解码
InputStream inputStream = new ByteArrayInputStream(pdfBytes);
// 读取PDF文件
PdfReader reader = new PdfReader(inputStream);
// 初始化文档(仅第一次循环时)
if (i == 0) {
document = new Document(reader.getPageSizeWithRotation(1));
pdfCopy = new PdfCopy(document, outputStream);
document.open();
}
// 合并所有页面
for (int pageNum = 1; pageNum <= reader.getNumberOfPages(); pageNum++) {
pdfCopy.addPage(pdfCopy.getImportedPage(reader, pageNum));
}
// 释放资源
pdfCopy.freeReader(reader);
reader.close();
inputStream.close();
}
} finally {
// 关闭文档和输出流
if (document != null) {
document.close();
}
if (outputStream != null) {
outputStream.close();
}
}
return outputStream.toByteArray();
}
/**
* 合并多个Base64编码的PDF为一个PDF文件
* @param base64PdfList 包含多个PDF的Base64字符串列表
* @param outputFilePath 合并后PDF的输出路径(如:D:/merged.pdf)
* @throws Exception 处理过程中的异常
*/
public static void mergeToFile(List<String> base64PdfList, String outputFilePath) throws Exception {
// 1. 合并所有PDF字节流为一个字节数组
byte[] mergedBytes = mergeBase64Pdfs(base64PdfList);
// 2. 将合并后的字节数组写入文件
try (FileOutputStream fos = new FileOutputStream(outputFilePath)) {
fos.write(mergedBytes);
}
System.out.println("PDF合并完成,输出路径:" + outputFilePath);
}
/**
* 判断目标字符串是否在两个字符串之间(按字典顺序)
* @param target 目标字符串
* @param start 起始字符串
* @param end 结束字符串
* @return 如果target在start和end之间(包含边界),返回true,否则返回false
*/
public static boolean isStringBetween(String target, String start, String end) {
// 检查目标字符串是否大于等于start且小于等于end
return target.compareTo(start) >= 0 && target.compareTo(end) <= 0;
}
public static String isBank(Object param){
String s = String.valueOf(param);
if(s.equals("null")){
return "";
}else{
return s;
}
}
}