# Conflicts:
#	z-doc/warehouse_management_schema.sql
master
ljw 2025-12-08 10:17:35 +08:00
commit 4acaccf533
435 changed files with 44544 additions and 984 deletions

13
.editorconfig 100644
View File

@ -0,0 +1,13 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 1000
[*.{java,c,cpp,kt,xml}]
indent_size = 4

6
.gitignore vendored 100644
View File

@ -0,0 +1,6 @@
/**/logs
/**/*.iml
/**/.idea
/**/target
/**/.DS_Store
/**/.njzscloud-dispose

View File

@ -0,0 +1,34 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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>21</maven.compiler.source>
<maven.compiler.target>21</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>
</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,56 @@
package com.njzscloud.common.cache;
import com.njzscloud.common.core.thread.SyncUtil;
import java.util.function.Supplier;
public class DualCache implements Cache {
private final Cache FIRST_CACHE;
private final Cache SECOND_CACHE;
public DualCache(Cache firstCache, Cache secondCache) {
FIRST_CACHE = firstCache;
SECOND_CACHE = secondCache;
}
@Override
public <T> T get(String key) {
return SyncUtil.syncR(key, () -> FIRST_CACHE.get(key, () -> SECOND_CACHE.get(key)));
}
@Override
public <T> T get(String key, Supplier<T> supplier) {
return SyncUtil.syncW(key, () -> FIRST_CACHE.get(key, () -> SECOND_CACHE.get(key, supplier)));
}
@Override
public <T> T get(String key, long timeout, Supplier<T> supplier) {
return SyncUtil.syncW(key, () -> FIRST_CACHE.get(key, timeout, () -> SECOND_CACHE.get(key, timeout, supplier)));
}
@Override
public void put(String key, Object value) {
SyncUtil.syncW(key, () -> {
FIRST_CACHE.put(key, value);
SECOND_CACHE.put(key, value);
});
}
@Override
public void put(String key, Object value, long timeout) {
SyncUtil.syncW(key, () -> {
FIRST_CACHE.put(key, value);
SECOND_CACHE.put(key, value, timeout);
});
}
@Override
public void remove(String key) {
SyncUtil.syncW(key, () -> {
FIRST_CACHE.remove(key);
SECOND_CACHE.remove(key);
});
}
}

View File

@ -0,0 +1,45 @@
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);
}
@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,51 @@
package com.njzscloud.common.cache;
import com.njzscloud.common.redis.util.Redis;
import io.lettuce.core.SetArgs;
import java.util.function.Supplier;
@SuppressWarnings("unchecked")
public class SecondCache implements Cache {
private final long timeout;
public SecondCache(long timeout) {
this.timeout = timeout;
}
public <T> T get(String key) {
return Redis.get(key);
}
@Override
public <T> T get(String key, Supplier<T> supplier) {
Object o = Redis.get(key);
if (o != null) return (T) o;
o = supplier.get();
if (o != null) Redis.set(key, o);
return (T) o;
}
@Override
public <T> T get(String key, long timeout, Supplier<T> supplier) {
Object 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;
}
public void put(String key, Object value, long timeout) {
Redis.set(key, value, new SetArgs().ex(timeout));
}
public void put(String key, Object value) {
if (timeout > 0) Redis.set(key, value, new SetArgs().ex(timeout));
else Redis.set(key, value);
}
public void remove(String key) {
Redis.del(key);
}
}

View File

@ -0,0 +1,25 @@
package com.njzscloud.common.cache.config;
import com.njzscloud.common.cache.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration(proxyBeanMethods = false)
@ConditionalOnBooleanProperty(prefix = "cache", name = "enable")
@EnableConfigurationProperties(CacheProperties.class)
public class CacheAutoConfiguration {
@Bean
public Cache cache(CacheProperties properties) {
CacheProperties.FirstCacheProperties first = properties.getFirst();
CacheProperties.SecondCacheProperties second = properties.getSecond();
Cache noCache = new NoCache();
Cache firstCache = first.isEnabled() ? new FirstCache(first.getCapacity(), first.getTimeout()) : noCache;
Cache secondCache = second.isEnabled() ? new SecondCache(second.getTimeout()) : noCache;
return new DualCache(firstCache, secondCache);
}
}

View File

