master
commit
2b3e56352c
|
|
@ -0,0 +1,7 @@
|
||||||
|
/**/logs
|
||||||
|
/**/*.iml
|
||||||
|
/**/.idea
|
||||||
|
/**/target
|
||||||
|
/**/.DS_Store
|
||||||
|
/**/.back*
|
||||||
|
db-model/*/
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<groupId>com.njzscloud</groupId>
|
||||||
|
<artifactId>gps</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- fastjson2 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
<version>2.0.51</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--<editor-fold desc="hutool">-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-cache</artifactId>
|
||||||
|
<version>5.8.28</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-core</artifactId>
|
||||||
|
<version>5.8.28</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-extra</artifactId>
|
||||||
|
<version>5.8.28</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-captcha</artifactId>
|
||||||
|
<version>5.8.28</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-crypto</artifactId>
|
||||||
|
<version>5.8.28</version>
|
||||||
|
</dependency>
|
||||||
|
<!--</editor-fold>-->
|
||||||
|
|
||||||
|
<!-- lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.30</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- slf4j -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.36</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- logback -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>1.2.11</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--<editor-fold desc="jackson">-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.13.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
|
<artifactId>jackson-dataformat-xml</artifactId>
|
||||||
|
<version>2.13.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||||
|
<version>2.13.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<version>2.13.4</version>
|
||||||
|
</dependency>
|
||||||
|
<!--</editor-fold>-->
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.njzscloud.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.njzscloud.common.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.njzscloud.common.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.njzscloud.common.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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.njzscloud.common.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.njzscloud.common.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.njzscloud.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.njzscloud.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.njzscloud.common.ex;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import com.njzscloud.common.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
package com.njzscloud.common.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
package com.njzscloud.common.fastjson.serializer;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONReader;
|
||||||
|
import com.alibaba.fastjson2.reader.ObjectReader;
|
||||||
|
import com.alibaba.fastjson2.util.TypeUtils;
|
||||||
|
import com.njzscloud.common.ex.Exceptions;
|
||||||
|
import com.njzscloud.common.ienum.Dict;
|
||||||
|
import com.njzscloud.common.ienum.DictInt;
|
||||||
|
import com.njzscloud.common.ienum.DictStr;
|
||||||
|
import com.njzscloud.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package com.njzscloud.common.fastjson.serializer;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONWriter;
|
||||||
|
import com.alibaba.fastjson2.util.TypeUtils;
|
||||||
|
import com.alibaba.fastjson2.writer.ObjectWriter;
|
||||||
|
import com.njzscloud.common.ienum.Dict;
|
||||||
|
import com.njzscloud.common.ienum.DictInt;
|
||||||
|
import com.njzscloud.common.ienum.DictStr;
|
||||||
|
import com.njzscloud.common.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.njzscloud.common.ienum;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.njzscloud.common.fastjson.serializer.DictObjectDeserializer;
|
||||||
|
import com.njzscloud.common.fastjson.serializer.DictObjectSerializer;
|
||||||
|
import com.njzscloud.common.jackson.serializer.DictDeserializer;
|
||||||
|
import com.njzscloud.common.jackson.serializer.DictSerializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典枚举<br/>
|
||||||
|
* 仅两个子接口 DictInt、DictStr<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();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.njzscloud.common.ienum;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "值" 类型为 Integer<br/>
|
||||||
|
* 枚举应实现此接口
|
||||||
|
*
|
||||||
|
* @see DictStr
|
||||||
|
*/
|
||||||
|
public interface DictInt extends Dict<Integer> {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.njzscloud.common.ienum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "值" 类型为 String<br/>
|
||||||
|
* 枚举应实现此接口
|
||||||
|
*
|
||||||
|
* @see DictInt
|
||||||
|
*/
|
||||||
|
public interface DictStr extends Dict<String> {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.njzscloud.common.ienum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举接口
|
||||||
|
*/
|
||||||
|
public interface IEnum {
|
||||||
|
/**
|
||||||
|
* 枚举单独序列化时的属性<br/>
|
||||||
|
* 存放枚举的全限定类名
|
||||||
|
*/
|
||||||
|
String ENUM_TYPE = "type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举单独序列化时的属性<br/>
|
||||||
|
* 存放枚举的 name 属性值
|
||||||
|
*/
|
||||||
|
String ENUM_NAME = "name";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举单独序列化时的属性<br/>
|
||||||
|
* 存放枚举的 ordinal 属性值
|
||||||
|
*/
|
||||||
|
String ENUM_ORDINAL = "ordinal";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
package com.njzscloud.common.jackson;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
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.ex.Exceptions;
|
||||||
|
import com.njzscloud.common.jackson.serializer.BigDecimalModule;
|
||||||
|
import com.njzscloud.common.jackson.serializer.LongModule;
|
||||||
|
import com.njzscloud.common.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 = createObjectMapper();
|
||||||
|
xmlMapper = createXmlMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列化为 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<List<String>>(){}, 类型对象建议缓存
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.njzscloud.common.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.njzscloud.common.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
package com.njzscloud.common.jackson.serializer;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.*;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
import com.fasterxml.jackson.databind.node.IntNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.TextNode;
|
||||||
|
import com.njzscloud.common.ex.Exceptions;
|
||||||
|
import com.njzscloud.common.ienum.Dict;
|
||||||
|
import com.njzscloud.common.ienum.DictInt;
|
||||||
|
import com.njzscloud.common.ienum.DictStr;
|
||||||
|
import com.njzscloud.common.ienum.IEnum;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dict 枚举的 Jackson 反序列化器<br/><br/>
|
||||||
|
* JSON 格式见对应的序列化器 {@link DictSerializer}
|
||||||
|
*
|
||||||
|
* @see Dict
|
||||||
|
* @see DictInt
|
||||||
|
* @see DictStr
|
||||||
|
* @see DictSerializer
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DictDeserializer extends JsonDeserializer<Dict> {
|
||||||
|
private static final ClassLoader CLASSLOADER = DictDeserializer.class.getClassLoader();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dict deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
|
||||||
|
JsonToken currentToken = p.getCurrentToken();
|
||||||
|
|
||||||
|
if (currentToken == JsonToken.START_OBJECT) {
|
||||||
|
TreeNode treeNode = p.getCodec().readTree(p);
|
||||||
|
TreeNode enumType_node = treeNode.get(IEnum.ENUM_TYPE);
|
||||||
|
String enumTypeField = ((TextNode) enumType_node).textValue();
|
||||||
|
TreeNode val_node = treeNode.get(Dict.ENUM_VAL);
|
||||||
|
|
||||||
|
Class<?> clazz;
|
||||||
|
try {
|
||||||
|
clazz = CLASSLOADER.loadClass(enumTypeField);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw Exceptions.error(e, "类型加载失败:{}", enumTypeField);
|
||||||
|
}
|
||||||
|
if (val_node instanceof TextNode) {
|
||||||
|
if (DictStr.class.isAssignableFrom(clazz)) {
|
||||||
|
String val = ((TextNode) val_node).textValue();
|
||||||
|
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
|
||||||
|
return Dict.parse(val, constants);
|
||||||
|
} else if (DictInt.class.isAssignableFrom(clazz)) {
|
||||||
|
String val = ((TextNode) val_node).textValue();
|
||||||
|
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
|
||||||
|
return Dict.parse(Integer.parseInt(val), constants);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (val_node instanceof IntNode) {
|
||||||
|
if (DictStr.class.isAssignableFrom(clazz)) {
|
||||||
|
int val = ((IntNode) val_node).intValue();
|
||||||
|
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
|
||||||
|
return Dict.parse(String.valueOf(val), constants);
|
||||||
|
} else if (DictInt.class.isAssignableFrom(clazz)) {
|
||||||
|
int val = ((IntNode) val_node).intValue();
|
||||||
|
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
|
||||||
|
return Dict.parse(val, constants);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
JsonStreamContext context = p.getParsingContext();
|
||||||
|
String currentName = context.getCurrentName();
|
||||||
|
|
||||||
|
Object currentValue = p.getCurrentValue();
|
||||||
|
|
||||||
|
Class<?> valueClazz = currentValue.getClass();
|
||||||
|
try {
|
||||||
|
Field field = valueClazz.getDeclaredField(currentName);
|
||||||
|
Class<?> clazz = field.getType();
|
||||||
|
if (DictStr.class.isAssignableFrom(clazz)) {
|
||||||
|
String val = p.getValueAsString();
|
||||||
|
DictStr[] constants = (DictStr[]) clazz.getEnumConstants();
|
||||||
|
return Dict.parse(val, constants);
|
||||||
|
} else if (DictInt.class.isAssignableFrom(clazz)) {
|
||||||
|
int val = p.getValueAsInt();
|
||||||
|
DictInt[] constants = (DictInt[]) clazz.getEnumConstants();
|
||||||
|
return Dict.parse(val, constants);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("字典枚举反序列化失败", e);
|
||||||
|
throw Exceptions.error(e, "字典枚举反序列化失败,字段名:{},值:{}", currentName, currentValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.njzscloud.common.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.ienum.Dict;
|
||||||
|
import com.njzscloud.common.ienum.DictInt;
|
||||||
|
import com.njzscloud.common.ienum.DictStr;
|
||||||
|
import com.njzscloud.common.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.njzscloud.common.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.njzscloud.common.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.njzscloud.common.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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,625 @@
|
||||||
|
package com.njzscloud.common.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import com.njzscloud.common.ex.Exceptions;
|
||||||
|
import com.njzscloud.common.fastjson.Fastjson;
|
||||||
|
|
||||||
|
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<K, SV>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, SV>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, VT>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, SV>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, SV>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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<K, SV>
|
||||||
|
*/
|
||||||
|
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<K, V>
|
||||||
|
*/
|
||||||
|
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("值【{}】与【{}】对应的键重复", Fastjson.toJsonStr(oldVal), Fastjson.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<K, List<V>>
|
||||||
|
*/
|
||||||
|
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<K, List<V>>
|
||||||
|
*/
|
||||||
|
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<K, List<SV>>
|
||||||
|
*/
|
||||||
|
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<K, List<V>>
|
||||||
|
*/
|
||||||
|
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<K, List<V>>
|
||||||
|
*/
|
||||||
|
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<K, List<V>>
|
||||||
|
*/
|
||||||
|
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<K, List<SV>>
|
||||||
|
*/
|
||||||
|
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<K, List<V>>
|
||||||
|
*/
|
||||||
|
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<K, Set<SV>>
|
||||||
|
*/
|
||||||
|
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<K, Set<V>>
|
||||||
|
*/
|
||||||
|
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<K, Set<SV>>
|
||||||
|
*/
|
||||||
|
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<K, Set<V>>
|
||||||
|
*/
|
||||||
|
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<K, Set<SV>>
|
||||||
|
*/
|
||||||
|
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<K, Set<V>>
|
||||||
|
*/
|
||||||
|
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<K, Set<SV>>
|
||||||
|
*/
|
||||||
|
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<K, Set<V>>
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.njzscloud.gps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hello world!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class App {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration debug="false">
|
||||||
|
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||||
|
|
||||||
|
<property name="log_path" value="logs"/>
|
||||||
|
<property name="service_name" value="${project.artifactId}"/>
|
||||||
|
|
||||||
|
<property name="console_pattern" value="%magenta(%d{yyyy-MM-dd HH:mm:ss.SSS}) %clr(%-5p) %blue([%t]) %cyan(%c) %blue([%M:%L]): %clr(%m%n)"/>
|
||||||
|
|
||||||
|
<property name="file_pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p [%t] %c [%M:%L] : %m%n"/>
|
||||||
|
|
||||||
|
<appender name="console_appender" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<withJansi>false</withJansi>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${console_pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="file_appender" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log_path}/${service_name}.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${log_path}/%d{yyyy-MM, aux}/${service_name}.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
|
||||||
|
<maxFileSize>50MB</maxFileSize>
|
||||||
|
<maxHistory>15</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${file_pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- TRACE < DEBUG < INFO < WARN < ERROR -->
|
||||||
|
<root level="WARN">
|
||||||
|
<appender-ref ref="console_appender"/>
|
||||||
|
<appender-ref ref="file_appender"/>
|
||||||
|
</root>
|
||||||
|
</configuration>
|
||||||
Loading…
Reference in New Issue