refactor(测试计划): 修改测试计划统计查询方法,增加定时任务的查询

This commit is contained in:
Jianguo-Genius 2024-06-06 14:41:23 +08:00 committed by 建国
parent 051b3acbdd
commit 63168b9067
5 changed files with 102 additions and 23 deletions

View File

@ -19,10 +19,8 @@ import io.metersphere.api.parser.step.StepParser;
import io.metersphere.api.parser.step.StepParserFactory; import io.metersphere.api.parser.step.StepParserFactory;
import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiFileResourceService; import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.service.definition.ApiDefinitionModuleService;
import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.service.queue.ApiExecutionSetService;
import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils; import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
import io.metersphere.functional.domain.FunctionalCaseTestExample; import io.metersphere.functional.domain.FunctionalCaseTestExample;
@ -65,6 +63,7 @@ import io.metersphere.system.service.OperationHistoryService;
import io.metersphere.system.service.UserLoginService; import io.metersphere.system.service.UserLoginService;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator; import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.ScheduleUtils;
import io.metersphere.system.utils.ServiceUtils; import io.metersphere.system.utils.ServiceUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
@ -79,10 +78,6 @@ import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils; import org.mybatis.spring.SqlSessionUtils;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.TriggerBuilder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -242,7 +237,7 @@ public class ApiScenarioService extends MoveNodeService {
} }
item.setScheduleConfig(request); item.setScheduleConfig(request);
if (schedule.getEnable()) { if (schedule.getEnable()) {
item.setNextTriggerTime(getNextTriggerTime(schedule.getValue())); item.setNextTriggerTime(ScheduleUtils.getNextTriggerTime(schedule.getValue()));
} }
} }
if (MapUtils.isNotEmpty(reportMap) && reportMap.containsKey(item.getLastReportId())) { if (MapUtils.isNotEmpty(reportMap) && reportMap.containsKey(item.getLastReportId())) {
@ -251,22 +246,6 @@ public class ApiScenarioService extends MoveNodeService {
}); });
} }
/**
* 获取下次执行时间getFireTimeAfter也可以下下次...
*
* @param cron cron表达式
* @return 下次执行时间
*/
private static Long getNextTriggerTime(String cron) {
if (!CronExpression.isValidExpression(cron)) {
return null;
}
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("Calculate Date").withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
Date time0 = trigger.getStartTime();
Date time1 = trigger.getFireTimeAfter(time0);
return time1 == null ? 0 : time1.getTime();
}
private Set<String> extractUserIds(List<ApiScenarioDTO> list) { private Set<String> extractUserIds(List<ApiScenarioDTO> list) {
return list.stream() return list.stream()
.flatMap(apiScenario -> Stream.of(apiScenario.getUpdateUser(), apiScenario.getDeleteUser(), apiScenario.getCreateUser())) .flatMap(apiScenario -> Stream.of(apiScenario.getUpdateUser(), apiScenario.getDeleteUser(), apiScenario.getCreateUser()))

View File

@ -0,0 +1,27 @@
package io.metersphere.system.utils;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.TriggerBuilder;
import java.util.Date;
public class ScheduleUtils {
/**
* 获取下次执行时间getFireTimeAfter也可以下下次...
*
* @param cron cron表达式
* @return 下次执行时间
*/
public static Long getNextTriggerTime(String cron) {
if (!CronExpression.isValidExpression(cron)) {
return null;
}
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("Calculate Date").withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
Date time0 = trigger.getStartTime();
Date time1 = trigger.getFireTimeAfter(time0);
return time1 == null ? 0 : time1.getTime();
}
}

View File

@ -2,6 +2,7 @@ package io.metersphere.plan.dto.response;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.metersphere.plan.serializer.CustomRateSerializer; import io.metersphere.plan.serializer.CustomRateSerializer;
import io.metersphere.system.dto.request.schedule.BaseScheduleConfigRequest;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -55,4 +56,8 @@ public class TestPlanStatisticsResponse {
private Integer apiScenarioCount = 0; private Integer apiScenarioCount = 0;
@Schema(description = "缺陷数量") @Schema(description = "缺陷数量")
private Integer bugCount = 0; private Integer bugCount = 0;
@Schema(description = "定时任务配置")
private BaseScheduleConfigRequest scheduleConfig;
@Schema(description = "定时任务下一次执行时间")
private Long nextTriggerTime;
} }

View File

@ -10,6 +10,13 @@ import io.metersphere.plan.mapper.ExtTestPlanFunctionalCaseMapper;
import io.metersphere.plan.mapper.TestPlanConfigMapper; import io.metersphere.plan.mapper.TestPlanConfigMapper;
import io.metersphere.plan.utils.RateCalculateUtils; import io.metersphere.plan.utils.RateCalculateUtils;
import io.metersphere.sdk.constants.ExecStatus; import io.metersphere.sdk.constants.ExecStatus;
import io.metersphere.sdk.constants.ScheduleResourceType;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.domain.Schedule;
import io.metersphere.system.domain.ScheduleExample;
import io.metersphere.system.dto.request.schedule.BaseScheduleConfigRequest;
import io.metersphere.system.mapper.ScheduleMapper;
import io.metersphere.system.utils.ScheduleUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -29,6 +36,8 @@ public class TestPlanStatisticsService {
private ExtTestPlanFunctionalCaseMapper extTestPlanFunctionalCaseMapper; private ExtTestPlanFunctionalCaseMapper extTestPlanFunctionalCaseMapper;
@Resource @Resource
private ExtTestPlanBugMapper extTestPlanBugMapper; private ExtTestPlanBugMapper extTestPlanBugMapper;
@Resource
private ScheduleMapper scheduleMapper;
/** /**
* 计划的用例统计数据 * 计划的用例统计数据
@ -83,6 +92,13 @@ public class TestPlanStatisticsService {
// 计划-功能用例的关联数据 // 计划-功能用例的关联数据
List<TestPlanFunctionalCase> planFunctionalCases = extTestPlanFunctionalCaseMapper.getPlanFunctionalCaseByIds(planIds); List<TestPlanFunctionalCase> planFunctionalCases = extTestPlanFunctionalCaseMapper.getPlanFunctionalCaseByIds(planIds);
Map<String, List<TestPlanFunctionalCase>> planFunctionalCaseMap = planFunctionalCases.stream().collect(Collectors.groupingBy(TestPlanFunctionalCase::getTestPlanId)); Map<String, List<TestPlanFunctionalCase>> planFunctionalCaseMap = planFunctionalCases.stream().collect(Collectors.groupingBy(TestPlanFunctionalCase::getTestPlanId));
//查询定时任务
ScheduleExample scheduleExample = new ScheduleExample();
scheduleExample.createCriteria().andResourceIdIn(planIds).andResourceTypeEqualTo(ScheduleResourceType.TEST_PLAN.name());
List<Schedule> schedules = scheduleMapper.selectByExample(scheduleExample);
Map<String, Schedule> scheduleMap = schedules.stream().collect(Collectors.toMap(Schedule::getResourceId, t -> t));
// TODO: 计划-接口用例的关联数据 // TODO: 计划-接口用例的关联数据
planIds.forEach(planId -> { planIds.forEach(planId -> {
TestPlanStatisticsResponse statisticsResponse = new TestPlanStatisticsResponse(); TestPlanStatisticsResponse statisticsResponse = new TestPlanStatisticsResponse();
@ -115,6 +131,23 @@ public class TestPlanStatisticsService {
statisticsResponse.setPassRate(RateCalculateUtils.divWithPrecision(statisticsResponse.getSuccessCount(), statisticsResponse.getCaseTotal(), 2)); statisticsResponse.setPassRate(RateCalculateUtils.divWithPrecision(statisticsResponse.getSuccessCount(), statisticsResponse.getCaseTotal(), 2));
statisticsResponse.setExecuteRate(RateCalculateUtils.divWithPrecision(statisticsResponse.getCaseTotal() - statisticsResponse.getPendingCount(), statisticsResponse.getCaseTotal(), 2)); statisticsResponse.setExecuteRate(RateCalculateUtils.divWithPrecision(statisticsResponse.getCaseTotal() - statisticsResponse.getPendingCount(), statisticsResponse.getCaseTotal(), 2));
planStatisticsResponses.add(statisticsResponse); planStatisticsResponses.add(statisticsResponse);
//定时任务
if (scheduleMap.containsKey(planId)) {
Schedule schedule = scheduleMap.get(planId);
BaseScheduleConfigRequest request = new BaseScheduleConfigRequest();
request.setEnable(schedule.getEnable());
request.setCron(schedule.getValue());
request.setResourceId(planId);
if (schedule.getConfig() != null) {
request.setRunConfig(JSON.parseObject(schedule.getConfig(), Map.class));
}
statisticsResponse.setScheduleConfig(request);
if (schedule.getEnable()) {
statisticsResponse.setNextTriggerTime(ScheduleUtils.getNextTriggerTime(schedule.getValue()));
}
}
}); });
return planStatisticsResponses; return planStatisticsResponses;
} }

View File

@ -8,6 +8,7 @@ import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.dto.response.TestPlanOperationResponse;
import io.metersphere.plan.dto.response.TestPlanResponse; import io.metersphere.plan.dto.response.TestPlanResponse;
import io.metersphere.plan.dto.response.TestPlanStatisticsResponse;
import io.metersphere.plan.mapper.ExtTestPlanMapper; import io.metersphere.plan.mapper.ExtTestPlanMapper;
import io.metersphere.plan.mapper.TestPlanMapper; import io.metersphere.plan.mapper.TestPlanMapper;
import io.metersphere.plan.mapper.TestPlanReportMapper; import io.metersphere.plan.mapper.TestPlanReportMapper;
@ -1365,10 +1366,23 @@ public class TestPlanTests extends BaseTest {
this.requestGet(String.format(URL_POST_TEST_PLAN_SCHEDULE_DELETE, groupTestPlanId7)).andExpect(status().is5xxServerError()); this.requestGet(String.format(URL_POST_TEST_PLAN_SCHEDULE_DELETE, groupTestPlanId7)).andExpect(status().is5xxServerError());
//恢复 //恢复
testPlanTestService.resetProjectModule(project, PROJECT_MODULE); testPlanTestService.resetProjectModule(project, PROJECT_MODULE);
//正是测试
MvcResult result = this.requestPostAndReturn(URL_POST_TEST_PLAN_SCHEDULE, request); MvcResult result = this.requestPostAndReturn(URL_POST_TEST_PLAN_SCHEDULE, request);
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class); ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
String scheduleId = resultHolder.getData().toString(); String scheduleId = resultHolder.getData().toString();
testPlanTestService.checkSchedule(scheduleId, groupTestPlanId7, request.isEnable()); testPlanTestService.checkSchedule(scheduleId, groupTestPlanId7, request.isEnable());
//检查统计接口查询的是否正确
List<TestPlanStatisticsResponse> statisticsResponses = JSON.parseArray(
JSON.toJSONString(
JSON.parseObject(
this.requestPostAndReturn(URL_POST_TEST_PLAN_STATISTICS, List.of(groupTestPlanId7))
.getResponse().getContentAsString(), ResultHolder.class).getData()),
TestPlanStatisticsResponse.class);
Assertions.assertTrue(statisticsResponses.size() == 1);
Assertions.assertTrue(statisticsResponses.getFirst().getNextTriggerTime() > 0);
Assertions.assertTrue(statisticsResponses.getFirst().getScheduleConfig().isEnable());
//增加日志检查 //增加日志检查
LOG_CHECK_LIST.add( LOG_CHECK_LIST.add(
@ -1383,6 +1397,17 @@ public class TestPlanTests extends BaseTest {
//检查两个scheduleId是否相同 //检查两个scheduleId是否相同
Assertions.assertEquals(scheduleId, newScheduleId); Assertions.assertEquals(scheduleId, newScheduleId);
testPlanTestService.checkSchedule(newScheduleId, groupTestPlanId7, request.isEnable()); testPlanTestService.checkSchedule(newScheduleId, groupTestPlanId7, request.isEnable());
//检查统计接口查询的是否正确
statisticsResponses = JSON.parseArray(
JSON.toJSONString(
JSON.parseObject(
this.requestPostAndReturn(URL_POST_TEST_PLAN_STATISTICS, List.of(groupTestPlanId7))
.getResponse().getContentAsString(), ResultHolder.class).getData()),
TestPlanStatisticsResponse.class);
Assertions.assertTrue(statisticsResponses.size() == 1);
Assertions.assertTrue(statisticsResponses.getFirst().getNextTriggerTime() == null);
Assertions.assertFalse(statisticsResponses.getFirst().getScheduleConfig().isEnable());
//测试各种corn表达式用于校验正则的准确性 //测试各种corn表达式用于校验正则的准确性
String[] cornStrArr = new String[]{ String[] cornStrArr = new String[]{
@ -1451,6 +1476,16 @@ public class TestPlanTests extends BaseTest {
//测试删除 //测试删除
this.requestGetWithOk(String.format(URL_POST_TEST_PLAN_SCHEDULE_DELETE, groupTestPlanId7)); this.requestGetWithOk(String.format(URL_POST_TEST_PLAN_SCHEDULE_DELETE, groupTestPlanId7));
testPlanTestService.checkScheduleIsRemove(groupTestPlanId7); testPlanTestService.checkScheduleIsRemove(groupTestPlanId7);
//检查统计接口查询的是否正确
statisticsResponses = JSON.parseArray(
JSON.toJSONString(
JSON.parseObject(
this.requestPostAndReturn(URL_POST_TEST_PLAN_STATISTICS, List.of(groupTestPlanId7))
.getResponse().getContentAsString(), ResultHolder.class).getData()),
TestPlanStatisticsResponse.class);
Assertions.assertTrue(statisticsResponses.size() == 1);
Assertions.assertTrue(statisticsResponses.getFirst().getNextTriggerTime() == null);
Assertions.assertTrue(statisticsResponses.getFirst().getScheduleConfig() == null);
} }
@Test @Test