feat(系统设置): 任务中心增加测试计划列表
This commit is contained in:
parent
a7356c2d98
commit
4e4266972a
|
@ -45,10 +45,9 @@ public class MessageListener {
|
||||||
@KafkaListener(id = MESSAGE_CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TASK_TOPIC, groupId = MESSAGE_CONSUME_ID)
|
@KafkaListener(id = MESSAGE_CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TASK_TOPIC, groupId = MESSAGE_CONSUME_ID)
|
||||||
public void messageConsume(ConsumerRecord<?, String> record) {
|
public void messageConsume(ConsumerRecord<?, String> record) {
|
||||||
try {
|
try {
|
||||||
LogUtils.info("接收到发送通知信息:{}", record.key());
|
|
||||||
if (ObjectUtils.isNotEmpty(record.value())) {
|
if (ObjectUtils.isNotEmpty(record.value())) {
|
||||||
ApiNoticeDTO dto = JSON.parseObject(record.value(), ApiNoticeDTO.class);
|
ApiNoticeDTO dto = JSON.parseObject(record.value(), ApiNoticeDTO.class);
|
||||||
|
LogUtils.info("接收到发送通知信息:{}", dto.getReportId());
|
||||||
// 集合报告不发送通知
|
// 集合报告不发送通知
|
||||||
if (!BooleanUtils.isTrue(dto.getIntegratedReport())) {
|
if (!BooleanUtils.isTrue(dto.getIntegratedReport())) {
|
||||||
apiReportSendNoticeService.sendNotice(dto);
|
apiReportSendNoticeService.sendNotice(dto);
|
||||||
|
|
|
@ -217,7 +217,6 @@
|
||||||
left join project on ar.project_id = project.id
|
left join project on ar.project_id = project.id
|
||||||
where
|
where
|
||||||
ar.deleted = false
|
ar.deleted = false
|
||||||
and ar.test_plan_id = 'NONE'
|
|
||||||
and ar.start_time BETWEEN #{startTime} AND #{endTime}
|
and ar.start_time BETWEEN #{startTime} AND #{endTime}
|
||||||
and ar.exec_status in ('PENDING', 'RUNNING', 'RERUNNING')
|
and ar.exec_status in ('PENDING', 'RUNNING', 'RERUNNING')
|
||||||
<if test="ids != null and ids.size() > 0">
|
<if test="ids != null and ids.size() > 0">
|
||||||
|
|
|
@ -111,7 +111,6 @@
|
||||||
left join project on asr.project_id = project.id
|
left join project on asr.project_id = project.id
|
||||||
where
|
where
|
||||||
asr.deleted = false
|
asr.deleted = false
|
||||||
and asr.test_plan_id = 'NONE'
|
|
||||||
and asr.start_time BETWEEN #{startTime} AND #{endTime}
|
and asr.start_time BETWEEN #{startTime} AND #{endTime}
|
||||||
and asr.exec_status in ('PENDING', 'RUNNING', 'RERUNNING')
|
and asr.exec_status in ('PENDING', 'RUNNING', 'RERUNNING')
|
||||||
<if test="ids != null and ids.size() > 0">
|
<if test="ids != null and ids.size() > 0">
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package io.metersphere.plan.controller;
|
||||||
|
|
||||||
|
import io.metersphere.plan.service.TestPlanTaskCenterService;
|
||||||
|
import io.metersphere.sdk.constants.PermissionConstants;
|
||||||
|
import io.metersphere.system.dto.taskcenter.TaskCenterDTO;
|
||||||
|
import io.metersphere.system.dto.taskcenter.request.TaskCenterPageRequest;
|
||||||
|
import io.metersphere.system.utils.Pager;
|
||||||
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(value = "/task/center/plan")
|
||||||
|
@Tag(name = "任务中心-实时任务-测试计划")
|
||||||
|
public class TestPlanTaskCenterController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TestPlanTaskCenterService testPlanTaskCenterService;
|
||||||
|
|
||||||
|
private static final String PROJECT = "project";
|
||||||
|
private static final String ORG = "org";
|
||||||
|
private static final String SYSTEM = "system";
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/project/real-time/page")
|
||||||
|
@Operation(summary = "项目-任务中心-测试计划-实时任务列表")
|
||||||
|
public Pager<List<TaskCenterDTO>> projectList(@Validated @RequestBody TaskCenterPageRequest request) {
|
||||||
|
return testPlanTaskCenterService.getProjectPage(request, SessionUtils.getCurrentProjectId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/org/real-time/page")
|
||||||
|
@Operation(summary = "组织-任务中心-测试计划-实时任务列表")
|
||||||
|
@RequiresPermissions(PermissionConstants.ORGANIZATION_TASK_CENTER_READ)
|
||||||
|
public Pager<List<TaskCenterDTO>> orgList(@Validated @RequestBody TaskCenterPageRequest request) {
|
||||||
|
return testPlanTaskCenterService.getOrganizationPage(request, SessionUtils.getCurrentOrganizationId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/system/real-time/page")
|
||||||
|
@Operation(summary = "系统-任务中心-测试计划-实时任务列表")
|
||||||
|
@RequiresPermissions(PermissionConstants.SYSTEM_TASK_CENTER_READ)
|
||||||
|
public Pager<List<TaskCenterDTO>> systemList(@Validated @RequestBody TaskCenterPageRequest request) {
|
||||||
|
return testPlanTaskCenterService.getSystemPage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import io.metersphere.plan.dto.request.TestPlanReportBatchRequest;
|
||||||
import io.metersphere.plan.dto.request.TestPlanReportPageRequest;
|
import io.metersphere.plan.dto.request.TestPlanReportPageRequest;
|
||||||
import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
|
import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
|
||||||
import io.metersphere.system.dto.sdk.ApiReportMessageDTO;
|
import io.metersphere.system.dto.sdk.ApiReportMessageDTO;
|
||||||
|
import io.metersphere.system.dto.taskcenter.TaskCenterDTO;
|
||||||
|
import io.metersphere.system.dto.taskcenter.request.TaskCenterPageRequest;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -36,4 +38,8 @@ public interface ExtTestPlanReportMapper {
|
||||||
List<String> selectReportIdByProjectIdAndTime(@Param("time") long timeMills, @Param("projectId") String projectId);
|
List<String> selectReportIdByProjectIdAndTime(@Param("time") long timeMills, @Param("projectId") String projectId);
|
||||||
|
|
||||||
List<String> selectReportIdTestPlanIds(@Param("testPlanIds") List<String> testPlanIds);
|
List<String> selectReportIdTestPlanIds(@Param("testPlanIds") List<String> testPlanIds);
|
||||||
|
|
||||||
|
List<TaskCenterDTO> taskCenterlist(@Param("request") TaskCenterPageRequest request, @Param("projectIds") List<String> projectIds,
|
||||||
|
@Param("startTime") long startTime, @Param("endTime") long endTime);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,43 @@
|
||||||
#{testPlanId}
|
#{testPlanId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="taskCenterlist" resultType="io.metersphere.system.dto.taskcenter.TaskCenterDTO">
|
||||||
|
select
|
||||||
|
distinct tpr.id,
|
||||||
|
tpr.project_id,
|
||||||
|
tpr.integrated,
|
||||||
|
tpr.result_status as status,
|
||||||
|
tpr.exec_status,
|
||||||
|
tpr.start_time AS operationTime,
|
||||||
|
tpr.create_user AS operationName,
|
||||||
|
tpr.trigger_mode,
|
||||||
|
tpr.start_time,
|
||||||
|
project.organization_id,
|
||||||
|
|
||||||
|
tp.num AS resourceNum,
|
||||||
|
tp.name AS resourceName,
|
||||||
|
tp.id AS resourceId
|
||||||
|
FROM
|
||||||
|
test_plan_report tpr
|
||||||
|
INNER JOIN test_plan tp ON tpr.test_plan_id = tp.id
|
||||||
|
left join project on tpr.project_id = project.id
|
||||||
|
where
|
||||||
|
tpr.start_time BETWEEN #{startTime} AND #{endTime}
|
||||||
|
<if test="projectIds != null and projectIds.size() > 0">
|
||||||
|
and
|
||||||
|
tpr.project_id IN
|
||||||
|
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
|
||||||
|
#{projectId}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="request.keyword != null and request.keyword != ''">
|
||||||
|
and (tp.num like concat('%', #{request.keyword},'%')
|
||||||
|
|
||||||
|
or tp.name like concat('%', #{request.keyword},'%')
|
||||||
|
)
|
||||||
|
</if>
|
||||||
|
<include refid="filter"/>
|
||||||
|
</select>
|
||||||
|
|
||||||
<sql id="queryWhereCondition">
|
<sql id="queryWhereCondition">
|
||||||
<where>
|
<where>
|
||||||
|
@ -104,10 +141,24 @@
|
||||||
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
|
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
|
||||||
</when>
|
</when>
|
||||||
<!-- 执行结果 -->
|
<!-- 执行结果 -->
|
||||||
<when test="key == 'resultStatus'">
|
<when test="key == 'resultStatus' || key == 'status'">
|
||||||
and tpr.result_status in
|
and tpr.result_status in
|
||||||
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
|
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
|
||||||
</when>
|
</when>
|
||||||
|
<!-- 项目id -->
|
||||||
|
<when test="key=='projectIds'">
|
||||||
|
and tpr.project_id in
|
||||||
|
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||||
|
#{value}
|
||||||
|
</foreach>
|
||||||
|
</when>
|
||||||
|
<!-- 组织id -->
|
||||||
|
<when test="key=='organizationIds'">
|
||||||
|
and project.organization_id in
|
||||||
|
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||||
|
#{value}
|
||||||
|
</foreach>
|
||||||
|
</when>
|
||||||
</choose>
|
</choose>
|
||||||
</if>
|
</if>
|
||||||
</foreach>
|
</foreach>
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
package io.metersphere.plan.service;
|
||||||
|
|
||||||
|
import com.github.pagehelper.Page;
|
||||||
|
import com.github.pagehelper.page.PageMethod;
|
||||||
|
import io.metersphere.api.dto.definition.ExecuteReportDTO;
|
||||||
|
import io.metersphere.api.mapper.ExtApiScenarioReportMapper;
|
||||||
|
import io.metersphere.plan.mapper.ExtTestPlanReportMapper;
|
||||||
|
import io.metersphere.project.domain.Project;
|
||||||
|
import io.metersphere.project.mapper.ProjectMapper;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.util.DateUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import io.metersphere.system.domain.Organization;
|
||||||
|
import io.metersphere.system.dto.sdk.OptionDTO;
|
||||||
|
import io.metersphere.system.dto.taskcenter.TaskCenterDTO;
|
||||||
|
import io.metersphere.system.dto.taskcenter.request.TaskCenterPageRequest;
|
||||||
|
import io.metersphere.system.log.service.OperationLogService;
|
||||||
|
import io.metersphere.system.mapper.BaseProjectMapper;
|
||||||
|
import io.metersphere.system.mapper.ExtOrganizationMapper;
|
||||||
|
import io.metersphere.system.mapper.OrganizationMapper;
|
||||||
|
import io.metersphere.system.service.TestResourcePoolService;
|
||||||
|
import io.metersphere.system.service.UserLoginService;
|
||||||
|
import io.metersphere.system.utils.PageUtils;
|
||||||
|
import io.metersphere.system.utils.Pager;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.kafka.core.KafkaTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public class TestPlanTaskCenterService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
ExtTestPlanReportMapper extTestPlanReportMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
ExtOrganizationMapper extOrganizationMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
BaseProjectMapper baseProjectMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
UserLoginService userLoginService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
ProjectMapper projectMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
OrganizationMapper organizationMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
ExtApiScenarioReportMapper extApiScenarioReportMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
TestResourcePoolService testResourcePoolService;
|
||||||
|
@Resource
|
||||||
|
OperationLogService operationLogService;
|
||||||
|
@Resource
|
||||||
|
private KafkaTemplate<String, String> kafkaTemplate;
|
||||||
|
private static final String DEFAULT_SORT = "start_time desc";
|
||||||
|
private final static String PROJECT_STOP = "/task/center/api/project/stop";
|
||||||
|
private final static String ORG_STOP = "/task/center/api/org/stop";
|
||||||
|
private final static String SYSTEM_STOP = "/task/center/api/system/stop";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务中心实时任务列表-项目级
|
||||||
|
*
|
||||||
|
* @param request 请求参数
|
||||||
|
* @return 任务中心实时任务列表
|
||||||
|
*/
|
||||||
|
public Pager<List<TaskCenterDTO>> getProjectPage(TaskCenterPageRequest request, String projectId) {
|
||||||
|
checkProjectExist(projectId);
|
||||||
|
List<OptionDTO> projectList = getProjectOption(projectId);
|
||||||
|
return createTaskCenterPager(request, projectList, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务中心实时任务列表-组织级
|
||||||
|
*
|
||||||
|
* @param request 请求参数
|
||||||
|
* @return 任务中心实时任务列表
|
||||||
|
*/
|
||||||
|
public Pager<List<TaskCenterDTO>> getOrganizationPage(TaskCenterPageRequest request, String organizationId) {
|
||||||
|
checkOrganizationExist(organizationId);
|
||||||
|
List<OptionDTO> projectList = getOrgProjectList(organizationId);
|
||||||
|
return createTaskCenterPager(request, projectList, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务中心实时任务列表-系统级
|
||||||
|
*
|
||||||
|
* @param request 请求参数
|
||||||
|
* @return 任务中心实时任务列表
|
||||||
|
*/
|
||||||
|
public Pager<List<TaskCenterDTO>> getSystemPage(TaskCenterPageRequest request) {
|
||||||
|
List<OptionDTO> projectList = getSystemProjectList();
|
||||||
|
return createTaskCenterPager(request, projectList, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pager<List<TaskCenterDTO>> createTaskCenterPager(TaskCenterPageRequest request, List<OptionDTO> projectList, boolean isSystem) {
|
||||||
|
Page<Object> page = PageMethod.startPage(request.getCurrent(), request.getPageSize(),
|
||||||
|
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : DEFAULT_SORT);
|
||||||
|
return PageUtils.setPageInfo(page, getPage(request, projectList, isSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TaskCenterDTO> getPage(TaskCenterPageRequest request, List<OptionDTO> projectList, boolean isSystem) {
|
||||||
|
List<TaskCenterDTO> list = new ArrayList<>();
|
||||||
|
List<String> projectIds = projectList.stream().map(OptionDTO::getId).toList();
|
||||||
|
Map<String, ExecuteReportDTO> historyDeletedMap = new HashMap<>();
|
||||||
|
list = extTestPlanReportMapper.taskCenterlist(request, isSystem ? new ArrayList<>() : projectIds, DateUtils.getDailyStartTime(), DateUtils.getDailyEndTime());
|
||||||
|
//执行历史列表
|
||||||
|
/*List<String> reportIds = list.stream().map(TaskCenterDTO::getId).toList();
|
||||||
|
if (CollectionUtils.isNotEmpty(reportIds)) {
|
||||||
|
List<ExecuteReportDTO> historyDeletedList = extTestPlanReportMapper.getHistoryDeleted(reportIds);
|
||||||
|
historyDeletedMap = historyDeletedList.stream().collect(Collectors.toMap(ExecuteReportDTO::getId, Function.identity()));
|
||||||
|
}*/
|
||||||
|
processTaskCenter(list, projectList, projectIds, historyDeletedMap);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processTaskCenter(List<TaskCenterDTO> list, List<OptionDTO> projectList, List<String> projectIds, Map<String, ExecuteReportDTO> historyDeletedMap) {
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
// 取所有的userid
|
||||||
|
Set<String> userSet = list.stream()
|
||||||
|
.flatMap(item -> Stream.of(item.getOperationName()))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Map<String, String> userMap = userLoginService.getUserNameMap(new ArrayList<>(userSet));
|
||||||
|
// 项目
|
||||||
|
Map<String, String> projectMap = projectList.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
|
||||||
|
// 组织
|
||||||
|
List<OptionDTO> orgListByProjectList = getOrgListByProjectIds(projectIds);
|
||||||
|
Map<String, String> orgMap = orgListByProjectList.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
|
||||||
|
|
||||||
|
list.forEach(item -> {
|
||||||
|
item.setOperationName(userMap.getOrDefault(item.getOperationName(), StringUtils.EMPTY));
|
||||||
|
item.setProjectName(projectMap.getOrDefault(item.getProjectId(), StringUtils.EMPTY));
|
||||||
|
item.setOrganizationName(orgMap.getOrDefault(item.getProjectId(), StringUtils.EMPTY));
|
||||||
|
item.setHistoryDeleted(MapUtils.isNotEmpty(historyDeletedMap) && !historyDeletedMap.containsKey(item.getId()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OptionDTO> getProjectOption(String id) {
|
||||||
|
return baseProjectMapper.getProjectOptionsById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OptionDTO> getOrgProjectList(String orgId) {
|
||||||
|
return baseProjectMapper.getProjectOptionsByOrgId(orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OptionDTO> getSystemProjectList() {
|
||||||
|
return baseProjectMapper.getProjectOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OptionDTO> getOrgListByProjectIds(List<String> projectIds) {
|
||||||
|
return extOrganizationMapper.getOrgListByProjectIds(projectIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看项目是否存在
|
||||||
|
*
|
||||||
|
* @param projectId 项目ID
|
||||||
|
*/
|
||||||
|
private void checkProjectExist(String projectId) {
|
||||||
|
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||||
|
if (project == null) {
|
||||||
|
throw new MSException(Translator.get("project_not_exist"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看组织是否存在
|
||||||
|
*
|
||||||
|
* @param orgId 组织ID
|
||||||
|
*/
|
||||||
|
private void checkOrganizationExist(String orgId) {
|
||||||
|
Organization organization = organizationMapper.selectByPrimaryKey(orgId);
|
||||||
|
if (organization == null) {
|
||||||
|
throw new MSException(Translator.get("organization_not_exist"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package io.metersphere.plan.controller;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
|
import io.metersphere.sdk.constants.TaskCenterResourceType;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import io.metersphere.system.base.BaseTest;
|
||||||
|
import io.metersphere.system.controller.handler.ResultHolder;
|
||||||
|
import io.metersphere.system.dto.taskcenter.request.TaskCenterPageRequest;
|
||||||
|
import io.metersphere.system.utils.Pager;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
|
import org.springframework.test.context.jdbc.SqlConfig;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
import org.springframework.test.web.servlet.ResultMatcher;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
public class TestPlanTaskCenterControllerTests extends BaseTest {
|
||||||
|
|
||||||
|
private static final String BASE_PATH = "/task/center/plan/";
|
||||||
|
private final static String REAL_TIME_PROJECT_PAGE = BASE_PATH + "project/real-time/page";
|
||||||
|
private final static String REAL_TIME_ORG_PAGE = BASE_PATH + "org/real-time/page";
|
||||||
|
private final static String REAL_TIME_SYSTEM_PAGE = BASE_PATH + "system/real-time/page";
|
||||||
|
|
||||||
|
private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError();
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(9)
|
||||||
|
@Sql(scripts = {"/dml/init_task_plan.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
|
||||||
|
public void getPage() throws Exception {
|
||||||
|
|
||||||
|
doTaskCenterPage("KEYWORD", REAL_TIME_PROJECT_PAGE, TaskCenterResourceType.TEST_PLAN.toString());
|
||||||
|
doTaskCenterPage("FILTER", REAL_TIME_PROJECT_PAGE, TaskCenterResourceType.TEST_PLAN.toString());
|
||||||
|
doTaskCenterPage("KEYWORD", REAL_TIME_ORG_PAGE, TaskCenterResourceType.TEST_PLAN.toString());
|
||||||
|
doTaskCenterPage("FILTER", REAL_TIME_ORG_PAGE, TaskCenterResourceType.TEST_PLAN.toString());
|
||||||
|
doTaskCenterPage("KEYWORD", REAL_TIME_SYSTEM_PAGE, TaskCenterResourceType.TEST_PLAN.toString());
|
||||||
|
doTaskCenterPage("FILTER", REAL_TIME_SYSTEM_PAGE, TaskCenterResourceType.TEST_PLAN.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTaskCenterPage(String search, String url, String moduleType) throws Exception {
|
||||||
|
TaskCenterPageRequest request = new TaskCenterPageRequest();
|
||||||
|
request.setModuleType(moduleType);
|
||||||
|
request.setCurrent(1);
|
||||||
|
request.setPageSize(10);
|
||||||
|
request.setSort(Map.of("startTime", "asc"));
|
||||||
|
// "KEYWORD", "FILTER"
|
||||||
|
switch (search) {
|
||||||
|
case "KEYWORD" -> configureKeywordSearch(request);
|
||||||
|
case "FILTER" -> configureFilterSearch(request);
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(url)
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
|
.header(SessionConstants.CURRENT_PROJECT, DEFAULT_PROJECT_ID)
|
||||||
|
.header(SessionConstants.CURRENT_ORGANIZATION, DEFAULT_ORGANIZATION_ID)
|
||||||
|
.content(JSON.toJSONString(request))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
|
||||||
|
|
||||||
|
// 获取返回值
|
||||||
|
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||||
|
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
|
||||||
|
LogUtils.info(resultHolder);
|
||||||
|
// 返回请求正常
|
||||||
|
Assertions.assertNotNull(resultHolder);
|
||||||
|
Pager<?> pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
|
||||||
|
// 返回值不为空
|
||||||
|
Assertions.assertNotNull(pageData);
|
||||||
|
// 返回值的页码和当前页码相同
|
||||||
|
Assertions.assertEquals(pageData.getCurrent(), request.getCurrent());
|
||||||
|
// 返回的数据量不超过规定要返回的数据量相同
|
||||||
|
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTaskCenterPageError(String url, String moduleType) throws Exception {
|
||||||
|
TaskCenterPageRequest request = new TaskCenterPageRequest();
|
||||||
|
request.setModuleType(moduleType);
|
||||||
|
request.setCurrent(1);
|
||||||
|
request.setPageSize(10);
|
||||||
|
request.setSort(Map.of("startTime", "asc"));
|
||||||
|
configureKeywordSearch(request);
|
||||||
|
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.post(url)
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
|
.header(SessionConstants.CURRENT_PROJECT, "DEFAULT_PROJECT_ID")
|
||||||
|
.header(SessionConstants.CURRENT_ORGANIZATION, "DEFAULT_ORGANIZATION_ID")
|
||||||
|
.content(JSON.toJSONString(request))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(ERROR_REQUEST_MATCHER);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureKeywordSearch(TaskCenterPageRequest request) {
|
||||||
|
request.setKeyword("18");
|
||||||
|
request.setSort(Map.of("triggerMode", "asc"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureFilterSearch(TaskCenterPageRequest request) {
|
||||||
|
Map<String, List<String>> filters = new HashMap<>();
|
||||||
|
request.setSort(Map.of());
|
||||||
|
filters.put("triggerMode", List.of("MANUAL"));
|
||||||
|
request.setFilter(filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(10)
|
||||||
|
public void getPageError() throws Exception {
|
||||||
|
doTaskCenterPageError(REAL_TIME_PROJECT_PAGE, TaskCenterResourceType.TEST_PLAN.toString());
|
||||||
|
doTaskCenterPageError(REAL_TIME_ORG_PAGE, TaskCenterResourceType.TEST_PLAN.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
replace INTO `test_plan`(`id`, `num`, `project_id`, `group_id`, `module_id`, `name`, `status`, `type`, `tags`, `create_time`, `create_user`, `update_time`, `update_user`, `planned_start_time`, `planned_end_time`, `actual_start_time`, `actual_end_time`, `description`)
|
||||||
|
VALUES ('test_plan_id_1', 5000, '100001100001', 'NONE', '1', '测试一下计划', 'PREPARED', 'TEST_PLAN', NULL,
|
||||||
|
1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11');
|
||||||
|
|
||||||
|
replace INTO `test_plan_report`(`id`, `test_plan_id`, `name`, `create_user`, `create_time`, `start_time`, `end_time`, `trigger_mode`, `exec_status`, `result_status`, `pass_threshold`, `pass_rate`, `project_id`, `integrated`, `deleted`)
|
||||||
|
VALUES
|
||||||
|
('test-plan-report-id-1', 'test_plan_id_1', '测试一下计划报告1', 'admin', UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, 'MANUAL', 'PENDING', 'SUCCESS', '99.99', 100.00, '100001100001', 0, 0),
|
||||||
|
('test-plan-report-id-2', 'test_plan_id_1', '测试一下计划报告1', 'admin', UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, 'MANUAL', 'PENDING', '-', '99.99', 100.00, '100001100001', 0, 0),
|
||||||
|
('test-plan-report-id-3', 'test_plan_id_1', '测试一下计划报告3', 'admin', UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, 'MANUAL', 'PENDING', '-', '99.99', 100.00, '100001100001',1, 0),
|
||||||
|
('test-plan-report-id-4', 'test_plan_id_1', '测试一下计划报告4', 'admin', UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, 'MANUAL', 'PENDING', '-', '99.99', 100.00, '100001100001', 1, 0);
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,11 @@ import {
|
||||||
stopRealSysApiUrl,
|
stopRealSysApiUrl,
|
||||||
systemRealTotal,
|
systemRealTotal,
|
||||||
systemScheduleTotal,
|
systemScheduleTotal,
|
||||||
|
taskOrgPlanRealCenterListUrl,
|
||||||
taskOrgRealCenterListUrl,
|
taskOrgRealCenterListUrl,
|
||||||
|
taskProPlanRealCenterListUrl,
|
||||||
taskProRealCenterListUrl,
|
taskProRealCenterListUrl,
|
||||||
|
taskSysPlanRealCenterListUrl,
|
||||||
taskSysRealCenterListUrl,
|
taskSysRealCenterListUrl,
|
||||||
updateScheduleOrgTaskUrl,
|
updateScheduleOrgTaskUrl,
|
||||||
updateScheduleProTaskUrl,
|
updateScheduleProTaskUrl,
|
||||||
|
@ -193,4 +196,17 @@ export function getProjectRealTotal() {
|
||||||
return MSR.get({ url: `${projectRealTotal}` });
|
return MSR.get({ url: `${projectRealTotal}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 实时任务 测试计划
|
||||||
|
export function getRealSysPlanList(data: TableQueryParams) {
|
||||||
|
return MSR.post<CommonList<RealTaskCenterApiCaseItem>>({ url: taskSysPlanRealCenterListUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRealOrgPlanList(data: TableQueryParams) {
|
||||||
|
return MSR.post<CommonList<RealTaskCenterApiCaseItem>>({ url: taskOrgPlanRealCenterListUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRealProPlanList(data: TableQueryParams) {
|
||||||
|
return MSR.post<CommonList<RealTaskCenterApiCaseItem>>({ url: taskProPlanRealCenterListUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -76,3 +76,7 @@ export const projectScheduleTotal = '/task/center/project/schedule/total';
|
||||||
export const systemRealTotal = '/task/center/system/real/total';
|
export const systemRealTotal = '/task/center/system/real/total';
|
||||||
export const orgRealTotal = '/task/center/org/real/total';
|
export const orgRealTotal = '/task/center/org/real/total';
|
||||||
export const projectRealTotal = '/task/center/project/real/total';
|
export const projectRealTotal = '/task/center/project/real/total';
|
||||||
|
|
||||||
|
export const taskSysPlanRealCenterListUrl = '/task/center/plan/system/real-time/page';
|
||||||
|
export const taskOrgPlanRealCenterListUrl = '/task/center/plan/org/real-time/page';
|
||||||
|
export const taskProPlanRealCenterListUrl = '/task/center/plan/project/real-time/page';
|
||||||
|
|
|
@ -86,6 +86,9 @@ export enum TableKeyEnum {
|
||||||
TASK_SCHEDULE_TASK_TEST_PLAN_SYSTEM = 'taskCenterScheduleTestPlanSystem',
|
TASK_SCHEDULE_TASK_TEST_PLAN_SYSTEM = 'taskCenterScheduleTestPlanSystem',
|
||||||
TASK_SCHEDULE_TASK_TEST_PLAN_ORGANIZATION = 'taskCenterScheduleTestPlanOrganization',
|
TASK_SCHEDULE_TASK_TEST_PLAN_ORGANIZATION = 'taskCenterScheduleTestPlanOrganization',
|
||||||
TASK_SCHEDULE_TASK_TEST_PLAN_PROJECT = 'taskCenterScheduleTestPlanProject',
|
TASK_SCHEDULE_TASK_TEST_PLAN_PROJECT = 'taskCenterScheduleTestPlanProject',
|
||||||
|
TASK_PLAN_SYSTEM = 'taskCenterPlanSystem',
|
||||||
|
TASK_PLAN_ORGANIZATION = 'taskCenterPlanOrganization',
|
||||||
|
TASK_PLAN_PROJECT = 'taskCenterPlanProject',
|
||||||
}
|
}
|
||||||
|
|
||||||
// 具有特殊功能的列
|
// 具有特殊功能的列
|
||||||
|
|
|
@ -14,7 +14,20 @@
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<a-divider margin="0" class="!mb-[16px]"></a-divider>
|
<a-divider margin="0" class="!mb-[16px]"></a-divider>
|
||||||
<!-- 接口用例列表-->
|
<!-- 接口用例列表-->
|
||||||
<ApiCase v-if="activeTask === 'real'" :name="listName" :module-type="activeTab" :group="props.group" />
|
<ApiCase
|
||||||
|
v-if="
|
||||||
|
activeTask === 'real' && (activeTab === TaskCenterEnum.API_CASE || activeTab === TaskCenterEnum.API_SCENARIO)
|
||||||
|
"
|
||||||
|
:name="listName"
|
||||||
|
:module-type="activeTab"
|
||||||
|
:group="props.group"
|
||||||
|
/>
|
||||||
|
<!-- 测试计划列表-->
|
||||||
|
<TestPlan
|
||||||
|
v-if="activeTask === 'real' && activeTab === TaskCenterEnum.TEST_PLAN"
|
||||||
|
:name="listName"
|
||||||
|
:group="props.group"
|
||||||
|
/>
|
||||||
<ScheduledTask v-if="activeTask === 'timing'" :name="listName" :group="props.group" :module-type="activeTab" />
|
<ScheduledTask v-if="activeTask === 'timing'" :name="listName" :group="props.group" :module-type="activeTab" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,10 +38,10 @@
|
||||||
|
|
||||||
import ApiCase from './apiCase.vue';
|
import ApiCase from './apiCase.vue';
|
||||||
import ScheduledTask from './scheduledTask.vue';
|
import ScheduledTask from './scheduledTask.vue';
|
||||||
|
import TestPlan from './testPlan.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
import type { ResourceTypeMapKey } from '@/enums/taskCenter';
|
|
||||||
import { TaskCenterEnum } from '@/enums/taskCenter';
|
import { TaskCenterEnum } from '@/enums/taskCenter';
|
||||||
|
|
||||||
import type { ExtractedKeys } from './utils';
|
import type { ExtractedKeys } from './utils';
|
||||||
|
@ -51,6 +64,10 @@
|
||||||
value: TaskCenterEnum.API_SCENARIO,
|
value: TaskCenterEnum.API_SCENARIO,
|
||||||
label: t('project.taskCenter.apiScenario'),
|
label: t('project.taskCenter.apiScenario'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: TaskCenterEnum.TEST_PLAN,
|
||||||
|
label: t('project.taskCenter.testPlan'),
|
||||||
|
},
|
||||||
// TODO 第一个版本目前不上以下几类
|
// TODO 第一个版本目前不上以下几类
|
||||||
// {
|
// {
|
||||||
// value: TaskCenterEnum.UI_TEST,
|
// value: TaskCenterEnum.UI_TEST,
|
||||||
|
@ -60,10 +77,6 @@
|
||||||
// value: TaskCenterEnum.LOAD_TEST,
|
// value: TaskCenterEnum.LOAD_TEST,
|
||||||
// label: t('project.taskCenter.performanceTest'),
|
// label: t('project.taskCenter.performanceTest'),
|
||||||
// },
|
// },
|
||||||
// {
|
|
||||||
// value: TaskCenterEnum.TEST_PLAN,
|
|
||||||
// label: t('project.taskCenter.testPlan'),
|
|
||||||
// },
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const timingTabList = ref([
|
const timingTabList = ref([
|
||||||
|
@ -78,7 +91,7 @@
|
||||||
{
|
{
|
||||||
value: TaskCenterEnum.TEST_PLAN,
|
value: TaskCenterEnum.TEST_PLAN,
|
||||||
label: t('project.taskCenter.testPlan'),
|
label: t('project.taskCenter.testPlan'),
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const activeTask = ref(route.query.tab || 'real');
|
const activeTask = ref(route.query.tab || 'real');
|
||||||
|
@ -110,10 +123,12 @@
|
||||||
.box {
|
.box {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
width: 252px;
|
width: 252px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-right: 1px solid var(--color-text-n8);
|
border-right: 1px solid var(--color-text-n8);
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
height: 38px;
|
height: 38px;
|
||||||
|
@ -121,18 +136,21 @@
|
||||||
line-height: 38px;
|
line-height: 38px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: rgb(var(--primary-5));
|
color: rgb(var(--primary-5));
|
||||||
background: rgb(var(--primary-1));
|
background: rgb(var(--primary-1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
width: calc(100% - 300px);
|
width: calc(100% - 300px);
|
||||||
flex-grow: 1; /* 自适应 */
|
flex-grow: 1; /* 自适应 */
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-content {
|
.no-content {
|
||||||
:deep(.arco-tabs-content) {
|
:deep(.arco-tabs-content) {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|
|
@ -0,0 +1,419 @@
|
||||||
|
<template>
|
||||||
|
<div class="px-[16px]">
|
||||||
|
<div class="mb-[16px] flex items-center justify-between">
|
||||||
|
<div class="flex items-center"></div>
|
||||||
|
<div class="items-right flex gap-[8px]">
|
||||||
|
<a-input-search
|
||||||
|
v-model:model-value="keyword"
|
||||||
|
:placeholder="t('system.organization.searchIndexPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
class="mx-[8px] w-[240px]"
|
||||||
|
@search="searchList"
|
||||||
|
@press-enter="searchList"
|
||||||
|
@clear="searchList"
|
||||||
|
></a-input-search>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ms-base-table
|
||||||
|
v-bind="propsRes"
|
||||||
|
ref="tableRef"
|
||||||
|
:action-config="tableBatchActions"
|
||||||
|
:selectable="hasOperationPermission"
|
||||||
|
v-on="propsEvent"
|
||||||
|
@batch-action="handleTableBatch"
|
||||||
|
>
|
||||||
|
<template #resourceNum="{ record }">
|
||||||
|
<div
|
||||||
|
v-if="!record.integrated"
|
||||||
|
type="text"
|
||||||
|
class="one-line-text w-full"
|
||||||
|
:class="[hasJumpPermission ? 'text-[rgb(var(--primary-5))]' : '']"
|
||||||
|
@click="showDetail()"
|
||||||
|
>{{ record.resourceNum }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #resourceName="{ record }">
|
||||||
|
<div
|
||||||
|
v-if="!record.integrated"
|
||||||
|
class="one-line-text max-w-[300px]"
|
||||||
|
:class="[hasJumpPermission ? 'text-[rgb(var(--primary-5))]' : '']"
|
||||||
|
@click="showDetail()"
|
||||||
|
>{{ record.resourceName }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #status="{ record }">
|
||||||
|
<ExecutionStatus :status="record.status" />
|
||||||
|
</template>
|
||||||
|
<template #execStatus="{ record }">
|
||||||
|
<ExecStatus :status="record.execStatus" />
|
||||||
|
</template>
|
||||||
|
<template #[FilterSlotNameEnum.TEST_PLAN_REPORT_EXEC_STATUS]="{ filterContent }">
|
||||||
|
<ExecStatus :status="filterContent.value" />
|
||||||
|
</template>
|
||||||
|
<template #[FilterSlotNameEnum.TEST_PLAN_STATUS_FILTER]="{ filterContent }">
|
||||||
|
<ExecutionStatus :status="filterContent.value" />
|
||||||
|
</template>
|
||||||
|
<template #projectName="{ record }">
|
||||||
|
<a-tooltip :content="`${record.projectName}`" position="tl">
|
||||||
|
<div class="one-line-text">{{ characterLimit(record.projectName) }}</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<template #organizationName="{ record }">
|
||||||
|
<a-tooltip :content="`${record.organizationName}`" position="tl">
|
||||||
|
<div class="one-line-text">{{ characterLimit(record.organizationName) }}</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<template #triggerMode="{ record }">
|
||||||
|
<span>{{ t(ExecutionMethodsLabel[record.triggerMode as keyof typeof ExecutionMethodsLabel]) }}</span>
|
||||||
|
</template>
|
||||||
|
<template #operationTime="{ record }">
|
||||||
|
<span>{{ dayjs(record.operationTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||||
|
</template>
|
||||||
|
<template #operation="{ record }">
|
||||||
|
<div v-if="record.historyDeleted">
|
||||||
|
<a-tooltip :content="t('project.executionHistory.cleared')">
|
||||||
|
<MsButton
|
||||||
|
class="!mr-0"
|
||||||
|
:disabled="record.historyDeleted || !hasAnyPermission(permissionsMap[props.group].report)"
|
||||||
|
@click="viewReport(record.id)"
|
||||||
|
>{{ t('project.taskCenter.viewReport') }}
|
||||||
|
</MsButton>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<MsButton
|
||||||
|
class="!mr-0"
|
||||||
|
:disabled="record.historyDeleted || !hasAnyPermission(permissionsMap[props.group].report)"
|
||||||
|
@click="viewReport(record.id)"
|
||||||
|
>{{ t('project.taskCenter.viewReport') }}
|
||||||
|
</MsButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider v-if="['RUNNING', 'RERUNNING'].includes(record.execStatus)" direction="vertical" />
|
||||||
|
<MsButton
|
||||||
|
v-if="
|
||||||
|
['RUNNING', 'RERUNNING'].includes(record.execStatus) && hasAnyPermission(permissionsMap[props.group].stop)
|
||||||
|
"
|
||||||
|
class="!mr-0"
|
||||||
|
@click="stop()"
|
||||||
|
>{{ t('project.taskCenter.stop') }}
|
||||||
|
</MsButton>
|
||||||
|
</template>
|
||||||
|
</ms-base-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
import ExecStatus from '@/views/test-plan/report/component/execStatus.vue';
|
||||||
|
import ExecutionStatus from '@/views/test-plan/report/component/reportStatus.vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getRealOrgPlanList,
|
||||||
|
getRealProPlanList,
|
||||||
|
getRealSysPlanList,
|
||||||
|
} from '@/api/modules/project-management/taskCenter';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useOpenNewPage from '@/hooks/useOpenNewPage';
|
||||||
|
import { useTableStore } from '@/store';
|
||||||
|
import { characterLimit } from '@/utils';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
|
import { BatchApiParams } from '@/models/common';
|
||||||
|
import { ReportExecStatus } from '@/enums/apiEnum';
|
||||||
|
import { PlanReportStatus } from '@/enums/reportEnum';
|
||||||
|
import { RouteEnum } from '@/enums/routeEnum';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||||
|
import { ExecutionMethodsLabel, TaskCenterEnum } from '@/enums/taskCenter';
|
||||||
|
|
||||||
|
import { getOrgColumns, getProjectColumns, Group } from './utils';
|
||||||
|
|
||||||
|
const { openNewPage } = useOpenNewPage();
|
||||||
|
|
||||||
|
const tableStore = useTableStore();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps<{
|
||||||
|
group: Group;
|
||||||
|
name: string;
|
||||||
|
}>();
|
||||||
|
const keyword = ref<string>('');
|
||||||
|
|
||||||
|
const permissionsMap: Record<Group, any> = {
|
||||||
|
organization: {
|
||||||
|
stop: ['ORGANIZATION_TASK_CENTER:READ+STOP', 'PROJECT_TEST_PLAN:READ+EXECUTE'],
|
||||||
|
jump: ['PROJECT_TEST_PLAN:READ'],
|
||||||
|
report: ['PROJECT_TEST_PLAN:READ+EXECUTE', 'PROJECT_TEST_PLAN_REPORT:READ'],
|
||||||
|
},
|
||||||
|
system: {
|
||||||
|
stop: ['SYSTEM_TASK_CENTER:READ+STOP', 'PROJECT_TEST_PLAN:READ+EXECUTE'],
|
||||||
|
jump: ['PROJECT_TEST_PLAN:READ'],
|
||||||
|
report: ['PROJECT_TEST_PLAN:READ+EXECUTE', 'PROJECT_TEST_PLAN_REPORT:READ'],
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
stop: ['PROJECT_TEST_PLAN:READ+EXECUTE'],
|
||||||
|
jump: ['PROJECT_TEST_PLAN:READ'],
|
||||||
|
report: ['PROJECT_TEST_PLAN:READ+EXECUTE', 'PROJECT_TEST_PLAN_REPORT:READ'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadRealMap = ref({
|
||||||
|
system: {
|
||||||
|
list: getRealSysPlanList,
|
||||||
|
},
|
||||||
|
organization: {
|
||||||
|
list: getRealOrgPlanList,
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
list: getRealProPlanList,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const hasJumpPermission = computed(() => hasAnyPermission(permissionsMap[props.group].jump));
|
||||||
|
const hasOperationPermission = computed(() => hasAnyPermission(permissionsMap[props.group].stop));
|
||||||
|
|
||||||
|
const statusResultOptions = computed(() => {
|
||||||
|
return Object.keys(PlanReportStatus).map((key) => {
|
||||||
|
return {
|
||||||
|
value: key,
|
||||||
|
label: PlanReportStatus[key].statusText,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const ExecStatusList = computed(() => {
|
||||||
|
return Object.values(ReportExecStatus).map((e) => {
|
||||||
|
return {
|
||||||
|
value: e,
|
||||||
|
key: e,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const triggerModeList = [
|
||||||
|
{
|
||||||
|
value: 'SCHEDULE',
|
||||||
|
label: t('project.taskCenter.scheduledTask'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'MANUAL',
|
||||||
|
label: t('project.taskCenter.manualExecution'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'API',
|
||||||
|
label: t('project.taskCenter.interfaceCall'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'BATCH',
|
||||||
|
label: t('project.taskCenter.batchExecution'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const staticColumns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'project.taskCenter.resourceID',
|
||||||
|
dataIndex: 'resourceNum',
|
||||||
|
slotName: 'resourceNum',
|
||||||
|
width: 200,
|
||||||
|
sortIndex: 1,
|
||||||
|
fixed: 'left',
|
||||||
|
showTooltip: true,
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: false,
|
||||||
|
columnSelectorDisabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'project.taskCenter.resourceName',
|
||||||
|
slotName: 'resourceName',
|
||||||
|
dataIndex: 'resourceName',
|
||||||
|
width: 300,
|
||||||
|
showDrag: false,
|
||||||
|
showTooltip: true,
|
||||||
|
showInTable: true,
|
||||||
|
columnSelectorDisabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'project.taskCenter.executionResult',
|
||||||
|
dataIndex: 'status',
|
||||||
|
slotName: 'status',
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
filterConfig: {
|
||||||
|
options: statusResultOptions.value,
|
||||||
|
filterSlotName: FilterSlotNameEnum.TEST_PLAN_STATUS_FILTER,
|
||||||
|
},
|
||||||
|
showInTable: true,
|
||||||
|
width: 200,
|
||||||
|
showDrag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'project.taskCenter.status',
|
||||||
|
dataIndex: 'execStatus',
|
||||||
|
slotName: 'execStatus',
|
||||||
|
filterConfig: {
|
||||||
|
options: ExecStatusList.value,
|
||||||
|
filterSlotName: FilterSlotNameEnum.TEST_PLAN_REPORT_EXEC_STATUS,
|
||||||
|
},
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
showInTable: true,
|
||||||
|
width: 200,
|
||||||
|
showDrag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'project.taskCenter.executionMode',
|
||||||
|
dataIndex: 'triggerMode',
|
||||||
|
slotName: 'triggerMode',
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
filterConfig: {
|
||||||
|
options: triggerModeList,
|
||||||
|
},
|
||||||
|
showInTable: true,
|
||||||
|
width: 150,
|
||||||
|
showDrag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'project.taskCenter.resourcePool',
|
||||||
|
slotName: 'poolName',
|
||||||
|
dataIndex: 'poolName',
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: true,
|
||||||
|
showTooltip: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'project.taskCenter.operator',
|
||||||
|
slotName: 'operationName',
|
||||||
|
dataIndex: 'operationName',
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: true,
|
||||||
|
showTooltip: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'project.taskCenter.operating',
|
||||||
|
dataIndex: 'operationTime',
|
||||||
|
slotName: 'operationTime',
|
||||||
|
width: 180,
|
||||||
|
showDrag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'common.operation',
|
||||||
|
slotName: 'operation',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: hasOperationPermission.value ? 180 : 100,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const tableKeysMap: Record<string, any> = {
|
||||||
|
system: TableKeyEnum.TASK_PLAN_SYSTEM,
|
||||||
|
organization: TableKeyEnum.TASK_PLAN_ORGANIZATION,
|
||||||
|
project: TableKeyEnum.TASK_PLAN_PROJECT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupColumnsMap: Record<string, any> = {
|
||||||
|
system: [getOrgColumns(), getProjectColumns(tableKeysMap[props.group]), ...staticColumns],
|
||||||
|
organization: [getProjectColumns(tableKeysMap[props.group]), ...staticColumns],
|
||||||
|
project: staticColumns,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, resetFilterParams } = useTable(
|
||||||
|
loadRealMap.value[props.group].list,
|
||||||
|
{
|
||||||
|
tableKey: tableKeysMap[props.group],
|
||||||
|
scroll: {
|
||||||
|
x: 1400,
|
||||||
|
},
|
||||||
|
showSetting: true,
|
||||||
|
selectable: hasOperationPermission.value,
|
||||||
|
heightUsed: 330,
|
||||||
|
enableDrag: false,
|
||||||
|
showSelectAll: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function initData() {
|
||||||
|
setLoadListParams({
|
||||||
|
moduleType: TaskCenterEnum.TEST_PLAN,
|
||||||
|
keyword: keyword.value,
|
||||||
|
filter: {
|
||||||
|
...propsRes.value.filter,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableBatchActions = {
|
||||||
|
baseAction: [
|
||||||
|
{
|
||||||
|
label: 'project.taskCenter.batchStop',
|
||||||
|
eventTag: 'batchStop',
|
||||||
|
anyPermission: permissionsMap[props.group].stop,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const batchParams = ref<BatchApiParams>({
|
||||||
|
selectIds: [],
|
||||||
|
selectAll: false,
|
||||||
|
excludeIds: [] as string[],
|
||||||
|
condition: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||||
|
batchParams.value = { ...params, selectIds: params?.selectedIds || [], condition: params?.condition || {} };
|
||||||
|
if (event.eventTag === 'batchStop') {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewReport(id: string) {
|
||||||
|
openNewPage(RouteEnum.TEST_PLAN_REPORT_DETAIL, {
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDetail() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchList() {
|
||||||
|
resetSelector();
|
||||||
|
initData();
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
initData();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.group,
|
||||||
|
(val) => {
|
||||||
|
if (val) {
|
||||||
|
resetSelector();
|
||||||
|
resetFilterParams();
|
||||||
|
initData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await tableStore.initColumn(tableKeysMap[props.group], groupColumnsMap[props.group], 'drawer', true);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -121,7 +121,10 @@ export const TaskStatus: Record<ResourceTypeMapKey, Record<string, { icon: strin
|
||||||
|
|
||||||
export type Group = 'system' | 'organization' | 'project';
|
export type Group = 'system' | 'organization' | 'project';
|
||||||
|
|
||||||
export type ExtractedKeys = Extract<ResourceTypeMapKey, TaskCenterEnum.API_CASE | TaskCenterEnum.API_SCENARIO>;
|
export type ExtractedKeys = Extract<
|
||||||
|
ResourceTypeMapKey,
|
||||||
|
TaskCenterEnum.API_CASE | TaskCenterEnum.API_SCENARIO | TaskCenterEnum.TEST_PLAN
|
||||||
|
>;
|
||||||
|
|
||||||
export const resourceTypeMap: Record<ResourceTypeMapKey, Record<string, any>> = {
|
export const resourceTypeMap: Record<ResourceTypeMapKey, Record<string, any>> = {
|
||||||
[TaskCenterEnum.API_CASE]: {
|
[TaskCenterEnum.API_CASE]: {
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
</template>
|
</template>
|
||||||
<!-- 执行状态筛选 -->
|
<!-- 执行状态筛选 -->
|
||||||
<template #resultStatus="{ record }">
|
<template #resultStatus="{ record }">
|
||||||
<ExecutionStatus :module-type="ReportStatusEnum.REPORT_STATUS" :status="record.resultStatus" />
|
<ExecutionStatus :status="record.resultStatus" />
|
||||||
</template>
|
</template>
|
||||||
<template #execStatus="{ record }">
|
<template #execStatus="{ record }">
|
||||||
<ExecStatus :status="record.execStatus" />
|
<ExecStatus :status="record.execStatus" />
|
||||||
|
@ -114,7 +114,7 @@
|
||||||
|
|
||||||
import { BatchApiParams } from '@/models/common';
|
import { BatchApiParams } from '@/models/common';
|
||||||
import { ReportExecStatus } from '@/enums/apiEnum';
|
import { ReportExecStatus } from '@/enums/apiEnum';
|
||||||
import { PlanReportStatus, ReportStatusEnum, TriggerModeLabel } from '@/enums/reportEnum';
|
import { PlanReportStatus, TriggerModeLabel } from '@/enums/reportEnum';
|
||||||
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||||
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
|
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
|
||||||
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
function getExecutionResult(): IconType {
|
function getExecutionResult(): IconType {
|
||||||
return iconTypeStatus[props.status] ?? iconTypeStatus[props.status]?.DEFAULT;
|
return iconTypeStatus[props.status] ? iconTypeStatus[props.status] : iconTypeStatus.DEFAULT;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue