refactor(测试计划): 优化任务计划执行历史
This commit is contained in:
parent
3747eedfec
commit
ccc10bacc2
|
@ -127,13 +127,21 @@ public class TestPlanReportController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/get-task/{taskId}")
|
@GetMapping("/get-task/{taskId}")
|
||||||
@Operation(summary = "测试计划|组-任务-执行结果")
|
@Operation(summary = "测试计划|组-执行历史-执行结果")
|
||||||
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
|
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
|
||||||
@CheckOwner(resourceId = "#taskId", resourceType = "exec_task")
|
@CheckOwner(resourceId = "#taskId", resourceType = "exec_task")
|
||||||
public TestPlanTaskReportResponse getTaskDetail(@PathVariable String taskId) {
|
public TestPlanTaskReportResponse getTaskDetail(@PathVariable String taskId) {
|
||||||
return testPlanReportService.getTaskDetail(taskId);
|
return testPlanReportService.getTaskDetail(taskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get-result/{taskId}")
|
||||||
|
@Operation(summary = "测试计划|组-任务-执行结果")
|
||||||
|
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
|
||||||
|
@CheckOwner(resourceId = "#reportId", resourceType = "test_plan_report")
|
||||||
|
public TestPlanReportDetailResponse getTaskResult(@PathVariable String taskId) {
|
||||||
|
return testPlanReportService.getTaskResult(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/get-layout/{reportId}")
|
@GetMapping("/get-layout/{reportId}")
|
||||||
@Operation(summary = "测试计划-报告-组件布局")
|
@Operation(summary = "测试计划-报告-组件布局")
|
||||||
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
|
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
|
||||||
|
|
|
@ -24,4 +24,8 @@ public class TestPlanExecuteHisDTO {
|
||||||
private Long endTime;
|
private Long endTime;
|
||||||
@Schema(description = "报告是否删除")
|
@Schema(description = "报告是否删除")
|
||||||
private Boolean deleted;
|
private Boolean deleted;
|
||||||
|
@Schema(description = "报告ID")
|
||||||
|
private String reportId;
|
||||||
|
@Schema(description = "执行结果是否删除")
|
||||||
|
private Boolean resultDeleted;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import io.metersphere.system.serializer.CustomRateSerializer;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ import java.util.List;
|
||||||
@Data
|
@Data
|
||||||
public class TestPlanTaskReportResponse implements Serializable {
|
public class TestPlanTaskReportResponse implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "报告ID")
|
@Schema(description = "报告ID")
|
||||||
|
@ -40,10 +42,16 @@ public class TestPlanTaskReportResponse implements Serializable {
|
||||||
@Schema(description = "执行完成率(实时)")
|
@Schema(description = "执行完成率(实时)")
|
||||||
@JsonSerialize(using = CustomRateSerializer.class)
|
@JsonSerialize(using = CustomRateSerializer.class)
|
||||||
private Double executeRate;
|
private Double executeRate;
|
||||||
|
@Schema(description = "接口明细总数")
|
||||||
|
private Integer apiCaseTotal;
|
||||||
|
@Schema(description = "场景明细总数")
|
||||||
|
private Integer apiScenarioTotal;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class ChildPlan {
|
public static class ChildPlan {
|
||||||
private String id;
|
private String id;
|
||||||
private String name;
|
private String name;
|
||||||
|
private Integer apiCaseTotal;
|
||||||
|
private Integer apiScenarioTotal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -659,7 +659,7 @@
|
||||||
|
|
||||||
<select id="listHis" resultType="io.metersphere.plan.dto.TestPlanExecuteHisDTO">
|
<select id="listHis" resultType="io.metersphere.plan.dto.TestPlanExecuteHisDTO">
|
||||||
select et.id, from_unixtime(et.create_time / 1000, '%Y%m%d%H%i%s') as num, et.trigger_mode triggerMode, et.status execStatus, et.result execResult,
|
select et.id, from_unixtime(et.create_time / 1000, '%Y%m%d%H%i%s') as num, et.trigger_mode triggerMode, et.status execStatus, et.result execResult,
|
||||||
et.create_user operationUser, et.start_time startTime, et.end_time endTime
|
et.create_user operationUser, et.start_time startTime, et.end_time endTime, arrt.report_id reportId
|
||||||
from exec_task et where et.resource_id = #{request.testPlanId}
|
from exec_task et where et.resource_id = #{request.testPlanId}
|
||||||
<include refid="filterTask"/>
|
<include refid="filterTask"/>
|
||||||
order by et.create_time desc
|
order by et.create_time desc
|
||||||
|
|
|
@ -34,10 +34,7 @@ import io.metersphere.system.mapper.BaseUserMapper;
|
||||||
import io.metersphere.system.mapper.ExecTaskMapper;
|
import io.metersphere.system.mapper.ExecTaskMapper;
|
||||||
import io.metersphere.system.mapper.UserMapper;
|
import io.metersphere.system.mapper.UserMapper;
|
||||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||||
import io.metersphere.system.service.BaseTaskHubService;
|
import io.metersphere.system.service.*;
|
||||||
import io.metersphere.system.service.CommonFileService;
|
|
||||||
import io.metersphere.system.service.FileService;
|
|
||||||
import io.metersphere.system.service.SimpleUserService;
|
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import io.metersphere.system.utils.RateCalculateUtils;
|
import io.metersphere.system.utils.RateCalculateUtils;
|
||||||
import io.metersphere.system.utils.ServiceUtils;
|
import io.metersphere.system.utils.ServiceUtils;
|
||||||
|
@ -130,6 +127,8 @@ public class TestPlanReportService {
|
||||||
private ApiReportRelateTaskMapper apiReportRelateTaskMapper;
|
private ApiReportRelateTaskMapper apiReportRelateTaskMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ExecTaskMapper execTaskMapper;
|
private ExecTaskMapper execTaskMapper;
|
||||||
|
@Resource
|
||||||
|
private UserLoginService userLoginService;
|
||||||
|
|
||||||
private static final int MAX_REPORT_NAME_LENGTH = 300;
|
private static final int MAX_REPORT_NAME_LENGTH = 300;
|
||||||
|
|
||||||
|
@ -766,16 +765,10 @@ public class TestPlanReportService {
|
||||||
*/
|
*/
|
||||||
public TestPlanReportDetailResponse getReport(String reportId) {
|
public TestPlanReportDetailResponse getReport(String reportId) {
|
||||||
TestPlanReport planReport = checkReport(reportId);
|
TestPlanReport planReport = checkReport(reportId);
|
||||||
TestPlanReportSummaryExample example = new TestPlanReportSummaryExample();
|
|
||||||
example.createCriteria().andTestPlanReportIdEqualTo(reportId);
|
|
||||||
List<TestPlanReportSummary> testPlanReportSummaries = testPlanReportSummaryMapper.selectByExampleWithBLOBs(example);
|
|
||||||
if (CollectionUtils.isEmpty(testPlanReportSummaries)) {
|
|
||||||
throw new MSException(Translator.get("test_plan_report_detail_not_exist"));
|
|
||||||
}
|
|
||||||
TestPlanReportSummary reportSummary = testPlanReportSummaries.getFirst();
|
|
||||||
TestPlanReportDetailResponse planReportDetail = new TestPlanReportDetailResponse();
|
TestPlanReportDetailResponse planReportDetail = new TestPlanReportDetailResponse();
|
||||||
BeanUtils.copyBean(planReportDetail, planReport);
|
BeanUtils.copyBean(planReportDetail, planReport);
|
||||||
// 用例总数需单独返回, 不然前端表格不展示, 影响执行中的数据
|
// 用例总数需单独返回, 不然前端表格不展示, 影响执行中的数据
|
||||||
|
TestPlanReportSummary reportSummary = getReportSummary(reportId);
|
||||||
int caseTotal = (int) (reportSummary.getFunctionalCaseCount() + reportSummary.getApiCaseCount() + reportSummary.getApiScenarioCount());
|
int caseTotal = (int) (reportSummary.getFunctionalCaseCount() + reportSummary.getApiCaseCount() + reportSummary.getApiScenarioCount());
|
||||||
planReportDetail.setCaseTotal(caseTotal);
|
planReportDetail.setCaseTotal(caseTotal);
|
||||||
planReportDetail.setFunctionalTotal(reportSummary.getFunctionalCaseCount().intValue());
|
planReportDetail.setFunctionalTotal(reportSummary.getFunctionalCaseCount().intValue());
|
||||||
|
@ -822,31 +815,56 @@ public class TestPlanReportService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取计划任务执行结果
|
* 获取计划任务执行结果 (执行历史)
|
||||||
* @param taskId 任务ID
|
* @param taskId 任务ID
|
||||||
* @return 计划执行结果
|
* @return 计划|组 执行结果
|
||||||
*/
|
*/
|
||||||
public TestPlanTaskReportResponse getTaskDetail(String taskId) {
|
public TestPlanTaskReportResponse getTaskDetail(String taskId) {
|
||||||
TestPlanTaskReportResponse testPlanTaskReportResponse = new TestPlanTaskReportResponse();
|
TestPlanTaskReportResponse taskDetail = new TestPlanTaskReportResponse();
|
||||||
ExecTask task = execTaskMapper.selectByPrimaryKey(taskId);
|
ExecTask task = execTaskMapper.selectByPrimaryKey(taskId);
|
||||||
BeanUtils.copyBean(testPlanTaskReportResponse, task);
|
BeanUtils.copyBean(taskDetail, task);
|
||||||
|
Map<String, String> userNameMap = userLoginService.getUserNameMap(List.of(taskDetail.getCreateUser()));
|
||||||
|
taskDetail.setCreateUser(userNameMap.getOrDefault(taskDetail.getCreateUser(), taskDetail.getCreateUser()));
|
||||||
ApiReportRelateTaskExample example = new ApiReportRelateTaskExample();
|
ApiReportRelateTaskExample example = new ApiReportRelateTaskExample();
|
||||||
example.createCriteria().andTaskResourceIdEqualTo(taskId);
|
example.createCriteria().andTaskResourceIdEqualTo(taskId);
|
||||||
List<ApiReportRelateTask> taskReports = apiReportRelateTaskMapper.selectByExample(example);
|
List<ApiReportRelateTask> taskReports = apiReportRelateTaskMapper.selectByExample(example);
|
||||||
if (CollectionUtils.isEmpty(taskReports)) {
|
if (CollectionUtils.isEmpty(taskReports)) {
|
||||||
// 暂未生成报告
|
// 暂未生成报告
|
||||||
return testPlanTaskReportResponse;
|
return taskDetail;
|
||||||
}
|
}
|
||||||
String reportId = taskReports.getFirst().getReportId();
|
String reportId = taskReports.getFirst().getReportId();
|
||||||
List<TestPlanReport> planChildrenTask = extTestPlanReportMapper.getPlanChildrenTask(reportId);
|
List<TestPlanReport> planChildrenTask = extTestPlanReportMapper.getPlanChildrenTask(reportId);
|
||||||
List<TestPlanTaskReportResponse.ChildPlan> childPlans = planChildrenTask.stream().map(childTask -> {
|
List<TestPlanTaskReportResponse.ChildPlan> childPlans = planChildrenTask.stream().map(childTask -> {
|
||||||
|
TestPlanTaskReportResponse childTaskDetail = calcTaskExecActual(childTask.getId(), new TestPlanTaskReportResponse());
|
||||||
TestPlanTaskReportResponse.ChildPlan childPlan = new TestPlanTaskReportResponse.ChildPlan();
|
TestPlanTaskReportResponse.ChildPlan childPlan = new TestPlanTaskReportResponse.ChildPlan();
|
||||||
childPlan.setId(childTask.getId());
|
BeanUtils.copyBean(childPlan, childTaskDetail);
|
||||||
childPlan.setName(childTask.getName());
|
|
||||||
return childPlan;
|
return childPlan;
|
||||||
}).toList();
|
}).toList();
|
||||||
testPlanTaskReportResponse.setChildPlans(childPlans);
|
taskDetail.setChildPlans(childPlans);
|
||||||
return calcTaskExecActual(reportId, testPlanTaskReportResponse);
|
return calcTaskExecActual(reportId, taskDetail);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取计划任务执行结果 (任务中心)
|
||||||
|
* @param taskId 任务ID
|
||||||
|
* @return 计划|组 执行结果
|
||||||
|
*/
|
||||||
|
public TestPlanReportDetailResponse getTaskResult(String taskId) {
|
||||||
|
TestPlanReportDetailResponse taskResult = new TestPlanReportDetailResponse();
|
||||||
|
ExecTask task = execTaskMapper.selectByPrimaryKey(taskId);
|
||||||
|
ApiReportRelateTaskExample example = new ApiReportRelateTaskExample();
|
||||||
|
example.createCriteria().andTaskResourceIdEqualTo(taskId);
|
||||||
|
List<ApiReportRelateTask> taskReports = apiReportRelateTaskMapper.selectByExample(example);
|
||||||
|
if (CollectionUtils.isEmpty(taskReports)) {
|
||||||
|
// 暂未生成报告
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String reportId = taskReports.getFirst().getReportId();
|
||||||
|
// 子计划报告存在
|
||||||
|
List<TestPlanReport> planChildrenTask = extTestPlanReportMapper.getPlanChildrenTask(reportId);
|
||||||
|
List<TestPlanReportDetailResponse> childPlans = planChildrenTask.stream().map(childTask -> calcTaskExecFinish(childTask.getId(), childTask.getName(), new TestPlanReportDetailResponse())).toList();
|
||||||
|
taskResult.setChildren(childPlans);
|
||||||
|
return calcTaskExecFinish(reportId, task.getTaskName(), taskResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TestPlanReportComponent> getLayout(String reportId) {
|
public List<TestPlanReportComponent> getLayout(String reportId) {
|
||||||
|
@ -1402,13 +1420,14 @@ public class TestPlanReportService {
|
||||||
private TestPlanTaskReportResponse calcTaskExecActual(String reportId, TestPlanTaskReportResponse testPlanTaskReportResponse) {
|
private TestPlanTaskReportResponse calcTaskExecActual(String reportId, TestPlanTaskReportResponse testPlanTaskReportResponse) {
|
||||||
testPlanTaskReportResponse.setReportId(reportId);
|
testPlanTaskReportResponse.setReportId(reportId);
|
||||||
List<String> calcReportIds = getActualReportIds(reportId);
|
List<String> calcReportIds = getActualReportIds(reportId);
|
||||||
// 计算接口用例
|
// 历史执行结果只需统计 接口 + 场景 (数据来源: 关联表实时状态数据)
|
||||||
List<CaseStatusCountMap> apiCountMapList = extTestPlanReportApiCaseMapper.countExecuteResult(calcReportIds);
|
List<CaseStatusCountMap> apiCountMapList = extTestPlanReportApiCaseMapper.countExecuteResult(calcReportIds);
|
||||||
CaseCount apiCaseCount = countMap(apiCountMapList);
|
CaseCount apiCaseCount = countMap(apiCountMapList);
|
||||||
// 计算场景用例
|
|
||||||
List<CaseStatusCountMap> scenarioCountMapList = extTestPlanReportApiScenarioMapper.countExecuteResult(calcReportIds);
|
List<CaseStatusCountMap> scenarioCountMapList = extTestPlanReportApiScenarioMapper.countExecuteResult(calcReportIds);
|
||||||
CaseCount scenarioCaseCount = countMap(scenarioCountMapList);
|
CaseCount scenarioCaseCount = countMap(scenarioCountMapList);
|
||||||
// 汇总接口&&场景用例的执行情况
|
// 汇总接口&&场景用例的执行情况
|
||||||
|
testPlanTaskReportResponse.setApiCaseTotal(apiCaseCount.sum());
|
||||||
|
testPlanTaskReportResponse.setApiScenarioTotal(scenarioCaseCount.sum());
|
||||||
CaseCount caseCount = CountUtils.summarizeProperties(List.of(apiCaseCount, scenarioCaseCount));
|
CaseCount caseCount = CountUtils.summarizeProperties(List.of(apiCaseCount, scenarioCaseCount));
|
||||||
testPlanTaskReportResponse.setExecuteCaseCount(caseCount);
|
testPlanTaskReportResponse.setExecuteCaseCount(caseCount);
|
||||||
// 完成率 = (总数 - 未执行数) / 总数
|
// 完成率 = (总数 - 未执行数) / 总数
|
||||||
|
@ -1417,6 +1436,28 @@ public class TestPlanReportService {
|
||||||
return testPlanTaskReportResponse;
|
return testPlanTaskReportResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算计划任务的用例执行情况(取计划报告的最终汇总)
|
||||||
|
* @return 用例执行情况
|
||||||
|
*/
|
||||||
|
private TestPlanReportDetailResponse calcTaskExecFinish(String reportId, String detailName, TestPlanReportDetailResponse detail) {
|
||||||
|
detail.setId(reportId);
|
||||||
|
detail.setName(detailName);
|
||||||
|
TestPlanReport report = checkReport(reportId);
|
||||||
|
BeanUtils.copyBean(detail, report);
|
||||||
|
TestPlanReportSummary summary = getReportSummary(reportId);
|
||||||
|
// 任务执行结果只需统计 接口 + 场景 (数据来源: 报告汇总)
|
||||||
|
detail.setApiCaseCount(summary.getApiExecuteResult() == null ? CaseCount.builder().build() : JSON.parseObject(new String(summary.getApiExecuteResult()), CaseCount.class));
|
||||||
|
detail.setApiScenarioCount(summary.getScenarioExecuteResult() == null ? CaseCount.builder().build() : JSON.parseObject(new String(summary.getScenarioExecuteResult()), CaseCount.class));
|
||||||
|
detail.setApiCaseTotal(detail.getApiCaseCount().sum());
|
||||||
|
detail.setApiScenarioTotal(detail.getApiScenarioCount().sum());
|
||||||
|
detail.setExecuteCount(CountUtils.summarizeProperties(List.of(detail.getApiCaseCount(), detail.getApiScenarioCount())));
|
||||||
|
CaseCount executeCount = detail.getExecuteCount();
|
||||||
|
detail.setExecuteRate(RateCalculateUtils.divWithPrecision((executeCount.sum() - executeCount.getPending()), executeCount.sum(), 2));
|
||||||
|
detail.setPassRate(RateCalculateUtils.divWithPrecision(executeCount.getSuccess(), executeCount.sum(), 2));
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取实际的报告ID集合 (计划组报告则会返回多个)
|
* 获取实际的报告ID集合 (计划组报告则会返回多个)
|
||||||
* @param reportId 报告ID
|
* @param reportId 报告ID
|
||||||
|
@ -1434,4 +1475,19 @@ public class TestPlanReportService {
|
||||||
return List.of(reportId);
|
return List.of(reportId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取报告汇总详情
|
||||||
|
* @param reportId 报告ID
|
||||||
|
* @return 汇总详情
|
||||||
|
*/
|
||||||
|
private TestPlanReportSummary getReportSummary(String reportId) {
|
||||||
|
TestPlanReportSummaryExample example = new TestPlanReportSummaryExample();
|
||||||
|
example.createCriteria().andTestPlanReportIdEqualTo(reportId);
|
||||||
|
List<TestPlanReportSummary> testPlanReportSummaries = testPlanReportSummaryMapper.selectByExampleWithBLOBs(example);
|
||||||
|
if (CollectionUtils.isEmpty(testPlanReportSummaries)) {
|
||||||
|
throw new MSException(Translator.get("test_plan_report_detail_not_exist"));
|
||||||
|
}
|
||||||
|
return testPlanReportSummaries.getFirst();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -872,7 +872,10 @@ public class TestPlanService extends TestPlanBaseUtilsService {
|
||||||
List<String> userIds = hisList.stream().map(TestPlanExecuteHisDTO::getOperationUser).distinct().toList();
|
List<String> userIds = hisList.stream().map(TestPlanExecuteHisDTO::getOperationUser).distinct().toList();
|
||||||
List<OptionDTO> userOptions = baseUserMapper.selectUserOptionByIds(userIds);
|
List<OptionDTO> userOptions = baseUserMapper.selectUserOptionByIds(userIds);
|
||||||
Map<String, String> userMap = userOptions.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
|
Map<String, String> userMap = userOptions.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
|
||||||
hisList.forEach(his -> his.setOperationUser(userMap.getOrDefault(his.getOperationUser(), his.getOperationUser())));
|
hisList.forEach(his -> {
|
||||||
|
his.setOperationUser(userMap.getOrDefault(his.getOperationUser(), his.getOperationUser()));
|
||||||
|
his.setResultDeleted(StringUtils.isEmpty(his.getReportId()));
|
||||||
|
});
|
||||||
return hisList;
|
return hisList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue