feat(用例管理): 批量编辑接口

This commit is contained in:
WangXu10 2023-11-14 15:55:31 +08:00 committed by Craftsman
parent 66877a5043
commit 25d4618192
15 changed files with 305 additions and 15 deletions

View File

@ -133,6 +133,7 @@ public class FunctionalCaseController {
@Operation(summary = "功能用例-批量删除用例")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.batchDeleteFunctionalCaseLog(#request)", msClass = FunctionalCaseLogService.class)
@SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.DELETE, target = "#targetClass.getBatchDeleteFunctionalCaseDTO(#request)", targetClass = FunctionalCaseNoticeService.class)
public void batchDeleteFunctionalCaseToGc(@Validated @RequestBody FunctionalCaseBatchRequest request) {
String userId = SessionUtils.getUserId();
functionalCaseService.batchDeleteFunctionalCaseToGc(request, userId);
@ -162,4 +163,15 @@ public class FunctionalCaseController {
functionalCaseService.batchCopyFunctionalCase(request, userId);
}
@PostMapping("/batch/edit")
@Operation(summary = "功能用例-批量编辑用例")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.batchEditFunctionalCaseLog(#request)", msClass = FunctionalCaseLogService.class)
@SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getBatchEditFunctionalCaseDTO(#request)", targetClass = FunctionalCaseNoticeService.class)
public void batchEditFunctionalCase(@Validated @RequestBody FunctionalCaseBatchEditRequest request) {
String userId = SessionUtils.getUserId();
functionalCaseService.batchEditFunctionalCase(request, userId);
}
}

View File

@ -12,4 +12,6 @@ public interface ExtFunctionalCaseCustomFieldMapper {
List<FunctionalCaseCustomField> getCustomFieldByCaseIds(@Param("ids") List<String> ids);
void batchUpdate(@Param("functionalCaseCustomField") FunctionalCaseCustomField functionalCaseCustomField, @Param("ids") List<String> ids);
}

View File

@ -9,4 +9,14 @@
</foreach>
</select>
<update id="batchUpdate">
update functional_case_custom_field
set `value` = #{functionalCaseCustomField.value}
where field_id = #{functionalCaseCustomField.fieldId}
and case_id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</update>
</mapper>

View File

@ -29,7 +29,7 @@ public interface ExtFunctionalCaseMapper {
List<FunctionalCasePageDTO> list(@Param("request") FunctionalCasePageRequest request, @Param("deleted") boolean deleted);
void recoverCase(@Param("ids") List<String> ids, @Param("userId") String userId, @Param("time") long time);
void recoverCase(@Param("ids") List<String> ids, @Param("userId") String userId, @Param("time") long time);
List<String> getIds(@Param("request") TableBatchProcessDTO request, @Param("projectId") String projectId, @Param("deleted") boolean deleted);
@ -41,4 +41,9 @@ public interface ExtFunctionalCaseMapper {
List<String> getRefIds(@Param("ids") List<String> ids);
void batchMoveModule(@Param("request") FunctionalCaseBatchMoveRequest request, @Param("ids") List<String> ids, @Param("userId") String userId);
List<FunctionalCase> getTagsByIds(@Param("ids") List<String> ids);
void batchUpdate(@Param("functionalCase") FunctionalCase functionalCase, @Param("ids") List<String> ids);
}

View File

@ -105,8 +105,11 @@
<if test="request.projectId != null">
and functional_case.project_id = #{request.projectId}
</if>
<if test="request.moduleId != null">
and functional_case.module_id = #{request.moduleId}
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and functional_case.module_id in
<foreach collection="request.moduleIds" item="moduleId" separator="," open="(" close=")">
#{moduleId}
</foreach>
</if>
<if test="request.keyword != null">
and (
@ -307,4 +310,36 @@
and deleted = false
and project_id = #{request.projectId}
</update>
<select id="getTagsByIds" resultType="io.metersphere.functional.domain.FunctionalCase">
select id, tags from functional_case
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
and deleted = false
</select>
<update id="batchUpdate">
update functional_case
<set>
<if test="functionalCase.tags != null and functionalCase.tags != ''">
tags = #{functionalCase.tags},
</if>
<if test="functionalCase.updateUser != null and functionalCase.updateUser != ''">
update_user = #{functionalCase.updateUser},
</if>
<if test="functionalCase.updateTime != null">
update_time = #{functionalCase.updateTime},
</if>
</set>
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
and deleted = false
and project_id = #{functionalCase.projectId}
</update>
</mapper>

View File

@ -0,0 +1,33 @@
package io.metersphere.functional.request;
import io.metersphere.functional.dto.CaseCustomFieldDTO;
import io.metersphere.system.dto.table.TableBatchProcessDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.List;
/**
* @author wx
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class FunctionalCaseBatchEditRequest extends TableBatchProcessDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String projectId;
@Schema(description = "是否追加", requiredMode = Schema.RequiredMode.REQUIRED)
private boolean append;
@Schema(description = "标签")
private List<String> tags;
@Schema(description = "自定义字段")
private CaseCustomFieldDTO customField;
}

View File

@ -6,6 +6,8 @@ import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* @author wx
*/
@ -26,5 +28,5 @@ public class FunctionalCasePageRequest extends BasePageRequest {
private String refId;
@Schema(description = "模块id")
private String moduleId;
private List<String> moduleIds;
}

View File

@ -121,4 +121,17 @@ public class FunctionalCaseCustomFieldService {
public void batchSaveCustomField(List<FunctionalCaseCustomField> customFields) {
functionalCaseCustomFieldMapper.batchInsert(customFields);
}
/**
* 批量更新自定义字段
*
* @param customField
* @param ids
*/
public void batchUpdate(CaseCustomFieldDTO customField, List<String> ids) {
FunctionalCaseCustomField functionalCaseCustomField = new FunctionalCaseCustomField();
functionalCaseCustomField.setFieldId(customField.getFieldId());
functionalCaseCustomField.setValue(customField.getValue());
extFunctionalCaseCustomFieldMapper.batchUpdate(functionalCaseCustomField, ids);
}
}

View File

@ -3,10 +3,7 @@ package io.metersphere.functional.service;
import io.metersphere.functional.domain.FunctionalCase;
import io.metersphere.functional.mapper.ExtFunctionalCaseMapper;
import io.metersphere.functional.mapper.FunctionalCaseMapper;
import io.metersphere.functional.request.FunctionalCaseAddRequest;
import io.metersphere.functional.request.FunctionalCaseBatchRequest;
import io.metersphere.functional.request.FunctionalCaseDeleteRequest;
import io.metersphere.functional.request.FunctionalCaseEditRequest;
import io.metersphere.functional.request.*;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.log.constants.OperationLogModule;
@ -164,6 +161,7 @@ public class FunctionalCaseLogService {
/**
* 恢复项目
*
* @param id 接口请求参数
* @return 日志详情
*/
@ -186,6 +184,7 @@ public class FunctionalCaseLogService {
/**
* 彻底删除
*
* @param id 接口请求参数
* @return 日志详情
*/
@ -208,4 +207,29 @@ public class FunctionalCaseLogService {
}
return null;
}
public List<LogDTO> batchEditFunctionalCaseLog(FunctionalCaseBatchEditRequest request) {
List<String> ids = functionalCaseService.doSelectIds(request, request.getProjectId());
List<LogDTO> dtoList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(ids)) {
List<FunctionalCase> functionalCases = extFunctionalCaseMapper.getLogInfo(ids);
functionalCases.forEach(functionalCase -> {
LogDTO dto = new LogDTO(
functionalCase.getProjectId(),
null,
functionalCase.getId(),
null,
OperationLogType.DELETE.name(),
OperationLogModule.FUNCTIONAL_CASE,
functionalCase.getName());
dto.setPath("/functional/case/batch/edit");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(functionalCase));
dtoList.add(dto);
});
}
return dtoList;
}
}

View File

@ -7,12 +7,15 @@ import io.metersphere.functional.dto.CaseCustomFieldDTO;
import io.metersphere.functional.dto.FunctionalCaseDTO;
import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper;
import io.metersphere.functional.mapper.FunctionalCaseMapper;
import io.metersphere.functional.request.FunctionalCaseBatchEditRequest;
import io.metersphere.functional.request.FunctionalCaseBatchRequest;
import io.metersphere.functional.request.FunctionalCaseCommentRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.domain.CustomField;
import io.metersphere.system.domain.CustomFieldExample;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.mapper.CustomFieldMapper;
import io.metersphere.system.mapper.ExtOrganizationCustomFieldMapper;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.utils.SessionUtils;
import jakarta.annotation.Resource;
@ -21,6 +24,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@Service
@ -35,6 +39,15 @@ public class FunctionalCaseNoticeService {
@Resource
private CustomFieldMapper customFieldMapper;
@Resource
private FunctionalCaseService functionalCaseService;
@Resource
private FunctionalCaseCustomFieldService functionalCaseCustomFieldService;
@Resource
private ExtOrganizationCustomFieldMapper extOrganizationCustomFieldMapper;
public FunctionalCaseDTO getFunctionalCaseDTO(FunctionalCaseCommentRequest functionalCaseCommentRequest) {
FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseCommentRequest.getCaseId());
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
@ -66,8 +79,7 @@ public class FunctionalCaseNoticeService {
} else {
functionalCaseDTO.setRelatedUsers(replyUser);
}
}
else {
} else {
if (StringUtils.isNotBlank(replyUser) && StringUtils.isNotBlank(notifier)) {
List<String> notifierList = Arrays.asList(notifier.split(";"));
StringBuilder notifierStr = new StringBuilder();
@ -114,7 +126,7 @@ public class FunctionalCaseNoticeService {
return optionDTOList;
}
public FunctionalCaseDTO getMainFunctionalCaseDTO(String name, String caseEditType,String projectId, List<CaseCustomFieldDTO> customFields) {
public FunctionalCaseDTO getMainFunctionalCaseDTO(String name, String caseEditType, String projectId, List<CaseCustomFieldDTO> customFields) {
String userId = SessionUtils.getUserId();
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
functionalCaseDTO.setName(name);
@ -141,7 +153,7 @@ public class FunctionalCaseNoticeService {
}
public FunctionalCaseDTO getDeleteFunctionalCaseDTO(String id){
public FunctionalCaseDTO getDeleteFunctionalCaseDTO(String id) {
FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(id);
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
Optional.ofNullable(functionalCase).ifPresent(functional -> {
@ -155,4 +167,43 @@ public class FunctionalCaseNoticeService {
return functionalCaseDTO;
}
public List<FunctionalCaseDTO> getBatchDeleteFunctionalCaseDTO(FunctionalCaseBatchRequest request) {
List<String> ids = functionalCaseService.doSelectIds(request, request.getProjectId());
return handleBatchNotice(request.getProjectId(), ids);
}
private List<FunctionalCaseDTO> handleBatchNotice(String projectId, List<String> ids) {
List<FunctionalCaseDTO> dtoList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(ids)) {
Map<String, FunctionalCase> functionalCaseMap = functionalCaseService.copyBaseInfo(projectId, ids);
Map<String, List<FunctionalCaseCustomField>> customFieldMap = functionalCaseCustomFieldService.getCustomFieldMapByCaseIds(ids);
String userId = SessionUtils.getUserId();
AtomicReference<List<OptionDTO>> optionDTOS = new AtomicReference<>(new ArrayList<>());
ids.forEach(id -> {
FunctionalCase functionalCase = functionalCaseMap.get(id);
List<FunctionalCaseCustomField> customFields = customFieldMap.get(id);
if (CollectionUtils.isNotEmpty(customFields)) {
List<String> fields = customFields.stream().map(functionalCaseCustomField -> functionalCaseCustomField.getFieldId()).toList();
optionDTOS.set(extOrganizationCustomFieldMapper.getCustomFieldOptions(fields));
}
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
functionalCaseDTO.setName(functionalCase.getName());
functionalCaseDTO.setProjectId(functionalCase.getProjectId());
functionalCaseDTO.setCaseEditType(functionalCase.getCaseEditType());
functionalCaseDTO.setCreateUser(userId);
functionalCaseDTO.setFields(optionDTOS.get());
dtoList.add(functionalCaseDTO);
});
//TODO:设置测试计划名称
//TODO设置用例评审名称
}
return dtoList;
}
public List<FunctionalCaseDTO> getBatchEditFunctionalCaseDTO(FunctionalCaseBatchEditRequest request) {
List<String> ids = functionalCaseService.doSelectIds(request, request.getProjectId());
return handleBatchNotice(request.getProjectId(), ids);
}
}

View File

@ -507,7 +507,7 @@ public class FunctionalCaseService {
}
private Map<String, FunctionalCaseBlob> copyBlobInfo(List<String> ids) {
public Map<String, FunctionalCaseBlob> copyBlobInfo(List<String> ids) {
FunctionalCaseBlobExample blobExample = new FunctionalCaseBlobExample();
blobExample.createCriteria().andIdIn(ids);
List<FunctionalCaseBlob> functionalCaseBlobs = functionalCaseBlobMapper.selectByExampleWithBLOBs(blobExample);
@ -515,11 +515,79 @@ public class FunctionalCaseService {
return functionalCaseBlobMap;
}
private Map<String, FunctionalCase> copyBaseInfo(String projectId, List<String> ids) {
public Map<String, FunctionalCase> copyBaseInfo(String projectId, List<String> ids) {
FunctionalCaseExample example = new FunctionalCaseExample();
example.createCriteria().andProjectIdEqualTo(projectId).andDeletedEqualTo(false).andIdIn(ids);
List<FunctionalCase> functionalCaseLists = functionalCaseMapper.selectByExample(example);
Map<String, FunctionalCase> functionalMap = functionalCaseLists.stream().collect(Collectors.toMap(FunctionalCase::getId, functionalCase -> functionalCase));
return functionalMap;
}
/**
* 批量编辑
*
* @param request
* @param userId
*/
public void batchEditFunctionalCase(FunctionalCaseBatchEditRequest request, String userId) {
List<String> ids = doSelectIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) {
//标签处理
handleTags(request, userId, ids);
//自定义字段处理
handleCustomFields(request, userId, ids);
}
}
private void handleCustomFields(FunctionalCaseBatchEditRequest request, String userId, List<String> ids) {
Optional.ofNullable(request.getCustomField()).ifPresent(customField -> {
functionalCaseCustomFieldService.batchUpdate(customField, ids);
FunctionalCase functionalCase = new FunctionalCase();
functionalCase.setProjectId(request.getProjectId());
functionalCase.setUpdateTime(System.currentTimeMillis());
functionalCase.setUpdateUser(userId);
extFunctionalCaseMapper.batchUpdate(functionalCase, ids);
});
}
private void handleTags(FunctionalCaseBatchEditRequest request, String userId, List<String> ids) {
if (CollectionUtils.isNotEmpty(request.getTags())) {
if (request.isAppend()) {
//追加标签
List<FunctionalCase> caseList = extFunctionalCaseMapper.getTagsByIds(ids);
Map<String, FunctionalCase> collect = caseList.stream().collect(Collectors.toMap(FunctionalCase::getId, v -> v));
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
FunctionalCaseMapper caseMapper = sqlSession.getMapper(FunctionalCaseMapper.class);
ids.forEach(id -> {
FunctionalCase functionalCase = new FunctionalCase();
if (StringUtils.isNotBlank(collect.get(id).getTags())) {
List<String> tags = JSON.parseArray(collect.get(id).getTags(), String.class);
tags.addAll(request.getTags());
functionalCase.setTags(JSON.toJSONString(tags));
} else {
functionalCase.setTags(JSON.toJSONString(request.getTags()));
}
functionalCase.setId(id);
functionalCase.setUpdateTime(System.currentTimeMillis());
functionalCase.setUpdateUser(userId);
caseMapper.updateByPrimaryKeySelective(functionalCase);
});
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
} else {
//替换标签
FunctionalCase functionalCase = new FunctionalCase();
functionalCase.setTags(JSON.toJSONString(request.getTags()));
functionalCase.setProjectId(request.getProjectId());
functionalCase.setUpdateTime(System.currentTimeMillis());
functionalCase.setUpdateUser(userId);
extFunctionalCaseMapper.batchUpdate(functionalCase, ids);
}
}
}
}

View File

@ -48,6 +48,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
public static final String FUNCTIONAL_CASE_BATCH_MOVE_URL = "/functional/case/batch/move";
public static final String FUNCTIONAL_CASE_BATCH_COPY_URL = "/functional/case/batch/copy";
public static final String FUNCTIONAL_CASE_VERSION_URL = "/functional/case/version/";
public static final String FUNCTIONAL_CASE_BATCH_EDIT_URL = "/functional/case/batch/edit";
@Resource
private NotificationMapper notificationMapper;
@ -352,4 +353,27 @@ public class FunctionalCaseControllerTests extends BaseTest {
Assertions.assertNotNull(resultHolder);
}
@Test
@Order(2)
public void testBatchEdit() throws Exception {
FunctionalCaseBatchEditRequest request = new FunctionalCaseBatchEditRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setAppend(false);
request.setSelectAll(false);
this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_BATCH_EDIT_URL, request);
request.setSelectIds(Arrays.asList("TEST_FUNCTIONAL_CASE_ID_1", "TEST_FUNCTIONAL_CASE_ID_2"));
this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_BATCH_EDIT_URL, request);
request.setAppend(true);
request.setTags(Arrays.asList("追加标签_1", "追加标签_2"));
this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_BATCH_EDIT_URL, request);
request.setAppend(false);
request.setTags(Arrays.asList("覆盖标签1", "覆盖标签2"));
request.setSelectAll(true);
CaseCustomFieldDTO caseCustomFieldDTO = new CaseCustomFieldDTO();
caseCustomFieldDTO.setFieldId("TEST_FIELD_ID");
caseCustomFieldDTO.setValue("批量编辑自定义字段");
request.setCustomField(caseCustomFieldDTO);
this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_BATCH_EDIT_URL, request);
}
}

View File

@ -5,7 +5,7 @@ INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, r
VALUES ('TEST_FUNCTIONAL_CASE_ID', 1, 'TEST_MODULE_ID', '100001100001', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time)
VALUES ('TEST_FUNCTIONAL_CASE_ID_1', 2, 'TEST_MODULE_ID', '100001100001', '100001', '测试多版本', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'TEST_REF_ID', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
VALUES ('TEST_FUNCTIONAL_CASE_ID_1', 2, 'TEST_MODULE_ID', '100001100001', '100001', '测试多版本', 'UN_REVIEWED', '["测试标签_1"]', 'STEP', 0, 'v1.0.0', 'TEST_REF_ID', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time)
VALUES ('TEST_FUNCTIONAL_CASE_ID_2', 3, 'TEST_MODULE_ID', '100001100001', '100001', 'copy_测试多版本', 'UN_REVIEWED', NULL, 'STEP', 0, 'v2.0.0', 'TEST_REF_ID', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);

View File

@ -1,5 +1,6 @@
package io.metersphere.system.mapper;
import io.metersphere.system.dto.sdk.OptionDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -9,4 +10,6 @@ import java.util.List;
*/
public interface ExtOrganizationCustomFieldMapper {
List<String> getCustomFieldByRefId(@Param("refId") String refId);
List<OptionDTO> getCustomFieldOptions(@Param("ids") List<String> ids);
}

View File

@ -4,4 +4,12 @@
<select id="getCustomFieldByRefId" resultType="java.lang.String">
SELECT id FROM custom_field WHERE ref_id = #{refId}
</select>
<select id="getCustomFieldOptions" resultType="io.metersphere.system.dto.sdk.OptionDTO">
SELECT id, name FROM custom_field
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
</mapper>