@ -0,0 +1,31 @@
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 boolean enabled = false;
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,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>21</maven.compiler.source>
<maven.compiler.target>21</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>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-poi</artifactId>
</dependency>
<!--</editor-fold>-->
<!-- 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,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,124 @@
package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TreeNode;
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 {
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[] declaredFields = valueClazz.getDeclaredFields();
Field field = null;
for (Field field_ : declaredFields) {
if (field_.getName().equals(currentName)) {
field = field_;
break;
}
}
if (field == null) {
declaredFields = valueClazz.getSuperclass().getDeclaredFields();
for (Field field_ : declaredFields) {
if (field_.getName().equals(currentName)) {
field = field_;
break;
}
}
}
if (field == null) {
throw new NoSuchFieldException("字段不存在:" + 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,113 @@
package com.njzscloud.common.core.log;
import java.util.Arrays;
/**
* Ansi
*/
public enum AnsiColor {
/**
*
*/
DEFAULT("39"),
/**
*
*/
BLACK("30"),
/**
*
*/
RED("31"),
/**
* 绿
*/
GREEN("32"),
/**
*
*/
YELLOW("33"),
/**
*
*/
BLUE("34"),
/**
*
*/
MAGENTA("35"),
/**
*
*/
CYAN("36"),
/**
*
*/
WHITE("37"),
/**
*
*/
BRIGHT_BLACK("90"),
/**
*
*/
BRIGHT_RED("91"),
/**
* 绿
*/
BRIGHT_GREEN("92"),
/**
*
*/
BRIGHT_YELLOW("93"),
/**
*
*/
BRIGHT_BLUE("94"),
/**
*
*/
BRIGHT_MAGENTA("95"),
/**
*
*/
BRIGHT_CYAN("96"),
/**
*
*/
BRIGHT_WHITE("97");
private final String code;
AnsiColor(String code) {
this.code = code;
}
public static AnsiColor parse(String name) {
return Arrays.stream(AnsiColor.values())
.filter(member -> member.toString().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
}
@Override
public String toString() {
return this.code;
}
}

View File

@ -0,0 +1,37 @@
package com.njzscloud.common.core.log;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.pattern.CompositeConverter;
/**
*
*/
public class ColorConverter extends CompositeConverter<ILoggingEvent> {
@Override
protected String transform(ILoggingEvent event, String in) {
AnsiColor color = AnsiColor.parse(getFirstOption());
if (color == null) {
switch (event.getLevel().toInt()) {
case Level.ERROR_INT:
color = AnsiColor.RED;
break;
case Level.WARN_INT:
color = AnsiColor.YELLOW;
break;
case Level.INFO_INT:
color = AnsiColor.BLUE;
break;
case Level.DEBUG_INT:
color = AnsiColor.GREEN;
break;
case Level.TRACE_INT:
color = AnsiColor.MAGENTA;
break;
default:
color = AnsiColor.BLACK;
}
}
return "\033[" + color + "m" + in + "\033[0m";
}
}

View File

@ -0,0 +1,48 @@
package com.njzscloud.common.core.thread;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
private final boolean virtual;
public DefaultThreadFactory(boolean virtual, String poolName) {
if (StrUtil.isBlank(poolName)) namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
else namePrefix = poolName + "-";
this.virtual = virtual;
}
@Override
public Thread newThread(Runnable r) {
String name = namePrefix + threadNumber.getAndIncrement();
Runnable task = () -> {
try {
r.run();
} finally {
threadNumber.decrementAndGet();
}
};
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = (t1, e) -> log.error("线程异常:{}", t1.getName(), e);
if (virtual) {
return Thread.ofVirtual()
.name(name)
.uncaughtExceptionHandler(uncaughtExceptionHandler)
.unstarted(task);
} else {
return Thread.ofPlatform()
.name(name)
.daemon(true)
.uncaughtExceptionHandler(uncaughtExceptionHandler)
.priority(Thread.NORM_PRIORITY)
.unstarted(task);
}
}
}

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,385 @@
package com.njzscloud.common.core.thread;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.ex.SysThrowable;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class SyncUtil {
public static void sync(String blockName, long timeout, boolean fair, SyncBlock syncBlock) {
LockWrapper lockWrapper = null;
try {
lockWrapper = LockWrapper.get(blockName, timeout, fair);
if (lockWrapper != null) {
// 加锁成功
syncBlock.block();
} else {
throw Exceptions.error("锁获取失败:锁名称:{}", blockName);
}
} catch (SysThrowable e) {
throw e;
} catch (Exception e) {
throw Exceptions.error(e, "同步块执行失败:锁名称:{}", blockName);
} finally {
if (lockWrapper != null) {
lockWrapper.unlock();
}
}
}
public static <T> T sync(String blockName, long timeout, boolean fair, SyncSupplierBlock<T> syncBlock) {
LockWrapper lockWrapper = null;
try {
lockWrapper = LockWrapper.get(blockName, timeout, fair);
if (lockWrapper != null) {
// 加锁成功
return syncBlock.block();
} else {
throw Exceptions.error("锁获取失败:锁名称:{}", blockName);
}
} catch (SysThrowable e) {
throw e;
} catch (Exception e) {
throw Exceptions.error(e, "同步块执行失败:锁名称:{}", blockName);
} finally {
if (lockWrapper != null) {
lockWrapper.unlock();
}
}
}
public static <T> T sync(String blockName, boolean fair, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, 0, fair, syncBlock);
}
public static <T> T sync(String blockName, long timeout, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, timeout, false, syncBlock);
}
public static <T> T sync(String blockName, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, 0, false, syncBlock);
}
public static void sync(String blockName, boolean fair, SyncBlock syncBlock) {
sync(blockName, 0, fair, syncBlock);
}
public static void sync(String blockName, long timeout, SyncBlock syncBlock) {
sync(blockName, timeout, false, syncBlock);
}
public static void sync(String blockName, SyncBlock syncBlock) {
sync(blockName, 0, false, syncBlock);
}
public static void syncR(String blockName, long timeout, boolean fair, SyncBlock syncBlock) {
LockWrapper lockWrapper = null;
try {
lockWrapper = LockWrapper.getRLock(blockName, timeout, fair);
if (lockWrapper != null) {
// 加锁成功
syncBlock.block();
} else {
throw Exceptions.error("锁获取失败:锁名称:{}", blockName);
}
} catch (SysThrowable e) {
throw e;
} catch (Exception e) {
throw Exceptions.error(e, "同步块执行失败:锁名称:{}", blockName);
} finally {
if (lockWrapper != null) {
lockWrapper.unrlock();
}
}
}
public static <T> T syncR(String blockName, long timeout, boolean fair, SyncSupplierBlock<T> syncBlock) {
LockWrapper lockWrapper = null;
try {
lockWrapper = LockWrapper.getRLock(blockName, timeout, fair);
if (lockWrapper != null) {
// 加锁成功
return syncBlock.block();
} else {
throw Exceptions.error("锁获取失败:锁名称:{}", blockName);
}
} catch (SysThrowable e) {
throw e;
} catch (Exception e) {
throw Exceptions.error(e, "同步块执行失败:锁名称:{}", blockName);
} finally {
if (lockWrapper != null) {
lockWrapper.unrlock();
}
}
}
public static <T> T syncR(String blockName, boolean fair, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, 0, fair, syncBlock);
}
public static <T> T syncR(String blockName, long timeout, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, timeout, false, syncBlock);
}
public static <T> T syncR(String blockName, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, 0, false, syncBlock);
}
public static void syncR(String blockName, boolean fair, SyncBlock syncBlock) {
sync(blockName, 0, fair, syncBlock);
}
public static void syncR(String blockName, long timeout, SyncBlock syncBlock) {
sync(blockName, timeout, false, syncBlock);
}
public static void syncR(String blockName, SyncBlock syncBlock) {
sync(blockName, 0, false, syncBlock);
}
public static void syncW(String blockName, long timeout, boolean fair, SyncBlock syncBlock) {
LockWrapper lockWrapper = null;
try {
lockWrapper = LockWrapper.getWLock(blockName, timeout, fair);
if (lockWrapper != null) {
// 加锁成功
syncBlock.block();
} else {
throw Exceptions.error("锁获取失败:锁名称:{}", blockName);
}
} catch (SysThrowable e) {
throw e;
} catch (Exception e) {
throw Exceptions.error(e, "同步块执行失败:锁名称:{}", blockName);
} finally {
if (lockWrapper != null) {
lockWrapper.unwlock();
}
}
}
public static <T> T syncW(String blockName, long timeout, boolean fair, SyncSupplierBlock<T> syncBlock) {
LockWrapper lockWrapper = null;
try {
lockWrapper = LockWrapper.getWLock(blockName, timeout, fair);
if (lockWrapper != null) {
// 加锁成功
return syncBlock.block();
} else {
throw Exceptions.error("锁获取失败:锁名称:{}", blockName);
}
} catch (SysThrowable e) {
throw e;
} catch (Exception e) {
throw Exceptions.error(e, "同步块执行失败:锁名称:{}", blockName);
} finally {
if (lockWrapper != null) {
lockWrapper.unwlock();
}
}
}
public static <T> T syncW(String blockName, boolean fair, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, 0, fair, syncBlock);
}
public static <T> T syncW(String blockName, long timeout, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, timeout, false, syncBlock);
}
public static <T> T syncW(String blockName, SyncSupplierBlock<T> syncBlock) {
return sync(blockName, 0, false, syncBlock);
}
public static void syncW(String blockName, boolean fair, SyncBlock syncBlock) {
sync(blockName, 0, fair, syncBlock);
}
public static void syncW(String blockName, long timeout, SyncBlock syncBlock) {
sync(blockName, timeout, false, syncBlock);
}
public static void syncW(String blockName, SyncBlock syncBlock) {
sync(blockName, 0, false, syncBlock);
}
public interface SyncBlock {
void block();
}
public interface SyncSupplierBlock<T> {
T block();
}
private static class LockWrapper {
private static final Map<String, LockWrapper> LOCKS = new ConcurrentHashMap<>();
private final String blockName;
private final ReentrantLock lock;
private final ReentrantReadWriteLock rwlock;
private int count;
private LockWrapper(String blockName, boolean fair) {
this(blockName, fair, false);
}
private LockWrapper(String blockName, boolean fair, boolean rw) {
if (rw) {
rwlock = new ReentrantReadWriteLock(fair);
lock = null;
} else {
lock = new ReentrantLock(fair);
rwlock = null;
}
this.count = 0;
this.blockName = blockName;
}
public static LockWrapper getRLock(String blockName, long timeout, boolean fair) {
LockWrapper lockWrapper = LOCKS.compute(blockName, (k, v) -> {
LockWrapper wrapper = Objects.requireNonNullElseGet(v, () -> new LockWrapper(blockName, fair, true));
// 增加引用数
wrapper.count++;
return wrapper;
});
if (timeout > 0) {
if (!lockWrapper.tryRLock(timeout)) {
// 加锁失败,回退引用数
LOCKS.compute(blockName, (k, v) -> {
if (v == null) return null;
v.count--;
return v.count > 0 ? v : null;
});
return null;
} else {
return lockWrapper;
}
} else {
lockWrapper.rlock();
return lockWrapper;
}
}
public static LockWrapper getWLock(String blockName, long timeout, boolean fair) {
LockWrapper lockWrapper = LOCKS.compute(blockName, (k, v) -> {
LockWrapper wrapper = Objects.requireNonNullElseGet(v, () -> new LockWrapper(blockName, fair, true));
// 增加引用数
wrapper.count++;
return wrapper;
});
if (timeout > 0) {
if (!lockWrapper.tryWLock(timeout)) {
// 加锁失败,回退引用数
LOCKS.compute(blockName, (k, v) -> {
if (v == null) return null;
v.count--;
return v.count > 0 ? v : null;
});
return null;
} else {
return lockWrapper;
}
} else {
lockWrapper.wlock();
return lockWrapper;
}
}
public static LockWrapper get(String blockName, long timeout, boolean fair) {
LockWrapper lockWrapper = LOCKS.compute(blockName, (k, v) -> {
LockWrapper wrapper = Objects.requireNonNullElseGet(v, () -> new LockWrapper(blockName, fair));
// 增加引用数
wrapper.count++;
return wrapper;
});
if (timeout > 0) {
if (!lockWrapper.tryLock(timeout)) {
// 加锁失败,回退引用数
LOCKS.compute(blockName, (k, v) -> {
if (v == null) return null;
v.count--;
return v.count > 0 ? v : null;
});
return null;
} else {
return lockWrapper;
}
} else {
lockWrapper.lock();
return lockWrapper;
}
}
private void rlock() {
rwlock.readLock().lock();
}
private boolean tryRLock(long timeout) {
try {
return rwlock.readLock().tryLock(timeout, TimeUnit.MICROSECONDS);
} catch (InterruptedException e) {
return false;
}
}
private void wlock() {
rwlock.writeLock().lock();
}
private boolean tryWLock(long timeout) {
try {
return rwlock.writeLock().tryLock(timeout, TimeUnit.MICROSECONDS);
} catch (InterruptedException e) {
return false;
}
}
private void lock() {
lock.lock();
}
private boolean tryLock(long timeout) {
try {
return lock.tryLock(timeout, TimeUnit.MICROSECONDS);
} catch (InterruptedException e) {
return false;
}
}
public void unrlock() {
// 正常解锁
rwlock.readLock().unlock();
LOCKS.compute(blockName, (k, v) -> {
if (v == null) return null;
v.count--;
return v.count > 0 ? v : null;
});
}
public void unwlock() {
// 正常解锁
rwlock.writeLock().unlock();
LOCKS.compute(blockName, (k, v) -> {
if (v == null) return null;
v.count--;
return v.count > 0 ? v : null;
});
}
private void unlock() {
lock.unlock();
LOCKS.compute(blockName, (k, v) -> {
if (v == null) return null;
v.count--;
return v.count > 0 ? v : null;
});
}
}
}

View File

@ -0,0 +1,63 @@
package com.njzscloud.common.core.thread;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@SuppressWarnings("unchecked")
public class ThreadContext {
private static final Map<Thread, Map<String, Object>> CONTEXT_CACHE = new ConcurrentHashMap<>();
/**
*
*
* @param key
* @param defaultValue
*/
public static <T> T get(String key, T defaultValue) {
return (T) CONTEXT_CACHE
.computeIfAbsent(Thread.currentThread(), k -> new ConcurrentHashMap<>())
.computeIfAbsent(key, k -> defaultValue);
}
/**
*
*
* @param key
*/
public static <T> T get(String key) {
return (T) CONTEXT_CACHE
.computeIfAbsent(Thread.currentThread(), k -> new ConcurrentHashMap<>())
.get(key);
}
/**
*
*
* @param key
* @param value
*/
public static <T> T set(String key, T value) {
return (T) CONTEXT_CACHE
.computeIfAbsent(Thread.currentThread(), k -> new ConcurrentHashMap<>())
.put(key, value);
}
/**
*
*
* @param key
*/
public static void remove(String key) {
CONTEXT_CACHE
.computeIfAbsent(Thread.currentThread(), k -> new ConcurrentHashMap<>())
.remove(key);
}
/**
* 线
*/
public static void clear() {
CONTEXT_CACHE.remove(Thread.currentThread());
}
}

View File

