From 2026a753018ac3aa13be68dc4f2ce4017ad12a7d Mon Sep 17 00:00:00 2001 From: yangjiyu Date: Wed, 23 Nov 2022 15:49:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BB=A3=E7=A0=81=E4=B8=80=E9=94=AE?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=B1=BB=E7=9A=84=E5=BC=95=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 代码一键生成类的引入代码一键生成类的引入 --- .../common/exception/ExceptionInfo.java | 48 + .../java/com/aos/starter/core/CoreBundle.java | 62 ++ .../aos/starter/core/api/GeneralResult.java | 98 +++ .../aos/starter/core/assertion/IAssert.java | 223 +++++ .../core/convert/CustomConversionService.java | 78 ++ .../starter/core/convert/CustomConverter.java | 99 +++ .../core/convert/EnumToStringConverter.java | 160 ++++ .../com/aos/starter/core/enums/FieldFill.java | 22 + .../DefaultBeanSerializerModifier.java | 154 ++++ .../com/aos/starter/core/node/ForestNode.java | 39 + .../starter/core/node/ForestNodeManager.java | 81 ++ .../starter/core/node/ForestNodeMerger.java | 41 + .../core/reflection/DefaultMetaObject.java | 53 ++ .../reflection/DefaultReflectorFactory.java | 61 ++ .../factory/DefaultObjectFactory.java | 155 ++++ .../reflection/invoker/GetFieldInvoker.java | 67 ++ .../wrapper/DefaultObjectWrapperFactory.java | 43 + .../aos/starter/core/util/ConvertUtils.java | 97 ++ .../aos/starter/core/util/DateTimeUtils.java | 136 +++ .../com/aos/starter/core/util/DateUtils.java | 827 ++++++++++++++++++ .../aos/starter/core/util/DigestUtils.java | 777 ++++++++++++++++ .../com/aos/starter/core/util/FileUtils.java | 704 +++++++++++++++ .../com/aos/starter/core/util/Holder.java | 23 + .../starter/core/util/HttpClientUtils.java | 297 +++++++ .../resources/messages/CoreBundle.properties | 16 + .../src/main/resources/templates/dto.java.vm | 34 + .../main/resources/templates/entity.java.vm | 128 +++ .../src/main/resources/templates/form.java.vm | 34 + 28 files changed, 4557 insertions(+) create mode 100644 aos-element/aos-element-common/src/main/java/com/aos/element/common/exception/ExceptionInfo.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/CoreBundle.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/api/GeneralResult.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/assertion/IAssert.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/CustomConversionService.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/CustomConverter.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/EnumToStringConverter.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/enums/FieldFill.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/jackson/DefaultBeanSerializerModifier.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNode.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNodeManager.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNodeMerger.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/DefaultMetaObject.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/DefaultReflectorFactory.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/factory/DefaultObjectFactory.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/invoker/GetFieldInvoker.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/wrapper/DefaultObjectWrapperFactory.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/ConvertUtils.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DateTimeUtils.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DateUtils.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DigestUtils.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/FileUtils.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/Holder.java create mode 100644 aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/HttpClientUtils.java create mode 100644 aos-element/aos-element-core/src/main/resources/messages/CoreBundle.properties create mode 100644 aos-element/aos-element-devtools/src/main/resources/templates/dto.java.vm create mode 100644 aos-element/aos-element-devtools/src/main/resources/templates/entity.java.vm create mode 100644 aos-element/aos-element-devtools/src/main/resources/templates/form.java.vm diff --git a/aos-element/aos-element-common/src/main/java/com/aos/element/common/exception/ExceptionInfo.java b/aos-element/aos-element-common/src/main/java/com/aos/element/common/exception/ExceptionInfo.java new file mode 100644 index 0000000..365932d --- /dev/null +++ b/aos-element/aos-element-common/src/main/java/com/aos/element/common/exception/ExceptionInfo.java @@ -0,0 +1,48 @@ +package com.aos.element.common.exception; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 开发环境时输出更多的异常信息

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 14:55 + * @since 1.0.0 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ExceptionInfo implements Serializable { + /** serialVersionUID */ + private static final long serialVersionUID = -5072425562316472427L; + /** 请求路径 */ + private String path; + /** 请求参数 */ + private Object params; + /** 请求方式 */ + private String method; + /** 请求方地址 */ + private String remoteAddr; + /** header */ + private Object headers; + /** 追踪 id */ + private String traceId; + /** 异常类 */ + private String exceptionClass; + /** 错误信息 */ + private String errorMessage; + /** 异常堆栈 */ + private String stackTrace; + /** 日志系统链接 */ + private String hyperlink; + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/CoreBundle.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/CoreBundle.java new file mode 100644 index 0000000..c0adb09 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/CoreBundle.java @@ -0,0 +1,62 @@ +package com.aos.starter.core; + +import com.aos.element.basic.basic.bundle.DynamicBundle; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 消息外置, 需要在 resources/messages 创建对应的 [CoreBundle.properties] 文件

+ * + * @author dong4j + * @version 1.4.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.05.19 09:26 + * @since 1.4.0 + */ +public final class CoreBundle extends DynamicBundle { + /** BUNDLE */ + @NonNls + private static final String BUNDLE = "messages.CoreBundle"; + /** INSTANCE */ + private static final CoreBundle INSTANCE = new CoreBundle(); + + /** + * Plugin bundle + * + * @since 0.0.1 + */ + @Contract(pure = true) + private CoreBundle() { + super(BUNDLE); + } + + /** + * Message + * + * @param key key + * @param params params + * @return the string + * @since 1.4.0 + */ + @NotNull + public static String message(@NotNull String key, Object... params) { + return INSTANCE.getMessage(key, params); + } + + /** + * Message pointer + * + * @param key key + * @param params params + * @return the supplier + * @since 1.4.0 + */ + public static @NotNull Supplier messagePointer(@NotNull String key, Object... params) { + return INSTANCE.getLazyMessage(key, params); + } +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/api/GeneralResult.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/api/GeneralResult.java new file mode 100644 index 0000000..ad8bbc6 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/api/GeneralResult.java @@ -0,0 +1,98 @@ +package com.aos.starter.core.api; + +import com.aos.element.basic.basic.Result; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.0.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.10.14 00:55 + * @since 1.6.0 + */ +public interface GeneralResult { + + /** + * 请求成功 + * + * @param parameter + * @return the result + * @since 1.0.0 + */ + default Result ok() { + return this.ok(null); + } + + /** + * 请求成功 + * + * @param 对象泛型 + * @param data 数据内容 + * @return the result + * @since 1.0.0 + */ + default Result ok(T data) { + return R.succeed(data); + } + + /** + * 请求失败 + * + * @param parameter + * @param msg 提示内容 + * @return the result + * @since 1.0.0 + */ + default Result fail(String msg) { + return R.failed(msg); + } + + /** + * 请求失败 + * + * @param parameter + * @param errorCode 请求错误码 + * @return the result + * @since 1.0.0 + */ + default Result fail(IResultCode errorCode) { + return R.failed(errorCode); + } + + /** + * Status result. + * + * @param flag the flag + * @return the result + * @since 1.0.0 + */ + default Result status(boolean flag) { + return this.status(flag, BaseCodes.FAILURE); + } + + /** + * Status result + * + * @param flag flag + * @param resultCode result code + * @return the result + * @since 1.0.0 + */ + default Result status(boolean flag, IResultCode resultCode) { + return R.status(flag, resultCode); + } + + /** + * Status result + * + * @param flag flag + * @param message message + * @return the result + * @since 1.0.0 + */ + default Result status(boolean flag, String message) { + return R.status(flag, message); + } +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/assertion/IAssert.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/assertion/IAssert.java new file mode 100644 index 0000000..51f7638 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/assertion/IAssert.java @@ -0,0 +1,223 @@ +package com.aos.starter.core.assertion; + +import com.aos.element.basic.basic.asserts.Assertions; +import com.aos.element.basic.core.function.CheckedCallable; +import com.aos.element.basic.core.function.CheckedRunnable; +import com.aos.starter.core.exception.BaseException; + +import java.util.Collection; +import java.util.Map; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 枚举类异常断言,提供简便的方式判断条件,并在条件满足时抛出异常 + * 错误码和错误信息定义在枚举类中,在本断言方法中,传递错误信息需要的参数 + * 底层会使用 {@link Assertions} + *

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.26 20:06 + * @since 1.0.0 + */ +public interface IAssert { + + /** + * New exception + * + * @param args args + * @return the base exception + * @since 1.7.0 + */ + BaseException newException(Object... args); + + /** + * New exception + * + * @param t t + * @param args args + * @return the base exception + * @since 1.7.0 + */ + BaseException newException(Throwable t, Object... args); + + /** + * Not blank + * + * @param str str + * @param args args + * @since 1.7.0 + */ + default void notBlank(String str, Object... args) { + Assertions.notBlank(str, () -> this.newException(args)); + } + + /** + * Not empty + * + * @param arrays arrays + * @param args args + * @since 1.7.0 + */ + default void notEmpty(Object[] arrays, Object... args) { + Assertions.notEmpty(arrays, () -> this.newException(args)); + } + + /** + * Not empty + * + * @param c c + * @param args args + * @since 1.7.0 + */ + default void notEmpty(Collection c, Object... args) { + Assertions.notEmpty(c, () -> this.newException(args)); + } + + /** + * Not empty + * + * @param map map + * @param args args + * @since 1.7.0 + */ + default void notEmpty(Map map, Object... args) { + Assertions.notEmpty(map, () -> this.newException(args)); + } + + /** + * Is false + * + * @param expression expression + * @param args args + * @since 1.7.0 + */ + default void isFalse(boolean expression, Object... args) { + Assertions.isFalse(expression, () -> this.newException(args)); + } + + /** + * Is true + * + * @param expression expression + * @param args args + * @since 1.7.0 + */ + default void isTrue(boolean expression, Object... args) { + Assertions.isTrue(expression, () -> this.newException(args)); + } + + /** + * Is null + * + * @param obj obj + * @param args args + * @since 1.7.0 + */ + default void isNull(Object obj, Object... args) { + Assertions.isNull(obj, () -> this.newException(args)); + } + + /** + * Not null + * + * @param obj obj + * @param args args + * @since 1.7.0 + */ + default void notNull(Object obj, Object... args) { + Assertions.notNull(obj, () -> this.newException(args)); + } + + /** + * 适用于没有占位符的错误消息 + * + * @param obj obj + * @param runnable runnable + * @since 1.7.0 + */ + default void notNull(Object obj, CheckedRunnable runnable) { + Assertions.notNull(obj, this::newException, runnable); + } + + /** + * Equals + * + * @param o1 o 1 + * @param o2 o 2 + * @param args args + * @since 1.7.0 + */ + default void equals(Object o1, Object o2, Object... args) { + Assertions.equals(o1, o2, () -> this.newException(args)); + } + + /** + * Not equals + * + * @param o1 o 1 + * @param o2 o 2 + * @param args args + * @since 1.7.0 + */ + default void notEquals(Object o1, Object o2, Object... args) { + Assertions.notEquals(o1, o2, () -> this.newException(args)); + } + + /** + * Wrapper + * + * @param runnable runnable + * @param args args + * @since 1.7.0 + */ + default void wrapper(CheckedRunnable runnable, Object... args) { + try { + runnable.run(); + } catch (Throwable throwable) { + this.fail(throwable, args); + } + } + + /** + * Wrapper + * + * @param parameter + * @param callable callable + * @param args args + * @return the t + * @since 1.8.0 + */ + default T wrapper(CheckedCallable callable, Object... args) { + try { + return callable.call(); + } catch (Throwable throwable) { + this.fail(throwable, args); + } + return null; + } + + /** + * Fail + * + * @param args args + * @since 1.7.0 + */ + default void fail(Object... args) { + this.fail(this.newException(args)); + } + + /** + * Fail + * + * @param t t + * @param args args + * @since 1.7.0 + */ + default void fail(Throwable t, Object... args) { + Assertions.fail(() -> this.newException(t, args)); + } + + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/CustomConversionService.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/CustomConversionService.java new file mode 100644 index 0000000..05795a9 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/CustomConversionService.java @@ -0,0 +1,78 @@ +package com.aos.starter.core.convert; + +import org.jetbrains.annotations.Contract; +import org.springframework.boot.convert.ApplicationConversionService; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.lang.Nullable; +import org.springframework.util.StringValueResolver; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 类型 转换 服务,添加了 IEnum 转换

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:06 + * @since 1.0.0 + */ +public final class CustomConversionService extends ApplicationConversionService { + + /** + * Custom conversion service + * + * @since 1.0.0 + */ + private CustomConversionService() { + this(null); + } + + /** + * Custom conversion service + * + * @param embeddedValueResolver the embedded value resolver + * @since 1.0.0 + */ + private CustomConversionService(@Nullable StringValueResolver embeddedValueResolver) { + super(embeddedValueResolver); + super.addConverter(new EnumToStringConverter()); + super.addConverter(new StringToEnumConverter()); + } + + /** + * Gets instance. + * + * @return the instance + * @since 1.0.0 + */ + @Contract(pure = true) + public static GenericConversionService getInstance() { + return SingletonHolder.INSTANCE; + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 静态内部类实现单例

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:06 + * @since 1.0.0 + */ + private static final class SingletonHolder { + /** INSTANCE */ + private static final CustomConversionService INSTANCE = new CustomConversionService(); + + /** + * Singleton holder + * + * @since 1.0.0 + */ + @Contract(pure = true) + private SingletonHolder() { + + } + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/CustomConverter.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/CustomConverter.java new file mode 100644 index 0000000..5579f42 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/CustomConverter.java @@ -0,0 +1,99 @@ +package com.aos.starter.core.convert; + + +import com.aos.element.basic.basic.util.ClassUtils; +import com.aos.element.basic.core.function.CheckedFunction; +import com.aos.starter.core.util.ConvertUtils; +import com.aos.starter.core.util.ReflectionUtils; +import com.aos.starter.core.util.Unchecked; + +import org.jetbrains.annotations.NotNull; +import org.springframework.cglib.core.Converter; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.lang.Nullable; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 组合 spring cglib Converter 和 spring ConversionService

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:06 + * @since 1.0.0 + */ +@Slf4j +@AllArgsConstructor +public class CustomConverter implements Converter { + /** TYPE_CACHE */ + private static final ConcurrentMap TYPE_CACHE = new ConcurrentHashMap<>(); + /** Source clazz */ + private final Class sourceClazz; + /** Target clazz */ + private final Class targetClazz; + + /** + * cglib convert + * + * @param value 源对象属性 + * @param target 目标对象属性类 + * @param fieldName 目标的field名,原为 set 方法名,MicaBeanCopier 里做了更改 + * @return {Object} + * @since 1.0.0 + */ + @Override + @Nullable + @SuppressWarnings("checkstyle:ReturnCount") + public Object convert(Object value, Class target, Object fieldName) { + if (value == null) { + return null; + } + // 类型一样,不需要转换 + if (ClassUtils.isAssignableValue(target, value)) { + return value; + } + try { + TypeDescriptor targetDescriptor = CustomConverter.getTypeDescriptor(this.targetClazz, (String) fieldName); + // 1. 判断 sourceClazz 为 Map + if (Map.class.isAssignableFrom(this.sourceClazz)) { + return ConvertUtils.convert(value, targetDescriptor); + } else { + TypeDescriptor sourceDescriptor = CustomConverter.getTypeDescriptor(this.sourceClazz, (String) fieldName); + return ConvertUtils.convert(value, sourceDescriptor, targetDescriptor); + } + } catch (Exception e) { + log.warn("Converter error", e); + } + return null; + } + + /** + * Gets type descriptor * + * + * @param clazz clazz + * @param fieldName field name + * @return the type descriptor + * @since 1.0.0 + */ + private static TypeDescriptor getTypeDescriptor(@NotNull Class clazz, String fieldName) { + String srcCacheKey = clazz.getName() + fieldName; + // 忽略抛出异常的函数,定义完整泛型,避免编译问题 + CheckedFunction uncheckedFunction = key -> { + // 这里 property 理论上不会为 null + Field field = ReflectionUtils.getField(clazz, fieldName); + if (field == null) { + throw new NoSuchFieldException(fieldName); + } + return new TypeDescriptor(field); + }; + return TYPE_CACHE.computeIfAbsent(srcCacheKey, Unchecked.function(uncheckedFunction)); + } +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/EnumToStringConverter.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/EnumToStringConverter.java new file mode 100644 index 0000000..b5f434d --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/convert/EnumToStringConverter.java @@ -0,0 +1,160 @@ +package com.aos.starter.core.convert; + +import com.google.common.collect.Maps; + +import com.aos.starter.core.util.ConvertUtils; +import com.fasterxml.jackson.annotation.JsonValue; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConditionalGenericConverter; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import lombok.extern.slf4j.Slf4j; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 接收参数 同 jackson Enum -> String 转换

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:05 + * @since 1.0.0 + */ +@Slf4j +public class EnumToStringConverter implements ConditionalGenericConverter { + /** 缓存 Enum 类信息,提供性能 */ + private static final ConcurrentMap, AccessibleObject> ENUM_CACHE_MAP = Maps.newConcurrentMap(); + + /** + * Matches boolean + * + * @param sourceType source type + * @param targetType target type + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean matches(@NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) { + return true; + } + + /** + * Gets convertible types * + * + * @return the convertible types + * @since 1.0.0 + */ + @Override + public Set getConvertibleTypes() { + Set pairSet = new HashSet<>(4); + pairSet.add(new ConvertiblePair(Enum.class, String.class)); + pairSet.add(new ConvertiblePair(Enum.class, Integer.class)); + pairSet.add(new ConvertiblePair(Enum.class, Long.class)); + return Collections.unmodifiableSet(pairSet); + } + + /** + * Convert object + * + * @param source source + * @param sourceType source type + * @param targetType target type + * @return the object + * @since 1.0.0 + */ + @Override + @SuppressWarnings("all") + public Object convert(@Nullable Object source, @NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) { + if (source == null) { + return null; + } + Class sourceClazz = sourceType.getType(); + AccessibleObject accessibleObject = ENUM_CACHE_MAP.computeIfAbsent(sourceClazz, EnumToStringConverter::getAnnotation); + Class targetClazz = targetType.getType(); + // 如果为null,走默认的转换 + if (accessibleObject == null) { + if (String.class == targetClazz) { + return ((Enum) source).name(); + } + int ordinal = ((Enum) source).ordinal(); + return ConvertUtils.convert(ordinal, targetClazz); + } + try { + return EnumToStringConverter.invoke(sourceClazz, accessibleObject, source, targetClazz); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * Gets annotation * + * + * @param clazz clazz + * @return the annotation + * @since 1.0.0 + */ + @Nullable + @SuppressWarnings("java:S3011") + private static AccessibleObject getAnnotation(@NotNull Class clazz) { + Set accessibleObjects = new HashSet<>(); + // JsonValue METHOD, FIELD + Field[] fields = clazz.getDeclaredFields(); + Collections.addAll(accessibleObjects, fields); + // methods + Method[] methods = clazz.getDeclaredMethods(); + Collections.addAll(accessibleObjects, methods); + for (AccessibleObject accessibleObject : accessibleObjects) { + // 复用 jackson 的 JsonValue 注解 + JsonValue jsonValue = accessibleObject.getAnnotation(JsonValue.class); + if (jsonValue != null && jsonValue.value()) { + accessibleObject.setAccessible(true); + return accessibleObject; + } + } + return null; + } + + /** + * Invoke object + * + * @param clazz clazz + * @param accessibleObject accessible object + * @param source source + * @param targetClazz target clazz + * @return the object + * @throws IllegalAccessException illegal access exception + * @throws InvocationTargetException invocation target exception + * @since 1.0.0 + */ + @Nullable + private static Object invoke(Class clazz, AccessibleObject accessibleObject, Object source, Class targetClazz) + throws IllegalAccessException, InvocationTargetException { + Object value = null; + if (accessibleObject instanceof Field) { + Field field = (Field) accessibleObject; + value = field.get(source); + } else if (accessibleObject instanceof Method) { + Method method = (Method) accessibleObject; + Class paramType = method.getParameterTypes()[0]; + // 类型转换 + Object object = ConvertUtils.convert(source, paramType); + value = method.invoke(clazz, object); + } + if (value == null) { + return null; + } + return ConvertUtils.convert(value, targetClazz); + } +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/enums/FieldFill.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/enums/FieldFill.java new file mode 100644 index 0000000..f9c4c47 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/enums/FieldFill.java @@ -0,0 +1,22 @@ +package com.aos.starter.core.enums; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 字段填充策略枚举类

+ * + * @author dong4j + * @version 1.3.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.04.08 12:41 + * @since 1.0.0 + */ +public enum FieldFill { + /** 默认不处理 */ + DEFAULT, + /** 插入时填充字段 */ + INSERT, + /** 更新时填充字段 */ + UPDATE, + /** 插入和更新时填充字段 */ + INSERT_UPDATE +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/jackson/DefaultBeanSerializerModifier.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/jackson/DefaultBeanSerializerModifier.java new file mode 100644 index 0000000..ad2e2f7 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/jackson/DefaultBeanSerializerModifier.java @@ -0,0 +1,154 @@ +package com.aos.starter.core.jackson; + +import com.aos.element.basic.basic.util.StringPool; +import com.aos.element.basic.basic.util.StringUtils; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; + + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.time.temporal.TemporalAccessor; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: jackson 默认值为 null 时的处理, 主要是为了避免 app 端出现null导致闪退 + * 规则: + * {@code + * number: 0 + * string: null + * date: null + * boolean: false + * array: [] + * Object: {} + * } + * todo-dong4j : (2020-07-10 14:5) [提供扩展接口, 可自定义 changeProperties ] + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 01:46 + * @since 1.0.0 + */ +public class DefaultBeanSerializerModifier extends com.fasterxml.jackson.databind.ser.BeanSerializerModifier { + /** + * Change properties list + * + * @param config config + * @param beanDesc bean desc + * @param beanProperties bean properties + * @return the list + * @see DefaultSerializerProvider#_serializeNull(JsonGenerator) + * @since 1.0.0 + */ + @Override + @SuppressWarnings("all") + public List changeProperties(SerializationConfig config, + BeanDescription beanDesc, + @NotNull List beanProperties) { + beanProperties.forEach(writer -> { + // 如果已经有 null 序列化处理如注解: @JsonSerialize(nullsUsing = xxx) 跳过 + if (writer.hasNullSerializer()) { + return; + } + JavaType type = writer.getType(); + Class clazz = type.getRawClass(); + if (type.isTypeOrSubTypeOf(Number.class)) { + // writer.assignNullSerializer(NullJsonSerializers.NUMBER_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(Boolean.class)) { + writer.assignNullSerializer(NullJsonSerializers.BOOLEAN_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(Character.class)) { + writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(String.class)) { + writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER); + } else if (type.isArrayType() || clazz.isArray() || type.isTypeOrSubTypeOf(Collection.class)) { + writer.assignNullSerializer(NullJsonSerializers.ARRAY_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(OffsetDateTime.class)) { + writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(Date.class) || type.isTypeOrSubTypeOf(TemporalAccessor.class)) { + writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER); + } else { + writer.assignNullSerializer(NullJsonSerializers.OBJECT_JSON_SERIALIZER); + } + }); + return super.changeProperties(config, beanDesc, beanProperties); + } + + /** + * The interface Null json serializers. + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 01:46 + * @since 1.0.0 + */ + @SuppressWarnings("checkstyle:InterfaceIsType") + public interface NullJsonSerializers { + + /** + * The constant STRING_JSON_SERIALIZER. + */ + JsonSerializer STRING_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(StringPool.EMPTY); + } + }; + + /** + * The constant NUMBER_JSON_SERIALIZER. + */ + JsonSerializer NUMBER_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeNumber(StringUtils.INDEX_NOT_FOUND); + } + }; + + /** + * The constant BOOLEAN_JSON_SERIALIZER. + */ + JsonSerializer BOOLEAN_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeObject(Boolean.FALSE); + } + }; + + /** + * The constant ARRAY_JSON_SERIALIZER. + */ + JsonSerializer ARRAY_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartArray(); + gen.writeEndArray(); + } + }; + + /** + * The constant OBJECT_JSON_SERIALIZER. + */ + JsonSerializer OBJECT_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeEndObject(); + } + }; + + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNode.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNode.java new file mode 100644 index 0000000..9be5471 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNode.java @@ -0,0 +1,39 @@ +package com.aos.starter.core.node; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 森林节点类

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.26 20:42 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class ForestNode extends BaseNode { + + /** + * 节点内容 + */ + private Object content; + + /** + * Instantiates a new Forest node. + * + * @param id the id + * @param parentId the parent id + * @param content the content + * @since 1.0.0 + */ + public ForestNode(Integer id, Integer parentId, Object content) { + this.id = id; + this.parentId = parentId; + this.content = content; + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNodeManager.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNodeManager.java new file mode 100644 index 0000000..479fa3f --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNodeManager.java @@ -0,0 +1,81 @@ +package com.aos.starter.core.node; + +import java.util.ArrayList; +import java.util.List; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 森林管理类

+ * + * @param the type parameter + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 14:53 + * @since 1.0.0 + */ +public class ForestNodeManager { + + /** + * 森林的所有节点 + */ + private final List list; + + /** + * 森林的父节点ID + */ + private final List parentIds = new ArrayList<>(); + + /** + * Instantiates a new Forest node manager. + * + * @param items the items + * @since 1.0.0 + */ + public ForestNodeManager(List items) { + list = items; + } + + /** + * 根据节点ID获取一个节点 + * + * @param id 节点ID + * @return 对应的节点对象 tree node at + * @since 1.0.0 + */ + public INode getTreeNodeAt(int id) { + for (INode forestNode : list) { + if (forestNode.getId() == id) { + return forestNode; + } + } + return null; + } + + /** + * 增加父节点ID + * + * @param parentId 父节点ID + * @since 1.0.0 + */ + public void addParentId(Integer parentId) { + parentIds.add(parentId); + } + + /** + * 获取树的根节点(一个森林对应多颗树) + * + * @return 树的根节点集合 root + * @since 1.0.0 + */ + public List getRoot() { + List roots = new ArrayList<>(); + for (T forestNode : list) { + if (forestNode.getParentId() == 0 || parentIds.contains(forestNode.getId())) { + roots.add(forestNode); + } + } + return roots; + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNodeMerger.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNodeMerger.java new file mode 100644 index 0000000..3c7ed8a --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/node/ForestNodeMerger.java @@ -0,0 +1,41 @@ +package com.aos.starter.core.node; + +import java.util.List; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 森林节点归并类

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.26 20:42 + * @since 1.0.0 + */ +public class ForestNodeMerger { + + /** + * 将节点数组归并为一个森林 (多棵树) (填充节点的children域) + * 时间复杂度为O(n^2) + * + * @param T 泛型标记 + * @param items 节点域 + * @return 多棵树的根节点集合 list + * @since 1.0.0 + */ + public static List merge(List items) { + ForestNodeManager forestNodeManager = new ForestNodeManager<>(items); + items.forEach(forestNode -> { + if (forestNode.getParentId() != 0) { + INode node = forestNodeManager.getTreeNodeAt(forestNode.getParentId()); + if (node != null) { + node.getChildren().add(forestNode); + } else { + forestNodeManager.addParentId(forestNode.getId()); + } + } + }); + return forestNodeManager.getRoot(); + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/DefaultMetaObject.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/DefaultMetaObject.java new file mode 100644 index 0000000..215c569 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/DefaultMetaObject.java @@ -0,0 +1,53 @@ +package com.aos.starter.core.reflection; + +import com.aos.starter.core.reflection.factory.DefaultObjectFactory; +import com.aos.starter.core.reflection.factory.ObjectFactory; +import com.aos.starter.core.reflection.wrapper.DefaultObjectWrapperFactory; +import com.aos.starter.core.reflection.wrapper.ObjectWrapperFactory; + +import org.jetbrains.annotations.Contract; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 默认的实体元数据包装对象, 使用 {@link DefaultMetaObject#forObject} 生成 {@link MetaObject}

+ * + * @author dong4j + * @version 1.3.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.04.12 11:30 + * @since 1.0.0 + */ +public final class DefaultMetaObject { + + /** DEFAULT_OBJECT_FACTORY */ + public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory(); + /** DEFAULT_OBJECT_WRAPPER_FACTORY */ + public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory(); + /** NULL_META_OBJECT */ + public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(Object.class, + DEFAULT_OBJECT_FACTORY, + DEFAULT_OBJECT_WRAPPER_FACTORY, + new DefaultReflectorFactory()); + + /** + * Default meta object + * + * @since 1.0.0 + */ + @Contract(pure = true) + private DefaultMetaObject() { + } + + /** + * For object meta object + * + * @param object object + * @return the meta object + * @since 1.0.0 + */ + @Contract("!null -> new") + public static MetaObject forObject(Object object) { + return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/DefaultReflectorFactory.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/DefaultReflectorFactory.java new file mode 100644 index 0000000..cf3cd82 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/DefaultReflectorFactory.java @@ -0,0 +1,61 @@ +package com.aos.starter.core.reflection; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.3.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.04.12 11:54 + * @since 1.0.0 + */ +public class DefaultReflectorFactory implements ReflectorFactory { + /** Reflector map */ + private final ConcurrentMap, Reflector> reflectorMap = new ConcurrentHashMap<>(); + /** Class cache enabled */ + private boolean classCacheEnabled = true; + + /** + * Is class cache enabled boolean + * + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean isClassCacheEnabled() { + return this.classCacheEnabled; + } + + /** + * Sets class cache enabled * + * + * @param classCacheEnabled class cache enabled + * @since 1.0.0 + */ + @Override + public void setClassCacheEnabled(boolean classCacheEnabled) { + this.classCacheEnabled = classCacheEnabled; + } + + /** + * Find for class reflector + * + * @param type type + * @return the reflector + * @since 1.0.0 + */ + @Override + public Reflector findForClass(Class type) { + if (this.classCacheEnabled) { + // synchronized (type) removed see issue #461 + return this.reflectorMap.computeIfAbsent(type, Reflector::new); + } else { + return new Reflector(type); + } + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/factory/DefaultObjectFactory.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/factory/DefaultObjectFactory.java new file mode 100644 index 0000000..c0f9c71 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/factory/DefaultObjectFactory.java @@ -0,0 +1,155 @@ +package com.aos.starter.core.reflection.factory; + + + +import com.aos.starter.core.reflection.ReflectionException; +import com.aos.starter.core.reflection.Reflector; + +import org.jetbrains.annotations.NotNull; + +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.stream.Collectors; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.3.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.04.12 11:46 + * @since 1.0.0 + */ +public class DefaultObjectFactory implements ObjectFactory, Serializable { + + /** serialVersionUID */ + private static final long serialVersionUID = -8855120656740914948L; + + /** + * Create t + * + * @param parameter + * @param type type + * @return the t + * @since 1.0.0 + */ + @Override + public T create(Class type) { + return this.create(type, null, null); + } + + /** + * Create t + * + * @param parameter + * @param type type + * @param constructorArgTypes constructor arg types + * @param constructorArgs constructor args + * @return the t + * @since 1.0.0 + */ + @SuppressWarnings("unchecked") + @Override + public T create(Class type, List> constructorArgTypes, List constructorArgs) { + Class classToCreate = this.resolveInterface(type); + // we know types are assignable + return (T) this.instantiateClass(classToCreate, constructorArgTypes, constructorArgs); + } + + /** + * Is collection boolean + * + * @param parameter + * @param type type + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean isCollection(Class type) { + return Collection.class.isAssignableFrom(type); + } + + /** + * Resolve interface class + * + * @param type type + * @return the class + * @since 1.0.0 + */ + protected Class resolveInterface(Class type) { + Class classToCreate; + if (type == List.class || type == Collection.class || type == Iterable.class) { + classToCreate = ArrayList.class; + } else if (type == Map.class) { + classToCreate = HashMap.class; + } else if (type == SortedSet.class) { + classToCreate = TreeSet.class; + } else if (type == Set.class) { + classToCreate = HashSet.class; + } else { + classToCreate = type; + } + return classToCreate; + } + + /** + * Instantiate class t + * + * @param parameter + * @param type type + * @param constructorArgTypes constructor arg types + * @param constructorArgs constructor args + * @return the t + * @since 1.0.0 + */ + @SuppressWarnings("all") + private @NotNull T instantiateClass(Class type, List> constructorArgTypes, List constructorArgs) { + try { + Constructor constructor; + if (constructorArgTypes == null || constructorArgs == null) { + constructor = type.getDeclaredConstructor(); + try { + return constructor.newInstance(); + } catch (IllegalAccessException e) { + if (Reflector.canControlMemberAccessible()) { + constructor.setAccessible(true); + return constructor.newInstance(); + } else { + throw e; + } + } + } + constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0])); + try { + return constructor.newInstance(constructorArgs.toArray(new Object[0])); + } catch (IllegalAccessException e) { + if (Reflector.canControlMemberAccessible()) { + constructor.setAccessible(true); + return constructor.newInstance(constructorArgs.toArray(new Object[0])); + } else { + throw e; + } + } + } catch (Exception e) { + String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList) + .stream().map(Class::getSimpleName).collect(Collectors.joining(",")); + String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList) + .stream().map(String::valueOf).collect(Collectors.joining(",")); + throw new ReflectionException("Error instantiating {} with invalid types ({}) or values ({}). Cause: ", + type, argTypes, argValues, e, e); + } + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/invoker/GetFieldInvoker.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/invoker/GetFieldInvoker.java new file mode 100644 index 0000000..82e49d9 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/invoker/GetFieldInvoker.java @@ -0,0 +1,67 @@ +package com.aos.starter.core.reflection.invoker; + +import com.aos.starter.core.reflection.Reflector; + +import org.jetbrains.annotations.Contract; + +import java.lang.reflect.Field; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.3.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.04.12 11:46 + * @since 1.0.0 + */ +public class GetFieldInvoker implements Invoker { + /** Field */ + private final Field field; + + /** + * Get field invoker + * + * @param field field + * @since 1.0.0 + */ + @Contract(pure = true) + public GetFieldInvoker(Field field) { + this.field = field; + } + + /** + * Invoke object + * + * @param target target + * @param args args + * @return the object + * @throws IllegalAccessException illegal access exception + * @since 1.0.0 + */ + @Override + public Object invoke(Object target, Object[] args) throws IllegalAccessException { + try { + return this.field.get(target); + } catch (IllegalAccessException e) { + if (Reflector.canControlMemberAccessible()) { + this.field.setAccessible(true); + return this.field.get(target); + } else { + throw e; + } + } + } + + /** + * Gets type * + * + * @return the type + * @since 1.0.0 + */ + @Override + public Class getType() { + return this.field.getType(); + } +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/wrapper/DefaultObjectWrapperFactory.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/wrapper/DefaultObjectWrapperFactory.java new file mode 100644 index 0000000..b935370 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/reflection/wrapper/DefaultObjectWrapperFactory.java @@ -0,0 +1,43 @@ +package com.aos.starter.core.reflection.wrapper; + +import com.aos.starter.core.reflection.MetaObject; +import com.aos.starter.core.reflection.ReflectionException; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.3.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.04.12 11:52 + * @since 1.0.0 + */ +public class DefaultObjectWrapperFactory implements ObjectWrapperFactory { + + /** + * Has wrapper for boolean + * + * @param object object + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean hasWrapperFor(Object object) { + return false; + } + + /** + * Gets wrapper for * + * + * @param metaObject meta object + * @param object object + * @return the wrapper for + * @since 1.0.0 + */ + @Override + public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) { + throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper."); + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/ConvertUtils.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/ConvertUtils.java new file mode 100644 index 0000000..1dfc5c0 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/ConvertUtils.java @@ -0,0 +1,97 @@ +package com.aos.starter.core.util; + +import com.aos.starter.core.convert.CustomConversionService; + +import org.jetbrains.annotations.Contract; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.lang.Nullable; + +import lombok.experimental.UtilityClass; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 基于 spring ConversionService 类型转换

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:18 + * @since 1.0.0 + */ +@UtilityClass +@SuppressWarnings("unchecked") +public class ConvertUtils { + + /** + * Convenience operation for converting a source object to the specified targetType. + * {@link TypeDescriptor#forObject(Object)}. + * + * @param 泛型标记 + * @param source the source object + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null}, or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _ -> null") + @Nullable + public static T convert(@Nullable Object source, Class targetType) { + if (source == null) { + return null; + } + if (ClassUtils.isAssignableValue(targetType, source)) { + return (T) source; + } + GenericConversionService conversionService = CustomConversionService.getInstance(); + return conversionService.convert(source, targetType); + } + + /** + * Convenience operation for converting a source object to the specified targetType, + * where the target type is a descriptor that provides additional conversion context. + * {@link TypeDescriptor#forObject(Object)}. + * + * @param 泛型标记 + * @param source the source object + * @param sourceType the source type + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null}, or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _, _ -> null") + @Nullable + public static T convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + if (source == null) { + return null; + } + GenericConversionService conversionService = CustomConversionService.getInstance(); + return (T) conversionService.convert(source, sourceType, targetType); + } + + /** + * Convenience operation for converting a source object to the specified targetType, + * where the target type is a descriptor that provides additional conversion context. + * Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and + * encapsulates the construction of the source type descriptor using + * {@link TypeDescriptor#forObject(Object)}. + * + * @param 泛型标记 + * @param source the source object + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null}, or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _ -> null") + @Nullable + public static T convert(@Nullable Object source, TypeDescriptor targetType) { + if (source == null) { + return null; + } + GenericConversionService conversionService = CustomConversionService.getInstance(); + return (T) conversionService.convert(source, targetType); + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DateTimeUtils.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DateTimeUtils.java new file mode 100644 index 0000000..6eff815 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DateTimeUtils.java @@ -0,0 +1,136 @@ +package com.aos.starter.core.util; + +import org.jetbrains.annotations.NotNull; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; + +import lombok.experimental.UtilityClass; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: DateTime 工具类

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:18 + * @since 1.0.0 + */ +@UtilityClass +public class DateTimeUtils { + /** + * The constant DATETIME_FORMAT. + */ + public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATETIME); + /** + * The constant DATE_FORMAT. + */ + public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATE); + /** + * The constant TIME_FORMAT. + */ + public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateUtils.PATTERN_TIME); + + /** + * 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDateTime(TemporalAccessor temporal) { + return DATETIME_FORMAT.format(temporal); + } + + /** + * 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDate(TemporalAccessor temporal) { + return DATE_FORMAT.format(temporal); + } + + /** + * 时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatTime(TemporalAccessor temporal) { + return TIME_FORMAT.format(temporal); + } + + /** + * 日期格式化 + * + * @param temporal 时间 + * @param pattern 表达式 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String format(TemporalAccessor temporal, String pattern) { + return DateTimeFormatter.ofPattern(pattern).format(temporal); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @return 时间 temporal accessor + * @since 1.0.0 + */ + @NotNull + public static TemporalAccessor parse(String dateStr, String pattern) { + DateTimeFormatter format = DateTimeFormatter.ofPattern(pattern); + return format.parse(dateStr); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param formatter DateTimeFormatter + * @return 时间 temporal accessor + * @since 1.0.0 + */ + @NotNull + public static TemporalAccessor parse(String dateStr, @NotNull DateTimeFormatter formatter) { + return formatter.parse(dateStr); + } + + /** + * 时间转 Instant + * + * @param dateTime 时间 + * @return Instant instant + * @since 1.0.0 + */ + public static Instant toInstant(@NotNull LocalDateTime dateTime) { + return dateTime.atZone(ZoneId.systemDefault()).toInstant(); + } + + /** + * Instant 转 时间 + * + * @param instant Instant + * @return Instant local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime toDateTime(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DateUtils.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DateUtils.java new file mode 100644 index 0000000..cf8d289 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DateUtils.java @@ -0,0 +1,827 @@ +package com.aos.starter.core.util; + +import com.aos.element.basic.basic.asserts.Assertions; +import com.aos.element.basic.basic.constant.ConfigDefaultValue; +import com.aos.element.basic.basic.util.ConcurrentDateFormat; +import com.aos.element.basic.basic.util.Exceptions; +import com.aos.element.basic.basic.util.StringPool; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.Assert; + +import java.text.ParseException; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAmount; +import java.time.temporal.TemporalQuery; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import lombok.experimental.UtilityClass; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 日期工具类

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.09 10:38 + * @since 1.0.0 + */ +@UtilityClass +@SuppressWarnings("checkstyle:MethodLimit") +public class DateUtils { + + /** PATTERN_MS_DATETIME */ + public static final String PATTERN_MS_DATETIME = ConfigDefaultValue.DEFAULT_DATE_FORMAT.concat(":SSS"); + /** The constant PATTERN_DATETIME. */ + public static final String PATTERN_DATETIME = ConfigDefaultValue.DEFAULT_DATE_FORMAT; + /** The constant PATTERN_DATE. */ + public static final String PATTERN_DATE = "yyyy-MM-dd"; + /** The constant PATTERN_DATE_HOUR. */ + public static final String PATTERN_DATE_HOUR = "yyyy-MM-dd HH"; + /** PATTERN_MONTH */ + public static final String PATTERN_MONTH = "yyyyMM"; + /** 时分秒 */ + public static final String PATTERN_TIME = "HH:mm:ss"; + /** PATTERN_DATE_NO_SEPARATOR */ + public static final String PATTERN_DATE_NO_SEPARATOR = "yyyyMMdd"; + /** PATTERN_TIME_NO_SEPARATOR */ + public static final String PATTERN_TIME_NO_SEPARATOR = "HHmmss"; + /** PATTERN_DATETIME_NO_SEPARATOR */ + public static final String PATTERN_DATETIME_NO_SEPARATOR = PATTERN_DATE_NO_SEPARATOR + PATTERN_TIME_NO_SEPARATOR; + /** 老 date 格式化 */ + public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME); + /** DATETIME_FORMAT_NO_SEPARATOR */ + public static final ConcurrentDateFormat DATETIME_FORMAT_NO_SEPARATOR = ConcurrentDateFormat.of(PATTERN_DATETIME_NO_SEPARATOR); + /** The constant DATE_FORMAT. */ + public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE); + /** DATE_FORMAT_NO_SEPARATOR */ + public static final ConcurrentDateFormat DATE_FORMAT_NO_SEPARATOR = ConcurrentDateFormat.of(PATTERN_DATE_NO_SEPARATOR); + /** The constant TIME_FORMAT. */ + public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME); + /** TIME_FORMAT_NO_SEPARATOR */ + public static final ConcurrentDateFormat TIME_FORMAT_NO_SEPARATOR = ConcurrentDateFormat.of(PATTERN_TIME_NO_SEPARATOR); + /** java 8 时间格式化 */ + public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATETIME); + /** DATETIME_FORMATTER_NO_SEPARATOR */ + public static final DateTimeFormatter DATETIME_FORMATTER_NO_SEPARATOR = + DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATETIME_NO_SEPARATOR); + /** The constant DATE_FORMATTER. */ + public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATE); + /** DATE_FORMATTER_NO_SEPARATOR */ + public static final DateTimeFormatter DATE_FORMATTER_NO_SEPARATOR = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATE_NO_SEPARATOR); + /** The constant TIME_FORMATTER. */ + public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtils.PATTERN_TIME); + /** TIME_FORMATTER_NO_SEPARATOR */ + public static final DateTimeFormatter TIME_FORMATTER_NO_SEPARATOR = DateTimeFormatter.ofPattern(DateUtils.PATTERN_TIME_NO_SEPARATOR); + + /** + * 添加年 + * + * @param date 时间 + * @param yearsToAdd 添加的年数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusYears(Date date, int yearsToAdd) { + return DateUtils.set(date, Calendar.YEAR, yearsToAdd); + } + + /** + * 设置日期属性 + * + * @param date 时间 + * @param calendarField 更改的属性 + * @param amount 更改数,-1表示减少 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + private static Date set(Date date, int calendarField, int amount) { + Assert.notNull(date, "The date must not be null"); + Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + /** + * 添加月 + * + * @param date 时间 + * @param monthsToAdd 添加的月数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusMonths(Date date, int monthsToAdd) { + return DateUtils.set(date, Calendar.MONTH, monthsToAdd); + } + + /** + * 添加周 + * + * @param date 时间 + * @param weeksToAdd 添加的周数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusWeeks(Date date, int weeksToAdd) { + return DateUtils.plus(date, Period.ofWeeks(weeksToAdd)); + } + + /** + * 日期添加时间量 + * + * @param date 时间 + * @param amount 时间量 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plus(@NotNull Date date, TemporalAmount amount) { + Instant instant = date.toInstant(); + return Date.from(instant.plus(amount)); + } + + /** + * 添加天 + * + * @param date 时间 + * @param daysToAdd 添加的天数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusDays(Date date, long daysToAdd) { + return DateUtils.plus(date, Duration.ofDays(daysToAdd)); + } + + /** + * 添加小时 + * + * @param date 时间 + * @param hoursToAdd 添加的小时数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusHours(Date date, long hoursToAdd) { + return DateUtils.plus(date, Duration.ofHours(hoursToAdd)); + } + + /** + * 添加分钟 + * + * @param date 时间 + * @param minutesToAdd 添加的分钟数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusMinutes(Date date, long minutesToAdd) { + return DateUtils.plus(date, Duration.ofMinutes(minutesToAdd)); + } + + /** + * 添加秒 + * + * @param date 时间 + * @param secondsToAdd 添加的秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusSeconds(Date date, long secondsToAdd) { + return DateUtils.plus(date, Duration.ofSeconds(secondsToAdd)); + } + + /** + * 添加毫秒 + * + * @param date 时间 + * @param millisToAdd 添加的毫秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusMillis(Date date, long millisToAdd) { + return DateUtils.plus(date, Duration.ofMillis(millisToAdd)); + } + + /** + * 添加纳秒 + * + * @param date 时间 + * @param nanosToAdd 添加的纳秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusNanos(Date date, long nanosToAdd) { + return DateUtils.plus(date, Duration.ofNanos(nanosToAdd)); + } + + /** + * 减少年 + * + * @param date 时间 + * @param years 减少的年数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusYears(Date date, int years) { + return DateUtils.set(date, Calendar.YEAR, -years); + } + + /** + * 减少月 + * + * @param date 时间 + * @param months 减少的月数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusMonths(Date date, int months) { + return DateUtils.set(date, Calendar.MONTH, -months); + } + + /** + * 减少周 + * + * @param date 时间 + * @param weeks 减少的周数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusWeeks(Date date, int weeks) { + return DateUtils.minus(date, Period.ofWeeks(weeks)); + } + + /** + * 日期减少时间量 + * + * @param date 时间 + * @param amount 时间量 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minus(@NotNull Date date, TemporalAmount amount) { + Instant instant = date.toInstant(); + return Date.from(instant.minus(amount)); + } + + /** + * 减少天 + * + * @param date 时间 + * @param days 减少的天数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusDays(Date date, long days) { + return DateUtils.minus(date, Duration.ofDays(days)); + } + + /** + * 减少小时 + * + * @param date 时间 + * @param hours 减少的小时数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusHours(Date date, long hours) { + return DateUtils.minus(date, Duration.ofHours(hours)); + } + + /** + * 减少分钟 + * + * @param date 时间 + * @param minutes 减少的分钟数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusMinutes(Date date, long minutes) { + return DateUtils.minus(date, Duration.ofMinutes(minutes)); + } + + /** + * 减少秒 + * + * @param date 时间 + * @param seconds 减少的秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusSeconds(Date date, long seconds) { + return DateUtils.minus(date, Duration.ofSeconds(seconds)); + } + + /** + * 减少毫秒 + * + * @param date 时间 + * @param millis 减少的毫秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusMillis(Date date, long millis) { + return DateUtils.minus(date, Duration.ofMillis(millis)); + } + + /** + * 减少纳秒 + * + * @param date 时间 + * @param nanos 减少的纳秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusNanos(Date date, long nanos) { + return DateUtils.minus(date, Duration.ofNanos(nanos)); + } + + /** + * 日期时间格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static String formatDateTime(Date date) { + return DATETIME_FORMAT.format(date); + } + + /** + * 日期格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static String formatDate(Date date) { + return DATE_FORMAT.format(date); + } + + /** + * 时间格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static String formatTime(Date date) { + return TIME_FORMAT.format(date); + } + + /** + * java8 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDateTime(TemporalAccessor temporal) { + return DATETIME_FORMATTER.format(temporal); + } + + /** + * java8 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDate(TemporalAccessor temporal) { + return DATE_FORMATTER.format(temporal); + } + + /** + * java8 时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatTime(TemporalAccessor temporal) { + return TIME_FORMATTER.format(temporal); + } + + /** + * java8 日期格式化 + * + * @param temporal 时间 + * @param pattern 表达式 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String format(TemporalAccessor temporal, String pattern) { + return DateTimeFormatter.ofPattern(pattern).format(temporal); + } + + /** + * Parse date time + * + * @param dateStr date str + * @return the date + * @since 1.6.0 + */ + public static Date parseDateTime(String dateStr) { + return parse(dateStr, PATTERN_DATETIME); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @return 时间 date + * @since 1.0.0 + */ + @NotNull + public static Date parse(String dateStr, String pattern) { + Assertions.notBlank(dateStr, "参数错误, 时间字符串不能为空"); + ConcurrentDateFormat format = ConcurrentDateFormat.of(pattern); + try { + return format.parse(dateStr); + } catch (ParseException e) { + throw Exceptions.unchecked(StringUtils.format("不能将 {} 以 {} 格式转换为 Date 类型", dateStr, pattern), e); + } + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param format ConcurrentDateFormat + * @return 时间 date + * @since 1.0.0 + */ + public static Date parse(String dateStr, @NotNull ConcurrentDateFormat format) { + try { + return format.parse(dateStr); + } catch (ParseException e) { + throw Exceptions.unchecked(StringUtils.format("不能将 {} 以 {} 格式转换为 Date 类型", dateStr, format.getFormat()), e); + } + } + + /** + * 将字符串转换为时间 + * + * @param the type parameter + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @param query the query + * @return 时间 t + * @since 1.0.0 + */ + public static T parse(String dateStr, String pattern, TemporalQuery query) { + return DateTimeFormatter.ofPattern(pattern).parse(dateStr, query); + } + + /** + * Instant 转 时间 + * + * @param instant Instant + * @return Instant local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime toDateTime(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + /** + * 转换成 date + * + * @param dateTime LocalDateTime + * @return Date date + * @since 1.0.0 + */ + @NotNull + public static Date toDate(LocalDateTime dateTime) { + return Date.from(DateUtils.toInstant(dateTime)); + } + + /** + * 时间转 Instant + * + * @param dateTime 时间 + * @return Instant instant + * @since 1.0.0 + */ + public static Instant toInstant(@NotNull LocalDateTime dateTime) { + return dateTime.atZone(ZoneId.systemDefault()).toInstant(); + } + + /** + * 转换成 date + * + * @param localDate LocalDate + * @return Date date + * @since 1.0.0 + */ + @NotNull + public static Date toDate(@NotNull LocalDate localDate) { + return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + + /** + * Converts local date time to Calendar. + * + * @param localDateTime the local date time + * @return the calendar + * @since 1.0.0 + */ + @NotNull + public static Calendar toCalendar(LocalDateTime localDateTime) { + return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault())); + } + + /** + * localDate 转换成毫秒数 + * + * @param localDate LocalDate + * @return long long + * @since 1.0.0 + */ + public static long toMilliseconds(@NotNull LocalDate localDate) { + return toMilliseconds(localDate.atStartOfDay()); + } + + /** + * localDateTime 转换成毫秒数 + * + * @param localDateTime LocalDateTime + * @return long long + * @since 1.0.0 + */ + public static long toMilliseconds(@NotNull LocalDateTime localDateTime) { + return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + /** + * 转换成java8 时间 + * + * @param calendar 日历 + * @return LocalDateTime local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime fromCalendar(@NotNull Calendar calendar) { + TimeZone tz = calendar.getTimeZone(); + ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId(); + return LocalDateTime.ofInstant(calendar.toInstant(), zid); + } + + /** + * 转换成java8 时间 + * + * @param instant Instant + * @return LocalDateTime local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime fromInstant(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + /** + * 转换成java8 时间 + * + * @param date Date + * @return LocalDateTime local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime fromDate(@NotNull Date date) { + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + } + + /** + * 转换成java8 时间 + * + * @param milliseconds 毫秒数 + * @return LocalDateTime local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime fromMilliseconds(long milliseconds) { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault()); + } + + /** + * 比较2个时间差,跨度比较小 + * + * @param startInclusive 开始时间 + * @param endExclusive 结束时间 + * @return 时间间隔 duration + * @since 1.0.0 + */ + public static Duration between(Temporal startInclusive, Temporal endExclusive) { + return Duration.between(startInclusive, endExclusive); + } + + /** + * 比较2个时间差,跨度比较大,年月日为单位 + * + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 时间间隔 period + * @since 1.0.0 + */ + public static Period between(LocalDate startDate, LocalDate endDate) { + return Period.between(startDate, endDate); + } + + /** + * 比较2个 时间差 + * + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 时间间隔 duration + * @since 1.0.0 + */ + public static Duration between(@NotNull Date startDate, @NotNull Date endDate) { + return Duration.between(startDate.toInstant(), endDate.toInstant()); + } + + /** + * Between + * + * @param startTime start time + * @param targetDate target date + * @param endTime end time + * @return the boolean + * @since 1.6.0 + */ + public static boolean between(Date startTime, Date targetDate, Date endTime) { + return startTime.getTime() <= targetDate.getTime() + && endTime.getTime() >= targetDate.getTime(); + } + + /** + * 将秒数转换为日时分秒 + * + * @param second 秒数 + * @return 时间 string + * @since 1.0.0 + */ + public static String secondToTime(Long second) { + // 判断是否为空 + if (second == null || second == 0L) { + return StringPool.EMPTY; + } + //转换天数 + long days = second / 86400; + //剩余秒数 + second = second % 86400; + //转换小时 + long hours = second / 3600; + //剩余秒数 + second = second % 3600; + //转换分钟 + long minutes = second / 60; + //剩余秒数 + second = second % 60; + if (days > 0) { + return StringUtils.format("{}天{}小时{}分{}秒", days, hours, minutes, second); + } else { + return StringUtils.format("{}小时{}分{}秒", hours, minutes, second); + } + } + + /** + * 将字符串格式的时间转换为 long + * + * @param dateString the date string + * @return the long + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static Long timeToSecond(String dateString) { + return parse(dateString, PATTERN_DATETIME).getTime(); + } + + /** + * Time to millisecond + * + * @param dateString date string + * @return the long + * @since 1.7.0 + */ + @NotNull + @Contract(pure = true) + public static Long timeToMillisecond(String dateString) { + return parse(dateString, PATTERN_MS_DATETIME).getTime(); + } + + /** + * 获取今天的日期 + * + * @return 时间 string + * @since 1.0.0 + */ + public static String today() { + return format(new Date(), PATTERN_DATE_NO_SEPARATOR); + } + + /** + * Now date + * + * @return the date + * @since 1.0.0 + */ + @NotNull + @Contract(" -> new") + public static Date now() { + return new Date(); + } + + /** + * 日期格式化 + * + * @param date 时间 + * @param pattern 表达式 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static String format(Date date, String pattern) { + return ConcurrentDateFormat.of(pattern).format(date); + } + + /** + * 获取当前时间到当日最后时间相差的分钟数 + * + * @param nowTime the now time + * @return int int + * @since 1.0.0 + */ + public static int diffMinutesTime(Date nowTime) { + nowTime = null == nowTime ? now() : nowTime; + + Calendar todayLastTime = DateUtils.getCurrentCalendar(); + todayLastTime.set(Calendar.HOUR_OF_DAY, 23); + todayLastTime.set(Calendar.MINUTE, 59); + todayLastTime.set(Calendar.SECOND, 59); + + return (int) ((todayLastTime.getTimeInMillis() - nowTime.getTime()) / 1000 / 60); + } + + /** + * Diff minutes time int + * + * @return the int + * @since 1.0.0 + */ + public static int diffMinutesTime() { + return diffMinutesTime(now()); + } + + /** + * Gets current calendar * + * + * @return the current calendar + * @since 1.0.0 + */ + private static Calendar getCurrentCalendar() { + Calendar calendar = Calendar.getInstance(); + TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); + calendar.setTimeZone(timeZone); + return calendar; + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DigestUtils.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DigestUtils.java new file mode 100644 index 0000000..ca152a4 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/DigestUtils.java @@ -0,0 +1,777 @@ +package com.aos.starter.core.util; + +import com.aos.element.basic.basic.util.Charsets; +import com.aos.element.basic.basic.util.Exceptions; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.lang.Nullable; + +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import lombok.experimental.UtilityClass; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 加密相关工具类

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:25 + * @since 1.0.0 + */ +@UtilityClass +@SuppressWarnings("all") +public class DigestUtils extends org.springframework.util.DigestUtils { + + /** + * HEX_VALUE + */ + private static final String HEX_VALUE = "0123456789abcdef"; + /** + * HEX_CODE + */ + private static final char[] HEX_CODE = HEX_VALUE.toCharArray(); + + /** + * Calculates the MD5 digest. + * + * @param data Data to digest + * @return MD5 digest as a hex array + * @since 1.0.0 + */ + public static byte[] md5(byte[] data) { + return org.springframework.util.DigestUtils.md5Digest(data); + } + + /** + * Calculates the MD5 digest. + * + * @param data Data to digest + * @return MD5 digest as a hex array + * @since 1.0.0 + */ + public static byte[] md5(@NotNull String data) { + return org.springframework.util.DigestUtils.md5Digest(data.getBytes(Charsets.UTF_8)); + } + + /** + * Return a hexadecimal string representation of the MD5 digest of the given bytes. + * + * @param bytes the bytes to calculate the digest over + * @return a hexadecimal digest string + * @since 1.0.0 + */ + @NotNull + public static String md5Hex(byte[] bytes) { + return org.springframework.util.DigestUtils.md5DigestAsHex(bytes); + } + + /** + * sha1Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha1Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha1(data.getBytes(Charsets.UTF_8))); + } + + /** + * encode Hex + * + * @param bytes Data to Hex + * @return bytes as a hex string + * @since 1.0.0 + */ + @NotNull + public static String encodeHex(byte[] bytes) { + StringBuilder r = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + r.append(HEX_CODE[(b >> 4) & 0xF]); + r.append(HEX_CODE[(b & 0xF)]); + } + return r.toString(); + } + + /** + * sha1 + * + * @param bytes Data to digest + * @return digest as a hex array + * @since 1.0.0 + */ + public static byte[] sha1(byte[] bytes) { + return DigestUtils.digest("SHA-1", bytes); + } + + /** + * digest + * + * @param algorithm 算法 + * @param bytes Data to digest + * @return digest byte array + * @since 1.0.0 + */ + public static byte[] digest(String algorithm, byte[] bytes) { + try { + MessageDigest md = MessageDigest.getInstance(algorithm); + return md.digest(bytes); + } catch (NoSuchAlgorithmException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * sha1Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha1Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha1(bytes)); + } + + /** + * SHA224 + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha224(@NotNull String data) { + return DigestUtils.sha224(data.getBytes(Charsets.UTF_8)); + } + + /** + * SHA224 + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha224(byte[] bytes) { + return DigestUtils.digest("SHA-224", bytes); + } + + /** + * SHA224Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha224Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha224(data.getBytes(Charsets.UTF_8))); + } + + /** + * SHA224Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha224Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha224(bytes)); + } + + /** + * sha256Hex + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha256(@NotNull String data) { + return DigestUtils.sha256(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha256Hex + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha256(byte[] bytes) { + return DigestUtils.digest("SHA-256", bytes); + } + + /** + * sha256Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha256Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha256(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha256Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha256Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha256(bytes)); + } + + /** + * sha384 + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha384(@NotNull String data) { + return DigestUtils.sha384(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha384 + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha384(byte[] bytes) { + return DigestUtils.digest("SHA-384", bytes); + } + + /** + * sha384Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha384Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha384(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha384Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha384Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha384(bytes)); + } + + /** + * sha512Hex + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha512(@NotNull String data) { + return DigestUtils.sha512(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha512Hex + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha512(byte[] bytes) { + return DigestUtils.digest("SHA-512", bytes); + } + + /** + * sha512Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha512Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha512(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha512Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha512Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha512(bytes)); + } + + /** + * digest Hex + * + * @param algorithm 算法 + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String digestHex(String algorithm, byte[] bytes) { + return DigestUtils.encodeHex(digest(algorithm, bytes)); + } + + /** + * hmacMd5 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacMd5(@NotNull String data, String key) { + return DigestUtils.hmacMd5(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacMd5 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacMd5(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacMD5", bytes, key); + } + + /** + * digest Hmac + * + * @param algorithm 算法 + * @param bytes Data to digest + * @param key the key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] digestHmac(String algorithm, byte[] bytes, @NotNull String key) { + SecretKey secretKey = new SecretKeySpec(key.getBytes(Charsets.UTF_8), algorithm); + try { + Mac mac = Mac.getInstance(secretKey.getAlgorithm()); + mac.init(secretKey); + return mac.doFinal(bytes); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * hmacMd5 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacMd5Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacMd5(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacMd5 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacMd5Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacMd5(bytes, key)); + } + + /** + * hmacSha1 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha1(@NotNull String data, String key) { + return DigestUtils.hmacSha1(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha1 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha1(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA1", bytes, key); + } + + /** + * hmacSha1 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha1Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha1(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha1 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha1Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha1(bytes, key)); + } + + /** + * hmacSha224 + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha224(@NotNull String data, String key) { + return DigestUtils.hmacSha224(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha224 + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha224(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA224", bytes, key); + } + + /** + * hmacSha224 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha224Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha224(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha224 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha224Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha224(bytes, key)); + } + + /** + * hmacSha256 + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha256(@NotNull String data, String key) { + return DigestUtils.hmacSha256(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha256 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha256(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA256", bytes, key); + } + + /** + * hmacSha256 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + @NotNull + public static String hmacSha256Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha256(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha256 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha256Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha256(bytes, key)); + } + + /** + * hmacSha384 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha384(@NotNull String data, String key) { + return DigestUtils.hmacSha384(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha384 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha384(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA384", bytes, key); + } + + /** + * hmacSha384 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha384Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha384(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha384 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha384Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha384(bytes, key)); + } + + /** + * hmacSha512 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha512(@NotNull String data, String key) { + return DigestUtils.hmacSha512(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha512 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha512(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA512", bytes, key); + } + + /** + * hmacSha512 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha512Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha512(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha512 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha512Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha512(bytes, key)); + } + + /** + * digest Hmac Hex + * + * @param algorithm 算法 + * @param bytes Data to digest + * @param key the key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String digestHmacHex(String algorithm, byte[] bytes, String key) { + return DigestUtils.encodeHex(DigestUtils.digestHmac(algorithm, bytes, key)); + } + + /** + * decode Hex + * + * @param hexStr Hex string + * @return decode hex to bytes + * @since 1.0.0 + */ + @SuppressWarnings("PMD.UndefineMagicConstantRule") + public static byte[] decodeHex(@NotNull String hexStr) { + int len = hexStr.length(); + if ((len & 0x01) != 0) { + throw new IllegalArgumentException("hexBinary needs to be even-length: " + hexStr); + } + String hexText = hexStr.toLowerCase(); + byte[] out = new byte[len >> 1]; + for (int i = 0; i < len; i += 2) { + int hn = HEX_VALUE.indexOf(hexText.charAt(i)); + int ln = HEX_VALUE.indexOf(hexText.charAt(i + 1)); + if (hn == -1 || ln == -1) { + throw new IllegalArgumentException("contains illegal character for hexBinary: " + hexStr); + } + out[i / 2] = (byte) ((hn << 4) | ln); + } + return out; + } + + /** + * 比较字符串,避免字符串因为过长,产生耗时 + * + * @param a String + * @param b String + * @return 是否相同 boolean + * @since 1.0.0 + */ + @Contract("null, _ -> false; !null, null -> false") + public static boolean slowEquals(@Nullable String a, @Nullable String b) { + if (a == null || b == null) { + return false; + } + return DigestUtils.slowEquals(a.getBytes(Charsets.UTF_8), b.getBytes(Charsets.UTF_8)); + } + + /** + * 比较 byte 数组,避免字符串因为过长,产生耗时 + * + * @param a byte array + * @param b byte array + * @return 是否相同 boolean + * @since 1.0.0 + */ + @Contract(value = "null, _ -> false; !null, null -> false", pure = true) + public static boolean slowEquals(@Nullable byte[] a, @Nullable byte[] b) { + if (a == null || b == null) { + return false; + } + if (a.length != b.length) { + return false; + } + int diff = a.length ^ b.length; + for (int i = 0; i < a.length; i++) { + diff |= a[i] ^ b[i]; + } + return diff == 0; + } + + /** + * 自定义加密 先MD5再SHA1 + * + * @param data 数据 + * @return String string + * @since 1.0.0 + */ + @NotNull + public static String encrypt(String data) { + return DigestUtils.encodeHex(sha1(md5Hex(data))); + } + + /** + * sha1 + * + * @param data Data to digest + * @return digest as a hex array + * @since 1.0.0 + */ + public static byte[] sha1(@NotNull String data) { + return DigestUtils.sha1(data.getBytes(Charsets.UTF_8)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data Data to digest + * @return MD5 digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String md5Hex(@NotNull String data) { + return org.springframework.util.DigestUtils.md5DigestAsHex(data.getBytes(Charsets.UTF_8)); + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/FileUtils.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/FileUtils.java new file mode 100644 index 0000000..1867780 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/FileUtils.java @@ -0,0 +1,704 @@ +package com.aos.starter.core.util; + + +import com.aos.element.basic.basic.asserts.Assertions; +import com.aos.element.basic.basic.constant.ConfigKey; +import com.aos.element.basic.basic.util.CharPool; +import com.aos.element.basic.basic.util.Charsets; +import com.aos.element.basic.basic.util.Exceptions; +import com.aos.element.basic.basic.util.IoUtils; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.FileSystemUtils; +import org.springframework.util.PatternMatchUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 文件工具类

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:26 + * @since 1.0.0 + */ +@Slf4j +@UtilityClass +@SuppressWarnings("checkstyle:MethodLimit") +public class FileUtils extends org.springframework.util.FileCopyUtils { + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(String path) { + File file = new File(path); + return list(file, TrueFilter.TRUE); + } + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @param fileNamePattern 文件名 * 号 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(String path, String fileNamePattern) { + File file = new File(path); + return list(file, pathname -> { + String fileName = pathname.getName(); + return PatternMatchUtils.simpleMatch(fileNamePattern, fileName); + }); + } + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @param filter 文件过滤 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(String path, FileFilter filter) { + File file = new File(path); + return list(file, filter); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(File file) { + List fileList = new ArrayList<>(); + return list(file, fileList, TrueFilter.TRUE); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param fileNamePattern Spring AntPathMatcher 规则 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(File file, String fileNamePattern) { + List fileList = new ArrayList<>(); + return list(file, fileList, pathname -> { + String fileName = pathname.getName(); + return PatternMatchUtils.simpleMatch(fileNamePattern, fileName); + }); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param filter 文件过滤 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(File file, FileFilter filter) { + List fileList = new ArrayList<>(); + return list(file, fileList, filter); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param fileList file list + * @param filter 文件过滤 + * @return 文件集合 list + * @since 1.0.0 + */ + @Contract("_, _, _ -> param2") + private static List list(@NotNull File file, List fileList, FileFilter filter) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + for (File f : files) { + list(f, fileList, filter); + } + } + } else { + // 过滤文件 + boolean accept = filter.accept(file); + if (file.exists() && accept) { + fileList.add(file); + } + } + return fileList; + } + + /** + * 获取文件后缀名 + * + * @param fullName 文件全名 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String getFileExtension(String fullName) { + Assertions.notNull(fullName, "file fullName is null."); + String fileName = new File(fullName).getName(); + int dotIndex = fileName.lastIndexOf('.'); + return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); + } + + /** + * 获取文件名,去除后缀名 + * + * @param file 文件 + * @return {String} + * @since 1.0.0 + */ + public static @NotNull String getNameWithoutExtension(String file) { + Assertions.notNull(file, "file is null."); + String fileName = new File(file).getName(); + int dotIndex = fileName.lastIndexOf(CharPool.DOT); + return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); + } + + /** + * Returns a {@link File} representing the system temporary directory. + * + * @return the system temporary directory. + * @since 1.0.0 + */ + @NotNull + @Contract(" -> new") + public static File getTempDir() { + return new File(getTempDirPath()); + } + + /** + * Returns the path to the system temporary directory. + * + * @return the path to the system temporary directory. + * @since 1.0.0 + */ + public static String getTempDirPath() { + return System.getProperty(ConfigKey.JvmConfigKey.TMP_DIR); + } + + /** + * 拼接临时文件目录. + * + * @param subDirFile sub dir file + * @return 临时文件目录. string + * @since 1.0.0 + */ + @NotNull + public static String toTempDirPath(String subDirFile) { + return FileUtils.toTempDir(subDirFile).getAbsolutePath(); + } + + /** + * 拼接临时文件目录. + * + * @param subDirFile sub dir file + * @return the system temporary directory. + * @since 1.0.0 + */ + public static @NotNull File toTempDir(@NotNull String subDirFile) { + String tempDirPath = FileUtils.getTempDirPath(); + // 删除子目录目录分隔符 + if (subDirFile.startsWith(File.separator)) { + subDirFile = subDirFile.substring(1); + } + // 为临时目录最后添加目录分隔符 + if (!tempDirPath.endsWith(File.separator)) { + tempDirPath += File.separator; + } + String fullPath = tempDirPath.concat(subDirFile); + File fullFilePath = new File(fullPath); + File dir = fullFilePath.getParentFile(); + if (!dir.exists() && dir.mkdirs()) { + log.debug("{} 不存在, 创建成功", dir); + } + return fullFilePath; + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + @NotNull + public static String readToString(File file) { + return readToString(file, Charsets.UTF_8); + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @param encoding the encoding to use, {@code null} means platform default + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + @NotNull + public static String readToString(File file, Charset encoding) { + try (InputStream in = Files.newInputStream(file.toPath())) { + return IoUtils.toString(in, encoding); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + @NotNull + public static byte[] readToByteArray(File file) { + try (InputStream in = Files.newInputStream(file.toPath())) { + return IoUtils.toByteArray(in); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @since 1.0.0 + */ + public static void writeToFile(File file, String data) { + writeToFile(file, data, Charsets.UTF_8, false); + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param encoding the encoding to use, {@code null} means platform default + * @param append if {@code true}, then the String will be added to the end of the file rather than overwriting + * @since 1.0.0 + */ + public static void writeToFile(File file, String data, Charset encoding, boolean append) { + try (OutputStream out = new FileOutputStream(file, append)) { + IoUtils.write(data, out, encoding); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param append if {@code true}, then the String will be added to the end of the file rather than overwriting + * @since 1.0.0 + */ + public static void writeToFile(File file, String data, boolean append) { + writeToFile(file, data, Charsets.UTF_8, append); + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param encoding the encoding to use, {@code null} means platform default + * @since 1.0.0 + */ + public static void writeToFile(File file, String data, Charset encoding) { + writeToFile(file, data, encoding, false); + } + + /** + * 转成file + * + * @param multipartFile MultipartFile + * @param file File + * @since 1.0.0 + */ + public static void toFile(@NotNull MultipartFile multipartFile, File file) { + try { + FileUtils.toFile(multipartFile.getInputStream(), file); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * 转成file + * + * @param in InputStream + * @param file File + * @since 1.0.0 + */ + public static void toFile(InputStream in, File file) { + try (OutputStream out = new FileOutputStream(file)) { + FileUtils.copy(in, out); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Moves a file. + *

+ * When the destination file is on another file system, do a "copy and delete". + * + * @param srcFile the file to be moved + * @param destFile the destination file + * @throws IOException if source or destination is invalid + * @since 1.0.0 + */ + public static void moveFile(File srcFile, File destFile) throws IOException { + Assert.notNull(srcFile, "Source must not be null"); + Assert.notNull(destFile, "Destination must not be null"); + if (!srcFile.exists()) { + throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); + } + if (srcFile.isDirectory()) { + throw new IOException("Source '" + srcFile + "' is a directory"); + } + if (destFile.exists()) { + throw new IOException("Destination '" + destFile + "' already exists"); + } + if (destFile.isDirectory()) { + throw new IOException("Destination '" + destFile + "' is a directory"); + } + boolean rename = srcFile.renameTo(destFile); + if (!rename) { + FileUtils.copy(srcFile, destFile); + if (!srcFile.delete()) { + FileUtils.deleteQuietly(destFile); + throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'"); + } + } + } + + /** + * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. + *

+ * The difference between File.delete() and this method are: + *

    + *
  • A directory to be deleted does not have to be empty.
  • + *
  • No exceptions are thrown when a file or directory cannot be deleted.
  • + *
+ * + * @param file file or directory to delete, can be {@code null} + * @return {@code true} if the file or directory was deleted, otherwise {@code false} + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean deleteQuietly(@Nullable File file) { + if (file == null) { + return false; + } + try { + if (file.isDirectory()) { + FileSystemUtils.deleteRecursively(file); + } + } catch (Exception ignored) { + } + + try { + return file.delete(); + } catch (Exception ignored) { + return false; + } + } + + /** + * NIO 按行读取文件 + * + * @param path 文件路径 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(String path) { + return readLines(Paths.get(path)); + } + + /** + * NIO 按行读取文件 + * + * @param path 文件路径 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(Path path) { + return readLines(path, Charsets.UTF_8); + } + + /** + * NIO 按行读取文件 + * + * @param path 文件路径 + * @param cs 字符集 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(Path path, Charset cs) { + try { + return Files.readAllLines(path, cs); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * NIO 按行读取文件 + * + * @param file 文件 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(@NotNull File file) { + return readLines(file.toPath()); + } + + /** + * NIO 按行读取文件 + * + * @param path 文件路径 + * @param cs 字符集 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(String path, Charset cs) { + return readLines(Paths.get(path), cs); + } + + /** + * NIO 按行读取文件 + * + * @param file 文件 + * @param cs 字符集 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(@NotNull File file, Charset cs) { + return readLines(file.toPath(), cs); + } + + /** + * 转换成不同平台的下的路径 + * + * @param path the path + * @return the string + * @since 1.0.0 + */ + @NotNull + public static String getRealFilePath(@NotNull String path) { + return path.replace("/", File.separator).replace("\\", File.separator); + } + + /** + * 拼接不同平台下的文件路径, 末尾不含路径分隔符 + * + * @param paths paths + * @return the string + * @since 1.0.0 + */ + @NotNull + public static String appendPath(@NotNull String... paths) { + // 删除前缀后后缀 + for (int i = 0; i < paths.length; i++) { + paths[i] = StringUtils.removeSuffix(paths[i], File.separator); + // 第一个路径不删除前缀 + if (i == 0) { + continue; + } + paths[i] = StringUtils.removePrefix(paths[i], File.separator); + } + + return String.join(File.separator, paths); + } + + /** + * 递归删除指定目录及文件 + * + * @param first first + * @param more more + * @return the boolean + * @since 1.0.0 + */ + @SneakyThrows + public static boolean deleteFiles(String first, String... more) { + Path start = Paths.get(first, more); + if (start.toFile().exists()) { + Files.walkFileTree(start, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + try { + Files.delete(file); + } catch (IOException ignored) { + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException e) { + if (e == null) { + try { + Files.delete(dir); + } catch (IOException ignored) { + } + return FileVisitResult.CONTINUE; + } + // 如果存在异常, 则说明文件不存在 + return FileVisitResult.SKIP_SUBTREE; + } + }); + } + + return true; + } + + /** + * 创建文件路径且处理 path 最后一个路径分隔符 + * + * @param path path + * @return the string + * @since 1.4.0 + */ + public static @NotNull String toPath(String path) { + File logFile = new File(path); + if (!logFile.exists() && logFile.mkdirs()) { + log.info(String.format("创建日志目录: %s", logFile.getPath())); + } + return logFile.getPath(); + } + + /** + * 默认为true + * + * @author L.cm + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:26 + * @since 1.0.0 + */ + public static class TrueFilter implements FileFilter, Serializable { + /** + * The constant TRUE. + */ + public static final TrueFilter TRUE = new TrueFilter(); + /** serialVersionUID */ + private static final long serialVersionUID = -6420452043795072619L; + + /** + * Accept boolean + * + * @param pathname pathname + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean accept(File pathname) { + return true; + } + } + + /** + * Touch + * + * @param file file + * @throws IOException io exception + * @since 1.5.0 + */ + public static void touch(@NotNull File file) throws IOException { + if (!file.exists()) { + OutputStream out = openOutputStream(file); + IoUtils.closeQuietly(out); + } + + boolean success = file.setLastModified(System.currentTimeMillis()); + if (!success) { + throw new IOException("Unable to set the last modification time for " + file); + } + } + + /** + * Open output stream + * + * @param file file + * @return the file output stream + * @throws IOException io exception + * @since 1.5.0 + */ + public static @NotNull FileOutputStream openOutputStream(File file) throws IOException { + return openOutputStream(file, false); + } + + /** + * Open output stream + * + * @param file file + * @param append append + * @return the file output stream + * @throws IOException io exception + * @since 1.5.0 + */ + @Contract("_, _ -> new") + public static @NotNull FileOutputStream openOutputStream(@NotNull File file, boolean append) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + + if (!file.canWrite()) { + throw new IOException("File '" + file + "' cannot be written to"); + } + } else { + File parent = file.getParentFile(); + if (parent != null && !parent.mkdirs() && !parent.isDirectory()) { + throw new IOException("Directory '" + parent + "' could not be created"); + } + } + + return new FileOutputStream(file, append); + } + +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/Holder.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/Holder.java new file mode 100644 index 0000000..4be4e36 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/Holder.java @@ -0,0 +1,23 @@ +package com.aos.starter.core.util; + +import java.security.SecureRandom; +import java.util.Random; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 一些常用的单例对象

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:17 + * @since 1.0.0 + */ +public final class Holder { + + /** RANDOM */ + public static final Random RANDOM = new Random(); + + /** SECURE_RANDOM */ + public static final SecureRandom SECURE_RANDOM = new SecureRandom(); +} diff --git a/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/HttpClientUtils.java b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/HttpClientUtils.java new file mode 100644 index 0000000..71351f7 --- /dev/null +++ b/aos-element/aos-element-core/src/main/java/com/aos/starter/core/util/HttpClientUtils.java @@ -0,0 +1,297 @@ +package com.aos.starter.core.util; + +import com.aos.element.basic.basic.util.Charsets; +import com.aos.element.basic.basic.util.HttpsUtils; +import com.aos.element.basic.basic.util.JsonUtils; + +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.jetbrains.annotations.NotNull; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.client.DefaultResponseErrorHandler; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; +import java.nio.charset.Charset; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; + +import lombok.experimental.UtilityClass; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.3.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.03.04 14:02 + * @since 1.0.0 + */ +@UtilityClass +public class HttpClientUtils { + + /** 最大连接数 */ + private static final int MAX_CONNECTION_TOTAL = 300; + /** 路由并发数 */ + private static final int ROUTE_MAX_COUNT = 200; + /** 重试次数 */ + private static final int RETRY_COUNT = 3; + /** 连接超时 */ + private static final int CONNECTION_TIME_OUT = 45000; + /** 数据超时 */ + private static final int READ_TIME_OUT = 75000; + /** 连接等待 */ + private static final int CONNECTION_REQUEST_TIME_OUT = 5000; + /** 编码 */ + private static final String CHARSET = Charsets.UTF_8_NAME; + + /** + * Post for object t + * + * @param parameter + * @param url url + * @param requestBody request body + * @param contentType content type + * @param responseType response type + * @return the t + * @throws Exception exception + * @since 1.0.0 + */ + public static T postForObject(String url, + Object requestBody, + MediaType contentType, + Class responseType) throws Exception { + return postForObjectWithHeader(url, requestBody, contentType, responseType, null); + } + + /** + * Post for object with header t + * + * @param parameter + * @param url url + * @param requestBody request body + * @param contentType content type + * @param responseType response type + * @param headers headers + * @return the t + * @throws Exception exception + * @since 1.0.0 + */ + public static T postForObjectWithHeader(String url, + Object requestBody, + MediaType contentType, + Class responseType, + Map headers) throws Exception { + try (CloseableHttpClient httpClient = acceptsUntrustedCertsHttpClient()) { + + RestTemplate restTemplate = getRestTemplate(httpClient); + + // headers + HttpHeaders httpHeaders = getDefaultHeader(contentType); + if (!Objects.isNull(headers)) { + fillHeaders(httpHeaders, headers); + } + + HttpEntity httpEntity = new HttpEntity<>(requestBody, httpHeaders); + return restTemplate.postForObject(url, httpEntity, responseType); + } catch (Exception e) { + throw new Exception("网络异常或请求错误.", e); + } + } + + /** + * Gets for object with header * + * + * @param parameter + * @param url url + * @param responseType response type + * @param headerName header name + * @param headerValue header value + * @return the for object with header + * @throws Exception exception + * @since 1.0.0 + */ + public static T getForObjectWithHeader(String url, + Class responseType, + String headerName, + String headerValue) throws Exception { + HttpHeaders httpHeaders = getDefaultHeader(MediaType.APPLICATION_JSON); + httpHeaders.add(headerName, headerValue); + return getForObject(url, responseType, httpHeaders); + } + + /** + * Gets for object * + * + * @param parameter + * @param url url + * @param responseType response type + * @return the for object + * @throws Exception exception + * @since 1.0.0 + */ + public static T getForObject(String url, Class responseType) throws Exception { + return getForObject(url, responseType, getDefaultHeader(MediaType.APPLICATION_JSON)); + } + + /** + * Gets default header * + * + * @param contentType content type + * @return the default header + * @since 1.0.0 + */ + private static HttpHeaders getDefaultHeader(MediaType contentType) { + HttpHeaders headers = new HttpHeaders(); + List acceptableMediaTypes = new ArrayList<>(); + acceptableMediaTypes.add(MediaType.ALL); + headers.setAccept(acceptableMediaTypes); + headers.setContentType(contentType); + headers.add("Connection", "Keep-Alive"); + headers.add("Content-Encoding", "gzip"); + headers.add("Vary", "Accept-Encoding"); + headers.add("Transfer-Encoding", "chunked"); + return headers; + } + + /** + * Gets for object * + * + * @param parameter + * @param url url + * @param responseType response type + * @param httpHeaders http headers + * @return the for object + * @throws Exception exception + * @since 1.0.0 + */ + private static T getForObject(String url, Class responseType, HttpHeaders httpHeaders) throws Exception { + try (CloseableHttpClient httpClient = acceptsUntrustedCertsHttpClient()) { + + RestTemplate restTemplate = getRestTemplate(httpClient); + + HttpEntity httpEntity = new HttpEntity<>(null, httpHeaders); + + URI uri = new URI(url); + String responseStr = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, String.class).getBody(); + + return JsonUtils.parse(responseStr, responseType); + } catch (Exception e) { + throw new Exception("网络异常或请求错误.", e); + } + } + + /** + * Gets rest template * + * + * @param httpClient http client + * @return the rest template + * @since 1.0.0 + */ + @NotNull + private static RestTemplate getRestTemplate(CloseableHttpClient httpClient) { + HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + clientHttpRequestFactory.setConnectTimeout(CONNECTION_TIME_OUT); + clientHttpRequestFactory.setReadTimeout(READ_TIME_OUT); + clientHttpRequestFactory.setConnectionRequestTimeout(CONNECTION_REQUEST_TIME_OUT); + clientHttpRequestFactory.setBufferRequestBody(false); + + RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory); + restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); + + List> messageConverters = restTemplate.getMessageConverters(); + messageConverters.forEach(m -> { + if (m instanceof StringHttpMessageConverter) { + ((StringHttpMessageConverter) m).setDefaultCharset(Charset.forName(CHARSET)); + } + }); + restTemplate.setMessageConverters(messageConverters); + return restTemplate; + } + + /** + * 接受未信任的请求 + * + * @return closeable http client + * @throws KeyStoreException key store exception + * @throws NoSuchAlgorithmException no such algorithm exception + * @throws KeyManagementException key management exception + * @since 1.0.0 + */ + public static CloseableHttpClient acceptsUntrustedCertsHttpClient() + throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); + SSLContext sslContext = HttpsUtils.getSslContext(); + + httpClientBuilder.setSSLContext(sslContext); + HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE; + SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier); + Registry socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", sslSocketFactory) + .build(); + + PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + connMgr.setMaxTotal(MAX_CONNECTION_TOTAL); + connMgr.setDefaultMaxPerRoute(ROUTE_MAX_COUNT); + + httpClientBuilder.setConnectionManager(connMgr); + httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(RETRY_COUNT, true)); + httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()); + + return httpClientBuilder.build(); + } + + /** + * Gets for object with header * + * + * @param parameter + * @param url url + * @param headers headers + * @param responseType response type + * @return the for object with header + * @throws Exception exception + * @since 1.0.0 + */ + public static T getForObjectWithHeader(String url, Map headers, Class responseType) throws Exception { + HttpHeaders httpHeaders = getDefaultHeader(MediaType.APPLICATION_JSON); + fillHeaders(httpHeaders, headers); + return getForObject(url, responseType, httpHeaders); + } + + /** + * Fill headers * + * + * @param httpHeaders http headers + * @param headers headers + * @since 1.0.0 + */ + private static void fillHeaders(HttpHeaders httpHeaders, @NotNull Map headers) { + headers.forEach((key, value1) -> Stream.of(value1).forEach(value -> httpHeaders.add(key, value))); + } +} diff --git a/aos-element/aos-element-core/src/main/resources/messages/CoreBundle.properties b/aos-element/aos-element-core/src/main/resources/messages/CoreBundle.properties new file mode 100644 index 0000000..2fa5fbe --- /dev/null +++ b/aos-element/aos-element-core/src/main/resources/messages/CoreBundle.properties @@ -0,0 +1,16 @@ +code.default.null.data=\u6682\u65E0\u6570\u636E +code.param.verify.error=\u53C2\u6570\u6821\u9A8C\u5931\u8D25: [{}] +code.data.error=\u6570\u636E\u4E0D\u5B58\u5728 +code.option.failure=\u64CD\u4F5C\u5931\u8D25 +code.config.error=\u914D\u7F6E\u9519\u8BEF +code.server.inner.error=\u670D\u52A1\u5185\u90E8\u9519\u8BEF +code.service.invoke.error=\u670D\u52A1\u4E0D\u53EF\u7528 +code.client.invoke.error=Rest Client \u8C03\u7528\u5931\u8D25 +code.agent.disable.error=Agent Service \u4E0D\u53EF\u7528 +code.agent.not.found.error=\u672A\u627E\u5230\u6307\u5B9A\u670D\u52A1 +code.rpc.invoke.error=\u8FDC\u7A0B\u670D\u52A1\u4E0D\u53EF\u7528 +code.gateway.instances.error=\u7F51\u5173\u8DEF\u7531\u5931\u8D25, \u672A\u627E\u5230\u6307\u5B9A\u670D\u52A1 +code.gateway.router.error=\u7F51\u5173\u8DEF\u7531\u914D\u7F6E\u9519\u8BEF: 1.\u8BF7\u68C0\u67E5\u8C03\u7528\u7AEF\u914D\u7F6E fkh.gateway.enable-router \u662F\u5426\u4E3A true; 2.\u68C0\u67E5\u8DEF\u7531\u914D\u7F6E\u662F\u5426\u6B63\u786E. +code.server.busy=\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528,\u8BF7\u7A0D\u540E\u91CD\u8BD5! +code.server.error=\u7F51\u7EDC\u5F02\u5E38 + diff --git a/aos-element/aos-element-devtools/src/main/resources/templates/dto.java.vm b/aos-element/aos-element-devtools/src/main/resources/templates/dto.java.vm new file mode 100644 index 0000000..8a5b65d --- /dev/null +++ b/aos-element/aos-element-devtools/src/main/resources/templates/dto.java.vm @@ -0,0 +1,34 @@ +package ${cfg.package_dto}; + +import com.fkhwl.starter.common.base.BaseDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: $!{table.comment} 数据传输实体 (根据业务需求添加字段)

+ * + * @author ${author} + * @version ${cfg.version} + * @email "mailto:${author}@fkhwl.com" + * @date ${cfg.date} + * @since ${cfg.version} + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ${entity}DTO extends BaseDTO { + private static final long serialVersionUID = 1L; + + /** todo: [自动生成的字段, 避免此实体没有字段导致启动失败的问题, 可删除] */ + private String autoField; +} diff --git a/aos-element/aos-element-devtools/src/main/resources/templates/entity.java.vm b/aos-element/aos-element-devtools/src/main/resources/templates/entity.java.vm new file mode 100644 index 0000000..77a4994 --- /dev/null +++ b/aos-element/aos-element-devtools/src/main/resources/templates/entity.java.vm @@ -0,0 +1,128 @@ +package ${cfg.package_po}; + +#foreach($pkg in ${table.importPackages}) +import ${pkg}; +#end +#if(${entityLombokModel}) + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +#end + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: $!{table.comment} 实体类

+ * + * @author ${author} + * @version ${cfg.version} + * @email "mailto:${author}@fkhwl.com" + * @date ${cfg.date} + * @since ${cfg.version} + */ +#if(${entityLombokModel}) +@Data +@AllArgsConstructor +@NoArgsConstructor +#if(${superEntityClass}) +@EqualsAndHashCode(callSuper = true) +#end +@Accessors(chain = true) +#end +#if(${table.convert}) +@TableName("${table.name}") +#end +#if(${superEntityClass}) +public class ${entity} extends ${superEntityClass}#if(${activeRecord})#end { +#elseif(${activeRecord}) +public class ${entity} extends Model<${entity}> { +#else +public class ${entity} implements Serializable { +#end + +#if(${entityColumnConstant}) +#foreach($field in ${table.fields}) + public static final String ${field.name.toUpperCase()} = "${field.name}"; +#end +#end + private static final long serialVersionUID = 1L; + +## ---------- BEGIN 字段循环遍历 ---------- +#foreach($field in ${table.fields}) +#if(${field.keyFlag}) +#set($keyPropertyName=${field.propertyName}) +#end +#if("$!field.comment" != "") + /** ${field.comment} */ +#end +#if(${field.keyFlag}) +## 主键 +#if(${field.keyIdentityFlag}) + @TableId(value = "${field.name}", type = IdType.AUTO) +#elseif(!$null.isNull(${idType}) && "$!idType" != "") + @TableId(value = "${field.name}", type = IdType.${idType}) +#elseif(${field.convert}) + @TableId("${field.name}") +#end +## 普通字段 +#elseif(${field.fill}) +## ----- 存在字段填充设置 ----- +#if(${field.convert}) + @TableField(value = "${field.name}", fill = FieldFill.${field.fill}) +#else + @TableField(fill = FieldFill.${field.fill}) +#end +#elseif(${field.convert}) + @TableField("${field.name}") +#end +## 乐观锁注解 +#if(${versionFieldName}==${field.name}) + @Version +#end +## 逻辑删除注解 +#if(${logicDeleteFieldName}==${field.name}) + @TableLogic +#end + private ${field.propertyType} ${field.propertyName}; +#end +## ---------- END 字段循环遍历 ---------- + +#if(!${entityLombokModel}) +#foreach($field in ${table.fields}) +#if(${field.propertyType.equals("boolean")}) +#set($getprefix="is") +#else +#set($getprefix="get") +#end + public ${field.propertyType} ${getprefix}${field.capitalName}() { + return ${field.propertyName}; + } +#if(${entityBuilderModel}) + public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { +#else + public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { +#end + this.${field.propertyName} = ${field.propertyName}; +#if(${entityBuilderModel}) + return this; +#end + } +#end +#end +#if(!${entityLombokModel}) + @Override + public String toString() { + return "${entity}{" + +#foreach($field in ${table.fields}) +#if($!{velocityCount}==1) + "${field.propertyName}=" + ${field.propertyName} + +#else + ", ${field.propertyName}=" + ${field.propertyName} + +#end +#end + "}"; + } +#end +} diff --git a/aos-element/aos-element-devtools/src/main/resources/templates/form.java.vm b/aos-element/aos-element-devtools/src/main/resources/templates/form.java.vm new file mode 100644 index 0000000..faec89f --- /dev/null +++ b/aos-element/aos-element-devtools/src/main/resources/templates/form.java.vm @@ -0,0 +1,34 @@ +package ${cfg.package_form}; + +import com.fkhwl.starter.common.base.BaseForm; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: $!{table.comment} 入参实体 (根据业务需求添加字段)

+ * + * @author ${author} + * @version ${cfg.version} + * @email "mailto:${author}@fkhwl.com" + * @date ${cfg.date} + * @since ${cfg.version} + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ${entity}Form extends BaseForm { + private static final long serialVersionUID = 1L; + + /** todo: [自动生成的字段, 避免此实体没有字段导致启动失败的问题, 可删除] */ + private String autoField; +}