feat(任务中心): 补充测试计划任务执行结果&&计划执行历史

This commit is contained in:
song-cc-rock 2024-11-14 18:33:45 +08:00 committed by jianxing
parent 7ccb733c4f
commit 0d35aaf821
10 changed files with 140 additions and 14 deletions

View File

@ -187,7 +187,7 @@
#{lastStatus} #{lastStatus}
</foreach> </foreach>
</if> </if>
<if test="request.todoParam.platformUser != null"> <if test="request.todoParam.platformUser != null and request.todoParam.platformUser != ''">
and b.handle_user = #{request.todoParam.platformUser} and b.handle_user = #{request.todoParam.platformUser}
</if> </if>
) )

View File

@ -8,10 +8,7 @@ import io.metersphere.plan.constants.TestPlanResourceConfig;
import io.metersphere.plan.domain.TestPlanReportComponent; import io.metersphere.plan.domain.TestPlanReportComponent;
import io.metersphere.plan.dto.ReportDetailCasePageDTO; import io.metersphere.plan.dto.ReportDetailCasePageDTO;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanCaseExecHistoryResponse; import io.metersphere.plan.dto.response.*;
import io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse;
import io.metersphere.plan.dto.response.TestPlanReportDetailResponse;
import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
import io.metersphere.plan.service.*; import io.metersphere.plan.service.*;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.log.annotation.Log; import io.metersphere.system.log.annotation.Log;
@ -129,6 +126,14 @@ public class TestPlanReportController {
return testPlanReportService.getReport(reportId); return testPlanReportService.getReport(reportId);
} }
@GetMapping("/get-task/{taskId}")
@Operation(summary = "测试计划|组-任务-执行结果")
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
@CheckOwner(resourceId = "#taskId", resourceType = "exec_task")
public TestPlanTaskReportResponse getTaskDetail(@PathVariable String taskId) {
return testPlanReportService.getTaskDetail(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)

View File

@ -30,4 +30,8 @@ public class CaseCount {
@Schema(description = "未执行用例数量") @Schema(description = "未执行用例数量")
@Builder.Default @Builder.Default
private Integer pending = 0; private Integer pending = 0;
public Integer sum() {
return success + error + fakeError + block + pending;
}
} }

View File

