feat(测试计划): 测试计划详情场景用例批量关联缺陷&批量新增缺陷

This commit is contained in:
WangXu10 2024-09-02 14:21:43 +08:00 committed by Craftsman
parent 41d723bee9
commit 1ea2d56b07
5 changed files with 267 additions and 1 deletions

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
});
}
}

View File

@ -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);
}
}