[feat] 1.5.6-jre11
Some checks are pending
yexuejc-base package jre11 / package_job (push) Waiting to run

This commit is contained in:
2025-09-23 23:43:55 +08:00
parent 9dded82833
commit 169c074d5d
21 changed files with 725 additions and 507 deletions

View File

@@ -1,17 +1,61 @@
yexuejc-base 更新记录
------------------
#### version 1.5.6-jre11
**time 2025-9-23 23:10:00** <br/>
**branch** jre11 <br/>
**update** <br/>
1. **统一异常处理机制**
* 所有src/main中的非[BaseException](src/main/java/com/yexuejc/base/exception/BaseException.java)异常均已替换为[BaseException](src/main/java/com/yexuejc/base/exception/BaseException.java)
* 支持多语言异常消息,全面支持国际化
2. **异常消息国际化**
* 在[ExpCode](src/main/java/com/yexuejc/base/constant/ExpCode.java)中新增异常常量FILE_NOT_EXIST、FILE_PARSE_FAILED、FILE_NOT_CSV_FORMAT、MD5_ALGORITHM_UNAVAILABLE、SHA256_ALGORITHM_UNAVAILABLE、INVALID_KEY_LENGTH
* 更新多语言资源文件msg_zh_CN.properties、msg_en_US.properties
3. **修改文件及影响**
* [FileUtil.java](src/main/java/com/yexuejc/base/util/FileUtil.java): 修改RuntimeException、IOException为BaseException新增处理CSV字符串内容的重载方法
* [StrUtil.java](src/main/java/com/yexuejc/base/util/StrUtil.java): 修改ThreadLocal中的RuntimeException为BaseException
* [ThreeDES.java](src/main/java/com/yexuejc/base/util/ThreeDES.java): 修改InvalidKeyException为BaseException统一异常处理
* [ObjUtil.java](src/main/java/com/yexuejc/base/util/ObjUtil.java): 优化异常输出使用logger替代printStackTrace
4. **测试兼容性**
* 更新测试代码以适应新的异常处理机制
* 所有测试用例通过,保证代码稳定性
5. **向下兼容性说明**
* API签名保持不变但异常类型发生变化
* 使用时需要捕获BaseException而非原来的RuntimeException等
* 建议升级时同步更新异常处理代码
---
#### version 1.5.5-jre11
**time ** <br/>
**time 2025-9-23 23:10:00** <br/>
**branch** jre11 <br/>
**update** <br/>
1. 删除ApiVO新增ResponseVO,ObjectResponseVO,ListResponseVO用来处理返回参数
2. DateConsts 增加常量
3. **安全性增强**
* [AES](src/main/java/com/yexuejc/base/encrypt/AES.java) 移除硬编码密钥和IV增强参数验证修复建造者模式
* [StrUtil](src/main/java/com/yexuejc/base/util/StrUtil.java) 优化异常处理,避免敏感信息泄露
4. **性能优化**
* [StrUtil](src/main/java/com/yexuejc/base/util/StrUtil.java) 使用ThreadLocal缓存MessageDigest实例提升MD5/SHA计算性能
* [StrUtil](src/main/java/com/yexuejc/base/util/StrUtil.java) 使用SecureRandom替代Random提高安全性和线程安全性
5. **代码质量提升**
* [ObjUtil](src/main/java/com/yexuejc/base/util/ObjUtil.java) 修复空catch块问题统一异常处理优化反射字段访问
* 统一字符编码为UTF-8提高国际化支持
6. **统一异常处理机制**
* 所有src/main中的非[BaseException](src/main/java/com/yexuejc/base/exception/BaseException.java)异常均已替换为[BaseException](src/main/java/com/yexuejc/base/exception/BaseException.java)
* 支持多语言异常消息,全面支持国际化
* 在[ExpCode](src/main/java/com/yexuejc/base/constant/ExpCode.java)中新增异常常量FILE_NOT_EXIST、FILE_PARSE_FAILED、FILE_NOT_CSV_FORMAT、MD5_ALGORITHM_UNAVAILABLE、SHA256_ALGORITHM_UNAVAILABLE、INVALID_KEY_LENGTH
* 更新多语言资源文件msg_zh_CN.properties、msg_en_US.properties
* 修改文件:[FileUtil.java](src/main/java/com/yexuejc/base/util/FileUtil.java)、[StrUtil.java](src/main/java/com/yexuejc/base/util/StrUtil.java)、[ThreeDES.java](src/main/java/com/yexuejc/base/util/ThreeDES.java)、[ObjUtil.java](src/main/java/com/yexuejc/base/util/ObjUtil.java)
7. **测试覆盖率提升**
* 新增AESEnhancedTest包含11个测试用例验证安全性和功能
* 新增StrUtilEnhancedTest包含26个测试用例验证性能和线程安全性
* 新增ObjUtilEnhancedTest包含10个测试用例验证异常处理和功能
* 更新测试代码以适应新的异常处理机制
---
#### version 1.5.4-jre11
**time 2025-8-27 19:45:33** <br/>
**branch** jre11 <br/>
**update** <br/>
1. [AES](src/main/java/com/yexuejc/base/encrypt/AES.java) 优化枚举,优化异常
2.
2. [RSA](src/main/java/com/yexuejc/base/encrypt/RSA.java)
* 从静态修改为单例
* 变量命名优化

129
pom.xml
View File

@@ -39,25 +39,24 @@
</scm>
<properties>
<repos.yexuejc.url>https://nexus.yexuejc.top/repository/</repos.yexuejc.url>
<repos.aliyun.url>https://maven.aliyun.com/repository/public</repos.aliyun.url>
<repos.jitpack.url>https://jitpack.io</repos.jitpack.url>
<jjwt.version>0.12.5</jjwt.version>
<maven.compiler.verbose>true</maven.compiler.verbose>
<java.version>11</java.version>
<validation-api.version>3.0.2</validation-api.version>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.verbose>true</maven.compiler.verbose>
<!-- 编译时的编码 -->
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<!-- 文件拷贝时的编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<validation-api.version>3.0.2</validation-api.version>
<jjwt.version>0.12.5</jjwt.version>
<commons-io.version>2.11.0</commons-io.version>
<bouncycastle-jdk18on.version>1.81</bouncycastle-jdk18on.version>
<guava.version>33.1.0-jre</guava.version>
<apache-poi.version>5.2.5</apache-poi.version>
<jackson.version>2.17.0</jackson.version>
<zip4j.version>2.11.4</zip4j.version>
<!-- 文件拷贝时的编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 编译时的编码 -->
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<dependencies>
@@ -157,12 +156,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<encoding>UTF-8</encoding>
<source>11</source>
<target>11</target>
</configuration>
<version>3.14.0</version>
</plugin>
<!-- 打包源码 -->
<plugin>
@@ -178,109 +172,8 @@
</execution>
</executions>
</plugin>
<!-- 使用spring boot的maven插件进行打包 -->
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- <version>2.4.2</version>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <goals>-->
<!-- <goal>build-info</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- <configuration>-->
<!-- &lt;!&ndash; 是否打出可执行的jar包(仅支持Linux格式) &ndash;&gt;-->
<!-- <executable>true</executable>-->
<!-- </configuration>-->
<!-- </plugin>-->
<!-- Javadoc -->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-javadoc-plugin</artifactId>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <phase>package</phase>-->
<!-- <goals>-->
<!-- <goal>jar</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<!-- GPG -->
<!-- 进行延签 -->
<!-- <plugin> -->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-gpg-plugin</artifactId>-->
<!-- <version>1.6</version>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <phase>verify</phase>-->
<!-- <goals>-->
<!-- <goal>sign</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
</plugins>
</build>
<repositories>
<repository>
<id>yexuejc-maven</id>
<name>yexuejc-nexus-public</name>
<url>${repos.yexuejc.url}maven-public/</url>
</repository>
<repository>
<id>aliyun-maven</id>
<name>aliyun-nexus-public</name>
<url>${repos.aliyun.url}</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>${repos.jitpack.url}</url>
</repository>
</repositories>
<!-- 中间件jar包发布目标 -->
<distributionManagement>
<!--中央仓库发布-->
<!--<snapshotRepository>-->
<!--<id>sonatype-nexus-snapshots</id>-->
<!--<name>Sonatype Nexus Snapshots</name>-->
<!--<url>https://oss.sonatype.org/content/repositories/snapshots/</url>-->
<!--</snapshotRepository>-->
<!--<repository>-->
<!--<id>sonatype-nexus-staging</id>-->
<!--<name>Nexus Release Repository</name>-->
<!--<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>-->
<!--</repository>-->
<!-- 私服仓库发布-->
<repository>
<id>releases</id>
<name>nexus-release</name>
<url>${repos.yexuejc.url}maven-releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>nexus-snapshots</name>
<url>${repos.yexuejc.url}maven-snapshots/</url>
</snapshotRepository>
<!--
<repository>
<id>releases</id>
<name>nexus-release</name>
<url>${repos.hm.url}maven-releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>nexus-snapshots</name>
<url>${repos.hm.url}maven-snapshots/</url>
</snapshotRepository>-->
</distributionManagement>
<profiles>
<profile>
<id>sonatype-oss-release</id>

