lzq 2025-07-29 18:43:18 +08:00
commit baa6ff9cc9
586 changed files with 489763 additions and 0 deletions

7
.gitignore vendored 100644
View File

@ -0,0 +1,7 @@
/**/logs
/**/*.iml
/**/.idea
/**/target
/**/.DS_Store
/**/.xcodemap
/**/.back*

View File

@ -0,0 +1,40 @@
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>njzscloud-common-cache</artifactId>
<packaging>jar</packaging>
<name>njzscloud-common-cache</name>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common-core</artifactId>
<scope>provided</scope>
</dependency>
<!-- redis -->
<dependency>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common-redis</artifactId>
</dependency>
<!-- junit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,17 @@
package com.njzscloud.common.cache;
import java.util.function.Supplier;
public interface Cache {
<T> T get(String key);
<T> T get(String key, Supplier<T> supplier);
<T> T get(String key, long timeout, Supplier<T> supplier);
void put(String key, Object value, long timeout);
void put(String key, Object value);
void remove(String key);
}

View File

@ -0,0 +1,30 @@
package com.njzscloud.common.cache;
import cn.hutool.extra.spring.SpringUtil;
import java.util.function.Supplier;
public final class Caches {
public static final Cache CACHE = SpringUtil.getBean(Cache.class);
public static <T> T get(String key) {
return CACHE.get(key);
}
public static <T> T get(String key, long timeout, Supplier<T> supplier) {
return CACHE.get(key, timeout, supplier);
}
public static void put(String key, Object value, long timeout) {
CACHE.put(key, value, timeout);
}
public static void put(String key, Object value) {
CACHE.put(key, value);
}
public static void remove(String key) {
CACHE.remove(key);
}
}

View File

@ -0,0 +1,75 @@
package com.njzscloud.common.cache;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;
public class DualCache implements Cache {
private final FirstCache FIRST_CACHE;
private final SecondCache SECOND_CACHE;
private final ReentrantReadWriteLock DUAL_CACHE_LOCK = new ReentrantReadWriteLock();
public DualCache(int capacity, long firstTimeout, long secondTimeout) {
FIRST_CACHE = new FirstCache(capacity, firstTimeout);
SECOND_CACHE = new SecondCache(secondTimeout);
}
public <T> T get(String key) {
DUAL_CACHE_LOCK.readLock().lock();
try {
return FIRST_CACHE.get(key, () -> SECOND_CACHE.get(key));
} finally {
DUAL_CACHE_LOCK.readLock().unlock();
}
}
public <T> T get(String key, Supplier<T> supplier) {
DUAL_CACHE_LOCK.readLock().lock();
try {
return (T) FIRST_CACHE.get(key, () -> SECOND_CACHE.get(key, supplier));
} finally {
DUAL_CACHE_LOCK.readLock().unlock();
}
}
@Override
public <T> T get(String key, long timeout, Supplier<T> supplier) {
DUAL_CACHE_LOCK.readLock().lock();
try {
return (T) FIRST_CACHE.get(key, timeout, () -> SECOND_CACHE.get(key, timeout, supplier));
} finally {
DUAL_CACHE_LOCK.readLock().unlock();
}
}
public void put(String key, Object value) {
DUAL_CACHE_LOCK.writeLock().lock();
try {
FIRST_CACHE.put(key, value);
SECOND_CACHE.put(key, value);
} finally {
DUAL_CACHE_LOCK.writeLock().unlock();
}
}
public void put(String key, Object value, long timeout) {
DUAL_CACHE_LOCK.writeLock().lock();
try {
FIRST_CACHE.put(key, value);
SECOND_CACHE.put(key, value, timeout);
} finally {
DUAL_CACHE_LOCK.writeLock().unlock();
}
}
public void remove(String key) {
DUAL_CACHE_LOCK.writeLock().lock();
try {
FIRST_CACHE.remove(key);
SECOND_CACHE.remove(key);
} finally {
DUAL_CACHE_LOCK.writeLock().unlock();
}
}
}

View File

@ -0,0 +1,46 @@
package com.njzscloud.common.cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.LFUCache;
import java.util.function.Supplier;
@SuppressWarnings("unchecked")
public class FirstCache implements Cache {
private final LFUCache<String, Object> CACHE;
public FirstCache(int capacity, long timeout) {
CACHE = CacheUtil.newLFUCache(capacity, timeout);
CacheUtil.newNoCache().get("first", null);
}
@Override
public <T> T get(String key) {
return (T) CACHE.get(key);
}
public <T> T get(String key, Supplier<T> supplier) {
return (T) CACHE.get(key, supplier::get);
}
@Override
public <T> T get(String key, long timeout, Supplier<T> supplier) {
return (T) CACHE.get(key, true, timeout, supplier::get);
}
@Override
public void put(String key, Object value, long timeout) {
CACHE.put(key, value, timeout);
}
@Override
public void put(String key, Object value) {
CACHE.put(key, value);
}
@Override
public void remove(String key) {
CACHE.remove(key);
}
}

View File

@ -0,0 +1,36 @@
package com.njzscloud.common.cache;
import java.util.function.Supplier;
public class NoCache implements Cache {
public NoCache() {
}
@Override
public <T> T get(String key) {
return null;
}
public <T> T get(String key, Supplier<T> supplier) {
return supplier.get();
}
@Override
public <T> T get(String key, long timeout, Supplier<T> supplier) {
return supplier.get();
}
@Override
public void put(String key, Object value, long timeout) {
}
@Override
public void put(String key, Object value) {
}
@Override
public void remove(String key) {
}
}

View File

@ -0,0 +1,103 @@
package com.njzscloud.common.cache;
import com.njzscloud.common.redis.util.Redis;
import io.lettuce.core.SetArgs;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;
@SuppressWarnings("unchecked")
public class SecondCache implements Cache {
private final ConcurrentHashMap<String, ReentrantReadWriteLock> LOCKS = new ConcurrentHashMap<>();
private final long timeout;
public SecondCache(long timeout) {
this.timeout = timeout;
}
public <T> T get(String key) {
ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
lock.readLock().lock();
try {
return Redis.get(key);
} finally {
LOCKS.remove(key);
lock.readLock().unlock();
}
}
@Override
public <T> T get(String key, Supplier<T> supplier) {
Object o = Redis.get(key);
if (o != null) return (T) o;
ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
lock.writeLock().lock();
try {
o = Redis.get(key);
if (o != null) return (T) o;
o = supplier.get();
if (o != null) Redis.set(key, o);
return (T) o;
} finally {
LOCKS.remove(key);
lock.writeLock().unlock();
}
}
@Override
public <T> T get(String key, long timeout, Supplier<T> supplier) {
Object o = Redis.get(key);
if (o != null) return (T) o;
ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
lock.writeLock().lock();
try {
o = Redis.get(key);
if (o != null) return (T) o;
o = supplier.get();
if (o != null) Redis.set(key, o, new SetArgs().ex(timeout));
return (T) o;
} finally {
LOCKS.remove(key);
lock.writeLock().unlock();
}
}
public void put(String key, Object value, long timeout) {
ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
lock.writeLock().lock();
try {
Redis.set(key, value, new SetArgs().ex(timeout));
} finally {
LOCKS.remove(key);
lock.writeLock().unlock();
}
}
public void put(String key, Object value) {
ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
lock.writeLock().lock();
try {
if (timeout > 0) Redis.set(key, value, new SetArgs().ex(timeout));
else Redis.set(key, value);
} finally {
LOCKS.remove(key);
lock.writeLock().unlock();
}
}
public void remove(String key) {
ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
lock.writeLock().lock();
try {
Redis.del(key);
} finally {
LOCKS.remove(key);
lock.writeLock().unlock();
}
}
}

View File

@ -0,0 +1,34 @@
package com.njzscloud.common.cache.config;
import com.njzscloud.common.cache.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(CacheProperties.class)
public class CacheAutoConfiguration {
@Bean
public Cache cache(CacheProperties properties) {
CacheProperties.FirstCacheProperties first = properties.getFirst();
CacheProperties.SecondCacheProperties second = properties.getSecond();
Cache cache;
if (first.isEnabled()) {
if (second.isEnabled()) {
cache = new DualCache(first.getCapacity(), first.getTimeout(), second.getTimeout());
} else {
cache = new FirstCache(first.getCapacity(), first.getTimeout());
}
} else {
if (second.isEnabled()) {
cache = new SecondCache(second.getTimeout());
} else {
cache = new NoCache();
}
}
return cache;
}
}

View File

@ -0,0 +1,30 @@
package com.njzscloud.common.cache.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@Setter
@ConfigurationProperties(prefix = "cache")
public class CacheProperties {
private FirstCacheProperties first = new FirstCacheProperties();
private SecondCacheProperties second = new SecondCacheProperties();
@Getter
@Setter
public static class FirstCacheProperties {
private boolean enabled = true;
private int capacity = 100000;
private long timeout = 3600 * 24;
}
@Getter
@Setter
public static class SecondCacheProperties {
private boolean enabled = false;
private long timeout = 3600 * 24;
}
}

View File

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration = \
com.njzscloud.common.cache.config.CacheAutoConfiguration

View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>njzscloud-common-core</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<!-- fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!--<editor-fold desc="jackson">-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!--</editor-fold>-->
<!--<editor-fold desc="hutool">-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-cache</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
<!--</editor-fold>-->
<!-- cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
<!-- okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!-- junit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- spring-boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- configuration processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,25 @@
package com.njzscloud.common.core.ex;
import cn.hutool.core.util.StrUtil;
/**
* ,
*/
public class CliException extends SysThrowable {
/**
*
*
* @param cause
* @param expect
* @param msg
* @param message
*/
protected CliException(Throwable cause, Object expect, ExceptionMsg msg, Object message) {
super(cause, expect, msg, message);
}
@Override
public String getMessage() {
return StrUtil.format("客户端错误, 错误码: {}, 错误信息: {}, 详细信息: {}", this.msg.code, this.msg.msg, this.message);
}
}

View File

@ -0,0 +1,93 @@
package com.njzscloud.common.core.ex;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.SimpleCache;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
/**
* <p></p>
* <p></p>
*
* @see org.springframework.core.ExceptionDepthComparator
*/
public class ExceptionDepthComparator implements Comparator<Class<? extends Throwable>> {
/**
*
*/
private static final SimpleCache<Class<? extends Throwable>, ExceptionDepthComparator> CACHE = new SimpleCache<>();
/**
*
*/
private final Class<? extends Throwable> targetException;
/**
*
*
* @param exception
*/
public ExceptionDepthComparator(Throwable exception) {
Assert.notNull(exception, "目标异常不能为空");
this.targetException = exception.getClass();
}
/**
*
*
* @param exceptionType
*/
public ExceptionDepthComparator(Class<? extends Throwable> exceptionType) {
Assert.notNull(exceptionType, "目标异常类型不能为空");
this.targetException = exceptionType;
}
/**
*
*
* @param exceptionTypes
* @param targetException
* @return
*/
public static Class<? extends Throwable> findClosestMatch(Collection<Class<? extends Throwable>> exceptionTypes, Throwable targetException) {
Assert.notEmpty(exceptionTypes, "不能为空");
Assert.notNull(targetException, "不能为空");
if (exceptionTypes.size() == 1) {
return exceptionTypes.iterator().next();
}
List<Class<? extends Throwable>> handledExceptions = new ArrayList<>(exceptionTypes);
ExceptionDepthComparator comparator = CACHE.get(targetException.getClass(), () -> new ExceptionDepthComparator(targetException));
handledExceptions.sort(comparator);
return handledExceptions.get(0);
}
@Override
public int compare(Class<? extends Throwable> o1, Class<? extends Throwable> o2) {
int depth1 = getDepth(o1, this.targetException);
int depth2 = getDepth(o2, this.targetException);
return depth1 - depth2;
}
/**
*
*
* @param declaredException
* @param exceptionToMatch
* @return 0
*/
private int getDepth(Class<?> declaredException, Class<?> exceptionToMatch) {
int depth = 0;
do {
if (exceptionToMatch.equals(declaredException)) return depth;
if (exceptionToMatch == Throwable.class) return Integer.MAX_VALUE;
depth++;
exceptionToMatch = exceptionToMatch.getSuperclass();
} while (true);
}
}

View File

@ -0,0 +1,53 @@
package com.njzscloud.common.core.ex;
/**
*
* <p></p>
* <p>SYS_EXP_MSG</p>
* <p>CLI_ERR_MSG</p>
* <p>SYS_ERR_MSG</p>
*/
public final class ExceptionMsg {
/**
*
*/
public static final ExceptionMsg SYS_EXP_MSG = new ExceptionMsg(ExceptionType.SYS_EXP, 1111, "操作失败");
/**
*
*/
public static final ExceptionMsg CLI_ERR_MSG = new ExceptionMsg(ExceptionType.CLI_ERR, 5555, "操作失败");
/**
*
*/
public static final ExceptionMsg SYS_ERR_MSG = new ExceptionMsg(ExceptionType.SYS_ERR, 9999, "系统错误");
/**
* 0< code 9999
*/
public final int code;
/**
*
*/
public final String msg;
/**
* {@link ExceptionType}
*/
public final ExceptionType type;
/**
*
*
* @param type {@link ExceptionType}
* @param code 0< code 9999
* @param msg
*/
private ExceptionMsg(ExceptionType type, int code, String msg) {
this.code = code;
this.type = type;
this.msg = msg;
}
}

View File

@ -0,0 +1,21 @@
package com.njzscloud.common.core.ex;
import lombok.RequiredArgsConstructor;
/**
*
*/
@RequiredArgsConstructor
public enum ExceptionType {
SYS_EXP(1, "系统异常"),
CLI_ERR(5, "客户端错误"),
SYS_ERR(9, "系统错误");
public final int code;
public final String name;
}

View File

@ -0,0 +1,77 @@
package com.njzscloud.common.core.ex;
import cn.hutool.core.util.StrUtil;
/**
*
*
*/
public class Exceptions {
public static SysThrowable clierr(Object message, Object... param) {
return build(null, ExpectData.NULL_DATA, ExceptionMsg.CLI_ERR_MSG, message, param);
}
public static SysThrowable clierr(Object message) {
return build(null, ExpectData.NULL_DATA, ExceptionMsg.CLI_ERR_MSG, message, null);
}
public static SysThrowable exception(Object message, Object... param) {
return build(null, ExpectData.NULL_DATA, ExceptionMsg.SYS_EXP_MSG, message, param);
}
public static SysThrowable exception(Object message) {
return build(null, ExpectData.NULL_DATA, ExceptionMsg.SYS_EXP_MSG, message, null);
}
public static SysThrowable error(Object message, Object... param) {
return build(null, ExpectData.NULL_DATA, ExceptionMsg.SYS_ERR_MSG, message, param);
}
public static SysThrowable error(Object message) {
return build(null, ExpectData.NULL_DATA, ExceptionMsg.SYS_ERR_MSG, message, null);
}
public static SysThrowable error(Throwable cause, Object message, Object... param) {
return build(cause, ExpectData.NULL_DATA, ExceptionMsg.SYS_ERR_MSG, message, param);
}
public static SysThrowable error(Throwable cause, Object message) {
return build(cause, ExpectData.NULL_DATA, ExceptionMsg.SYS_ERR_MSG, message, null);
}
/**
* <p></p>
*
* @param cause
* @param expect
* @param msg
* @param message (, : {})
* @param param
* @see SysThrowable
* @see ExceptionMsg
* @see ExpectData
*/
private static SysThrowable build(Throwable cause, ExpectData expect, ExceptionMsg msg, Object message, Object[] param) {
if (message instanceof String
&& !StrUtil.isBlank((String) message)
&& param != null
&& param.length > 0) {
message = StrUtil.format((String) message, param);
}
SysThrowable sysThrowable;
if (msg.type == ExceptionType.SYS_EXP) {
sysThrowable = new SysException(cause, expect, msg, message);
} else if (msg.type == ExceptionType.CLI_ERR) {
sysThrowable = new CliException(cause, expect, msg, message);
} else {
sysThrowable = new SysError(cause, expect, msg, message);
}
return sysThrowable;
}
}

View File

@ -0,0 +1,60 @@
package com.njzscloud.common.core.ex;
import lombok.AllArgsConstructor;
import java.util.Collections;
/**
*
* <p>HTTP JSON </p>
*/
@AllArgsConstructor
public enum ExpectData {
/**
* : null
*/
NULL_DATA(null),
/**
* : []
*/
ARR_DATA(Collections.emptyList()),
/**
* : {}
*/
KV_DATA(Collections.emptyMap()),
/**
* : ""
*/
STR_BLANK_DATA(""),
/**
* : 0
*/
NUM_ZERO_DATA(0),
/**
* : false
*/
BOOL_FALSE_DATA(Boolean.FALSE),
/**
* : true
*/
BOOL_TRUE_DATA(Boolean.TRUE),
/**
*
*/
VOID_DATA(Void.TYPE);
private final Object data;
@SuppressWarnings("unchecked")
public <T> T getData() {
return (T) data;
}
}

View File

@ -0,0 +1,26 @@
package com.njzscloud.common.core.ex;
import cn.hutool.core.util.StrUtil;
/**
* ,
*/
public class SysError extends SysThrowable {
/**
*
*
* @param cause
* @param expect
* @param msg
* @param message
*/
protected SysError(Throwable cause, Object expect, ExceptionMsg msg, Object message) {
super(cause, expect, msg, message);
}
@Override
public String getMessage() {
return StrUtil.format("内部服务错误, 错误码: {}, 错误信息: {}, 详细信息: {}", this.msg.code, this.msg.msg, this.message);
}
}

View File

@ -0,0 +1,25 @@
package com.njzscloud.common.core.ex;
import cn.hutool.core.util.StrUtil;
/**
* ,
*/
public class SysException extends SysThrowable {
/**
*
*
* @param cause
* @param expect
* @param msg
* @param message
*/
protected SysException(Throwable cause, Object expect, ExceptionMsg msg, Object message) {
super(cause, expect, msg, message);
}
@Override
public String getMessage() {
return StrUtil.format("系统异常, 错误码: {}, 错误信息: {}, 详细信息: {}", this.msg.code, this.msg.msg, this.message);
}
}

View File

@ -0,0 +1,52 @@
package com.njzscloud.common.core.ex;
import cn.hutool.core.map.MapUtil;
import com.njzscloud.common.core.jackson.Jackson;
/**
*
*/
public abstract class SysThrowable extends RuntimeException {
/**
*
*
* @see ExpectData
*/
public final Object expect;
/**
* ()
*/
public final ExceptionMsg msg;
/**
* ()
*/
public final Object message;
/**
*
*
* @param cause
* @param expect
* @param msg
* @param message
*/
protected SysThrowable(Throwable cause, Object expect, ExceptionMsg msg, Object message) {
super(msg.msg, cause);
this.msg = msg;
this.message = message == null ? "" : message;
this.expect = expect;
}
@Override
public String toString() {
return Jackson.toJsonStr(MapUtil.builder()
.put("code", msg.code)
.put("expect", expect)
.put("msg", msg.msg)
.put("message", message)
.build());
}
}

View File

