fix(用例管理): 导入上千条顺序问题
--bug=1039706 --user=王旭 【用例管理】导入功能用例的顺序和excel不一致 https://www.tapd.cn/55049933/s/1501064
This commit is contained in:
parent
12e3eae473
commit
e7a8fd38ab
|
@ -31,6 +31,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.io.Serial;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
@ -84,7 +85,9 @@ public class FunctionalCaseImportEventListener extends AnalysisEventListener<Map
|
|||
protected static final int TAGS_COUNT = 15;
|
||||
protected static final int TAG_LENGTH = 64;
|
||||
|
||||
public FunctionalCaseImportEventListener(FunctionalCaseImportRequest request, Class clazz, List<TemplateCustomFieldDTO> customFields, Set<ExcelMergeInfo> mergeInfoSet, SessionUser user) {
|
||||
private AtomicLong lastPos;
|
||||
|
||||
public FunctionalCaseImportEventListener(FunctionalCaseImportRequest request, Class clazz, List<TemplateCustomFieldDTO> customFields, Set<ExcelMergeInfo> mergeInfoSet, SessionUser user, Long pos) {
|
||||
this.mergeInfoSet = mergeInfoSet;
|
||||
this.request = request;
|
||||
excelDataClass = clazz;
|
||||
|
@ -93,6 +96,7 @@ public class FunctionalCaseImportEventListener extends AnalysisEventListener<Map
|
|||
moduleTree = CommonBeanFactory.getBean(FunctionalCaseModuleService.class).getTree(request.getProjectId());
|
||||
functionalCaseService = CommonBeanFactory.getBean(FunctionalCaseService.class);
|
||||
customFieldValidatorMap = CustomFieldValidatorFactory.getValidatorMap();
|
||||
lastPos = new AtomicLong(pos);
|
||||
this.user = user;
|
||||
|
||||
}
|
||||
|
@ -201,7 +205,7 @@ public class FunctionalCaseImportEventListener extends AnalysisEventListener<Map
|
|||
*/
|
||||
private void saveData() {
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
functionalCaseService.saveImportData(list, request, moduleTree, customFieldsMap, pathMap, user);
|
||||
functionalCaseService.saveImportData(list, request, moduleTree, customFieldsMap, pathMap, user, lastPos);
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(updateList)) {
|
||||
|
|
|
@ -27,4 +27,7 @@ public class FunctionalCaseImportRequest implements Serializable {
|
|||
@Schema(description = "是否覆盖原用例", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{functional_case.cover.not_blank}")
|
||||
private boolean cover;
|
||||
|
||||
@Schema(description = "导入数量")
|
||||
private String count;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import io.metersphere.system.dto.sdk.SessionUser;
|
|||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||
import io.metersphere.system.dto.sdk.TemplateDTO;
|
||||
import io.metersphere.system.excel.utils.EasyExcelExporter;
|
||||
import io.metersphere.system.utils.ServiceUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -47,6 +48,9 @@ public class FunctionalCaseFileService {
|
|||
@Resource
|
||||
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
|
||||
|
||||
@Resource
|
||||
private FunctionalCaseService functionalCaseService;
|
||||
|
||||
|
||||
/**
|
||||
* 下载excel导入模板
|
||||
|
@ -242,6 +246,8 @@ public class FunctionalCaseFileService {
|
|||
if (StringUtils.isEmpty(request.getVersionId())) {
|
||||
request.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(request.getProjectId()));
|
||||
}
|
||||
Long nextPos = functionalCaseService.getNextOrder(request.getProjectId());
|
||||
Long lasePos = nextPos + (ServiceUtils.POS_STEP * Integer.parseInt(request.getCount()));
|
||||
//根据本地语言环境选择用哪种数据对象进行存放读取的数据
|
||||
Class clazz = new FunctionalCaseExcelDataFactory().getExcelDataByLocal();
|
||||
//获取当前项目默认模板的自定义字段
|
||||
|
@ -250,7 +256,7 @@ public class FunctionalCaseFileService {
|
|||
// 预处理,查询合并单元格信息
|
||||
EasyExcel.read(file.getInputStream(), null, new FunctionalCasePretreatmentListener(mergeInfoSet))
|
||||
.extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();
|
||||
FunctionalCaseImportEventListener eventListener = new FunctionalCaseImportEventListener(request, clazz, customFields, mergeInfoSet, user);
|
||||
FunctionalCaseImportEventListener eventListener = new FunctionalCaseImportEventListener(request, clazz, customFields, mergeInfoSet, user, lasePos);
|
||||
EasyExcelFactory.read(file.getInputStream(), eventListener).sheet().doRead();
|
||||
response.setErrorMessages(eventListener.getErrList());
|
||||
response.setSuccessCount(eventListener.getSuccessCount());
|
||||
|
|
|
@ -46,8 +46,6 @@ import io.metersphere.system.log.service.OperationLogService;
|
|||
import io.metersphere.system.mapper.OperationHistoryMapper;
|
||||
import io.metersphere.system.mapper.UserMapper;
|
||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||
import io.metersphere.system.resolver.field.AbstractCustomFieldResolver;
|
||||
import io.metersphere.system.resolver.field.CustomFieldResolverFactory;
|
||||
import io.metersphere.system.service.*;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import io.metersphere.system.uid.NumGenerator;
|
||||
|
@ -66,6 +64,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -1023,7 +1022,7 @@ public class FunctionalCaseService {
|
|||
* @param pathMap 模块路径
|
||||
* @param user user
|
||||
*/
|
||||
public void saveImportData(List<FunctionalCaseExcelData> list, FunctionalCaseImportRequest request, List<BaseTreeNode> moduleTree, Map<String, TemplateCustomFieldDTO> customFieldsMap, Map<String, String> pathMap, SessionUser user) {
|
||||
public void saveImportData(List<FunctionalCaseExcelData> list, FunctionalCaseImportRequest request, List<BaseTreeNode> moduleTree, Map<String, TemplateCustomFieldDTO> customFieldsMap, Map<String, String> pathMap, SessionUser user, AtomicLong lastPos) {
|
||||
//默认模板
|
||||
TemplateDTO defaultTemplateDTO = projectTemplateService.getDefaultTemplateDTO(request.getProjectId(), TemplateScene.FUNCTIONAL.name());
|
||||
//模块路径
|
||||
|
@ -1035,18 +1034,18 @@ public class FunctionalCaseService {
|
|||
FunctionalCaseMapper caseMapper = sqlSession.getMapper(FunctionalCaseMapper.class);
|
||||
FunctionalCaseBlobMapper caseBlobMapper = sqlSession.getMapper(FunctionalCaseBlobMapper.class);
|
||||
FunctionalCaseCustomFieldMapper customFieldMapper = sqlSession.getMapper(FunctionalCaseCustomFieldMapper.class);
|
||||
Long nextOrder = getNextOrder(request.getProjectId());
|
||||
List<FunctionalCaseDTO> noticeList = new ArrayList<>();
|
||||
List<FunctionalCaseHistoryLogDTO> historyLogDTOS = new ArrayList<>();
|
||||
List<LogDTO> logDTOS = new ArrayList<>();
|
||||
List<String> caseIds = new ArrayList<>();
|
||||
for (int i = list.size() - 1; i > -1; i--) {
|
||||
parseInsertDataToModule(list.get(i), request, user.getId(), caseModulePathMap, defaultTemplateDTO, nextOrder, caseMapper, caseBlobMapper, customFieldMapper, customFieldsMap, caseIds, historyLogDTOS);
|
||||
nextOrder += ServiceUtils.POS_STEP;
|
||||
Long pos = lastPos.get();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
parseInsertDataToModule(list.get(i), request, user.getId(), caseModulePathMap, defaultTemplateDTO, pos, caseMapper, caseBlobMapper, customFieldMapper, customFieldsMap, caseIds, historyLogDTOS);
|
||||
pos -= ServiceUtils.POS_STEP;
|
||||
//通知
|
||||
noticeModule(noticeList, list.get(i), request, user.getId(), customFieldsMap);
|
||||
}
|
||||
|
||||
lastPos.set(pos);
|
||||
sqlSession.flushStatements();
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
|
||||
|
|
|
@ -675,6 +675,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
|||
FunctionalCaseImportRequest request = new FunctionalCaseImportRequest();
|
||||
request.setCover(true);
|
||||
request.setProjectId("100001100001");
|
||||
request.setCount("1");
|
||||
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("file", file);
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<MsIcon
|
||||
:type="lastExecuteResultMap[props.executeResult]?.icon || ''"
|
||||
class="mr-1"
|
||||
:class="[lastExecuteResultMap[props.executeResult].color]"
|
||||
:style="{ color: lastExecuteResultMap[props.executeResult].color }"
|
||||
></MsIcon>
|
||||
<span>{{ status?.text || '' }}</span>
|
||||
<span class="text-[var(--color-text-2)]">{{ status?.text || '' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
|||
label: 'UN_EXECUTED',
|
||||
icon: StatusType.UN_EXECUTED,
|
||||
statusText: 'caseManagement.featureCase.nonExecution',
|
||||
color: 'text-[rgb(var(--warning-6))]',
|
||||
color: 'var(--color-text-brand)',
|
||||
},
|
||||
PASSED: {
|
||||
label: 'PASSED',
|
||||
|
@ -39,13 +39,13 @@
|
|||
label: 'SKIPPED',
|
||||
icon: StatusType.SKIPPED,
|
||||
statusText: 'caseManagement.featureCase.skip',
|
||||
color: 'text-[rgb(var(--link-6))]',
|
||||
color: 'rgb(var(--link-6))',
|
||||
},
|
||||
BLOCKED: {
|
||||
label: 'BLOCKED',
|
||||
icon: StatusType.BLOCKED,
|
||||
statusText: 'caseManagement.featureCase.chokeUp',
|
||||
color: 'text-[rgb(var(--warning-6))]',
|
||||
color: 'rgb(var(--warning-6))',
|
||||
},
|
||||
FAILED: {
|
||||
label: 'FAILED',
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
>
|
||||
<a-button
|
||||
type="text"
|
||||
class="arco-btn-text--secondary p-[8px_4px]"
|
||||
class="arco-btn-text--secondary p-[8px_4px] text-[14px] leading-[22px]"
|
||||
size="mini"
|
||||
@click="executeResultFilterVisible = true"
|
||||
>
|
||||
|
@ -167,7 +167,11 @@
|
|||
</template>
|
||||
<template #reviewStatusFilter="{ columnConfig }">
|
||||
<a-trigger v-model:popup-visible="statusFilterVisible" trigger="click" @popup-visible-change="handleFilterHidden">
|
||||
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click="statusFilterVisible = true">
|
||||
<a-button
|
||||
type="text"
|
||||
class="arco-btn-text--secondary p-[8px_4px] text-[14px] leading-[22px]"
|
||||
@click="statusFilterVisible = true"
|
||||
>
|
||||
<div class="font-medium">
|
||||
{{ t(columnConfig.title as string) }}
|
||||
</div>
|
||||
|
@ -209,7 +213,7 @@
|
|||
@change="() => handleStatusChange(record)"
|
||||
>
|
||||
<template #label>
|
||||
<span class="text-[var(--color-text-2)]"> <executeResult :execute-result="record.lastExecuteResult" /></span>
|
||||
<executeResult :execute-result="record.lastExecuteResult" />
|
||||
</template>
|
||||
<a-option v-for="item of LastExecuteResults" :key="item" :value="item">
|
||||
<executeResult :execute-result="item" />
|
||||
|
|
|
@ -388,6 +388,7 @@
|
|||
projectId: appStore.currentProjectId,
|
||||
versionId: '',
|
||||
cover: isCover.value,
|
||||
count: validateInfo.value.successCount,
|
||||
};
|
||||
await importExcelCase({ request: params, fileList: fileList.value.map((item: any) => item.file) });
|
||||
Message.success(t('caseManagement.featureCase.importSuccess'));
|
||||
|
|
Loading…
Reference in New Issue