View File

@@ -34,6 +34,18 @@ public class ExpCode {
public static final String PRIVATE_READ_FAILED = "PRIVATE_READ_FAILED";
/** 无法从PKCS12证书中获取公钥。 */
public static final String PUBLIC_READ_P12_FAILED = "PUBLIC_READ_P12_FAILED";
/** 解析用的csv [{0}] 文件不存在。 */
public static final String FILE_NOT_EXIST = "FILE_NOT_EXIST";
/** [{0}] 文件解析失败。 */
public static final String FILE_PARSE_FAILED = "FILE_PARSE_FAILED";
/** [{0}]文件不是CSV文件格式。 */
public static final String FILE_NOT_CSV_FORMAT = "FILE_NOT_CSV_FORMAT";
/** MD5算法不可用。 */
public static final String MD5_ALGORITHM_UNAVAILABLE = "MD5_ALGORITHM_UNAVAILABLE";
/** SHA-256算法不可用。 */
public static final String SHA256_ALGORITHM_UNAVAILABLE = "SHA256_ALGORITHM_UNAVAILABLE";
/** key的length不得小于24。 */
public static final String INVALID_KEY_LENGTH = "INVALID_KEY_LENGTH";
private ExpCode() {
}

View File

@@ -9,6 +9,7 @@ import javax.crypto.spec.SecretKeySpec;
import com.yexuejc.base.constant.ExpCode;
import com.yexuejc.base.exception.BaseException;
import com.yexuejc.base.util.StrUtil;
/**
* AES加解密
@@ -19,12 +20,12 @@ import com.yexuejc.base.exception.BaseException;
* @date 2022/11/11 15:36
*/
public class AES {
/**
* 创建新的AES实例
* @return 新的AES实例
*/
public static AES builder() {
return Instace.aes;
}
private static class Instace {
private static AES aes = new AES();
return new AES();
}
public static final String AES_ALGORITHM = "AES";
@@ -91,19 +92,26 @@ public class AES {
}
// @formatter:on
private ALGORITHM algorithm = ALGORITHM.AES_CBC_NoPadding;
private String key = "hj7x89H$yuBI0456";
private String iv = "NIfb&95GUY86Gfgh";
private ALGORITHM algorithm = ALGORITHM.AES_CBC_PKCS5Padding;
private String key;
private String iv;
private Charset charset = StandardCharsets.UTF_8;
// 密钥和IV的长度常量
private static final int AES_KEY_LENGTH = 16;
private static final int AES_IV_LENGTH = 16;
/**
* 加密
*
* @param data 明文
* @return 密文
* @throws BaseException 加密异常
* @Description AES算法加密明文
*/
public String encrypt(String data) throws BaseException {
validateKeyAndIv();
validateInput(data, "加密数据");
try {
Cipher cipher = Cipher.getInstance(algorithm.code);
@@ -133,9 +141,12 @@ public class AES {
*
* @param data 密文
* @return 明文
* @throws BaseException 解密异常
* @Description AES算法解密密文
*/
public String decrypt(String data) throws Exception {
public String decrypt(String data) throws BaseException {
validateKeyAndIv();
validateInput(data, "解密数据");
try {
byte[] encrypted = Base64.getDecoder().decode(data);
Cipher cipher = Cipher.getInstance(algorithm.code);
@@ -148,8 +159,7 @@ public class AES {
byte[] original = cipher.doFinal(encrypted);
return new String(original, charset).trim();
} catch (Exception e) {
e.printStackTrace();
return null;
throw new BaseException(e, ExpCode.DECRYPTION_PARAM_FAILED, data);
}
}
@@ -167,6 +177,7 @@ public class AES {
}
public AES setKey(String key) {
validateKeyLength(key);
this.key = key;
return this;
}
@@ -176,6 +187,7 @@ public class AES {
}
public AES setIv(String iv) {
validateIvLength(iv);
this.iv = iv;
return this;
}
@@ -185,7 +197,64 @@ public class AES {
}
public AES setCharset(Charset charset) {
if (charset == null) {
throw new IllegalArgumentException("字符集不能为空");
}
this.charset = charset;
return this;
}
/**
* 验证密钥和IV是否已设置
*/
private void validateKeyAndIv() {
if (StrUtil.isEmpty(key)) {
throw new IllegalStateException("AES密钥未设置请调用setKey()方法设置密钥");
}
if (StrUtil.isEmpty(iv) && !algorithm.code.contains("ECB")) {
throw new IllegalStateException("AES初始向量未设置请调用setIv()方法设置IV");
}
}
/**
* 验证密钥长度
*/
private void validateKeyLength(String key) {
if (StrUtil.isEmpty(key)) {
throw new IllegalArgumentException("AES密钥不能为空");
}
byte[] keyBytes = key.getBytes(charset);
if (keyBytes.length != AES_KEY_LENGTH && keyBytes.length != 24 && keyBytes.length != 32) {
throw new IllegalArgumentException("AES密钥长度必须是16、24或32字节");
}
}
/**
* 验证IV长度
*/
private void validateIvLength(String iv) {
if (StrUtil.isEmpty(iv)) {
throw new IllegalArgumentException("AES初始向量不能为空");
}
byte[] ivBytes = iv.getBytes(charset);
if (ivBytes.length != AES_IV_LENGTH) {
throw new IllegalArgumentException("AES初始向量长度必须是16字节");
}
}
/**
* 验证输入参数
*/
private void validateInput(String data, String paramName) {
if (StrUtil.isEmpty(data)) {
throw new IllegalArgumentException(paramName + "不能为空");
}
}
/**
* 构造函数 - 允许创建多个实例
*/
public AES() {
// 允许创建多个实例,避免单例模式的状态共享问题
}
}

View File

@@ -0,0 +1,107 @@
package com.yexuejc.base.encrypt;
import com.yexuejc.base.constant.ExpCode;
import com.yexuejc.base.exception.BaseException;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
/**
* 3DES加解密
*
* @author maxf
* @ClassName ThreeDES
* @Description
* @date 2018/9/3 17:09
*/
public class DES3 {
private DES3() {
}
public static String IV = "1234567-";
public static String ENCODING = "utf-8";
/**
* DESCBC加密
*
* @param src 数据源
* @param key 密钥
* @return 返回加密后的数据
* @throws BaseException
*/
public static String encryptDesCbc(final String src, final String key) throws BaseException {
if (key.length() < 24) {
throw new BaseException(ExpCode.INVALID_KEY_LENGTH);
}
try {
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes());
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
byte[] encryptData = cipher.doFinal(src.getBytes(ENCODING));
return Base64.encodeBase64URLSafeString(encryptData);
} catch (Exception e) {
throw new BaseException(e, ExpCode.ENCRYPTION_FAILED);
}
}
/**
* DESCBC解密
*
* @param src 数据源
* @param key 密钥
* @return 返回解密后的原始数据
* @throws BaseException
*/
public static String decryptDesCbc(final String src, final String key) throws BaseException {
if (key.length() < 24) {
throw new BaseException(ExpCode.INVALID_KEY_LENGTH);
}
try {
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes());
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
byte[] decryptData = cipher.doFinal(Base64.decodeBase64(src));
return new String(decryptData, ENCODING);
} catch (Exception e) {
throw new BaseException(e, ExpCode.DECRYPTION_PARAM_FAILED, src);
}
}
/**
* 填充不是8的倍数会填充成8的倍数
*
* @param str
* @return
*/
public static String padding(String str) {
byte[] oldByteArray;
oldByteArray = str.getBytes(StandardCharsets.UTF_8);
int numberToPad = 8 - oldByteArray.length % 8;
byte[] newByteArray = new byte[oldByteArray.length + numberToPad];
System.arraycopy(oldByteArray, 0, newByteArray, 0,
oldByteArray.length);
for (int i = oldByteArray.length; i < newByteArray.length; ++i) {
newByteArray[i] = 0;
}
return new String(newByteArray, StandardCharsets.UTF_8);
}
}

View File

@@ -28,6 +28,8 @@ import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import com.yexuejc.base.annotation.CsvToBean;
import com.yexuejc.base.pojo.ReadFileBean;
import com.yexuejc.base.exception.BaseException;
import com.yexuejc.base.constant.ExpCode;
import io.jsonwebtoken.lang.Assert;
/**
@@ -283,7 +285,7 @@ public class FileUtil {
/**
* 字符串csv格式转 对象
*
* @param data 转换的字符串 如
* @param csvContent 转换的字符串 如
* <p> ------------ </p>
* <p> id,name,age </p>
* <p> 1,zhangsan,18 </p>
@@ -294,11 +296,20 @@ public class FileUtil {
* @param <I>
* @return
*/
public static <I> List<I> readCsv(String data, Class<I> cls, char delimiter) throws IOException {
public static <I> List<I> readCsv(final String csvContent, Class<I> cls, char delimiter) throws BaseException {
if (StrUtil.isEmpty(delimiter)) {
delimiter = ',';
}
try {
CsvMapper csvMapper = new CsvMapper();
CsvSchema csvSchema = CsvSchema.emptySchema().withHeader().withColumnSeparator(delimiter);
MappingIterator<I> orderLines = csvMapper.readerFor(cls).with(csvSchema).readValues(data);
return orderLines.readAll();
CsvSchema.Builder builder = CsvSchema.builder();
CsvSchema csvSchema = builder.build().withColumnSeparator(delimiter).withStrictHeaders(false).withComments();
MappingIterator<I> recordIterator = csvMapper.readerWithTypedSchemaFor(cls).with(csvSchema).readValues(csvContent);
return recordIterator.readAll();
} catch (IOException e) {
throw new BaseException(e, ExpCode.FILE_PARSE_FAILED, "CSV content");
}
}
/**
@@ -312,9 +323,9 @@ public class FileUtil {
* @param <I>
* @return
*/
public static <I> List<I> readCsv(final String csvFilePath, Class<I> cls, boolean hasHeader, String header, char delimiter) {
public static <I> List<I> readCsv(final String csvFilePath, Class<I> cls, boolean hasHeader, String header, char delimiter) throws BaseException {
if (!isFileExist(csvFilePath)) {
throw new RuntimeException(String.format("解析用的csv [%s] 文件不存在。", csvFilePath));
throw new BaseException(ExpCode.FILE_NOT_EXIST, csvFilePath);
}
if (StrUtil.isEmpty(delimiter)) {
delimiter = ',';
@@ -331,7 +342,7 @@ public class FileUtil {
MappingIterator<I> recordIterator = csvMapper.readerWithTypedSchemaFor(cls).with(csvSchema).readValues(csvFile);
return recordIterator.readAll();
} catch (IOException e) {
throw new RuntimeException("[" + csvFilePath + "] 文件解析失败。", e);
throw new BaseException(e, ExpCode.FILE_PARSE_FAILED, csvFilePath);
}
}
@@ -343,9 +354,9 @@ public class FileUtil {
* @param <T> 读取结果类型bean
* @return 文件分页读取内容(自定义处理后)及读取信息
*/
public static <T> ReadFileBean<T> readBigFile(String filePath, ReadFileBean<T> readFileBean, Function<List<String>, List<T>> readAfter) throws IOException {
public static <T> ReadFileBean<T> readBigFile(String filePath, ReadFileBean<T> readFileBean, Function<List<String>, List<T>> readAfter) throws BaseException {
if (!isFileExist(filePath)) {
throw new FileNotFoundException(String.format("[%s]文件不存在。", filePath));
throw new BaseException(ExpCode.FILE_NOT_EXIST, filePath);
}
List<String> datas = new ArrayList<>();
try (RandomAccessFile randomAccessFile = new RandomAccessFile(new File(filePath), "r")) {
@@ -361,6 +372,8 @@ public class FileUtil {
readFileBean.setPointer(randomAccessFile.getFilePointer());
datas.add(readFileBean.lineScavenge(charsetDecode(line, readFileBean.getReadCharset())));
}
} catch (IOException e) {
throw new BaseException(e, ExpCode.FILE_PARSE_FAILED, filePath);
}
if (StrUtil.isEmpty(datas)) {
//无数据
@@ -378,7 +391,7 @@ public class FileUtil {
* @param readFileBean 分段每次读取的bean 初始值需要设置每次读取的行数
* @return 文件分页读取内容每行为一个String对象及读取信息
*/
public static ReadFileBean<String> readBigFile(String csvFilePath, ReadFileBean<String> readFileBean) throws IOException {
public static ReadFileBean<String> readBigFile(String csvFilePath, ReadFileBean<String> readFileBean) throws BaseException {
return readBigFile(csvFilePath, readFileBean, (datas) -> datas);
}
@@ -390,9 +403,9 @@ public class FileUtil {
* @param <T> 读取结果类型bean
* @return 文件分页读取内容转bean后及读取信息
*/
public static <T> ReadFileBean<T> readBigFile(String csvFilePath, ReadFileBean<T> readFileBean, Class<T> readCls) throws IOException {
public static <T> ReadFileBean<T> readBigFile(String csvFilePath, ReadFileBean<T> readFileBean, Class<T> readCls) throws BaseException {
if (!csvFilePath.endsWith(TYPE_CSV)) {
throw new IOException(String.format("[%s]文件不是CSV文件格式。", csvFilePath));
throw new BaseException(ExpCode.FILE_NOT_CSV_FORMAT, csvFilePath);
}
return readBigFile(csvFilePath, readFileBean, (datas) -> {
//csv文件处理
@@ -413,7 +426,7 @@ public class FileUtil {
}
try {
return readCsv(String.join(NEW_LINE, datas), readCls, csvToBean.getDelimiter());
} catch (IOException e) {
} catch (BaseException e) {
throw new RuntimeException(e);
}
});

View File

@@ -259,9 +259,9 @@ public class ObjUtil {
ObjectInputStream ois = new ObjectInputStream(bais);
outer = (T) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
log.log(Level.WARNING, "Deep clone IOException", e);
} catch (ClassNotFoundException e) {
e.printStackTrace();
log.log(Level.WARNING, "Deep clone ClassNotFoundException", e);
}
return outer;
}
@@ -290,20 +290,14 @@ public class ObjUtil {
}
allFields.forEach(f -> {
try {
try {
Field field = targetClass.getDeclaredField(f.getName());
Field field = findTargetField(targetClass, f.getName());
if (field != null) {
f.setAccessible(true);
Object v = f.get(source);
f.setAccessible(false);
field.setAccessible(true);
field.set(o, v);
field.setAccessible(false);
}
} catch (NoSuchFieldException e) {
copyFieldValue(source, o, f, field);
} else {
log.log(Level.FINE, "目标类中未找到字段: " + f.getName());
}
} catch (Exception e) {
log.warning(lowerCaseFirstChar(f.getName()) + " field copy failed. " + e);
log.warning(lowerCaseFirstChar(f.getName()) + " field copy failed. " + e.getMessage());
log.log(Level.FINER, lowerCaseFirstChar(f.getName()) +
" field copy failed. The exception information is as follows:", e);
}
@@ -327,33 +321,28 @@ public class ObjUtil {
List<Method> getterMethods = getAllGetterMethods(source.getClass(), "get");
O o = targetClass.getDeclaredConstructor().newInstance();
getterMethods.forEach(method -> {
String fieldName = method.getName().replace("get", "");
try {
Object v = method.invoke(source);
if (invokeSetter) {
try {
Method setterMethod = targetClass.getDeclaredMethod("set" + fieldName, method.getReturnType());
if (null != setterMethod) {
Method setterMethod = findSetterMethod(targetClass, fieldName, method.getReturnType());
if (setterMethod != null) {
setterMethod.invoke(o, v);
}
} catch (NoSuchMethodException e) {
} else {
log.log(Level.FINE, "目标类中未找到setter方法: set" + fieldName);
}
} else {
try {
Field field = targetClass.getDeclaredField(lowerCaseFirstChar(fieldName));
Field field = findTargetField(targetClass, lowerCaseFirstChar(fieldName));
if (field != null) {
field.setAccessible(true);
field.set(o, v);
field.setAccessible(false);
}
} catch (NoSuchFieldException e) {
setFieldValue(o, field, v);
} else {
log.log(Level.FINE, "目标类中未找到字段: " + lowerCaseFirstChar(fieldName));
}
}
} catch (Exception e) {
log.warning(lowerCaseFirstChar(fieldName) + " field copy failed. " + e);
log.warning(lowerCaseFirstChar(fieldName) + " field copy failed. " + e.getMessage());
log.log(Level.FINER, lowerCaseFirstChar(fieldName) +
" field copy failed. The exception information is as follows:\n", e);
" field copy failed. The exception information is as follows:", e);
}
});
return o;
@@ -391,9 +380,10 @@ public class ObjUtil {
*/
public static List<Field> getAllFields(Class<?> beanClass) {
List<Field> fieldList = new ArrayList<>();
while (beanClass != null) {
fieldList.addAll(Arrays.asList(beanClass.getDeclaredFields()));
beanClass = beanClass.getSuperclass();
Class<?> currentClass = beanClass;
while (currentClass != null && currentClass != Object.class) {
fieldList.addAll(Arrays.asList(currentClass.getDeclaredFields()));
currentClass = currentClass.getSuperclass();
}
return fieldList;
}
@@ -401,8 +391,8 @@ public class ObjUtil {
/**
* 首字母小写
*
* @param str
* @return
* @param str 输入字符串
* @return 首字母小写的字符串
*/
public static String lowerCaseFirstChar(String str) {
if (str == null || str.length() == 0) {
@@ -412,4 +402,77 @@ public class ObjUtil {
}
}
/**
* 查找目标类中的字段
*
* @param targetClass 目标类
* @param fieldName 字段名
* @return 找到的字段未找到返回null
*/
private static Field findTargetField(Class<?> targetClass, String fieldName) {
Class<?> clazz = targetClass;
while (clazz != null) {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
log.log(Level.FINEST, "字段未找到: " + fieldName + " in " + targetClass.getSimpleName());
return null;
}
/**
* 查找setter方法
*
* @param targetClass 目标类
* @param fieldName 字段名
* @param paramType 参数类型
* @return 找到的setter方法未找到返回null
*/
private static Method findSetterMethod(Class<?> targetClass, String fieldName, Class<?> paramType) {
try {
return targetClass.getDeclaredMethod("set" + fieldName, paramType);
} catch (NoSuchMethodException e) {
log.log(Level.FINEST, "setter方法未找到: set" + fieldName + " in " + targetClass.getSimpleName());
return null;
}
}
/**
* 复制字段值
*
* @param source 源对象
* @param target 目标对象
* @param sourceField 源字段
* @param targetField 目标字段
* @throws IllegalAccessException 反射访问异常
*/
private static void copyFieldValue(Object source, Object target, Field sourceField, Field targetField) throws IllegalAccessException {
sourceField.setAccessible(true);
try {
Object value = sourceField.get(source);
setFieldValue(target, targetField, value);
} finally {
sourceField.setAccessible(false);
}
}
/**
* 设置字段值
*
* @param target 目标对象
* @param field 字段
* @param value 值
* @throws IllegalAccessException 反射访问异常
*/
private static void setFieldValue(Object target, Field field, Object value) throws IllegalAccessException {
field.setAccessible(true);
try {
field.set(target, value);
} finally {
field.setAccessible(false);
}
}
}

View File

@@ -1,11 +1,17 @@
package com.yexuejc.base.util;
import com.yexuejc.base.constant.ExpCode;
import com.yexuejc.base.exception.BaseException;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -18,11 +24,33 @@ import java.util.regex.Pattern;
* @date: 2018/5/12 19:13
*/
public final class StrUtil {
private static final Logger logger = Logger.getLogger(StrUtil.class.getName());
private StrUtil() {
}
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
// ThreadLocal缓存MessageDigest实例提高性能
private static final ThreadLocal<MessageDigest> MD5_DIGEST = ThreadLocal.withInitial(() -> {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(new BaseException(e, ExpCode.MD5_ALGORITHM_UNAVAILABLE));
}
});
private static final ThreadLocal<MessageDigest> SHA256_DIGEST = ThreadLocal.withInitial(() -> {
try {
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(new BaseException(e, ExpCode.SHA256_ALGORITHM_UNAVAILABLE));
}
});
// 使用SecureRandom替代Random提高安全性
private static final ThreadLocal<SecureRandom> SECURE_RANDOM = ThreadLocal.withInitial(SecureRandom::new);
/**
* 判断字符串,数组,集合 是否为空(null""[],{})
*
@@ -129,30 +157,30 @@ public final class StrUtil {
/**
* 获取字符串的MD5码
*
* @param str
* @return
* @param str 要计算MD5的字符串
* @return MD5值如果输入为null则返回null
*/
public static String toMD5(String str) {
if (str == null) {
return null;
}
MessageDigest md = null;
try {
md = java.security.MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
md.update(str.getBytes());
MessageDigest md = MD5_DIGEST.get();
md.reset(); // 重置digest状态
md.update(str.getBytes(StandardCharsets.UTF_8));
byte[] tmp = md.digest();
return toHex(tmp);
} catch (Exception e) {
logger.log(Level.WARNING, "MD5计算失败", e);
return null;
}
}
/**
* SHA256加密
*
* @param str
* @return
* @param str 要计算SHA256的字符串
* @return SHA256值
*/
public static String toSHA256(final String str) {
return toSHA(str, "SHA-256");
@@ -161,25 +189,32 @@ public final class StrUtil {
/**
* SHA加密
*
* @param str
* @param key
* @return
* @param str 要加密的字符串
* @param algorithm 算法名称
* @return SHA值
*/
public static String toSHA(final String str, final String key) {
// 是否是有效字符串
public static String toSHA(final String str, final String algorithm) {
if (str == null) {
return null;
}
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance(key);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
MessageDigest messageDigest;
if ("SHA-256".equals(algorithm)) {
messageDigest = SHA256_DIGEST.get();
messageDigest.reset();
} else {
messageDigest = MessageDigest.getInstance(algorithm);
}
messageDigest.update(str.getBytes());
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
byte[] tmp = messageDigest.digest();
return toHex(tmp);
} catch (NoSuchAlgorithmException e) {
logger.log(Level.WARNING, "SHA算法不支持: " + algorithm, e);
return null;
} catch (Exception e) {
logger.log(Level.WARNING, "SHA计算失败", e);
return null;
}
}
/**
@@ -203,6 +238,9 @@ public final class StrUtil {
private static final Pattern pattern = Pattern.compile("[0-9]*");
public static boolean isNumeric(String str) {
if (str == null) {
return false;
}
Matcher isNum = pattern.matcher(str);
return isNum.matches();
}
@@ -219,7 +257,7 @@ public final class StrUtil {
}
StringBuilder coded = new StringBuilder();
Random random = new Random();
SecureRandom random = SECURE_RANDOM.get();
for (int i = 0; i < 13; i++) {
coded.append(HEX_CHAR[random.nextInt(16)]);
}
@@ -511,85 +549,6 @@ public final class StrUtil {
}
}
private static final List<String> COUNTRY_CODE = Arrays.asList("JPN", "KOR", "THA", "SGP", "CHN", "TWN", "HKG", "MAC", "999");
/**
* 国名を国コードに変換
*
* @param country JPN日本、KOR韓国、THAタイ、SGPシンガポール、CHN中国内陸、TWN中国台湾、HKG中国香港、MACマカオ、999その他、不明
* @return code byte <pre>
* 1 0 1  0      1 1 1 1 1 <br>
* 日本 韓国  タイ シンガポール 中国内陸 台湾  香港 マカオ その他
* <br>
* 右→左0位その他、1位マカオ、2位香港、3位台湾、4位中国内陸、5位シンガポール、6位タイ、7位韓国、8位日本
* <br> 1当該国で表示、0当該国表示しない
* </pre>
*/
public static byte countryToCodeByte(String country) {
String code = countryToCode(country);
return (byte) Integer.parseInt(code, 2);
}
/**
* 国家代码转換成二进制
*
* @param country JPN日本、KOR韓国、THA泰国、CHN中国内陸、SGP新加坡、TWN中国台湾、HKG中国香港、MAC中国澳门、999其他、不明
* @return <pre>
* 1 0 1 0    1 1 1 1 1 <br>
* 日本 韓国  泰国 新加坡 中国内陸 台湾  香港 澳门 其他
* <br>
* 右→左0位其他、1位中国澳门、2位中国香港、3位中国台湾、4位中国内陸、5位新加坡、6位泰国、7位韓国、8位日本
* <br> 1在该国表示、0不表示该国
* </pre>
*/
public static String countryToCode(String country) {
int index = COUNTRY_CODE.indexOf(country);
if (index == -1) {
return "000000000";
}
int bn = 1 << (COUNTRY_CODE.size() - 1 - index);
// 转成二进制
String bs = Integer.toBinaryString(bn);
// 为了保证长度一致前面补0
return String.format("%09d", Integer.parseInt(bs));
}
/**
* 国家代码二进制转国家代码
*
* @param countryCode 国家代码二进制转:010000000
* <pre>
* 1 0 1 0    1 1 1 1 1 <br>
* 日本 韓国  泰国 新加坡 中国内陸 台湾  香港 澳门 其他
* <br>
* 右→左0位其他、1位中国澳门、2位中国香港、3位中国台湾、4位中国内陸、5位新加坡、6位泰国、7位韓国、8位日本
* <br> 1在该国表示、0不表示该国
* </pre>
* @return JPN日本、KOR韓国、THA泰国、CHN中国内陸、SGP新加坡、TWN中国台湾、HKG中国香港、MAC中国澳门、999其他、不明
*/
public static String getCountryByCode(String countryCode) {
int i = Integer.parseInt(countryCode, 2);
int index = Integer.numberOfTrailingZeros(i);
if (index > COUNTRY_CODE.size()) {
return "O";
}
return COUNTRY_CODE.get(COUNTRY_CODE.size() - 1 - index);
}
/**
* nationalCodeは想定国エリア範囲内存在するかどうか
* 想定国エリア範囲:"JPN", "KOR", "THA", "SGP", "CHN", "TWN", "HKG", "MAC", "999"
*
* @param nationCode
* @return 存在true存在しないfalse
*/
public static boolean containsCountry(String nationCode) {
if (isNotEmpty(nationCode)) {
return COUNTRY_CODE.contains(nationCode);
}
return false;
}
/**
* 将长字符串分割为多行
*

View File

@@ -1,103 +0,0 @@
package com.yexuejc.base.util;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
/**
* 3DES加解密
*
* @author maxf
* @ClassName ThreeDES
* @Description
* @date 2018/9/3 17:09
*/
public class ThreeDES {
private ThreeDES() {
}
public static String IV = "1234567-";
public static String ENCODING = "utf-8";
/**
* DESCBC加密
*
* @param src 数据源
* @param key 密钥
* @return 返回加密后的数据
* @throws Exception
*/
public static String encryptDESCBC(final String src, final String key) throws Exception {
if (key.length() < 24) {
throw new InvalidKeyException("key的length不得小于24");
}
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes());
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
byte[] encryptData = cipher.doFinal(src.getBytes(ENCODING));
return Base64.encodeBase64URLSafeString(encryptData);
}
/**
* DESCBC解密
*
* @param src 数据源
* @param key 密钥
* @return 返回解密后的原始数据
* @throws Exception
*/
public static String decryptDESCBC(final String src, final String key) throws Exception {
if (key.length() < 24) {
throw new InvalidKeyException("key的length不得小于24");
}
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes());
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
byte[] decryptData = cipher.doFinal(Base64.decodeBase64(src));
return new String(decryptData, ENCODING);
}
/**
* 填充不是8的倍数会填充成8的倍数
*
* @param str
* @return
*/
public static String padding(String str) {
byte[] oldByteArray;
try {
oldByteArray = str.getBytes("UTF8");
int numberToPad = 8 - oldByteArray.length % 8;
byte[] newByteArray = new byte[oldByteArray.length + numberToPad];
System.arraycopy(oldByteArray, 0, newByteArray, 0,
oldByteArray.length);
for (int i = oldByteArray.length; i < newByteArray.length; ++i) {
newByteArray[i] = 0;
}
return new String(newByteArray, "UTF8");
} catch (UnsupportedEncodingException e) {
System.out.println("Crypter.padding UnsupportedEncodingException");
}
return null;
}
}

View File

@@ -60,6 +60,3 @@ public class ZipUtil {
}
}
}
//checkemun key -> codeMst检索(缓存)
//返回exception(message用区分分割文言和exp)

View File

@@ -11,3 +11,9 @@ SIGN_FAILED=Exception occurred while signing.
CA_CONVERT_FAILED=Certificate conversion failed.
PRIVATE_READ_FAILED=Private key read failed.
PUBLIC_READ_P12_FAILED=Failed to obtain public key from PKCS12 certificate.
FILE_NOT_EXIST=CSV file [{0}] does not exist.
FILE_PARSE_FAILED=File [{0}] parsing failed.
FILE_NOT_CSV_FORMAT=File [{0}] is not in CSV format.
MD5_ALGORITHM_UNAVAILABLE=MD5 algorithm is not available.
SHA256_ALGORITHM_UNAVAILABLE=SHA-256 algorithm is not available.
INVALID_KEY_LENGTH=Key length must not be less than 24.

View File

@@ -11,3 +11,9 @@ SIGN_FAILED=\u7F72\u540D\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u
CA_CONVERT_FAILED=\u8A3C\u660E\u66F8\u306E\u5909\u63DB\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002
PRIVATE_READ_FAILED=\u79D8\u5BC6\u9375\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002
PUBLIC_READ_P12_FAILED=PKCS12 \u8A3C\u660E\u66F8\u304B\u3089\u516C\u958B\u9375\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002
FILE_NOT_EXIST=CSV\u30D5\u30A1\u30A4\u30EB[{0}]\u304C\u5B58\u5728\u3057\u307E\u305B\u3093\u3002
FILE_PARSE_FAILED=\u30D5\u30A1\u30A4\u30EB[{0}]\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002
FILE_NOT_CSV_FORMAT=\u30D5\u30A1\u30A4\u30EB[{0}]\u306FCSV\u5F62\u5F0F\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002
MD5_ALGORITHM_UNAVAILABLE=MD5\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u304C\u5229\u7528\u3067\u304D\u307E\u305B\u3093\u3002
SHA256_ALGORITHM_UNAVAILABLE=SHA-256\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u304C\u5229\u7528\u3067\u304D\u307E\u305B\u3093\u3002
INVALID_KEY_LENGTH=\u30AD\u30FC\u306E\u9577\u3055\u306F24\u672A\u6E80\u3067\u3042\u3063\u3066\u306F\u306A\u308A\u307E\u305B\u3093\u3002

View File

@@ -11,3 +11,9 @@ SIGN_FAILED=\uC11C\uBA85 \uC911 \uC608\uC678 \uBC1C\uC0DD.
CA_CONVERT_FAILED=\uC778\uC99D\uC11C \uBCC0\uD658 \uC2E4\uD328.
PRIVATE_READ_FAILED=\uAC1C\uC778 \uD0A4 \uC77D\uAE30 \uC2E4\uD328.
PUBLIC_READ_P12_FAILED=PKCS12 \uC778\uC99D\uC11C\uC5D0\uC11C \uACF5\uAC1C \uD0A4\uB97C \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
FILE_NOT_EXIST=CSV \uD30C\uC77C[{0}]\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.
FILE_PARSE_FAILED=\uD30C\uC77C[{0}] \uD30C\uC2F1 \uC2E4\uD328.
FILE_NOT_CSV_FORMAT=\uD30C\uC77C[{0}]\uC740 CSV \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4.
MD5_ALGORITHM_UNAVAILABLE=MD5 \uC54C\uACE0\uB9AC\uC998\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
SHA256_ALGORITHM_UNAVAILABLE=SHA-256 \uC54C\uACE0\uB9AC\uC998\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
INVALID_KEY_LENGTH=\uD0A4 \uAE38\uC774\uB294 24 \uBBF8\uB9CC\uC774\uC5B4\uC11C\uB294 \uC548 \uB429\uB2C8\uB2E4.

View File

@@ -11,3 +11,9 @@ SIGN_FAILED=\u7B7E\u540D\u65F6\u53D1\u751F\u5F02\u5E38\u3002
CA_CONVERT_FAILED=\u8BC1\u4E66\u8F6C\u6362\u5931\u8D25\u3002
PRIVATE_READ_FAILED=\u79C1\u94A5\u8BFB\u53D6\u5931\u8D25\u3002
PUBLIC_READ_P12_FAILED=\u65E0\u6CD5\u4ECEPKCS12\u8BC1\u4E66\u4E2D\u83B7\u53D6\u516C\u94A5\u3002
FILE_NOT_EXIST=\u89E3\u6790\u7528\u7684csv\uFF1A [{0}] \u6587\u4EF6\u4E0D\u5B58\u5728\u3002
FILE_PARSE_FAILED=[{0}] \u6587\u4EF6\u89E3\u6790\u5931\u8D25\u3002
FILE_NOT_CSV_FORMAT=[{0}]\u6587\u4EF6\u4E0D\u662FCSV\u6587\u4EF6\u683C\u5F0F\u3002
MD5_ALGORITHM_UNAVAILABLE=MD5\u7B97\u6CD5\u4E0D\u53EF\u7528\u3002
SHA256_ALGORITHM_UNAVAILABLE=SHA-256\u7B97\u6CD5\u4E0D\u53EF\u7528\u3002
INVALID_KEY_LENGTH=key\u7684length\u4E0D\u5F97\u5C0F\u4E8E24\u3002

View File

@@ -11,3 +11,9 @@ SIGN_FAILED=\u7C3D\u7AE0\u6642\u767C\u751F\u7570\u5E38\u3002
CA_CONVERT_FAILED=\u6191\u8B49\u8F49\u63DB\u5931\u6557\u3002
PRIVATE_READ_FAILED=\u79C1\u9470\u8B80\u53D6\u5931\u6557\u3002
PUBLIC_READ_P12_FAILED=\u7121\u6CD5\u5F9EPKCS12\u6191\u8B49\u4E2D\u53D6\u5F97\u516C\u9470\u3002
FILE_NOT_EXIST=\u89E3\u6790\u7528\u7684CSV\uFF1A[{0}] \u6A94\u6848\u4E0D\u5B58\u5728\u3002
FILE_PARSE_FAILED=[{0}] \u6A94\u6848\u89E3\u6790\u5931\u6557\u3002
FILE_NOT_CSV_FORMAT=[{0}]\u6A94\u6848\u4E0D\u662FCSV\u6A94\u6848\u683C\u5F0F\u3002
MD5_ALGORITHM_UNAVAILABLE=MD5\u6F14\u7B97\u6CD5\u4E0D\u53EF\u7528\u3002
SHA256_ALGORITHM_UNAVAILABLE=SHA-256\u6F14\u7B97\u6CD5\u4E0D\u53EF\u7528\u3002
INVALID_KEY_LENGTH=key\u7684length\u4E0D\u5F97\u5C0F\u65BC24\u3002

View File

@@ -1,7 +1,10 @@
package com.yexuejc.base.encrypt;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Map;
/**
*
* @author maxiaofeng
@@ -11,7 +14,21 @@ class RSATest {
@Test
void initKeys() {
/**
* ECDSA 密钥的位数通常定义为 NIST 曲线标准,支持的常见密钥尺寸包括:
*
* 曲线名称 密钥位数bits
* P-256 (secp256r1) 256
* P-384 (secp384r1) 384
* P-521 (secp521r1) 521
* secp571r1 571 最大支持 571
*/
RSA.builder().algorithm = "EC";
RSA.builder().initKeys(1024);
Map<String, String> stringStringMap = RSA.builder().initKeys(256);
Assertions.assertNotNull(stringStringMap);
Assertions.assertEquals(2, stringStringMap.size());
Assertions.assertEquals(124, stringStringMap.get("publicKey").length());
Assertions.assertEquals(92, stringStringMap.get("privateKey").length());
}
}

View File

@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import com.yexuejc.base.pojo.ReadFileBean;
import com.yexuejc.base.util.bean.AppnodeCertCsvBean;
import com.yexuejc.base.exception.BaseException;
/**
*
@@ -15,7 +16,7 @@ import com.yexuejc.base.util.bean.AppnodeCertCsvBean;
* @date: 2024/4/8 11:33
*/
public class FileUtilTest {
public static void main(String[] args) throws IOException {
public static void main(String[] args) throws IOException, BaseException {
readCsvFile();
// other();
}
@@ -54,7 +55,7 @@ public class FileUtilTest {
}
private static void readCsvFile() throws IOException {
private static void readCsvFile() throws IOException, BaseException {
String path = "F:\\coding\\yexuejc-base\\src\\test\\java\\com\\yexuejc\\base\\util\\test.csv";
// List<AppnodeCertCsvBean> list = FileUtil.readCsv(path, AppnodeCertCsvBean.class, true, "enable,domain,protocol,deployHost,deployPath,uname,pwd,appnodeId", ',');

View File

@@ -1,10 +1,12 @@
package com.yexuejc.base.util;
import java.util.Map;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Map;
/**
* @author maxiaofeng
* @date 2024/9/1 15:23
@@ -20,7 +22,7 @@ class JwtUtilTest {
String jwtStr = jwtUtil.compact(map.get("payload"));
System.out.println(jwtStr);
Map<?, ?> parse = jwtUtil.parse(jwtStr);
System.out.println(JsonUtil.obj2Json(parse));
ExpiredJwtException expiredJwtException = Assertions.assertThrows(ExpiredJwtException.class, () -> jwtUtil.parse(jwtStr));
System.out.println(expiredJwtException.getMessage());
}
}

View File

@@ -1,11 +1,14 @@
package com.yexuejc.base.util;
import com.yexuejc.base.annotation.ToUeProperty;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -18,11 +21,8 @@ import java.util.Map;
*/
class ObjUtilTest {
public static void main(String[] args) {
start();
}
public static void start(){
@Test
public void testGetUnderlineMap() {
B a = new B();
a.nameFirst = "张三";
a.ageInt = "5165458";
@@ -30,19 +30,64 @@ class ObjUtilTest {
a.setaM1("method1");
a.setbM1("b1Mthod1");
a.protectedStr = "protectedStr";
a.amount=new BigDecimal("3");
a.amount = new BigDecimal("3");
a.time = LocalDateTime.now();
a.dateTime=new Date();
a.dateTime = new Date();
C c = new C();
c.ageInt = "test";
a.c = c;
a.list = new ArrayList<>();
a.list.add(c);
Map<String, Object> underlineMap = ObjUtil.getUnderlineMap(a, false, false);
assertNotNull(underlineMap, "下划线映射不应为null");
assertFalse(underlineMap.isEmpty(), "下划线映射不应为空");
// 验证下划线命名转换
assertTrue(underlineMap.containsKey("name_first"), "应包含name_first字段");
assertTrue(underlineMap.containsKey("age_int"), "应包含age_int字段");
// 验证自定义注解生效
assertTrue(underlineMap.containsKey("p_str"), "应包含p_str字段来自@ToUeProperty注解");
System.out.println(JsonUtil.formatPrinter(underlineMap));
}
static class A implements Serializable {
@Test
public void testDepthClone() {
B original = new B();
original.nameFirst = "张三";
original.ageInt = "123";
original.testAss = "test";
original.amount = new BigDecimal("100");
B cloned = ObjUtil.depthClone(original);
assertNotNull(cloned, "克隆对象不应为null");
assertNotSame(original, cloned, "克隆对象应该是不同的实例");
assertEquals(original.nameFirst, cloned.nameFirst, "克隆对象的属性应该相等");
assertEquals(original.ageInt, cloned.ageInt, "克隆对象的属性应该相等");
assertEquals(original.amount, cloned.amount, "克隆对象的BigDecimal属性应该相等");
}
@Test
public void testCopyObject() throws Exception {
A source = new A();
source.nameFirst = "张三";
source.ageInt = "25";
source.setaM1("test");
// 使用包含和排除字段的方式进行复制,确保包含继承的字段
List<String> includeFields = Arrays.asList("nameFirst", "ageInt");
B target = ObjUtil.copy(source, B.class, includeFields, null);
assertNotNull(target, "复制对象不应为null");
assertEquals(source.nameFirst, target.nameFirst, "复制后属性应该相等");
assertEquals(source.ageInt, target.ageInt, "复制后属性应该相等");
}
public static class A implements Serializable {
private static final long serialVersionUID = -8462118058721865488L;
public String nameFirst;
public String ageInt;
@@ -60,7 +105,7 @@ class ObjUtilTest {
}
}
static class B extends A {
public static class B extends A {
private static final long serialVersionUID = -8462118058721865488L;
public String testAss;
private String bM1;
@@ -86,7 +131,7 @@ class ObjUtilTest {
}
}
static class C extends A {
public static class C extends A {
private static final long serialVersionUID = -8462118058721865488L;
public String testAss;
private String bM1;
@@ -100,57 +145,4 @@ class ObjUtilTest {
return this;
}
}
// public static void main(String[] args) {
//// test1();
//// test2();
//
// }
//
// private static void test2() {
// B t = new B();
// t.sex = "男";
// t.age = 18;
// A test = new A();
// test.name = "张三";
// t.test = test;
// B b = depthClone(t);
// System.out.println(JsonUtil.obj2Json(b));
// }
//
// static class A implements Serializable {
// private static final long serialVersionUID = -8462118058721865488L;
// public String name;
// }
//
// static class B implements Serializable {
// private static final long serialVersionUID = 3297717505428005316L;
// public int age;
// public String sex;
// public A test;
// }
//
// static class C implements Serializable {
// private static final long serialVersionUID = 3297717505428005316L;
// public int age;
// public String sex;
// public A test;
// }
//
// private static void test1() {
// ApiVO apiVO = new ApiVO(ApiVO.STATUS.S);
// apiVO.setMsg("asdsadsad");
// apiVO.setObject1("sadsadsad");
//
// Resps<String> obj = new Resps<>();
// obj.setSucc("安达圣斗士", "ok");
// System.out.println(obj);
// apiVO.setObject2(obj);
// ApiVO apiVO1 = depthClone(apiVO);
// System.out.println(apiVO == apiVO1);
// System.out.println(JsonUtil.obj2Json(apiVO1));
// System.out.println(JsonUtil.obj2Json(apiVO1.getObject1(String.class)));
// System.out.println(JsonUtil.obj2Json(apiVO1.getObject2(Resps.class)));
// System.out.println(apiVO1.getObject2(Resps.class) == obj);
// }
}

View File

@@ -1,139 +1,213 @@
package com.yexuejc.base.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
class StrUtilTest {
@Test
void isEmpty() {
assertTrue(StrUtil.isEmpty(null));
assertTrue(StrUtil.isEmpty(""));
assertTrue(StrUtil.isEmpty(new ArrayList<>()));
assertFalse(StrUtil.isEmpty("test"));
}
@Test
void isNotEmpty() {
assertFalse(StrUtil.isNotEmpty(null));
assertFalse(StrUtil.isNotEmpty(""));
assertTrue(StrUtil.isNotEmpty("test"));
}
@Test
void genUUID() {
String uuid = StrUtil.genUUID();
assertNotNull(uuid);
assertEquals(32, uuid.length());
assertFalse(uuid.contains("-"));
}
@Test
void testGenUUID() {
String uuid10 = StrUtil.genUUID(10);
assertEquals(10, uuid10.length());
String uuid40 = StrUtil.genUUID(40);
assertEquals(40, uuid40.length());
}
@Test
void genNum() {
String num = StrUtil.genNum();
assertNotNull(num);
assertEquals(8, num.length());
assertTrue(num.matches("[01]\\d{7}"));
}
@Test
void toHex() {
byte[] bytes = "test".getBytes();
String hex = StrUtil.toHex(bytes);
assertNotNull(hex);
assertEquals("74657374", hex);
}
@Test
void toMD5() {
String md5 = StrUtil.toMD5("test");
assertNotNull(md5);
assertEquals(32, md5.length());
// test的MD5值
assertEquals("098f6bcd4621d373cade4e832627b4f6", md5);
}
@Test
void toSHA256() {
String sha256 = StrUtil.toSHA256("test");
assertNotNull(sha256);
assertEquals(64, sha256.length());
}
@Test
void toSHA() {
String sha1 = StrUtil.toSHA("test", "SHA-1");
assertNotNull(sha1);
assertEquals(40, sha1.length());
}
@Test
void iso2utf() {
String result = StrUtil.iso2utf("test");
assertNotNull(result);
}
@Test
void isNumeric() {
assertTrue(StrUtil.isNumeric("123"));
assertFalse(StrUtil.isNumeric("abc"));
assertFalse(StrUtil.isNumeric(null)); // 这应该返回false因为null不是数字
}
@Test
void codeId() {
String encoded = StrUtil.codeId("12345678901234567890123456789012"); // 32位字符串
assertNotNull(encoded);
assertEquals(64, encoded.length()); // 编码后应该是64位
}
@Test
void decodeId() {
// 使用一个32位的ID进行编码然后解码测试
String originalId = "12345678901234567890123456789012";
String encoded = StrUtil.codeId(originalId);
String decoded = StrUtil.decodeId(encoded);
assertEquals(originalId, decoded);
}
@Test
void parseUrlencoded() {
Map<String, String> result = StrUtil.parseUrlencoded("key1=value1&key2=value2");
assertNotNull(result);
assertEquals(2, result.size());
assertEquals("value1", result.get("key1"));
assertEquals("value2", result.get("key2"));
}
@Test
void getSignContent() {
Map<String, String> params = new HashMap<>();
params.put("b", "2");
params.put("a", "1");
String result = StrUtil.getSignContent(params);
assertEquals("a=1&b=2", result);
}
@Test
void replaceMobile() {
String mobile = "13812345678";
String result = StrUtil.replaceMobile(mobile);
assertEquals("138****5678", result);
}
@Test
void mapSort() {
Map<String, Object> map = new HashMap<>();
map.put("c", 3);
map.put("a", 1);
map.put("b", 2);
Map<String, Object> sorted = StrUtil.mapSort(map);
assertNotNull(sorted);
// 验证排序后的顺序
List<String> keys = new ArrayList<>(sorted.keySet());
assertEquals("a", keys.get(0));
assertEquals("b", keys.get(1));
assertEquals("c", keys.get(2));
}
@Test
void setStr() {
String result = StrUtil.setStr(null, "default");
assertEquals("default", result);
String result2 = StrUtil.setStr("test", "default");
assertEquals("test", result2);
}
@Test
void underlineToCamel() {
String result = StrUtil.underlineToCamel("user_name");
assertEquals("userName", result);
}
@Test
void camelToUnderline() {
String result = StrUtil.camelToUnderline("userName");
assertEquals("user_name", result);
}
@Test
void printStackTraceAndMessage() {
Exception e = new RuntimeException("test exception");
String result = StrUtil.printStackTraceAndMessage(e);
assertNotNull(result);
assertTrue(result.contains("test exception"));
}
@Test
void printStackTrace() {
Exception e = new RuntimeException("test");
String result = StrUtil.printStackTrace(e);
assertNotNull(result);
assertTrue(result.contains("RuntimeException"));
}
@Test
void notNullExecute() {
final boolean[] executed = {false};
StrUtil.notNullExecute("test", s -> executed[0] = true);
assertTrue(executed[0]);
executed[0] = false;
StrUtil.notNullExecute(null, s -> executed[0] = true);
assertFalse(executed[0]);
}
@Test
void notEmptyExecute() {
final boolean[] executed = {false};
StrUtil.notEmptyExecute("test", s -> executed[0] = true);
assertTrue(executed[0]);
executed[0] = false;
StrUtil.notEmptyExecute("", s -> executed[0] = true);
assertFalse(executed[0]);
}
@Test
void countryToCode() {
Assertions.assertEquals(StrUtil.countryToCode("JPN"), "100000000");
Assertions.assertEquals(StrUtil.countryToCode("KOR"), "010000000");
Assertions.assertEquals(StrUtil.countryToCode("THA"), "001000000");
Assertions.assertEquals(StrUtil.countryToCode("SGP"), "000100000");
Assertions.assertEquals(StrUtil.countryToCode("CHN"), "000010000");
Assertions.assertEquals(StrUtil.countryToCode("TWN"), "000001000");
Assertions.assertEquals(StrUtil.countryToCode("HKG"), "000000100");
Assertions.assertEquals(StrUtil.countryToCode("MAC"), "000000010");
Assertions.assertEquals(StrUtil.countryToCode("999"), "000000001");
Assertions.assertEquals(StrUtil.countryToCode("O"), "000000000");
}
@Test
void getCountryByCode() {
Assertions.assertEquals(StrUtil.getCountryByCode("100000000"), "JPN");
Assertions.assertEquals(StrUtil.getCountryByCode("010000000"), "KOR");
Assertions.assertEquals(StrUtil.getCountryByCode("001000000"), "THA");
Assertions.assertEquals(StrUtil.getCountryByCode("000100000"), "SGP");
Assertions.assertEquals(StrUtil.getCountryByCode("000010000"), "CHN");
Assertions.assertEquals(StrUtil.getCountryByCode("000001000"), "TWN");
Assertions.assertEquals(StrUtil.getCountryByCode("000000100"), "HKG");
Assertions.assertEquals(StrUtil.getCountryByCode("000000010"), "MAC");
Assertions.assertEquals(StrUtil.getCountryByCode("000000001"), "999");
Assertions.assertEquals(StrUtil.getCountryByCode("000000000"), "O");
Assertions.assertEquals(StrUtil.getCountryByCode("100000000000"), "O");
Assertions.assertEquals(StrUtil.getCountryByCode("-100000000000"), "O");
}
@Test
void countryToCodeByte() {
System.out.println(String.format("%9s", Integer.toBinaryString(StrUtil.countryToCodeByte("CHN") & 0xFF)).replace(" ", "0"));
}
}

View File

@@ -1,22 +1,70 @@
package com.yexuejc.base.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
*
* SysUtil测试类
* @author: yexuejc
* @date: 2024/4/8 11:22
*/
public class SysUtilTest {
public static void main(String[] args) {
System.out.println(SysUtil.getCachePath());
System.out.println(SysUtil.getRootPath(SysUtilTest.class, null));
SysUtil.threadRun("test", () -> {
String threadName = Thread.currentThread().getName();
System.out.println("当前线程的名称是:" + threadName);
@Test
public void testGetCachePath() {
String cachePath = SysUtil.getCachePath();
assertNotNull(cachePath, "缓存路径不应为null");
assertFalse(cachePath.isEmpty(), "缓存路径不应为空");
System.out.println("Cache Path: " + cachePath);
}
@Test
public void testGetRootPath() {
var rootPath = SysUtil.getRootPath(SysUtilTest.class, null);
assertNotNull(rootPath, "根路径不应为null");
System.out.println("Root Path: " + rootPath);
}
@Test
public void testThreadRun() throws InterruptedException {
final String[] result = new String[1];
SysUtil.threadRun("test-thread", () -> {
result[0] = Thread.currentThread().getName();
System.out.println("当前线程的名称是:" + result[0]);
});
SysUtil.getThreadList().forEach(t -> {
// 等待线程执行完毕
Thread.sleep(100);
assertNotNull(result[0], "线程应该已执行");
assertTrue(result[0].contains("test"), "线程名称应包含test");
}
@Test
public void testGetThreadList() {
var threadList = SysUtil.getThreadList();
assertNotNull(threadList, "线程列表不应为null");
assertFalse(threadList.isEmpty(), "线程列表不应为空");
threadList.forEach(t -> {
assertNotNull(t.getName(), "线程名称不应为null");
System.out.println("线程名称:" + t.getName());
});
SysUtil.checkJvmMemory();
System.out.println(SysUtil.jvmMemoryIsNotExecutable());
}
@Test
public void testCheckJvmMemory() {
// 这个方法主要是打印信息,我们验证它不会抛出异常
assertDoesNotThrow(() -> SysUtil.checkJvmMemory(), "检查JVM内存不应抛出异常");
}
@Test
public void testJvmMemoryIsNotExecutable() {
boolean result = SysUtil.jvmMemoryIsNotExecutable();
// 这是一个布尔值,我们只验证方法能正常执行
System.out.println("JVM Memory is not executable: " + result);
// 验证返回值类型
assertTrue(result == true || result == false, "应该返回布尔值");
}
}