2025-07-23 14:19:57 +08:00
|
|
|
|
package com.czlis.interfaceCommon.utils;
|
|
|
|
|
|
|
2025-09-20 22:37:20 +08:00
|
|
|
|
import cn.hutool.core.codec.Base64;
|
2025-09-09 19:51:56 +08:00
|
|
|
|
import com.czlis.common.utils.DateUtils;
|
2025-09-20 22:37:20 +08:00
|
|
|
|
import com.lowagie.text.Document;
|
|
|
|
|
|
import com.lowagie.text.pdf.PdfCopy;
|
|
|
|
|
|
import com.lowagie.text.pdf.PdfReader;
|
2025-09-09 19:51:56 +08:00
|
|
|
|
|
2025-09-20 22:37:20 +08:00
|
|
|
|
import java.io.*;
|
2025-09-15 19:25:29 +08:00
|
|
|
|
import java.lang.reflect.Field;
|
2025-09-04 18:35:55 +08:00
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
|
import java.math.RoundingMode;
|
2025-09-15 19:25:29 +08:00
|
|
|
|
import java.text.SimpleDateFormat;
|
2025-09-09 19:51:56 +08:00
|
|
|
|
import java.time.Duration;
|
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
|
|
import java.time.ZoneId;
|
2025-09-17 18:50:28 +08:00
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
2025-07-23 14:19:57 +08:00
|
|
|
|
public class LisUtil {
|
2025-08-28 18:13:57 +08:00
|
|
|
|
// 换行符常量(跨平台)
|
|
|
|
|
|
public static final String LINE_SEPARATOR = System.lineSeparator();
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
2025-07-23 14:19:57 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 生日格式为:yyyy-MM-dd
|
|
|
|
|
|
* 根据出生日期计算年龄
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-07-23 14:19:57 +08:00
|
|
|
|
* @param birthday
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
2025-09-20 22:37:20 +08:00
|
|
|
|
public static Map<String, String> getAgeFromBirthDay(String birthday) {
|
2025-07-27 12:52:36 +08:00
|
|
|
|
return AgeUtils.getAgeFromBirthDay(birthday);
|
2025-07-23 14:19:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据年龄获取出生日期
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-07-23 14:19:57 +08:00
|
|
|
|
* @param nl
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
2025-07-27 12:52:36 +08:00
|
|
|
|
public static Date getBirthDayFromAge(int nl, String nldw) {
|
2025-09-20 22:37:20 +08:00
|
|
|
|
return AgeUtils.getBirthDayFromAge(nl, nldw);
|
2025-07-23 14:19:57 +08:00
|
|
|
|
}
|
2025-08-09 22:28:44 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 按GBK编码的字节宽度截取字符串,最多取cutlens个字节(避免截取半个汉字)
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @param str 原始字符串
|
2025-08-09 22:28:44 +08:00
|
|
|
|
* @param beginidx 起始位置
|
2025-09-20 22:37:20 +08:00
|
|
|
|
* @param cutlens 截取长度
|
2025-08-09 22:28:44 +08:00
|
|
|
|
* @return 截取后的字符串
|
|
|
|
|
|
*/
|
2025-09-20 22:37:20 +08:00
|
|
|
|
public static String substringByGbkWidth(String str, int beginidx, int cutlens) {
|
2025-08-09 22:28:44 +08:00
|
|
|
|
if (str == null || str.isEmpty()) {
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int targetWidth = cutlens; // 目标字节宽度
|
2025-09-20 22:37:20 +08:00
|
|
|
|
int currentWidth = beginidx; // 当前累计字节宽度
|
2025-08-09 22:28:44 +08:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
2025-09-17 18:50:28 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询字符串GBK编码的长度
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-09-17 18:50:28 +08:00
|
|
|
|
* @param str
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
2025-09-20 22:37:20 +08:00
|
|
|
|
public static int getGbkByteLength(String str) {
|
2025-09-17 18:50:28 +08:00
|
|
|
|
if (str == null) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 将字符串按GBK编码转换为字节数组,数组长度即为GBK编码下的宽度
|
|
|
|
|
|
try {
|
|
|
|
|
|
return str.getBytes("GBK").length;
|
|
|
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
2025-09-17 18:50:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 字符串转ASCII值
|
|
|
|
|
|
* 中文:返回GBK编码第一个字节的ASCII值(无符号)
|
|
|
|
|
|
* 英文及其他单字节字符:直接返回其ASCII值
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-09-17 18:50:28 +08:00
|
|
|
|
* @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;
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
2025-09-17 18:50:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 计算以10为底的对数
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-09-17 18:50:28 +08:00
|
|
|
|
* @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);
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
2025-09-17 18:50:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 判断字符是否为中文字符
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-09-17 18:50:28 +08:00
|
|
|
|
* @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
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
2025-08-15 19:57:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 使用正则表达式判断字符串是否为数字
|
|
|
|
|
|
* 支持:整数(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);
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
2025-08-15 19:57:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 使用正则表达式判断字符串是否为数字
|
|
|
|
|
|
* 支持:整数(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);
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
2025-08-15 19:57:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 将包含比较运算符的字符串转换为特定格式的数值字符串
|
|
|
|
|
|
* 不支持的格式或非数字内容将返回原值
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-08-15 19:57:36 +08:00
|
|
|
|
* @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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-04 18:35:55 +08:00
|
|
|
|
|
2025-08-15 19:57:36 +08:00
|
|
|
|
/**
|
2025-09-20 22:37:20 +08:00
|
|
|
|
* 保留指定小数位数(四舍五入)
|
|
|
|
|
|
* 支持比较符号 (<=、>=、<、>) 和负号 (-) 同时存在的情况
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param input 输入字符串,如 "123.1234"、">123.252"、"<=123.252"、"-45.67"、">=-89.01"
|
|
|
|
|
|
* @param decimalPlaces 要保留的小数位数,需为非负整数
|
|
|
|
|
|
* @return 格式化后的字符串,无法处理时返回原值
|
2025-08-15 19:57:36 +08:00
|
|
|
|
*/
|
2025-09-20 22:37:20 +08:00
|
|
|
|
public static String round(String input, int decimalPlaces) {
|
2025-09-04 18:35:55 +08:00
|
|
|
|
// 处理空输入或无效小数位数
|
2025-09-20 22:37:20 +08:00
|
|
|
|
if (input == null || input.trim().isEmpty() || decimalPlaces < 0) {
|
2025-08-15 19:57:36 +08:00
|
|
|
|
return input;
|
|
|
|
|
|
}
|
2025-09-04 18:35:55 +08:00
|
|
|
|
// 定义比较运算符前缀(按长度排序,确保正确识别)
|
2025-09-20 22:37:20 +08:00
|
|
|
|
String[] comparisonPrefixes = {"<=", ">=", "<", ">"};
|
2025-08-15 19:57:36 +08:00
|
|
|
|
String comparisonPrefix = "";
|
2025-09-20 22:37:20 +08:00
|
|
|
|
String remainingPart = input.trim(); // 去除首尾空格
|
2025-09-04 18:35:55 +08:00
|
|
|
|
// 提取比较运算符前缀
|
2025-08-15 19:57:36 +08:00
|
|
|
|
for (String prefix : comparisonPrefixes) {
|
2025-09-20 22:37:20 +08:00
|
|
|
|
if (remainingPart.startsWith(prefix)) {
|
2025-08-15 19:57:36 +08:00
|
|
|
|
comparisonPrefix = prefix;
|
2025-09-20 22:37:20 +08:00
|
|
|
|
remainingPart = remainingPart.substring(prefix.length()).trim(); // 去除前缀后的空格
|
2025-08-15 19:57:36 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-04 18:35:55 +08:00
|
|
|
|
// 尝试解析剩余部分为数字(使用 BigDecimal 避免精度问题)
|
2025-08-15 19:57:36 +08:00
|
|
|
|
try {
|
2025-09-04 18:35:55 +08:00
|
|
|
|
// 解析为 BigDecimal(支持正负号和小数)
|
2025-09-20 22:37:20 +08:00
|
|
|
|
BigDecimal number = new BigDecimal(remainingPart);
|
2025-09-04 18:35:55 +08:00
|
|
|
|
// 设置四舍五入模式并保留指定小数位数
|
2025-09-20 22:37:20 +08:00
|
|
|
|
BigDecimal rounded = number.setScale(decimalPlaces, RoundingMode.HALF_UP);
|
2025-09-04 18:35:55 +08:00
|
|
|
|
// 拼接比较前缀和格式化后的数字
|
2025-09-20 22:37:20 +08:00
|
|
|
|
return comparisonPrefix + rounded.toPlainString(); //toPlainString 避免科学计数法
|
2025-08-15 19:57:36 +08:00
|
|
|
|
} catch (NumberFormatException e) {
|
2025-09-04 18:35:55 +08:00
|
|
|
|
// 数字部分无法解析时返回原值
|
2025-08-15 19:57:36 +08:00
|
|
|
|
return input;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 18:13:57 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据天数返回日期,正数往后推,负数往前推
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-08-28 18:13:57 +08:00
|
|
|
|
* @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
|
2025-09-20 22:37:20 +08:00
|
|
|
|
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
|
calendar.set(Calendar.MINUTE, 0);
|
|
|
|
|
|
calendar.set(Calendar.SECOND, 0);
|
2025-08-28 18:13:57 +08:00
|
|
|
|
// 3. 返回推算的出生日期
|
|
|
|
|
|
return calendar.getTime();
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
|
|
|
|
|
public static Date relativeDate(Date date, int day) {
|
2025-08-28 18:13:57 +08:00
|
|
|
|
// 1. 获取当前时间(基于Calendar)
|
2025-09-20 22:37:20 +08:00
|
|
|
|
Calendar calendar = Calendar.getInstance();
|
2025-08-28 18:13:57 +08:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
2025-08-28 18:13:57 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 根据天数返回日期时间,正数往后推,负数往前推
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-08-28 18:13:57 +08:00
|
|
|
|
* @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();
|
|
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
|
|
|
|
|
|
public static Date relativeDateTime(Date date, int day) {
|
2025-08-28 18:13:57 +08:00
|
|
|
|
// 1. 获取当前时间(基于Calendar)
|
|
|
|
|
|
Calendar calendar = Calendar.getInstance(); // 当前时间
|
|
|
|
|
|
calendar.setTime(date);
|
|
|
|
|
|
// 2天:忽略小时,按整天推算
|
|
|
|
|
|
calendar.add(Calendar.DAY_OF_MONTH, day);
|
|
|
|
|
|
// 3. 返回推算的时间
|
|
|
|
|
|
return calendar.getTime();
|
|
|
|
|
|
}
|
2025-09-09 19:51:56 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断前一个时间与后面时间间隔几分钟
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-09-09 19:51:56 +08:00
|
|
|
|
* @param pastDate
|
|
|
|
|
|
* @param datenow
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
2025-09-20 22:37:20 +08:00
|
|
|
|
public static long relativeMinute(Date pastDate, Date datenow) {
|
2025-09-09 19:51:56 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断一个时间与当前时间间隔几分钟
|
2025-09-20 22:37:20 +08:00
|
|
|
|
*
|
2025-09-09 19:51:56 +08:00
|
|
|
|
* @param pastDate
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static long relativeMinute(Date pastDate) {
|
2025-09-20 22:37:20 +08:00
|
|
|
|
return relativeMinute(pastDate, new Date());
|
2025-09-09 19:51:56 +08:00
|
|
|
|
}
|
2025-09-15 19:25:29 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-09-20 22:37:20 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取实体类中指定字段的属性和值
|
|
|
|
|
|
*
|
|
|
|
|
|
* @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";
|
2025-09-15 19:25:29 +08:00
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
return valueStr;
|
|
|
|
|
|
} catch (NoSuchFieldException e) {
|
|
|
|
|
|
return "字段不存在:" + fieldName;
|
|
|
|
|
|
} catch (IllegalAccessException e) {
|
|
|
|
|
|
return "无法访问字段:" + fieldName + ",原因:" + e.getMessage();
|
2025-09-15 19:25:29 +08:00
|
|
|
|
}
|
2025-09-20 22:37:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:25:29 +08:00
|
|
|
|
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. 获取字段属性信息
|
2025-09-20 22:37:20 +08:00
|
|
|
|
return field.getType().getSimpleName(); // 字段类型(如String、Integer)
|
2025-09-15 19:25:29 +08:00
|
|
|
|
} catch (NoSuchFieldException e) {
|
|
|
|
|
|
return "字段不存在:" + fieldName;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-20 22:37:20 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 合并多个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);
|
|
|
|
|
|
}
|
2025-07-23 14:19:57 +08:00
|
|
|
|
}
|