@ -0,0 +1,139 @@
package com.njzscloud.common.core.fastjson;
import cn.hutool.core.date.DatePattern;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import com.alibaba.fastjson2.filter.ValueFilter;
import com.alibaba.fastjson2.reader.ObjectReader;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import static com.alibaba.fastjson2.JSONReader.Feature.IgnoreCheckClose;
/**
* <p>Fastjson </p>
* <p>/</p>
*/
@Slf4j
public class Fastjson {
private static final JSONWriter.Context JSON_WRITER_CONTEXT;
private static final JSONReader.Context JSON_READER_CONTEXT;
static {
JSON_WRITER_CONTEXT = new JSONWriter.Context(
JSONWriter.Feature.WriteNulls, // 序列化输出空值字段
JSONWriter.Feature.BrowserCompatible, // 在大范围超过JavaScript支持的整数输出为字符串格式
JSONWriter.Feature.WriteClassName, // 序列化时输出类型信息
JSONWriter.Feature.PrettyFormat, // 格式化输出
JSONWriter.Feature.SortMapEntriesByKeys, // 对Map中的KeyValue按照Key做排序后再输出。在有些验签的场景需要使用这个Feature
JSONWriter.Feature.WriteBigDecimalAsPlain, // 序列化BigDecimal使用toPlainString避免科学计数法
JSONWriter.Feature.WriteNullListAsEmpty, // 将List类型字段的空值序列化输出为空数组"[]"
JSONWriter.Feature.WriteNullStringAsEmpty, // 将String类型字段的空值序列化输出为空字符串""
JSONWriter.Feature.WriteLongAsString // 将 Long 作为字符串输出
);
ZoneId zoneId = ZoneId.of("GMT+8");
JSON_WRITER_CONTEXT.setZoneId(zoneId);
JSON_WRITER_CONTEXT.setDateFormat(DatePattern.NORM_DATETIME_PATTERN);
JSON_WRITER_CONTEXT.configFilter((ValueFilter) (object, name, value) -> {
if (value != null) {
Class<?> clazz = value.getClass();
if (clazz == LocalDate.class) {
return DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN).withZone(zoneId).format((LocalDate) value);
} else if (clazz == LocalTime.class) {
return DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN).withZone(zoneId).format((LocalTime) value);
} else if (clazz == BigDecimal.class) {
return ((BigDecimal) value).toPlainString();
}
}
return value;
});
JSON_READER_CONTEXT = new JSONReader.Context(
JSONReader.Feature.IgnoreSetNullValue, // 忽略输入为null的字段
JSONReader.Feature.SupportAutoType // 支持自动类型,要读取带"@type"类型信息的JSON数据需要显示打开SupportAutoType
);
JSON_READER_CONTEXT.setZoneId(zoneId);
JSON_READER_CONTEXT.setDateFormat(DatePattern.NORM_DATETIME_PATTERN);
}
/**
* JSON
*
* @param object
* @return String
* @see JSON#toJSONString(Object, JSONWriter.Context)
*/
public static String toJsonStr(Object object) {
return JSON.toJSONString(object, JSON_WRITER_CONTEXT);
}
/**
* JSON
*
* @param object
* @return byte[]
* @see JSON#toJSONBytes(Object, Filter...)
*/
public static byte[] toJsonBytes(Object object) {
return JSON.toJSONBytes(object, StandardCharsets.UTF_8, JSON_WRITER_CONTEXT);
}
/**
*
*
* @param json JSON
* @param type
* @return T
* @see JSON#parseObject(String, Class, JSONReader.Context)
*/
@SuppressWarnings("unchecked")
public static <T> T toBean(String json, Type type) {
if (json == null || json.isEmpty()) {
return null;
}
return toBean(JSONReader.of(json, JSON_READER_CONTEXT), type);
}
public static <T> T toBean(InputStream json, Type type) {
if (json == null) {
return null;
}
return toBean(JSONReader.of(json, StandardCharsets.UTF_8, JSON_READER_CONTEXT), type);
}
public static <T> T toBean(byte[] json, Type type) {
if (json == null || json.length == 0) {
return null;
}
return toBean(JSONReader.of(json, JSON_READER_CONTEXT), type);
}
@SuppressWarnings("unchecked")
private static <T> T toBean(JSONReader reader, Type type) {
ObjectReader<T> objectReader = JSON_READER_CONTEXT.getObjectReader(type);
try {
T object = objectReader.readObject(reader, type, null, 0);
reader.handleResolveTasks(object);
if (!reader.isEnd() && (JSON_READER_CONTEXT.getFeatures() & IgnoreCheckClose.mask) == 0) {
throw new JSONException(reader.info("input not end"));
}
return object;
} finally {
reader.close();
}
}
}

View File

@ -0,0 +1,107 @@
package com.njzscloud.common.core.fastjson.serializer;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.util.TypeUtils;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.core.ienum.IEnum;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Type;
import java.util.Map;
/**
* Dict Fastjson <br/>
* 使: <br/>
* 1使 JSONField <br/>
* @JSONField(deserializeUsing = DictObjectSerializer.class)<br/>
* 2使 JSONType <br/>
* @JSONType(writeEnumAsJavaBean = true, deserializer = DictObjectSerializer.class)<br/><br/>
* JSON {@link DictObjectSerializer}
*
* @see Dict
* @see DictInt
* @see DictStr
* @see DictObjectSerializer
*/
@Slf4j
public class DictObjectDeserializer implements ObjectReader<Dict> {
private static final ClassLoader CLASSLOADER = DictObjectDeserializer.class.getClassLoader();
@Override
public Dict readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
try {
if (jsonReader.isObject()) {
Map<String, Object> map = jsonReader.readObject();
String typeField = (String) map.get(IEnum.ENUM_TYPE);
Object valField = map.get(Dict.ENUM_VAL);
Class<?> clazz = CLASSLOADER.loadClass(typeField);
if (valField instanceof String) {
if (DictStr.class.isAssignableFrom(clazz)) {
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
return Dict.parse((String) valField, constants);
} else if (DictInt.class.isAssignableFrom(clazz)) {
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
return Dict.parse(Integer.parseInt((String) valField), constants);
} else {
return null;
}
} else if (valField instanceof Integer) {
if (DictStr.class.isAssignableFrom(clazz)) {
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
return Dict.parse(String.valueOf(valField), constants);
} else if (DictInt.class.isAssignableFrom(clazz)) {
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
return Dict.parse((Integer) valField, constants);
} else {
return null;
}
} else {
return null;
}
} else {
if (jsonReader.isString()) {
Class<?> clazz = TypeUtils.getClass(fieldType);
if (DictStr.class.isAssignableFrom(clazz)) {
String val = jsonReader.readString();
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
return Dict.parse(val, constants);
} else if (DictInt.class.isAssignableFrom(clazz)) {
String val = jsonReader.readString();
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
return Dict.parse(Integer.parseInt(val), constants);
} else {
return null;
}
} else if (jsonReader.isInt()) {
Class<?> clazz = TypeUtils.getClass(fieldType);
if (DictStr.class.isAssignableFrom(clazz)) {
String val = jsonReader.readString();
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
return Dict.parse(val, constants);
} else if (DictInt.class.isAssignableFrom(clazz)) {
Integer val = jsonReader.readInt32();
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
return Dict.parse(val, constants);
} else {
return null;
}
} else {
return null;
}
}
} catch (Exception e) {
log.error("字典枚举反序列化失败", e);
throw Exceptions.error(e, "字典枚举反序列化失败,类型:{},字段名:{}", fieldType, fieldName);
}
}
}

View File

@ -0,0 +1,97 @@
package com.njzscloud.common.core.fastjson.serializer;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.util.TypeUtils;
import com.alibaba.fastjson2.writer.ObjectWriter;
import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.core.ienum.IEnum;
import java.lang.reflect.Type;
/**
* Dict Fastjson <br/>
* 使: <br/>
* 1使 JSONField <br/>
* @JSONField(serializeUsing = DictObjectSerializer.class)<br/>
* 2使 JSONType <br/>
* @JSONType(writeEnumAsJavaBean = true, serializer = DictObjectSerializer.class)<br/><br/>
* JSON <br/>
* 1<br/>
* <pre>
* {
* "type": "", // 枚举全限定类名, 反序列化时会用到
* "name": "", // name 属性
* "ordinal": 0, // ordinal 属性
* "val": 1, // val 属性(字符串/数字), 反序列化时会用到
* "txt": "1" // txt 属性
* }</pre>
* 2<br/>
* <pre>
* {
* // ... 其他属性
* "原字段名称": 1, // val 属性(字符串/数字), 反序列化时会用到
* "原字段名称Txt": "1" // txt 属性
* }</pre>
*
* @see Dict
* @see DictInt
* @see DictStr
*/
public class DictObjectSerializer implements ObjectWriter<Dict> {
@Override
public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
if (object == null) {
jsonWriter.writeNull();
return;
}
if (fieldType == null) {
Dict<?> dict = (Dict<?>) object;
jsonWriter.startObject();
jsonWriter.writeName(IEnum.ENUM_TYPE);
jsonWriter.writeColon();
jsonWriter.writeString(dict.getClass().getName());
jsonWriter.writeName(IEnum.ENUM_NAME);
jsonWriter.writeColon();
jsonWriter.writeString(((Enum<?>) dict).name());
jsonWriter.writeName(IEnum.ENUM_ORDINAL);
jsonWriter.writeColon();
jsonWriter.writeInt32(((Enum<?>) dict).ordinal());
jsonWriter.writeName(Dict.ENUM_VAL);
jsonWriter.writeColon();
jsonWriter.writeAny(dict.getVal());
jsonWriter.writeName(Dict.ENUM_TXT);
jsonWriter.writeColon();
jsonWriter.writeString(dict.getTxt());
jsonWriter.endObject();
} else {
Class<?> clazz = TypeUtils.getClass(fieldType);
if (DictInt.class.isAssignableFrom(clazz)) {
DictInt dictInt = (DictInt) object;
jsonWriter.writeInt32(dictInt.getVal());
jsonWriter.writeName(fieldName + "Txt");
jsonWriter.writeColon();
jsonWriter.writeString(dictInt.getTxt());
} else if (DictStr.class.isAssignableFrom(clazz)) {
DictStr dictStr = (DictStr) object;
jsonWriter.writeString(dictStr.getVal());
jsonWriter.writeName(fieldName + "Txt");
jsonWriter.writeColon();
jsonWriter.writeString(dictStr.getTxt());
} else {
jsonWriter.writeNull();
}
}
}
}

View File

@ -0,0 +1,208 @@
package com.njzscloud.common.core.http;
import cn.hutool.core.collection.CollUtil;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.support.ResponseInfo;
import com.njzscloud.common.core.tuple.Tuple2;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
/**
* HTTP
*
* @see okhttp3.OkHttpClient
*/
@Slf4j
public final class HttpClient {
private static volatile HttpClient DEFAULT;
private final OkHttpClient OK_HTTP_CLIENT;
public HttpClient(HttpClientProperties httpClientProperties) {
this(httpClientProperties, null);
}
public HttpClient(OkHttpClient okHttpClient) {
OK_HTTP_CLIENT = okHttpClient;
}
public HttpClient(HttpClientProperties httpClientProperties, ExecutorService executorService) {
Duration callTimeout = httpClientProperties.getCallTimeout();
Duration keepAliveTime = httpClientProperties.getKeepAliveTime();
long keepAliveTimeSeconds = keepAliveTime.getSeconds();
int maxIdleConnections = httpClientProperties.getMaxIdleConnections();
Duration readTimeout = httpClientProperties.getReadTimeout();
Duration connectTimeout = httpClientProperties.getConnectTimeout();
ConnectionPool connectionPool = new ConnectionPool(maxIdleConnections, keepAliveTimeSeconds, TimeUnit.SECONDS);
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.callTimeout(callTimeout)
.readTimeout(readTimeout)
.connectTimeout(connectTimeout)
.connectionPool(connectionPool);
if (executorService != null) {
Dispatcher dispatcher = new Dispatcher(executorService);
builder.dispatcher(dispatcher);
}
this.OK_HTTP_CLIENT = builder.build();
}
private HttpClient() {
this(HttpClientProperties.DEFAULT);
}
public static synchronized HttpClient defaultHttpClient() {
if (DEFAULT == null) DEFAULT = new HttpClient();
return DEFAULT;
}
/* private Request buildRequest(HttpServer httpServer,
HttpEndpoint httpEndpoint,
RequestParamBuilder requestParamBuilder,
RequestInterceptor requestInterceptor) {
if (requestInterceptor != null) {
RequestParam processed = requestInterceptor.process(httpServer, httpEndpoint, requestParamBuilder);
if (processed != null) requestParam = processed;
}
HttpMethod httpMethod = httpEndpoint.httpMethod;
String url = httpEndpoint.urlTpl;
BodyParam<?> bodyParam = requestParam.bodyParam;
Map<String, Object> pathParamMap = requestParam.pathParam.getParam();
if (CollUtil.isNotEmpty(pathParamMap)) {
url = StrUtil.format(url, pathParamMap);
}
Map<String, Object> queryParamMap = requestParam.queryParam.getParam();
String query = Param.kvStr(queryParamMap);
if (StrUtil.isNotBlank(query)) {
query = "?" + query;
}
url = url + query;
Headers headers = null;
Map<String, Object> headerParamMap = requestParam.headerParam.getParam();
if (CollUtil.isNotEmpty(headerParamMap)) {
Headers.Builder headerBuilder = new Headers.Builder();
Set<Map.Entry<String, Object>> entries = headerParamMap.entrySet();
for (Map.Entry<String, Object> entry : entries) {
String key = entry.getKey();
Object value = entry.getValue();
if (value != null) headerBuilder.set(key, value.toString());
}
headers = headerBuilder.build();
}
RequestBody postBody = null;
if (httpMethod != HttpMethod.GET) {
byte[] param = bodyParam.getParam();
postBody = RequestBody.create(param, MediaType.parse(bodyParam.contentType));
}
Request.Builder requestBuilder = new Request.Builder()
.url(httpServer + url)
.method(httpMethod.name(), postBody);
if (headers != null) {
requestBuilder.headers(headers);
}
return requestBuilder.build();
}
public <T> T execute(
HttpServer httpServer,
HttpEndpoint endpoint,
RequestParamBuilder requestParamBuilder,
Type responseType,
RequestInterceptor requestInterceptor,
ResponseInterceptor<T> responseInterceptor
) {
ResponseResult.ResponseResultBuilder responseResultBuilder = ResponseResult.builder();
Response response = null;
ResponseBody responseBody = null;
Integer code = null;
String message = null;
Headers headers = null;
byte[] body = new byte[0];
try {
Request request = buildRequest(httpServer, endpoint, requestParamBuilder, requestInterceptor);
Call call = this.OK_HTTP_CLIENT.newCall(request);
response = call.execute();
responseBody = response.body();
if (responseBody != null) {
body = responseBody.bytes();
}
code = response.code();
message = response.message();
headers = response.headers();
responseResultBuilder.
code(code)
.status(message)
.headers(headers.toMultimap())
.body(body)
.build();
} catch (Exception e) {
log.error("", e);
responseResultBuilder.e(e);
} finally {
if (responseBody != null) responseBody.close();
if (response != null) response.close();
}
return responseInterceptor.process(
responseType,
httpServer, endpoint, requestParam,
responseResultBuilder.build());
} */
public ResponseInfo execute(HttpMethod httpMethod, String url, Map<String, String> headers, Tuple2<String, byte[]> body) {
Request.Builder requestBuilder = new Request.Builder().url(url);
if (httpMethod != HttpMethod.GET) {
requestBuilder.method(httpMethod.name(),
RequestBody.create(body.get_1(),
MediaType.parse(body.get_0())
));
}
if (CollUtil.isNotEmpty(headers)) {
Headers.Builder headerBuilder = new Headers.Builder();
headers.forEach(headerBuilder::set);
requestBuilder.headers(headerBuilder.build());
}
try (
Response response = this.OK_HTTP_CLIENT
.newCall(requestBuilder.build())
.execute();
ResponseBody responseBody = response.body()
) {
int code = response.code();
String message = response.message();
Map<String, List<String>> responseHeaders = response.headers().toMultimap();
byte[] bytes = responseBody == null ? new byte[0] : responseBody.bytes();
return ResponseInfo.create(code, message, responseHeaders, bytes);
} catch (Exception e) {
log.error("", e);
return ResponseInfo.create(0, "http 请求失败", null, null);
}
}
}

View File

@ -0,0 +1,25 @@
package com.njzscloud.common.core.http;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
*/
@Configuration
@EnableConfigurationProperties(HttpClientProperties.class)
public class HttpClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HttpClient httpClient(HttpClientProperties httpClientProperties) {
return new HttpClient(httpClientProperties);
}
@Bean
public HttpClientDecorator httpClientDecorator(HttpClient httpClient) {
return new HttpClientDecorator(httpClient);
}
}

View File

@ -0,0 +1,201 @@
package com.njzscloud.common.core.http;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.util.ReflectUtil;
import com.njzscloud.common.core.http.annotation.GetEndpoint;
import com.njzscloud.common.core.http.annotation.PostEndpoint;
import com.njzscloud.common.core.http.annotation.RemoteServer;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.interceptor.RequestInterceptor;
import com.njzscloud.common.core.http.interceptor.ResponseInterceptor;
import com.njzscloud.common.core.http.resolver.*;
import com.njzscloud.common.core.http.support.RequestInfo;
import com.njzscloud.common.core.http.support.ResponseInfo;
import com.njzscloud.common.core.http.support.ResponseResult;
import com.njzscloud.common.core.tuple.Tuple2;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* , "接口注解式" HTTP , 使 cglib
* <p>: 使, void, HTTP </p>
*/
@SuppressWarnings("unchecked")
public class HttpClientDecorator {
private static final SimpleCache<Class<?>, Object> ENHANCER_CACHE = new SimpleCache<>();
private final HttpClient HTTP_CLIENT;
public HttpClientDecorator(HttpClient httpClient) {
HTTP_CLIENT = httpClient;
}
public <T> T decorate(Class<T> clazz) {
return (T) ENHANCER_CACHE.get(clazz, () -> {
MethodInterceptor methodInterceptor = new MethodInterceptorImpl(clazz, HTTP_CLIENT);
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(new Class<?>[]{clazz});
enhancer.setCallback(methodInterceptor);
return enhancer.create();
});
}
@SuppressWarnings("RedundantLengthCheck")
private static class MethodInterceptorImpl implements MethodInterceptor {
private static final Pattern ADDR_PATTERN = Pattern.compile("(?<protocol>http(?:s)?):\\/\\/(?<host>[0-9a-zA-Z_\\-\\.]+)(?::(?<port>[0-9]+))?(?<baseUrl>\\/\\S*)*");
private final HttpClient HTTP_CLIENT;
private final String baseUrl;
private final RequestInterceptor requestInterceptor;
private final ResponseInterceptor responseInterceptor;
public MethodInterceptorImpl(Class<?> clazz, HttpClient httpClient) {
RemoteServer anno = clazz.getAnnotation(RemoteServer.class);
baseUrl = anno.value();
Matcher matcher = ADDR_PATTERN.matcher(baseUrl);
Assert.isTrue(matcher.matches(), "地址不合法");
Class<? extends RequestInterceptor> requestedInterceptorClazz = anno.requestInterceptor();
Class<? extends ResponseInterceptor> responseInterceptorClazz = anno.responseInterceptor();
requestInterceptor = ReflectUtil.newInstance(requestedInterceptorClazz);
responseInterceptor = ReflectUtil.newInstance(responseInterceptorClazz);
HTTP_CLIENT = httpClient;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
HttpMethod httpMethod;
String urlTpl;
String desc;
GetEndpoint getEndpoint = method.getAnnotation(GetEndpoint.class);
if (getEndpoint == null) {
PostEndpoint postEndpoint = method.getAnnotation(PostEndpoint.class);
if (postEndpoint == null) {
//
return null;
} else {
httpMethod = HttpMethod.POST;
urlTpl = baseUrl + postEndpoint.value();
desc = postEndpoint.desc();
}
} else {
httpMethod = HttpMethod.GET;
urlTpl = baseUrl + getEndpoint.value();
desc = getEndpoint.desc();
}
Object[] additional = requestInterceptor.process(httpMethod, urlTpl, args);
Parameter[] parameters = method.getParameters();
PathParamResolver pathParamResolver = new PathParamResolver();
QueryParamResolver queryParamResolver = new QueryParamResolver();
HeaderParamResolver headerParamResolver = new HeaderParamResolver();
BodyParamResolver bodyParamResolver = null;
JsonBodyParamResolver jsonBodyParamResolver = null;
XmlBodyParamResolver xmlBodyParamResolver = null;
FormBodyParamResolver formBodyParamResolver = null;
MultiBodyParamResolver multiBodyParamResolver = null;
List<ParamResolver<? extends Annotation, ?>> paramResolvers = ListUtil.list(false,
pathParamResolver,
queryParamResolver,
headerParamResolver
);
if (httpMethod != HttpMethod.GET) {
jsonBodyParamResolver = new JsonBodyParamResolver();
xmlBodyParamResolver = new XmlBodyParamResolver();
formBodyParamResolver = new FormBodyParamResolver();
multiBodyParamResolver = new MultiBodyParamResolver();
bodyParamResolver = new BodyParamResolver();
paramResolvers.add(jsonBodyParamResolver);
paramResolvers.add(xmlBodyParamResolver);
paramResolvers.add(formBodyParamResolver);
paramResolvers.add(multiBodyParamResolver);
paramResolvers.add(bodyParamResolver);
}
if (additional != null) {
for (Object o : additional) {
for (ParamResolver<? extends Annotation, ?> resolver : paramResolvers) {
resolver.resolve(o);
}
}
}
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
for (ParamResolver<? extends Annotation, ?> resolver : paramResolvers) {
resolver.resolve(parameter, args[i]);
}
}
urlTpl = pathParamResolver.resolve(httpMethod, urlTpl);
urlTpl = queryParamResolver.resolve(httpMethod, urlTpl);
Map<String, String> headers = headerParamResolver.resolve(httpMethod, urlTpl);
Tuple2<String, byte[]> body = null;
if (httpMethod != HttpMethod.GET) {
body = jsonBodyParamResolver.resolve(httpMethod, urlTpl);
if (body == null) body = xmlBodyParamResolver.resolve(httpMethod, urlTpl);
if (body == null) body = formBodyParamResolver.resolve(httpMethod, urlTpl);
if (body == null) body = multiBodyParamResolver.resolve(httpMethod, urlTpl);
if (body == null) body = bodyParamResolver.resolve(httpMethod, urlTpl);
if (body == null) body = Tuple2.create("", new byte[0]);
}
ResponseInfo responseInfo = HTTP_CLIENT.execute(httpMethod, urlTpl, headers, body);
RequestInfo requestInfo = RequestInfo.create(desc, httpMethod, urlTpl, headers, body);
Type genericReturnType = method.getGenericReturnType();
Type returnType;
Type rawType;
if (genericReturnType instanceof ParameterizedType) {
ParameterizedType typeWrap = (ParameterizedType) genericReturnType;
rawType = typeWrap.getRawType();
if (rawType == ResponseResult.class) {
returnType = typeWrap.getActualTypeArguments()[0];
} else {
returnType = typeWrap;
}
} else {
returnType = genericReturnType;
rawType = genericReturnType;
}
Object res = responseInterceptor.process(requestInfo, responseInfo, returnType);
if (ResponseResult.class == rawType) {
return ResponseResult
.builder()
.code(responseInfo.code)
.status(responseInfo.message)
.headers(responseInfo.header)
.body(res)
.e(responseInfo.e)
.build();
}
return res;
}
}
}

