refactor(接口测试): 执行结果处理优化,嵌套事务拆分成小事务

--story=1010710 --user=赵勇 【bug转需求】优化消息通知机制(同步发1.20) https://www.tapd.cn/55049933/s/1315550
This commit is contained in:
fit2-zhao 2022-12-14 12:16:35 +08:00 committed by fit2-zhao
parent 0f174ae73e
commit 22eecf4335
3 changed files with 105 additions and 127 deletions

View File

@ -535,7 +535,7 @@
<artifactItem>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.2</version>
<version>2.7.0</version>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>src/main/resources/jmeter/lib/ext</outputDirectory>

View File

@ -1,7 +1,6 @@
package io.metersphere.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.dto.RequestResultExpandDTO;
import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
@ -9,13 +8,15 @@ import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.OrderRequest;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ExecuteResult;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ResponseUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.dto.RequestResult;
import io.metersphere.dto.ResultDTO;
import io.metersphere.notice.sender.NoticeModel;
import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.track.dto.PlanReportCaseDTO;
import io.metersphere.track.dto.TestPlanDTO;
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
@ -23,7 +24,6 @@ import io.metersphere.track.request.testcase.TrackCount;
import io.metersphere.track.service.TestCaseReviewApiCaseService;
import io.metersphere.track.service.TestPlanTestCaseService;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
@ -58,51 +58,35 @@ public class ApiDefinitionExecResultService {
@Resource
private TestCaseReviewApiCaseMapper testCaseReviewApiCaseMapper;
@Resource
private NoticeSendService noticeSendService;
@Resource
private TestPlanTestCaseService testPlanTestCaseService;
@Resource
private ApiTestCaseService apiTestCaseService;
@Resource
private UserMapper userMapper;
@Resource
private ProjectMapper projectMapper;
@Resource
private SqlSessionFactory sqlSessionFactory;
public void saveApiResult(ResultDTO dto) {
public List<ApiDefinitionExecResult> saveApiResult(ResultDTO dto) {
LoggerUtil.info("接收到API/CASE执行结果【 " + dto.getRequestResults().size() + " 】条");
List<ApiDefinitionExecResult> results = new LinkedList<>();
for (RequestResult item : dto.getRequestResults()) {
if (item.getResponseResult() != null && item.getResponseResult().getResponseTime() <= 0) {
item.getResponseResult().setResponseTime((item.getEndTime() - item.getStartTime()));
}
if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) {
ApiDefinitionExecResult result = this.editResult(item, dto.getReportId(), dto.getConsole(), dto.getRunMode(), dto.getTestId(), null);
if (result != null) {
User user = null;
if (MapUtils.isNotEmpty(dto.getExtendedParameters())) {
if (dto.getExtendedParameters().containsKey("userId") && dto.getExtendedParameters().containsKey("userName")) {
user = new User() {{
this.setId(dto.getExtendedParameters().get("userId").toString());
this.setName(dto.getExtendedParameters().get("userName").toString());
}};
} else if (dto.getExtendedParameters().containsKey("userId")) {
result.setUserId(dto.getExtendedParameters().get("userId").toString());
}
}
// 发送通知
if (result != null && !StringUtils.startsWithAny(dto.getRunMode(), "SCHEDULE","API_PLAN")) {
result.setResourceId(dto.getTestId());
LoggerUtil.info("执行结果【 " + result.getName() + " 】入库存储完成");
sendNotice(result, user);
results.add(result);
}
}
}
return results;
}
public void batchSaveApiResult(List<ResultDTO> resultDTOS, boolean isSchedule) {
public Map<ResultDTO, List<ApiDefinitionExecResult>> batchSaveApiResult(List<ResultDTO> resultDTOS, boolean isSchedule) {
Map<ResultDTO, List<ApiDefinitionExecResult>> results = new HashMap<>();
if (CollectionUtils.isEmpty(resultDTOS)) {
return;
return results;
}
LoggerUtil.info("接收到API/CASE执行结果【 " + resultDTOS.size() + "");
@ -111,8 +95,8 @@ public class ApiDefinitionExecResultService {
TestPlanApiCaseMapper planApiCaseMapper = sqlSession.getMapper(TestPlanApiCaseMapper.class);
TestCaseReviewApiCaseMapper reviewApiCaseMapper = sqlSession.getMapper(TestCaseReviewApiCaseMapper.class);
ApiTestCaseMapper batchApiTestCaseMapper = sqlSession.getMapper(ApiTestCaseMapper.class);
for (ResultDTO dto : resultDTOS) {
List<ApiDefinitionExecResult> resultList = new LinkedList<>();
if (CollectionUtils.isNotEmpty(dto.getRequestResults())) {
for (RequestResult item : dto.getRequestResults()) {
if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) {
@ -123,78 +107,27 @@ public class ApiDefinitionExecResultService {
planApiCaseMapper, reviewApiCaseMapper, batchApiTestCaseMapper
);
if (result != null && !StringUtils.startsWithAny(dto.getRunMode(), "SCHEDULE")) {
User user = null;
if (MapUtils.isNotEmpty(dto.getExtendedParameters()) && dto.getExtendedParameters().containsKey("user") && dto.getExtendedParameters().get("user") instanceof User) {
user = (User) dto.getExtendedParameters().get("user");
} else if (MapUtils.isNotEmpty(dto.getExtendedParameters()) && dto.getExtendedParameters().containsKey("userId")) {
result.setUserId(String.valueOf(dto.getExtendedParameters().get("userId")));
}
if (result != null && !StringUtils.startsWithAny(dto.getRunMode(), "SCHEDULE","API_PLAN")) {
// 发送通知
result.setResourceId(dto.getTestId());
sendNotice(result, user);
resultList.add(result);
}
}
}
if (isSchedule) {
// 这个方法得优化大批量跑有问题
updateTestCaseStates(dto.getTestId());
Map<String, String> apiIdResultMap = new HashMap<>();
long errorSize = dto.getRequestResults().stream().filter(requestResult -> requestResult.getError() > 0).count();
String status = errorSize > 0 || dto.getRequestResults().isEmpty() ? TestPlanApiExecuteStatus.FAILD.name() : TestPlanApiExecuteStatus.SUCCESS.name();
if (StringUtils.isNotEmpty(dto.getReportId())) {
apiIdResultMap.put(dto.getReportId(), status);
}
LoggerUtil.info("TestPlanReportId[" + dto.getTestPlanReportId() + "] API CASE OVER. API CASE STATUS:" + JSONObject.toJSONString(apiIdResultMap));
}
}
if (CollectionUtils.isNotEmpty(resultList)) {
results.put(dto, resultList);
}
}
sqlSession.flushStatements();
if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
}
private void sendNotice(ApiDefinitionExecResult result, User user) {
try {
String resourceId = result.getResourceId();
ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(resourceId);
// 接口定义直接执行不发通知
if (apiTestCaseWithBLOBs == null) {
return;
}
BeanMap beanMap = new BeanMap(apiTestCaseWithBLOBs);
String event;
String status;
if (StringUtils.equals(result.getStatus(), "success")) {
event = NoticeConstants.Event.EXECUTE_SUCCESSFUL;
status = "成功";
} else {
event = NoticeConstants.Event.EXECUTE_FAILED;
status = "失败";
}
if (user == null && StringUtils.isNotBlank(result.getUserId())) {
user = userMapper.selectByPrimaryKey(result.getUserId());
}
Map paramMap = new HashMap<>(beanMap);
paramMap.put("operator", user != null ? user.getName() : result.getUserId());
paramMap.put("status", result.getStatus());
String context = "${operator}执行接口用例" + status + ": ${name}";
NoticeModel noticeModel = NoticeModel.builder()
.operator(result.getUserId() != null ? result.getUserId() : SessionUtils.getUserId())
.context(context)
.subject("接口用例通知")
.paramMap(paramMap)
.event(event)
.build();
String taskType = NoticeConstants.TaskType.API_DEFINITION_TASK;
Project project = projectMapper.selectByPrimaryKey(apiTestCaseWithBLOBs.getProjectId());
noticeSendService.send(project, taskType, noticeModel);
} catch (Exception e) {
LogUtil.error("消息发送失败:" + e.getMessage());
}
return results;
}
public void setExecResult(String id, String status, Long time) {
@ -336,12 +269,6 @@ public class ApiDefinitionExecResultService {
}
}
updateTestCaseStates(dto.getTestId());
Map<String, String> apiIdResultMap = new HashMap<>();
long errorSize = dto.getRequestResults().stream().filter(requestResult -> requestResult.getError() > 0).count();
String status = errorSize > 0 || dto.getRequestResults().isEmpty() ? TestPlanApiExecuteStatus.FAILD.name() : TestPlanApiExecuteStatus.SUCCESS.name();
if (StringUtils.isNotEmpty(dto.getReportId())) {
apiIdResultMap.put(dto.getReportId(), status);
}
}
@ -507,30 +434,12 @@ public class ApiDefinitionExecResultService {
}
}
public ApiDefinitionExecResult getInfo(String id) {
return apiDefinitionExecResultMapper.selectByPrimaryKey(id);
}
public List<PlanReportCaseDTO> selectForPlanReport(List<String> apiReportIds) {
if (CollectionUtils.isEmpty(apiReportIds))
return new ArrayList<>();
return extApiDefinitionExecResultMapper.selectForPlanReport(apiReportIds);
}
private static List<OrderRequest> getDefaultOrderByField(String prefix, List<OrderRequest> orders, String field) {
if (orders == null || orders.size() < 1) {
OrderRequest orderRequest = new OrderRequest();
orderRequest.setName(field);
orderRequest.setType("desc");
if (StringUtils.isNotBlank(prefix)) {
orderRequest.setPrefix(prefix);
}
orders = new ArrayList<>();
orders.add(orderRequest);
return orders;
}
return orders;
}
public List<ApiDefinitionExecResultExpand> apiReportList(QueryAPIReportRequest request) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders(), "end_time"));