@ -0,0 +1,108 @@
package com.njzscloud.common.core.thread;
import cn.hutool.core.lang.Assert;
import com.njzscloud.common.core.ex.Exceptions;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
import java.util.function.Supplier;
@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("Default");
}
}
}
return THREAD_POOL;
}
/**
* 线线
*
* @param poolName 线
* @param poolSize 线
* @param bufferSize
*/
public static ThreadPoolExecutor createThreadPool(String poolName, int poolSize, int bufferSize) {
return createPlatformThreadPool(poolName, 0, poolSize, 60, bufferSize, Integer.MAX_VALUE, null);
}
/**
* 线线
*
* @param poolName 线
*/
public static ThreadPoolExecutor createThreadPool(String poolName) {
return createVirtualThreadPool(poolName, null);
}
/**
* 线线
*
* @param poolName 线
* @param rejectedExecutionHandler
*/
public static ThreadPoolExecutor createVirtualThreadPool(String poolName, RejectedExecutionHandler rejectedExecutionHandler) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(),
new DefaultThreadFactory(true, poolName),
rejectedExecutionHandler == null ? new ThreadPoolExecutor.AbortPolicy() : rejectedExecutionHandler);
threadPoolExecutor.allowCoreThreadTimeOut(true);
return threadPoolExecutor;
}
/**
*
* @param poolName 线
* @param corePoolSize 线
* @param maxPoolSize 线
* @param keepAliveSeconds
* @param windowCapacity
* @param standbyCapacity
* @param rejectedExecutionHandler
*/
public static ThreadPoolExecutor createPlatformThreadPool(String poolName,
int corePoolSize, int maxPoolSize, long keepAliveSeconds,
int windowCapacity, int standbyCapacity,
RejectedExecutionHandler rejectedExecutionHandler
) {
Assert.isTrue(windowCapacity > 0 && standbyCapacity > 0 && windowCapacity <= standbyCapacity, () -> Exceptions.error("队列大小设置错误"));
Q<Runnable> q = new Q<>(windowCapacity, standbyCapacity);
RejectedExecutionHandler abortPolicy = rejectedExecutionHandler == null ? new ThreadPoolExecutor.AbortPolicy() : rejectedExecutionHandler;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize, maxPoolSize, keepAliveSeconds <= 0 ? 60 : keepAliveSeconds, TimeUnit.SECONDS,
q,
new DefaultThreadFactory(false, poolName),
(r, p) -> {
if (!q.offerStandby(r)) {
log.debug("任务队列已满");
abortPolicy.rejectedExecution(r, p);
} else {
log.debug("任务已加入备用队列");
}
});
threadPoolExecutor.allowCoreThreadTimeOut(corePoolSize == 0);
return threadPoolExecutor;
}
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return CompletableFuture.runAsync(runnable, defaultThreadPool());
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return CompletableFuture.supplyAsync(supplier, defaultThreadPool());
}
}

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,118 @@
package com.njzscloud.common.core.tuple;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 2
*
* @param <_0>
* @param <_1>
*/
public class Tuple2<_0, _1> {
/**
*
*/
@JsonIgnore
public final int size = 2;
/**
* 0
*/
@JsonIgnore
protected final _0 _0_;
/**
* 1
*/
@JsonIgnore
protected final _1 _1_;
/**
*
*
* @param _0_ 0
* @param _1_ 1
*/
public 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> of(__0 _0_, __1 _1_) {
return new Tuple2<>(_0_, _1_);
}
/**
* 0
*/
@JsonProperty("0")
public _0 get_0() {
return _0_;
}
/**
* 1
*/
@JsonProperty("1")
public _1 get_1() {
return _1_;
}
/**
* <p></p>
* <p> null</p>
*
* @param index
* @return T
*/
@SuppressWarnings("unchecked")
public <T> T get(int index) {
return switch (index) {
case 0 -> (T) this._0_;
case 1 -> (T) this._1_;
default -> null;
};
}
/**
* 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,86 @@
package com.njzscloud.common.core.tuple;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* 3
*
* @param <_0>
* @param <_1>
* @param <_2>
*/
public class Tuple3<_0, _1, _2> extends Tuple2<_0, _1> {
/**
*
*/
@JsonIgnore
public final int size = 3;
/**
* 2
*/
@JsonIgnore
protected final _2 _2_;
/**
*
*
* @param _0_ 0
* @param _1_ 1
* @param _2_ 2
*/
public 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> of(__0 _0_, __1 _1_, __2 _2_) {
return new Tuple3<>(_0_, _1_, _2_);
}
/**
* 2
*/
@JsonProperty("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) {
return switch (index) {
case 0 -> (T) this._0_;
case 1 -> (T) this._1_;
case 2 -> (T) this._2_;
default -> null;
};
}
/**
*
*
* @return Object[]
*/
@Override
public Object[] toArray() {
return new Object[]{this._0_, this._1_, this._2_};
}
}

View File

@ -0,0 +1,88 @@
package com.njzscloud.common.core.tuple;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* 4
*
* @param <_0>
* @param <_1>
* @param <_2>
* @param <_3>
*/
public class Tuple4<_0, _1, _2, _3> extends Tuple3<_0, _1, _2> {
/**
*
*/
@JsonIgnore
public final int size = 4;
/**
* 3
*/
protected final _3 _3_;
/**
*
*
* @param _0_ 0
* @param _1_ 1
* @param _2_ 2
* @param _3_ 3
*/
public 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> of(__0 _0_, __1 _1_, __2 _2_, __3 _3_) {
return new Tuple4<>(_0_, _1_, _2_, _3_);
}
/**
* 3
*/
@JsonProperty("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) {
return switch (index) {
case 0 -> (T) this._0_;
case 1 -> (T) this._1_;
case 2 -> (T) this._2_;
case 3 -> (T) this._3_;
default -> null;
};
}
/**
*
*
* @return Object[]
*/
@Override
public Object[] toArray() {
return new Object[]{this._0_, this._1_, this._2_, this._3_};
}
}

View File

@ -0,0 +1,12 @@
package com.njzscloud.common.core.utils;
public class BCD {
public static String bcdToStr(byte[] bcd) {
StringBuilder sb = new StringBuilder();
for (byte b : bcd) {
sb.append(String.format("%02x", b));
}
// 移除前面的0
return sb.toString().replaceFirst("^0+", "");
}
}

View File

@ -0,0 +1,80 @@
package com.njzscloud.common.core.utils;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.tuple.Tuple2;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
public class CallerUtil {
/**
*
*
* @return null
*/
public static Caller getCaller(int depth) {
/*
getStackTrace
getCaller
getCaller
getCaller
*/
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
int i = 2 + depth;
if (stackTrace.length < i + 1) {
// 调用栈过短(理论上不可能,除非在 JVM 启动初期调用)
return Caller.defaultCaller;
}
return resolveCaller(stackTrace, depth);
}
public static Caller resolveCaller(StackTraceElement[] stackTrace, int depth) {
StackTraceElement callerElement = stackTrace[depth];
// 解析栈元素的信息
String className = callerElement.getClassName(); // 调用者类名(全限定名)
String methodName = callerElement.getMethodName();// 调用者方法名
int lineNumber = callerElement.getLineNumber(); // 调用者代码行号(-1 表示未知)
String fileName = callerElement.getFileName(); // 调用者所在文件名(可能为 null
return new Caller(className, methodName, lineNumber, fileName);
}
public static Tuple2<String, Caller> printStackTrace(Throwable throwable) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (PrintStream printStream = new PrintStream(outputStream)) {
// 3. 调用 printStackTrace输出到 PrintStream实际写入 ByteArrayOutputStream
throwable.printStackTrace(printStream);
// 4. 将字节流转为字符串(默认 UTF-8 编码,兼容所有字符)
String string = outputStream.toString(StandardCharsets.UTF_8);
return Tuple2.of(string, resolveCaller(throwable.getStackTrace(), 0));
} catch (Exception e) {
// 极端情况下(如编码异常),返回简化信息
return Tuple2.of(throwable.getMessage(), resolveCaller(throwable.getStackTrace(), 0));
}
}
/**
*
*
* @param className Getter com.example.Test
* @param methodName main
* @param lineNumber
* @param fileName Test.java
*/
public record Caller(String className, String methodName, int lineNumber, String fileName) {
public static Caller defaultCaller = new Caller();
public Caller() {
this("", "", -1, "");
}
@Override
public String toString() {
return StrUtil.format("调用者信息, 类名:{}、方法名:{}、行号:{}、文件名:{}",
className, methodName, lineNumber, fileName);
}
}
}

View File