View File

@ -0,0 +1,62 @@
package com.njzscloud.common.core.http;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;
/**
* HTTP
*/
@Getter
@Setter
@Accessors(chain = true)
@ConfigurationProperties("okhttp-client")
public class HttpClientProperties {
public static final HttpClientProperties DEFAULT = new HttpClientProperties();
/**
*
* DNS
* 00
*/
private Duration callTimeout = Duration.ofSeconds(0);
/**
*
* 10s0
*/
private Duration readTimeout = Duration.ofSeconds(10);
/**
*
* TCP
* 10s0
*/
private Duration connectTimeout = Duration.ofSeconds(10);
/**
* , IP port HTTP
* 500
*/
private int maxIdleConnections = 500;
/**
*
* 10min
*/
private Duration keepAliveTime = Duration.ofMinutes(10);
/**
* SSL , false
*/
private boolean disableSslValidation = false;
/**
* , true
*/
private boolean followRedirects = true;
}

View File

@ -0,0 +1,23 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.processor.BodyParamProcessor;
import com.njzscloud.common.core.http.processor.DefaultBodyParamProcessor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, PARAMETER, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface BodyParam {
String contentType() default "";
/**
*
*
* @return ParamProcessor
*/
Class<? extends BodyParamProcessor> processor() default DefaultBodyParamProcessor.class;
}

View File

@ -0,0 +1,32 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.processor.FormBodyParamProcessor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.RoundingMode;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, PARAMETER, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormBodyParam {
/**
*
*
* @return String
*/
String value() default "";
String format() default "";
RoundingMode roundingMode() default RoundingMode.HALF_UP;
/**
*
*
* @return ParamProcessor
*/
Class<? extends FormBodyParamProcessor> processor() default FormBodyParamProcessor.class;
}

View File

@ -0,0 +1,28 @@
package com.njzscloud.common.core.http.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
/**
* GET
*/
@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GetEndpoint {
/**
*
*
* @return String
*/
String value() default "";
/**
*
*
* @return String
*/
String desc() default "";
}

View File

@ -0,0 +1,33 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.processor.DefaultHeaderParamProcessor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.RoundingMode;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, PARAMETER, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HeaderParam {
/**
*
*
* @return String
*/
String value() default "";
String format() default "";
RoundingMode roundingMode() default RoundingMode.HALF_UP;
/**
*
*
* @return ParamProcessor
*/
Class<? extends DefaultHeaderParamProcessor> processor() default DefaultHeaderParamProcessor.class;
}

View File

@ -0,0 +1,21 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.processor.DefaultJsonBodyParamProcessor;
import com.njzscloud.common.core.http.processor.JsonBodyParamProcessor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, PARAMETER, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonBodyParam {
/**
*
*
* @return ParamProcessor
*/
Class<? extends JsonBodyParamProcessor> processor() default DefaultJsonBodyParamProcessor.class;
}

View File

@ -0,0 +1,37 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.processor.MultiBodyParamProcessor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.RoundingMode;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, PARAMETER, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiBodyParam {
/**
*
*
* @return String
*/
String value() default "";
String format() default "";
String filename() default "";
RoundingMode roundingMode() default RoundingMode.HALF_UP;
String contentType() default "";
/**
*
*
* @return ParamProcessor
*/
Class<? extends MultiBodyParamProcessor> processor() default MultiBodyParamProcessor.class;
}

View File

@ -0,0 +1,34 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.processor.DefaultPathParamProcessor;
import com.njzscloud.common.core.http.processor.PathParamProcessor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.RoundingMode;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, PARAMETER, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PathParam {
/**
*
*
* @return String
*/
String value() default "";
String format() default "";
RoundingMode roundingMode() default RoundingMode.HALF_UP;
/**
*
*
* @return ParamProcessor
*/
Class<? extends PathParamProcessor> processor() default DefaultPathParamProcessor.class;
}

View File

@ -0,0 +1,28 @@
package com.njzscloud.common.core.http.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
/**
* POST
*/
@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PostEndpoint {
/**
*
*
* @return String
*/
String value() default "";
/**
*
*
* @return String
*/
String desc() default "";
}

View File

@ -0,0 +1,32 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.processor.DefaultQueryParamProcessor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.RoundingMode;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, PARAMETER, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface QueryParam {
/**
*
*
* @return String
*/
String value() default "";
String format() default "";
RoundingMode roundingMode() default RoundingMode.HALF_UP;
/**
*
*
* @return ParamProcessor
*/
Class<? extends DefaultQueryParamProcessor> processor() default DefaultQueryParamProcessor.class;
}

View File