@ -9,6 +9,9 @@ import lombok.Data;
import java.util.List; import java.util.List;
/**
* @author song-cc-rock
*/
@Data @Data
public class TestPlanReportDetailResponse { public class TestPlanReportDetailResponse {

View File

@ -0,0 +1,33 @@
package io.metersphere.plan.dto.response;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.metersphere.plan.dto.CaseCount;
import io.metersphere.system.serializer.CustomRateSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author song-cc-rock
*/
@Data
public class TestPlanTaskReportResponse {
@Schema(description = "执行结果")
private String result;
@Schema(description = "执行状态")
private String status;
@Schema(description = "操作人")
private String createUser;
@Schema(description = "任务发起时间")
private Long createTime;
@Schema(description = "任务开始起时间")
private Long startTime;
@Schema(description = "任务结束时间")
private Long endTime;
@Schema(description = "执行用例统计(实时)")
private CaseCount executeCaseCount;
@Schema(description = "执行完成率(实时)")
@JsonSerialize(using = CustomRateSerializer.class)
private Double executeRate;
}

View File

@ -642,10 +642,11 @@
</select> </select>
<select id="listHis" resultType="io.metersphere.plan.dto.TestPlanExecuteHisDTO"> <select id="listHis" resultType="io.metersphere.plan.dto.TestPlanExecuteHisDTO">
select tpr.id, from_unixtime(tpr.create_time / 1000, '%Y%m%d%H%i%s') as num, tpr.trigger_mode triggerMode, tpr.exec_status execStatus, tpr.result_status 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,
tpr.create_user operationUser, tpr.start_time startTime, tpr.end_time endTime, tpr.deleted as deleted from test_plan_report tpr et.create_user operationUser, et.start_time startTime, et.end_time endTime
where tpr.test_plan_id = #{request.testPlanId} from exec_task et where et.resource_id = #{request.testPlanId}
<include refid="filter"/> <include refid="filterTask"/>
order by et.create_time desc
</select> </select>
<select id="selectGroupIdByKeyword" resultType="java.lang.String"> <select id="selectGroupIdByKeyword" resultType="java.lang.String">
@ -737,6 +738,26 @@
</if> </if>
</sql> </sql>
<sql id="filterTask">
<if test="request.filter != null and request.filter.size() > 0">
<foreach collection="request.filter.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'triggerMode'">
and et.trigger_mode in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
<!-- 执行结果 -->
<when test="key == 'execResult'">
and et.result in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
</choose>
</if>
</foreach>
</if>
</sql>
<select id="projectPlanCount" <select id="projectPlanCount"
resultType="io.metersphere.project.dto.ProjectCountDTO"> resultType="io.metersphere.project.dto.ProjectCountDTO">
SELECT test_plan.project_id as projectId, count(test_plan.id) as count SELECT test_plan.project_id as projectId, count(test_plan.id) as count

View File

@ -327,6 +327,7 @@ public class TestPlanExecuteService {
execTask.setOrganizationId(project.getOrganizationId()); execTask.setOrganizationId(project.getOrganizationId());
execTask.setTriggerMode(triggerMode); execTask.setTriggerMode(triggerMode);
execTask.setTaskType(StringUtils.equalsIgnoreCase(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_PLAN) ? ExecTaskType.TEST_PLAN.name() : ExecTaskType.TEST_PLAN_GROUP.name()); execTask.setTaskType(StringUtils.equalsIgnoreCase(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_PLAN) ? ExecTaskType.TEST_PLAN.name() : ExecTaskType.TEST_PLAN_GROUP.name());
execTask.setResourceId(testPlan.getId());
baseTaskHubService.insertExecTask(execTask); baseTaskHubService.insertExecTask(execTask);
// 创建报告和任务的关联关系 // 创建报告和任务的关联关系

View File

@ -12,10 +12,7 @@ import io.metersphere.plan.constants.CollectionQueryType;
import io.metersphere.plan.domain.*; import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.*; import io.metersphere.plan.dto.*;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanCaseExecHistoryResponse; import io.metersphere.plan.dto.response.*;
import io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse;
import io.metersphere.plan.dto.response.TestPlanReportDetailResponse;
import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
import io.metersphere.plan.enums.TestPlanReportAttachmentSourceType; import io.metersphere.plan.enums.TestPlanReportAttachmentSourceType;
import io.metersphere.plan.mapper.*; import io.metersphere.plan.mapper.*;
import io.metersphere.plan.utils.CountUtils; import io.metersphere.plan.utils.CountUtils;
@ -29,10 +26,12 @@ import io.metersphere.sdk.file.FileCopyRequest;
import io.metersphere.sdk.file.FileRepository; import io.metersphere.sdk.file.FileRepository;
import io.metersphere.sdk.file.FileRequest; import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.*; import io.metersphere.sdk.util.*;
import io.metersphere.system.domain.ExecTask;
import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.domain.ExecTaskItem;
import io.metersphere.system.domain.User; import io.metersphere.system.domain.User;
import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.mapper.BaseUserMapper; import io.metersphere.system.mapper.BaseUserMapper;
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.BaseTaskHubService;
@ -128,6 +127,8 @@ public class TestPlanReportService {
private ApiReportRelateTaskMapper apiReportRelateTaskMapper; private ApiReportRelateTaskMapper apiReportRelateTaskMapper;
@Resource @Resource
private TestPlanCollectionMapper testPlanCollectionMapper; private TestPlanCollectionMapper testPlanCollectionMapper;
@Resource
private ExecTaskMapper execTaskMapper;
private static final int MAX_REPORT_NAME_LENGTH = 300; private static final int MAX_REPORT_NAME_LENGTH = 300;
@ -798,6 +799,21 @@ public class TestPlanReportService {
return planReportDetail; return planReportDetail;
} }
public TestPlanTaskReportResponse getTaskDetail(String taskId) {
TestPlanTaskReportResponse testPlanTaskReportResponse = new TestPlanTaskReportResponse();
ExecTask task = execTaskMapper.selectByPrimaryKey(taskId);
BeanUtils.copyBean(testPlanTaskReportResponse, task);
ApiReportRelateTaskExample example = new ApiReportRelateTaskExample();
example.createCriteria().andTaskResourceIdEqualTo(taskId);
List<ApiReportRelateTask> taskReports = apiReportRelateTaskMapper.selectByExample(example);
if (CollectionUtils.isEmpty(taskReports)) {
// 暂未生成报告
return testPlanTaskReportResponse;
}
String reportId = taskReports.getFirst().getReportId();
return calcTaskExecActual(reportId, testPlanTaskReportResponse);
}
public List<TestPlanReportComponent> getLayout(String reportId) { public List<TestPlanReportComponent> getLayout(String reportId) {
TestPlanReportComponentExample example = new TestPlanReportComponentExample(); TestPlanReportComponentExample example = new TestPlanReportComponentExample();
example.createCriteria().andTestPlanReportIdEqualTo(reportId); example.createCriteria().andTestPlanReportIdEqualTo(reportId);
@ -1403,4 +1419,24 @@ public class TestPlanReportService {
testPlanReportLogService.exportLog(reports, userId, projectId, "/test-plan/report/batch-export"); testPlanReportLogService.exportLog(reports, userId, projectId, "/test-plan/report/batch-export");
} }
} }
/**
* 计算计划任务的用例执行情况(实时, 并不取计划报告的最终汇总)
* @return 用例执行情况
*/
private TestPlanTaskReportResponse calcTaskExecActual(String reportId, TestPlanTaskReportResponse testPlanTaskReportResponse) {
// 计算接口用例
List<CaseStatusCountMap> apiCountMapList = extTestPlanReportApiCaseMapper.countExecuteResult(reportId);
CaseCount apiCaseCount = countMap(apiCountMapList);
// 计算场景用例
List<CaseStatusCountMap> scenarioCountMapList = extTestPlanReportApiScenarioMapper.countExecuteResult(reportId);
CaseCount scenarioCaseCount = countMap(scenarioCountMapList);
// 汇总接口&&场景用例的执行情况
CaseCount caseCount = CountUtils.summarizeProperties(List.of(apiCaseCount, scenarioCaseCount));
testPlanTaskReportResponse.setExecuteCaseCount(caseCount);
// 完成率 = (总数 - 未执行数) / 总数
testPlanTaskReportResponse.setExecuteRate((caseCount.sum() == 0) ?
0 : RateCalculateUtils.divWithPrecision(caseCount.sum() - caseCount.getPending(), caseCount.sum(), 2));
return testPlanTaskReportResponse;
}
} }

