Merge pull request 'master' (#89) from master into gateway

Reviewed-on: http://git.feashow.cn/clay/fateverse/pulls/89
This commit is contained in:
clay
2024-05-08 13:30:42 +00:00
68 changed files with 2084 additions and 431 deletions

View File

@@ -74,7 +74,7 @@ volumes:
path: /home/build/fateverse path: /home/build/fateverse
- name: skywalking - name: skywalking
host: host:
path: /home/skywalking-agent path: /home/build/skywalking-agent
- name: config # k8s对接的配置文件 - name: config # k8s对接的配置文件
host: host:
path: /home/kubect path: /home/kubect

View File

@@ -2,17 +2,15 @@ FROM azul/zulu-openjdk:11.0.22-jdk
VOLUME /tmp VOLUME /tmp
RUN useradd -b /home -m -s /bin/bash clay RUN useradd -b /home -m -s /bin/bash clay
RUN chmod a+xr -R /home/clay && chown clay:clay -R /home/clay RUN chmod a+xr -R /home/clay && chown clay:clay -R /home/clay
#COPY skywalking /home/clay/skywalking-agent COPY skywalking /home/clay/skywalking-agent
#RUN chmod a+xr -R /home/clay/skywalking-agent && chown clay:clay -R /home/clay/skywalking-agent RUN chmod a+xr -R /home/clay/skywalking-agent && chown clay:clay -R /home/clay/skywalking-agent
USER clay USER clay
ARG NAME=$DRONE_COMMIT_BRANCH ARG NAME=$DRONE_COMMIT_BRANCH
COPY ./$NAME.jar /home/clay/$NAME.jar COPY ./$NAME.jar /home/clay/$NAME.jar
COPY start.sh /home/clay/start.sh COPY start.sh /home/clay/start.sh
WORKDIR /home/clay WORKDIR /home/clay
RUN mkdir -p /home/clay/logs && touch /home/clay/logs/spring.log RUN mkdir -p /home/clay/logs && touch /home/clay/logs/spring.log
#RUN mkdir -p /home/clay/code RUN mkdir -p /home/clay/skywalking-agent/logs && touch /home/clay/skywalking-agent/logs/skywalking-api.log
#RUN chmod 777 /home/clay/code
#RUN mkdir -p /home/clay/skywalking-agent/logs && touch /home/clay/skywalking-agent/logs/skywalking-api.log
ENV REF_NAME dev ENV REF_NAME dev
EXPOSE 8080 EXPOSE 8080
CMD bash /home/clay/start.sh $REF_NAME && echo "start logging..." && : > /home/clay/logs/spring.log && tail -F -n 500 /home/clay/logs/spring.log CMD bash /home/clay/start.sh $REF_NAME && echo "start logging..." && : > /home/clay/logs/spring.log && tail -F -n 500 /home/clay/logs/spring.log

View File

@@ -21,12 +21,12 @@
| 依赖 | 版本 | | 依赖 | 版本 |
|----------------------|------------| |----------------------|------------|
| Spring Boot | 2.7.5 | | Spring Boot | 2.7.3 |
| Spring Cloud | 2021.0.5 | | Spring Cloud | 2021.0.3 |
| Spring Cloud Alibaba | 2021.0.4.0 | | Spring Cloud Alibaba | 2021.0.1.0 |
| Mybatis | 3.5.2 | | Mybatis | 3.5.2 |
| Vue | 3.1.3 | | Vue | 3.1.3 |
| React | 3.1.3 | | React | 18.2.0 |
### 模块说明 ### 模块说明

View File

@@ -21,6 +21,16 @@
<groupId>cn.fateverse</groupId> <groupId>cn.fateverse</groupId>
<artifactId>common-core</artifactId> <artifactId>common-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
<version>22.1.0</version>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<version>22.1.0</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -14,7 +14,7 @@ public class JavaCodeAutoConfiguration {
@Bean @Bean
public JavaCodeEngine javaCodeEngine(JavaCodeProperties javaCodeProperties) { public JavaCodeEngine javaCodeEngine(JavaCodeProperties javaCodeProperties) {
return new JavaCodeEngine(javaCodeProperties); return new JavaCodeEngine(javaCodeProperties.getClassPath());
} }

View File

@@ -0,0 +1,51 @@
package cn.fateverse.common.code.console;
import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.common.core.exception.CustomException;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
/**
* 控制台输出捕获
*
* @author Clay
* @date 2024/4/22 17:08
*/
public class ConsoleCapture {
/**
* 捕获方法
*
* @param task 任務
* @return 返回结果
*/
public static EngineResult capture(Task task) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream oldOut = System.out;
System.setOut(new PrintStream(baos));
Object result;
String capturedOutput;
try {
result = task.execute();
} catch (Exception e) {
if (e instanceof CustomException) {
throw (CustomException) e;
}
throw new RuntimeException(e);
} finally {
System.setOut(oldOut);
// 从捕获的字节数组输出流中获取打印的文本
capturedOutput = baos.toString();
}
return new EngineResult(result, capturedOutput);
}
public interface Task {
Object execute() throws Exception;
}
}

View File

@@ -1,19 +1,21 @@
package cn.fateverse.common.code.engine; package cn.fateverse.common.code.engine;
import cn.fateverse.common.code.config.JavaCodeProperties; import cn.fateverse.common.code.console.ConsoleCapture;
import cn.fateverse.common.code.exception.SandboxClassNotFoundException; import cn.fateverse.common.code.exception.SandboxClassNotFoundException;
import cn.fateverse.common.code.lock.SegmentLock; import cn.fateverse.common.code.lock.SegmentLock;
import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.common.code.sandbox.SandboxClassLoader; import cn.fateverse.common.code.sandbox.SandboxClassLoader;
import cn.fateverse.common.code.sandbox.SandboxSecurityManager; import cn.fateverse.common.code.sandbox.SandboxSecurityManager;
import cn.fateverse.common.core.exception.CustomException;
import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
import javax.tools.ToolProvider; import javax.tools.ToolProvider;
import java.io.File; import java.io.*;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@@ -36,9 +38,7 @@ public class JavaCodeEngine {
private final URL url; private final URL url;
private final URLClassLoader classLoader; private final Map<String, CacheWrapper> classCache = new ConcurrentHashMap<>();
private final Map<String, Class<?>> classCache = new ConcurrentHashMap<>();
private final SandboxSecurityManager securityManager = new SandboxSecurityManager(classCache); private final SandboxSecurityManager securityManager = new SandboxSecurityManager(classCache);
@@ -47,54 +47,31 @@ public class JavaCodeEngine {
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
public JavaCodeEngine(JavaCodeProperties javaCodeProperties) { public JavaCodeEngine(String classPath) {
try { try {
CLASS_PATH = javaCodeProperties.getClassPath(); CLASS_PATH = classPath;
File file = new File(CLASS_PATH); File file = new File(CLASS_PATH);
if (!file.exists()) { if (!file.exists()) {
file.mkdirs(); file.mkdirs();
} }
url = file.toURI().toURL(); url = file.toURI().toURL();
classLoader = new SandboxClassLoader(new URL[]{url});
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
throw new RuntimeException(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 code 需要执行的代码字符串
* @param className 类名 * @param className 类名
* @param methodName 方法名 * @param methodName 方法名
* @param paramClass 参数类型数组
* @param args 参数数组 * @param args 参数数组
* @param <T> 接收泛型
* @return 执行结构 * @return 执行结构
*/ */
@SneakyThrows @SneakyThrows
private <T> T developmentExecute(String code, String className, String methodName, Class<?>[] paramClass, Object[] args) { public EngineResult mockExecute(String code, String className, String methodName, Object[] args) {
Class<?> loadClass = null; Class<?> loadClass = null;
try { try {
// 加锁,确保类只加载一次 // 加锁,确保类只加载一次
@@ -104,12 +81,12 @@ public class JavaCodeEngine {
// 创建一个URLClassLoader用于加载代码字符串 // 创建一个URLClassLoader用于加载代码字符串
tempClassLoader = new URLClassLoader(new URL[]{url}); tempClassLoader = new URLClassLoader(new URL[]{url});
// 编译代码字符串为类 // 编译代码字符串为类
Class<?> tempClass = compilerClass(className, code, tempClassLoader); return compilerClass(className, code, tempClassLoader);
// 将编译好的类放入缓存
classCache.put(className, tempClass);
return tempClass;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
if (e instanceof CustomException) {
throw (CustomException) e;
}
// 异常处理并抛出自定义的SandboxClassNotFoundException异常 // 异常处理并抛出自定义的SandboxClassNotFoundException异常
throw new SandboxClassNotFoundException(e.getMessage()); throw new SandboxClassNotFoundException(e.getMessage());
} finally { } finally {
@@ -119,14 +96,17 @@ public class JavaCodeEngine {
} }
}); });
// 获取需要执行的方法 // 获取需要执行的方法
Method method = loadClass.getMethod(methodName, paramClass); Method method = getMethod(methodName, loadClass);
// 设置安全检查器 // 设置安全检查器
System.setSecurityManager(securityManager); System.setSecurityManager(securityManager);
// 执行方法并返回结果 // 执行方法并返回结果
return (T) method.invoke(null, args); return ConsoleCapture.capture(() -> method.invoke(null, args));
} catch (CustomException e) {
EngineResult result = new EngineResult();
result.setSuccess(Boolean.FALSE);
result.setConsole(e.getMessage());
return result;
} finally { } finally {
// 从缓存中移除编译好的类
classCache.remove(className);
// 清空安全检查器 // 清空安全检查器
System.setSecurityManager(null); System.setSecurityManager(null);
if (loadClass != null) { if (loadClass != null) {
@@ -153,37 +133,53 @@ public class JavaCodeEngine {
* @param code 需要执行的代码字符串 * @param code 需要执行的代码字符串
* @param className 类名 * @param className 类名
* @param methodName 方法名 * @param methodName 方法名
* @param paramClass 参数类型数组
* @param args 参数数组 * @param args 参数数组
* @param <T> 接收泛型
* @return 执行结构 * @return 执行结构
*/ */
private <T> T onlineExecute(String code, String className, String methodName, Class<?>[] paramClass, Object[] args) { public Object execute(String code, String className, String methodName, Object[] args) {
try { try {
Class<?> loadClass = null; Class<?> loadClass = null;
loadClass = classCache.get(className); //从缓存中获取
if (loadClass == null) { CacheWrapper wrapper = classCache.get(className);
loadClass = getLoadClass(code, className); //缓存中不存在
Method method = loadClass.getMethod(methodName, paramClass); if (wrapper == null) {
System.setSecurityManager(securityManager); //加载
return (T) method.invoke(null, args); wrapper = getLoadClass(code, className);
} }
//获取到类信息
loadClass = wrapper.getClazz();
//获取方法
Method method = getMethod(methodName, loadClass);
//开启安全模式
System.setSecurityManager(securityManager);
//执行方法
return method.invoke(null, args);
} catch (Exception e) { } catch (Exception e) {
remove(className);
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
System.setSecurityManager(null); 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; return null;
} }
/**
* 获取到方法
*
* @param methodName 方法名称
* @param loadClass 类信息
* @return 方法对象
*/
private Method getMethod(String methodName, Class<?> loadClass) {
Method method = null;
for (Method declaredMethod : loadClass.getDeclaredMethods()) {
if (declaredMethod.getName().equals(methodName)) {
method = declaredMethod;
}
}
return method;
}
/** /**
* 获取到编译完成的Class对象 * 获取到编译完成的Class对象
* *
@@ -191,15 +187,18 @@ public class JavaCodeEngine {
* @param className 类名 * @param className 类名
* @return 编译后的Java对象 * @return 编译后的Java对象
*/ */
private Class<?> getLoadClass(String code, String className) { private CacheWrapper getLoadClass(String code, String className) {
//使用分段锁,提高效率,放多并发情况下多次对同一个类进行编译 //使用分段锁,提高效率,放多并发情况下多次对同一个类进行编译
return SegmentLock.lock(className, () -> { return SegmentLock.lock(className, () -> {
try { try {
URLClassLoader classLoader = new SandboxClassLoader(new URL[]{url});
//执行编译 //执行编译
Class<?> tempClass = compilerClass(className, code, classLoader); Class<?> tempClass = compilerClass(className, code, classLoader);
//创建缓存包装对象
CacheWrapper wrapper = new CacheWrapper(tempClass, classLoader);
//将编译之后的类对象放在缓存中,提高线上环境的运行效率 //将编译之后的类对象放在缓存中,提高线上环境的运行效率
classCache.put(className, tempClass); classCache.put(className, wrapper);
return tempClass; return wrapper;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -215,32 +214,49 @@ public class JavaCodeEngine {
* @return 编译完成的类对象 * @return 编译完成的类对象
*/ */
private Class<?> compilerClass(String className, String code, URLClassLoader classLoader) { private Class<?> compilerClass(String className, String code, URLClassLoader classLoader) {
log.info(code);
File tempFile = new File(CLASS_PATH + className + JAVA_SUFFIX); File tempFile = new File(CLASS_PATH + className + JAVA_SUFFIX);
try (FileWriter writer = new FileWriter(tempFile)) { try (FileWriter writer = new FileWriter(tempFile)) {
writer.write(code); writer.write(code);
writer.close(); writer.close();
ByteArrayOutputStream errorStream = new ByteArrayOutputStream(10240);
// 编译.java文件 // 编译.java文件
compiler.run(null, null, null, tempFile.getPath()); compiler.run(null, null, errorStream, tempFile.getPath());
String trace = errorStream.toString();//存放控制台输出的字符串
if (!ObjectUtils.isEmpty(trace)) {
trace = trace.replace(CLASS_PATH + className + ".", "");
throw new CustomException("编译错误: " + trace);
}
return classLoader.loadClass(className); return classLoader.loadClass(className);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
throw new RuntimeException(e); if (e instanceof CustomException) {
throw (CustomException) e;
}
throw new CustomException("执行或者编辑错误!");
} }
} }
/** /**
* 删除类 * 删除类
*
* @param className 删除类 * @param className 删除类
* @return 删除结果 * @return 删除结果
*/ */
public Boolean remove(String className) { public Boolean remove(String className) {
return SegmentLock.lock(className, () -> { return SegmentLock.lock(className, () -> {
CacheWrapper wrapper = classCache.get(className);
if (wrapper != null) {
classCache.remove(className); classCache.remove(className);
wrapper.remove();
}
//进行gc 垃圾挥手
System.gc();
//删除Java文件
File javaFile = new File(CLASS_PATH + className + JAVA_SUFFIX); File javaFile = new File(CLASS_PATH + className + JAVA_SUFFIX);
if (javaFile.exists()) { if (javaFile.exists()) {
javaFile.delete(); javaFile.delete();
} }
//删除class文件
File classFile = new File(CLASS_PATH + className + CLASS_SUFFIX); File classFile = new File(CLASS_PATH + className + CLASS_SUFFIX);
if (classFile.exists()) { if (classFile.exists()) {
classFile.delete(); classFile.delete();
@@ -248,4 +264,24 @@ public class JavaCodeEngine {
return true; return true;
}); });
} }
@Getter
public static class CacheWrapper {
private Class<?> clazz;
private URLClassLoader classLoader;
public CacheWrapper(Class<?> clazz, URLClassLoader classLoader) {
this.clazz = clazz;
this.classLoader = classLoader;
}
public void remove(){
clazz = null;
classLoader = null;
}
}
} }

View File

@@ -1,9 +1,15 @@
package cn.fateverse.common.code.engine; package cn.fateverse.common.code.engine;
import javax.script.Invocable; import cn.fateverse.common.code.console.ConsoleCapture;
import javax.script.ScriptEngine; import cn.fateverse.common.code.lock.SegmentLock;
import javax.script.ScriptEngineManager; import cn.fateverse.common.code.model.EngineResult;
import javax.script.ScriptException; import cn.fateverse.common.core.exception.CustomException;
import com.alibaba.fastjson2.JSON;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import java.util.HashMap;
import java.util.Map;
/** /**
* js 工具类 * js 工具类
@@ -13,25 +19,89 @@ import javax.script.ScriptException;
*/ */
public class JavaScriptEngine { public class JavaScriptEngine {
// 创建 GraalVM 上下文
private static final Context context = Context.newBuilder()
.allowAllAccess(true)
// .allowHostClassLoading(true)
// .allowIO(true)
// .allowNativeAccess(true)
.build();
private static final Map<String, Value> functionMap = new HashMap<>();
/** /**
* 执行js代码 * 执行js代码
*
* @param script js脚本 * @param script js脚本
* @param function js函数名 * @param functionName js函数名
* @param args 参数 * @param args 参数
* @return 返回结构 * @return 返回结构
* @param <T> 泛型类型
*/ */
public static <T> T executeScript(String script, String function, Object... args) { public static Object execute(String script, String functionName, Object args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
try { try {
engine.eval(script); Value executeFunction = getFunction(functionName, script);
Invocable inv = (Invocable) engine; Value javaObjectAsValue = Value.asValue(args);
return (T) inv.invokeFunction(function, args); Value result = executeFunction.execute(javaObjectAsValue);
} catch (ScriptException | NoSuchMethodException e) { return result.as(Object.class);
throw new RuntimeException(e); } catch (Exception e) {
remove(functionName);
throw new CustomException("运行失败!");
} }
} }
public static EngineResult mockExecute(String script, String functionName, Object args) {
try {
return ConsoleCapture.capture(() -> {
Context context = Context.newBuilder()
.allowAllAccess(true)
// .allowHostClassLoading(true)
// .allowIO(true)
// .allowNativeAccess(true)
.build();
try {
context.eval("js", script);
} catch (Exception e) {
String message = e.getMessage();
message = message.replace("java.lang.RuntimeException: org.graalvm.polyglot.PolyglotException: SyntaxError:", "");
throw new CustomException("js has error : " + message);
}
Value executeFunction = context.getBindings("js").getMember(functionName);
Value javaObjectAsValue = Value.asValue(args);
Value result = executeFunction.execute(javaObjectAsValue);
return result.as(Object.class);
});
} catch (CustomException e) {
EngineResult result = new EngineResult();
result.setSuccess(Boolean.FALSE);
result.setConsole(e.getMessage());
return result;
}
}
private static Value getFunction(String functionName, String script) {
return SegmentLock.lock(functionName, () -> {
if (functionMap.containsKey(functionName)) {
return functionMap.get(functionName);
}
context.eval("js", script);
Value executeFunction = context.getBindings("js").getMember(functionName);
functionMap.put(functionName, executeFunction);
return executeFunction;
});
}
public static void remove(String functionName) {
SegmentLock.lock(functionName, () -> {
functionMap.remove(functionName);
return null;
});
}
} }

View File

@@ -6,6 +6,8 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* 分段锁对象
*
* @author Clay * @author Clay
* @date 2023-10-25 * @date 2023-10-25
*/ */
@@ -15,10 +17,11 @@ public class SegmentLock {
/** /**
* 分段锁 * 分段锁
*
* @param key 锁名称 * @param key 锁名称
* @param supplier 需要执行的函数 * @param supplier 需要执行的函数
* @return 执行后的结果
* @param <T> 接收泛型 * @param <T> 接收泛型
* @return 执行后的结果
*/ */
public static <T> T lock(String key, Supplier<T> supplier) { public static <T> T lock(String key, Supplier<T> supplier) {
ReentrantLock lock = lockMap.get(key); ReentrantLock lock = lockMap.get(key);

View File

@@ -0,0 +1,28 @@
package cn.fateverse.common.code.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Clay
* @date 2024/4/22 17:10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EngineResult {
private Object result;
private String console;
private Boolean success;
public EngineResult(Object result, String console) {
success = true;
this.result = result;
this.console = console;
}
}

View File

@@ -1,5 +1,7 @@
package cn.fateverse.common.code.sandbox; package cn.fateverse.common.code.sandbox;
import cn.fateverse.common.code.engine.JavaCodeEngine;
import java.io.FilePermission; import java.io.FilePermission;
import java.lang.reflect.ReflectPermission; import java.lang.reflect.ReflectPermission;
import java.security.Permission; import java.security.Permission;
@@ -9,9 +11,9 @@ import java.util.Set;
public class SandboxSecurityManager extends SecurityManager { public class SandboxSecurityManager extends SecurityManager {
private final Map<String, Class<?>> classCache; private final Map<String, JavaCodeEngine.CacheWrapper> classCache;
public SandboxSecurityManager(Map<String, Class<?>> classCache) { public SandboxSecurityManager(Map<String, JavaCodeEngine.CacheWrapper> classCache) {
this.classCache = classCache; this.classCache = classCache;
} }

View File

@@ -12,4 +12,23 @@ import java.lang.annotation.Target;
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt { public @interface Encrypt {
Position value() default Position.ALL;
EncryptType type() default EncryptType.SM4;
enum EncryptType {
SM4,
}
enum Position {
ALL,
OUT,
IN
}
} }

View File

@@ -11,4 +11,12 @@ import java.lang.annotation.Target;
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField { public @interface EncryptField {
Position value() default Position.ALL;
enum Position {
ALL,
OUT,
IN
}
} }

View File

@@ -2,6 +2,7 @@ package cn.fateverse.common.decrypt.aspect;
import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.core.result.Result; import cn.fateverse.common.core.result.Result;
import cn.fateverse.common.decrypt.annotation.Encrypt;
import cn.fateverse.common.decrypt.annotation.EncryptField; import cn.fateverse.common.decrypt.annotation.EncryptField;
import cn.fateverse.common.decrypt.service.EncryptService; import cn.fateverse.common.decrypt.service.EncryptService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -11,12 +12,10 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import javax.annotation.Resource;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.Collection; import java.util.*;
import java.util.List;
@Slf4j @Slf4j
@Aspect @Aspect
@@ -41,36 +40,35 @@ public class EncryptAspect {
@Around("@annotation(cn.fateverse.common.decrypt.annotation.Encrypt)") @Around("@annotation(cn.fateverse.common.decrypt.annotation.Encrypt)")
public Object decryptField(ProceedingJoinPoint point) throws Throwable { public Object decryptField(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature(); MethodSignature signature = (MethodSignature) point.getSignature();
//获取请求参数
Object[] args = point.getArgs();
//获取方法 //获取方法
Method method = signature.getMethod(); Method method = signature.getMethod();
//获取方法参数 Parameter对象集 参数修饰符、参数名、注解及注解类型 Encrypt encrypt = method.getAnnotation(Encrypt.class);
Parameter[] parameters = method.getParameters(); if (encrypt == null) {
for (int i = 0; i < parameters.length; i++) { return point.proceed();
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;
list.replaceAll(encryptService::decrypt);
args[i] = list;
} catch (Exception e) {
throw new CustomException("接受参数类型错误,请使用String类型的泛型参数");
}
}
} else if (parameter.getType().getName().startsWith(BASE_PACKAGE)) { //返回一个类对象,该类对象标识此参数对象表示的参数的声明类型
decrypt(arg);
} }
//获取请求参数
Object[] args = point.getArgs();
if (Encrypt.Position.ALL.equals(encrypt.value()) || Encrypt.Position.IN.equals(encrypt.value())) {
decryptParams(args, method);
} }
//正常执行业务最后返回的返回值为Result //正常执行业务最后返回的返回值为Result
Object proceed = point.proceed(args); Object proceed = point.proceed(args);
if (Encrypt.Position.ALL.equals(encrypt.value()) || Encrypt.Position.OUT.equals(encrypt.value())) {
Result<Object> error = encryptResult(proceed);
if (error != null) {
return error;
}
}
return proceed;
}
/**
* 加密返回值
*
* @param proceed 返回执
* @return 加密结果
*/
private Result<Object> encryptResult(Object proceed) {
if (proceed instanceof Result) { if (proceed instanceof Result) {
Result<Object> result = (Result<Object>) proceed; Result<Object> result = (Result<Object>) proceed;
Object data = result.getData(); Object data = result.getData();
@@ -91,25 +89,67 @@ public class EncryptAspect {
return Result.error("加密异常!"); return Result.error("加密异常!");
} }
} }
return proceed; return null;
} }
/**
* 解密参数
*
* @param args 参数
* @param method 方法
* @throws Exception 异常
*/
private void decryptParams(Object[] args, Method method) throws Exception {
//获取方法参数 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;
list.replaceAll(encryptService::decrypt);
args[i] = list;
} catch (Exception e) {
throw new CustomException("接受参数类型错误,请使用String类型的泛型参数");
}
}
} else if (parameter.getType().getName().startsWith(BASE_PACKAGE)) {
//返回一个类对象,该类对象标识此参数对象表示的参数的声明类型
decrypt(arg);
}
}
}
private void encrypt(Object data) throws Exception { /**
* 加密
*
* @param data 数据
*/
private void encrypt(Object data) {
if (data == null) { if (data == null) {
return; return;
} }
Class<?> argClass = data.getClass(); Class<?> argClass = data.getClass();
List<Field> fieldList = new ArrayList<>();
if (argClass.getTypeName().startsWith(BASE_PACKAGE)) { if (argClass.getTypeName().startsWith(BASE_PACKAGE)) {
Field[] fields = argClass.getDeclaredFields(); getFields(argClass, fieldList);
for (Field field : fields) { for (Field field : fieldList) {
EncryptField encryptField = field.getAnnotation(EncryptField.class); EncryptField encryptField = field.getAnnotation(EncryptField.class);
field.setAccessible(true); field.setAccessible(true);
Object value = ReflectionUtils.getField(field, data); Object value = ReflectionUtils.getField(field, data);
if (null == value) { if (null == value) {
continue; continue;
} }
if (null != encryptField && value instanceof String) { if (null != encryptField && value instanceof String
&& (EncryptField.Position.ALL.equals(encryptField.value())
|| EncryptField.Position.IN.equals(encryptField.value()))) {
String decrypt = encryptService.encrypt((String) value); String decrypt = encryptService.encrypt((String) value);
ReflectionUtils.setField(field, data, decrypt); ReflectionUtils.setField(field, data, decrypt);
} else if (field.getType().getName().startsWith(BASE_PACKAGE)) { } else if (field.getType().getName().startsWith(BASE_PACKAGE)) {
@@ -128,21 +168,31 @@ public class EncryptAspect {
for (Object item : collection) { for (Object item : collection) {
encrypt(item); encrypt(item);
} }
} else if (data instanceof Map) {
Map<Object, Object> map = (Map<Object, Object>) data;
for (Object key : map.keySet()) {
Object value = map.get(key);
encrypt(key);
encrypt(value);
}
} }
} }
private void decrypt(Object arg) throws Exception { private void decrypt(Object arg) throws Exception {
Class<?> argClass = arg.getClass(); Class<?> argClass = arg.getClass();
Field[] fields = argClass.getDeclaredFields(); List<Field> fieldList = new ArrayList<>();
for (Field field : fields) { getFields(argClass, fieldList);
for (Field field : fieldList) {
EncryptField encryptField = field.getAnnotation(EncryptField.class); EncryptField encryptField = field.getAnnotation(EncryptField.class);
field.setAccessible(true); field.setAccessible(true);
Object value = ReflectionUtils.getField(field, arg); Object value = ReflectionUtils.getField(field, arg);
if (null == value) { if (null == value) {
continue; continue;
} }
if (null != encryptField && value instanceof String) { if (null != encryptField && value instanceof String
&& (EncryptField.Position.ALL.equals(encryptField.value())
|| EncryptField.Position.OUT.equals(encryptField.value()))) {
String decrypt = encryptService.decrypt((String) value); String decrypt = encryptService.decrypt((String) value);
ReflectionUtils.setField(field, arg, decrypt); ReflectionUtils.setField(field, arg, decrypt);
} else if (field.getType().getName().startsWith(BASE_PACKAGE)) { } else if (field.getType().getName().startsWith(BASE_PACKAGE)) {
@@ -153,4 +203,14 @@ public class EncryptAspect {
} }
} }
private void getFields(Class<?> argClass, List<Field> fieldList) {
if (argClass.getTypeName().startsWith(BASE_PACKAGE)) {
Field[] fields = argClass.getDeclaredFields();
fieldList.addAll(Arrays.asList(fields));
getFields(argClass.getSuperclass(), fieldList);
}
}
} }

View File

@@ -1,6 +1,5 @@
package cn.fateverse.common.security.filter; package cn.fateverse.common.security.filter;
import cn.fateverse.common.core.utils.ObjectUtils;
import cn.fateverse.common.security.service.TokenService; import cn.fateverse.common.security.service.TokenService;
import cn.fateverse.common.security.entity.LoginUser; import cn.fateverse.common.security.entity.LoginUser;
import cn.fateverse.common.security.utils.SecurityUtils; import cn.fateverse.common.security.utils.SecurityUtils;
@@ -8,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource; import javax.annotation.Resource;

View File

@@ -76,6 +76,10 @@
<groupId>cn.fateverse</groupId> <groupId>cn.fateverse</groupId>
<artifactId>common-excel</artifactId> <artifactId>common-excel</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>${project.artifactId}</finalName> <finalName>${project.artifactId}</finalName>

View File

@@ -0,0 +1,53 @@
package cn.fateverse.query.config;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.TimeUnit;
/**
* @author Clay
* @date 2024/4/22 10:28
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate httpRestTemplate() {
ClientHttpRequestFactory factory = httpRequestFactory();
RestTemplate restTemplate = new RestTemplate(factory);
// 可以添加消息转换
//restTemplate.setMessageConverters(...);
// 可以增加拦截器
//restTemplate.setInterceptors(...);
return restTemplate;
}
public ClientHttpRequestFactory httpRequestFactory() {
return new OkHttp3ClientHttpRequestFactory(okHttpConfigClient());
}
public OkHttpClient okHttpConfigClient() {
return new OkHttpClient().newBuilder()
.connectionPool(pool())
.connectTimeout(1, TimeUnit.SECONDS)
.readTimeout(3, TimeUnit.SECONDS)
.writeTimeout(3, TimeUnit.SECONDS)
.hostnameVerifier((hostname, session) -> true)
// 设置代理
// .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888)))
// 拦截器
// .addInterceptor()
.build();
}
public ConnectionPool pool() {
return new ConnectionPool(2000, 300, TimeUnit.SECONDS);
}
}

View File

@@ -6,9 +6,29 @@ package cn.fateverse.query.constant;
*/ */
public class QueryConstant { public class QueryConstant {
/**
* 自定义查询权限前缀
*/
public static final String PERMISSIONS_KEY = "custom:query:online:"; public static final String PERMISSIONS_KEY = "custom:query:online:";
/**
* 自定义接口Redis前缀key
*/
public static final String PORTAL_KEY = "custom:query:portal:"; public static final String PORTAL_KEY = "custom:query:portal:";
/**
* 接口开发状态
*/
public static final Integer PORTAL_DEV = 0;
/**
* 接口发布状态
*/
public static final Integer PORTAL_PUBLISH = 1;
/**
* 接口内部使用
*/
public static final Integer PORTAL_INWARD = 2;
} }

View File

@@ -66,13 +66,11 @@ public class DataAdapterController {
} }
@ApiOperation("获取数据源适配器详细信息") @ApiOperation("获取数据源适配器详细信息")
@Encrypt
@GetMapping("/{adapterId}") @GetMapping("/{adapterId}")
@PreAuthorize("@ss.hasPermission('query:adapter:info')") @PreAuthorize("@ss.hasPermission('query:adapter:info')")
public Result<DataAdapterVo> info(@PathVariable @EncryptField String adapterId) { public Result<DataAdapterVo> info(@PathVariable Long adapterId) {
ObjectUtils.checkPk(adapterId); ObjectUtils.checkPk(adapterId);
Long id = Long.valueOf(adapterId); DataAdapterVo dataAdapter = dataAdapterService.searchById(adapterId);
DataAdapterVo dataAdapter = dataAdapterService.searchById(id);
return Result.ok(dataAdapter); return Result.ok(dataAdapter);
} }

View File

@@ -5,7 +5,10 @@ import cn.fateverse.common.core.result.page.TableDataInfo;
import cn.fateverse.common.core.utils.ObjectUtils; import cn.fateverse.common.core.utils.ObjectUtils;
import cn.fateverse.common.decrypt.annotation.Encrypt; import cn.fateverse.common.decrypt.annotation.Encrypt;
import cn.fateverse.common.decrypt.annotation.EncryptField; import cn.fateverse.common.decrypt.annotation.EncryptField;
import cn.fateverse.query.entity.dto.DataAdapterCodeDto;
import cn.fateverse.query.entity.dto.MockParam;
import cn.fateverse.query.entity.dto.PortalDto; import cn.fateverse.query.entity.dto.PortalDto;
import cn.fateverse.query.entity.dto.PortalPublish;
import cn.fateverse.query.entity.query.PortalQuery; import cn.fateverse.query.entity.query.PortalQuery;
import cn.fateverse.query.entity.vo.PortalIdWrapper; import cn.fateverse.query.entity.vo.PortalIdWrapper;
import cn.fateverse.query.entity.vo.PortalVo; import cn.fateverse.query.entity.vo.PortalVo;
@@ -36,7 +39,7 @@ public class PortalController {
} }
@ApiOperation("获取接口管理表详细信息") @ApiOperation("获取接口管理表详细信息")
@Encrypt @Encrypt(Encrypt.Position.IN)
@GetMapping("/{portalId}") @GetMapping("/{portalId}")
@PreAuthorize("@ss.hasPermission('query:portal:info')") @PreAuthorize("@ss.hasPermission('query:portal:info')")
public Result<PortalVo> info(@PathVariable @EncryptField String portalId) { public Result<PortalVo> info(@PathVariable @EncryptField String portalId) {
@@ -47,6 +50,17 @@ public class PortalController {
} }
@ApiOperation("详情接口")
@Encrypt(Encrypt.Position.IN)
@GetMapping("/detail/{portalId}")
public Result<PortalVo> detail(@PathVariable @EncryptField String portalId) {
ObjectUtils.checkPk(portalId);
Long value = Long.valueOf(portalId);
PortalVo portal = portalService.searchDetailById(value);
return Result.ok(portal);
}
@ApiOperation("获取接口管理表列表") @ApiOperation("获取接口管理表列表")
@GetMapping @GetMapping
@Encrypt @Encrypt
@@ -57,7 +71,7 @@ public class PortalController {
} }
@ApiOperation("新增接口") @ApiOperation("新增接口")
@Encrypt @Encrypt(Encrypt.Position.OUT)
@PostMapping @PostMapping
@PreAuthorize("@ss.hasPermission('query:portal:add')") @PreAuthorize("@ss.hasPermission('query:portal:add')")
public Result<PortalIdWrapper> add(@RequestBody @Validated PortalDto portalDto) { public Result<PortalIdWrapper> add(@RequestBody @Validated PortalDto portalDto) {
@@ -66,7 +80,7 @@ public class PortalController {
} }
@ApiOperation("修改接口") @ApiOperation("修改接口")
@Encrypt @Encrypt(Encrypt.Position.OUT)
@PutMapping @PutMapping
@PreAuthorize("@ss.hasPermission('query:portal:edit')") @PreAuthorize("@ss.hasPermission('query:portal:edit')")
public Result<PortalIdWrapper> edit(@RequestBody @Validated PortalDto portalDto) { public Result<PortalIdWrapper> edit(@RequestBody @Validated PortalDto portalDto) {
@@ -76,4 +90,75 @@ public class PortalController {
} }
@ApiOperation("模拟执行")
@PostMapping("/mock/execute")
@PreAuthorize("@ss.hasPermission('query:portal:execute')")
public Result<Object> mockExecute(@RequestBody @Validated MockParam mockParam) {
if (ObjectUtils.isEmpty(mockParam.getCode())) {
return Result.error("代码不能为空");
}
Object result = portalService.mockExecute(mockParam);
return Result.ok(result);
}
@ApiOperation("获取接口数据")
@PostMapping("/mock/data")
@PreAuthorize("@ss.hasPermission('query:portal:data')")
public Result<Object> mockData(@RequestBody @Validated MockParam mockParam) {
Object result = portalService.mockData(mockParam);
return Result.ok(result);
}
@ApiOperation("保存代码")
@PostMapping("/save/code")
@PreAuthorize("@ss.hasPermission('query:portal:edit')")
public Result<Void> saveCode(@RequestBody @Validated DataAdapterCodeDto dataAdapterCodeDto) {
portalService.saveCode(dataAdapterCodeDto);
return Result.ok();
}
@ApiOperation("列表接口发布")
@PostMapping("/list/publish")
@Encrypt(Encrypt.Position.IN)
@PreAuthorize("@ss.hasPermission('query:portal:edit')")
public Result<Void> listPublish(@RequestBody @Validated PortalPublish portalPublish) {
portalService.publish(portalPublish, Boolean.TRUE);
return Result.ok();
}
@ApiOperation("设计器接口发布")
@PostMapping("/publish")
@PreAuthorize("@ss.hasPermission('query:portal:edit')")
public Result<Void> publish(@RequestBody @Validated PortalPublish portalPublish) {
if (ObjectUtils.isEmpty(portalPublish.getCode())){
return Result.error("代码不能为空!");
}
portalService.publish(portalPublish, Boolean.FALSE);
return Result.ok();
}
@ApiOperation("撤销发布")
@Encrypt
@PutMapping("/cancel/{portalId}")
@PreAuthorize("@ss.hasPermission('query:portal:edit')")
public Result<PortalIdWrapper> cancel(@PathVariable @EncryptField String portalId) {
ObjectUtils.checkPk(portalId);
Long value = Long.valueOf(portalId);
portalService.cancel(value);
return Result.ok();
}
@ApiOperation("删除接口")
@Encrypt
@DeleteMapping("/{portalId}")
@PreAuthorize("@ss.hasPermission('query:portal:remove')")
public Result<Void> remove(@PathVariable @EncryptField String portalId) {
ObjectUtils.checkPk(portalId);
Long value = Long.valueOf(portalId);
portalService.delete(value);
return Result.ok();
}
} }

View File

@@ -1,7 +1,11 @@
package cn.fateverse.query.dubbo; package cn.fateverse.query.dubbo;
import cn.fateverse.query.constant.QueryConstant; import cn.fateverse.query.constant.QueryConstant;
import cn.fateverse.query.entity.Portal;
import cn.fateverse.query.entity.PortalMapping;
import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.entity.bo.PortalBo;
import cn.fateverse.query.mapper.PortalMapper;
import cn.fateverse.query.mapper.PortalMappingMapper;
import cn.fateverse.query.portal.service.HandlerMethodService; import cn.fateverse.query.portal.service.HandlerMethodService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.DubboService;
@@ -9,6 +13,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
/** /**
* @author Clay * @author Clay
@@ -18,10 +23,14 @@ import javax.annotation.Resource;
@DubboService(scope = "remote") @DubboService(scope = "remote")
public class DubboDispatchServletPublishImpl implements DubboDispatchServletPublish { public class DubboDispatchServletPublishImpl implements DubboDispatchServletPublish {
@Resource @Resource
private HandlerMethodService methodService; private HandlerMethodService methodService;
@Resource
private PortalMapper portalMapper;
@Resource
private PortalMappingMapper portalMappingMapper;
@Resource @Resource
private RedisTemplate<String, PortalBo> redisTemplate; private RedisTemplate<String, PortalBo> redisTemplate;
@@ -30,8 +39,14 @@ public class DubboDispatchServletPublishImpl implements DubboDispatchServletPubl
public Boolean publish(String path, String requestMethod) { public Boolean publish(String path, String requestMethod) {
PortalBo portalBo = redisTemplate.opsForValue().get(QueryConstant.PORTAL_KEY + path + ":" + requestMethod); PortalBo portalBo = redisTemplate.opsForValue().get(QueryConstant.PORTAL_KEY + path + ":" + requestMethod);
if (portalBo == null) { if (portalBo == null) {
Portal portal = portalMapper.selectByPath(path, requestMethod);
if (portal == null) {
return Boolean.FALSE; return Boolean.FALSE;
} }
List<PortalMapping> portalMappings = portalMappingMapper.selectByPortalId(portal.getPortalId());
portalBo = PortalBo.toPortalBo(portal, portalMappings);
redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo);
}
try { try {
log.info("registerMapping, path:{}, requestMethod:{}", path, requestMethod); log.info("registerMapping, path:{}, requestMethod:{}", path, requestMethod);
methodService.registerMapping(path, RequestMethod.valueOf(requestMethod), Boolean.FALSE); methodService.registerMapping(path, RequestMethod.valueOf(requestMethod), Boolean.FALSE);

View File

@@ -69,15 +69,19 @@ public class DataAdapter extends BaseEntity {
this.code = "\n" + this.code = "\n" +
"import java.util.*;\n" + "import java.util.*;\n" +
"import java.util.stream.*;\n" + "import java.util.stream.*;\n" +
"import com.alibaba.fastjson2.*;\n" +
"\n" + "\n" +
"public class DataAdapter {\n" + "public class DataAdapter {\n" +
"\n" + "\n" +
" public static Object execute(List<Map<String, Object>> data) {\n" + " public static Object execute(Object data) {\n" +
" return data;\n" + " return data;\n" +
" }\n" + " }\n" +
"}\n"; "}\n";
} else if (DataAdapterType.JAVA_SCRIPT.equals(type)) { } else if (DataAdapterType.JAVA_SCRIPT.equals(type)) {
//JavaScript代码初始化 //JavaScript代码初始化
this.code = "function execute(data) {\n" +
"\n" +
"}";
} }
} }

View File

@@ -3,11 +3,11 @@ package cn.fateverse.query.entity;
import cn.fateverse.common.core.annotaion.EnableAutoField; import cn.fateverse.common.core.annotaion.EnableAutoField;
import cn.fateverse.common.core.entity.BaseEntity; import cn.fateverse.common.core.entity.BaseEntity;
import cn.fateverse.query.enums.PortalEnum; import cn.fateverse.query.enums.PortalEnum;
import cn.fateverse.query.enums.PortalPremEnum;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.springframework.web.bind.annotation.RequestMethod;
/** /**
@@ -33,6 +33,16 @@ public class Portal extends BaseEntity {
*/ */
private Long queryId; private Long queryId;
/**
* 菜单id
*/
private Long menuId;
/**
* 接口id
*/
private Long interfaceId;
/** /**
* 数据适配器id * 数据适配器id
*/ */
@@ -44,9 +54,9 @@ public class Portal extends BaseEntity {
private String portalName; private String portalName;
/** /**
* 是否匿名 * 权限类型
*/ */
private Boolean anonymity; private PortalPremEnum permissionType;
/** /**
* 接口类型 * 接口类型
@@ -58,19 +68,21 @@ public class Portal extends BaseEntity {
*/ */
private String requestMethod; private String requestMethod;
/**
* 是否创建数据适配器
*/
private Boolean createDataAdapter; private Boolean createDataAdapter;
/**
* 是否分页
*/
private Boolean page;
/** /**
* 系统暴露地址 * 系统暴露地址
*/ */
private String path; private String path;
/**
* 第三方接口地址
*/
private String url;
/** /**
* 状态 * 状态
*/ */

View File

@@ -0,0 +1,34 @@
package cn.fateverse.query.entity;
import lombok.Data;
/**
* 第三方接口信息
*
* @author Clay
* @date 2024/4/26 11:17
*/
@Data
public class PortalInterface {
/**
* 接口id
*/
private Long interfaceId;
/**
* 请求地址
*/
private String url;
/**
* 请求类型
*/
private String contentType;
/**
* 请求类型
*/
private String requestMethod;
}

View File

@@ -43,12 +43,26 @@ public class PortalMapping {
* 映射值 自定义查询映射值为查询条件的id 第三方接口则为接口查询的key * 映射值 自定义查询映射值为查询条件的id 第三方接口则为接口查询的key
*/ */
private String mappingValue; private String mappingValue;
/**
* 输入类型
*/
private String inputType;
private Boolean required;
/**
* 描述
*/
private String description;
public Boolean check() { public Boolean check() {
if (ObjectUtils.isEmpty(mappingKey) if (ObjectUtils.isEmpty(mappingKey)
|| ObjectUtils.isEmpty(mappingValue) || ObjectUtils.isEmpty(mappingValue)
|| ObjectUtils.isEmpty(mappingType)){ || ObjectUtils.isEmpty(mappingType)
// || ObjectUtils.isEmpty(inputType)
) {
return Boolean.FALSE; return Boolean.FALSE;
} }
return Boolean.TRUE; return Boolean.TRUE;

View File

@@ -22,11 +22,13 @@ import java.util.List;
public class PortalBo implements Serializable { public class PortalBo implements Serializable {
private Long portalId; private Long portalId;
private Long queryId; private Long queryId;
private Long interfaceId;
private Long adapterId; private Long adapterId;
private PortalEnum type; private PortalEnum type;
private String requestMethod; private String requestMethod;
private Boolean createDataAdapter;
private Boolean page;
private String path; private String path;
private String url;
private Integer state; private Integer state;
private List<PortalMapping> mappings; private List<PortalMapping> mappings;
@@ -34,11 +36,13 @@ public class PortalBo implements Serializable {
return PortalBo.builder() return PortalBo.builder()
.portalId(portal.getPortalId()) .portalId(portal.getPortalId())
.queryId(portal.getQueryId()) .queryId(portal.getQueryId())
.interfaceId(portal.getInterfaceId())
.adapterId(portal.getAdapterId()) .adapterId(portal.getAdapterId())
.type(portal.getType()) .type(portal.getType())
.requestMethod(portal.getRequestMethod()) .requestMethod(portal.getRequestMethod())
.createDataAdapter(portal.getCreateDataAdapter())
.page(portal.getPage())
.path(portal.getPath()) .path(portal.getPath())
.url(portal.getUrl())
.state(portal.getState()) .state(portal.getState())
.mappings(mappings) .mappings(mappings)
.build(); .build();

View File

@@ -0,0 +1,21 @@
package cn.fateverse.query.entity.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author Clay
* @date 2024/4/24 8:57
*/
@Data
public class DataAdapterCodeDto {
@NotNull(message = "id不能为空")
private Long portalId;
@NotBlank(message = "代码内容不能为空!")
private String code;
}

View File

@@ -0,0 +1,35 @@
package cn.fateverse.query.entity.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* @author Clay
* @date 2024/4/21 19:07
*/
@Data
public class MockParam {
@NotNull(message = "id不能为空")
private Long portalId;
private String code;
private Integer pageSize;
private Integer pageNum;
private List<Param> params;
@Data
public static class Param {
private String key;
private Object value;
private String type;
}
}

View File

@@ -3,7 +3,9 @@ package cn.fateverse.query.entity.dto;
import cn.fateverse.common.core.utils.ObjectUtils; import cn.fateverse.common.core.utils.ObjectUtils;
import cn.fateverse.query.entity.Portal; import cn.fateverse.query.entity.Portal;
import cn.fateverse.query.entity.PortalMapping; import cn.fateverse.query.entity.PortalMapping;
import cn.fateverse.query.enums.DataAdapterType;
import cn.fateverse.query.enums.PortalEnum; import cn.fateverse.query.enums.PortalEnum;
import cn.fateverse.query.enums.PortalPremEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@@ -49,11 +51,11 @@ public class PortalDto {
private String portalName; private String portalName;
/** /**
* 是否匿名 * 权限类型
*/ */
@ApiModelProperty("是否匿名") @ApiModelProperty("权限类型")
@NotNull(message = "是否匿名不能为空") @NotNull(message = "权限类型不能为空")
private Boolean anonymity; private PortalPremEnum permissionType;
/** /**
* 接口类型 * 接口类型
@@ -70,6 +72,21 @@ public class PortalDto {
@NotNull(message = "是否创建数据适配器不能为空!") @NotNull(message = "是否创建数据适配器不能为空!")
private Boolean createDataAdapter; private Boolean createDataAdapter;
@ApiModelProperty("请求地址")
private String url;
@ApiModelProperty("请求类型")
private String contentType;
@ApiModelProperty("接口请求方法类型")
private String interfaceRequestMethod;
/**
* 是否分页
*/
private Boolean page;
/** /**
* 系统暴露地址 * 系统暴露地址
*/ */
@@ -77,13 +94,6 @@ public class PortalDto {
@NotBlank(message = "系统暴露地址不能为空") @NotBlank(message = "系统暴露地址不能为空")
private String path; private String path;
/**
* 第三方接口地址
*/
@ApiModelProperty("第三方接口地址")
private String url;
/** /**
* 状态 * 状态
*/ */
@@ -96,10 +106,8 @@ public class PortalDto {
@ApiModelProperty("备注") @ApiModelProperty("备注")
private String remark; private String remark;
@ApiModelProperty("适配器代码类型")
@ApiModelProperty("数据适配器信息") private DataAdapterType adapterCodeType;
private DataAdapterDto dataAdapter;
@ApiModelProperty("映射关系") @ApiModelProperty("映射关系")
private List<PortalMapping> mappings; private List<PortalMapping> mappings;
@@ -110,12 +118,12 @@ public class PortalDto {
.queryId(queryId) .queryId(queryId)
.adapterId(adapterId) .adapterId(adapterId)
.portalName(portalName) .portalName(portalName)
.anonymity(anonymity) .permissionType(permissionType)
.createDataAdapter(createDataAdapter) .createDataAdapter(createDataAdapter)
.requestMethod(requestMethod) .requestMethod(requestMethod)
.page(page)
.type(type) .type(type)
.path(path) .path(path)
.url(url)
.state(state) .state(state)
.build(); .build();
try { try {
@@ -131,6 +139,9 @@ public class PortalDto {
if (!mapping.check()) { if (!mapping.check()) {
throw new RuntimeException("映射关系不完全,请检查映射关系"); throw new RuntimeException("映射关系不完全,请检查映射关系");
} }
if (ObjectUtils.isEmpty(mapping.getRequired())) {
mapping.setRequired(Boolean.FALSE);
}
} }
} }
portal.setRemark(remark); portal.setRemark(remark);

View File

@@ -0,0 +1,26 @@
package cn.fateverse.query.entity.dto;
import cn.fateverse.common.decrypt.annotation.EncryptField;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author Clay
* @date 2024/4/30 16:58
*/
@Data
public class PortalPublish {
@EncryptField
@NotBlank(message = "接口id不能为空")
private String portalId;
private String code;
public Long getLongPortalId() {
return Long.valueOf(portalId);
}
}

View File

@@ -31,7 +31,6 @@ public class DataAdapterQuery {
@ApiModelProperty("数据适配器代码类型 Java or Js") @ApiModelProperty("数据适配器代码类型 Java or Js")
private DataAdapterType type; private DataAdapterType type;
@ApiModelProperty("是否通用")
private Boolean common; private Boolean common;
} }

View File

@@ -1,6 +1,7 @@
package cn.fateverse.query.entity.query; package cn.fateverse.query.entity.query;
import cn.fateverse.query.enums.PortalEnum; import cn.fateverse.query.enums.PortalEnum;
import cn.fateverse.query.enums.PortalPremEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -26,10 +27,10 @@ public class PortalQuery {
private String portalName; private String portalName;
/** /**
* 是否匿名 * 权限类型
*/ */
@ApiModelProperty("是否匿名") @ApiModelProperty("权限类型")
private Boolean anonymity; private PortalPremEnum permissionType;
/** /**
* 接口类型 * 接口类型
@@ -42,4 +43,6 @@ public class PortalQuery {
*/ */
@ApiModelProperty("系统暴露地址") @ApiModelProperty("系统暴露地址")
private String path; private String path;
private Integer state;
} }

View File

@@ -2,6 +2,7 @@ package cn.fateverse.query.entity.vo;
import cn.fateverse.query.entity.Portal; import cn.fateverse.query.entity.Portal;
import cn.fateverse.query.entity.PortalMapping; import cn.fateverse.query.entity.PortalMapping;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import lombok.Data; import lombok.Data;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
@@ -20,12 +21,34 @@ import java.util.List;
@ApiModel("接口详细管理表Vo") @ApiModel("接口详细管理表Vo")
public class PortalVo extends SimplePortalVo { public class PortalVo extends SimplePortalVo {
/**
* 查询类型
*/
private Integer queryType; private Integer queryType;
/**
* 是否创建数据适配器
*/
private Boolean createDataAdapter; private Boolean createDataAdapter;
/**
* 是否分页
*/
private Boolean page;
private String url;
/**
* 条件映射
*/
private List<PortalMapping> mappings; private List<PortalMapping> mappings;
/**
* 数据适配器信息
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private DataAdapterVo dataAdapter;
public static PortalVo toPortalVo(Portal portal) { public static PortalVo toPortalVo(Portal portal) {
return toPortalVo(portal, null); return toPortalVo(portal, null);
} }

View File

@@ -3,14 +3,15 @@ package cn.fateverse.query.entity.vo;
import cn.fateverse.common.core.annotaion.Excel; import cn.fateverse.common.core.annotaion.Excel;
import cn.fateverse.common.decrypt.annotation.EncryptField; import cn.fateverse.common.decrypt.annotation.EncryptField;
import cn.fateverse.query.entity.Portal; import cn.fateverse.query.entity.Portal;
import cn.fateverse.query.enums.DataAdapterType;
import cn.fateverse.query.enums.PortalEnum; import cn.fateverse.query.enums.PortalEnum;
import cn.fateverse.query.enums.PortalPremEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.springframework.util.ObjectUtils;
import java.util.Date; import java.util.Date;
@@ -37,16 +38,14 @@ public class SimplePortalVo {
* 自定义查询id * 自定义查询id
*/ */
@ApiModelProperty("自定义查询id") @ApiModelProperty("自定义查询id")
@EncryptField private Long queryId;
private String queryId;
/** /**
* 数据适配器id * 数据适配器id
*/ */
@ApiModelProperty("数据适配器id") @ApiModelProperty("数据适配器id")
@EncryptField private Long adapterId;
private String adapterId;
/** /**
* 接口名称 * 接口名称
@@ -68,17 +67,20 @@ public class SimplePortalVo {
/** /**
* 数据适配器名称 * 数据适配器名称
*/ */
@ApiModelProperty("数据适配器名称") @ApiModelProperty("数据适配器类型")
@Excel("数据适配器名称") @Excel("数据适配器类型")
private String adapterName; private DataAdapterType adapterCodeType;
private Boolean createDataAdapter;
/** /**
* 是否匿名 * 权限类型
*/ */
@ApiModelProperty("是否匿名") @ApiModelProperty("权限类型")
@Excel("是否匿名") @Excel("权限类型")
private Boolean anonymity; private PortalPremEnum permissionType;
/** /**
* 接口类型 * 接口类型
@@ -126,7 +128,10 @@ public class SimplePortalVo {
SimplePortalVo portalVo = SimplePortalVo.builder() SimplePortalVo portalVo = SimplePortalVo.builder()
.portalId(String.valueOf(portal.getPortalId())) .portalId(String.valueOf(portal.getPortalId()))
.portalName(portal.getPortalName()) .portalName(portal.getPortalName())
.anonymity(portal.getAnonymity()) .permissionType(portal.getPermissionType())
.queryId(portal.getQueryId())
.adapterId(portal.getAdapterId())
.createDataAdapter(portal.getCreateDataAdapter())
.type(portal.getType()) .type(portal.getType())
.path(portal.getPath()) .path(portal.getPath())
.state(portal.getState()) .state(portal.getState())
@@ -136,12 +141,6 @@ public class SimplePortalVo {
.build(); .build();
portalVo.setCreateTime(portal.getCreateTime()); portalVo.setCreateTime(portal.getCreateTime());
portalVo.setUpdateTime(portal.getUpdateTime()); portalVo.setUpdateTime(portal.getUpdateTime());
if (!ObjectUtils.isEmpty(portal.getQueryId())) {
portalVo.setQueryId(String.valueOf(portal.getQueryId()));
}
if (!ObjectUtils.isEmpty(portal.getAdapterId())) {
portalVo.setAdapterId(String.valueOf(portal.getAdapterId()));
}
return portalVo; return portalVo;
} }

View File

@@ -2,7 +2,7 @@ package cn.fateverse.query.enums;
public enum DataAdapterType { public enum DataAdapterType {
JAVA("java"), JAVA("Java"),
JAVA_SCRIPT("JavaScript"); JAVA_SCRIPT("JavaScript");

View File

@@ -0,0 +1,22 @@
package cn.fateverse.query.enums;
/**
* @author Clay
* @date 2024/4/15 11:23
*/
public enum PortalPremEnum {
/**
* 匿名
*/
ANONYMITY,
/**
* 第三方授权
*/
EXTERNAL,
/**
* 内部使用
*/
LOCAL;
}

View File

@@ -0,0 +1,73 @@
package cn.fateverse.query.handler.adapter;
import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.query.entity.DataAdapter;
import cn.fateverse.query.handler.reader.EngineExecuteHandlerReader;
import cn.fateverse.query.mapper.DataAdapterMapper;
import org.springframework.util.ObjectUtils;
/**
* @author Clay
* @date 2024/4/19 22:44
*/
public abstract class AbstractDataAdapterHandler implements DataAdapterHandler {
protected final DataAdapterMapper dataAdapterMapper;
protected final EngineExecuteHandlerReader handlerReader;
protected AbstractDataAdapterHandler(DataAdapterMapper dataAdapterMapper, EngineExecuteHandlerReader handlerReader) {
this.dataAdapterMapper = dataAdapterMapper;
this.handlerReader = handlerReader;
}
protected Object execute(Long adapterId, Object data) {
if (ObjectUtils.isEmpty(adapterId)) {
return data;
}
//获取当当前接口对应的数据适配器
DataAdapter dataAdapter = dataAdapterMapper.selectById(adapterId);
if (ObjectUtils.isEmpty(dataAdapter)) {
throw new RuntimeException("dataAdapter is null");
}
handlerReader.preconditioning(dataAdapter);
Object result = handlerReader.execute(dataAdapter, data);
if (ObjectUtils.isEmpty(result)) {
throw new RuntimeException("执行结果错误");
}
return result;
}
protected EngineResult mockExecute(Long adapterId, String code, Object data) {
//获取当当前接口对应的数据适配器
DataAdapter dataAdapter = dataAdapterMapper.selectById(adapterId);
if (ObjectUtils.isEmpty(dataAdapter)) {
throw new RuntimeException("dataAdapter is null");
}
dataAdapter.setCode(code);
handlerReader.preconditioning(dataAdapter);
EngineResult execute = handlerReader.mockExecute(dataAdapter, data);
if (ObjectUtils.isEmpty(execute)) {
throw new RuntimeException("执行结果错误");
}
dataAdapterMapper.updateCode(dataAdapter);
return execute;
}
@Override
public boolean remove(Long adapterId) {
if (ObjectUtils.isEmpty(adapterId)) {
throw new RuntimeException("adapterId is null");
}
DataAdapter dataAdapter = dataAdapterMapper.selectById(adapterId);
if (ObjectUtils.isEmpty(dataAdapter)) {
throw new RuntimeException("dataAdapter is null");
}
return handlerReader.remove(dataAdapter);
}
}

View File

@@ -1,8 +1,10 @@
package cn.fateverse.query.handler.adapter; package cn.fateverse.query.handler.adapter;
import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.dto.MockParam;
import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.entity.bo.PortalBo;
import javax.servlet.http.HttpServletRequest;
/** /**
* @author Clay * @author Clay
* @date 2023-10-31 20:52 * @date 2023-10-31 20:52
@@ -12,19 +14,27 @@ public interface DataAdapterHandler {
/** /**
* 模拟执行 * 模拟执行
* *
* @param dataAdapter 数据适配器信息 * @param portal 接口对象
* @param portal * @param mockParam 请求头
* @return 执行结果 * @return 执行结果
*/ */
Object mockExecute(DataAdapter dataAdapter, PortalBo portal, Object param); Object mockExecute(PortalBo portal, MockParam mockParam);
/** /**
* 真实执行 * 真实执行
* *
* @param dataAdapter 数据适配器信息 * @param portal 接口对象
* @param portal * @param request
* @return 执行结果 * @return 执行结果
*/ */
Object execute(DataAdapter dataAdapter, PortalBo portal, Object param); Object execute(PortalBo portal, HttpServletRequest request);
/**
* 删除数据适配器
*
* @param adapterId 数据适配器id
* @return 删除结果
*/
boolean remove(Long adapterId);
} }

View File

@@ -0,0 +1,129 @@
package cn.fateverse.query.handler.adapter.impl;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.query.entity.PortalInterface;
import cn.fateverse.query.entity.PortalMapping;
import cn.fateverse.query.entity.dto.MockParam;
import cn.fateverse.query.entity.bo.PortalBo;
import cn.fateverse.query.enums.PortalEnum;
import cn.fateverse.query.handler.adapter.AbstractDataAdapterHandler;
import cn.fateverse.query.handler.reader.EngineExecuteHandlerReader;
import cn.fateverse.query.mapper.DataAdapterMapper;
import cn.fateverse.query.mapper.PortalInterfaceMapper;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @author Clay
* @date 2024/4/19 22:12
*/
@Slf4j
@Service
public class ExternalDataAdapterHandler extends AbstractDataAdapterHandler {
/**
* 请求对象
*/
private final RestTemplate restTemplate;
private final PortalInterfaceMapper portalInterfaceMapper;
public ExternalDataAdapterHandler(RestTemplate restTemplate,
DataAdapterMapper dataAdapterMapper,
EngineExecuteHandlerReader handlerReader,
PortalInterfaceMapper portalInterfaceMapper) {
super(dataAdapterMapper, handlerReader);
this.restTemplate = restTemplate;
this.portalInterfaceMapper = portalInterfaceMapper;
}
@Override
public Object mockExecute(PortalBo portal, MockParam mockParam) {
if (!PortalEnum.EXTERNAL.equals(portal.getType())) {
return null;
}
PortalInterface portalInterface = portalInterfaceMapper.selectById(portal.getInterfaceId());
if (ObjectUtils.isEmpty(portalInterface)) {
throw new CustomException("接口不存在");
}
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(portalInterface.getContentType());
headers.setContentType(type);
Map<String, Object> requestParams = new HashMap<>();
if (!ObjectUtils.isEmpty(mockParam.getParams())) {
for (MockParam.Param param : mockParam.getParams()) {
if (!ObjectUtils.isEmpty(param.getKey()) && !ObjectUtils.isEmpty(param.getValue())) {
requestParams.put(param.getKey(), param.getValue());
}
}
}
JSONObject response = null;
switch (portalInterface.getRequestMethod()) {
case "GET":
response = restTemplate.getForObject(portalInterface.getUrl(), JSONObject.class, requestParams);
break;
case "POST":
response = restTemplate.postForObject(portalInterface.getUrl(), requestParams, JSONObject.class);
break;
default:
throw new CustomException("请求方式错误");
}
if (ObjectUtils.isEmpty(portal.getAdapterId()) || !portal.getCreateDataAdapter()) {
return response;
} else {
return super.mockExecute(portal.getAdapterId(), mockParam.getCode(), response);
}
}
@Override
public Object execute(PortalBo portal, HttpServletRequest request) {
if (!PortalEnum.EXTERNAL.equals(portal.getType())) {
return null;
}
PortalInterface portalInterface = portalInterfaceMapper.selectById(portal.getInterfaceId());
if (ObjectUtils.isEmpty(portalInterface)) {
throw new CustomException("接口不存在");
}
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(portalInterface.getContentType());
headers.setContentType(type);
Map<String, Object> requestParams = new HashMap<>();
for (PortalMapping mapping : portal.getMappings()) {
String mappingValue = mapping.getMappingValue();
String mappingKey = mapping.getMappingKey();
if (mapping.getMappingType() == 0) {
requestParams.put(mappingKey, request.getParameter(mappingValue));
} else if (mapping.getMappingType() == 1) {
headers.add(mappingKey, request.getHeader(mappingValue));
} else {
requestParams.put(mappingKey, request.getParameter(mappingValue));
}
}
JSONObject response = null;
switch (portalInterface.getRequestMethod()) {
case "GET":
response = restTemplate.getForObject(portalInterface.getUrl(), JSONObject.class, requestParams);
break;
case "POST":
response = restTemplate.postForObject(portalInterface.getUrl(), requestParams, JSONObject.class);
break;
default:
throw new CustomException("请求方式错误");
}
if (ObjectUtils.isEmpty(portal.getAdapterId()) || !portal.getCreateDataAdapter()) {
return response;
} else {
return super.execute(portal.getAdapterId(), response);
}
}
}

View File

@@ -2,20 +2,23 @@ package cn.fateverse.query.handler.adapter.impl;
import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.core.result.page.TableDataInfo; import cn.fateverse.common.core.result.page.TableDataInfo;
import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.dto.MockParam;
import cn.fateverse.query.entity.PortalMapping;
import cn.fateverse.query.entity.UniQuery; import cn.fateverse.query.entity.UniQuery;
import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.entity.bo.PortalBo;
import cn.fateverse.query.entity.dto.SearchInfo; import cn.fateverse.query.entity.dto.UniConDto;
import cn.fateverse.query.enums.PortalEnum; import cn.fateverse.query.enums.PortalEnum;
import cn.fateverse.query.handler.adapter.DataAdapterHandler; import cn.fateverse.query.handler.adapter.AbstractDataAdapterHandler;
import cn.fateverse.query.handler.reader.EngineExecuteHandlerReader; import cn.fateverse.query.handler.reader.EngineExecuteHandlerReader;
import cn.fateverse.query.mapper.DataAdapterMapper;
import cn.fateverse.query.mapper.UniQueryMapper; import cn.fateverse.query.mapper.UniQueryMapper;
import cn.fateverse.query.service.DynamicDataSearchService; import cn.fateverse.query.service.DynamicDataSearchService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -25,55 +28,86 @@ import java.util.Map;
*/ */
@Slf4j @Slf4j
@Service @Service
public class LocalDataAdapterHandler implements DataAdapterHandler { public class LocalDataAdapterHandler extends AbstractDataAdapterHandler {
private final UniQueryMapper uniQueryMapper; private final UniQueryMapper uniQueryMapper;
private final EngineExecuteHandlerReader handlerReader;
private final DynamicDataSearchService dynamicDataSearchService; private final DynamicDataSearchService dynamicDataSearchService;
public LocalDataAdapterHandler(UniQueryMapper uniQueryMapper, public LocalDataAdapterHandler(UniQueryMapper uniQueryMapper,
DataAdapterMapper dataAdapterMapper,
EngineExecuteHandlerReader handlerReader, EngineExecuteHandlerReader handlerReader,
DynamicDataSearchService dynamicDataSearchService) { DynamicDataSearchService dynamicDataSearchService) {
super(dataAdapterMapper, handlerReader);
this.uniQueryMapper = uniQueryMapper; this.uniQueryMapper = uniQueryMapper;
this.handlerReader = handlerReader;
this.dynamicDataSearchService = dynamicDataSearchService; this.dynamicDataSearchService = dynamicDataSearchService;
} }
@Override @Override
public Object mockExecute(DataAdapter dataAdapter, PortalBo portal, Object param) { public Object mockExecute(PortalBo portal, MockParam mockParam) {
if (portal.getType() != PortalEnum.LOCAL) { if (portal.getType() != PortalEnum.LOCAL) {
return null; return null;
} }
handlerReader.preconditioning(dataAdapter); //自定义查询编辑查询对象
if (null == param) { List<UniConDto> uniConList = new ArrayList<>();
throw new CustomException("参数对象不能为空"); if (!ObjectUtils.isEmpty(mockParam.getParams())) {
for (MockParam.Param param : mockParam.getParams()) {
if (!ObjectUtils.isEmpty(param.getKey()) && !ObjectUtils.isEmpty(param.getValue())) {
UniConDto uniCon = new UniConDto();
uniCon.setQuery(param.getValue());
uniCon.setUcId(Long.parseLong(param.getKey()));
}
} }
if (!(param instanceof SearchInfo)) {
throw new CustomException("数据类型不匹配");
} }
SearchInfo info = (SearchInfo) param;
UniQuery query = uniQueryMapper.selectSampleById(portal.getQueryId()); UniQuery query = uniQueryMapper.selectSampleById(portal.getQueryId());
if (null == query) { if (null == query) {
throw new CustomException("数据源为空!"); throw new CustomException("数据源为空!");
} }
//根据设置的参数动态调整当前是否需要分页操作 //根据设置的参数动态调整当前是否需要分页操作
TableDataInfo<Map<String, Object>> tableDataInfo = dynamicDataSearchService.searchData(info.getList(), query, null, Boolean.TRUE); TableDataInfo<Map<String, Object>> tableDataInfo = dynamicDataSearchService.searchData(uniConList, query, null, Boolean.TRUE);
return handlerReader.execute(dataAdapter, tableDataInfo.getRows(), true); if (ObjectUtils.isEmpty(portal.getAdapterId()) || !portal.getCreateDataAdapter()) {
return tableDataInfo.getRows();
} else {
return super.mockExecute(portal.getAdapterId(), mockParam.getCode(), tableDataInfo.getRows());
}
} }
@Override @Override
public Object execute(DataAdapter dataAdapter, PortalBo portal, Object param) { public Object execute(PortalBo portal, HttpServletRequest request) {
if (portal.getType() != PortalEnum.LOCAL) { if (portal.getType() != PortalEnum.LOCAL) {
return null; return null;
} }
handlerReader.preconditioning(dataAdapter); //自定义查询编辑查询对象
List<Map<String, Object>> data = JSON.parseObject(dataAdapter.getMockData(), new TypeReference<List<Map<String, Object>>>() { List<UniConDto> uniConList = new ArrayList<>();
}); //根据映射关系从request中获取请求参数
return handlerReader.execute(dataAdapter, data, false); for (PortalMapping portalMapping : portal.getMappings()) {
UniConDto uniCon = new UniConDto();
String mappingValue = portalMapping.getMappingValue();
String mappingKey = portalMapping.getMappingKey();
uniCon.setUcId(Long.parseLong(mappingKey));
if (portalMapping.getMappingType() == 0) {
uniCon.setQuery(request.getParameter(mappingValue));
} else if (portalMapping.getMappingType() == 1) {
uniCon.setQuery(request.getHeaders(mappingValue));
} else {
uniCon.setQuery(request.getParameter(mappingValue));
}
uniConList.add(uniCon);
}
UniQuery query = uniQueryMapper.selectSampleById(portal.getQueryId());
if (null == query) {
throw new CustomException("数据源为空!");
}
//根据设置的参数动态调整当前是否需要分页操作
TableDataInfo<Map<String, Object>> tableDataInfo = dynamicDataSearchService.searchData(uniConList, query, null, Boolean.TRUE);
if (portal.getCreateDataAdapter()) {
return super.execute(portal.getAdapterId(), tableDataInfo.getRows());
} else {
return tableDataInfo.getRows();
} }
} }
}

View File

@@ -1,5 +1,6 @@
package cn.fateverse.query.handler.engine; package cn.fateverse.query.handler.engine;
import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.DataAdapter;
public interface EngineExecuteHandler { public interface EngineExecuteHandler {
@@ -10,10 +11,18 @@ public interface EngineExecuteHandler {
* *
* @param dataAdapter 数据适配器 * @param dataAdapter 数据适配器
* @param data 数据列表 * @param data 数据列表
* @param development
* @return JSONObject对象 * @return JSONObject对象
*/ */
Object execute(DataAdapter dataAdapter, Object data, boolean development); Object execute(DataAdapter dataAdapter, Object data);
/**
* 模拟执行方法
*
* @param dataAdapter 数据适配器
* @param data 数据列表
* @return JSONObject对象
*/
EngineResult mockExecute(DataAdapter dataAdapter, Object data);
/** /**
* 预处理数据适配器 * 预处理数据适配器
@@ -25,8 +34,10 @@ public interface EngineExecuteHandler {
/** /**
* 删除数据适配器 * 删除数据适配器
*
* @param dataAdapter 数据适配器 * @param dataAdapter 数据适配器
* @return 删除结果 * @return 删除结果
*/ */
Boolean remove(DataAdapter dataAdapter); Boolean remove(DataAdapter dataAdapter);
} }

View File

@@ -1,6 +1,7 @@
package cn.fateverse.query.handler.engine.impl; package cn.fateverse.query.handler.engine.impl;
import cn.fateverse.common.code.engine.JavaCodeEngine; import cn.fateverse.common.code.engine.JavaCodeEngine;
import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.DataAdapter;
import cn.fateverse.query.enums.DataAdapterType; import cn.fateverse.query.enums.DataAdapterType;
@@ -8,7 +9,6 @@ import cn.fateverse.query.handler.engine.EngineExecuteHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -31,12 +31,21 @@ public class JavaEngineExecuteHandler implements EngineExecuteHandler {
@Override @Override
public Object execute(DataAdapter dataAdapter, Object data, boolean development) { public Object execute(DataAdapter dataAdapter, Object data) {
if (!DataAdapterType.JAVA.equals(dataAdapter.getType())) { if (!DataAdapterType.JAVA.equals(dataAdapter.getType())) {
return null; return null;
} }
return javaCodeEngine.execute(dataAdapter.getExecuteCode(), getClassName(dataAdapter), return javaCodeEngine.execute(dataAdapter.getExecuteCode(), getClassName(dataAdapter),
"execute", new Class[]{List.class}, new Object[]{data}, development); "execute", new Object[]{data});
}
@Override
public EngineResult mockExecute(DataAdapter dataAdapter, Object data) {
if (!DataAdapterType.JAVA.equals(dataAdapter.getType())) {
return null;
}
return javaCodeEngine.mockExecute(dataAdapter.getExecuteCode(), getClassName(dataAdapter),
"execute", new Object[]{data});
} }
private static String getClassName(DataAdapter dataAdapter) { private static String getClassName(DataAdapter dataAdapter) {
@@ -52,7 +61,8 @@ public class JavaEngineExecuteHandler implements EngineExecuteHandler {
String modifiedCode = modifiedCode(dataAdapter.getCode()); String modifiedCode = modifiedCode(dataAdapter.getCode());
String replacedCode = replacedClass(modifiedCode, getClassName(dataAdapter)); String replacedCode = replacedClass(modifiedCode, getClassName(dataAdapter));
String IMPORT_CODE = "import java.util.*;\n" + String IMPORT_CODE = "import java.util.*;\n" +
"import java.util.stream.*;\n"; "import java.util.stream.*;\n" +
"import com.alibaba.fastjson2.*;";
dataAdapter.setExecuteCode(IMPORT_CODE + replacedCode); dataAdapter.setExecuteCode(IMPORT_CODE + replacedCode);
return Boolean.TRUE; return Boolean.TRUE;
} }

View File

@@ -1,12 +1,17 @@
package cn.fateverse.query.handler.engine.impl; package cn.fateverse.query.handler.engine.impl;
import cn.fateverse.common.code.engine.JavaScriptEngine; import cn.fateverse.common.code.engine.JavaScriptEngine;
import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.DataAdapter;
import cn.fateverse.query.enums.DataAdapterType; import cn.fateverse.query.enums.DataAdapterType;
import cn.fateverse.query.handler.engine.EngineExecuteHandler; import cn.fateverse.query.handler.engine.EngineExecuteHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* JavaScript 代码执行器 * JavaScript 代码执行器
* *
@@ -18,23 +23,49 @@ import org.springframework.stereotype.Component;
public class JavaScriptEngineExecuteHandler implements EngineExecuteHandler { public class JavaScriptEngineExecuteHandler implements EngineExecuteHandler {
@Override @Override
public Object execute(DataAdapter dataAdapter, Object data, boolean development) { public Object execute(DataAdapter dataAdapter, Object data) {
if (!DataAdapterType.JAVA_SCRIPT.equals(dataAdapter.getType())) { if (!DataAdapterType.JAVA_SCRIPT.equals(dataAdapter.getType())) {
return null; return null;
} }
return JavaScriptEngine.executeScript(dataAdapter.getExecuteCode(), "execute", data); return JavaScriptEngine.execute(dataAdapter.getExecuteCode(), "execute" + dataAdapter.getAdapterId(), data);
}
@Override
public EngineResult mockExecute(DataAdapter dataAdapter, Object data) {
if (!DataAdapterType.JAVA_SCRIPT.equals(dataAdapter.getType())) {
return null;
}
return JavaScriptEngine.mockExecute(dataAdapter.getExecuteCode(), "execute" + dataAdapter.getAdapterId(), data);
} }
@Override @Override
public Boolean preconditioning(DataAdapter dataAdapter) { public Boolean preconditioning(DataAdapter dataAdapter) {
if (DataAdapterType.JAVA_SCRIPT.equals(dataAdapter.getType())) { if (!DataAdapterType.JAVA_SCRIPT.equals(dataAdapter.getType())) {
return Boolean.TRUE;
}
return Boolean.FALSE; return Boolean.FALSE;
} }
String code = dataAdapter.getCode();
// 正则表达式匹配类定义
String regex = "function .*? ";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(code);
if (matcher.find()) {
// 执行替换操作
String replacedCode = code.replaceFirst(regex, "function execute" + dataAdapter.getAdapterId() + "(data) ");
dataAdapter.setExecuteCode(replacedCode);
} else {
throw new CustomException("请勿修改类定义");
}
return Boolean.TRUE;
}
@Override @Override
public Boolean remove(DataAdapter dataAdapter) { public Boolean remove(DataAdapter dataAdapter) {
if (!DataAdapterType.JAVA_SCRIPT.equals(dataAdapter.getType())) {
return Boolean.FALSE; return Boolean.FALSE;
} }
JavaScriptEngine.remove("execute" + dataAdapter.getAdapterId());
return Boolean.TRUE;
}
} }

View File

@@ -2,12 +2,13 @@ package cn.fateverse.query.handler.reader;
import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.dto.MockParam;
import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.entity.bo.PortalBo;
import cn.fateverse.query.handler.adapter.DataAdapterHandler; import cn.fateverse.query.handler.adapter.DataAdapterHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.List; import java.util.List;
/** /**
@@ -28,9 +29,9 @@ public class DataAdapterHandlerReader {
} }
public Object mockExecute(DataAdapter dataAdapter, PortalBo portal, Object params) { public Object mockExecute(PortalBo portal, MockParam mockParam) {
for (DataAdapterHandler dataAdapterHandler : handlerList) { for (DataAdapterHandler dataAdapterHandler : handlerList) {
Object result = dataAdapterHandler.mockExecute(dataAdapter, portal, params); Object result = dataAdapterHandler.mockExecute(portal, mockParam);
if (result != null) { if (result != null) {
return result; return result;
} }
@@ -39,9 +40,9 @@ public class DataAdapterHandlerReader {
} }
public Object execute(DataAdapter dataAdapter, PortalBo portal, Object params) { public Object execute(PortalBo portal, HttpServletRequest request) {
for (DataAdapterHandler dataAdapterHandler : handlerList) { for (DataAdapterHandler dataAdapterHandler : handlerList) {
Object result = dataAdapterHandler.execute(dataAdapter, portal, params); Object result = dataAdapterHandler.execute(portal, request);
if (result != null) { if (result != null) {
return result; return result;
} }
@@ -49,5 +50,15 @@ public class DataAdapterHandlerReader {
throw new CustomException("当前数据源类型不支持!"); throw new CustomException("当前数据源类型不支持!");
} }
public Boolean remove(Long adaptorId) {
for (DataAdapterHandler dataAdapterHandler : handlerList) {
boolean result = dataAdapterHandler.remove(adaptorId);
if (result) {
return true;
}
}
return false;
}
} }

View File

@@ -1,5 +1,6 @@
package cn.fateverse.query.handler.reader; package cn.fateverse.query.handler.reader;
import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.DataAdapter;
import cn.fateverse.query.handler.engine.EngineExecuteHandler; import cn.fateverse.query.handler.engine.EngineExecuteHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -47,11 +48,11 @@ public class EngineExecuteHandlerReader {
* @param data 数据列表 * @param data 数据列表
* @return 执行结果 * @return 执行结果
*/ */
public Object execute(DataAdapter dataAdapter, Object data, boolean development) { public Object execute(DataAdapter dataAdapter, Object data) {
// 遍历引擎执行处理器列表 // 遍历引擎执行处理器列表
for (EngineExecuteHandler engineExecuteHandler : handlerList) { for (EngineExecuteHandler engineExecuteHandler : handlerList) {
// 执行数据适配器的处理方法 // 执行数据适配器的处理方法
Object result = engineExecuteHandler.execute(dataAdapter, data, development); Object result = engineExecuteHandler.execute(dataAdapter, data);
if (result != null) { if (result != null) {
return result; return result;
} }
@@ -59,6 +60,22 @@ public class EngineExecuteHandlerReader {
// 若未找到匹配的数据适配器处理器则返回null // 若未找到匹配的数据适配器处理器则返回null
return null; return null;
} }
public EngineResult mockExecute(DataAdapter dataAdapter, Object data) {
// 遍历引擎执行处理器列表
for (EngineExecuteHandler engineExecuteHandler : handlerList) {
// 执行数据适配器的处理方法
EngineResult result = engineExecuteHandler.mockExecute(dataAdapter, data);
if (result != null) {
return result;
}
}
// 若未找到匹配的数据适配器处理器则返回null
return null;
}
public Boolean remove(DataAdapter dataAdapter) { public Boolean remove(DataAdapter dataAdapter) {

View File

@@ -48,6 +48,14 @@ public interface DataAdapterMapper {
*/ */
int update(DataAdapter dataAdapter); int update(DataAdapter dataAdapter);
/**
* 更新代码
*
* @param dataAdapter 数据适配器
* @return 结果
*/
int updateCode(DataAdapter dataAdapter);
/** /**
* 删除数据源适配器 * 删除数据源适配器
* *

View File

@@ -0,0 +1,44 @@
package cn.fateverse.query.mapper;
import cn.fateverse.query.entity.PortalInterface;
/**
* @author Clay
* @date 2024/4/26 11:23
*/
public interface PortalInterfaceMapper {
/**
* 根据id查询
*
* @param interfaceId 接口id
* @return 接口信息
*/
PortalInterface selectById(Long interfaceId);
/**
* 新增
*
* @param portalInterface 接口信息
* @return 影响行数
*/
Integer insert(PortalInterface portalInterface);
/**
* 修改
*
* @param portalInterface 接口信息
* @return 影响行数
*/
Integer update(PortalInterface portalInterface);
/**
* 删除
*
* @param interfaceId 接口id
* @return 影响行数
*/
Integer deleteById(Long interfaceId);
}

View File

@@ -48,4 +48,28 @@ public interface PortalMapper {
* @return 更新数量 * @return 更新数量
*/ */
int insert(Portal portal); int insert(Portal portal);
/**
* 修改接口
*
* @param portal 接口信息
* @return 更新数量
*/
int update(Portal portal);
/**
* 更新状态
*
* @param portalId 接口id
* @param state 状态
*/
void updateState(@Param("portalId") Long portalId, @Param("state") Integer state);
/**
* 删除
*
* @param portalId id
* @return 删除行数
*/
int deleteById(Long portalId);
} }

View File

@@ -3,14 +3,10 @@ package cn.fateverse.query.portal;
import cn.fateverse.common.core.result.Result; import cn.fateverse.common.core.result.Result;
import cn.fateverse.common.security.utils.ResponseRender; import cn.fateverse.common.security.utils.ResponseRender;
import cn.fateverse.query.constant.QueryConstant; import cn.fateverse.query.constant.QueryConstant;
import cn.fateverse.query.entity.DataAdapter;
import cn.fateverse.query.entity.Portal; import cn.fateverse.query.entity.Portal;
import cn.fateverse.query.entity.PortalMapping; import cn.fateverse.query.entity.PortalMapping;
import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.entity.bo.PortalBo;
import cn.fateverse.query.entity.dto.SearchInfo;
import cn.fateverse.query.entity.dto.UniConDto;
import cn.fateverse.query.handler.reader.DataAdapterHandlerReader; import cn.fateverse.query.handler.reader.DataAdapterHandlerReader;
import cn.fateverse.query.mapper.DataAdapterMapper;
import cn.fateverse.query.mapper.PortalMapper; import cn.fateverse.query.mapper.PortalMapper;
import cn.fateverse.query.mapper.PortalMappingMapper; import cn.fateverse.query.mapper.PortalMappingMapper;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
@@ -20,7 +16,6 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@@ -35,19 +30,14 @@ public class PortalDispatchServlet {
private final PortalMapper portalMapper; private final PortalMapper portalMapper;
private final DataAdapterMapper dataAdapterMapper;
private final PortalMappingMapper portalMappingMapper; private final PortalMappingMapper portalMappingMapper;
private final DataAdapterHandlerReader dataAdapterHandler; private final DataAdapterHandlerReader dataAdapterHandler;
public PortalDispatchServlet(PortalMapper portalMapper, public PortalDispatchServlet(PortalMapper portalMapper,
DataAdapterMapper dataAdapterMapper,
PortalMappingMapper portalMappingMapper, PortalMappingMapper portalMappingMapper,
DataAdapterHandlerReader dataAdapterHandler) { DataAdapterHandlerReader dataAdapterHandler) {
this.portalMapper = portalMapper; this.portalMapper = portalMapper;
this.dataAdapterMapper = dataAdapterMapper;
this.portalMappingMapper = portalMappingMapper; this.portalMappingMapper = portalMappingMapper;
this.dataAdapterHandler = dataAdapterHandler; this.dataAdapterHandler = dataAdapterHandler;
} }
@@ -70,34 +60,8 @@ public class PortalDispatchServlet {
portalBo = PortalBo.toPortalBo(portal, portalMappings); portalBo = PortalBo.toPortalBo(portal, portalMappings);
redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo); redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo);
} }
//自定义查询编辑查询对象
SearchInfo searchInfo = new SearchInfo();
List<UniConDto> uniConList = new ArrayList<>();
//根据映射关系从request中获取请求参数
for (PortalMapping portalMapping : portalBo.getMappings()) {
UniConDto uniCon = new UniConDto();
String mappingValue = portalMapping.getMappingValue();
String mappingKey = portalMapping.getMappingKey();
uniCon.setUcId(Long.parseLong(mappingValue));
if (portalMapping.getMappingType() == 0) {
uniCon.setQuery(request.getParameter(mappingKey));
} else if (portalMapping.getMappingType() == 1) {
uniCon.setQuery(request.getHeaders(mappingKey));
} else {
uniCon.setQuery(request.getParameter(mappingKey));
}
uniConList.add(uniCon);
}
searchInfo.setList(uniConList);
//获取当当前接口对应的数据适配器
DataAdapter dataAdapter = dataAdapterMapper.selectById(portalBo.getAdapterId());
//进行数据适配器的执行逻辑 //进行数据适配器的执行逻辑
Object result = null; Object result = dataAdapterHandler.execute(portalBo, request);
if (portalBo.getState() == 1 || portalBo.getState() == 2) {
result = dataAdapterHandler.execute(dataAdapter, portalBo, searchInfo);
} else {
result = dataAdapterHandler.mockExecute(dataAdapter, portalBo, searchInfo);
}
//将返回结果放入response //将返回结果放入response
ResponseRender.renderString(response, Result.ok(result)); ResponseRender.renderString(response, Result.ok(result));
} }

View File

@@ -14,6 +14,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.cluster.specifyaddress.Address; import org.apache.dubbo.rpc.cluster.specifyaddress.Address;
import org.apache.dubbo.rpc.cluster.specifyaddress.UserSpecifiedAddressUtil; import org.apache.dubbo.rpc.cluster.specifyaddress.UserSpecifiedAddressUtil;
import org.bouncycastle.jcajce.provider.asymmetric.RSA;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -122,24 +123,35 @@ public class DispatchSyncService {
} }
//如果为false,则将当前任务重新放入到队列中,等待下一次执行 //如果为false,则将当前任务重新放入到队列中,等待下一次执行
if (state == null || !state) { if (state == null || !state) {
queue.add(this); retry();
} }
} catch (NacosException e) { } catch (NacosException e) {
log.error("NacosException: {}", e.getMessage()); log.error("NacosException: {}", e.getMessage());
//如果发生异常也是 //如果发生异常也是
//todo 后续可以添加当前任务报错次数的处理 retry();
queue.add(this);
} finally { } finally {
//将指定的地址恢复 //将指定的地址恢复
UserSpecifiedAddressUtil.setAddress(null); UserSpecifiedAddressUtil.setAddress(null);
} }
} }
/**
* 进行重试
*/
private void retry() {
retryCount++;
if (retryCount > 5) {
log.error("任务超过最大重试次数!");
}
queue.add(this);
}
}; };
//像阻塞队列中添加任务 //像阻塞队列中添加任务
queue.add(task); queue.add(task);
} }
} }
} catch (NacosException e) { } catch (NacosException e) {
log.error("NacosException: {}", e.getMessage());
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@@ -168,6 +180,8 @@ public class DispatchSyncService {
protected final String requestMethod; protected final String requestMethod;
protected Integer retryCount = 0;
private Task(String ip, int port, String path, Boolean publish, String requestMethod) { private Task(String ip, int port, String path, Boolean publish, String requestMethod) {
this.ip = ip; this.ip = ip;
this.port = port; this.port = port;

View File

@@ -2,28 +2,12 @@ package cn.fateverse.query.portal.service;
import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.core.utils.SpringContextHolder; import cn.fateverse.common.core.utils.SpringContextHolder;
import cn.fateverse.query.dubbo.DubboDispatchServletPublish;
import cn.fateverse.query.portal.PortalDispatchServlet; import cn.fateverse.query.portal.PortalDispatchServlet;
import cn.fateverse.query.portal.config.DubboServiceBean;
import cn.fateverse.query.portal.event.DispatchSyncEvent; import cn.fateverse.query.portal.event.DispatchSyncEvent;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcContextAttachment;
import org.apache.dubbo.rpc.cluster.specifyaddress.Address;
import org.apache.dubbo.rpc.cluster.specifyaddress.UserSpecifiedAddressUtil;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@@ -32,12 +16,7 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/** /**
* @author Clay * @author Clay
@@ -49,7 +28,7 @@ public class HandlerMethodService implements ApplicationContextAware {
public static final String CUSTOM_INTERFACE = "customInterface:"; public static final String CUSTOM_INTERFACE = "customInterface:";
private RequestMappingHandlerMapping mapping; private RequestMappingHandlerMapping handlerMapping;
private Method mappingMethod; private Method mappingMethod;
@@ -73,7 +52,7 @@ public class HandlerMethodService implements ApplicationContextAware {
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//获取到web mvc的接口存储mapping //获取到web mvc的接口存储mapping
mapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping"); handlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
} }
/** /**
@@ -92,12 +71,12 @@ public class HandlerMethodService implements ApplicationContextAware {
.consumes(empty) .consumes(empty)
.produces(empty) .produces(empty)
.mappingName(CUSTOM_INTERFACE + path); .mappingName(CUSTOM_INTERFACE + path);
RequestMappingInfo requestMappingInfo = builder.options(mapping.getBuilderConfiguration()).build(); RequestMappingInfo requestMappingInfo = builder.options(handlerMapping.getBuilderConfiguration()).build();
Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods(); Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
if (handlerMethods.containsKey(requestMappingInfo)) { if (handlerMethods.containsKey(requestMappingInfo)) {
throw new CustomException("path is exist"); throw new CustomException("path is exist");
} }
mapping.registerMapping(requestMappingInfo, "portalDispatchServlet", mappingMethod); handlerMapping.registerMapping(requestMappingInfo, "portalDispatchServlet", mappingMethod);
// 判断是否需要发起同步 // 判断是否需要发起同步
if (sync) { if (sync) {
//使用事件监听机制,避免循环注入 //使用事件监听机制,避免循环注入
@@ -112,12 +91,12 @@ public class HandlerMethodService implements ApplicationContextAware {
* @param requestMethod 请求类型 * @param requestMethod 请求类型
*/ */
public void unregisterMapping(String path, RequestMethod requestMethod, Boolean sync) { public void unregisterMapping(String path, RequestMethod requestMethod, Boolean sync) {
Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods(); Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
for (RequestMappingInfo mappingInfo : handlerMethods.keySet()) { for (RequestMappingInfo mappingInfo : handlerMethods.keySet()) {
if (!ObjectUtils.isEmpty(mappingInfo.getName()) if (!ObjectUtils.isEmpty(mappingInfo.getName())
&& (CUSTOM_INTERFACE + path).equals(mappingInfo.getName()) && (CUSTOM_INTERFACE + path).equals(mappingInfo.getName())
&& mappingInfo.getMethodsCondition().getMethods().contains(requestMethod)) { && mappingInfo.getMethodsCondition().getMethods().contains(requestMethod)) {
mapping.unregisterMapping(mappingInfo); handlerMapping.unregisterMapping(mappingInfo);
if (sync) { if (sync) {
SpringContextHolder.publishEvent(new DispatchSyncEvent(path, requestMethod.name(), Boolean.FALSE)); SpringContextHolder.publishEvent(new DispatchSyncEvent(path, requestMethod.name(), Boolean.FALSE));
} }

View File

@@ -71,7 +71,9 @@ public class DynamicDataSearchService {
} else { } else {
uniConList = uniConMapper.selectByQueryIdAndUcIdList(query.getId(), ids); uniConList = uniConMapper.selectByQueryIdAndUcIdList(query.getId(), ids);
} }
uniConList = uniConList.stream().filter(uniCon -> null != queryMap.get(uniCon.getUcId())).peek(uniCon -> uniCon.setUcMock(queryMap.get(uniCon.getUcId()))).collect(Collectors.toList()); uniConList = uniConList.stream().filter(uniCon -> null != queryMap.get(uniCon.getUcId()))
.peek(uniCon -> uniCon.setUcMock(queryMap.get(uniCon.getUcId())))
.collect(Collectors.toList());
if (query.getType() == 1) { if (query.getType() == 1) {
SqlSelect select = new SqlSelect(); SqlSelect select = new SqlSelect();
select.setSelectSql(query.getUqSql()); select.setSelectSql(query.getUqSql());

View File

@@ -1,7 +1,10 @@
package cn.fateverse.query.service; package cn.fateverse.query.service;
import cn.fateverse.common.core.result.page.TableDataInfo; import cn.fateverse.common.core.result.page.TableDataInfo;
import cn.fateverse.query.entity.dto.DataAdapterCodeDto;
import cn.fateverse.query.entity.dto.MockParam;
import cn.fateverse.query.entity.dto.PortalDto; import cn.fateverse.query.entity.dto.PortalDto;
import cn.fateverse.query.entity.dto.PortalPublish;
import cn.fateverse.query.entity.query.PortalQuery; import cn.fateverse.query.entity.query.PortalQuery;
import cn.fateverse.query.entity.vo.PortalIdWrapper; import cn.fateverse.query.entity.vo.PortalIdWrapper;
import cn.fateverse.query.entity.vo.PortalVo; import cn.fateverse.query.entity.vo.PortalVo;
@@ -21,6 +24,14 @@ public interface PortalService {
*/ */
PortalVo searchById(Long portalId); PortalVo searchById(Long portalId);
/**
* 根据id查询详情
*
* @param portalId id
* @return 结果
*/
PortalVo searchDetailById(Long portalId);
/** /**
* 查询接口列表 * 查询接口列表
* *
@@ -29,6 +40,30 @@ public interface PortalService {
*/ */
TableDataInfo<SimplePortalVo> searchList(PortalQuery query); TableDataInfo<SimplePortalVo> searchList(PortalQuery query);
/**
* 模拟执行
*
* @param mockParam 执行数据
* @return 执行结果
*/
Object mockExecute(MockParam mockParam);
/**
* 获取到模拟数据
*
* @param mockParam 模拟参数
* @return 执行结果
*/
Object mockData(MockParam mockParam);
/**
* 保存代码
*
* @param dataAdapterCodeDto 数据适配器代码
*/
void saveCode(DataAdapterCodeDto dataAdapterCodeDto);
/** /**
* 保存接口信息 * 保存接口信息
* *
@@ -44,4 +79,27 @@ public interface PortalService {
* @return 修改完成后接口id和数据适配器id * @return 修改完成后接口id和数据适配器id
*/ */
PortalIdWrapper edit(PortalDto portalDto); PortalIdWrapper edit(PortalDto portalDto);
/**
* 发布接口
*
* @param portalPublish 发布接口对象
* @param isList 是否为list发布
*/
void publish(PortalPublish portalPublish, Boolean isList);
/**
* 取消发布
*
* @param portalId 接口id
*/
void cancel(Long portalId);
/**
* 删除接口信息
*
* @param portalId 接口id
*/
void delete(Long portalId);
} }

View File

@@ -50,6 +50,7 @@ public class DataAdapterServiceImpl implements DataAdapterService {
@Override @Override
public TableDataInfo<DataAdapterVo> searchList(DataAdapterQuery query) { public TableDataInfo<DataAdapterVo> searchList(DataAdapterQuery query) {
PageUtils.startPage(); PageUtils.startPage();
query.setCommon(true);
List<DataAdapter> list = dataAdapterMapper.selectList(query); List<DataAdapter> list = dataAdapterMapper.selectList(query);
return PageUtils.convertDataTable(list, DataAdapterVo::toDataAdapterVo); return PageUtils.convertDataTable(list, DataAdapterVo::toDataAdapterVo);
} }
@@ -87,6 +88,8 @@ public class DataAdapterServiceImpl implements DataAdapterService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int save(DataAdapterDto dataAdapter) { public int save(DataAdapterDto dataAdapter) {
DataAdapter info = dataAdapter.toDataAdapter(); DataAdapter info = dataAdapter.toDataAdapter();
info.setCommon(Boolean.TRUE);
info.init();
info.setCreateBy(SecurityUtils.getUsername()); info.setCreateBy(SecurityUtils.getUsername());
return dataAdapterMapper.insert(info); return dataAdapterMapper.insert(info);
} }

View File

@@ -3,24 +3,25 @@ package cn.fateverse.query.service.impl;
import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.core.result.page.TableDataInfo; import cn.fateverse.common.core.result.page.TableDataInfo;
import cn.fateverse.common.mybatis.utils.PageUtils; import cn.fateverse.common.mybatis.utils.PageUtils;
import cn.fateverse.query.entity.*;
import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.entity.bo.PortalBo;
import cn.fateverse.query.entity.dto.DataAdapterCodeDto;
import cn.fateverse.query.entity.dto.MockParam;
import cn.fateverse.query.entity.dto.PortalPublish;
import cn.fateverse.query.entity.vo.DataAdapterVo;
import cn.fateverse.query.entity.vo.PortalIdWrapper; import cn.fateverse.query.entity.vo.PortalIdWrapper;
import cn.fateverse.query.enums.DataAdapterType;
import cn.fateverse.query.enums.PortalPremEnum;
import cn.fateverse.query.handler.reader.DataAdapterHandlerReader;
import cn.fateverse.query.handler.reader.EngineExecuteHandlerReader;
import cn.fateverse.query.mapper.*;
import cn.fateverse.query.portal.service.HandlerMethodService; import cn.fateverse.query.portal.service.HandlerMethodService;
import cn.fateverse.query.constant.QueryConstant; import cn.fateverse.query.constant.QueryConstant;
import cn.fateverse.query.entity.DataAdapter;
import cn.fateverse.query.entity.Portal;
import cn.fateverse.query.entity.PortalMapping;
import cn.fateverse.query.entity.UniQuery;
import cn.fateverse.query.entity.dto.DataAdapterDto;
import cn.fateverse.query.entity.dto.PortalDto; import cn.fateverse.query.entity.dto.PortalDto;
import cn.fateverse.query.entity.query.PortalQuery; import cn.fateverse.query.entity.query.PortalQuery;
import cn.fateverse.query.entity.vo.PortalVo; import cn.fateverse.query.entity.vo.PortalVo;
import cn.fateverse.query.entity.vo.SimplePortalVo; import cn.fateverse.query.entity.vo.SimplePortalVo;
import cn.fateverse.query.enums.PortalEnum; import cn.fateverse.query.enums.PortalEnum;
import cn.fateverse.query.mapper.DataAdapterMapper;
import cn.fateverse.query.mapper.PortalMapper;
import cn.fateverse.query.mapper.PortalMappingMapper;
import cn.fateverse.query.mapper.UniQueryMapper;
import cn.fateverse.query.service.PortalService; import cn.fateverse.query.service.PortalService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
@@ -32,7 +33,6 @@ import org.springframework.web.bind.annotation.RequestMethod;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -57,19 +57,41 @@ public class PortalServiceImpl implements PortalService {
private final PortalMappingMapper portalMappingMapper; private final PortalMappingMapper portalMappingMapper;
private final PortalInterfaceMapper portalInterfaceMapper;
private final DataAdapterHandlerReader handlerReader;
private final EngineExecuteHandlerReader executeHandlerReader;
public PortalServiceImpl(PortalMapper portalMapper, public PortalServiceImpl(PortalMapper portalMapper,
UniQueryMapper queryMapper, UniQueryMapper queryMapper,
DataAdapterMapper adapterMapper, DataAdapterMapper adapterMapper,
PortalMappingMapper portalMappingMapper, PortalMappingMapper portalMappingMapper,
ThreadPoolTaskExecutor taskExecuteExecutor, ThreadPoolTaskExecutor taskExecuteExecutor,
HandlerMethodService methodService) { HandlerMethodService methodService,
PortalInterfaceMapper portalInterfaceMapper,
DataAdapterHandlerReader handlerReader,
EngineExecuteHandlerReader executeHandlerReader) {
this.portalMapper = portalMapper; this.portalMapper = portalMapper;
this.queryMapper = queryMapper; this.queryMapper = queryMapper;
this.adapterMapper = adapterMapper; this.adapterMapper = adapterMapper;
this.portalMappingMapper = portalMappingMapper; this.portalMappingMapper = portalMappingMapper;
this.methodService = methodService; this.methodService = methodService;
this.portalInterfaceMapper = portalInterfaceMapper;
this.handlerReader = handlerReader;
this.executeHandlerReader = executeHandlerReader;
taskExecuteExecutor.execute(() -> { taskExecuteExecutor.execute(() -> {
List<Portal> portalList = portalMapper.selectList(new PortalQuery()); PortalQuery query = new PortalQuery();
// query.setState(QueryConstant.PORTAL_PUBLISH);
List<Portal> portalList = portalMapper.selectList(query);
if (ObjectUtils.isEmpty(portalList)) {
log.info("portal is empty!");
return;
}
portalList = portalList.stream()
.filter(portal -> PortalPremEnum.ANONYMITY.equals(portal.getPermissionType()) || PortalPremEnum.EXTERNAL.equals(portal.getPermissionType()))
.collect(Collectors.toList());
if (ObjectUtils.isEmpty(portalList)) { if (ObjectUtils.isEmpty(portalList)) {
log.info("portal is empty!"); log.info("portal is empty!");
return; return;
@@ -103,6 +125,9 @@ public class PortalServiceImpl implements PortalService {
@Override @Override
public PortalVo searchById(Long portalId) { public PortalVo searchById(Long portalId) {
Portal portal = portalMapper.selectById(portalId); Portal portal = portalMapper.selectById(portalId);
if (!QueryConstant.PORTAL_DEV.equals(portal.getState())) {
throw new CustomException("当前状态不允许查询");
}
PortalVo portalVo = PortalVo.toPortalVo(portal); PortalVo portalVo = PortalVo.toPortalVo(portal);
List<PortalMapping> portalMappings = portalMappingMapper.selectByPortalId(portalId); List<PortalMapping> portalMappings = portalMappingMapper.selectByPortalId(portalId);
if (ObjectUtils.isEmpty(portalMappings)) { if (ObjectUtils.isEmpty(portalMappings)) {
@@ -110,12 +135,39 @@ public class PortalServiceImpl implements PortalService {
} }
if (PortalEnum.LOCAL.equals(portal.getType())) { if (PortalEnum.LOCAL.equals(portal.getType())) {
UniQuery uniQuery = queryMapper.selectById(portal.getQueryId()); UniQuery uniQuery = queryMapper.selectById(portal.getQueryId());
if (!ObjectUtils.isEmpty(uniQuery)) {
portalVo.setQueryType(uniQuery.getType()); portalVo.setQueryType(uniQuery.getType());
} }
} else {
PortalInterface portalInterface = portalInterfaceMapper.selectById(portal.getInterfaceId());
if (!ObjectUtils.isEmpty(portalInterface)) {
portalVo.setUrl(portalInterface.getUrl());
}
}
if (portal.getCreateDataAdapter()) {
DataAdapter dataAdapter = adapterMapper.selectById(portal.getAdapterId());
if (!ObjectUtils.isEmpty(dataAdapter)) {
portalVo.setAdapterCodeType(dataAdapter.getType());
}
}
portalVo.setMappings(portalMappings); portalVo.setMappings(portalMappings);
return portalVo; return portalVo;
} }
@Override
public PortalVo searchDetailById(Long portalId) {
PortalVo portalVo = searchById(portalId);
if (!ObjectUtils.isEmpty(portalVo.getAdapterId()) && portalVo.getCreateDataAdapter()) {
DataAdapter dataAdapter = adapterMapper.selectById(portalVo.getAdapterId());
if (ObjectUtils.isEmpty(dataAdapter)) {
return portalVo;
}
DataAdapterVo dataAdapterVo = DataAdapterVo.toDataAdapterVo(dataAdapter);
portalVo.setDataAdapter(dataAdapterVo);
}
return portalVo;
}
@Override @Override
public TableDataInfo<SimplePortalVo> searchList(PortalQuery query) { public TableDataInfo<SimplePortalVo> searchList(PortalQuery query) {
PageUtils.startPage(); PageUtils.startPage();
@@ -142,11 +194,11 @@ public class PortalServiceImpl implements PortalService {
} }
} }
//映射数据适配器名称 //映射数据适配器名称
Map<Long, String> adapterMap = new HashMap<>(); Map<Long, DataAdapterType> adapterMap = new HashMap<>();
if (!ObjectUtils.isEmpty(adapterIds)) { if (!ObjectUtils.isEmpty(adapterIds)) {
List<DataAdapter> adapterList = adapterMapper.selectListByIds(adapterIds); List<DataAdapter> adapterList = adapterMapper.selectListByIds(adapterIds);
for (DataAdapter adapter : adapterList) { for (DataAdapter adapter : adapterList) {
adapterMap.put(adapter.getAdapterId(), adapter.getAdapterName()); adapterMap.put(adapter.getAdapterId(), adapter.getType());
} }
} }
//映射重组 //映射重组
@@ -157,26 +209,66 @@ public class PortalServiceImpl implements PortalService {
simplePortalVo.setQueryName(uniQuery.getUqName()); simplePortalVo.setQueryName(uniQuery.getUqName());
simplePortalVo.setQueryType(uniQuery.getType()); simplePortalVo.setQueryType(uniQuery.getType());
} }
simplePortalVo.setAdapterName(adapterMap.getOrDefault(portal.getAdapterId(), null)); simplePortalVo.setAdapterCodeType(adapterMap.getOrDefault(portal.getAdapterId(), null));
return simplePortalVo; return simplePortalVo;
}); });
} }
@Override
public Object mockExecute(MockParam mockParam) {
Portal portal = portalMapper.selectById(mockParam.getPortalId());
if (!QueryConstant.PORTAL_DEV.equals(portal.getState())) {
throw new CustomException("当前状态不允许执行");
}
PortalBo portalBo = PortalBo.toPortalBo(portal, null);
return handlerReader.mockExecute(portalBo, mockParam);
}
@Override
public Object mockData(MockParam mockParam) {
Portal portal = portalMapper.selectById(mockParam.getPortalId());
PortalBo portalBo = PortalBo.toPortalBo(portal, null);
portalBo.setCreateDataAdapter(false);
return handlerReader.mockExecute(portalBo, mockParam);
}
@Override
public void saveCode(DataAdapterCodeDto dataAdapterCodeDto) {
Portal portal = portalMapper.selectById(dataAdapterCodeDto.getPortalId());
if (ObjectUtils.isEmpty(portal)) {
throw new CustomException("未找到对应的接口");
}
if (!QueryConstant.PORTAL_DEV.equals(portal.getState())) {
throw new CustomException("当前状态不允许保存");
}
if (ObjectUtils.isEmpty(portal.getAdapterId()) || !portal.getCreateDataAdapter()) {
throw new CustomException("未找到数据适配器");
}
DataAdapter dataAdapter = new DataAdapter();
dataAdapter.setAdapterId(portal.getAdapterId());
dataAdapter.setCode(dataAdapterCodeDto.getCode());
adapterMapper.update(dataAdapter);
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public PortalIdWrapper save(PortalDto portalDto) { public PortalIdWrapper save(PortalDto portalDto) {
checkPortalType(portalDto);
Portal portal = portalDto.toPortal(); Portal portal = portalDto.toPortal();
checkPortalType(portal);
Portal old = portalMapper.selectByPath(portal.getPath(), portal.getRequestMethod()); Portal old = portalMapper.selectByPath(portal.getPath(), portal.getRequestMethod());
if (!ObjectUtils.isEmpty(old)) { if (!ObjectUtils.isEmpty(old)) {
throw new CustomException("系统中存在当前请求路径"); throw new CustomException("系统中存在当前请求路径");
} }
if (PortalEnum.EXTERNAL.equals(portal.getType())) {
createPortalInterface(portalDto, portal);
} else {
UniQuery uniQuery = queryMapper.selectById(portal.getQueryId()); UniQuery uniQuery = queryMapper.selectById(portal.getQueryId());
if (ObjectUtils.isEmpty(uniQuery)) { if (ObjectUtils.isEmpty(uniQuery)) {
throw new CustomException("未找到对应的查询接口!"); throw new CustomException("未找到对应的查询接口!");
} }
portal.setState(0); }
portal.setState(QueryConstant.PORTAL_DEV);
// 判断是否需要创建数据适配器 // 判断是否需要创建数据适配器
if (portal.getCreateDataAdapter()) { if (portal.getCreateDataAdapter()) {
createDataAdapter(portalDto, portal); createDataAdapter(portalDto, portal);
@@ -203,14 +295,17 @@ public class PortalServiceImpl implements PortalService {
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public PortalIdWrapper edit(PortalDto portalDto) { public PortalIdWrapper edit(PortalDto portalDto) {
checkPortalType(portalDto);
Portal portal = portalDto.toPortal(); Portal portal = portalDto.toPortal();
checkPortalType(portal);
Portal old = portalMapper.selectByPath(portal.getPath(), portal.getRequestMethod()); Portal old = portalMapper.selectByPath(portal.getPath(), portal.getRequestMethod());
if (!QueryConstant.PORTAL_DEV.equals(old.getState())) {
throw new CustomException("当前状态不允许修改");
}
if (!ObjectUtils.isEmpty(old) && !old.getPortalId().equals(portal.getPortalId())) { if (!ObjectUtils.isEmpty(old) && !old.getPortalId().equals(portal.getPortalId())) {
throw new CustomException("系统中存在当前请求路径"); throw new CustomException("系统中存在当前请求路径");
} }
if (portal.getCreateDataAdapter() != old.getCreateDataAdapter()) { if (portal.getCreateDataAdapter() != old.getCreateDataAdapter()) {
if (portal.getCreateDataAdapter()) { if (portal.getCreateDataAdapter()) {
createDataAdapter(portalDto, portal); createDataAdapter(portalDto, portal);
@@ -218,50 +313,91 @@ public class PortalServiceImpl implements PortalService {
adapterMapper.deleteById(old.getAdapterId()); adapterMapper.deleteById(old.getAdapterId());
} }
} }
PortalBo portalBo = PortalBo.toPortalBo(portal, portalDto.getMappings()); if (!old.getType().equals(portal.getType())) {
//存在接口路径或者请求类型不同则需要重新发布 if (PortalEnum.LOCAL.equals(portal.getType())) {
if (!old.getPath().equals(portal.getPath()) portalInterfaceMapper.deleteById(old.getInterfaceId());
|| !old.getRequestMethod().equals(portal.getRequestMethod())) { } else {
//先卸载接口,需要进行数据同步 createPortalInterface(portalDto, portal);
unpublishPortal(old, true); }
//注册新的映射 } else {
methodService.registerMapping(portalBo.getPath(), RequestMethod.valueOf(portalBo.getRequestMethod()), true); if (PortalEnum.EXTERNAL.equals(portal.getType())) {
PortalInterface portalInterface = new PortalInterface();
portalInterface.setInterfaceId(old.getInterfaceId());
portalInterface.setContentType(portalDto.getContentType());
portalInterface.setRequestMethod(portalDto.getInterfaceRequestMethod());
portalInterface.setUrl(portalDto.getUrl());
portalInterfaceMapper.update(portalInterface);
}
}
portalMappingMapper.deleteByPortalId(portal.getPortalId());
portalMapper.update(portal);
if (!ObjectUtils.isEmpty(portalDto.getMappings())) {
List<PortalMapping> mappings = portalDto.getMappings();
for (PortalMapping mapping : mappings) {
mapping.setPortalId(portal.getPortalId());
}
portalMappingMapper.insertBatch(mappings);
} }
//修改Redis中的数据信息
redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo);
//返回接口id和适配器id //返回接口id和适配器id
return PortalIdWrapper.builder() return PortalIdWrapper.builder()
.portalId(String.valueOf(portal.getPortalId())) .portalId(String.valueOf(portal.getPortalId()))
.adapterId(String.valueOf(portal.getAdapterId())) .adapterId(String.valueOf(portal.getAdapterId()))
.build(); .build();
} }
private boolean mappingCheckEquals(List<PortalMapping> oldPortalMappingList, List<PortalMapping> mappings) { @Override
if (ObjectUtils.isEmpty(oldPortalMappingList) && ObjectUtils.isEmpty(mappings)) { public void cancel(Long portalId) {
return true; Portal portal = portalMapper.selectById(portalId);
if (ObjectUtils.isEmpty(portal)) {
return;
} }
if (ObjectUtils.isEmpty(oldPortalMappingList) || ObjectUtils.isEmpty(mappings)) { if (QueryConstant.PORTAL_DEV.equals(portal.getState())) {
return false; throw new CustomException("当前状态错误");
} }
Optional<PortalMapping> optional = mappings.stream().filter(ObjectUtils::isEmpty).findAny(); portalMapper.updateState(portalId, QueryConstant.PORTAL_DEV);
if (optional.isEmpty()) { if (QueryConstant.PORTAL_PUBLISH.equals(portal.getState())) {
return false; unpublishPortal(portal, Boolean.TRUE);
}
if (oldPortalMappingList.size() == mappings.size()) {
Map<Long, PortalMapping> oldPortalMappingMap = oldPortalMappingList.stream()
.collect(Collectors.toMap(PortalMapping::getMappingId, Function.identity()));
Map<Long, PortalMapping> portalMappingMap = mappings.stream()
.collect(Collectors.toMap(PortalMapping::getMappingId, Function.identity()));
for (Long mappingId : oldPortalMappingMap.keySet()) {
PortalMapping portalMapping = portalMappingMap.get(mappingId);
if (ObjectUtils.isEmpty(portalMapping)) {
return false;
} }
} }
@Override
public void publish(PortalPublish portalPublish, Boolean isList) {
Long portalId = portalPublish.getLongPortalId();
Portal portal = portalMapper.selectById(portalId);
List<PortalMapping> mappings = portalMappingMapper.selectByPortalId(portalId);
if (!isList && portal.getCreateDataAdapter() && !ObjectUtils.isEmpty(portal.getAdapterId())) {
DataAdapter dataAdapter = adapterMapper.selectById(portal.getAdapterId());
dataAdapter.setCode(portalPublish.getCode());
adapterMapper.updateCode(dataAdapter);
} }
return false; portalMapper.updateState(portalId, QueryConstant.PORTAL_PUBLISH);
if (PortalPremEnum.EXTERNAL.equals(portal.getPermissionType()) || PortalPremEnum.ANONYMITY.equals(portal.getPermissionType())) {
publishPortal(portal, mappings, Boolean.TRUE);
} }
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long portalId) {
Portal portal = portalMapper.selectById(portalId);
if (ObjectUtils.isEmpty(portal)) {
return;
}
if (!QueryConstant.PORTAL_DEV.equals(portal.getState())) {
throw new CustomException("当前状态不允许删除");
}
if (portal.getCreateDataAdapter()) {
adapterMapper.deleteById(portal.getAdapterId());
}
portalMappingMapper.deleteByPortalId(portalId);
if (PortalEnum.EXTERNAL.equals(portal.getType())) {
portalInterfaceMapper.deleteById(portal.getInterfaceId());
}
portalMapper.deleteById(portalId);
}
/** /**
* 发布接口 * 发布接口
@@ -294,15 +430,15 @@ public class PortalServiceImpl implements PortalService {
* @param portal 接口信息 * @param portal 接口信息
*/ */
private void createDataAdapter(PortalDto portalDto, Portal portal) { private void createDataAdapter(PortalDto portalDto, Portal portal) {
DataAdapterDto dataAdapterDto = portalDto.getDataAdapter(); if (ObjectUtils.isEmpty(portalDto.getAdapterCodeType())) {
if (ObjectUtils.isEmpty(dataAdapterDto)) {
throw new CustomException("数据适配器为空!"); throw new CustomException("数据适配器为空!");
} }
DataAdapter dataAdapter = dataAdapterDto.toDataAdapter(); DataAdapter dataAdapter = new DataAdapter();
dataAdapter.setType(portalDto.getAdapterCodeType());
if (ObjectUtils.isEmpty(dataAdapter.getType())) { if (ObjectUtils.isEmpty(dataAdapter.getType())) {
throw new CustomException("请选择数据适配器类型!"); throw new CustomException("请选择数据适配器类型!");
} }
dataAdapter.setAdapterName(portal.getPortalName() + "专用数据适配器!"); dataAdapter.setAdapterName(portal.getPortalName() + "专用");
dataAdapter.setCommon(Boolean.FALSE); dataAdapter.setCommon(Boolean.FALSE);
//初始化对应的数据适配器 //初始化对应的数据适配器
dataAdapter.init(); dataAdapter.init();
@@ -312,19 +448,40 @@ public class PortalServiceImpl implements PortalService {
portal.setAdapterId(dataAdapter.getAdapterId()); portal.setAdapterId(dataAdapter.getAdapterId());
} }
/**
* 创建第三方接口
*
* @param portalDto 接口dto对象
* @param portal 接口对象
*/
private void createPortalInterface(PortalDto portalDto, Portal portal) {
PortalInterface portalInterface = new PortalInterface();
portalInterface.setContentType(portalDto.getContentType());
portalInterface.setRequestMethod(portalDto.getInterfaceRequestMethod());
portalInterface.setUrl(portalDto.getUrl());
portalInterfaceMapper.insert(portalInterface);
portal.setInterfaceId(portalInterface.getInterfaceId());
}
/** /**
* 检查接口类型是否正确 * 检查接口类型是否正确
* *
* @param portal 接口信息 * @param portalDto 接口信息
*/ */
private void checkPortalType(Portal portal) { private void checkPortalType(PortalDto portalDto) {
if (PortalEnum.LOCAL.equals(portal.getType())) { if (PortalEnum.LOCAL.equals(portalDto.getType())) {
if (ObjectUtils.isEmpty(portal.getQueryId())) { if (ObjectUtils.isEmpty(portalDto.getQueryId())) {
throw new CustomException("请选择查询接口!"); throw new CustomException("请选择查询接口!");
} }
portal.setUrl(null); } else if (PortalEnum.EXTERNAL.equals(portalDto.getType())) {
} else if (PortalEnum.EXTERNAL.equals(portal.getType())) { if (ObjectUtils.isEmpty(portalDto.getContentType())) {
if (ObjectUtils.isEmpty(portal.getUrl())) { throw new CustomException("外部接口请求类型不能为空");
}
if (ObjectUtils.isEmpty(portalDto.getInterfaceRequestMethod())) {
throw new CustomException("外部接口请求方法不能为空");
}
if (ObjectUtils.isEmpty(portalDto.getUrl())) {
throw new CustomException("第三方接口地址不能为空!"); throw new CustomException("第三方接口地址不能为空!");
} }
} else { } else {

View File

@@ -20,9 +20,10 @@
<sql id="selectDataAdapterVo"> <sql id="selectDataAdapterVo">
select adapter_id, select adapter_id,
adapter_name, adapter_name,
`type`, type,
common, common,
code, code,
execute_code,
mock_data, mock_data,
create_by, create_by,
create_time, create_time,
@@ -35,8 +36,10 @@
<select id="selectList" resultMap="DataAdapterResult"> <select id="selectList" resultMap="DataAdapterResult">
<include refid="selectDataAdapterVo"/> <include refid="selectDataAdapterVo"/>
<where> <where>
<if test="adapterName != null and adapterName != ''">and adapter_name like concat('%', #{adapterName}, '%')</if> <if test="adapterName != null and adapterName != ''">and adapter_name like concat('%', #{adapterName},
<if test="type != null">and `type` = #{type}</if> '%')
</if>
<if test="type != null">and type = #{type}</if>
<if test="common != null">and common = #{common}</if> <if test="common != null">and common = #{common}</if>
</where> </where>
</select> </select>
@@ -58,7 +61,7 @@
insert into data_adapter insert into data_adapter
<trim prefix="(" suffix=")" suffixOverrides=","> <trim prefix="(" suffix=")" suffixOverrides=",">
<if test="adapterName != null">adapter_name,</if> <if test="adapterName != null">adapter_name,</if>
<if test="type != null">`type`,</if> <if test="type != null">type,</if>
<if test="common != null">common,</if> <if test="common != null">common,</if>
<if test="code != null">code,</if> <if test="code != null">code,</if>
<if test="mockData != null">mock_data,</if> <if test="mockData != null">mock_data,</if>
@@ -80,7 +83,7 @@
update data_adapter update data_adapter
<set> <set>
<if test="adapterName != null">adapter_name = #{adapterName},</if> <if test="adapterName != null">adapter_name = #{adapterName},</if>
<if test="type != null">`type` = #{type},</if> <if test="type != null">type = #{type},</if>
<if test="code != null">code = #{code},</if> <if test="code != null">code = #{code},</if>
<if test="mockData != null">mock_data = #{mockData},</if> <if test="mockData != null">mock_data = #{mockData},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
@@ -89,6 +92,18 @@
where adapter_id = #{adapterId} where adapter_id = #{adapterId}
</update> </update>
<update id="updateCode">
update data_adapter
<set>
<if test="code != null and code != ''">code = #{code},</if>
<if test="executeCode != null and executeCode != ''">execute_code = #{code},</if>
<if test="mockData != null and mockData != ''">mock_data = #{mockData},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="updateTime != null ">update_time = #{updateTime},</if>
</set>
where adapter_id = #{adapterId}
</update>
<delete id="deleteById"> <delete id="deleteById">
delete delete
from data_adapter from data_adapter

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fateverse.query.mapper.PortalInterfaceMapper">
<select id="selectById" resultType="cn.fateverse.query.entity.PortalInterface">
select interface_id, url, content_type, request_method
from portal_interface
where interface_id = #{interfaceId}
</select>
<insert id="insert" useGeneratedKeys="true" keyColumn="interface_id" keyProperty="interfaceId">
insert into portal_interface (interface_id, url, content_type, request_method)
values (#{interfaceId}, #{url}, #{contentType}, #{requestMethod})
</insert>
<update id="update">
update portal_interface
set content_type = #{contentType},
url = #{url},
request_method = #{requestMethod}
where interface_id = #{interfaceId}
</update>
<delete id="deleteById">
delete
from portal_interface
where interface_id = #{interfaceId}
</delete>
</mapper>

View File

@@ -7,14 +7,16 @@
<sql id="selectVo"> <sql id="selectVo">
select portal_id, select portal_id,
query_id, query_id,
menu_id,
interface_id,
adapter_id, adapter_id,
portal_name, portal_name,
anonymity, permission_type,
type, type,
request_method, request_method,
create_data_adapter, create_data_adapter,
page,
path, path,
url,
state, state,
create_by, create_by,
create_time, create_time,
@@ -23,7 +25,6 @@
remark remark
from portal from portal
</sql> </sql>
<select id="selectById" resultType="cn.fateverse.query.entity.Portal"> <select id="selectById" resultType="cn.fateverse.query.entity.Portal">
<include refid="selectVo"/> <include refid="selectVo"/>
where portal_id = #{portalId} where portal_id = #{portalId}
@@ -32,9 +33,11 @@
<select id="selectList" resultType="cn.fateverse.query.entity.Portal"> <select id="selectList" resultType="cn.fateverse.query.entity.Portal">
<include refid="selectVo"/> <include refid="selectVo"/>
<where> <where>
<if test="portalName != null and portalName != ''">and portal_name like concat('%', #{portalName}, '%') </if> <if test="portalName != null and portalName != ''">and portal_name like concat('%', #{portalName}, '%')
<if test="anonymity != null ">and anonymity = #{anonymity}</if> </if>
<if test="permissionType != null ">and permission_type = #{permissionType}</if>
<if test="type != null">and type = #{type}</if> <if test="type != null">and type = #{type}</if>
<if test="state != null">and state = #{state}</if>
<if test="path != null and path != ''">and path like concat('%', #{path}, '%')</if> <if test="path != null and path != ''">and path like concat('%', #{path}, '%')</if>
</where> </where>
</select> </select>
@@ -49,13 +52,14 @@
<trim prefix="(" suffix=")" suffixOverrides=","> <trim prefix="(" suffix=")" suffixOverrides=",">
<if test="queryId != null">query_id,</if> <if test="queryId != null">query_id,</if>
<if test="adapterId != null">adapter_id,</if> <if test="adapterId != null">adapter_id,</if>
<if test="interfaceId != null">interface_id,</if>
<if test="portalName != null">portal_name,</if> <if test="portalName != null">portal_name,</if>
<if test="anonymity != null">anonymity,</if> <if test="permissionType != null">permission_type,</if>
<if test="type != null">type,</if> <if test="type != null">type,</if>
<if test="page != null">page,</if>
<if test="requestMethod != null">request_method,</if> <if test="requestMethod != null">request_method,</if>
<if test="createDataAdapter != null">create_data_adapter,</if> <if test="createDataAdapter != null">create_data_adapter,</if>
<if test="path != null">path,</if> <if test="path != null">path,</if>
<if test="url != null">url,</if>
<if test="state != null">state,</if> <if test="state != null">state,</if>
<if test="createBy != null and createBy != ''">create_by,</if> <if test="createBy != null and createBy != ''">create_by,</if>
<if test="createTime != null ">create_time,</if> <if test="createTime != null ">create_time,</if>
@@ -63,16 +67,48 @@
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="queryId != null">#{queryId},</if> <if test="queryId != null">#{queryId},</if>
<if test="adapterId != null">#{adapterId},</if> <if test="adapterId != null">#{adapterId},</if>
<if test="interfaceId != null">#{interfaceId},</if>
<if test="portalName != null">#{portalName},</if> <if test="portalName != null">#{portalName},</if>
<if test="anonymity != null">#{anonymity},</if> <if test="permissionType != null">#{permissionType},</if>
<if test="type != null">#{type},</if> <if test="type != null">#{type},</if>
<if test="page != null">#{page},</if>
<if test="requestMethod != null">#{requestMethod},</if> <if test="requestMethod != null">#{requestMethod},</if>
<if test="createDataAdapter != null">#{createDataAdapter},</if> <if test="createDataAdapter != null">#{createDataAdapter},</if>
<if test="path != null">#{path},</if> <if test="path != null">#{path},</if>
<if test="url != null">#{url},</if>
<if test="state != null">#{state},</if> <if test="state != null">#{state},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if> <if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="createTime != null ">#{createTime},</if> <if test="createTime != null ">#{createTime},</if>
</trim> </trim>
</insert> </insert>
<update id="update">
update portal
<set>
<if test="queryId != null">query_id = #{queryId},</if>
<if test="adapterId != null">adapter_id = #{adapterId},</if>
<if test="interfaceId != null">interface_id = #{interfaceId},</if>
<if test="portalName != null">portal_name = #{portalName},</if>
<if test="permissionType != null">permission_type = #{permissionType},</if>
<if test="type != null">type = #{type},</if>
<if test="page != null">type = #{page},</if>
<if test="requestMethod != null">request_method = #{requestMethod},</if>
<if test="createDataAdapter != null">create_data_adapter = #{createDataAdapter},</if>
<if test="path != null">path = #{path},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
</set>
where portal_id = #{portalId}
</update>
<update id="updateState">
update portal
set state = #{state}
where portal_id = #{portalId}
</update>
<delete id="deleteById">
delete
from portal
where portal_id = #{portalId}
</delete>
</mapper> </mapper>

View File

@@ -4,7 +4,14 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fateverse.query.mapper.PortalMappingMapper"> <mapper namespace="cn.fateverse.query.mapper.PortalMappingMapper">
<sql id="selectPortalMapping"> <sql id="selectPortalMapping">
select mapping_id, portal_id, mapping_key, mapping_type, mapping_value select mapping_id,
portal_id,
mapping_key,
mapping_type,
mapping_value,
input_type,
required,
description
from portal_mapping from portal_mapping
</sql> </sql>
@@ -21,12 +28,13 @@
</foreach> </foreach>
</select> </select>
<insert id="insertBatch"> <insert id="insertBatch">
insert into portal_mapping (portal_id, mapping_key, mapping_type, mapping_value) insert into portal_mapping (portal_id, mapping_key, mapping_type, mapping_value, input_type, required,
description)
values values
<foreach collection="list" item="item" separator=","> <foreach collection="list" item="item" separator=",">
(#{item.portalId}, #{item.mappingKey}, #{item.mappingType}, #{item.mappingValue}) (#{item.portalId}, #{item.mappingKey}, #{item.mappingType}, #{item.mappingValue}, #{item.inputType},
#{item.required},#{item.description})
</foreach> </foreach>
</insert> </insert>

View File

@@ -0,0 +1,32 @@
package cn.fateverse.query;
import java.io.*;
public class ConsoleCapture {
public static String captureOutput(Runnable codeToRun) throws IOException {
PrintStream originalOut = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream capturingOut = new PrintStream(baos);
try {
System.setOut(capturingOut);
codeToRun.run();
} finally {
System.setOut(originalOut);
}
return baos.toString();
}
// 使用示例
public static void main(String[] args) throws IOException {
String capturedText = captureOutput(() -> {
System.out.println("Hello, Console!");
System.out.println("This is being captured.");
});
System.out.println("Captured output:");
System.out.println(capturedText);
}
}

View File

@@ -1,14 +0,0 @@
package cn.fateverse.query;
import java.util.*;
import java.util.stream.*;
public class DataAdapter {
public static Object execute(List<Map<String, Object>> data) {
for (Map<String, Object> objectMap : data) {
objectMap.remove("oper_location");
}
return data;
}
}

View File

@@ -0,0 +1,16 @@
package cn.fateverse.query;
/**
* @author Clay
* @date 2024/4/22 18:59
*/
public class DataAdapter1100 {
public static Object execute(String data) {
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
return "qwewe";
}
}

View File

@@ -0,0 +1,77 @@
package cn.fateverse.query;
import cn.fateverse.common.code.engine.JavaScriptEngine;
import cn.fateverse.common.code.model.EngineResult;
import com.alibaba.fastjson2.JSONObject;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.junit.jupiter.api.Test;
import javax.script.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
/**
* @author Clay
* @date 2024/4/23 9:42
*/
public class JavaScriptParamTest {
static String finalCode = "function execute1(data) {\n" +
" let res = [data.length];\n" +
" for (let i = 0; i < data.length; i++) {\n" +
" console.log(data[i])\n" +
" console.log(data[i].foo)\n" +
" res[i] = data[i].foo\n" +
" }\n" +
" return res;\n" +
"}";
@Test
public void testParam() {
List<Object> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Map<String, Object> param = new HashMap<>();
param.put("foo", "sdsdsdsds");
list.add(param);
}
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine jsEngine = engineManager.getEngineByName("graal.js");
Context context = Context.newBuilder()
.allowAllAccess(true)
.build();
// Context context = Context.create();
context.eval("js", finalCode);
Value executeFunction = context.getBindings("js").getMember("execute1");
Value javaObjectAsValue = Value.asValue(list);
Value result = executeFunction.execute(javaObjectAsValue);
System.out.println(result);
}
@Test
public void testParam2() throws Exception {
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine jsEngine = engineManager.getEngineByName("graal.js");
Invocable funcCall = (Invocable) jsEngine;
Bindings bindings = jsEngine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.allowHostAccess", true);
bindings.put("polyglot.js.allowHostClassLookup", (Predicate<String>) s -> true);
jsEngine.eval(finalCode);
JSONObject jsonObject = new JSONObject();
jsonObject.put("foo", "sdsdsdsds");
Object res= funcCall.invokeFunction("execute1", jsonObject);
}
}

View File

@@ -0,0 +1,122 @@
package cn.fateverse.query;
import cn.fateverse.common.code.config.JavaCodeProperties;
import cn.fateverse.common.code.engine.JavaCodeEngine;
import cn.fateverse.common.code.engine.JavaScriptEngine;
import cn.fateverse.common.code.model.EngineResult;
import java.util.concurrent.FutureTask;
/**
* @author Clay
* @date 2024/4/22 16:04
*/
public class JavaScriptTest {
public static void main(String[] args) throws Exception {
String code = "function execute(data) {\n" +
" console.log(data)\n" +
" console.log(\"test console!\")\n" +
" let add = (a, b) => a + b; // 使用箭头函数\n" +
" console.log(\"Adding 5 and 3:\", add(5, 3));\n\n" +
" for (let i = 0; i < 1000; i++) {\n" +
" console.log(i)\n" +
" }" +
" return \"success\";\n" +
"}";
JavaCodeProperties properties = new JavaCodeProperties();
properties.setClassPath("C:\\home\\clay\\code\\");
JavaCodeEngine javaCodeEngine = new JavaCodeEngine(properties);
// EngineResult<Object> executeScript = JavaScriptEngine.executeScript(code, "execute", "测试控制台输出");
String finalCode = "function execute(data) {\n" +
" console.log(data)\n" +
" console.log(\"test console!\")\n" +
" let add = (a, b) => a + b; // 使用箭头函数\n" +
" console.log(\"Adding 5 and 3:\", add(5, 3));\n\n" +
" for (let i = 0; i < 1000; i++) {\n" +
" console.log(i)\n" +
" }" +
" return \"success\";\n" +
"}";
FutureTask<EngineResult> engineResultFutureTask = new FutureTask<>(() -> {
return JavaScriptEngine.execute(finalCode, "execute", false, "测试控制台输出");
});
String finalCode1 = "import java.util.*;\n" +
"import java.util.stream.*;\n" +
"\n" +
"public class DataAdapter100 {" +
"\n" +
" public static Object execute(String data) {\n" +
" for (int i = 0; i < 1000; i++) {\n" +
" System.out.println(i);\n" +
" }\n" +
" return \"qwewe\";\n" +
" }" +
"}\n";
FutureTask<EngineResult> engineResultFutureTask1 = new FutureTask<>(() -> {
return javaCodeEngine.execute(finalCode1, "DataAdapter100", "execute", new Class[]{String.class}, new Object[]{"test"}, true);
});
new Thread(engineResultFutureTask).start();
new Thread(engineResultFutureTask1).start();
// EngineResult<Object> execute = javaCodeEngine.execute(code, "DataAdapter100", "execute", new Class[]{String.class}, new Object[]{"test"}, true);
EngineResult x = engineResultFutureTask.get();
EngineResult x1 = engineResultFutureTask1.get();
System.out.println(x);
System.out.println(x1);
// EngineResult<Object> capture = MultiThreadedCapture.capture(() -> {
// ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal.js");
// Invocable inv = (Invocable) engine;
// try {
// engine.eval(finalCode);
// return inv.invokeFunction("execute", "args");
// } catch (ScriptException | NoSuchMethodException e) {
// throw new RuntimeException(e);
// }
// });
// System.out.println(capture);
// EngineResult<Object> objectEngineResult = JavaScriptEngine.executeScript(finalCode, "execute", "测试控制台输出");
//
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// PrintStream oldOut = System.out;
// System.setOut(new PrintStream(baos));
// EngineResult<Object> capture = MultiThreadedCapture.capture(() -> {
// int someNumber = 42;
// System.out.println(someNumber);
// return null;
// });
// 执行产生数值输出的代码
// System.out.println(objectEngineResult);
// ScriptEngine engine = JavaScriptEngine.getEngine();
// Invocable inv = (Invocable) engine;
// engine.eval(finalCode);
// inv.invokeFunction("execute", "args");
// System.out.println(capture);
// MultiThreadedCapture.executor.shutdown();
// 恢复标准输出流
// System.setOut(oldOut);
//
// System.setOut(oldOut);
// // 从捕获的字节数组输出流中获取打印的文本
// String capturedOutput = baos.toString();
// System.out.println("Captured output: " + capturedOutput);
}
}

View File

@@ -0,0 +1,38 @@
package cn.fateverse.query;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
public class Main {
public static void main(String[] args) {
// 创建 GraalVM 上下文
Context context = Context.create();
// 定义一个 JavaScript 函数
String script = "function execute(data) {\n" +
" console.log(data)\n" +
" console.log(\"test console!\")\n" +
" let add = (a, b) => a + b; // 使用箭头函数\n" +
" console.log(\"Adding 5 and 3:\", add(5, 3));\n\n" +
" for (let i = 0; i < 1000; i++) {\n" +
" console.log(i)\n" +
" }" +
" return \"success\";\n" +
"}";
// 编译 JavaScript 函数
context.eval("js", script);
// 获取函数对象
Value addFunction = context.getBindings("js").getMember("execute");
// 执行函数并传入参数
Value result = addFunction.execute("测试");
Value result2 = addFunction.execute("测试");
// 获取返回值
// int sum = result.asInt();
// 输出结果
System.out.println("Sum: "); // 输出Sum: 5
}
}

View File

@@ -0,0 +1,45 @@
package cn.fateverse.query;
import cn.fateverse.common.code.engine.JavaScriptEngine;
import cn.fateverse.common.code.model.EngineResult;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
public class SystemOut {
public static void outTest() {
ByteArrayOutputStream baoStream = new ByteArrayOutputStream(1024);
PrintStream cacheStream = new PrintStream(baoStream);//临时输出
PrintStream oldStream = System.out;//缓存系统输出
System.setOut(cacheStream);
System.out.print("...");//不会打印到控制台
String result = baoStream.toString();//存放控制台输出的字符串
System.setOut(oldStream);//还原到系统输出
System.out.println("result");
}
public static void main(String[] args) {
// outTest();
String finalCode = "function execute1(data) {\n" +
" console.log(data)\n" +
" console.log(\"test console!\")\n" +
" let add = (a, b) => a + b; // 使用箭头函数\n" +
" console.log(\"Adding 5 and 3:\", add(5, 3));\n\n" +
" for (let i = 0; i < 1000; i++) {\n" +
" console.log(i)\n" +
" }" +
" return \"success\";\n" +
"}";
// String finalCode = "function myFunction() { return 'Hello, world!'; }";
long start1 = System.currentTimeMillis();
EngineResult execute = JavaScriptEngine.execute(finalCode, "execute1", false, "测试控制台输出");
long start2 = System.currentTimeMillis();
EngineResult execute1 = JavaScriptEngine.execute(finalCode, "execute1", false, "测试控制台输出");
long start3 = System.currentTimeMillis();
System.out.println("耗时1" + (start2 - start1) + "ms");
System.out.println("耗时2" + (start3 - start2) + "ms");
System.out.println(execute);
}
}

View File

@@ -31,13 +31,13 @@ batch=${DRONE_COMMIT_BRANCH}
ignoreArr=("monitor","sentinel-dashboard-pro","sentinel-dashboard") ignoreArr=("monitor","sentinel-dashboard-pro","sentinel-dashboard")
#if [[ "${ignoreArr[*]}" =~ "${batch}" ]]; then if [[ "${ignoreArr[*]}" =~ "${batch}" ]]; then
# JAVA_AGENT="-javaagent:/home/clay/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=$DRONE_COMMIT_BRANCH -Dskywalking.trace.ignore_path=/actuator/** -Dskywalking.collector.backend_service=10.7.89.101:11800 --add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true" JAVA_AGENT="-javaagent:/home/clay/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=$DRONE_COMMIT_BRANCH -Dskywalking.trace.ignore_path=/actuator/** -Dskywalking.collector.backend_service=10.7.125.151:11800 --add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true"
# nohup java -jar $JAVA_AGENT $JAVA_OPTS $JAVA_MEM_OPTS $DRONE_COMMIT_BRANCH.jar --spring.profiles.active=pro --server.port=8080 >./logs/spring.log 2>&1 & nohup java -jar $JAVA_AGENT $JAVA_OPTS $JAVA_MEM_OPTS $DRONE_COMMIT_BRANCH.jar --spring.profiles.active=pro --server.port=8080 >./logs/spring.log 2>&1 &
#else else
# echo "exist"; echo "exist";
# nohup java -jar $JAVA_OPTS $JAVA_MEM_OPTS $DRONE_COMMIT_BRANCH.jar --spring.profiles.active=pro --server.port=8080 >./logs/spring.log 2>&1 & nohup java -jar $JAVA_OPTS $JAVA_MEM_OPTS $DRONE_COMMIT_BRANCH.jar --spring.profiles.active=pro --server.port=8080 >./logs/spring.log 2>&1 &
#fi fi
nohup java -jar $JAVA_OPTS $JAVA_MEM_OPTS $DRONE_COMMIT_BRANCH.jar --spring.profiles.active=pro --server.port=8080 >./logs/spring.log 2>&1 & nohup java -jar $JAVA_OPTS $JAVA_MEM_OPTS $DRONE_COMMIT_BRANCH.jar --spring.profiles.active=pro --server.port=8080 >./logs/spring.log 2>&1 &

View File

@@ -162,10 +162,10 @@ public class TriggerService {
try { try {
//判断请求是否有效 //判断请求是否有效
if (response.getStatusCode() == HttpStatus.OK) { if (response.getStatusCode() == HttpStatus.OK) {
jsResult = JavaScriptEngine.executeScript(http.getSuccess(), "handlerSuccess", result); jsResult = (ScriptObjectMirror) JavaScriptEngine.execute(http.getSuccess(), "handlerSuccess", result);
operation.setState(OperationStateEnums.SUCCESS); operation.setState(OperationStateEnums.SUCCESS);
} else { } else {
jsResult = JavaScriptEngine.executeScript(http.getFail(), "handlerFail", result); jsResult = (ScriptObjectMirror) JavaScriptEngine.execute(http.getFail(), "handlerFail", result);
operation.setState(OperationStateEnums.FAILURE); operation.setState(OperationStateEnums.FAILURE);
} }
} catch (Exception e) { } catch (Exception e) {