@ -0,0 +1,104 @@
package com.njzscloud.common.core.utils;
import com.njzscloud.common.core.tuple.Tuple2;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* WGS-84GCJ-02()
* WGS-84GPS
* GCJ-02使
*/
public class CoordinateConverter {
// 长半径
private static final double PI = 3.1415926535897932384626;
private static final double A = 6378245.0;
// 扁率
private static final double EE = 0.00669342162296594323;
/**
*
*
* @param lat
* @param lon
* @return
*/
public static boolean outOfChina(double lat, double lon) {
if (lon < 72.004 || lon > 137.8347) {
return true;
}
return lat < 0.8293 || lat > 55.8271;
}
/**
*
*
* @param x
* @param y
* @return
*/
private static double transformLat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
/**
*
*
* @param x
* @param y
* @return
*/
private static double transformLon(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
/**
* WGS-84GCJ-02
*
* @param wgsLat WGS-84
* @param wgsLon WGS-84
* @return [, ]
*/
public static Tuple2<Double, Double> wgs84ToGcj02(double wgsLat, double wgsLon) {
// 如果不在中国境内,直接返回原坐标
if (outOfChina(wgsLat, wgsLon)) {
return Tuple2.of(wgsLat, wgsLon);
}
double dLat = transformLat(wgsLon - 105.0, wgsLat - 35.0);
double dLon = transformLon(wgsLon - 105.0, wgsLat - 35.0);
double radLat = wgsLat / 180.0 * PI;
double magic = Math.sin(radLat);
magic = 1 - EE * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
dLon = (dLon * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);
// 保留6位小数约10厘米精度
double gcjLat = round(wgsLat + dLat, 6);
double gcjLon = round(wgsLon + dLon, 6);
return Tuple2.of(gcjLat, gcjLon);
}
/**
*
*
* @param value
* @param scale
* @return
*/
private static double round(double value, int scale) {
return new BigDecimal(value).setScale(scale, RoundingMode.HALF_UP).doubleValue();
}
}

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,164 @@
package com.njzscloud.common.core.utils;
import com.njzscloud.common.core.ex.Exceptions;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class ImgUtil {
// 默认字体(可根据需求调整)
private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 24);
// 默认水印颜色(半透明黑色)
private static final Color DEFAULT_COLOR = Color.WHITE;
// 行间距字体大小的1.2倍)
private static final float LINE_SPACING_RATIO = 1.2f;
// 左右边距(避免文本贴边)
private static final int PADDING_HORIZONTAL = 10;
// 上下边距
private static final int PADDING_VERTICAL = 10;
/**
*
*
* @param sourceImageIn
* @param type jpg png
* @param txt
*/
public static byte[] addTxtWatermark(InputStream sourceImageIn, String type, String txt) {
try {
BufferedImage sourceImage = ImageIO.read(sourceImageIn);
if (sourceImage == null) {
throw Exceptions.error("图片读取失败");
}
BufferedImage targetImage = new BufferedImage(
sourceImage.getWidth(),
sourceImage.getHeight(),
type.equalsIgnoreCase("png") ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB
);
Graphics2D g2d = targetImage.createGraphics();
g2d.drawImage(sourceImage, 0, 0, null);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
g2d.setFont(DEFAULT_FONT);
g2d.setColor(DEFAULT_COLOR);
int maxWatermarkWidth = (sourceImage.getWidth() / 2) - 2 * PADDING_HORIZONTAL;
if (maxWatermarkWidth <= 0) {
throw Exceptions.error("图片宽度过小,无法添加水印");
}
int lineHeight = g2d.getFontMetrics().getHeight();
String[] txts = txt.split("\n");
int currentY = PADDING_VERTICAL + g2d.getFontMetrics().getAscent();
int maxHeight = targetImage.getHeight();
for (String s : txts) {
List<String> lines = wrapText(s, DEFAULT_FONT, maxWatermarkWidth);
for (String line : lines) {
g2d.drawString(line, PADDING_HORIZONTAL, currentY);
currentY += (int) (lineHeight * LINE_SPACING_RATIO);
if (currentY >= maxHeight) {
throw Exceptions.error("图片高度不足,无法添加水印");
}
}
}
g2d.dispose();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
boolean write = ImageIO.write(targetImage, type, outputStream);
if (write) {
return outputStream.toByteArray();
} else {
return new byte[0];
}
} catch (Exception e) {
throw Exceptions.error(e, "水印添加失败");
}
}
/**
*
*
* @param text
* @param font
* @param maxWidth
* @return
*/
private static List<String> wrapText(String text, Font font, int maxWidth) {
List<String> lines = new ArrayList<>();
if (text == null || text.isBlank()) {
return lines;
}
Graphics2D g2d = (Graphics2D) new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics();
g2d.setFont(font);
FontMetrics metrics = g2d.getFontMetrics();
g2d.dispose();
String[] words = text.split(" ");
StringBuilder currentLine = new StringBuilder();
for (String word : words) {
String tempLine = currentLine.isEmpty() ? word : currentLine + " " + word;
Rectangle2D bounds = metrics.getStringBounds(tempLine, null);
if (bounds.getWidth() <= maxWidth) {
// 当前行加上单词后未超过最大宽度,继续添加
currentLine = new StringBuilder(tempLine);
} else {
// 超过最大宽度,保存当前行,开始新行
if (!currentLine.isEmpty()) {
lines.add(currentLine.toString());
currentLine = new StringBuilder(word);
} else {
// 单个单词超过最大宽度,强制截断(特殊情况处理)
lines.add(truncateWord(word, metrics, maxWidth));
currentLine = new StringBuilder();
}
}
}
// 添加最后一行
if (!currentLine.isEmpty()) {
lines.add(currentLine.toString());
}
return lines;
}
/**
*
*
* @param word
* @param metrics
* @param maxWidth
* @return
*/
private static String truncateWord(String word, FontMetrics metrics, int maxWidth) {
StringBuilder truncated = new StringBuilder();
for (char c : word.toCharArray()) {
String temp = truncated + String.valueOf(c);
if (metrics.getStringBounds(temp, null).getWidth() <= maxWidth) {
truncated.append(c);
} else {
break;
}
}
return truncated.toString();
}
}

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

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,113 @@
package com.njzscloud.common.core.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StrUtil {
// 匹配非特殊字符的正则表达式,对应 TS 中的 [^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+
private static final Pattern SPLIT_CHAR_PATTERN = Pattern.compile("[^\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\x7f]+");
// 匹配大写字母的正则表达式,用于分割单词
private static final Pattern UPPER_CASE_PATTERN = Pattern.compile("([A-Z])");
/**
*
*/
private static boolean isBlank(String str) {
return str == null || str.trim().isEmpty();
}
/**
*
*/
private static String capitalize(String word) {
if (isBlank(word)) {
return word;
}
return Character.toUpperCase(word.charAt(0)) +
(word.length() > 1 ? word.substring(1).toLowerCase() : "");
}
/**
*
*/
public static String processWords(String str, WordJoiner joiner) {
if (isBlank(str)) {
return "";
}
// 处理字符串:在大写字母前添加分隔符,然后转为小写
String processed = UPPER_CASE_PATTERN.matcher(str).replaceAll(" $1").toLowerCase();
// 提取匹配的单词
Matcher matcher = SPLIT_CHAR_PATTERN.matcher(processed);
List<String> words = new ArrayList<>();
while (matcher.find()) {
words.add(matcher.group().trim());
}
// 使用 joiner 连接单词
StringBuilder result = new StringBuilder();
for (int i = 0; i < words.size(); i++) {
result.append(joiner.join(words.get(i), i));
}
return result.toString();
}
/**
* (camelCase)
*/
public static String camelCase(String str) {
return processWords(str, (word, index) -> {
if (index == 0) {
return word;
} else {
return capitalize(word);
}
});
}
/**
* (PascalCase)
*/
public static String pascalCase(String str) {
String camel = camelCase(str);
return capitalize(camel);
}
/**
* 线 (snake_case)
*/
public static String snakeCase(String str) {
return processWords(str, (word, index) -> {
if (index == 0) {
return word;
} else {
return "_" + word;
}
});
}
/**
* (kebab-case)
*/
public static String kebabCase(String str) {
return processWords(str, (word, index) -> {
if (index == 0) {
return word;
} else {
return "-" + word;
}
});
}
/**
*
*/
@FunctionalInterface
public interface WordJoiner {
String join(String word, int index);
}
}

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>21</maven.compiler.source>
<maven.compiler.target>21</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>
<!-- 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.tuple.Tuple2;
import com.njzscloud.common.core.utils.Mime;
import jakarta.mail.util.ByteArrayDataSource;
import lombok.Getter;
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.of(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 jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.util.ByteArrayDataSource;
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 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 ? "邮件服务器认证失败" : "邮件发送失败");
}
}
}

View File

@ -0,0 +1,46 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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-gen</artifactId>
<packaging>jar</packaging>
<name>njzscloud-common-gen</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common-mvc</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common-mp</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.19.2.RELEASE</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,121 @@
package com.njzscloud.common.gen;
import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.njzscloud.common.core.utils.Mime;
import com.njzscloud.common.core.utils.R;
import com.njzscloud.common.mp.support.PageParam;
import com.njzscloud.common.mp.support.PageResult;
import com.njzscloud.common.mvc.util.ServletUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Map;
/**
*
*/
@Slf4j
@RestController
@RequestMapping("/tpl")
@RequiredArgsConstructor
public class TplController {
private final TplService tplService;
/**
*
*/
@PostMapping("/add")
public R<?> add(@RequestBody TplEntity tplEntity) {
tplService.add(tplEntity);
return R.success();
}
/**
*
*/
@PostMapping("/modify")
public R<?> modify(@RequestBody TplEntity tplEntity) {
tplService.modify(tplEntity);
return R.success();
}
/**
*
*/
@PostMapping("/del")
public R<?> del(@RequestBody List<Long> ids) {
tplService.del(ids);
return R.success();
}
@PostMapping("/download")
public void download(@RequestParam("lang") String lang,
@RequestParam("tableName") String tableName,
@RequestBody(required = false) Map<String, Object> data,
HttpServletResponse response) {
ByteArrayOutputStream out = tplService.download(lang, tableName, data);
ServletUtil.download(response, out.toByteArray(), Mime.ZIP, DateTime.now() + ".zip");
}
@PostMapping("/preview")
public R<?> preview(@RequestParam("lang") String lang,
@RequestParam("tableName") String tableName,
@RequestBody(required = false) Map<String, Object> data) {
Map<String, Map<String, String>> map = tplService.preview(lang, tableName, data);
return R.success(map);
}
/**
*
*/
@GetMapping("/table/paging")
public R<PageResult<Map<String, Object>>> tablePaging(PageParam pageParam,
@RequestParam(value = "tableSchema", required = false) String tableSchema,
@RequestParam(value = "tableName", required = false) String tableName
) {
return R.success(tplService.tablePaging(pageParam, tableSchema, tableName));
}
/**
*
*/
@GetMapping("/list_all")
public R<?> listAll(@RequestParam("lang") String lang) {
return R.success(tplService
.list(Wrappers.lambdaQuery(TplEntity.class)
.eq(TplEntity::getLang, lang)
.orderByAsc(TplEntity::getTplName)));
}
/**
*
*/
@GetMapping("/detail")
public R<TplEntity> detail(@RequestParam("id") Long id) {
return R.success(tplService.detail(id));
}
/**
*
*/
@GetMapping("/paging")
public R<PageResult<TplEntity>> paging(PageParam pageParam, TplEntity tplEntity) {
return R.success(tplService.paging(pageParam, tplEntity));
}
}

View File

@ -0,0 +1,51 @@
package com.njzscloud.common.gen;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.njzscloud.common.gen.support.Tpl;
import com.njzscloud.common.mp.support.handler.j.JsonTypeHandler;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Map;
/**
*
*/
@Getter
@Setter
@ToString
@Accessors(chain = true)
@TableName(value = "sys_tpl", autoResultMap = true)
public class TplEntity {
/**
* Id
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
*
*/
private String tplName;
private String lang;
/**
*
*/
@TableField(typeHandler = JsonTypeHandler.class)
private Tpl tpl;
/**
*
*/
@TableField(typeHandler = JsonTypeHandler.class)
private Map<String, Object> modelData;
}

View File

@ -0,0 +1,23 @@
package com.njzscloud.common.gen;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Map;
/**
*
*/
@Mapper
public interface TplMapper extends BaseMapper<TplEntity> {
@Select("SELECT TABLE_SCHEMA tableSchema, TABLE_NAME tableName, TABLE_COMMENT tableComment\n" +
"FROM information_schema.TABLES\n" +
"${ew.customSqlSegment}")
IPage<Map<String, Object>> tablePaging(Page<Object> page, @Param("ew") QueryWrapper<Object> ew);
}

View File

