调整代码

This commit is contained in:
mxd
2020-11-28 23:43:27 +08:00
parent fcaeefc58a
commit 9556caca42
35 changed files with 783 additions and 244 deletions

24
db/v0.4.x-v0.5.sql Normal file
View File

@@ -0,0 +1,24 @@
-- 创建分组表
CREATE TABLE `magic_group` (
`id` varchar(32) NOT NULL,
`group_name` varchar(64) NULL COMMENT '组名',
`group_type` varchar(1) NULL COMMENT '组类型1接口分组2函数分组',
`group_path` varchar(64) NULL COMMENT '分组路径',
`parent_id` varchar(32) NULL COMMENT '父级ID',
`deleted` char(1) NULL DEFAULT 0 COMMENT '是否被删除10',
PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'MagicAPI分组信息表' ROW_FORMAT = Dynamic;
-- 插入分组数据
insert into magic_group select md5(uuid()),api_group_name,'1',api_group_prefix,'0','0' from magic_api_info group by api_group_name;
-- 修改字段
ALTER TABLE `magic_api_info` ADD COLUMN `api_group_id` varchar(32) NULL COMMENT '分组ID' AFTER `api_name`;
-- 修改字段
ALTER TABLE `magic_api_info_his` ADD COLUMN `api_group_id` varchar(32) NULL COMMENT '分组ID' AFTER `api_name`;
-- 赋值api_group_id字段
UPDATE magic_api_info mai JOIN magic_group mg ON mg.group_name = mai.api_group_name AND mg.group_path = mai.api_group_prefix SET mai.api_group_id = mg.id;
-- 对关联不上的,归根节点
UPDATE magic_api_info SET api_group_id = '0' where api_group_id IS NULL;
-- 删除字段
ALTER TABLE `magic_api_info` DROP COLUMN `api_group_name`,DROP COLUMN `api_group_prefix`;
ALTER TABLE `magic_api_info_his` DROP COLUMN `api_group_name`,DROP COLUMN `api_group_prefix`;

View File

@@ -1,6 +1,6 @@
package org.ssssssss.magicapi.cache;
import org.ssssssss.magicapi.functions.BoundSql;
import org.ssssssss.magicapi.modules.BoundSql;
import org.ssssssss.magicapi.utils.MD5Utils;
import java.util.Arrays;

View File

@@ -11,7 +11,12 @@ 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.model.ApiInfo;
import org.ssssssss.magicapi.model.Group;
import org.ssssssss.magicapi.model.TreeNode;
import org.ssssssss.magicapi.provider.ApiServiceProvider;
import org.ssssssss.magicapi.provider.GroupServiceProvider;
import org.ssssssss.magicapi.utils.PathUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -53,11 +58,22 @@ public class MappingHandlerMapping {
* 接口信息读取
*/
private ApiServiceProvider magicApiService;
/**
* 分组信息读取
*/
private GroupServiceProvider groupServiceProvider;
/**
* 统一接口前缀
*/
private String prefix;
/**
* 接口分组
*/
private TreeNode<Group> groups;
/**
* 是否覆盖应用接口
*/
@@ -102,10 +118,9 @@ public class MappingHandlerMapping {
*
* @param requestMethod 请求方法
* @param requestMapping 请求路径
* @return
*/
public static String buildMappingKey(String requestMethod, String requestMapping) {
//TODO 判断 requestMapping 是否已 “/” 开头
if (!StringUtils.isEmpty(requestMapping) && !requestMapping.startsWith("/")) {
requestMapping = "/" + requestMapping;
}
@@ -124,17 +139,30 @@ public class MappingHandlerMapping {
this.magicApiService = magicApiService;
}
public void setGroupServiceProvider(GroupServiceProvider groupServiceProvider) {
this.groupServiceProvider = groupServiceProvider;
}
public List<ApiInfo> getApiInfos() {
return apiInfos;
}
/**
* 加载所有分组
*/
public void loadGroup() {
groups = groupServiceProvider.apiGroupList();
}
/**
* 注册请求
*/
public void registerAllMapping() {
try {
loadGroup();
List<ApiInfo> list = magicApiService.listWithScript();
if (list != null) {
list = list.stream().filter(it -> groupServiceProvider.getFullPath(it.getGroupId()) != null).collect(Collectors.toList());
for (ApiInfo info : list) {
try {
// 当接口存在时,刷新缓存
@@ -169,34 +197,86 @@ public class MappingHandlerMapping {
return mappings.get(buildMappingKey(method, requestMapping));
}
public void updateGroupPrefix(String oldGroupName, String newGroupName, String prefix) {
for (ApiInfo info : apiInfos) {
if (oldGroupName.equals(info.getGroupName())) {
unregisterMapping(info.getId(), false);
info.setGroupName(newGroupName);
info.setGroupPrefix(prefix);
registerMapping(info, false);
/**
* 检测是否允许修改
*/
boolean checkGroup(Group group) {
Group oldGroup = groups.findNode((item) -> item.getId().equals(group.getId()));
// 如果只改了名字,则不做任何操作
if (Objects.equals(oldGroup.getParentId(), group.getParentId()) &&
Objects.equals(oldGroup.getPath(), group.getPath())) {
return true;
}
// 新的接口分组路径
String newPath = groupServiceProvider.getFullPath(group.getParentId()) + "/" + Objects.toString(group.getPath(), "");
// 获取要移动的接口
List<ApiInfo> infos = apiInfos.stream().filter(info -> Objects.equals(info.getGroupId(), oldGroup.getId())).collect(Collectors.toList());
// 判断是否有冲突
for (ApiInfo info : infos) {
String path = getRequestPath(newPath, info.getPath());
String mappingKey = buildMappingKey(info.getMethod(), path);
if (mappings.containsKey(mappingKey)) {
return false;
}
if (!allowOverride) {
Map<RequestMappingInfo, HandlerMethod> handlerMethods = this.requestMappingHandlerMapping.getHandlerMethods();
if (handlerMethods != null) {
if (handlerMethods.get(getRequestMapping(info.getMethod(), path)) != null) {
return false;
}
}
}
}
return true;
}
/**
* 删除分组
*/
void deleteGroup(String groupId) {
// 找到下级所有分组
List<String> groupIds = groups.findNodes((item) -> item.getId().equals(groupId)).stream().map(Group::getId).collect(Collectors.toList());
groupIds.add(groupId);
// 找到对应的所有接口
List<ApiInfo> deleteInfos = apiInfos.stream().filter(info -> groupIds.contains(info.getGroupId())).collect(Collectors.toList());
for (ApiInfo info : deleteInfos) {
unregisterMapping(info.getId(), true);
}
// 全部删除
apiInfos.removeAll(deleteInfos);
}
/**
* 修改分组
*/
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 -> {
unregisterMapping(info.getId(), false);
info.setGroupId(group.getId());
registerMapping(info, false);
});
}
/**
* 判断是否已注册
*/
public boolean hasRegisterMapping(ApiInfo info) {
boolean hasRegisterMapping(ApiInfo info) {
if (info.getId() != null) {
ApiInfo oldInfo = mappings.get(info.getId());
if (oldInfo != null
&& Objects.equals(oldInfo.getGroupPrefix(), info.getGroupPrefix())
&& Objects.equals(oldInfo.getGroupId(), info.getGroupId())
&& Objects.equals(oldInfo.getMethod(), info.getMethod())
&& Objects.equals(oldInfo.getPath(), info.getPath())) {
return false;
}
}
if(mappings.containsKey(getMappingKey(info))){
if (mappings.containsKey(getMappingKey(info))) {
return true;
}
if(!allowOverride){
if (!allowOverride) {
Map<RequestMappingInfo, HandlerMethod> handlerMethods = this.requestMappingHandlerMapping.getHandlerMethods();
if (handlerMethods != null) {
return handlerMethods.get(getRequestMapping(info)) != null;
@@ -205,10 +285,23 @@ public class MappingHandlerMapping {
return false;
}
/**
* 接口移动
*/
boolean move(String id, String groupId) {
ApiInfo oldInfo = mappings.get(id);
if (oldInfo == null) {
return false;
}
oldInfo.setGroupId(groupId);
registerMapping(oldInfo, false);
return true;
}
/**
* 注册请求映射
*/
public void registerMapping(ApiInfo info, boolean delete) {
void registerMapping(ApiInfo info, boolean delete) {
// 先判断是否已注册,如果已注册,则先取消注册在进行注册。
ApiInfo oldInfo = mappings.get(info.getId());
String newMappingKey = getMappingKey(info);
@@ -219,12 +312,12 @@ public class MappingHandlerMapping {
if (!info.equals(oldInfo)) {
mappings.put(info.getId(), info);
mappings.put(newMappingKey, info);
logger.info("刷新接口:{}", info.getName());
logger.info("刷新接口:{},{}", info.getName(), newMappingKey);
}
return;
}
// URL不一致时需要取消注册旧接口重新注册新接口
logger.info("取消注册接口:{}", oldInfo.getName());
logger.info("取消注册接口:{},{}", oldInfo.getName(), newMappingKey);
// 取消注册
mappings.remove(oldMappingKey);
requestMappingHandlerMapping.unregisterMapping(getRequestMapping(oldInfo));
@@ -232,17 +325,11 @@ public class MappingHandlerMapping {
// 注册
RequestMappingInfo requestMapping = getRequestMapping(info);
// 如果与应用冲突
if (requestMappingHandlerMapping.getHandlerMethods().containsKey(requestMapping)) {
if (!allowOverride) {
// 不允许覆盖
logger.error("接口{}与应用冲突,无法注册", info.getName());
return;
}
logger.warn("取消注册应用接口:{}", requestMapping);
// 取消注册原接口
requestMappingHandlerMapping.unregisterMapping(requestMapping);
if (!overrideApplicationMapping(requestMapping)) {
logger.error("接口{},{}与应用冲突,无法注册", info.getName(), newMappingKey);
return;
}
logger.info("注册接口:{}", info.getName());
logger.info("注册接口:{},{}", info.getName(), newMappingKey);
mappings.put(info.getId(), info);
mappings.put(newMappingKey, info);
requestMappingHandlerMapping.registerMapping(requestMapping, handler, method);
@@ -255,7 +342,7 @@ public class MappingHandlerMapping {
/**
* 取消注册请求映射
*/
public void unregisterMapping(String id, boolean delete) {
void unregisterMapping(String id, boolean delete) {
ApiInfo info = mappings.remove(id);
if (info != null) {
logger.info("取消注册接口:{}", info.getName());
@@ -271,35 +358,51 @@ public class MappingHandlerMapping {
* 根据接口信息获取绑定map的key
*/
private String getMappingKey(ApiInfo info) {
return buildMappingKey(info.getMethod(), getRequestPath(info.getGroupPrefix(), info.getPath()));
return buildMappingKey(info.getMethod(), getRequestPath(info.getGroupId(), info.getPath()));
}
/**
* 处理前缀
*
* @param groupPrefix 分组前缀
* @param path 请求路径
* @param groupId 分组ID
* @param path 请求路径
*/
public String getRequestPath(String groupPrefix, String path) {
groupPrefix = groupPrefix == null ? "" : groupPrefix;
while (groupPrefix.endsWith("/")) {
groupPrefix = groupPrefix.substring(0, groupPrefix.length() - 1);
}
while (path.startsWith("/")) {
path = path.substring(1);
}
path = groupPrefix + "/" + path;
public String getRequestPath(String groupId, String path) {
path = groupServiceProvider.getFullPath(groupId) + "/" + path;
if (prefix != null) {
path = prefix + (path.startsWith("/") ? path.substring(1) : path);
path = prefix + "/" + path;
}
return path;
return PathUtils.replaceSlash(path);
}
/**
* 覆盖应用接口
*/
private boolean overrideApplicationMapping(RequestMappingInfo requestMapping) {
if (requestMappingHandlerMapping.getHandlerMethods().containsKey(requestMapping)) {
if (!allowOverride) {
// 不允许覆盖
return false;
}
logger.warn("取消注册应用接口:{}", requestMapping);
// 取消注册原接口
requestMappingHandlerMapping.unregisterMapping(requestMapping);
}
return true;
}
/**
* 根据接口信息构建 RequestMappingInfo
*/
private RequestMappingInfo getRequestMapping(ApiInfo info) {
return RequestMappingInfo.paths(getRequestPath(info.getGroupPrefix(), info.getPath())).methods(RequestMethod.valueOf(info.getMethod().toUpperCase())).build();
return RequestMappingInfo.paths(getRequestPath(info.getGroupId(), info.getPath())).methods(RequestMethod.valueOf(info.getMethod().toUpperCase())).build();
}
/**
* 根据接口信息构建 RequestMappingInfo
*/
private RequestMappingInfo getRequestMapping(String method, String path) {
return RequestMappingInfo.paths(path).methods(RequestMethod.valueOf(method.toUpperCase())).build();
}
public void enableRefresh(int interval) {

View File

@@ -18,10 +18,12 @@ import org.ssssssss.magicapi.context.CookieContext;
import org.ssssssss.magicapi.context.HeaderContext;
import org.ssssssss.magicapi.context.RequestContext;
import org.ssssssss.magicapi.context.SessionContext;
import org.ssssssss.magicapi.functions.ResponseFunctions;
import org.ssssssss.magicapi.interceptor.RequestInterceptor;
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.modules.ResponseModule;
import org.ssssssss.magicapi.provider.ResultProvider;
import org.ssssssss.magicapi.script.ScriptManager;
import org.ssssssss.script.MagicScript;
@@ -247,7 +249,7 @@ public class RequestHandler {
return ResponseEntity.ok(new JsonBean<>(entity.getBody()));
}
return ResponseEntity.ok(new JsonBean<>(convertToBase64(entity.getBody())));
} else if (result instanceof ResponseFunctions.NullValue) {
} else if (result instanceof ResponseModule.NullValue) {
return new JsonBean<>(1, "empty.");
}
return new JsonBean<>(resultProvider.buildResult(result));
@@ -379,7 +381,7 @@ public class RequestHandler {
private Object response(Object value) {
if (value instanceof ResponseEntity) {
return value;
} else if (value instanceof ResponseFunctions.NullValue) {
} else if (value instanceof ResponseModule.NullValue) {
return null;
}
return resultProvider.buildResult(value);

View File

@@ -6,10 +6,14 @@ import org.slf4j.LoggerFactory;
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.functions.SQLExecutor;
import org.ssssssss.magicapi.interceptor.RequestInterceptor;
import org.ssssssss.magicapi.logging.MagicLoggerContext;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.magicapi.model.Group;
import org.ssssssss.magicapi.model.JsonBean;
import org.ssssssss.magicapi.modules.SQLModule;
import org.ssssssss.magicapi.provider.ApiServiceProvider;
import org.ssssssss.magicapi.provider.GroupServiceProvider;
import org.ssssssss.magicapi.provider.MagicAPIService;
import org.ssssssss.magicapi.utils.MD5Utils;
import org.ssssssss.script.MagicModuleLoader;
@@ -36,10 +40,15 @@ public class WebUIController {
private MappingHandlerMapping mappingHandlerMapping;
/**
* 接口查询service
* 接口查询Service
*/
private ApiServiceProvider magicApiService;
/**
* 分组查询Service
*/
private GroupServiceProvider groupServiceProvider;
/**
* 拦截器
@@ -65,10 +74,14 @@ public class WebUIController {
public WebUIController() {
// 给前端添加代码提示
MagicScriptEngine.addScriptClass(SQLExecutor.class);
MagicScriptEngine.addScriptClass(SQLModule.class);
MagicScriptEngine.addScriptClass(MagicAPIService.class);
}
public void setGroupServiceProvider(GroupServiceProvider groupServiceProvider) {
this.groupServiceProvider = groupServiceProvider;
}
public void setMagicDynamicDataSource(MagicDynamicDataSource magicDynamicDataSource) {
this.magicDynamicDataSource = magicDynamicDataSource;
}
@@ -93,15 +106,15 @@ public class WebUIController {
this.password = password;
}
/**
* 删除接口
*
* @param request
* @param id 接口ID
* @param id 接口ID
*/
@RequestMapping("/delete")
@ResponseBody
public JsonBean<Boolean> delete(HttpServletRequest request, String id) {
public JsonBean<Boolean> delete(String id, HttpServletRequest request) {
if (!allowVisit(request, RequestInterceptor.Authorization.DELETE)) {
return new JsonBean<>(-10, "无权限执行删除方法");
}
@@ -118,28 +131,45 @@ public class WebUIController {
}
/**
* 删除接口分组
*
* @param apiIds 接口ID列表逗号分隔
* @param groupName 分组名称
* 创建分组
*/
@RequestMapping("/group/create")
@ResponseBody
public JsonBean<String> createGroup(Group group, HttpServletRequest request) {
if (!allowVisit(request, RequestInterceptor.Authorization.SAVE)) {
return new JsonBean<>(-10, "无权限执行保存方法");
}
if (StringUtils.isBlank(group.getParentId())) {
group.setParentId("0");
}
if (StringUtils.isBlank(group.getName())) {
return new JsonBean<>(0, "分组名称不能为空");
}
if (StringUtils.isBlank(group.getType())) {
return new JsonBean<>(0, "分组类型不能为空");
}
try {
groupServiceProvider.insert(group);
return new JsonBean<>(group.getId());
} catch (Exception e) {
logger.error("保存分组出错", e);
return new JsonBean<>(-1, e.getMessage());
}
}
/**
* 删除分组
*/
@RequestMapping("/group/delete")
@ResponseBody
public JsonBean<Boolean> deleteGroup(HttpServletRequest request, String apiIds, String groupName) {
public JsonBean<Boolean> deleteGroup(String groupId, HttpServletRequest request) {
if (!allowVisit(request, RequestInterceptor.Authorization.DELETE)) {
return new JsonBean<>(-10, "无权限执行删除方法");
}
try {
boolean success = this.magicApiService.deleteGroup(groupName);
boolean success = this.magicApiService.deleteGroup(groupId) && this.groupServiceProvider.delete(groupId);
if (success) { //删除成功时取消注册
if (StringUtils.isNotBlank(apiIds)) {
String[] ids = apiIds.split(",");
if (ids.length > 0) {
for (String id : ids) {
mappingHandlerMapping.unregisterMapping(id, true);
}
}
}
mappingHandlerMapping.deleteGroup(groupId);
}
return new JsonBean<>(success);
} catch (Exception e) {
@@ -150,29 +180,52 @@ public class WebUIController {
/**
* 修改分组
*
* @param groupName 分组名称
* @param oldGroupName 原分组名称
* @param prefix 分组前缀
*/
@RequestMapping("/group/update")
@ResponseBody
public JsonBean<Boolean> groupUpdate(String groupName, String oldGroupName, String prefix, HttpServletRequest request) {
public synchronized JsonBean<Boolean> groupUpdate(Group group, HttpServletRequest request) {
if (!allowVisit(request, RequestInterceptor.Authorization.SAVE)) {
return new JsonBean<>(-10, "无权限执行删除方法");
}
if (StringUtils.isBlank(group.getParentId())) {
group.setParentId("0");
}
if (StringUtils.isBlank(group.getName())) {
return new JsonBean<>(0, "分组名称不能为空");
}
if (StringUtils.isBlank(group.getType())) {
return new JsonBean<>(0, "分组类型不能为空");
}
try {
boolean success = magicApiService.updateGroup(oldGroupName, groupName, prefix);
if (success) {
mappingHandlerMapping.updateGroupPrefix(oldGroupName, groupName, prefix);
boolean isApiGroup = "1".equals(group.getType());
if (!isApiGroup || mappingHandlerMapping.checkGroup(group)) {
boolean success = groupServiceProvider.update(group);
if (success && isApiGroup) { // 如果数据库修改成功,则修改接口路径
mappingHandlerMapping.updateGroup(group);
}
return new JsonBean<>(success);
}
return new JsonBean<>(success);
return new JsonBean<>(-20, "修改分组后,接口路径会有冲突,请检查!");
} catch (Exception e) {
logger.error("修改分组出错", e);
return new JsonBean<>(-1, e.getMessage());
}
}
/**
* 查询所有分组
*/
@RequestMapping("/group/list")
@ResponseBody
public JsonBean<List<Group>> groupList() {
try {
return new JsonBean<>(groupServiceProvider.groupList());
} catch (Exception e) {
logger.error("查询分组列表失败", e);
return new JsonBean<>(-1, e.getMessage());
}
}
/**
* 查询所有接口
*/
@@ -212,9 +265,9 @@ public class WebUIController {
public JsonBean<Map<String, Map<String, ScriptClass>>> classes() {
Map<String, ScriptClass> classMap = MagicScriptEngine.getScriptClassMap();
classMap.putAll(MagicModuleLoader.getModules());
ScriptClass db = classMap.get(SQLExecutor.class.getName());
ScriptClass db = classMap.get(SQLModule.class.getName());
if (db != null) {
List<ScriptClass.ScriptAttribute> attributes = new ArrayList<>();
List<ScriptClass.ScriptAttribute> attributes = new ArrayList<>();
// 给与前台动态数据源提示
magicDynamicDataSource.datasources().stream().filter(StringUtils::isNotBlank)
.forEach(item -> attributes.add(new ScriptClass.ScriptAttribute("db", item)));
@@ -290,6 +343,30 @@ public class WebUIController {
return new JsonBean<>(magicApiService.backupInfo(id, timestamp));
}
/**
* 移动接口
*/
@RequestMapping("/api/move")
@ResponseBody
public JsonBean<Boolean> apiMove(String id, String groupId, HttpServletRequest request) {
if (!allowVisit(request, RequestInterceptor.Authorization.SAVE)) {
return new JsonBean<>(-10, "无权限执行保存方法");
}
if (!groupServiceProvider.contains(groupId)) {
return new JsonBean<>(0, "找不到分组信息");
}
try {
if (!mappingHandlerMapping.move(id, groupId)) {
return new JsonBean<>(0, "移动接口失败!");
} else {
return new JsonBean<>(magicApiService.move(id, groupId));
}
} catch (Exception e) {
logger.error("移动接口出错", e);
return new JsonBean<>(-1, e.getMessage());
}
}
/**
* 保存接口
*
@@ -305,12 +382,6 @@ public class WebUIController {
if (StringUtils.isBlank(info.getMethod())) {
return new JsonBean<>(0, "请求方法不能为空");
}
if (info.getGroupName() != null && (info.getGroupName().contains("'") || info.getGroupName().contains("\""))) {
return new JsonBean<>(0, "分组名不能包含特殊字符' \"");
}
if (info.getGroupPrefix() != null && (info.getGroupPrefix().contains("'") || info.getGroupPrefix().contains("\""))) {
return new JsonBean<>(0, "分组前缀不能包含特殊字符' \"");
}
if (StringUtils.isBlank(info.getPath())) {
return new JsonBean<>(0, "请求路径不能为空");
}
@@ -325,13 +396,13 @@ public class WebUIController {
}
if (StringUtils.isBlank(info.getId())) {
// 先判断接口是否存在
if (magicApiService.exists(info.getGroupPrefix(), info.getMethod(), info.getPath())) {
if (magicApiService.exists(info.getGroupId(), info.getMethod(), info.getPath())) {
return new JsonBean<>(0, String.format("接口%s:%s已存在", info.getMethod(), info.getPath()));
}
magicApiService.insert(info);
} else {
// 先判断接口是否存在
if (magicApiService.existsWithoutId(info.getGroupPrefix(), info.getMethod(), info.getPath(), info.getId())) {
if (magicApiService.existsWithoutId(info.getGroupId(), info.getMethod(), info.getPath(), info.getId())) {
return new JsonBean<>(0, String.format("接口%s:%s已存在", info.getMethod(), info.getPath()));
}
magicApiService.update(info);

View File

@@ -1,7 +1,7 @@
package org.ssssssss.magicapi.dialect;
import org.ssssssss.magicapi.functions.BoundSql;
import org.ssssssss.magicapi.modules.BoundSql;
public class DB2Dialect implements Dialect {
@Override

View File

@@ -1,7 +1,7 @@
package org.ssssssss.magicapi.dialect;
import org.ssssssss.magicapi.functions.BoundSql;
import org.ssssssss.magicapi.modules.BoundSql;
public interface Dialect {

View File

@@ -1,7 +1,7 @@
package org.ssssssss.magicapi.dialect;
import org.ssssssss.magicapi.functions.BoundSql;
import org.ssssssss.magicapi.modules.BoundSql;
public class MySQLDialect implements Dialect {

View File

@@ -1,7 +1,7 @@
package org.ssssssss.magicapi.dialect;
import org.ssssssss.magicapi.functions.BoundSql;
import org.ssssssss.magicapi.modules.BoundSql;
public class OracleDialect implements Dialect {

View File

@@ -1,7 +1,7 @@
package org.ssssssss.magicapi.dialect;
import org.ssssssss.magicapi.functions.BoundSql;
import org.ssssssss.magicapi.modules.BoundSql;
public class PostgreSQLDialect implements Dialect {
@Override

View File

@@ -1,7 +1,7 @@
package org.ssssssss.magicapi.dialect;
import org.apache.commons.lang3.StringUtils;
import org.ssssssss.magicapi.functions.BoundSql;
import org.ssssssss.magicapi.modules.BoundSql;
public class SQLServer2005Dialect implements Dialect {
@Override

View File

@@ -1,7 +1,7 @@
package org.ssssssss.magicapi.dialect;
import org.ssssssss.magicapi.functions.BoundSql;
import org.ssssssss.magicapi.modules.BoundSql;
public class SQLServerDialect implements Dialect {
@Override

View File

@@ -1,5 +1,6 @@
package org.ssssssss.magicapi.config;
package org.ssssssss.magicapi.interceptor;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.script.MagicScriptContext;
import javax.servlet.http.HttpServletRequest;

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.config;
package org.ssssssss.magicapi.model;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -38,14 +38,9 @@ public class ApiInfo {
private String name;
/**
* 接口分组名称
* 分组ID
*/
private String groupName;
/**
* 分组前缀
*/
private String groupPrefix;
private String groupId;
/**
* 设置的请求参数
@@ -107,14 +102,6 @@ public class ApiInfo {
this.name = name;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public String getGroupName() {
return groupName;
}
public String getParameter() {
return parameter;
}
@@ -131,15 +118,14 @@ public class ApiInfo {
this.output = output;
}
public String getGroupPrefix() {
return groupPrefix;
public String getGroupId() {
return groupId;
}
public void setGroupPrefix(String groupPrefix) {
this.groupPrefix = groupPrefix;
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public Map getOptionMap() {
return optionMap;
}
@@ -178,8 +164,7 @@ public class ApiInfo {
Objects.equals(path, apiInfo.path) &&
Objects.equals(script, apiInfo.script) &&
Objects.equals(name, apiInfo.name) &&
Objects.equals(groupName, apiInfo.groupName) &&
Objects.equals(groupPrefix, apiInfo.groupPrefix) &&
Objects.equals(groupId, apiInfo.groupId) &&
Objects.equals(parameter, apiInfo.parameter) &&
Objects.equals(option, apiInfo.option) &&
Objects.equals(output, apiInfo.output);
@@ -187,6 +172,6 @@ public class ApiInfo {
@Override
public int hashCode() {
return Objects.hash(id, method, path, script, name, groupName, groupPrefix, parameter, option, output);
return Objects.hash(id, method, path, script, name, groupId, parameter, option, output);
}
}

View File

@@ -0,0 +1,62 @@
package org.ssssssss.magicapi.model;
public class Group {
private String id;
private String name;
private String type;
private String parentId;
private String path;
public Group(String id, String name) {
this.id = id;
this.name = name;
}
public Group() {
}
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 getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}

View File

@@ -0,0 +1,22 @@
package org.ssssssss.magicapi.model;
public enum Options {
WRAP_REQUEST_PARAMETERS("包装请求参数到一个变量中", "wrap_request_parameter");
private final String name;
private final String value;
Options(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
}

View File

@@ -0,0 +1,84 @@
package org.ssssssss.magicapi.model;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class TreeNode<T> {
private T node;
private List<TreeNode<T>> children = new ArrayList<>();
public TreeNode() {
}
public TreeNode(T node) {
this.node = node;
}
public T getNode() {
return node;
}
public void setNode(T node) {
this.node = node;
}
public List<TreeNode<T>> getChildren() {
return children;
}
public void setChildren(List<TreeNode<T>> children) {
this.children = children;
}
public List<T> findNodes(Function<T, Boolean> mapping) {
return findNodes(this.children, mapping);
}
public T findNode(Function<T, Boolean> mapping) {
return findNode(this.children, mapping);
}
private T findNode(List<TreeNode<T>> childs, Function<T, Boolean> mapping) {
for (TreeNode<T> item : childs) {
if (mapping.apply(item.node)) {
return item.node;
}
T found = findNode(childs, mapping);
if (found != null) {
return found;
}
}
return null;
}
public void moveTo(TreeNode<T> node) {
node.children.add(this);
}
private List<T> findNodes(List<TreeNode<T>> childs, Function<T, Boolean> mapping) {
List<T> nodes = new ArrayList<>();
childs.forEach(item -> {
if (mapping.apply(item.getNode())) {
nodes.add(item.getNode());
}
nodes.addAll(findNodes(item.children, mapping));
});
return nodes;
}
public List<T> flat() {
return flat(this.children);
}
private List<T> flat(List<TreeNode<T>> childs) {
List<T> result = new ArrayList<>();
for (TreeNode<T> item : childs) {
result.add(item.getNode());
result.addAll(flat(childs));
}
return result;
}
}

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import org.apache.commons.lang3.StringUtils;
import org.ssssssss.magicapi.config.MagicModule;
@@ -10,7 +10,7 @@ import java.util.regex.Pattern;
/**
* 断言模块
*/
public class AssertFunctions implements MagicModule {
public class AssertModule implements MagicModule {
/**
* 判断值不能为null

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import org.ssssssss.magicapi.cache.SqlCache;
import org.ssssssss.script.MagicScriptContext;

View File

@@ -1,14 +1,14 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import org.springframework.core.env.Environment;
import org.ssssssss.magicapi.config.MagicModule;
import org.ssssssss.script.annotation.Comment;
public class EnvFunctions implements MagicModule {
public class EnvModule implements MagicModule {
private Environment environment;
public EnvFunctions(Environment environment) {
public EnvModule(Environment environment) {
this.environment = environment;
}

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCursor;

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
@@ -9,11 +9,11 @@ import java.util.HashMap;
/**
* mongo模块
*/
public class MongoFunctions extends HashMap<String,Object> implements MagicModule {
public class MongoModule extends HashMap<String, Object> implements MagicModule {
private MongoClient mongoClient;
public MongoFunctions(MongoClient mongoClient) {
public MongoModule(MongoClient mongoClient) {
this.mongoClient = mongoClient;
}

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
@@ -7,13 +7,12 @@ import org.ssssssss.magicapi.config.MagicModule;
import org.ssssssss.script.functions.DynamicMethod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* redis模块
*/
public class RedisFunctions implements MagicModule, DynamicMethod {
public class RedisModule implements MagicModule, DynamicMethod {
@Override
public String getModuleName() {
@@ -22,7 +21,7 @@ public class RedisFunctions implements MagicModule, DynamicMethod {
private StringRedisTemplate redisTemplate;
public RedisFunctions(RedisConnectionFactory connectionFactory) {
public RedisModule(RedisConnectionFactory connectionFactory) {
this.redisTemplate = new StringRedisTemplate(connectionFactory);
}

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
@@ -15,7 +15,7 @@ import java.util.List;
/**
* request 模块
*/
public class RequestFunctions {
public class RequestModule {
/**
* 获取文件信息

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
@@ -21,11 +21,11 @@ import java.util.Map;
/**
* response模块
*/
public class ResponseFunctions {
public class ResponseModule {
private ResultProvider resultProvider;
public ResponseFunctions(ResultProvider resultProvider) {
public ResponseModule(ResultProvider resultProvider) {
this.resultProvider = resultProvider;
}

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
@@ -31,7 +31,7 @@ import java.util.function.Function;
/**
* 数据库查询模块
*/
public class SQLExecutor extends HashMap<String, SQLExecutor> implements MagicModule {
public class SQLModule extends HashMap<String, SQLModule> implements MagicModule {
@UnableCall
private MagicDynamicDataSource dynamicDataSource;
@@ -66,11 +66,11 @@ public class SQLExecutor extends HashMap<String, SQLExecutor> implements MagicMo
@UnableCall
private long ttl;
public SQLExecutor() {
public SQLModule() {
}
public SQLExecutor(MagicDynamicDataSource dynamicDataSource) {
public SQLModule(MagicDynamicDataSource dynamicDataSource) {
this.dynamicDataSource = dynamicDataSource;
this.dataSourceNode = dynamicDataSource.getDataSource();
}
@@ -127,19 +127,19 @@ public class SQLExecutor extends HashMap<String, SQLExecutor> implements MagicMo
}
@UnableCall
private SQLExecutor cloneSQLExecutor() {
SQLExecutor sqlExecutor = new SQLExecutor();
sqlExecutor.setDynamicDataSource(this.dynamicDataSource);
sqlExecutor.setDataSourceNode(this.dataSourceNode);
sqlExecutor.setPageProvider(this.pageProvider);
sqlExecutor.setColumnMapperProvider(this.columnMapperAdapter);
sqlExecutor.setColumnMapRowMapper(this.columnMapRowMapper);
sqlExecutor.setRowMapColumnMapper(this.rowMapColumnMapper);
sqlExecutor.setSqlCache(this.sqlCache);
sqlExecutor.setTtl(this.ttl);
sqlExecutor.setResultProvider(this.resultProvider);
sqlExecutor.setDialectAdapter(this.dialectAdapter);
return sqlExecutor;
private SQLModule cloneSQLModule() {
SQLModule sqlModule = new SQLModule();
sqlModule.setDynamicDataSource(this.dynamicDataSource);
sqlModule.setDataSourceNode(this.dataSourceNode);
sqlModule.setPageProvider(this.pageProvider);
sqlModule.setColumnMapperProvider(this.columnMapperAdapter);
sqlModule.setColumnMapRowMapper(this.columnMapRowMapper);
sqlModule.setRowMapColumnMapper(this.rowMapColumnMapper);
sqlModule.setSqlCache(this.sqlCache);
sqlModule.setTtl(this.ttl);
sqlModule.setResultProvider(this.resultProvider);
sqlModule.setDialectAdapter(this.dialectAdapter);
return sqlModule;
}
/**
@@ -192,14 +192,14 @@ public class SQLExecutor extends HashMap<String, SQLExecutor> implements MagicMo
* @return
*/
@Comment("使用缓存")
public SQLExecutor cache(@Comment("缓存名") String cacheName, @Comment("过期时间") long ttl) {
public SQLModule cache(@Comment("缓存名") String cacheName, @Comment("过期时间") long ttl) {
if (cacheName == null) {
return this;
}
SQLExecutor query = cloneSQLExecutor();
query.setCacheName(cacheName);
query.setTtl(ttl);
return query;
SQLModule sqlModule = cloneSQLModule();
sqlModule.setCacheName(cacheName);
sqlModule.setTtl(ttl);
return sqlModule;
}
/**
@@ -209,55 +209,55 @@ public class SQLExecutor extends HashMap<String, SQLExecutor> implements MagicMo
* @return
*/
@Comment("使用缓存,过期时间采用默认配置")
public SQLExecutor cache(@Comment("缓存名") String cacheName) {
public SQLModule cache(@Comment("缓存名") String cacheName) {
return cache(cacheName, 0);
}
@Comment("采用驼峰列名")
public SQLExecutor camel() {
public SQLModule camel() {
return columnCase("camel");
}
@Comment("采用帕斯卡列名")
public SQLExecutor pascal() {
public SQLModule pascal() {
return columnCase("pascal");
}
@Comment("采用全小写列名")
public SQLExecutor lower() {
public SQLModule lower() {
return columnCase("lower");
}
@Comment("采用全大写列名")
public SQLExecutor upper() {
public SQLModule upper() {
return columnCase("upper");
}
@Comment("列名保持原样")
public SQLExecutor normal() {
public SQLModule normal() {
return columnCase("default");
}
@Comment("指定列名转换")
public SQLExecutor columnCase(String name) {
SQLExecutor sqlExecutor = cloneSQLExecutor();
sqlExecutor.setColumnMapRowMapper(this.columnMapperAdapter.getColumnMapRowMapper(name));
sqlExecutor.setRowMapColumnMapper(this.columnMapperAdapter.getRowMapColumnMapper(name));
return sqlExecutor;
public SQLModule columnCase(String name) {
SQLModule sqlModule = cloneSQLModule();
sqlModule.setColumnMapRowMapper(this.columnMapperAdapter.getColumnMapRowMapper(name));
sqlModule.setRowMapColumnMapper(this.columnMapperAdapter.getRowMapColumnMapper(name));
return sqlModule;
}
/**
* 数据源切换
*/
@Override
public SQLExecutor get(Object key) {
SQLExecutor sqlExecutor = cloneSQLExecutor();
public SQLModule get(Object key) {
SQLModule sqlModule = cloneSQLModule();
if (key == null) {
sqlExecutor.setDataSourceNode(dynamicDataSource.getDataSource());
sqlModule.setDataSourceNode(dynamicDataSource.getDataSource());
} else {
sqlExecutor.setDataSourceNode(dynamicDataSource.getDataSource(key.toString()));
sqlModule.setDataSourceNode(dynamicDataSource.getDataSource(key.toString()));
}
return sqlExecutor;
return sqlModule;
}

View File

@@ -1,4 +1,4 @@
package org.ssssssss.magicapi.functions;
package org.ssssssss.magicapi.modules;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;

View File

@@ -1,6 +1,6 @@
package org.ssssssss.magicapi.provider;
import org.ssssssss.magicapi.config.ApiInfo;
import org.ssssssss.magicapi.model.ApiInfo;
import java.util.List;
@@ -11,105 +11,109 @@ public interface ApiServiceProvider {
/**
* 删除接口
*
* @param id 接口ID
* @param id 接口ID
*/
boolean delete(String id);
/**
* 根据组名删除接口
*
* @param groupName 分组名称
*/
boolean deleteGroup(String groupName);
/**
* 查询所有接口(提供给页面,无需带script
*
*/
List<ApiInfo> list();
/**
* 查询所有接口内部使用需要带Script
*
*/
List<ApiInfo> listWithScript();
/**
* 查询接口详情(主要给页面使用)
*
* @param id 接口ID
* @param id 接口ID
*/
ApiInfo get(String id);
/**
* 判断接口是否存在
* @param groupPrefix 分组前缀
* @param method 请求方法
* @param path 请求路径
* 移动接口
*
* @param id 接口ID
* @param groupId 分组ID
*/
boolean exists(String groupPrefix, String method, String path);
boolean move(String id, String groupId);
/**
* 修改分组信息
* @param oldGroupName 旧分组名称
* @param groupName 新分组名称
* @param groupPrefix 分组前缀
* 根据组ID删除
*/
boolean updateGroup(String oldGroupName,String groupName, String groupPrefix);
boolean deleteGroup(String groupId);
/**
* 判断接口是否存在
*
* @param groupPrefix 分组前缀
* @param method 请求方法
* @param path 请求路径
* @param id 排除接口
* @param groupId 分组Id
* @param method 请求方法
* @param path 请求路径
*/
boolean existsWithoutId(String groupPrefix, String method, String path, String id);
boolean exists(String groupId, String method, String path);
/**
* 判断接口是否存在
*
* @param groupId 分组ID
* @param method 请求方法
* @param path 请求路径
* @param id 排除接口
*/
boolean existsWithoutId(String groupId, String method, String path, String id);
/**
* 添加接口信息
* @param info 接口信息
*
* @param info 接口信息
*/
boolean insert(ApiInfo info);
/**
* 修改接口信息
* @param info 接口信息
*
* @param info 接口信息
*/
boolean update(ApiInfo info);
/**
* 备份历史记录
*
* @param apiId 接口ID
* @param apiId 接口ID
*/
void backup(String apiId);
/**
* 查询API历史记录
* @param apiId 接口ID
*
* @param apiId 接口ID
* @return 时间戳列表
*/
List<Long> backupList(String apiId);
/**
* 查询API历史记录详情
* @param apiId 接口ID
*
* @param apiId 接口ID
* @param timestamp 时间戳
*/
ApiInfo backupInfo(String apiId, Long timestamp);
/**
* 包装接口信息(可用于加密)
* @param info 接口信息
*
* @param info 接口信息
*/
default void wrap(ApiInfo info) {
}
/**
* 解除包装接口信息(可用于解密)
* @param info 接口信息
*
* @param info 接口信息
*/
default void unwrap(ApiInfo info) {
}

View File

@@ -0,0 +1,49 @@
package org.ssssssss.magicapi.provider;
import org.ssssssss.magicapi.model.Group;
import org.ssssssss.magicapi.model.TreeNode;
import java.util.List;
public interface GroupServiceProvider {
/**
* 添加分组
*/
boolean insert(Group group);
/**
* 修改分组
*/
boolean update(Group group);
/**
* 删除分组
*/
boolean delete(String groupId);
/**
* 是否有该分组
*/
boolean contains(String groupId);
/**
* 接口分组列表
*/
TreeNode<Group> apiGroupList();
/**
* 分组列表
*/
List<Group> groupList();
/**
* 根据分组Id获取分组路径
*/
String getFullPath(String groupId);
/**
* 根据分组Id获取分组名称
*/
String getFullName(String groupId);
}

View File

@@ -2,7 +2,7 @@ package org.ssssssss.magicapi.provider.impl;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.ssssssss.magicapi.config.ApiInfo;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.magicapi.provider.ApiServiceProvider;
import java.util.List;
@@ -12,8 +12,7 @@ public class DefaultApiServiceProvider extends BeanPropertyRowMapper<ApiInfo> im
private final String COMMON_COLUMNS = "id,\n" +
"api_name,\n" +
"api_group_name,\n" +
"api_group_prefix,\n" +
"api_group_id,\n" +
"api_path,\n" +
"api_method";
@@ -34,11 +33,6 @@ public class DefaultApiServiceProvider extends BeanPropertyRowMapper<ApiInfo> im
return template.update(deleteById, id) > 0;
}
public boolean deleteGroup(String groupName) {
String deleteByGroupName = "delete from magic_api_info where api_group_name = ?";
return template.update(deleteByGroupName, groupName) > 0;
}
public List<ApiInfo> list() {
String selectList = "select " + COMMON_COLUMNS + " from magic_api_info order by api_update_time desc";
return template.query(selectList, this);
@@ -62,15 +56,19 @@ public class DefaultApiServiceProvider extends BeanPropertyRowMapper<ApiInfo> im
return info;
}
public boolean exists(String groupPrefix, String method, String path) {
String exists = "select count(*) from magic_api_info where api_method = ? and api_path = ? and api_group_prefix = ?";
return template.queryForObject(exists, Integer.class, method, path, groupPrefix) > 0;
@Override
public boolean move(String id, String groupId) {
return template.update("update magic_api_info SET api_group_id = ? where id = ?", groupId, id) > 0;
}
@Override
public boolean updateGroup(String oldGroupName, String groupName, String groupPrefix) {
String updateGroup = "update magic_api_info set api_group_name = ?,api_group_prefix=?,api_update_time = ? where api_group_name = ?";
return template.update(updateGroup, groupName, groupPrefix, System.currentTimeMillis(), oldGroupName) > 0;
public boolean deleteGroup(String groupId) {
return template.update("delete from magic_api_info where api_group_id = ?", groupId) > 0;
}
public boolean exists(String groupId, String method, String path) {
String exists = "select count(*) from magic_api_info where api_method = ? and api_path = ? and api_group_id = ?";
return template.queryForObject(exists, Integer.class, method, path, groupId) > 0;
}
public boolean existsWithoutId(String groupPrefix, String method, String path, String id) {
@@ -82,14 +80,14 @@ public class DefaultApiServiceProvider extends BeanPropertyRowMapper<ApiInfo> im
info.setId(UUID.randomUUID().toString().replace("-", ""));
wrap(info);
long time = System.currentTimeMillis();
String insert = "insert into magic_api_info(id,api_method,api_path,api_script,api_name,api_group_name,api_parameter,api_option,api_output,api_group_prefix,api_create_time,api_update_time) values(?,?,?,?,?,?,?,?,?,?,?,?)";
return template.update(insert, info.getId(), info.getMethod(), info.getPath(), info.getScript(), info.getName(), info.getGroupName(), info.getParameter(), info.getOption(), info.getOutput(), info.getGroupPrefix(), time, time) > 0;
String insert = "insert into magic_api_info(id,api_method,api_path,api_script,api_name,api_group_id,api_parameter,api_option,api_output,api_create_time,api_update_time) values(?,?,?,?,?,?,?,?,?,?,?,?)";
return template.update(insert, info.getId(), info.getMethod(), info.getPath(), info.getScript(), info.getName(), info.getGroupId(), info.getParameter(), info.getOption(), info.getOutput(), time, time) > 0;
}
public boolean update(ApiInfo info) {
wrap(info);
String update = "update magic_api_info set api_method = ?,api_path = ?,api_script = ?,api_name = ?,api_group_name = ?,api_parameter = ?,api_option = ?,api_output = ?,api_group_prefix = ?,api_update_time = ? where id = ?";
return template.update(update, info.getMethod(), info.getPath(), info.getScript(), info.getName(), info.getGroupName(), info.getParameter(), info.getOption(), info.getOutput(), info.getGroupPrefix(), System.currentTimeMillis(), info.getId()) > 0;
String update = "update magic_api_info set api_method = ?,api_path = ?,api_script = ?,api_name = ?,api_group_id = ?,api_parameter = ?,api_option = ?,api_output = ?,api_update_time = ? where id = ?";
return template.update(update, info.getMethod(), info.getPath(), info.getScript(), info.getName(), info.getGroupId(), info.getParameter(), info.getOption(), info.getOutput(), System.currentTimeMillis(), info.getId()) > 0;
}
@Override
@@ -98,6 +96,7 @@ public class DefaultApiServiceProvider extends BeanPropertyRowMapper<ApiInfo> im
template.update(backupSql, apiId);
}
@Override
public List<Long> backupList(String apiId) {
return template.queryForList("select api_update_time from magic_api_info_his where id = ? order by api_update_time desc", Long.class, apiId);
@@ -107,7 +106,7 @@ public class DefaultApiServiceProvider extends BeanPropertyRowMapper<ApiInfo> im
public ApiInfo backupInfo(String apiId, Long timestamp) {
String selectOne = "select " + COMMON_COLUMNS + "," + SCRIPT_COLUMNS + " from magic_api_info_his where id = ? and api_update_time = ?";
List<ApiInfo> list = template.query(selectOne, this, apiId, timestamp);
if(list != null && !list.isEmpty()){
if (list != null && !list.isEmpty()) {
ApiInfo info = list.get(0);
unwrap(info);
return info;
@@ -117,6 +116,6 @@ public class DefaultApiServiceProvider extends BeanPropertyRowMapper<ApiInfo> im
@Override
protected String lowerCaseName(String name) {
return super.lowerCaseName(name).replace("api_","");
return super.lowerCaseName(name).replace("api_", "");
}
}

View File

@@ -0,0 +1,111 @@
package org.ssssssss.magicapi.provider.impl;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.ssssssss.magicapi.model.Group;
import org.ssssssss.magicapi.model.TreeNode;
import org.ssssssss.magicapi.provider.GroupServiceProvider;
import org.ssssssss.magicapi.utils.PathUtils;
import java.util.*;
import java.util.stream.Collectors;
public class DefaultGroupServiceProvider extends BeanPropertyRowMapper<Group> implements GroupServiceProvider {
private JdbcTemplate template;
private Map<String, Group> cacheTree = new HashMap<>();
public DefaultGroupServiceProvider(JdbcTemplate template) {
super(Group.class);
this.template = template;
}
@Override
public boolean insert(Group group) {
group.setId(UUID.randomUUID().toString().replace("-", ""));
String insertGroup = "insert into magic_group(id,group_name,group_type,group_path,parent_id) values(?,?,?,?,?)";
return template.update(insertGroup, group.getId(), group.getName(), group.getType(), group.getPath(), group.getParentId()) > 0;
}
@Override
public boolean update(Group group) {
String updateGroup = "update magic_group set group_name = ?,group_path=?,parent_id = ? where id = ?";
return template.update(updateGroup, group.getName(), group.getPath(), group.getParentId(), group.getId()) > 0;
}
@Override
public boolean delete(String groupId) {
String deleteByGroupId = "delete from magic_api_info where api_group_id = ?";
return template.update(deleteByGroupId, groupId) > 0;
}
@Override
public boolean contains(String groupId) {
return "0".equals(groupId) || cacheTree.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;
}
@Override
public List<Group> groupList() {
return template.query("select * from magic_group",this);
}
@Override
public String getFullPath(String groupId) {
StringBuilder path = new StringBuilder();
Group group;
while ((group = cacheTree.get(groupId)) != null) {
path.insert(0, '/' + Objects.toString(group.getPath(), ""));
groupId = group.getParentId();
}
// 需要找到根节点,否则说明中间被删除了
if (!"0".equals(groupId)) {
return null;
}
return PathUtils.replaceSlash(path.toString());
}
@Override
public String getFullName(String groupId) {
if (groupId == null || "0".equals(groupId)) {
return "";
}
StringBuilder name = new StringBuilder();
Group group;
while ((group = cacheTree.get(groupId)) != null) {
name.insert(0, '/' + group.getName());
groupId = group.getParentId();
}
// 需要找到根节点,否则说明中间被删除了
if (!"0".equals(groupId)) {
return null;
}
return name.substring(1);
}
private void convertToTree(List<Group> groups, TreeNode<Group> current) {
List<TreeNode<Group>> treeNodes = groups.stream()
.filter(it -> current.getNode().getId().equals(it.getParentId()))
.map(TreeNode::new)
.collect(Collectors.toList());
current.setChildren(treeNodes);
treeNodes.forEach(it -> convertToTree(groups, it));
}
@Override
protected String lowerCaseName(String name) {
return super.lowerCaseName(name).replace("group_", "");
}
}

View File

@@ -1,8 +1,8 @@
package org.ssssssss.magicapi.provider.impl;
import org.ssssssss.magicapi.config.ApiInfo;
import org.ssssssss.magicapi.config.MappingHandlerMapping;
import org.ssssssss.magicapi.exception.MagicServiceException;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.magicapi.provider.MagicAPIService;
import org.ssssssss.magicapi.provider.ResultProvider;
import org.ssssssss.magicapi.script.ScriptManager;

View File

@@ -2,8 +2,9 @@ package org.ssssssss.magicapi.swagger;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.ResponseBody;
import org.ssssssss.magicapi.config.ApiInfo;
import org.ssssssss.magicapi.config.MappingHandlerMapping;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.magicapi.provider.GroupServiceProvider;
import java.io.IOException;
import java.util.List;
@@ -33,10 +34,16 @@ public class SwaggerProvider {
*/
private String version;
private GroupServiceProvider groupServiceProvider;
public void setMappingHandlerMapping(MappingHandlerMapping mappingHandlerMapping) {
this.mappingHandlerMapping = mappingHandlerMapping;
}
public void setGroupServiceProvider(GroupServiceProvider groupServiceProvider) {
this.groupServiceProvider = groupServiceProvider;
}
public void setDescription(String description) {
this.description = description;
}
@@ -50,47 +57,48 @@ public class SwaggerProvider {
}
@ResponseBody
public SwaggerEntity swaggerJson(){
public SwaggerEntity swaggerJson() {
List<ApiInfo> infos = mappingHandlerMapping.getApiInfos();
SwaggerEntity swaggerEntity = new SwaggerEntity();
SwaggerEntity.License license = new SwaggerEntity.License("MIT", "https://gitee.com/ssssssss-team/magic-api/blob/master/LICENSE");
swaggerEntity.setInfo(new SwaggerEntity.Info(this.description, this.version, this.title, license));
ObjectMapper mapper = new ObjectMapper();
for (ApiInfo info : infos) {
swaggerEntity.addTag(info.getGroupName(),info.getGroupPrefix());
String groupName = groupServiceProvider.getFullName(info.getGroupId());
swaggerEntity.addTag(groupName, groupServiceProvider.getFullPath(info.getGroupId()));
SwaggerEntity.Path path = new SwaggerEntity.Path();
path.addTag(info.getGroupName());
path.addTag(groupName);
try {
path.addResponse("200",mapper.readValue(Objects.toString(info.getOutput(),"{}"),Object.class));
path.addResponse("200", mapper.readValue(Objects.toString(info.getOutput(), "{}"), Object.class));
} catch (IOException ignored) {
}
path.addConsume("*/*");
path.addProduce("application/json");
path.setSummary(info.getName());
try {
Map map = mapper.readValue(Objects.toString(info.getParameter(),"{}"),Map.class);
Map map = mapper.readValue(Objects.toString(info.getParameter(), "{}"), Map.class);
Object request = map.get("request");
if(request instanceof Map){
if (request instanceof Map) {
Map requestMap = (Map) request;
Set keys = requestMap.keySet();
for (Object key : keys) {
path.addParameter(new SwaggerEntity.Parameter(key.toString(),"query","string", requestMap.getOrDefault(key,"")));
path.addParameter(new SwaggerEntity.Parameter(key.toString(), "query", "string", requestMap.getOrDefault(key, "")));
}
}
Object header = map.get("header");
if(header instanceof Map){
if (header instanceof Map) {
Map headers = (Map) header;
Set keys = headers.keySet();
for (Object key : keys) {
path.addParameter(new SwaggerEntity.Parameter(key.toString(),"header","string", headers.getOrDefault(key,"")));
path.addParameter(new SwaggerEntity.Parameter(key.toString(), "header", "string", headers.getOrDefault(key, "")));
}
}
if(map.containsKey("body")){
path.addParameter(new SwaggerEntity.Parameter("body","body",null,map.get("body")));
if (map.containsKey("body")) {
path.addParameter(new SwaggerEntity.Parameter("body", "body", null, map.get("body")));
}
} catch (IOException ignored) {
}
swaggerEntity.addPath(mappingHandlerMapping.getRequestPath(info.getGroupPrefix(),info.getPath()),info.getMethod(),path);
swaggerEntity.addPath(mappingHandlerMapping.getRequestPath(info.getGroupId(), info.getPath()), info.getMethod(), path);
}
return swaggerEntity;
}

View File

@@ -0,0 +1,15 @@
package org.ssssssss.magicapi.utils;
import java.util.regex.Pattern;
public class PathUtils {
private static final Pattern REPLACE_SLASH_REGX = Pattern.compile("/+");
/**
* 将多个/替换为一个/
*/
public static String replaceSlash(String path) {
return REPLACE_SLASH_REGX.matcher(path).replaceAll("/");
}
}