init
This commit is contained in:
26
common/common-code/pom.xml
Normal file
26
common/common-code/pom.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>common</artifactId>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-code</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.fateverse.common.code;
|
||||
|
||||
import cn.fateverse.common.code.config.JavaCodeProperties;
|
||||
import cn.fateverse.common.code.engine.JavaCodeEngine;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-10-30 23:09
|
||||
*/
|
||||
@EnableConfigurationProperties({JavaCodeProperties.class})
|
||||
public class JavaCodeAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public JavaCodeEngine javaCodeEngine(JavaCodeProperties javaCodeProperties){
|
||||
return new JavaCodeEngine(javaCodeProperties);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.fateverse.common.code.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-10-30 23:09
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "code.java")
|
||||
public class JavaCodeProperties {
|
||||
|
||||
private String classPath;
|
||||
|
||||
public String getClassPath() {
|
||||
return classPath;
|
||||
}
|
||||
|
||||
public void setClassPath(String classPath) {
|
||||
this.classPath = classPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package cn.fateverse.common.code.engine;
|
||||
|
||||
|
||||
import cn.fateverse.common.code.config.JavaCodeProperties;
|
||||
import cn.fateverse.common.code.exception.SandboxClassNotFoundException;
|
||||
import cn.fateverse.common.code.lock.SegmentLock;
|
||||
import cn.fateverse.common.code.sandbox.SandboxClassLoader;
|
||||
import cn.fateverse.common.code.sandbox.SandboxSecurityManager;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-10-24
|
||||
*/
|
||||
@Slf4j
|
||||
public class JavaCodeEngine {
|
||||
|
||||
private final String JAVA_SUFFIX = ".java";
|
||||
|
||||
private final String CLASS_SUFFIX = ".class";
|
||||
|
||||
private final String CLASS_PATH;
|
||||
|
||||
private final URL url;
|
||||
|
||||
private final URLClassLoader classLoader;
|
||||
|
||||
private final Map<String, Class<?>> classCache = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
private final SandboxSecurityManager securityManager = new SandboxSecurityManager(classCache);
|
||||
|
||||
// 获取Java编译器
|
||||
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
|
||||
public JavaCodeEngine(JavaCodeProperties javaCodeProperties) {
|
||||
try {
|
||||
CLASS_PATH = javaCodeProperties.getClassPath();
|
||||
File file = new File(CLASS_PATH);
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
url = file.toURI().toURL();
|
||||
classLoader = new SandboxClassLoader(new URL[]{url});
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 执行方法
|
||||
*
|
||||
* @param code 代码字符串
|
||||
* @param className 类名
|
||||
* @param methodName 方法名
|
||||
* @param paramClass 参数类型数组
|
||||
* @param args 参数数组
|
||||
* @param development 是否为开发环境 开发环境下会将生成的类在执行完成后删除,不是生产环境则会缓存提高运行效率
|
||||
* @return 执行结果
|
||||
*/
|
||||
public <T> T execute(String code, String className, String methodName, Class<?>[] paramClass, Object[] args, boolean development) {
|
||||
if (development) {
|
||||
return developmentExecute(code, className, methodName, paramClass, args);
|
||||
} else {
|
||||
return onlineExecute(code, className, methodName, paramClass, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用于在开发环境中执行代码的私有方法。
|
||||
*
|
||||
* @param code 需要执行的代码字符串
|
||||
* @param className 类名
|
||||
* @param methodName 方法名
|
||||
* @param paramClass 参数类型数组
|
||||
* @param args 参数数组
|
||||
* @param <T> 接收泛型
|
||||
* @return 执行结构
|
||||
*/
|
||||
@SneakyThrows
|
||||
private <T> T developmentExecute(String code, String className, String methodName, Class<?>[] paramClass, Object[] args) {
|
||||
Class<?> loadClass = null;
|
||||
try {
|
||||
// 加锁,确保类只加载一次
|
||||
loadClass = SegmentLock.lock(className, () -> {
|
||||
URLClassLoader tempClassLoader = null;
|
||||
try {
|
||||
// 创建一个URLClassLoader,用于加载代码字符串
|
||||
tempClassLoader = new URLClassLoader(new URL[]{url});
|
||||
// 编译代码字符串为类
|
||||
Class<?> tempClass = compilerClass(className, code, tempClassLoader);
|
||||
// 将编译好的类放入缓存
|
||||
classCache.put(className, tempClass);
|
||||
return tempClass;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 异常处理,并抛出自定义的SandboxClassNotFoundException异常
|
||||
throw new SandboxClassNotFoundException(e.getMessage());
|
||||
} finally {
|
||||
if (tempClassLoader != null) {
|
||||
tempClassLoader = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
// 获取需要执行的方法
|
||||
Method method = loadClass.getMethod(methodName, paramClass);
|
||||
// 设置安全检查器
|
||||
System.setSecurityManager(securityManager);
|
||||
// 执行方法并返回结果
|
||||
return (T) method.invoke(null, args);
|
||||
} finally {
|
||||
// 从缓存中移除编译好的类
|
||||
classCache.remove(className);
|
||||
// 清空安全检查器
|
||||
System.setSecurityManager(null);
|
||||
if (loadClass != null) {
|
||||
loadClass = null;
|
||||
}
|
||||
// 删除生成的java文件
|
||||
File javaFile = new File(CLASS_PATH + className + JAVA_SUFFIX);
|
||||
if (javaFile.exists()) {
|
||||
javaFile.delete();
|
||||
}
|
||||
// 删除生成的class文件
|
||||
File classFile = new File(CLASS_PATH + className + CLASS_SUFFIX);
|
||||
if (classFile.exists()) {
|
||||
classFile.delete();
|
||||
}
|
||||
// 执行垃圾回收
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 线上环境执行
|
||||
*
|
||||
* @param code 需要执行的代码字符串
|
||||
* @param className 类名
|
||||
* @param methodName 方法名
|
||||
* @param paramClass 参数类型数组
|
||||
* @param args 参数数组
|
||||
* @param <T> 接收泛型
|
||||
* @return 执行结构
|
||||
*/
|
||||
private <T> T onlineExecute(String code, String className, String methodName, Class<?>[] paramClass, Object[] args) {
|
||||
try {
|
||||
Class<?> loadClass = null;
|
||||
loadClass = classCache.get(className);
|
||||
if (loadClass == null) {
|
||||
loadClass = getLoadClass(code, className);
|
||||
Method method = loadClass.getMethod(methodName, paramClass);
|
||||
System.setSecurityManager(securityManager);
|
||||
return (T) method.invoke(null, args);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
System.setSecurityManager(null);
|
||||
File javaFile = new File(CLASS_PATH + className + JAVA_SUFFIX);
|
||||
if (javaFile.exists()) {
|
||||
javaFile.delete();
|
||||
}
|
||||
File classFile = new File(CLASS_PATH + className + CLASS_SUFFIX);
|
||||
if (classFile.exists()) {
|
||||
classFile.delete();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到编译完成的Class对象
|
||||
*
|
||||
* @param code 需要编译的代码
|
||||
* @param className 类名
|
||||
* @return 编译后的Java对象
|
||||
*/
|
||||
private Class<?> getLoadClass(String code, String className) {
|
||||
//使用分段锁,提高效率,放多并发情况下多次对同一个类进行编译
|
||||
return SegmentLock.lock(className, () -> {
|
||||
try {
|
||||
//执行编译
|
||||
Class<?> tempClass = compilerClass(className, code, classLoader);
|
||||
//将编译之后的类对象放在缓存中,提高线上环境的运行效率
|
||||
classCache.put(className, tempClass);
|
||||
return tempClass;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编译Java代码
|
||||
*
|
||||
* @param className 类名
|
||||
* @param code Java代码
|
||||
* @param classLoader 类加载器
|
||||
* @return 编译完成的类对象
|
||||
*/
|
||||
private Class<?> compilerClass(String className, String code, URLClassLoader classLoader) {
|
||||
log.info(code);
|
||||
File tempFile = new File(CLASS_PATH + className + JAVA_SUFFIX);
|
||||
try (FileWriter writer = new FileWriter(tempFile)) {
|
||||
writer.write(code);
|
||||
writer.close();
|
||||
// 编译.java文件
|
||||
compiler.run(null, null, null, tempFile.getPath());
|
||||
return classLoader.loadClass(className);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.fateverse.common.code.engine;
|
||||
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
/**
|
||||
* js 工具类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-24
|
||||
*/
|
||||
public class JavaScriptEngine {
|
||||
|
||||
/**
|
||||
* 执行js代码
|
||||
* @param script js脚本
|
||||
* @param function js函数名
|
||||
* @param args 参数
|
||||
* @return 返回结构
|
||||
* @param <T> 泛型类型
|
||||
*/
|
||||
public static <T> T executeScript(String script, String function, Object... args) {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName("JavaScript");
|
||||
try {
|
||||
engine.eval(script);
|
||||
Invocable inv = (Invocable) engine;
|
||||
return (T) inv.invokeFunction(function, args);
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.fateverse.common.code.exception;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-10-25
|
||||
*/
|
||||
public class SandboxClassNotFoundException extends CustomException {
|
||||
|
||||
public SandboxClassNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.fateverse.common.code.lock;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-10-25
|
||||
*/
|
||||
public class SegmentLock {
|
||||
|
||||
private static final Map<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 分段锁
|
||||
* @param key 锁名称
|
||||
* @param supplier 需要执行的函数
|
||||
* @return 执行后的结果
|
||||
* @param <T> 接收泛型
|
||||
*/
|
||||
public static <T> T lock(String key, Supplier<T> supplier) {
|
||||
ReentrantLock lock = lockMap.get(key);
|
||||
if (lock == null) {
|
||||
lock = lockMap.get(key);
|
||||
if (lock == null) {
|
||||
synchronized (lockMap) {
|
||||
lock = new ReentrantLock();
|
||||
lockMap.put(key, lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
lock.lock();
|
||||
try {
|
||||
return supplier.get();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.fateverse.common.code.sandbox;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
public class SandboxClassLoader extends URLClassLoader {
|
||||
|
||||
|
||||
public SandboxClassLoader(final URL[] urls) {
|
||||
super(urls);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package cn.fateverse.common.code.sandbox;
|
||||
|
||||
import java.io.FilePermission;
|
||||
import java.lang.reflect.ReflectPermission;
|
||||
import java.security.Permission;
|
||||
import java.util.Map;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.Set;
|
||||
|
||||
public class SandboxSecurityManager extends SecurityManager {
|
||||
|
||||
private final Map<String, Class<?>> classCache;
|
||||
|
||||
public SandboxSecurityManager(Map<String, Class<?>> classCache) {
|
||||
this.classCache = classCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkPermission(Permission perm) {
|
||||
if (isSandboxCode(perm)) {
|
||||
if (!isAllowedPermission(perm)) {
|
||||
throw new SecurityException("Permission denied " + perm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isSandboxCode(Permission perm) {
|
||||
Set<String> classKeySet = classCache.keySet();
|
||||
for (String key : classKeySet) {
|
||||
if (perm.getName().contains(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean isAllowedPermission(Permission permission) {
|
||||
//权限:用于校验文件系统访问权限,包括读取、写入、删除文件,以及目录操作。权限名称可能包括文件路径和操作,如 "read", "write", "delete", "execute" 等。
|
||||
if (permission instanceof FilePermission){
|
||||
System.out.println("触发文件读写权限");
|
||||
return false;
|
||||
}
|
||||
//权限:用于校验运行时权限,如程序启动、关闭虚拟机等。您可以根据名称进行控制,如 "exitVM"、"setSecurityManager" 等。
|
||||
if (permission instanceof RuntimePermission) {
|
||||
System.out.println("用于校验运行时权限");
|
||||
return false;
|
||||
}
|
||||
//权限:用于校验Java反射操作的权限,如 `suppressAccessChecks`、`newProxyInPackage` 等。
|
||||
if (permission instanceof ReflectPermission) {
|
||||
System.out.println("用于校验Java反射操作的权限");
|
||||
return false;
|
||||
}
|
||||
//权限:用于校验系统属性的权限,包括读取和设置系统属性。权限名称通常以属性名称和操作(如 "read" 或 "write")表示。
|
||||
if (permission instanceof PropertyPermission) {
|
||||
System.out.println("用于校验系统属性的权限");
|
||||
return false;
|
||||
}
|
||||
// 权限:用于校验数据库访问权限,包括连接数据库、执行SQL语句等。权限名称通常与数据库URL和操作相关。
|
||||
// if (permission instanceof SQLPermission) {
|
||||
// return false;
|
||||
//
|
||||
// }
|
||||
//权限:用于校验网络套接字的权限,包括连接到特定主机和端口。权限名称通常以主机名和端口号的形式表示,如 "www.example.com:80".
|
||||
// if (permission instanceof SocketPermission) {
|
||||
// return false;
|
||||
//
|
||||
// }
|
||||
//权限:用于校验安全管理器操作的权限,如 `createAccessControlContext`、`setPolicy` 等。
|
||||
// if (permission instanceof SecurityPermission) {
|
||||
// return false;
|
||||
//
|
||||
// }
|
||||
// //序列化
|
||||
// if (permission instanceof SerializablePermission) {
|
||||
// return false;
|
||||
// }
|
||||
// System.out.println(permission);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
cn.fateverse.common.code.JavaCodeAutoConfiguration
|
||||
71
common/common-core/pom.xml
Normal file
71
common/common-core/pom.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>common</artifactId>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-core</artifactId>
|
||||
|
||||
<description>common-core核心模块</description>
|
||||
|
||||
<properties>
|
||||
<guava.version>30.1-jre</guava.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!-- Alibaba Fastjson -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- Apache Lang3 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.fateverse.common.core.annotaion;
|
||||
|
||||
import cn.fateverse.common.core.enums.MethodEnum;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* mybatis自动注入时间
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-05-05
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AutoTime {
|
||||
|
||||
/**
|
||||
* sql 方法 INSERT,UPDATE
|
||||
* @return 方法类型
|
||||
*/
|
||||
MethodEnum method();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.fateverse.common.core.annotaion;
|
||||
|
||||
import cn.fateverse.common.core.enums.AutoUserEnum;
|
||||
import cn.fateverse.common.core.enums.MethodEnum;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* mybatis自动注入用户信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-05-05
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AutoUser {
|
||||
|
||||
AutoUserEnum value() default AutoUserEnum.USER_NAME;
|
||||
|
||||
/**
|
||||
* sql 方法 INSERT,UPDATE
|
||||
*
|
||||
* @return 方法类型
|
||||
*/
|
||||
MethodEnum method();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.fateverse.common.core.annotaion;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 开启mybatis自动注入的能力
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-05-05
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EnableAutoField {
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package cn.fateverse.common.core.annotaion;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户实体类需要excel导出的字段
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/19
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface Excel {
|
||||
/**
|
||||
* 字段描述
|
||||
*/
|
||||
String value() default "";
|
||||
/**
|
||||
* 字段排序
|
||||
*/
|
||||
int order() default Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* 时间格式
|
||||
*/
|
||||
String dateFormat() default "yyyy-MM-dd";
|
||||
|
||||
/**
|
||||
* 字典类型
|
||||
*/
|
||||
String dictType() default "";
|
||||
|
||||
/**
|
||||
* 导出时在excel中每个列的高度 单位为字符
|
||||
* 占时未实现后其迭代
|
||||
*/
|
||||
double height() default 14;
|
||||
|
||||
/**
|
||||
* 导出时在excel中每个列的宽 单位为字符
|
||||
* 占时未实现后其迭代
|
||||
*/
|
||||
double width() default 16;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.fateverse.common.core.annotaion;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 用于标注当前对象中存在@Excel注解标注的需要导出的字段
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/19
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface Excels {
|
||||
|
||||
/**
|
||||
* 字段描述
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
|
||||
/**
|
||||
* 字段排序
|
||||
*/
|
||||
int order() default Integer.MAX_VALUE;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.fateverse.common.core.annotaion;
|
||||
|
||||
|
||||
import cn.fateverse.common.core.enums.GenIdEnum;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自动生成id
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-05-05
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface GenerateId {
|
||||
|
||||
GenIdEnum idType() default GenIdEnum.UUID;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.fateverse.common.core.constant;
|
||||
|
||||
/**
|
||||
* 缓存key
|
||||
* @author Clay
|
||||
* @date 2022/11/9
|
||||
*/
|
||||
public class CacheConstants {
|
||||
|
||||
/**
|
||||
* 验证码 redis key
|
||||
*/
|
||||
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
|
||||
/**
|
||||
* 路由缓存地址
|
||||
*/
|
||||
public static final String ROUTE_CACHE_KEY = "router_key:";
|
||||
/**
|
||||
* 登录用户 redis key
|
||||
*/
|
||||
public static final String LOGIN_TOKEN_KEY = "login_info:";
|
||||
/**
|
||||
* 字典类型 redis key
|
||||
*/
|
||||
public static final String DICT_KEY = "dict:";
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.fateverse.common.core.constant;
|
||||
|
||||
/**
|
||||
* 通用常量信息
|
||||
*
|
||||
* @author Clay
|
||||
*/
|
||||
public class Constants {
|
||||
/**
|
||||
* 令牌前缀
|
||||
*/
|
||||
public static final String TOKEN_PREFIX = "Bearer ";
|
||||
/**
|
||||
* http请求
|
||||
*/
|
||||
public static final String HTTP = "http://";
|
||||
|
||||
/**
|
||||
* https请求
|
||||
*/
|
||||
public static final String HTTPS = "https://";
|
||||
|
||||
|
||||
/**
|
||||
* 令牌前缀
|
||||
*/
|
||||
public static final String LOGIN_USER_KEY = "login_user_key";
|
||||
|
||||
/**
|
||||
* 验证码有效期(分钟)
|
||||
*/
|
||||
public static final long CAPTCHA_EXPIRATION = 2;
|
||||
|
||||
|
||||
public static final String UTF8 = "UTF-8";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.fateverse.common.core.constant;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-05-25
|
||||
*/
|
||||
public class DateConstants {
|
||||
|
||||
public static String YYYY = "yyyy";
|
||||
|
||||
public static String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cn.fateverse.common.core.constant;
|
||||
|
||||
/**
|
||||
* 用户常量信息
|
||||
*
|
||||
* @author Clay
|
||||
*/
|
||||
public class UserConstants {
|
||||
|
||||
/**
|
||||
* 平台内系统用户的唯一标志
|
||||
*/
|
||||
public static final String SYS_USER = "SYS_USER";
|
||||
|
||||
/**
|
||||
* 匿名用户
|
||||
*/
|
||||
public static final String ANONYMOUS_USER = "anonymousUser";
|
||||
|
||||
/**
|
||||
* 正常状态
|
||||
*/
|
||||
public static final String NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 异常状态
|
||||
*/
|
||||
public static final String EXCEPTION = "1";
|
||||
|
||||
|
||||
/**
|
||||
* 部门停用状态
|
||||
*/
|
||||
public static final String DEPT_DISABLE = "1";
|
||||
|
||||
|
||||
/**
|
||||
* Layout组件标识
|
||||
*/
|
||||
public final static String LAYOUT = "Layout";
|
||||
|
||||
/**
|
||||
* 校验返回结果码
|
||||
*/
|
||||
public final static String UNIQUE = "0";
|
||||
|
||||
public final static String NOT_UNIQUE = "1";
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package cn.fateverse.common.core.entity;
|
||||
|
||||
import cn.fateverse.common.core.annotaion.AutoTime;
|
||||
import cn.fateverse.common.core.annotaion.AutoUser;
|
||||
import cn.fateverse.common.core.enums.MethodEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
@AutoUser(method = MethodEnum.INSERT)
|
||||
private Object createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@AutoTime(method = MethodEnum.INSERT)
|
||||
@JsonFormat(locale = "zh",timezone = "GMT+8",pattern = "yyyy-MM-dd")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
@AutoUser(method = MethodEnum.UPDATE)
|
||||
private Object updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@AutoTime(method = MethodEnum.UPDATE)
|
||||
@JsonFormat(locale = "zh",timezone = "GMT+8",pattern = "yyyy-MM-dd")
|
||||
private Date updateTime;
|
||||
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
|
||||
public Object getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(Object createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public Date getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(Date createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public Object getUpdateBy() {
|
||||
return updateBy;
|
||||
}
|
||||
|
||||
public void setUpdateBy(Object updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
}
|
||||
|
||||
public Date getUpdateTime() {
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
public void setUpdateTime(Date updateTime) {
|
||||
this.updateTime = updateTime;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cn.fateverse.common.core.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-11-14 21:11
|
||||
*/
|
||||
@Data
|
||||
public class IdWrapper implements Serializable {
|
||||
|
||||
private Long id;
|
||||
|
||||
private List<Long> ids;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.fateverse.common.core.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/4
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Option {
|
||||
/**
|
||||
* 节点ID
|
||||
*/
|
||||
private Object value;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String label;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package cn.fateverse.common.core.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Treeselect树结构实体类
|
||||
*
|
||||
* @author Clay
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OptionTree implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* 节点ID
|
||||
*/
|
||||
private Object value;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private Object label;
|
||||
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private List<OptionTree> children;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.fateverse.common.core.entity;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
@Data
|
||||
public class PageInfo {
|
||||
|
||||
/**
|
||||
* 当前记录起始索引
|
||||
*/
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
private String orderByColumn;
|
||||
|
||||
/**
|
||||
* 排序的方向desc或者asc
|
||||
*/
|
||||
private String isAsc = "asc";
|
||||
|
||||
/**
|
||||
* 分页参数合理化
|
||||
*/
|
||||
private Boolean reasonable = true;
|
||||
|
||||
public String getOrderBy() {
|
||||
if (StrUtil.isEmpty(orderByColumn)) {
|
||||
return "";
|
||||
}
|
||||
return StrUtil.toUnderlineCase(orderByColumn) + " " + isAsc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.fateverse.common.core.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/9
|
||||
*/
|
||||
@Data
|
||||
public class QueryTime implements Serializable {
|
||||
|
||||
/**
|
||||
* 筛选创建时间的开始时间
|
||||
*/
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 筛选创建时间的结束时间
|
||||
*/
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.fateverse.common.core.enums;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-05-05
|
||||
*/
|
||||
public enum AutoUserEnum {
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
USER_NAME,
|
||||
USER_ID,
|
||||
NICK_NAME,
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.fateverse.common.core.enums;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-05-05
|
||||
*/
|
||||
public enum GenIdEnum {
|
||||
/**
|
||||
* id生成类型
|
||||
*/
|
||||
UUID,
|
||||
SNOWFLAKE
|
||||
;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.fateverse.common.core.enums;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/9
|
||||
*/
|
||||
public enum MenuEnum {
|
||||
|
||||
/**
|
||||
* 状态信息
|
||||
*/
|
||||
DIRECTORY("D", "目录"),
|
||||
MENU("M", "菜单"),
|
||||
BUTTON("B", "按钮"),
|
||||
LAYOUT("LAYOUT", "Layout"),
|
||||
PARENT_VIEW("PARENT_VIEW", "ParentView"),
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
MenuEnum(String code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.fateverse.common.core.enums;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-05-05
|
||||
*/
|
||||
public enum MethodEnum {
|
||||
|
||||
/**
|
||||
* 需要设置的方法
|
||||
*/
|
||||
UPDATE,
|
||||
INSERT,
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package cn.fateverse.common.core.enums;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-05-10
|
||||
*/
|
||||
public enum ResultEnum {
|
||||
/**
|
||||
* 返回状态枚举
|
||||
*/
|
||||
SUCCESS(1000, "操作成功", HttpStatus.OK),
|
||||
|
||||
NO_DATA(1001, "查询结果为空", HttpStatus.OK),
|
||||
|
||||
RESUBMIT_LOCK(2002, "重复提交", HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
|
||||
ERROR(2000, "操作失败", HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
|
||||
SYS_ERROR(2001, "系统异常", HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
|
||||
SENTINEL_FLOW(3000, "限流了", HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
|
||||
SENTINEL_PARAM_FLOW(3000, "热点参数限流", HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
|
||||
SENTINEL_SYSTEM(3000, "系统规则负载等不满足要求", HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
|
||||
SENTINEL_AUTHORITY(3000, "授权规则不通过", HttpStatus.UNAUTHORIZED),
|
||||
|
||||
SENTINEL_DEGRADE(3000, "降级了", HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
;
|
||||
|
||||
ResultEnum(int code, String msg, HttpStatus status) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public final int code;
|
||||
|
||||
public final String msg;
|
||||
|
||||
public final transient HttpStatus status;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.fateverse.common.core.enums;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/6
|
||||
*/
|
||||
public enum StateEnum {
|
||||
|
||||
/**
|
||||
* 状态信息
|
||||
*/
|
||||
NORMAL("1", "正常"),
|
||||
DISABLE("0", "停用");
|
||||
|
||||
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
StateEnum(String code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.fateverse.common.core.enums;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
public enum UserState {
|
||||
/**
|
||||
* 用户状态信息
|
||||
*/
|
||||
DISABLE("0", "停用"),
|
||||
OK("1", "正常"),
|
||||
DELETED("2", "删除");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
UserState(String code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package cn.fateverse.common.core.exception;
|
||||
|
||||
/**
|
||||
* 基础异常
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
|
||||
public class BaseException extends RuntimeException {
|
||||
|
||||
|
||||
/**
|
||||
* 所属模块
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 错误码对应的参数
|
||||
*/
|
||||
private Object[] args;
|
||||
|
||||
/**
|
||||
* 错误消息
|
||||
*/
|
||||
private String defaultMessage;
|
||||
|
||||
public BaseException(String module, String code, Object[] args, String defaultMessage) {
|
||||
this.module = module;
|
||||
this.code = code;
|
||||
this.args = args;
|
||||
this.defaultMessage = defaultMessage;
|
||||
}
|
||||
|
||||
public BaseException(String module, String code, Object[] args) {
|
||||
this(module, code, args, null);
|
||||
}
|
||||
|
||||
public BaseException(String module, String defaultMessage) {
|
||||
this(module, null, null, defaultMessage);
|
||||
}
|
||||
|
||||
public BaseException(String code, Object[] args) {
|
||||
this(null, code, args, null);
|
||||
}
|
||||
|
||||
public BaseException(String defaultMessage) {
|
||||
this(null, null, null, defaultMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String message = null;
|
||||
if (message == null) {
|
||||
message = defaultMessage;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public String getDefaultMessage() {
|
||||
return defaultMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.fateverse.common.core.exception;
|
||||
|
||||
/**
|
||||
* 自定义异常
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/29
|
||||
*/
|
||||
public class CustomException extends RuntimeException {
|
||||
|
||||
|
||||
private Integer code;
|
||||
|
||||
private String message;
|
||||
|
||||
public CustomException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public CustomException(String message, Integer code) {
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public CustomException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public CustomException() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package cn.fateverse.common.core.exception;
|
||||
|
||||
import cn.fateverse.common.core.enums.ResultEnum;
|
||||
|
||||
public class TierDownException extends CustomException {
|
||||
|
||||
public TierDownException(String message) {
|
||||
super(ResultEnum.SYS_ERROR.msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.fateverse.common.core.exception;
|
||||
|
||||
/**
|
||||
* 用户信息异常类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
public class UserException extends BaseException {
|
||||
|
||||
|
||||
public UserException(String code, Object[] args) {
|
||||
super("user", code, args, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.fateverse.common.core.exception;
|
||||
|
||||
|
||||
/**
|
||||
* 用户密码不正确或不符合规范异常类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
public class UserPasswordNotMatchException extends UserException {
|
||||
|
||||
|
||||
public UserPasswordNotMatchException() {
|
||||
super("user.password.not.match", null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package cn.fateverse.common.core.result;
|
||||
|
||||
|
||||
import cn.fateverse.common.core.enums.ResultEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 返回结果集
|
||||
*
|
||||
* @author: Clay
|
||||
* @date: 2022/5/31 10:00
|
||||
*/
|
||||
public class Result<T> implements Serializable {
|
||||
private Integer code;
|
||||
private String msg;
|
||||
private T data;
|
||||
private transient HttpStatus status;
|
||||
|
||||
|
||||
public Result() {
|
||||
}
|
||||
|
||||
public Result(Integer code, String msg, T data, HttpStatus status) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(Integer code, String msg, T data) {
|
||||
return new Result<>(code, msg, data, HttpStatus.OK);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(Integer code, String msg, T data, HttpStatus status) {
|
||||
return new Result<>(code, msg, data, status);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(String msg, T data) {
|
||||
return Result.ok(ResultEnum.SUCCESS.code, msg, data, ResultEnum.SUCCESS.status);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(Integer code, T data) {
|
||||
return Result.ok(code, ResultEnum.SUCCESS.msg, data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(String msg) {
|
||||
return ok(ResultEnum.SUCCESS.code, msg, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(T data) {
|
||||
return Result.ok(ResultEnum.SUCCESS.msg, data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok() {
|
||||
return Result.ok(ResultEnum.SUCCESS.msg, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(String msg, T data) {
|
||||
return Result.error(ResultEnum.ERROR.code, msg, data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(Integer code, String msg) {
|
||||
return Result.error(code, msg, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> notFound(String msg) {
|
||||
return Result.error(HttpStatus.NOT_FOUND.value(), msg, null, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(Integer code, String msg, T data) {
|
||||
return new Result<>(code, msg, data, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(Integer code, String msg, T data, HttpStatus status) {
|
||||
return new Result<>(code, msg, data, status);
|
||||
}
|
||||
|
||||
public static <T> Result<T> unauthorized(String msg) {
|
||||
return new Result<>(HttpStatus.UNAUTHORIZED.value(), msg, null, HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(HttpStatus status, String msg) {
|
||||
return new Result<>(status.value(), msg, null, status);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(ResultEnum resultEnum) {
|
||||
return Result.error(resultEnum.code, resultEnum.msg, null, resultEnum.status);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(String msg) {
|
||||
return Result.error(ResultEnum.ERROR.code, msg, null, ResultEnum.ERROR.status);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error() {
|
||||
return Result.error(ResultEnum.ERROR.code, ResultEnum.ERROR.msg, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> info(String msg) {
|
||||
return Result.ok(ResultEnum.NO_DATA.code, msg, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> info(ResultEnum resultEnum) {
|
||||
return Result.error(resultEnum.code, resultEnum.msg, null, resultEnum.status);
|
||||
}
|
||||
|
||||
public static <T> Result<T> noData() {
|
||||
return Result.ok(ResultEnum.NO_DATA.code, ResultEnum.NO_DATA.msg, null);
|
||||
}
|
||||
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public HttpStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public void setStatus(HttpStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.fateverse.common.core.result.page;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表格分页数据对象
|
||||
*
|
||||
* @author binlin
|
||||
*/
|
||||
@Data
|
||||
public class TableDataInfo<T> implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
private long total;
|
||||
|
||||
/**
|
||||
* 列表数据
|
||||
*/
|
||||
private List<T> rows;
|
||||
|
||||
/**
|
||||
* 表格数据对象
|
||||
*/
|
||||
public TableDataInfo() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*
|
||||
* @param list 列表数据
|
||||
* @param total 总记录数
|
||||
*/
|
||||
public TableDataInfo(List<T> list, int total) {
|
||||
this.rows = list;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import cn.fateverse.common.core.annotaion.AutoTime;
|
||||
import cn.fateverse.common.core.annotaion.AutoUser;
|
||||
import cn.fateverse.common.core.constant.UserConstants;
|
||||
import cn.fateverse.common.core.enums.AutoUserEnum;
|
||||
import cn.fateverse.common.core.enums.MethodEnum;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-05-25
|
||||
*/
|
||||
public class AutoSetValueUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AutoSetValueUtils.class);
|
||||
|
||||
public static final String LONG_TYPE = "java.lang.Long";
|
||||
public static final String OBJECT_TYPE = "java.lang.Object";
|
||||
public static final String STRING_TYPE = "java.lang.String";
|
||||
public static String BASE_PACKAGE;
|
||||
|
||||
|
||||
static {
|
||||
BASE_PACKAGE = getBasePackage(AutoSetValueUtils.class);
|
||||
}
|
||||
|
||||
public static String getBasePackage(Class<?> clazz) {
|
||||
String typeName = clazz.getTypeName();
|
||||
int fastIndex = typeName.indexOf(".");
|
||||
return typeName.substring(0, typeName.indexOf(".", fastIndex + 1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自动设置userID
|
||||
*
|
||||
* @param parameter 参数
|
||||
* @param methodEnum sql方法
|
||||
* @param field 字段
|
||||
*/
|
||||
public static void autoUser(Object parameter, MethodEnum methodEnum, Field field) {
|
||||
//获取到设置用户id的注解
|
||||
AutoUser autoUser = field.getAnnotation(AutoUser.class);
|
||||
if (null != autoUser && autoUser.method() == methodEnum) {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
Object principal = context.getAuthentication().getPrincipal();
|
||||
if (null == principal || UserConstants.ANONYMOUS_USER.equals(principal)) {
|
||||
return;
|
||||
}
|
||||
JSONObject user = ReflectUserUtils.getUser(principal);
|
||||
AutoUserEnum value = autoUser.value();
|
||||
Class<?> type = field.getType();
|
||||
Object object = null;
|
||||
String typeName = type.getTypeName();
|
||||
if (value == AutoUserEnum.USER_ID) {
|
||||
//设置用户id
|
||||
long userId = user.getLongValue("userId");
|
||||
switch (typeName) {
|
||||
case OBJECT_TYPE:
|
||||
case LONG_TYPE:
|
||||
object = userId;
|
||||
break;
|
||||
case STRING_TYPE:
|
||||
object = Long.toString(userId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (value == AutoUserEnum.USER_NAME || value == AutoUserEnum.NICK_NAME) {
|
||||
object = value == AutoUserEnum.USER_NAME ? user.getString("userName") : user.getString("nickName");
|
||||
if (!(OBJECT_TYPE.equals(typeName) || STRING_TYPE.equals(typeName))) {
|
||||
throw new CustomException("数据类型不配配,Field字段类型为" + typeName + "不能放入String类型的数据");
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, object);
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error("字段:{}自动设置参数失败", field.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动设置时间
|
||||
*
|
||||
* @param parameter 参数
|
||||
* @param methodEnum sql方法
|
||||
* @param field 字段
|
||||
*/
|
||||
public static void autoTime(Object parameter, MethodEnum methodEnum, Field field) {
|
||||
//获取到设置时间的注解
|
||||
AutoTime autoTime = field.getAnnotation(AutoTime.class);
|
||||
if (null != autoTime && autoTime.method() == methodEnum) {
|
||||
field.setAccessible(true);
|
||||
Class<?> type = field.getType();
|
||||
Object time = null;
|
||||
try {
|
||||
time = type.newInstance();
|
||||
field.set(parameter, time);
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
log.error("字段:{}自动设置参数失败,参数为:{}", field.getName(), time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动设置userID
|
||||
*
|
||||
* @param parameter 参数
|
||||
* @param methodEnum sql方法
|
||||
* @param fields 字段
|
||||
*/
|
||||
public static void autoUserNew(Object parameter, MethodEnum methodEnum, Set<Field> fields) {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
Authentication authentication = context.getAuthentication();
|
||||
if (null == authentication) {
|
||||
return;
|
||||
}
|
||||
Object principal = authentication.getPrincipal();
|
||||
if (null == principal || UserConstants.ANONYMOUS_USER.equals(principal)) {
|
||||
return;
|
||||
}
|
||||
JSONObject user = ReflectUserUtils.getUser(principal);
|
||||
for (Field field : fields) {
|
||||
setUser(methodEnum, user, parameter, field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动设置时间
|
||||
*
|
||||
* @param parameter 参数
|
||||
* @param methodEnum sql方法
|
||||
* @param fields 字段
|
||||
*/
|
||||
public static void autoTimeNew(Object parameter, MethodEnum methodEnum, Set<Field> fields) {
|
||||
for (Field field : fields) {
|
||||
setTime(methodEnum, parameter, field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动设置userID
|
||||
*
|
||||
* @param list 参数
|
||||
* @param methodEnum sql方法
|
||||
* @param fields 字段
|
||||
*/
|
||||
public static void autoUserList(List<Object> list, MethodEnum methodEnum, Set<Field> fields) {
|
||||
if (fields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
Object principal = context.getAuthentication().getPrincipal();
|
||||
if (null == principal || UserConstants.ANONYMOUS_USER.equals(principal)) {
|
||||
return;
|
||||
}
|
||||
JSONObject user = ReflectUserUtils.getUser(principal);
|
||||
for (Object param : list) {
|
||||
for (Field field : fields) {
|
||||
setUser(methodEnum, user, param, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置用户通用函数
|
||||
*
|
||||
* @param methodEnum 方法类型
|
||||
* @param user 用户信息
|
||||
* @param param 参数对象
|
||||
* @param field 字段对象
|
||||
*/
|
||||
private static void setUser(MethodEnum methodEnum, JSONObject user, Object param, Field field) {
|
||||
long userId = user.getLongValue("userId");
|
||||
//获取到设置用户id的注解
|
||||
AutoUser autoUser = field.getAnnotation(AutoUser.class);
|
||||
if (null != autoUser && autoUser.method() == methodEnum) {
|
||||
AutoUserEnum value = autoUser.value();
|
||||
Class<?> type = field.getType();
|
||||
Object object = null;
|
||||
String typeName = type.getTypeName();
|
||||
if (value == AutoUserEnum.USER_ID) {
|
||||
//设置用户id
|
||||
if (type == Object.class || type == Long.class) {
|
||||
object = userId;
|
||||
} else if (type == String.class) {
|
||||
object = Long.toString(userId);
|
||||
}
|
||||
} else if (value == AutoUserEnum.USER_NAME || value == AutoUserEnum.NICK_NAME) {
|
||||
object = value == AutoUserEnum.USER_NAME ? user.getString("userName") : user.getString("nickName");
|
||||
if (!(type == Object.class || type == Long.class)) {
|
||||
throw new CustomException("数据类型不配配,Field字段类型为" + typeName + "不能放入String类型的数据");
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
field.set(param, object);
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error("字段:{}自动设置参数失败", field.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动设置时间
|
||||
*
|
||||
* @param list 参数
|
||||
* @param methodEnum sql方法
|
||||
* @param fields 时间
|
||||
*/
|
||||
public static void autoTimeList(List<Object> list, MethodEnum methodEnum, Set<Field> fields) {
|
||||
if (fields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Object param : list) {
|
||||
for (Field field : fields) {
|
||||
setTime(methodEnum, param, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置时间
|
||||
*
|
||||
* @param methodEnum 方法类型
|
||||
* @param param 参数对象
|
||||
* @param field 字段
|
||||
*/
|
||||
private static void setTime(MethodEnum methodEnum, Object param, Field field) {
|
||||
//获取到设置时间的注解
|
||||
AutoTime autoTime = field.getAnnotation(AutoTime.class);
|
||||
if (null != autoTime && autoTime.method() == methodEnum) {
|
||||
field.setAccessible(true);
|
||||
Class<?> type = field.getType();
|
||||
Object time = null;
|
||||
try {
|
||||
time = type.newInstance();
|
||||
field.set(param, time);
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
log.error("字段:{}自动设置参数失败,参数为:{}", field.getName(), time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-05-10
|
||||
*/
|
||||
public class HttpServletUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 获取request信息
|
||||
* @return
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
return attributes.getRequest();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取http响应对象
|
||||
*
|
||||
* @return response
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
return attributes.getResponse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-10-15
|
||||
*/
|
||||
public class IpBackUtils {
|
||||
|
||||
public static final String BLACK_LIST = "black:list:";
|
||||
public final static String BLACK_LIST_IPV_4 = BLACK_LIST + "ipv4";
|
||||
public final static String BLACK_LIST_IPV_6 = BLACK_LIST + "ipv6";
|
||||
public final static String BLACK_LIST_IP = BLACK_LIST + "ip";
|
||||
public final static String IPV_4 = "ipv4";
|
||||
public final static String IPV_6 = "ipv6";
|
||||
|
||||
|
||||
public static long ipToDecimal(String ipAddress) {
|
||||
try {
|
||||
// 将IP地址拆分为四个部分
|
||||
String[] ipParts = ipAddress.split("\\.");
|
||||
// 将每个部分转换为整数
|
||||
long decimalNumber = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
long ipPart = Long.parseLong(ipParts[i]);
|
||||
// 每个部分的权重为256的(3-i)次方
|
||||
decimalNumber += ipPart * Math.pow(256, 3 - i);
|
||||
}
|
||||
return decimalNumber;
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getIpType(String ip) {
|
||||
try {
|
||||
InetAddress address = InetAddress.getByName(ip);
|
||||
if (address instanceof Inet4Address) {
|
||||
return IPV_4;
|
||||
} else if (address instanceof Inet6Address) {
|
||||
return IPV_6;
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
throw new CustomException("无效ip地址");
|
||||
}
|
||||
throw new CustomException("无效ip地址");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* 获取IP方法
|
||||
*
|
||||
* @author binlin
|
||||
*/
|
||||
public class IpUtils {
|
||||
|
||||
public static String getIpAdder(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return "unknown";
|
||||
}
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.removeHtmlTag(ip);
|
||||
}
|
||||
|
||||
public static boolean internalIp(String ip) {
|
||||
byte[] addr = textToNumericFormatV4(ip);
|
||||
return internalIp(addr) || "127.0.0.1".equals(ip);
|
||||
}
|
||||
|
||||
private static boolean internalIp(byte[] addr) {
|
||||
if (ObjectUtils.isEmpty(addr) || addr.length < 2) {
|
||||
return true;
|
||||
}
|
||||
final byte b0 = addr[0];
|
||||
final byte b1 = addr[1];
|
||||
// 10.x.x.x/8
|
||||
final byte SECTION_1 = 0x0A;
|
||||
// 172.16.x.x/12
|
||||
final byte SECTION_2 = (byte) 0xAC;
|
||||
final byte SECTION_3 = (byte) 0x10;
|
||||
final byte SECTION_4 = (byte) 0x1F;
|
||||
// 192.168.x.x/16
|
||||
final byte SECTION_5 = (byte) 0xC0;
|
||||
final byte SECTION_6 = (byte) 0xA8;
|
||||
switch (b0) {
|
||||
case SECTION_1:
|
||||
return true;
|
||||
case SECTION_2:
|
||||
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
|
||||
return true;
|
||||
}
|
||||
case SECTION_5:
|
||||
switch (b1) {
|
||||
case SECTION_6:
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将IPv4地址转换成字节
|
||||
*
|
||||
* @param text IPv4地址
|
||||
* @return byte 字节
|
||||
*/
|
||||
public static byte[] textToNumericFormatV4(String text) {
|
||||
if (text.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[4];
|
||||
String[] elements = text.split("\\.", -1);
|
||||
try {
|
||||
long l;
|
||||
int i;
|
||||
switch (elements.length) {
|
||||
case 1:
|
||||
l = Long.parseLong(elements[0]);
|
||||
if ((l < 0L) || (l > 4294967295L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
|
||||
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 2:
|
||||
l = Integer.parseInt(elements[0]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l & 0xFF);
|
||||
l = Integer.parseInt(elements[1]);
|
||||
if ((l < 0L) || (l > 16777215L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 3:
|
||||
for (i = 0; i < 2; ++i) {
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
l = Integer.parseInt(elements[2]);
|
||||
if ((l < 0L) || (l > 65535L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 4:
|
||||
for (i = 0; i < 4; ++i) {
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static String getHostIp() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
}
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
public static String getHostName() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
|
||||
/**
|
||||
* Long工具类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/11/6
|
||||
*/
|
||||
public class LongUtils {
|
||||
|
||||
|
||||
public static boolean isNull(Long num){
|
||||
return null == num || 0L == num;
|
||||
}
|
||||
|
||||
public static boolean isNotNull(Long num){
|
||||
return !isNull(num);
|
||||
}
|
||||
|
||||
public static void checkId(Long pk){
|
||||
checkId(pk,"缺少必要参数!");
|
||||
}
|
||||
|
||||
public static void checkId(Long pk, String message){
|
||||
if (isNull(pk)) {
|
||||
throw new CustomException(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import cn.fateverse.common.core.enums.MenuEnum;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/9
|
||||
*/
|
||||
public class MenuTypeUtils {
|
||||
|
||||
public static boolean checkMenuTypeLegal(String menuType){
|
||||
return MenuEnum.MENU.getInfo().equals(menuType) ||
|
||||
MenuEnum.DIRECTORY.getInfo().equals(menuType)||
|
||||
MenuEnum.BUTTON.getInfo().equals(menuType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/18
|
||||
*/
|
||||
public class ObjectUtils extends org.springframework.util.ObjectUtils {
|
||||
/**
|
||||
* 检查主键
|
||||
* @param pk 主键
|
||||
*/
|
||||
public static void checkPk(Object pk) {
|
||||
checkPk(pk, "缺少必要参数!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查主键
|
||||
* @param pk 主键
|
||||
* @param message 错误提示消息
|
||||
*/
|
||||
public static void checkPk(Object pk, String message) {
|
||||
if (pk instanceof Long) {
|
||||
LongUtils.checkId((Long) pk, message);
|
||||
} else if (pk instanceof String) {
|
||||
if (isEmpty(pk)) {
|
||||
throw new CustomException(message);
|
||||
}
|
||||
} else {
|
||||
if (null == pk) {
|
||||
throw new CustomException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查主键list
|
||||
* @param pkList 主键list
|
||||
*/
|
||||
public static void checkPkList(List<?> pkList) {
|
||||
checkPkList(pkList,"缺少必要参数!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查主键list
|
||||
* @param pkList 主键list
|
||||
* @param message 错误提示消息
|
||||
*/
|
||||
public static void checkPkList(List<?> pkList, String message) {
|
||||
if (isEmpty(pkList)){
|
||||
throw new CustomException(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-05-10
|
||||
*/
|
||||
public class ReflectUserUtils {
|
||||
|
||||
/**
|
||||
* 从loginUser中获取到用户信息
|
||||
*
|
||||
* @param loginUser 登录后的用户
|
||||
* @return 用户的字段信息
|
||||
*/
|
||||
private static Field getUserField(Object loginUser) {
|
||||
Class<?> userClass = loginUser.getClass();
|
||||
try {
|
||||
return userClass.getDeclaredField("user");
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到token 信息
|
||||
*
|
||||
* @param loginUser 登录后的用户
|
||||
* @return token
|
||||
*/
|
||||
public static String getToken(Object loginUser) {
|
||||
Class<?> loginUserClass = loginUser.getClass();
|
||||
Field token;
|
||||
try {
|
||||
token = loginUserClass.getDeclaredField("token");
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return getValue(loginUser, token).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*
|
||||
* @param loginUser 登录后的用户
|
||||
* @return 用户信息
|
||||
*/
|
||||
public static JSONObject getUser(Object loginUser) {
|
||||
Field userField = getUserField(loginUser);
|
||||
Object user = getValue(loginUser, userField);
|
||||
return (JSONObject) JSON.toJSON(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到用户Id
|
||||
*
|
||||
* @param loginUser 登录后的用户
|
||||
* @return 用户id
|
||||
*/
|
||||
public static String getUserId(Object loginUser) {
|
||||
return getFieldValue(loginUser, "userId");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到用户名
|
||||
*
|
||||
* @param loginUser 登录后的用户
|
||||
* @return 用户名
|
||||
*/
|
||||
public static String getUsername(Object loginUser) {
|
||||
return getFieldValue(loginUser, "userName");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到用户别名
|
||||
*
|
||||
* @param loginUser 登录后的用户
|
||||
* @return 用户别名
|
||||
*/
|
||||
public static String getNickname(Object loginUser) {
|
||||
return getFieldValue(loginUser, "nickName");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到值
|
||||
*
|
||||
* @param user 登录后的用户
|
||||
* @param field 字段
|
||||
* @return 值
|
||||
*/
|
||||
private static Object getValue(Object user, Field field) {
|
||||
field.setAccessible(true);
|
||||
return ReflectionUtils.getField(field, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过字段名获取到值
|
||||
*
|
||||
* @param loginUser 登录后的用户
|
||||
* @param fieldName 字段名
|
||||
* @return 值
|
||||
*/
|
||||
private static String getFieldValue(Object loginUser, String fieldName) {
|
||||
Field userField = getUserField(loginUser);
|
||||
Class<?> userClass = userField.getType().getSuperclass();
|
||||
Object user = getValue(loginUser, userField);
|
||||
Field field = null;
|
||||
try {
|
||||
field = userClass.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return getValue(user, field).toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* Spring 事件发布工具类
|
||||
* @author Clay
|
||||
* @date 2022/11/1
|
||||
*/
|
||||
@Slf4j
|
||||
@Lazy(false)
|
||||
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
|
||||
|
||||
private static ApplicationContext applicationContext = null;
|
||||
|
||||
|
||||
/**
|
||||
* 从applicationContext中获取到Bean,并转换成为requiredType对应的类型
|
||||
*/
|
||||
public static <T> T getBean(Class<T> requiredType){
|
||||
return applicationContext.getBean(requiredType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从applicationContext中获取Bean,并自动转成对应接受的类型
|
||||
*/
|
||||
public static <T> T getBean(String name){
|
||||
return (T) applicationContext.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布事件,实现异步执行功能
|
||||
* @param event
|
||||
*/
|
||||
public static void publishEvent(ApplicationEvent event){
|
||||
if (applicationContext == null){
|
||||
return;
|
||||
}
|
||||
applicationContext.publishEvent(event);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
public static ApplicationContext getApplicationContext(){
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除SpringContextHolder中的applicationContext
|
||||
*/
|
||||
public static void clearHolder(){
|
||||
//如果是debug环境下,打印清除ApplicationContext的日志
|
||||
if (log.isDebugEnabled()){
|
||||
log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
|
||||
}
|
||||
applicationContext = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
SpringContextHolder.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
SpringContextHolder.clearHolder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import cn.fateverse.common.core.enums.StateEnum;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/6
|
||||
*/
|
||||
public class StateUtils {
|
||||
|
||||
public static boolean checkStateLegal(String state){
|
||||
return StateEnum.NORMAL.getCode().equals(state) ||
|
||||
StateEnum.DISABLE.getCode().equals(state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package cn.fateverse.common.core.utils;
|
||||
|
||||
import cn.fateverse.common.core.entity.PageInfo;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 表格数据处理
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
public class TableSupport {
|
||||
|
||||
/**
|
||||
* 当前记录起始索引
|
||||
*/
|
||||
public static final String PAGE_NUM = "pageNum";
|
||||
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
public static final String PAGE_SIZE = "pageSize";
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
public static final String ORDER_BY_COLUMN = "orderByColumn";
|
||||
|
||||
/**
|
||||
* 排序的方向 "desc" 或者 "asc".
|
||||
*/
|
||||
public static final String IS_ASC = "isAsc";
|
||||
|
||||
/**
|
||||
* 分页参数合理化
|
||||
*/
|
||||
public static final String REASONABLE = "reasonable";
|
||||
|
||||
/**
|
||||
* 封装分页对象
|
||||
*/
|
||||
public static PageInfo getPageInfo() {
|
||||
PageInfo pageInfo = new PageInfo();
|
||||
pageInfo.setPageNum(Convert.toInt(getParameter(PAGE_NUM), 1));
|
||||
pageInfo.setPageSize(Convert.toInt(getParameter(PAGE_SIZE), 10));
|
||||
pageInfo.setOrderByColumn(getParameter(ORDER_BY_COLUMN));
|
||||
pageInfo.setIsAsc(getParameter(IS_ASC));
|
||||
pageInfo.setReasonable(getParameterToBool(REASONABLE));
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
public static PageInfo buildPageRequest() {
|
||||
return getPageInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name) {
|
||||
return Convert.toBool(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name) {
|
||||
return Convert.toStr(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
return attributes.getRequest();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.fateverse.common.core.utils.convert;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/16
|
||||
*/
|
||||
public class ObjectConfig {
|
||||
|
||||
|
||||
private boolean copy;
|
||||
private Map<String,String> mapper;
|
||||
private Set<String> exclude;
|
||||
|
||||
public ObjectConfig() {
|
||||
mapper = new LinkedHashMap<>();
|
||||
exclude = new LinkedHashSet<>();
|
||||
copy = true;
|
||||
}
|
||||
|
||||
public void setMapper(String targetKey, String sourceKey){
|
||||
mapper.put(targetKey,sourceKey);
|
||||
}
|
||||
|
||||
public void setExclude(String excludeKey){
|
||||
exclude.add(excludeKey);
|
||||
}
|
||||
|
||||
public Map<String,String> getMapper(){
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public Set<String> getExclude() {
|
||||
return exclude;
|
||||
}
|
||||
|
||||
|
||||
public void setOption(String value,String label){
|
||||
this.copy = false;
|
||||
mapper.put("label",label);
|
||||
mapper.put("value",value);
|
||||
}
|
||||
|
||||
public boolean isCopy() {
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void setCopy(boolean copy) {
|
||||
this.copy = copy;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package cn.fateverse.common.core.utils.convert;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/16
|
||||
* 对象之间的转换
|
||||
*/
|
||||
public class ObjectConvertUtil {
|
||||
|
||||
private static TreeConfig treeConfig;
|
||||
|
||||
|
||||
private static Map<String, Field> cacheTargetField;
|
||||
|
||||
private static Map<String,Field> cacheSourceField;
|
||||
|
||||
/**
|
||||
* 目标class
|
||||
*/
|
||||
private static Class<?> targetClass;
|
||||
|
||||
private static Class<?> sourceClass;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param list
|
||||
* @param target
|
||||
* @param config
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> List<T> build(List<?> list, Class<T> target, Consumer<TreeConfig> config) {
|
||||
if (list.isEmpty()){
|
||||
return new ArrayList<>();
|
||||
}
|
||||
//将目标class对象设置为全局对象
|
||||
targetClass = target;
|
||||
sourceClass = list.get(0).getClass();
|
||||
//初始化config
|
||||
treeConfig = new TreeConfig();
|
||||
//将目标class对象设置为全局对象
|
||||
//提供给实现类对config进行修改
|
||||
config.accept(treeConfig);
|
||||
List<Object> collect = list.stream().map(ObjectConvertUtil::conversion).collect(Collectors.toList());
|
||||
return (List<T>) collect;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T conversion(Object object) {
|
||||
try {
|
||||
Object targetObject = targetClass.newInstance();
|
||||
//相同字段名称直接赋值
|
||||
if (treeConfig.isCopy()) {
|
||||
BeanUtils.copyProperties(object, targetObject);
|
||||
}
|
||||
//不同字段名进行赋值
|
||||
for (Map.Entry<String, String> entry : treeConfig.getMapper().entrySet()) {
|
||||
Object value = getSourceValue(object,entry.getValue());
|
||||
setTargetValue(targetObject,entry.getKey(),value);
|
||||
}
|
||||
for (String key : treeConfig.getExclude()) {
|
||||
setTargetValue(targetObject,key,null);
|
||||
}
|
||||
//返回结果
|
||||
return (T) targetObject;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getSourceValue(Object source,String sourceFiled){
|
||||
Field field = cacheSourceField.get(sourceFiled);
|
||||
if (null == field){
|
||||
try {
|
||||
field = sourceClass.getDeclaredField(sourceFiled);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
cacheSourceField.put(sourceFiled,field);
|
||||
}
|
||||
field.setAccessible(true);
|
||||
return ReflectionUtils.getField(field,source);
|
||||
}
|
||||
|
||||
|
||||
private static void setTargetValue(Object target,String targetFiled,Object value){
|
||||
Field field = cacheTargetField.get(targetFiled);
|
||||
if (null == field){
|
||||
try {
|
||||
field = targetClass.getDeclaredField(targetFiled);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
cacheTargetField.put(targetFiled,field);
|
||||
}
|
||||
field.setAccessible(true);
|
||||
ReflectionUtils.setField(field,target,value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package cn.fateverse.common.core.utils.convert;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/16
|
||||
* TreeConfig的配置文件
|
||||
*/
|
||||
public class TreeConfig {
|
||||
|
||||
|
||||
private boolean copy;
|
||||
|
||||
private final Map<String, String> mapper;
|
||||
private final Set<String> exclude;
|
||||
|
||||
private String idField;
|
||||
|
||||
private String parentField;
|
||||
|
||||
private String childrenField;
|
||||
|
||||
private Boolean isStore = false;
|
||||
|
||||
private String storeField;
|
||||
|
||||
|
||||
public TreeConfig() {
|
||||
mapper = new LinkedHashMap<>();
|
||||
exclude = new LinkedHashSet<>();
|
||||
copy = true;
|
||||
this.idField = "id";
|
||||
this.parentField = "parentId";
|
||||
this.childrenField = "children";
|
||||
}
|
||||
|
||||
public void setParentField(String parentField) {
|
||||
if (StrUtil.isEmpty(parentField)) {
|
||||
return;
|
||||
}
|
||||
this.parentField = parentField;
|
||||
}
|
||||
|
||||
public Boolean getStore() {
|
||||
return isStore;
|
||||
}
|
||||
|
||||
public String getStoreField() {
|
||||
return storeField;
|
||||
}
|
||||
|
||||
public String getParentField() {
|
||||
return parentField;
|
||||
}
|
||||
|
||||
|
||||
public String getIdField() {
|
||||
return idField;
|
||||
}
|
||||
|
||||
public void setIdField(String idField) {
|
||||
if (StrUtil.isEmpty(idField)) {
|
||||
return;
|
||||
}
|
||||
this.idField = idField;
|
||||
}
|
||||
|
||||
public void setSortOrder(Boolean isStore,String storeField) {
|
||||
this.isStore = isStore;
|
||||
this.storeField = storeField;
|
||||
}
|
||||
|
||||
public String getChildrenField() {
|
||||
return childrenField;
|
||||
}
|
||||
|
||||
public void setChildrenField(String childrenField) {
|
||||
if (StrUtil.isEmpty(childrenField)) {
|
||||
return;
|
||||
}
|
||||
this.childrenField = childrenField;
|
||||
}
|
||||
|
||||
public void setMapper(String targetKey, String sourceKey) {
|
||||
mapper.put(targetKey, sourceKey);
|
||||
}
|
||||
|
||||
public void setExclude(String excludeKey) {
|
||||
exclude.add(excludeKey);
|
||||
}
|
||||
|
||||
public Map<String, String> getMapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public Set<String> getExclude() {
|
||||
return exclude;
|
||||
}
|
||||
|
||||
public void setOption(String valueField, String labelField) {
|
||||
this.copy = false;
|
||||
mapper.put("value", valueField);
|
||||
mapper.put("label", labelField);
|
||||
}
|
||||
|
||||
public boolean isCopy() {
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void setCopy(boolean copy) {
|
||||
this.copy = copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 空实现
|
||||
*/
|
||||
//@Override
|
||||
//public void build(TreeConfig treeConfig) {
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package cn.fateverse.common.core.utils.convert;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/16
|
||||
* 树形结构转换递归方式
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class TreeUtil {
|
||||
/**
|
||||
* 配置信息
|
||||
*/
|
||||
private static TreeConfig treeConfig;
|
||||
|
||||
private static Map<String, Field> cacheTargetField;
|
||||
|
||||
private static Map<String, Field> cacheSourceField;
|
||||
|
||||
/**
|
||||
* 目标class
|
||||
*/
|
||||
private static Class<?> targetClass;
|
||||
|
||||
private static Class<?> sourceClass;
|
||||
|
||||
/**
|
||||
* 构建tree结构
|
||||
*
|
||||
* @param list 需要转换的list
|
||||
* @param target 目标class对象
|
||||
* @param config 配置项
|
||||
* @param <T> 目标对象类型
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> build(List<?> list, Class<T> target, Consumer<TreeConfig> config) {
|
||||
if (list.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
//将目标class对象设置为全局对象
|
||||
targetClass = target;
|
||||
sourceClass = list.get(0).getClass();
|
||||
//初始化config
|
||||
treeConfig = new TreeConfig();
|
||||
//初始化字段缓存
|
||||
cacheTargetField = new HashMap<>();
|
||||
cacheSourceField = new HashMap<>();
|
||||
//提供给实现类对config进行修改
|
||||
config.accept(treeConfig);
|
||||
//获取到最小的
|
||||
Object min = list.stream().min(Comparator.comparing(object -> getParentId(object).toString())).get();
|
||||
//获取到最小的父级id
|
||||
Object minPid = getParentId(min);
|
||||
//将数据通过他们的各自的父id进行分组
|
||||
Map<Object, List<Object>> listMap = list.stream().collect(Collectors.groupingBy(TreeUtil::getParentId));
|
||||
//最终开始进行tree的构建
|
||||
return getChildren(listMap, minPid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到子节点
|
||||
*
|
||||
* @param listMap
|
||||
* @param parentId
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
private static <T> List<T> getChildren(Map<Object, List<Object>> listMap, Object parentId) {
|
||||
//获取到缓存中的list
|
||||
List<?> objects = listMap.get(parentId);
|
||||
if (chickList(objects)) {
|
||||
return null;
|
||||
}
|
||||
if (treeConfig.getStore()) {
|
||||
objects = objects.stream().sorted(Comparator.comparing(item ->
|
||||
Convert.toInt(getSourceValue(item, treeConfig.getStoreField()))
|
||||
)).collect(Collectors.toList());
|
||||
}
|
||||
listMap.remove(parentId);
|
||||
//遍历数组,并将对象一一的转换
|
||||
List<Object> collect = objects.stream().map(object ->
|
||||
conversion(object, listMap)).collect(Collectors.toList());
|
||||
return (List<T>) collect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象直接的字段进行转换
|
||||
*
|
||||
* @param object
|
||||
* @param listMap
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
private static <T> T conversion(Object object, Map<Object, List<Object>> listMap) {
|
||||
try {
|
||||
Object targetObject = targetClass.newInstance();
|
||||
//相同字段名称直接赋值
|
||||
if (treeConfig.isCopy()) {
|
||||
BeanUtils.copyProperties(object, targetObject);
|
||||
}
|
||||
//不同字段名进行赋值
|
||||
for (Map.Entry<String, String> entry : treeConfig.getMapper().entrySet()) {
|
||||
Object value = getSourceValue(object, entry.getValue());
|
||||
setTargetValue(targetObject, entry.getKey(), value);
|
||||
}
|
||||
//设置子节点
|
||||
Object id = getSourceValue(object, treeConfig.getIdField());
|
||||
List<Object> children = getChildren(listMap, id);
|
||||
if (!chickList(children)) {
|
||||
setTargetValue(targetObject, treeConfig.getChildrenField(), children);
|
||||
}
|
||||
//设置完毕后,将需要排除的字段进行置空
|
||||
for (String key : treeConfig.getExclude()) {
|
||||
setTargetValue(targetObject, key, null);
|
||||
}
|
||||
//返回结果
|
||||
return (T) targetObject;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getSourceValue(Object source, String sourceFiled) {
|
||||
Field field = cacheSourceField.get(sourceFiled);
|
||||
if (null == field) {
|
||||
try {
|
||||
field = sourceClass.getDeclaredField(sourceFiled);
|
||||
field.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
cacheSourceField.put(sourceFiled, field);
|
||||
}
|
||||
return ReflectionUtils.getField(field, source);
|
||||
}
|
||||
|
||||
|
||||
private static void setTargetValue(Object target, String targetFiled, Object value) {
|
||||
Field field = cacheTargetField.get(targetFiled);
|
||||
if (null == field) {
|
||||
try {
|
||||
field = targetClass.getDeclaredField(targetFiled);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
field.setAccessible(true);
|
||||
cacheTargetField.put(targetFiled, field);
|
||||
}
|
||||
ReflectionUtils.setField(field, target, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查list是否为空
|
||||
*
|
||||
* @param objects
|
||||
* @return
|
||||
*/
|
||||
private static boolean chickList(List<?> objects) {
|
||||
return null == objects || objects.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到父级id
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
private static Object getParentId(Object object) {
|
||||
try {
|
||||
return getSourceValue(object, treeConfig.getParentField());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.fateverse.common.core.utils.sql;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* sql操作工具类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
public class SqlUtil {
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
|
||||
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
*/
|
||||
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
|
||||
|
||||
/**
|
||||
* 检查字符,防止注入绕过
|
||||
*/
|
||||
public static String escapeOrderBySql(String value) {
|
||||
if (!StrUtil.isEmpty(value) && !isValidOrderBySql(value)) {
|
||||
throw new CustomException("参数不符合规范,不能进行查询");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 order by 语法是否符合规范
|
||||
*/
|
||||
public static boolean isValidOrderBySql(String value) {
|
||||
return value.matches(SQL_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL关键字检查
|
||||
*/
|
||||
public static void filterKeyword(String value) {
|
||||
if (StrUtil.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||
for (String sqlKeyword : sqlKeywords) {
|
||||
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
|
||||
throw new CustomException("参数存在SQL注入风险");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.fateverse.common.core.utils.uuid;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
|
||||
/**
|
||||
* ID生成器工具类
|
||||
*
|
||||
* @author Clay
|
||||
*/
|
||||
public class IdUtils {
|
||||
/**
|
||||
* 获取随机UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String randomUUID() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String simpleUUID() {
|
||||
return UUID.randomUUID().toString(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String fastUUID() {
|
||||
return UUID.fastUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String fastSimpleUUID() {
|
||||
return UUID.fastUUID().toString(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
cn.fateverse.common.core.utils.SpringContextHolder
|
||||
30
common/common-decrypt/pom.xml
Normal file
30
common/common-decrypt/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>common-decrypt</artifactId>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.fateverse.common.decrypt;
|
||||
|
||||
import cn.fateverse.common.decrypt.aspect.EncryptAspect;
|
||||
import cn.fateverse.common.decrypt.config.EncryptProperties;
|
||||
import cn.fateverse.common.decrypt.service.DefaultEncryptService;
|
||||
import cn.fateverse.common.decrypt.service.EncryptService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-07-04
|
||||
*/
|
||||
@EnableConfigurationProperties({EncryptProperties.class})
|
||||
public class DecryptConfiguration {
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({EncryptService.class})
|
||||
public EncryptService encryptService(EncryptProperties encryptProperties) {
|
||||
return new DefaultEncryptService(encryptProperties);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(EncryptService.class)
|
||||
public EncryptAspect encryptAspect(EncryptService encryptService) {
|
||||
return new EncryptAspect(encryptService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.fateverse.common.decrypt.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-07-03
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Encrypt {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.fateverse.common.decrypt.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 定义注解 标记需要加密的字段
|
||||
*/
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EncryptField {
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package cn.fateverse.common.decrypt.aspect;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.result.Result;
|
||||
import cn.fateverse.common.decrypt.annotation.EncryptField;
|
||||
import cn.fateverse.common.decrypt.service.EncryptService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Aspect
|
||||
public class EncryptAspect {
|
||||
|
||||
protected static String BASE_PACKAGE;
|
||||
|
||||
|
||||
static {
|
||||
String typeName = EncryptAspect.class.getTypeName();
|
||||
int fastIndex = typeName.indexOf(".");
|
||||
BASE_PACKAGE = typeName.substring(0, typeName.indexOf(".", fastIndex + 1));
|
||||
}
|
||||
|
||||
private final EncryptService encryptService;
|
||||
|
||||
public EncryptAspect(EncryptService encryptService) {
|
||||
this.encryptService = encryptService;
|
||||
}
|
||||
|
||||
|
||||
@Around("@annotation(cn.fateverse.common.decrypt.annotation.Encrypt)")
|
||||
public Object decryptField(ProceedingJoinPoint point) throws Throwable {
|
||||
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||
//获取请求参数
|
||||
Object[] args = point.getArgs();
|
||||
//获取方法
|
||||
Method method = signature.getMethod();
|
||||
//获取方法参数 Parameter对象集 参数修饰符、参数名、注解及注解类型
|
||||
Parameter[] parameters = method.getParameters();
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
Parameter parameter = parameters[i];
|
||||
//获取参数注解
|
||||
EncryptField encryptField = parameter.getAnnotation(EncryptField.class);
|
||||
Object arg = args[i];
|
||||
if (null != encryptField) {
|
||||
if (arg instanceof String) {
|
||||
String decrypt = encryptService.decrypt((String) arg);
|
||||
args[i] = decrypt;
|
||||
} else if (arg instanceof List) {
|
||||
try {
|
||||
List<String> list = (List<String>) arg;
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
String ciphertext = list.get(j);
|
||||
String decrypt = encryptService.decrypt(ciphertext);
|
||||
list.set(j, decrypt);
|
||||
}
|
||||
args[i] = list;
|
||||
} catch (Exception e) {
|
||||
throw new CustomException("接受参数类型错误,请使用String类型的泛型参数");
|
||||
}
|
||||
}
|
||||
} else if (parameter.getType().getName().startsWith(BASE_PACKAGE)) { //返回一个类对象,该类对象标识此参数对象表示的参数的声明类型
|
||||
decrypt(arg);
|
||||
}
|
||||
}
|
||||
//正常执行业务,最后返回的返回值为Result
|
||||
Object proceed = point.proceed(args);
|
||||
if (proceed instanceof Result) {
|
||||
Result<Object> result = (Result<Object>) proceed;
|
||||
Object data = result.getData();
|
||||
if (null != data) {
|
||||
try {
|
||||
encrypt(data);
|
||||
} catch (Exception e) {
|
||||
log.error("加密异常", e);
|
||||
return Result.error("加密异常!");
|
||||
}
|
||||
}
|
||||
result.setData(data);
|
||||
} else {
|
||||
try {
|
||||
encrypt(proceed);
|
||||
} catch (Exception e) {
|
||||
log.error("加密异常", e);
|
||||
return Result.error("加密异常!");
|
||||
}
|
||||
}
|
||||
return proceed;
|
||||
}
|
||||
|
||||
|
||||
private void encrypt(Object data) throws Exception {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
Class<?> argClass = data.getClass();
|
||||
if (argClass.getTypeName().startsWith(BASE_PACKAGE)) {
|
||||
Field[] fields = argClass.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
||||
field.setAccessible(true);
|
||||
Object value = ReflectionUtils.getField(field, data);
|
||||
if (null == value) {
|
||||
continue;
|
||||
}
|
||||
if (null != encryptField && value instanceof String) {
|
||||
String decrypt = encryptService.encrypt((String) value);
|
||||
ReflectionUtils.setField(field, data, decrypt);
|
||||
} else if (field.getType().getName().startsWith(BASE_PACKAGE)) {
|
||||
encrypt(value);
|
||||
} else if (value instanceof Collection) {
|
||||
Collection<Object> collection = (Collection<Object>) value;
|
||||
for (Object item : collection) {
|
||||
encrypt(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (data instanceof Collection) {
|
||||
Collection<Object> collection = (Collection<Object>) data;
|
||||
for (Object item : collection) {
|
||||
encrypt(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void decrypt(Object arg) throws Exception {
|
||||
Class<?> argClass = arg.getClass();
|
||||
Field[] fields = argClass.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
||||
field.setAccessible(true);
|
||||
Object value = ReflectionUtils.getField(field, arg);
|
||||
if (null == value) {
|
||||
continue;
|
||||
}
|
||||
if (null != encryptField && value instanceof String) {
|
||||
String decrypt = encryptService.decrypt((String) value);
|
||||
ReflectionUtils.setField(field, arg, decrypt);
|
||||
} else if (field.getType().getName().startsWith(BASE_PACKAGE)) {
|
||||
decrypt(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.fateverse.common.decrypt.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-07-04
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "encrypt")
|
||||
public class EncryptProperties {
|
||||
|
||||
private String secretKey = "1234567890abcdef";
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cn.fateverse.common.decrypt.service;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.decrypt.config.EncryptProperties;
|
||||
import cn.fateverse.common.decrypt.utils.SM4Util;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-08-07
|
||||
*/
|
||||
|
||||
public class DefaultEncryptService implements EncryptService {
|
||||
|
||||
private final EncryptProperties properties;
|
||||
|
||||
public DefaultEncryptService(EncryptProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String plainText) {
|
||||
try {
|
||||
return SM4Util.encrypt(plainText, properties.getSecretKey());
|
||||
} catch (Exception e) {
|
||||
throw new CustomException("加密失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String cipherText) {
|
||||
try {
|
||||
return SM4Util.decrypt(cipherText, properties.getSecretKey());
|
||||
} catch (Exception e) {
|
||||
throw new CustomException("解密失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.fateverse.common.decrypt.service;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2024/2/19 16:19
|
||||
*/
|
||||
public interface EncryptService {
|
||||
|
||||
/**
|
||||
* 加密方法
|
||||
*
|
||||
* @param plainText 需要加密的文档
|
||||
* @return 加密之后的数据
|
||||
*/
|
||||
String encrypt(String plainText);
|
||||
|
||||
/**
|
||||
* 解密方法
|
||||
*
|
||||
* @param cipherText 需要解密的文档
|
||||
* @return 解密之后的文档
|
||||
*/
|
||||
String decrypt(String cipherText);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package cn.fateverse.common.decrypt.utils;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.Security;
|
||||
|
||||
public class SM4Util {
|
||||
|
||||
private static final String ALGORITHM_NAME = "SM4";
|
||||
private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
|
||||
private static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS7Padding";
|
||||
private static final String CHARSET_NAME = "UTF-8";
|
||||
private static final String IV_PARAMETER = "0000000000000000";
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* SM4 加密
|
||||
*
|
||||
* @param plainText 明文
|
||||
* @param secretKey 密钥
|
||||
* @return 密文
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
public static String encrypt(String plainText, String secretKey) throws Exception {
|
||||
byte[] keyBytes = secretKey.getBytes(CHARSET_NAME);
|
||||
byte[] plainBytes = plainText.getBytes(CHARSET_NAME);
|
||||
byte[] cipherBytes = encrypt_ECB_Padding(keyBytes, plainBytes);
|
||||
return new String(Hex.encode(cipherBytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* SM4 解密
|
||||
*
|
||||
* @param cipherText 密文
|
||||
* @param secretKey 密钥
|
||||
* @return 明文
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
public static String decrypt(String cipherText, String secretKey) throws Exception {
|
||||
byte[] keyBytes = secretKey.getBytes(CHARSET_NAME);
|
||||
byte[] cipherBytes = Hex.decode(cipherText);
|
||||
byte[] plainBytes = decrypt_ECB_Padding(keyBytes, cipherBytes);
|
||||
return new String(plainBytes, CHARSET_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* ECB 模式加密
|
||||
*
|
||||
* @param keyBytes 密钥
|
||||
* @param plainBytes 明文
|
||||
* @return 密文
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
private static byte[] encrypt_ECB_Padding(byte[] keyBytes, byte[] plainBytes) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM_NAME);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
|
||||
return cipher.doFinal(plainBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* ECB 模式解密
|
||||
*
|
||||
* @param keyBytes 密钥
|
||||
* @param cipherBytes 密文
|
||||
* @return 明文
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
private static byte[] decrypt_ECB_Padding(byte[] keyBytes, byte[] cipherBytes) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM_NAME);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
|
||||
return cipher.doFinal(cipherBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* CBC 模式加密
|
||||
*
|
||||
* @param keyBytes 密钥
|
||||
* @param plainBytes 明文
|
||||
* @param ivBytes 向量
|
||||
* @return 密文
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
private static byte[] encrypt_CBC_Padding(byte[] keyBytes, byte[] plainBytes, byte[] ivBytes) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM_NAME);
|
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
|
||||
return cipher.doFinal(plainBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* CBC 模式解密
|
||||
*
|
||||
* @param keyBytes 密钥
|
||||
* @param cipherBytes 密文
|
||||
* @param ivBytes 向量
|
||||
* @return 明文
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
private static byte[] decrypt_CBC_Padding(byte[] keyBytes, byte[] cipherBytes, byte[] ivBytes) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM_NAME); IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); return cipher.doFinal(cipherBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
cn.fateverse.common.decrypt.DecryptConfiguration
|
||||
58
common/common-dubbo/pom.xml
Normal file
58
common/common-dubbo/pom.xml
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>common</artifactId>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-dubbo</artifactId>
|
||||
|
||||
<properties>
|
||||
<dubbo.version>3.0.3</dubbo.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- Dubbo Nacos registry dependency -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-nacos</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-common</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- dubbo 基础依赖-->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
</dependency>
|
||||
<!--dubbo sentinel 适配器-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.alibaba.csp</groupId>-->
|
||||
<!-- <artifactId>sentinel-apache-dubbo3-adapter</artifactId>-->
|
||||
<!-- <version>1.8.6</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.fateverse.common.dubbo;
|
||||
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.*;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-08-03
|
||||
*/
|
||||
@Activate(group = CommonConstants.PROVIDER)
|
||||
public class DubboSentinelFilter implements Filter {
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
demoProviderFilter=cn.fateverse.common.dubbo.DubboSentinelFilter
|
||||
27
common/common-email/pom.xml
Normal file
27
common/common-email/pom.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>common</artifactId>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-email</artifactId>
|
||||
|
||||
<properties>
|
||||
<mail.vaersion>1.6.2</mail.vaersion>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
<version>${mail.vaersion}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,31 @@
|
||||
package cn.fateverse.common.email.config;
|
||||
|
||||
import cn.fateverse.common.email.service.EmailService;
|
||||
import cn.fateverse.common.email.service.impl.EmailServiceImpl;
|
||||
import cn.fateverse.common.email.service.session.EmailSessionProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* 邮件配置类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
@EnableConfigurationProperties({EmailProperties.class})
|
||||
public class EmailConfiguration {
|
||||
|
||||
@Bean
|
||||
EmailService emailService() {
|
||||
return new EmailServiceImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(EmailSessionProvider.class)
|
||||
EmailSessionProvider emailSessionProvider() {
|
||||
return new EmailSessionProvider();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package cn.fateverse.common.email.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* 系统邮件发送服务器
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "email")
|
||||
public class EmailProperties {
|
||||
/**
|
||||
* 邮件服务器发送者邮箱
|
||||
*/
|
||||
private String sender;
|
||||
/**
|
||||
* 发送人名称
|
||||
*/
|
||||
private String personal;
|
||||
/**
|
||||
* 邮件服务器
|
||||
*/
|
||||
private String emailSmtpHost;
|
||||
/**
|
||||
* 邮件服务器端口
|
||||
*/
|
||||
private String emailSmtpPort;
|
||||
/**
|
||||
* 邮件发送者用户名
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 发送者密码
|
||||
*/
|
||||
private String password;
|
||||
/**
|
||||
* 邮件服务器协议
|
||||
*/
|
||||
private String encryption;
|
||||
|
||||
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSender(String sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public String getPersonal() {
|
||||
return personal;
|
||||
}
|
||||
|
||||
public void setPersonal(String personal) {
|
||||
this.personal = personal;
|
||||
}
|
||||
|
||||
public String getEmailSmtpHost() {
|
||||
return emailSmtpHost;
|
||||
}
|
||||
|
||||
public void setEmailSmtpHost(String emailSmtpHost) {
|
||||
this.emailSmtpHost = emailSmtpHost;
|
||||
}
|
||||
|
||||
public String getEmailSmtpPort() {
|
||||
return emailSmtpPort;
|
||||
}
|
||||
|
||||
public void setEmailSmtpPort(String emailSmtpPort) {
|
||||
this.emailSmtpPort = emailSmtpPort;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEncryption() {
|
||||
return encryption;
|
||||
}
|
||||
|
||||
public void setEncryption(String encryption) {
|
||||
this.encryption = encryption;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package cn.fateverse.common.email.entity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 邮件发送对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
public class SendEmailInfo {
|
||||
|
||||
/**
|
||||
* 邮件主题
|
||||
*/
|
||||
private String subject;
|
||||
/**
|
||||
* 发送对象
|
||||
*/
|
||||
private List<String> senders;
|
||||
/**
|
||||
* 抄送对象
|
||||
*/
|
||||
private List<String> ccPersons;
|
||||
/**
|
||||
* 邮件内容
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* 主题
|
||||
*/
|
||||
private String ThemeEnums;
|
||||
|
||||
|
||||
public SendEmailInfo() {
|
||||
}
|
||||
|
||||
|
||||
public SendEmailInfo(String subject, List<String> senders, List<String> ccPersons, String content) {
|
||||
this.subject = subject;
|
||||
this.senders = senders;
|
||||
this.ccPersons = ccPersons;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public SendEmailInfo(String subject, List<String> senders, List<String> ccPersons, String content, String themeEnums) {
|
||||
this.subject = subject;
|
||||
this.senders = senders;
|
||||
this.ccPersons = ccPersons;
|
||||
this.content = content;
|
||||
ThemeEnums = themeEnums;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public void setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public List<String> getSenders() {
|
||||
return senders;
|
||||
}
|
||||
|
||||
public void setSenders(List<String> senders) {
|
||||
this.senders = senders;
|
||||
}
|
||||
|
||||
public List<String> getCcPersons() {
|
||||
return ccPersons;
|
||||
}
|
||||
|
||||
public void setCcPersons(List<String> ccPersons) {
|
||||
this.ccPersons = ccPersons;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getThemeEnums() {
|
||||
return ThemeEnums;
|
||||
}
|
||||
|
||||
public void setThemeEnums(String themeEnums) {
|
||||
ThemeEnums = themeEnums;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.fateverse.common.email.enums;
|
||||
|
||||
/**
|
||||
* 主题枚举
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
public enum ThemeEnums {
|
||||
/**
|
||||
* 主题枚举
|
||||
*/
|
||||
DEFAULT,
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.fateverse.common.email.service;
|
||||
|
||||
import cn.fateverse.common.email.entity.SendEmailInfo;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
public interface EmailService {
|
||||
|
||||
/**
|
||||
* 同步发送邮件
|
||||
*
|
||||
* @param email 邮件信息
|
||||
* @return 发送结果
|
||||
*/
|
||||
boolean sendMail(SendEmailInfo email);
|
||||
|
||||
/**
|
||||
* 异步发送邮件
|
||||
*
|
||||
* @param email 邮件信息
|
||||
*/
|
||||
void asyncSendMail(SendEmailInfo email);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package cn.fateverse.common.email.service.impl;
|
||||
|
||||
import cn.fateverse.common.email.service.session.EmailSessionProvider;
|
||||
import cn.fateverse.common.email.entity.SendEmailInfo;
|
||||
import cn.fateverse.common.email.service.EmailService;
|
||||
import cn.fateverse.common.email.config.EmailProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.mail.*;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 邮件服务默认实现
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
@Service
|
||||
public class EmailServiceImpl implements EmailService {
|
||||
|
||||
@Autowired
|
||||
private EmailProperties properties;
|
||||
|
||||
@Autowired
|
||||
private EmailSessionProvider sessionProvider;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean sendMail(SendEmailInfo email) {
|
||||
Session session = sessionProvider.getSession();
|
||||
// 创建默认的 MimeMessage 对象
|
||||
MimeMessage message = new MimeMessage(session);
|
||||
Transport transport = null;
|
||||
try {
|
||||
message.setSubject(email.getSubject());
|
||||
message.setFrom(new InternetAddress(properties.getSender(), properties.getPersonal(), "UTF-8"));
|
||||
|
||||
InternetAddress[] senders = new InternetAddress[email.getSenders().size()];
|
||||
for (int i = 0; i < email.getSenders().size(); i++) {
|
||||
senders[i] = new InternetAddress(email.getSenders().get(i));
|
||||
}
|
||||
message.addRecipients(Message.RecipientType.TO, senders);
|
||||
if (null != email.getCcPersons() && !email.getCcPersons().isEmpty()) {
|
||||
InternetAddress[] ccSenders = new InternetAddress[email.getCcPersons().size()];
|
||||
for (int i = 0; i < email.getCcPersons().size(); i++) {
|
||||
ccSenders[i] = new InternetAddress(email.getCcPersons().get(i));
|
||||
}
|
||||
message.addRecipients(Message.RecipientType.CC, ccSenders);
|
||||
}
|
||||
message.setContent(email.getContent(), "text/html;charset=UTF-8");
|
||||
//new InternetAddress()
|
||||
message.setSentDate(new Date());
|
||||
|
||||
message.saveChanges();
|
||||
|
||||
session.setDebug(false);
|
||||
transport = session.getTransport();
|
||||
transport.connect();
|
||||
|
||||
transport.sendMessage(message, message.getAllRecipients());
|
||||
// 7. 关闭连接
|
||||
} catch (MessagingException | UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
assert transport != null;
|
||||
transport.close();
|
||||
} catch (MessagingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void asyncSendMail(SendEmailInfo email) {
|
||||
sendMail(email);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.fateverse.common.email.service.session;
|
||||
|
||||
import cn.fateverse.common.email.config.EmailProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.mail.Authenticator;
|
||||
import javax.mail.PasswordAuthentication;
|
||||
import javax.mail.Session;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 邮件发送Session获取
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
public class EmailSessionProvider {
|
||||
|
||||
|
||||
@Autowired
|
||||
private EmailProperties properties;
|
||||
|
||||
private volatile Session session;
|
||||
|
||||
public Session getSession() {
|
||||
if (session == null) {
|
||||
synchronized (this) {
|
||||
if (session == null) {
|
||||
// 参数配置
|
||||
Properties props = new Properties();
|
||||
// 使用的协议(JavaMail规范要求)
|
||||
props.setProperty("mail.transport.protocol", "smtp");
|
||||
// 发件人的邮箱的 SMTP 服务器地址
|
||||
props.setProperty("mail.smtp.host", properties.getEmailSmtpHost());
|
||||
//发件人的邮箱的 SMTP 服务器端口
|
||||
props.setProperty("mail.smtp.port", properties.getEmailSmtpPort());
|
||||
// 需要请求认证
|
||||
props.setProperty("mail.smtp.auth", "true");
|
||||
props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
|
||||
props.setProperty("mail.smtp.socketFactory.fallback", "false");
|
||||
props.setProperty("mail.smtp.socketFactory.port", properties.getEmailSmtpPort());
|
||||
session = Session.getDefaultInstance(props, new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(properties.getUsername(), properties.getPassword());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return session;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "email",
|
||||
"type": "cn.fateverse.email.config.EMailProperties",
|
||||
"sourceType": "cn.fateverse.email.config.EMailProperties"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "email.sender",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "cn.fateverse.email.config.EMailProperties"
|
||||
},
|
||||
{
|
||||
"name": "email.personal",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "cn.fateverse.email.config.EMailProperties"
|
||||
},
|
||||
{
|
||||
"name": "email.emailSmtpHost",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "cn.fateverse.email.config.EMailProperties"
|
||||
},
|
||||
{
|
||||
"name": "email.emailSmtpPort",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "cn.fateverse.email.config.EMailProperties"
|
||||
},
|
||||
{
|
||||
"name": "email.username",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "cn.fateverse.email.config.EMailProperties"
|
||||
},
|
||||
{
|
||||
"name": "email.password",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "cn.fateverse.email.config.EMailProperties"
|
||||
},
|
||||
{
|
||||
"name": "email.encryption",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "cn.fateverse.email.config.EMailProperties"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
cn.fateverse.common.email.config.EmailConfiguration
|
||||
42
common/common-excel/pom.xml
Normal file
42
common/common-excel/pom.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>common-excel</artifactId>
|
||||
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>admin-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>${easyexcel.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.fateverse.common.excel;
|
||||
|
||||
import cn.fateverse.common.excel.service.ExcelService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-11-10 20:18
|
||||
*/
|
||||
public class ExcelAutoConfiguration {
|
||||
|
||||
/**
|
||||
* Excel导出时字典对接服务等
|
||||
*
|
||||
* @return excelService
|
||||
*/
|
||||
@Bean
|
||||
public ExcelService excelService() {
|
||||
return new ExcelService();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.fateverse.common.excel.service;
|
||||
|
||||
import cn.fateverse.admin.dubbo.DubboDictDataService;
|
||||
import cn.fateverse.admin.vo.DictDataVo;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-02-20
|
||||
*/
|
||||
public class ExcelService {
|
||||
|
||||
@DubboReference
|
||||
private DubboDictDataService dictDataService;
|
||||
|
||||
public DubboDictDataService getDictDataService() {
|
||||
return dictDataService;
|
||||
}
|
||||
|
||||
public Map<String, Map<String, DictDataVo>> searchDictDataCacheKeys(List<String> cacheKeys){
|
||||
return dictDataService.searchDictDataCacheKeys(cacheKeys);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package cn.fateverse.common.excel.utils;
|
||||
|
||||
import cn.fateverse.common.core.annotaion.Excel;
|
||||
import cn.fateverse.common.core.annotaion.Excels;
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/19
|
||||
*/
|
||||
@Data
|
||||
public class ExcelAssist {
|
||||
|
||||
private Field field;
|
||||
|
||||
private Excel excel;
|
||||
|
||||
private Excels excels;
|
||||
|
||||
private int order;
|
||||
|
||||
private String objectFieldName;
|
||||
|
||||
public ExcelAssist(Field field, Excels excels) {
|
||||
this.field = field;
|
||||
this.objectFieldName = field.getName();
|
||||
this.excels = excels;
|
||||
order = excels.order();
|
||||
}
|
||||
|
||||
public ExcelAssist(Field field, Excel excel, ExcelAssist assist) {
|
||||
this.field = field;
|
||||
this.excel = excel;
|
||||
if (null != assist) {
|
||||
this.objectFieldName = assist.objectFieldName;
|
||||
if (excel.order() != Integer.MAX_VALUE && assist.getExcels().order() != Integer.MAX_VALUE) {
|
||||
order = assist.getExcels().order() * 10 + excel.order();
|
||||
} else {
|
||||
order = excel.order();
|
||||
}
|
||||
} else {
|
||||
order = excel.order();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package cn.fateverse.common.excel.utils;
|
||||
|
||||
import cn.fateverse.common.excel.service.ExcelService;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.fateverse.admin.vo.DictDataVo;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.utils.HttpServletUtils;
|
||||
import cn.fateverse.common.core.utils.SpringContextHolder;
|
||||
import cn.fateverse.common.core.annotaion.Excel;
|
||||
import cn.fateverse.common.core.annotaion.Excels;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||
import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/19
|
||||
*/
|
||||
public class ExcelUtil {
|
||||
|
||||
private static final Map<Class<?>, ExcelAssistWrapper> wrapperMap = new ConcurrentHashMap<>(8);
|
||||
|
||||
private static Map<String, Map<String, DictDataVo>> dictDataMap;
|
||||
|
||||
|
||||
private static ExcelService getBean() {
|
||||
return SpringContextHolder.getBean(ExcelService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到数据列表
|
||||
*
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
private static List<List<Object>> getDataList(List<?> list, List<ExcelAssist> assistList) {
|
||||
return list.stream().map(object ->
|
||||
assistList.stream().map(assist -> {
|
||||
Field field = assist.getField();
|
||||
field.setAccessible(true);
|
||||
Excel excel = assist.getExcel();
|
||||
Object value = null;
|
||||
if (StrUtil.isEmpty(assist.getObjectFieldName())) {
|
||||
value = getData(object, excel, field);
|
||||
} else {
|
||||
Object sunValue = null;
|
||||
try {
|
||||
sunValue = ReflectionUtils.getField(field, object);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
if (null != sunValue) {
|
||||
value = getData(object, excel, field);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}).collect(Collectors.toList())
|
||||
).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到对象中的数据
|
||||
*
|
||||
* @param object
|
||||
* @param excel
|
||||
* @param field
|
||||
* @return
|
||||
*/
|
||||
private static Object getData(Object object, Excel excel, Field field) {
|
||||
try {
|
||||
Object objectValue = ReflectionUtils.getField(field, object);
|
||||
if (objectValue instanceof Date) {
|
||||
objectValue = DateUtil.format((Date) objectValue, excel.dateFormat());
|
||||
}
|
||||
String dictType = excel.dictType();
|
||||
if (!StrUtil.isEmpty(dictType)) {
|
||||
Map<String, DictDataVo> dictMap = dictDataMap.get(dictType);
|
||||
if (null != dictMap) {
|
||||
DictDataVo dictData = dictMap.get(objectValue.toString());
|
||||
if (null != dictData) {
|
||||
objectValue = dictData.getDictLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
return objectValue;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化header数据
|
||||
*
|
||||
* @param clazz
|
||||
* @param assist
|
||||
*/
|
||||
private static void initHeader(Class<?> clazz, ExcelAssist assist, List<ExcelAssist> assistList) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
Excel excel = field.getAnnotation(Excel.class);
|
||||
if (null != excel) {
|
||||
assistList.add(new ExcelAssist(field, excel, assist));
|
||||
} else {
|
||||
Excels excels = field.getAnnotation(Excels.class);
|
||||
if (excels == null) {
|
||||
continue;
|
||||
}
|
||||
Class<?> objectValue = field.getType();
|
||||
if (clazz == objectValue) {
|
||||
throw new CustomException("不允许嵌套对象导出Excel");
|
||||
}
|
||||
ExcelAssist excelAssist = new ExcelAssist(field, excels);
|
||||
initHeader(objectValue, excelAssist, assistList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportExcel(List<?> list, Class<?> clazz) {
|
||||
exportExcel(list, clazz, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出数据Excel
|
||||
*
|
||||
* @param list
|
||||
* @param clazz
|
||||
* @param sheetName
|
||||
*/
|
||||
public static void exportExcel(List<?> list, Class<?> clazz, String sheetName) {
|
||||
Set<String> dictList = new HashSet<>();
|
||||
|
||||
ExcelAssistWrapper wrapper = wrapperMap.get(clazz);
|
||||
if (wrapper == null) {
|
||||
synchronized (wrapperMap) {
|
||||
wrapper = wrapperMap.get(clazz);
|
||||
if (wrapper == null) {
|
||||
wrapper = new ExcelAssistWrapper();
|
||||
List<ExcelAssist> assistList = new ArrayList<>();
|
||||
initHeader(clazz, null, assistList);
|
||||
List<List<String>> headerList = new ArrayList<>(assistList.size());
|
||||
assistList = assistList.stream().sorted(Comparator.comparing(ExcelAssist::getOrder))
|
||||
.peek(assist -> headerList.add(Collections.singletonList(assist.getExcel().value())))
|
||||
.peek(assist -> {
|
||||
String dictType = assist.getExcel().dictType();
|
||||
if (!StrUtil.isEmpty(dictType)) {
|
||||
dictList.add(dictType);
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
wrapper.assistList = assistList;
|
||||
wrapper.headerList = headerList;
|
||||
wrapperMap.put(clazz, wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dictList.size() > 0) {
|
||||
getDictData(new ArrayList<>(dictList));
|
||||
}
|
||||
List<List<Object>> dataList = getDataList(list, wrapper.assistList);
|
||||
try {
|
||||
sheetName = StrUtil.isEmpty(sheetName) ? "sheet" : sheetName;
|
||||
HttpServletResponse response = getResponse(sheetName);
|
||||
EasyExcel.write(response.getOutputStream())
|
||||
.head(wrapper.headerList)
|
||||
.excelType(ExcelTypeEnum.XLSX)
|
||||
.sheet(StrUtil.isEmpty(sheetName) ? "sheet" : sheetName)
|
||||
.doWrite(dataList);
|
||||
} catch (IOException e) {
|
||||
throw new CustomException("Excel导出失败!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static HttpServletResponse getResponse(String sheetName) {
|
||||
HttpServletResponse response = HttpServletUtils.getResponse();
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.setContentType("multipart/form-data");
|
||||
response.setHeader("Content-Disposition",
|
||||
"attachment;fileName=" + sheetName + UUID.randomUUID() + ".xlsx");
|
||||
return response;
|
||||
}
|
||||
|
||||
private static void getDictData(List<String> dictList) {
|
||||
ExcelService dictDataService = getBean();
|
||||
Map<String, Map<String, DictDataVo>> result = dictDataService.searchDictDataCacheKeys(dictList);
|
||||
if (null != result) {
|
||||
System.out.println(result.toString());
|
||||
dictDataMap = result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExcelAssistWrapper {
|
||||
private List<ExcelAssist> assistList;
|
||||
List<List<String>> headerList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
cn.fateverse.common.excel.ExcelAutoConfiguration
|
||||
114
common/common-file/pom.xml
Normal file
114
common/common-file/pom.xml
Normal file
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>common</artifactId>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-file</artifactId>
|
||||
|
||||
<properties>
|
||||
<ftp.version>3.8.0</ftp.version>
|
||||
<okhttp.version>4.9.3</okhttp.version>
|
||||
<fastdfs.version>1.27.2</fastdfs.version>
|
||||
<aliyun.oss.version>3.8.0</aliyun.oss.version>
|
||||
<huawei.obs.version>3.22.3.1</huawei.obs.version>
|
||||
<thumbnailator.version>0.4.8</thumbnailator.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!--阿里云oss-->
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>${aliyun.oss.version}</version>
|
||||
</dependency>
|
||||
<!-- 华为云 OBS -->
|
||||
<dependency>
|
||||
<groupId>com.huaweicloud</groupId>
|
||||
<artifactId>esdk-obs-java</artifactId>
|
||||
<version>${huawei.obs.version}</version>
|
||||
</dependency>
|
||||
<!-- MinIO -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.4.3</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okio</groupId>
|
||||
<artifactId>okio</artifactId>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<!-- FTP上传文件 -->
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>${ftp.version}</version>
|
||||
</dependency>
|
||||
<!-- FastDFS对象存储 -->
|
||||
<dependency>
|
||||
<groupId>com.github.tobato</groupId>
|
||||
<artifactId>fastdfs-client</artifactId>
|
||||
<version>${fastdfs.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.coobird</groupId>
|
||||
<artifactId>thumbnailator</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- Common Core 核心依赖 -->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
</dependency>
|
||||
<!-- Apache io -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!--图片处理工具,处理缩略图-->
|
||||
<dependency>
|
||||
<groupId>net.coobird</groupId>
|
||||
<artifactId>thumbnailator</artifactId>
|
||||
<version>${thumbnailator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
import cn.fateverse.common.file.service.AliyunFileService;
|
||||
import cn.fateverse.common.file.service.client.AliyunClient;
|
||||
import cn.fateverse.common.file.service.client.AliyunClientProvider;
|
||||
import cn.fateverse.common.file.service.impl.AliyunFileStoreService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* 阿里云oss 自动装配
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-17
|
||||
*/
|
||||
@EnableConfigurationProperties({AliyunProperties.class})
|
||||
public class AliyunAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public AliyunFileService aliyunFileService() {
|
||||
return new AliyunFileService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AliyunClientProvider.class)
|
||||
public AliyunClientProvider aliyunClient() {
|
||||
return new AliyunClient();
|
||||
}
|
||||
|
||||
@Bean("aliyunFileStoreService")
|
||||
public AliyunFileStoreService aliyunFileStoreService(AliyunFileService aliyunFileService) {
|
||||
return new AliyunFileStoreService(aliyunFileService);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
|
||||
/**
|
||||
* 阿里云 配置文件信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "file.store.aliyun")
|
||||
public class AliyunProperties {
|
||||
|
||||
/**
|
||||
* 地域节点
|
||||
*/
|
||||
private String endpoint;
|
||||
private String accessKeyId;
|
||||
private String secretAccessKey;
|
||||
/**
|
||||
* OSS的Bucket名称
|
||||
*/
|
||||
private String bucket;
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public void setBucket(String bucket) {
|
||||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
public String getAccessKeyId() {
|
||||
return accessKeyId;
|
||||
}
|
||||
|
||||
public void setAccessKeyId(String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
}
|
||||
|
||||
public String getSecretAccessKey() {
|
||||
return secretAccessKey;
|
||||
}
|
||||
|
||||
public void setSecretAccessKey(String secretAccessKey) {
|
||||
this.secretAccessKey = secretAccessKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
import cn.fateverse.common.file.service.FTPFileService;
|
||||
import cn.fateverse.common.file.service.client.FTPClientProvider;
|
||||
import cn.fateverse.common.file.service.impl.FTPFileStoreService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* FTP 自动装配
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-15
|
||||
*/
|
||||
@EnableConfigurationProperties({FTPProperties.class})
|
||||
public class FTPAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FTPFileService ftpFileService() {
|
||||
return new FTPFileService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(FTPClientProvider.class)
|
||||
public FTPClientProvider ftpClient() {
|
||||
return new FTPClientProvider();
|
||||
}
|
||||
|
||||
@Bean("ftpFileStoreService")
|
||||
public FTPFileStoreService ftpFileStoreService(FTPFileService ftpFileService) {
|
||||
return new FTPFileStoreService(ftpFileService);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* FTP 配置信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-15
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "file.store.ftp")
|
||||
public class FTPProperties {
|
||||
|
||||
//服务器地址
|
||||
private String address;
|
||||
//端口号
|
||||
private Integer port;
|
||||
//用户名
|
||||
private String username;
|
||||
//密码
|
||||
private String password;
|
||||
//字符集编码
|
||||
private String encoding;
|
||||
//资源地址
|
||||
private String asset;
|
||||
//公开目录
|
||||
private String pubfiles;
|
||||
//保护目录
|
||||
private String prifiles;
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public String getAsset() {
|
||||
return asset;
|
||||
}
|
||||
|
||||
public void setAsset(String asset) {
|
||||
this.asset = asset;
|
||||
}
|
||||
|
||||
public String getPubfiles() {
|
||||
return pubfiles;
|
||||
}
|
||||
|
||||
public void setPubfiles(String pubfiles) {
|
||||
this.pubfiles = pubfiles;
|
||||
}
|
||||
|
||||
public String getPrifiles() {
|
||||
return prifiles;
|
||||
}
|
||||
|
||||
public void setPrifiles(String prifiles) {
|
||||
this.prifiles = prifiles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
import cn.fateverse.common.file.service.impl.FastDFSStoreService;
|
||||
import com.github.tobato.fastdfs.FdfsClientConfig;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.EnableMBeanExport;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.jmx.support.RegistrationPolicy;
|
||||
|
||||
@Import(FdfsClientConfig.class)
|
||||
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
|
||||
@EnableConfigurationProperties({FastDFSProperties.class})
|
||||
public class FastDFSAutoConfiguration {
|
||||
|
||||
@Bean("fastDFSStoreService")
|
||||
public FastDFSStoreService fastDFSStoreService(){
|
||||
return new FastDFSStoreService();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@ConfigurationProperties(prefix = "fdfs")
|
||||
public class FastDFSProperties {
|
||||
|
||||
|
||||
private String bucket;
|
||||
|
||||
|
||||
public String getBucket() {
|
||||
return ObjectUtils.isEmpty(bucket) ? "group1" : bucket;
|
||||
}
|
||||
|
||||
public void setBucket(String bucket) {
|
||||
this.bucket = bucket;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
/**
|
||||
* 华为 OBS 配置信息
|
||||
* @author Clay
|
||||
* @date 2023-03-17
|
||||
*/
|
||||
public class HuaweiOBSProperties {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
import cn.fateverse.common.file.service.MinioFileService;
|
||||
import cn.fateverse.common.file.service.client.MinIoClient;
|
||||
import cn.fateverse.common.file.service.client.MinioClientProvider;
|
||||
import cn.fateverse.common.file.service.impl.MinioFileStoreService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* minio 自动装配
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-17
|
||||
*/
|
||||
@EnableConfigurationProperties({MinioProperties.class})
|
||||
public class MinioAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public MinioFileService minioFileService() {
|
||||
return new MinioFileService();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MinioClientProvider.class)
|
||||
public MinioClientProvider minioClient() {
|
||||
return new MinIoClient();
|
||||
}
|
||||
|
||||
|
||||
@Bean("minioFileStoreService")
|
||||
public MinioFileStoreService minioFileStoreService(MinioFileService minioFileService) {
|
||||
return new MinioFileStoreService(minioFileService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package cn.fateverse.common.file.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* minio 配置信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-17
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "file.store.minio")
|
||||
public class MinioProperties {
|
||||
private String endpoint;
|
||||
private String bucket;
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public void setBucket(String bucket) {
|
||||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public void setAccessKey(String accessKey) {
|
||||
this.accessKey = accessKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package cn.fateverse.common.file.entity;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class FileInfo {
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
/**
|
||||
* 缩略图名称
|
||||
*/
|
||||
private String thumbnailFileName;
|
||||
/**
|
||||
* 文件原始名称
|
||||
*/
|
||||
private String originalFilename;
|
||||
/**
|
||||
* 文件url
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 资源路径
|
||||
*/
|
||||
private String uri;
|
||||
/**
|
||||
* 缩略图uri
|
||||
*/
|
||||
private String thumbnailUri;
|
||||
/**
|
||||
* 路径
|
||||
*/
|
||||
private String path;
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
private String fileType;
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
private Long size;
|
||||
/**
|
||||
* 是否是图片
|
||||
*/
|
||||
private Boolean isImage;
|
||||
|
||||
|
||||
private String contentType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.fateverse.common.file.enums;
|
||||
|
||||
import cn.fateverse.common.file.service.FileStoreService;
|
||||
import cn.fateverse.common.file.service.impl.AliyunFileStoreService;
|
||||
import cn.fateverse.common.file.service.impl.FTPFileStoreService;
|
||||
import cn.fateverse.common.file.service.impl.MinioFileStoreService;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-02-16
|
||||
*/
|
||||
public enum FTLStoreServiceEnum {
|
||||
/**
|
||||
* 阿里云 oss 实现
|
||||
*/
|
||||
ALIYUN_OSS(AliyunFileStoreService.class),
|
||||
/**
|
||||
* minio 对象存储 实现
|
||||
*/
|
||||
MINIO_FILE(MinioFileStoreService.class),
|
||||
/**
|
||||
* FTP 文件传输 实现
|
||||
*/
|
||||
FTP_FILE(FTPFileStoreService.class),
|
||||
;
|
||||
|
||||
private final Class<? extends FileStoreService> type;
|
||||
|
||||
|
||||
FTLStoreServiceEnum(Class<? extends FileStoreService> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Class<? extends FileStoreService> getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user