@ -0,0 +1,186 @@
package com.njzscloud.common.gen;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.core.tuple.Tuple3;
import com.njzscloud.common.gen.support.DbMetaData;
import com.njzscloud.common.gen.support.Generator;
import com.njzscloud.common.gen.support.TemplateEngine;
import com.njzscloud.common.gen.support.Tpl;
import com.njzscloud.common.mp.support.PageParam;
import com.njzscloud.common.mp.support.PageResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
*
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TplService extends ServiceImpl<TplMapper, TplEntity> implements IService<TplEntity> {
/**
*
*
* @param tplEntity
*/
@Transactional(rollbackFor = Exception.class)
public void add(TplEntity tplEntity) {
String tplName = tplEntity.getTplName();
Assert.notBlank(tplName, () -> Exceptions.clierr("模板名称不能为空"));
boolean exists = this.exists(Wrappers.lambdaQuery(TplEntity.class).eq(TplEntity::getTplName, tplName));
Assert.isFalse(exists, () -> Exceptions.clierr("模板:{} 已存在", tplName));
Tpl tpl = tplEntity.getTpl();
if (tpl == null) {
tplEntity.setTpl(new Tpl());
}
Map<String, Object> modelData = tplEntity.getModelData();
if (modelData == null) {
tplEntity.setModelData(MapUtil.empty());
}
this.save(tplEntity);
}
/**
*
*
* @param tplEntity
*/
@Transactional(rollbackFor = Exception.class)
public void modify(TplEntity tplEntity) {
String tplName = tplEntity.getTplName();
Assert.notBlank(tplName, () -> Exceptions.clierr("模板名称不能为空"));
Long id = tplEntity.getId();
boolean exists = this.exists(Wrappers.lambdaQuery(TplEntity.class)
.eq(TplEntity::getTplName, tplName)
.ne(TplEntity::getId, id)
);
Assert.isFalse(exists, () -> Exceptions.clierr("模板:{} 已存在", tplName));
TplEntity oldData = this.getById(id);
Assert.notNull(oldData, () -> Exceptions.clierr("未找到要修改数据"));
TemplateEngine.rmEngine(oldData.getTplName());
this.updateById(tplEntity);
}
/**
*
*
* @param ids Ids
*/
@Transactional(rollbackFor = Exception.class)
public void del(List<Long> ids) {
this.removeBatchByIds(ids);
}
/**
*
*
* @param id Id
* @return SysTplEntity
*/
public TplEntity detail(Long id) {
return this.getById(id);
}
/**
*
*
* @param tplEntity
* @param pageParam
* @return PageResult&lt;SysTplEntity&gt;
*/
public PageResult<TplEntity> paging(PageParam pageParam, TplEntity tplEntity) {
return PageResult.of(this.page(pageParam.toPage(), Wrappers.query(tplEntity)));
}
private final DbMetaData dbMetaData;
public PageResult<Map<String, Object>> tablePaging(PageParam pageParam, String tableSchema, String tableName) {
pageParam.setOrders("TABLE_NAME");
if (StrUtil.isBlank(tableSchema)) {
tableSchema = dbMetaData.getDatabaseName();
}
Assert.notBlank(tableSchema, () -> Exceptions.exception("数据库名称获取失败"));
return PageResult.of(baseMapper.tablePaging(pageParam.toPage(), Wrappers.query()
.eq("TABLE_SCHEMA", tableSchema)
.like(StrUtil.isNotBlank(tableName), "TABLE_NAME", tableName)
));
}
public Map<String, Map<String, String>> preview(String lang, String tableName, Map<String, Object> data) {
Map<String, Map<String, String>> map = MapUtil.newHashMap();
List<String> tplNames = this.list(Wrappers.lambdaQuery(TplEntity.class)
.eq(TplEntity::getLang, lang))
.stream()
.map(TplEntity::getTplName)
.toList();
for (String tplName : tplNames) {
Tuple2<String, String> res = generate(tplName, tableName, data);
map.put(tplName, MapUtil.<String, String>builder()
.put("path", res.get_0())
.put("content", res.get_1())
.build());
}
return map;
}
public ByteArrayOutputStream download(String lang, String tableName, Map<String, Object> data) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
List<String> tplNames = this.list(Wrappers.lambdaQuery(TplEntity.class)
.eq(TplEntity::getLang, lang))
.stream()
.map(TplEntity::getTplName)
.toList();
if (tplNames.isEmpty()) {
return out;
}
int size = tplNames.size();
String[] paths = new String[size];
InputStream[] ins = new InputStream[size];
for (int i = 0; i < size; i++) {
String tplName = tplNames.get(i);
Tuple2<String, String> res = generate(tplName, tableName, data);
paths[i] = res.get_0();
ins[i] = new ByteArrayInputStream(res.get_1().getBytes());
}
ZipUtil.zip(out, paths, ins);
return out;
}
private Tuple2<String, String> generate(String tplName, String tableName, Map<String, Object> data) {
Tuple3<String, String, String> res = Generator.generate(tplName, tableName, data);
String res0 = res.get_0();
String res1 = res.get_1();
String res2 = res.get_2();
StringBuilder pathBuilder = new StringBuilder();
if (StrUtil.isNotBlank(res0)) {
pathBuilder.append(res0.trim());
if (!res0.endsWith("/")) pathBuilder.append("/");
}
if (StrUtil.isNotBlank(res1)) pathBuilder.append(res1.trim());
else pathBuilder.append(IdUtil.fastSimpleUUID()).append(".txt");
return Tuple2.of(pathBuilder.toString(), res2);
}
}

View File

@ -0,0 +1,31 @@
package com.njzscloud.common.gen.config;
import com.njzscloud.common.gen.TplController;
import com.njzscloud.common.gen.TplService;
import com.njzscloud.common.gen.support.DbMetaData;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
@MapperScan("com.njzscloud.common.gen")
public class GenAutoConfiguration {
@Bean
public TplService sysTplService(DbMetaData dbMetaData) {
return new TplService(dbMetaData);
}
@Bean
public TplController sysTplController(TplService tplService) {
return new TplController(tplService);
}
@Bean
public DbMetaData dbMetaData(DataSource dataSource) {
return new DbMetaData(dataSource);
}
}

View File

@ -0,0 +1,24 @@
package com.njzscloud.common.gen.contant;
import com.njzscloud.common.core.ienum.DictStr;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* tpl_category
*
*/
@Getter
@RequiredArgsConstructor
public enum TplCategory implements DictStr {
Controller("Controller", "Controller"),
Service("Service", "Service"),
Mapper("Mapper", "Mapper"),
MapperXml("MapperXml", "MapperXml"),
Entity("Entity", "Entity"),
;
private final String val;
private final String txt;
}

View File

