Răsfoiți Sursa

新增历史记录、代码对比以及恢复

mxd 5 ani în urmă
părinte
comite
fe484fb833

+ 19 - 7
src/main/java/org/ssssssss/magicapi/config/WebUIController.java

@@ -231,12 +231,11 @@ public class WebUIController {
 	}
 
 	/**
-	 * 获取单个class
-	 *
+	 * 创建控制台输出
 	 */
 	@RequestMapping("/console")
 	public SseEmitter console() throws IOException {
-		String sessionId = UUID.randomUUID().toString().replace("-","");
+		String sessionId = UUID.randomUUID().toString().replace("-", "");
 		SseEmitter emitter = MagicLoggerContext.createEmitter(sessionId);
 		emitter.send(SseEmitter.event().data(sessionId).name("create"));
 		return emitter;
@@ -287,11 +286,11 @@ public class WebUIController {
 			try {
 				context.setBreakpoints((List<Integer>) breakpoints);    //设置断点
 				context.setTimeout(this.debugTimeout);    //设置断点超时时间
-				if(sessionId != null){
+				if (sessionId != null) {
 					context.setId(sessionId.toString());
-					context.onComplete(()->MagicLoggerContext.remove(sessionId.toString()));
-					logger.info("Start Console Session : {},{}",sessionId,this.getClass().getClassLoader().getClass().getName());
-					context.onStart(()-> MDC.put(MagicLoggerContext.MAGIC_CONSOLE_SESSION,sessionId.toString()));
+					context.onComplete(() -> MagicLoggerContext.remove(sessionId.toString()));
+					logger.info("Start Console Session : {},{}", sessionId, this.getClass().getClassLoader().getClass().getName());
+					context.onStart(() -> MDC.put(MagicLoggerContext.MAGIC_CONSOLE_SESSION, sessionId.toString()));
 				}
 				Object result = MagicScriptEngine.execute(MagicScriptCompiler.compile(script.toString()), context);
 				if (context.isRunning()) {    //判断是否执行完毕
@@ -351,6 +350,18 @@ public class WebUIController {
 		}
 	}
 
+	@RequestMapping("/backups")
+	@ResponseBody
+	public JsonBean<List<Long>> backups(String id) {
+		return new JsonBean<>(magicApiService.backupList(id));
+	}
+
+	@RequestMapping("/backup/get")
+	@ResponseBody
+	public JsonBean<ApiInfo> backups(String id, Long timestamp) {
+		return new JsonBean<>(magicApiService.backupInfo(id, timestamp));
+	}
+
 	/**
 	 * 保存接口
 	 *
@@ -390,6 +401,7 @@ public class WebUIController {
 				}
 				magicApiService.update(info);
 			}
+			magicApiService.backup(info.getId());
 			// 注册接口
 			mappingHandlerMapping.registerMapping(info, true);
 			return new JsonBean<>(info.getId());

+ 21 - 0
src/main/java/org/ssssssss/magicapi/provider/ApiServiceProvider.java

@@ -80,6 +80,27 @@ public interface ApiServiceProvider {
 	 */
 	boolean update(ApiInfo info);
 
+	/**
+	 * 备份历史记录
+	 *
+	 * @param apiId
+	 */
+	void backup(String apiId);
+
+	/**
+	 * 查询API历史记录
+	 * @param apiId
+	 * @return 时间戳列表
+	 */
+	List<Long> backupList(String apiId);
+
+	/**
+	 * 查询API历史记录详情
+	 * @param apiId
+	 * @param timestamp 时间戳
+	 */
+	ApiInfo backupInfo(String apiId, Long timestamp);
+
 	/**
 	 * 包装接口信息(可用于加密)
 	 *

+ 19 - 0
src/main/java/org/ssssssss/magicapi/provider/impl/DefaultApiServiceProvider.java

@@ -93,4 +93,23 @@ public class DefaultApiServiceProvider implements ApiServiceProvider {
 		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;
 	}
+
+	@Override
+	public void backup(String apiId) {
+		String backupSql = "insert into magic_api_info_his select * from magic_api_info where id = ?";
+		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);
+	}
+
+	@Override
+	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 = ? limit 1";
+		ApiInfo info = template.queryForObject(selectOne, rowMapper, apiId,timestamp);
+		unwrap(info);
+		return info;
+	}
 }

+ 70 - 7
src/main/resources/magicapi-support/css/index.css

@@ -551,7 +551,6 @@ ul li {
     display: inline-block;
     vertical-align: middle;
     position: relative;
-    padding-left: 5px;
     min-width : 250px;
     box-shadow: 0px 0px 8px #CFCFCF;
     max-width: 800px;
@@ -559,11 +558,11 @@ ul li {
 .dialog-wrapper .dialog .dialog-header{
     height : 30px;
     line-height: 30px;
-    padding-left : 25px;
+    padding-left : 30px;
     padding-right: 75px;
     background-image: url(../images/logo.png);
     background-size: 22px 24px;
-    background-position: 0px 4px;
+    background-position: 5px 4px;
     background-repeat: no-repeat;
     text-align: left;
 }
@@ -610,7 +609,61 @@ ul li {
     background: #E3F1FA;
     border-color: #0784DE;
 }
-
+.dialog-wrapper .dialog.history-list{
+    max-width: inherit;
+    width : 80%;
+    height : 750px;
+}
+.dialog-wrapper .dialog.history-list .dialog-content{
+    border-top: 1px solid #C0C0C0;
+    overflow: auto;
+    position: relative;
+    width : 100%;
+    height : 680px
+}
+.dialog-wrapper .dialog.history-list .version{
+    position: absolute;
+    left : 160px;
+    right : 0px;
+}
+.dialog-wrapper .dialog.history-list .version span{
+    float : left;
+    display: inline-block;
+    line-height: 16px;
+    padding: 0 10px;
+}
+.dialog-wrapper .dialog.history-list .version span.current{
+    float : right
+}
+.dialog-wrapper .dialog.history-list .dialog-content ul{
+    position: absolute;
+    left : 0px;
+    width : 160px;
+    background: #fff;
+    bottom : 5px;
+    top: 0px;
+    color : #666;
+    overflow: auto;
+}
+.dialog-wrapper .dialog.history-list .diff-editor{
+    position: absolute;
+    left : 160px;
+    right : 0px;
+    top : 24px;
+    bottom : 5px;
+}
+.dialog-wrapper .dialog.history-list .dialog-content ul li{
+    height : 20px;
+    line-height: 20px;
+    color : #666;
+    padding-left: 5px;
+    border-bottom: 1px solid #c0c0c0;
+}
+.dialog-wrapper .dialog.history-list .dialog-content ul li:hover,
+.dialog-wrapper .dialog.history-list .dialog-content ul li.selected{
+    background: #1A7DC4;
+    color : #fff;
+}
 .breakpoints{
     background: #db5860;
     width: 10px !important;
@@ -764,10 +817,20 @@ ul li {
 .skin-dark .context-menu li:not(:last-child){
     border-bottom: 1px solid #616161;
 }
-.skin-dark .context-menu li:not(:last-child){
-
+.skin-dark .dialog-wrapper .dialog.history-list .dialog-content{
+    border-top: 1px solid #323232;
+}
+.skin-dark .dialog-wrapper .dialog.history-list .dialog-content ul{
+    background: #3C3F41;
+}
+.skin-dark .dialog-wrapper .dialog.history-list .dialog-content ul li{
+    border-bottom : 1px solid #323232;
+    color: #bbb;
 }
-.skin-dark .select ul li:hover,.skin-dark .skin-dark .context-menu li:hover{
+.dialog.history-list .dialog-content ul li:hover,
+.dialog.history-list .dialog-content ul li:selected,
+.skin-dark .select ul li:hover,
+.skin-dark .skin-dark .context-menu li:hover{
     background: #4B6EAF;
     color: #bbbbbb;
 }

+ 1 - 0
src/main/resources/magicapi-support/index.html

@@ -39,6 +39,7 @@
 	<span class="button-run" title="运行(Ctrl+Q)"><i class="iconfont icon-run"></i></span>
 	<span class="button-save" title="保存(Ctrl+S)"><i class="iconfont icon-save"></i></span>
 	<span class="button-delete" title="删除"><i class="iconfont icon-delete"></i></span>
+	<span class="button-history" title="历史记录"><i class="iconfont icon-history"></i></span>
 	<span class="button-skin" title="换肤"><i class="iconfont icon-skin"></i></span>
 	<span class="button-gitee" title="Gitee"><i class="iconfont icon-gitee"></i></span>
 	<span class="button-github" title="Github"><i class="iconfont icon-git"></i></span>

+ 106 - 52
src/main/resources/magicapi-support/js/index.js

@@ -246,11 +246,7 @@ var MagicEditor = {
                         }
                     });
                     if(exists){
-                        _this.createDialog({
-                            title : '创建分组',
-                            content : '分组已存在!',
-                            buttons : [{name : 'OK'}]
-                        })
+                        _this.alert('创建分组','分组已存在!');
                         return false;
                     }
                     _this.ajax({
@@ -320,11 +316,7 @@ var MagicEditor = {
                     });
                     if(exists){
                         _this.setStatusBar('分组「'+groupName + '」');
-                        MagicEditor.createDialog({
-                            title : '创建分组',
-                            content : '分组已存在!',
-                            buttons : [{name : 'OK'}]
-                        })
+                        _this.alert('创建分组','分组已存在!');
                         return false;
                     }
                     _this.addedGroups[groupName] = {
@@ -424,40 +416,22 @@ var MagicEditor = {
                 }else{
                     var val = options.exception&&options.exception(json.code,json.message,json);
                     if(val !== false){
-                        MagicEditor.createDialog({
-                            title : 'Error',
-                            content : json.message,
-                            buttons : [{name : 'OK'}]
-                        });
+                        MagicEditor.alert('Error',json.message);
                     }
                 }
             },
             error : function(){
                 MagicEditor.setStatusBar('ajax请求失败');
-                MagicEditor.createDialog({
-                    title : '网络错误',
-                    content : 'ajax请求失败',
-                    buttons : [{
-                        name : '关闭'
-                    }]
-                });
+                MagicEditor.alert('网络错误','ajax请求失败');
                 options.error&&options.error();
             }
         })
     },
     copyApi : function(){
-        MagicEditor.createDialog({
-            title : '复制接口',
-            content : '功能暂未实现!',
-            buttons : [{name : '知道了'}]
-        })
+        MagicEditor.alert('复制接口','功能暂未实现!');
     },
     copyApiPath : function(){
-        MagicEditor.createDialog({
-            title : '复制接口路径',
-            content : '功能暂未实现!',
-            buttons : [{name : '知道了'}]
-        })
+        MagicEditor.alert('复制接口路径', '功能暂未实现!');
     },
     resetDebugContent : function(){
         $('.bottom-item-body table tbody').html('<tr><td colspan="3" align="center">no message.</td></tr>');
@@ -552,24 +526,12 @@ var MagicEditor = {
             request = JSON.parse(request);
             if(typeof request != 'object'){
                 _this.setStatusBar('请求参数有误!');
-                _this.createDialog({
-                    title : '运行测试',
-                    content : '请求参数有误!',
-                    buttons : [{
-                        name : '确定'
-                    }]
-                });
+                _this.alert('运行测试','请求参数有误!');
                 return;
             }
         }catch(e){
             _this.setStatusBar('请求参数有误!');
-            _this.createDialog({
-                title : '运行测试',
-                content : '请求参数有误!',
-                buttons : [{
-                    name : '确定'
-                }]
-            });
+            _this.alert('运行测试','请求参数有误!');
             return;
         }
         _this.setStatusBar('开始测试...');
@@ -796,6 +758,95 @@ var MagicEditor = {
             }
         })
     },
+    alert : function(title,content){
+        this.createDialog({
+            title : title,
+            content : content,
+            buttons : [{name : 'OK'}]
+        });
+    },
+    doShowHistory : function(){
+        if(!this.apiId){
+            this.alert('历史记录','请选择接口后在查看历史记录');
+            return;
+        }
+        var _this = this;
+        var apiId = this.apiId;
+        var name = $('input[name=name]').val();
+        var scriptModel = monaco.editor.createModel(this.scriptEditor.getValue(),'magicscript');
+        this.ajax({
+            url : 'backups',
+            data : {
+                id : apiId
+            },
+            success : function(timestamps){
+                if(timestamps.length == 0){
+                    _this.alert('历史记录','暂无历史记录信息');
+                    return;
+                }
+                var $ul = $('<ul class="not-select"/>')
+                for(var i=0,len = timestamps.length;i<len;i++){
+                    var timestamp = timestamps[i];
+                    var timeStr = _this.getTimeStr(new Date(timestamp));
+                    $ul.append($('<li/>').attr('data-timestamp',timestamp).attr('data-id',apiId).append(timeStr))
+                }
+                var html = $ul[0].outerHTML;
+                html+= '<div class="version"><span class="version-time"></span><span class="current">当前版本</span></div>'
+                html += '<div class="diff-editor"></div>';
+                _this.createDialog({
+                    title : '历史记录:' + (name || ''),
+                    content : html,
+                    replace : false,
+                    className : 'history-list',
+                    buttons : [{
+                        name : '恢复',
+                        click : function(){
+                            _this.scriptEditor.setValue(scriptModel.getValue());
+                        }
+                    },{
+                        name : '取消'
+                    }],
+                    close : function(){
+                      _this.diffEditor = null;
+                    },
+                    onCreate : function($dom){
+                        _this.diffEditor = monaco.editor.createDiffEditor($dom.find('.diff-editor')[0], {
+                            enableSplitViewResizing: false,
+                            minimap : {
+                                enabled : false
+                            },
+                            folding : false,
+                            lineDecorationsWidth : 20,
+                            fixedOverflowWidgets :false
+                        });
+                        _this.diffEditor.setModel({
+                            original : scriptModel,
+                            modified : scriptModel
+                        });
+                        var $version = $dom.find('.version-time');
+                        $dom.on('click','ul li[data-timestamp]',function(){
+                            $(this).addClass('selected').siblings().removeClass('selected');
+                            var timestamp = $(this).data('timestamp');
+                            $version.html($(this).text());
+                            _this.ajax({
+                                url : 'backup/get',
+                                data : {
+                                    id : apiId,
+                                    timestamp : timestamp
+                                },
+                                success : function(info){
+                                    _this.diffEditor.setModel({
+                                        original : monaco.editor.createModel(info.script,'magicscript'),
+                                        modified : scriptModel
+                                    });
+                                }
+                            })
+                        })
+                    }
+                })
+            }
+        })
+    },
     //初始化右键菜单
     initContextMenu : function(){
         var _this = this;
@@ -832,11 +883,7 @@ var MagicEditor = {
                 name : '移动',
                 shortKey : 'Ctrl+M',
                 click : function(){
-                    _this.createDialog({
-                        title : '移动接口',
-                        content : '功能暂未实现!',
-                        buttons : [{name : '知道了'}]
-                    })
+                    _this.alert('移动接口','功能暂未实现!');
                 }
             },{
                 name : '删除接口',
@@ -879,6 +926,8 @@ var MagicEditor = {
             _this.loadAPI($(this).addClass('selected').data('id'))
         }).on('click','.button-run',function(){
             _this.doTest();
+        }).on('click','.button-history',function(){
+            _this.doShowHistory();
         }).on('click','.button-delete',function(){
             if($(this).hasClass('disabled')){
                 return;
@@ -1092,6 +1141,9 @@ var MagicEditor = {
     createDialog : function(options){
         options = options || {};
         var $dialog = $('<div/>').addClass('dialog');
+        if(options.className){
+            $dialog.addClass(options.className);
+        }
         var $header = $('<div/>').addClass('dialog-header').addClass('not-select').append(options.title || '');
         var $close = $('<span/>').append('<i class="iconfont icon-close"></i>');
         $header.append($close);
@@ -1100,7 +1152,7 @@ var MagicEditor = {
             $wrapper.remove();
         })
         $dialog.append($header);
-        var content = options.content;
+        var content = options.content || '';
         if(options.replace !== false){
             content = content.replace(/\n/g,'<br>').replace(/ /g,'&nbsp;').replace(/\t/g,'&nbsp;&nbsp;&nbsp;&nbsp;');
         }
@@ -1126,6 +1178,7 @@ var MagicEditor = {
             $wrapper.remove();
         })
         $('body').append($wrapper);
+        options.onCreate&&options.onCreate($wrapper);
     },
     createContextMenu : function(menus,left,top,$dom){
         $('.context-menu').remove();
@@ -1299,6 +1352,7 @@ var MagicEditor = {
         this.optionsEditor&&this.optionsEditor.layout();
         this.requestEditor&&this.requestEditor.layout();
         this.resultEditor&&this.resultEditor.layout();
+        this.diffEditor&&this.diffEditor.layout();
     }
 }
 $(function(){