From 26ea5b94e29e63eb5f4d1f27f973b7c4ea9351a6 Mon Sep 17 00:00:00 2001 From: xuwangcheng Date: Mon, 9 Mar 2020 23:12:26 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E7=BB=84=E5=90=88=E5=9C=BA=E6=99=AF?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=9C=BA=E6=99=AF=E5=8F=AF=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E6=89=A7=E8=A1=8C=E6=96=B9=E5=BC=8F=EF=BC=8C?= =?UTF-8?q?=E8=8A=82=E7=9C=81=E6=89=A7=E8=A1=8C=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/message/action/UploadAction.java | 2 +- .../message/bean/ComplexSceneConfig.java | 15 +- .../CustomSettingVariable.java | 2 +- .../yi/master/constant/ReturnCodeConsts.java | 6 +- .../yi/master/constant/WebScriptConsts.java | 4 +- .../message/test/MessageAutoTest.java | 230 +++++++++++------- .../message/test/TestMessageScene.java | 16 +- .../performance/PerformanceTestObject.java | 2 +- .../yi/master/listener/InitWebListener.java | 2 +- .../webapp/resource/message/messageScene.js | 2 + .../resource/template/customTemplate.htm | 13 + update.md | 7 + 12 files changed, 196 insertions(+), 105 deletions(-) rename src/main/java/yi/master/{util/cache => constant}/CustomSettingVariable.java (98%) diff --git a/src/main/java/yi/master/business/message/action/UploadAction.java b/src/main/java/yi/master/business/message/action/UploadAction.java index 0b39c04..60d156a 100644 --- a/src/main/java/yi/master/business/message/action/UploadAction.java +++ b/src/main/java/yi/master/business/message/action/UploadAction.java @@ -8,7 +8,7 @@ import yi.master.business.base.bean.ReturnJSONObject; import yi.master.constant.ReturnCodeConsts; import yi.master.util.FrameworkUtil; import yi.master.util.ParameterMap; -import yi.master.util.cache.CustomSettingVariable; +import yi.master.constant.CustomSettingVariable; import yi.master.util.upload.Upload; import java.io.File; diff --git a/src/main/java/yi/master/business/message/bean/ComplexSceneConfig.java b/src/main/java/yi/master/business/message/bean/ComplexSceneConfig.java index d5f4c70..c0282ce 100644 --- a/src/main/java/yi/master/business/message/bean/ComplexSceneConfig.java +++ b/src/main/java/yi/master/business/message/bean/ComplexSceneConfig.java @@ -40,6 +40,11 @@ public class ComplexSceneConfig { private String systemId; + /** + * 是否异步执行 + */ + private String async = "0"; + public ComplexSceneConfig(Integer messageSceneId, Map saveVariables, Map useVariables, Integer retryCount, @@ -119,7 +124,15 @@ public class ComplexSceneConfig { this.errorExecFlag = errorExecFlag; } - @Override + public void setAsync(String async) { + this.async = async; + } + + public String getAsync() { + return async; + } + + @Override public String toString() { return "ComplexSceneConfig [messageSceneId=" + messageSceneId + ", saveVariables=" + saveVariables + ", useVariables=" diff --git a/src/main/java/yi/master/util/cache/CustomSettingVariable.java b/src/main/java/yi/master/constant/CustomSettingVariable.java similarity index 98% rename from src/main/java/yi/master/util/cache/CustomSettingVariable.java rename to src/main/java/yi/master/constant/CustomSettingVariable.java index b056688..45a1658 100644 --- a/src/main/java/yi/master/util/cache/CustomSettingVariable.java +++ b/src/main/java/yi/master/constant/CustomSettingVariable.java @@ -1,4 +1,4 @@ -package yi.master.util.cache; +package yi.master.constant; import cn.hutool.core.io.FileUtil; import org.apache.commons.lang3.StringUtils; diff --git a/src/main/java/yi/master/constant/ReturnCodeConsts.java b/src/main/java/yi/master/constant/ReturnCodeConsts.java index 5ea1602..175628b 100644 --- a/src/main/java/yi/master/constant/ReturnCodeConsts.java +++ b/src/main/java/yi/master/constant/ReturnCodeConsts.java @@ -12,17 +12,17 @@ public interface ReturnCodeConsts { * 成功
* '0' */ - public static final Integer SUCCESS_CODE = 0; + Integer SUCCESS_CODE = 0; /** * 系统处理错误
* '1' */ - public static final Integer SYSTEM_ERROR_CODE = 1; + Integer SYSTEM_ERROR_CODE = 1; /** * 上传 文件时候未发现文件
* '6' */ - public static final Integer NO_FILE_UPLOAD_CODE = 6; + Integer NO_FILE_UPLOAD_CODE = 6; } diff --git a/src/main/java/yi/master/constant/WebScriptConsts.java b/src/main/java/yi/master/constant/WebScriptConsts.java index 7c3dd4b..8aadbde 100644 --- a/src/main/java/yi/master/constant/WebScriptConsts.java +++ b/src/main/java/yi/master/constant/WebScriptConsts.java @@ -9,10 +9,10 @@ public interface WebScriptConsts { /** * 框架类型 */ - public static final String FRAMEWORK_TYPE_WATIR_CUCUMBER = "watir-cucmber"; + String FRAMEWORK_TYPE_WATIR_CUCUMBER = "watir-cucmber"; /** * web自动化测试报告生成位置 */ - public static final String WEB_SCRIPT_REPORT_FOLDER_PATH = "webReport"; + String WEB_SCRIPT_REPORT_FOLDER_PATH = "webReport"; } diff --git a/src/main/java/yi/master/coretest/message/test/MessageAutoTest.java b/src/main/java/yi/master/coretest/message/test/MessageAutoTest.java index b18ee16..10e0bb2 100644 --- a/src/main/java/yi/master/coretest/message/test/MessageAutoTest.java +++ b/src/main/java/yi/master/coretest/message/test/MessageAutoTest.java @@ -247,103 +247,30 @@ public class MessageAutoTest { int lastSeqNum = 1; + //是否使用新的测试客户端来进行测试 if (testScene.isNewClient()) { procotolClient = (DefaultHttpClient) testScene.getTestClient().getTestClient(); } + //需要异步执行的场景 + List asyncTestScenes = new ArrayList<>(); + for (TestMessageScene scene:testScene.getScenes()) { if (stopFlag || scene == null || (lastTestFlag && !scene.getScene().getSequenceNum().equals(testScene.getScenes().size()))) { complexMark.append("测试序号为[" + ++lastSeqNum + "]" + ",跳过测试该场景!\n"); continue; } - lastSeqNum = scene.getScene().getSequenceNum(); - //替换上下文变量 - for (Map.Entry entry:scene.getScene().getConfig().getUseVariables().entrySet()) { - String value = null; - if (saveVariables.containsKey(entry.getValue())) { - //如果有对应上下文替换变量的就替换掉,否则使用常量 - value = saveVariables.get(entry.getValue()); - } else { - value = entry.getValue(); - } - - if (scene.getCallParameter() == null) { - scene.setCallParameter(new HashMap()); - scene.getCallParameter().put(MessageKeys.HTTP_PARAMETER_HEADER, new HashMap()); - } - if (scene.getCallParameter().get(MessageKeys.HTTP_PARAMETER_QUERYS) == null) { - scene.getCallParameter().put(MessageKeys.HTTP_PARAMETER_QUERYS, new HashMap()); - } - - //根据变量名来判断是替换请求头还是请求体还是query参数 - if (entry.getKey().startsWith("RequestHeader.")) { - ((Map) scene.getCallParameter().get(MessageKeys.HTTP_PARAMETER_HEADER)).put(entry.getKey().substring(entry.getKey().indexOf(".") + 1) - , value); - } else if (entry.getKey().startsWith("Querys.")) { - ((Map) scene.getCallParameter().get(MessageKeys.HTTP_PARAMETER_QUERYS)).put(entry.getKey().substring(entry.getKey().indexOf(".") + 1) - , value); - } else { - scene.setRequestMessage(scene.getRequestMessage().replace(MessageKeys.CUSTOM_PARAMETER_BOUNDARY_SYMBOL_LEFT + - entry.getValue() + MessageKeys.CUSTOM_PARAMETER_BOUNDARY_SYMBOL_RIGHT, value)); - } + if (SystemConsts.DefaultBooleanIdentify.TRUE.getNumber().equals(scene.getScene().getConfig().getAsync())) { + asyncTestScenes.add(scene); + continue; + } - //处理可能存在的路径参数 - scene.setRequestUrl(PracticalUtils.replacePathVariableParameter(scene.getRequestUrl(), scene.getRequestMessage(), scene.getParseUtil())); - } - boolean successFlag = false; - int requestCount = 0; - TestResult result = null; - int maxRetryCount = scene.getScene().getConfig().getRetryCount(); - while (!successFlag && maxRetryCount >= requestCount ++) { - - result = singleTest(scene, procotolClient); - - //如果场景测试成功 - if (MessageKeys.TestRunStatus.SUCCESS.getCode().equals(result.getRunStatus())) { - successFlag = true; - continue; - } - try { - Thread.sleep(scene.getScene().getConfig().getIntervalTime()); - } catch (InterruptedException e) { - e.printStackTrace(); + lastSeqNum = scene.getScene().getSequenceNum(); - } - } - result.setMark("组合场景名 [" + testScene.getComplexScene().getComplexSceneName() + "] ,执行序号 [" + scene.getScene().getSequenceNum() + "] \n\n" + result.getMark()); - results.add(result); - //测试成功要获取保存变量并设置数据状态 - if (successFlag) { - String key = null; - //保存上下文变量 - for (Map.Entry entry:scene.getScene().getConfig().getSaveVariables().entrySet()) { - String str = null; - key = entry.getKey().replaceAll("\"#\"", "\":\""); - //保存响应头中的变量 - if (entry.getKey().startsWith("ResponseHeader.") && StringUtils.isNotBlank(result.getHeaders())) { - JSONObject header = JSONObject.fromObject(result.getHeaders()); - String headerKey = entry.getKey().substring(entry.getKey().indexOf(".") + 1); - if (header.getJSONObject("ResponseHeader").has(headerKey)) { - str = header.getJSONObject("ResponseHeader").getString(headerKey); - } - //保存入参中的变量 - } else if (entry.getKey().startsWith("RequestMessage.")) { - String pathKey = entry.getKey().replace("RequestMessage.", ""); - str = scene.getParseUtil().getObjectByPath(scene.getRequestMessage(), pathKey); - //通过关联规则去关联 - } else if (MessageKeys.MessageType.JSON.name().equals(MessageParse.judgeType(key))) { - str = PracticalUtils.getValueByRelationKeyWord(JSONObject.fromObject(key), result.getResponseMessage()); - } else { - //保存body体中的变量 - str = MessageParse.judgeMessageType(result.getResponseMessage()).getObjectByPath(result.getResponseMessage(), entry.getKey()); - } + boolean successFlag = testSceneOfComplex(scene, saveVariables, procotolClient + , testScene.getComplexScene().getComplexSceneName(), results, false); - if (StringUtils.isNotEmpty(str)) { - saveVariables.put(entry.getValue(), str); - } - } - } //测试不成功的处理 if (!successFlag) { allSuccessFlag = false; @@ -373,8 +300,44 @@ public class MessageAutoTest { } } } - } + //并发执行异步测试场景 + if (asyncTestScenes.size() > 0) { + final boolean[] flags = new boolean[]{false, allSuccessFlag}; + final DefaultHttpClient procotolClient2 = procotolClient; + //场景数量 分别为finishCount和totalCount + final int[] count = new int[]{0, asyncTestScenes.size()}; + final Object lock = new Object(); + new Thread(){ + public void run () { + for (final TestMessageScene scene:asyncTestScenes) { + boolean successFlag = testSceneOfComplex(scene, saveVariables, procotolClient2 + , testScene.getComplexScene().getComplexSceneName(), results,true); + if (!successFlag) { + flags[1] = false; + } + + synchronized (lock) { + count[0] ++; + //判断是否完成 + if (count[0] == count[1]) { + flags[0] = true; + } + } + } + } + }.start(); + + while (!flags[0]) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + LOGGER.warn("InterruptedException", e); + } + } + + } + } if(testScene.getTestClient() != null) { @@ -427,7 +390,100 @@ public class MessageAutoTest { return null; } - + + + private boolean testSceneOfComplex(TestMessageScene scene, Map saveVariables, DefaultHttpClient procotolClient + , String complexSceneName, List results, boolean async) { + //替换上下文变量 + for (Map.Entry entry:scene.getScene().getConfig().getUseVariables().entrySet()) { + String value = null; + if (saveVariables.containsKey(entry.getValue())) { + //如果有对应上下文替换变量的就替换掉,否则使用常量 + value = saveVariables.get(entry.getValue()); + } else { + value = entry.getValue(); + } + + if (scene.getCallParameter() == null) { + scene.setCallParameter(new HashMap()); + scene.getCallParameter().put(MessageKeys.HTTP_PARAMETER_HEADER, new HashMap()); + } + if (scene.getCallParameter().get(MessageKeys.HTTP_PARAMETER_QUERYS) == null) { + scene.getCallParameter().put(MessageKeys.HTTP_PARAMETER_QUERYS, new HashMap()); + } + + //根据变量名来判断是替换请求头还是请求体还是query参数 + if (entry.getKey().startsWith("RequestHeader.")) { + ((Map) scene.getCallParameter().get(MessageKeys.HTTP_PARAMETER_HEADER)).put(entry.getKey().substring(entry.getKey().indexOf(".") + 1) + , value); + } else if (entry.getKey().startsWith("Querys.")) { + ((Map) scene.getCallParameter().get(MessageKeys.HTTP_PARAMETER_QUERYS)).put(entry.getKey().substring(entry.getKey().indexOf(".") + 1) + , value); + } else { + scene.setRequestMessage(scene.getRequestMessage().replace(MessageKeys.CUSTOM_PARAMETER_BOUNDARY_SYMBOL_LEFT + + entry.getValue() + MessageKeys.CUSTOM_PARAMETER_BOUNDARY_SYMBOL_RIGHT, value)); + } + + //处理可能存在的路径参数 + scene.setRequestUrl(PracticalUtils.replacePathVariableParameter(scene.getRequestUrl(), scene.getRequestMessage(), scene.getParseUtil())); + } + boolean successFlag = false; + int requestCount = 0; + TestResult result = null; + int maxRetryCount = scene.getScene().getConfig().getRetryCount(); + while (!successFlag && maxRetryCount >= requestCount ++) { + + result = singleTest(scene, procotolClient); + + //如果场景测试成功 + if (MessageKeys.TestRunStatus.SUCCESS.getCode().equals(result.getRunStatus())) { + successFlag = true; + continue; + } + try { + Thread.sleep(scene.getScene().getConfig().getIntervalTime()); + } catch (InterruptedException e) { + e.printStackTrace(); + + } + } + result.setMark("组合场景名 [" + complexSceneName + "] , " + (async ? "该场景为异步执行方式。" : "执行序号 [" + scene.getScene().getSequenceNum() + "]") + " \n\n" + result.getMark()); + results.add(result); + //测试成功要获取保存变量并设置数据状态 + if (successFlag) { + String key = null; + //保存上下文变量 + for (Map.Entry entry:scene.getScene().getConfig().getSaveVariables().entrySet()) { + String str = null; + key = entry.getKey().replaceAll("\"#\"", "\":\""); + //保存响应头中的变量 + if (entry.getKey().startsWith("ResponseHeader.") && StringUtils.isNotBlank(result.getHeaders())) { + JSONObject header = JSONObject.fromObject(result.getHeaders()); + String headerKey = entry.getKey().substring(entry.getKey().indexOf(".") + 1); + if (header.getJSONObject("ResponseHeader").has(headerKey)) { + str = header.getJSONObject("ResponseHeader").getString(headerKey); + } + //保存入参中的变量 + } else if (entry.getKey().startsWith("RequestMessage.")) { + String pathKey = entry.getKey().replace("RequestMessage.", ""); + str = scene.getParseUtil().getObjectByPath(scene.getRequestMessage(), pathKey); + //通过关联规则去关联 + } else if (MessageKeys.MessageType.JSON.name().equals(MessageParse.judgeType(key))) { + str = PracticalUtils.getValueByRelationKeyWord(JSONObject.fromObject(key), result.getResponseMessage()); + } else { + //保存body体中的变量 + str = MessageParse.judgeMessageType(result.getResponseMessage()).getObjectByPath(result.getResponseMessage(), entry.getKey()); + } + + if (StringUtils.isNotEmpty(str)) { + saveVariables.put(entry.getValue(), str); + } + } + } + + return successFlag; + } + /** * 批量测试/测试集测试 * @author xuwangcheng @@ -608,7 +664,7 @@ public class MessageAutoTest { } Set tss = packageRequestObject(scene, config, system); if (tss.size() >= 1) { - testScene.getScenes().add(new ArrayList(tss).get(0)); + testScene.getScenes().add(new ArrayList<>(tss).get(0)); if (ComplexSceneSuccessFlag.SEPARATE_STATISTICS_RESULT.getFlag().equals(complexScene.getSuccessFlag())) { sceneCount++; } diff --git a/src/main/java/yi/master/coretest/message/test/TestMessageScene.java b/src/main/java/yi/master/coretest/message/test/TestMessageScene.java index 580c9cb..fbdacc9 100644 --- a/src/main/java/yi/master/coretest/message/test/TestMessageScene.java +++ b/src/main/java/yi/master/coretest/message/test/TestMessageScene.java @@ -1,12 +1,7 @@ package yi.master.coretest.message.test; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - import org.apache.log4j.Logger; - import yi.master.business.message.bean.ComplexScene; import yi.master.business.message.bean.MessageScene; import yi.master.business.testconfig.bean.BusinessSystem; @@ -14,6 +9,10 @@ import yi.master.business.testconfig.bean.TestConfig; import yi.master.coretest.message.parse.MessageParse; import yi.master.coretest.message.protocol.TestClient; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + /** * * 临时组装的测试对象,包含单个场景或者组合场景测试需要的所有信息 @@ -50,7 +49,7 @@ public class TestMessageScene implements Cloneable { private Boolean complexFlag = false; /** - * 如果为组合场景则对应包含的测试数据 + * 如果为组合场景则对应包含的测试场景 */ private List scenes = new ArrayList(); @@ -91,6 +90,7 @@ public class TestMessageScene implements Cloneable { */ private Integer priority = 0; + public TestMessageScene(MessageScene scene, String requestUrl, String requestMessage, Integer dataId, Boolean complexFlag) { super(); @@ -123,8 +123,8 @@ public class TestMessageScene implements Cloneable { } - - public void setTestClient(TestClient testClient) { + + public void setTestClient(TestClient testClient) { this.testClient = testClient; } diff --git a/src/main/java/yi/master/coretest/message/test/performance/PerformanceTestObject.java b/src/main/java/yi/master/coretest/message/test/performance/PerformanceTestObject.java index 7d22817..713eff0 100644 --- a/src/main/java/yi/master/coretest/message/test/performance/PerformanceTestObject.java +++ b/src/main/java/yi/master/coretest/message/test/performance/PerformanceTestObject.java @@ -30,7 +30,7 @@ import yi.master.coretest.message.test.TestMessageScene; import yi.master.util.FrameworkUtil; import yi.master.util.PracticalUtils; import yi.master.util.cache.CacheUtil; -import yi.master.util.cache.CustomSettingVariable; +import yi.master.constant.CustomSettingVariable; import yi.master.util.excel.PoiExcelUtil; import yi.master.util.jsonlib.JsonDateValueProcessor; diff --git a/src/main/java/yi/master/listener/InitWebListener.java b/src/main/java/yi/master/listener/InitWebListener.java index 15d2da4..0f59695 100644 --- a/src/main/java/yi/master/listener/InitWebListener.java +++ b/src/main/java/yi/master/listener/InitWebListener.java @@ -17,7 +17,7 @@ import yi.master.coretest.message.test.mock.MockServer; import yi.master.coretest.task.JobManager; import yi.master.util.FrameworkUtil; import yi.master.util.cache.CacheUtil; -import yi.master.util.cache.CustomSettingVariable; +import yi.master.constant.CustomSettingVariable; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; diff --git a/src/main/webapp/resource/message/messageScene.js b/src/main/webapp/resource/message/messageScene.js index dea686e..fb93ddb 100644 --- a/src/main/webapp/resource/message/messageScene.js +++ b/src/main/webapp/resource/message/messageScene.js @@ -297,6 +297,7 @@ var eventList = { currentScene = data; layer_show("编辑配置信息", templates["scene-show-variables"](data.config), null, null, 1, function () { $("#errorExecFlag").val(data.config.errorExecFlag); + $("#async").val(data.config.async); //显示测试环境 if (strIsNotEmpty($("#systemId").val())) { $.post(REQUEST_URL.BUSINESS_SYSTEM.GET, {id:$("#systemId").val()}, function(json) { @@ -315,6 +316,7 @@ var eventList = { updateConfig["intervalTime"] = Number($("#intervalTime").val()); updateConfig["errorExecFlag"] = $("#errorExecFlag").val(); updateConfig["systemId"] = $("#systemId").val(); + updateConfig["async"] = $("#async").val(); updateConfig["useVariables"] = {}; updateConfig["saveVariables"] = {}; $.each($("#save-variables").siblings('div').children(".edit-this-variables"), function(i, n) { diff --git a/src/main/webapp/resource/template/customTemplate.htm b/src/main/webapp/resource/template/customTemplate.htm index 3a001c5..1be8797 100644 --- a/src/main/webapp/resource/template/customTemplate.htm +++ b/src/main/webapp/resource/template/customTemplate.htm @@ -524,6 +524,17 @@ +
+ +
+ + + +
+

@@ -534,6 +545,8 @@

重试次数:测试失败时的重试次数,默认0次。

测试间隔:该场景测试完成之好的睡眠时间,默认1秒,单位毫秒。

失败处理:该场景测试失败之后的处理。

+

异步执行:异步执行该场景。(所有标注了异步执行的场景都将统一在最后统一并发执行,避免在异步执行的场景中设置保存变量,因为可能在获取之前就被使用了)

+

注意:设置了异步执行,则 测试间隔失败处理 这两处配置将无效!

diff --git a/update.md b/update.md index 8c3bde1..39e3f45 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,10 @@ +### v1.1.0 +#### 2020.3.9 +- ~~新增:项目管理模块~~; +- 新增:组合场景中的场景可设置异步执行方式,节省执行时间; + + + ### v1.0.4 #### 2020.3.9 - 修复:性能测试结果文件保存出错的问题; -- Gitee From f4baf14f007427414b1d8d8f5d8f6be950cb0ab0 Mon Sep 17 00:00:00 2001 From: xuwangcheng Date: Tue, 10 Mar 2020 14:10:40 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- update.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/update.md b/update.md index 39e3f45..37bdd0e 100644 --- a/update.md +++ b/update.md @@ -1,17 +1,11 @@ -### v1.1.0 -#### 2020.3.9 -- ~~新增:项目管理模块~~; -- 新增:组合场景中的场景可设置异步执行方式,节省执行时间; - - - ### v1.0.4 #### 2020.3.9 - 修复:性能测试结果文件保存出错的问题; - 修复:在测试集测试、性能测试中,如果接口中使用了动态接口全局变量时会出现无法获取到正确的值的错误; - 新增:对路径参数的支持,在接口、报文、场景的请求路径中使用#参数path#,例如/get/#id#或者/query/#root.id#; - 更新:非对象数组参数可自定义数据,例如{"root":{"bb":[22,33]}},其中bb数组可在测试数据中被定义,可设置值22,33,44或者"aa","bb"(字符串需加上双引号); -- 新增:在组合场景中可以通过关联规则来获取保存变量。 +- 新增:在组合场景中可以通过关联规则来获取保存变量; +- 新增:组合场景中的场景可设置异步执行方式,节省执行时间。 ### v1.0.3 #### 2020.2.27 -- Gitee