@ -0,0 +1,44 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.interceptor.CompositeInterceptor;
import com.njzscloud.common.core.http.interceptor.RequestInterceptor;
import com.njzscloud.common.core.http.interceptor.ResponseInterceptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
/**
* HTTP
*/
@Target({TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RemoteServer {
/**
* , , http://localhost:80/api
*
* @return String
*/
String value();
/**
* ,
*
* @return RequestInterceptor.class
* @see RequestInterceptor
* @see CompositeInterceptor
*/
Class<? extends RequestInterceptor> requestInterceptor() default CompositeInterceptor.class;
/**
* ,
*
* @return ResponseInterceptor.class
* @see ResponseInterceptor
* @see CompositeInterceptor
*/
Class<? extends ResponseInterceptor> responseInterceptor() default CompositeInterceptor.class;
}

View File

@ -0,0 +1,21 @@
package com.njzscloud.common.core.http.annotation;
import com.njzscloud.common.core.http.processor.DefaultXmlBodyParamProcessor;
import com.njzscloud.common.core.http.processor.XmlBodyParamProcessor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, PARAMETER, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface XmlBodyParam {
/**
*
*
* @return ParamProcessor
*/
Class<? extends XmlBodyParamProcessor> processor() default DefaultXmlBodyParamProcessor.class;
}

View File

@ -0,0 +1,8 @@
package com.njzscloud.common.core.http.constant;
/**
* HTTP
*/
public enum HttpMethod {
GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT, PATCH
}

View File

@ -0,0 +1,39 @@
package com.njzscloud.common.core.http.constant;
import cn.hutool.core.util.StrUtil;
/**
* MIME
*/
public interface Mime {
String FORM = "application/x-www-form-urlencoded";
String MULTIPART_FORM = "multipart/form-data";
String BINARY = "application/octet-stream";
String JSON = "application/json";
String X_NDJSON = "application/x-ndjson";
String XML = "application/xml";
String TXT = "text/plain";
String HTML = "text/html";
String CSS = "text/css";
String GIF = "image/gif";
String JPG = "image/jpeg";
String PNG = "image/png";
String SVG = "image/svg+xml";
String WEBP = "image/webp";
String PDF = "image/pdf";
String PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
String DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
String PPT = "application/vnd.ms-powerpoint";
String XLS = "application/vnd.ms-excel";
String DOC = "application/msword";
String ZIP = "application/zip";
String MP3 = "audio/mpeg";
String MP4 = "video/mp4";
static String u8Val(String mime) {
return StrUtil.format("{};charset={}", mime, "UTF-8");
}
}

View File

@ -0,0 +1,47 @@
package com.njzscloud.common.core.http.interceptor;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.support.RequestInfo;
import com.njzscloud.common.core.http.support.ResponseInfo;
import com.njzscloud.common.core.jackson.Jackson;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Type;
/**
* , JSON
*
* @see RequestInterceptor
* @see ResponseInterceptor
*/
@Slf4j
public class CompositeInterceptor implements RequestInterceptor, ResponseInterceptor {
@Override
public Object[] process(HttpMethod method, String url, Object[] args) {
return null;
}
@Override
public Object process(RequestInfo requestInfo, ResponseInfo responseInfo, Type responseType) {
System.out.println(Jackson.toJsonStr(requestInfo));
System.out.println(new String(requestInfo.body));
Object data = new String(responseInfo.body);
/* if (responseInfo.success) {
if (responseInfo.body != null) {
data = Jackson.toBean(responseInfo.body, responseType);
log.info("Jackson: {}", JSON.toJSONString(data));
data = JSON.parseObject(responseInfo.body, responseType);
log.info("Fastjson: {}", JSON.toJSONString(data));
}
} else {
log.error("HTTP请求失败");
} */
return data;
}
}

View File

@ -0,0 +1,11 @@
package com.njzscloud.common.core.http.interceptor;
import com.njzscloud.common.core.http.constant.HttpMethod;
/**
* , ,
*/
public interface RequestInterceptor {
Object[] process(HttpMethod method, String url, Object[] args);
}

View File

@ -0,0 +1,17 @@
package com.njzscloud.common.core.http.interceptor;
import com.njzscloud.common.core.http.support.RequestInfo;
import com.njzscloud.common.core.http.support.ResponseInfo;
import java.lang.reflect.Type;
/**
* , ,
* ,
*
*/
public interface ResponseInterceptor {
Object process(RequestInfo requestInfo, ResponseInfo responseInfo, Type responseType);
}

View File

@ -0,0 +1,7 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.BodyParam;
public interface BodyParamProcessor {
byte[] process(BodyParam bodyParam, String paramName, Class<?> paramClazz, Object paramValue);
}

View File

@ -0,0 +1,19 @@
package com.njzscloud.common.core.http.processor;
import cn.hutool.core.util.ReflectUtil;
import com.njzscloud.common.core.http.annotation.BodyParam;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
public class DefaultBodyParamProcessor implements BodyParamProcessor {
@Override
public byte[] process(BodyParam bodyParam, String paramName, Class<?> paramClazz, Object paramValue) {
Method toBytesMethod = ReflectUtil.getMethod(paramClazz, "toBytes");
if (toBytesMethod == null) {
return paramValue.toString().getBytes(StandardCharsets.UTF_8);
} else {
return ReflectUtil.invoke(paramValue, toBytesMethod);
}
}
}

View File

@ -0,0 +1,13 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.FormBodyParam;
import java.util.List;
import java.util.Map;
public class DefaultFormBodyParamProcessor implements FormBodyParamProcessor {
@Override
public void process(FormBodyParam formBodyParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, List<String>> result) {
}
}

View File

@ -0,0 +1,13 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.HeaderParam;
import java.util.List;
import java.util.Map;
public class DefaultHeaderParamProcessor implements HeaderParamProcessor {
@Override
public void process(HeaderParam headerParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, List<String>> result) {
}
}

View File

@ -0,0 +1,11 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.JsonBodyParam;
import com.njzscloud.common.core.jackson.Jackson;
public class DefaultJsonBodyParamProcessor implements JsonBodyParamProcessor {
@Override
public byte[] process(JsonBodyParam jsonBodyParam, String paramName, Class<?> paramClazz, Object paramValue) {
return Jackson.toJsonBytes(paramValue);
}
}

View File

@ -0,0 +1,13 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.MultiBodyParam;
import com.njzscloud.common.core.tuple.Tuple3;
import java.util.Map;
public class DefaultMultiBodyParamProcessor implements MultiBodyParamProcessor {
@Override
public void process(MultiBodyParam multiBodyParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, Tuple3<String, String, byte[]>> result) {
}
}

View File

@ -0,0 +1,13 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.PathParam;
import java.util.Map;
public class DefaultPathParamProcessor implements PathParamProcessor {
@Override
public void process(PathParam pathParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, String> result) {
}
}

View File

@ -0,0 +1,13 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.QueryParam;
import java.util.List;
import java.util.Map;
public class DefaultQueryParamProcessor implements QueryParamProcessor {
@Override
public void process(QueryParam queryParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, List<String>> result) {
}
}

View File

@ -0,0 +1,11 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.XmlBodyParam;
import com.njzscloud.common.core.jackson.Jackson;
public class DefaultXmlBodyParamProcessor implements XmlBodyParamProcessor {
@Override
public byte[] process(XmlBodyParam xmlBodyParam, String paramName, Class<?> paramClazz, Object paramValue) {
return Jackson.toXmlBytes(paramValue);
}
}

View File

@ -0,0 +1,10 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.FormBodyParam;
import java.util.List;
import java.util.Map;
public interface FormBodyParamProcessor {
void process(FormBodyParam formBodyParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, List<String>> result);
}

View File

@ -0,0 +1,10 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.HeaderParam;
import java.util.List;
import java.util.Map;
public interface HeaderParamProcessor {
void process(HeaderParam headerParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, List<String>> result);
}

View File

@ -0,0 +1,7 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.JsonBodyParam;
public interface JsonBodyParamProcessor {
byte[] process(JsonBodyParam jsonBodyParam, String paramName, Class<?> paramClazz, Object paramValue);
}

View File

@ -0,0 +1,10 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.MultiBodyParam;
import com.njzscloud.common.core.tuple.Tuple3;
import java.util.Map;
public interface MultiBodyParamProcessor {
void process(MultiBodyParam multiBodyParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, Tuple3<String, String, byte[]>> result);
}

View File

@ -0,0 +1,9 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.PathParam;
import java.util.Map;
public interface PathParamProcessor {
void process(PathParam pathParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, String> result);
}

View File

@ -0,0 +1,10 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.QueryParam;
import java.util.List;
import java.util.Map;
public interface QueryParamProcessor {
void process(QueryParam queryParam, String paramName, Class<?> paramClazz, Object paramValue, Map<String, List<String>> result);
}

View File

@ -0,0 +1,7 @@
package com.njzscloud.common.core.http.processor;
import com.njzscloud.common.core.http.annotation.XmlBodyParam;
public interface XmlBodyParamProcessor {
byte[] process(XmlBodyParam xmlBodyParam, String paramName, Class<?> paramClazz, Object paramValue);
}

View File

@ -0,0 +1,43 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.util.ReflectUtil;
import com.njzscloud.common.core.http.annotation.BodyParam;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.constant.Mime;
import com.njzscloud.common.core.http.processor.BodyParamProcessor;
import com.njzscloud.common.core.tuple.Tuple2;
public class BodyParamResolver extends ParamResolver<BodyParam, Tuple2<String, byte[]>> {
byte[] result = null;
String contentType;
public BodyParamResolver() {
super(BodyParam.class);
}
@Override
public Tuple2<String, byte[]> resolve(HttpMethod httpMethod, String urlTpl) {
if (result == null) return null;
return Tuple2.create(contentType == null ? Mime.BINARY : contentType, result);
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveParameter(BodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
contentType = anno.contentType();
Class<? extends BodyParamProcessor> processorClazz = anno.processor();
result = ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue);
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveField(boolean hasParameterAnno, BodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (hasParameterAnno) return false;
contentType = anno.contentType();
Class<? extends BodyParamProcessor> processorClazz = anno.processor();
result = ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue);
return true;
}
}

View File

@ -0,0 +1,97 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.http.annotation.FormBodyParam;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.constant.Mime;
import com.njzscloud.common.core.http.processor.FormBodyParamProcessor;
import com.njzscloud.common.core.tuple.Tuple2;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class FormBodyParamResolver extends ParamResolver<FormBodyParam, Tuple2<String, byte[]>> {
Map<String, List<String>> result = new HashMap<>();
public FormBodyParamResolver() {
super(FormBodyParam.class);
}
@Override
public Tuple2<String, byte[]> resolve(HttpMethod httpMethod, String urlTpl) {
if (result.isEmpty()) return null;
StringJoiner joiner = new StringJoiner("&");
result.forEach((k, v) -> v.forEach(it -> joiner.add(k + "=" + it)));
byte[] bytes = joiner.toString().getBytes(StandardCharsets.UTF_8);
return Tuple2.create(Mime.FORM, bytes);
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveParameter(FormBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
String value = anno.value();
if (isKv(paramClazz)) resolveKv(paramValue);
else if (isArr(paramClazz)) resolveArr(StrUtil.isBlank(value) ? paramName : value, paramValue);
else if (isStr(paramClazz) || isNum(paramClazz) || isDt(paramClazz)) resolveFormable(anno, paramName, paramClazz, paramValue);
else return false;
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveField(boolean hasParameterAnno, FormBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (anno != null) {
Class<? extends FormBodyParamProcessor> processorClazz = anno.processor();
if (processorClazz != FormBodyParamProcessor.class) {
ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue, result);
} else if (!hasParameterAnno && isKv(paramClazz)) {
resolveKv(paramValue);
}
}
if (anno != null) {
String value = anno.value();
if (StrUtil.isNotBlank(value)) paramName = value;
}
if (isArr(paramClazz)) resolveArr(paramName, paramValue);
else resolveFormable(anno, paramName, paramClazz, paramValue);
return true;
}
protected void resolveKv(Object paramValue) {
((Map<?, ?>) paramValue).forEach((k, v) -> {
if (v == null) return;
Class<?> clazz = v.getClass();
String paramName = k.toString();
if (isArr(clazz)) resolveArr(paramName, v);
else resolveFormable(null, paramName, clazz, v);
});
}
protected void resolveFormable(FormBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
String key = paramName;
String val;
String format = null;
RoundingMode roundingMode = null;
if (anno != null) {
format = anno.format();
roundingMode = anno.roundingMode();
String value = anno.value();
if (StrUtil.isNotBlank(value)) key = value;
}
if (isNum(paramClazz)) {
val = formatNum(format, roundingMode, paramValue);
} else if (isDt(paramClazz)) {
val = formatDt(format, paramValue);
} else {
val = paramValue.toString();
}
result.computeIfAbsent(key, it -> new ArrayList<>()).add(val);
}
}

View File

@ -0,0 +1,92 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.http.annotation.HeaderParam;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.processor.DefaultHeaderParamProcessor;
import java.math.RoundingMode;
import java.util.*;
public class HeaderParamResolver extends ParamResolver<HeaderParam, Map<String, String>> {
Map<String, List<String>> result = new TreeMap<>();
public HeaderParamResolver() {
super(HeaderParam.class);
}
@Override
public Map<String, String> resolve(HttpMethod httpMethod, String urlTpl) {
HashMap<String, String> map = new HashMap<>();
result.forEach((k, v) -> map.put(k, String.join(",", v)));
return map;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveParameter(HeaderParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
String value = anno.value();
if (isKv(paramClazz)) resolveKv(paramValue);
else if (isArr(paramClazz)) resolveArr(StrUtil.isBlank(value) ? paramName : value, paramValue);
else if (isStr(paramClazz) || isNum(paramClazz) || isDt(paramClazz)) resolveFormable(anno, paramName, paramClazz, paramValue);
else return false;
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveField(boolean hasParameterAnno, HeaderParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (anno != null) {
Class<? extends DefaultHeaderParamProcessor> processorClazz = anno.processor();
if (processorClazz != DefaultHeaderParamProcessor.class) {
ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue, result);
} else if (!hasParameterAnno && isKv(paramClazz)) {
resolveKv(paramValue);
}
}
if (anno != null) {
String value = anno.value();
if (StrUtil.isNotBlank(value)) paramName = value;
}
if (isArr(paramClazz)) resolveArr(paramName, paramValue);
else resolveFormable(anno, paramName, paramClazz, paramValue);
return true;
}
protected void resolveKv(Object paramValue) {
((Map<?, ?>) paramValue).forEach((k, v) -> {
if (v == null) return;
Class<?> clazz = v.getClass();
String paramName = k.toString();
if (isArr(clazz)) resolveArr(paramName, v);
else resolveFormable(null, paramName, clazz, v);
});
}
protected void resolveFormable(HeaderParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
String key = paramName;
String val;
String format = null;
RoundingMode roundingMode = null;
if (anno != null) {
format = anno.format();
roundingMode = anno.roundingMode();
String value = anno.value();
if (StrUtil.isNotBlank(value)) key = value;
}
if (isNum(paramClazz)) {
val = formatNum(format, roundingMode, paramValue);
} else if (isDt(paramClazz)) {
val = formatDt(format, paramValue);
} else {
val = paramValue.toString();
}
result.computeIfAbsent(key, it -> new ArrayList<>()).add(val);
}
}

View File

@ -0,0 +1,41 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.util.ReflectUtil;
import com.njzscloud.common.core.http.annotation.JsonBodyParam;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.constant.Mime;
import com.njzscloud.common.core.http.processor.JsonBodyParamProcessor;
import com.njzscloud.common.core.tuple.Tuple2;
public class JsonBodyParamResolver extends ParamResolver<JsonBodyParam, Tuple2<String, byte[]>> {
byte[] result = null;
public JsonBodyParamResolver() {
super(JsonBodyParam.class);
}
@Override
public Tuple2<String, byte[]> resolve(HttpMethod httpMethod, String urlTpl) {
if (result == null) return null;
return Tuple2.create(Mime.JSON, result);
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveParameter(JsonBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
Class<? extends JsonBodyParamProcessor> processorClazz = anno.processor();
result = ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue);
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveField(boolean hasParameterAnno, JsonBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (hasParameterAnno) return false;
Class<? extends JsonBodyParamProcessor> processorClazz = anno.processor();
result = ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue);
return true;
}
}

View File

@ -0,0 +1,141 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.njzscloud.common.core.http.annotation.MultiBodyParam;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.constant.Mime;
import com.njzscloud.common.core.http.processor.MultiBodyParamProcessor;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.core.tuple.Tuple3;
import java.io.ByteArrayOutputStream;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class MultiBodyParamResolver extends ParamResolver<MultiBodyParam, Tuple2<String, byte[]>> {
private static final byte[] DASHDASH = "--".getBytes(StandardCharsets.UTF_8);
private static final byte[] CRLF = StrUtil.CRLF.getBytes(StandardCharsets.UTF_8);
Map<String, Tuple3<String, String, byte[]>> result = new HashMap<>();
public MultiBodyParamResolver() {
super(MultiBodyParam.class);
}
@Override
public Tuple2<String, byte[]> resolve(HttpMethod httpMethod, String urlTpl) {
if (result.isEmpty()) return null;
String boundary = RandomUtil.randomString(16);
String contentType = Mime.MULTIPART_FORM + "; boundary=" + boundary;
byte[] bytes = buildContent(boundary.getBytes(StandardCharsets.UTF_8));
return Tuple2.create(contentType, bytes);
}
private byte[] buildContent(byte[] boundary) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
result.forEach((k, v) -> {
String filename = URLUtil.encode(v.get_0());
String contentType = v.get_1();
byte[] bytes = v.get_2();
IoUtil.write(byteArrayOutputStream, false, DASHDASH);
IoUtil.write(byteArrayOutputStream, false, boundary);
IoUtil.write(byteArrayOutputStream, false, CRLF);
byte[] contentDisposition;
if (StrUtil.isNotBlank(filename)) {
contentDisposition = StrUtil.format("Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"", k, filename).getBytes(StandardCharsets.UTF_8);
} else {
contentDisposition = StrUtil.format("Content-Disposition: form-data; name=\"{}\"", k).getBytes(StandardCharsets.UTF_8);
}
IoUtil.write(byteArrayOutputStream, false, contentDisposition);
IoUtil.write(byteArrayOutputStream, false, CRLF);
if (StrUtil.isNotBlank(contentType)) {
IoUtil.write(byteArrayOutputStream, false, ("Content-Type: " + contentType).getBytes(StandardCharsets.UTF_8));
IoUtil.write(byteArrayOutputStream, false, CRLF);
}
IoUtil.write(byteArrayOutputStream, false, CRLF);
IoUtil.write(byteArrayOutputStream, false, bytes);
IoUtil.write(byteArrayOutputStream, false, CRLF);
});
IoUtil.write(byteArrayOutputStream, false, DASHDASH);
IoUtil.write(byteArrayOutputStream, false, boundary);
IoUtil.write(byteArrayOutputStream, false, DASHDASH);
return byteArrayOutputStream.toByteArray();
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveParameter(MultiBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (isKv(paramClazz)) resolveKv(paramValue);
else if (isStr(paramClazz) || isNum(paramClazz) || isDt(paramClazz) || isBytes(paramClazz) || isIn(paramClazz) || isFile(paramClazz)) resolveFormable(anno, paramName, paramClazz, paramValue);
else return false;
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveField(boolean hasParameterAnno, MultiBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (anno != null) {
Class<? extends MultiBodyParamProcessor> processorClazz = anno.processor();
if (processorClazz != MultiBodyParamProcessor.class) {
ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue, result);
} else if (!hasParameterAnno && isKv(paramClazz)) {
resolveKv(paramValue);
}
}
resolveFormable(anno, paramName, paramClazz, paramValue);
return true;
}
protected void resolveFormable(MultiBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
String key = paramName;
byte[] val;
String mime;
String filename = null;
String format = null;
RoundingMode roundingMode = null;
if (anno != null) {
format = anno.format();
roundingMode = anno.roundingMode();
String value = anno.value();
if (StrUtil.isNotBlank(value)) key = value;
}
if (isNum(paramClazz)) {
mime = Mime.TXT;
val = formatNum(format, roundingMode, paramValue).getBytes(StandardCharsets.UTF_8);
} else if (isDt(paramClazz)) {
mime = Mime.TXT;
val = formatDt(format, paramValue).getBytes(StandardCharsets.UTF_8);
} else if (isBytes(paramClazz)) {
mime = Mime.BINARY;
val = (byte[]) paramValue;
} else if (isIn(paramClazz) || isFile(paramClazz)) {
mime = Mime.BINARY;
Tuple2<String, byte[]> fileInfo = toFileInfo(paramValue);
filename = fileInfo.get_0();
val = fileInfo.get_1();
} else {
mime = Mime.TXT;
val = paramValue.toString().getBytes(StandardCharsets.UTF_8);
}
result.put(key, Tuple3.create(filename, mime, val));
}
}

View File

@ -0,0 +1,242 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.core.tuple.Tuple3;
import lombok.RequiredArgsConstructor;
import java.io.File;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.*;
@RequiredArgsConstructor
public abstract class ParamResolver<T extends Annotation, R> {
protected final Class<T> annoType;
abstract public R resolve(HttpMethod httpMethod, String urlTpl);
public final void resolve(Parameter parameter, Object obj) {
if (obj == null) return;
Class<?> parameterClazz = obj.getClass();
String parameterName;
T parameterAnno = null;
if (parameter == null) {
parameterName = StrUtil.lowerFirst(obj.getClass().getName());
} else {
parameterName = parameter.getName();
parameterAnno = parameter.getAnnotation(this.annoType);
}
if (parameterAnno == null) {
parameterAnno = parameterClazz.getAnnotation(this.annoType);
}
int state = 0;
if (parameterAnno != null) {
state = this.resolveParameter(parameterAnno, parameterName, parameterClazz, obj) ?
1 :
2;
}
if (state == 1) return;
List<Method> getterMethods = ReflectUtil.getPublicMethods(parameterClazz,
it -> (it.getName().startsWith("get") || it.getName().startsWith("is")) &&
!Modifier.isStatic(it.getModifiers()) &&
!it.getName().equals("getClass")
);
for (Method getterMethod : getterMethods) {
Object fieldValue = ReflectUtil.invoke(obj, getterMethod);
if (fieldValue == null) continue;
String getterMethodName = getterMethod.getName();
Class<?> returnType = getterMethod.getReturnType();
String fieldName = StrUtil.lowerFirst(getterMethodName.startsWith("get") ?
getterMethodName.substring(3) :
getterMethodName.substring(2));
T fieldAnno = getterMethod.getAnnotation(this.annoType);
if (fieldAnno == null) {
Field field = ReflectUtil.getField(parameterClazz, fieldName);
fieldAnno = field.getAnnotation(this.annoType);
}
if (fieldAnno == null && state == 0) continue;
this.resolveField(state == 2, fieldAnno, fieldName, returnType, fieldValue);
}
}
public final void resolve(Object obj) {
resolve(null, obj);
}
protected abstract boolean resolveParameter(T anno, String paramName, Class<?> paramClazz, Object paramValue);
protected abstract boolean resolveField(boolean hasParameterAnno, T anno, String paramName, Class<?> paramClazz, Object paramValue);
public final boolean isNum(Class<?> clazz) {
return clazz == byte.class || clazz == short.class ||
clazz == int.class || clazz == long.class ||
clazz == float.class || clazz == double.class
|| Number.class.isAssignableFrom(clazz);
}
protected final boolean isStr(Class<?> clazz) {
return clazz == String.class || clazz == char.class || clazz == Character.class;
}
protected final boolean isDt(Class<?> clazz) {
return clazz == LocalDate.class ||
clazz == LocalTime.class ||
clazz == LocalDateTime.class ||
Date.class.isAssignableFrom(clazz);
}
protected final boolean isArr(Class<?> clazz) {
return Collection.class.isAssignableFrom(clazz) || clazz.isArray();
}
protected final Collection<?> toArr(Object obj) {
if (obj == null) return null;
Class<?> clazz = obj.getClass();
if (clazz.isArray()) {
return Arrays.asList((Object[]) obj);
} else if (Collection.class.isAssignableFrom(clazz)) {
return (Collection<?>) obj;
} else {
throw new RuntimeException("不是数组");
}
}
protected final boolean isKv(Class<?> clazz) {
return Map.class.isAssignableFrom(clazz);
}
protected final boolean isIn(Class<?> clazz) {
return InputStream.class.isAssignableFrom(clazz);
}
protected final boolean isBytes(Class<?> clazz) {
return clazz == byte[].class || clazz == Byte[].class;
}
protected final Tuple2<String, byte[]> toFileInfo(Object obj) {
if (obj == null) return null;
Class<?> clazz = obj.getClass();
if (isIn(clazz)) {
byte[] bytes = IoUtil.readBytes((InputStream) obj);
return Tuple3.create(null, bytes);
} else if (isFile(clazz)) {
byte[] bytes = FileUtil.readBytes((File) obj);
String name = ((File) obj).getName();
return Tuple3.create(name, bytes);
} else {
throw new RuntimeException("不是文件");
}
}
protected final boolean isFile(Class<?> clazz) {
return File.class.isAssignableFrom(clazz);
}
protected final String formatDt(String format, Object value) {
if (value == null) return null;
Class<?> clazz = value.getClass();
if (Date.class.isAssignableFrom(clazz)) {
if (StrUtil.isBlank(format)) format = DatePattern.NORM_DATETIME_PATTERN;
return DateUtil.format(((Date) value), format);
} else if (LocalDateTime.class.isAssignableFrom(clazz)) {
if (StrUtil.isBlank(format)) format = DatePattern.NORM_DATETIME_PATTERN;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
return dateTimeFormatter.format((TemporalAccessor) value);
} else if (LocalDate.class.isAssignableFrom(clazz)) {
if (StrUtil.isBlank(format)) format = DatePattern.NORM_DATE_PATTERN;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
return dateTimeFormatter.format((TemporalAccessor) value);
} else if (LocalTime.class.isAssignableFrom(clazz)) {
if (StrUtil.isBlank(format)) format = DatePattern.NORM_TIME_PATTERN;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
return dateTimeFormatter.format((TemporalAccessor) value);
} else {
throw new RuntimeException("不是时间");
}
}
protected final String formatDt(Object value) {
return formatDt(null, value);
}
protected final String formatNum(String format, RoundingMode roundingMode, Object value) {
if (value == null) return null;
if (StrUtil.isBlank(format)) return formatNum(value);
Class<?> clazz = value.getClass();
if (isNum(clazz)) {
return NumberUtil.decimalFormat(format, value, roundingMode);
} else {
throw new RuntimeException("不是数字");
}
}
protected final String formatNum(String format, Object value) {
return formatNum(format, null, value);
}
protected final String formatNum(Object value) {
if (value == null) return null;
Class<?> clazz = value.getClass();
if (isNum(clazz)) {
if (clazz == BigDecimal.class) {
return ((BigDecimal) value).toPlainString();
} else {
return value.toString();
}
} else {
throw new RuntimeException("不是数字");
}
}
protected void resolveKv(Object paramValue) {
((Map<?, ?>) paramValue).forEach((k, v) -> {
if (v == null) return;
resolveFormable(null, k.toString(), v.getClass(), v);
});
}
protected void resolveArr(String paramName, Object paramValue) {
toArr(paramValue)
.stream()
.filter(Objects::nonNull)
.forEach(it -> resolveFormable(null, paramName, it.getClass(), it));
}
protected void resolveFormable(T anno, String paramName, Class<?> paramClazz, Object paramValue) {
}
}

View File

@ -0,0 +1,74 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.http.annotation.PathParam;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.processor.PathParamProcessor;
import java.math.RoundingMode;
import java.util.Map;
import java.util.TreeMap;
public class PathParamResolver extends ParamResolver<PathParam, String> {
Map<String, String> result = new TreeMap<>();
public PathParamResolver() {
super(PathParam.class);
}
@Override
public String resolve(HttpMethod httpMethod, String urlTpl) {
if (result.isEmpty()) return urlTpl;
return StrUtil.format(urlTpl, result);
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveParameter(PathParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (isKv(paramClazz)) resolveKv(paramValue);
else if (isStr(paramClazz) || isNum(paramClazz) || isDt(paramClazz)) resolveFormable(anno, paramName, paramClazz, paramValue);
else return false;
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveField(boolean hasParameterAnno, PathParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (anno != null) {
Class<? extends PathParamProcessor> processorClazz = anno.processor();
if (processorClazz != PathParamProcessor.class) {
ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue, result);
} else if (!hasParameterAnno && isKv(paramClazz)) {
resolveKv(paramValue);
}
}
resolveFormable(anno, paramName, paramClazz, paramValue);
return true;
}
protected void resolveFormable(PathParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
String key = paramName;
String val;
String format = null;
RoundingMode roundingMode = null;
if (anno != null) {
format = anno.format();
roundingMode = anno.roundingMode();
String value = anno.value();
if (StrUtil.isNotBlank(value)) key = value;
}
if (isNum(paramClazz)) {
val = formatNum(format, roundingMode, paramValue);
} else if (isDt(paramClazz)) {
val = formatDt(format, paramValue);
} else {
val = paramValue.toString();
}
result.put(key, val);
}
}

View File

@ -0,0 +1,92 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.http.annotation.QueryParam;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.processor.DefaultQueryParamProcessor;
import java.math.RoundingMode;
import java.util.*;
public class QueryParamResolver extends ParamResolver<QueryParam, String> {
Map<String, List<String>> result = new TreeMap<>();
public QueryParamResolver() {
super(QueryParam.class);
}
@Override
public String resolve(HttpMethod httpMethod, String urlTpl) {
if (result.isEmpty()) return urlTpl;
StringJoiner joiner = new StringJoiner("&", urlTpl + (urlTpl.contains("?") ? "&" : "?"), "");
result.forEach((k, v) -> v.forEach(it -> joiner.add(k + "=" + it)));
return joiner.toString();
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveParameter(QueryParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
String value = anno.value();
if (isKv(paramClazz)) resolveKv(paramValue);
else if (isArr(paramClazz)) resolveArr(StrUtil.isBlank(value) ? paramName : value, paramValue);
else if (isStr(paramClazz) || isNum(paramClazz) || isDt(paramClazz)) resolveFormable(anno, paramName, paramClazz, paramValue);
else return false;
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveField(boolean hasParameterAnno, QueryParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (anno != null) {
Class<? extends DefaultQueryParamProcessor> processorClazz = anno.processor();
if (processorClazz != DefaultQueryParamProcessor.class) {
ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue, result);
} else if (!hasParameterAnno && isKv(paramClazz)) {
resolveKv(paramValue);
}
}
if (anno != null) {
String value = anno.value();
if (StrUtil.isNotBlank(value)) paramName = value;
}
if (isArr(paramClazz)) resolveArr(paramName, paramValue);
else resolveFormable(anno, paramName, paramClazz, paramValue);
return true;
}
protected void resolveKv(Object paramValue) {
((Map<?, ?>) paramValue).forEach((k, v) -> {
if (v == null) return;
Class<?> clazz = v.getClass();
String paramName = k.toString();
if (isArr(clazz)) resolveArr(paramName, v);
else resolveFormable(null, paramName, clazz, v);
});
}
protected void resolveFormable(QueryParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
String key = paramName;
String val;
String format = null;
RoundingMode roundingMode = null;
if (anno != null) {
format = anno.format();
roundingMode = anno.roundingMode();
String value = anno.value();
if (StrUtil.isNotBlank(value)) key = value;
}
if (isNum(paramClazz)) {
val = formatNum(format, roundingMode, paramValue);
} else if (isDt(paramClazz)) {
val = formatDt(format, paramValue);
} else {
val = paramValue.toString();
}
result.computeIfAbsent(key, it -> new ArrayList<>()).add(val);
}
}

View File

@ -0,0 +1,41 @@
package com.njzscloud.common.core.http.resolver;
import cn.hutool.core.util.ReflectUtil;
import com.njzscloud.common.core.http.annotation.XmlBodyParam;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.http.constant.Mime;
import com.njzscloud.common.core.http.processor.XmlBodyParamProcessor;
import com.njzscloud.common.core.tuple.Tuple2;
public class XmlBodyParamResolver extends ParamResolver<XmlBodyParam, Tuple2<String, byte[]>> {
byte[] result = null;
public XmlBodyParamResolver() {
super(XmlBodyParam.class);
}
@Override
public Tuple2<String, byte[]> resolve(HttpMethod httpMethod, String urlTpl) {
if (result == null) return null;
return Tuple2.create(Mime.XML, result);
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveParameter(XmlBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
Class<? extends XmlBodyParamProcessor> processorClazz = anno.processor();
result = ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue);
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean resolveField(boolean hasParameterAnno, XmlBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
if (hasParameterAnno) return false;
Class<? extends XmlBodyParamProcessor> processorClazz = anno.processor();
result = ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue);
return true;
}
}

View File

@ -0,0 +1,10 @@
package com.njzscloud.common.core.http.support;
import java.lang.annotation.Annotation;
import java.util.List;
public class ParameterInfo {
private String name;
private List<? extends Annotation> annoList;
private Object value;
}

View File

@ -0,0 +1,29 @@
package com.njzscloud.common.core.http.support;
import cn.hutool.core.map.MapUtil;
import com.njzscloud.common.core.http.constant.HttpMethod;
import com.njzscloud.common.core.tuple.Tuple2;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import java.util.Map;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestInfo {
public final String desc;
public final HttpMethod method;
public final String url;
public final Map<String, String> headers;
public final String contentType;
public final byte[] body;
public static RequestInfo create(String desc, HttpMethod method, String url, Map<String, String> headers, Tuple2<String, byte[]> body) {
String contentType = null;
byte[] bytes = null;
if (body != null) {
contentType = body.get_0();
bytes = body.get_1();
}
return new RequestInfo(desc, method, url, MapUtil.unmodifiable(headers), contentType, bytes);
}
}

View File

@ -0,0 +1,32 @@
package com.njzscloud.common.core.http.support;
import cn.hutool.core.map.MapUtil;
import java.util.List;
import java.util.Map;
public class ResponseInfo {
public final boolean success;
public final int code;
public final String message;
public final Map<String, List<String>> header;
public final byte[] body;
public final Exception e;
public ResponseInfo(boolean success, int code, String message, Map<String, List<String>> header, byte[] body, Exception e) {
this.success = success;
this.code = code;
this.message = message;
this.header = header;
this.body = body;
this.e = e;
}
public static ResponseInfo create(int code, String message, Map<String, List<String>> header, byte[] body) {
return new ResponseInfo(code >= 200 && code <= 299, code, message, header, body, null);
}
public static ResponseInfo create(Exception e) {
return new ResponseInfo(false, 0, e.getMessage(), MapUtil.empty(), new byte[0], e);
}
}

View File

@ -0,0 +1,97 @@
package com.njzscloud.common.core.http.support;
import java.util.List;
import java.util.Map;
public final class ResponseResult<T> {
/**
* , HTTP 200..299
*/
public final boolean success;
/**
* HTTP
*/
public final int code;
/**
* HTTP
*/
public final String status;
/**
* HTTP
*/
public final Map<String, List<String>> headers;
/**
* HTTP
*/
public final T body;
/**
* ,
*/
public final Exception e;
private ResponseResult(boolean success, int code, String status, Map<String, List<String>> headers, T body, Exception e) {
this.success = success;
this.code = code;
this.status = status;
this.headers = headers;
this.body = body;
this.e = e;
}
public static <T> ResponseResultBuilder<T> builder() {
return new ResponseResultBuilder<T>();
}
public static class ResponseResultBuilder<T> {
private int code;
private String status;
private Map<String, List<String>> headers;
private T body;
private Exception e;
private ResponseResultBuilder() {
}
public ResponseResultBuilder<T> code(int code) {
this.code = code;
return this;
}
public ResponseResultBuilder<T> status(String status) {
this.status = status;
return this;
}
public ResponseResultBuilder<T> headers(Map<String, List<String>> headers) {
this.headers = headers;
return this;
}
public ResponseResultBuilder<T> body(T body) {
this.body = body;
return this;
}
public ResponseResultBuilder<T> e(Exception e) {
this.e = e;
return this;
}
public ResponseResult<T> build() {
return new ResponseResult<>(
code >= 200 && code <= 299,
code,
status,
headers,
body,
e
);
}
}
}

View File

@ -0,0 +1,70 @@
package com.njzscloud.common.core.ienum;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.njzscloud.common.core.fastjson.serializer.DictObjectDeserializer;
import com.njzscloud.common.core.fastjson.serializer.DictObjectSerializer;
import com.njzscloud.common.core.jackson.serializer.DictDeserializer;
import com.njzscloud.common.core.jackson.serializer.DictSerializer;
/**
* <br/>
* DictIntDictStr<br/>
*
* @see DictInt
* @see DictStr
* @see DictDeserializer
* @see DictSerializer
* @see DictObjectDeserializer
* @see DictObjectSerializer
*/
@JsonDeserialize(using = DictDeserializer.class)
@JsonSerialize(using = DictSerializer.class)
public interface Dict<T> extends IEnum {
/**
* <br/>
* val
*/
String ENUM_VAL = "val";
/**
* <br/>
* txt
*/
String ENUM_TXT = "txt";
/**
* "值"
*
* @param val
* @param ds
* @param <V>
* @param <D>
* @return Dict
*/
static <V, D extends Dict<V>> D parse(V val, D[] ds) {
for (D d : ds) {
if (d.getVal().equals(val)) {
return d;
}
}
return null;
}
/**
*
*
* @return T
*/
T getVal();
/**
*
*
* @return String
*/
String getTxt();
}

View File

@ -0,0 +1,11 @@
package com.njzscloud.common.core.ienum;
/**
* "值" Integer<br/>
*
*
* @see DictStr
*/
public interface DictInt extends Dict<Integer> {
}

View File

@ -0,0 +1,10 @@
package com.njzscloud.common.core.ienum;
/**
* "值" String<br/>
*
*
* @see DictInt
*/
public interface DictStr extends Dict<String> {
}

View File

@ -0,0 +1,24 @@
package com.njzscloud.common.core.ienum;
/**
*
*/
public interface IEnum {
/**
* <br/>
*
*/
String ENUM_TYPE = "type";
/**
* <br/>
* name
*/
String ENUM_NAME = "name";
/**
* <br/>
* ordinal
*/
String ENUM_ORDINAL = "ordinal";
}

View File

@ -0,0 +1,229 @@
package com.njzscloud.common.core.jackson;
import cn.hutool.core.date.DatePattern;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.jackson.serializer.BigDecimalModule;
import com.njzscloud.common.core.jackson.serializer.LongModule;
import com.njzscloud.common.core.jackson.serializer.TimeModule;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.TimeZone;
/**
* Jackson <br/>
* Spring ObjectMapper
*/
@Slf4j
public class Jackson {
private static final ObjectMapper objectMapper;
private static final XmlMapper xmlMapper;
static {
ObjectMapper temp;
try {
temp = SpringUtil.getBean(ObjectMapper.class);
} catch (Throwable e) {
log.warn("从 Spring 中获取 ObjectMapper 失败");
temp = createObjectMapper();
}
objectMapper = temp;
XmlMapper _xmlMapper;
try {
_xmlMapper = SpringUtil.getBean(XmlMapper.class);
} catch (Throwable e) {
log.warn("从 Spring 中获取 XmlMapper 失败");
_xmlMapper = createXmlMapper();
}
xmlMapper = _xmlMapper;
}
/**
* JSON
*
* @param o
* @return String
*/
public static String toJsonStr(Object o) {
try {
return objectMapper.writeValueAsString(o);
} catch (JsonProcessingException e) {
log.error("Jackson 序列化失败", e);
throw Exceptions.error(e, "Jackson 序列化失败");
}
}
/**
* JSON
*
* @param o
* @return byte[]
*/
public static byte[] toJsonBytes(Object o) {
try {
return objectMapper.writeValueAsBytes(o);
} catch (JsonProcessingException e) {
log.error("Jackson 序列化失败", e);
throw Exceptions.error(e, "Jackson 序列化失败");
}
}
/**
* ()<br/>
* : new TypeReference&lt;List&lt;String&gt;&gt;(){},
*
* @param json json JSON
* @param type
* @return T
* @see TypeReference
*/
public static <T> T toBean(String json, Type type) {
try {
return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type));
} catch (JsonProcessingException e) {
log.error("Jackson 反序列化失败", e);
throw Exceptions.error(e, "Jackson 反序列化失败");
}
}
public static <T> T toBean(InputStream json, Type type) {
try {
return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type));
} catch (Exception e) {
log.error("Jackson 反序列化失败", e);
throw Exceptions.error(e, "Jackson 反序列化失败");
}
}
public static <T> T toBean(byte[] json, Type type) {
try {
return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type));
} catch (Exception e) {
log.error("Jackson 反序列化失败", e);
throw Exceptions.error(e, "Jackson 反序列化失败");
}
}
/**
* JSON
*
* @param o
* @return String
*/
public static String toXmlStr(Object o) {
try {
return xmlMapper.writeValueAsString(o);
} catch (JsonProcessingException e) {
log.error("Jackson 序列化失败", e);
throw Exceptions.error(e, "Jackson 序列化失败");
}
}
/**
* JSON
*
* @param o
* @return byte[]
*/
public static byte[] toXmlBytes(Object o) {
try {
return xmlMapper.writeValueAsBytes(o);
} catch (JsonProcessingException e) {
log.error("Jackson 序列化失败", e);
throw Exceptions.error(e, "Jackson 序列化失败");
}
}
public static <T> T xmlToBean(String json, Type type) {
try {
return xmlMapper.readValue(json, xmlMapper.getTypeFactory().constructType(type));
} catch (JsonProcessingException e) {
log.error("Jackson 反序列化失败", e);
throw Exceptions.error(e, "Jackson 反序列化失败");
}
}
public static <T> T xmlToBean(InputStream json, Type type) {
try {
return xmlMapper.readValue(json, xmlMapper.getTypeFactory().constructType(type));
} catch (Exception e) {
log.error("Jackson 反序列化失败", e);
throw Exceptions.error(e, "Jackson 反序列化失败");
}
}
public static <T> T xmlToBean(byte[] json, Type type) {
try {
return xmlMapper.readValue(json, xmlMapper.getTypeFactory().constructType(type));
} catch (Exception e) {
log.error("Jackson 反序列化失败", e);
throw Exceptions.error(e, "Jackson 反序列化失败");
}
}
/**
* ObjectMapper
*
* @return ObjectMapper
*/
private static ObjectMapper createObjectMapper() {
return new ObjectMapper()
.setLocale(Locale.CHINA)
.setTimeZone(TimeZone.getTimeZone("GMT+8"))
.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN))
.setSerializationInclusion(JsonInclude.Include.ALWAYS)
.configure(SerializationFeature.INDENT_OUTPUT, true) // 格式化输出
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true) // 对Map中的KeyValue按照Key做排序后再输出。在有些验签的场景需要使用这个Feature
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
// .configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true)
.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true)
.registerModules(new TimeModule(), new LongModule(), new BigDecimalModule())
;
}
private static XmlMapper createXmlMapper() {
return XmlMapper.xmlBuilder()
.defaultLocale(Locale.CHINA)
.defaultTimeZone(TimeZone.getTimeZone("GMT+8"))
.defaultDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN))
.serializationInclusion(JsonInclude.Include.ALWAYS)
.configure(SerializationFeature.INDENT_OUTPUT, true)
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true) // 对Map中的KeyValue按照Key做排序后再输出。在有些验签的场景需要使用这个Feature
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.addModules(new TimeModule(), new LongModule(), new BigDecimalModule())
.build()
;
}
/**
* objectMapper
*
* @return ObjectMapper
*/
public static ObjectMapper objectMapper() {
return objectMapper;
}
public static XmlMapper xmlMapper() {
return xmlMapper;
}
}

