mirror of
https://gitee.com/san-bing/JChargePointProtocol
synced 2026-05-04 09:59:55 +08:00
云快充1.5.0 初始化
This commit is contained in:
81
jcpp-infrastructure-util/pom.xml
Normal file
81
jcpp-infrastructure-util/pom.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
抖音关注:程序员三丙
|
||||
知识星球:https://t.zsxq.com/j9b21
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>sanbing</groupId>
|
||||
<artifactId>jcpp-parent</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jcpp-infrastructure-util</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>JChargePointProtocol Infrastructure Util Module</name>
|
||||
<description>基础工具模块</description>
|
||||
|
||||
<properties>
|
||||
<main.dir>${basedir}/..</main.dir>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-json</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.el</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util;
|
||||
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author baigod
|
||||
*/
|
||||
public class JCPPHashUtil {
|
||||
public static HashFunction forName(String name) {
|
||||
return switch (name) {
|
||||
case "murmur3_32" -> Hashing.murmur3_32_fixed();
|
||||
case "murmur3_128" -> Hashing.murmur3_128();
|
||||
case "sha256" -> Hashing.sha256();
|
||||
default -> throw new IllegalArgumentException("Can't find hash function with name " + name);
|
||||
};
|
||||
}
|
||||
|
||||
public static int hash(HashFunction hashFunction, String key) {
|
||||
return hashFunction.hashString(key, StandardCharsets.UTF_8).asInt();
|
||||
}
|
||||
|
||||
public static int hash(HashFunction hashFunction, UUID key) {
|
||||
return hashFunction.hashString(key.toString(), StandardCharsets.UTF_8).asInt();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class JCPPPair<S, T> {
|
||||
private S first;
|
||||
private T second;
|
||||
|
||||
public static <S, T> JCPPPair<S, T> of(S first, T second) {
|
||||
return new JCPPPair<>(first, second);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.GlobalMemory;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.file.FileStore;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
public class SystemUtil {
|
||||
|
||||
private static final HardwareAbstractionLayer HARDWARE;
|
||||
|
||||
static {
|
||||
HARDWARE = new SystemInfo().getHardware();
|
||||
}
|
||||
|
||||
public static Optional<Integer> getMemoryUsage() {
|
||||
try {
|
||||
GlobalMemory memory = HARDWARE.getMemory();
|
||||
long total = memory.getTotal();
|
||||
long available = memory.getAvailable();
|
||||
return Optional.of(toPercent(total - available, total));
|
||||
} catch (Throwable e) {
|
||||
log.debug("Failed to get memory usage!!!", e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static Optional<Long> getTotalMemory() {
|
||||
try {
|
||||
return Optional.of(HARDWARE.getMemory().getTotal());
|
||||
} catch (Throwable e) {
|
||||
log.debug("Failed to get total memory!!!", e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static Optional<Integer> getCpuUsage() {
|
||||
try {
|
||||
return Optional.of((int) (HARDWARE.getProcessor().getSystemCpuLoad(1000) * 100.0));
|
||||
} catch (Throwable e) {
|
||||
log.debug("Failed to get cpu usage!!!", e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static Optional<Integer> getCpuCount() {
|
||||
try {
|
||||
return Optional.of(HARDWARE.getProcessor().getLogicalProcessorCount());
|
||||
} catch (Throwable e) {
|
||||
log.debug("Failed to get total cpu count!!!", e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static Optional<Integer> getDiscSpaceUsage() {
|
||||
try {
|
||||
FileStore store = Files.getFileStore(Paths.get("/"));
|
||||
long total = store.getTotalSpace();
|
||||
long available = store.getUsableSpace();
|
||||
return Optional.of(toPercent(total - available, total));
|
||||
} catch (Throwable e) {
|
||||
log.debug("Failed to get free disc space!!!", e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static Optional<Long> getTotalDiscSpace() {
|
||||
try {
|
||||
FileStore store = Files.getFileStore(Paths.get("/"));
|
||||
return Optional.of(store.getTotalSpace());
|
||||
} catch (Throwable e) {
|
||||
log.debug("Failed to get total disc space!!!", e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static int toPercent(long used, long total) {
|
||||
BigDecimal u = new BigDecimal(used);
|
||||
BigDecimal t = new BigDecimal(total);
|
||||
BigDecimal i = new BigDecimal(100);
|
||||
return u.multiply(i).divide(t, RoundingMode.HALF_UP).intValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.annotation;
|
||||
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
@Order
|
||||
public @interface AfterStartUp {
|
||||
|
||||
int DISCOVERY_SERVICE = 1;
|
||||
int REGULAR_SERVICE = 2;
|
||||
|
||||
@AliasFor(annotation = Order.class, attribute = "value")
|
||||
int order();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.annotation;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author baigod
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='app'")
|
||||
@Component
|
||||
public @interface AppComponent {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.annotation;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.stereotype.Component;
|
||||
import sanbing.jcpp.infrastructure.util.annotation.ProtocolComponent.ProtocolCondition;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author baigod
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Conditional(ProtocolCondition.class)
|
||||
@Component
|
||||
public @interface ProtocolComponent {
|
||||
|
||||
@AliasFor(annotation = Component.class)
|
||||
String value() default "";
|
||||
|
||||
class ProtocolCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
if (!metadata.isAnnotated(ProtocolComponent.class.getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String serviceType = context.getEnvironment().getProperty("service.type", "null");
|
||||
|
||||
String protocolName = (String) metadata.getAnnotationAttributes(ProtocolComponent.class.getName()).get("value");
|
||||
|
||||
String enabled = context.getEnvironment().getProperty("service.protocols." + protocolName + ".enabled", "false");
|
||||
|
||||
return ("monolith".equals(serviceType) || "protocol".equals(serviceType)) && "true".equals(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.async;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class JCPPAsynchron {
|
||||
|
||||
public static <T> void withCallback(ListenableFuture<T> future, Consumer<T> onSuccess,
|
||||
Consumer<Throwable> onFailure) {
|
||||
withCallback(future, onSuccess, onFailure, null);
|
||||
}
|
||||
|
||||
public static <T> void withCallback(ListenableFuture<T> future, Consumer<T> onSuccess,
|
||||
Consumer<Throwable> onFailure, Executor executor) {
|
||||
FutureCallback<T> callback = new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(T result) {
|
||||
try {
|
||||
onSuccess.accept(result);
|
||||
} catch (Throwable th) {
|
||||
onFailure(th);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
onFailure.accept(t);
|
||||
}
|
||||
};
|
||||
Futures.addCallback(future, callback, Objects.requireNonNullElseGet(executor, MoreExecutors::directExecutor));
|
||||
}
|
||||
|
||||
public static <T> ListenableFuture<T> submit(Callable<T> task, Consumer<T> onSuccess, Consumer<Throwable> onFailure, Executor executor) {
|
||||
return submit(task, onSuccess, onFailure, executor, null);
|
||||
}
|
||||
|
||||
public static <T> ListenableFuture<T> submit(Callable<T> task, Consumer<T> onSuccess, Consumer<Throwable> onFailure, Executor executor, Executor callbackExecutor) {
|
||||
ListenableFuture<T> future = Futures.submit(task, executor);
|
||||
withCallback(future, onSuccess, onFailure, callbackExecutor);
|
||||
return future;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.async;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
|
||||
public class JCPPExecutors {
|
||||
|
||||
public static ExecutorService newWorkStealingPool(int parallelism, String namePrefix) {
|
||||
return new ForkJoinPool(parallelism,
|
||||
new JCPPForkJoinWorkerThreadFactory(namePrefix),
|
||||
null, true);
|
||||
}
|
||||
|
||||
public static ExecutorService newWorkStealingPool(int parallelism, Class<?> clazz) {
|
||||
return newWorkStealingPool(parallelism, clazz.getSimpleName());
|
||||
}
|
||||
|
||||
public static ExecutorService newVirtualThreadPool(String namePrefix) {
|
||||
return Executors.newThreadPerTaskExecutor(new JCPPVirtualThreadFactory(namePrefix));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.async;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinWorkerThread;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@ToString
|
||||
public class JCPPForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {
|
||||
private final String namePrefix;
|
||||
private final AtomicLong threadNumber = new AtomicLong(1);
|
||||
|
||||
public JCPPForkJoinWorkerThreadFactory(@NonNull String namePrefix) {
|
||||
this.namePrefix = namePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
|
||||
ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
|
||||
thread.setContextClassLoader(this.getClass().getClassLoader());
|
||||
thread.setName(namePrefix + "-" + thread.getPoolIndex() + "-" + threadNumber.getAndIncrement());
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.async;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public class JCPPThreadFactory {
|
||||
public static final String THREAD_TOPIC_SEPARATOR = " | ";
|
||||
|
||||
public static ThreadFactory forName(String name) {
|
||||
return new ThreadFactoryBuilder()
|
||||
.setNameFormat(name)
|
||||
.setDaemon(true)
|
||||
.setPriority(Thread.NORM_PRIORITY)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ThreadFactory forName(String name, int priority) {
|
||||
return new ThreadFactoryBuilder()
|
||||
.setNameFormat(name)
|
||||
.setDaemon(true)
|
||||
.setPriority(priority)
|
||||
.build();
|
||||
}
|
||||
public static void updateCurrentThreadName(String threadSuffix) {
|
||||
String name = Thread.currentThread().getName();
|
||||
int spliteratorIndex = name.indexOf(THREAD_TOPIC_SEPARATOR);
|
||||
if (spliteratorIndex > 0) {
|
||||
name = name.substring(0, spliteratorIndex);
|
||||
}
|
||||
name = name + THREAD_TOPIC_SEPARATOR + threadSuffix;
|
||||
Thread.currentThread().setName(name);
|
||||
}
|
||||
|
||||
public static void addThreadNamePrefix(String prefix) {
|
||||
String name = Thread.currentThread().getName();
|
||||
name = prefix + "-" + name;
|
||||
Thread.currentThread().setName(name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.async;
|
||||
|
||||
import sanbing.jcpp.infrastructure.util.trace.TracerRunnable;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class JCPPVirtualThreadFactory implements ThreadFactory {
|
||||
private final String namePrefix;
|
||||
private final AtomicLong threadNumber = new AtomicLong(1);
|
||||
|
||||
public JCPPVirtualThreadFactory(String namePrefix) {
|
||||
this.namePrefix = namePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return Thread.ofVirtual().name(namePrefix + "-" + threadNumber.getAndIncrement()).unstarted(new TracerRunnable(r));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.codec;
|
||||
|
||||
|
||||
public class BCDUtil {
|
||||
private static final String HEX = "0123456789ABCDEF";
|
||||
|
||||
/**
|
||||
* 十进制 转 BCD字节数组
|
||||
*
|
||||
* @param num long (8字节)
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] longToBcdBytes(long num) {
|
||||
int digits = 0;
|
||||
long temp = num;
|
||||
while (temp != 0) {
|
||||
digits++;
|
||||
temp /= 10;
|
||||
}
|
||||
int byteLen = digits % 2 == 0 ? digits / 2 : (digits + 1) / 2;
|
||||
byte[] bcd = new byte[byteLen];
|
||||
for (int i = 0; i < digits; i++) {
|
||||
byte tmp = (byte) (num % 10);
|
||||
if (i % 2 == 0) {
|
||||
bcd[i / 2] = tmp;
|
||||
} else {
|
||||
bcd[i / 2] |= (byte) (tmp << 4);
|
||||
}
|
||||
num /= 10;
|
||||
}
|
||||
for (int i = 0; i < byteLen / 2; i++) {
|
||||
byte tmp = bcd[i];
|
||||
bcd[i] = bcd[byteLen - i - 1];
|
||||
bcd[byteLen - i - 1] = tmp;
|
||||
}
|
||||
return bcd;
|
||||
}
|
||||
|
||||
/**
|
||||
* BCD字节数组 转 十进制
|
||||
*
|
||||
* @param bcd byte[]
|
||||
* @return long
|
||||
*/
|
||||
public static long bcdBytesToLong(byte[] bcd) {
|
||||
return Long.parseLong(BCDUtil.toString(bcd));
|
||||
}
|
||||
|
||||
/**
|
||||
* bcd字节数组 转 数字字符串
|
||||
*
|
||||
* @param bcd byte[]
|
||||
* @return String
|
||||
*/
|
||||
public static String toString(byte[] bcd) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bcd) {
|
||||
sb.append(toString(b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字节BCD 转 数字字符串
|
||||
*
|
||||
* @param bcd byte
|
||||
* @return String
|
||||
*/
|
||||
public static String toString(byte bcd) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
byte high = (byte) (bcd & 0xf0);
|
||||
high >>>= (byte) 4;
|
||||
high = (byte) (high & 0x0f);
|
||||
byte low = (byte) (bcd & 0x0f);
|
||||
|
||||
sb.append(high);
|
||||
sb.append(low);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字字符串 转 BCD字节数组
|
||||
*
|
||||
* @param str 数字字符串
|
||||
* @return BCD字节数组
|
||||
*/
|
||||
public static byte[] numStrToBcdBytes(String str) {
|
||||
//若为奇数,补0为偶
|
||||
if ((str.length() & 0x1) == 1) {
|
||||
str = "0" + str;
|
||||
}
|
||||
byte[] ret = new byte[str.length() / 2];
|
||||
byte[] bs = str.getBytes();
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
byte high = ascII2Bcd(bs[2 * i]);
|
||||
byte low = ascII2Bcd(bs[2 * i + 1]);
|
||||
ret[i] = (byte) ((high << 4) | low);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte ascII2Bcd(byte asc) {
|
||||
if ((asc >= '0') && (asc <= '9'))
|
||||
return (byte) (asc - '0');
|
||||
else if ((asc >= 'A') && (asc <= 'F'))
|
||||
return (byte) (asc - 'A' + 10);
|
||||
else if ((asc >= 'a') && (asc <= 'f'))
|
||||
return (byte) (asc - 'a' + 10);
|
||||
else
|
||||
return (byte) (asc - 48);
|
||||
}
|
||||
|
||||
/**
|
||||
* BCD 转 数字
|
||||
*
|
||||
* @param bcd byte
|
||||
* @return int
|
||||
*/
|
||||
public static int bcdByteToInt(byte bcd) {
|
||||
return ((bcd & 0xF0) >>> 4) * 10 + (bcd & 0x0F);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* char to byte
|
||||
*
|
||||
* @param c char
|
||||
* @return byte
|
||||
*/
|
||||
private static byte charToByte(char c) {
|
||||
return (byte) HEX.indexOf(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hex 转 BCD字节数组
|
||||
*
|
||||
* @param hex String
|
||||
* @return BCD字节数组
|
||||
*/
|
||||
public static byte[] toBytes(String hex) {
|
||||
int len = (hex.length() / 2);
|
||||
byte[] result = new byte[len];
|
||||
char[] cr = hex.toCharArray();
|
||||
for (int i = 0; i < len; i++) {
|
||||
int pos = i * 2;
|
||||
result[i] = (byte) (charToByte(cr[pos]) << 4 | charToByte(cr[pos + 1]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* BCD字节数组 转 Hex
|
||||
*
|
||||
* @param bcd BCD字节数组
|
||||
* @return Hex
|
||||
*/
|
||||
public static String bcdBytesToHex(byte[] bcd) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bcd) {
|
||||
int highNibble = (b >> 4) & 0x0F;
|
||||
int lowNibble = b & 0x0F;
|
||||
sb.append(Integer.toHexString(highNibble));
|
||||
sb.append(Integer.toHexString(lowNibble));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.codec;
|
||||
|
||||
import cn.hutool.core.io.checksum.crc16.CRC16Modbus;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import sanbing.jcpp.infrastructure.util.JCPPPair;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author baigod
|
||||
*/
|
||||
public class ByteUtil {
|
||||
|
||||
public static byte[] uuidToBytes(UUID uuid) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(16);
|
||||
buf.putLong(uuid.getMostSignificantBits());
|
||||
buf.putLong(uuid.getLeastSignificantBits());
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
public static UUID bytesToUuid(byte[] bytes) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||
long firstLong = bb.getLong();
|
||||
long secondLong = bb.getLong();
|
||||
return new UUID(firstLong, secondLong);
|
||||
}
|
||||
|
||||
public static byte[] stringToBytes(String string) {
|
||||
return string.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String bytesToString(byte[] data) {
|
||||
return new String(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static byte[] longToBytes(long x) {
|
||||
ByteBuffer longBuffer = ByteBuffer.allocate(Long.BYTES);
|
||||
longBuffer.putLong(0, x);
|
||||
return longBuffer.array();
|
||||
}
|
||||
|
||||
public static long bytesToLong(byte[] bytes) {
|
||||
return ByteBuffer.wrap(bytes).getLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算校验和
|
||||
*/
|
||||
public static int crcSum(byte[] data) {
|
||||
CRC16Modbus crc16Modbus = new CRC16Modbus();
|
||||
crc16Modbus.update(data);
|
||||
return (int) crc16Modbus.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证校验和
|
||||
*/
|
||||
public static JCPPPair<Boolean, Integer> checkCrcSum(byte[] data, int checkSum) {
|
||||
int expectedCs = crcSum(data);
|
||||
return JCPPPair.of(expectedCs == checkSum, expectedCs);
|
||||
}
|
||||
|
||||
/**
|
||||
* ByteBuf转byte数组
|
||||
*
|
||||
* @param byteBuf
|
||||
* @return
|
||||
*/
|
||||
public static byte[] toBytes(ByteBuf byteBuf) {
|
||||
int msgLength = byteBuf.readableBytes();
|
||||
byte[] bytes = new byte[msgLength];
|
||||
byteBuf.readBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.codec;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
public class CP56Time2aUtil {
|
||||
/**
|
||||
* 解码 CP56Time2a 字节数组为 Instant 对象
|
||||
*
|
||||
* @param bytes 字节数组
|
||||
* @return Instant 对象
|
||||
*/
|
||||
public static Instant decode(byte[] bytes) {
|
||||
// 将字节数组解释为各个时间部分
|
||||
int milliseconds = ((bytes[0] & 0xFF) + ((bytes[1] & 0xFF) << 8)); // 处理字节的无符号值
|
||||
int minutes = bytes[2] & 0x3F;
|
||||
int hours = bytes[3] & 0x1F;
|
||||
int days = bytes[4] & 0x1F;
|
||||
int months = bytes[5] & 0x0F;
|
||||
int years = bytes[6] & 0x7F;
|
||||
|
||||
// 将 CP56Time2a 转换为 LocalDateTime
|
||||
LocalDateTime dateTime = LocalDateTime.of(
|
||||
years + 2000,
|
||||
months,
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
milliseconds / 1000 // 秒数
|
||||
);
|
||||
|
||||
// 返回对应的 Instant 对象
|
||||
return dateTime.atZone(ZoneId.systemDefault()).toInstant();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码 Instant 对象为 CP56Time2a 字节数组
|
||||
*
|
||||
* @param instant Instant 对象
|
||||
* @return 字节数组
|
||||
*/
|
||||
public static byte[] encode(Instant instant) {
|
||||
// 将 Instant 转换到 LocalDateTime
|
||||
LocalDateTime aTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
|
||||
byte[] result = new byte[7];
|
||||
int milliseconds = aTime.getSecond() * 1000; // 获取毫秒部分
|
||||
|
||||
// 填充字节数组
|
||||
result[0] = (byte) (milliseconds % 256);
|
||||
result[1] = (byte) (milliseconds / 256);
|
||||
result[2] = (byte) aTime.getMinute();
|
||||
result[3] = (byte) aTime.getHour();
|
||||
result[4] = (byte) aTime.getDayOfMonth();
|
||||
result[5] = (byte) aTime.getMonthValue(); // 1-12
|
||||
result[6] = (byte) (aTime.getYear() % 100); // 00-99
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.config;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.metadata.ConstraintDescriptor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hibernate.validator.HibernateValidator;
|
||||
import org.hibernate.validator.HibernateValidatorConfiguration;
|
||||
import org.hibernate.validator.cfg.ConstraintMapping;
|
||||
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
|
||||
import org.hibernate.validator.internal.engine.ConfigurationImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import sanbing.jcpp.infrastructure.util.exception.DataValidationException;
|
||||
import sanbing.jcpp.infrastructure.util.validation.Length;
|
||||
import sanbing.jcpp.infrastructure.util.validation.StringLengthValidator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class ConstraintValidator {
|
||||
|
||||
private static Validator fieldsValidator;
|
||||
|
||||
static {
|
||||
initializeValidators();
|
||||
}
|
||||
|
||||
public static void validateFields(Object data) {
|
||||
validateFields(data, "Validation error: ");
|
||||
}
|
||||
|
||||
public static void validateFields(Object data, String errorPrefix) {
|
||||
Set<ConstraintViolation<Object>> constraintsViolations = fieldsValidator.validate(data);
|
||||
if (!constraintsViolations.isEmpty()) {
|
||||
throw new DataValidationException(errorPrefix + getErrorMessage(constraintsViolations));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getErrorMessage(Collection<ConstraintViolation<Object>> constraintsViolations) {
|
||||
return constraintsViolations.stream()
|
||||
.map(ConstraintValidator::getErrorMessage)
|
||||
.distinct().sorted().collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
public static String getErrorMessage(ConstraintViolation<Object> constraintViolation) {
|
||||
ConstraintDescriptor<?> constraintDescriptor = constraintViolation.getConstraintDescriptor();
|
||||
String property = (String) constraintDescriptor.getAttributes().get("fieldName");
|
||||
if (StringUtils.isEmpty(property) && !(constraintDescriptor.getAnnotation() instanceof AssertTrue)) {
|
||||
property = Iterators.getLast(constraintViolation.getPropertyPath().iterator()).toString();
|
||||
}
|
||||
|
||||
String error = "";
|
||||
if (StringUtils.isNotEmpty(property)) {
|
||||
error += property + " ";
|
||||
}
|
||||
error += constraintViolation.getMessage();
|
||||
return error;
|
||||
}
|
||||
|
||||
private static void initializeValidators() {
|
||||
HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure();
|
||||
|
||||
ConstraintMapping constraintMapping = getCustomConstraintMapping();
|
||||
validatorConfiguration.addMapping(constraintMapping);
|
||||
|
||||
fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocalValidatorFactoryBean validatorFactoryBean() {
|
||||
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
|
||||
localValidatorFactoryBean.setConfigurationInitializer(configuration -> {
|
||||
((ConfigurationImpl) configuration).addMapping(getCustomConstraintMapping());
|
||||
});
|
||||
return localValidatorFactoryBean;
|
||||
}
|
||||
|
||||
private static ConstraintMapping getCustomConstraintMapping() {
|
||||
ConstraintMapping constraintMapping = new DefaultConstraintMapping(null);
|
||||
constraintMapping.constraintDefinition(Length.class).validatedBy(StringLengthValidator.class);
|
||||
return constraintMapping;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.config;
|
||||
|
||||
import com.google.common.hash.HashFunction;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory;
|
||||
import sanbing.jcpp.infrastructure.util.trace.TracerRunnable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import static sanbing.jcpp.infrastructure.util.JCPPHashUtil.forName;
|
||||
import static sanbing.jcpp.infrastructure.util.JCPPHashUtil.hash;
|
||||
|
||||
/**
|
||||
* @author baigod
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ShardingThreadPool {
|
||||
@Value("${thread-pool.sharding.hash_function_name:murmur3_128}")
|
||||
private String hashFunctionName;
|
||||
|
||||
@Value("${thread-pool.sharding.parallelism:128}")
|
||||
private int parallelism;
|
||||
|
||||
private HashFunction hashFunction;
|
||||
|
||||
private final Map<Integer, ExecutorService> EXECUTOR_SERVICE_MAP = new ConcurrentHashMap<>(128);
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.hashFunction = forName(hashFunctionName);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
for (ExecutorService executorService : EXECUTOR_SERVICE_MAP.values()) {
|
||||
executorService.shutdownNow();
|
||||
log.info("Sharding Thread [{}] Shutdown completed.", executorService);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelayString = "${thread-pool.sharding.stats-print-interval-ms:10000}")
|
||||
public void printStats() {
|
||||
EXECUTOR_SERVICE_MAP.forEach((k, v) -> {
|
||||
|
||||
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) v;
|
||||
|
||||
log.info("分区 {}/{} 的线程池中剩余 {} 条待执行任务,当前正在执行的线程数 {}, 已完成任务 {} / {}",
|
||||
k,
|
||||
EXECUTOR_SERVICE_MAP.size(),
|
||||
threadPoolExecutor.getQueue().size(),
|
||||
threadPoolExecutor.getActiveCount(),
|
||||
threadPoolExecutor.getCompletedTaskCount(),
|
||||
threadPoolExecutor.getTaskCount());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交分片任务
|
||||
*/
|
||||
public void execute(UUID hashKey, TracerRunnable runnable) {
|
||||
int partition = hash(hashFunction, hashKey);
|
||||
|
||||
EXECUTOR_SERVICE_MAP.computeIfAbsent(partition % parallelism,
|
||||
p -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("sharding-threads-" + p)))
|
||||
.execute(runnable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.config;
|
||||
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import sanbing.jcpp.infrastructure.util.async.JCPPExecutors;
|
||||
import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author baigod
|
||||
*/
|
||||
@Configuration
|
||||
public class ThreadPoolConfiguration {
|
||||
|
||||
public static final ExecutorService JCPP_COMMON_THREAD_POOL = JCPPExecutors.newVirtualThreadPool("jcpp-common-virtual");
|
||||
|
||||
public static final ScheduledExecutorService PROTOCOL_SESSION_SCHEDULED = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("protocol-session-schedule"));
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
PROTOCOL_SESSION_SCHEDULED.shutdownNow();
|
||||
|
||||
JCPP_COMMON_THREAD_POOL.shutdown();
|
||||
|
||||
try {
|
||||
if (!JCPP_COMMON_THREAD_POOL.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
JCPP_COMMON_THREAD_POOL.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
JCPP_COMMON_THREAD_POOL.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.exception;
|
||||
|
||||
public class DataValidationException extends RuntimeException {
|
||||
|
||||
public DataValidationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DataValidationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.exception;
|
||||
|
||||
/**
|
||||
* @author baigod
|
||||
*/
|
||||
public class DownlinkException extends RuntimeException {
|
||||
|
||||
public DownlinkException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DownlinkException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.exception;
|
||||
|
||||
|
||||
public class IncorrectParameterException extends RuntimeException {
|
||||
|
||||
public IncorrectParameterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public IncorrectParameterException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
|
||||
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@JacksonStdImpl
|
||||
public class BigNumberSerializer extends NumberSerializer {
|
||||
|
||||
private static final long JS_NUM_MAX = 9007199254740992L;
|
||||
private static final long JS_NUM_MIN = -9007199254740992L;
|
||||
public static final BigNumberSerializer instance = new BigNumberSerializer(Number.class);
|
||||
|
||||
public BigNumberSerializer(Class<? extends Number> rawType) {
|
||||
super(rawType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
long longValue = value.longValue();
|
||||
if (longValue >= JS_NUM_MIN && longValue <= JS_NUM_MAX) {
|
||||
super.serialize(value, gen, provider);
|
||||
} else {
|
||||
gen.writeString(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 类型转换
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class DataTypeModule extends SimpleModule {
|
||||
public static final DataTypeModule INSTANCE = new DataTypeModule();
|
||||
|
||||
private DataTypeModule() {
|
||||
super(DataTypeModule.class.getName());
|
||||
|
||||
// number
|
||||
this.addSerializer(Long.class, BigNumberSerializer.instance);
|
||||
this.addSerializer(Long.TYPE, BigNumberSerializer.instance);
|
||||
this.addSerializer(BigInteger.class, BigNumberSerializer.instance);
|
||||
this.addSerializer(BigDecimal.class, BigNumberSerializer.instance);
|
||||
|
||||
// time
|
||||
this.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE);
|
||||
this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
||||
this.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE);
|
||||
this.addSerializer(Instant.class, InstantSerializer.INSTANCE);
|
||||
this.addSerializer(Date.class, DateSerializer.INSTANCE);
|
||||
this.addSerializer(java.sql.Date.class, SqlDateSerializer.INSTANCE);
|
||||
this.addSerializer(Timestamp.class, TimestampSerializer.INSTANCE);
|
||||
|
||||
this.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);
|
||||
this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
||||
this.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
|
||||
this.addDeserializer(Instant.class, InstantDeserializer.INSTANCE);
|
||||
this.addDeserializer(Date.class, DateDeserializer.INSTANCE);
|
||||
this.addDeserializer(java.sql.Date.class, SqlDateDeserializer.INSTANCE);
|
||||
this.addDeserializer(Timestamp.class, TimestampDeserializer.INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间反序列化
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class DateDeserializer extends JsonDeserializer<Date> {
|
||||
public static final DateDeserializer INSTANCE = new DateDeserializer();
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private DateDeserializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
|
||||
String dateString = p.getText();
|
||||
|
||||
return dateString.length() > 19
|
||||
? Date.from(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER_MS).atZone(ZoneOffset.systemDefault()).toInstant())
|
||||
: Date.from(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER).atZone(ZoneOffset.systemDefault()).toInstant());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间序列化
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class DateSerializer extends StdSerializer<Date> {
|
||||
public static final DateSerializer INSTANCE = new DateSerializer();
|
||||
private static final FastDateFormat FAST_DATE_FORMAT_MS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private DateSerializer() {
|
||||
super(Date.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
gen.writeString(FAST_DATE_FORMAT_MS.format(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* Instant 反序列化
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class InstantDeserializer extends com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer<Instant> {
|
||||
public static final InstantDeserializer INSTANCE = new InstantDeserializer();
|
||||
|
||||
private final FastDateFormat FAST_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
|
||||
private final FastDateFormat FAST_DATE_FORMAT_MS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private InstantDeserializer() {
|
||||
super(InstantDeserializer.INSTANT, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Instant deserialize(JsonParser parser, DeserializationContext context) {
|
||||
String timestamp = parser.getText();
|
||||
|
||||
return timestamp.length() > 19
|
||||
? FAST_DATE_FORMAT_MS.parse(timestamp).toInstant()
|
||||
: FAST_DATE_FORMAT.parse(timestamp).toInstant();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* Instant 序列化
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class InstantSerializer extends com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer {
|
||||
public static final InstantSerializer INSTANCE = new InstantSerializer();
|
||||
|
||||
private InstantSerializer() {
|
||||
super(com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer.INSTANCE, true,false, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser.Feature;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.json.JsonWriteFeature;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author baigod
|
||||
*/
|
||||
public class JacksonUtil {
|
||||
|
||||
public static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder()
|
||||
.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
|
||||
.configure(Feature.ALLOW_SINGLE_QUOTES, true)
|
||||
.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true)
|
||||
.configure(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS, false)
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.defaultTimeZone(TimeZone.getTimeZone("GMT+8"))
|
||||
.build()
|
||||
.registerModules(DataTypeModule.INSTANCE);
|
||||
|
||||
public static final ObjectMapper PRETTY_SORTED_JSON_MAPPER = JsonMapper.builder()
|
||||
.enable(SerializationFeature.INDENT_OUTPUT)
|
||||
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
|
||||
.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
|
||||
.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true)
|
||||
.serializationInclusion(Include.NON_NULL)
|
||||
.defaultTimeZone(TimeZone.getTimeZone("GMT+8"))
|
||||
.build()
|
||||
.registerModules(DataTypeModule.INSTANCE);
|
||||
|
||||
|
||||
public static <T> T convertValue(Object fromValue, Class<T> toValueType) {
|
||||
try {
|
||||
return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueType) : null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("The given object value: "
|
||||
+ fromValue + " cannot be converted to " + toValueType, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T convertValue(Object fromValue, TypeReference<T> toValueTypeRef) {
|
||||
try {
|
||||
return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueTypeRef) : null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("The given object value: "
|
||||
+ fromValue + " cannot be converted to " + toValueTypeRef, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T fromString(String string, Class<T> clazz) {
|
||||
try {
|
||||
return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("The given string value: "
|
||||
+ string + " cannot be transformed to Json object", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T fromString(String string, TypeReference<T> valueTypeRef) {
|
||||
try {
|
||||
return string != null ? OBJECT_MAPPER.readValue(string, valueTypeRef) : null;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("The given string value: "
|
||||
+ string + " cannot be transformed to Json object", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T fromBytes(byte[] bytes, Class<T> clazz) {
|
||||
try {
|
||||
return bytes != null ? OBJECT_MAPPER.readValue(bytes, clazz) : null;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("The given string value: "
|
||||
+ Arrays.toString(bytes) + " cannot be transformed to Json object", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode fromBytes(byte[] bytes) {
|
||||
try {
|
||||
return OBJECT_MAPPER.readTree(bytes);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("The given byte[] value: "
|
||||
+ Arrays.toString(bytes) + " cannot be transformed to Json object", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String toString(Object value) {
|
||||
try {
|
||||
return value != null ? OBJECT_MAPPER.writeValueAsString(value) : null;
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException("The given Json object value: "
|
||||
+ value + " cannot be transformed to a String", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String toPrettyString(Object o) {
|
||||
try {
|
||||
return PRETTY_SORTED_JSON_MAPPER.writeValueAsString(o);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T treeToValue(JsonNode node, Class<T> clazz) {
|
||||
try {
|
||||
return OBJECT_MAPPER.treeToValue(node, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("Can't convert value: " + node.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode toJsonNode(String value) {
|
||||
return toJsonNode(value, OBJECT_MAPPER);
|
||||
}
|
||||
|
||||
public static JsonNode toJsonNode(String value, ObjectMapper mapper) {
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return mapper.readTree(value);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjectNode newObjectNode() {
|
||||
return newObjectNode(OBJECT_MAPPER);
|
||||
}
|
||||
|
||||
public static ObjectNode newObjectNode(ObjectMapper mapper) {
|
||||
return mapper.createObjectNode();
|
||||
}
|
||||
|
||||
public static ArrayNode newArrayNode() {
|
||||
return newArrayNode(OBJECT_MAPPER);
|
||||
}
|
||||
|
||||
public static ArrayNode newArrayNode(ObjectMapper mapper) {
|
||||
return mapper.createArrayNode();
|
||||
}
|
||||
|
||||
public static <T> T clone(T value) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> valueClass = (Class<T>) value.getClass();
|
||||
return fromString(toString(value), valueClass);
|
||||
}
|
||||
|
||||
public static <T> JsonNode valueToTree(T value) {
|
||||
return OBJECT_MAPPER.valueToTree(value);
|
||||
}
|
||||
|
||||
public static <T> byte[] writeValueAsBytes(T value) {
|
||||
try {
|
||||
return OBJECT_MAPPER.writeValueAsBytes(value);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException("The given Json object value: "
|
||||
+ value + " cannot be transformed to a String", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode getSafely(JsonNode node, String... path) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
for (String p : path) {
|
||||
if (!node.has(p)) {
|
||||
return null;
|
||||
} else {
|
||||
node = node.get(p);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并两个ObjectNode.
|
||||
* 如果存在相同的字段,优先保留第二个ObjectNode中的值。
|
||||
*
|
||||
* @param node1 the first ObjectNode
|
||||
* @param node2 the second ObjectNode
|
||||
* @return 合并后的结果
|
||||
*/
|
||||
public static ObjectNode merge(ObjectNode node1, ObjectNode node2) {
|
||||
ObjectNode mergedNode = OBJECT_MAPPER.createObjectNode();
|
||||
|
||||
// 把第一个节点的所有字段添加到mergedNode中
|
||||
node1.fields().forEachRemaining(entry -> {
|
||||
mergedNode.set(entry.getKey(), entry.getValue());
|
||||
});
|
||||
|
||||
// 把第二个节点的所有字段添加到mergedNode中,覆盖相同字段
|
||||
node2.fields().forEachRemaining(entry -> {
|
||||
mergedNode.set(entry.getKey(), entry.getValue());
|
||||
});
|
||||
|
||||
return mergedNode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* LocalDateTime类型反序列化
|
||||
* 需要用到的字段上加 @JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
||||
*/
|
||||
public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
|
||||
public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer();
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private LocalDateTimeDeserializer() {
|
||||
super(LocalDateTime.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext context)
|
||||
throws IOException {
|
||||
|
||||
String dateString = jsonParser.getText();
|
||||
return dateString.length() > 19
|
||||
? LocalDateTime.parse(dateString, DATE_TIME_FORMATTER_MS)
|
||||
: LocalDateTime.parse(dateString, DATE_TIME_FORMATTER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 时间类型序列化工具
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
|
||||
public static final LocalDateTimeSerializer INSTANCE = new LocalDateTimeSerializer();
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private LocalDateTimeSerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeString(value.format(DATE_TIME_FORMATTER_MS));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* LocalDateTime类型反序列化
|
||||
* 需要用到的字段上加 @JsonDeserialize(using = EnergyLocalTimeDeserializer.class)
|
||||
*/
|
||||
public class LocalTimeDeserializer extends StdDeserializer<LocalTime> {
|
||||
public static final LocalTimeDeserializer INSTANCE = new LocalTimeDeserializer();
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
|
||||
|
||||
|
||||
private LocalTimeDeserializer() {
|
||||
super(LocalDateTime.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalTime deserialize(JsonParser jsonParser, DeserializationContext context)
|
||||
throws IOException {
|
||||
|
||||
String dateString = jsonParser.getText();
|
||||
return dateString.length() > 8
|
||||
? LocalTime.parse(dateString, DATE_TIME_FORMATTER_MS)
|
||||
: LocalTime.parse(dateString, DATE_TIME_FORMATTER);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 时间类型序列化工具
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class LocalTimeSerializer extends JsonSerializer<LocalTime> {
|
||||
public static final LocalTimeSerializer INSTANCE = new LocalTimeSerializer();
|
||||
|
||||
private LocalTimeSerializer() {
|
||||
}
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
|
||||
|
||||
@Override
|
||||
public void serialize(LocalTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeString(value.format(DATE_TIME_FORMATTER_MS));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
/**
|
||||
* 13位时间戳反序列化器
|
||||
* @author baigod
|
||||
*/
|
||||
public class LongTimestampDeserializer extends JsonDeserializer<Long> {
|
||||
|
||||
@Override
|
||||
public Long deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
||||
|
||||
// 判定是否是long类型
|
||||
if ("LONG".equals(jsonParser.getNumberType().name())) {
|
||||
return jsonParser.getLongValue();
|
||||
}
|
||||
LocalDateTime localDateTime = LocalDateTime.parse(jsonParser.getValueAsString().replace(" ", "T").replace("Z", ""));
|
||||
ZoneId systemDefaultZone = ZoneId.systemDefault();
|
||||
return localDateTime.atZone(systemDefaultZone).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Date;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* sqlDate 反序列化
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class SqlDateDeserializer extends JsonDeserializer<Date> {
|
||||
|
||||
public static final SqlDateDeserializer INSTANCE = new SqlDateDeserializer();
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private SqlDateDeserializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
String dateString = p.getText();
|
||||
|
||||
return dateString.length() > 19
|
||||
? new Date(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER_MS).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli())
|
||||
: new Date(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Date;
|
||||
|
||||
/**
|
||||
* sqlDate序列化
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class SqlDateSerializer extends StdSerializer<Date> {
|
||||
public static final SqlDateSerializer INSTANCE = new SqlDateSerializer();
|
||||
|
||||
private static final FastDateFormat FAST_DATE_FORMAT_MS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private SqlDateSerializer() {
|
||||
super(Date.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
gen.writeString(FAST_DATE_FORMAT_MS.format(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* timestamp 反序列化
|
||||
* @author baigod
|
||||
*/
|
||||
public class TimestampDeserializer extends JsonDeserializer<Timestamp> {
|
||||
|
||||
public static final TimestampDeserializer INSTANCE = new TimestampDeserializer();
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private TimestampDeserializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
|
||||
String dateString = p.getText();
|
||||
|
||||
return dateString.length() > 19
|
||||
? Timestamp.from(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER_MS).atZone(ZoneOffset.systemDefault()).toInstant())
|
||||
: Timestamp.from(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER).atZone(ZoneOffset.systemDefault()).toInstant());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* timestamp 序列化
|
||||
*
|
||||
* @author baigod
|
||||
*/
|
||||
public class TimestampSerializer extends StdSerializer<Timestamp> {
|
||||
public static final TimestampSerializer INSTANCE = new TimestampSerializer();
|
||||
|
||||
private static final FastDateFormat FAST_DATE_FORMAT_MS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
private TimestampSerializer() {
|
||||
super(Timestamp.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Timestamp value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
gen.writeString(FAST_DATE_FORMAT_MS.format(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.mdc;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.MDC;
|
||||
import sanbing.jcpp.infrastructure.util.trace.Tracer;
|
||||
import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil;
|
||||
|
||||
|
||||
public class MDCUtils {
|
||||
|
||||
private static final String TRACE_ID = "TRACE_ID";
|
||||
|
||||
public static String putIfAbsentTracer() {
|
||||
String traceId = MDC.get(TRACE_ID);
|
||||
|
||||
if (StringUtils.isEmpty(traceId)) {
|
||||
return recordTracer();
|
||||
}
|
||||
|
||||
return traceId;
|
||||
}
|
||||
|
||||
public static String recordTracer() {
|
||||
Tracer tracer = TracerContextUtil.getCurrentTracer();
|
||||
|
||||
if (!StringUtils.isEmpty(tracer.getTraceId())) {
|
||||
MDC.put(TRACE_ID, tracer.getTraceId());
|
||||
} else {
|
||||
MDC.remove(TRACE_ID);
|
||||
}
|
||||
|
||||
return tracer.getTraceId();
|
||||
|
||||
}
|
||||
|
||||
public static void cleanTracer() {
|
||||
MDC.remove(TRACE_ID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.property;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class JCPPProperty {
|
||||
|
||||
private String key;
|
||||
private String value;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.property;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class PropertyUtils {
|
||||
|
||||
public static Map<String, String> getProps(String properties) {
|
||||
Map<String, String> configs = new HashMap<>();
|
||||
if (StringUtils.isNotEmpty(properties)) {
|
||||
for (String property : properties.split(";")) {
|
||||
if (StringUtils.isNotEmpty(property)) {
|
||||
int delimiterPosition = property.indexOf(":");
|
||||
String key = property.substring(0, delimiterPosition);
|
||||
String value = property.substring(delimiterPosition + 1);
|
||||
configs.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
public static Map<String, String> getProps(Map<String, String> defaultProperties, String propertiesStr) {
|
||||
return getProps(defaultProperties, propertiesStr, PropertyUtils::getProps);
|
||||
}
|
||||
|
||||
public static Map<String, String> getProps(Map<String, String> defaultProperties, String propertiesStr, Function<String, Map<String, String>> parser) {
|
||||
Map<String, String> properties = defaultProperties;
|
||||
if (StringUtils.isNotBlank(propertiesStr)) {
|
||||
properties = new HashMap<>(properties);
|
||||
properties.putAll(parser.apply(propertiesStr));
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.trace;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class TraceIdGenerator {
|
||||
|
||||
//127.0.0.1
|
||||
private static String IP_16 = "7F000001";
|
||||
|
||||
private static final int MAX_COUNT_INDEX = 9000;
|
||||
|
||||
private static final AtomicInteger COUNT = new AtomicInteger(1000);
|
||||
|
||||
static {
|
||||
try {
|
||||
String ipAddress = InetAddress.getLocalHost().getHostAddress();
|
||||
if (ipAddress != null) {
|
||||
IP_16 = getIP_16(ipAddress);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static String generate() {
|
||||
return getTraceId(IP_16, System.currentTimeMillis(), getNextId());
|
||||
}
|
||||
|
||||
private static String getIP_16(String ip) {
|
||||
String[] ips = ip.split("\\.");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String column : ips) {
|
||||
String hex = Integer.toHexString(Integer.parseInt(column)).toUpperCase();
|
||||
if (hex.length() == 1) {
|
||||
sb.append('0').append(hex);
|
||||
} else {
|
||||
sb.append(hex);
|
||||
}
|
||||
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String getTraceId(String ip, long timestamp, String nextId) {
|
||||
return ip + timestamp + nextId;
|
||||
}
|
||||
|
||||
private static String getNextId() {
|
||||
|
||||
int count = COUNT.incrementAndGet();
|
||||
|
||||
if (count > 9000) {
|
||||
synchronized (TraceIdGenerator.class) {
|
||||
if (COUNT.get() > MAX_COUNT_INDEX) {
|
||||
COUNT.set(1000);
|
||||
}
|
||||
}
|
||||
|
||||
return String.valueOf(COUNT.incrementAndGet());
|
||||
} else {
|
||||
return String.valueOf(count);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.trace;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class Tracer implements Serializable {
|
||||
|
||||
private String traceId;
|
||||
|
||||
private String origin;
|
||||
|
||||
private final long tracerTs;
|
||||
|
||||
public Tracer(String traceId, String origin) {
|
||||
this.traceId = traceId;
|
||||
this.origin = origin;
|
||||
this.tracerTs = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Tracer(String traceId, String origin, long tracerTs) {
|
||||
this.traceId = traceId;
|
||||
this.origin = origin;
|
||||
this.tracerTs = tracerTs;
|
||||
}
|
||||
|
||||
public Tracer(String traceId, long tracerTs) {
|
||||
this.traceId = traceId;
|
||||
this.origin = "JCPP";
|
||||
this.tracerTs = tracerTs;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.trace;
|
||||
|
||||
|
||||
import sanbing.jcpp.infrastructure.util.mdc.MDCUtils;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class TracerCallable<T> implements Callable<T> {
|
||||
|
||||
private Tracer tracer;
|
||||
private final Callable<T> callable;
|
||||
|
||||
public TracerCallable(Callable<T> callable) {
|
||||
this.tracer = TracerContextUtil.getCurrentTracer();
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T call() throws Exception {
|
||||
try {
|
||||
if (this.tracer != null) {
|
||||
TracerContextUtil.newTracer(tracer.getTraceId(), tracer.getOrigin(), tracer.getTracerTs());
|
||||
|
||||
MDCUtils.recordTracer();
|
||||
}
|
||||
|
||||
return this.callable.call();
|
||||
} finally {
|
||||
TracerContextUtil.cleanTracer();
|
||||
|
||||
MDCUtils.cleanTracer();
|
||||
|
||||
this.tracer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.trace;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Tracer上下文工具类
|
||||
*/
|
||||
public class TracerContextUtil {
|
||||
|
||||
public static final String JCPP_TRACER_ID = "jcpp_tracer_id";
|
||||
public static final String JCPP_TRACER_ORIGIN = "jcpp_tracer_origin";
|
||||
public static final String JCPP_TRACER_TS = "jcpp_tracer_ts";
|
||||
|
||||
private static final ThreadLocal<Tracer> TRACE_ID_CONTAINER = new ThreadLocal<>();
|
||||
|
||||
public static Tracer newTracer(String traceId, String origin) {
|
||||
Tracer tracer;
|
||||
|
||||
if (StringUtils.isEmpty(traceId)) {
|
||||
tracer = new Tracer(TraceIdGenerator.generate(), origin);
|
||||
} else {
|
||||
tracer = new Tracer(traceId, origin);
|
||||
}
|
||||
|
||||
TRACE_ID_CONTAINER.set(tracer);
|
||||
|
||||
return tracer;
|
||||
}
|
||||
|
||||
public static Tracer newTracer(String traceId, String origin, long ts) {
|
||||
final Tracer tracer;
|
||||
|
||||
if (StringUtils.isEmpty(traceId)) {
|
||||
tracer = new Tracer(TraceIdGenerator.generate(), origin, ts);
|
||||
} else {
|
||||
tracer = new Tracer(traceId, origin, ts);
|
||||
}
|
||||
|
||||
|
||||
TRACE_ID_CONTAINER.set(tracer);
|
||||
|
||||
return tracer;
|
||||
}
|
||||
|
||||
public static Tracer newTracer(String origin) {
|
||||
return newTracer(TraceIdGenerator.generate(), origin);
|
||||
}
|
||||
|
||||
public static Tracer newTracer() {
|
||||
return newTracer(TraceIdGenerator.generate(), null);
|
||||
}
|
||||
|
||||
public static Tracer getCurrentTracer() {
|
||||
Tracer tracer = TRACE_ID_CONTAINER.get();
|
||||
|
||||
if (tracer == null) {
|
||||
return newTracer();
|
||||
}
|
||||
|
||||
return tracer;
|
||||
}
|
||||
|
||||
public static void cleanTracer() {
|
||||
TRACE_ID_CONTAINER.remove();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.trace;
|
||||
|
||||
|
||||
import sanbing.jcpp.infrastructure.util.mdc.MDCUtils;
|
||||
|
||||
public class TracerRunnable implements Runnable {
|
||||
|
||||
private Tracer tracer;
|
||||
private final Runnable runnable;
|
||||
|
||||
public TracerRunnable(Runnable runnable) {
|
||||
this.tracer = TracerContextUtil.getCurrentTracer();
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (this.tracer != null) {
|
||||
TracerContextUtil.newTracer(tracer.getTraceId(), tracer.getOrigin(), tracer.getTracerTs());
|
||||
|
||||
MDCUtils.recordTracer();
|
||||
}
|
||||
|
||||
this.runnable.run();
|
||||
} finally {
|
||||
TracerContextUtil.cleanTracer();
|
||||
|
||||
MDCUtils.cleanTracer();
|
||||
|
||||
this.tracer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.validation;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@Constraint(validatedBy = {})
|
||||
public @interface Length {
|
||||
String message() default "length must be equal or less than {max}";
|
||||
|
||||
String fieldName() default "";
|
||||
|
||||
int max() default 255;
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.validation;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Slf4j
|
||||
public class StringLengthValidator implements ConstraintValidator<Length, Object> {
|
||||
private int max;
|
||||
|
||||
@Override
|
||||
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||
String stringValue;
|
||||
if (value instanceof CharSequence || value instanceof JsonNode) {
|
||||
stringValue = value.toString();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
if (StringUtils.isEmpty(stringValue)) {
|
||||
return true;
|
||||
}
|
||||
return stringValue.length() <= max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Length constraintAnnotation) {
|
||||
this.max = constraintAnnotation.max();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.validation;
|
||||
|
||||
import sanbing.jcpp.infrastructure.util.exception.IncorrectParameterException;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Validator {
|
||||
|
||||
|
||||
public static void validateString(String val, String errorMessage) {
|
||||
if (val == null || val.isEmpty()) {
|
||||
throw new IncorrectParameterException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void validateString(String val, Function<String, String> errorMessageFunction) {
|
||||
if (val == null || val.isEmpty()) {
|
||||
throw new IncorrectParameterException(errorMessageFunction.apply(val));
|
||||
}
|
||||
}
|
||||
|
||||
public static void validatePositiveNumber(long val, String errorMessage) {
|
||||
if (val <= 0) {
|
||||
throw new IncorrectParameterException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void validateId(UUID id, String errorMessage) {
|
||||
if (id == null) {
|
||||
throw new IncorrectParameterException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateId(UUID id, Function<UUID, String> errorMessageFunction) {
|
||||
if (id == null) {
|
||||
throw new IncorrectParameterException(errorMessageFunction.apply(id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void checkNotNull(Object reference, String errorMessage) {
|
||||
if (reference == null) {
|
||||
throw new IncorrectParameterException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.codec;
|
||||
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
|
||||
class BCDUtilTest {
|
||||
|
||||
@Test
|
||||
void toBytesTest() {
|
||||
String pileCodeHex = "20231212000010";
|
||||
|
||||
byte[] bytes = HexUtil.decodeHex(pileCodeHex);
|
||||
|
||||
String pileCode = BCDUtil.toString(bytes);
|
||||
|
||||
assert pileCodeHex.equals(pileCode);
|
||||
|
||||
byte[] pileCodeBytes = BCDUtil.toBytes(pileCodeHex);
|
||||
|
||||
assertArrayEquals(pileCodeBytes, bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 抖音关注:程序员三丙
|
||||
* 知识星球:https://t.zsxq.com/j9b21
|
||||
*/
|
||||
package sanbing.jcpp.infrastructure.util.codec;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
||||
class CP56Time2aUtilTest {
|
||||
|
||||
@Test
|
||||
void encodeTest() {
|
||||
Instant time = Instant.ofEpochMilli(1727798453000L);
|
||||
|
||||
byte[] bytes = CP56Time2aUtil.encode(time);
|
||||
|
||||
System.out.println(Arrays.toString(bytes));
|
||||
|
||||
Instant decode = CP56Time2aUtil.decode(bytes);
|
||||
|
||||
assert time.equals(decode);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user