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 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 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 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 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; } }