View File

@ -0,0 +1,16 @@
package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.math.BigDecimal;
/**
* BigDecimal
*/
public class BigDecimalModule extends SimpleModule {
{
this.addSerializer(BigDecimal.class, new BigDecimalSerializer())
.addDeserializer(BigDecimal.class, new NumberDeserializers.BigDecimalDeserializer());
}
}

View File

@ -0,0 +1,22 @@
package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.math.BigDecimal;
/**
* BigDecimal
*/
public class BigDecimalSerializer extends JsonSerializer<BigDecimal> {
@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value != null) {
gen.writeString(value.toPlainString());
} else {
gen.writeNull();
}
}
}

View File

@ -0,0 +1,102 @@
package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.core.ienum.IEnum;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.lang.reflect.Field;
/**
* Dict Jackson <br/><br/>
* JSON {@link DictSerializer}
*
* @see Dict
* @see DictInt
* @see DictStr
* @see DictSerializer
*/
@Slf4j
public class DictDeserializer extends JsonDeserializer<Dict> {
private static final ClassLoader CLASSLOADER = DictDeserializer.class.getClassLoader();
@Override
public Dict deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
JsonToken currentToken = p.getCurrentToken();
if (currentToken == JsonToken.START_OBJECT) {
TreeNode treeNode = p.getCodec().readTree(p);
TreeNode enumType_node = treeNode.get(IEnum.ENUM_TYPE);
String enumTypeField = ((TextNode) enumType_node).textValue();
TreeNode val_node = treeNode.get(Dict.ENUM_VAL);
Class<?> clazz;
try {
clazz = CLASSLOADER.loadClass(enumTypeField);
} catch (ClassNotFoundException e) {
throw Exceptions.error(e, "类型加载失败:{}", enumTypeField);
}
if (val_node instanceof TextNode) {
if (DictStr.class.isAssignableFrom(clazz)) {
String val = ((TextNode) val_node).textValue();
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
return Dict.parse(val, constants);
} else if (DictInt.class.isAssignableFrom(clazz)) {
String val = ((TextNode) val_node).textValue();
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
return Dict.parse(Integer.parseInt(val), constants);
} else {
return null;
}
} else if (val_node instanceof IntNode) {
if (DictStr.class.isAssignableFrom(clazz)) {
int val = ((IntNode) val_node).intValue();
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
return Dict.parse(String.valueOf(val), constants);
} else if (DictInt.class.isAssignableFrom(clazz)) {
int val = ((IntNode) val_node).intValue();
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
return Dict.parse(val, constants);
} else {
return null;
}
} else {
return null;
}
} else {
JsonStreamContext context = p.getParsingContext();
String currentName = context.getCurrentName();
Object currentValue = p.getCurrentValue();
Class<?> valueClazz = currentValue.getClass();
try {
Field field = valueClazz.getDeclaredField(currentName);
Class<?> clazz = field.getType();
if (DictStr.class.isAssignableFrom(clazz)) {
String val = p.getValueAsString();
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
return Dict.parse(val, constants);
} else if (DictInt.class.isAssignableFrom(clazz)) {
int val = p.getValueAsInt();
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
return Dict.parse(val, constants);
} else {
return null;
}
} catch (Exception e) {
log.error("字典枚举反序列化失败", e);
throw Exceptions.error(e, "字典枚举反序列化失败,字段名:{},值:{}", currentName, currentValue);
}
}
}
}

View File

@ -0,0 +1,61 @@
package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.core.ienum.IEnum;
import java.io.IOException;
/**
* Dict Jackson <br/><br/>
* JSON <br/>
* 1<br/>
* <pre>
* {
* "type": "", // 枚举全限定类名, 反序列化时会用到
* "name": "", // name 属性
* "ordinal": 0, // ordinal 属性
* "val": 1, // val 属性(字符串/数字), 反序列化时会用到
* "txt": "1" // txt 属性
* }</pre>
* 2<br/>
* <pre>
* {
* // ... 其他属性
* "原字段名称": 1, // val 属性(字符串/数字), 反序列化时会用到
* "原字段名称Txt": "1" // txt 属性
* }</pre>
*
* @see Dict
* @see DictInt
* @see DictStr
* @see DictDeserializer
*/
public class DictSerializer extends JsonSerializer<Dict> {
@Override
public void serialize(Dict value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
gen.writeNull();
} else {
JsonStreamContext ctx = gen.getOutputContext();
if (ctx.inRoot()) {
gen.writeStartObject();
gen.writeStringField(IEnum.ENUM_TYPE, value.getClass().getName());
gen.writeStringField(IEnum.ENUM_NAME, ((Enum<?>) value).name());
gen.writeNumberField(IEnum.ENUM_ORDINAL, ((Enum<?>) value).ordinal());
gen.writeObjectField(Dict.ENUM_VAL, value.getVal());
gen.writeStringField(Dict.ENUM_TXT, value.getTxt());
gen.writeEndObject();
} else {
gen.writeObject(value.getVal());
String currentName = ctx.getCurrentName();
gen.writeStringField(currentName + "Txt", value.getTxt());
}
}
}
}

View File

@ -0,0 +1,15 @@
package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* Long
*/
public class LongModule extends SimpleModule {
{
this.addSerializer(Long.class, new LongSerializer())
.addDeserializer(Long.class, new NumberDeserializers.LongDeserializer(Long.class, null));
}
}

View File

@ -0,0 +1,21 @@
package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**
* Long
*/
public class LongSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value != null) {
gen.writeString(value.toString());
} else {
gen.writeNull();
}
}
}

View File

@ -0,0 +1,29 @@
package com.njzscloud.common.core.jackson.serializer;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
*
*/
public class TimeModule extends SimpleModule {
{
this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)))
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));
}
}

View File