View File

@ -1,5 +1,7 @@
package io.metersphere.plan.controller; package io.metersphere.plan.controller;
import io.metersphere.api.domain.ApiReportRelateTaskExample;
import io.metersphere.api.mapper.ApiReportRelateTaskMapper;
import io.metersphere.plan.constants.CollectionQueryType; import io.metersphere.plan.constants.CollectionQueryType;
import io.metersphere.plan.domain.*; import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.TestPlanShareInfo; import io.metersphere.plan.dto.TestPlanShareInfo;
@ -50,6 +52,7 @@ public class TestPlanReportControllerTests extends BaseTest {
private static final String GET_MANUAL_PLAN_REPORT_LAYOUT = "/test-plan/report/get-layout"; private static final String GET_MANUAL_PLAN_REPORT_LAYOUT = "/test-plan/report/get-layout";
private static final String AUTO_GEN_PLAN_REPORT = "/test-plan/report/auto-gen"; private static final String AUTO_GEN_PLAN_REPORT = "/test-plan/report/auto-gen";
private static final String GET_PLAN_REPORT = "/test-plan/report/get"; private static final String GET_PLAN_REPORT = "/test-plan/report/get";
private static final String GET_PLAN_TASK_RESULT = "/test-plan/report/get-task";
private static final String EDIT_PLAN_REPORT_AND_UPLOAD_PIC = "/test-plan/report/upload/md/file"; private static final String EDIT_PLAN_REPORT_AND_UPLOAD_PIC = "/test-plan/report/upload/md/file";
private static final String EDIT_PLAN_REPORT = "/test-plan/report/detail/edit"; private static final String EDIT_PLAN_REPORT = "/test-plan/report/detail/edit";
private static final String GET_PLAN_REPORT_DETAIL_BUG_PAGE = "/test-plan/report/detail/bug/page"; private static final String GET_PLAN_REPORT_DETAIL_BUG_PAGE = "/test-plan/report/detail/bug/page";
@ -86,6 +89,8 @@ public class TestPlanReportControllerTests extends BaseTest {
private static String GEN_REPORT_ID; private static String GEN_REPORT_ID;
private static String GEN_SHARE_ID; private static String GEN_SHARE_ID;
@Autowired
private ApiReportRelateTaskMapper apiReportRelateTaskMapper;
@Test @Test
@Order(1) @Order(1)
@ -297,6 +302,10 @@ public class TestPlanReportControllerTests extends BaseTest {
void testGetReportSuccess() throws Exception { void testGetReportSuccess() throws Exception {
this.requestGet(GET_PLAN_REPORT + "/" + GEN_REPORT_ID); this.requestGet(GET_PLAN_REPORT + "/" + GEN_REPORT_ID);
this.requestGet(GET_PLAN_REPORT + "/" + "test-plan-report-id-5"); this.requestGet(GET_PLAN_REPORT + "/" + "test-plan-report-id-5");
this.requestGet(GET_PLAN_TASK_RESULT + "/" + "task-id-1");
this.requestGet(GET_PLAN_TASK_RESULT + "/" + "task-id-2");
// 为了不影响后续报告的清理
cleanDefaultTaskReportRelate();
} }
@Test @Test
@ -501,4 +510,10 @@ public class TestPlanReportControllerTests extends BaseTest {
request.setSelectAll(true); request.setSelectAll(true);
this.requestPost(BATCH_EXPORT_REPORT, request); this.requestPost(BATCH_EXPORT_REPORT, request);
} }
private void cleanDefaultTaskReportRelate() {
ApiReportRelateTaskExample example = new ApiReportRelateTaskExample();
example.createCriteria().andTaskResourceIdEqualTo("task-id-1");
apiReportRelateTaskMapper.deleteByExample(example);
}
} }

