Эх сурвалжийг харах

上传、备份还原、注册日志

mxd 3 жил өмнө
parent
commit
505fcaf52d
20 өөрчлөгдсөн 273 нэмэгдсэн , 73 устгасан
  1. 1 1
      magic-api/src/main/java/org/ssssssss/magicapi/adapter/resource/ZipResource.java
  2. 44 15
      magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicBackupController.java
  3. 1 2
      magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicWorkbenchController.java
  4. 1 1
      magic-api/src/main/java/org/ssssssss/magicapi/event/EventAction.java
  5. 2 0
      magic-api/src/main/java/org/ssssssss/magicapi/model/JsonCodeConstants.java
  6. 1 1
      magic-api/src/main/java/org/ssssssss/magicapi/provider/MagicAPIService.java
  7. 3 0
      magic-api/src/main/java/org/ssssssss/magicapi/provider/MagicBackupService.java
  8. 6 1
      magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/DefaultMagicAPIService.java
  9. 16 0
      magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/MagicDatabaseBackupService.java
  10. 14 11
      magic-api/src/main/java/org/ssssssss/magicapi/service/AbstractMagicDynamicRegistry.java
  11. 0 5
      magic-api/src/main/java/org/ssssssss/magicapi/service/MagicDynamicRegistry.java
  12. 3 0
      magic-api/src/main/java/org/ssssssss/magicapi/service/MagicResourceService.java
  13. 6 0
      magic-api/src/main/java/org/ssssssss/magicapi/service/MagicSynchronizationService.java
  14. 6 0
      magic-api/src/main/java/org/ssssssss/magicapi/service/impl/AbstractPathMagicResourceStorage.java
  15. 2 2
      magic-api/src/main/java/org/ssssssss/magicapi/service/impl/ApiInfoMagicResourceStorage.java
  16. 6 0
      magic-api/src/main/java/org/ssssssss/magicapi/service/impl/DataSourceMagicDynamicRegistry.java
  17. 137 32
      magic-api/src/main/java/org/ssssssss/magicapi/service/impl/DefaultMagicResourceService.java
  18. 1 2
      magic-api/src/main/java/org/ssssssss/magicapi/service/impl/FunctionInfoMagicResourceStorage.java
  19. 14 0
      magic-api/src/main/java/org/ssssssss/magicapi/service/impl/FunctionMagicDynamicRegistry.java
  20. 9 0
      magic-api/src/main/java/org/ssssssss/magicapi/utils/IoUtils.java

+ 1 - 1
magic-api/src/main/java/org/ssssssss/magicapi/adapter/resource/ZipResource.java

