master #15

Merged
minglipro merged 2 commits from master into dev 2026-02-08 03:19:12 +08:00
90 changed files with 7945 additions and 2458 deletions
Showing only changes of commit a5a532bc82 - Show all commits

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils
* CurrentFile build.gradle.kts
* LastUpdate 2026-01-14 13:01:44
* LastUpdate 2026-02-05 11:04:04
* UpdateUser MingLiPro
*/
@ -59,6 +59,10 @@ sourceSets {
}
}
tasks.test {
useJUnitPlatform()
}
java {
withSourcesJar()
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
@ -67,19 +71,24 @@ java {
dependencies {
implementation("org.slf4j:slf4j-api:2.0.17")
implementation("com.mingliqiye.utils.jna:WinKernel32Api:1.0.1")
compileOnly("org.mindrot:jbcrypt:0.4")
compileOnly("org.springframework.boot:spring-boot-starter:2.7.14")
compileOnly("com.fasterxml.jackson.core:jackson-databind:2.19.2")
compileOnly("com.google.code.gson:gson:2.13.1")
compileOnly("com.squareup.okhttp3:okhttp:5.3.2")
compileOnly("com.fasterxml.jackson.core:jackson-databind:2.21.0")
compileOnly("com.fasterxml.jackson.module:jackson-module-kotlin:2.21.0")
compileOnly("org.springframework.boot:spring-boot-starter-web:2.7.18")
compileOnly("org.mybatis:mybatis:3.5.19")
compileOnly("com.alibaba.fastjson2:fastjson2:2.0.58")
compileOnly("io.netty:netty-all:4.1.130.Final")
compileOnly("com.baomidou:mybatis-plus-core:3.5.15")
compileOnly("net.java.dev.jna:jna:5.17.0")
compileOnly("com.baomidou:mybatis-plus-extension:3.5.15")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
// testImplementation("com.squareup.okhttp3:okhttp:5.3.2")
testImplementation("com.mingliqiye.logger:logger-log4j2:1.0.5")
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.21.0")
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.21.0")
}
@ -126,6 +135,8 @@ tasks.withType<org.gradle.jvm.tasks.Jar> {
}
val isJdk8Build = project.findProperty("buildForJdk8") == "true"
repositories {
maven {
url = uri("https://maven.aliyun.com/repository/public/")

View File

@ -16,13 +16,13 @@
# ProjectName mingli-utils
# ModuleName mingli-utils
# CurrentFile gradle.properties
# LastUpdate 2026-01-14 13:01:41
# LastUpdate 2026-02-05 11:06:50
# UpdateUser MingLiPro
#
JDKVERSIONS=1.8
GROUPSID=com.mingliqiye.utils
ARTIFACTID=mingli-utils
VERSIONS=4.3.5
VERSIONS=4.6.0
signing.keyId=B22AA93B
signing.password=
signing.secretKeyRingFile=secret.gpg

View File

@ -24,8 +24,6 @@ plugins {
id("java-library")
id("maven-publish")
signing
kotlin("jvm") version "2.2.20"
id("org.jetbrains.dokka") version "2.0.0"
}
val GROUPSID = project.properties["GROUPSID"] as String
val VERSIONS = project.properties["VERSIONS"] as String
@ -84,6 +82,7 @@ publishing {
}
java.toolchain.languageVersion.set(JavaLanguageVersion.of(8))
dependencies {
api(rootProject)
implementation("com.mingliqiye.utils.jna:WinKernel32Platform:1.0.1")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,388 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ArrayUtils.kt
* LastUpdate 2026-01-28 08:03:28
* UpdateUser MingLiPro
*/
@file:JvmName("ArrayUtils")
package com.mingliqiye.utils.array
/**
* 复制数组元素到目标数组
* @param from 源数组
* @param to 目标数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun <T> arrayCopy(from: Array<T>, to: Array<T>, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前数组元素复制到目标数组
* @param to 目标数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun <T> Array<T>.copyTo(to: Array<T>, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个数组到目标数组重载版本
* @param from 源数组
* @param to 目标数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun <T> arrayCopy(from: Array<T>, to: Array<T>) {
return arrayCopy(from, to, 0, 0, from.size)
}
/**
* 复制字节数组元素到目标数组
* @param from 源字节数组
* @param to 目标字节数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: ByteArray, to: ByteArray, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前字节数组元素复制到目标数组
* @param to 目标字节数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun ByteArray.copyTo(to: ByteArray, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个字节数组到目标数组重载版本
* @param from 源字节数组
* @param to 目标字节数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: ByteArray, to: ByteArray) {
arrayCopy(from, to, 0, 0, from.size)
}
/**
* 复制短整型数组元素到目标数组
* @param from 源短整型数组
* @param to 目标短整型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: ShortArray, to: ShortArray, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前短整型数组元素复制到目标数组
* @param to 目标短整型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun ShortArray.copyTo(to: ShortArray, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个短整型数组到目标数组重载版本
* @param from 源短整型数组
* @param to 目标短整型数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: ShortArray, to: ShortArray) {
arrayCopy(from, to, 0, 0, from.size)
}
/**
* 复制整型数组元素到目标数组
* @param from 源整型数组
* @param to 目标整型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: IntArray, to: IntArray, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前整型数组元素复制到目标数组
* @param to 目标整型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun IntArray.copyTo(to: IntArray, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个整型数组到目标数组重载版本
* @param from 源整型数组
* @param to 目标整型数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: IntArray, to: IntArray) {
arrayCopy(from, to, 0, 0, from.size)
}
/**
* 复制长整型数组元素到目标数组
* @param from 源长整型数组
* @param to 目标长整型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: LongArray, to: LongArray, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前长整型数组元素复制到目标数组
* @param to 目标长整型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun LongArray.copyTo(to: LongArray, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个长整型数组到目标数组重载版本
* @param from 源长整型数组
* @param to 目标长整型数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: LongArray, to: LongArray) {
arrayCopy(from, to, 0, 0, from.size)
}
/**
* 复制浮点型数组元素到目标数组
* @param from 源浮点型数组
* @param to 目标浮点型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: FloatArray, to: FloatArray, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前浮点型数组元素复制到目标数组
* @param to 目标浮点型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun FloatArray.copyTo(to: FloatArray, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个浮点型数组到目标数组重载版本
* @param from 源浮点型数组
* @param to 目标浮点型数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: FloatArray, to: FloatArray) {
arrayCopy(from, to, 0, 0, from.size)
}
/**
* 复制双精度浮点型数组元素到目标数组
* @param from 源双精度浮点型数组
* @param to 目标双精度浮点型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: DoubleArray, to: DoubleArray, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前双精度浮点型数组元素复制到目标数组
* @param to 目标双精度浮点型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun DoubleArray.copyTo(to: DoubleArray, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个双精度浮点型数组到目标数组重载版本
* @param from 源双精度浮点型数组
* @param to 目标双精度浮点型数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: DoubleArray, to: DoubleArray) {
arrayCopy(from, to, 0, 0, from.size)
}
/**
* 复制布尔型数组元素到目标数组
* @param from 源布尔型数组
* @param to 目标布尔型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(
from: BooleanArray, to: BooleanArray, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos
) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前布尔型数组元素复制到目标数组
* @param to 目标布尔型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun BooleanArray.copyTo(to: BooleanArray, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个布尔型数组到目标数组重载版本
* @param from 源布尔型数组
* @param to 目标布尔型数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: BooleanArray, to: BooleanArray) {
arrayCopy(from, to, 0, 0, from.size)
}
/**
* 复制字符型数组元素到目标数组
* @param from 源字符型数组
* @param to 目标字符型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: CharArray, to: CharArray, fromPos: Int = 0, toPos: Int = 0, length: Int = from.size - fromPos) {
System.arraycopy(from, fromPos, to, toPos, length)
}
/**
* 扩展函数将当前字符型数组元素复制到目标数组
* @param to 目标字符型数组
* @param fromPos 源数组起始位置默认为0
* @param toPos 目标数组起始位置默认为0
* @param length 要复制的元素数量默认为源数组从起始位置到末尾的长度
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
* @throws ArrayStoreException 当类型不匹配时抛出
*/
@JvmSynthetic
@Throws(IndexOutOfBoundsException::class, ArrayStoreException::class)
fun CharArray.copyTo(to: CharArray, fromPos: Int = 0, toPos: Int = 0, length: Int = this.size - fromPos) {
System.arraycopy(this, fromPos, to, toPos, length)
}
/**
* 复制整个字符型数组到目标数组重载版本
* @param from 源字符型数组
* @param to 目标字符型数组
* @throws IndexOutOfBoundsException 当索引超出数组边界时抛出
*/
@Throws(IndexOutOfBoundsException::class)
fun arrayCopy(from: CharArray, to: CharArray) {
arrayCopy(from, to, 0, 0, from.size)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,18 +15,16 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonException.kt
* LastUpdate 2025-09-15 22:32:50
* CurrentFile BaseType.kt
* LastUpdate 2026-02-04 21:54:04
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
package com.mingliqiye.utils.base
class JsonException : RuntimeException {
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
constructor(cause: Throwable) : this(cause.message ?: "", cause)
enum class BaseType(val baseCodec: BaseCodec) {
BASE16(com.mingliqiye.utils.base.BASE16),
BASE64(com.mingliqiye.utils.base.BASE64),
BASE91(com.mingliqiye.utils.base.BASE91),
BASE256(com.mingliqiye.utils.base.BASE256),
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,15 +16,15 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile CloneUtils.kt
* LastUpdate 2025-09-20 14:01:29
* LastUpdate 2026-02-04 21:00:48
* UpdateUser MingLiPro
*/
@file:JvmName("CloneUtils")
package com.mingliqiye.utils.clone
import com.mingliqiye.utils.json.JsonApi
import com.mingliqiye.utils.json.JsonException
import com.mingliqiye.utils.json.api.base.JsonApi
import com.mingliqiye.utils.json.api.exception.JsonException
import java.io.*
@ -34,8 +34,7 @@ inline fun <reified T> Serializable.deepClone(): T {
inline fun <reified T> T.deepJsonClone(jsonApi: JsonApi): T {
try {
return jsonApi.convert(this, this!!.javaClass) as T
return jsonApi.convert(this as Any, this!!.javaClass) as T
} catch (e: Exception) {
throw JsonException(
"Failed to deep clone object using JSON", e

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile IsChanged.kt
* LastUpdate 2025-09-19 20:17:07
* LastUpdate 2026-01-31 21:13:45
* UpdateUser MingLiPro
*/
@ -39,17 +39,14 @@ class IsChanged<T> {
*/
private val atomicReferenceData: AtomicReference<T> = AtomicReference()
/**
* 默认构造函数初始化数据为 null
*/
constructor() : this(null)
constructor()
/**
* 带参数的构造函数使用指定的初始值初始化
*
* @param data 初始数据值
*/
constructor(data: T?) : super() {
constructor(data: T) : super() {
atomicReferenceData.set(data)
}
@ -67,7 +64,7 @@ class IsChanged<T> {
*
* @return 当前数据值
*/
fun get(): T? {
fun get(): T {
return atomicReferenceData.get()
}
@ -77,7 +74,7 @@ class IsChanged<T> {
* @param data 要设置的新数据值
* @return 设置前的旧数据值
*/
fun setAndGet(data: T): T? {
fun setAndGet(data: T): T {
return atomicReferenceData.getAndSet(data)
}
@ -89,7 +86,7 @@ class IsChanged<T> {
* @return 如果值发生变化返回 true否则返回 false
*/
fun setAndChanged(data: T): Boolean {
var currentData: T?
var currentData: T
do {
currentData = get()
// 如果新值与当前值相等,则认为没有变化,直接返回 false

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile FileUtils.kt
* LastUpdate 2026-01-11 09:20:20
* LastUpdate 2026-01-28 10:49:14
* UpdateUser MingLiPro
*/
@file:JvmName("FileUtils")
@ -642,27 +642,6 @@ fun File.copyTo(target: File) {
target.parentFile?.mkdirs()
this.copyTo(target, overwrite = true)
}
// 删除文件
/**
* 删除文件
*
* @return 删除操作是否成功
*/
fun File.delete(): Boolean {
return this.delete()
}
// 检查文件是否存在
/**
* 检查文件是否存在
*
* @return 文件是否存在
*/
fun File.exists(): Boolean {
return this.exists()
}
// 获取文件大小
/**
* 获取文件大小

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Functions.kt
* LastUpdate 2026-01-11 09:10:48
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
@ -24,6 +24,19 @@
package com.mingliqiye.utils.functions
fun <T> T.with(function: Function): T = function.call().let {
this
}
fun <T> T.with(function: P1Function<T>): T = function.call(this).let {
this
}
@FunctionalInterface
fun interface Function {
fun call()
}
@FunctionalInterface
fun interface P1Function<P> {
fun call(p: P)
@ -128,3 +141,597 @@ fun interface P10Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9> {
fun interface P10RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9): R
}
@FunctionalInterface
fun interface P11Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10)
}
@FunctionalInterface
fun interface P11RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10): R
}
@FunctionalInterface
fun interface P12Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11)
}
@FunctionalInterface
fun interface P12RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11): R
}
@FunctionalInterface
fun interface P13Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12)
}
@FunctionalInterface
fun interface P13RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12
): R
}
@FunctionalInterface
fun interface P14Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13
)
}
@FunctionalInterface
fun interface P14RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13
): R
}
@FunctionalInterface
fun interface P15Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14
)
}
@FunctionalInterface
fun interface P15RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14
): R
}
@FunctionalInterface
fun interface P16Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15
)
}
@FunctionalInterface
fun interface P16RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15
): R
}
@FunctionalInterface
fun interface P17Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16
)
}
@FunctionalInterface
fun interface P17RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16
): R
}
@FunctionalInterface
fun interface P18Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17
)
}
@FunctionalInterface
fun interface P18RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17
): R
}
@FunctionalInterface
fun interface P19Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18
)
}
@FunctionalInterface
fun interface P19RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18
): R
}
@FunctionalInterface
fun interface P20Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19
)
}
@FunctionalInterface
fun interface P20RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19
): R
}
@FunctionalInterface
fun interface P21Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19,
p20: P20
)
}
@FunctionalInterface
fun interface P21RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19,
p20: P20
): R
}
@FunctionalInterface
fun interface P22Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19,
p20: P20,
p21: P21
)
}
@FunctionalInterface
fun interface P22RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19,
p20: P20,
p21: P21
): R
}
@FunctionalInterface
fun interface P23Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19,
p20: P20,
p21: P21,
p22: P22
)
}
@FunctionalInterface
fun interface P23RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19,
p20: P20,
p21: P21,
p22: P22
): R
}
@FunctionalInterface
fun interface P24Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19,
p20: P20,
p21: P21,
p22: P22,
p23: P23
)
}
@FunctionalInterface
fun interface P24RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, R> {
fun call(
p: P,
p1: P1,
p2: P2,
p3: P3,
p4: P4,
p5: P5,
p6: P6,
p7: P7,
p8: P8,
p9: P9,
p10: P10,
p11: P11,
p12: P12,
p13: P13,
p14: P14,
p15: P15,
p16: P16,
p17: P17,
p18: P18,
p19: P19,
p20: P20,
p21: P21,
p22: P22,
p23: P23
): R
}

View File

@ -0,0 +1,272 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Pipeline.kt
* LastUpdate 2026-02-05 09:53:44
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions
import com.mingliqiye.utils.require.Require
import java.util.*
/**
* 流水线操作类提供链式调用的操作方式来处理数据转换和操作
* @param T 泛型类型表示流水线中处理的数据类型
*/
class Pipeline<T>(private val value: T) {
/**
* 对当前值进行转换操作并返回新的Pipeline实例
* @param transformer 转换函数接收当前值并返回转换后的结果
* @return 包含转换后结果的新Pipeline实例
*/
fun <R> transform(transformer: P1RFunction<T, R>): Pipeline<R> = Pipeline(transformer.call(value))
/**
* 对当前值进行转换操作功能与transform相同提供不同的方法名选择
* @param transformer 转换函数接收当前值并返回转换后的结果
* @return 包含转换后结果的新Pipeline实例
*/
fun <R> then(transformer: P1RFunction<T, R>): Pipeline<R> = transform(transformer)
/**
* 消费当前值但不改变流水线状态用于执行副作用操作
* @param consumer 消费函数接收当前值进行消费操作
* @return 当前Pipeline实例支持链式调用
*/
fun consume(consumer: P1Function<T>): Pipeline<T> {
consumer.call(value)
return this
}
/**
* 判断当前值是否为null
* @return true表示当前值为nullfalse表示不为null
*/
fun isNull(): Boolean = (value == null)
/**
* 判断当前值是否不为null
* @return true表示当前值不为nullfalse表示为null
*/
fun isNotNull(): Boolean = (value != null)
/**
* 根据条件过滤当前值如果条件满足则保持原值否则设置为null
* @param predicate 过滤条件函数接收当前值并返回布尔值
* @return 包含过滤后结果的Pipeline实例
*/
fun filter(predicate: P1RFunction<T, Boolean>): Pipeline<T?> =
if (predicate.call(value)) Pipeline(value) else Pipeline(null)
/**
* 获取当前流水线中的值
* @return 当前流水线中存储的值
*/
fun getValue(): T = value
/**
* 安全地转换当前值允许转换结果为null
* @param transformer 转换函数可能返回null
* @return 包含转换后结果可能为null的Pipeline实例
*/
fun <R> safeTransform(transformer: P1RFunction<T, R?>): Pipeline<R?> = Pipeline(transformer.call(value))
/**
* 将当前值映射为另一个Pipeline实例
* @param mapper 映射函数接收当前值并返回Pipeline实例
* @return 映射后的Pipeline实例
*/
fun <R> flatMap(mapper: P1RFunction<T, Pipeline<R>>): Pipeline<R> = mapper.call(value)
/**
* 将当前值映射为非null结果如果当前值为null则直接返回null
* @param mapper 映射函数可能返回null
* @return 包含映射后结果的Pipeline实例
*/
fun <R> mapNotNull(mapper: P1RFunction<T, R?>): Pipeline<out R?> =
if (value != null) Pipeline(mapper.call(value)!!) else Pipeline(null)
/**
* 将当前值强制转换为指定类型
* @param type 目标类型Class对象
* @return 包含转换后类型的Pipeline实例
* @throws ClassCastException 当类型转换失败时抛出异常
*/
@Throws(ClassCastException::class)
fun <E> cast(type: Class<E>): Pipeline<E> = Pipeline(value as E)
/**
* 根据条件判断是否保留当前值条件满足时保留否则返回null
* @param predicate 条件判断函数
* @return 包含条件判断结果的Pipeline实例
*/
fun takeIf(predicate: P1RFunction<T, Boolean>): Pipeline<T?> = Pipeline(if (predicate.call(value)) value else null)
/**
* 根据条件判断是否保留当前值条件不满足时保留否则返回null
* @param predicate 条件判断函数
* @return 包含条件判断结果的Pipeline实例
*/
fun takeUnless(predicate: P1RFunction<T, Boolean>): Pipeline<T?> =
Pipeline(if (!predicate.call(value)) value else null)
/**
* 在当前值上执行指定操作并返回操作结果
* @param block 执行函数接收当前值并返回结果
* @return 包含执行结果的Pipeline实例
*/
fun <R> let(block: P1RFunction<T, R>): Pipeline<R> = Pipeline(block.call(value))
/**
* 如果当前值不为null则返回自身否则返回默认值的Pipeline实例
* @param defaultValue 默认值
* @return 当前值或默认值的Pipeline实例
*/
fun orElse(defaultValue: T): Pipeline<T> = if (value != null) this else Pipeline(defaultValue)
/**
* 如果当前值不为null则返回自身否则通过供应商函数获取默认值
* @param supplier 默认值供应商函数
* @return 当前值或供应商提供的值的Pipeline实例
*/
fun orElseGet(supplier: RFunction<T>): Pipeline<T> = if (value != null) this else Pipeline(supplier.call())
/**
* 验证当前值是否等于指定值如果不相等则抛出异常
* @param any 比较的目标值
* @param message 错误消息
* @param exception 异常类型默认为IllegalArgumentException
* @return 当前Pipeline实例
*/
fun require(
any: Any, message: String, exception: Class<out Exception> = IllegalArgumentException::class.java
): Pipeline<T> {
Require.require(Objects.equals(any, value), message, exception)
return this
}
/**
* 验证当前值是否等于指定值如果不相等则抛出IllegalArgumentException异常
* @param any 比较的目标值
* @param message 错误消息
* @return 当前Pipeline实例
*/
fun require(
any: Any, message: String
): Pipeline<T> {
Require.require(any == value, message)
return this
}
/**
* 使用P1RFunction验证当前值是否满足条件不满足则抛出异常
* @param must 验证条件函数
* @param message 错误消息
* @param exception 异常类型默认为IllegalArgumentException
* @return 当前Pipeline实例
*/
fun require(
must: P1RFunction<T, Boolean>,
message: String,
exception: Class<out Throwable> = IllegalArgumentException::class.java
): Pipeline<T> {
Require.require(must.call(value), message, exception)
return this
}
/**
* 使用P1RFunction验证当前值是否满足条件不满足则抛出IllegalArgumentException异常
* @param must 验证条件函数
* @param message 错误消息
* @return 当前Pipeline实例
*/
fun require(
must: P1RFunction<T, Boolean>, message: String
): Pipeline<T> {
Require.require(must.call(value), message, IllegalArgumentException::class.java)
return this
}
/**
* 对当前值执行指定操作但不改变流水线状态
* @param block 执行函数接收当前值
* @return 当前Pipeline实例
*/
fun also(block: P1Function<T>): Pipeline<T> {
block.call(value)
return this
}
/**
* 验证当前值是否满足指定条件不满足则抛出IllegalArgumentException异常
* @param predicate 验证条件函数
* @param lazyMessage 延迟错误消息生成函数默认返回"Requirement failed"
* @return 当前Pipeline实例
*/
fun require(
predicate: P1RFunction<T, Boolean>,
lazyMessage: P1RFunction<T, Any> = P1RFunction { "Requirement failed" }
): Pipeline<T> {
if (!predicate.call(value)) {
throw IllegalArgumentException(lazyMessage.call(value).toString())
}
return this
}
/**
* 使用比较器比较当前值与另一个值
* @param other 另一个比较值
* @param comparator 比较器
* @return 比较结果负数表示小于0表示相等正数表示大于
*/
fun compareWith(other: T, comparator: Comparator<in T>): Int = comparator.compare(value, other)
/**
* 使用选择器将当前值转换为可比较类型后与另一个值比较
* @param other 另一个比较值
* @param selector 选择器函数将当前值转换为可比较类型
* @return 比较结果
*/
fun <R : Comparable<R>> compareTo(other: R, selector: P1RFunction<T, R>): Int =
selector.call(value).compareTo(other)
companion object {
/**
* 创建包含指定值的Pipeline实例
* @param value 要包装的值
* @return 新的Pipeline实例
*/
@JvmStatic
fun <T> of(value: T): Pipeline<T> = Pipeline(value)
/**
* 通过供应商函数创建Pipeline实例
* @param transformer 值供应商函数
* @return 新的Pipeline实例
*/
@JvmStatic
fun <T> of(transformer: RFunction<T>): Pipeline<T> = Pipeline(transformer.call())
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,48 +16,243 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Response.kt
* LastUpdate 2025-09-15 09:04:05
* LastUpdate 2026-02-03 20:09:10
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.http
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.Formatter
/**
* HTTP响应数据类
* 封装了时间消息数据和状态码等响应信息
*
* @param T 响应数据的类型
* @property time 响应时间
* @property message 响应消息
* @property data 响应数据
* @property statusCode 状态码
*/
data class Response<T>(
val time: String,
var message: String,
var data: T?,
var statusCode: Int
private var time: DateTime, private var message: String, private var data: T?, private var statusCode: Int
) {
companion object {
@JvmStatic
fun <T> ok(data: T): Response<T?> {
return Response(DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7), "操作成功", data, 200)
}
/**
* 创建一个成功的响应对象仅包含消息
*
* @param message 响应消息
* @return Response<Any> 成功的响应对象
*/
@JvmStatic
fun <T> ok(message: String): Response<T?> {
return Response(DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7), message, null, 200)
}
fun ok(message: String) = Response(
time = DateTime.now(),
message = message,
data = null,
statusCode = 200
)
/**
* 创建一个成功的响应对象仅包含数据
*
* @param T 响应数据的类型
* @param data 响应数据
* @return Response<T> 成功的响应对象
*/
@JvmStatic
fun <T> okData(data: T) = Response(
time = DateTime.now(),
message = "操作成功",
data = data,
statusCode = 200
)
/**
* 创建一个成功的响应对象包含数据和消息
*
* @param T 响应数据的类型
* @param data 响应数据
* @param message 响应消息
* @return Response<T> 成功的响应对象
*/
@JvmStatic
fun <T> okData(data: T, message: String) = Response(
time = DateTime.now(),
message = message,
data = data,
statusCode = 200
)
/**
* 创建一个指定状态码的响应对象仅包含状态码
*
* @param statusCode 状态码
* @return Response<Any> 指定状态码的响应对象
*/
@JvmStatic
fun code(statusCode: Int) = Response(
time = DateTime.now(),
message = "操作成功",
data = null,
statusCode = statusCode
)
/**
* 创建一个指定状态码的响应对象包含状态码和消息
*
* @param statusCode 状态码
* @param message 响应消息
* @return Response<Any> 指定状态码的响应对象
*/
@JvmStatic
fun code(statusCode: Int, message: String) = Response(
time = DateTime.now(),
message = message,
data = null,
statusCode = statusCode
)
}
/**
* 默认构造函数创建一个默认的成功响应对象
*/
constructor() : this(
time = DateTime.now(), message = "操作成功", statusCode = 200, data = null
)
/**
* 获取响应时间
*
* @return DateTime 响应时间
*/
fun getTime(): DateTime = time
/**
* 设置响应时间
*
* @param dateTime 响应时间
* @return Response<T> 当前响应对象用于链式调用
*/
fun setTime(dateTime: DateTime): Response<T> {
time = dateTime
return this
}
/**
* 获取响应消息
*
* @return String 响应消息
*/
fun getMessage(): String = message
/**
* 设置响应消息
*
* @param message 响应消息
* @return Response<T> 当前响应对象用于链式调用
*/
fun setMessage(message: String): Response<T> {
this.message = message
return this
}
fun setData(data: T): Response<T?> {
this.data = data
return ok(this.data)
.setMessage(this.message)
.setStatusCode(this.statusCode)
/**
* 获取响应数据
*
* @return T? 响应数据可能为空
*/
fun getData(): T? = data
/**
* 获取非空数据对象
*
* 此方法强制解包data属性如果data为null则会抛出NullPointerException异常
*
* @param T 泛型类型参数表示返回的数据类型
* @return 返回非空的数据对象类型为T
* @throws NullPointerException 当data属性为null时抛出此异常
*/
@Throws(NullPointerException::class)
fun notNullData(): T {
return data ?: throw NullPointerException("at Response.notNullData() because data is null")
}
/**
* 设置响应数据
*
* @param D 数据类型
* @param data 响应数据
* @return Response<D> 当前响应对象用于链式调用
*
*/
fun setData(data: T): Response<T> {
this.data = data
return this
}
/**
* 创建一个新的Response对象使用指定的时间
*
* @param time 新的响应时间
* @return Response<T> 新的响应对象
*/
fun withTime(time: DateTime): Response<T> =
if (this.time == time) this else Response(time, message, data, statusCode)
/**
* 创建一个新的Response对象使用指定的数据
*
* @param D 新的数据类型
* @param data 新的响应数据
* @return Response<D> 新的响应对象
*/
fun <D> withData(data: D): Response<D> =
Response(this.time, this.message, data, this.statusCode)
/**
* 创建一个新的Response对象使用指定的消息
*
* @param message 新的响应消息
* @return Response<T> 新的响应对象
*/
fun withMessage(message: String): Response<T> =
if (this.message == message) this else Response(time, message, data, statusCode)
/**
* 创建一个新的Response对象使用指定的状态码
*
* @param statusCode 新的状态码
* @return Response<T> 新的响应对象
*/
fun withStatusCode(statusCode: Int): Response<T> =
if (this.statusCode == statusCode) this else Response(time, message, data, statusCode)
/**
* 获取状态码
*
* @return Int 状态码
*/
fun getStatusCode(): Int = statusCode
/**
* 设置状态码
*
* @param statusCode 状态码
* @return Response<T> 当前响应对象用于链式调用
*/
fun setStatusCode(statusCode: Int): Response<T> {
this.statusCode = statusCode
return this
}
/**
* 返回响应对象的字符串表示
*/
override fun toString(): String =
"Response(time=${getTime()}, message=${getMessage()}, data:[${data?.javaClass?.simpleName}]=${getData()}, statusCode=${getStatusCode()})"
}

View File

@ -0,0 +1,372 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Exceptions.kt
* LastUpdate 2026-02-05 11:12:36
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.http.exception
/**
* 表示 HTTP 异常的基类继承自 [RuntimeException]
*
* @param statusCode HTTP 状态码
* @param message 异常信息默认为 null
* @param cause 异常原因默认为 null
*/
sealed class HttpException(
open val statusCode: Int,
override val message: String? = null,
override val cause: Throwable? = null
) : RuntimeException(message, cause)
// 3xx - 重定向异常
/**
* 表示 HTTP 300 Multiple Choices 异常
*
* @param message 异常信息默认为 "Multiple Choices"
* @param cause 异常原因默认为 null
*/
class MultipleChoicesException(
override val message: String? = "Multiple Choices",
override val cause: Throwable? = null,
) : HttpException(300, message, cause)
/**
* 表示 HTTP 301 Moved Permanently 异常
*
* @param message 异常信息默认为 "Moved Permanently"
* @param cause 异常原因默认为 null
*/
class MovedPermanentlyException(
override val message: String? = "Moved Permanently",
override val cause: Throwable? = null,
) : HttpException(301, message, cause)
/**
* 表示 HTTP 302 Found 异常
*
* @param message 异常信息默认为 "Found"
* @param cause 异常原因默认为 null
*/
class FoundException(
override val message: String? = "Found",
override val cause: Throwable? = null,
) : HttpException(302, message, cause)
/**
* 表示 HTTP 303 See Other 异常
*
* @param message 异常信息默认为 "See Other"
* @param cause 异常原因默认为 null
*/
class SeeOtherException(
override val message: String? = "See Other",
override val cause: Throwable? = null,
) : HttpException(303, message, cause)
/**
* 表示 HTTP 304 Not Modified 异常
*
* @param message 异常信息默认为 "Not Modified"
* @param cause 异常原因默认为 null
*/
class NotModifiedException(
override val message: String? = "Not Modified",
override val cause: Throwable? = null,
) : HttpException(304, message, cause)
/**
* 表示 HTTP 305 Use Proxy 异常
*
* @param message 异常信息默认为 "Use Proxy"
* @param cause 异常原因默认为 null
*/
class UseProxyException(
override val message: String? = "Use Proxy",
override val cause: Throwable? = null,
) : HttpException(305, message, cause)
/**
* 表示 HTTP 307 Temporary Redirect 异常
*
* @param message 异常信息默认为 "Temporary Redirect"
* @param cause 异常原因默认为 null
*/
class TemporaryRedirectException(
override val message: String? = "Temporary Redirect",
override val cause: Throwable? = null,
) : HttpException(307, message, cause)
// 4xx - 客户端错误异常
/**
* 表示 HTTP 400 Bad Request 异常
*
* @param message 异常信息默认为 "Bad Request"
* @param cause 异常原因默认为 null
*/
class BadRequestException(
override val message: String? = "Bad Request",
override val cause: Throwable? = null,
) : HttpException(400, message, cause)
/**
* 表示 HTTP 401 Unauthorized 异常
*
* @param message 异常信息默认为 "Unauthorized"
* @param cause 异常原因默认为 null
*/
class UnauthorizedException(
override val message: String? = "Unauthorized",
override val cause: Throwable? = null,
) : HttpException(401, message, cause)
/**
* 表示 HTTP 402 Payment Required 异常
*
* @param message 异常信息默认为 "Payment Required"
* @param cause 异常原因默认为 null
*/
class PaymentRequiredException(
override val message: String? = "Payment Required",
override val cause: Throwable? = null,
) : HttpException(402, message, cause)
/**
* 表示 HTTP 403 Forbidden 异常
*
* @param message 异常信息默认为 "Forbidden"
* @param cause 异常原因默认为 null
*/
class ForbiddenException(
override val message: String? = "Forbidden",
override val cause: Throwable? = null,
) : HttpException(403, message, cause)
/**
* 表示 HTTP 404 Not Found 异常
*
* @param message 异常信息默认为 "Not Found"
* @param cause 异常原因默认为 null
*/
class NotFoundException(
override val message: String? = "Not Found",
override val cause: Throwable? = null,
) : HttpException(404, message, cause)
/**
* 表示 HTTP 405 Method Not Allowed 异常
*
* @param message 异常信息默认为 "Method Not Allowed"
* @param cause 异常原因默认为 null
*/
class MethodNotAllowedException(
override val message: String? = "Method Not Allowed",
override val cause: Throwable? = null,
) : HttpException(405, message, cause)
/**
* 表示 HTTP 406 Not Acceptable 异常
*
* @param message 异常信息默认为 "Not Acceptable"
* @param cause 异常原因默认为 null
*/
class NotAcceptableException(
override val message: String? = "Not Acceptable",
override val cause: Throwable? = null,
) : HttpException(406, message, cause)
/**
* 表示 HTTP 407 Proxy Authentication Required 异常
*
* @param message 异常信息默认为 "Proxy Authentication Required"
* @param cause 异常原因默认为 null
*/
class ProxyAuthenticationRequiredException(
override val message: String? = "Proxy Authentication Required",
override val cause: Throwable? = null,
) : HttpException(407, message, cause)
/**
* 表示 HTTP 408 Request Timeout 异常
*
* @param message 异常信息默认为 "Request Timeout"
* @param cause 异常原因默认为 null
*/
class RequestTimeoutException(
override val message: String? = "Request Timeout",
override val cause: Throwable? = null,
) : HttpException(408, message, cause)
/**
* 表示 HTTP 409 Conflict 异常
*
* @param message 异常信息默认为 "Conflict"
* @param cause 异常原因默认为 null
*/
class ConflictException(
override val message: String? = "Conflict",
override val cause: Throwable? = null,
) : HttpException(409, message, cause)
/**
* 表示 HTTP 410 Gone 异常
*
* @param message 异常信息默认为 "Gone"
* @param cause 异常原因默认为 null
*/
class GoneException(
override val message: String? = "Gone",
override val cause: Throwable? = null,
) : HttpException(410, message, cause)
/**
* 表示 HTTP 411 Length Required 异常
*
* @param message 异常信息默认为 "Length Required"
* @param cause 异常原因默认为 null
*/
class LengthRequiredException(
override val message: String? = "Length Required",
override val cause: Throwable? = null,
) : HttpException(411, message, cause)
/**
* 表示 HTTP 412 Precondition Failed 异常
*
* @param message 异常信息默认为 "Precondition Failed"
* @param cause 异常原因默认为 null
*/
class PreconditionFailedException(
override val message: String? = "Precondition Failed",
override val cause: Throwable? = null,
) : HttpException(412, message, cause)
/**
* 表示 HTTP 413 Request Entity Too Large 异常
*
* @param message 异常信息默认为 "Request Entity Too Large"
* @param cause 异常原因默认为 null
*/
class RequestEntityTooLargeException(
override val message: String? = "Request Entity Too Large",
override val cause: Throwable? = null,
) : HttpException(413, message, cause)
/**
* 表示 HTTP 414 Request URI Too Large 异常
*
* @param message 异常信息默认为 "Request URI Too Large"
* @param cause 异常原因默认为 null
*/
class RequestUriTooLargeException(
override val message: String? = "Request URI Too Large",
override val cause: Throwable? = null,
) : HttpException(414, message, cause)
/**
* 表示 HTTP 415 Unsupported Media Type 异常
*
* @param message 异常信息默认为 "Unsupported Media Type"
* @param cause 异常原因默认为 null
*/
class UnsupportedMediaTypeException(
override val message: String? = "Unsupported Media Type",
override val cause: Throwable? = null,
) : HttpException(415, message, cause)
/**
* 表示 HTTP 416 Requested Range Not Satisfiable 异常
*
* @param message 异常信息默认为 "Requested Range Not Satisfiable"
* @param cause 异常原因默认为 null
*/
class RequestedRangeNotSatisfiableException(
override val message: String? = "Requested Range Not Satisfiable",
override val cause: Throwable? = null,
) : HttpException(416, message, cause)
// 5xx - 服务器错误异常
/**
* 表示 HTTP 500 Internal Server Error 异常
*
* @param message 异常信息默认为 "Internal Server Error"
* @param cause 异常原因默认为 null
*/
class InternalServerErrorException(
override val message: String? = "Internal Server Error",
override val cause: Throwable? = null,
) : HttpException(500, message, cause)
/**
* 表示 HTTP 501 Not Implemented 异常
*
* @param message 异常信息默认为 "Not Implemented"
* @param cause 异常原因默认为 null
*/
class NotImplementedException(
override val message: String? = "Not Implemented",
override val cause: Throwable? = null,
) : HttpException(501, message, cause)
/**
* 表示 HTTP 502 Bad Gateway 异常
*
* @param message 异常信息默认为 "Bad Gateway"
* @param cause 异常原因默认为 null
*/
class BadGatewayException(
override val message: String? = "Bad Gateway",
override val cause: Throwable? = null,
) : HttpException(502, message, cause)
/**
* 表示 HTTP 503 Service Unavailable 异常
*
* @param message 异常信息默认为 "Service Unavailable"
* @param cause 异常原因默认为 null
*/
class ServiceUnavailableException(
override val message: String? = "Service Unavailable",
override val cause: Throwable? = null,
) : HttpException(503, message, cause)
/**
* 表示 HTTP 504 Gateway Timeout 异常
*
* @param message 异常信息默认为 "Gateway Timeout"
* @param cause 异常原因默认为 null
*/
class GatewayTimeoutException(
override val message: String? = "Gateway Timeout",
override val cause: Throwable? = null,
) : HttpException(504, message, cause)
/**
* 表示 HTTP 505 HTTP Version Not Supported 异常
*
* @param message 异常信息默认为 "HTTP Version Not Supported"
* @param cause 异常原因默认为 null
*/
class HttpVersionNotSupportedException(
override val message: String? = "HTTP Version Not Supported",
override val cause: Throwable? = null,
) : HttpException(505, message, cause)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,48 +16,217 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile IO.kt
* LastUpdate 2025-09-20 16:03:14
* LastUpdate 2026-02-04 13:12:57
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.io
fun Any?.println() {
IO.println(this)
}
import com.mingliqiye.utils.logger.MingLiLoggerFactory
import com.mingliqiye.utils.string.join
import org.slf4j.Logger
import java.io.OutputStream
import java.io.PrintStream
class IO {
companion object {
/**
* IO工具类提供打印功能和系统输出流重定向到日志的功能
*/
object IO {
@JvmStatic
fun print(vararg args: Any?) {
printA(" ", *args)
}
@JvmStatic
fun <T : List<*>> T.println(): T {
println("{" + ",".join(this) + "}")
return this
}
@JvmStatic
fun println(vararg args: Any?) {
printlnA(" ", *args)
}
@JvmStatic
fun <T> Array<T>.println(): Array<T> {
println("{" + ",".join(this) + "}")
return this
}
@JvmStatic
fun printlnA(sp: String, vararg args: Any?) {
printA(" ", *args)
@JvmStatic
fun <T> T.println(): T {
println(this)
return this
}
@JvmStatic
var originalOut: PrintStream = System.out
@JvmStatic
var originalErr: PrintStream = System.err
@JvmStatic
val outLog: Logger = MingLiLoggerFactory.getLogger("out")
@JvmStatic
val errLog: Logger = MingLiLoggerFactory.getLogger("err")
/**
* 打印多个参数使用空格分隔
* @param args 要打印的参数数组
*/
@JvmStatic
fun print(vararg args: Any?) {
printA(" ", *args)
}
/**
* 打印多个参数并换行使用空格分隔
* @param args 要打印的参数数组
*/
@JvmStatic
fun println(vararg args: Any?) {
printlnA(" ", *args)
}
/**
* 打印多个参数并换行指定分隔符
* @param sp 分隔符
* @param args 要打印的参数数组
*/
@JvmStatic
fun printlnA(sp: String, vararg args: Any?) {
printA(" ", *args)
kotlin.io.println()
}
/**
* 打印多个参数指定分隔符
* @param sp 分隔符默认为空字符串
* @param args 要打印的参数数组
*/
@JvmStatic
fun printA(sp: String = "", vararg args: Any?) {
if (args.isEmpty()) {
kotlin.io.println()
}
@JvmStatic
fun printA(sp: String = "", vararg args: Any?) {
if (args.isEmpty()) {
kotlin.io.println()
}
val sb = StringBuilder()
for (i in args.indices) {
sb.append(args[i])
if (i < args.size - 1) sb.append(sp)
}
kotlin.io.print(sb)
val sb = StringBuilder()
for (i in args.indices) {
sb.append(args[i])
if (i < args.size - 1) sb.append(sp)
}
kotlin.io.print(sb)
}
@JvmStatic
/**
* 重定向 System.out INFO 级别日志
*/
fun redirectOutToInfo() {
val outLogger = PrintStream(object : OutputStream() {
private val buffer = StringBuilder()
override fun write(b: Int) {
if (b == '\n'.code) {
flushBuffer()
} else {
buffer.append(b.toChar())
}
}
override fun write(b: ByteArray, off: Int, len: Int) {
val str = String(b, off, len)
if (str.contains("\n")) {
val lines = str.split("\n")
lines.forEachIndexed { index, line ->
if (index == lines.size - 1 && !str.endsWith("\n")) {
buffer.append(line)
} else {
buffer.append(line)
flushBuffer()
}
}
} else {
buffer.append(str)
}
}
private fun flushBuffer() {
val message = buffer.toString().trim()
if (message.isNotBlank()) {
outLog.info(message)
}
buffer.clear()
}
override fun flush() {
flushBuffer()
super.flush()
}
}, true)
System.setOut(outLogger)
}
@JvmStatic
/**
* 重定向 System.err ERROR 级别
*/
fun redirectErrToError() {
val errLogger = PrintStream(object : OutputStream() {
private val buffer = StringBuilder()
override fun write(b: Int) {
if (b == '\n'.code) {
flushBuffer()
} else {
buffer.append(b.toChar())
}
}
override fun write(b: ByteArray, off: Int, len: Int) {
val str = String(b, off, len)
if (str.contains("\n")) {
val lines = str.split("\n")
lines.forEachIndexed { index, line ->
if (index == lines.size - 1 && !str.endsWith("\n")) {
buffer.append(line)
} else {
buffer.append(line)
flushBuffer()
}
}
} else {
buffer.append(str)
}
}
private fun flushBuffer() {
val message = buffer.toString().trim()
if (message.isNotBlank()) {
errLog.error(message)
}
buffer.clear()
}
override fun flush() {
flushBuffer()
super.flush()
}
}, true)
System.setErr(errLogger)
}
@JvmStatic
/**
* 完全重定向包括第三方库的输出
*/
fun redirectAll() {
redirectOutToInfo()
redirectErrToError()
}
@JvmStatic
/**
* 恢复原始输出流
*/
fun restore() {
System.setOut(originalOut)
System.setErr(originalErr)
}
}

View File

@ -1,231 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile GsonJsonApi.kt
* LastUpdate 2025-09-15 22:07:43
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
import com.google.gson.*
import com.mingliqiye.utils.json.converters.JsonConverter
import com.mingliqiye.utils.json.converters.JsonStringConverter
class GsonJsonApi : JsonApi {
private var gsonUnicode: Gson
private var gsonPretty: Gson
private var gsonPrettyUnicode: Gson
private var gson: Gson
constructor() {
gson = GsonBuilder()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
gsonUnicode = GsonBuilder()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
gsonPretty = GsonBuilder()
.setPrettyPrinting()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
gsonPrettyUnicode = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
}
constructor(gson: Gson) {
this.gson = gson
.newBuilder()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
this.gsonUnicode = gson
.newBuilder()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
this.gsonPretty = gson
.newBuilder()
.setPrettyPrinting()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
this.gsonPrettyUnicode = gson
.newBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
}
override fun <T> parse(json: String, clazz: Class<T>): T {
return gson.fromJson(json, clazz)
}
override fun <T> parse(json: String, type: JsonTypeReference<T>): T {
return gson.fromJson(json, type.type)
}
override fun format(obj: Any): String {
return gson.toJson(obj)
}
override fun formatUnicode(obj: Any): String {
return gsonUnicode.toJson(obj)
}
override fun formatPretty(obj: Any): String {
return gsonPretty.toJson(obj)
}
override fun formatPrettyUnicode(obj: Any): String {
return gsonPrettyUnicode.toJson(obj)
}
override fun isValidJson(json: String): Boolean {
return try {
JsonParser.parseString(json)
true
} catch (e: JsonSyntaxException) {
false
} catch (e: Exception) {
false
}
}
override fun merge(vararg jsons: String): String {
val merged = JsonObject()
for (json in jsons) {
if (json.isNullOrEmpty()) {
continue
}
try {
val obj = JsonParser.parseString(json).asJsonObject
for (key in obj.keySet()) {
merged.add(key, obj.get(key))
}
} catch (e: Exception) {
// 忽略无效的 JSON 字符串
}
}
return gson.toJson(merged)
}
override fun getNodeValue(json: String, path: String): String? {
return try {
var element = JsonParser.parseString(json)
val paths = path.split("\\.".toRegex()).toTypedArray()
var current = element
for (p in paths) {
if (current.isJsonObject) {
current = current.asJsonObject.get(p)
} else {
return null
}
if (current == null) {
return null
}
}
if (current.isJsonPrimitive) current.asString else current.toString()
} catch (e: Exception) {
null
}
}
override fun updateNodeValue(json: String, path: String, newValue: Any): String {
return try {
val obj = JsonParser.parseString(json).asJsonObject
val paths = path.split("\\.".toRegex()).toTypedArray()
var current = obj
// 导航到倒数第二层
for (i in 0 until paths.size - 1) {
val p = paths[i]
if (!current.has(p) || !current.get(p).isJsonObject) {
current.add(p, JsonObject())
}
current = current.getAsJsonObject(p)
}
// 设置最后一层的值
val lastPath = paths[paths.size - 1]
val element = gson.toJsonTree(newValue)
current.add(lastPath, element)
gson.toJson(obj)
} catch (e: Exception) {
json
}
}
override fun <T, D> convert(source: T, destinationClass: Class<D>): D {
val json = gson.toJson(source)
return gson.fromJson(json, destinationClass)
}
override fun <T, D> convert(source: T, destinationType: JsonTypeReference<D>): D {
val json = gson.toJson(source)
return gson.fromJson(json, destinationType.type)
}
override fun addJsonConverter(c: JsonConverter<*, *>) {
c.getStringConverter()?.let {
gson = gson
.newBuilder()
.registerTypeAdapter(
it.tClass,
it.gsonJsonStringConverterAdapter
)
.create()
gsonUnicode = gsonUnicode
.newBuilder()
.registerTypeAdapter(
it.tClass,
it.gsonJsonStringConverterAdapter
)
.create()
gsonPretty = gsonPretty
.newBuilder()
.registerTypeAdapter(
it.tClass,
it.gsonJsonStringConverterAdapter
)
.create()
gsonPrettyUnicode = gsonPrettyUnicode
.newBuilder()
.registerTypeAdapter(
it.tClass,
it.gsonJsonStringConverterAdapter
)
.create()
}
}
override fun addJsonStringConverter(c: JsonStringConverter<*>) {
addJsonConverter(c)
}
}

View File

@ -0,0 +1,644 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JSONA.kt
* LastUpdate 2026-02-05 10:34:30
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.api
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.kotlinModule
import com.mingliqiye.utils.json.api.base.JsonApi
import com.mingliqiye.utils.json.api.type.JsonTypeReference
import com.mingliqiye.utils.json.api.type.listType
import com.mingliqiye.utils.json.converters.base.BaseJsonConverter
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Path
/**
* JSON工具类提供静态方法访问JSON API功能
*/
object JSONA {
@JvmStatic
private var jsonApi: JsonApi? = null
/**
* 获取JSON API实例
*
* @return JSON API实例
* @throws NullPointerException 当JSON API未初始化时抛出异常
*/
@Throws(NullPointerException::class)
@JvmStatic
fun getJsonApi(): JsonApi {
if (jsonApi == null) {
throw NullPointerException("jsonApi is null plase setJsonApi first")
}
return jsonApi!!
}
/**
* 设置JSON API实例
*
* @param jsa JSON API实例
*/
@JvmStatic
fun setJsonApi(jsa: JsonApi) {
jsonApi = jsa
}
/**
* 使用反射创建并设置指定类型的JSON API实例
*
* @param T 继承自JsonApi的具体实现类
*/
inline fun <reified T : JsonApi> setJsonApi() {
setJsonApi(T::class.java.newInstance() as JsonApi)
}
/**
* 将JSON字符串解析为指定类型的对象
*
* @param json 待解析的JSON字符串
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parse(json: String, clazz: Class<T>): T = getJsonApi().parse(json, clazz)
/**
* 将JSON字符串解析为指定泛型类型对象
*
* @param json 待解析的JSON字符串
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
inline fun <reified T> parse(json: String): T {
return getJsonApi().parse(json, (object : JsonTypeReference<T>() {}))
}
/**
* 将字节数组形式的JSON解析为指定泛型类型对象
*
* @param json 待解析的JSON字节数组
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
inline fun <reified T> parse(json: ByteArray): T = getJsonApi().parse(json, object : JsonTypeReference<T>() {})
/**
* 将JSON字符串解析为指定泛型类型对象
*
* @param json 待解析的JSON字符串
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parse(json: String, type: JsonTypeReference<T>): T = getJsonApi().parse(json, type)
/**
* 将对象格式化为JSON字符串
*
* @param obj 待格式化的对象
* @return 格式化后的JSON字符串
*/
@JvmStatic
fun format(obj: Any): String = getJsonApi().format(obj)
/**
* 将对象格式化为带Unicode转义的JSON字符串
*
* @param obj 待格式化的对象
* @return 格式化后的带Unicode转义的JSON字符串
*/
@JvmStatic
fun formatUnicode(obj: Any): String = getJsonApi().formatPrettyUnicode(obj)
/**
* 从文件路径解析JSON为指定类型的对象
*
* @param path 文件路径
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parseFrom(path: String, clazz: Class<T>): T = getJsonApi().parseFrom(path, clazz)
/**
* 从Path对象解析JSON为指定类型的对象
*
* @param path Path对象
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parseFrom(path: Path, clazz: Class<T>): T = getJsonApi().parseFrom(path, clazz)
/**
* 从File对象解析JSON为指定类型的对象
*
* @param file File对象
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parseFrom(file: File, clazz: Class<T>): T = getJsonApi().parseFrom(file, clazz)
/**
* 从InputStream解析JSON为指定类型的对象
*
* @param inputStream InputStream对象
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parseFrom(inputStream: InputStream, clazz: Class<T>): T = getJsonApi().parseFrom(inputStream, clazz)
/**
* 从文件路径解析JSON为指定泛型类型对象
*
* @param path 文件路径
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parseFrom(path: String, type: JsonTypeReference<T>): T = getJsonApi().parseFrom(path, type)
/**
* 从Path对象解析JSON为指定泛型类型对象
*
* @param path Path对象
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parseFrom(path: Path, type: JsonTypeReference<T>): T = getJsonApi().parseFrom(path, type)
/**
* 从File对象解析JSON为指定泛型类型对象
*
* @param file File对象
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parseFrom(file: File, type: JsonTypeReference<T>): T = getJsonApi().parseFrom(file, type)
/**
* 从InputStream解析JSON为指定泛型类型对象
*
* @param inputStream InputStream对象
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parseFrom(inputStream: InputStream, type: JsonTypeReference<T>): T =
getJsonApi().parseFrom(inputStream, type)
/**
* 将字节数组形式的JSON解析为指定类型的对象
*
* @param json 待解析的JSON字节数组
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parse(json: ByteArray, clazz: Class<T>): T = getJsonApi().parse(json, clazz)
/**
* 将字节数组形式的JSON解析为指定泛型类型对象
*
* @param json 待解析的JSON字节数组
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
@JvmStatic
fun <T> parse(json: ByteArray, type: JsonTypeReference<T>): T = getJsonApi().parse(json, type)
/**
* 将JSON字符串解析为指定类型的对象解析失败时返回默认值
*
* @param json 待解析的JSON字符串
* @param clazz 目标对象的Class类型
* @param defaultValue 解析失败时返回的默认值
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例或默认值
*/
@JvmStatic
fun <T> parse(json: String, clazz: Class<T>, defaultValue: T): T = getJsonApi().parse(json, clazz, defaultValue)
/**
* 将JSON字符串解析为指定泛型类型对象解析失败时返回默认值
*
* @param json 待解析的JSON字符串
* @param type 目标对象的Type类型支持泛型
* @param defaultValue 解析失败时返回的默认值
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例或默认值
*/
@JvmStatic
fun <T> parse(
json: String, type: JsonTypeReference<T>, defaultValue: T
): T = getJsonApi().parse(json, type, defaultValue)
/**
* 将对象格式化为美化格式的JSON字符串带缩进和换行
*
* @param obj 待格式化的对象
* @return 格式化后的美化JSON字符串
*/
@JvmStatic
fun formatPretty(obj: Any): String = getJsonApi().formatPretty(obj)
/**
* 将对象格式化为美化格式的JSON字节数组
*
* @param obj 待格式化的对象
* @return 格式化后的美化JSON字节数组
*/
@JvmStatic
fun formatPrettyBytes(obj: Any): ByteArray = getJsonApi().formatPrettyBytes(obj)
/**
* 将对象格式化为带Unicode转义的美化JSON字符串
*
* @param obj 待格式化的对象
* @return 格式化后的带Unicode转义的美化JSON字符串
*/
@JvmStatic
fun formatPrettyUnicode(obj: Any): String = getJsonApi().formatPrettyUnicode(obj)
/**
* 将对象格式化为带Unicode转义的美化JSON字节数组
*
* @param obj 待格式化的对象
* @return 格式化后的带Unicode转义的美化JSON字节数组
*/
@JvmStatic
fun formatPrettyUnicodeBytes(obj: Any): ByteArray = getJsonApi().formatPrettyUnicodeBytes(obj)
/**
* 将对象格式化为美化格式的JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file 文件路径
*/
@JvmStatic
fun formatPretty(obj: Any, file: String) = getJsonApi().formatPretty(obj, file)
/**
* 将对象格式化为美化格式的JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file Path对象
*/
@JvmStatic
fun formatPretty(obj: Any, file: Path) = getJsonApi().formatPretty(obj, file)
/**
* 将对象格式化为美化格式的JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file File对象
*/
@JvmStatic
fun formatPretty(obj: Any, file: File) = getJsonApi().formatPretty(obj, file)
/**
* 将对象格式化为美化格式的JSON字符串并写入输出流
*
* @param obj 待格式化的对象
* @param stream OutputStream对象
*/
@JvmStatic
fun formatPretty(obj: Any, stream: OutputStream) = getJsonApi().formatPretty(obj, stream)
/**
* 将对象格式化为带Unicode转义的美化JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file 文件路径
*/
@JvmStatic
fun formatPrettyUnicode(obj: Any, file: String) = getJsonApi().formatPrettyUnicode(obj, file)
/**
* 将对象格式化为带Unicode转义的美化JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file Path对象
*/
@JvmStatic
fun formatPrettyUnicode(obj: Any, file: Path) = getJsonApi().formatPrettyUnicode(obj, file)
/**
* 将对象格式化为带Unicode转义的美化JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file File对象
*/
@JvmStatic
fun formatPrettyUnicode(obj: Any, file: File) = getJsonApi().formatPrettyUnicode(obj, file)
/**
* 将对象格式化为带Unicode转义的美化JSON字符串并写入输出流
*
* @param obj 待格式化的对象
* @param stream OutputStream对象
*/
@JvmStatic
fun formatPrettyUnicode(obj: Any, stream: OutputStream) = getJsonApi().formatPrettyUnicode(obj, stream)
/**
* 将对象格式化为JSON字节数组
*
* @param obj 待格式化的对象
* @return 格式化后的JSON字节数组
*/
@JvmStatic
fun formatBytes(obj: Any): ByteArray = getJsonApi().formatBytes(obj)
/**
* 将对象格式化为带Unicode转义的JSON字节数组
*
* @param obj 待格式化的对象
* @return 格式化后的带Unicode转义的JSON字节数组
*/
@JvmStatic
fun formatUnicodeBytes(obj: Any): ByteArray = getJsonApi().formatUnicodeBytes(obj)
/**
* 将对象格式化为JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file 文件路径
*/
@JvmStatic
fun format(obj: Any, file: String) = getJsonApi().format(obj, file)
/**
* 将对象格式化为JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file Path对象
*/
@JvmStatic
fun format(obj: Any, file: Path) = getJsonApi().format(obj, file)
/**
* 将对象格式化为JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file File对象
*/
@JvmStatic
fun format(obj: Any, file: File) = getJsonApi().format(obj, file)
/**
* 将对象格式化为JSON字符串并写入输出流
*
* @param obj 待格式化的对象
* @param stream OutputStream对象
*/
@JvmStatic
fun format(obj: Any, stream: OutputStream) = getJsonApi().format(obj, stream)
/**
* 将对象格式化为带Unicode转义的JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file 文件路径
*/
@JvmStatic
fun formatUnicode(obj: Any, file: String) = getJsonApi().formatUnicode(obj, file)
/**
* 将对象格式化为带Unicode转义的JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file Path对象
*/
@JvmStatic
fun formatUnicode(obj: Any, file: Path) = getJsonApi().formatUnicode(obj, file)
/**
* 将对象格式化为带Unicode转义的JSON字符串并写入文件
*
* @param obj 待格式化的对象
* @param file File对象
*/
@JvmStatic
fun formatUnicode(obj: Any, file: File) = getJsonApi().formatUnicode(obj, file)
/**
* 将对象格式化为带Unicode转义的JSON字符串并写入输出流
*
* @param obj 待格式化的对象
* @param stream OutputStream对象
*/
@JvmStatic
fun formatUnicode(obj: Any, stream: OutputStream) = getJsonApi().formatUnicode(obj, stream)
/**
* 将JSON字符串解析为指定元素类型的List集合
*
* @param json 待解析的JSON字符串
* @param elementType List中元素的类型
* @param <T> 泛型参数表示List中元素的类型
* @return 解析后的List集合
*/
@JvmStatic
fun <T> parseList(json: String, elementType: Class<T>): List<T> = parse(json, listType(elementType))
/**
* 将JSON字符串解析为指定元素类型的List集合
*
* @param json 待解析的JSON字符串
* @param <T> 泛型参数表示List中元素的类型
* @return 解析后的List集合
*/
@JvmStatic
inline fun <reified T> parseList(json: String): List<T> = parse(json, listType(T::class.java))
/**
* 将JSON字符串解析为指定键值类型的Map集合
*
* @param json 待解析的JSON字符串
* @param keyType Map中键的类型
* @param valueType Map中值的类型
* @param <K> 泛型参数表示Map中键的类型
* @param <V> 泛型参数表示Map中值的类型
* @return 解析后的Map集合
*/
@JvmStatic
fun <K, V> parseMap(
json: String, keyType: Class<K>, valueType: Class<V>
): MutableMap<K, V> = getJsonApi().parseMap(json, keyType, valueType)
/**
* 将JSON字符串解析为指定键值类型的Map集合
*
* @param json 待解析的JSON字符串
* @param <K> 泛型参数表示Map中键的类型
* @param <V> 泛型参数表示Map中值的类型
* @return 解析后的Map集合
*/
@JvmStatic
inline fun <reified K, reified V> parseMap(
json: String
): MutableMap<K, V> = getJsonApi().parseMap(json, K::class.java, V::class.java)
/**
* 验证字符串是否为有效的JSON格式
*
* @param json 待验证的字符串
* @return 如果是有效的JSON格式返回true否则返回false
*/
@JvmStatic
fun isValidJson(json: String): Boolean = getJsonApi().isValidJson(json)
/**
* 将对象转换为JSON字节数组
*
* @param object 待转换的对象
* @return 转换后的JSON字节数组
*/
@JvmStatic
fun toBytes(obj: Any): ByteArray = getJsonApi().toBytes(obj)
/**
* 将对象转换为美化格式的JSON字节数组
*
* @param object 待转换的对象
* @return 转换后的美化格式JSON字节数组
*/
@JvmStatic
fun toBytesPretty(obj: Any): ByteArray = getJsonApi().toBytesPretty(obj)
/**
* 合并多个JSON字符串为一个JSON对象
*
* @param jsons 待合并的JSON字符串数组
* @return 合并后的JSON字符串
*/
@JvmStatic
fun merge(vararg jsons: String): String = getJsonApi().merge(*jsons)
/**
* 获取JSON字符串中指定路径节点的值
*
* @param json JSON字符串
* @param path 节点路径"user.name"
* @return 节点值的字符串表示
*/
@JvmStatic
fun getNodeValue(json: String, path: String): String? = getJsonApi().getNodeValue(json, path)
/**
* 更新JSON字符串中指定路径节点的值
*
* @param json 原始JSON字符串
* @param path 节点路径"user.name"
* @param newValue 新的节点值
* @return 更新后的JSON字符串
*/
@JvmStatic
fun updateNodeValue(json: String, path: String, newValue: Any): String =
getJsonApi().updateNodeValue(json, path, newValue)
/**
* 将源对象转换为目标类型的对象
*
* @param T 源对象的类型
* @param D 目标对象的类型
* @param source 需要转换的源对象
* @param destinationClass 目标对象的Class类型
* @return 转换后的目标类型对象
*/
@JvmStatic
fun <D> convert(source: Any, destinationClass: Class<D>): D = getJsonApi().convert(source, destinationClass)
/**
* 使用内联函数和reified类型参数将源对象转换为目标类型的对象
*
* @param T 源对象的类型
* @param D 目标对象的类型通过reified类型参数推断
* @param source 需要转换的源对象
* @return 转换后的目标类型对象
*/
@JvmStatic
inline fun <reified D> convert(source: Any): D = getJsonApi().convert(source, object : JsonTypeReference<D>() {})
/**
* 将源对象转换为目标类型的对象使用JsonTypeReference指定目标类型
*
* @param T 源对象的类型
* @param D 目标对象的类型
* @param source 需要转换的源对象
* @param destinationType 目标对象的JsonTypeReference类型引用
* @return 转换后的目标类型对象
*/
@JvmStatic
fun <D> convert(source: Any, destinationType: JsonTypeReference<D>): D =
getJsonApi().convert(source, destinationType)
/**
* 添加JSON转换器到API中
*
* @param c 需要添加的JSON转换器实例
*/
@JvmStatic
fun addJsonConverter(c: BaseJsonConverter<*, *>) = getJsonApi().addJsonConverter(c)
/**
* 使用反射创建并添加指定类型的JSON转换器到API中
*
* @param T 继承自BaseJsonConverter的具体实现类
*/
inline fun <reified T : BaseJsonConverter<*, *>> addJsonConverter() =
addJsonConverter(T::class.java.newInstance())
inline fun <reified T> String.parseJson() = parse<T>(this)
inline fun <reified T> ByteArray.parseJson() = parse<T>(this)
inline fun <reified T> InputStream.parseJson() = this.use { parse<T>(readBytes()) }
inline fun <reified T> File.parseJson() = this.inputStream().parseJson<T>()
inline fun <reified T> Path.parseJson() = this.toFile().inputStream().parseJson<T>()
fun Any.toJson() = format(this)
fun jacksonKotlinObjectMapper(): ObjectMapper = ObjectMapper().jacksonKotlinObjectMapper()
fun ObjectMapper.jacksonKotlinObjectMapper(): ObjectMapper = this.registerModule(kotlinModule())
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +16,11 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JacksonJsonApi.kt
* LastUpdate 2025-09-15 22:07:43
* LastUpdate 2026-02-05 10:31:14
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
package com.mingliqiye.utils.json.api
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonProcessingException
@ -28,9 +28,10 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.ObjectReader
import com.fasterxml.jackson.databind.node.ObjectNode
import com.mingliqiye.utils.json.converters.JsonConverter
import com.mingliqiye.utils.json.converters.JsonStringConverter
import java.io.IOException
import com.mingliqiye.utils.json.api.base.JsonApi
import com.mingliqiye.utils.json.api.exception.JsonException
import com.mingliqiye.utils.json.api.type.JsonTypeReference
import com.mingliqiye.utils.json.converters.base.BaseJsonConverter
/**
* 基于Jackson的JSON处理实现类提供JSON字符串解析格式化合并节点操作等功能
@ -52,7 +53,7 @@ class JacksonJsonApi : JsonApi {
* @param objectMapper 自定义的ObjectMapper实例
*/
constructor(objectMapper: ObjectMapper) {
this.objectMapper = objectMapper.copy()
this.objectMapper = objectMapper
}
/**
@ -62,12 +63,12 @@ class JacksonJsonApi : JsonApi {
* @param clazz 目标对象类型
* @param <T> 泛型参数表示目标对象类型
* @return 解析后的对象
* @throws JsonException 当解析失败时抛出异常
* @throws com.mingliqiye.utils.json.api.exception.JsonException 当解析失败时抛出异常
*/
override fun <T> parse(json: String, clazz: Class<T>): T {
return try {
objectMapper.readValue(json, clazz)
} catch (e: IOException) {
} catch (e: Exception) {
throw JsonException("Failed to parse JSON string", e)
}
}
@ -87,7 +88,7 @@ class JacksonJsonApi : JsonApi {
objectMapper.constructType(type.type)
)
reader.readValue(json)
} catch (e: IOException) {
} catch (e: Exception) {
throw JsonException("Failed to parse JSON string", e)
}
}
@ -102,21 +103,17 @@ class JacksonJsonApi : JsonApi {
override fun format(obj: Any): String {
return try {
objectMapper.writeValueAsString(obj)
} catch (e: JsonProcessingException) {
} catch (e: Exception) {
throw JsonException(
"Failed to format object to JSON string",
e
"Failed to format object to JSON string", e
)
}
}
override fun formatUnicode(obj: Any): String {
return try {
objectMapper
.writer()
.with(JsonGenerator.Feature.ESCAPE_NON_ASCII)
.writeValueAsString(obj)
} catch (e: JsonProcessingException) {
objectMapper.writer().with(JsonGenerator.Feature.ESCAPE_NON_ASCII).writeValueAsString(obj)
} catch (e: Exception) {
throw JsonException(e)
}
}
@ -130,27 +127,21 @@ class JacksonJsonApi : JsonApi {
*/
override fun formatPretty(obj: Any): String {
return try {
objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(obj)
} catch (e: JsonProcessingException) {
objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj)
} catch (e: Exception) {
throw JsonException(
"Failed to format object to pretty JSON string",
e
"Failed to format object to pretty JSON string", e
)
}
}
override fun formatPrettyUnicode(obj: Any): String {
return try {
objectMapper
.writerWithDefaultPrettyPrinter()
.with(JsonGenerator.Feature.ESCAPE_NON_ASCII)
objectMapper.writerWithDefaultPrettyPrinter().with(JsonGenerator.Feature.ESCAPE_NON_ASCII)
.writeValueAsString(obj)
} catch (e: JsonProcessingException) {
} catch (e: Exception) {
throw JsonException(
"Failed to format object to pretty JSON string",
e
"Failed to format object to pretty JSON string", e
)
}
}
@ -186,8 +177,7 @@ class JacksonJsonApi : JsonApi {
if (node.isObject) {
result.setAll<JsonNode>(node as ObjectNode)
}
} catch (e: IOException) {
// 忽略无效的JSON字符串
} catch (e: Exception) {
}
}
return try {
@ -213,7 +203,7 @@ class JacksonJsonApi : JsonApi {
node = node.get(p)
}
node.asText()
} catch (e: IOException) {
} catch (e: Exception) {
throw JsonException("Failed to get node value", e)
}
}
@ -247,15 +237,14 @@ class JacksonJsonApi : JsonApi {
if (current is ObjectNode) {
val parent: ObjectNode = current
parent.set<JsonNode>(
paths[paths.size - 1],
objectMapper.valueToTree(newValue)
paths[paths.size - 1], objectMapper.valueToTree(newValue)
)
}
objectMapper.writeValueAsString(objectNode)
}
json
} catch (e: IOException) {
} catch (e: Exception) {
throw JsonException("Failed to update node value", e)
}
}
@ -269,8 +258,12 @@ class JacksonJsonApi : JsonApi {
* @param <D> 目标对象类型
* @return 转换后的对象
*/
override fun <T, D> convert(source: T, destinationClass: Class<D>): D {
return objectMapper.convertValue(source, destinationClass)
override fun <D> convert(source: Any, destinationClass: Class<D>): D {
try {
return objectMapper.convertValue(source, destinationClass)
} catch (e: Exception) {
throw JsonException("Failed to update node value", e)
}
}
/**
@ -282,20 +275,21 @@ class JacksonJsonApi : JsonApi {
* @param <D> 目标对象类型
* @return 转换后的对象
*/
override fun <T, D> convert(source: T, destinationType: JsonTypeReference<D>): D {
return objectMapper.convertValue(
source,
objectMapper.constructType(destinationType.type)
)
}
override fun addJsonConverter(c: JsonConverter<*, *>) {
c.getStringConverter()?.let {
objectMapper.registerModule(it.jacksonJsonStringConverterAdapter.jacksonModule)
override fun <D> convert(source: Any, destinationType: JsonTypeReference<D>): D {
try {
return objectMapper.convertValue(
source, objectMapper.constructType(destinationType.type)
)
} catch (e: Exception) {
throw JsonException("Failed to update node value", e)
}
}
override fun addJsonStringConverter(c: JsonStringConverter<*>) {
addJsonConverter(c)
override fun addJsonConverter(c: BaseJsonConverter<*, *>) {
try {
objectMapper.registerModule(c.getJacksonModule())
} catch (e: Exception) {
throw JsonException("Failed to update node value", e)
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,24 +16,128 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonApi.kt
* LastUpdate 2025-09-15 22:32:50
* LastUpdate 2026-02-04 21:00:48
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
package com.mingliqiye.utils.json.api.base
import com.mingliqiye.utils.json.converters.JsonConverter
import com.mingliqiye.utils.json.converters.JsonStringConverter
import com.mingliqiye.utils.json.api.type.JsonTypeReference
import com.mingliqiye.utils.json.api.type.listType
import com.mingliqiye.utils.json.converters.base.BaseJsonConverter
import java.io.*
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
/**
* JSON处理接口提供JSON字符串与Java对象之间的相互转换功能
*/
interface JsonApi {
companion object {
/**
* 将JSON字符串解析为指定泛型类型对象
*
* @param json 待解析的JSON字符串
* @param T 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
inline fun <reified T> JsonApi.parse(json: String): T = parse(json, object : JsonTypeReference<T>() {})
/**
* 将JSON字节数组解析为指定泛型类型对象
*
* @param json 待解析的JSON字节数组
* @param T 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
inline fun <reified T> JsonApi.parse(json: ByteArray): T = parse(json, object : JsonTypeReference<T>() {})
/**
* 将JSON字符串解析为指定泛型类型对象解析失败时返回默认值
*
* @param json 待解析的JSON字符串
* @param defect 解析失败时返回的默认值
* @param T 泛型参数表示目标对象的类型
* @return 解析后的对象实例或默认值
*/
inline fun <reified T> JsonApi.parse(json: String, defect: T): T =
parse(json, object : JsonTypeReference<T>() {}, defect)
/**
* 从文件路径读取JSON并解析为指定泛型类型对象
*
* @param path 文件路径
* @param T 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
inline fun <reified T> JsonApi.parseFrom(path: String): T = parseFrom(path, object : JsonTypeReference<T>() {})
/**
* 从文件读取JSON并解析为指定泛型类型对象
*
* @param path 文件对象
* @param T 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
inline fun <reified T> JsonApi.parseFrom(path: File): T = parseFrom(path, object : JsonTypeReference<T>() {})
/**
* 从输入流读取JSON并解析为指定泛型类型对象
*
* @param path 输入流
* @param T 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
inline fun <reified T> JsonApi.parseFrom(path: InputStream): T =
parseFrom(path, object : JsonTypeReference<T>() {})
/**
* 从路径读取JSON并解析为指定泛型类型对象
*
* @param path 路径对象
* @param T 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
inline fun <reified T> JsonApi.parseFrom(path: Path): T = parseFrom(path, object : JsonTypeReference<T>() {})
/**
* 将JSON字符串解析为指定键值类型的Map集合
*
* @param json 待解析的JSON字符串
* @param K 泛型参数表示Map中键的类型
* @param V 泛型参数表示Map中值的类型
* @return 解析后的Map集合
*/
inline fun <reified K, reified V> JsonApi.parseMap(
json: String
): MutableMap<K, V> = parseMap(json, K::class.java, V::class.java)
/**
* 使用内联函数和reified类型参数将源对象转换为目标类型的对象
*
* @param T 源对象的类型
* @param D 目标对象的类型通过reified类型参数推断
* @param source 需要转换的源对象
* @return 转换后的目标类型对象
*/
@JvmStatic
inline fun <reified D> JsonApi.convert(source: Any): D = convert(source, object : JsonTypeReference<D>() {})
/**
* 添加JSON转换器到API中
*
* @param T 需要添加的JSON转换器类
*/
@JvmStatic
inline fun <reified T : BaseJsonConverter<*, *>> JsonApi.addJsonConverter() {
addJsonConverter(T::class.java.newInstance())
}
}
/**
* 将JSON字符串解析为指定类型的对象
*
@ -64,24 +168,52 @@ interface JsonApi {
fun formatUnicode(obj: Any): String
@Throws(IOException::class)
/**
* 从文件路径读取JSON并解析为指定类型对象
*
* @param path 文件路径
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
fun <T> parseFrom(path: String, clazz: Class<T>): T {
return parseFrom(Paths.get(path), clazz)
}
@Throws(IOException::class)
/**
* 从路径读取JSON并解析为指定类型对象
*
* @param path 路径对象
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
fun <T> parseFrom(path: Path, clazz: Class<T>): T {
return parseFrom(path.toFile(), clazz)
}
@Throws(IOException::class)
/**
* 从文件读取JSON并解析为指定类型对象
*
* @param file 文件对象
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
fun <T> parseFrom(file: File, clazz: Class<T>): T {
Files.newInputStream(file.toPath()).use { inputStream ->
return parseFrom(inputStream, clazz)
}
}
@Throws(IOException::class)
/**
* 从输入流读取JSON并解析为指定类型对象
*
* @param inputStream 输入流
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
fun <T> parseFrom(inputStream: InputStream, clazz: Class<T>): T {
val bytes = ByteArray(1024)
ByteArrayOutputStream().use { bos ->
@ -93,24 +225,52 @@ interface JsonApi {
}
}
@Throws(IOException::class)
/**
* 从文件路径读取JSON并解析为指定泛型类型对象
*
* @param path 文件路径
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
fun <T> parseFrom(path: String, type: JsonTypeReference<T>): T {
return parseFrom(Paths.get(path), type)
}
@Throws(IOException::class)
/**
* 从路径读取JSON并解析为指定泛型类型对象
*
* @param path 路径对象
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
fun <T> parseFrom(path: Path, type: JsonTypeReference<T>): T {
return parseFrom(path.toFile(), type)
}
@Throws(IOException::class)
/**
* 从文件读取JSON并解析为指定泛型类型对象
*
* @param file 文件对象
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
fun <T> parseFrom(file: File, type: JsonTypeReference<T>): T {
Files.newInputStream(file.toPath()).use { inputStream ->
return parseFrom<T>(inputStream, type)
}
}
@Throws(IOException::class)
/**
* 从输入流读取JSON并解析为指定泛型类型对象
*
* @param inputStream 输入流
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
fun <T> parseFrom(inputStream: InputStream, type: JsonTypeReference<T>): T {
val bytes = ByteArray(1024)
ByteArrayOutputStream().use { bos ->
@ -156,10 +316,10 @@ interface JsonApi {
* @return 解析后的对象实例或默认值
</T> */
fun <T> parse(json: String, clazz: Class<T>, defaultValue: T): T {
try {
return parse(json, clazz)
return try {
parse(json, clazz)
} catch (e: Exception) {
return defaultValue
defaultValue
}
}
@ -173,14 +333,12 @@ interface JsonApi {
* @return 解析后的对象实例或默认值
**/
fun <T> parse(
json: String,
type: JsonTypeReference<T>,
defaultValue: T
json: String, type: JsonTypeReference<T>, defaultValue: T
): T {
try {
return parse<T>(json, type)
return try {
parse<T>(json, type)
} catch (e: Exception) {
return defaultValue
defaultValue
}
}
@ -202,46 +360,38 @@ interface JsonApi {
return formatPrettyUnicode(obj)!!.toByteArray()
}
@Throws(IOException::class)
fun formatPretty(obj: Any, file: String) {
formatPretty(obj, Paths.get(file))
}
@Throws(IOException::class)
fun formatPretty(obj: Any, file: Path) {
formatPretty(obj, file.toFile())
}
@Throws(IOException::class)
fun formatPretty(obj: Any, file: File) {
FileOutputStream(file).use { fos ->
formatPretty(obj, fos)
}
}
@Throws(IOException::class)
fun formatPretty(obj: Any, stream: OutputStream) {
stream.write(formatPrettyBytes(obj))
}
@Throws(IOException::class)
fun formatPrettyUnicode(obj: Any, file: String) {
formatPrettyUnicode(obj, Paths.get(file))
}
@Throws(IOException::class)
fun formatPrettyUnicode(obj: Any, file: Path) {
formatPrettyUnicode(obj, file.toFile())
}
@Throws(IOException::class)
fun formatPrettyUnicode(obj: Any, file: File) {
FileOutputStream(file).use { fos ->
formatPrettyUnicode(obj, fos)
}
}
@Throws(IOException::class)
fun formatPrettyUnicode(obj: Any, stream: OutputStream) {
stream.write(formatPrettyUnicodeBytes(obj))
}
@ -254,46 +404,38 @@ interface JsonApi {
return formatUnicode(obj)!!.toByteArray()
}
@Throws(IOException::class)
fun format(obj: Any, file: String) {
format(obj, Paths.get(file))
}
@Throws(IOException::class)
fun format(obj: Any, file: Path) {
format(obj, file.toFile())
}
@Throws(IOException::class)
fun format(obj: Any, file: File) {
FileOutputStream(file).use { fos ->
format(obj, fos)
}
}
@Throws(IOException::class)
fun format(obj: Any, stream: OutputStream) {
stream.write(formatPrettyBytes(obj))
}
@Throws(IOException::class)
fun formatUnicode(obj: Any, file: String) {
formatUnicode(obj, Paths.get(file))
}
@Throws(IOException::class)
fun formatUnicode(obj: Any, file: Path) {
formatUnicode(obj, file.toFile())
}
@Throws(IOException::class)
fun formatUnicode(obj: Any, file: File) {
FileOutputStream(file).use { fos ->
formatUnicode(obj, fos)
}
}
@Throws(IOException::class)
fun formatUnicode(obj: Any, stream: OutputStream) {
stream.write(formatPrettyUnicodeBytes(obj))
}
@ -321,9 +463,7 @@ interface JsonApi {
* @return 解析后的Map集合
</V></K> */
fun <K, V> parseMap(
json: String,
keyType: Class<K>,
valueType: Class<V>
json: String, keyType: Class<K>, valueType: Class<V>
): MutableMap<K, V> {
val mapType: JsonTypeReference<MutableMap<K, V>> = object : JsonTypeReference<MutableMap<K, V>>() {}
return parse<MutableMap<K, V>>(json, mapType)
@ -384,10 +524,9 @@ interface JsonApi {
*/
fun updateNodeValue(json: String, path: String, newValue: Any): String
fun <T, D> convert(source: T, destinationClass: Class<D>): D
fun <D> convert(source: Any, destinationClass: Class<D>): D
fun <T, D> convert(source: T, destinationType: JsonTypeReference<D>): D
fun <D> convert(source: Any, destinationType: JsonTypeReference<D>): D
fun addJsonConverter(c: JsonConverter<*, *>)
fun addJsonStringConverter(c: JsonStringConverter<*>)
fun addJsonConverter(c: BaseJsonConverter<*, *>)
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonException.kt
* LastUpdate 2026-02-05 11:12:36
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.api.exception
/**
* 自定义异常类用于处理 JSON 相关操作中出现的错误
*
* 该类继承自 [RuntimeException]提供了三种构造函数以支持不同的异常场景
* 1. 仅包含错误信息的构造函数
* 2. 包含错误信息和原因Throwable的构造函数
* 3. 仅包含原因Throwable的构造函数
*/
class JsonException : RuntimeException {
/**
* 构造函数创建一个带有指定错误信息的 [JsonException] 实例
*
* @param message 错误信息描述
*/
constructor(message: String) : super(message)
/**
* 构造函数创建一个带有指定错误信息和原因的 [JsonException] 实例
*
* @param message 错误信息描述
* @param cause 导致此异常的根本原因
*/
constructor(message: String, cause: Throwable) : super(message, cause)
/**
* 构造函数创建一个由指定原因引发的 [JsonException] 实例
*
* @param cause 导致此异常的根本原因
*/
constructor(cause: Throwable) : super(cause)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +16,11 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonTypeReference.kt
* LastUpdate 2025-09-15 22:32:50
* LastUpdate 2026-02-05 11:12:36
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
package com.mingliqiye.utils.json.api.type
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
@ -68,8 +68,7 @@ abstract class JsonTypeReference<T> : Comparable<JsonTypeReference<T>> {
* @return 类型引用实例
*/
companion object {
@JvmStatic
fun <T> of(): JsonTypeReference<T> {
inline fun <reified T> of(): JsonTypeReference<T> {
return object : JsonTypeReference<T>() {}
}
@ -116,6 +115,11 @@ abstract class JsonTypeReference<T> : Comparable<JsonTypeReference<T>> {
throw IllegalStateException("无法获取原始类型: $type")
}
/**
* 判断两个 JsonTypeReference 实例是否相等
* @param other 另一个对象
* @return 是否相等
*/
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this.javaClass != other.javaClass) return false
@ -142,6 +146,10 @@ abstract class JsonTypeReference<T> : Comparable<JsonTypeReference<T>> {
return Objects.equals(type, that.type)
}
/**
* 计算当前实例的哈希码
* @return 哈希码值
*/
override fun hashCode(): Int {
if (type is ParameterizedType) {
val paramType = type as ParameterizedType
@ -154,10 +162,19 @@ abstract class JsonTypeReference<T> : Comparable<JsonTypeReference<T>> {
return Objects.hash(type)
}
/**
* 返回当前实例的字符串表示形式
* @return 字符串描述
*/
override fun toString(): String {
return "JsonTypeReference{$type}"
return "com.mingliqiye.utils.json.JsonTypeReference<${TypeReference.getRawString(type)}>"
}
/**
* 比较当前实例与另一个实例的大小关系
* @param other 另一个 JsonTypeReference 实例
* @return 比较结果
*/
override fun compareTo(other: JsonTypeReference<T>): Int {
return this.type.toString().compareTo(other.type.toString())
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,12 +16,12 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonTypeUtils.kt
* LastUpdate 2025-09-17 11:12:06
* LastUpdate 2026-02-04 21:00:48
* UpdateUser MingLiPro
*/
@file:JvmName("JsonTypeUtils")
package com.mingliqiye.utils.json
package com.mingliqiye.utils.json.api.type
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

View File

@ -0,0 +1,76 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile TypeReference.kt
* LastUpdate 2026-02-04 21:00:48
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.api.type
import com.mingliqiye.utils.string.join
import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
class TypeReference(
val rawClass: Type,
vararg paramType: Type
) : ParameterizedType {
private val typeArguments: Array<out Type> = paramType
override fun getActualTypeArguments(): Array<out Type> = typeArguments
override fun getRawType(): Type = rawClass
override fun getOwnerType(): Type? = null
override fun toString(): String = "${getRawString(rawType)}<${
",".join(typeArguments, TypeReference::getRawString)
}>"
companion object {
fun getRawString(s: Any): String {
if (s is Class<*>) {
return s.name
}
if (s is WildcardTypeImpl) {
return getRawString(s.upperBounds[0])
}
if (s is ParameterizedType) {
return of(s).toString()
}
return s.toString()
}
inline fun <reified T> of(): TypeReference {
return of((object : JsonTypeReference<T>() {}).type)
}
fun of(type: Type): TypeReference {
if (type is Class<*>) {
return TypeReference(type)
}
if (type is ParameterizedType) {
return TypeReference(type.rawType, *type.actualTypeArguments)
}
throw IllegalStateException("无法获取类型: $type")
}
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTimeJsonConverter.kt
* LastUpdate 2026-02-05 11:18:57
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters
import com.mingliqiye.utils.json.api.type.JsonTypeReference
import com.mingliqiye.utils.json.converters.base.AnnotationGetter
import com.mingliqiye.utils.json.converters.base.AnnotationGetter.Companion.get
import com.mingliqiye.utils.json.converters.base.BaseJsonStringConverter
import com.mingliqiye.utils.objects.isNull
import com.mingliqiye.utils.string.isNullish
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.DateTimeJsonFormat
import com.mingliqiye.utils.time.Formatter
/**
* DateTimeJsonConverter 是一个用于处理 DateTime 类型与 JSON 字符串之间转换的类
* 它继承自 BaseJsonStringConverter提供了序列化convert和反序列化deConvert的功能
*/
class DateTimeJsonConverter : BaseJsonStringConverter<DateTime> {
/**
* DateTime 对象转换为 JSON 字符串
*
* @param obj 需要转换的 DateTime 对象可能为 null
* @param annotationGetter 用于获取注解信息的对象用于决定格式化方式
* @return 转换后的字符串如果输入为 null 则返回 null
* @throws Exception 如果在转换过程中发生异常
*/
@Throws(Exception::class)
override fun convert(obj: DateTime?, annotationGetter: AnnotationGetter): String? {
// 如果输入对象为 null直接返回 null
if (obj.isNull()) return null
// 获取 DateTimeJsonFormat 注解信息
val dateTimeJsonFormat: DateTimeJsonFormat? = annotationGetter.get<DateTimeJsonFormat>()
// 根据注解中的格式化规则进行转换
return if (Formatter.NONE != dateTimeJsonFormat?.value && dateTimeJsonFormat != null) {
obj.format(dateTimeJsonFormat.value, dateTimeJsonFormat.repcZero)
} else if (dateTimeJsonFormat?.formatter?.isEmpty() == false) {
obj.format(dateTimeJsonFormat.formatter, dateTimeJsonFormat.repcZero)
} else {
obj.format()
}
}
/**
* JSON 字符串转换为 DateTime 对象
*
* @param obj 需要转换的字符串可能为 null 或空
* @param annotationGetter 用于获取注解信息的对象用于决定解析方式
* @return 解析后的 DateTime 对象如果输入为 null 或空则返回 null
* @throws Exception 如果在解析过程中发生异常
*/
@Throws(Exception::class)
override fun deConvert(obj: String?, annotationGetter: AnnotationGetter): DateTime? {
// 如果输入字符串为 null 或空,直接返回 null
if (obj.isNullish()) return null
// 获取 DateTimeJsonFormat 注解信息
val dateTimeJsonFormat: DateTimeJsonFormat? = annotationGetter.get<DateTimeJsonFormat>()
// 根据注解中的解析规则进行转换
return if (Formatter.NONE != dateTimeJsonFormat?.value && dateTimeJsonFormat != null) {
DateTime.parse(obj, dateTimeJsonFormat.value, dateTimeJsonFormat.repcZero)
} else if (dateTimeJsonFormat?.formatter?.isEmpty() == false) {
DateTime.parse(obj, dateTimeJsonFormat.formatter, dateTimeJsonFormat.repcZero)
} else {
DateTime.parse(obj)
}
}
/**
* 获取当前转换器支持的目标类型引用
*
* @return 返回 DateTime 类型的 JsonTypeReference
* @throws Exception 如果在获取类型引用时发生异常
*/
@Throws(Exception::class)
override fun getFromType(): JsonTypeReference<DateTime> = JsonTypeReference.of<DateTime>()
}

View File

@ -1,251 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonStringConverter.kt
* LastUpdate 2025-09-17 19:09:17
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters
import com.alibaba.fastjson2.JSONReader
import com.alibaba.fastjson2.JSONWriter
import com.alibaba.fastjson2.reader.ObjectReader
import com.alibaba.fastjson2.writer.ObjectWriter
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.module.SimpleModule
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.DateTime.Companion.parse
import com.mingliqiye.utils.uuid.UUID
import com.mingliqiye.utils.uuid.UUID.Companion.of
import java.io.IOException
import java.lang.reflect.Type
/**
* JSON转换器接口提供对象与字符串之间的相互转换功能并支持多种JSON库
*
* @param <T> 需要转换的对象类型
</T> */
abstract class JsonStringConverter<T> : JsonConverter<T, String> {
val fastjsonJsonStringConverterAdapter: FastjsonJsonStringConverterAdapter<JsonStringConverter<T>, T>
/**
* 获取 Fastjson 的适配器
* @return 适配器实例
*/
get() = FastjsonJsonStringConverterAdapter.of(this)
val gsonJsonStringConverterAdapter: GsonJsonStringConverterAdapter<JsonStringConverter<T>, T>
/**
* 获取 Gson 的适配器
* @return 适配器实例
*/
get() = GsonJsonStringConverterAdapter.of(this)
val jacksonJsonStringConverterAdapter: JacksonJsonStringConverterAdapter<JsonStringConverter<T>, T>
/**
* 获取 Jackson 的适配器
* @return 适配器实例
*/
get() = JacksonJsonStringConverterAdapter.of(this)
}
class JacksonJsonStringConverterAdapter<T : JsonStringConverter<TT>, TT
> private constructor(val jsonStringConverter: T) {
val jacksonJsonDeserializer: JsonDeserializer<TT?>
/**
* 获取Jackson反序列化器
*
* @return Jackson的JsonDeserializer实例
*/
get() = object : JsonDeserializer<TT?>() {
@Throws(IOException::class)
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): TT? {
if (p.isNaN) return null
return jsonStringConverter.deConvert(p.valueAsString)
}
}
val jacksonJsonSerializer: JsonSerializer<TT?>
/**
* 获取Jackson序列化器
*
* @return Jackson的JsonSerializer实例
*/
get() = object : JsonSerializer<TT?>() {
@Throws(IOException::class)
override fun serialize(
value: TT?,
gen: JsonGenerator,
serializers: SerializerProvider?
) {
if (value == null) {
gen.writeNull()
return
}
gen.writeString(jsonStringConverter.convert(value))
}
}
val jacksonModule: Module
/**
*
* 获取 Jackson 的格式化模块
*
* @return 格式化模块
*/
get() {
val tClass = jsonStringConverter.tClass
val m = SimpleModule(tClass.getSimpleName())
m.addSerializer<TT?>(tClass, this.jacksonJsonSerializer)
m.addDeserializer<TT?>(tClass, this.jacksonJsonDeserializer)
return m
}
companion object {
/**
*
* @param t JSON转换器实例
* @return JSON转换器的适配器
* @param <T> JSON转换器
* @param <TT> JSON转换器的泛型
**/
fun <T : JsonStringConverter<TT>, TT
> of(t: T): JacksonJsonStringConverterAdapter<T, TT> {
return JacksonJsonStringConverterAdapter(t)
}
}
}
class GsonJsonStringConverterAdapter<T : JsonStringConverter<TT>, TT
>(val jsonStringConverter: T) {
val gsonTypeAdapter: TypeAdapter<TT?>
/**
* 获取Gson类型适配器
*
* @return Gson的TypeAdapter实例
*/
get() = object : TypeAdapter<TT?>() {
@Throws(IOException::class)
override fun write(out: JsonWriter, value: TT?) {
if (value == null) {
out.nullValue()
return
}
out.value(jsonStringConverter.convert(value))
}
@Throws(IOException::class)
override fun read(`in`: JsonReader): TT? {
val value = `in`.nextString()
return jsonStringConverter.deConvert(value)
}
}
companion object {
fun <T : JsonStringConverter<TT>, TT
> of(t: T): GsonJsonStringConverterAdapter<T, TT> {
return GsonJsonStringConverterAdapter(t)
}
}
}
class FastjsonJsonStringConverterAdapter<T : JsonConverter<TT, String>, TT
>(val jsonStringConverter: T) {
@Suppress("UNCHECKED_CAST")
val fastJsonObjectWriter: ObjectWriter<T>
/**
* 获取FastJson对象写入器
*
* @return FastJson的ObjectWriter实例
*/
get() = ObjectWriter { writer: JSONWriter?, obj: Any?, _: Any?, _: Type?, _: Long
->
// 如果对象为null则写入null
if (obj == null) {
writer!!.writeNull()
return@ObjectWriter
}
writer!!.writeString(jsonStringConverter.convert(obj as TT))
}
val fastJsonObjectReader: ObjectReader<TT>
/**
* 获取FastJson对象读取器
*
* @return FastJson的ObjectReader实例
*/
get() = ObjectReader { reader: JSONReader?, _: Type?, _: Any?, _: Long
->
val value = reader!!.readString()
jsonStringConverter.deConvert(value)
}
companion object {
fun <T : JsonConverter<TT, String>, TT
> of(t: T): FastjsonJsonStringConverterAdapter<T, TT> {
return FastjsonJsonStringConverterAdapter(t)
}
}
}
class DateTimeJsonConverter : JsonStringConverter<DateTime>() {
override val tClass = DateTime::class.java
override fun convert(obj: DateTime?): String? {
if (obj == null) {
return null
}
return obj.format()
}
override fun deConvert(obj: String?): DateTime? {
if (obj == null) {
return null
}
return parse(
obj
)
}
}
class UUIDJsonStringConverter : JsonStringConverter<UUID>() {
override val tClass: Class<UUID> = UUID::class.java
override fun convert(obj: UUID?): String? {
if (obj == null) {
return null
}
return obj.getString()
}
override fun deConvert(obj: String?): UUID? {
if (obj == null) {
return null
}
return of(obj)
}
}

View File

@ -0,0 +1,101 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUIDJsonConverter.kt
* LastUpdate 2026-02-05 11:19:45
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters
import com.mingliqiye.utils.base.BaseType
import com.mingliqiye.utils.json.api.type.JsonTypeReference
import com.mingliqiye.utils.json.converters.base.AnnotationGetter
import com.mingliqiye.utils.json.converters.base.AnnotationGetter.Companion.get
import com.mingliqiye.utils.json.converters.base.BaseJsonStringConverter
import com.mingliqiye.utils.objects.isNull
import com.mingliqiye.utils.string.isNullish
import com.mingliqiye.utils.uuid.UUID
import com.mingliqiye.utils.uuid.UUIDFormatType
import com.mingliqiye.utils.uuid.UUIDJsonFormat
/**
* UUIDJsonConverter 是一个用于处理 UUID 类型与 JSON 字符串之间转换的类
* 它继承自 BaseJsonStringConverter并实现了 convert deConvert 方法
* 分别用于将 UUID 对象序列化为字符串以及反序列化字符串为 UUID 对象
*/
class UUIDJsonConverter : BaseJsonStringConverter<UUID> {
/**
* UUID 对象转换为字符串形式
*
* @param obj 需要转换的 UUID 对象可能为 null
* @param annotationGetter 用于获取注解信息的对象
* @return 转换后的字符串如果输入为 null 则返回 null
*/
override fun convert(
obj: UUID?,
annotationGetter: AnnotationGetter
): String? {
// 如果输入对象为 null直接返回 null
if (obj.isNull()) return null
// 获取 UUIDJsonFormat 注解信息,若未找到则使用默认格式
val dateTimeJsonFormat: UUIDJsonFormat = annotationGetter.get<UUIDJsonFormat>() ?: return obj.getString()
// 根据注解中的 base 和 value 属性选择合适的字符串表示方式
return if (BaseType.BASE16 != dateTimeJsonFormat.base) {
obj.getString(dateTimeJsonFormat.base)
} else if (UUIDFormatType.NO_UPPER_SPACE != dateTimeJsonFormat.value) {
obj.getString(dateTimeJsonFormat.value)
} else {
obj.getString()
}
}
/**
* 将字符串反序列化为 UUID 对象
*
* @param obj 需要反序列化的字符串可能为 null 或空
* @param annotationGetter 用于获取注解信息的对象
* @return 反序列化后的 UUID 对象如果输入无效则返回 null
*/
override fun deConvert(
obj: String?,
annotationGetter: AnnotationGetter
): UUID? {
// 如果输入字符串为 null 或空,直接返回 null
if (obj.isNullish()) return null
// 获取 UUIDJsonFormat 注解信息,若未找到则使用默认解析方式
val dateTimeJsonFormat: UUIDJsonFormat = annotationGetter.get<UUIDJsonFormat>() ?: return UUID.of(obj)
// 根据注解中的 base 属性选择合适的解析方式
return if (BaseType.BASE16 != dateTimeJsonFormat.base) {
UUID.of(obj, dateTimeJsonFormat.base)
} else {
UUID.of(obj)
}
}
/**
* 返回当前转换器支持的目标类型引用
*
* @return 表示 UUID 类型的 JsonTypeReference 对象
*/
override fun getFromType() = JsonTypeReference.of<UUID>()
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile AnnotationGetter.kt
* LastUpdate 2026-02-05 11:12:36
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
/**
* 定义一个用于获取注解的接口
* 提供了一个通用方法来根据注解类型获取对应的注解实例
*/
interface AnnotationGetter {
/**
* 根据指定的注解类型获取该类型的注解实例
*
* @param clazz 注解的Class对象表示要获取的注解类型
* @return 返回对应类型的注解实例如果未找到则返回null
*/
fun <T : Annotation> get(clazz: Class<T>): T?
companion object {
/**
* 使用内联函数和重ified类型参数简化注解获取操作
* 通过传入泛型参数自动推断注解类型避免手动传递Class对象
*
* @param T 注解类型必须继承自Annotation
* @return 返回对应类型的注解实例如果未找到则返回null
*/
inline fun <reified T : Annotation> AnnotationGetter.get(): T? = this.get(T::class.java)
/**
* 提供一个默认实现始终返回null
* 可用于需要空注解获取器的场景
*/
val nullGetter = object : AnnotationGetter {
/**
* 始终返回null不执行任何实际逻辑
*
* @param clazz 注解的Class对象未使用
* @return 始终返回null
*/
override fun <T : Annotation> get(clazz: Class<T>): T? = null
}
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonBigDecimalConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
import java.math.BigDecimal
/**
* 接口 [BaseJsonBigDecimalConverter] 定义了一个用于将 JSON 数据转换为 [BigDecimal] 类型的转换器
*
* 此接口继承自 [BaseJsonConverter]并指定目标类型为 [BigDecimal]
* 实现此接口的类需要提供具体的转换逻辑
*
* @param E 转换器处理的源数据类型
*/
interface BaseJsonBigDecimalConverter<E> : BaseJsonConverter<E, BigDecimal> {
/**
* 获取目标类型的引用信息
*
* 该方法返回一个 [JsonTypeReference] 对象表示目标类型为 [BigDecimal]
* 通过调用 [JsonTypeReference.Companion.of] 方法创建类型引用
*
* @return 目标类型 [BigDecimal] 的类型引用
* @throws Exception 如果在获取类型引用时发生异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<BigDecimal>()
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonBigIntegerConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
import java.math.BigInteger
/**
* 接口 [BaseJsonBigIntegerConverter] 定义了一个用于将 JSON 数据转换为 `BigInteger` 类型的转换器
* 该接口继承自 [BaseJsonConverter]并指定了泛型参数 [E] `BigInteger`
*
* @param E 表示需要转换的目标类型由实现类具体指定
*/
interface BaseJsonBigIntegerConverter<E> : BaseJsonConverter<E, BigInteger> {
/**
* 获取目标类型的引用用于 JSON 反序列化时确定目标类型
* 该方法重写了父接口中的 [getToType] 方法并返回 `BigInteger` 类型的引用
*
* @return 返回 [JsonTypeReference] 对象表示 `BigInteger` 类型的引用
* @throws Exception 如果在获取类型引用过程中发生异常则抛出该异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<BigInteger>()
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonBooleanConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
/**
* 接口 [BaseJsonBooleanConverter] 定义了一个用于处理布尔类型 JSON 转换的基础转换器
*
* 该接口继承自 [BaseJsonConverter]并专门用于将某种类型 [E] 转换为布尔类型 [Boolean]
* 实现该接口的类需要提供具体的转换逻辑
*/
interface BaseJsonBooleanConverter<E> : BaseJsonConverter<E, Boolean> {
/**
* 获取目标类型的引用信息
*
* 该方法返回一个 [JsonTypeReference] 对象表示目标类型为 [Boolean]
* 此方法重写了父接口中的定义并通过 [JsonTypeReference.Companion.of] 方法获取布尔类型的引用
*
* @return [JsonTypeReference<Boolean>] 表示布尔类型的引用
* @throws Exception 如果在获取类型引用时发生异常则抛出此异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<Boolean>()
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonByteConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
/**
* BaseJsonByteConverter 是一个接口继承自 BaseJsonConverter用于定义字节类型Byte JSON 转换器
*
* 该接口的主要作用是提供一个标准的字节类型转换实现并通过 getToType 方法返回目标类型的引用
*
* @param E 泛型参数表示需要转换的目标对象类型
*/
interface BaseJsonByteConverter<E> : BaseJsonConverter<E, Byte> {
/**
* 获取目标类型的引用用于标识转换的目标类型为 Byte
*
* @return 返回 JsonTypeReference 类型的实例表示目标类型为 Byte
* @throws Exception 如果在获取类型引用过程中发生异常则抛出该异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<Byte>()
}

View File

@ -0,0 +1,210 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonConverter.kt
* LastUpdate 2026-02-05 11:16:12
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonToken
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.deser.ContextualDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.ser.ContextualSerializer
import com.mingliqiye.utils.json.api.type.JsonTypeReference
import java.math.BigDecimal
import java.math.BigInteger
/**
* BaseJsonConverter 是一个通用的 JSON 转换器接口用于定义对象之间的双向转换逻辑
*
* @param F 源类型表示需要被转换的对象类型
* @param T 目标类型表示转换后的对象类型
*/
interface BaseJsonConverter<F, T> {
/**
* 将源对象转换为目标对象
*
* @param obj 源对象可能为 null
* @param annotationGetter 注解获取器用于获取字段上的注解信息
* @return 转换后的目标对象可能为 null
* @throws Exception 转换过程中可能抛出的异常
*/
@Throws(Exception::class)
fun convert(obj: F?, annotationGetter: AnnotationGetter): T?
/**
* 将目标对象反向转换为源对象
*
* @param obj 目标对象可能为 null
* @param annotationGetter 注解获取器用于获取字段上的注解信息
* @return 反向转换后的源对象可能为 null
* @throws Exception 转换过程中可能抛出的异常
*/
@Throws(Exception::class)
fun deConvert(obj: T?, annotationGetter: AnnotationGetter): F?
/**
* 获取源类型的类型引用
*
* @return 源类型的 JsonTypeReference 对象
* @throws Exception 获取过程中可能抛出的异常
*/
@Throws(Exception::class)
fun getFromType(): JsonTypeReference<F>
/**
* 获取目标类型的类型引用
*
* @return 目标类型的 JsonTypeReference 对象
* @throws Exception 获取过程中可能抛出的异常
*/
@Throws(Exception::class)
fun getToType(): JsonTypeReference<T>
/**
* 获取源类型的原始类
*
* @return 源类型的 Class 对象
* @throws Exception 获取过程中可能抛出的异常
*/
@Throws(Exception::class)
fun getFromClass() = getFromType().getRawType()
/**
* 获取目标类型的原始类
*
* @return 目标类型的 Class 对象
* @throws Exception 获取过程中可能抛出的异常
*/
@Throws(Exception::class)
fun getToClass() = getToType().getRawType()
/**
* 创建并返回一个 Jackson 模块该模块包含自定义的序列化器和反序列化器
*
* @return 配置了自定义序列化器和反序列化器的 SimpleModule 对象
*/
fun getJacksonModule(): SimpleModule {
// 创建一个 Jackson 模块,名称由源类型和目标类型组成
val module = SimpleModule("${getFromClass().name}To${getToClass().name}")
// 定义自定义序列化器
val serializer = object : JsonSerializer<F>(), ContextualSerializer {
var property: BeanProperty? = null
/**
* 序列化方法将源对象转换为目标对象并根据目标对象的类型写入 JSON
*
* @param value 源对象可能为 null
* @param gen JSON 生成器用于写入 JSON 数据
* @param provider 序列化提供者可选参数
*/
override fun serialize(
value: F?,
gen: JsonGenerator,
provider: SerializerProvider?
) {
// 执行转换逻辑
val data: T? = convert(value, object : AnnotationGetter {
override fun <T : Annotation> get(clazz: Class<T>): T? = property?.getAnnotation(clazz)
})
// 根据目标对象的类型写入对应的 JSON 值
when (data) {
null -> gen.writeNull()
is String -> gen.writeString(data)
is Long -> gen.writeNumber(data)
is Short -> gen.writeNumber(data)
is Byte -> gen.writeNumber(data.toShort())
is Int -> gen.writeNumber(data)
is BigDecimal -> gen.writeNumber(data)
is BigInteger -> gen.writeNumber(data)
is Float -> gen.writeNumber(data)
is Double -> gen.writeNumber(data)
is Boolean -> gen.writeBoolean(data)
else -> throw IllegalArgumentException("not sport data type ${data.javaClass} $data")
}
}
/**
* 上下文感知方法设置当前属性信息
*
* @param prov 序列化提供者可选参数
* @param property 当前属性信息
* @return 返回当前序列化器实例
*/
override fun createContextual(
prov: SerializerProvider?,
property: BeanProperty?
): JsonSerializer<F> {
this.property = property
return this
}
}
// 定义自定义反序列化器
val deserializer = object : JsonDeserializer<F>(), ContextualDeserializer {
/**
* 反序列化方法 JSON 解析数据并转换回源对象
*
* @param p JSON 解析器
* @param ctxt 反序列化上下文可选参数
* @return 转换后的源对象可能为 null
*/
override fun deserialize(
p: JsonParser,
ctxt: DeserializationContext?
): F? {
// 处理 null 值情况
if (p.currentToken == JsonToken.VALUE_NULL) return null
// 执行反向转换逻辑
return deConvert(p.readValueAs(getToClass()), object : AnnotationGetter {
override fun <T : Annotation> get(clazz: Class<T>): T? = property?.getAnnotation(clazz)
})
}
var property: BeanProperty? = null
/**
* 上下文感知方法设置当前属性信息
*
* @param ctxt 反序列化上下文可选参数
* @param property 当前属性信息
* @return 返回当前反序列化器实例
*/
override fun createContextual(
ctxt: DeserializationContext?,
property: BeanProperty?
): JsonDeserializer<F> {
this.property = property
return this
}
}
// 将自定义序列化器和反序列化器注册到模块中
return module
.addSerializer(getFromClass(), serializer)
.addDeserializer(getFromClass(), deserializer)
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonDoubleConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
/**
* BaseJsonDoubleConverter 是一个接口继承自 BaseJsonConverter
* 用于定义将 JSON 数据转换为 Double 类型的转换器
*
* @param E 泛型参数表示需要转换的目标类型
*/
interface BaseJsonDoubleConverter<E> : BaseJsonConverter<E, Double> {
/**
* 获取目标类型的引用用于标识转换的目标类型为 Double
*
* @return 返回 JsonTypeReference<Double> 类型的实例表示目标类型为 Double
* @throws Exception 如果在获取类型引用时发生异常则抛出该异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<Double>()
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonFloatConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
/**
* 接口 [BaseJsonFloatConverter] 定义了一个用于处理浮点数类型 JSON 转换的基础转换器
*
* 该接口继承自 [BaseJsonConverter]并指定泛型参数 [E] [Float]
* 表示将某种类型 [E] 转换为 [Float] 类型的 JSON 数据
*/
interface BaseJsonFloatConverter<E> : BaseJsonConverter<E, Float> {
/**
* 获取目标类型的引用信息
*
* 该方法重写了父接口中的 [getToType] 方法返回 [Float] 类型的 [JsonTypeReference] 实例
* 用于标识当前转换器的目标类型为 [Float]
*
* @return [JsonTypeReference] 表示 [Float] 类型的引用信息
* @throws Exception 如果在获取类型引用时发生异常则抛出此异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<Float>()
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonIntConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
/**
* 接口 [BaseJsonIntConverter] 是一个泛型接口继承自 [BaseJsonConverter]
* 用于定义将 JSON 数据转换为 [Int] 类型的转换器
*
* @param E 表示需要转换的目标类型通常是一个具体的业务实体或数据模型
*/
interface BaseJsonIntConverter<E> : BaseJsonConverter<E, Int> {
/**
* 获取目标类型的引用信息
*
* 该方法重写了父接口中的 [getToType] 方法返回 [Int] 类型的 [JsonTypeReference] 实例
* 用于标识当前转换器的目标类型为 [Int]
*
* @return 返回 [JsonTypeReference] 的实例表示目标类型为 [Int]
* @throws Exception 如果在获取类型引用时发生异常则抛出该异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<Int>()
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonLongConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
/**
* 接口 [BaseJsonLongConverter] 定义了一个用于将 JSON 数据转换为 Long 类型的转换器
*
* 此接口继承自 [BaseJsonConverter]并指定泛型参数 [E] [Long]
* 表示该转换器处理的是从类型 [E] [Long] 的转换
*/
interface BaseJsonLongConverter<E> : BaseJsonConverter<E, Long> {
/**
* 获取目标类型的引用用于标识此转换器的目标类型为 [Long]
*
* @return 返回 [JsonTypeReference] 的实例表示目标类型为 [Long]
* @throws Exception 如果在获取类型引用时发生异常则抛出此异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<Long>()
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonShortConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
/**
* 接口 [BaseJsonShortConverter] 定义了一个用于将 JSON 数据转换为 Short 类型的转换器
*
* 该接口继承自 [BaseJsonConverter]并指定泛型参数 [E] [Short]
* 表示该转换器处理的源类型为 [E]目标类型为 [Short]
*/
interface BaseJsonShortConverter<E> : BaseJsonConverter<E, Short> {
/**
* 获取目标类型的引用信息
*
* 该方法重写了父接口中的 [getToType] 方法返回 [Short] 类型的 [JsonTypeReference] 实例
* 通过 [JsonTypeReference.Companion.of] 方法创建类型引用
*
* @return [JsonTypeReference] 表示 [Short] 类型的引用
* @throws Exception 如果在获取类型引用过程中发生异常则抛出该异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<Short>()
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseJsonStringConverter.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters.base
import com.mingliqiye.utils.json.api.type.JsonTypeReference
/**
* 接口 [BaseJsonStringConverter] 定义了一个用于 JSON 字符串转换的基础接口
*
* 该接口继承自 [BaseJsonConverter]并指定泛型参数 [E] [String]
* 表示将类型 [E] 转换为字符串类型的 JSON 数据
*/
interface BaseJsonStringConverter<E> : BaseJsonConverter<E, String> {
/**
* 获取目标类型的引用信息
*
* 该方法重写了父接口中的 [getToType] 方法返回一个表示 [String] 类型的 [JsonTypeReference] 实例
* 此方法可能抛出异常因此使用 [@Throws(Exception::class)] 注解标记
*
* @return 返回一个 [JsonTypeReference] 实例表示目标类型为 [String]
* @throws Exception 如果在获取类型引用时发生错误则抛出此异常
*/
@Throws(Exception::class)
override fun getToType() = JsonTypeReference.of<String>()
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonConverter.kt
* LastUpdate 2026-02-05 11:18:33
* UpdateUser MingLiPro
*/
@file:JvmName("JsonConverter")
package com.mingliqiye.utils.json.converters.base
import com.fasterxml.jackson.databind.ObjectMapper
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
/**
* 获取给定类型的实际类对象
*
* @param type 类型对象可以是ClassParameterizedType或其他Type的实现
* @return 返回与给定类型对应的Class对象如果无法解析则返回null
*/
fun getClass(type: Type?): Class<*>? {
// 尝试将type直接转换为Class类型如果失败则检查是否为ParameterizedType
val clazz: Class<*>? = type as? Class<*> ?: if (type is ParameterizedType) {
// 如果是ParameterizedType则递归获取其原始类型
getClass(type.rawType)
} else {
// 其他情况返回null
null
}
return clazz
}
/**
* 扩展ObjectMapper用于注册指定类型的模块
*
* 此函数通过反射创建指定类型的实例并调用其getJacksonModule方法来获取Jackson模块
* 然后将该模块注册到当前ObjectMapper实例中
*
* @param T 必须继承自BaseJsonConverter的泛型类型
* @return 返回注册了模块后的ObjectMapper实例
*/
inline fun <reified T : BaseJsonConverter<*, *>> ObjectMapper.registerModule(): ObjectMapper =
this.registerModule(T::class.java.newInstance().getJacksonModule())

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Loggers.kt
* LastUpdate 2025-09-18 09:30:48
* LastUpdate 2026-02-05 10:20:31
* UpdateUser MingLiPro
*/
@ -26,9 +26,7 @@ package com.mingliqiye.utils.logger
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.slf4j.Marker
import java.util.*
enum class MingLiLoggerLevel {
TRACE,
@ -38,9 +36,9 @@ enum class MingLiLoggerLevel {
ERROR
}
class MingLiLogger : Logger {
class MingLiLogger(private val name: String) : Logger {
override fun getName(): String {
return "MingLiLogger"
return name
}
override fun isTraceEnabled(): Boolean {
@ -345,14 +343,18 @@ class MingLiLogger : Logger {
fun toPrintln(message: String, level: MingLiLoggerLevel) {
when (level) {
MingLiLoggerLevel.TRACE -> println("[TRACE] $message")
MingLiLoggerLevel.DEBUG -> println("[DEBUG] $message")
MingLiLoggerLevel.INFO -> println("[INFO] $message")
MingLiLoggerLevel.WARN -> println("[WARN] $message")
MingLiLoggerLevel.ERROR -> println("[ERROR] $message")
MingLiLoggerLevel.TRACE -> wirteToSteam("[TRACE] [$name] $message\n")
MingLiLoggerLevel.DEBUG -> wirteToSteam("[DEBUG] [$name] $message\n")
MingLiLoggerLevel.INFO -> wirteToSteam("[INFO] [$name] $message\n")
MingLiLoggerLevel.WARN -> wirteToSteam("[WARN] [$name] $message\n")
MingLiLoggerLevel.ERROR -> wirteToSteam("[ERROR] [$name] $message\n")
}
}
fun wirteToSteam(string: String) {
System.out.write(string.toByteArray())
}
private fun format1(format: String, arg: Any?): String {
return if (format.contains("{}")) {
format.replaceFirst("{}", arg?.toString() ?: "null")
@ -374,48 +376,3 @@ class MingLiLogger : Logger {
return result
}
}
class MingLiLoggerFactory {
private var hasSLF4JImplementation: Boolean? = null
// 线程安全的延迟初始化
private fun checkSLF4JImplementation(): Boolean {
if (hasSLF4JImplementation == null) {
synchronized(this) {
if (hasSLF4JImplementation == null) {
hasSLF4JImplementation = try {
// 更可靠的检测方式
ServiceLoader.load(
Class.forName("org.slf4j.spi.SLF4JServiceProvider")
).iterator().hasNext()
} catch (e: ClassNotFoundException) {
false
} catch (e: NoClassDefFoundError) {
false
}
}
}
}
return hasSLF4JImplementation ?: false
}
fun getLogger(name: String): Logger {
return if (checkSLF4JImplementation()) {
LoggerFactory.getLogger(name)
} else {
MingLiLogger()
}
}
fun getLogger(clazz: Class<*>): Logger {
return if (checkSLF4JImplementation()) {
LoggerFactory.getLogger(clazz)
} else {
MingLiLogger()
}
}
}
val mingLiLoggerFactory: MingLiLoggerFactory by lazy { MingLiLoggerFactory() }

View File

@ -0,0 +1,106 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MingLiLoggerFactory.kt
* LastUpdate 2026-02-05 10:20:31
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.logger
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.lang.reflect.Method
import java.util.*
class MingLiLoggerFactory {
companion object {
private var nameMethod: Method? = null
private var clazzMethod: Method? = null
init {
try {
val clazz = Class.forName("com.mingliqiye.logger.Loggers")
nameMethod = clazz.getMethod("getLogger", String::class.java)
clazzMethod = clazz.getMethod("getLogger", Class::class.java)
} catch (e: Exception) {
}
}
private val mingLiLoggerFactory: MingLiLoggerFactory by lazy {
MingLiLoggerFactory()
}
@JvmStatic
fun getLogger(name: String): Logger {
return if (mingLiLoggerFactory.checkSLF4JImplementation()) {
if (nameMethod != null) {
nameMethod!!.invoke(null, name) as Logger
} else {
LoggerFactory.getLogger(name)
}
} else mingLiLoggerFactory.getLogger(name)
}
@JvmStatic
fun getLogger(clazz: Class<*>): Logger {
return if (mingLiLoggerFactory.checkSLF4JImplementation()) {
if (clazzMethod != null) {
clazzMethod!!.invoke(null, clazz) as Logger
} else {
LoggerFactory.getLogger(clazz)
}
} else mingLiLoggerFactory.getLogger(clazz)
}
inline fun <reified T> getLogger() = getLogger(T::class.java)
}
private var hasSLF4JImplementation: Boolean? = null
// 线程安全的延迟初始化
private fun checkSLF4JImplementation(): Boolean {
if (hasSLF4JImplementation == null) {
synchronized(this) {
if (hasSLF4JImplementation == null) {
hasSLF4JImplementation = try {
// 更可靠的检测方式
ServiceLoader.load(
Class.forName("org.slf4j.spi.SLF4JServiceProvider")
).iterator().hasNext()
} catch (e: ClassNotFoundException) {
false
} catch (e: NoClassDefFoundError) {
false
}
}
}
}
return hasSLF4JImplementation ?: false
}
@JvmName("getLogger_prc_name")
private fun getLogger(name: String): Logger = MingLiLogger(name)
@JvmName("getLogger_prc_clazz")
private fun getLogger(clazz: Class<*>): Logger = MingLiLogger(clazz.name)
}

View File

@ -0,0 +1,374 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MathUtils.kt
* LastUpdate 2026-02-05 10:25:32
* UpdateUser MingLiPro
*/
@file:JvmName("MathUtils")
package com.mingliqiye.utils.math
import java.math.BigDecimal
import java.math.BigInteger
/**
* BigDecimal减法运算符重载 - BigDecimal类型
* @param value 被减数
* @return 减法运算结果
*/
operator fun BigDecimal.minus(value: BigDecimal): BigDecimal = this.subtract(value)
/**
* BigDecimal减法运算符重载 - Long类型
* @param value 被减数
* @return 减法运算结果
*/
operator fun BigDecimal.minus(value: Long): BigDecimal = this.subtract(BigDecimal(value))
/**
* BigDecimal减法运算符重载 - Int类型
* @param value 被减数
* @return 减法运算结果
*/
operator fun BigDecimal.minus(value: Int): BigDecimal = this.subtract(BigDecimal(value))
/**
* BigDecimal减法运算符重载 - Double类型
* @param value 被减数
* @return 减法运算结果
*/
operator fun BigDecimal.minus(value: Double): BigDecimal = this.subtract(BigDecimal(value.toString()))
/**
* BigDecimal减法运算符重载 - Float类型
* @param value 被减数
* @return 减法运算结果
*/
operator fun BigDecimal.minus(value: Float): BigDecimal = this.subtract(BigDecimal(value.toString()))
/**
* BigDecimal减法运算符重载 - BigInteger类型
* @param value 被减数
* @return 减法运算结果
*/
operator fun BigDecimal.minus(value: BigInteger): BigDecimal = this.subtract(BigDecimal(value))
/**
* BigDecimal加法运算符重载 - BigDecimal类型
* @param value 加数
* @return 加法运算结果
*/
operator fun BigDecimal.plus(value: BigDecimal): BigDecimal = this.add(value)
/**
* BigDecimal加法运算符重载 - Long类型
* @param value 加数
* @return 加法运算结果
*/
operator fun BigDecimal.plus(value: Long): BigDecimal = this.add(BigDecimal(value))
/**
* BigDecimal加法运算符重载 - Int类型
* @param value 加数
* @return 加法运算结果
*/
operator fun BigDecimal.plus(value: Int): BigDecimal = this.add(BigDecimal(value))
/**
* BigDecimal加法运算符重载 - Double类型
* @param value 加数
* @return 加法运算结果
*/
operator fun BigDecimal.plus(value: Double): BigDecimal = this.add(BigDecimal(value.toString()))
/**
* BigDecimal加法运算符重载 - Float类型
* @param value 加数
* @return 加法运算结果
*/
operator fun BigDecimal.plus(value: Float): BigDecimal = this.add(BigDecimal(value.toString()))
/**
* BigDecimal加法运算符重载 - BigInteger类型
* @param value 加数
* @return 加法运算结果
*/
operator fun BigDecimal.plus(value: BigInteger): BigDecimal = this.add(BigDecimal(value))
/**
* BigDecimal乘法运算符重载 - BigDecimal类型
* @param value 乘数
* @return 乘法运算结果
*/
operator fun BigDecimal.times(value: BigDecimal): BigDecimal = this.multiply(value)
/**
* BigDecimal乘法运算符重载 - Long类型
* @param value 乘数
* @return 乘法运算结果
*/
operator fun BigDecimal.times(value: Long): BigDecimal = this.multiply(BigDecimal(value))
/**
* BigDecimal乘法运算符重载 - Int类型
* @param value 乘数
* @return 乘法运算结果
*/
operator fun BigDecimal.times(value: Int): BigDecimal = this.multiply(BigDecimal(value))
/**
* BigDecimal乘法运算符重载 - Double类型
* @param value 乘数
* @return 乘法运算结果
*/
operator fun BigDecimal.times(value: Double): BigDecimal = this.multiply(BigDecimal(value.toString()))
/**
* BigDecimal乘法运算符重载 - Float类型
* @param value 乘数
* @return 乘法运算结果
*/
operator fun BigDecimal.times(value: Float): BigDecimal = this.multiply(BigDecimal(value.toString()))
/**
* BigDecimal乘法运算符重载 - BigInteger类型
* @param value 乘数
* @return 乘法运算结果
*/
operator fun BigDecimal.times(value: BigInteger): BigDecimal = this.multiply(BigDecimal(value))
/**
* BigDecimal除法运算符重载 - BigDecimal类型
* @param value 除数
* @return 除法运算结果
*/
operator fun BigDecimal.div(value: BigDecimal): BigDecimal = this.divide(value)
/**
* BigDecimal除法运算符重载 - Long类型
* @param value 除数
* @return 除法运算结果
*/
operator fun BigDecimal.div(
value: Long
): BigDecimal = this.divide(BigDecimal(value))
/**
* BigDecimal除法运算符重载 - Int类型
* @param value 除数
* @return 除法运算结果
*/
operator fun BigDecimal.div(
value: Int
): BigDecimal = this.divide(BigDecimal(value))
/**
* BigDecimal除法运算符重载 - Double类型
* @param value 除数
* @return 除法运算结果
*/
operator fun BigDecimal.div(
value: Double
): BigDecimal = this.divide(BigDecimal(value.toString()))
/**
* BigDecimal除法运算符重载 - Float类型
* @param value 除数
* @return 除法运算结果
*/
operator fun BigDecimal.div(
value: Float
): BigDecimal = this.divide(BigDecimal(value.toString()))
/**
* BigDecimal除法运算符重载 - BigInteger类型
* @param value 除数
* @return 除法运算结果
*/
operator fun BigDecimal.div(
value: BigInteger
): BigDecimal = this.divide(BigDecimal(value))
/**
* BigDecimal取余运算符重载 - BigDecimal类型
* @param value 除数
* @return 取余运算结果
*/
operator fun BigDecimal.rem(value: BigDecimal): BigDecimal = this.remainder(value)
/**
* BigDecimal取余运算符重载 - Long类型
* @param value 除数
* @return 取余运算结果
*/
operator fun BigDecimal.rem(value: Long): BigDecimal = this.remainder(BigDecimal(value))
/**
* BigDecimal取余运算符重载 - Int类型
* @param value 除数
* @return 取余运算结果
*/
operator fun BigDecimal.rem(value: Int): BigDecimal = this.remainder(BigDecimal(value))
/**
* BigDecimal取余运算符重载 - Double类型
* @param value 除数
* @return 取余运算结果
*/
operator fun BigDecimal.rem(value: Double): BigDecimal = this.remainder(BigDecimal(value.toString()))
/**
* BigDecimal取余运算符重载 - Float类型
* @param value 除数
* @return 取余运算结果
*/
operator fun BigDecimal.rem(value: Float): BigDecimal = this.remainder(BigDecimal(value.toString()))
/**
* BigDecimal取余运算符重载 - BigInteger类型
* @param value 除数
* @return 取余运算结果
*/
operator fun BigDecimal.rem(value: BigInteger): BigDecimal = this.remainder(BigDecimal(value))
/**
* 将BigDecimal转换为去除末尾零的字符串表示
* @return 去除末尾零后的字符串表示
*/
fun BigDecimal.toFlexString(): String = this.stripTrailingZeros().toPlainString()
/**
* 字符串转BigDecimal扩展函数
* @return 转换后的BigDecimal对象
*/
fun String.toBigDecimal(): BigDecimal = BigDecimal(this)
/**
* Int转BigDecimal扩展函数
* @return 转换后的BigDecimal对象
*/
fun Int.toBigDecimal(): BigDecimal = BigDecimal(this)
/**
* Byte转BigDecimal扩展函数
* @return 转换后的BigDecimal对象
*/
fun Byte.toBigDecimal(): BigDecimal = BigDecimal(this.toInt())
/**
* Short转BigDecimal扩展函数
* @return 转换后的BigDecimal对象
*/
fun Short.toBigDecimal(): BigDecimal = BigDecimal(this.toInt())
/**
* Float转BigDecimal扩展函数
* @return 转换后的BigDecimal对象
*/
fun Float.toBigDecimal(): BigDecimal = BigDecimal(this.toString())
/**
* Double转BigDecimal扩展函数
* @return 转换后的BigDecimal对象
*/
fun Double.toBigDecimal(): BigDecimal = BigDecimal(this.toString())
/**
* Long转BigDecimal扩展函数
* @return 转换后的BigDecimal对象
*/
fun Long.toBigDecimal(): BigDecimal = BigDecimal(this)
/**
* BigInteger转BigDecimal扩展函数
* @return 转换后的BigDecimal对象
*/
fun BigInteger.toBigDecimal(): BigDecimal = BigDecimal(this)
/**
* 带精度设置的字符串转BigDecimal扩展函数
* @param scale 小数位数
* @return 转换后并设置精度的BigDecimal对象
*/
fun String.toBigDecimal(scale: Int): BigDecimal = BigDecimal(this).setScale(scale)
/**
* 带精度设置的Int转BigDecimal扩展函数
* @param scale 小数位数
* @return 转换后并设置精度的BigDecimal对象
*/
fun Int.toBigDecimal(scale: Int): BigDecimal = BigDecimal(this).setScale(scale)
/**
* 带精度设置的Byte转BigDecimal扩展函数
* @param scale 小数位数
* @return 转换后并设置精度的BigDecimal对象
*/
fun Byte.toBigDecimal(scale: Int): BigDecimal = BigDecimal(this.toInt()).setScale(scale)
/**
* 带精度设置的Short转BigDecimal扩展函数
* @param scale 小数位数
* @return 转换后并设置精度的BigDecimal对象
*/
fun Short.toBigDecimal(scale: Int): BigDecimal = BigDecimal(this.toInt()).setScale(scale)
/**
* 带精度设置的Float转BigDecimal扩展函数
* @param scale 小数位数
* @return 转换后并设置精度的BigDecimal对象
*/
fun Float.toBigDecimal(scale: Int): BigDecimal = BigDecimal(this.toString()).setScale(scale)
/**
* 带精度设置的Double转BigDecimal扩展函数
* @param scale 小数位数
* @return 转换后并设置精度的BigDecimal对象
*/
fun Double.toBigDecimal(scale: Int): BigDecimal = BigDecimal(this.toString()).setScale(scale)
/**
* 带精度设置的Long转BigDecimal扩展函数
* @param scale 小数位数
* @return 转换后并设置精度的BigDecimal对象
*/
fun Long.toBigDecimal(scale: Int): BigDecimal = BigDecimal(this).setScale(scale)
/**
* 将当前 Number 对象转换为 BigDecimal 类型
*
* @return 返回与当前 Number 对象等值的 BigDecimal 实例
*/
fun Number.toBigDecimal(): BigDecimal = BigDecimal(this.toString())
/**
* 将当前 Number 对象转换为 BigInteger 类型
*
* @return 返回与当前 Number 对象等值的 BigInteger 实例
*/
fun Number.toBigInteger(): BigInteger = BigInteger(this.toString())
/**
* 将当前 Number 对象转换为指定精度的 BigDecimal 类型
*
* @param scale 指定小数点后的位数精度
* @return 返回与当前 Number 对象等值且具有指定精度的 BigDecimal 实例
*/
fun Number.toBigDecimal(scale: Int): BigDecimal = BigDecimal(this.toString()).setScale(scale)

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile QuickBaseTypeHandler.kt
* LastUpdate 2026-01-08 07:59:47
* LastUpdate 2026-01-28 10:01:35
* UpdateUser MingLiPro
*/
@ -53,7 +53,7 @@ abstract class QuickBaseTypeHandler<T> : BaseTypeHandler<T>() {
ct: CallType,
ci: Int?,
cn: String?
): T
): T?
/**
* 抽象方法用于将 Java 类型 T 设置到 PreparedStatement
@ -65,7 +65,7 @@ abstract class QuickBaseTypeHandler<T> : BaseTypeHandler<T>() {
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
abstract fun setValue(ps: PreparedStatement, index: Int, parameter: T, jdbcType: JdbcType?)
abstract fun setValue(ps: PreparedStatement, index: Int, parameter: T?, jdbcType: JdbcType?)
/**
* 实现 BaseTypeHandler setNonNullParameter 方法
@ -78,7 +78,7 @@ abstract class QuickBaseTypeHandler<T> : BaseTypeHandler<T>() {
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
override fun setNonNullParameter(ps: PreparedStatement, i: Int, parameter: T, jdbcType: JdbcType?) {
override fun setNonNullParameter(ps: PreparedStatement, i: Int, parameter: T?, jdbcType: JdbcType?) {
setValue(ps, i, parameter, jdbcType)
}
@ -92,7 +92,7 @@ abstract class QuickBaseTypeHandler<T> : BaseTypeHandler<T>() {
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
override fun getNullableResult(rs: ResultSet, columnName: String): T {
override fun getNullableResult(rs: ResultSet, columnName: String): T? {
return getValue(QuickBaseTypeHandlerValueGetter(null, rs), CallType.RESULTSET_NAME, null, columnName)
}
@ -106,7 +106,7 @@ abstract class QuickBaseTypeHandler<T> : BaseTypeHandler<T>() {
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
override fun getNullableResult(rs: ResultSet, columnIndex: Int): T {
override fun getNullableResult(rs: ResultSet, columnIndex: Int): T? {
return getValue(QuickBaseTypeHandlerValueGetter(null, rs), CallType.RESULTSET_INDEX, columnIndex, null)
}
@ -120,7 +120,7 @@ abstract class QuickBaseTypeHandler<T> : BaseTypeHandler<T>() {
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
override fun getNullableResult(cs: CallableStatement, columnIndex: Int): T {
override fun getNullableResult(cs: CallableStatement, columnIndex: Int): T? {
return getValue(QuickBaseTypeHandlerValueGetter(cs, null), CallType.CALLABLE_STATEMENT_INDEX, columnIndex, null)
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTimeTypeHandler.kt
* LastUpdate 2026-01-07 19:23:12
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
@ -30,6 +30,7 @@ import org.apache.ibatis.type.JdbcType
import org.apache.ibatis.type.MappedTypes
import java.sql.PreparedStatement
import java.sql.Timestamp
import java.sql.Types.TIMESTAMP
/**
* DateTime类型处理器用于在数据库和Java对象之间转换DateTime类型
@ -42,22 +43,30 @@ class DateTimeTypeHandler : QuickBaseTypeHandler<DateTime>() {
ct: CallType,
ci: Int?,
cn: String?
): DateTime {
): DateTime? {
val data = (when (ct) {
CallType.RESULTSET_INDEX -> vg.resultSet!!.getTimestamp(ci!!)
CallType.RESULTSET_NAME -> vg.resultSet!!.getTimestamp(cn!!)
CallType.CALLABLE_STATEMENT_INDEX -> vg.callableStatement!!.getTimestamp(ci!!)
})
if (data == null) {
return null
}
return DateTime.of(
(when (ct) {
CallType.RESULTSET_INDEX -> vg.resultSet!!.getTimestamp(ci!!)
CallType.RESULTSET_NAME -> vg.resultSet!!.getTimestamp(cn!!)
CallType.CALLABLE_STATEMENT_INDEX -> vg.callableStatement!!.getTimestamp(ci!!)
})
data
)
}
override fun setValue(
ps: PreparedStatement,
index: Int,
parameter: DateTime,
parameter: DateTime?,
jdbcType: JdbcType?
) {
if (parameter == null) {
ps.setNull(index, TIMESTAMP)
return
}
ps.setTimestamp(index, Timestamp.valueOf(parameter.toLocalDateTime()))
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MysqlUUIDBinaryTypeHandler.kt
* LastUpdate 2026-01-07 19:30:31
* LastUpdate 2026-01-28 10:08:46
* UpdateUser MingLiPro
*/
@ -59,19 +59,29 @@ class MysqlUUIDBinaryTypeHandler : QuickBaseTypeHandler<UUID>() {
override fun getValue(
vg: QuickBaseTypeHandlerValueGetter, ct: CallType, ci: Int?, cn: String?
): UUID {
): UUID? {
val data: ByteArray? = when (ct) {
CallType.RESULTSET_NAME -> vg.resultSet!!.getBytes(cn!!)
CallType.RESULTSET_INDEX -> vg.resultSet!!.getBytes(ci!!)
CallType.CALLABLE_STATEMENT_INDEX -> vg.resultSet!!.getBytes(ci!!)
}
if (data == null) {
return null
}
return toUUID(
when (ct) {
CallType.RESULTSET_NAME -> vg.resultSet!!.getBytes(cn!!)
CallType.RESULTSET_INDEX -> vg.resultSet!!.getBytes(ci!!)
CallType.CALLABLE_STATEMENT_INDEX -> vg.resultSet!!.getBytes(ci!!)
}
)!!
data
)
}
override fun setValue(
ps: PreparedStatement, index: Int, parameter: UUID, jdbcType: JdbcType?
ps: PreparedStatement, index: Int, parameter: UUID?, jdbcType: JdbcType?
) {
if (parameter == null) {
ps.setNull(index, java.sql.Types.BINARY)
return
}
ps.setBytes(index, toByteArray(parameter))
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile PgsqlUUIDTypeHandler.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatis.typehandler.pgsql.uuid
import com.mingliqiye.utils.mybatis.CallType
import com.mingliqiye.utils.mybatis.QuickBaseTypeHandler
import com.mingliqiye.utils.mybatis.QuickBaseTypeHandlerValueGetter
import com.mingliqiye.utils.uuid.UUID
import org.apache.ibatis.type.JdbcType
import org.apache.ibatis.type.MappedTypes
import java.sql.PreparedStatement
@MappedTypes(UUID::class)
class PgsqlUUIDTypeHandler : QuickBaseTypeHandler<UUID>() {
override fun getValue(
vg: QuickBaseTypeHandlerValueGetter, ct: CallType, ci: Int?, cn: String?
): UUID? {
val data: String? = when (ct) {
CallType.RESULTSET_INDEX ->
vg.resultSet!!.getString(ci!!)
CallType.RESULTSET_NAME ->
vg.resultSet!!.getString(cn)
CallType.CALLABLE_STATEMENT_INDEX ->
vg.callableStatement!!.getString(cn)
}
if (data == null) {
return null
}
return UUID.of(data)
}
override fun setValue(
ps: PreparedStatement, index: Int, parameter: UUID?, jdbcType: JdbcType?
) {
if (parameter == null) {
ps.setNull(index, jdbcType?.TYPE_CODE ?: JdbcType.JAVA_OBJECT.TYPE_CODE)
return
}
ps.setObject(index, parameter.getUuid())
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile QueryWrapper.kt
* LastUpdate 2026-02-03 12:03:27
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatisplus
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import com.baomidou.mybatisplus.extension.kotlin.KtUpdateChainWrapper
import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper
/**
* BaseMapperQuery接口扩展了BaseMapper提供了通用的查询包装器功能
*
* @param T 实体类类型
*/
interface BaseMapperQuery<T> : BaseMapper<T> {
/**
* 创建并返回一个新的QueryWrapper实例
*
* @return QueryWrapper<T> 返回类型化的查询包装器实例
*/
fun queryWrapper() = QueryWrapper<T>()
/**
* 创建并返回一个新的UpdateWrapper实例
*
* @return UpdateWrapper<T> 返回类型化的更新包装器实例
*/
fun updateWrapper() = UpdateWrapper<T>()
/**
* 创建并返回一个新的LambdaQueryWrapper实例
*
* @return LambdaQueryWrapper<T> 返回类型化的Lambda查询包装器实例
*/
fun lambdaQueryWrapper() = LambdaQueryWrapper<T>()
/**
* 创建并返回一个新的LambdaUpdateWrapper实例
*
* @return LambdaUpdateWrapper<T> 返回类型化的Lambda更新包装器实例
*/
fun lambdaUpdateWrapper() = LambdaUpdateWrapper<T>()
companion object {
/**
* 创建并返回一个新的QueryWrapper实例
*
* @return QueryWrapper<T> 返回类型化的查询包装器实例
*/
inline fun <reified T> BaseMapper<T>.queryWrapper(): QueryWrapper<T> = QueryWrapper<T>()
/**
* 创建并返回一个新的UpdateWrapper实例
*
* @return UpdateWrapper<T> 返回类型化的更新包装器实例
*/
inline fun <reified T> BaseMapper<T>.updateWrapper(): UpdateWrapper<T> = UpdateWrapper<T>()
/**
* 创建并返回一个新的LambdaQueryWrapper实例
*
* @return LambdaQueryWrapper<T> 返回类型化的Lambda查询包装器实例
*/
inline fun <reified T> BaseMapper<T>.lambdaQueryWrapper(): LambdaQueryWrapper<T> =
LambdaQueryWrapper<T>(T::class.java)
/**
* 创建并返回一个新的LambdaUpdateWrapper实例
*
* @return LambdaUpdateWrapper<T> 返回类型化的Lambda更新包装器实例
*/
inline fun <reified T> BaseMapper<T>.lambdaUpdateWrapper(): LambdaUpdateWrapper<T> =
LambdaUpdateWrapper<T>(T::class.java)
/**
* 创建并返回一个新的KtUpdateWrapper实例
*
* @return KtUpdateWrapper<T> 返回类型化的Kotlin更新包装器实例
*/
inline fun <reified T : Any> BaseMapper<T>.ktUpdateWrapper(): KtUpdateWrapper<T> =
KtUpdateWrapper(T::class.java)
/**
* 创建并返回一个新的KtQueryWrapper实例
*
* @return KtQueryWrapper<T> 返回类型化的Kotlin查询包装器实例
*/
inline fun <reified T : Any> BaseMapper<T>.ktQueryWrapper(): KtQueryWrapper<T> = KtQueryWrapper(T::class.java)
/**
* 创建并返回一个新的KtUpdateChainWrapper实例
*
* @return KtUpdateChainWrapper<T> 返回类型化的Kotlin更新链式包装器实例
*/
inline fun <reified T : Any> BaseMapper<T>.ktUpdateChainWrapper(): KtUpdateChainWrapper<T> =
KtUpdateChainWrapper(T::class.java)
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile QueryWrapper.kt
* LastUpdate 2026-01-14 13:00:31
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatisplus
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper
/**
* BaseMapperQuery接口扩展了BaseMapper提供了通用的查询包装器功能
*
* @param T 实体类类型
*/
interface BaseMapperQuery<T> : BaseMapper<T> {
/**
* 创建并返回一个新的QueryWrapper实例
*
* @return QueryWrapper<T> 返回类型化的查询包装器实例
*/
fun queryWrapper(): QueryWrapper<T> {
return QueryWrapper<T>()
}
/**
* 创建并返回一个新的UpdateWrapper实例
*
* @return UpdateWrapper<T> 返回类型化的更新包装器实例
*/
fun updateWrapper(): UpdateWrapper<T> {
return UpdateWrapper<T>()
}
/**
* 创建并返回一个新的LambdaQueryWrapper实例
*
* @return LambdaQueryWrapper<T> 返回类型化的Lambda查询包装器实例
*/
fun lambdaQueryWrapper(): LambdaQueryWrapper<T> {
return LambdaQueryWrapper<T>()
}
/**
* 创建并返回一个新的LambdaUpdateWrapper实例
*
* @return LambdaUpdateWrapper<T> 返回类型化的Lambda更新包装器实例
*/
fun lambdaUpdateWrapper(): LambdaUpdateWrapper<T> {
return LambdaUpdateWrapper<T>()
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ClientScheduleReconnect.kt
* LastUpdate 2026-01-08 10:45:37
* LastUpdate 2026-01-20 08:10:15
* UpdateUser MingLiPro
*/
@ -28,26 +28,128 @@ import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelFutureListener
import java.util.concurrent.TimeUnit
abstract class ClientScheduleReconnect(
protected val bootstrap: Bootstrap,
protected var delay: Long = 10L,
protected var timeUnit: TimeUnit = TimeUnit.SECONDS
) {
protected var isStop = false
protected var channel: Channel? = null
/**
* 客户端调度重连抽象类用于处理Netty客户端的自动重连机制
* @param bootstrap Netty引导配置对象
* @param delay 重连延迟时间默认为10秒
* @param timeUnit 延迟时间单位默认为秒
*/
abstract class ClientScheduleReconnect {
private val bootstrap: Bootstrap
private var delay: Long = 10L
private var timeUnit: TimeUnit = TimeUnit.SECONDS
constructor() : this(Bootstrap())
constructor(bootstrap: Bootstrap) : this(bootstrap, 10L)
constructor(bootstrap: Bootstrap, delay: Long) : this(bootstrap, delay, TimeUnit.SECONDS)
constructor(bootstrap: Bootstrap, delay: Long, timeUnit: TimeUnit) {
this.bootstrap = bootstrap
this.delay = delay
this.timeUnit = timeUnit
}
open fun start() {
initBootstrap(bootstrap)
connect()
}
/**
* 获取Netty引导配置对象
* @return Bootstrap 引导配置对象
*/
open fun getBootstrap(): Bootstrap = bootstrap
/**
* 获取当前连接的通道
* @return Channel? 当前连接的通道可能为空
*/
open fun getChannel(): Channel? = channel
/**
* 获取时间单位
* @return TimeUnit 时间单位
*/
open fun getTimeUnit(): TimeUnit = timeUnit
/**
* 设置时间单位
* @param timeUnit 新的时间单位
*/
open fun setTimeUnit(timeUnit: TimeUnit) {
this.timeUnit = timeUnit
}
/**
* 获取重连延迟时间
* @return Long 延迟时间
*/
open fun getDelay(): Long = delay
/**
* 设置重连延迟时间
* @param delay 新的延迟时间
*/
open fun setDelay(delay: Long) {
this.delay = delay
}
/**
* 检查是否已连接
* @return Boolean 是否已连接
*/
open fun isConnected(): Boolean = channel?.isActive ?: false
private var isStop = false
private var channel: Channel? = null
/**
* 连接成功时的日志记录回调
* @param channel 成功连接的通道
*/
abstract fun onConnectedLog(channel: Channel)
/**
* 连接失败时的日志记录回调
* @param cause 连接失败的原因可能为空
*/
abstract fun onConnectFailedLog(cause: Throwable?)
/**
* 停止连接时的日志记录回调
*/
abstract fun onStoppedLog()
abstract fun onConnectClosed()
/**
* 执行连接操作
* @return ChannelFuture 连接结果的异步操作对象
*/
abstract fun doConnect(): ChannelFuture
/**
* 初始化引导配置
* @param bootstrap 要初始化的引导配置对象
*/
open fun initBootstrap(bootstrap: Bootstrap) {
}
/**
* 更新重连延迟配置
* @param newDelay 新的延迟时间必须大于0
* @param newTimeUnit 新的时间单位
* @throws IllegalArgumentException 当newDelay小于等于0时抛出
*/
open fun updateReconnectDelay(newDelay: Long, newTimeUnit: TimeUnit) {
require(newDelay > 0) { "Delay must be positive" }
this.delay = newDelay
this.timeUnit = newTimeUnit
}
/**
* 执行连接操作设置连接完成监听器
*/
open fun connect() {
doConnect().addListener(object : ChannelFutureListener {
override fun operationComplete(future: ChannelFuture) {
@ -60,24 +162,33 @@ abstract class ClientScheduleReconnect(
})
}
/**
* 停止连接并标记为停止状态
*/
open fun stop() {
isStop = true
disConnect()
}
/**
* 断开当前连接并清理通道引用
*/
open fun disConnect() {
channel?.close()
channel = null
}
/**
* 调度下一次重连操作
*/
open fun scheduleReconnect() {
// 检查是否已停止,如果已停止则记录日志并返回
if (isStop) {
onStoppedLog()
return
}
bootstrap.config().group().schedule({
// 再次检查停止状态,避免在调度期间被停止
if (isStop) {
onStoppedLog()
return@schedule
@ -86,20 +197,27 @@ abstract class ClientScheduleReconnect(
}, delay, timeUnit)
}
/**
* 处理连接成功的逻辑
* @param channel 成功连接的通道
*/
open fun onConnected(channel: Channel) {
this.channel = channel
onConnectedLog(channel)
// 监听通道关闭事件,以便在断开后重新调度连接
channel.closeFuture().addListener { _ ->
scheduleReconnect()
onConnectClosed()
}
}
/**
* 处理连接失败的逻辑
* @param cause 连接失败的原因
*/
open fun onConnectFailed(cause: Throwable?) {
onConnectFailedLog(cause)
scheduleReconnect()
}
open fun isConnected(): Boolean {
return channel?.isActive ?: false
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NamedNioEventLoopGroup.kt
* LastUpdate 2026-02-05 10:20:31
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.netty
import com.mingliqiye.utils.system.availableProcessors
import io.netty.channel.nio.NioEventLoopGroup
import java.text.MessageFormat
/**
* 命名的NIO事件循环组继承自NioEventLoopGroup
* 用于创建具有自定义命名规则的线程池便于线程管理和调试
*
* @param name 线程池的名称默认为"NioEventLoopGroup"
* @param template 线程名称的格式模板默认为"{0}-{2}-{3}"{0} 名字 {1} 类名 {2} 线程池序号 {3} 线程池内线程的序号
* @param nThreads 线程数量默认为核心数的2倍
*/
open class NamedNioEventLoopGroup(
val template: String = "{0}-{2}-{3}",
val name: String = "NioEventLoopGroup",
nThreads: Int = availableProcessors * 2
) :
NioEventLoopGroup(
nThreads,
NamedThreadFactory(
getName = { a, b, c -> MessageFormat.format(template, name, a, b, c) }
)
)

View File

@ -16,17 +16,19 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NamedThreadFactory.kt
* LastUpdate 2026-01-08 13:21:00
* LastUpdate 2026-01-31 21:05:02
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.netty
import io.netty.util.concurrent.FastThreadLocalThread
import java.util.concurrent.ThreadFactory
import java.util.concurrent.atomic.AtomicInteger
/**
* 命名线程工厂类用于创建具有自定义名称的线程
* @param getName 用于获取线程名称的回调函数接口
*/
open class NamedThreadFactory(private val getName: NamedThreadFactoryNameGetter) : ThreadFactory {
companion object {
@ -34,15 +36,33 @@ open class NamedThreadFactory(private val getName: NamedThreadFactoryNameGetter)
@JvmStatic
private val allThreadPoolNumber = AtomicInteger(0)
/**
* 函数式接口用于定义线程名称生成规则
*/
@FunctionalInterface
fun interface NamedThreadFactoryNameGetter {
/**
* 获取线程名称
* @param clazz 线程工厂类
* @param poolNumber 线程池编号
* @param threadNumber 线程编号
* @return 生成的线程名称
*/
fun getName(clazz: Class<out NamedThreadFactory>, poolNumber: Int, threadNumber: Int): String
}
/**
* 默认的线程名称生成器
*/
@JvmStatic
val defaultGetName =
NamedThreadFactoryNameGetter { clazz, poolNumber, threadNumber -> "${clazz.simpleName}-$poolNumber-$threadNumber" }
/**
* 创建命名线程工厂实例
* @param name 线程名称前缀
* @return NamedThreadFactory实例
*/
@JvmStatic
fun of(name: String): NamedThreadFactory {
return NamedThreadFactory { a, b, c ->
@ -50,24 +70,42 @@ open class NamedThreadFactory(private val getName: NamedThreadFactoryNameGetter)
}
}
/**
* 创建命名线程工厂实例
* @param getter 线程名称生成器默认使用defaultGetName
* @return NamedThreadFactory实例
*/
@JvmStatic
fun of(getter: NamedThreadFactoryNameGetter = defaultGetName): NamedThreadFactory {
return NamedThreadFactory(getter)
}
}
// 当前线程工厂的线程计数器
private val threadNumber = AtomicInteger(0)
// 全局线程池计数器,用于标识不同的线程池
private val threadPoolNumber = allThreadPoolNumber.addAndGet(1)
/**
* 获取线程名称
* @param clazz 线程工厂类
* @param poolNumber 线程池编号
* @param threadNumber 线程编号
* @return 生成的线程名称
*/
open fun getThreadName(clazz: Class<out NamedThreadFactory>, poolNumber: Int, threadNumber: Int) =
getName.getName(clazz, poolNumber, threadNumber)
/**
* 创建新线程
* @param r 线程执行的任务
* @return 新创建的线程对象
*/
override fun newThread(r: Runnable): Thread {
return FastThreadLocalThread(
null,
r,
getThreadName(this.javaClass, threadPoolNumber, threadNumber.addAndGet(1))
)
return Thread(r).let {
it.name = getThreadName(this.javaClass, threadPoolNumber, threadNumber.addAndGet(1))
it
}
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NettyUtils.kt
* LastUpdate 2026-01-07 10:00:31
* LastUpdate 2026-01-31 20:50:41
* UpdateUser MingLiPro
*/
@ -24,9 +24,15 @@
package com.mingliqiye.utils.netty
import com.mingliqiye.utils.functions.P1Function
import io.netty.bootstrap.Bootstrap
import io.netty.bootstrap.ServerBootstrap
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.Channel
import io.netty.channel.ChannelInitializer
import java.nio.ByteBuffer
import java.nio.charset.Charset
/**
* 将ByteBuffer转换为Netty的ByteBuf对象
@ -79,3 +85,206 @@ fun ByteBuf.toByteArray(): ByteArray {
fun ByteArray.toByteBuf(): ByteBuf {
return Unpooled.wrappedBuffer(this)
}
/**
* 为ServerBootstrap配置子通道初始化器
*
* 该函数用于设置ServerBootstrap的childHandler当新的客户端连接建立时
* 会调用提供的函数来初始化子通道
*
* @receiver ServerBootstrap 需要配置的服务器引导程序实例
* @param funa 用于初始化通道的回调函数接收Channel作为参数
* @return ServerBootstrap 返回当前实例以支持链式调用
*/
@JvmName("channelInit")
fun ServerBootstrap.channelInitializer(funa: P1Function<Channel>): ServerBootstrap {
this.childHandler(object : ChannelInitializer<Channel>() {
override fun initChannel(ch: Channel) {
funa.call(ch)
}
})
return this
}
/**
* 为Bootstrap配置通道初始化器
*
* 该函数用于设置Bootstrap的handler当客户端连接建立时
* 会调用提供的函数来初始化通道
*
* @receiver Bootstrap 需要配置的客户端引导程序实例
* @param funa 用于初始化通道的回调函数接收Channel作为参数
* @return Bootstrap 返回当前实例以支持链式调用
*/
@JvmName("channelInit")
fun Bootstrap.channelInitializer(funa: P1Function<Channel>): Bootstrap {
this.handler(object : ChannelInitializer<Channel>() {
override fun initChannel(ch: Channel) {
funa.call(ch)
}
})
return this
}
/**
* 将字符串转换为Netty的ByteBuf对象
*
* @receiver String 需要转换的原始字符串
* @param charset 字符编码默认UTF-8
* @return ByteBuf 转换后的Netty ByteBuf对象
*/
fun String.toByteBuf(charset: Charset = Charsets.UTF_8): ByteBuf {
return Unpooled.wrappedBuffer(this.toByteArray(charset))
}
/**
* 将ByteBuf转换为字符串
*
* @receiver ByteBuf 需要转换的ByteBuf对象
* @param charset 字符编码默认UTF-8
* @return String 转换后的字符串
*/
fun ByteBuf.toString(charset: Charset = Charsets.UTF_8): String {
val bytes = this.toByteArray()
return String(bytes, charset)
}
/**
* 将Int转换为ByteBuf
*
* @receiver Int 需要转换的整数值
* @return ByteBuf 转换后的Netty ByteBuf对象包含4字节的整数数据
*/
fun Int.toByteBuf(): ByteBuf {
val buffer = Unpooled.buffer(4)
buffer.writeInt(this)
return buffer
}
/**
* 将Long转换为ByteBuf
*
* @receiver Long 需要转换的长整数值
* @return ByteBuf 转换后的Netty ByteBuf对象包含8字节的长整数数据
*/
fun Long.toByteBuf(): ByteBuf {
val buffer = Unpooled.buffer(8)
buffer.writeLong(this)
return buffer
}
/**
* 将ByteBuf转换为Int
*
* @receiver ByteBuf 需要转换的ByteBuf对象
* @return Int 从ByteBuf中读取的整数值
*/
fun ByteBuf.toInt(): Int {
return this.readInt()
}
/**
* 将ByteBuf转换为Long
*
* @receiver ByteBuf 需要转换的ByteBuf对象
* @return Long 从ByteBuf中读取的长整数值
*/
fun ByteBuf.toLong(): Long {
return this.readLong()
}
/**
* 安全地获取ByteBuf的字节数组避免堆外内存访问异常
*
* @receiver ByteBuf 需要转换的ByteBuf对象
* @return ByteArray 转换后的字节数组
*/
fun ByteBuf.safeToByteArray(): ByteArray {
return if (this.hasArray()) {
// 如果是堆内内存,直接获取数组
this.array().copyOfRange(this.arrayOffset(), this.arrayOffset() + this.readableBytes())
} else {
// 如果是堆外内存使用readBytes
val array = ByteArray(this.readableBytes())
this.getBytes(this.readerIndex(), array)
array
}
}
/**
* 获取ByteBuf的十六进制字符串表示
*
* @receiver ByteBuf 需要转换的ByteBuf对象
* @return String 十六进制字符串表示
*/
fun ByteBuf.toHexString(): String {
val array = this.safeToByteArray()
return array.joinToString("") { "%02x".format(it) }
}
/**
* 合并多个ByteBuf为一个
*
* @param byteBufs 需要合并的ByteBuf数组
* @return ByteBuf 合并后的ByteBuf对象
*/
fun combineByteBuf(vararg byteBufs: ByteBuf): ByteBuf {
val totalCapacity = byteBufs.sumOf { it.readableBytes() }
val combined = Unpooled.buffer(totalCapacity)
byteBufs.forEach { combined.writeBytes(it.duplicate()) }
return combined
}
/**
* 安全关闭通道
*/
fun Channel.closeSafely() {
if (isOpen) {
close()
}
}
/**
* 检查通道是否处于活跃状态
*/
val Channel.isActive: Boolean
get() = isOpen && isActive
/**
* 异步写入数据到通道
*
* @param data 需要写入的数据
*/
fun Channel.writeAndFlushAsync(data: Any) {
writeAndFlush(data).addListener { future ->
if (!future.isSuccess) {
// 可以在这里添加错误处理逻辑
println("Write failed: ${future.cause()}")
}
}
}
/**
* 安全释放ByteBuf资源
*
* @receiver ByteBuf 需要释放的ByteBuf对象
* @return Boolean 释放操作是否成功
*/
fun ByteBuf.releaseSafely(): Boolean {
return if (refCnt() > 0) release() else true
}
/**
* 尝试释放ByteBuf资源不抛出异常
*
* @receiver ByteBuf 需要释放的ByteBuf对象
* @return Boolean 释放操作是否成功
*/
fun ByteBuf.tryRelease(): Boolean {
return try {
if (refCnt() > 0) release() else false
} catch (e: Exception) {
false
}
}

View File

@ -1,509 +0,0 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile AddressPort.kt
* LastUpdate 2026-01-06 14:03:47
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network
import com.mingliqiye.utils.string.join
import java.io.Serializable
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.UnknownHostException
import java.nio.ByteBuffer
import java.util.regex.Pattern
/**
* 网络地址类用于表示一个网络地址IP或域名并提供相关操作
* 支持IPv4和IPv6地址的解析与验证
*
* @author MingLiPro
*/
class NetworkAddress private constructor(domip: String) : Serializable {
/**
* IPv6标识
*/
companion object {
const val IPV6 = 6
/**
* IPv4标识
*/
const val IPV4 = 4
/**
* IPv4地址正则表达式
*/
private const val IPV4REG =
"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" + "(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$"
/**
* 编译后的IPv4地址匹配模式
*/
private val IPV4_PATTERN = Pattern.compile(IPV4REG)
/**
* IPv6地址正则表达式
*/
private const val IPV6REG =
"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" + "^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" + "|" + "^(::)$|" + "^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" + "^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$"
/**
* 编译后的IPv6地址匹配模式
*/
private val IPV6_PATTERN = Pattern.compile(IPV6REG)
/**
* 静态工厂方法创建 NetworkAddress 实例
*
* @param domip 可能是IP地址或域名的字符串
* @return 新建的 NetworkAddress 实例
*/
@JvmStatic
fun of(domip: String): NetworkAddress {
return NetworkAddress(domip)
}
@JvmStatic
fun ofIpv4(byteBuffer: ByteBuffer): NetworkAddress {
val byteArray = ByteArray(4)
byteBuffer.get(byteArray)
return ofIpv4(byteArray)
}
@JvmStatic
fun ofIpv4(byteArray: ByteArray): NetworkAddress {
return of(".".join(byteArray.map {
(it.toInt() and 0xFF).toString()
}))
}
/**
* 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例
*
* @param inetAddress InetAddress 对象
* @return 新建的 NetworkAddress 实例
*/
@JvmStatic
fun of(inetAddress: InetAddress): NetworkAddress {
return NetworkAddress(inetAddress.hostAddress)
}
/**
* 从DNS服务器解析域名获取对应的IP地址
*
* @param domain 域名
* @return 解析出的第一个IP地址
* @throws UnknownHostException 如果域名无法解析
*/
@JvmStatic
@Throws(UnknownHostException::class)
fun getHostIp(domain: String): String {
val addresses = InetAddress.getAllByName(domain.trim())
return addresses[0].hostAddress
}
/**
* 检测给定字符串是否为有效的IPv4或IPv6地址
*
* @param ip 要检测的IP地址字符串
* @return 4 表示IPv46 表示IPv6
* @throws NetworkException 如果IP格式无效
*/
@JvmStatic
fun testIp(ip: String?): Int {
if (ip == null) {
throw NetworkException("IP地址不能为null")
}
val trimmedIp = ip.trim()
// 判断是否匹配IPv4格式
if (IPV4_PATTERN.matcher(trimmedIp).matches()) {
return IPV4
}
// 判断是否匹配IPv6格式
if (IPV6_PATTERN.matcher(trimmedIp).matches()) {
return IPV6
}
// 不符合任一格式时抛出异常
throw NetworkException(
"[$ip] 不是有效的IPv4或IPv6地址"
)
}
}
/**
* IP地址类型4 表示 IPv46 表示 IPv6
*/
var iPv: Int = 0
private set
/**
* IP地址字符串
*/
var ip: String? = null
private set
/**
* 域名如果输入的是域名
*/
private var domain: String? = null
/**
* 标识是否是域名解析来的IP
*/
private var isdom = false
/**
* 构造方法根据传入的字符串判断是IP地址还是域名并进行相应处理
*
* @param domip 可能是IP地址或域名的字符串
*/
init {
try {
// 尝试将输入识别为IP地址
this.iPv = testIp(domip)
this.ip = domip
} catch (e: NetworkException) {
try {
// 如果不是有效IP则尝试作为域名解析
val ips = getHostIp(domip)
this.iPv = testIp(ips)
this.ip = ips
this.isdom = true
this.domain = domip
} catch (ex: UnknownHostException) {
throw NetworkException(ex)
}
}
}
/**
* 将IPv4地址转换为字节数组
*
* @return 返回表示IPv4地址的4字节数组
* @throws NetworkException 当当前地址不是IPv4地址时抛出异常
*/
fun toIpv4ByteArray(): ByteArray {
// 验证地址类型是否为IPv4
if (iPv != IPV4) {
throw NetworkException("该地址 不是IPv4地址")
}
// 将IP地址字符串按点分割转换为整数并进行位运算处理最后转为字节数组
return ip!!.split(".").map { it.toInt() and 0xFF }.map { it.toByte() }.toByteArray()
}
/**
* 将IPv4地址写入到指定的ByteBuffer中
*
* @param byteBuffer 要写入的ByteBuffer对象IPv4地址将以字节数组形式写入到该缓冲区
* @return 返回写入了IPv4地址的ByteBuffer对象便于链式调用
*/
fun writeIpv4ToByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
return byteBuffer.put(toIpv4ByteArray())
}
/**
* 将当前 NetworkAddress 转换为 InetAddress 对象
*
* @return InetAddress 对象
*/
fun toInetAddress(): InetAddress {
try {
return InetAddress.getByName(if (ip != null) ip else domain)
} catch (e: UnknownHostException) {
throw RuntimeException(e)
}
}
/**
* 返回 NetworkAddress 的字符串表示形式
*
* @return 字符串表示
*/
override fun toString(): String {
return if (isdom) "NetworkAddress(IP='$ip',type='$iPv',domain='$domain')"
else "NetworkAddress(IP='$ip',type='$iPv')"
}
}
class NetworkPort : Serializable {
val port: Int
constructor(port: Int) {
testPort(port)
this.port = port
}
fun toByteArray(): ByteArray {
val byteArray = ByteArray(2)
byteArray[0] = (port shr 8 and 0xFF).toByte()
byteArray[1] = (port and 0xFF).toByte()
return byteArray
}
fun writeToByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
return byteBuffer.put(toByteArray())
}
companion object {
/**
* 创建NetworkPort实例
* @param port 端口号
* @return NetworkPort实例
*/
@JvmStatic
fun of(port: Int): NetworkPort {
return NetworkPort(port)
}
/**
* 从字节数组创建NetworkPort实例
* @param byteArray 包含端口信息的字节数组长度至少为2
* @return NetworkPort实例
*/
@JvmStatic
fun of(byteArray: ByteArray): NetworkPort {
// 将字节数组的前两个字节转换为端口号
val port = ((byteArray[0].toInt() and 0xFF) shl 8) or (byteArray[1].toInt() and 0xFF)
return of(port)
}
/**
* 从ByteBuffer创建NetworkPort实例
* @param byteBuffer 包含端口信息的ByteBuffer
* @return NetworkPort实例
*/
@JvmStatic
fun of(byteBuffer: ByteBuffer): NetworkPort {
val byteArray = ByteArray(2)
byteBuffer.get(byteArray)
return of(byteArray)
}
@JvmStatic
fun testPort(port: Int) {
// 验证端口号范围是否在0-65535之间
if (port !in 0..65535) {
throw NetworkException("$port 不是正确的端口号")
}
}
}
}
class NetworkException : RuntimeException {
/**
* 构造一个带有指定详细消息的网络异常
*
* @param message 异常的详细消息
*/
constructor(message: String?) : super(message)
/**
* 构造一个网络异常指定原因异常
*
* @param e 导致此异常的原因异常
*/
constructor(e: Exception?) : super(e)
}
/**
* IP和端口聚集类用于封装网络地址与端口信息
* 该类提供了与InetSocketAddress之间的相互转换功能
*
* @author MingLiPro
* @see java.net.InetSocketAddress
*/
class NetworkEndpoint private constructor(
val networkAddress: NetworkAddress, val networkPort: NetworkPort
) : Serializable {
companion object {
/**
* 根据给定的InetSocketAddress对象创建NetworkEndpoint实例
*
* @param address InetSocketAddress对象
* @return 新建的NetworkEndpoint实例
* @see java.net.InetSocketAddress
*/
@JvmStatic
fun of(address: InetSocketAddress): NetworkEndpoint {
return NetworkEndpoint(
NetworkAddress.of(address.hostString), NetworkPort(address.port)
)
}
/**
* 根据主机名或IP字符串和端口号创建NetworkEndpoint实例
*
* @param s 主机名或IP地址字符串
* @param i 端口号
* @return 新建的NetworkEndpoint实例
*/
@JvmStatic
fun of(s: String, i: Int): NetworkEndpoint {
val networkAddress = NetworkAddress.of(s)
val networkPort = NetworkPort(i)
return NetworkEndpoint(networkAddress, networkPort)
}
/**
* 从字节数组创建IPv4网络端点
*
* @param byteArray 包含IPv4地址和端口信息的字节数组前4个字节表示IP地址后2个字节表示端口号
* @return NetworkEndpoint对象封装了IPv4地址和端口信息
*/
@JvmStatic
fun ofIpv4(byteArray: ByteArray): NetworkEndpoint {
// 提取前4个字节作为IP地址
val address = ByteArray(4) {
byteArray[it]
}
// 提取后2个字节作为端口号
val portInt = ByteArray(2) {
byteArray[it + 4]
}
return NetworkEndpoint(NetworkAddress.ofIpv4(address), NetworkPort.of(portInt))
}
/**
* 从字节缓冲区创建IPv4网络端点实例
*
* 该方法从给定的ByteBuffer中读取数据构建一个包含IPv4地址和端口的网络端点对象
* 该方法按照协议顺序从缓冲区中读取IPv4地址数据和端口数据
*
* @param byteBuffer 包含IPv4地址和端口数据的字节缓冲区缓冲区中的数据应按照先地址后端口的顺序排列
* @return 返回一个NetworkEndpoint实例包含从缓冲区解析出的IPv4地址和端口信息
*/
@JvmStatic
fun ofIpv4(byteBuffer: ByteBuffer): NetworkEndpoint {
return NetworkEndpoint(
NetworkAddress.ofIpv4(byteBuffer),
NetworkPort.of(byteBuffer)
)
}
/**
* 根据"host:port"格式的字符串创建NetworkEndpoint实例
* 例如"127.0.0.1:8080"
*
* @param s "host:port"格式的字符串
* @return 新建的NetworkEndpoint实例
*/
@JvmStatic
fun of(s: String): NetworkEndpoint {
val lastColonIndex = s.lastIndexOf(':')
return of(
s.take(lastColonIndex), s.substring(lastColonIndex + 1).toInt()
)
}
}
/**
* 将当前NetworkEndpoint转换为InetSocketAddress对象
*
* @return 对应的InetSocketAddress对象
* @see InetSocketAddress
*/
fun toInetSocketAddress(): InetSocketAddress {
return InetSocketAddress(
networkAddress.toInetAddress(), networkPort.port
)
}
/**
* 将当前NetworkEndpoint转换为"host:port"格式的字符串
* 例如"127.0.0.1:25563"
*
* @return 格式化后的字符串
*/
fun toHostPortString(): String {
return "${networkAddress.ip}:${networkPort.port}"
}
/**
* 返回NetworkEndpoint的详细字符串表示形式
* 格式NetworkEndpoint(IP=...,Port=...,Endpoint=...)
*
* @return 包含详细信息的字符串
*/
override fun toString(): String {
return "NetworkEndpoint(IP=${networkAddress.ip},Port=${networkPort.port})"
}
/**
* 获取主机名或IP地址字符串
*
* @return 主机名或IP地址
*/
fun host(): String {
return networkAddress.ip ?: ""
}
/**
* 获取端口号
*
* @return 端口号
*/
fun port(): Int {
return networkPort.port
}
/**
* 将网络地址和端口转换为IPv4字节数组
*
* 该函数将当前对象的网络地址转换为IPv4字节数组并与端口字节数组合并
* 生成一个包含6个字节的数组其中前4个字节为IPv4地址后2个字节为端口号
*
* @return ByteArray 包含6个字节的数组前4个字节为IPv4地址后2个字节为端口号
*/
fun toIpv4ByteArray(): ByteArray {
val ipv4ByteArray = networkAddress.toIpv4ByteArray()
val portByteArray = networkPort.toByteArray()
// 构建包含IPv4地址和端口的6字节数组
val byteArray = ByteArray(6) {
if (it < 4) {
ipv4ByteArray[it]
} else {
portByteArray[it - 4]
}
}
return byteArray
}
/**
* 将IPv4地址和端口信息写入字节缓冲区
*
* 该函数依次将网络地址的IPv4表示和网络端口写入到指定的字节缓冲区中
*
* @param byteBuffer 要写入数据的目标字节缓冲区
*/
fun writeIpv4toByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
networkAddress.writeIpv4ToByteBuffer(byteBuffer)
networkPort.writeToByteBuffer(byteBuffer)
return byteBuffer
}
}

View File

@ -0,0 +1,247 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NetworkAddress.kt
* LastUpdate 2026-02-05 10:20:31
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network
import com.mingliqiye.utils.string.join
import java.io.Serializable
import java.net.InetAddress
import java.net.UnknownHostException
import java.nio.ByteBuffer
import java.util.regex.Pattern
/**
* 网络地址类用于表示一个网络地址IP或域名并提供相关操作
* 支持IPv4和IPv6地址的解析与验证
*
* @author MingLiPro
*/
class NetworkAddress private constructor(domip: String) : Serializable {
/**
* IPv6标识
*/
companion object {
const val IPV6 = 6
/**
* IPv4标识
*/
const val IPV4 = 4
/**
* IPv4地址正则表达式
*/
private const val IPV4REG =
"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" + "(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$"
/**
* 编译后的IPv4地址匹配模式
*/
private val IPV4_PATTERN = Pattern.compile(IPV4REG)
/**
* IPv6地址正则表达式
*/
private const val IPV6REG =
"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" + "^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" + "|" + "^(::)$|" + "^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" + "^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$"
/**
* 编译后的IPv6地址匹配模式
*/
private val IPV6_PATTERN = Pattern.compile(IPV6REG)
/**
* 静态工厂方法创建 NetworkAddress 实例
*
* @param domip 可能是IP地址或域名的字符串
* @return 新建的 NetworkAddress 实例
*/
@JvmStatic
fun of(domip: String): NetworkAddress {
return NetworkAddress(domip)
}
@JvmStatic
fun ofIpv4(byteBuffer: ByteBuffer): NetworkAddress {
val byteArray = ByteArray(4)
byteBuffer.get(byteArray)
return ofIpv4(byteArray)
}
@JvmStatic
fun ofIpv4(byteArray: ByteArray): NetworkAddress {
return of(".".join(byteArray.map {
(it.toInt() and 0xFF).toString()
}))
}
/**
* 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例
*
* @param inetAddress InetAddress 对象
* @return 新建的 NetworkAddress 实例
*/
@JvmStatic
fun of(inetAddress: InetAddress): NetworkAddress {
return NetworkAddress(inetAddress.hostAddress)
}
/**
* 从DNS服务器解析域名获取对应的IP地址
*
* @param domain 域名
* @return 解析出的第一个IP地址
* @throws java.net.UnknownHostException 如果域名无法解析
*/
@JvmStatic
@Throws(UnknownHostException::class)
fun getHostIp(domain: String): String {
val addresses = InetAddress.getAllByName(domain.trim())
return addresses[0].hostAddress
}
/**
* 检测给定字符串是否为有效的IPv4或IPv6地址
*
* @param ip 要检测的IP地址字符串
* @return 4 表示IPv46 表示IPv6
* @throws NetworkException 如果IP格式无效
*/
@JvmStatic
fun testIp(ip: String?): Int {
if (ip == null) {
throw NetworkException("IP地址不能为null")
}
val trimmedIp = ip.trim()
// 判断是否匹配IPv4格式
if (IPV4_PATTERN.matcher(trimmedIp).matches()) {
return IPV4
}
// 判断是否匹配IPv6格式
if (IPV6_PATTERN.matcher(trimmedIp).matches()) {
return IPV6
}
// 不符合任一格式时抛出异常
throw NetworkException(
"[$ip] 不是有效的IPv4或IPv6地址"
)
}
}
/**
* IP地址类型4 表示 IPv46 表示 IPv6
*/
var iPv: Int = 0
private set
/**
* IP地址字符串
*/
var ip: String? = null
private set
/**
* 域名如果输入的是域名
*/
private var domain: String? = null
/**
* 标识是否是域名解析来的IP
*/
private var isdom = false
/**
* 构造方法根据传入的字符串判断是IP地址还是域名并进行相应处理
*
* @param domip 可能是IP地址或域名的字符串
*/
init {
try {
// 尝试将输入识别为IP地址
this.iPv = testIp(domip)
this.ip = domip
} catch (e: NetworkException) {
try {
// 如果不是有效IP则尝试作为域名解析
val ips = getHostIp(domip)
this.iPv = testIp(ips)
this.ip = ips
this.isdom = true
this.domain = domip
} catch (ex: UnknownHostException) {
throw NetworkException(ex)
}
}
}
/**
* 将IPv4地址转换为字节数组
*
* @return 返回表示IPv4地址的4字节数组
* @throws NetworkException 当当前地址不是IPv4地址时抛出异常
*/
fun toIpv4ByteArray(): ByteArray {
// 验证地址类型是否为IPv4
if (iPv != IPV4) {
throw NetworkException("该地址 不是IPv4地址")
}
// 将IP地址字符串按点分割转换为整数并进行位运算处理最后转为字节数组
return ip!!.split(".").map { it.toInt() and 0xFF }.map { it.toByte() }.toByteArray()
}
/**
* 将IPv4地址写入到指定的ByteBuffer中
*
* @param byteBuffer 要写入的ByteBuffer对象IPv4地址将以字节数组形式写入到该缓冲区
* @return 返回写入了IPv4地址的ByteBuffer对象便于链式调用
*/
fun writeIpv4ToByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
return byteBuffer.put(toIpv4ByteArray())
}
/**
* 将当前 NetworkAddress 转换为 InetAddress 对象
*
* @return InetAddress 对象
*/
fun toInetAddress(): InetAddress {
try {
return InetAddress.getByName(if (ip != null) ip else domain)
} catch (e: UnknownHostException) {
throw RuntimeException(e)
}
}
/**
* 返回 NetworkAddress 的字符串表示形式
*
* @return 字符串表示
*/
override fun toString(): String {
return if (isdom) "NetworkAddress(IP='$ip',type='$iPv',domain='$domain')"
else "NetworkAddress(IP='$ip',type='$iPv')"
}
}

View File

@ -0,0 +1,206 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NetworkEndpoint.kt
* LastUpdate 2026-02-05 10:20:31
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network
import java.io.Serializable
import java.net.InetSocketAddress
import java.nio.ByteBuffer
/**
* IP和端口聚集类用于封装网络地址与端口信息
* 该类提供了与InetSocketAddress之间的相互转换功能
*
* @author MingLiPro
* @see java.net.InetSocketAddress
*/
class NetworkEndpoint private constructor(
val networkAddress: NetworkAddress, val networkPort: NetworkPort
) : Serializable, InetSocketAddress(networkAddress.toInetAddress(), networkPort.port) {
companion object {
/**
* 根据给定的InetSocketAddress对象创建NetworkEndpoint实例
*
* @param address InetSocketAddress对象
* @return 新建的NetworkEndpoint实例
* @see InetSocketAddress
*/
@JvmStatic
fun of(address: InetSocketAddress): NetworkEndpoint {
return NetworkEndpoint(
NetworkAddress.of(address.hostString), NetworkPort(address.port)
)
}
/**
* 根据主机名或IP字符串和端口号创建NetworkEndpoint实例
*
* @param s 主机名或IP地址字符串
* @param i 端口号
* @return 新建的NetworkEndpoint实例
*/
@JvmStatic
fun of(s: String, i: Int): NetworkEndpoint {
val networkAddress = NetworkAddress.of(s)
val networkPort = NetworkPort(i)
return NetworkEndpoint(networkAddress, networkPort)
}
/**
* 从字节数组创建IPv4网络端点
*
* @param byteArray 包含IPv4地址和端口信息的字节数组前4个字节表示IP地址后2个字节表示端口号
* @return NetworkEndpoint对象封装了IPv4地址和端口信息
*/
@JvmStatic
fun ofIpv4(byteArray: ByteArray): NetworkEndpoint {
// 提取前4个字节作为IP地址
val address = ByteArray(4) {
byteArray[it]
}
// 提取后2个字节作为端口号
val portInt = ByteArray(2) {
byteArray[it + 4]
}
return NetworkEndpoint(NetworkAddress.ofIpv4(address), NetworkPort.of(portInt))
}
/**
* 从字节缓冲区创建IPv4网络端点实例
*
* 该方法从给定的ByteBuffer中读取数据构建一个包含IPv4地址和端口的网络端点对象
* 该方法按照协议顺序从缓冲区中读取IPv4地址数据和端口数据
*
* @param byteBuffer 包含IPv4地址和端口数据的字节缓冲区缓冲区中的数据应按照先地址后端口的顺序排列
* @return 返回一个NetworkEndpoint实例包含从缓冲区解析出的IPv4地址和端口信息
*/
@JvmStatic
fun ofIpv4(byteBuffer: ByteBuffer): NetworkEndpoint {
return NetworkEndpoint(
NetworkAddress.ofIpv4(byteBuffer),
NetworkPort.of(byteBuffer)
)
}
/**
* 根据"host:port"格式的字符串创建NetworkEndpoint实例
* 例如"127.0.0.1:8080"
*
* @param s "host:port"格式的字符串
* @return 新建的NetworkEndpoint实例
*/
@JvmStatic
fun of(s: String): NetworkEndpoint {
val lastColonIndex = s.lastIndexOf(':')
return of(
s.take(lastColonIndex), s.substring(lastColonIndex + 1).toInt()
)
}
}
/**
* 将当前NetworkEndpoint转换为InetSocketAddress对象
*
* @return 对应的InetSocketAddress对象
* @see InetSocketAddress
*/
fun toInetSocketAddress(): InetSocketAddress {
return InetSocketAddress(
networkAddress.toInetAddress(), networkPort.port
)
}
/**
* 将当前NetworkEndpoint转换为"host:port"格式的字符串
* 例如"127.0.0.1:25563"
*
* @return 格式化后的字符串
*/
fun toHostPortString(): String {
return "${networkAddress.ip}:${networkPort.port}"
}
/**
* 返回NetworkEndpoint的详细字符串表示形式
* 格式NetworkEndpoint(IP=...,Port=...,Endpoint=...)
*
* @return 包含详细信息的字符串
*/
override fun toString(): String {
return "NetworkEndpoint(IP=${networkAddress.ip},Port=${networkPort.port})"
}
/**
* 获取主机名或IP地址字符串
*
* @return 主机名或IP地址
*/
fun host(): String {
return networkAddress.ip ?: ""
}
/**
* 获取端口号
*
* @return 端口号
*/
fun port(): Int {
return networkPort.port
}
/**
* 将网络地址和端口转换为IPv4字节数组
*
* 该函数将当前对象的网络地址转换为IPv4字节数组并与端口字节数组合并
* 生成一个包含6个字节的数组其中前4个字节为IPv4地址后2个字节为端口号
*
* @return ByteArray 包含6个字节的数组前4个字节为IPv4地址后2个字节为端口号
*/
fun toIpv4ByteArray(): ByteArray {
val ipv4ByteArray = networkAddress.toIpv4ByteArray()
val portByteArray = networkPort.toByteArray()
// 构建包含IPv4地址和端口的6字节数组
val byteArray = ByteArray(6) {
if (it < 4) {
ipv4ByteArray[it]
} else {
portByteArray[it - 4]
}
}
return byteArray
}
/**
* 将IPv4地址和端口信息写入字节缓冲区
*
* 该函数依次将网络地址的IPv4表示和网络端口写入到指定的字节缓冲区中
*
* @param byteBuffer 要写入数据的目标字节缓冲区
*/
fun writeIpv4toByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
networkAddress.writeIpv4ToByteBuffer(byteBuffer)
networkPort.writeToByteBuffer(byteBuffer)
return byteBuffer
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NetworkException.kt
* LastUpdate 2026-02-05 10:20:31
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network
class NetworkException : RuntimeException {
/**
* 构造一个带有指定详细消息的网络异常
*
* @param message 异常的详细消息
*/
constructor(message: String?) : super(message)
/**
* 构造一个网络异常指定原因异常
*
* @param e 导致此异常的原因异常
*/
constructor(e: Exception?) : super(e)
}

View File

@ -0,0 +1,91 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NetworkPort.kt
* LastUpdate 2026-02-05 10:20:31
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network
import java.io.Serializable
import java.nio.ByteBuffer
class NetworkPort : Serializable {
val port: Int
constructor(port: Int) {
testPort(port)
this.port = port
}
fun toByteArray(): ByteArray {
val byteArray = ByteArray(2)
byteArray[0] = (port shr 8 and 0xFF).toByte()
byteArray[1] = (port and 0xFF).toByte()
return byteArray
}
fun writeToByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
return byteBuffer.put(toByteArray())
}
companion object {
/**
* 创建NetworkPort实例
* @param port 端口号
* @return NetworkPort实例
*/
@JvmStatic
fun of(port: Int): NetworkPort {
return NetworkPort(port)
}
/**
* 从字节数组创建NetworkPort实例
* @param byteArray 包含端口信息的字节数组长度至少为2
* @return NetworkPort实例
*/
@JvmStatic
fun of(byteArray: ByteArray): NetworkPort {
// 将字节数组的前两个字节转换为端口号
val port = ((byteArray[0].toInt() and 0xFF) shl 8) or (byteArray[1].toInt() and 0xFF)
return of(port)
}
/**
* 从ByteBuffer创建NetworkPort实例
* @param byteBuffer 包含端口信息的ByteBuffer
* @return NetworkPort实例
*/
@JvmStatic
fun of(byteBuffer: ByteBuffer): NetworkPort {
val byteArray = ByteArray(2)
byteBuffer.get(byteArray)
return of(byteArray)
}
@JvmStatic
fun testPort(port: Int) {
// 验证端口号范围是否在0-65535之间
if (port !in 0..65535) {
throw NetworkException("$port 不是正确的端口号")
}
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NullUtils.kt
* LastUpdate 2026-02-05 10:17:02
* UpdateUser MingLiPro
*/
@file:JvmName("NullUtils")
package com.mingliqiye.utils.objects
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@OptIn(ExperimentalContracts::class)
fun Any?.isNotNull(): Boolean {
contract {
returns(true) implies (this@isNotNull != null)
}
return !isNull()
}
@OptIn(ExperimentalContracts::class)
fun Any?.isNull(): Boolean {
contract {
returns(false) implies (this@isNull != null)
}
return this == null
}
fun areEquals(a: Any?, b: Any?) = (a == null && b == null) || (a == b)
@OptIn(ExperimentalContracts::class)
fun areEqualsNoNull(a: Any?, b: Any?): Boolean {
contract {
returns(true) implies (a != null && b != null)
}
return a != null && b != null && a == b
}

View File

@ -28,8 +28,17 @@ import java.io.File
import java.net.URI
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
import java.util.function.Consumer
class OsPath private constructor(private val path: Path) : Path by path {
override fun forEach(action: Consumer<in Path>?) {
path.forEach(action)
}
override fun spliterator(): Spliterator<Path?> {
return path.spliterator()
}
companion object {
@JvmStatic

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,15 +16,13 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile RandomBytes.kt
* LastUpdate 2025-09-16 17:42:26
* LastUpdate 2026-01-28 10:47:13
* UpdateUser MingLiPro
*/
@file:JvmName("RandomBytes")
package com.mingliqiye.utils.random
import java.security.SecureRandom
/**
* 生成指定长度的随机字节数组
* @param length 数组长度
@ -38,6 +36,7 @@ fun randomBytes(length: Int): ByteArray {
return bytes
}
/**
* 生成指定长度的随机字节数组
* 从给定的字节数组中随机选择字节来填充新数组
@ -81,12 +80,8 @@ fun randomByteNoHave(from: Byte, to: Byte): Byte {
return (randomValue and 0xFF).toByte()
}
val secureRandom: SecureRandom by lazy {
SecureRandom.getInstanceStrong()
}
fun randomByteSecure(size: Int): ByteArray {
fun randomByte(size: Int): ByteArray {
val bytes = ByteArray(size)
secureRandom.nextBytes(bytes)
return bytes

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +16,24 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile RandomInt.kt
* LastUpdate 2025-09-12 17:08:32
* LastUpdate 2026-01-28 10:46:02
* UpdateUser MingLiPro
*/
@file:JvmName("RandomInt")
package com.mingliqiye.utils.random
import java.util.concurrent.ThreadLocalRandom
import java.security.SecureRandom
val secureRandom: SecureRandom by lazy {
SecureRandom.getInstanceStrong()
}
fun SecureRandom.nextInt(min: Int, max: Int): Int {
return min + nextInt(max - min)
}
/**
* 生成指定范围内的随机整数
@ -42,7 +52,7 @@ fun randomIntNoHave(min: Int, max: Int): Int {
if (min == max) {
return min
}
return ThreadLocalRandom.current().nextInt(min, max)
return secureRandom.nextInt(min, max)
}
/**
@ -55,6 +65,3 @@ fun randomInt(min: Int, max: Int): Int {
var max = max
return randomIntNoHave(min, ++max)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile RandomString.kt
* LastUpdate 2025-09-12 17:10:43
* LastUpdate 2026-02-05 11:12:36
* UpdateUser MingLiPro
*/
@file:JvmName("RandomString")

View File

@ -1,225 +0,0 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Require.kt
* LastUpdate 2026-01-10 09:01:03
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.request
import com.mingliqiye.utils.functions.P1RFunction
import com.mingliqiye.utils.functions.RFunction
/**
* 扩展函数基于布尔值创建Require对象并指定异常消息和异常构造器
* @param message 异常消息
* @param exception 异常构造器函数
* @return Require对象
*/
fun Boolean.require(message: String, exception: P1RFunction<String, out Exception>): Require {
return Require(this, message, exception)
}
/**
* 扩展函数基于布尔值创建Require对象并指定异常消息和异常类型
* @param message 异常消息
* @param exception 异常类型默认为IllegalArgumentException
* @return Require对象
*/
fun Boolean.require(
message: String,
exception: Class<out Exception> = IllegalArgumentException::class.java
): Require {
return Require(this, message, exception)
}
/**
* 条件检查工具类用于验证条件并抛出相应异常
* @param must 需要验证的布尔条件
*/
class Require(private val must: Boolean) {
/**
* 构造函数通过函数调用结果初始化条件检查器
* @param funs 返回布尔值的函数
*/
constructor(funs: RFunction<Boolean>) : this(funs.call())
/**
* 构造函数通过函数调用结果初始化条件检查器并立即执行检查
* @param must 返回布尔值的函数
* @param message 检查失败时的异常消息
*/
constructor(must: RFunction<Boolean>, message: String) : this(must) {
throws(message)
}
/**
* 构造函数通过函数调用结果初始化条件检查器并立即执行检查
* @param must 返回布尔值的函数
* @param message 检查失败时的异常消息
* @param exception 检查失败时抛出的异常类型默认为IllegalArgumentException
*/
constructor(
must: RFunction<Boolean>,
message: String,
exception: Class<out Exception> = IllegalArgumentException::class.java
) : this(must) {
throws(message, exception)
}
/**
* 构造函数通过布尔值初始化条件检查器并立即执行检查
* @param must 需要验证的布尔条件
* @param message 检查失败时的异常消息
*/
constructor(must: Boolean, message: String) : this(must) {
throws(message)
}
/**
* 构造函数通过布尔值初始化条件检查器并立即执行检查
* @param must 需要验证的布尔条件
* @param message 检查失败时的异常消息
* @param exception 检查失败时抛出的异常类型默认为IllegalArgumentException
*/
constructor(
must: Boolean, message: String, exception: Class<out Exception> = IllegalArgumentException::class.java
) : this(must) {
throws(message, exception)
}
/**
* 构造函数通过布尔值初始化条件检查器并立即执行检查
* @param must 需要验证的布尔条件
* @param message 检查失败时的异常消息
* @param exception 检查失败时抛出的异常构造器函数
*/
constructor(
must: Boolean, message: String, exception: P1RFunction<String, out Exception>
) : this(must) {
throws(message, exception)
}
companion object {
/**
* 工厂方法创建Require对象并指定异常消息和异常类型
* @param must 需要验证的布尔条件
* @param message 检查失败时的异常消息
* @param exception 检查失败时抛出的异常类型默认为IllegalArgumentException
* @return Require对象
*/
@JvmStatic
fun require(
must: Boolean, message: String, exception: Class<out Exception> = IllegalArgumentException::class.java
): Require {
return Require(must, message, exception)
}
/**
* 工厂方法创建Require对象并指定异常消息
* @param must 需要验证的布尔条件
* @param message 检查失败时的异常消息
* @return Require对象
*/
@JvmStatic
fun require(must: Boolean, message: String): Require {
return Require(must, message)
}
/**
* 工厂方法创建Require对象
* @param must 需要验证的布尔条件
* @return Require对象
*/
@JvmStatic
fun require(must: Boolean): Require {
return Require(must)
}
/**
* 工厂方法创建Require对象并指定异常消息和异常类型
* @param must 返回布尔值的函数
* @param message 检查失败时的异常消息
* @param exception 检查失败时抛出的异常类型默认为IllegalArgumentException
* @return Require对象
*/
@JvmStatic
fun require(
must: RFunction<Boolean>,
message: String,
exception: Class<out Exception> = IllegalArgumentException::class.java
): Require {
return Require(must, message, exception)
}
/**
* 工厂方法创建Require对象并指定异常消息
* @param must 返回布尔值的函数
* @param message 检查失败时的异常消息
* @return Require对象
*/
@JvmStatic
fun require(must: RFunction<Boolean>, message: String): Require {
return Require(must, message)
}
/**
* 工厂方法创建Require对象
* @param must 返回布尔值的函数
* @return Require对象
*/
@JvmStatic
fun require(must: RFunction<Boolean>): Require {
return Require(must)
}
}
/**
* 执行条件检查如果条件为false则抛出IllegalArgumentException
* @param message 检查失败时的异常消息
*/
fun throws(message: String) {
if (!must) {
throw IllegalArgumentException(message)
}
}
/**
* 执行条件检查如果条件为false则抛出指定类型的异常
* @param string 检查失败时的异常消息
* @param exception 检查失败时抛出的异常类型
*/
fun throws(string: String, exception: Class<out Exception>) {
if (!must) {
throw exception.getConstructor(String::class.java).newInstance(string)
}
}
/**
* 执行条件检查如果条件为false则抛出由函数构造的异常
* @param string 检查失败时的异常消息
* @param exception 检查失败时抛出的异常构造器函数
*/
fun throws(string: String, exception: P1RFunction<String, out Exception>) {
if (!must) {
throw exception.call(string)
}
}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Require.kt
* LastUpdate 2026-02-05 11:08:03
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.require
import com.mingliqiye.utils.logger.MingLiLoggerFactory
import org.slf4j.Logger
import java.lang.reflect.Constructor
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
/**
* 工具对象用于提供条件检查功能
* 支持抛出自定义异常延迟消息构造以及日志记录等功能
*/
object Require {
/**
* 日志记录器实例用于记录错误信息
*/
@JvmStatic
var logger: Logger? = MingLiLoggerFactory.getLogger<Require>()
/**
* 控制是否在抛出异常时记录错误日志
*/
@JvmStatic
var isLogError = true
/**
* 检查给定条件是否为真如果为假则抛出 [IllegalArgumentException] 异常
*
* @param value 需要检查的布尔值
*/
@OptIn(ExperimentalContracts::class)
@JvmStatic
fun require(value: Boolean) {
contract {
returns() implies value
}
require<IllegalArgumentException>(value, "the require conditions are not met.")
}
/**
* 检查给定条件是否为真如果为假则根据指定的异常类型抛出异常
*
* @param value 需要检查的布尔值
* @param message 异常消息
* @param T 异常类型
*/
@OptIn(ExperimentalContracts::class)
@JvmName("__inline_Require")
inline fun <reified T : Throwable> require(value: Boolean, message: String) {
contract {
returns() implies value
}
require(value, message, T::class.java)
}
/**
* 检查给定条件是否为真如果为假则抛出 [IllegalArgumentException] 异常
*
* @param value 需要检查的布尔值
* @param message 异常消息
*/
@OptIn(ExperimentalContracts::class)
fun require(value: Boolean, message: String) {
contract {
returns() implies value
}
if (!value) throwThrowable(getExceptionConstructor<IllegalArgumentException>().newInstance(message))
}
/**
* 检查给定条件是否为真如果为假则使用延迟消息构造器生成异常并抛出
*
* @param value 需要检查的布尔值
* @param throwable 异常类型
* @param layzMessage 延迟消息构造器
*/
@OptIn(ExperimentalContracts::class)
@JvmStatic
fun require(value: Boolean, throwable: Class<out Throwable>, layzMessage: RequireLayzMessageConstructor) {
contract {
returns() implies value
}
require(value, layzMessage.call(), throwable)
}
/**
* 检查给定条件是否为真如果为假则使用延迟异常构造器生成异常并抛出
*
* @param value 需要检查的布尔值
* @param layzThrowable 延迟异常构造器
*/
@OptIn(ExperimentalContracts::class)
@JvmStatic
fun require(value: Boolean, layzThrowable: RequireLayzExceptionConstructor) {
contract {
returns() implies value
}
if (!value) throwThrowable(layzThrowable.call())
}
/**
* 检查给定条件是否为真如果为假则使用延迟消息构造器生成 [IllegalArgumentException] 并抛出
*
* @param value 需要检查的布尔值
* @param layzMessage 延迟消息构造器
*/
@OptIn(ExperimentalContracts::class)
@JvmStatic
fun requireLayzMessage(value: Boolean, layzMessage: RequireLayzMessageConstructor) {
contract {
returns() implies value
}
require<IllegalArgumentException>(value, layzMessage)
}
/**
* 检查给定条件是否为真如果为假则根据指定的异常类型和延迟消息构造器生成异常并抛出
*
* @param value 需要检查的布尔值
* @param layzMessage 延迟消息构造器
* @param T 异常类型
*/
@OptIn(ExperimentalContracts::class)
@JvmName("__inline_Require")
inline fun <reified T : Throwable> require(value: Boolean, layzMessage: RequireLayzMessageConstructor) {
contract {
returns() implies value
}
if (!value) throwThrowable(getExceptionConstructor<T>().newInstance(layzMessage.call()))
}
/**
* 检查给定条件是否为真如果为假则根据指定的异常类型和消息生成异常并抛出
*
* @param value 需要检查的布尔值
* @param message 异常消息
* @param throwable 异常类型
*/
@OptIn(ExperimentalContracts::class)
@JvmStatic
fun require(value: Boolean, message: String, throwable: Class<out Throwable>) {
contract {
returns() implies value
}
if (!value) throwThrowable(getExceptionConstructor(throwable).newInstance(message))
}
/**
* 抛出指定的异常并在启用日志记录时记录错误信息
*
* @param throwable 需要抛出的异常
*/
fun throwThrowable(throwable: Throwable) {
if (isLogError && logger != null) logger!!.error(throwable.message, throwable)
throw throwable
}
/**
* 获取指定异常类型的构造函数带字符串参数
*
* @param T 异常类型
* @return 异常类型的构造函数
*/
@JvmName("__inline_GetExceptionConstructor")
inline fun <reified T : Throwable> getExceptionConstructor(): Constructor<out T> =
getExceptionConstructor(T::class.java)
/**
* 获取指定异常类型的构造函数带字符串参数
*
* @param throwable 异常类型
* @return 异常类型的构造函数
*/
@JvmName("__GetExceptionConstructor")
fun <T : Throwable> getExceptionConstructor(throwable: Class<out T>): Constructor<out T> =
throwable.getConstructor(String::class.java)
}

View File

@ -15,16 +15,14 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Main.kt
* LastUpdate 2026-01-06 14:36:10
* CurrentFile RequireLayzExceptionConstructor.kt
* LastUpdate 2026-02-05 09:36:19
* UpdateUser MingLiPro
*/
@file:JvmName("Main")
package com.mingliqiye.utils.main
package com.mingliqiye.utils.require
import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration
fun main() {
AutoConfiguration.printBanner()
@FunctionalInterface
fun interface RequireLayzExceptionConstructor {
fun call(): Throwable
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile RequireLayzMessageConstructor.kt
* LastUpdate 2026-02-05 09:36:09
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.require
@FunctionalInterface
fun interface RequireLayzMessageConstructor {
fun call(): String
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,72 +16,120 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ResourceUtils.kt
* LastUpdate 2025-09-20 10:26:47
* LastUpdate 2026-02-05 11:09:16
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.resource
import java.io.IOException
class ResourceUtils {
companion object {
@JvmStatic
@Throws(IOException::class)
fun getResource(resourceName: String): ByteArray {
return getResource(resourceName, ResourceUtils::class.java)
}
/**
* 工具类用于从类路径中加载资源文件
*/
object ResourceUtils {
@JvmStatic
@Throws(IOException::class)
fun getResource(resourceName: String, clazz: Class<*>): ByteArray {
return clazz.getResourceAsStream(resourceName)?.use {
it.readBytes()
} ?: throw IOException("Resource not found: $resourceName")
}
/**
* 从默认类路径中加载指定名称的资源文件并以字节数组形式返回
*
* @param resourceName 资源文件的名称相对于类路径根目录
* @return 资源文件的内容作为字节数组
* @throws IOException 如果资源未找到或读取失败时抛出
*/
@JvmStatic
@Throws(IOException::class)
fun getResource(resourceName: String): ByteArray {
return getResource(resourceName, ResourceUtils::class.java)
}
/**
* 从指定类的类路径中加载指定名称的资源文件并以字节数组形式返回
*
* @param resourceName 资源文件的名称相对于类路径根目录
* @param clazz 用于定位资源的类
* @return 资源文件的内容作为字节数组
* @throws IOException 如果资源未找到或读取失败时抛出
*/
@JvmStatic
@Throws(IOException::class)
fun getResource(resourceName: String, clazz: Class<*>): ByteArray {
return clazz.getResourceAsStream(resourceName)?.use {
it.readBytes()
} ?: throw IOException("Resource not found: $resourceName")
}
@JvmStatic
@Throws(IOException::class)
fun getStringResource(resourceName: String): String {
return getStringResource(resourceName, ResourceUtils::class.java)
}
/**
* 从默认类路径中加载指定名称的资源文件并以字符串形式返回
*
* @param resourceName 资源文件的名称相对于类路径根目录
* @return 资源文件的内容作为字符串使用UTF-8编码
* @throws IOException 如果资源未找到或读取失败时抛出
*/
@JvmStatic
@Throws(IOException::class)
fun getStringResource(resourceName: String): String {
return getStringResource(resourceName, ResourceUtils::class.java)
}
/**
* 从指定类的类路径中加载指定名称的资源文件并以字符串形式返回
*
* @param resourceName 资源文件的名称相对于类路径根目录
* @param clazz 用于定位资源的类
* @return 资源文件的内容作为字符串使用UTF-8编码
* @throws IOException 如果资源未找到或读取失败时抛出
*/
@JvmStatic
@Throws(IOException::class)
fun getStringResource(resourceName: String, clazz: Class<*>): String {
return clazz.getResourceAsStream(resourceName)?.use {
it.readBytes().toString(charset = Charsets.UTF_8)
} ?: throw IOException("Resource not found: $resourceName")
}
@JvmStatic
@Throws(IOException::class)
fun getStringResource(resourceName: String, clazz: Class<*>): String {
return clazz.getResourceAsStream(resourceName)?.use {
it.readBytes().toString(charset = Charsets.UTF_8)
} ?: throw IOException("Resource not found: $resourceName")
}
/**
* 从调用者的类路径中加载指定名称的资源文件并以字符串形式返回
*
* @param resourceName 资源文件的名称相对于类路径根目录
* @return 资源文件的内容作为字符串使用UTF-8编码
* @throws IOException 如果资源未找到或读取失败时抛出
*/
@JvmStatic
@Throws(IOException::class)
fun getStringResourceCallers(resourceName: String): String {
return getStringResource(resourceName, getCallerClass())
}
/**
* 从调用者的类路径中加载指定名称的资源文件并以字节数组形式返回
*
* @param resourceName 资源文件的名称相对于类路径根目录
* @return 资源文件的内容作为字节数组
* @throws IOException 如果资源未找到或读取失败时抛出
*/
@JvmStatic
@Throws(IOException::class)
fun getResourceCallers(resourceName: String): ByteArray {
return getResource(resourceName, getCallerClass())
}
@JvmStatic
@Throws(IOException::class)
fun getStringResourceCallers(resourceName: String): String {
return getStringResource(resourceName, getCallerClass())
}
@JvmStatic
@Throws(IOException::class)
fun getResourceCallers(resourceName: String): ByteArray {
return getResource(resourceName, getCallerClass())
}
private fun getCallerClass(): Class<*> {
val stackTrace = Thread.currentThread().stackTrace
for (i in 2 until stackTrace.size) {
val className = stackTrace[i].className
try {
val clazz = Class.forName(className)
if (clazz != ResourceUtils::class.java && clazz != Companion::class.java) {
return clazz
}
} catch (e: ClassNotFoundException) {
continue
/**
* 获取当前调用栈中第一个非ResourceUtils类的调用者类
*
* @return 调用者类对象如果未找到则返回ResourceUtils类本身
*/
private fun getCallerClass(): Class<*> {
val stackTrace = Thread.currentThread().stackTrace
for (i in 2 until stackTrace.size) {
val className = stackTrace[i].className
try {
val clazz = Class.forName(className)
if (clazz != ResourceUtils::class.java) {
return clazz
}
} catch (e: ClassNotFoundException) {
continue
}
return ResourceUtils::class.java
}
return ResourceUtils::class.java
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile SecureUtils.kt
* LastUpdate 2025-09-15 22:32:50
* LastUpdate 2026-02-05 11:12:36
* UpdateUser MingLiPro
*/
@ -29,27 +29,64 @@ import java.security.SecureRandom
import javax.crypto.SecretKey
import javax.crypto.spec.SecretKeySpec
/**
* 全局安全随机数生成器实例用于生成加密安全的随机字节
*/
internal val SECURE_RANDOM = SecureRandom()
/**
* 生成指定长度的加密安全随机字节数组
*
* @param length 需要生成的字节数组的长度
* @return 包含随机字节的 ByteArray
*/
fun getRandomBytes(length: Int): ByteArray {
val bytes = ByteArray(length)
SECURE_RANDOM.nextBytes(bytes)
return bytes
}
/**
* 使用指定算法对输入数据进行哈希处理生成密钥字节数组
*
* @param algorithm 哈希算法名称 SHA-256
* @param data 输入的字节数组数据
* @return 经过哈希处理后的密钥字节数组
*/
fun createSecretKey(algorithm: String, data: ByteArray): ByteArray {
val md = MessageDigest.getInstance(algorithm)
return md.digest(data)
}
/**
* 使用指定算法对输入字符串进行哈希处理生成密钥字节数组
*
* @param algorithm 哈希算法名称 SHA-256
* @param data 输入的字符串数据
* @return 经过哈希处理后的密钥字节数组
*/
fun createSecretKey(algorithm: String, data: String): ByteArray {
return createSecretKey(algorithm, data.toByteArray())
}
/**
* 使用指定算法和输入字符串创建 SecretKeySpec 对象
*
* @param algorithm 哈希算法名称 AES
* @param data 输入的字符串数据
* @return 根据输入数据和算法生成的 SecretKey 对象
*/
fun createSecretKeySpec(algorithm: String, data: String): SecretKey {
return SecretKeySpec(createSecretKey(algorithm, data), algorithm)
}
/**
* 使用指定算法和输入字节数组创建 SecretKeySpec 对象
*
* @param algorithm 哈希算法名称 AES
* @param data 输入的字节数组数据
* @return 根据输入数据和算法生成的 SecretKey 对象
*/
fun createSecretKeySpec(algorithm: String, data: ByteArray): SecretKey {
return SecretKeySpec(createSecretKey(algorithm, data), algorithm)
}

View File

@ -0,0 +1,79 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile SleepUtils.kt
* LastUpdate 2026-01-15 16:50:50
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.sleep
import java.util.concurrent.TimeUnit
/**
* 等待工具类提供多种等待和延迟执行的方式
*/
object SleepUtils {
/**
* 基于 Thread.sleep 的等待单位毫秒
*
* @param millis 等待时间毫秒
* @throws RuntimeException 当线程被中断时抛出
*/
@JvmStatic
fun sleep(millis: Long) {
try {
Thread.sleep(millis)
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
throw RuntimeException("Sleep interrupted", e)
}
}
/**
* 基于 Thread.sleep 的等待单位毫秒
*
* @param millis 等待时间毫秒
* @throws RuntimeException 当线程被中断时抛出
*/
@JvmStatic
fun sleep(millis: Int) {
try {
Thread.sleep(millis.toLong())
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
throw RuntimeException("Sleep interrupted", e)
}
}
/**
* 基于 TimeUnit 的等待
*
* @param timeout 等待时间
* @param unit 时间单位
* @throws RuntimeException 当线程被中断时抛出
*/
@JvmStatic
fun sleep(timeout: Long, unit: TimeUnit) {
try {
unit.sleep(timeout)
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
throw RuntimeException("Sleep interrupted", e)
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,13 +16,13 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile AutoConfiguration.kt
* LastUpdate 2025-09-20 10:47:00
* LastUpdate 2026-02-05 10:45:05
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.springboot.autoconfigure
import com.mingliqiye.utils.logger.mingLiLoggerFactory
import com.mingliqiye.utils.logger.MingLiLoggerFactory
import com.mingliqiye.utils.system.computerName
import com.mingliqiye.utils.system.getPid
import com.mingliqiye.utils.system.jdkVersion
@ -32,14 +32,24 @@ import com.mingliqiye.utils.time.Formatter
import org.springframework.context.annotation.ComponentScan
import java.io.IOException
/**
* 自动配置类用于Spring Boot应用启动时加载相关组件和打印启动横幅
*
* 该类通过@ComponentScan注解扫描指定包下的Bean并在初始化时打印包含系统信息的启动横幅
*/
@org.springframework.boot.autoconfigure.AutoConfiguration
@ComponentScan(
"com.mingliqiye.utils.springboot.bean",
"com.mingliqiye.utils.springboot.converters"
)
open class AutoConfiguration {
private val log = MingLiLoggerFactory.getLogger("MingliUtils-AutoConfiguration")
companion object {
private const val banner =
/**
* 启动横幅字符串包含艺术字体和占位符
*/
private const val BANNER =
"---------------------------------------------------------\n" +
"| $$\\ $$\\ $$\\ $$\\ $$\\ $$$$$$$$\\ $$$$$$\\ |\n" +
"| $$$\\ $$$ |$$ | $$ | $$ |\\__$$ __|$$ __$$\\ |\n" +
@ -49,25 +59,38 @@ open class AutoConfiguration {
"| $$ |\\$ /$$ |$$ | $$ | $$ | $$ | $$\\ $$ | |\n" +
"| $$ | \\_/ $$ |$$$$$$$$\\\\$$$$$$ | $$ | \\$$$$$$ | |\n" +
"| \\__| \\__|\\________|\\______/ \\__| \\______/ |\n"
private val log = mingLiLoggerFactory.getLogger("MingliUtils-AutoConfiguration")
/**
* 打印启动横幅包含系统元数据如JDK版本进程ID计算机名等
*
* 该方法从资源文件中读取元数据并将其格式化后追加到横幅中进行输出
* 如果读取资源失败则仅打印默认横幅
*/
fun printBanner() {
val bannerBuilder = StringBuilder(banner)
val bannerBuilder = StringBuilder(BANNER)
// 尝试从资源文件中读取元数据并拼接到横幅中
try {
val inputStream = AutoConfiguration::class.java.getResourceAsStream("/META-INF/meta-data") ?: return
inputStream.use { stream ->
var readlen: Int
val buffer = ByteArray(1024)
val metaData = StringBuilder()
// 逐块读取资源文件内容
while (stream.read(buffer).also { readlen = it } != -1) {
metaData.append(String(buffer, 0, readlen))
}
// 解析元数据并添加额外的系统信息
val da = metaData.toString().split("\n").toMutableList()
da.add("jdkRuntime=$jdkVersion")
da.add("pid=$getPid")
da.add("computerName=$computerName")
da.add("userName=$userName")
da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true))
da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7, false))
// 格式化每条元数据并追加到横幅中
da.forEach { s: String ->
val d = s.trim { it <= ' ' }.split("=".toRegex(), 2).toTypedArray()
if (d.size >= 2) {
@ -89,15 +112,23 @@ open class AutoConfiguration {
}
}
} catch (e: IOException) {
// 捕获IO异常并打印堆栈跟踪
e.printStackTrace()
}
// 输出最终构建的横幅
println(bannerBuilder.toString().trim())
println("---------------------------------------------------------")
}
}
/**
* 初始化块在类实例化时执行
*
* 调用printBanner方法打印启动横幅并记录日志表示自动配置成功
*/
init {
printBanner()
log.info("MingliUtils AutoConfiguration succeed")
log.info("MingliUtils AutoConfigurationBean succeed")
}
}

View File

@ -1,91 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile GsonAutoConfiguration.kt
* LastUpdate 2025-09-15 10:29:30
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.springboot.autoconfigure
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.mingliqiye.utils.json.GsonJsonApi
import com.mingliqiye.utils.json.JsonApi
import com.mingliqiye.utils.json.converters.DateTimeJsonConverter
import com.mingliqiye.utils.json.converters.JsonStringConverter
import com.mingliqiye.utils.json.converters.UUIDJsonStringConverter
import com.mingliqiye.utils.logger.mingLiLoggerFactory
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.uuid.UUID
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.AutoConfigureAfter
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer
import org.springframework.context.annotation.Bean
@ConditionalOnClass(Gson::class)
@AutoConfiguration
@AutoConfigureAfter(
name = ["org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration",
"com.mingliqiye.utils.springboot.autoconfigure.JacksonAutoConfiguration"]
)
open class GsonAutoConfiguration {
companion object {
private val log: Logger = LoggerFactory.getLogger("MingliUtils-GsonAutoConfiguration")
fun addTypeAdapter(gsonBuilder: GsonBuilder): GsonBuilder {
val dateTimeJsonConverter: JsonStringConverter<DateTime> = DateTimeJsonConverter()
val uuidJsonStringConverter: JsonStringConverter<UUID> = UUIDJsonStringConverter()
try {
return gsonBuilder
.registerTypeAdapter(
uuidJsonStringConverter.tClass,
uuidJsonStringConverter
.gsonJsonStringConverterAdapter
.gsonTypeAdapter
)
.registerTypeAdapter(
dateTimeJsonConverter.tClass,
dateTimeJsonConverter
.gsonJsonStringConverterAdapter
.gsonTypeAdapter
)
} finally {
log.info("MingliUtils GsonBuilder TypeAdapter add")
}
}
}
private val log: Logger = mingLiLoggerFactory.getLogger("MingliUtils-GsonAutoConfiguration")
@Bean
open fun mingliGsonCustomizer(): GsonBuilderCustomizer {
return GsonBuilderCustomizer { gsonBuilder: GsonBuilder -> addTypeAdapter(gsonBuilder) }
}
@Bean
@ConditionalOnMissingBean
open fun jsonApi(gson: Gson): JsonApi {
log.info("MingliUtils-JsonApiAutoConfiguration: GsonJsonApi bean is created.")
return GsonJsonApi(gson)
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,61 +16,91 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JacksonAutoConfiguration.kt
* LastUpdate 2025-09-15 10:29:02
* LastUpdate 2026-02-05 10:45:19
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.springboot.autoconfigure
import com.fasterxml.jackson.databind.ObjectMapper
import com.mingliqiye.utils.json.JacksonJsonApi
import com.mingliqiye.utils.json.JsonApi
import com.mingliqiye.utils.json.api.JSONA
import com.mingliqiye.utils.json.api.JacksonJsonApi
import com.mingliqiye.utils.json.api.base.JsonApi
import com.mingliqiye.utils.json.converters.DateTimeJsonConverter
import com.mingliqiye.utils.json.converters.UUIDJsonStringConverter
import com.mingliqiye.utils.logger.mingLiLoggerFactory
import com.mingliqiye.utils.json.converters.UUIDJsonConverter
import com.mingliqiye.utils.json.converters.base.registerModule
import com.mingliqiye.utils.logger.MingLiLoggerFactory
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.AutoConfigureAfter
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Primary
@ConditionalOnClass(ObjectMapper::class)
@AutoConfiguration
@AutoConfigureAfter(org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration::class)
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JacksonAutoConfiguration.kt
* LastUpdate 2026-02-05 10:39:54
* UpdateUser MingLiPro
*/
/*
* JacksonAutoConfiguration 是一个 Spring Boot 自动配置类用于配置 Jackson 相关的序列化和反序列化功能
* 该类在检测到 ObjectMapper Spring Boot JacksonAutoConfiguration 存在时自动生效
* 并注册自定义的 JSON 转换器模块 UUID DateTime 转换器
*
* @param objectMapper 用于 JSON 序列化和反序列化的 ObjectMapper 实例
*/
@ConditionalOnClass(name = ["com.fasterxml.jackson.databind.ObjectMapper", "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration"])
@AutoConfigureAfter(name = ["org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration"])
open class JacksonAutoConfiguration(objectMapper: ObjectMapper) {
companion object {
private val log: Logger = mingLiLoggerFactory.getLogger("MingliUtils-JacksonAutoConfiguration")
fun addModules(objectMapper: ObjectMapper): ObjectMapper {
return objectMapper
.registerModule(
DateTimeJsonConverter()
.jacksonJsonStringConverterAdapter
.jacksonModule
)
.registerModule(
UUIDJsonStringConverter()
.jacksonJsonStringConverterAdapter
.jacksonModule
)
}
}
private val log: Logger = LoggerFactory.getLogger("MingliUtils-JacksonAutoConfiguration")
private val log: Logger = MingLiLoggerFactory.getLogger("MingliUtils-JacksonAutoConfiguration")
/*
* 初始化块在类实例化时执行
* 注册自定义的 UUID DateTime JSON 转换器模块到 ObjectMapper
*/
init {
addModules(objectMapper)
log.info("MingliUtils Jackson Serializers created")
objectMapper
.registerModule<UUIDJsonConverter>()
.registerModule<DateTimeJsonConverter>()
}
/*
* 创建并返回一个 JsonApi Bean 实例
* 该方法会在没有其他 JsonApi Bean 存在时被调用并将创建的 JacksonJsonApi 实例设置为全局默认的 JSON API
*
* @param objectMapper 用于 JSON 操作的 ObjectMapper 实例
* @return 返回配置好的 JsonApi 实例
*/
@Bean
@Primary
@ConditionalOnMissingBean
open fun jsonApi(objectMapper: ObjectMapper): JsonApi {
log.info("MingliUtils-JsonApiAutoConfiguration: JacksonJsonApi bean is created.")
return JacksonJsonApi(objectMapper)
return JacksonJsonApi(objectMapper).also {
try {
JSONA.getJsonApi()
} catch (_: NullPointerException) {
JSONA.setJsonApi(it)
log.info("JSONA Use {}", it.javaClass.name)
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile SpringBeanUtils.kt
* LastUpdate 2025-09-19 20:07:08
* LastUpdate 2026-02-04 16:50:39
* UpdateUser MingLiPro
*/
@ -39,6 +39,9 @@ class SpringBeanUtils : ApplicationContextAware {
@JvmStatic
private var applicationContext: ApplicationContext? = null
@Throws(BeansException::class)
inline fun <reified T> getBean() = getBean(T::class.java)
/**
* 根据Bean名称获取Bean实例
*
@ -48,11 +51,9 @@ class SpringBeanUtils : ApplicationContextAware {
* @throws ClassCastException 当类型转换失败时抛出
*/
@JvmStatic
@Throws(BeansException::class, ClassCastException::class)
@Throws(BeansException::class)
@Suppress("UNCHECKED_CAST")
fun <T> getBean(name: String): T {
return applicationContext!!.getBean(name) as T
}
fun getBean(name: String): Any = applicationContext!!.getBean(name)
/**
* 根据Bean类型获取Bean实例

View File

@ -0,0 +1,35 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTimeToStringConverter.kt
* LastUpdate 2026-02-04 21:57:43
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.springboot.converters
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.Formatter
import org.springframework.core.convert.converter.Converter
import org.springframework.stereotype.Component
@Component
class DateTimeToStringConverter : Converter<DateTime, String?> {
override fun convert(source: DateTime): String {
return source.format(Formatter.STANDARD_DATETIME)
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,8 +15,8 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Converters.kt
* LastUpdate 2025-09-15 09:19:48
* CurrentFile StringToDateTimeConverter.kt
* LastUpdate 2026-02-04 21:57:43
* UpdateUser MingLiPro
*/
@ -24,35 +24,12 @@ package com.mingliqiye.utils.springboot.converters
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.Formatter
import com.mingliqiye.utils.uuid.UUID
import com.mingliqiye.utils.uuid.UUID.Companion.of
import org.springframework.core.convert.converter.Converter
import org.springframework.stereotype.Component
@Component
class DateTimeToStringConverter : Converter<DateTime, String?> {
override fun convert(source: DateTime): String {
return source.format(Formatter.STANDARD_DATETIME)
}
}
@Component
class UUIDToStringConverter : Converter<UUID, String> {
override fun convert(source: UUID): String {
return source.getString()
}
}
@Component
class StringToDateTimeConverter : Converter<String, DateTime> {
override fun convert(source: String): DateTime {
return DateTime.parse(source, Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
}
}
@Component
class StringToUUIDConverter : Converter<String, UUID> {
override fun convert(source: String): UUID {
return of(source)
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,22 +15,20 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonConverter.kt
* LastUpdate 2025-09-15 11:12:07
* CurrentFile StringToUUIDConverter.kt
* LastUpdate 2026-02-04 21:58:01
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters
package com.mingliqiye.utils.springboot.converters
interface JsonConverter<F, T> {
fun convert(obj: F?): T?
fun deConvert(obj: T?): F?
val tClass: Class<F>
import com.mingliqiye.utils.uuid.UUID
import org.springframework.core.convert.converter.Converter
import org.springframework.stereotype.Component
fun getStringConverter(): JsonStringConverter<F>? {
if (this is JsonStringConverter<*>) {
return this as JsonStringConverter<F>
}
return null
@Component
class StringToUUIDConverter : Converter<String, UUID> {
override fun convert(source: String): UUID {
return UUID.of(source)
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUIDToStringConverter.kt
* LastUpdate 2026-02-04 21:57:43
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.springboot.converters
import com.mingliqiye.utils.uuid.UUID
import org.springframework.core.convert.converter.Converter
import org.springframework.stereotype.Component
@Component
class UUIDToStringConverter : Converter<UUID, String> {
override fun convert(source: UUID): String {
return source.getString()
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,17 +16,26 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile StringUtils.kt
* LastUpdate 2025-09-18 09:26:41
* LastUpdate 2026-02-05 11:05:33
* UpdateUser MingLiPro
*/
@file:JvmName("StringUtils")
package com.mingliqiye.utils.string
import com.mingliqiye.utils.logger.mingLiLoggerFactory
import com.mingliqiye.utils.base.BASE16
import com.mingliqiye.utils.logger.MingLiLoggerFactory
import com.mingliqiye.utils.objects.isNull
import java.net.URLDecoder
import java.net.URLEncoder
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
val log = mingLiLoggerFactory.getLogger("StringUtils")
private val log = MingLiLoggerFactory.getLogger(Class.forName("com.mingliqiye.utils.string.StringUtils"))
val NULLISH_STRINGS = setOf("null", "NaN", "undefined", "None", "none")
@ -36,11 +45,18 @@ val NULLISH_STRINGS = setOf("null", "NaN", "undefined", "None", "none")
* @param str 待判断的字符串
* @return `true`: `false`: 非空
*/
@OptIn(ExperimentalContracts::class)
@JvmName("isEmpty")
fun String?.isNullish(): Boolean {
return this == null || this.isBlank() || this in NULLISH_STRINGS
contract {
returns(false) implies (this@isNullish != null)
}
return this.isNullOrBlank() || this in NULLISH_STRINGS
}
@JvmName("__Formatde")
fun String.formatd(vararg args: Any) = com.mingliqiye.utils.string.format(this, *args)
/**
* 格式化字符串将字符串中的占位符{}替换为对应的参数值
*
@ -82,8 +98,16 @@ fun format(str: String, vararg args: Any?): String {
// 检查参数数量
val placeholderCount = matches.count()
if (argIndex != args.size) {
if (placeholderCount != args.size) {
log.warn("Placeholder count: $placeholderCount, Argument count: ${args.size}")
log.warn("template : $str")
log.warn(
"Arguments : [${
", ".join(args) {
if (it.isNull()) return@join "null:null"
"${it.javaClass.simpleName}:$it"
}
}]")
}
return finalResult
@ -214,3 +238,65 @@ fun <T> String.join(list: List<T>, getstring: (T) -> String = { it.toString() })
return sb.toString()
}
fun <T> String.join(array: Array<T>, getstring: (T) -> String = { it.toString() }): String {
// 使用StringBuilder构建结果字符串
val sb = StringBuilder()
for (i in array.indices) {
sb.append(getstring(array[i]))
// 除了最后一个元素外,都在后面添加当前字符串作为分隔符
if (i != array.size - 1) {
sb.append(this)
}
}
return sb.toString()
}
fun String?.parserTemplate(template: String): List<String>? {
if (this == null) return null
val regex: Regex = Regex(
"^" +
template
.replace("\\", "\\\\")
.replace("(", "\\(")
.replace(")", "\\)")
.replace("[", "\\[")
.replace("]", "\\]")
.replace("+", "\\+")
.replace("=", "\\=")
.replace("{}", "((?s).*)")
.toRegex() +
"$"
)
val datas = regex.find(this)?.groupValues ?: return null
return List(datas.size - 1) {
datas[it + 1]
}
}
fun String.urlEncode() = URLEncoder.encode(this, Charsets.UTF_8.name())
fun String.urlDecode() = URLDecoder.decode(this, Charsets.UTF_8.name())
fun String.hmacSHA256String(keyS: String): String {
val instance = Mac.getInstance("HmacSHA256")
val key = keyS.toByteArray()
instance.init(SecretKeySpec(key, 0, key.size, "HmacSHA256"))
val bytes = instance.doFinal(this.toByteArray())
return BASE16.encode(bytes)
}
fun String.md5String(): String {
val instance = MessageDigest.getInstance("MD5")
instance.update(this.toByteArray())
val bytes = instance.digest()
return BASE16.encode(bytes)
}
fun String.hmacMd5String(keyS: String): String {
val instance = Mac.getInstance("HmacMD5")
val key = keyS.toByteArray()
instance.init(SecretKeySpec(key, 0, key.size, "HmacSHA256"))
val bytes = instance.doFinal(this.toByteArray())
return BASE16.encode(bytes)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,19 +16,16 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile SystemUtil.kt
* LastUpdate 2025-09-16 17:36:11
* LastUpdate 2026-01-31 20:47:59
* UpdateUser MingLiPro
*/
@file:JvmName("SystemUtils")
package com.mingliqiye.utils.system
import com.mingliqiye.utils.random.randomByteSecure
import com.mingliqiye.utils.random.randomByte
import java.lang.management.ManagementFactory
import java.net.Inet4Address
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketException
import java.net.*
/**
* 操作系统名称属性延迟初始化
@ -256,10 +253,7 @@ val computerName: String by lazy {
*/
val userName: String by lazy {
try {
getEnvVar("USERNAME")
?: getEnvVar("USER")
?: System.getProperty("user.name")
?: "unknown"
getEnvVar("USERNAME") ?: getEnvVar("USER") ?: System.getProperty("user.name") ?: "unknown"
} catch (e: SecurityException) {
"unknown"
} catch (e: Exception) {
@ -297,11 +291,11 @@ val macAddressBytes: ByteArray by lazy {
return@lazy mac
}
}
randomByteSecure(6)
randomByte(6)
} catch (e: SocketException) {
randomByteSecure(6)
randomByte(6)
} catch (e: Exception) {
randomByteSecure(6)
randomByte(6)
}
}
@ -375,3 +369,91 @@ val allMacAddressesStringList: Map<String, List<String>> by lazy {
entry.value.map { String.format("%02X", it) }
}
}
/**
* 获取可用处理器数量的懒加载属性
*/
val availableProcessors: Int by lazy {
Runtime.getRuntime().availableProcessors()
}
private var isLoadprotocol = false
private var protocol = ""
/**
* 判断是否为开发模式file协议
*/
val isDevMode: Boolean by lazy {
protocol == "file"
}
/**
* 判断是否不为开发模式
*/
val isNotDevMode: Boolean by lazy {
protocol != "file"
}
/**
* 判断是否为JAR模式
*/
val isJarMode: Boolean by lazy {
protocol == "jar"
}
/**
* 判断是否不为JAR模式
*/
val isNotJarMode: Boolean by lazy {
protocol != "jar"
}
/**
* 判断是否为WAR模式
*/
val isWarMode: Boolean by lazy {
protocol == "war"
}
/**
* 判断是否不为WAR模式
*/
val isNotMode: Boolean by lazy {
protocol != "war"
}
/**
* 判断是否为JSWAR模式
*/
val isJswarMode: Boolean by lazy {
protocol == "jswar"
}
/**
* 判断是否不为JSWAR模式
*/
val isNotJswarMode: Boolean by lazy {
protocol != "jswar"
}
/**
* 加载并获取资源协议
* @param clazz 要获取协议的类对象默认为null
* @return 返回资源的协议字符串
*/
fun loadProtocol(clazz: Class<*>? = null): String {
// 如果已经加载过协议且clazz为null则直接返回已缓存的协议
if (isLoadprotocol && clazz == null) {
return protocol
}
val resource: URL? = clazz!!.getResource(
clazz.getSimpleName() + ".class"
)
protocol = resource!!.protocol
return protocol.let {
isLoadprotocol = true
return@let it
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ThreadEvent.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.thread
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
/**
* 线程事件同步工具类用于线程间的事件通知和等待
*/
open class ThreadEvent<T> {
private var latch = CountDownLatch(1)
@Volatile
private var data: T
constructor(data: T) {
this.data = data
}
/**
* 获取当前事件状态
*
* @return 当前事件的数据值如果未设置则返回null
*/
fun get(): T = data
/**
* 设置事件数据并释放等待的线程
*
* @param data 要设置的事件数据
*/
@Synchronized
fun set(data: T) {
this.data = data
latch.countDown()
}
/**
* 重置事件状态重新初始化CountDownLatch
*/
@Synchronized
fun reset() {
latch = CountDownLatch(1)
}
/**
* 等待事件被设置阻塞当前线程直到事件被触发
*/
fun await() {
latch.await()
}
/**
* 等待事件被设置并获取数据
*
* @return 事件数据当事件被设置后返回对应的数据
*/
fun awaitAndGet(): T {
await()
return data
}
/**
* 在指定超时时间内等待事件被设置并获取数据
*
* @param timeout 等待超时时间
* @param unit 时间单位
* @return 事件数据如果在超时时间内事件被设置则返回数据否则返回null
*/
fun awaitAndGet(timeout: Long, unit: TimeUnit): T? {
await(timeout, unit)
return data
}
/**
* 在指定超时时间内等待事件被设置
*
* @param timeout 等待超时时间
* @param unit 时间单位
* @return 如果在超时时间内事件被设置则返回true否则返回false
*/
fun await(timeout: Long, unit: TimeUnit): Boolean {
return latch.await(timeout, unit)
}
fun setAndReset(data: T) {
set(data)
reset()
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ThreadRunner.kt
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.thread
import com.mingliqiye.utils.netty.NamedThreadFactory
import com.mingliqiye.utils.system.availableProcessors
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
/**
* 线程执行器工具类用于管理线程池并提供异步任务执行功能
*/
object ThreadRunner {
var executorService: ExecutorService? = null
/**
* 关闭线程池执行器
* 首先尝试正常关闭如果失败则强制关闭
*/
@JvmStatic
fun close() {
try {
executorService?.shutdown()
} catch (_: Exception) {
executorService?.shutdownNow()
}
}
/**
* 初始化线程池执行器
* 创建固定大小的线程池线程数量为可用处理器核心数的两倍
*
* @param string 线程名称前缀默认为"MingliUtilThread"
*/
@JvmStatic
fun init(string: String = "MingliUtilThread") {
executorService = Executors.newFixedThreadPool(
availableProcessors * 2,
NamedThreadFactory { clazz, poolNumber, threadNumber ->
"$string #$threadNumber"
})
}
/**
* 异步执行Runnable任务
*
* @param runnable 要执行的Runnable任务
* @return Future对象可用于获取任务执行结果或控制任务状态
*/
@JvmStatic
fun runThread(runnable: Runnable): Future<*> {
return executorService!!.submit(runnable)
}
/**
* 异步执行Callable任务
*
* @param runnable 要执行的Callable任务
* @return Future对象可用于获取任务执行结果或控制任务状态
*/
@JvmStatic
fun <T> runThread(runnable: Callable<T>): Future<T> {
return executorService!!.submit(runnable)
}
/**
* 同步执行Runnable任务阻塞等待任务完成
*
* @param runnable 要执行的Runnable任务
* @return 任务执行结果通常为null
*/
@JvmStatic
fun runThreadAwait(runnable: Runnable): Any {
return executorService!!.submit(runnable).get()
}
/**
* 同步执行Callable任务阻塞等待任务完成并返回结果
*
* @param runnable 要执行的Callable任务
* @return 任务执行结果
*/
@JvmStatic
fun <T> runThreadAwait(runnable: Callable<T>): T {
return executorService!!.submit(runnable).get()
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ThreadUtils.kt
* LastUpdate 2026-01-31 20:48:19
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.thread
/**
* 线程工具类提供线程名称的获取和设置功能
*/
object ThreadUtils {
/**
* 获取或设置当前线程的名称
*
* Getter: 返回当前线程的名称
* Setter: 设置当前线程的名称
*
* @return 当前线程的名称字符串
*/
@JvmStatic
var name: String
get() = Thread.currentThread().name
set(s) {
Thread.currentThread().name = s
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTime.kt
* LastUpdate 2025-09-17 19:06:39
* LastUpdate 2026-02-04 21:54:04
* UpdateUser MingLiPro
*/
@ -26,7 +26,7 @@ import com.mingliqiye.utils.jna.FILETIME_EPOCH_OFFSET
import com.mingliqiye.utils.jna.NANOS_PER_100NS
import com.mingliqiye.utils.jna.WinKernel32Api
import com.mingliqiye.utils.jna.getWinKernel32Apis
import com.mingliqiye.utils.logger.mingLiLoggerFactory
import com.mingliqiye.utils.logger.MingLiLoggerFactory
import com.mingliqiye.utils.system.isWindows
import com.mingliqiye.utils.system.javaVersionAsInteger
import org.slf4j.Logger
@ -40,131 +40,6 @@ import kotlin.time.ExperimentalTime
import kotlin.time.Instant
/**
* 时间位移
*
* @author MingLiPro
*/
class DateTimeOffset private constructor(
val offsetType: ChronoUnit, val offset: Long
) {
companion object {
/**
* 创建一个新的DateTimeOffset实例
*
* @param offsetType 偏移量的单位类型指定偏移量的计算单位
* @param offset 偏移量的数值可以为正数负数或零
* @return 返回一个新的DateTimeOffset对象包含指定的偏移量信息
*/
@JvmStatic
fun of(offsetType: ChronoUnit, offset: Long): DateTimeOffset {
return DateTimeOffset(offsetType, offset)
}
/**
* 创建一个 DateTimeOffset 实例
*
* @param offset 偏移量数值
* @param offsetType 偏移量的时间单位类型
* @return 返回一个新的 DateTimeOffset 实例
*/
@JvmStatic
fun of(offset: Long, offsetType: ChronoUnit): DateTimeOffset {
return DateTimeOffset(offsetType, offset)
}
}
}
/**
* 时间格式化枚举类
*
*
* 定义了常用的时间格式化模式用于日期时间的解析和格式化操作
* 每个枚举常量包含对应的格式化字符串和字符串长度
*
*/
enum class Formatter(private val value: String) {
/**
* 标准日期时间格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
STANDARD_DATETIME_MILLISECOUND8("yyyy-MM-dd HH:mm:ss.SSSSSSSS"),
STANDARD_DATETIME_MILLISECOUND9("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"),
/**
* 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
*/
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
/**
* 标准日期时间格式(5位毫秒)yyyy-MM-dd HH:mm:ss.SSSSS
*/
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
/**
* 标准日期时间格式(4位毫秒)yyyy-MM-dd HH:mm:ss.SSSS
*/
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
/**
* 标准日期时间格式(3位毫秒)yyyy-MM-dd HH:mm:ss.SSS
*/
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
/**
* 标准日期时间格式(2位毫秒)yyyy-MM-dd HH:mm:ss.SS
*/
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
/**
* 标准日期时间格式(1位毫秒)yyyy-MM-dd HH:mm:ss.S
*/
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
/**
* 标准ISO格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
/**
* 标准日期时间秒格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期格式yyyy-MM-dd
*/
STANDARD_DATE("yyyy-MM-dd"),
/**
* ISO8601格式yyyy-MM-dd'T'HH:mm:ss.SSS'000'
*/
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
/**
* 紧凑型日期时间格式yyyyMMddHHmmss
*/
COMPACT_DATETIME("yyyyMMddHHmmss");
private val len: Int = value.length
fun getLen(): Int {
return this.len
}
fun getValue(): String {
return this.value
}
}
/**
* 时间类用于处理日期时间的转换格式化等操作
* 提供了多种静态方法来创建 DateTime 实例并支持与 DateLocalDateTime 等类型的互转
@ -186,7 +61,7 @@ class DateTime private constructor(
companion object {
private val WIN_KERNEL_32_API: WinKernel32Api? = if (javaVersionAsInteger == 8 && isWindows) {
val log: Logger = mingLiLoggerFactory.getLogger("mingli-utils DateTime")
val log: Logger = MingLiLoggerFactory.getLogger("mingli-utils DateTime")
val a = getWinKernel32Apis()
if (a.size > 1) {
@ -284,11 +159,10 @@ class DateTime private constructor(
timestr: String
): DateTime {
val formatterString = Formatter.STANDARD_DATETIME_MILLISECOUND9.getValue()
val formatterString = Formatter.STANDARD_DATETIME_MILLISECOUND9.value
return DateTime(
LocalDateTime.parse(
getFillZeroByLen(timestr, formatterString),
DateTimeFormatter.ofPattern(formatterString)
getFillZeroByLen(timestr, formatterString), DateTimeFormatter.ofPattern(formatterString)
)
)
}
@ -305,7 +179,7 @@ class DateTime private constructor(
fun parse(
timestr: String, formatter: Formatter, fillZero: Boolean
): DateTime {
return parse(timestr, formatter.getValue(), fillZero)
return parse(timestr, formatter.value, fillZero)
}
/**
@ -317,7 +191,7 @@ class DateTime private constructor(
*/
@JvmStatic
fun parse(timestr: String, formatter: Formatter): DateTime {
return parse(timestr, formatter.getValue())
return parse(timestr, formatter.value)
}
/**
@ -340,23 +214,24 @@ class DateTime private constructor(
* @return 补零后的时间字符串
*/
private fun getFillZeroByLen(dstr: String, formats: String): String {
if (dstr.length == formats.length) {
val formatslen = formats.replace("'", "").length
if (dstr.length == formatslen) {
return dstr
}
if (formats.length > dstr.length) {
if (formatslen > dstr.length) {
var modifiedDstr = dstr
if (dstr.length == 19) {
modifiedDstr += "."
}
val sb = StringBuilder(modifiedDstr)
for (i in 0 until formats.length - sb.length) {
for (i in 0 until formatslen - sb.length) {
sb.append("0")
}
return sb.toString()
}
throw IllegalArgumentException(
String.format(
"Text: '%s' len %s < %s %s", dstr, dstr.length, formats, formats.length
"Text: '%s' len %s < %s %s", dstr, dstr.length, formats, formatslen
)
)
}
@ -520,6 +395,20 @@ class DateTime private constructor(
)
}
/**
* 重载加法运算符将指定的DateTimeOffset添加到当前DateTime对象
* @param dateTimeOffset 要添加的时间偏移量
* @return 返回相加后的新DateTime对象
*/
operator fun plus(dateTimeOffset: DateTimeOffset): DateTime = add(dateTimeOffset)
/**
* 重载减法运算符从当前DateTime对象中减去指定的DateTimeOffset
* @param dateTimeOffset 要减去的时间偏移量
* @return 返回相减后的新DateTime对象
*/
operator fun minus(dateTimeOffset: DateTimeOffset): DateTime = sub(dateTimeOffset)
/**
* 在当前时间基础上减少指定的时间偏移量
*
@ -547,11 +436,11 @@ class DateTime private constructor(
* @return 返回格式化后的时间字符串
*/
fun format(formatter: Formatter): String {
return format(formatter.getValue())
return format(formatter.value)
}
fun format(): String {
return format(Formatter.STANDARD_DATETIME_MILLISECOUND9.getValue(), true)
return format(Formatter.STANDARD_DATETIME_MILLISECOUND9.value, true)
}
/**
@ -581,7 +470,7 @@ class DateTime private constructor(
* @return 返回格式化后的时间字符串
*/
fun format(formatter: Formatter, repcZero: Boolean): String {
return format(formatter.getValue(), repcZero)
return format(formatter.value, repcZero)
}
/**
@ -668,7 +557,6 @@ class DateTime private constructor(
*/
fun toNanoTime(): Long {
val instant = toInstant()
return try {
val secondsInNanos = Math.multiplyExact(instant.epochSecond, 1_000_000_000L)
Math.addExact(secondsInNanos, instant.nano.toLong())

View File

@ -0,0 +1,30 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTimeJsonFormat.kt
* LastUpdate 2026-02-04 22:14:47
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time
@Target(AnnotationTarget.FIELD)
annotation class DateTimeJsonFormat(
val value: Formatter = Formatter.NONE,
val formatter: String = "",
val repcZero: Boolean = true,
)

View File

@ -0,0 +1,61 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTimeOffset.kt
* LastUpdate 2026-02-04 21:54:04
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time
import java.time.temporal.ChronoUnit
/**
* 时间位移
*
* @author MingLiPro
*/
class DateTimeOffset private constructor(
val offsetType: ChronoUnit, val offset: Long
) {
companion object {
/**
* 创建一个新的DateTimeOffset实例
*
* @param offsetType 偏移量的单位类型指定偏移量的计算单位
* @param offset 偏移量的数值可以为正数负数或零
* @return 返回一个新的DateTimeOffset对象包含指定的偏移量信息
*/
@JvmStatic
fun of(offsetType: ChronoUnit, offset: Long): DateTimeOffset {
return DateTimeOffset(offsetType, offset)
}
/**
* 创建一个 DateTimeOffset 实例
*
* @param offset 偏移量数值
* @param offsetType 偏移量的时间单位类型
* @return 返回一个新的 DateTimeOffset 实例
*/
@JvmStatic
fun of(offset: Long, offsetType: ChronoUnit): DateTimeOffset {
return DateTimeOffset(offsetType, offset)
}
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Formatter.kt
* LastUpdate 2026-02-04 21:54:36
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time
/**
* 时间格式化枚举类
*
*
* 定义了常用的时间格式化模式用于日期时间的解析和格式化操作
* 每个枚举常量包含对应的格式化字符串和字符串长度
*
*/
enum class Formatter(val value: String) {
/**
* 标准日期时间格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
NONE(""),
/**
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
STANDARD_DATETIME_MILLISECOUND8("yyyy-MM-dd HH:mm:ss.SSSSSSSS"),
STANDARD_DATETIME_MILLISECOUND9("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"),
/**
* 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
*/
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
/**
* 标准日期时间格式(5位毫秒)yyyy-MM-dd HH:mm:ss.SSSSS
*/
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
/**
* 标准日期时间格式(4位毫秒)yyyy-MM-dd HH:mm:ss.SSSS
*/
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
/**
* 标准日期时间格式(3位毫秒)yyyy-MM-dd HH:mm:ss.SSS
*/
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
/**
* 标准日期时间格式(2位毫秒)yyyy-MM-dd HH:mm:ss.SS
*/
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
/**
* 标准日期时间格式(1位毫秒)yyyy-MM-dd HH:mm:ss.S
*/
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
/**
* 标准ISO格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
/**
* 标准日期时间秒格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期格式yyyy-MM-dd
*/
STANDARD_DATE("yyyy-MM-dd"),
/**
* ISO8601格式yyyy-MM-dd'T'HH:mm:ss.SSSSSS
*/
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"),
/**
* 紧凑型日期时间格式yyyyMMddHHmmss
*/
COMPACT_DATETIME("yyyyMMddHHmmss");
private val len: Int = value.replace("'", "").length
fun getLen(): Int {
return this.len
}
}

View File

@ -16,16 +16,14 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUID.kt
* LastUpdate 2026-01-08 13:21:00
* LastUpdate 2026-02-05 11:20:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.uuid
import com.mingliqiye.utils.base.BASE256
import com.mingliqiye.utils.base.BASE64
import com.mingliqiye.utils.base.BASE91
import com.mingliqiye.utils.random.randomByteSecure
import com.mingliqiye.utils.base.*
import com.mingliqiye.utils.random.randomByte
import com.mingliqiye.utils.random.secureRandom
import com.mingliqiye.utils.system.macAddressBytes
import com.mingliqiye.utils.time.DateTime
@ -109,7 +107,7 @@ class UUID : Serializable {
*/
@JvmStatic
fun getV4(): UUID {
val randomBytes = randomByteSecure(16)
val randomBytes = randomByte(16)
randomBytes[6] = (randomBytes[6].toInt() and 0x0F).toByte()
randomBytes[6] = (randomBytes[6].toInt() or 0x40).toByte()
randomBytes[8] = (randomBytes[8].toInt() and 0x3F).toByte()
@ -192,7 +190,7 @@ class UUID : Serializable {
val buffer = ByteBuffer.allocate(16)
buffer.putInt((instant shr 16).toInt())
buffer.putShort((instant).toShort())
buffer.put(randomByteSecure(2))
buffer.put(randomByte(2))
buffer.putLong(secureRandom.nextLong())
val bytes = buffer.array()
bytes[6] = (bytes[6].toInt() and 0x0F or 0x70).toByte()
@ -363,6 +361,10 @@ class UUID : Serializable {
return result
}
fun of(str: String, base: BaseType): UUID {
return UUID(base.baseCodec.decode(str))
}
}
/**
@ -485,6 +487,18 @@ class UUID : Serializable {
}
}
fun getString(uuidFormatType: UUIDFormatType): String {
return getString(isUpper = uuidFormatType.isUpper, isnotSpace = uuidFormatType.isnotSpace)
}
fun getString(baseType: BaseType): String {
return getString(baseType.baseCodec)
}
fun getString(baseCodec: BaseCodec): String {
return baseCodec.encode(data)
}
/**
* 返回标准格式的 UUID 字符串带连字符
*

View File

@ -0,0 +1,30 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUIDFormatType.kt
* LastUpdate 2026-02-04 21:54:04
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.uuid
enum class UUIDFormatType(val isUpper: Boolean, val isnotSpace: Boolean) {
UPPER_SPACE(true, false),
NO_UPPER_SPACE(false, false),
NO_UPPER_NO_SPACE(false, true),
UPPER_NO_SPACE(true, true),
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUIDJsonFormat.kt
* LastUpdate 2026-02-04 22:14:47
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.uuid
import com.mingliqiye.utils.base.BaseType
@Target(AnnotationTarget.FIELD)
annotation class UUIDJsonFormat(
val value: UUIDFormatType = UUIDFormatType.NO_UPPER_SPACE,
val base: BaseType = BaseType.BASE16
)

View File

@ -1,5 +1,5 @@
#
# Copyright 2025 mingliqiye
# Copyright 2026 mingliqiye
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -16,10 +16,9 @@
# ProjectName mingli-utils
# ModuleName mingli-utils.main
# CurrentFile org.springframework.boot.autoconfigure.AutoConfiguration.imports
# LastUpdate 2025-09-15 22:32:50
# LastUpdate 2026-02-04 16:47:27
# UpdateUser MingLiPro
#
com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration
com.mingliqiye.utils.springboot.autoconfigure.JacksonAutoConfiguration
com.mingliqiye.utils.springboot.autoconfigure.GsonAutoConfiguration

View File

@ -0,0 +1,82 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.test
* CurrentFile JsonTest.kt
* LastUpdate 2026-02-05 10:57:18
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils
import com.mingliqiye.utils.base.BaseType
import com.mingliqiye.utils.io.IO.println
import com.mingliqiye.utils.json.api.JSONA
import com.mingliqiye.utils.json.api.JacksonJsonApi
import com.mingliqiye.utils.json.converters.DateTimeJsonConverter
import com.mingliqiye.utils.json.converters.UUIDJsonConverter
import com.mingliqiye.utils.string.formatd
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.DateTimeJsonFormat
import com.mingliqiye.utils.time.Formatter
import com.mingliqiye.utils.uuid.UUID
import com.mingliqiye.utils.uuid.UUIDFormatType
import com.mingliqiye.utils.uuid.UUIDJsonFormat
import org.junit.jupiter.api.Test
class JsonTest {
@Test
fun testJSONA() {
JSONA.setJsonApi(
JacksonJsonApi(
JSONA.jacksonKotlinObjectMapper()
)
)
JSONA.addJsonConverter<UUIDJsonConverter>()
JSONA.addJsonConverter<DateTimeJsonConverter>()
"{} {} {} {}".formatd("0", 1).println()
}
@Test
fun testBANNER() {
com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration.printBanner()
}
}
data class AC<T>(
var a: String = "AC",
@field:DateTimeJsonFormat(Formatter.ISO8601, repcZero = false)
var time: DateTime = DateTime.now(),
@field:UUIDJsonFormat(
base = BaseType.BASE256,
value = UUIDFormatType.UPPER_NO_SPACE
)
var uuid: UUID = UUID.getV4(),
var b: T
)
data class BC<T>(
var a: String = "BC",
var b: T
)
data class CC<T>(
var a: String = "BC",
var b: T
)