View File

@ -46,4 +46,12 @@ INSERT INTO `share_info`(`id`, `create_time`, `create_user`, `update_time`, `sha
-- 功能用例执行信息 -- 功能用例执行信息
INSERT INTO test_plan_case_execute_history (`id`, `test_plan_case_id`, `test_plan_id`, `case_id`, `status`, `content`, `steps`, `deleted`, `notifier`, `create_user`, `create_time`) VALUES INSERT INTO test_plan_case_execute_history (`id`, `test_plan_case_id`, `test_plan_id`, `case_id`, `status`, `content`, `steps`, `deleted`, `notifier`, `create_user`, `create_time`) VALUES
('execute-his-1', 'test-plan-case-id-for-oasis', 'test-plan-id-for-oasis', 'case-id-for-oasis', 'PENDING', null, null, 0, null, 'admin', UNIX_TIMESTAMP()), ('execute-his-1', 'test-plan-case-id-for-oasis', 'test-plan-id-for-oasis', 'case-id-for-oasis', 'PENDING', null, null, 0, null, 'admin', UNIX_TIMESTAMP()),
('execute-his-2', 'test-plan-case-id-for-oasis', 'test-plan-id-for-oasis', 'case-id-for-oasis', 'PENDING', '1', '1', 0, null, 'admin', UNIX_TIMESTAMP()); ('execute-his-2', 'test-plan-case-id-for-oasis', 'test-plan-id-for-oasis', 'case-id-for-oasis', 'PENDING', '1', '1', 0, null, 'admin', UNIX_TIMESTAMP());
-- 任务-报告关联表
INSERT INTO exec_task (id, num, task_name, status, case_count, result, task_type, trigger_mode, project_id, organization_id, create_time, create_user, start_time, end_time, integrated) VALUE
('task-id-1', 100001, '任务-1', 'PENDING', 1, 'SUCCESS', 'TEST_PLAN', 'MANUAL', '100001100001', '100001', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 0);
INSERT INTO api_report_relate_task (task_resource_id, report_id) VALUE
('task-id-1', 'test-plan-report-id-6');
INSERT INTO exec_task (id, num, task_name, status, case_count, result, task_type, trigger_mode, project_id, organization_id, create_time, create_user, start_time, end_time, integrated) VALUE
('task-id-2', 100001, '任务-1', 'PENDING', 1, 'SUCCESS', 'TEST_PLAN', 'MANUAL', '100001100001', '100001', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 0);