@@ -82,7 +82,7 @@ public class ZipResource implements Resource {
 	public String name() {
 	public String name() {
 		String name = this.path;
 		String name = this.path;
 		if (isDirectory()) {
 		if (isDirectory()) {
-			name = this.path.substring(0, name.length() - 1);
+			name = this.path.length() > 0 ? this.path.substring(0, name.length() - 1) : "";
 		}
 		}
 		int index = name.lastIndexOf("/");
 		int index = name.lastIndexOf("/");
 		return index > -1 ? name.substring(index + 1) : name;
 		return index > -1 ? name.substring(index + 1) : name;

+ 44 - 15
magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicBackupController.java

@@ -4,15 +4,17 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.ssssssss.magicapi.config.MagicConfiguration;
 import org.ssssssss.magicapi.config.MagicConfiguration;
-import org.ssssssss.magicapi.model.Backup;
-import org.ssssssss.magicapi.model.JsonBean;
+import org.ssssssss.magicapi.model.*;
 import org.ssssssss.magicapi.provider.MagicBackupService;
 import org.ssssssss.magicapi.provider.MagicBackupService;
+import org.ssssssss.magicapi.service.MagicDynamicRegistry;
+import org.ssssssss.magicapi.utils.JsonUtils;
+import org.ssssssss.magicapi.utils.WebUtils;
 
 
-import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.util.List;
 import java.util.List;
 
 
-public class MagicBackupController extends MagicController {
+public class MagicBackupController extends MagicController implements MagicExceptionHandler{
 
 
 	private final MagicBackupService service;
 	private final MagicBackupService service;
 
 
@@ -27,18 +29,45 @@ public class MagicBackupController extends MagicController {
 		return new JsonBean<>(service.backupList(timestamp == null ? System.currentTimeMillis() : timestamp));
 		return new JsonBean<>(service.backupList(timestamp == null ? System.currentTimeMillis() : timestamp));
 	}
 	}
 
 
-	@PostMapping("/backup")
+	@GetMapping("/backup/rollback")
 	@ResponseBody
 	@ResponseBody
-	public JsonBean<Boolean> doBackup(String tag) throws IOException {
-		ByteArrayOutputStream baos = new ByteArrayOutputStream();
-		MagicConfiguration.getMagicResourceService().export(null, null, baos);
-		Backup backup = new Backup();
-		backup.setId("full");
-		backup.setType("full");
-		backup.setName("全量备份");
-		backup.setContent(baos.toByteArray());
-		backup.setTag(tag);
-		service.doBackup(backup);
+	public JsonBean<Boolean> rollback(String id, Long timestamp) throws IOException {
+		Backup backup = service.backupInfo(id, timestamp);
+		if("full".equals(id)){
+			service.doBackupAll("还原全量备份前,系统自动全量备份", WebUtils.currentUserName());
+			configuration.getMagicAPIService().upload(new ByteArrayInputStream(backup.getContent()), Constants.UPLOAD_MODE_FULL);
+			return new JsonBean<>(true);
+		}
+		if(backup.getType().endsWith("-group")){
+			Group group = JsonUtils.readValue(backup.getContent(), Group.class);
+			return new JsonBean<>(MagicConfiguration.getMagicResourceService().saveGroup(group));
+		}
+		MagicEntity entity = configuration.getMagicDynamicRegistries().stream()
+				.map(MagicDynamicRegistry::getMagicResourceStorage)
+				.filter(it -> it.folder().equals(backup.getType()))
+				.map(it -> it.read(backup.getContent()))
+				.findFirst()
+				.orElse(null);
+		if(entity != null){
+			return new JsonBean<>(MagicConfiguration.getMagicResourceService().saveFile(entity));
+		}
+		return new JsonBean<>(false);
+	}
+
+	@GetMapping("/backup")
+	@ResponseBody
+	public JsonBean<String> backup(Long timestamp, String id) {
+		notBlank(id, PARAMETER_INVALID);
+		notNull(timestamp, PARAMETER_INVALID);
+		Backup backup = service.backupInfo(id, timestamp);
+		MagicEntity entity = JsonUtils.readValue(backup.getContent(), MagicEntity.class);
+		return new JsonBean<>(entity == null ? null : entity.getScript());
+	}
+
+	@PostMapping("/backup/full")
+	@ResponseBody
+	public JsonBean<Boolean> doBackup() throws IOException {
+		service.doBackupAll("主动全量备份", WebUtils.currentUserName());
 		return new JsonBean<>(true);
 		return new JsonBean<>(true);
 	}
 	}
 }
 }

+ 1 - 2
magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicWorkbenchController.java

@@ -244,8 +244,7 @@ public class MagicWorkbenchController extends MagicController implements MagicEx
 	@ResponseBody
 	@ResponseBody
 	public JsonBean<Boolean> upload(MultipartFile file, String mode) throws IOException {
 	public JsonBean<Boolean> upload(MultipartFile file, String mode) throws IOException {
 		notNull(file, FILE_IS_REQUIRED);
 		notNull(file, FILE_IS_REQUIRED);
-		magicAPIService.upload(file.getInputStream(), mode);
-		return new JsonBean<>(SUCCESS, true);
+		return new JsonBean<>(magicAPIService.upload(file.getInputStream(), mode));
 	}
 	}
 
 
 	@RequestMapping("/push")
 	@RequestMapping("/push")

+ 1 - 1
magic-api/src/main/java/org/ssssssss/magicapi/event/EventAction.java

@@ -2,5 +2,5 @@ package org.ssssssss.magicapi.event;
 
 
 public enum EventAction {
 public enum EventAction {
 
 
-	CREATE, SAVE, DELETE, MOVE, LOAD, WS_C_S, WS_S_C, RELOAD
+	CREATE, SAVE, DELETE, MOVE, LOAD, WS_C_S, WS_S_C, RELOAD, CLEAR
 }
 }

+ 2 - 0
magic-api/src/main/java/org/ssssssss/magicapi/model/JsonCodeConstants.java

@@ -31,6 +31,8 @@ public interface JsonCodeConstants {
 
 
 	JsonCode PATH_CONFLICT = new JsonCode(0, "该路径已被使用,请换一个路径在试");
 	JsonCode PATH_CONFLICT = new JsonCode(0, "该路径已被使用,请换一个路径在试");
 
 
+	JsonCode RESOURCE_PATH_CONFLICT = new JsonCode(0, "资源中[%s]有冲突,请检查");
+
 	JsonCode MOVE_PATH_CONFLICT = new JsonCode(0, "移动后路径会冲突,请换一个路径在试");
 	JsonCode MOVE_PATH_CONFLICT = new JsonCode(0, "移动后路径会冲突,请换一个路径在试");
 
 
 	JsonCode FUNCTION_PATH_CONFLICT = new JsonCode(0, "该路径已被映射,请换一个请求方法或路径");
 	JsonCode FUNCTION_PATH_CONFLICT = new JsonCode(0, "该路径已被映射,请换一个请求方法或路径");

+ 1 - 1
magic-api/src/main/java/org/ssssssss/magicapi/provider/MagicAPIService.java

@@ -83,7 +83,7 @@ public interface MagicAPIService extends MagicModule {
 	/**
 	/**
 	 * 上传
 	 * 上传
 	 */
 	 */
-	void upload(InputStream inputStream, String mode) throws IOException;
+	boolean upload(InputStream inputStream, String mode) throws IOException;
 
 
 	/**
 	/**
 	 * 下载
 	 * 下载

+ 3 - 0
magic-api/src/main/java/org/ssssssss/magicapi/provider/MagicBackupService.java

@@ -2,6 +2,7 @@ package org.ssssssss.magicapi.provider;
 
 
 import org.ssssssss.magicapi.model.Backup;
 import org.ssssssss.magicapi.model.Backup;
 
 
+import java.io.IOException;
 import java.util.List;
 import java.util.List;
 
 
 /**
 /**
@@ -20,6 +21,8 @@ public interface MagicBackupService {
 	 */
 	 */
 	void doBackup(Backup backup);
 	void doBackup(Backup backup);
 
 
+	void doBackupAll(String name, String createBy) throws IOException;
+
 	/**
 	/**
 	 * 根据时间戳查询最近的 FETCH_SIZE 条记录
 	 * 根据时间戳查询最近的 FETCH_SIZE 条记录
 	 *
 	 *

+ 6 - 1
magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/DefaultMagicAPIService.java

@@ -12,6 +12,8 @@ import org.springframework.util.MultiValueMap;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.client.RestTemplate;
 import org.ssssssss.magicapi.config.WebSocketSessionManager;
 import org.ssssssss.magicapi.config.WebSocketSessionManager;
 import org.ssssssss.magicapi.controller.MagicWebSocketDispatcher;
 import org.ssssssss.magicapi.controller.MagicWebSocketDispatcher;
+import org.ssssssss.magicapi.event.EventAction;
+import org.ssssssss.magicapi.event.MagicEvent;
 import org.ssssssss.magicapi.exception.MagicResourceNotFoundException;
 import org.ssssssss.magicapi.exception.MagicResourceNotFoundException;
 import org.ssssssss.magicapi.model.*;
 import org.ssssssss.magicapi.model.*;
 import org.ssssssss.magicapi.provider.MagicAPIService;
 import org.ssssssss.magicapi.provider.MagicAPIService;
@@ -132,7 +134,8 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
 	}
 	}
 
 
 	@Override
 	@Override
-	public void upload(InputStream inputStream, String mode) throws IOException {
+	public boolean upload(InputStream inputStream, String mode) throws IOException {
+		return resourceService.upload(inputStream, Constants.UPLOAD_MODE_FULL.equals(mode));
 	}
 	}
 
 
 	@Override
 	@Override
@@ -184,6 +187,8 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
 				return processWebSocketMessageReceived(magicNotify.getSessionId(), magicNotify.getContent());
 				return processWebSocketMessageReceived(magicNotify.getSessionId(), magicNotify.getContent());
 			case WS_S_C:
 			case WS_S_C:
 				return processWebSocketSendMessage(magicNotify.getSessionId(), magicNotify.getContent());
 				return processWebSocketSendMessage(magicNotify.getSessionId(), magicNotify.getContent());
+			case CLEAR:
+				publisher.publishEvent(new MagicEvent("clear", EventAction.CLEAR, Constants.EVENT_SOURCE_NOTIFY));
 		}
 		}
 		return resourceService.processNotify(magicNotify);
 		return resourceService.processNotify(magicNotify);
 	}
 	}

+ 16 - 0
magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/MagicDatabaseBackupService.java

@@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.context.event.EventListener;
 import org.springframework.context.event.EventListener;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.ssssssss.magicapi.config.MagicConfiguration;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
 import org.ssssssss.magicapi.model.Backup;
 import org.ssssssss.magicapi.model.Backup;
@@ -14,6 +15,8 @@ import org.ssssssss.magicapi.provider.MagicBackupService;
 import org.ssssssss.magicapi.utils.JsonUtils;
 import org.ssssssss.magicapi.utils.JsonUtils;
 import org.ssssssss.magicapi.utils.WebUtils;
 import org.ssssssss.magicapi.utils.WebUtils;
 
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.util.List;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.Stream;
@@ -58,6 +61,19 @@ public class MagicDatabaseBackupService implements MagicBackupService {
 		this.FIND_BY_ID_AND_TIMESTAMP = String.format("select * from %s where id = ? and create_date = ?", tableName);
 		this.FIND_BY_ID_AND_TIMESTAMP = String.format("select * from %s where id = ? and create_date = ?", tableName);
 	}
 	}
 
 
+	@Override
+	public void doBackupAll(String name, String createBy) throws IOException {
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		MagicConfiguration.getMagicResourceService().export(null, null, baos);
+		Backup backup = new Backup();
+		backup.setId("full");
+		backup.setType("full");
+		backup.setName(name);
+		backup.setCreateBy(createBy);
+		backup.setContent(baos.toByteArray());
+		doBackup(backup);
+	}
+
 	@Override
 	@Override
 	public void doBackup(Backup backup) {
 	public void doBackup(Backup backup) {
 		try {
 		try {

+ 14 - 11
magic-api/src/main/java/org/ssssssss/magicapi/service/AbstractMagicDynamicRegistry.java

@@ -1,13 +1,15 @@
 package org.ssssssss.magicapi.service;
 package org.ssssssss.magicapi.service;
 
 
+import org.springframework.context.event.EventListener;
 import org.ssssssss.magicapi.event.EventAction;
 import org.ssssssss.magicapi.event.EventAction;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
+import org.ssssssss.magicapi.event.MagicEvent;
 import org.ssssssss.magicapi.exception.MagicAPIException;
 import org.ssssssss.magicapi.exception.MagicAPIException;
 import org.ssssssss.magicapi.model.MagicEntity;
 import org.ssssssss.magicapi.model.MagicEntity;
 import org.ssssssss.magicapi.provider.MagicResourceStorage;
 import org.ssssssss.magicapi.provider.MagicResourceStorage;
 
 
-import java.util.List;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentHashMap;
@@ -55,6 +57,17 @@ public abstract class AbstractMagicDynamicRegistry<T extends MagicEntity> implem
 		return false;
 		return false;
 	}
 	}
 
 
+	@EventListener(condition = "#event.action == T(org.ssssssss.magicapi.event.EventAction).CLEAR")
+	public void clear(MagicEvent event){
+		Iterator<Map.Entry<String, MappingNode<T>>> iterator = mappings.entrySet().iterator();
+		while (iterator.hasNext()){
+			Map.Entry<String, MappingNode<T>> entry = iterator.next();
+			unregister(entry.getValue());
+			iterator.remove();
+		}
+
+	}
+
 	protected void processEvent(FileEvent event) {
 	protected void processEvent(FileEvent event) {
 		T info = (T) event.getEntity();
 		T info = (T) event.getEntity();
 		if (event.getAction() == EventAction.DELETE) {
 		if (event.getAction() == EventAction.DELETE) {
@@ -102,16 +115,6 @@ public abstract class AbstractMagicDynamicRegistry<T extends MagicEntity> implem
 
 
 	}
 	}
 
 
-	public boolean register(List<T> entities) {
-		mappings.values().stream().distinct().forEach(node -> {
-			unregister(node);
-			mappings.remove(node.getMappingKey());
-			mappings.remove(node.getEntity().getId());
-		});
-		entities.forEach(this::register);
-		return true;
-	}
-
 	protected MappingNode<T> buildMappingNode(T entity) {
 	protected MappingNode<T> buildMappingNode(T entity) {
 		MappingNode<T> mappingNode = new MappingNode<>(entity);
 		MappingNode<T> mappingNode = new MappingNode<>(entity);
 		mappingNode.setMappingKey(this.magicResourceStorage.buildKey(entity));
 		mappingNode.setMappingKey(this.magicResourceStorage.buildKey(entity));

+ 0 - 5
magic-api/src/main/java/org/ssssssss/magicapi/service/MagicDynamicRegistry.java

@@ -12,11 +12,6 @@ public interface MagicDynamicRegistry<T extends MagicEntity> {
 	 */
 	 */
 	boolean register(T entity);
 	boolean register(T entity);
 
 
-	/**
-	 * 注册全部
-	 */
-	boolean register(List<T> entities);
-
 	/**
 	/**
 	 * 取消注册
 	 * 取消注册
 	 */
 	 */

+ 3 - 0
magic-api/src/main/java/org/ssssssss/magicapi/service/MagicResourceService.java

@@ -4,6 +4,7 @@ import org.ssssssss.magicapi.adapter.Resource;
 import org.ssssssss.magicapi.model.*;
 import org.ssssssss.magicapi.model.*;
 
 
 import java.io.IOException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.OutputStream;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -98,6 +99,8 @@ public interface MagicResourceService {
 
 
 	boolean unlock(String id);
 	boolean unlock(String id);
 
 
+	boolean upload(InputStream inputStream, boolean full) throws IOException;
+
 
 
 	public String getGroupPath(String groupId);
 	public String getGroupPath(String groupId);
 
 

+ 6 - 0
magic-api/src/main/java/org/ssssssss/magicapi/service/MagicSynchronizationService.java

@@ -3,6 +3,7 @@ package org.ssssssss.magicapi.service;
 import org.springframework.context.event.EventListener;
 import org.springframework.context.event.EventListener;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
+import org.ssssssss.magicapi.event.MagicEvent;
 import org.ssssssss.magicapi.model.Constants;
 import org.ssssssss.magicapi.model.Constants;
 import org.ssssssss.magicapi.model.MagicNotify;
 import org.ssssssss.magicapi.model.MagicNotify;
 import org.ssssssss.magicapi.provider.MagicNotifyService;
 import org.ssssssss.magicapi.provider.MagicNotifyService;
@@ -47,4 +48,9 @@ public class MagicSynchronizationService {
 				break;
 				break;
 		}
 		}
 	}
 	}
+
+	@EventListener(condition = "#event.action == T(org.ssssssss.magicapi.event.EventAction).CLEAR && #event.source != T(org.ssssssss.magicapi.model.Constants).EVENT_SOURCE_NOTIFY")
+	public void onClearEvent(MagicEvent event){
+		magicNotifyService.sendNotify(new MagicNotify(instanceId, null, event.getAction(), null));
+	}
 }
 }