@ -0,0 +1,688 @@
package com.njzscloud.common.core.thread;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@Slf4j
@SuppressWarnings("unchecked")
public class Q<E> extends AbstractQueue<E> implements BlockingQueue<E> {
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();
private final int capacity;
private final int standbyCapacity;
private final AtomicInteger count = new AtomicInteger();
private final AtomicInteger standbyCount = new AtomicInteger();
private Node<E> head;
private Node<E> last;
private Node<E> border;
public Q() {
this(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
public Q(int capacity, int standbyCapacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
this.standbyCapacity = standbyCapacity;
border = last = head = new Node<>(null);
}
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
private void enqueue(Node<E> node) {
last = last.next = node;
}
private E dequeue() {
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
private void fullyLock() {
putLock.lock();
takeLock.lock();
}
private void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
public int size() {
return count.get();
}
public int remainingCapacity() {
return capacity - count.get();
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
border = node;
c = count.getAndIncrement();
if (c + 1 < capacity) notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0) signalNotEmpty();
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
Node<E> node = new Node<>(e);
enqueue(node);
border = node;
c = count.getAndIncrement();
if (c + 1 < capacity) notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity) return false;
int c = -1;
Node<E> node = new Node<>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(node);
border = node;
c = count.getAndIncrement();
if (c + 1 < capacity) notFull.signal();
}
// log.info("放1窗口数{},备份数:{}", count.get(), standbyCount.get());
} finally {
putLock.unlock();
}
if (c == 0) signalNotEmpty();
return c >= 0;
}
public boolean offerStandby(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (standbyCount.get() == standbyCapacity) return false;
int c = -1;
Node<E> node = new Node<>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (standbyCount.get() < standbyCapacity) {
enqueue(node);
c = count.get();
if (c == capacity) {
c--;
standbyCount.getAndIncrement();
} else {
border = border.next;
c = count.getAndIncrement();
}
if (c + 1 < capacity) notFull.signal();
}
// log.info("放2窗口数{},备份数:{}", count.get(), standbyCount.get());
} finally {
putLock.unlock();
}
if (c == 0) signalNotEmpty();
return c >= 0;
}
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
if (border.next == null) {
c = count.getAndDecrement();
} else {
border = border.next;
standbyCount.getAndDecrement();
c = count.get();
}
x = dequeue();
if (c > 1) notEmpty.signal();
// log.info("取2窗口数{},备份数:{}", count.get(), standbyCount.get());
} finally {
takeLock.unlock();
}
if (c == capacity) signalNotFull();
return x;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
if (border.next == null) {
c = count.getAndDecrement();
} else {
border = border.next;
standbyCount.getAndDecrement();
c = count.get();
}
x = dequeue();
if (c > 1) notEmpty.signal();
// log.info("取1窗口数{},备份数:{}", count.get(), standbyCount.get());
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) {
if (border.next == null) {
c = count.getAndDecrement();
} else {
border = border.next;
standbyCount.getAndDecrement();
c = count.get();
}
x = dequeue();
if (c > 1) notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E peek() {
if (count.get() == 0) return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
Node<E> first = head.next;
if (first == null) return null;
else return first.item;
} finally {
takeLock.unlock();
}
}
void unlink(Node<E> trail, Node<E> p, int borderFlag) {
p.item = null;
trail.next = p.next;
if (last == p) last = trail;
if (borderFlag == 0) {
if (last == p) {
border = trail;
if (count.getAndDecrement() == capacity) notFull.signal();
} else {
border = p.next;
}
} else if (borderFlag == -1) {
if (last == border) {
if (count.getAndDecrement() == capacity) notFull.signal();
} else {
border = border.next;
}
} else if (borderFlag == 1) {
standbyCount.getAndDecrement();
}
}
public boolean remove(Object o) {
if (o == null) return false;
fullyLock();
try {
// -1-->左边、0-->边界、1-->右边
int borderFlag = -1;
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (p == border) {
borderFlag = 0;
} else if (borderFlag == 0) {
borderFlag = 1;
}
if (o.equals(p.item)) {
unlink(trail, p, borderFlag);
return true;
}
}
return false;
} finally {
fullyUnlock();
}
}
public boolean contains(Object o) {
if (o == null) return false;
fullyLock();
try {
for (Node<E> p = head.next; p != null; p = p.next)
if (o.equals(p.item))
return true;
return false;
} finally {
fullyUnlock();
}
}
public Object[] toArray() {
fullyLock();
try {
int size = count.get() + standbyCount.get();
Object[] a = new Object[size];
int k = 0;
for (Node<E> p = head.next; p != null; p = p.next)
a[k++] = p.item;
return a;
} finally {
fullyUnlock();
}
}
public <T> T[] toArray(T[] a) {
fullyLock();
try {
int size = count.get() + standbyCount.get();
if (a.length < size)
a = (T[]) java.lang.reflect.Array.newInstance
(a.getClass().getComponentType(), size);
int k = 0;
for (Node<E> p = head.next; p != null; p = p.next)
a[k++] = (T) p.item;
if (a.length > k)
a[k] = null;
return a;
} finally {
fullyUnlock();
}
}
public String toString() {
fullyLock();
try {
Node<E> p = head.next;
if (p == null)
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (; ; ) {
E e = p.item;
sb.append(e == this ? "(this Collection)" : e);
p = p.next;
if (p == null)
return sb.append(']').toString();
sb.append(',').append(' ');
}
} finally {
fullyUnlock();
}
}
public void clear() {
fullyLock();
try {
for (Node<E> p, h = head; (p = h.next) != null; h = p) {
h.next = h;
p.item = null;
}
head = last;
// assert head.item == null && head.next == null;
standbyCount.getAndSet(0);
if (count.getAndSet(0) == capacity)
notFull.signal();
} finally {
fullyUnlock();
}
}
public int drainTo(Collection<? super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
public int drainTo(Collection<? super E> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0) return 0;
boolean signalNotFull = false;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
int n = Math.min(maxElements, count.get() + standbyCount.get());
// count.get provides visibility to first n Nodes
Node<E> h = head;
int i = 0;
int x = 0;
int y = 0;
try {
while (i < n) {
Node<E> p = h.next;
c.add(p.item);
if (border == last) {
x++;
} else {
border = border.next;
y++;
}
p.item = null;
h.next = h;
h = p;
++i;
}
return n;
} finally {
head = h;
if (x > 0) {
signalNotFull = (count.getAndAdd(-x) == capacity);
}
if (y > 0) {
standbyCount.getAndAdd(-y);
}
}
} finally {
takeLock.unlock();
if (signalNotFull)
signalNotFull();
}
}
public Iterator<E> iterator() {
return new Itr();
}
public Spliterator<E> spliterator() {
return new LBQSpliterator<E>(this);
}
static class Node<E> {
E item;
Node<E> next;
Node(E x) {
item = x;
}
}
static final class LBQSpliterator<E> implements Spliterator<E> {
static final int MAX_BATCH = 1 << 25; // max batch array size;
final Q<E> queue;
Node<E> current; // current node; null until initialized
int batch; // batch size for splits
boolean exhausted; // true when no more nodes
long est; // size estimate
LBQSpliterator(Q<E> queue) {
this.queue = queue;
this.est = queue.size();
}
public long estimateSize() {
return est;
}
public Spliterator<E> trySplit() {
Node<E> h;
final Q<E> q = this.queue;
int b = batch;
int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
if (!exhausted &&
((h = current) != null || (h = q.head.next) != null) &&
h.next != null) {
Object[] a = new Object[n];
int i = 0;
Node<E> p = current;
q.fullyLock();
try {
if (p != null || (p = q.head.next) != null) {
do {
if ((a[i] = p.item) != null)
++i;
} while ((p = p.next) != null && i < n);
}
} finally {
q.fullyUnlock();
}
if ((current = p) == null) {
est = 0L;
exhausted = true;
} else if ((est -= i) < 0L)
est = 0L;
if (i > 0) {
batch = i;
return Spliterators.spliterator
(a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
Spliterator.CONCURRENT);
}
}
return null;
}
public void forEachRemaining(Consumer<? super E> action) {
if (action == null) throw new NullPointerException();
final Q<E> q = this.queue;
if (!exhausted) {
exhausted = true;
Node<E> p = current;
do {
E e = null;
q.fullyLock();
try {
if (p == null)
p = q.head.next;
while (p != null) {
e = p.item;
p = p.next;
if (e != null)
break;
}
} finally {
q.fullyUnlock();
}
if (e != null)
action.accept(e);
} while (p != null);
}
}
public boolean tryAdvance(Consumer<? super E> action) {
if (action == null) throw new NullPointerException();
final Q<E> q = this.queue;
if (!exhausted) {
E e = null;
q.fullyLock();
try {
if (current == null)
current = q.head.next;
while (current != null) {
e = current.item;
current = current.next;
if (e != null)
break;
}
} finally {
q.fullyUnlock();
}
if (current == null)
exhausted = true;
if (e != null) {
action.accept(e);
return true;
}
}
return false;
}
public int characteristics() {
return Spliterator.ORDERED | Spliterator.NONNULL |
Spliterator.CONCURRENT;
}
}
private class Itr implements Iterator<E> {
private Node<E> current;
private Node<E> lastRet;
private E currentElement;
Itr() {
fullyLock();
try {
current = head.next;
if (current != null)
currentElement = current.item;
} finally {
fullyUnlock();
}
}
public boolean hasNext() {
return current != null;
}
private Node<E> nextNode(Node<E> p) {
for (; ; ) {
Node<E> s = p.next;
if (s == p)
return head.next;
if (s == null || s.item != null)
return s;
p = s;
}
}
public E next() {
fullyLock();
try {
if (current == null)
throw new NoSuchElementException();
E x = currentElement;
lastRet = current;
current = nextNode(current);
currentElement = (current == null) ? null : current.item;
return x;
} finally {
fullyUnlock();
}
}
public void remove() {
if (lastRet == null)
throw new IllegalStateException();
fullyLock();
try {
Node<E> node = lastRet;
lastRet = null;
// -1-->左边、0-->边界、1-->右边
int borderFlag = -1;
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (p == border) {
borderFlag = 0;
} else if (borderFlag == 0) {
borderFlag = 1;
}
if (p == node) {
unlink(trail, p, borderFlag);
break;
}
}
} finally {
fullyUnlock();
}
}
}
}

View File

@ -0,0 +1,138 @@
package com.njzscloud.common.core.thread;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class ThreadPool {
private static volatile ThreadPoolExecutor THREAD_POOL;
public static ThreadPoolExecutor defaultThreadPool() {
if (THREAD_POOL == null) {
synchronized (ThreadPool.class) {
if (THREAD_POOL == null) {
THREAD_POOL = createThreadPool(null,
10, 200,
5 * 60,
20, 2000,
null);
}
}
}
return THREAD_POOL;
}
public static ThreadPoolExecutor createThreadPool(String poolName, int corePoolSize, int maxPoolSize, long keepAliveSeconds, int windowCapacity, int standbyCapacity, RejectedExecutionHandler abortPolicy) {
RejectedExecutionHandler abortPolicy_ = abortPolicy == null ? new ThreadPoolExecutor.AbortPolicy() : abortPolicy;
Q<Runnable> q = new Q<>(windowCapacity, standbyCapacity);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS,
q,
new DefaultThreadFactory(poolName),
(r, p) -> {
if (!q.offerStandby(r)) {
log.debug("任务队列已满");
abortPolicy_.rejectedExecution(r, p);
} else {
log.debug("任务已加入备用队列");
}
}
);
threadPoolExecutor.allowCoreThreadTimeOut(true);
return threadPoolExecutor;
}
public static void main(String[] args) {
ThreadPoolExecutor threadPool = defaultThreadPool();
try {
threadPool.execute(() -> {
System.out.println("1--->" + Thread.currentThread().getName());
ThreadUtil.sleep(3000);
System.out.println("1<---");
});
threadPool.execute(() -> {
System.out.println("2--->" + Thread.currentThread().getName());
ThreadUtil.sleep(3000);
System.out.println("2<---");
});
threadPool.execute(() -> {
System.out.println("3--->" + Thread.currentThread().getName());
ThreadUtil.sleep(3000);
System.out.println("3<---");
});
threadPool.execute(() -> {
System.out.println("4--->" + Thread.currentThread().getName());
ThreadUtil.sleep(3000);
System.out.println("4<---");
});
threadPool.execute(() -> {
System.out.println("5--->" + Thread.currentThread().getName());
ThreadUtil.sleep(3000);
System.out.println("5<---");
});
/*
threadPool.execute(()->{
System.out.println("6--->"+Thread.currentThread().getName());
ThreadUtil.sleep(60000);
System.out.println("6<---");
});
threadPool.execute(()->{
System.out.println("7--->"+Thread.currentThread().getName());
ThreadUtil.sleep(70000);
System.out.println("7<---");
});*/
} catch (Exception e) {
e.printStackTrace();
}
ThreadUtil.sleep(300000);
}
private static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public DefaultThreadFactory(String poolName) {
if (StrUtil.isBlank(poolName)) namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
else namePrefix = poolName + "-";
}
public DefaultThreadFactory() {
namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
}
@Override
public Thread newThread(@NotNull Runnable r) {
String name = namePrefix + threadNumber.getAndIncrement();
log.debug("创建新线程:{}", name);
Thread t = new Thread(r, name) {
@Override
public void run() {
try {
super.run();
} finally {
int i = threadNumber.decrementAndGet();
log.debug("线程结束:{},剩余:{}", this.getName(), i - 1);
}
}
};
if (t.isDaemon()) t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY);
t.setUncaughtExceptionHandler((t1, e) -> log.error("线程异常:{}", t1.getName(), e));
return t;
}
}
}

View File

