Kaynağa Gözat

结构优化

mxd 4 yıl önce
ebeveyn
işleme
f62c47a239

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

@@ -1,8 +1,11 @@
 package org.ssssssss.magicapi.config;
 
 import org.springframework.http.converter.HttpMessageConverter;
+import org.ssssssss.magicapi.controller.MagicDynamicDataSource;
+import org.ssssssss.magicapi.controller.RequestHandler;
 import org.ssssssss.magicapi.interceptor.RequestInterceptor;
 import org.ssssssss.magicapi.provider.ApiServiceProvider;
+import org.ssssssss.magicapi.provider.FunctionServiceProvider;
 import org.ssssssss.magicapi.provider.GroupServiceProvider;
 import org.ssssssss.magicapi.provider.ResultProvider;
 
@@ -43,6 +46,11 @@ public class MagicConfiguration {
 	 */
 	private GroupServiceProvider groupServiceProvider;
 
+	/**
+	 * 函数查询Service
+	 */
+	private FunctionServiceProvider functionServiceProvider;
+
 	/**
 	 * 动态数据源
 	 */
@@ -167,6 +175,14 @@ public class MagicConfiguration {
 		return enableWeb;
 	}
 
+	public FunctionServiceProvider getFunctionServiceProvider() {
+		return functionServiceProvider;
+	}
+
+	public void setFunctionServiceProvider(FunctionServiceProvider functionServiceProvider) {
+		this.functionServiceProvider = functionServiceProvider;
+	}
+
 	/**
 	 * 打印banner
 	 */

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

@@ -2,6 +2,7 @@ package org.ssssssss.magicapi.config;
 
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.http.HttpHeaders;
+import org.ssssssss.magicapi.controller.MagicAPIController;
 
 import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;

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

@@ -11,6 +11,7 @@ import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerMapping;
 import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.ssssssss.magicapi.controller.RequestHandler;
 import org.ssssssss.magicapi.model.ApiInfo;
 import org.ssssssss.magicapi.model.Group;
 import org.ssssssss.magicapi.model.TreeNode;
@@ -109,7 +110,7 @@ public class MappingHandlerMapping {
 	/**
 	 * 根据绑定的key获取接口信息
 	 */
-	public static ApiInfo getMappingApiInfo(String key) {
+	private static ApiInfo getMappingApiInfo(String key) {
 		return mappings.get(key);
 	}
 
@@ -119,7 +120,7 @@ public class MappingHandlerMapping {
 	 * @param requestMethod  请求方法
 	 * @param requestMapping 请求路径
 	 */
-	public static String buildMappingKey(String requestMethod, String requestMapping) {
+	private static String buildMappingKey(String requestMethod, String requestMapping) {
 
 		if (!StringUtils.isEmpty(requestMapping) && !requestMapping.startsWith("/")) {
 			requestMapping = "/" + requestMapping;
@@ -150,7 +151,7 @@ public class MappingHandlerMapping {
 	/**
 	 * 加载所有分组
 	 */
-	synchronized  void loadGroup() {
+	public synchronized void loadGroup() {
 		groups = groupServiceProvider.apiGroupList();
 	}
 
@@ -200,7 +201,7 @@ public class MappingHandlerMapping {
 	/**
 	 * 检测是否允许修改
 	 */
-	boolean checkGroup(Group group) {
+	public boolean checkGroup(Group group) {
 		Group oldGroup = groups.findNode((item) -> item.getId().equals(group.getId()));
 		// 如果只改了名字,则不做任何操作
 		if (Objects.equals(oldGroup.getParentId(), group.getParentId()) &&
@@ -234,7 +235,7 @@ public class MappingHandlerMapping {
 	/**
 	 * 删除分组
 	 */
-	void deleteGroup(String groupId) {
+	public void deleteGroup(String groupId) {
 		// 找到下级所有分组
 		List<String> groupIds = groups.findNodes((item) -> item.getId().equals(groupId)).stream().map(Group::getId).collect(Collectors.toList());
 		groupIds.add(groupId);
@@ -250,7 +251,7 @@ public class MappingHandlerMapping {
 	/**
 	 * 修改分组
 	 */
-	void updateGroup(Group group) {
+	public void updateGroup(Group group) {
 		loadGroup();    // 重新加载分组
 		Group oldGroup = groups.findNode((item) -> item.getId().equals(group.getId()));
 		apiInfos.stream().filter(info -> Objects.equals(info.getGroupId(), oldGroup.getId())).forEach(info -> {
@@ -263,7 +264,7 @@ public class MappingHandlerMapping {
 	/**
 	 * 判断是否已注册
 	 */
-	boolean hasRegisterMapping(ApiInfo info) {
+	public boolean hasRegisterMapping(ApiInfo info) {
 		if (info.getId() != null) {
 			ApiInfo oldInfo = mappings.get(info.getId());
 			if (oldInfo != null
@@ -288,7 +289,7 @@ public class MappingHandlerMapping {
 	/**
 	 * 接口移动
 	 */
-	boolean move(String id, String groupId) {
+	public boolean move(String id, String groupId) {
 		ApiInfo oldInfo = mappings.get(id);
 		if (oldInfo == null) {
 			return false;
@@ -301,7 +302,7 @@ public class MappingHandlerMapping {
 	/**
 	 * 注册请求映射
 	 */
-	void registerMapping(ApiInfo info, boolean delete) {
+	public void registerMapping(ApiInfo info, boolean delete) {
 		// 先判断是否已注册,如果已注册,则先取消注册在进行注册。
 		ApiInfo oldInfo = mappings.get(info.getId());
 		String newMappingKey = getMappingKey(info);
@@ -346,14 +347,14 @@ public class MappingHandlerMapping {
 		apiInfos.add(info);
 	}
 
-	public void registerMapping(RequestMappingInfo requestMapping, Object handler, Method method) {
+	private void registerMapping(RequestMappingInfo requestMapping, Object handler, Method method) {
 		requestMappingHandlerMapping.registerMapping(requestMapping, handler, method);
 	}
 
 	/**
 	 * 取消注册请求映射
 	 */
-	void unregisterMapping(String id, boolean delete) {
+	public void unregisterMapping(String id, boolean delete) {
 		ApiInfo info = mappings.remove(id);
 		if (info != null) {
 			logger.info("取消注册接口:{}", info.getName());

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

@@ -1,10 +1,11 @@
-package org.ssssssss.magicapi.config;
+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.ApiInfo;
 import org.ssssssss.magicapi.model.JsonBean;

+ 4 - 3
src/main/java/org/ssssssss/magicapi/config/MagicConfigController.java → src/main/java/org/ssssssss/magicapi/controller/MagicConfigController.java

@@ -1,12 +1,13 @@
-package org.ssssssss.magicapi.config;
+package org.ssssssss.magicapi.controller;
 
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.ssssssss.magicapi.config.MagicConfiguration;
 import org.ssssssss.magicapi.model.JsonBean;
 import org.ssssssss.magicapi.modules.SQLModule;
 import org.ssssssss.magicapi.provider.MagicAPIService;
-import org.ssssssss.script.MagicModuleLoader;
+import org.ssssssss.script.MagicResourceLoader;
 import org.ssssssss.script.MagicScriptEngine;
 import org.ssssssss.script.ScriptClass;
 
@@ -31,7 +32,7 @@ public class MagicConfigController extends MagicController {
 	@ResponseBody
 	public JsonBean<Map<String, Map<String, ScriptClass>>> classes() {
 		Map<String, ScriptClass> classMap = MagicScriptEngine.getScriptClassMap();
-		classMap.putAll(MagicModuleLoader.getModules());
+		classMap.putAll(MagicResourceLoader.getModules());
 		ScriptClass db = classMap.get(SQLModule.class.getName());
 		if (db != null) {
 			List<ScriptClass.ScriptAttribute> attributes = new ArrayList<>();

+ 8 - 9
src/main/java/org/ssssssss/magicapi/config/MagicController.java → src/main/java/org/ssssssss/magicapi/controller/MagicController.java

@@ -1,5 +1,6 @@
-package org.ssssssss.magicapi.config;
+package org.ssssssss.magicapi.controller;
 
+import org.ssssssss.magicapi.config.MagicConfiguration;
 import org.ssssssss.magicapi.interceptor.RequestInterceptor;
 import org.ssssssss.magicapi.utils.MD5Utils;
 
@@ -21,7 +22,7 @@ public class MagicController {
 
 	MagicConfiguration configuration;
 
-	public MagicController(MagicConfiguration configuration) {
+	MagicController(MagicConfiguration configuration) {
 		this.configuration = configuration;
 	}
 
@@ -29,14 +30,12 @@ public class MagicController {
 	 * 判断是否有权限访问按钮
 	 */
 	boolean allowVisit(HttpServletRequest request, RequestInterceptor.Authorization authorization) {
-		if (configuration.getUsername()!= null && configuration.getUsername() != null) {
-			String headerValue = request.getHeader(configuration.getTokenKey());
-			String realValue = MD5Utils.encrypt(String.format("%s||%s", configuration.getUsername(), configuration.getPassword()));
-			if(!Objects.equals(realValue,headerValue)){
-				return false;
-			}
-		}
 		if (authorization == null) {
+			if (configuration.getUsername() != null && configuration.getPassword() != null) {
+				String headerValue = request.getHeader(configuration.getTokenKey());
+				String realValue = MD5Utils.encrypt(String.format("%s||%s", configuration.getUsername(), configuration.getPassword()));
+				return Objects.equals(realValue, headerValue);
+			}
 			return true;
 		}
 		for (RequestInterceptor requestInterceptor : configuration.getRequestInterceptors()) {

+ 2 - 3
src/main/java/org/ssssssss/magicapi/config/MagicDynamicDataSource.java → src/main/java/org/ssssssss/magicapi/controller/MagicDynamicDataSource.java

@@ -1,4 +1,4 @@
-package org.ssssssss.magicapi.config;
+package org.ssssssss.magicapi.controller;
 
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -67,7 +67,6 @@ public class MagicDynamicDataSource {
 
 	/**
 	 * 获取默认数据源
-	 * @return
 	 */
 	public MagicDynamicDataSource.DataSourceNode getDataSource() {
 		return getDataSource(null);
@@ -99,7 +98,7 @@ public class MagicDynamicDataSource {
 
 		private Dialect dialect;
 
-		public DataSourceNode(DataSource dataSource) {
+		DataSourceNode(DataSource dataSource) {
 			this.dataSource = dataSource;
 			this.dataSourceTransactionManager = new DataSourceTransactionManager(this.dataSource);
 			this.jdbcTemplate = new JdbcTemplate(dataSource);

+ 2 - 1
src/main/java/org/ssssssss/magicapi/config/MagicGroupController.java → src/main/java/org/ssssssss/magicapi/controller/MagicGroupController.java

@@ -1,10 +1,11 @@
-package org.ssssssss.magicapi.config;
+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.Group;
 import org.ssssssss.magicapi.model.JsonBean;

+ 2 - 2
src/main/java/org/ssssssss/magicapi/config/MagicWorkbenchController.java → src/main/java/org/ssssssss/magicapi/controller/MagicWorkbenchController.java

@@ -1,15 +1,15 @@
-package org.ssssssss.magicapi.config;
+package org.ssssssss.magicapi.controller;
 
 import org.springframework.http.HttpHeaders;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+import org.ssssssss.magicapi.config.MagicConfiguration;
 import org.ssssssss.magicapi.logging.MagicLoggerContext;
 import org.ssssssss.magicapi.model.JsonBean;
 import org.ssssssss.magicapi.model.Options;
 import org.ssssssss.magicapi.utils.MD5Utils;
 
-import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;

+ 5 - 2
src/main/java/org/ssssssss/magicapi/config/RequestHandler.java → src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java

@@ -1,4 +1,4 @@
-package org.ssssssss.magicapi.config;
+package org.ssssssss.magicapi.controller;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.commons.io.IOUtils;
@@ -16,6 +16,8 @@ import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.context.request.RequestContextHolder;
+import org.ssssssss.magicapi.config.MagicConfiguration;
+import org.ssssssss.magicapi.config.MappingHandlerMapping;
 import org.ssssssss.magicapi.context.CookieContext;
 import org.ssssssss.magicapi.context.HeaderContext;
 import org.ssssssss.magicapi.context.RequestContext;
@@ -25,6 +27,7 @@ import org.ssssssss.magicapi.logging.MagicLoggerContext;
 import org.ssssssss.magicapi.model.ApiInfo;
 import org.ssssssss.magicapi.model.JsonBean;
 import org.ssssssss.magicapi.model.JsonBodyBean;
+import org.ssssssss.magicapi.model.Options;
 import org.ssssssss.magicapi.modules.ResponseModule;
 import org.ssssssss.magicapi.provider.ResultProvider;
 import org.ssssssss.magicapi.script.ScriptManager;
@@ -305,7 +308,7 @@ public class RequestHandler extends MagicController {
 	private MagicScriptContext createMagicScriptContext(ApiInfo info, HttpServletRequest request, Map<String, Object> pathVariables, Map<String, Object> parameters) throws IOException {
 		// 构建脚本上下文
 		MagicScriptContext context = isRequestedFromTest(request) ? new MagicScriptDebugContext() : new MagicScriptContext();
-		Object wrap = info.getOptionValue(ApiInfo.WRAP_REQUEST_PARAMETER);
+		Object wrap = info.getOptionValue(Options.WRAP_REQUEST_PARAMETERS.getValue());
 		if (wrap != null && StringUtils.isNotBlank(wrap.toString())) {
 			context.set(wrap.toString(), parameters);
 		}

+ 0 - 2
src/main/java/org/ssssssss/magicapi/model/ApiInfo.java

@@ -10,8 +10,6 @@ import java.util.Objects;
  */
 public class ApiInfo {
 
-	public static final String WRAP_REQUEST_PARAMETER = "wrap_request_parameter";
-
 	/**
 	 * 接口ID
 	 */

+ 64 - 0
src/main/java/org/ssssssss/magicapi/model/FunctionInfo.java

@@ -0,0 +1,64 @@
+package org.ssssssss.magicapi.model;
+
+public class FunctionInfo {
+
+	private String id;
+
+	private String name;
+
+	private String groupId;
+
+	private String script;
+
+	private String description;
+
+	private String arguments;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getGroupId() {
+		return groupId;
+	}
+
+	public void setGroupId(String groupId) {
+		this.groupId = groupId;
+	}
+
+	public String getScript() {
+		return script;
+	}
+
+	public void setScript(String script) {
+		this.script = script;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getArguments() {
+		return arguments;
+	}
+
+	public void setArguments(String arguments) {
+		this.arguments = arguments;
+	}
+}

+ 1 - 1
src/main/java/org/ssssssss/magicapi/modules/NamedTable.java

@@ -1,7 +1,7 @@
 package org.ssssssss.magicapi.modules;
 
 import org.apache.commons.lang3.StringUtils;
-import org.ssssssss.magicapi.config.MagicDynamicDataSource.DataSourceNode;
+import org.ssssssss.magicapi.controller.MagicDynamicDataSource.DataSourceNode;
 import org.ssssssss.magicapi.exception.MagicAPIException;
 import org.ssssssss.script.annotation.UnableCall;
 

+ 2 - 2
src/main/java/org/ssssssss/magicapi/modules/SQLModule.java

@@ -7,9 +7,9 @@ import org.springframework.jdbc.support.KeyHolder;
 import org.ssssssss.magicapi.adapter.ColumnMapperAdapter;
 import org.ssssssss.magicapi.adapter.DialectAdapter;
 import org.ssssssss.magicapi.cache.SqlCache;
-import org.ssssssss.magicapi.config.MagicDynamicDataSource;
-import org.ssssssss.magicapi.config.MagicDynamicDataSource.DataSourceNode;
 import org.ssssssss.magicapi.config.MagicModule;
+import org.ssssssss.magicapi.controller.MagicDynamicDataSource;
+import org.ssssssss.magicapi.controller.MagicDynamicDataSource.DataSourceNode;
 import org.ssssssss.magicapi.dialect.Dialect;
 import org.ssssssss.magicapi.interceptor.SQLInterceptor;
 import org.ssssssss.magicapi.model.Page;

+ 1 - 90
src/main/java/org/ssssssss/magicapi/provider/ApiServiceProvider.java

@@ -2,48 +2,11 @@ package org.ssssssss.magicapi.provider;
 
 import org.ssssssss.magicapi.model.ApiInfo;
 
-import java.util.List;
-
 /**
  * API存储接口
  */
-public interface ApiServiceProvider {
-	/**
-	 * 删除接口
-	 *
-	 * @param id 接口ID
-	 */
-	boolean delete(String id);
-
-	/**
-	 * 查询所有接口(提供给页面,无需带script)
-	 */
-	List<ApiInfo> list();
-
-	/**
-	 * 查询所有接口(内部使用,需要带Script)
-	 */
-	List<ApiInfo> listWithScript();
-
-	/**
-	 * 查询接口详情(主要给页面使用)
-	 *
-	 * @param id 接口ID
-	 */
-	ApiInfo get(String id);
-
-	/**
-	 * 移动接口
-	 *
-	 * @param id      接口ID
-	 * @param groupId 分组ID
-	 */
-	boolean move(String id, String groupId);
+public interface ApiServiceProvider extends StoreServiceProvider<ApiInfo> {
 
-	/**
-	 * 根据组ID删除
-	 */
-	boolean deleteGroup(String groupId);
 
 	/**
 	 * 判断接口是否存在
@@ -64,57 +27,5 @@ public interface ApiServiceProvider {
 	 */
 	boolean existsWithoutId(String groupId, String method, String path, String id);
 
-	/**
-	 * 添加接口信息
-	 *
-	 * @param info 接口信息
-	 */
-	boolean insert(ApiInfo info);
-
-	/**
-	 * 修改接口信息
-	 *
-	 * @param info 接口信息
-	 */
-	boolean update(ApiInfo info);
-
-	/**
-	 * 备份历史记录
-	 *
-	 * @param apiId 接口ID
-	 */
-	void backup(String apiId);
-
-
-	/**
-	 * 查询API历史记录
-	 *
-	 * @param apiId 接口ID
-	 * @return 时间戳列表
-	 */
-	List<Long> backupList(String apiId);
-
-	/**
-	 * 查询API历史记录详情
-	 *
-	 * @param apiId     接口ID
-	 * @param timestamp 时间戳
-	 */
-	ApiInfo backupInfo(String apiId, Long timestamp);
-
-	/**
-	 * 包装接口信息(可用于加密)
-	 *
-	 * @param info 接口信息
-	 */
-	default void wrap(ApiInfo info) {
-	}
 
-	/**
-	 * 解除包装接口信息(可用于解密)
-	 *
-	 * @param info 接口信息
-	 */
-	default void unwrap(ApiInfo info) {
-	}
 }

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

@@ -0,0 +1,7 @@
+package org.ssssssss.magicapi.provider;
+
+import org.ssssssss.magicapi.model.FunctionInfo;
+
+public interface FunctionServiceProvider extends StoreServiceProvider<FunctionInfo> {
+
+}

+ 90 - 0
src/main/java/org/ssssssss/magicapi/provider/StoreServiceProvider.java

@@ -0,0 +1,90 @@
+package org.ssssssss.magicapi.provider;
+
+import org.ssssssss.magicapi.model.ApiInfo;
+
+import java.util.List;
+
+public interface StoreServiceProvider<T> {
+
+	/**
+	 * 添加接口信息
+	 *
+	 * @param info 接口信息
+	 */
+	boolean insert(T info);
+
+	/**
+	 * 修改接口信息
+	 */
+	boolean update(T info);
+
+	/**
+	 * 备份历史记录
+	 *
+	 * @param apiId 接口ID
+	 */
+	void backup(String apiId);
+
+
+	/**
+	 * 查询历史记录
+	 *
+	 * @return 时间戳列表
+	 */
+	List<Long> backupList(String id);
+
+	/**
+	 * 查询历史记录详情
+	 *
+	 * @param id        ID
+	 * @param timestamp 时间戳
+	 */
+	ApiInfo backupInfo(String id, Long timestamp);
+
+	/**
+	 * 删除接口
+	 */
+	boolean delete(String id);
+
+	/**
+	 * 查询所有(提供给页面,无需带script)
+	 */
+	List<T> list();
+
+	/**
+	 * 查询所有(内部使用,需要带Script)
+	 */
+	List<T> listWithScript();
+
+	/**
+	 * 查询接口详情(主要给页面使用)
+	 *
+	 * @param id 接口ID
+	 */
+	T get(String id);
+
+	/**
+	 * 移动接口
+	 *
+	 * @param id      接口ID
+	 * @param groupId 分组ID
+	 */
+	boolean move(String id, String groupId);
+
+	/**
+	 * 根据组ID删除
+	 */
+	boolean deleteGroup(String groupId);
+
+	/**
+	 * 包装接口信息(可用于加密)
+	 */
+	default void wrap(T info) {
+	}
+
+	/**
+	 * 解除包装接口信息(可用于解密)
+	 */
+	default void unwrap(T info) {
+	}
+}

+ 99 - 0
src/main/java/org/ssssssss/magicapi/provider/impl/DefaultFunctionServiceProvider.java

@@ -0,0 +1,99 @@
+package org.ssssssss.magicapi.provider.impl;
+
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.ssssssss.magicapi.model.ApiInfo;
+import org.ssssssss.magicapi.model.FunctionInfo;
+import org.ssssssss.magicapi.provider.FunctionServiceProvider;
+
+import java.util.List;
+import java.util.UUID;
+
+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_description,\n" +
+			"function_create_time,\n" +
+			"function_update_time";
+
+	private final String SCRIPT_COLUMNS = "function_script";
+
+	private JdbcTemplate template;
+
+	public DefaultFunctionServiceProvider(JdbcTemplate template) {
+		this.template = template;
+	}
+
+	@Override
+	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);
+		long time = System.currentTimeMillis();
+		return template.update(insert, info.getId(), info.getName(), info.getGroupId(), info.getArguments(), info.getDescription(), info.getScript(), time, time) > 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;
+	}
+
+	@Override
+	public void backup(String apiId) {
+
+	}
+
+	@Override
+	public List<Long> backupList(String id) {
+		return null;
+	}
+
+	@Override
+	public ApiInfo backupInfo(String id, Long timestamp) {
+		return null;
+	}
+
+	@Override
+	public boolean delete(String id) {
+		return template.update("delete from magic_function where id = ?", id) > 0;
+	}
+
+	@Override
+	public List<FunctionInfo> list() {
+		String selectList = "select " + COMMON_COLUMNS + " from magic_api_info order by api_update_time desc";
+		return template.query(selectList, this);
+	}
+
+	@Override
+	public List<FunctionInfo> listWithScript() {
+		return null;
+	}
+
+	@Override
+	public FunctionInfo get(String id) {
+		String selectOne = "select " + COMMON_COLUMNS + "," + SCRIPT_COLUMNS + " from magic_function where id = ?";
+		FunctionInfo info = template.queryForObject(selectOne, this, id);
+		unwrap(info);
+		return info;
+	}
+
+	@Override
+	public boolean move(String id, String groupId) {
+		return false;
+	}
+
+	@Override
+	public boolean deleteGroup(String groupId) {
+		return template.update("delete from magic_function where function_group_id = ?", groupId) >= 0;
+	}
+
+	@Override
+	protected String lowerCaseName(String name) {
+		return super.lowerCaseName(name).replace("function_", "");
+	}
+}