+ 6 - 0
magic-api/src/main/java/org/ssssssss/magicapi/service/impl/AbstractPathMagicResourceStorage.java

@@ -5,6 +5,8 @@ import org.ssssssss.magicapi.provider.MagicResourceStorage;
 import org.ssssssss.magicapi.service.MagicResourceService;
 import org.ssssssss.magicapi.service.MagicResourceService;
 import org.ssssssss.magicapi.utils.PathUtils;
 import org.ssssssss.magicapi.utils.PathUtils;
 
 
+import java.util.Objects;
+
 public abstract class AbstractPathMagicResourceStorage<T extends PathMagicEntity> implements MagicResourceStorage<T> {
 public abstract class AbstractPathMagicResourceStorage<T extends PathMagicEntity> implements MagicResourceStorage<T> {
 
 
 
 
@@ -25,6 +27,10 @@ public abstract class AbstractPathMagicResourceStorage<T extends PathMagicEntity
 		this.magicResourceService = magicResourceService;
 		this.magicResourceService = magicResourceService;
 	}
 	}
 
 
+	public String buildMappingKey(T entity, String path) {
+		return PathUtils.replaceSlash("/" + Objects.toString(path, "") + "/"+ Objects.toString(entity.getPath(), ""));
+	}
+
 	@Override
 	@Override
 	public String buildScriptName(T entity) {
 	public String buildScriptName(T entity) {
 		String fullGroupName = magicResourceService.getGroupName(entity.getGroupId());
 		String fullGroupName = magicResourceService.getGroupName(entity.getGroupId());

+ 2 - 2
magic-api/src/main/java/org/ssssssss/magicapi/service/impl/ApiInfoMagicResourceStorage.java

@@ -1,7 +1,6 @@
 package org.ssssssss.magicapi.service.impl;
 package org.ssssssss.magicapi.service.impl;
 
 
 import org.ssssssss.magicapi.model.ApiInfo;
 import org.ssssssss.magicapi.model.ApiInfo;
-import org.ssssssss.magicapi.utils.PathUtils;
 
 
 public class ApiInfoMagicResourceStorage extends AbstractPathMagicResourceStorage<ApiInfo> {
 public class ApiInfoMagicResourceStorage extends AbstractPathMagicResourceStorage<ApiInfo> {
 
 
@@ -17,6 +16,7 @@ public class ApiInfoMagicResourceStorage extends AbstractPathMagicResourceStorag
 
 
 	@Override
 	@Override
 	public String buildMappingKey(ApiInfo info) {
 	public String buildMappingKey(ApiInfo info) {
-		return info.getMethod().toUpperCase() + ":" + PathUtils.replaceSlash("/" + magicResourceService.getGroupPath(info.getGroupId()) + "/" + info.getPath());
+		return info.getMethod().toUpperCase() + ":" + buildMappingKey(info, magicResourceService.getGroupPath(info.getGroupId()));
 	}
 	}
+
 }
 }

+ 6 - 0
magic-api/src/main/java/org/ssssssss/magicapi/service/impl/DataSourceMagicDynamicRegistry.java

@@ -1,6 +1,8 @@
 package org.ssssssss.magicapi.service.impl;
 package org.ssssssss.magicapi.service.impl;
 
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.boot.context.properties.bind.Bindable;
 import org.springframework.boot.context.properties.bind.Bindable;
 import org.springframework.boot.context.properties.bind.Binder;
 import org.springframework.boot.context.properties.bind.Binder;
@@ -25,6 +27,8 @@ public class DataSourceMagicDynamicRegistry extends AbstractMagicDynamicRegistry
 
 
 	private final MagicDynamicDataSource magicDynamicDataSource;
 	private final MagicDynamicDataSource magicDynamicDataSource;
 
 
+	private static final Logger logger = LoggerFactory.getLogger(DataSourceMagicDynamicRegistry.class);
+
 	private static final ClassLoader CLASSLOADER = DataSourceMagicDynamicRegistry.class.getClassLoader();
 	private static final ClassLoader CLASSLOADER = DataSourceMagicDynamicRegistry.class.getClassLoader();
 
 
 	// copy from DataSourceBuilder
 	// copy from DataSourceBuilder
@@ -57,12 +61,14 @@ public class DataSourceMagicDynamicRegistry extends AbstractMagicDynamicRegistry
 		}
 		}
 		DataSource datasource = createDataSource(getDataSourceType(info.getType()), properties);
 		DataSource datasource = createDataSource(getDataSourceType(info.getType()), properties);
 		magicDynamicDataSource.put(info.getId(), info.getKey(), info.getName(), datasource, info.getMaxRows());
 		magicDynamicDataSource.put(info.getId(), info.getKey(), info.getName(), datasource, info.getMaxRows());