View File

@ -2,9 +2,7 @@ package io.metersphere.api.service;
import io.metersphere.api.dto.automation.ApiTestReportVariable;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.UiScenarioMapper;
import io.metersphere.base.mapper.*;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.NoticeConstants;
@ -12,11 +10,11 @@ import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.RequestResult;
import io.metersphere.dto.ResultDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.notice.sender.NoticeModel;
import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.SystemParameterService;
@ -26,16 +24,14 @@ import io.metersphere.track.service.TestPlanScenarioCaseService;
import io.metersphere.track.service.TestPlanTestCaseService;
import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestResultService {
@Resource
private ApiDefinitionExecResultService apiDefinitionExecResultService;
@ -59,6 +55,14 @@ public class TestResultService {
private ApiEnvironmentRunningParamService apiEnvironmentRunningParamService;
@Resource
private RedisTemplateService redisTemplateService;
@Resource
private NoticeSendService noticeSendService;
@Resource
private UserMapper userMapper;
@Resource
private ProjectMapper projectMapper;
@Resource
private ApiTestCaseMapper apiTestCaseMapper;
// 场景
private static final List<String> scenarioRunModes = new ArrayList<>() {{
@ -93,6 +97,48 @@ public class TestResultService {
}};
private void sendNotice(ApiDefinitionExecResult result, User user) {
try {
String resourceId = result.getResourceId();
ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(resourceId);
// 接口定义直接执行不发通知
if (apiTestCaseWithBLOBs == null) {
return;
}
BeanMap beanMap = new BeanMap(apiTestCaseWithBLOBs);
String event;
String status;
if (StringUtils.equals(result.getStatus(), "success")) {
event = NoticeConstants.Event.EXECUTE_SUCCESSFUL;
status = "成功";
} else {
event = NoticeConstants.Event.EXECUTE_FAILED;
status = "失败";
}
if (user == null && StringUtils.isNotBlank(result.getUserId())) {
user = userMapper.selectByPrimaryKey(result.getUserId());
}
Map paramMap = new HashMap<>(beanMap);
paramMap.put("operator", user != null ? user.getName() : result.getUserId());
paramMap.put("status", result.getStatus());
String context = "${operator}执行接口用例" + status + ": ${name}";
NoticeModel noticeModel = NoticeModel.builder()
.operator(result.getUserId() != null ? result.getUserId() : SessionUtils.getUserId())
.context(context)
.subject("接口用例通知")
.paramMap(paramMap)
.event(event)
.build();
String taskType = NoticeConstants.TaskType.API_DEFINITION_TASK;
Project project = projectMapper.selectByPrimaryKey(apiTestCaseWithBLOBs.getProjectId());
noticeSendService.send(project, taskType, noticeModel);
} catch (Exception e) {
LogUtil.error("消息发送失败:" + e.getMessage());
}
}
/**
* 执行结果存储
*
@ -114,7 +160,8 @@ public class TestResultService {
apiDefinitionExecResultService.saveApiResultByScheduleTask(dto);
} else if (caseRunModes.contains(dto.getRunMode())) {
// 手动触发/批量触发 用例结果处理
apiDefinitionExecResultService.saveApiResult(dto);
List<ApiDefinitionExecResult> results = apiDefinitionExecResultService.saveApiResult(dto);
sendMessage(results, dto);
} else if (scenarioRunModes.contains(dto.getRunMode())) {
// 场景报告结果处理
apiScenarioReportService.saveResult(dto);
@ -125,6 +172,23 @@ public class TestResultService {
updateTestCaseStates(dto.getRequestResults(), dto.getRunMode());
}
private void sendMessage(List<ApiDefinitionExecResult> results, ResultDTO dto) {
results.forEach(result -> {
User user = null;
if (MapUtils.isNotEmpty(dto.getExtendedParameters())) {
if (dto.getExtendedParameters().containsKey("userId") && dto.getExtendedParameters().containsKey("userName")) {
user = new User() {{
this.setId(dto.getExtendedParameters().get("userId").toString());
this.setName(dto.getExtendedParameters().get("userName").toString());
}};
} else if (dto.getExtendedParameters().containsKey("userId")) {
result.setUserId(dto.getExtendedParameters().get("userId").toString());
}
}
sendNotice(result, user);
});
}
/**
* 批量存储执行结果
*
@ -134,8 +198,7 @@ public class TestResultService {
// 处理环境
List<String> environmentList = new LinkedList<>();
for (String key : resultDtoMap.keySet()) {
List<ResultDTO> dtos = resultDtoMap.get(key);
for (ResultDTO dto : dtos) {
for (ResultDTO dto : resultDtoMap.get(key)) {
if (dto.getArbitraryData() != null && dto.getArbitraryData().containsKey("ENV")) {
environmentList = (List<String>) dto.getArbitraryData().get("ENV");
}
@ -148,11 +211,17 @@ public class TestResultService {
}
//测试计划定时任务-接口执行逻辑的话需要同步测试计划的报告数据
if (StringUtils.equals(key, "schedule-task")) {
apiDefinitionExecResultService.batchSaveApiResult(dtos, true);
Map<ResultDTO, List<ApiDefinitionExecResult>> results = apiDefinitionExecResultService.batchSaveApiResult(resultDtoMap.get(key), true);
for (ResultDTO dto : results.keySet()) {
sendMessage(results.get(dto), dto);
}
} else if (StringUtils.equals(key, "api-test-case-task")) {
apiDefinitionExecResultService.batchSaveApiResult(dtos, false);
Map<ResultDTO, List<ApiDefinitionExecResult>> results = apiDefinitionExecResultService.batchSaveApiResult(resultDtoMap.get(key), false);
for (ResultDTO dto : results.keySet()) {
sendMessage(results.get(dto), dto);
}
} else if (StringUtils.equalsAny(key, "api-scenario-task")) {
apiScenarioReportService.batchSaveResult(dtos);
apiScenarioReportService.batchSaveResult(resultDtoMap.get(key));
}
}