@ -0,0 +1,67 @@
package com.njzscloud.common.gen.support;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import org.beetl.core.Context;
import org.beetl.core.Function;
import java.util.Map;
public final class Btl {
public static final Function toCamelCase = (Object[] paras, Context ctx) -> StrUtil.toCamelCase(paras[0].toString());
public static final Function toDashCase = (Object[] paras, Context ctx) -> StrUtil.toSymbolCase(paras[0].toString(), '-');
public static final Function upperFirst = (Object[] paras, Context ctx) -> StrUtil.upperFirst(paras[0].toString());
public static final Function isBlank = (Object[] paras, Context ctx) -> StrUtil.isBlank(paras[0].toString());
public static final Function subAfter = (Object[] paras, Context ctx) -> StrUtil.subAfter(paras[0].toString(), paras[1].toString(), false);
public static final Function javaType = new Function() {
private final Map<String, Map<String, String>> map = MapUtil.<String, Map<String, String>>builder()
.put("DEFAULT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "").build())
.put("VARCHAR", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "String").build())
.put("CHAR", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "String").build())
.put("LONGTEXT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "String").build())
.put("TEXT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "String").build())
.put("BIT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "Boolean").build())
.put("TINYINT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "Integer").build())
.put("SMALLINT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "Integer").build())
.put("MEDIUMINT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "Integer").build())
.put("INT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "Integer").build())
.put("BIGINT", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "Long").build())
.put("DOUBLE", MapUtil.<String, String>builder().put("importStatement", "").put("dataType", "Double").build())
.put("DECIMAL", MapUtil.<String, String>builder().put("importStatement", "import java.math.BigDecimal;").put("dataType", "BigDecimal").build())
.put("DATE", MapUtil.<String, String>builder().put("importStatement", "import java.time.LocalDate;").put("dataType", "LocalDate").build())
.put("TIME", MapUtil.<String, String>builder().put("importStatement", "import java.time.LocalTime;").put("dataType", "LocalTime").build())
.put("DATETIME", MapUtil.<String, String>builder().put("importStatement", "import java.time.LocalDateTime;").put("dataType", "LocalDateTime").build())
.build();
@Override
public Object call(Object[] paras, Context ctx) {
return map.getOrDefault(paras[0].toString().toUpperCase(), map.get("DEFAULT"));
}
};
public static final Function tsType = new Function() {
private final Map<String, Map<String, String>> map = MapUtil.<String, Map<String, String>>builder()
.put("DEFAULT", MapUtil.<String, String>builder().put("dataType", "").build())
.put("VARCHAR", MapUtil.<String, String>builder().put("dataType", "string").build())
.put("CHAR", MapUtil.<String, String>builder().put("dataType", "string").build())
.put("LONGTEXT", MapUtil.<String, String>builder().put("dataType", "string").build())
.put("TEXT", MapUtil.<String, String>builder().put("dataType", "string").build())
.put("BIT", MapUtil.<String, String>builder().put("dataType", "boolean").build())
.put("TINYINT", MapUtil.<String, String>builder().put("dataType", "number").build())
.put("SMALLINT", MapUtil.<String, String>builder().put("dataType", "number").build())
.put("MEDIUMINT", MapUtil.<String, String>builder().put("dataType", "number").build())
.put("INT", MapUtil.<String, String>builder().put("dataType", "number").build())
.put("BIGINT", MapUtil.<String, String>builder().put("dataType", "string").build())
.put("DOUBLE", MapUtil.<String, String>builder().put("dataType", "number").build())
.put("DECIMAL", MapUtil.<String, String>builder().put("dataType", "string").build())
.put("DATE", MapUtil.<String, String>builder().put("dataType", "string").build())
.put("TIME", MapUtil.<String, String>builder().put("dataType", "string").build())
.put("DATETIME", MapUtil.<String, String>builder().put("dataType", "string").build())
.build();
@Override
public Object call(Object[] paras, Context ctx) {
return map.getOrDefault(paras[0].toString().toUpperCase(), map.get("DEFAULT"));
}
};
}

View File

@ -0,0 +1,83 @@
package com.njzscloud.common.gen.support;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Slf4j
@RequiredArgsConstructor
public class DbMetaData {
private final DataSource dataSource;
public String getDatabaseName() {
try (Connection conn = dataSource.getConnection()) {
return conn.getCatalog();
} catch (Exception e) {
log.error("获取数据库名称失败", e);
return null;
}
}
public List<Map<String, Object>> getTableMetaData(String tableName) {
try (Connection connection = dataSource.getConnection()) {
return getTables(connection, tableName);
} catch (Exception e) {
log.error("获取数据库元数据失败", e);
return Collections.emptyList();
}
}
private List<Map<String, Object>> getTableColumns(Connection conn, String tableName) throws Exception {
List<Map<String, Object>> columns = new ArrayList<>();
DatabaseMetaData metaData = conn.getMetaData();
try (ResultSet columnsData = metaData.getColumns(null, null, tableName, "%")) {
while (columnsData.next()) {
columns.add(MapUtil.<String, Object>builder()
.put("name", columnsData.getString("COLUMN_NAME"))
.put("dataType", columnsData.getString("TYPE_NAME"))
.put("size", columnsData.getInt("COLUMN_SIZE"))
.put("nullable", columnsData.getInt("NULLABLE") == DatabaseMetaData.columnNullable)
.put("defaultValue", columnsData.getString("COLUMN_DEF"))
.put("comment", columnsData.getString("REMARKS"))
.build()
);
}
}
try (ResultSet primaryKeyData = metaData.getPrimaryKeys(null, null, tableName)) {
while (primaryKeyData.next()) {
String primaryKeyColumn = primaryKeyData.getString("COLUMN_NAME");
columns.forEach(column -> column.put("primaryKey", column.get("name").equals(primaryKeyColumn)));
}
}
return columns;
}
private List<Map<String, Object>> getTables(Connection conn, String tableName) throws Exception {
List<Map<String, Object>> tableInfos = new ArrayList<>();
DatabaseMetaData metaData = conn.getMetaData();
try (ResultSet tablesData = metaData.getTables(null, null, StrUtil.isNotBlank(tableName) ? tableName : "%", new String[]{"TABLE"})) {
while (tablesData.next()) {
String tableName_ = tablesData.getString("TABLE_NAME");
List<Map<String, Object>> columns = getTableColumns(conn, tableName_);
tableInfos.add(MapUtil.<String, Object>builder()
.put("name", tableName_)
.put("comment", tablesData.getString("REMARKS"))
.put("columns", columns)
.build());
}
}
return tableInfos;
}
}

View File

@ -0,0 +1,56 @@
package com.njzscloud.common.gen.support;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.jackson.Jackson;
import com.njzscloud.common.core.tuple.Tuple3;
import com.njzscloud.common.gen.TplEntity;
import com.njzscloud.common.gen.TplService;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class Generator {
private static final DbMetaData dbMetaData = SpringUtil.getBean(DbMetaData.class);
private static final TplService tplService = SpringUtil.getBean(TplService.class);
private static Tuple3<String, String, String> generate0(String tplName, String tableName, Map<String, Object> data) {
Assert.notBlank(tableName, () -> Exceptions.clierr("未指定表名称"));
data.put("tableName", tableName);
List<Map<String, Object>> table = dbMetaData.getTableMetaData(tableName);
TplEntity tplEntity = tplService.getOne(Wrappers.<TplEntity>lambdaQuery()
.eq(TplEntity::getTplName, tplName));
Tpl tpl = tplEntity.getTpl();
Map<String, Object> modelData = tplEntity.getModelData();
HashMap<String, Object> map = new HashMap<>(modelData);
map.put("table", CollUtil.isNotEmpty(table) ? table.getFirst() : MapUtil.empty());
map.putAll(data);
log.info("数据:{}", Jackson.toJsonStr(map));
TemplateEngine templateEngine = TemplateEngine.getEngine(tplName, tpl);
String content = templateEngine.renderContent(map);
String dir = templateEngine.renderDir(map);
String filename = templateEngine.renderFilename(map);
return Tuple3.of(dir, StrUtil.isBlank(filename) ? tplName + ".txt" : filename, content);
}
/**
*
* @param tplName
* @param tableName
* @param data
* @return 0-->1-->2-->
*/
public static Tuple3<String, String, String> generate(String tplName, String tableName, Map<String, Object> data) {
return generate0(tplName, tableName, data == null ? new HashMap<>() : data);
}
}

View File

@ -0,0 +1,91 @@
package com.njzscloud.common.gen.support;
import cn.hutool.core.map.MapUtil;
import com.njzscloud.common.core.ex.Exceptions;
import lombok.extern.slf4j.Slf4j;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.exception.BeetlException;
import org.beetl.core.exception.ErrorInfo;
import org.beetl.core.resource.MapResourceLoader;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class TemplateEngine {
private static final GroupTemplate GT;
private static final MapResourceLoader LOADER = new MapResourceLoader();
private static final Map<String, TemplateEngine> ENGINE = new ConcurrentHashMap<>();
static {
GroupTemplate getTemplate_;
try {
Configuration cfg = Configuration.defaultConfiguration();
getTemplate_ = new GroupTemplate(LOADER, cfg);
getTemplate_.registerFunction("toCamelCase", Btl.toCamelCase);
getTemplate_.registerFunction("toDashCase", Btl.toDashCase);
getTemplate_.registerFunction("upperFirst", Btl.upperFirst);
getTemplate_.registerFunction("isBlank", Btl.isBlank);
getTemplate_.registerFunction("subAfter", Btl.subAfter);
getTemplate_.registerFunction("javaType", Btl.javaType);
getTemplate_.registerFunction("tsType", Btl.tsType);
getTemplate_.setErrorHandler(TemplateEngine::processException);
} catch (IOException e) {
getTemplate_ = null;
log.error("模板引擎初始化失败", e);
}
GT = getTemplate_;
}
private final String tplName;
private TemplateEngine(String tplName, Tpl tpl) {
this.tplName = tplName;
LOADER.put(tplName + "-content", tpl.getContent());
LOADER.put(tplName + "-dir", tpl.getDir());
LOADER.put(tplName + "-filename", tpl.getFilename());
}
private static void processException(BeetlException beeException, GroupTemplate groupTemplate, Writer writer) {
ErrorInfo errorInfo = beeException.toErrorInfo();
throw Exceptions.error(MapUtil.builder()
.put("type", errorInfo.type)
.put("line", errorInfo.errorTokenLine)
.put("token", errorInfo.errorTokenText)
.put("msg", errorInfo.msg)
.build());
}
public static TemplateEngine getEngine(String tplName, Tpl tpl) {
return ENGINE.computeIfAbsent(tplName, key -> new TemplateEngine(tplName, tpl));
}
public static void rmEngine(String tplName) {
ENGINE.remove(tplName);
LOADER.remove(tplName + "-content");
LOADER.remove(tplName + "-dir");
LOADER.remove(tplName + "-filename");
}
public String render(String tplName, Map<String, Object> data) {
Template template = GT.getTemplate(tplName);
template.binding(data);
return template.render();
}
public String renderContent(Map<String, Object> data) {
return render(tplName + "-content", data);
}
public String renderDir(Map<String, Object> data) {
return render(tplName + "-dir", data);
}
public String renderFilename(Map<String, Object> data) {
return render(tplName + "-filename", data);
}
}

View File

@ -0,0 +1,27 @@
package com.njzscloud.common.gen.support;
import cn.hutool.core.util.StrUtil;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Setter
@ToString
@Accessors(chain = true)
public class Tpl {
private String content;
private String dir;
private String filename;
public String getContent() {
return content == null ? "" : content;
}
public String getDir() {
return StrUtil.isBlank(dir) ? "" : dir;
}
public String getFilename() {
return StrUtil.isBlank(filename) ? "" : filename;
}
}

View File

@ -0,0 +1,75 @@
<%
var entityName = toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix));
var controllerClass = upperFirst(entityName) + "Controller";
var entityClass = upperFirst(entityName) + "Entity";
var entityInstance = entityName + "Entity";
var serviceClass = upperFirst(entityName) + "Service";
var serviceInstance = entityName + "Service";
var mapperClass = upperFirst(entityName) + "Mapper";
var baseUrl = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
%>
package ${basePackage}.${moduleName}.controller;
import lombok.extern.slf4j.Slf4j;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import com.njzscloud.common.core.utils.R;
import com.njzscloud.common.mp.support.PageParam;
import com.njzscloud.common.mp.support.PageResult;
import java.util.List;
import ${basePackage}.${moduleName}.pojo.entity.${entityClass};
import ${basePackage}.${moduleName}.service.${serviceClass};
/**
* ${table.comment}
*/
@Slf4j
@RestController
@RequestMapping("/${baseUrl}")
@RequiredArgsConstructor
public class ${controllerClass} {
private final ${serviceClass} ${serviceInstance};
/**
* 新增
*/
@PostMapping("/add")
public R<?> add(@RequestBody ${entityClass} ${entityInstance}) {
${serviceInstance}.add(${entityInstance});
return R.success();
}
/**
* 修改
*/
@PostMapping("/modify")
public R<?> modify(@RequestBody ${entityClass} ${entityInstance}) {
${serviceInstance}.modify(${entityInstance});
return R.success();
}
/**
* 删除
*/
@PostMapping("/del")
public R<?> del(@RequestBody List<Long> ids) {
${serviceInstance}.del(ids);
return R.success();
}
/**
* 详情
*/
@GetMapping("/detail")
public R<${entityClass}> detail(@RequestParam Long id) {
return R.success(${serviceInstance}.detail(id));
}
/**
* 分页查询
*/
@GetMapping("/paging")
public R<PageResult<${entityClass}>> paging(PageParam pageParam, ${entityClass} ${entityInstance}) {
return R.success(${serviceInstance}.paging(pageParam, ${entityInstance}));
}
}

View File

@ -0,0 +1,56 @@
<%
var entityName = toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix));
var controllerClass = upperFirst(entityName) + "Controller";
var entityClass = upperFirst(entityName) + "Entity";
var entityInstance = entityName + "Entity";
var serviceClass = upperFirst(entityName) + "Service";
var serviceInstance = entityName + "Service";
var mapperClass = upperFirst(entityName) + "Mapper";
var baseUrl = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
%>
package ${basePackage}.${moduleName}.pojo.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import com.baomidou.mybatisplus.annotation.*;
import lombok.ToString;
<%for(column in table.columns) {
var map = javaType(column.dataType);
%>
<%if(!isBlank(map.importStatement)){%>
${map.importStatement}
<%}%>
<%}%>
/**
* ${table.comment}
*/
@Getter
@Setter
@ToString
@Accessors(chain = true)
@TableName("${table.name}")
public class ${entityClass} {
<%
for(column in table.columns) {
var map = javaType(column.dataType);
%>
/**
* ${column.comment}
*/
<%if(column.primaryKey){%>
@TableId(type = IdType.ASSIGN_ID)
<%}%>
<%if(column.name == "creator_id" || column.name == "create_time"){%>
@TableId(fill = FieldFill.INSERT)
<%}else if(column.name == "modifier_id" || column.name == "modify_time"){%>
@TableField(fill = FieldFill.INSERT_UPDATE)
<%}else if(column.name == "deleted"){%>
@TableLogic
<%}%>
private ${map.dataType} ${toCamelCase(column.name)};
<%}%>
}

View File

@ -0,0 +1,22 @@
<%
var entityName = toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix));
var controllerClass = upperFirst(entityName) + "Controller";
var entityClass = upperFirst(entityName) + "Entity";
var entityInstance = entityName + "Entity";
var serviceClass = upperFirst(entityName) + "Service";
var serviceInstance = entityName + "Service";
var mapperClass = upperFirst(entityName) + "Mapper";
var baseUrl = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
%>
package ${basePackage}.${moduleName}.mapper;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import ${basePackage}.${moduleName}.pojo.entity.${entityClass};
/**
* ${table.comment}
*/
@Mapper
public interface ${mapperClass} extends BaseMapper<${entityClass}> {
}