+		logger.debug("注册数据源:{}", info.getKey());
 		return true;
 		return true;
 	}
 	}
 
 
 
 
 	@Override
 	@Override
 	public boolean unregister(DataSourceInfo info) {
 	public boolean unregister(DataSourceInfo info) {
+		logger.debug("取消注册数据源:{}", info.getKey());
 		return magicDynamicDataSource.delete(info.getKey());
 		return magicDynamicDataSource.delete(info.getKey());
 	}
 	}
 
 

+ 137 - 32
magic-api/src/main/java/org/ssssssss/magicapi/service/impl/DefaultMagicResourceService.java

@@ -5,9 +5,11 @@ import org.springframework.boot.context.event.ApplicationStartedEvent;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationListener;
 import org.springframework.context.ApplicationListener;
 import org.ssssssss.magicapi.adapter.Resource;
 import org.ssssssss.magicapi.adapter.Resource;
+import org.ssssssss.magicapi.adapter.resource.ZipResource;
 import org.ssssssss.magicapi.event.EventAction;
 import org.ssssssss.magicapi.event.EventAction;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
+import org.ssssssss.magicapi.event.MagicEvent;
 import org.ssssssss.magicapi.exception.InvalidArgumentException;
 import org.ssssssss.magicapi.exception.InvalidArgumentException;
 import org.ssssssss.magicapi.model.*;
 import org.ssssssss.magicapi.model.*;
 import org.ssssssss.magicapi.provider.MagicResourceStorage;
 import org.ssssssss.magicapi.provider.MagicResourceStorage;
