feat(测试计划): 测试计划详情场景用例批量关联缺陷&批量新增缺陷
This commit is contained in:
parent
41d723bee9
commit
1ea2d56b07
|
@ -5,6 +5,9 @@ import com.github.pagehelper.PageHelper;
|
|||
import io.metersphere.api.dto.scenario.ApiScenarioReportDTO;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioReportDetailDTO;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioReportService;
|
||||
import io.metersphere.bug.domain.Bug;
|
||||
import io.metersphere.bug.dto.request.BugEditRequest;
|
||||
import io.metersphere.bug.service.BugService;
|
||||
import io.metersphere.dto.BugProviderDTO;
|
||||
import io.metersphere.plan.dto.request.*;
|
||||
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
|
||||
|
@ -17,6 +20,7 @@ import io.metersphere.request.BugPageProviderRequest;
|
|||
import io.metersphere.sdk.constants.HttpMethodConstants;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.system.dto.LogInsertModule;
|
||||
import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||
import io.metersphere.system.log.annotation.Log;
|
||||
|
@ -33,6 +37,7 @@ import org.apache.shiro.authz.annotation.Logical;
|
|||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -48,6 +53,8 @@ public class TestPlanApiScenarioController {
|
|||
private TestPlanApiScenarioBatchRunService testPlanApiScenarioBatchRunService;
|
||||
@Resource
|
||||
private ApiScenarioReportService apiScenarioReportService;
|
||||
@Resource
|
||||
private BugService bugService;
|
||||
|
||||
@PostMapping("/page")
|
||||
@Operation(summary = "测试计划-已关联场景用例列表分页查询")
|
||||
|
@ -181,4 +188,26 @@ public class TestPlanApiScenarioController {
|
|||
public void disassociateBug(@PathVariable String id) {
|
||||
testPlanApiScenarioService.disassociateBug(id);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/batch/add-bug")
|
||||
@Operation(summary = "测试计划-计划详情-场景用例-批量添加缺陷")
|
||||
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ_EXECUTE)
|
||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||
public void batchAddBug(@Validated @RequestPart("request") TestPlanScenarioBatchAddBugRequest request,
|
||||
@RequestPart(value = "files", required = false) List<MultipartFile> files) {
|
||||
BugEditRequest bugEditRequest = new BugEditRequest();
|
||||
BeanUtils.copyBean(bugEditRequest, request);
|
||||
Bug bug = bugService.addOrUpdate(bugEditRequest, files, SessionUtils.getUserId(), SessionUtils.getCurrentOrganizationId(), false);
|
||||
testPlanApiScenarioService.batchAssociateBug(request, bug.getId(), SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/batch/associate-bug")
|
||||
@Operation(summary = "测试计划-计划详情-场景用例-批量关联缺陷")
|
||||
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ_EXECUTE)
|
||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||
public void batchAssociateBug(@Validated @RequestBody TestPlanScenarioBatchAssociateBugRequest request) {
|
||||
testPlanApiScenarioService.batchAssociateBugByIds(request, SessionUtils.getUserId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package io.metersphere.plan.dto.request;
|
||||
|
||||
import io.metersphere.bug.dto.response.BugCustomFieldDTO;
|
||||
import io.metersphere.bug.dto.response.BugFileDTO;
|
||||
import io.metersphere.validation.groups.Created;
|
||||
import io.metersphere.validation.groups.Updated;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class TestPlanScenarioBatchAddBugRequest extends BasePlanCaseBatchRequest {
|
||||
|
||||
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{bug.id.not_blank}", groups = {Updated.class})
|
||||
@Size(min = 1, max = 50, message = "{bug.id.length_range}", groups = {Created.class, Updated.class})
|
||||
private String id;
|
||||
|
||||
@Schema(description = "缺陷标题")
|
||||
@Size(min = 1, max = 255, message = "{bug.title.length_range}", groups = {Created.class, Updated.class})
|
||||
private String title;
|
||||
|
||||
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{bug.project_id.not_blank}", groups = {Created.class, Updated.class})
|
||||
@Size(min = 1, max = 50, message = "{bug.project_id.length_range}", groups = {Created.class, Updated.class})
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "模板ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{bug.template_id.not_blank}", groups = {Created.class, Updated.class})
|
||||
@Size(min = 1, max = 50, message = "{bug.template_id.length_range}", groups = {Created.class, Updated.class})
|
||||
private String templateId;
|
||||
|
||||
@Schema(description = "标签")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "缺陷内容")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "自定义字段集合")
|
||||
private List<BugCustomFieldDTO> customFields;
|
||||
|
||||
@Schema(description = "删除的本地附件集合, {文件ID")
|
||||
private List<String> deleteLocalFileIds;
|
||||
|
||||
@Schema(description = "取消关联附件关系ID集合, 关联关系ID")
|
||||
private List<String> unLinkRefIds;
|
||||
|
||||
@Schema(description = "关联附件集合, 文件ID")
|
||||
private List<String> linkFileIds;
|
||||
|
||||
@Schema(description = "复制的附件")
|
||||
private List<BugFileDTO> copyFiles;
|
||||
|
||||
@Schema(description = "富文本临时文件ID")
|
||||
private List<String> richTextTmpFileIds;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io.metersphere.plan.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class TestPlanScenarioBatchAssociateBugRequest extends BasePlanCaseBatchRequest {
|
||||
@Schema(description = "缺陷ID集合", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "{bug.id.not_blank}")
|
||||
private List<String> bugIds;
|
||||
}
|
|
@ -13,6 +13,9 @@ import io.metersphere.api.service.scenario.ApiScenarioModuleService;
|
|||
import io.metersphere.api.service.scenario.ApiScenarioReportService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioRunService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.bug.domain.BugRelationCase;
|
||||
import io.metersphere.bug.domain.BugRelationCaseExample;
|
||||
import io.metersphere.bug.mapper.BugRelationCaseMapper;
|
||||
import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO;
|
||||
import io.metersphere.functional.dto.ProjectOptionDTO;
|
||||
import io.metersphere.plan.constants.AssociateCaseType;
|
||||
|
@ -38,6 +41,7 @@ import io.metersphere.sdk.exception.MSException;
|
|||
import io.metersphere.sdk.mapper.EnvironmentMapper;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||
import io.metersphere.sdk.util.SubListUtils;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.dto.LogInsertModule;
|
||||
import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||
|
@ -111,6 +115,9 @@ public class TestPlanApiScenarioService extends TestPlanResourceService {
|
|||
|
||||
private static final String EXECUTOR = "executeUserName";
|
||||
|
||||
@Resource
|
||||
private BugRelationCaseMapper bugRelationCaseMapper;
|
||||
|
||||
@Override
|
||||
public List<TestPlanResourceExecResultDTO> selectDistinctExecResultByProjectId(String projectId) {
|
||||
return extTestPlanApiScenarioMapper.selectDistinctExecResult(projectId);
|
||||
|
@ -715,8 +722,9 @@ public class TestPlanApiScenarioService extends TestPlanResourceService {
|
|||
|
||||
/**
|
||||
* 关联缺陷 (单条用例)
|
||||
*
|
||||
* @param request 请求参数
|
||||
* @param userId 用户ID
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
public void associateBug(TestPlanCaseAssociateBugRequest request, String userId) {
|
||||
super.associateBug(request, userId, CaseType.SCENARIO_CASE.getKey());
|
||||
|
@ -764,6 +772,7 @@ public class TestPlanApiScenarioService extends TestPlanResourceService {
|
|||
|
||||
/**
|
||||
* 处理执行人为空过滤参数
|
||||
*
|
||||
* @param request 请求参数
|
||||
*/
|
||||
protected void filterCaseRequest(TestPlanApiScenarioRequest request) {
|
||||
|
@ -776,4 +785,88 @@ public class TestPlanApiScenarioService extends TestPlanResourceService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void batchAssociateBug(TestPlanScenarioBatchAddBugRequest request, String bugId, String userId) {
|
||||
List<String> ids = doSelectIds(request);
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
handleAssociateBug(ids, userId, bugId, request.getTestPlanId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleAssociateBug(List<String> ids, String userId, String bugId, String testPlanId) {
|
||||
SubListUtils.dealForSubList(ids, 500, (subList) -> {
|
||||
Map<String, String> caseMap = getCaseMap(subList);
|
||||
List<BugRelationCase> list = new ArrayList<>();
|
||||
subList.forEach(id -> {
|
||||
BugRelationCase bugRelationCase = new BugRelationCase();
|
||||
bugRelationCase.setId(IDGenerator.nextStr());
|
||||
bugRelationCase.setBugId(bugId);
|
||||
bugRelationCase.setCaseId(caseMap.get(id));
|
||||
bugRelationCase.setCaseType(CaseType.SCENARIO_CASE.getKey());
|
||||
bugRelationCase.setCreateUser(userId);
|
||||
bugRelationCase.setCreateTime(System.currentTimeMillis());
|
||||
bugRelationCase.setUpdateTime(System.currentTimeMillis());
|
||||
bugRelationCase.setTestPlanCaseId(id);
|
||||
bugRelationCase.setTestPlanId(testPlanId);
|
||||
list.add(bugRelationCase);
|
||||
});
|
||||
bugRelationCaseMapper.batchInsert(list);
|
||||
});
|
||||
}
|
||||
|
||||
public Map<String, String> getCaseMap(List<String> ids) {
|
||||
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
|
||||
example.createCriteria().andIdIn(ids);
|
||||
List<TestPlanApiScenario> caseList = testPlanApiScenarioMapper.selectByExample(example);
|
||||
return caseList.stream().collect(Collectors.toMap(TestPlanApiScenario::getId, TestPlanApiScenario::getApiScenarioId));
|
||||
}
|
||||
|
||||
public void batchAssociateBugByIds(TestPlanScenarioBatchAssociateBugRequest request, String userId) {
|
||||
List<String> ids = doSelectIds(request);
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
handleAssociateBugByIds(ids, request, userId);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleAssociateBugByIds(List<String> ids, TestPlanScenarioBatchAssociateBugRequest request, String userId) {
|
||||
SubListUtils.dealForSubList(ids, 500, (subList) -> {
|
||||
BugRelationCaseExample example = new BugRelationCaseExample();
|
||||
example.createCriteria().andTestPlanCaseIdIn(subList).andTestPlanIdEqualTo(request.getTestPlanId()).andBugIdIn(request.getBugIds());
|
||||
List<BugRelationCase> bugRelationCases = bugRelationCaseMapper.selectByExample(example);
|
||||
Map<String, List<String>> bugMap = bugRelationCases.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
BugRelationCase::getTestPlanCaseId,
|
||||
Collectors.mapping(BugRelationCase::getBugId, Collectors.toList())
|
||||
));
|
||||
Map<String, String> caseMap = getCaseMap(subList);
|
||||
List<BugRelationCase> list = new ArrayList<>();
|
||||
subList.forEach(item -> {
|
||||
buildAssociateBugData(item, bugMap, list, request, caseMap, userId);
|
||||
});
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
bugRelationCaseMapper.batchInsert(list);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void buildAssociateBugData(String id, Map<String, List<String>> bugMap, List<BugRelationCase> list, TestPlanScenarioBatchAssociateBugRequest request, Map<String, String> caseMap, String userId) {
|
||||
List<String> bugIds = new ArrayList<>(request.getBugIds());
|
||||
if (bugMap.containsKey(id)) {
|
||||
bugIds.removeAll(bugMap.get(id));
|
||||
}
|
||||
bugIds.forEach(bugId -> {
|
||||
BugRelationCase bugRelationCase = new BugRelationCase();
|
||||
bugRelationCase.setId(IDGenerator.nextStr());
|
||||
bugRelationCase.setBugId(bugId);
|
||||
bugRelationCase.setCaseId(caseMap.get(id));
|
||||
bugRelationCase.setCaseType(CaseType.SCENARIO_CASE.getKey());
|
||||
bugRelationCase.setCreateUser(userId);
|
||||
bugRelationCase.setCreateTime(System.currentTimeMillis());
|
||||
bugRelationCase.setUpdateTime(System.currentTimeMillis());
|
||||
bugRelationCase.setTestPlanCaseId(id);
|
||||
bugRelationCase.setTestPlanId(request.getTestPlanId());
|
||||
list.add(bugRelationCase);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import io.metersphere.api.mapper.ApiScenarioReportMapper;
|
|||
import io.metersphere.api.service.scenario.ApiScenarioReportService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.bug.dto.response.BugCustomFieldDTO;
|
||||
import io.metersphere.plan.constants.AssociateCaseType;
|
||||
import io.metersphere.plan.domain.TestPlanApiScenario;
|
||||
import io.metersphere.plan.domain.TestPlanApiScenarioExample;
|
||||
|
@ -48,9 +49,11 @@ 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.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
@ -79,6 +82,8 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
|
|||
private static final String BUG_ASSOCIATE_PAGE = "/associate/bug/page";
|
||||
private static final String ASSOCIATE_BUG = "/associate/bug";
|
||||
private static final String DISASSOCIATE_BUG = "/disassociate/bug/{0}";
|
||||
public static final String API_SCENARIO_BATCH_ADD_BUG_URL = "/batch/add-bug";
|
||||
public static final String API_SCENARIO_BATCH_ASSOCIATE_BUG_URL = "/batch/associate-bug";
|
||||
|
||||
@Resource
|
||||
private TestPlanApiScenarioService testPlanApiScenarioService;
|
||||
|
@ -602,4 +607,64 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
|
|||
void testDisassociateBug() throws Exception {
|
||||
this.requestGet(DISASSOCIATE_BUG, "test_plan_case_id");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(15)
|
||||
public void testBatchAddBug() throws Exception {
|
||||
TestPlanCaseBatchAddBugRequest request = buildRequest(false);
|
||||
List<MockMultipartFile> files = new ArrayList<>();
|
||||
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("files", files);
|
||||
this.requestMultipartWithOkAndReturn(API_SCENARIO_BATCH_ADD_BUG_URL, paramMap);
|
||||
|
||||
request.setSelectAll(true);
|
||||
paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("files", files);
|
||||
this.requestMultipartWithOkAndReturn(API_SCENARIO_BATCH_ADD_BUG_URL, paramMap);
|
||||
}
|
||||
|
||||
private TestPlanCaseBatchAddBugRequest buildRequest(boolean isUpdate) {
|
||||
TestPlanCaseBatchAddBugRequest request = new TestPlanCaseBatchAddBugRequest();
|
||||
request.setTestPlanId("wxxx_plan_1");
|
||||
request.setProjectId("wxx_project_1234");
|
||||
request.setTitle("default-bug-title");
|
||||
request.setDescription("default-bug-description");
|
||||
request.setTemplateId("default-bug-template");
|
||||
request.setLinkFileIds(List.of("default-bug-file-id-1"));
|
||||
if (isUpdate) {
|
||||
request.setId("default-bug-id");
|
||||
request.setUnLinkRefIds(List.of("default-file-association-id"));
|
||||
request.setDeleteLocalFileIds(List.of("default-bug-file-id"));
|
||||
request.setLinkFileIds(List.of("default-bug-file-id-2"));
|
||||
}
|
||||
BugCustomFieldDTO fieldDTO1 = new BugCustomFieldDTO();
|
||||
fieldDTO1.setId("custom-field");
|
||||
fieldDTO1.setName("oasis");
|
||||
BugCustomFieldDTO fieldDTO2 = new BugCustomFieldDTO();
|
||||
fieldDTO2.setId("test_field");
|
||||
fieldDTO2.setName(JSON.toJSONString(List.of("test")));
|
||||
BugCustomFieldDTO handleUserField = new BugCustomFieldDTO();
|
||||
handleUserField.setId("handleUser");
|
||||
handleUserField.setName("处理人");
|
||||
handleUserField.setValue("admin");
|
||||
BugCustomFieldDTO statusField = new BugCustomFieldDTO();
|
||||
statusField.setId("status");
|
||||
statusField.setName("状态");
|
||||
statusField.setValue("1");
|
||||
request.setCustomFields(List.of(fieldDTO1, fieldDTO2, handleUserField, statusField));
|
||||
return request;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(19)
|
||||
public void testBatchAssociateBug() throws Exception {
|
||||
TestPlanCaseBatchAssociateBugRequest request = new TestPlanCaseBatchAssociateBugRequest();
|
||||
request.setBugIds(Arrays.asList("123456"));
|
||||
request.setTestPlanId("wxxx_plan_1");
|
||||
this.requestPostWithOk(API_SCENARIO_BATCH_ASSOCIATE_BUG_URL, request);
|
||||
request.setSelectAll(true);
|
||||
this.requestPostWithOk(API_SCENARIO_BATCH_ASSOCIATE_BUG_URL, request);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue