瀏覽代碼

在线定义函数

mxd 4 年之前
父節點
當前提交
1033221726

+ 13 - 0
src/main/java/org/ssssssss/magicapi/config/MagicConfiguration.java

@@ -18,6 +18,11 @@ public class MagicConfiguration {
 	 */
 	private MappingHandlerMapping mappingHandlerMapping;
 
+	/**
+	 * 函数管理
+	 */
+	private MagicFunctionManager magicFunctionManager;
+
 	/**
 	 * 用户名
 	 */
@@ -182,6 +187,14 @@ public class MagicConfiguration {
 		this.functionServiceProvider = functionServiceProvider;
 	}
 
+	public MagicFunctionManager getMagicFunctionManager() {
+		return magicFunctionManager;
+	}
+
+	public void setMagicFunctionManager(MagicFunctionManager magicFunctionManager) {
+		this.magicFunctionManager = magicFunctionManager;
+	}
+
 	/**
 	 * 打印banner
 	 */

+ 193 - 0
src/main/java/org/ssssssss/magicapi/config/MagicFunctionManager.java

@@ -0,0 +1,193 @@
+package org.ssssssss.magicapi.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.ssssssss.magicapi.model.FunctionInfo;
+import org.ssssssss.magicapi.model.Group;
+import org.ssssssss.magicapi.model.TreeNode;
+import org.ssssssss.magicapi.provider.FunctionServiceProvider;
+import org.ssssssss.magicapi.provider.GroupServiceProvider;
+import org.ssssssss.magicapi.script.ScriptManager;
+import org.ssssssss.magicapi.utils.PathUtils;
+import org.ssssssss.script.MagicResourceLoader;
+import org.ssssssss.script.MagicScriptContext;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class MagicFunctionManager {
+
+	private static Map<String, FunctionInfo> mappings = new ConcurrentHashMap<>();
+
+	private GroupServiceProvider groupServiceProvider;
+
+	private FunctionServiceProvider functionServiceProvider;
+
+	private TreeNode<Group> groups;
+
+	private static final Logger logger = LoggerFactory.getLogger(MagicFunctionManager.class);
+
+	public MagicFunctionManager(GroupServiceProvider groupServiceProvider, FunctionServiceProvider functionServiceProvider) {
+		this.groupServiceProvider = groupServiceProvider;
+		this.functionServiceProvider = functionServiceProvider;
+	}
+
+	public void registerFunctionLoader() {
+		MagicResourceLoader.addFunctionLoader((path) -> {
+			FunctionInfo info = mappings.get(path);
+			if (info != null) {
+				List<String> parameterNames = info.getParameterNames();
+				return (Function<Object[], Object>) objects -> {
+					MagicScriptContext functionContext = new MagicScriptContext(MagicScriptContext.get().getRootVariables());
+					if (objects != null) {
+						for (int i = 0, len = objects.length, size = parameterNames.size(); i < len && i < size; i++) {
+							functionContext.set(parameterNames.get(i), objects[i]);
+						}
+					}
+					return ScriptManager.executeScript(info.getScript(), functionContext);
+				};
+			}
+			return null;
+		});
+	}
+
+	/**
+	 * 加载所有分组
+	 */
+	public synchronized void loadGroup() {
+		groups = groupServiceProvider.functionGroupTree();
+	}
+
+	public void registerAllFunction() {
+		loadGroup();
+		functionServiceProvider.listWithScript().stream()
+				.filter(it -> groupServiceProvider.getFullPath(it.getGroupId()) != null)
+				.forEach(this::register);
+	}
+
+	public boolean hasRegister(FunctionInfo info) {
+		String path = PathUtils.replaceSlash(Objects.toString(groupServiceProvider.getFullPath(info.getGroupId()), "") + "/" + info.getPath());
+		FunctionInfo functionInfo = mappings.get(path);
+		return functionInfo != null && !Objects.equals(info.getId(), functionInfo.getId());
+	}
+
+	/**
+	 * 函数移动
+	 */
+	public boolean move(String id, String groupId) {
+		FunctionInfo info = mappings.get(id);
+		if (info == null) {
+			return false;
+		}
+		String path = Objects.toString(groupServiceProvider.getFullPath(groupId), "");
+		FunctionInfo functionInfo = mappings.get(PathUtils.replaceSlash(path + "/" + info.getPath()));
+		if (functionInfo != null && !Objects.equals(functionInfo.getId(), id)) {
+			return false;
+		}
+		unregister(id);
+		info.setGroupId(groupId);
+		register(info);
+		return true;
+	}
+
+
+	public void register(FunctionInfo functionInfo) {
+		FunctionInfo oldFunctionInfo = mappings.get(functionInfo.getId());
+		if (oldFunctionInfo != null) {
+			// 如果路径不一致,则需要取消注册
+			if (!Objects.equals(functionInfo.getPath(), oldFunctionInfo.getPath())) {
+				unregister(functionInfo.getId());
+			}
+		}
+		String path = Objects.toString(groupServiceProvider.getFullPath(functionInfo.getGroupId()), "");
+		mappings.put(functionInfo.getId(), functionInfo);
+		path = PathUtils.replaceSlash(path + "/" + functionInfo.getPath());
+		functionInfo.setMappingPath(path);
+		mappings.put(path, functionInfo);
+		logger.info("注册函数:[{}:{}]", functionInfo.getName(), path);
+	}
+
+	private boolean hasConflict(TreeNode<Group> group, String newPath) {
+		// 获取要移动的接口
+		List<FunctionInfo> infos = mappings.values().stream()
+				.filter(info -> Objects.equals(info.getGroupId(), group.getNode().getId()))
+				.distinct()
+				.collect(Collectors.toList());
+		// 判断是否有冲突
+		for (FunctionInfo info : infos) {
+			if (mappings.containsKey(PathUtils.replaceSlash(newPath + "/" + info.getPath()))) {
+				return true;
+			}
+		}
+		for (TreeNode<Group> child : group.getChildren()) {
+			if (hasConflict(child, newPath + "/" + Objects.toString(child.getNode().getPath(), ""))) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public boolean checkGroup(Group group) {
+		TreeNode<Group> oldTree = groups.findTreeNode((item) -> item.getId().equals(group.getId()));
+		// 如果只改了名字,则不做任何操作
+		if (Objects.equals(oldTree.getNode().getParentId(), group.getParentId()) &&
+				Objects.equals(oldTree.getNode().getPath(), group.getPath())) {
+			return true;
+		}
+		// 新的接口分组路径
+		String newPath = Objects.toString(groupServiceProvider.getFullPath(group.getParentId()), "");
+		// 检测冲突
+		return !hasConflict(oldTree, newPath + "/" + Objects.toString(group.getPath(), ""));
+	}
+
+	private void recurseUpdateGroup(TreeNode<Group> node, boolean updateGroupId) {
+		mappings.values().stream()
+				.filter(info -> Objects.equals(info.getGroupId(), node.getNode().getId()))
+				.distinct()
+				.collect(Collectors.toList())
+				.forEach(info -> {
+					unregister(info.getId());
+					if (updateGroupId) {
+						info.setGroupId(node.getNode().getId());
+					}
+					register(info);
+				});
+		for (TreeNode<Group> child : node.getChildren()) {
+			recurseUpdateGroup(child, false);
+		}
+	}
+
+	public void updateGroup(Group group) {
+		loadGroup();    // 重新加载分组
+		TreeNode<Group> groupTreeNode = groups.findTreeNode((item) -> item.getId().equals(group.getId()));
+		recurseUpdateGroup(groupTreeNode, true);
+	}
+
+	public void deleteGroup(List<String> groupIds) {
+		mappings.values().stream()
+				.filter(info -> groupIds.contains(info.getGroupId()))
+				.distinct()
+				.collect(Collectors.toList())
+				.forEach(info -> unregister(info.getId()));
+	}
+
+	public void unregister(String id) {
+		FunctionInfo functionInfo = mappings.remove(id);
+		if (functionInfo != null) {
+			mappings.remove(functionInfo.getMappingPath());
+			logger.info("取消注册函数:[{},{}]", functionInfo.getName(), functionInfo.getMappingPath());
+		}
+	}
+
+	public void enableRefresh(int interval) {
+		if (interval > 0) {
+			Executors.newScheduledThreadPool(1).scheduleAtFixedRate(this::registerAllFunction, interval, interval, TimeUnit.SECONDS);
+		}
+	}
+}

+ 1 - 1
src/main/java/org/ssssssss/magicapi/config/MappingHandlerMapping.java

@@ -152,7 +152,7 @@ public class MappingHandlerMapping {
 	 * 加载所有分组
 	 */
 	public synchronized void loadGroup() {
-		groups = groupServiceProvider.apiGroupList();
+		groups = groupServiceProvider.apiGroupTree();
 	}
 
 	/**

+ 1 - 1
src/main/java/org/ssssssss/magicapi/controller/MagicAPIController.java

@@ -116,7 +116,7 @@ public class MagicAPIController extends MagicController {
 		if (!allowVisit(request, RequestInterceptor.Authorization.SAVE)) {
 			return new JsonBean<>(-10, "无权限执行保存方法");
 		}
-		if (!configuration.getGroupServiceProvider().contains(groupId)) {
+		if (!configuration.getGroupServiceProvider().containsApiGroup(groupId)) {
 			return new JsonBean<>(0, "找不到分组信息");
 		}
 		try {

+ 128 - 0
src/main/java/org/ssssssss/magicapi/controller/MagicFunctionController.java

@@ -0,0 +1,128 @@
+package org.ssssssss.magicapi.controller;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.ssssssss.magicapi.config.MagicConfiguration;
+import org.ssssssss.magicapi.interceptor.RequestInterceptor;
+import org.ssssssss.magicapi.model.FunctionInfo;
+import org.ssssssss.magicapi.model.JsonBean;
+import org.ssssssss.magicapi.provider.FunctionServiceProvider;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+public class MagicFunctionController extends MagicController {
+
+	private FunctionServiceProvider functionService;
+
+	private static final Logger logger = LoggerFactory.getLogger(MagicFunctionController.class);
+
+	public MagicFunctionController(MagicConfiguration configuration) {
+		super(configuration);
+		this.functionService = configuration.getFunctionServiceProvider();
+	}
+
+	@RequestMapping("/function/list")
+	@ResponseBody
+	public JsonBean<List<FunctionInfo>> list() {
+		try {
+			return new JsonBean<>(functionService.list());
+		} catch (Exception e) {
+			logger.error("查询函数列表失败", e);
+			return new JsonBean<>(-1, e.getMessage());
+		}
+	}
+
+	@RequestMapping("/function/get")
+	@ResponseBody
+	public JsonBean<FunctionInfo> get(String id, HttpServletRequest request) {
+		if (!allowVisit(request, RequestInterceptor.Authorization.DETAIL)) {
+			return new JsonBean<>(-10, "无权限执行查看详情方法");
+		}
+		try {
+			return new JsonBean<>(functionService.get(id));
+		} catch (Exception e) {
+			logger.error("查询函数出错", e);
+			return new JsonBean<>(-1, e.getMessage());
+		}
+	}
+
+	@RequestMapping("/function/move")
+	@ResponseBody
+	public JsonBean<Boolean> move(String id, String groupId, HttpServletRequest request) {
+		if (!allowVisit(request, RequestInterceptor.Authorization.SAVE)) {
+			return new JsonBean<>(-10, "无权限执行移动函数");
+		}
+		try {
+			if (!configuration.getMagicFunctionManager().move(id, groupId)) {
+				return new JsonBean<>(0, "该路径已被映射,请换一个路径");
+			} else {
+				return new JsonBean<>(functionService.move(id, groupId));
+			}
+		} catch (Exception e) {
+			logger.error("移动函数出错", e);
+			return new JsonBean<>(-1, e.getMessage());
+		}
+	}
+
+	@RequestMapping("/function/save")
+	@ResponseBody
+	public JsonBean<String> save(FunctionInfo functionInfo, HttpServletRequest request) {
+		if (!allowVisit(request, RequestInterceptor.Authorization.SAVE)) {
+			return new JsonBean<>(-10, "无权限执行保存方法");
+		}
+		if (StringUtils.isBlank(functionInfo.getName())) {
+			return new JsonBean<>(0, "函数名称不能为空");
+		}
+		if (StringUtils.isBlank(functionInfo.getPath())) {
+			return new JsonBean<>(0, "函数路径不能为空");
+		}
+		if (StringUtils.isBlank(functionInfo.getScript())) {
+			return new JsonBean<>(0, "脚本内容不能为空");
+		}
+		if (configuration.getMagicFunctionManager().hasRegister(functionInfo)) {
+			return new JsonBean<>(0, "该路径已被映射,请换一个路径");
+		}
+		try {
+			if (StringUtils.isBlank(functionInfo.getId())) {
+				if (functionService.exists(functionInfo.getPath(), functionInfo.getGroupId())) {
+					return new JsonBean<>(0, String.format("函数%s已存在", functionInfo.getPath()));
+				}
+				functionService.insert(functionInfo);
+			} else {
+				if (functionService.existsWithoutId(functionInfo.getPath(), functionInfo.getGroupId(), functionInfo.getId())) {
+					return new JsonBean<>(0, String.format("函数%s已存在", functionInfo.getPath()));
+				}
+				functionService.update(functionInfo);
+			}
+			// 解除包装
+			functionService.unwrap(functionInfo);
+			configuration.getMagicFunctionManager().register(functionInfo);
+			return new JsonBean<>(functionInfo.getId());
+		} catch (Exception e) {
+			logger.error("保存函数出错", e);
+			return new JsonBean<>(-1, e.getMessage());
+		}
+	}
+
+	@RequestMapping("/function/delete")
+	@ResponseBody
+	public JsonBean<Boolean> delete(String id, HttpServletRequest request) {
+		if (!allowVisit(request, RequestInterceptor.Authorization.DELETE)) {
+			return new JsonBean<>(-10, "无权限执行删除方法");
+		}
+		try {
+			boolean success = functionService.delete(id);
+			if (success) {
+				configuration.getMagicFunctionManager().unregister(id);
+			}
+			return new JsonBean<>(success);
+		} catch (Exception e) {
+			logger.error("删除函数出错", e);
+			return new JsonBean<>(-1, e.getMessage());
+		}
+	}
+}

+ 41 - 17
src/main/java/org/ssssssss/magicapi/controller/MagicGroupController.java

@@ -37,21 +37,38 @@ public class MagicGroupController extends MagicController {
 			return new JsonBean<>(-10, "无权限执行删除方法");
 		}
 		try {
-			TreeNode<Group> treeNode = configuration.getGroupServiceProvider().apiGroupList().findTreeNode(group -> group.getId().equals(groupId));
+			boolean isApi = true;
+			TreeNode<Group> treeNode = configuration.getGroupServiceProvider().apiGroupTree().findTreeNode(group -> group.getId().equals(groupId));
 			if (treeNode == null) {
-				return new JsonBean<>(0, "分组不存在!");
+				treeNode = configuration.getGroupServiceProvider().functionGroupTree().findTreeNode(group -> group.getId().equals(groupId));
+				if (treeNode == null) {
+					return new JsonBean<>(0, "分组不存在!");
+				}
+				isApi = false;
 			}
 			List<String> groupIds = treeNode.flat().stream().map(Group::getId).collect(Collectors.toList());
-			// 删除接口
-			boolean success = configuration.getMagicApiService().deleteGroup(groupIds);
-			if (success) {
-				// 取消注册
-				configuration.getMappingHandlerMapping().deleteGroup(groupIds);
-				// 删除分组
-				success = this.groupServiceProvider.delete(groupId);
-				if (success) {
-					// 重新加载分组
-					configuration.getMappingHandlerMapping().loadGroup();
+			boolean success;
+			if (isApi) {
+				// 删除接口
+				if (success = configuration.getMagicApiService().deleteGroup(groupIds)) {
+					// 取消注册
+					configuration.getMappingHandlerMapping().deleteGroup(groupIds);
+					// 删除分组
+					if (success = this.groupServiceProvider.delete(groupId)) {
+						// 重新加载分组
+						configuration.getMappingHandlerMapping().loadGroup();
+					}
+				}
+			} else {
+				// 删除函数
+				if (success = configuration.getFunctionServiceProvider().deleteGroup(groupIds)) {
+					// 取消注册
+					configuration.getMagicFunctionManager().deleteGroup(groupIds);
+					// 删除分组
+					if (success = this.groupServiceProvider.delete(groupId)) {
+						// 重新加载分组
+						configuration.getMagicFunctionManager().loadGroup();
+					}
 				}
 			}
 			return new JsonBean<>(success);
@@ -81,14 +98,21 @@ public class MagicGroupController extends MagicController {
 		}
 		try {
 			boolean isApiGroup = "1".equals(group.getType());
-			if (!isApiGroup || configuration.getMappingHandlerMapping().checkGroup(group)) {
+			boolean isFunctionGroup = "2".equals(group.getType());
+			if (isApiGroup && configuration.getMappingHandlerMapping().checkGroup(group)) {
 				boolean success = groupServiceProvider.update(group);
-				if (success && isApiGroup) {    // 如果数据库修改成功,则修改接口路径
+				if (success) {    // 如果数据库修改成功,则修改接口路径
 					configuration.getMappingHandlerMapping().updateGroup(group);
 				}
 				return new JsonBean<>(success);
+			} else if (isFunctionGroup && configuration.getMagicFunctionManager().checkGroup(group)) {
+				boolean success = groupServiceProvider.update(group);
+				if (success) {    // 如果数据库修改成功,则修改接口路径
+					configuration.getMagicFunctionManager().updateGroup(group);
+				}
+				return new JsonBean<>(success);
 			}
-			return new JsonBean<>(-20, "修改分组后,接口路径会有冲突,请检查!");
+			return new JsonBean<>(-20, "修改分组后,路径会有冲突,请检查!");
 		} catch (Exception e) {
 			logger.error("修改分组出错", e);
 			return new JsonBean<>(-1, e.getMessage());
@@ -100,9 +124,9 @@ public class MagicGroupController extends MagicController {
 	 */
 	@RequestMapping("/group/list")
 	@ResponseBody
-	public JsonBean<List<Group>> groupList() {
+	public JsonBean<List<Group>> groupList(String type) {
 		try {
-			return new JsonBean<>(groupServiceProvider.groupList());
+			return new JsonBean<>(groupServiceProvider.groupList(type));
 		} catch (Exception e) {
 			logger.error("查询分组列表失败", e);
 			return new JsonBean<>(-1, e.getMessage());

+ 10 - 16
src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java

@@ -33,7 +33,6 @@ import org.ssssssss.magicapi.model.Options;
 import org.ssssssss.magicapi.modules.ResponseModule;
 import org.ssssssss.magicapi.provider.ResultProvider;
 import org.ssssssss.magicapi.script.ScriptManager;
-import org.ssssssss.script.MagicScript;
 import org.ssssssss.script.MagicScriptContext;
 import org.ssssssss.script.MagicScriptDebugContext;
 import org.ssssssss.script.exception.MagicScriptAssertException;
@@ -41,8 +40,6 @@ import org.ssssssss.script.exception.MagicScriptException;
 import org.ssssssss.script.functions.ObjectConvertExtension;
 import org.ssssssss.script.parsing.Span;
 
-import javax.script.ScriptContext;
-import javax.script.SimpleScriptContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
@@ -83,6 +80,10 @@ public class RequestHandler extends MagicController {
 		Object value;
 		// 执行前置拦截器
 		if ((value = doPreHandle(info, context, request, response)) != null) {
+			if (requestedFromTest) {
+				// 修正前端显示,当拦截器返回时,原样输出显示
+				response.setHeader(HEADER_RESPONSE_WITH_MAGIC_API, "false");
+			}
 			return value;
 		}
 		if (requestedFromTest) {
@@ -121,7 +122,7 @@ public class RequestHandler extends MagicController {
 		try {
 			// 初始化debug操作
 			initializeDebug(context, request, response);
-			Object result = executeScript(info.getScript(), context);
+			Object result = ScriptManager.executeScript(info.getScript(), context);
 			if (context.isRunning()) {
 				return new JsonBodyBean<>(1000, context.getId(), resultProvider.buildResult(1000, context.getId(), result), result);
 			} else if (context.isException()) {    //判断是否出现异常
@@ -130,7 +131,10 @@ public class RequestHandler extends MagicController {
 			Object value = result;
 			// 执行后置拦截器
 			if ((value = doPostHandle(info, context, value, request, response)) != null) {
-				return convertResult(value, response);
+				// 修正前端显示,当拦截器返回时,原样输出显示
+				response.setHeader(HEADER_RESPONSE_WITH_MAGIC_API, "false");
+				// 后置拦截器不包裹
+				return value;
 			}
 			return convertResult(result, response);
 		} catch (Exception e) {
@@ -141,7 +145,7 @@ public class RequestHandler extends MagicController {
 	private Object invokeRequest(ApiInfo info, MagicScriptContext context, HttpServletRequest request, HttpServletResponse response) throws Throwable {
 		try {
 			RequestContext.setRequestAttribute(request, response);
-			Object result = executeScript(info.getScript(), context);
+			Object result = ScriptManager.executeScript(info.getScript(), context);
 			Object value = result;
 			// 执行后置拦截器
 			if ((value = doPostHandle(info, context, value, request, response)) != null) {
@@ -167,16 +171,6 @@ public class RequestHandler extends MagicController {
 		}
 	}
 
-	/**
-	 * 执行脚本
-	 */
-	private Object executeScript(String script, MagicScriptContext context) {
-		SimpleScriptContext simpleScriptContext = new SimpleScriptContext();
-		simpleScriptContext.setAttribute(MagicScript.CONTEXT_ROOT, context, ScriptContext.ENGINE_SCOPE);
-		// 执行脚本
-		return ScriptManager.compile("MagicScript", script).eval(simpleScriptContext);
-	}
-
 	/**
 	 * 转换请求结果
 	 */

+ 54 - 5
src/main/java/org/ssssssss/magicapi/model/FunctionInfo.java

@@ -1,18 +1,33 @@
 package org.ssssssss.magicapi.model;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
 public class FunctionInfo {
 
 	private String id;
 
 	private String name;
 
+	private String path;
+
 	private String groupId;
 
 	private String script;
 
 	private String description;
 
-	private String arguments;
+	private String parameter;
+
+	private String returnType;
+
+	private String mappingPath;
+
+	private List<String> parameterNames = Collections.emptyList();
 
 	public String getId() {
 		return id;
@@ -54,11 +69,45 @@ public class FunctionInfo {
 		this.description = description;
 	}
 
-	public String getArguments() {
-		return arguments;
+	public String getParameter() {
+		return parameter;
+	}
+
+	public void setParameter(String parameter) {
+		this.parameter = parameter;
+		try {
+			this.parameterNames = new ObjectMapper().readTree(parameter).findValues("name")
+					.stream().map(JsonNode::asText)
+					.collect(Collectors.toList());
+		} catch (Throwable ignored) {
+		}
+	}
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
+
+	public String getMappingPath() {
+		return mappingPath;
+	}
+
+	public void setMappingPath(String mappingPath) {
+		this.mappingPath = mappingPath;
+	}
+
+	public List<String> getParameterNames() {
+		return parameterNames;
+	}
+
+	public String getReturnType() {
+		return returnType;
 	}
 
-	public void setArguments(String arguments) {
-		this.arguments = arguments;
+	public void setReturnType(String returnType) {
+		this.returnType = returnType;
 	}
 }

+ 4 - 0
src/main/java/org/ssssssss/magicapi/provider/FunctionServiceProvider.java

@@ -4,4 +4,8 @@ import org.ssssssss.magicapi.model.FunctionInfo;
 
 public interface FunctionServiceProvider extends StoreServiceProvider<FunctionInfo> {
 
+	boolean exists(String path, String groupId);
+
+	boolean existsWithoutId(String path, String groupId, String id);
+
 }

+ 8 - 3
src/main/java/org/ssssssss/magicapi/provider/GroupServiceProvider.java

@@ -25,17 +25,22 @@ public interface GroupServiceProvider {
 	/**
 	 * 是否有该分组
 	 */
-	boolean contains(String groupId);
+	boolean containsApiGroup(String groupId);
 
 	/**
 	 * 接口分组列表
 	 */
-	TreeNode<Group> apiGroupList();
+	TreeNode<Group> apiGroupTree();
+
+	/**
+	 * 函数分组列表
+	 */
+	TreeNode<Group> functionGroupTree();
 
 	/**
 	 * 分组列表
 	 */
-	List<Group> groupList();
+	List<Group> groupList(String type);
 
 	/**
 	 * 根据分组Id获取分组路径

+ 33 - 9
src/main/java/org/ssssssss/magicapi/provider/impl/DefaultFunctionServiceProvider.java

@@ -6,15 +6,19 @@ import org.ssssssss.magicapi.model.ApiInfo;
 import org.ssssssss.magicapi.model.FunctionInfo;
 import org.ssssssss.magicapi.provider.FunctionServiceProvider;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 public class DefaultFunctionServiceProvider extends BeanPropertyRowMapper<FunctionInfo> implements FunctionServiceProvider {
 
 	private final String COMMON_COLUMNS = "id,\n" +
 			"function_name,\n" +
 			"function_group_id,\n" +
-			"function_arguments,\n" +
+			"function_path,\n" +
+			"function_parameter,\n" +
+			"function_return_type,\n" +
 			"function_description,\n" +
 			"function_create_time,\n" +
 			"function_update_time";
@@ -24,6 +28,7 @@ public class DefaultFunctionServiceProvider extends BeanPropertyRowMapper<Functi
 	private JdbcTemplate template;
 
 	public DefaultFunctionServiceProvider(JdbcTemplate template) {
+		super(FunctionInfo.class);
 		this.template = template;
 	}
 
@@ -31,16 +36,16 @@ public class DefaultFunctionServiceProvider extends BeanPropertyRowMapper<Functi
 	public boolean insert(FunctionInfo info) {
 		info.setId(UUID.randomUUID().toString().replace("-", ""));
 		wrap(info);
-		String insert = String.format("insert into magic_function(%s,%s) values(?,?,?,?,?,?,?,?)", COMMON_COLUMNS, SCRIPT_COLUMNS);
+		String insert = String.format("insert into magic_function(%s,%s) values(?,?,?,?,?,?,?,?,?,?)", COMMON_COLUMNS, SCRIPT_COLUMNS);
 		long time = System.currentTimeMillis();
-		return template.update(insert, info.getId(), info.getName(), info.getGroupId(), info.getArguments(), info.getDescription(), info.getScript(), time, time) > 0;
+		return template.update(insert, info.getId(), info.getName(), info.getGroupId(), info.getPath(), info.getParameter(), info.getReturnType(), info.getDescription(), time, time, info.getScript()) > 0;
 	}
 
 	@Override
 	public boolean update(FunctionInfo info) {
 		wrap(info);
-		String update = "update magic_function set function_name = ?,function_arguments = ?,function_script = ?,function_description = ?,function_group_id = ?,function_update_time = ? where id = ?";
-		return template.update(update, info.getName(), info.getArguments(), info.getScript(), info.getDescription(), info.getGroupId(), System.currentTimeMillis(), info.getId()) > 0;
+		String update = "update magic_function set function_name = ?,function_parameter = ?,function_return_type = ?, function_script = ?,function_description = ?,function_path = ?,function_group_id = ?,function_update_time = ? where id = ?";
+		return template.update(update, info.getName(), info.getParameter(), info.getReturnType(), info.getScript(), info.getDescription(), info.getPath(), info.getGroupId(), System.currentTimeMillis(), info.getId()) > 0;
 	}
 
 	@Override
@@ -65,13 +70,20 @@ public class DefaultFunctionServiceProvider extends BeanPropertyRowMapper<Functi
 
 	@Override
 	public List<FunctionInfo> list() {
-		String selectList = "select " + COMMON_COLUMNS + " from magic_api_info order by api_update_time desc";
+		String selectList = "select " + COMMON_COLUMNS + " from magic_function order by function_update_time desc";
 		return template.query(selectList, this);
 	}
 
 	@Override
 	public List<FunctionInfo> listWithScript() {
-		return null;
+		String selectListWithScript = "select " + COMMON_COLUMNS + "," + SCRIPT_COLUMNS + " from magic_function";
+		List<FunctionInfo> infos = template.query(selectListWithScript, this);
+		if (infos != null) {
+			for (FunctionInfo info : infos) {
+				unwrap(info);
+			}
+		}
+		return infos;
 	}
 
 	@Override
@@ -84,16 +96,28 @@ public class DefaultFunctionServiceProvider extends BeanPropertyRowMapper<Functi
 
 	@Override
 	public boolean move(String id, String groupId) {
-		return false;
+		return template.update("update magic_function SET function_group_id = ? where id = ?", groupId, id) > 0;
 	}
 
 	@Override
 	public boolean deleteGroup(List<String> groupIds) {
-		return false;
+		List<Object[]> params = groupIds.stream().map(groupId -> new Object[]{groupId}).collect(Collectors.toList());
+		return Arrays.stream(template.batchUpdate("delete from magic_function where function_group_id = ?", params)).sum() >= 0;
 	}
 
 	@Override
 	protected String lowerCaseName(String name) {
 		return super.lowerCaseName(name).replace("function_", "");
 	}
+
+	@Override
+	public boolean exists(String path, String groupId) {
+		return template.queryForObject("select count(*) from magic_function where function_group_id = ? and function_path = ?", Integer.class, groupId, path) > 0;
+	}
+
+	@Override
+	public boolean existsWithoutId(String path, String groupId, String id) {
+		return template.queryForObject("select count(*) from magic_function where function_group_id = ? and function_path = ? and id != ?", Integer.class, groupId, path, id) > 0;
+	}
+
 }

+ 27 - 16
src/main/java/org/ssssssss/magicapi/provider/impl/DefaultGroupServiceProvider.java

@@ -14,7 +14,9 @@ public class DefaultGroupServiceProvider extends BeanPropertyRowMapper<Group> im
 
 	private JdbcTemplate template;
 
-	private Map<String, Group> cacheTree = new HashMap<>();
+	private Map<String, Group> cacheApiTree = new HashMap<>();
+
+	private Map<String, Group> cacheFunctionTree = new HashMap<>();
 
 	public DefaultGroupServiceProvider(JdbcTemplate template) {
 		super(Group.class);
@@ -41,32 +43,34 @@ public class DefaultGroupServiceProvider extends BeanPropertyRowMapper<Group> im
 	}
 
 	@Override
-	public boolean contains(String groupId) {
-		return "0".equals(groupId) || cacheTree.containsKey(groupId);
+	public boolean containsApiGroup(String groupId) {
+		return "0".equals(groupId) || cacheApiTree.containsKey(groupId);
 	}
 
 	@Override
-	public TreeNode<Group> apiGroupList() {
-		List<Group> groups = template.query("select * from magic_group where group_type = '1' ", this);
-		TreeNode<Group> root = new TreeNode<>();
-		root.setNode(new Group("0", "root"));
-		convertToTree(groups, root);
-		Map<String, Group> groupMap = new HashMap<>();
-		groups.forEach(group -> groupMap.put(group.getId(), group));
-		cacheTree = groupMap;
-		return root;
+	public TreeNode<Group> apiGroupTree() {
+		List<Group> groups = template.query("select * from magic_group where group_type = '1'", this);
+		cacheApiTree = groups.stream().collect(Collectors.toMap(Group::getId, value -> value));
+		return convertToTree(groups);
+	}
+
+	@Override
+	public TreeNode<Group> functionGroupTree() {
+		List<Group> groups = template.query("select * from magic_group where group_type = '2'", this);
+		cacheFunctionTree = groups.stream().collect(Collectors.toMap(Group::getId, value -> value));
+		return convertToTree(groups);
 	}
 
 	@Override
-	public List<Group> groupList() {
-		return template.query("select * from magic_group",this);
+	public List<Group> groupList(String type) {
+		return template.query("select * from magic_group where group_type = ?", this, type);
 	}
 
 	@Override
 	public String getFullPath(String groupId) {
 		StringBuilder path = new StringBuilder();
 		Group group;
-		while ((group = cacheTree.get(groupId)) != null) {
+		while ((group = cacheFunctionTree.getOrDefault(groupId, cacheApiTree.get(groupId))) != null) {
 			path.insert(0, '/' + Objects.toString(group.getPath(), ""));
 			groupId = group.getParentId();
 		}
@@ -84,7 +88,7 @@ public class DefaultGroupServiceProvider extends BeanPropertyRowMapper<Group> im
 		}
 		StringBuilder name = new StringBuilder();
 		Group group;
-		while ((group = cacheTree.get(groupId)) != null) {
+		while ((group = cacheFunctionTree.getOrDefault(groupId, cacheApiTree.get(groupId))) != null) {
 			name.insert(0, '/' + group.getName());
 			groupId = group.getParentId();
 		}
@@ -95,6 +99,13 @@ public class DefaultGroupServiceProvider extends BeanPropertyRowMapper<Group> im
 		return name.substring(1);
 	}
 
+	private TreeNode<Group> convertToTree(List<Group> groups) {
+		TreeNode<Group> root = new TreeNode<>();
+		root.setNode(new Group("0", "root"));
+		convertToTree(groups, root);
+		return root;
+	}
+
 	private void convertToTree(List<Group> groups, TreeNode<Group> current) {
 		List<TreeNode<Group>> treeNodes = groups.stream()
 				.filter(it -> current.getNode().getId().equals(it.getParentId()))

+ 13 - 1
src/main/java/org/ssssssss/magicapi/script/ScriptManager.java

@@ -3,10 +3,12 @@ package org.ssssssss.magicapi.script;
 import org.ssssssss.magicapi.cache.DefaultSqlCache;
 import org.ssssssss.magicapi.utils.MD5Utils;
 import org.ssssssss.script.MagicScript;
+import org.ssssssss.script.MagicScriptContext;
 
+import javax.script.ScriptContext;
 import javax.script.ScriptEngine;
-import javax.script.ScriptEngineFactory;
 import javax.script.ScriptEngineManager;
+import javax.script.SimpleScriptContext;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -35,6 +37,16 @@ public class ScriptManager {
 		return magicScript;
 	}
 
+	/**
+	 * 执行脚本
+	 */
+	public static Object executeScript(String script, MagicScriptContext context) {
+		SimpleScriptContext simpleScriptContext = new SimpleScriptContext();
+		simpleScriptContext.setAttribute(MagicScript.CONTEXT_ROOT, context, ScriptContext.ENGINE_SCOPE);
+		// 执行脚本
+		return compile("MagicScript", script).eval(simpleScriptContext);
+	}
+
 	public static ScriptEngine getEngine(String engine){
 		return scriptEngineManager.getEngineByName(engine);
 	}