@@ -17,6 +19,7 @@ import org.ssssssss.magicapi.utils.JsonUtils;
 import org.ssssssss.magicapi.utils.WebUtils;
 import org.ssssssss.magicapi.utils.WebUtils;
 
 
 import java.io.IOException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.OutputStream;
 import java.util.*;
 import java.util.*;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReadWriteLock;
@@ -96,34 +99,37 @@ public class DefaultMagicResourceService implements MagicResourceService, JsonCo
 		return false;
 		return false;
 	}
 	}
 
 
+	private void init(){
+		groupMappings.clear();
+		groupCache.clear();
+		fileMappings.clear();
+		fileCache.clear();
+		pathCache.clear();
+		storages.forEach((key, registry) -> {
+			if (registry.requirePath()) {
+				pathCache.put(registry.folder(), new HashMap<>(32));
+			}
+			Resource folder = root.getDirectory(key);
+			if (registry.allowRoot()) {
+				String rootId = key + ":0";
+				Group group = new Group();
+				group.setId(rootId);
+				group.setType(key);
+				group.setParentId("0");
+				putGroup(group, folder);
+			}
+			if (!folder.exists()) {
+				folder.mkdir();
+			}
+		});
+	}
+
 	@Override
 	@Override
 	public void refresh() {
 	public void refresh() {
 		writeLock(() -> {
 		writeLock(() -> {
-			groupMappings.clear();
-			groupCache.clear();
-			fileMappings.clear();
-			fileCache.clear();
-			pathCache.clear();
+			this.init();
 			this.root.readAll();
 			this.root.readAll();
-			storages.forEach((key, registry) -> {
-				if (registry.requirePath()) {
-					pathCache.put(registry.folder(), new HashMap<>(32));
-				}
-				Resource folder = root.getDirectory(key);
-				if (registry.allowRoot()) {
-					String rootId = key + ":0";
-					Group group = new Group();
-					group.setId(rootId);
-					group.setType(key);
-					group.setParentId("0");
-					putGroup(group, folder);
-				}
-				if (!folder.exists()) {
-					folder.mkdir();
-				} else {
-					refreshGroup(folder, registry);
-				}
-			});
+			storages.forEach((key, registry) -> refreshGroup(root.getDirectory(key), registry));
 			fileCache.values().forEach(entity -> {
 			fileCache.values().forEach(entity -> {
 				Group group = groupCache.get(entity.getGroupId());
 				Group group = groupCache.get(entity.getGroupId());
 				publisher.publishEvent(new FileEvent(group.getType(), EventAction.LOAD, entity));
 				publisher.publishEvent(new FileEvent(group.getType(), EventAction.LOAD, entity));
@@ -173,9 +179,11 @@ public class DefaultMagicResourceService implements MagicResourceService, JsonCo
 			}
 			}
 			Resource groupResource;
 			Resource groupResource;
 			GroupEvent event = new GroupEvent(group.getType(), group.getId() == null ? EventAction.CREATE : EventAction.SAVE, group);
 			GroupEvent event = new GroupEvent(group.getType(), group.getId() == null ? EventAction.CREATE : EventAction.SAVE, group);
-			if (group.getId() == null) {
+			if (group.getId() == null || !groupCache.containsKey(group.getId())) {
 				// 添加分组
 				// 添加分组
-				group.setId(UUID.randomUUID().toString().replace("-", ""));
+				if(group.getId() == null){
+					group.setId(UUID.randomUUID().toString().replace("-", ""));
+				}
 				groupResource = resource.getDirectory(group.getName());
 				groupResource = resource.getDirectory(group.getName());
 				// 判断分组文件夹需要不存在
 				// 判断分组文件夹需要不存在
 				isTrue(!groupResource.exists(), FILE_SAVE_FAILURE);
 				isTrue(!groupResource.exists(), FILE_SAVE_FAILURE);
@@ -186,7 +194,7 @@ public class DefaultMagicResourceService implements MagicResourceService, JsonCo
 				groupResource = resource.getDirectory(group.getName());
 				groupResource = resource.getDirectory(group.getName());
 				isTrue(oldResource != null && oldResource.exists(), GROUP_NOT_FOUND);
 				isTrue(oldResource != null && oldResource.exists(), GROUP_NOT_FOUND);
 				// 名字不一样时重命名
 				// 名字不一样时重命名
-				if (!oldResource.name().equals(resource.name())) {
+				if (!oldResource.getFilePath().equals(resource.getFilePath())) {
 					isTrue(oldResource.renameTo(groupResource), FILE_SAVE_FAILURE);
 					isTrue(oldResource.renameTo(groupResource), FILE_SAVE_FAILURE);
 				}
 				}
 			}
 			}
@@ -433,13 +441,14 @@ public class DefaultMagicResourceService implements MagicResourceService, JsonCo
 			String filename = entity.getName() + storage.suffix();
 			String filename = entity.getName() + storage.suffix();
 			// 获取修改前的信息
 			// 获取修改前的信息
 			Resource fileResource = groupResource.getResource(filename);
 			Resource fileResource = groupResource.getResource(filename);
-			if (entity.getId() == null) {
-				isTrue(!fileResource.exists(), FILE_SAVE_FAILURE);
-				// 新增操作赋值
-				entity.setId(UUID.randomUUID().toString().replace("-", ""));
+			if (entity.getId() == null || !fileCache.containsKey(entity.getId())) {
+				if(entity.getId() == null){
+					isTrue(!fileResource.exists(), FILE_SAVE_FAILURE);
+					// 新增操作赋值
+					entity.setId(UUID.randomUUID().toString().replace("-", ""));
+				}
 				entity.setCreateTime(System.currentTimeMillis());
 				entity.setCreateTime(System.currentTimeMillis());
 				entity.setCreateBy(WebUtils.currentUserName());
 				entity.setCreateBy(WebUtils.currentUserName());
-
 			} else {
 			} else {
 				// 修改操作赋值
 				// 修改操作赋值
 				entity.setUpdateTime(System.currentTimeMillis());
 				entity.setUpdateTime(System.currentTimeMillis());
@@ -612,6 +621,102 @@ public class DefaultMagicResourceService implements MagicResourceService, JsonCo
 		return doLockResource(id, Constants.UNLOCK);
 		return doLockResource(id, Constants.UNLOCK);
 	}
 	}
 
 
+	@Override
+	public boolean upload(InputStream inputStream, boolean full) throws IOException {
+		try {
+			ZipResource zipResource = new ZipResource(inputStream);
+			Set<Group> groups = new LinkedHashSet<>();
+			Set<MagicEntity> entities = new LinkedHashSet<>();
+			return writeLock(() -> {
+				readAllResource(zipResource, groups, entities,  !full);
+				if(full){
+					// 全量模式先删除处理。
+					root.delete();
+					this.init();
+					publisher.publishEvent(new MagicEvent("clear", EventAction.CLEAR));
+				}
+				for (Group group : groups) {
+					saveGroup(group);
+				}
+				for (MagicEntity entity : entities) {
+					saveFile(entity);
+				}
+				return true;
+			});
+		} finally {
+			IoUtils.close(inputStream);
+		}
+
+	}
+
+	private void readAllResource(Resource root, Set<Group> groups, Set<MagicEntity> entities, boolean checked){
+		readAllResource(root, null, groups, entities, null, "/", checked);
+	}
+
+	private void readAllResource(Resource root, MagicResourceStorage<? extends MagicEntity> storage, Set<Group> groups, Set<MagicEntity> entities, Set<String> mappingKeys, String path, boolean checked){
+		storage = storage == null ? storages.get(root.name()) : storage;
+		if(storage != null){
+			mappingKeys = mappingKeys == null ? new HashSet<>() : mappingKeys;
+			if(!storage.allowRoot()){
+				Resource resource = root.getResource(Constants.GROUP_METABASE);
+				// 分组信息不存在时,直接返回不处理
+				if(resource.exists()){
+					// 读取分组信息
+					Group group = JsonUtils.readValue(resource.read(), Group.class);
+					group.setType(mappingV1Type(group.getType()));
+					groups.add(group);
+					if(storage.requirePath()){
+						path = path + Objects.toString(group.getPath(), "") + "/";
+					}
+				}
+
+			}
+			for(Resource file : root.files(storage.suffix())){
+				MagicEntity entity = storage.read(file.read());
+				if(storage.allowRoot()){
+					entity.setGroupId(storage.folder() + ":0");
+				}
+				String mappingKey;
+				if(storage instanceof AbstractPathMagicResourceStorage){
+					mappingKey = ((AbstractPathMagicResourceStorage) storage).buildMappingKey((PathMagicEntity) entity, path);
+				}else{
+					mappingKey = storage.buildKey(entity);
+				}
+				if(checked){
+					String groupId = entity.getGroupId();
+					// 名字和路径冲突检查
+					fileCache.values().stream()
+							// 同一分组
+							.filter(it -> Objects.equals(it.getGroupId(), groupId))
+							// 非自身
+							.filter(it -> !it.getId().equals(entity.getId()))
+							.forEach(it -> isTrue(!Objects.equals(it.getName(), entity.getName()), RESOURCE_PATH_CONFLICT.format(entity.getName())));
+
+				}
+				// 自检
+				isTrue(mappingKeys.add(mappingKey), RESOURCE_PATH_CONFLICT.format(mappingKey));
+				entities.add(entity);
+			}
+
+		}
+		for (Resource directory : root.dirs()) {
+			readAllResource(directory, storage, groups, entities, mappingKeys, path, checked);
+		}
+	}
+
+	/**
+	 * 兼容1.x版本
+	 */
+	private String mappingV1Type(String type){
+		if("1".equals(type)){
+			return "api";
+		} else if ("2".equals(type)){
+			return "function";
+		}
+		return type;
+
+	}
+
 	@Override
 	@Override
 	public String getGroupName(String groupId) {
 	public String getGroupName(String groupId) {
 		return findGroups(groupId).stream()
 		return findGroups(groupId).stream()

+ 1 - 2
magic-api/src/main/java/org/ssssssss/magicapi/service/impl/FunctionInfoMagicResourceStorage.java

@@ -1,7 +1,6 @@
 package org.ssssssss.magicapi.service.impl;
 package org.ssssssss.magicapi.service.impl;
 
 
 import org.ssssssss.magicapi.model.FunctionInfo;
 import org.ssssssss.magicapi.model.FunctionInfo;
-import org.ssssssss.magicapi.utils.PathUtils;
 
 
 public class FunctionInfoMagicResourceStorage extends AbstractPathMagicResourceStorage<FunctionInfo> {
 public class FunctionInfoMagicResourceStorage extends AbstractPathMagicResourceStorage<FunctionInfo> {
 
 
@@ -17,6 +16,6 @@ public class FunctionInfoMagicResourceStorage extends AbstractPathMagicResourceS
 
 
 	@Override
 	@Override
 	public String buildMappingKey(FunctionInfo info) {
 	public String buildMappingKey(FunctionInfo info) {
-		return PathUtils.replaceSlash("/" + magicResourceService.getGroupPath(info.getGroupId()) + "/" + info.getPath());
+		return buildMappingKey(info, magicResourceService.getGroupPath(info.getGroupId()));
 	}
 	}
 }
 }

+ 14 - 0
magic-api/src/main/java/org/ssssssss/magicapi/service/impl/FunctionMagicDynamicRegistry.java

@@ -1,5 +1,7 @@
 package org.ssssssss.magicapi.service.impl;
 package org.ssssssss.magicapi.service.impl;
 
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.context.event.EventListener;
 import org.springframework.context.event.EventListener;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.FileEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
 import org.ssssssss.magicapi.event.GroupEvent;
@@ -18,6 +20,8 @@ import java.util.function.Function;
 
 
 public class FunctionMagicDynamicRegistry extends AbstractMagicDynamicRegistry<FunctionInfo> {
 public class FunctionMagicDynamicRegistry extends AbstractMagicDynamicRegistry<FunctionInfo> {
 
 
+	private static final Logger logger = LoggerFactory.getLogger(FunctionMagicDynamicRegistry.class);
+
 	public FunctionMagicDynamicRegistry(MagicResourceStorage<FunctionInfo> magicResourceStorage) {
 	public FunctionMagicDynamicRegistry(MagicResourceStorage<FunctionInfo> magicResourceStorage) {
 		super(magicResourceStorage);
 		super(magicResourceStorage);
 		MagicResourceLoader.addFunctionLoader(this::lookupLambdaFunction);
 		MagicResourceLoader.addFunctionLoader(this::lookupLambdaFunction);
@@ -56,4 +60,14 @@ public class FunctionMagicDynamicRegistry extends AbstractMagicDynamicRegistry<F
 		processEvent(event);
 		processEvent(event);
 	}
 	}
 
 
+	@Override
+	protected boolean register(MappingNode<FunctionInfo> mappingNode) {
+		logger.debug("注册函数:{}", mappingNode.getMappingKey());
+		return true;
+	}
+
+	@Override
+	protected void unregister(MappingNode<FunctionInfo> mappingNode) {
+		logger.debug("取消注册函数:{}", mappingNode.getMappingKey());
+	}
 }
 }

+ 9 - 0
magic-api/src/main/java/org/ssssssss/magicapi/utils/IoUtils.java

@@ -158,4 +158,13 @@ public class IoUtils {
 			}
 			}
 		}
 		}
 	}
 	}
+
+	public static void close(Closeable closeable){
+		try {
+			if(closeable != null){
+				closeable.close();
+			}
+		} catch (IOException ignored) {
+		}
+	}
 }
 }