From 350816070acfc9aada4bc161195997fe06aab7e2 Mon Sep 17 00:00:00 2001 From: wxg0103 <727495428@qq.com> Date: Fri, 19 May 2023 11:35:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=84=9A=E6=9C=AC=E5=A2=9E=E5=8A=A0=E7=AB=99?= =?UTF-8?q?=E5=86=85=E9=80=9A=E7=9F=A5=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --story=1012023 --user=王孝刚 接口脚本增加预警审核机制 https://www.tapd.cn/55049933/s/1373138 --- .../dto/definition/request/ElementUtil.java | 53 ++++++++++ .../definition/ApiTestCaseService.java | 67 ++++++++++++- .../service/scenario/ApiScenarioService.java | 26 ++++- .../component/ApiScenarioComponent.vue | 1 + .../definition/components/step/JmxStep.vue | 1 + .../frontend/src/components/notice/util.js | 16 ++- .../frontend/src/i18n/lang/en-US.js | 2 + .../frontend/src/i18n/lang/zh-CN.js | 2 + .../frontend/src/i18n/lang/zh-TW.js | 2 + .../commons/constants/NoticeConstants.java | 1 + .../constants/ProjectApplicationType.java | 8 ++ .../io/metersphere/dto/ProjectConfig.java | 2 + .../service/BaseEnvironmentService.java | 98 +++++++++++++++++++ .../frontend/src/api/app-setting.js | 5 + .../frontend/src/api/environment.js | 4 + .../src/business/menu/appmanage/AppManage.vue | 25 ++++- .../menu/appmanage/ReviewerConfig.vue | 27 +++-- .../menu/environment/EnvironmentList.vue | 15 ++- .../business/menu/function/EditFunction.vue | 29 +++++- .../frontend/src/i18n/lang/en-US.js | 4 + .../frontend/src/i18n/lang/zh-CN.js | 4 + .../frontend/src/i18n/lang/zh-TW.js | 4 + 22 files changed, 378 insertions(+), 18 deletions(-) diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java index fb13c1d6f8..f1f47613eb 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java @@ -43,6 +43,7 @@ import io.metersphere.metadata.service.FileMetadataService; import io.metersphere.plugin.core.MsParameter; import io.metersphere.plugin.core.MsTestElement; import io.metersphere.request.BodyFile; +import io.metersphere.service.MsHashTreeService; import io.metersphere.utils.LoggerUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; @@ -81,6 +82,13 @@ public class ElementUtil { private static final String ASSERTIONS = ElementConstants.ASSERTIONS; private static final String BODY_FILE_DIR = FileUtils.BODY_FILE_DIR; private static final String TEST_BEAN_GUI = "TestBeanGUI"; + public final static List scriptList = new ArrayList() {{ + this.add(ElementConstants.JSR223); + this.add(ElementConstants.JSR223_PRE); + this.add(ElementConstants.JSR223_POST); + }}; + + public static Map getEnvironmentConfig(String environmentId, String projectId) { @@ -1099,4 +1107,49 @@ public class ElementUtil { } return false; } + + public static Map scriptMap(String request) { + Map map = new HashMap<>(); + if (StringUtils.isBlank(request)) { + return map; + } + JSONObject element = JSONUtil.parseObject(request); + toMap(element.getJSONArray(ElementConstants.HASH_TREE), scriptList, map); + return map; + } + + private static void toMap(JSONArray hashTree, List scriptList, Map map) { + for (int i = 0; i < hashTree.length(); i++) { + JSONObject element = hashTree.optJSONObject(i); + if (element == null) { + continue; + } + if (scriptList.contains(element.optString(ElementConstants.TYPE))) { + JSONObject elementTarget = JSONUtil.parseObject(element.toString()); + if (elementTarget.has(ElementConstants.HASH_TREE)) { + elementTarget.remove(ElementConstants.HASH_TREE); + } + map.put(StringUtils.join(element.optString(MsHashTreeService.ID), + element.optString(MsHashTreeService.INDEX)), + elementTarget.toString()); + } + if (element.has(ElementConstants.HASH_TREE)) { + JSONArray elementJSONArray = element.optJSONArray(ElementConstants.HASH_TREE); + toMap(elementJSONArray, scriptList, map); + } + } + } + + public static boolean isSend(Map org, Map target) { + if (org.size() != target.size() && target.size() > 0) { + return true; + } + for (Map.Entry entry : org.entrySet()) { + if (target.containsKey(entry.getKey()) && !StringUtils.equals(entry.getValue(), target.get(entry.getKey()))) { + return true; + } + } + return false; + } + } diff --git a/api-test/backend/src/main/java/io/metersphere/service/definition/ApiTestCaseService.java b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiTestCaseService.java index 4abf0b6c83..c29fd4f40f 100644 --- a/api-test/backend/src/main/java/io/metersphere/service/definition/ApiTestCaseService.java +++ b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiTestCaseService.java @@ -28,9 +28,12 @@ import io.metersphere.log.utils.ReflexObjectUtil; import io.metersphere.log.vo.DetailColumn; import io.metersphere.log.vo.OperatingLogDetails; import io.metersphere.log.vo.api.DefinitionReference; +import io.metersphere.notice.service.NotificationService; import io.metersphere.plugin.core.MsTestElement; import io.metersphere.request.OrderRequest; import io.metersphere.request.ResetOrderRequest; +import io.metersphere.service.BaseProjectApplicationService; +import io.metersphere.service.BaseProjectService; import io.metersphere.service.BaseUserService; import io.metersphere.service.ServiceUtils; import io.metersphere.service.ext.ExtFileAssociationService; @@ -104,11 +107,18 @@ public class ApiTestCaseService { private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper; @Resource private ExtApiScenarioReferenceIdMapper extApiScenarioReferenceIdMapper; + @Resource + private BaseProjectApplicationService baseProjectApplicationService; + @Resource + private NotificationService notificationService; + @Resource + private BaseProjectService baseProjectService; + + private static final String BODY_FILE_DIR = FileUtils.BODY_FILE_DIR; private static final String DEFAULT_TIME_DATE = "-3D"; - //查询测试用例详情 public ApiTestCaseWithBLOBs getInfoJenkins(String id) { ApiTestCaseWithBLOBs apiTest = apiTestCaseMapper.selectByPrimaryKey(id); @@ -395,6 +405,7 @@ public class ApiTestCaseService { request.setRequest(tcpApiParamService.parseMsTestElement(request.getRequest())); final ApiTestCaseWithBLOBs test = apiTestCaseMapper.selectByPrimaryKey(request.getId()); if (test != null) { + String requestOrg = test.getRequest(); test.setName(request.getName()); test.setCaseStatus(request.getCaseStatus()); if (StringUtils.isEmpty(request.getCaseStatus())) { @@ -420,6 +431,14 @@ public class ApiTestCaseService { } apiTestCaseMapper.updateByPrimaryKeySelective(test); saveFollows(test.getId(), request.getFollows()); + this.checkAndSendReviewMessage(test.getId(), + test.getName(), + test.getProjectId(), + "接口用例通知", + NoticeConstants.TaskType.API_DEFINITION_TASK, + requestOrg, + test.getRequest() + ); } // 存储附件关系 extFileAssociationService.saveApi(test.getId(), request.getRequest(), FileAssociationTypeEnums.CASE.name()); @@ -486,6 +505,14 @@ public class ApiTestCaseService { apiTestCaseMapper.insert(test); saveFollows(test.getId(), request.getFollows()); } + this.checkAndSendReviewMessage(test.getId(), + test.getName(), + test.getProjectId(), + "接口用例通知", + NoticeConstants.TaskType.API_DEFINITION_TASK, + null, + test.getRequest() + ); // 存储附件关系 extFileAssociationService.saveApi(test.getId(), request.getRequest(), FileAssociationTypeEnums.CASE.name()); return test; @@ -1306,4 +1333,42 @@ public class ApiTestCaseService { return extApiTestCaseMapper.findPassRateById(id); } + + //检查并发送脚本审核的通知 + @Async + public void checkAndSendReviewMessage(String id, + String name, + String projectId, + String title, + String resourceType, + String requestOrg, + String requestTarget) { + ProjectApplication reviewLoadTestScript = baseProjectApplicationService.getProjectApplication( + projectId, ProjectApplicationType.API_REVIEW_TEST_SCRIPT.name()); + if (BooleanUtils.toBoolean(reviewLoadTestScript.getTypeValue())) { + ProjectApplication reviewerConfig = baseProjectApplicationService.getProjectApplication( + projectId, ProjectApplicationType.API_SCRIPT_REVIEWER.name()); + if (StringUtils.isNotEmpty(reviewerConfig.getTypeValue()) && + baseProjectService.isProjectMember(projectId, reviewerConfig.getTypeValue())) { + Map org = ElementUtil.scriptMap(requestOrg); + Map target = ElementUtil.scriptMap(requestTarget); + boolean isSend = ElementUtil.isSend(org, target); + if (isSend) { + Notification notification = new Notification(); + notification.setTitle(title); + notification.setOperator(SessionUtils.getUserId()); + notification.setOperation(NoticeConstants.Event.REVIEW); + notification.setResourceId(id); + notification.setResourceName(name); + notification.setResourceType(resourceType); + notification.setType(NotificationConstants.Type.SYSTEM_NOTICE.name()); + notification.setStatus(NotificationConstants.Status.UNREAD.name()); + notification.setCreateTime(System.currentTimeMillis()); + notification.setReceiver(reviewerConfig.getTypeValue()); + notificationService.sendAnnouncement(notification); + } + } + } + } + } diff --git a/api-test/backend/src/main/java/io/metersphere/service/scenario/ApiScenarioService.java b/api-test/backend/src/main/java/io/metersphere/service/scenario/ApiScenarioService.java index ac6adc1f7b..9169e3a5ff 100644 --- a/api-test/backend/src/main/java/io/metersphere/service/scenario/ApiScenarioService.java +++ b/api-test/backend/src/main/java/io/metersphere/service/scenario/ApiScenarioService.java @@ -51,6 +51,7 @@ import io.metersphere.request.ResetOrderRequest; import io.metersphere.sechedule.ApiScenarioTestJob; import io.metersphere.sechedule.SwaggerUrlImportJob; import io.metersphere.service.*; +import io.metersphere.service.definition.ApiTestCaseService; import io.metersphere.service.definition.TcpApiParamService; import io.metersphere.service.ext.ExtApiScheduleService; import io.metersphere.service.ext.ExtFileAssociationService; @@ -162,6 +163,8 @@ public class ApiScenarioService { private BaseQuotaService baseQuotaService; @Resource private ApiAutomationRelationshipEdgeService apiAutomationRelationshipEdgeService; + @Resource + private ApiTestCaseService apiTestCaseService; private ThreadLocal currentScenarioOrder = new ThreadLocal<>(); @@ -266,7 +269,7 @@ public class ApiScenarioService { extApiScenarioMapper.removeToGcByExample(example); } - public ApiScenario create(SaveApiScenarioRequest request, List bodyFiles, List scenarioFiles) { + public ApiScenarioWithBLOBs create(SaveApiScenarioRequest request, List bodyFiles, List scenarioFiles) { checkQuota(request.getProjectId()); request.setId(UUID.randomUUID().toString()); if (request.getScenarioDefinition() == null) { @@ -294,6 +297,15 @@ public class ApiScenarioService { apiScenarioReferenceIdService.saveApiAndScenarioRelation(scenario); // 存储依赖关系 apiAutomationRelationshipEdgeService.initRelationshipEdge(null, scenario); + apiTestCaseService.checkAndSendReviewMessage( + scenario.getId(), + scenario.getName(), + scenario.getProjectId(), + "场景用例通知", + NoticeConstants.TaskType.API_AUTOMATION_TASK, + null, + scenario.getScenarioDefinition() + ); uploadFiles(request, bodyFiles, scenarioFiles); return scenario; @@ -352,7 +364,7 @@ public class ApiScenarioService { } } - public ApiScenario update(SaveApiScenarioRequest request, List bodyFiles, List scenarioFiles) { + public ApiScenarioWithBLOBs update(SaveApiScenarioRequest request, List bodyFiles, List scenarioFiles) { checkNameExist(request, false); checkScenarioNum(request); //如果场景有TCP步骤的话,也要做参数计算处理 @@ -399,6 +411,16 @@ public class ApiScenarioService { // 存储依赖关系 apiAutomationRelationshipEdgeService.initRelationshipEdge(beforeScenario, scenario); + apiTestCaseService.checkAndSendReviewMessage( + scenario.getId(), + scenario.getName(), + scenario.getProjectId(), + "场景用例通知", + NoticeConstants.TaskType.API_AUTOMATION_TASK, + beforeScenario.getScenarioDefinition(), + scenario.getScenarioDefinition() + ); + String defaultVersion = baseProjectVersionMapper.getDefaultVersion(request.getProjectId()); if (StringUtils.equalsIgnoreCase(request.getVersionId(), defaultVersion)) { checkAndSetLatestVersion(beforeScenario.getRefId()); diff --git a/api-test/frontend/src/business/automation/scenario/component/ApiScenarioComponent.vue b/api-test/frontend/src/business/automation/scenario/component/ApiScenarioComponent.vue index feb7dba16b..7055029210 100644 --- a/api-test/frontend/src/business/automation/scenario/component/ApiScenarioComponent.vue +++ b/api-test/frontend/src/business/automation/scenario/component/ApiScenarioComponent.vue @@ -291,6 +291,7 @@ export default { arr[i].disabled = disabled; arr[i].isCopy = false; arr[i].projectId = this.calcProjectId(arr[i].projectId, id); + arr[i].notAddStep = true; // 处理子请求环境 let typeArray = ['JDBCPostProcessor', 'JDBCSampler', 'JDBCPreProcessor']; if (typeArray.indexOf(arr[i].type) !== -1) { diff --git a/api-test/frontend/src/business/definition/components/step/JmxStep.vue b/api-test/frontend/src/business/definition/components/step/JmxStep.vue index fcffc3151b..136cf634ce 100644 --- a/api-test/frontend/src/business/definition/components/step/JmxStep.vue +++ b/api-test/frontend/src/business/definition/components/step/JmxStep.vue @@ -13,6 +13,7 @@ @click="add" type="primary" v-if="tabType !== 'assertionsRule'" + :disabled="request.notAddStep" style="background-color: var(--primary_color); border-color: var(--primary_color)"> {{ $t('api_test.request.assertions.add') }} diff --git a/framework/sdk-parent/frontend/src/components/notice/util.js b/framework/sdk-parent/frontend/src/components/notice/util.js index 255a7713ae..aab3706db1 100644 --- a/framework/sdk-parent/frontend/src/components/notice/util.js +++ b/framework/sdk-parent/frontend/src/components/notice/util.js @@ -14,6 +14,16 @@ export function getResource(d) { let resourceType = i18n.t('notice.resource.' + d.resourceType); if (!d.operation.startsWith('EXECUTE_')) { + if (d.operation.startsWith('REVIEW') && d.resourceType === 'API_DEFINITION_TASK') { + resourceType = i18n.t('notice.api_case'); + } + if (d.operation.startsWith('REVIEW') && d.resourceType === 'API_AUTOMATION_TASK') { + resourceType = i18n.t('notice.scenario_case'); + } + if (d.operation.startsWith('REVIEW') && d.resourceType === 'ENV_TASK') { + resourceType = i18n.t('notice.env_task'); + } + return resourceType; } switch (d.resourceType) { @@ -90,7 +100,9 @@ export function getUrl(d) { url += "/api/definition?caseId=" + d.resourceId; } else if (d.operation.startsWith('MOCK_')) { url += "/api/definition?mockId=" + d.resourceId; - } else { + }else if (d.operation.startsWith('REVIEW')) { + url += "/api/definition?caseId=" + d.resourceId; + }else { url += "/api/definition?resourceId=" + d.resourceId; } break; @@ -115,6 +127,8 @@ export function getUrl(d) { case "TRACK_REPORT_TASK" : url += "/track/testPlan/reportList"; break; + case"ENV_TASK" : + url += "/project/env?resourceId=" + d.resourceId; default: break; } diff --git a/framework/sdk-parent/frontend/src/i18n/lang/en-US.js b/framework/sdk-parent/frontend/src/i18n/lang/en-US.js index 8988981363..5662365c06 100644 --- a/framework/sdk-parent/frontend/src/i18n/lang/en-US.js +++ b/framework/sdk-parent/frontend/src/i18n/lang/en-US.js @@ -2710,6 +2710,8 @@ const message = { EXECUTE_COMPLETED: " Completed", }, api_case: "API Case", + scenario_case: "Scenario Case", + env_task: "Environment", }, permission: { common: { diff --git a/framework/sdk-parent/frontend/src/i18n/lang/zh-CN.js b/framework/sdk-parent/frontend/src/i18n/lang/zh-CN.js index de22372a07..696e00f470 100644 --- a/framework/sdk-parent/frontend/src/i18n/lang/zh-CN.js +++ b/framework/sdk-parent/frontend/src/i18n/lang/zh-CN.js @@ -2615,6 +2615,8 @@ const message = { EXECUTE_COMPLETED: "完成", }, api_case: "接口用例", + scenario_case: "场景用例", + env_task: "环境", }, permission: { common: { diff --git a/framework/sdk-parent/frontend/src/i18n/lang/zh-TW.js b/framework/sdk-parent/frontend/src/i18n/lang/zh-TW.js index 1224d3d441..fc002e208d 100644 --- a/framework/sdk-parent/frontend/src/i18n/lang/zh-TW.js +++ b/framework/sdk-parent/frontend/src/i18n/lang/zh-TW.js @@ -2610,6 +2610,8 @@ const message = { EXECUTE_COMPLETED: "完成", }, api_case: "接口用例", + scenario_case: "場景用例", + env_task: "環境", }, permission: { common: { diff --git a/framework/sdk-parent/sdk/src/main/java/io/metersphere/commons/constants/NoticeConstants.java b/framework/sdk-parent/sdk/src/main/java/io/metersphere/commons/constants/NoticeConstants.java index 809005cbd3..b49c771388 100644 --- a/framework/sdk-parent/sdk/src/main/java/io/metersphere/commons/constants/NoticeConstants.java +++ b/framework/sdk-parent/sdk/src/main/java/io/metersphere/commons/constants/NoticeConstants.java @@ -21,6 +21,7 @@ public interface NoticeConstants { String UI_DEFINITION_TASK = "UI_DEFINITION_TASK"; String UI_HOME_TASK = "UI_HOME_TASK"; String UI_REPORT_TASK = "UI_REPORT_TASK"; + String ENV_TASK = "ENV_TASK"; } interface Mode { diff --git a/framework/sdk-parent/sdk/src/main/java/io/metersphere/commons/constants/ProjectApplicationType.java b/framework/sdk-parent/sdk/src/main/java/io/metersphere/commons/constants/ProjectApplicationType.java index fb6535cdf6..4558dd680f 100644 --- a/framework/sdk-parent/sdk/src/main/java/io/metersphere/commons/constants/ProjectApplicationType.java +++ b/framework/sdk-parent/sdk/src/main/java/io/metersphere/commons/constants/ProjectApplicationType.java @@ -116,4 +116,12 @@ public enum ProjectApplicationType { * 性能测试脚本评审人 */ PERFORMANCE_SCRIPT_REVIEWER, + /** + * 接口测试是否评审脚本 + */ + API_REVIEW_TEST_SCRIPT, + /** + * 接口测试脚本评审人 + */ + API_SCRIPT_REVIEWER, } diff --git a/framework/sdk-parent/sdk/src/main/java/io/metersphere/dto/ProjectConfig.java b/framework/sdk-parent/sdk/src/main/java/io/metersphere/dto/ProjectConfig.java index ab3a32acbc..a93955b7ed 100644 --- a/framework/sdk-parent/sdk/src/main/java/io/metersphere/dto/ProjectConfig.java +++ b/framework/sdk-parent/sdk/src/main/java/io/metersphere/dto/ProjectConfig.java @@ -38,4 +38,6 @@ public class ProjectConfig { private Boolean reReview = false; private String performanceScriptReviewer; private Boolean performanceReviewLoadTestScript = false; + private String apiScriptReviewer; + private Boolean apiReviewTestScript = false; } diff --git a/framework/sdk-parent/sdk/src/main/java/io/metersphere/environment/service/BaseEnvironmentService.java b/framework/sdk-parent/sdk/src/main/java/io/metersphere/environment/service/BaseEnvironmentService.java index 2e1b3e923d..421f6b5f8e 100644 --- a/framework/sdk-parent/sdk/src/main/java/io/metersphere/environment/service/BaseEnvironmentService.java +++ b/framework/sdk-parent/sdk/src/main/java/io/metersphere/environment/service/BaseEnvironmentService.java @@ -6,6 +6,8 @@ import io.metersphere.base.mapper.ext.BaseApiTestEnvironmentMapper; import io.metersphere.base.mapper.ext.BaseEnvironmentGroupMapper; import io.metersphere.base.mapper.ext.ExtApiTestEnvironmentMapper; import io.metersphere.commons.constants.FileAssociationType; +import io.metersphere.commons.constants.NoticeConstants; +import io.metersphere.commons.constants.NotificationConstants; import io.metersphere.commons.constants.ProjectApplicationType; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.*; @@ -19,6 +21,7 @@ import io.metersphere.log.vo.DetailColumn; import io.metersphere.log.vo.OperatingLogDetails; import io.metersphere.log.vo.system.SystemReference; import io.metersphere.metadata.service.FileAssociationService; +import io.metersphere.notice.service.NotificationService; import io.metersphere.request.BodyFile; import io.metersphere.request.variable.ScenarioVariable; import io.metersphere.service.BaseProjectApplicationService; @@ -37,6 +40,7 @@ import org.json.JSONArray; import org.json.JSONObject; import org.mybatis.spring.SqlSessionUtils; import org.springframework.beans.BeanUtils; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -78,8 +82,17 @@ public class BaseEnvironmentService extends NodeTreeService { private BaseProjectService baseProjectService; @Resource private BaseProjectApplicationService baseProjectApplicationService; + @Resource + private NotificationService notificationService; + public static final String MOCK_EVN_NAME = "Mock环境"; + public static final String POST_STEP = "postStepProcessor"; + public static final String PRE_STEP = "preStepProcessor"; + public static final String POST = "postProcessor"; + public static final String PRE = "preProcessor"; + public static final String SCRIPT = "script"; + public BaseEnvironmentService() { super(ApiModuleDTO.class); } @@ -399,6 +412,13 @@ public class BaseEnvironmentService extends NodeTreeService { apiTestEnvironmentMapper.insert(request); // 存储附件关系 saveEnvironment(request.getId(), request.getConfig(), FileAssociationType.ENVIRONMENT.name()); + checkAndSendReviewMessage(request.getId(), + request.getName(), + request.getProjectId(), + NoticeConstants.TaskType.ENV_TASK, + null, + request.getConfig() + ); return request.getId(); } @@ -478,7 +498,17 @@ public class BaseEnvironmentService extends NodeTreeService { apiTestEnvironment.setUpdateTime(System.currentTimeMillis()); // 存储附件关系 saveEnvironment(apiTestEnvironment.getId(), apiTestEnvironment.getConfig(), FileAssociationType.ENVIRONMENT.name()); + ApiTestEnvironmentWithBLOBs envOrg = apiTestEnvironmentMapper.selectByPrimaryKey(apiTestEnvironment.getId()); + apiTestEnvironmentMapper.updateByPrimaryKeyWithBLOBs(apiTestEnvironment); + checkAndSendReviewMessage(apiTestEnvironment.getId(), + apiTestEnvironment.getName(), + apiTestEnvironment.getProjectId(), + NoticeConstants.TaskType.ENV_TASK, + envOrg.getConfig(), + apiTestEnvironment.getConfig() + ); + } public List getNodeTreeByProjectId(String projectId, String protocol) { @@ -974,4 +1004,72 @@ public class BaseEnvironmentService extends NodeTreeService { return new ArrayList<>(); } } + + @Async + public void checkAndSendReviewMessage(String id, + String name, + String projectId, + String resourceType, + String requestOrg, + String requestTarget) { + ProjectApplication reviewLoadTestScript = baseProjectApplicationService.getProjectApplication( + projectId, ProjectApplicationType.API_REVIEW_TEST_SCRIPT.name()); + if (BooleanUtils.toBoolean(reviewLoadTestScript.getTypeValue())) { + ProjectApplication reviewerConfig = baseProjectApplicationService.getProjectApplication( + projectId, ProjectApplicationType.API_SCRIPT_REVIEWER.name()); + if (StringUtils.isNotEmpty(reviewerConfig.getTypeValue()) && + baseProjectService.isProjectMember(projectId, reviewerConfig.getTypeValue())) { + Map org = scriptMap(requestOrg); + Map target = scriptMap(requestTarget); + boolean isSend = isSend(org, target); + if (isSend) { + Notification notification = new Notification(); + notification.setTitle("环境设置"); + notification.setOperator(SessionUtils.getUserId()); + notification.setOperation(NoticeConstants.Event.REVIEW); + notification.setResourceId(id); + notification.setResourceName(name); + notification.setResourceType(resourceType); + notification.setType(NotificationConstants.Type.SYSTEM_NOTICE.name()); + notification.setStatus(NotificationConstants.Status.UNREAD.name()); + notification.setCreateTime(System.currentTimeMillis()); + notification.setReceiver(reviewerConfig.getTypeValue()); + notificationService.sendAnnouncement(notification); + } + } + } + } + + public static Map scriptMap(String request) { + Map configMap = JSON.parseObject(request, Map.class); + Map map = new HashMap<>(); + JSONObject configObj = new JSONObject(configMap); + toMap(map, configObj, POST_STEP, PRE_STEP); + toMap(map, configObj, PRE, POST); + return map; + } + + private static void toMap(Map map, JSONObject configObj, String pre, String post) { + JSONObject preProcessor = configObj.optJSONObject(pre); + if (StringUtils.isNotBlank(preProcessor.optString(SCRIPT))) { + map.put(pre, preProcessor.optString(SCRIPT)); + } + JSONObject postProcessor = configObj.optJSONObject(post); + if (StringUtils.isNotBlank(postProcessor.optString(SCRIPT))) { + map.put(post, postProcessor.optString(SCRIPT)); + } + } + + public static boolean isSend(Map orgMap, Map targetMap) { + if (orgMap.size() != targetMap.size() && + targetMap.size() > 0) { + return true; + } + for (Map.Entry entry : orgMap.entrySet()) { + if (targetMap.containsKey(entry.getKey()) && !StringUtils.equals(entry.getValue(), targetMap.get(entry.getKey()))) { + return true; + } + } + return false; + } } diff --git a/project-management/frontend/src/api/app-setting.js b/project-management/frontend/src/api/app-setting.js index ee90dffd3d..35bc9bd82c 100644 --- a/project-management/frontend/src/api/app-setting.js +++ b/project-management/frontend/src/api/app-setting.js @@ -7,3 +7,8 @@ export function batchModifyAppSetting(params) { export function getProjectAppSetting(projectId) { return get(`/project_application/get/config/${projectId}`); } + +export function getProjectApplicationConfig(projectId, type) { + let url = '/project_application/get/' + projectId + type; + return get(url); +} diff --git a/project-management/frontend/src/api/environment.js b/project-management/frontend/src/api/environment.js index bcf774e5f0..d13daeea75 100644 --- a/project-management/frontend/src/api/environment.js +++ b/project-management/frontend/src/api/environment.js @@ -4,6 +4,10 @@ export function getEnvironmentPages(goPage, pageSize, param) { return post(`/environment/list/${goPage}/${pageSize}`, param); } +export function getEnvironment(id) { + return get(`/environment/get/${id}`); +} + export function getEnvironments(projectId) { return get(`/environment/list/${projectId}`); } diff --git a/project-management/frontend/src/business/menu/appmanage/AppManage.vue b/project-management/frontend/src/business/menu/appmanage/AppManage.vue index ac525160eb..069e81eaf5 100644 --- a/project-management/frontend/src/business/menu/appmanage/AppManage.vue +++ b/project-management/frontend/src/business/menu/appmanage/AppManage.vue @@ -232,6 +232,26 @@ > + + @@ -384,7 +404,8 @@ :name="$t('pj.load_test_script_review')" :popTitle="$t('pj.load_test_script_review_detail')" :reviewers="userInProject" - :config.sync="config" + :reviewer.sync="config.performanceScriptReviewer" + :reviewerSwitch.sync="config.performanceReviewLoadTestScript" @reviewerChange=" switchChange( 'PERFORMANCE_SCRIPT_REVIEWER', @@ -676,6 +697,8 @@ export default { reReview: false, performanceScriptReviewer: "", performanceReviewLoadTestScript: false, + apiScriptReviewer: "", + apiReviewTestScript: false, }, showRuleSetting: false, showSyncTimeSetting: true, diff --git a/project-management/frontend/src/business/menu/appmanage/ReviewerConfig.vue b/project-management/frontend/src/business/menu/appmanage/ReviewerConfig.vue index d13e31db69..fe09ada602 100644 --- a/project-management/frontend/src/business/menu/appmanage/ReviewerConfig.vue +++ b/project-management/frontend/src/business/menu/appmanage/ReviewerConfig.vue @@ -21,7 +21,7 @@