@ -0,0 +1,693 @@
package com.njzscloud.common.core.thread;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@Slf4j
@SuppressWarnings("unchecked")
public class WindowBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E> {
private final int windowCapacity;
private final int totalCapacity;
private final AtomicInteger totalElementCount = new AtomicInteger();
private final AtomicInteger windowElementCount = new AtomicInteger();
private final AtomicInteger standbyElementCount = new AtomicInteger();
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();
private Node<E> head;
private Node<E> border;
private Node<E> last;
public WindowBlockingQueue() {
this(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
public WindowBlockingQueue(int totalCapacity, int windowCapacity) {
if (windowCapacity <= 0 || totalCapacity < windowCapacity) throw new IllegalArgumentException();
this.totalCapacity = totalCapacity;
this.windowCapacity = windowCapacity;
head = border = last = new Node<>(null);
}
public WindowBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE, Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == windowCapacity)
throw new IllegalStateException("Queue full");
enqueue(new Node<>(e));
++n;
}
windowElementCount.set(n);
} finally {
putLock.unlock();
}
}
private void signalNotEmpty() {
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
private void signalNotFull() {
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
private void enqueue(Node<E> node) {
last = last.next = node;
totalElementCount.incrementAndGet();
}
private E dequeue() {
Node<E> h = head;
Node<E> first = h.next;
h.next = h;
head = first;
E x = first.item;
first.item = null;
totalElementCount.decrementAndGet();
return x;
}
void fullyLock() {
putLock.lock();
takeLock.lock();
}
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
public int size() {
return windowElementCount.get();
}
public int remainingCapacity() {
return windowCapacity - windowElementCount.get();
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.windowElementCount;
putLock.lockInterruptibly();
try {
while (count.get() == windowCapacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < windowCapacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.windowElementCount;
putLock.lockInterruptibly();
try {
while (count.get() == windowCapacity) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e));
c = count.getAndIncrement();
if (c + 1 < windowCapacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
boolean offered = false;
int c = windowElementCount.get();
if (c == windowCapacity) return offered;
Node<E> node = new Node<>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (windowElementCount.get() < windowCapacity) {
enqueue(node);
offered = true;
c = windowElementCount.incrementAndGet();
border = node;
if (c < windowCapacity) notFull.signal();
}
} finally {
// log.debug("1、添加元素总数{}、窗口区:{}、备份区:{}", totalElementCount.get(), windowElementCount.get(), standbyElementCount.get());
putLock.unlock();
}
if (c > 0) signalNotEmpty();
return offered;
}
public boolean offerStandby(E e) {
if (e == null) throw new NullPointerException();
boolean offered = false;
int c = totalElementCount.get();
if (c == totalCapacity) return offered;
Node<E> node = new Node<>(e);
putLock.lock();
try {
if (totalElementCount.get() < totalCapacity) {
enqueue(node);
offered = true;
c = windowElementCount.get();
if (c + 1 <= windowCapacity) {
border = node;
windowElementCount.incrementAndGet();
} else {
standbyElementCount.incrementAndGet();
}
if (c + 1 < windowCapacity) notFull.signal();
}
c = windowElementCount.get();
} finally {
log.debug("3、添加元素总数{}、窗口区:{}、备份区:{}", totalElementCount.get(), windowElementCount.get(), standbyElementCount.get());
putLock.unlock();
}
if (c != 0) signalNotEmpty();
return offered;
}
public E take() throws InterruptedException {
E x;
int c = -1;
takeLock.lockInterruptibly();
try {
while (windowElementCount.get() == 0) {
notEmpty.await();
}
x = dequeue();
if (border.next == null) {
c = windowElementCount.decrementAndGet();
} else {
border = border.next;
standbyElementCount.decrementAndGet();
c = windowElementCount.get();
}
if (c > 0) notEmpty.signal();
} finally {
log.debug("1、提取元素总数{}、窗口:{}、备份区:{}", totalElementCount.get(), windowElementCount.get(), standbyElementCount.get());
takeLock.unlock();
}
if (c != windowCapacity) signalNotFull();
return x;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
takeLock.lockInterruptibly();
try {
while (windowElementCount.get() == 0) {
if (nanos <= 0) return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
if (border.next == null) {
c = windowElementCount.decrementAndGet();
} else {
border = border.next;
standbyElementCount.decrementAndGet();
c = windowElementCount.get();
}
if (c > 0) notEmpty.signal();
} finally {
// log.debug("2、提取元素总数{}、窗口:{}、备份区:{}", totalElementCount.get(), windowElementCount.get(), standbyElementCount.get());
takeLock.unlock();
}
if (c != windowCapacity) signalNotFull();
return x;
}
public E poll() {
if (windowElementCount.get() == 0) return null;
E x = null;
int c = -1;
takeLock.lock();
try {
if (windowElementCount.get() > 0) {
x = dequeue();
if (border.next == null) {
c = windowElementCount.decrementAndGet();
} else {
border = border.next;
standbyElementCount.decrementAndGet();
c = windowElementCount.get();
}
if (c > 0) notEmpty.signal();
}
} finally {
log.debug("3、提取元素总数{}、窗口:{}、备份区:{}", totalElementCount.get(), windowElementCount.get(), standbyElementCount.get());
takeLock.unlock();
}
if (c != windowCapacity) signalNotFull();
return x;
}
public E peek() {
if (windowElementCount.get() == 0)
return null;
takeLock.lock();
try {
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
void unlink(Node<E> p, Node<E> trail, boolean overBorder) {
totalElementCount.getAndDecrement();
if (p == border || !overBorder) {
if (border.next == null) {
windowElementCount.getAndDecrement();
} else {
border = border.next;
standbyElementCount.decrementAndGet();
}
} else {
standbyElementCount.decrementAndGet();
}
p.item = null;
trail.next = p.next;
if (last == p) last = trail;
if (windowElementCount.get() == windowCapacity) notFull.signal();
}
public boolean remove(Object o) {
if (o == null) return false;
fullyLock();
try {
boolean overBorder = false;
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (!overBorder) overBorder = trail == border;
if (o.equals(p.item)) {
unlink(p, trail, overBorder);
return true;
}
}
return false;
} finally {
log.debug("1、删除元素总数{}、窗口:{}、备份区:{}", totalElementCount.get(), windowElementCount.get(), standbyElementCount.get());
fullyUnlock();
}
}
public boolean contains(Object o) {
if (o == null) return false;
fullyLock();
try {
for (Node<E> p = head.next; p != null; p = p.next)
if (o.equals(p.item))
return true;
return false;
} finally {
fullyUnlock();
}
}
public Object[] toArray() {
fullyLock();
try {
int size = windowElementCount.get();
Object[] a = new Object[size];
int k = 0;
for (Node<E> p = head.next; p != null; p = p.next)
a[k++] = p.item;
return a;
} finally {
fullyUnlock();
}
}
public <T> T[] toArray(T[] a) {
fullyLock();
try {
int size = windowElementCount.get();
if (a.length < size)
a = (T[]) java.lang.reflect.Array.newInstance
(a.getClass().getComponentType(), size);
int k = 0;
for (Node<E> p = head.next; p != null; p = p.next)
a[k++] = (T) p.item;
if (a.length > k)
a[k] = null;
return a;
} finally {
fullyUnlock();
}
}
public String toString() {
fullyLock();
try {
Node<E> p = head.next;
if (p == null)
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (; ; ) {
E e = p.item;
sb.append(e == this ? "(this Collection)" : e);
p = p.next;
if (p == null)
return sb.append(']').toString();
sb.append(',').append(' ');
}
} finally {
fullyUnlock();
}
}
public void clear() {
fullyLock();
try {
for (Node<E> p, h = head; (p = h.next) != null; h = p) {
h.next = h;
p.item = null;
}
head = last;
totalElementCount.getAndSet(0);
standbyElementCount.getAndSet(0);
if (windowElementCount.getAndSet(0) == windowCapacity) notFull.signal();
} finally {
fullyUnlock();
}
}
public int drainTo(Collection<? super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
public int drainTo(Collection<? super E> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
boolean signalNotFull = false;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
int n = Math.min(maxElements, windowElementCount.get());
Node<E> h = head;
int i = 0;
try {
while (i < n) {
Node<E> p = h.next;
c.add(p.item);
p.item = null;
h.next = h;
h = p;
++i;
if (border.next == null) {
windowElementCount.decrementAndGet();
} else {
border = border.next;
standbyElementCount.decrementAndGet();
}
}
return n;
} finally {
if (i > 0) {
head = h;
totalElementCount.getAndAdd(-i);
signalNotFull = (windowElementCount.get() == windowCapacity);
}
}
} finally {
takeLock.unlock();
if (signalNotFull)
signalNotFull();
}
}
public Iterator<E> iterator() {
return new Itr();
}
public Spliterator<E> spliterator() {
return new LBQSpliterator<E>(this);
}
static class Node<E> {
E item;
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head.next
* - null, meaning there is no successor (this is the last node)
*/
Node<E> next;
Node(E x) {
item = x;
}
}
static final class LBQSpliterator<E> implements Spliterator<E> {
static final int MAX_BATCH = 1 << 25;
final WindowBlockingQueue<E> queue;
Node<E> current;
int batch;
boolean exhausted;
long est;
LBQSpliterator(WindowBlockingQueue<E> queue) {
this.queue = queue;
this.est = queue.size();
}
public long estimateSize() {
return est;
}
public Spliterator<E> trySplit() {
Node<E> h;
final WindowBlockingQueue<E> q = this.queue;
int b = batch;
int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
if (!exhausted &&
((h = current) != null || (h = q.head.next) != null) &&
h.next != null) {
Object[] a = new Object[n];
int i = 0;
Node<E> p = current;
q.fullyLock();
try {
if (p != null || (p = q.head.next) != null) {
do {
if ((a[i] = p.item) != null)
++i;
} while ((p = p.next) != null && i < n);
}
} finally {
q.fullyUnlock();
}
if ((current = p) == null) {
est = 0L;
exhausted = true;
} else if ((est -= i) < 0L)
est = 0L;
if (i > 0) {
batch = i;
return Spliterators.spliterator
(a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
Spliterator.CONCURRENT);
}
}
return null;
}
public void forEachRemaining(Consumer<? super E> action) {
if (action == null) throw new NullPointerException();
final WindowBlockingQueue<E> q = this.queue;
if (!exhausted) {
exhausted = true;
Node<E> p = current;
do {
E e = null;
q.fullyLock();
try {
if (p == null)
p = q.head.next;
while (p != null) {
e = p.item;
p = p.next;
if (e != null)
break;
}
} finally {
q.fullyUnlock();
}
if (e != null)
action.accept(e);
} while (p != null);
}
}
public boolean tryAdvance(Consumer<? super E> action) {
if (action == null) throw new NullPointerException();
final WindowBlockingQueue<E> q = this.queue;
if (!exhausted) {
E e = null;
q.fullyLock();
try {
if (current == null)
current = q.head.next;
while (current != null) {
e = current.item;
current = current.next;
if (e != null)
break;
}
} finally {
q.fullyUnlock();
}
if (current == null)
exhausted = true;
if (e != null) {
action.accept(e);
return true;
}
}
return false;
}
public int characteristics() {
return Spliterator.ORDERED | Spliterator.NONNULL |
Spliterator.CONCURRENT;
}
}
private class Itr implements Iterator<E> {
private Node<E> current;
private Node<E> lastRet;
private E currentElement;
Itr() {
fullyLock();
try {
current = head.next;
if (current != null)
currentElement = current.item;
} finally {
fullyUnlock();
}
}
public boolean hasNext() {
return current != null;
}
/**
* Returns the next live successor of p, or null if no such.
* <p>
* Unlike other traversal methods, iterators need to handle both:
* - dequeued nodes (p.next == p)
* - (possibly multiple) interior removed nodes (p.item == null)
*/
private Node<E> nextNode(Node<E> p) {
for (; ; ) {
Node<E> s = p.next;
if (s == p)
return head.next;
if (s == null || s.item != null)
return s;
p = s;
}
}
public E next() {
fullyLock();
try {
if (current == null)
throw new NoSuchElementException();
E x = currentElement;
lastRet = current;
current = nextNode(current);
currentElement = (current == null) ? null : current.item;
return x;
} finally {
fullyUnlock();
}
}
public void remove() {
if (lastRet == null)
throw new IllegalStateException();
fullyLock();
try {
Node<E> node = lastRet;
lastRet = null;
boolean overBorder = false;
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (!overBorder) overBorder = trail == border;
if (p == node) {
unlink(p, trail, overBorder);
break;
}
}
} finally {
fullyUnlock();
}
}
}
}

View File

@ -0,0 +1,137 @@
package com.njzscloud.common.core.tree;
import cn.hutool.core.collection.CollUtil;
import com.njzscloud.common.core.utils.GroupUtil;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
*
*/
public class Tree {
/**
* ()
*
* @param src
* @param rootId ID
* @param <T> ID
* @return List&lt;? extends TreeNode&lt;T&gt;&gt;
*/
public static <T> List<? extends TreeNode<T>> listToTreeDesc(List<? extends TreeNode<T>> src, T rootId) {
return listToTree(src.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList()), TreeNode::getId, TreeNode::getPid, TreeNode::setChildren, rootId, true);
}
/**
* ()
*
* @param src
* @param rootId ID
* @param <T> ID
* @return List&lt;? extends TreeNode&lt;T&gt;&gt;
*/
public static <T> List<? extends TreeNode<T>> listToTreeAsc(List<? extends TreeNode<T>> src, T rootId) {
return listToTree(src.stream().sorted().collect(Collectors.toList()), TreeNode::getId, TreeNode::getPid, TreeNode::setChildren, rootId, false);
}
/**
* ()
*
* @param src
* @param idFn id
* @param pidFn pid
* @param setChildrenFn
* @param rootId ID
* @param reverse
* @param <M>
* @param <T> ID
* @return List&lt;M&gt;
*/
public static <M extends Comparable<? super M>, T> List<M> listToTree(List<M> src, Function<M, T> idFn, Function<M, T> pidFn, BiConsumer<M, List<M>> setChildrenFn, T rootId, boolean reverse) {
if (CollUtil.isEmpty(src)) return Collections.emptyList();
if (reverse) {
src = src
.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
}
Map<T, List<M>> pid_treeNode_map = GroupUtil.k_a(src, pidFn);
for (M m : src) {
setChildrenFn.accept(m, pid_treeNode_map.get(idFn.apply(m)));
}
return CollUtil.emptyIfNull(pid_treeNode_map.get(rootId));
}
/**
* ()
*
* @param src
* @param idFn id
* @param pidFn pid
* @param setChildrenFn
* @param rootId ID
* @param <M>
* @param <T> ID
* @return List&lt;M&gt;
*/
public static <M, T> List<M> listToTree(List<M> src, Function<M, T> idFn, Function<M, T> pidFn, BiConsumer<M, List<M>> setChildrenFn, T rootId) {
if (CollUtil.isEmpty(src)) return Collections.emptyList();
Map<T, List<M>> pid_treeNode_map = GroupUtil.k_a(src, pidFn);
for (M m : src) {
setChildrenFn.accept(m, pid_treeNode_map.get(idFn.apply(m)));
}
return CollUtil.emptyIfNull(pid_treeNode_map.get(rootId));
}
/**
*
*
* @param src
* @param getChildrenFn
* @param convert , ->
* @param <S>
* @param <D>
* @return List&lt;D&gt;
*/
public static <S, D> List<D> treeToList(List<S> src, Function<S, List<S>> getChildrenFn, Function<S, D> convert) {
List<D> dest;
if (CollUtil.isNotEmpty(src)) {
dest = new ArrayList<>();
treeToList(src, dest, getChildrenFn, convert);
} else {
dest = Collections.emptyList();
}
return dest;
}
/**
*
*
* @param src
* @param dest
* @param getChildrenFn
* @param convert , ->
* @param <S>
* @param <D>
*/
public static <S, D> void treeToList(List<S> src, List<D> dest, Function<S, List<S>> getChildrenFn, Function<S, D> convert) {
if (CollUtil.isNotEmpty(src)) {
for (S s : src) {
D d = convert.apply(s);
dest.add(d);
List<S> children = getChildrenFn.apply(s);
if (CollUtil.isNotEmpty(children)) {
Tree.treeToList(children, dest, getChildrenFn, convert);
}
}
}
}
}

View File

@ -0,0 +1,51 @@
package com.njzscloud.common.core.tree;
import java.util.List;
/**
*
*
* @param <T> Id
*/
public interface TreeNode<T> extends Comparable<TreeNode<T>> {
/**
* ID
*
* @return T id
*/
T getId();
/**
* ID
*
* @return T pid
*/
T getPid();
/**
*
*
* @return int
*/
int getSort();
/**
*
*
* @return List&lt;? extends TreeNode&lt;T&gt;&gt;
*/
List<? extends TreeNode<T>> getChildren();
/**
*
*
* @param children
*/
void setChildren(List<? extends TreeNode<T>> children);
@Override
default int compareTo(TreeNode<T> o) {
return this.getSort() - o.getSort();
}
}

View File

@ -0,0 +1,121 @@
package com.njzscloud.common.core.tuple;
import java.util.*;
/**
* 2
*
* @param <_0>
* @param <_1>
*/
public class Tuple2<_0, _1> implements Iterable<Object> {
/**
*
*/
public final int size = 2;
/**
* 0
*/
protected final _0 _0_;
/**
* 1
*/
protected final _1 _1_;
/**
*
*
* @param _0_ 0
* @param _1_ 1
*/
protected Tuple2(_0 _0_, _1 _1_) {
this._0_ = _0_;
this._1_ = _1_;
}
/**
* Tuple2
*
* @param _0_ 0
* @param _1_ 1
* @return Tuple2&lt;__0, __1&gt;
*/
public static <__0, __1> Tuple2<__0, __1> create(__0 _0_, __1 _1_) {
return new Tuple2<>(_0_, _1_);
}
/**
* 0
*/
public _0 get_0() {
return _0_;
}
/**
* 1
*/
public _1 get_1() {
return _1_;
}
/**
* <p></p>
* <p> null</p>
*
* @param index
* @return T
*/
@SuppressWarnings("unchecked")
public <T> T get(int index) {
switch (index) {
case 0:
return (T) this._0_;
case 1:
return (T) this._1_;
default:
return null;
}
}
/**
*
*
* @return Iterator&lt;Object&gt;
*/
@Override
public Iterator<Object> iterator() {
return Collections.unmodifiableList(this.toList()).iterator();
}
/**
* List
*
* @return List&lt;Object&gt;
*/
public List<Object> toList() {
return Arrays.asList(this.toArray());
}
/**
*
*
* @return Object[]
*/
public Object[] toArray() {
return new Object[]{this._0_, this._1_};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tuple2<?, ?> tuple2 = (Tuple2<?, ?>) o;
return Objects.equals(_0_, tuple2._0_) && Objects.equals(_1_, tuple2._1_);
}
@Override
public int hashCode() {
return Objects.hash(_0_, _1_);
}
}

View File

@ -0,0 +1,84 @@
package com.njzscloud.common.core.tuple;
/**
* 3
*
* @param <_0>
* @param <_1>
* @param <_2>
*/
public class Tuple3<_0, _1, _2> extends Tuple2<_0, _1> {
/**
*
*/
public final int size = 3;
/**
* 2
*/
protected final _2 _2_;
/**
*
*
* @param _0_ 0
* @param _1_ 1
* @param _2_ 2
*/
protected Tuple3(_0 _0_, _1 _1_, _2 _2_) {
super(_0_, _1_);
this._2_ = _2_;
}
/**
* Tuple3
*
* @param _0_ 0
* @param _1_ 1
* @param _2_ 2
* @return Tuple3&lt;__0, __1, __2&gt;
*/
public static <__0, __1, __2> Tuple3<__0, __1, __2> create(__0 _0_, __1 _1_, __2 _2_) {
return new Tuple3<>(_0_, _1_, _2_);
}
/**
* 2
*/
public _2 get_2() {
return _2_;
}
/**
* <p></p>
* <p> null</p>
*
* @param index
* @return T
*/
@Override
@SuppressWarnings("unchecked")
public <T> T get(int index) {
switch (index) {
case 0:
return (T) this._0_;
case 1:
return (T) this._1_;
case 2:
return (T) this._2_;
default:
return null;
}
}
/**
*
*
* @return Object[]
*/
@Override
public Object[] toArray() {
return new Object[]{this._0_, this._1_, this._2_};
}
}

View File

@ -0,0 +1,87 @@
package com.njzscloud.common.core.tuple;
/**
* 4
*
* @param <_0>
* @param <_1>
* @param <_2>
* @param <_3>
*/
public class Tuple4<_0, _1, _2, _3> extends Tuple3<_0, _1, _2> {
/**
*
*/
public final int size = 4;
/**
* 3
*/
protected final _3 _3_;
/**
*
*
* @param _0_ 0
* @param _1_ 1
* @param _2_ 2
* @param _3_ 3
*/
protected Tuple4(_0 _0_, _1 _1_, _2 _2_, _3 _3_) {
super(_0_, _1_, _2_);
this._3_ = _3_;
}
/**
* Tuple4
*
* @param _0_ 0
* @param _1_ 1
* @param _2_ 2
* @param _3_ 3
* @return Tuple3&lt;__0, __1, __2, __3&gt;
*/
public static <__0, __1, __2, __3> Tuple4<__0, __1, __2, __3> create(__0 _0_, __1 _1_, __2 _2_, __3 _3_) {
return new Tuple4<>(_0_, _1_, _2_, _3_);
}
/**
* 3
*/
public _3 get_3() {
return _3_;
}
/**
* <p></p>
* <p> null</p>
*
* @param index
* @return T
*/
@Override
@SuppressWarnings("unchecked")
public <T> T get(int index) {
switch (index) {
case 0:
return (T) this._0_;
case 1:
return (T) this._1_;
case 2:
return (T) this._2_;
case 3:
return (T) this._3_;
default:
return null;
}
}
/**
*
*
* @return Object[]
*/
@Override
public Object[] toArray() {
return new Object[]{this._0_, this._1_, this._2_, this._3_};
}
}

View File

@ -0,0 +1,195 @@
package com.njzscloud.common.core.utils;
import java.util.regex.PatternSyntaxException;
/**
* @see sun.nio.fs.Globs
*/
public class Globs {
private static final String regexMetaChars = ".^$+{[]|()";
private static final String globMetaChars = "\\*?[{";
private static final char EOL = 0; // TBD
private Globs() {
}
private static boolean isRegexMeta(char c) {
return regexMetaChars.indexOf(c) != -1;
}
private static boolean isGlobMeta(char c) {
return globMetaChars.indexOf(c) != -1;
}
private static char next(String glob, int i) {
if (i < glob.length()) {
return glob.charAt(i);
}
return EOL;
}
/**
* Creates a regex pattern from the given glob expression.
*
* @throws PatternSyntaxException
*/
private static String toRegexPattern(String globPattern, boolean isDos) {
boolean inGroup = false;
StringBuilder regex = new StringBuilder("^");
int i = 0;
while (i < globPattern.length()) {
char c = globPattern.charAt(i++);
switch (c) {
case '\\':
// escape special characters
if (i == globPattern.length()) {
throw new PatternSyntaxException("No character to escape", globPattern, i - 1);
}
char next = globPattern.charAt(i++);
if (isGlobMeta(next) || isRegexMeta(next)) {
regex.append('\\');
}
regex.append(next);
break;
case '/':
if (isDos) {
regex.append("\\\\");
} else {
regex.append(c);
}
break;
case '[':
// don't match name separator in class
if (isDos) {
regex.append("[[^\\\\]&&[");
} else {
regex.append("[[^/]&&[");
}
if (next(globPattern, i) == '^') {
// escape the regex negation char if it appears
regex.append("\\^");
i++;
} else {
// negation
if (next(globPattern, i) == '!') {
regex.append('^');
i++;
}
// hyphen allowed at start
if (next(globPattern, i) == '-') {
regex.append('-');
i++;
}
}
boolean hasRangeStart = false;
char last = 0;
while (i < globPattern.length()) {
c = globPattern.charAt(i++);
if (c == ']') {
break;
}
if (c == '/' || (isDos && c == '\\')) {
throw new PatternSyntaxException("Explicit 'name separator' in class",
globPattern, i - 1);
}
// TBD: how to specify ']' in a class?
if (c == '\\' || c == '[' ||
c == '&' && next(globPattern, i) == '&') {
// escape '\', '[' or "&&" for regex class
regex.append('\\');
}
regex.append(c);
if (c == '-') {
if (!hasRangeStart) {
throw new PatternSyntaxException("Invalid range",
globPattern, i - 1);
}
if ((c = next(globPattern, i++)) == EOL || c == ']') {
break;
}
if (c < last) {
throw new PatternSyntaxException("Invalid range",
globPattern, i - 3);
}
regex.append(c);
hasRangeStart = false;
} else {
hasRangeStart = true;
last = c;
}
}
if (c != ']') {
throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
}
regex.append("]]");
break;
case '{':
if (inGroup) {
throw new PatternSyntaxException("Cannot nest groups",
globPattern, i - 1);
}
regex.append("(?:(?:");
inGroup = true;
break;
case '}':
if (inGroup) {
regex.append("))");
inGroup = false;
} else {
regex.append('}');
}
break;
case ',':
if (inGroup) {
regex.append(")|(?:");
} else {
regex.append(',');
}
break;
case '*':
if (next(globPattern, i) == '*') {
// crosses directory boundaries
regex.append(".*");
i++;
} else {
// within directory boundary
if (isDos) {
regex.append("[^\\\\]*");
} else {
regex.append("[^/]*");
}
}
break;
case '?':
if (isDos) {
regex.append("[^\\\\]");
} else {
regex.append("[^/]");
}
break;
default:
if (isRegexMeta(c)) {
regex.append('\\');
}
regex.append(c);
}
}
if (inGroup) {
throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
}
return regex.append('$').toString();
}
static String toUnixRegexPattern(String globPattern) {
return toRegexPattern(globPattern, false);
}
static String toWindowsRegexPattern(String globPattern) {
return toRegexPattern(globPattern, true);
}
}

View File

@ -0,0 +1,625 @@
package com.njzscloud.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.jackson.Jackson;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
*
*/
public class GroupUtil {
// region K-O
// region Collection
/**
* <p></p>
* <p>Map HashMap, kf, </p>
*
* @param src
* @param kf
* @param <SV> /
* @param <K>
* @return Map&lt;K, SV&gt;
*/
public static <SV, K> Map<K, SV> k_o(Collection<SV> src, Function<SV, K> kf) {
return k_o(src, kf, it -> it, HashMap::new);
}
/**
* <p></p>
* <p>Map HashMap, kf, vf</p>
*
* @param src
* @param kf
* @param vf
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V> Map<K, V> k_o(Collection<SV> src, Function<SV, K> kf, Function<SV, V> vf) {
return k_o(src, kf, vf, HashMap::new);
}
/**
* <p></p>
* <p>Map mf, kf, </p>
*
* @param src
* @param kf
* @param mf Map
* @param <SV> /
* @param <K>
* @param <M> Map
* @return Map&lt;K, SV&gt;
*/
public static <SV, K, M extends Map<K, SV>> M k_o(Collection<SV> src, Function<SV, K> kf, Supplier<M> mf) {
return k_o(src, kf, it -> it, mf);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param src
* @param kf
* @param vf
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V, M extends Map<K, V>> M k_o(Collection<SV> src, Function<SV, K> kf, Function<SV, V> vf, Supplier<M> mf) {
if (CollUtil.isEmpty(src)) return MapUtil.empty(null);
return k_o(src.stream(), kf, vf, mf);
}
// endregion
// region Collection 索引
/**
* <p></p>
* <p>Map HashMap, kf, </p>
* <p>kf </p>
*
* @param src
* @param kf
* @param <SV>
* @param <K>
* @return Map&lt;K, VT&gt;
*/
public static <SV, K> Map<K, SV> k_o(Collection<SV> src, BiFunction<Integer, SV, K> kf) {
return k_o(src, kf, it -> it, HashMap::new);
}
/**
* <p></p>
* Map HashMap, kf, vf</p>
* <p>kf </p>
*
* @param src
* @param kf K
* @param vf V
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V> Map<K, V> k_o(Collection<SV> src, BiFunction<Integer, SV, K> kf, Function<SV, V> vf) {
return k_o(src, kf, vf, HashMap::new);
}
/**
* <p></p>
* <p>Map mf, kf, </p>
* <p>kf </p>
*
* @param src
* @param kf K
* @param mf Map
* @param <SV>
* @param <K>
* @param <M> Map
* @return Map&lt;K, SV&gt;
*/
public static <SV, K, M extends Map<K, SV>> M k_o(Collection<SV> src, BiFunction<Integer, SV, K> kf, Supplier<M> mf) {
return k_o(src, kf, it -> it, mf);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
* <p>kf </p>
*
* @param src
* @param kf K
* @param vf V
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V, M extends Map<K, V>> M k_o(Collection<SV> src, BiFunction<Integer, SV, K> kf, Function<SV, V> vf, Supplier<M> mf) {
if (CollUtil.isEmpty(src)) return MapUtil.empty(null);
M map = mf.get();
int i = 0;
for (SV sv : src) {
K k = kf.apply(i, sv);
if (map.containsKey(k)) throw Exceptions.error("重复键:{}", sv);
V v = vf.apply(sv);
map.put(k, v);
i++;
}
return map;
}
/**
* <p></p>
* Map HashMap, kf, vf</p>
* <p>vf </p>
*
* @param src
* @param kf
* @param vf
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V> Map<K, V> k_o(Collection<SV> src, Function<SV, K> kf, BiFunction<Integer, SV, V> vf) {
return k_o(src, kf, vf, HashMap::new);
}
/**
* <p></p>
* Map mf, kf, vf</p>
* <p>vf </p>
*
* @param src
* @param kf
* @param vf
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V, M extends Map<K, V>> M k_o(Collection<SV> src, Function<SV, K> kf, BiFunction<Integer, SV, V> vf, Supplier<M> mf) {
if (CollUtil.isEmpty(src)) return MapUtil.empty(null);
M map = mf.get();
int i = 0;
for (SV sv : src) {
K k = kf.apply(sv);
if (map.containsKey(k)) throw Exceptions.error("重复键:{}", sv);
V v = vf.apply(i, sv);
map.put(k, v);
i++;
}
return map;
}
/**
* <p></p>
* Map HashMap, kf, vf</p>
* <p>kf vf </p>
*
* @param src
* @param kf
* @param vf
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V> Map<K, V> k_o(Collection<SV> src, BiFunction<Integer, SV, K> kf, BiFunction<Integer, SV, V> vf) {
return k_o(src, kf, vf, HashMap::new);
}
/**
* <p></p>
* Map mf, kf, vf</p>
* <p>kf vf </p>
*
* @param src
* @param kf
* @param vf
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V, M extends Map<K, V>> M k_o(Collection<SV> src, BiFunction<Integer, SV, K> kf, BiFunction<Integer, SV, V> vf, Supplier<M> mf) {
if (CollUtil.isEmpty(src)) return MapUtil.empty(null);
M map = mf.get();
int i = 0;
for (SV sv : src) {
K k = kf.apply(i, sv);
if (map.containsKey(k)) throw Exceptions.error("重复键:{}", sv);
V v = vf.apply(i, sv);
map.put(k, v);
i++;
}
return map;
}
// endregion
// region Stream
/**
* <p></p>
* <p>Map HashMap, kf, </p>
*
* @param stream
* @param kf
* @param <SV>
* @param <K>
* @return Map&lt;K, SV&gt;
*/
public static <SV, K> Map<K, SV> k_o(Stream<SV> stream, Function<SV, K> kf) {
return k_o(stream, kf, it -> it, HashMap::new);
}
/**
* <p></p>
* <p>Map HashMap, kf, vf</p>
*
* @param stream
* @param kf
* @param vf
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V> Map<K, V> k_o(Stream<SV> stream, Function<SV, K> kf, Function<SV, V> vf) {
return k_o(stream, kf, vf, HashMap::new);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param stream
* @param kf
* @param mf Map
* @param <SV>
* @param <K>
* @param <M> Map
* @return Map&lt;K, SV&gt;
*/
public static <SV, K, M extends Map<K, SV>> M k_o(Stream<SV> stream, Function<SV, K> kf, Supplier<M> mf) {
return k_o(stream, kf, it -> it, mf);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param stream
* @param kf
* @param vf
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, V&gt;
*/
public static <SV, K, V, M extends Map<K, V>> M k_o(Stream<SV> stream, Function<SV, K> kf, Function<SV, V> vf, Supplier<M> mf) {
if (stream == null) return MapUtil.empty(null);
BinaryOperator<V> op = (oldVal, val) -> {
throw Exceptions.error("值【{}】与【{}】对应的键重复", Jackson.toJsonStr(oldVal), Jackson.toJsonStr(val));
};
return stream.collect(Collectors.toMap(kf, vf, op, mf));
}
// endregion
// endregion
// region K-A
// region Collection
/**
* <p></p>
* <p>Map HashMap, kf, </p>
*
* @param src
* @param kf
* @param <K>
* @param <SV>
* @return Map&lt;K, List&lt;V&gt;&gt;
*/
public static <SV, K> Map<K, List<SV>> k_a(Collection<SV> src, Function<SV, K> kf) {
return k_a(src, kf, it -> it, HashMap::new);
}
/**
* <p></p>
* <p>Map HashMap, kf, vf</p>
*
* @param src
* @param kf
* @param vf
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, List&lt;V&gt;&gt;
*/
public static <SV, K, V> Map<K, List<V>> k_a(Collection<SV> src, Function<SV, K> kf, Function<SV, V> vf) {
return k_a(src, kf, vf, HashMap::new);
}
/**
* <p></p>
* <p>Map mf, kf, </p>
*
* @param src
* @param kf
* @param mf Map
* @param <SV>
* @param <K>
* @param <M> Map
* @return Map&lt;K, List&lt;SV&gt;&gt;
*/
public static <SV, K, M extends Map<K, List<SV>>> M k_a(Collection<SV> src, Function<SV, K> kf, Supplier<M> mf) {
return k_a(src, kf, it -> it, mf);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param src
* @param kf
* @param vf
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, List&lt;V&gt;&gt;
*/
public static <SV, K, V, M extends Map<K, List<V>>> M k_a(Collection<SV> src, Function<SV, K> kf, Function<SV, V> vf, Supplier<M> mf) {
if (CollUtil.isEmpty(src)) return MapUtil.empty(null);
return src.stream().collect(Collectors.groupingBy(kf, mf, Collectors.mapping(vf, Collectors.toList())));
}
// endregion
// region Stream
/**
* <p></p>
* <p>Map HashMap, kf, </p>
*
* @param stream
* @param kf
* @param <K>
* @param <SV>
* @return Map&lt;K, List&lt;V&gt;&gt;
*/
public static <SV, K> Map<K, List<SV>> k_a(Stream<SV> stream, Function<SV, K> kf) {
return k_a(stream, kf, it -> it, HashMap::new);
}
/**
* <p></p>
* <p>Map HashMap, kf, vf</p>
*
* @param stream
* @param kf
* @param vf
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, List&lt;V&gt;&gt;
*/
public static <SV, K, V> Map<K, List<V>> k_a(Stream<SV> stream, Function<SV, K> kf, Function<SV, V> vf) {
return k_a(stream, kf, vf, HashMap::new);
}
/**
* <p></p>
* <p>Map mf, kf, </p>
*
* @param stream
* @param kf
* @param mf Map
* @param <SV>
* @param <K>
* @param <M> Map
* @return Map&lt;K, List&lt;SV&gt;&gt;
*/
public static <SV, K, M extends Map<K, List<SV>>> M k_a(Stream<SV> stream, Function<SV, K> kf, Supplier<M> mf) {
return k_a(stream, kf, it -> it, mf);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param stream
* @param kf
* @param vf
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, List&lt;V&gt;&gt;
*/
public static <SV, K, V, M extends Map<K, List<V>>> M k_a(Stream<SV> stream, Function<SV, K> kf, Function<SV, V> vf, Supplier<M> mf) {
if (stream == null) return MapUtil.empty(null);
return stream.collect(Collectors.groupingBy(kf, mf, Collectors.mapping(vf, Collectors.toList())));
}
// endregion
// endregion
// region K-S
// region Collection
/**
* <p></p>
* <p>Map HashMap, kf, </p>
*
* @param src
* @param kf
* @param <SV>
* @param <K>
* @return Map&lt;K, Set&lt;SV&gt;&gt;
*/
public static <SV, K> Map<K, Set<SV>> k_s(Collection<SV> src, Function<SV, K> kf) {
return k_s(src, kf, it -> it, HashMap::new);
}
/**
* <p></p>
* <p>Map HashMap, kf, vf</p>
*
* @param src
* @param kf
* @param vf
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, Set&lt;V&gt;&gt;
*/
public static <SV, K, V> Map<K, Set<V>> k_s(Collection<SV> src, Function<SV, K> kf, Function<SV, V> vf) {
return k_s(src, kf, vf, HashMap::new);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param src
* @param kf
* @param mf Map
* @param <K>
* @param <SV>
* @param <M> Map
* @return Map&lt;K, Set&lt;SV&gt;&gt;
*/
public static <SV, K, M extends Map<K, Set<SV>>> M k_s(Collection<SV> src, Function<SV, K> kf, Supplier<M> mf) {
return k_s(src, kf, it -> it, mf);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param src
* @param kf
* @param vf
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, Set&lt;V&gt;&gt;
*/
public static <SV, K, V, M extends Map<K, Set<V>>> M k_s(Collection<SV> src, Function<SV, K> kf, Function<SV, V> vf, Supplier<M> mf) {
if (CollUtil.isEmpty(src)) return MapUtil.empty(null);
return src.stream().collect(Collectors.groupingBy(kf, mf, Collectors.mapping(vf, Collectors.toSet())));
}
// endregion
// region Stream
/**
* <p></p>
* <p>Map HashMap, kf, </p>
*
* @param stream
* @param kf
* @param <SV>
* @param <K>
* @return Map&lt;K, Set&lt;SV&gt;&gt;
*/
public static <SV, K> Map<K, Set<SV>> k_s(Stream<SV> stream, Function<SV, K> kf) {
return k_s(stream, kf, it -> it, HashMap::new);
}
/**
* <p></p>
* <p>Map HashMap, kf, vf</p>
*
* @param stream
* @param kf
* @param vf
* @param <SV>
* @param <K>
* @param <V>
* @return Map&lt;K, Set&lt;V&gt;&gt;
*/
public static <SV, K, V> Map<K, Set<V>> k_s(Stream<SV> stream, Function<SV, K> kf, Function<SV, V> vf) {
return k_s(stream, kf, vf, HashMap::new);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param stream
* @param kf
* @param mf Map
* @param <K>
* @param <SV>
* @param <M> Map
* @return Map&lt;K, Set&lt;SV&gt;&gt;
*/
public static <SV, K, M extends Map<K, Set<SV>>> M k_s(Stream<SV> stream, Function<SV, K> kf, Supplier<M> mf) {
return k_s(stream, kf, it -> it, mf);
}
/**
* <p></p>
* <p>Map mf, kf, vf</p>
*
* @param stream
* @param kf
* @param vf
* @param mf Map
* @param <SV>
* @param <K>
* @param <V>
* @param <M> Map
* @return Map&lt;K, Set&lt;V&gt;&gt;
*/
public static <SV, K, V, M extends Map<K, Set<V>>> M k_s(Stream<SV> stream, Function<SV, K> kf, Function<SV, V> vf, Supplier<M> mf) {
if (stream == null) return MapUtil.empty(null);
return stream.collect(Collectors.groupingBy(kf, mf, Collectors.mapping(vf, Collectors.toSet())));
}
// endregion
// endregion
}

View File

@ -0,0 +1,41 @@
package com.njzscloud.common.core.utils;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.ex.Exceptions;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
/**
* <p>/Redis Key</p>
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Key {
private final String keyTpl;
/**
* <p> KEY</p>
* <p>KEY {} </p>
*
* @param keyTpl KEY
* @return KEY
*/
public static Key create(String keyTpl) {
Assert.notEmpty(keyTpl, () -> Exceptions.error("KEY 字符串模板不能为空"));
keyTpl = keyTpl.replaceAll("\\{\\w+}", "{}");
return new Key(keyTpl);
}
/**
* <p> KEY </p>
*
* @param params
* @return key
*/
public String fill(Object... params) {
int count = StrUtil.count(keyTpl, "{}");
Assert.isTrue(count == params.length,
() -> Exceptions.error("占位符数量:[{}]与参数数量:[{}]不一致", count, params.length));
return StrUtil.format(keyTpl, params);
}
}

View File

@ -0,0 +1,243 @@
package com.njzscloud.common.core.utils;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.njzscloud.common.core.ex.ExceptionMsg;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.jackson.Jackson;
import lombok.Getter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
*
*
* @param <T>
*/
@Getter
@SuppressWarnings("unchecked")
public final class R<T> {
/**
* <p> >= 0</p>
* <p>0 --> </p>
* <p> --> </p>
*/
private final int code;
/**
*
*/
private final boolean success;
/**
*
*/
private final String msg;
/**
*
*/
private T data;
/**
*
*/
private Object message;
private R(T data, ExceptionMsg msg, Object message) {
this(msg.code, data, msg.msg, message);
}
@JsonCreator
private R(@JsonProperty("code") int code,
@JsonProperty("data") T data,
@JsonProperty("msg") String msg,
@JsonProperty("message") Object message) {
this.data = data;
this.code = code;
this.msg = msg;
this.message = message;
this.success = code == 0;
}
/**
* 0nullnull
*
* @param <D>
* @return R&lt;D&gt;
*/
public static <D> R<D> success() {
return new R<>(0, null, "成功", null);
}
/**
* 0null
*
* @param data
* @param <D>
* @return R&lt;D&gt;
*/
public static <D> R<D> success(D data) {
return new R<>(0, data, "成功", null);
}
/**
* 0null
*
* @param data
* @param msg
* @param <D>
* @return R&lt;D&gt;
*/
public static <D> R<D> success(D data, String msg) {
return new R<>(0, data, msg, null);
}
/**
* 11111nullnull
*
* @param <D>
* @return R&lt;D&gt;
*/
public static <D> R<D> failed() {
return new R<>(null, ExceptionMsg.SYS_EXP_MSG, null);
}
/**
* nullnull
*
* @param msg
* @param <D>
* @return R&lt;D&gt;
* @see ExceptionMsg
*/
public static <D> R<D> failed(ExceptionMsg msg) {
return new R<>(null, msg, null);
}
public static <D> R<D> failed(ExceptionMsg msg, Object message) {
return new R<>(null, msg, message);
}
public static <D> R<D> failed(ExceptionMsg msg, String message, Object... param) {
if (StrUtil.isNotBlank(message) && param != null && param.length > 0) {
message = StrUtil.format(message, param);
}
return new R<>(null, msg, message);
}
/**
* null
*
* @param data
* @param msg
* @param <D>
* @return R&lt;D&gt;
* @see ExceptionMsg
*/
public static <D> R<D> failed(D data, ExceptionMsg msg) {
return new R<>(null, msg, null);
}
public static <D> R<D> failed(D data, ExceptionMsg msg, Object message) {
return new R<>(null, msg, message);
}
/**
*
*
* @param data
* @return R&lt;D&gt;
*/
public R<T> setData(T data) {
this.data = data;
return this;
}
/**
* <p></p>
* <p> data Map Key Stringdata null </p>
*
* @param key
* @param val
* @return R&lt;T&gt;
*/
public <V> R<Map<String, V>> put(String key, V val) {
if (this.data == null) {
R<Map<String, V>> r = new R<>(this.code, new HashMap<>(), this.msg, this.message);
r.data.put(key, val);
return r;
} else if (Map.class.isAssignableFrom(data.getClass())) {
((Map<String, V>) this.data).put(key, val);
return (R<Map<String, V>>) this;
}
throw Exceptions.error("响应信息构建失败");
}
/**
* <p></p>
* <p> data List data null </p>
*
* @param val
* @return R&lt;T&gt;
*/
public <V> R<List<V>> add(V val) {
if (this.data == null) {
R<List<V>> r = new R<>(this.code, new ArrayList<>(), this.msg, this.message);
r.data.add(val);
return r;
} else if (List.class.isAssignableFrom(data.getClass())) {
((List<V>) this.data).add(val);
return (R<List<V>>) this;
}
throw Exceptions.error("响应信息构建失败");
}
/**
*
*
* @param message
* @return R&lt;T&gt;
*/
public R<T> setMessage(Object message) {
this.message = message;
return this;
}
/**
*
*
* @param message {}
* @param param
* @return R&lt;T&gt;
*/
public R<T> setMessage(String message, Object... param) {
if (StrUtil.isNotBlank(message) && param != null && param.length > 0) {
message = StrUtil.format(message, param);
}
this.message = message;
return this;
}
/**
* <p></p>
* <p></p>
* <p>codemsgmessage </p>
*
* @param converter
* @param <D>
* @return R&lt;D&gt;
*/
public <D> R<D> convert(Function<T, D> converter) {
D d = converter.apply(this.data);
return new R<>(this.code, d, this.msg, this.message);
}
@Override
public String toString() {
return Jackson.toJsonStr(this);
}
}

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>njzscloud-common-email</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common-core</artifactId>
<scope>provided</scope>
</dependency>
<!-- junit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- spring-boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- configuration processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,234 @@
package com.njzscloud.common.email;
import cn.hutool.core.lang.Assert;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.http.constant.Mime;
import com.njzscloud.common.core.tuple.Tuple2;
import lombok.Getter;
import javax.mail.util.ByteArrayDataSource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
@Getter
public class MailMessage {
/**
*
*/
private final List<String> tos;
/**
*
*/
private final String subject;
/**
*
*/
private final String content;
/**
* HTML
*/
private final boolean html;
/**
* 0-->1-->
*/
private final List<Tuple2<String, ByteArrayDataSource>> attachmentList;
/**
*
*
* @param tos
* @param subject
* @param content
* @param html HTML
* @param attachmentList 0-->1-->
*/
private MailMessage(List<String> tos, String subject, String content, boolean html, List<Tuple2<String, ByteArrayDataSource>> attachmentList) {
this.tos = tos;
this.subject = subject;
this.content = content;
this.html = html;
this.attachmentList = attachmentList;
}
/**
*
*
* @return
*/
public static Builder builder() {
return new Builder();
}
/**
*
*/
public static class Builder {
/**
*
*/
private List<String> tos;
/**
*
*/
private String subject;
/**
*
*/
private String content;
/**
* HTML
*/
private boolean html;
/**
* 0-->1-->
*/
private List<Tuple2<String, ByteArrayDataSource>> attachmentList;
/**
*
*
* @param to
* @return
*/
public Builder addTo(String to) {
if (this.tos == null) {
this.tos = new ArrayList<>();
}
this.tos.add(to);
return this;
}
/**
*
*
* @param tos
* @return
*/
public Builder tos(List<String> tos) {
this.tos = tos;
return this;
}
/**
*
*
* @param subject
* @return
*/
public Builder subject(String subject) {
this.subject = subject;
return this;
}
/**
*
*
* @param content
* @return
*/
public Builder content(String content) {
this.content = content;
this.html = false;
return this;
}
/**
*
*
* @param content HTML
* @return
*/
public Builder htmlContent(String content) {
this.content = content;
this.html = true;
return this;
}
/**
*
*
* @param attachmentList 0-->1-->
* @return
*/
public Builder attachmentList(List<Tuple2<String, ByteArrayDataSource>> attachmentList) {
this.attachmentList = attachmentList;
return this;
}
/**
*
*
* @param filename
* @param in
* @param mime
* @return
*/
public Builder addAttachment(String filename, InputStream in, String mime) {
Assert.notBlank(filename, "附件名称不能为空");
Assert.notNull(in, "附件不能为空");
Assert.notBlank(mime, "内容类型不能为空");
if (this.attachmentList == null) {
this.attachmentList = new ArrayList<>();
}
ByteArrayDataSource dataSource;
try (InputStream is = in) {
dataSource = new ByteArrayDataSource(is, mime);
} catch (IOException e) {
throw Exceptions.error(e, "附件读取失败");
}
this.attachmentList.add(Tuple2.create(filename, dataSource));
return this;
}
/**
*
*
* @param filename
* @param bytes
* @param mime
* @return
*/
public Builder addAttachment(String filename, byte[] bytes, String mime) {
return addAttachment(filename, new ByteArrayInputStream(bytes), mime);
}
/**
* application/octet-stream
*
* @param filename
* @param bytes
* @return
*/
public Builder addAttachment(String filename, byte[] bytes) {
return addAttachment(filename, new ByteArrayInputStream(bytes), Mime.BINARY);
}
/**
* application/octet-stream
*
* @param filename
* @param in
* @return
*/
public Builder addAttachment(String filename, InputStream in) {
return addAttachment(filename, in, Mime.BINARY);
}
/**
*
*
* @return MailMessage
*/
public MailMessage build() {
Assert.isTrue(this.tos != null && !this.tos.isEmpty(), "收件人邮箱不能为空");
Assert.notBlank(this.subject, "邮件主题不能为空");
return new MailMessage(this.tos, this.subject, this.content, this.html, this.attachmentList);
}
}
}

View File

@ -0,0 +1,98 @@
package com.njzscloud.common.email.util;
import cn.hutool.extra.spring.SpringUtil;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.email.MailMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.MailAuthenticationException;
import org.springframework.mail.MailException;
import org.springframework.mail.MailParseException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.util.ByteArrayDataSource;
import java.util.Collections;
import java.util.List;
/**
*
*/
@Slf4j
public class EMailUtil {
private static final String FROM;
private static final JavaMailSender MAIL_SENDER;
static {
MAIL_SENDER = SpringUtil.getBean(JavaMailSender.class);
FROM = SpringUtil.getProperty("spring.mail.username");
}
/**
*
*
* @param to
* @param subject
* @param content
*/
public void sendSimpleMail(String to, String subject, String content) {
sendSimpleMail(Collections.singletonList(to), subject, content);
}
/**
*
*
* @param tos
* @param subject
* @param content
*/
public void sendSimpleMail(List<String> tos, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(FROM);
message.setTo(tos.toArray(new String[0]));
message.setSubject(subject);
message.setText(content);
try {
MAIL_SENDER.send(message);
} catch (MailException e) {
throw Exceptions.error(e,
e instanceof MailParseException ? "邮件消息解析失败" :
e instanceof MailAuthenticationException ? "邮件服务器认证失败" : "邮件发送失败", e);
}
}
/**
*
*
* @param mailMessage
*/
public void sendComplexMail(MailMessage mailMessage) {
MimeMessage message = MAIL_SENDER.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(FROM);
helper.setTo(mailMessage.getTos().toArray(new String[0]));
helper.setSubject(mailMessage.getSubject());
helper.setText(mailMessage.getContent(), mailMessage.isHtml());
List<Tuple2<String, ByteArrayDataSource>> attachmentList = mailMessage.getAttachmentList();
if (attachmentList != null && !attachmentList.isEmpty()) {
for (Tuple2<String, ByteArrayDataSource> dataSource : attachmentList) {
helper.addAttachment(dataSource.get_0(), dataSource.get_1());
}
}
} catch (MessagingException e) {
throw Exceptions.error(e, "邮件创建失败");
}
try {
MAIL_SENDER.send(message);
} catch (MailException e) {
throw Exceptions.error(e,
e instanceof MailParseException ? "邮件消息解析失败" :
e instanceof MailAuthenticationException ? "邮件服务器认证失败" : "邮件发送失败");
}
}
}

Some files were not shown because too many files have changed in this diff Show More