View File

@ -0,0 +1,14 @@
<%
var entityName = toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix));
var controllerClass = upperFirst(entityName) + "Controller";
var entityClass = upperFirst(entityName) + "Entity";
var entityInstance = entityName + "Entity";
var serviceClass = upperFirst(entityName) + "Service";
var serviceInstance = entityName + "Service";
var mapperClass = upperFirst(entityName) + "Mapper";
var baseUrl = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
%>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${basePackage}.${moduleName}.pojo.entity.${entityClass}">
</mapper>

View File

@ -0,0 +1,69 @@
<%
var entityName = toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix));
var controllerClass = upperFirst(entityName) + "Controller";
var entityClass = upperFirst(entityName) + "Entity";
var entityInstance = entityName + "Entity";
var serviceClass = upperFirst(entityName) + "Service";
var serviceInstance = entityName + "Service";
var mapperClass = upperFirst(entityName) + "Mapper";
var baseUrl = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
%>
package ${basePackage}.${moduleName}.service;
import lombok.extern.slf4j.Slf4j;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.njzscloud.common.mp.support.PageParam;
import com.njzscloud.common.mp.support.PageResult;
import java.util.List;
import ${basePackage}.${moduleName}.pojo.entity.${entityClass};
import ${basePackage}.${moduleName}.mapper.${mapperClass};
/**
* ${table.comment}
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ${serviceClass} extends ServiceImpl<${mapperClass}, ${entityClass}> implements IService<${entityClass}> {
/**
* 新增
*/
public void add(${entityClass} ${entityInstance}) {
this.save(${entityInstance});
}
/**
* 修改
*/
public void modify(${entityClass} ${entityInstance}) {
this.updateById(${entityInstance});
}
/**
* 删除
*/
@Transactional(rollbackFor = Exception.class)
public void del(List<Long> ids) {
this.removeBatchByIds(ids);
}
/**
* 详情
*/
public ${entityClass} detail(Long id) {
return this.getById(id);
}
/**
* 分页查询
*/
public PageResult<${entityClass}> paging(PageParam pageParam, ${entityClass} ${entityInstance}) {
return PageResult.of(this.page(pageParam.toPage(), Wrappers.<${entityClass}>query(${entityInstance})));
}
}

View File

@ -0,0 +1,116 @@
{
"ts": [
{
"modelData": {
"prefix": "",
"basePackage": "",
"moduleName": "",
"subModuleName": ""
},
"tplName": "page.vue",
"tpl": {
"dir": "${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>",
"filename": "${pageVueName}",
"content": ""
}
},
{
"modelData": {
"moduleName": "",
"subModuleName": ""
},
"tplName": "form.vue",
"tpl": {
"dir": "${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>",
"filename": "${formVueName}",
"content": ""
}
},
{
"modelData": {
"moduleName": "",
"subModuleName": ""
},
"tplName": "page.ts",
"tpl": {
"dir": "${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>",
"filename": "${pageTsName}",
"content": ""
}
},
{
"modelData": {
"moduleName": "",
"subModuleName": ""
},
"tplName": "api.ts",
"tpl": {
"dir": "${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>",
"filename": "${apiTsName}",
"content": ""
}
},
{
"modelData": {
"moduleName": "",
"subModuleName": ""
},
"tplName": "d.ts",
"tpl": {
"dir": "${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>",
"filename": "${dTsName}",
"content": ""
}
}
],
"java": [
{
"modelData": {
"basePackage": "",
"moduleName": ""
},
"tplName": "entity.java",
"tpl": {
"dir": "pojo/entity",
"filename": "${upperFirst(toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix)))}Entity.java",
"content": ""
}
},
{
"modelData": {},
"tplName": "mapper.java",
"tpl": {
"dir": "mapper",
"filename": "${upperFirst(toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix)))}Mapper.java",
"content": ""
}
},
{
"modelData": {},
"tplName": "mapper.xml",
"tpl": {
"dir": "",
"filename": "${upperFirst(toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix)))}Mapper.xml",
"content": ""
}
},
{
"modelData": {},
"tplName": "service.java",
"tpl": {
"dir": "service",
"filename": "${upperFirst(toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix)))}Service.java",
"content": ""
}
},
{
"modelData": {},
"tplName": "controller.java",
"tpl": {
"dir": "controller",
"filename": "${upperFirst(toCamelCase(isBlank(prefix) ? table.name : subAfter(table.name, prefix)))}Controller.java",
"content": ""
}
}
]
}

View File

@ -0,0 +1,34 @@
<%
var ulc = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
var lcc = toCamelCase(ulc);
var ucc = upperFirst(lcc);
var dc = toDashCase(lcc);
var pageVueName = ucc + ".vue";
var formVueName = ucc + "Form.vue";
var pageTsName = dc + ".ts";
var apiTsName = dc + "-api.ts";
var dTsName = dc + ".d.ts";
%>
import {
get,
post
} from '@/common/utils/http-util.ts'
export default {
paging(data: ${ucc}Types.Search${ucc}Param) {
return get<G.PageResult<${ucc}Types.Search${ucc}Result>>('/${ulc}/paging', data)
},
detail(id: string) {
return get<${ucc}Types.Search${ucc}Result>('/${ulc}/detail', {id})
},
add(data: ${ucc}Types.Add${ucc}Param) {
return post('/${ulc}/add', data)
},
modify(data: ${ucc}Types.Modify${ucc}Param) {
return post('/${ulc}/modify', data)
},
del(ids: string[]) {
return post('/${ulc}/del', ids)
},
}

View File

@ -0,0 +1,57 @@
<%
var ulc = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
var lcc = toCamelCase(ulc);
var ucc = upperFirst(lcc);
var dc = toDashCase(lcc);
var pageVueName = ucc + ".vue";
var formVueName = ucc + "Form.vue";
var pageTsName = dc + ".ts";
var apiTsName = dc + "-api.ts";
var dTsName = dc + ".d.ts";
%>
export {}
declare global {
namespace ${ucc}Types {
interface Search${ucc}Param extends G.PageParam {
<%
for(column in table.columns) {
var map = tsType(column.dataType);
%>
// ${column.comment}
${toCamelCase(column.name)}?: ${map.dataType}
<%}%>
}
interface Search${ucc}Result {
<%
for(column in table.columns) {
var map = tsType(column.dataType);
%>
// ${column.comment}
${toCamelCase(column.name)}?: ${map.dataType}
<%}%>
}
interface Add${ucc}Param {
<%
for(column in table.columns) {
var map = tsType(column.dataType);
%>
// ${column.comment}
${toCamelCase(column.name)}?: ${map.dataType}
<%}%>
}
interface Modify${ucc}Param {
<%
for(column in table.columns) {
var map = tsType(column.dataType);
%>
// ${column.comment}
${toCamelCase(column.name)}?: ${map.dataType}
<%}%>
}
}
}

View File

@ -0,0 +1,120 @@
<%
var ulc = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
var lcc = toCamelCase(ulc);
var ucc = upperFirst(lcc);
var dc = toDashCase(lcc);
var pageVueName = ucc + ".vue";
var formVueName = ucc + "Form.vue";
var pageTsName = dc + ".ts";
var apiTsName = dc + "-api.ts";
var dTsName = dc + ".d.ts";
%>
<template>
<ElDialog v-model="showDialog"
:close-on-click-modal="false"
destroy-on-close
width="fit-content"
ref="${lcc}Form"
@close="dialogCloseHandler">
<ElForm :model="formData"
:rules="rules"
class="form-panel"
label-width="auto">
<%
for(column in table.columns) {
var map = tsType(column.dataType);
%>
<ElFormItem label="${column.comment}" prop="${toCamelCase(column.name)}">
<ElInput
v-model="formData.${toCamelCase(column.name)}"
:disabled="status === 'view'"
placeholder="${column.comment}"/>
</ElFormItem>
<%}%>
</ElForm>
<template #footer>
<ElButton @click="showDialog = false">{{ status === 'view' ? '关闭' : '取消' }}</ElButton>
<ElButton v-if="status !== 'view'" :loading="submiting" type="primary" @click="submitHandler">提交</ElButton>
</template>
</ElDialog>
</template>
<script lang="ts" setup>
import ${ucc}Api from '@/pages/${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>/${apiTsName}'
import Strings from '@/common/utils/strings.ts'
import FormUtil from '@/common/utils/formUtil.ts'
import {
ElMessage,
type FormInstance,
type FormRules,
} from 'element-plus'
const emits = defineEmits(['editSucc'])
const showDialog = ref(false)
const submiting = ref(false)
const status = ref<'add' | 'view' | 'modify'>('add')
const ${lcc}FormIns = useTemplateRef<FormInstance>('${lcc}Form')
const formData = ref<${ucc}Types.Search${ucc}Result>({})
const rules = reactive<FormRules<${ucc}Types.Search${ucc}Result>>({
<%
for(column in table.columns) {
%>
${toCamelCase(column.name)}: [{ required: true, message: '请填写${column.comment}', trigger: 'blur' }],
<%}%>
})
function dialogCloseHandler() {
formData.vaule = {}
}
function submitHandler() {
if (status.value === 'view') return
submiting.value = true
if (formData.vaule.id != null) {
FormUtil.submit(${lcc}FormIns, () => ${ucc}Api.modify(formData.vaule))
.then(() => {
ElMessage.success('修改成功')
emits('editSucc')
showDialog.value = false
})
.finally(() => {
submiting.value = false
})
} else {
FormUtil.submit(${lcc}FormIns, () => ${ucc}Api.add(formData.vaule))
.then(() => {
ElMessage.success('添加成功')
emits('editSucc')
showDialog.value = false
})
.finally(() => {
submiting.value = false
})
}
}
defineExpose({
open(data: ${ucc}Types.Search${ucc}Result = {}) {
showDialog.value = true
if (!Strings.isBlank(data.id)) {
status.value = 'modify'
${ucc}Api.detail(data.id!)
.then(res => {
formData.vaule = res.data
})
} else {
status.value = 'add'
formData.vaule = data
}
}
})
</script>
<style lang="stylus" scoped>
.form-panel {
padding 20px
}
</style>

View File

@ -0,0 +1,15 @@
<%
var ulc = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
var lcc = toCamelCase(ulc);
var ucc = upperFirst(lcc);
var dc = toDashCase(lcc);
var pageVueName = ucc + ".vue";
var formVueName = ucc + "Form.vue";
var pageTsName = dc + ".ts";
var apiTsName = dc + "-api.ts";
var dTsName = dc + ".d.ts";
%>
export default {
component: () => import('@/pages/${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>/${pageVueName}'),
} as RouterTypes.RouteConfig

View File

@ -0,0 +1,206 @@
<%
var ulc = isBlank(prefix) ? table.name : subAfter(table.name, prefix);
var lcc = toCamelCase(ulc);
var ucc = upperFirst(lcc);
var dc = toDashCase(lcc);
var pageVueName = ucc + ".vue";
var formVueName = ucc + "Form.vue";
var pageTsName = dc + ".ts";
var apiTsName = dc + "-api.ts";
var dTsName = dc + ".d.ts";
%>
<template>
<Page>
<ElForm v-show="showSearchForm" inline @submit.prevent="paging">
<%
for(column in table.columns) {
var map = tsType(column.dataType);
%>
<ElFormItem label="${column.comment}">
<ElInput
v-model="searchForm.${toCamelCase(column.name)}"
placeholder="${column.comment}"/>
</ElFormItem>
<%}%>
<ElFormItem>
<ElButton :icon="elIcons.Search" :loading="searching" native-type="submit" type="primary">搜索</ElButton>
<ElButton :icon="elIcons.Refresh" :loading="searching" @click="reset">重置</ElButton>
</ElFormItem>
</ElForm>
<div class="tool-bar">
<ElButton :icon="elIcons.Plus" type="primary" @click="addHandler">新建</ElButton>
<ElButton :icon="elIcons.Filter" type="default" @click="showSearchForm = !showSearchForm"/>
</div>
<ElTable v-loading="searching" :data="tableData"
cell-class-name="table-cell"
class="table-list"
empty-text="暂无数据"
header-row-class-name="table-header"
row-key="id">
<%
for(column in table.columns) {
var map = tsType(column.dataType);
%>
<ElTableColumn label="${column.comment}" prop="${toCamelCase(column.name)}"/>
<%}%>
<ElTableColumn label="操作" width="180">
<template #default="scope">
<div class="action-btn">
<ElPopconfirm
confirm-button-text="是"
cancel-button-text="否"
confirm-button-type="danger"
cancel-button-type="primary"
placement="top"
title="是否删除当前数据?"
width="180"
@confirm="delHandler(scope)">
<template #reference>
<ElButton text type="danger" :loading="deling">删除</ElButton>
</template>
</ElPopconfirm>
<ElButton text type="primary" @click="modifyHandler(scope)">修改</ElButton>
</div>
</template>
</ElTableColumn>
</ElTable>
<ElPagination
class="pagination"
layout="prev, pager, next"
:page-size="pagination.size"
:total="pagination.total"
@change="pageChangeHandler"/>
<${ucc}Form ref="${lcc}Form" @edit-succ="paging"/>
</Page>
</template>
<script lang="ts" setup>
import ${ucc}Api from '@/pages/${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>/${apiTsName}'
import ${ucc}Form from '@/pages/${moduleName}<%if(subModuleName != null){%>/${subModuleName}<%}%>/${formVueName}'
import Page from '@/components/page/Page.vue'
import { elIcons } from '@/common/element/element.ts'
const tableData = ref<${ucc}Types.Search${ucc}Result[]>([])
const searchForm = reactive<${ucc}Types.Search${ucc}Param>({
current: 1,
size: 20,
})
const searching = ref(false)
const deling = ref(false)
const showSearchForm = ref(true)
const ${lcc}FormIns = useTemplateRef<InstanceType<typeof ${ucc}Form>>('${lcc}Form')
const pagination = reactive<G.Pagination>({
total: 0,
current: 1,
size: 1,
})
function pageChangeHandler(currentPage: number, pageSize: number) {
searchForm.current = currentPage
searchForm.size = pageSize
paging()
}
function showDialog(data?: ${ucc}Types.Search${ucc}Result) {
${lcc}FormIns.value?.open(data)
}
function delHandler({row}: { row: ${ucc}Types.Search${ucc}Result }) {
deling.value = true
${ucc}Api.del([ row.id! ])
.then(() => {
ElMessage.success('删除成功')
paging()
})
.finally(() => {
deling.value = false
})
}
function modifyHandler({row}: { row: ${ucc}Types.Search${ucc}Result }) {
showDialog(row)
}
function addHandler() {
showDialog()
}
function reset() {
Object.assign(searchForm, {})
paging()
}
function paging() {
searching.value = true
${ucc}Api.paging(searchForm)
.then(res => {
tableData.value = res.data?.records ?? []
})
.finally(() => {
searching.value = false
})
}
onMounted(() => {
paging()
})
</script>
<style lang="stylus" scoped>
.table-list {
flex 1;
width 100%
:deep(.table-header) {
color #454C59
th {
background-color #EDF1F7
font-weight 500
position relative
& > div {
display flex
gap 5px
align-items center
}
&:not(:first-child) > div::before {
position: absolute;
top: 50%;
left: 1px;
width: 1px;
background-color: #D3D7DE;
transform: translateY(-50%);
content: "";
height 50%
}
}
}
:deep(.table-cell) {
color #2F3540
}
.action-btn {
width 100%
display flex
flex-wrap wrap
& > button {
margin 0
}
}
}
.tool-bar {
display flex
justify-content space-between
margin 0 0 20px 0
}
.pagination {
justify-content: end;
margin: 8px;
}
</style>

View File

@ -0,0 +1,25 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_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-http</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.njzscloud</groupId>
<artifactId>njzscloud-common-core</artifactId>
<scope>provided</scope>
</dependency>
<!-- okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,209 @@
package com.njzscloud.common.http;
import cn.hutool.core.collection.CollUtil;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.http.config.HttpClientProperties;
import com.njzscloud.common.http.constant.HttpMethod;
import com.njzscloud.common.http.support.ResponseInfo;
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,220 @@
package com.njzscloud.common.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 cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.http.annotation.GetEndpoint;
import com.njzscloud.common.http.annotation.PostEndpoint;
import com.njzscloud.common.http.annotation.RemoteServer;
import com.njzscloud.common.http.constant.HttpMethod;
import com.njzscloud.common.http.interceptor.RequestInterceptor;
import com.njzscloud.common.http.interceptor.ResponseInterceptor;
import com.njzscloud.common.http.resolver.*;
import com.njzscloud.common.http.support.RequestInfo;
import com.njzscloud.common.http.support.ResponseInfo;
import com.njzscloud.common.http.support.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* , "接口注解式" HTTP , 使 JDK
* <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, () -> Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class[]{clazz},
new MethodInterceptorImpl(clazz, HTTP_CLIENT)));
}
@Slf4j
private static class MethodInterceptorImpl implements InvocationHandler {
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 String name;
private final RequestInterceptor requestInterceptor;
private final ResponseInterceptor responseInterceptor;
public MethodInterceptorImpl(Class<?> clazz, HttpClient httpClient) {
RemoteServer anno = clazz.getAnnotation(RemoteServer.class);
String name = anno.name();
if (StrUtil.isBlank(name)) {
this.name = clazz.getName();
} else {
this.name = name;
}
String value = anno.value();
if (value.startsWith("${") && value.endsWith("}")) {
value = SpringUtil.getProperty(value.substring(2, value.length() - 1));
}
if (StrUtil.isBlank(value)) {
throw Exceptions.error("地址不合法");
}
Matcher matcher = ADDR_PATTERN.matcher(value);
Assert.isTrue(matcher.matches(), () -> Exceptions.error("地址不合法"));
baseUrl = value;
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 invoke(Object proxy, Method method, Object[] args) throws Throwable {
HttpMethod httpMethod;
String urlTpl;
String desc;
String methodName = method.getName();
switch (methodName) {
case "hashCode" -> {
return System.identityHashCode(proxy);
}
case "equals" -> {
return proxy == args[0];
}
case "toString" -> {
return name;
}
}
GetEndpoint getEndpoint = method.getAnnotation(GetEndpoint.class);
if (getEndpoint == null) {
PostEndpoint postEndpoint = method.getAnnotation(PostEndpoint.class);
if (postEndpoint == null) {
return method.invoke(this, args);
} 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.of("", 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 typeWrap) {
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,23 @@
package com.njzscloud.common.http.annotation;
import com.njzscloud.common.http.processor.BodyParamProcessor;
import com.njzscloud.common.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.http.annotation;
import com.njzscloud.common.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.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.http.annotation;
import com.njzscloud.common.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.http.annotation;
import com.njzscloud.common.http.processor.DefaultJsonBodyParamProcessor;
import com.njzscloud.common.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.http.annotation;
import com.njzscloud.common.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.http.annotation;
import com.njzscloud.common.http.processor.DefaultPathParamProcessor;
import com.njzscloud.common.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.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.http.annotation;
import com.njzscloud.common.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,46 @@
package com.njzscloud.common.http.annotation;
import com.njzscloud.common.http.interceptor.CompositeInterceptor;
import com.njzscloud.common.http.interceptor.RequestInterceptor;
import com.njzscloud.common.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() default "";
String name() default "";
/**
* ,
*
* @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;
}

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