feat(功能用例): 功能用例excel导入
This commit is contained in:
parent
ab87dcca8a
commit
99d294694c
|
@ -520,7 +520,7 @@ swagger_parse_error=Swagger parsing failed or file format is incorrect!
|
|||
permission.test_plan.name=Test plan
|
||||
permission.test_plan_module.name=Test plan module
|
||||
|
||||
excel.template.id=Not mandatory, add a new use case when the ID is empty
|
||||
excel.template.id=Non mandatory, add a new use case when the ID is empty or does not exist;
|
||||
excel.template.case_edit_type=Not mandatory, fill in STEP for step description, fill in Text for text description, default to Text if not filled in
|
||||
excel.template.tag=Not mandatory labels should be separated by semicolons or commas
|
||||
excel.template.text_description=Not mandatory, when the editing mode is STEP, the step description will be based on the identifier [1] [2] [3] To determine whether to split a cell into multiple steps, if not, it is a single step
|
||||
|
@ -536,7 +536,7 @@ custom_field_member_tip=[%s] must be current project member
|
|||
custom_field_select_tip=[%s] must be %s
|
||||
custom_field_int_tip=[%s] must be integer
|
||||
custom_field_float_tip=[%s] must be number
|
||||
|
||||
check_import_excel_error=Check import excel error
|
||||
#关联
|
||||
relate_source_id_not_blank=Source id cannot be empty
|
||||
relate_source_id_length_range=The association source ID must be between {min} and {max}
|
||||
|
|
|
@ -516,7 +516,7 @@ swagger_parse_error=Swagger 解析失败,请确认文件格式是否正确!
|
|||
permission.test_plan.name=测试计划
|
||||
permission.test_plan_module.name=测试计划模块
|
||||
|
||||
excel.template.id=非必填,ID为空时新增用例
|
||||
excel.template.id=非必填,ID为空或不存在时新增用例;
|
||||
excel.template.case_edit_type=非必填,步骤描述填写STEP,文本描述填写TEXT,未填写默认为TEXT
|
||||
excel.template.tag=非必填,标签之间以分号或者逗号隔开
|
||||
excel.template.text_description=非必填,编辑模式为STEP时,步骤描述会根据标识[1] [2] [3]...来判断是否将单元格拆分为多个步骤,没有则为一个步骤
|
||||
|
@ -532,6 +532,7 @@ custom_field_member_tip=[%s]必须当前项目成员
|
|||
custom_field_select_tip=[%s]必须为%s
|
||||
custom_field_int_tip=[%s]必须为整型
|
||||
custom_field_float_tip=[%s]必须为数字
|
||||
check_import_excel_error=检查导入Excel错误
|
||||
#关联
|
||||
relate_source_id_not_blank=关联来源ID不能为空
|
||||
relate_source_id_length_range=关联来源ID必须在{min}和{max}之间
|
||||
|
|
|
@ -516,7 +516,7 @@ swagger_parse_error=Swagger 解析失敗
|
|||
permission.test_plan.name=測試計劃
|
||||
permission.test_plan_module.name=測試計劃模塊
|
||||
|
||||
excel.template.id=非必填,ID為空時新增用例
|
||||
excel.template.id=非必填,ID為空時或不存在時新增用例
|
||||
excel.template.case_edit_type=非必填,步驟描述填寫STEP,文本描述填寫TEXT,為填寫默認為TEXT
|
||||
excel.template.tag=非必填,標簽之間以分號或者逗號隔開
|
||||
excel.template.text_description=非必填,編輯模式為STEP時,步驟描述會根據標識[1] [2] [3]...來判斷是否將單元格拆分為多個步驟,沒有則為一個步驟
|
||||
|
@ -532,7 +532,7 @@ custom_field_member_tip=[%s]必須當前項目成員
|
|||
custom_field_select_tip=[%s]必須為%s
|
||||
custom_field_int_tip=[%s]必須為整型
|
||||
custom_field_float_tip=[%s]必須為數字
|
||||
|
||||
check_import_excel_error=檢查導入Excel錯誤
|
||||
#关联
|
||||
relate_source_id_not_blank=關聯來源ID不能為空
|
||||
relate_source_id_length_range=關聯來源ID必須在{min}和{max}之間
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.functional.constants;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
public class FunctionalCaseTypeConstants {
|
||||
|
||||
public enum CaseEditType {
|
||||
TEXT, STEP
|
||||
}
|
||||
}
|
|
@ -226,4 +226,13 @@ public class FunctionalCaseController {
|
|||
public FunctionalCaseImportResponse preCheckExcel(@RequestPart("request") FunctionalCaseImportRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
||||
return functionalCaseFileService.preCheckExcel(request, file);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/import/excel")
|
||||
@Operation(summary = "用例管理-功能用例-excel导入")
|
||||
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE)
|
||||
public FunctionalCaseImportResponse importExcel(@RequestPart("request") FunctionalCaseImportRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
||||
String userId = SessionUtils.getUserId();
|
||||
return functionalCaseFileService.importExcel(request, userId, file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,13 +33,13 @@ public class FunctionalCaseExcelData {
|
|||
private String expectedResult;
|
||||
@ExcelIgnore
|
||||
private String caseEditType;
|
||||
|
||||
@ExcelIgnore
|
||||
private String steps;
|
||||
@ExcelIgnore
|
||||
Map<String, Object> customData = new LinkedHashMap<>();
|
||||
@ExcelIgnore
|
||||
Map<String, String> otherFields;
|
||||
@ExcelIgnore
|
||||
Set<String> textFieldSet = new HashSet<>(1);
|
||||
|
||||
/**
|
||||
* 合并文本描述
|
||||
*/
|
||||
|
|
|
@ -11,7 +11,7 @@ import io.metersphere.functional.excel.exception.CustomFieldValidateException;
|
|||
import io.metersphere.functional.excel.validate.AbstractCustomFieldValidator;
|
||||
import io.metersphere.functional.excel.validate.CustomFieldValidatorFactory;
|
||||
import io.metersphere.functional.request.FunctionalCaseImportRequest;
|
||||
import io.metersphere.plugin.sdk.util.MSPluginException;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.dto.excel.ExcelValidateHelper;
|
||||
|
@ -23,7 +23,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.io.Serial;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
@ -57,7 +56,6 @@ public class FunctionalCaseCheckEventListener extends AnalysisEventListener<Map<
|
|||
protected List<ExcelErrData<FunctionalCaseExcelData>> errList = new ArrayList<>();
|
||||
private static final String ERROR_MSG_SEPARATOR = ";";
|
||||
private HashMap<String, AbstractCustomFieldValidator> customFieldValidatorMap;
|
||||
private static AtomicInteger successCount = new AtomicInteger(0);
|
||||
|
||||
|
||||
public FunctionalCaseCheckEventListener(FunctionalCaseImportRequest request, Class clazz, List<TemplateCustomFieldDTO> customFields, Set<ExcelMergeInfo> mergeInfoSet) {
|
||||
|
@ -85,7 +83,7 @@ public class FunctionalCaseCheckEventListener extends AnalysisEventListener<Map<
|
|||
@Override
|
||||
public void invoke(Map<Integer, String> data, AnalysisContext analysisContext) {
|
||||
if (headMap == null) {
|
||||
throw new MSPluginException("case_import_table_header_missing");
|
||||
throw new MSException(Translator.get("case_import_table_header_missing"));
|
||||
}
|
||||
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
|
||||
//处理合并单元格
|
||||
|
@ -192,7 +190,7 @@ public class FunctionalCaseCheckEventListener extends AnalysisEventListener<Map<
|
|||
errList.add(excelErrData);
|
||||
} else {
|
||||
//通过数量
|
||||
successCount.incrementAndGet();
|
||||
list.add(functionalCaseExcelData);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,7 +219,11 @@ public class FunctionalCaseCheckEventListener extends AnalysisEventListener<Map<
|
|||
//当前读取的数据有ID
|
||||
if (StringUtils.isNotEmpty(data.getNum())) {
|
||||
Integer num = -1;
|
||||
num = Integer.parseInt(data.getNum());
|
||||
try {
|
||||
num = Integer.parseInt(data.getNum());
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
if (num < 0) {
|
||||
errMsg.append(Translator.get("id_not_rightful"))
|
||||
.append("[")
|
||||
|
@ -350,7 +352,7 @@ public class FunctionalCaseCheckEventListener extends AnalysisEventListener<Map<
|
|||
isMergeRow = false;
|
||||
isMergeLastRow = false;
|
||||
if (getNameColIndex() == null) {
|
||||
throw new MSPluginException("缺少名称表头");
|
||||
throw new MSException(Translator.get("case_import_table_header_missing"));
|
||||
}
|
||||
data.keySet().forEach(col -> {
|
||||
Iterator<ExcelMergeInfo> iterator = mergeInfoSet.iterator();
|
||||
|
@ -451,7 +453,7 @@ public class FunctionalCaseCheckEventListener extends AnalysisEventListener<Map<
|
|||
return errList;
|
||||
}
|
||||
|
||||
public Integer getSuccessCount() {
|
||||
return successCount.get();
|
||||
public List<FunctionalCaseExcelData> getList() {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,657 @@
|
|||
package io.metersphere.functional.excel.listener;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.event.AnalysisEventListener;
|
||||
import io.metersphere.functional.constants.FunctionalCaseTypeConstants;
|
||||
import io.metersphere.functional.excel.annotation.NotRequired;
|
||||
import io.metersphere.functional.excel.domain.ExcelMergeInfo;
|
||||
import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
|
||||
import io.metersphere.functional.excel.domain.FunctionalCaseExcelDataFactory;
|
||||
import io.metersphere.functional.excel.exception.CustomFieldValidateException;
|
||||
import io.metersphere.functional.excel.validate.AbstractCustomFieldValidator;
|
||||
import io.metersphere.functional.excel.validate.CustomFieldValidatorFactory;
|
||||
import io.metersphere.functional.request.FunctionalCaseImportRequest;
|
||||
import io.metersphere.functional.service.FunctionalCaseModuleService;
|
||||
import io.metersphere.functional.service.FunctionalCaseService;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.dto.excel.ExcelValidateHelper;
|
||||
import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||
import io.metersphere.system.excel.domain.ExcelErrData;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
public class FunctionalCaseImportEventListener extends AnalysisEventListener<Map<Integer, String>> {
|
||||
|
||||
private Class excelDataClass;
|
||||
private FunctionalCaseImportRequest request;
|
||||
private Map<Integer, String> headMap;
|
||||
Map<String, TemplateCustomFieldDTO> customFieldsMap = new HashMap<>();
|
||||
private Set<ExcelMergeInfo> mergeInfoSet;
|
||||
/**
|
||||
* 所有的模块集合
|
||||
*/
|
||||
private List<BaseTreeNode> moduleTree;
|
||||
private Map<String, String> excelHeadToFieldNameDic = new HashMap<>();
|
||||
/**
|
||||
* 标记下当前遍历的行是不是有合并单元格
|
||||
*/
|
||||
private Boolean isMergeRow;
|
||||
/**
|
||||
* 标记下当前遍历的行是不是合并单元格的最后一行
|
||||
*/
|
||||
private Boolean isMergeLastRow;
|
||||
/**
|
||||
* 存储合并单元格对应的数据,key 为重写了 compareTo 的 ExcelMergeInfo
|
||||
*/
|
||||
private HashMap<ExcelMergeInfo, String> mergeCellDataMap = new HashMap<>();
|
||||
/**
|
||||
* 存储当前合并的一条完整数据,其中步骤没有合并是多行
|
||||
*/
|
||||
private FunctionalCaseExcelData currentMergeData;
|
||||
private Integer firstMergeRowIndex;
|
||||
/**
|
||||
* 每隔5000条存储数据库,然后清理list ,方便内存回收
|
||||
*/
|
||||
protected static final int BATCH_COUNT = 5000;
|
||||
protected List<FunctionalCaseExcelData> list = new ArrayList<>();
|
||||
protected List<ExcelErrData<FunctionalCaseExcelData>> errList = new ArrayList<>();
|
||||
/**
|
||||
* 待更新用例的集合
|
||||
*/
|
||||
protected List<FunctionalCaseExcelData> updateList = new ArrayList<>();
|
||||
private static final String ERROR_MSG_SEPARATOR = ";";
|
||||
private HashMap<String, AbstractCustomFieldValidator> customFieldValidatorMap;
|
||||
private FunctionalCaseService functionalCaseService;
|
||||
private String userId;
|
||||
private int successCount = 0;
|
||||
private Map<String, String> pathMap = new HashMap<>();
|
||||
|
||||
|
||||
public FunctionalCaseImportEventListener(FunctionalCaseImportRequest request, Class clazz, List<TemplateCustomFieldDTO> customFields, Set<ExcelMergeInfo> mergeInfoSet, String userId) {
|
||||
this.mergeInfoSet = mergeInfoSet;
|
||||
this.request = request;
|
||||
excelDataClass = clazz;
|
||||
//当前项目模板的自定义字段
|
||||
customFieldsMap = customFields.stream().collect(Collectors.toMap(TemplateCustomFieldDTO::getFieldName, i -> i));
|
||||
moduleTree = CommonBeanFactory.getBean(FunctionalCaseModuleService.class).getTree(request.getProjectId());
|
||||
functionalCaseService = CommonBeanFactory.getBean(FunctionalCaseService.class);
|
||||
customFieldValidatorMap = CustomFieldValidatorFactory.getValidatorMap();
|
||||
this.userId = userId;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
|
||||
this.headMap = headMap;
|
||||
try {
|
||||
genExcelHeadToFieldNameDicAndGetNotRequiredFields();
|
||||
} catch (NoSuchFieldException e) {
|
||||
LogUtils.error(e);
|
||||
}
|
||||
formatHeadMap();
|
||||
super.invokeHeadMap(headMap, context);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void invoke(Map<Integer, String> data, AnalysisContext analysisContext) {
|
||||
if (headMap == null) {
|
||||
throw new MSException(Translator.get("case_import_table_header_missing"));
|
||||
}
|
||||
|
||||
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
|
||||
//处理合并单元格
|
||||
handleMergeData(data, rowIndex);
|
||||
|
||||
FunctionalCaseExcelData functionalCaseExcelData;
|
||||
// 读取名称列,如果该列是合并单元格,则读取多行数据后合并步骤
|
||||
if (isMergeRow) {
|
||||
if (currentMergeData == null) {
|
||||
firstMergeRowIndex = rowIndex;
|
||||
// 如果是合并单元格的首行
|
||||
functionalCaseExcelData = parseDataToModel(data);
|
||||
functionalCaseExcelData.setMergeTextDescription(new ArrayList<>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -2563948462432733672L;
|
||||
|
||||
{
|
||||
add(functionalCaseExcelData.getTextDescription());
|
||||
}
|
||||
});
|
||||
functionalCaseExcelData.setMergeExpectedResult(new ArrayList<>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 8985001651375529701L;
|
||||
|
||||
{
|
||||
add(functionalCaseExcelData.getExpectedResult());
|
||||
}
|
||||
});
|
||||
// 记录下数据并返回
|
||||
currentMergeData = functionalCaseExcelData;
|
||||
if (!isMergeLastRow) {
|
||||
return;
|
||||
} else {
|
||||
currentMergeData = null;
|
||||
}
|
||||
} else {
|
||||
// 获取存储的数据,并添加多个步骤
|
||||
currentMergeData.getMergeTextDescription()
|
||||
.add(data.get(getTextDescriptionColIndex()));
|
||||
currentMergeData.getMergeExpectedResult()
|
||||
.add(data.get(getExpectedResultColIndex()));
|
||||
// 是最后一行的合并单元格,保存并清空 currentMergeData,走之后的逻辑
|
||||
if (isMergeLastRow) {
|
||||
functionalCaseExcelData = currentMergeData;
|
||||
currentMergeData = null;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
firstMergeRowIndex = null;
|
||||
functionalCaseExcelData = parseDataToModel(data);
|
||||
}
|
||||
|
||||
//校验数据
|
||||
buildUpdateOrErrorList(rowIndex, functionalCaseExcelData);
|
||||
|
||||
if (list.size() > BATCH_COUNT || updateList.size() > BATCH_COUNT) {
|
||||
saveData();
|
||||
this.successCount += list.size() + updateList.size();
|
||||
list.clear();
|
||||
updateList.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
||||
// 如果文件最后一行是没有内容的步骤,这里处理最后一条合并单元格的数据
|
||||
if (currentMergeData != null) {
|
||||
buildUpdateOrErrorList(firstMergeRowIndex, currentMergeData);
|
||||
}
|
||||
saveData();
|
||||
this.successCount += list.size() + updateList.size();
|
||||
list.clear();
|
||||
updateList.clear();
|
||||
customFieldsMap.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 执行保存数据
|
||||
*/
|
||||
private void saveData() {
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
functionalCaseService.saveImportData(list, request, moduleTree, userId, customFieldsMap, pathMap);
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(updateList)) {
|
||||
functionalCaseService.updateImportData(updateList, request, moduleTree, userId, customFieldsMap, pathMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建数据
|
||||
*
|
||||
* @param rowIndex
|
||||
* @param functionalCaseExcelData
|
||||
*/
|
||||
private void buildUpdateOrErrorList(Integer rowIndex, FunctionalCaseExcelData functionalCaseExcelData) {
|
||||
StringBuilder errMsg;
|
||||
try {
|
||||
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
|
||||
errMsg = new StringBuilder(ExcelValidateHelper.validateEntity(functionalCaseExcelData));
|
||||
//自定义校验规则
|
||||
if (StringUtils.isEmpty(errMsg)) {
|
||||
validate(functionalCaseExcelData, errMsg);
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
errMsg = new StringBuilder(Translator.get("parse_data_error"));
|
||||
LogUtils.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(errMsg)) {
|
||||
//不存在错误信息,说明可以新增或更新
|
||||
handleImportDate(functionalCaseExcelData);
|
||||
|
||||
} else {
|
||||
Integer errorRowIndex = rowIndex;
|
||||
if (firstMergeRowIndex != null) {
|
||||
errorRowIndex = firstMergeRowIndex;
|
||||
}
|
||||
ExcelErrData excelErrData = new ExcelErrData(rowIndex,
|
||||
Translator.get("number")
|
||||
.concat(StringUtils.SPACE)
|
||||
.concat(String.valueOf(errorRowIndex + 1)).concat(StringUtils.SPACE)
|
||||
.concat(Translator.get("row"))
|
||||
.concat(Translator.get("error"))
|
||||
.concat(":")
|
||||
.concat(errMsg.toString()));
|
||||
//错误信息
|
||||
errList.add(excelErrData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理可以导入的数据
|
||||
*
|
||||
* @param functionalCaseExcelData
|
||||
*/
|
||||
private void handleImportDate(FunctionalCaseExcelData functionalCaseExcelData) {
|
||||
//处理id判断是新增还是更新
|
||||
handleId(functionalCaseExcelData);
|
||||
//处理单元格
|
||||
handleSteps(functionalCaseExcelData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理步骤描述和预期结果
|
||||
*
|
||||
* @param functionalCaseExcelData
|
||||
*/
|
||||
private void handleSteps(FunctionalCaseExcelData functionalCaseExcelData) {
|
||||
|
||||
if (StringUtils.isNotBlank(functionalCaseExcelData.getCaseEditType()) && StringUtils.equalsIgnoreCase(functionalCaseExcelData.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name())) {
|
||||
functionalCaseExcelData.setTextDescription(functionalCaseExcelData.getTextDescription());
|
||||
functionalCaseExcelData.setExpectedResult(functionalCaseExcelData.getExpectedResult());
|
||||
} else {
|
||||
String steps = getSteps(functionalCaseExcelData);
|
||||
functionalCaseExcelData.setSteps(steps);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getSteps(FunctionalCaseExcelData data) {
|
||||
List<Map<String, Object>> steps = new ArrayList<>();
|
||||
|
||||
if (CollectionUtils.isNotEmpty(data.getMergeTextDescription()) || CollectionUtils.isNotEmpty(data.getMergeExpectedResult())) {
|
||||
// 如果是合并单元格,则组合多条单元格的数据
|
||||
for (int i = 0; i < data.getMergeTextDescription().size(); i++) {
|
||||
List<Map<String, Object>> rowSteps = getSingleRowSteps(data.getMergeTextDescription().get(i), data.getMergeExpectedResult().get(i), steps.size());
|
||||
steps.addAll(rowSteps);
|
||||
}
|
||||
} else {
|
||||
// 如果不是合并单元格,则直接解析单元格数据
|
||||
steps.addAll(getSingleRowSteps(data.getTextDescription(), data.getExpectedResult(), steps.size()));
|
||||
}
|
||||
return JSON.toJSONString(steps);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析步骤描述。预期结果
|
||||
*
|
||||
* @param cellDesc 步骤描述
|
||||
* @param cellResult 预期结果
|
||||
* @param startStepIndex 步骤序号
|
||||
* @return
|
||||
*/
|
||||
private List<Map<String, Object>> getSingleRowSteps(String cellDesc, String cellResult, Integer startStepIndex) {
|
||||
List<Map<String, Object>> steps = new ArrayList<>();
|
||||
|
||||
List<String> stepDescList = parseStepCell(cellDesc);
|
||||
List<String> stepResList = parseStepCell(cellResult);
|
||||
|
||||
int index = Math.max(stepDescList.size(), stepResList.size());
|
||||
for (int i = 0; i < index; i++) {
|
||||
// 保持插入顺序,判断用例是否有相同的steps
|
||||
Map<String, Object> step = new LinkedHashMap<>();
|
||||
step.put("num", startStepIndex + i + 1);
|
||||
if (i < stepDescList.size()) {
|
||||
step.put("desc", stepDescList.get(i));
|
||||
} else {
|
||||
step.put("desc", StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
if (i < stepResList.size()) {
|
||||
step.put("result", stepResList.get(i));
|
||||
} else {
|
||||
step.put("result", StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
steps.add(step);
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析步骤类型的单元格内容
|
||||
*
|
||||
* @param cellContent 单元格内容
|
||||
* @return 解析后的字符文本
|
||||
*/
|
||||
private List<String> parseStepCell(String cellContent) {
|
||||
List<String> cellStepContentList = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(cellContent)) {
|
||||
// 根据[1], [2]...分割步骤描述, 开头空字符去掉, 末尾保留
|
||||
String[] cellContentArr = cellContent.split("\\[\\d+]", -1);
|
||||
if (StringUtils.isEmpty(cellContentArr[0])) {
|
||||
cellContentArr = Arrays.copyOfRange(cellContentArr, 1, cellContentArr.length);
|
||||
}
|
||||
for (String stepContent : cellContentArr) {
|
||||
cellStepContentList.add(stepContent.replaceAll("(?m)^\\s*|\\s*$", StringUtils.EMPTY));
|
||||
}
|
||||
} else {
|
||||
cellStepContentList.add(StringUtils.EMPTY);
|
||||
}
|
||||
return cellStepContentList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理新增数据集合还是更新数据集合
|
||||
*
|
||||
* @param functionalCaseExcelData
|
||||
*/
|
||||
private void handleId(FunctionalCaseExcelData functionalCaseExcelData) {
|
||||
if (StringUtils.isNotEmpty(functionalCaseExcelData.getNum())) {
|
||||
String checkResult = functionalCaseService.checkNumExist(functionalCaseExcelData.getNum(), request.getProjectId());
|
||||
if (StringUtils.isNotEmpty(checkResult)) {
|
||||
if (request.isCover()) {
|
||||
//如果是覆盖,那么有id的需要更新
|
||||
functionalCaseExcelData.setNum(checkResult);
|
||||
updateList.add(functionalCaseExcelData);
|
||||
}
|
||||
} else {
|
||||
list.add(functionalCaseExcelData);
|
||||
}
|
||||
} else {
|
||||
list.add(functionalCaseExcelData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验excel中的数据
|
||||
*
|
||||
* @param data
|
||||
* @param errMsg
|
||||
*/
|
||||
public void validate(FunctionalCaseExcelData data, StringBuilder errMsg) {
|
||||
//模块校验
|
||||
validateModule(data, errMsg);
|
||||
//校验自定义字段
|
||||
validateCustomField(data, errMsg);
|
||||
//校验id
|
||||
validateIdExist(data, errMsg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验Excel中是否有ID
|
||||
* 是否覆盖:
|
||||
* 1.覆盖:id存在则更新,id不存在则新增
|
||||
* 2.不覆盖:id存在不处理,id不存在新增
|
||||
*
|
||||
* @param data
|
||||
* @param errMsg
|
||||
*/
|
||||
@Nullable
|
||||
private void validateIdExist(FunctionalCaseExcelData data, StringBuilder errMsg) {
|
||||
//当前读取的数据有ID
|
||||
if (StringUtils.isNotEmpty(data.getNum())) {
|
||||
Integer num = -1;
|
||||
try {
|
||||
num = Integer.parseInt(data.getNum());
|
||||
} catch (Exception e) {
|
||||
data.setNum(null);
|
||||
return;
|
||||
}
|
||||
if (num < 0) {
|
||||
errMsg.append(Translator.get("id_not_rightful"))
|
||||
.append("[")
|
||||
.append(data.getNum())
|
||||
.append("]; ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验自定义字段,并记录错误提示
|
||||
* 如果填写的是自定义字段的选项值,则转换成ID保存
|
||||
*
|
||||
* @param data
|
||||
* @param errMsg
|
||||
*/
|
||||
private void validateCustomField(FunctionalCaseExcelData data, StringBuilder errMsg) {
|
||||
Map<String, Object> customData = data.getCustomData();
|
||||
for (String fieldName : customData.keySet()) {
|
||||
Object value = customData.get(fieldName);
|
||||
String originFieldName = fieldName;
|
||||
TemplateCustomFieldDTO templateCustomFieldDTO = customFieldsMap.get(fieldName);
|
||||
if (templateCustomFieldDTO == null) {
|
||||
continue;
|
||||
}
|
||||
AbstractCustomFieldValidator customFieldValidator = customFieldValidatorMap.get(templateCustomFieldDTO.getType());
|
||||
try {
|
||||
customFieldValidator.validate(templateCustomFieldDTO, value.toString());
|
||||
if (customFieldValidator.isKVOption) {
|
||||
// 这里如果填的是选项值,替换成选项ID,保存
|
||||
customData.put(originFieldName, customFieldValidator.parse2Key(value.toString(), templateCustomFieldDTO));
|
||||
}
|
||||
} catch (CustomFieldValidateException e) {
|
||||
errMsg.append(e.getMessage().concat(ERROR_MSG_SEPARATOR));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验模块
|
||||
*
|
||||
* @param data
|
||||
* @param errMsg
|
||||
*/
|
||||
private void validateModule(FunctionalCaseExcelData data, StringBuilder errMsg) {
|
||||
String module = data.getModule();
|
||||
if (StringUtils.isNotEmpty(module)) {
|
||||
String[] nodes = module.split("/");
|
||||
//模块名不能为空
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
if (i != 0 && StringUtils.equals(nodes[i].trim(), StringUtils.EMPTY)) {
|
||||
errMsg.append(Translator.get("module_not_null"))
|
||||
.append(ERROR_MSG_SEPARATOR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//增加字数校验,每一层不能超过100个字
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
String nodeStr = nodes[i];
|
||||
if (StringUtils.isNotEmpty(nodeStr)) {
|
||||
if (nodeStr.trim().length() > 100) {
|
||||
errMsg.append(Translator.get("module"))
|
||||
.append(Translator.get("functional_case.module.length_less_than"))
|
||||
.append("100:")
|
||||
.append(nodeStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 数据转换
|
||||
*
|
||||
* @param row
|
||||
* @return
|
||||
*/
|
||||
private FunctionalCaseExcelData parseDataToModel(Map<Integer, String> row) {
|
||||
FunctionalCaseExcelData data = new FunctionalCaseExcelDataFactory().getFunctionalCaseExcelDataLocal();
|
||||
for (Map.Entry<Integer, String> headEntry : headMap.entrySet()) {
|
||||
Integer index = headEntry.getKey();
|
||||
String field = headEntry.getValue();
|
||||
if (StringUtils.isBlank(field)) {
|
||||
continue;
|
||||
}
|
||||
String value = StringUtils.isEmpty(row.get(index)) ? StringUtils.EMPTY : row.get(index);
|
||||
|
||||
if (excelHeadToFieldNameDic.containsKey(field)) {
|
||||
field = excelHeadToFieldNameDic.get(field);
|
||||
}
|
||||
|
||||
if (StringUtils.equals(field, "id")) {
|
||||
data.setName(value);
|
||||
} else if (StringUtils.equals(field, "num")) {
|
||||
data.setNum(value);
|
||||
} else if (StringUtils.equals(field, "name")) {
|
||||
data.setName(value);
|
||||
} else if (StringUtils.equals(field, "module")) {
|
||||
data.setModule(value);
|
||||
} else if (StringUtils.equals(field, "tags")) {
|
||||
data.setTags(value);
|
||||
} else if (StringUtils.equals(field, "prerequisite")) {
|
||||
data.setPrerequisite(value);
|
||||
} else if (StringUtils.equals(field, "description")) {
|
||||
data.setDescription(value);
|
||||
} else if (StringUtils.equals(field, "textDescription")) {
|
||||
data.setTextDescription(value);
|
||||
} else if (StringUtils.equals(field, "expectedResult")) {
|
||||
data.setExpectedResult(value);
|
||||
} else if (StringUtils.equals(field, "caseEditType")) {
|
||||
data.setCaseEditType(value);
|
||||
} else {
|
||||
data.getCustomData().put(field, value);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理合并单元格
|
||||
*
|
||||
* @param data
|
||||
* @param rowIndex
|
||||
*/
|
||||
private void handleMergeData(Map<Integer, String> data, Integer rowIndex) {
|
||||
isMergeRow = false;
|
||||
isMergeLastRow = false;
|
||||
if (getNameColIndex() == null) {
|
||||
throw new MSException(Translator.get("case_import_table_header_missing"));
|
||||
}
|
||||
data.keySet().forEach(col -> {
|
||||
Iterator<ExcelMergeInfo> iterator = mergeInfoSet.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ExcelMergeInfo mergeInfo = iterator.next();
|
||||
// 如果单元格的行号在合并单元格的范围之间,并且列号相等,说明该单元格是合并单元格中的一部分
|
||||
if (mergeInfo.getFirstRowIndex() <= rowIndex && rowIndex <= mergeInfo.getLastRowIndex()
|
||||
&& col.equals(mergeInfo.getFirstColumnIndex())) {
|
||||
// 根据名称列是否是合并单元格判断是不是同一条用例
|
||||
if (getNameColIndex().equals(col)) {
|
||||
isMergeRow = true;
|
||||
}
|
||||
// 如果是合并单元格的第一个cell,则把这个单元格的数据存起来
|
||||
if (rowIndex.equals(mergeInfo.getFirstRowIndex())) {
|
||||
if (StringUtils.isNotBlank(data.get(col))) {
|
||||
mergeCellDataMap.put(mergeInfo, data.get(col));
|
||||
}
|
||||
} else {
|
||||
// 非第一个,获取存储的数据填充
|
||||
String cellData = mergeCellDataMap.get(mergeInfo);
|
||||
if (StringUtils.isNotBlank(cellData)) {
|
||||
data.put(col, cellData);
|
||||
}
|
||||
}
|
||||
// 如果合并单元格的最后一个单元格,标记下
|
||||
if (rowIndex.equals(mergeInfo.getLastRowIndex())) {
|
||||
// 根据名称列是否是合并单元格判断是不是同一条用例
|
||||
if (getNameColIndex().equals(col)) {
|
||||
isMergeLastRow = true;
|
||||
// 清除掉上一次已经遍历完成的数据,提高查询效率
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Integer getNameColIndex() {
|
||||
return findColIndex("name");
|
||||
}
|
||||
|
||||
private Integer getTextDescriptionColIndex() {
|
||||
return findColIndex("textDescription");
|
||||
}
|
||||
|
||||
private Integer getExpectedResultColIndex() {
|
||||
return findColIndex("expectedResult");
|
||||
}
|
||||
|
||||
private Integer findColIndex(String colName) {
|
||||
for (Integer key : headMap.keySet()) {
|
||||
if (StringUtils.equals(headMap.get(key), colName)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取注解里ExcelProperty的value
|
||||
*/
|
||||
public Set<String> genExcelHeadToFieldNameDicAndGetNotRequiredFields() throws NoSuchFieldException {
|
||||
|
||||
Set<String> result = new HashSet<>();
|
||||
Field field;
|
||||
Field[] fields = excelDataClass.getDeclaredFields();
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
field = excelDataClass.getDeclaredField(fields[i].getName());
|
||||
field.setAccessible(true);
|
||||
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||
if (excelProperty != null) {
|
||||
StringBuilder value = new StringBuilder();
|
||||
for (String v : excelProperty.value()) {
|
||||
value.append(v);
|
||||
}
|
||||
excelHeadToFieldNameDic.put(value.toString(), field.getName());
|
||||
// 检查是否必有的头部信息
|
||||
if (field.getAnnotation(NotRequired.class) != null) {
|
||||
result.add(value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void formatHeadMap() {
|
||||
for (Integer key : headMap.keySet()) {
|
||||
String name = headMap.get(key);
|
||||
if (excelHeadToFieldNameDic.containsKey(name)) {
|
||||
headMap.put(key, excelHeadToFieldNameDic.get(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<ExcelErrData<FunctionalCaseExcelData>> getErrList() {
|
||||
return errList;
|
||||
}
|
||||
|
||||
public int getSuccessCount() {
|
||||
return successCount;
|
||||
}
|
||||
}
|
|
@ -55,25 +55,8 @@ public abstract class AbstractCustomFieldValidator {
|
|||
|
||||
protected List<String> parse2Array(String name, String value) throws CustomFieldValidateException {
|
||||
try {
|
||||
// [a, b] => ["a","b"]
|
||||
if (!StringUtils.equals(value, "[]")) {
|
||||
if (!value.contains("[\"")) {
|
||||
value = value.replace("[", "[\"");
|
||||
}
|
||||
if (!value.contains("\"]")) {
|
||||
value = value.replace("]", "\"]");
|
||||
|
||||
}
|
||||
if (!value.contains("\",\"")) {
|
||||
value = value.replace(",", "\",\"");
|
||||
|
||||
}
|
||||
if (!value.contains("\",\"")) {
|
||||
value = value.replace(",", "\",\"");
|
||||
}
|
||||
value = value.replace(StringUtils.SPACE, StringUtils.EMPTY);
|
||||
}
|
||||
return JSON.parseArray(value, String.class);
|
||||
//a,b,c => ["a","b","c"]
|
||||
return JSON.parseArray(JSON.toJSONString(value.split(",")));
|
||||
} catch (Exception e) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_array_tip"), name));
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.functional.excel.validate;
|
|||
|
||||
|
||||
import io.metersphere.functional.excel.exception.CustomFieldValidateException;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -44,6 +45,6 @@ public class CustomFieldMultipleMemberValidator extends CustomFieldMemberValidat
|
|||
keyOrValues.set(i, userNameMap.get(item));
|
||||
}
|
||||
}
|
||||
return keyOrValues;
|
||||
return JSON.toJSONString(keyOrValues);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.functional.excel.validate;
|
|||
|
||||
|
||||
import io.metersphere.functional.excel.exception.CustomFieldValidateException;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -44,6 +45,6 @@ public class CustomFieldMultipleSelectValidator extends CustomFieldSelectValidat
|
|||
keyOrValues.set(i, nameMap.get(item));
|
||||
}
|
||||
}
|
||||
return keyOrValues;
|
||||
return JSON.toJSONString(keyOrValues);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,13 @@ package io.metersphere.functional.excel.validate;
|
|||
|
||||
|
||||
import io.metersphere.functional.excel.exception.CustomFieldValidateException;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
|
@ -32,6 +35,8 @@ public class CustomFieldMultipleTextValidator extends AbstractCustomFieldValidat
|
|||
if (StringUtils.isBlank(keyOrValuesStr)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return parse2Array(keyOrValuesStr);
|
||||
List<String> keyOrValues = parse2Array(keyOrValuesStr);
|
||||
|
||||
return JSON.toJSONString(keyOrValues);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,13 @@ import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
|
|||
import io.metersphere.functional.excel.domain.FunctionalCaseExcelDataFactory;
|
||||
import io.metersphere.functional.excel.handler.FunctionCaseTemplateWriteHandler;
|
||||
import io.metersphere.functional.excel.listener.FunctionalCaseCheckEventListener;
|
||||
import io.metersphere.functional.excel.listener.FunctionalCaseImportEventListener;
|
||||
import io.metersphere.functional.excel.listener.FunctionalCasePretreatmentListener;
|
||||
import io.metersphere.functional.request.FunctionalCaseImportRequest;
|
||||
import io.metersphere.plugin.sdk.util.MSPluginException;
|
||||
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
||||
import io.metersphere.project.service.ProjectTemplateService;
|
||||
import io.metersphere.sdk.constants.TemplateScene;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.domain.CustomFieldOption;
|
||||
|
@ -41,6 +43,8 @@ public class FunctionalCaseFileService {
|
|||
|
||||
@Resource
|
||||
private ProjectTemplateService projectTemplateService;
|
||||
@Resource
|
||||
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -185,7 +189,7 @@ public class FunctionalCaseFileService {
|
|||
*/
|
||||
public FunctionalCaseImportResponse preCheckExcel(FunctionalCaseImportRequest request, MultipartFile file) {
|
||||
if (file == null) {
|
||||
throw new MSPluginException("file_cannot_be_null");
|
||||
throw new MSException(Translator.get("file_cannot_be_null"));
|
||||
}
|
||||
FunctionalCaseImportResponse response = new FunctionalCaseImportResponse();
|
||||
checkImportExcel(response, request, file);
|
||||
|
@ -205,11 +209,11 @@ public class FunctionalCaseFileService {
|
|||
FunctionalCaseCheckEventListener eventListener = new FunctionalCaseCheckEventListener(request, clazz, customFields, mergeInfoSet);
|
||||
EasyExcelFactory.read(file.getInputStream(), eventListener).sheet().doRead();
|
||||
response.setErrorMessages(eventListener.getErrList());
|
||||
response.setSuccessCount(eventListener.getSuccessCount());
|
||||
response.setSuccessCount(eventListener.getList().size());
|
||||
response.setFailCount(eventListener.getErrList().size());
|
||||
} catch (Exception e) {
|
||||
LogUtils.error("checkImportExcel error", e);
|
||||
throw new MSPluginException("checkImportExcel error");
|
||||
throw new MSException(Translator.get("check_import_excel_error"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,4 +222,42 @@ public class FunctionalCaseFileService {
|
|||
List<TemplateCustomFieldDTO> customFields = Optional.ofNullable(defaultTemplateDTO.getCustomFields()).orElse(new ArrayList<>());
|
||||
return customFields;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导入excel
|
||||
*
|
||||
* @param request
|
||||
* @param userId
|
||||
* @param file
|
||||
*/
|
||||
public FunctionalCaseImportResponse importExcel(FunctionalCaseImportRequest request, String userId, MultipartFile file) {
|
||||
if (file == null) {
|
||||
throw new MSException(Translator.get("file_cannot_be_null"));
|
||||
}
|
||||
try {
|
||||
FunctionalCaseImportResponse response = new FunctionalCaseImportResponse();
|
||||
//设置默认版本
|
||||
if (StringUtils.isEmpty(request.getVersionId())) {
|
||||
request.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(request.getProjectId()));
|
||||
}
|
||||
//根据本地语言环境选择用哪种数据对象进行存放读取的数据
|
||||
Class clazz = new FunctionalCaseExcelDataFactory().getExcelDataByLocal();
|
||||
//获取当前项目默认模板的自定义字段
|
||||
List<TemplateCustomFieldDTO> customFields = getCustomFields(request.getProjectId());
|
||||
Set<ExcelMergeInfo> mergeInfoSet = new TreeSet<>();
|
||||
// 预处理,查询合并单元格信息
|
||||
EasyExcel.read(file.getInputStream(), null, new FunctionalCasePretreatmentListener(mergeInfoSet))
|
||||
.extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();
|
||||
FunctionalCaseImportEventListener eventListener = new FunctionalCaseImportEventListener(request, clazz, customFields, mergeInfoSet, userId);
|
||||
EasyExcelFactory.read(file.getInputStream(), eventListener).sheet().doRead();
|
||||
response.setErrorMessages(eventListener.getErrList());
|
||||
response.setSuccessCount(eventListener.getSuccessCount());
|
||||
response.setFailCount(eventListener.getErrList().size());
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
LogUtils.error("checkImportExcel error", e);
|
||||
throw new MSException(Translator.get("check_import_excel_error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,10 +42,8 @@ import org.mybatis.spring.SqlSessionUtils;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
|
||||
@Service
|
||||
|
@ -68,7 +66,7 @@ public class FunctionalCaseModuleService extends ModuleTreeService {
|
|||
List<BaseTreeNode> functionalModuleList = extFunctionalCaseModuleMapper.selectBaseByProjectId(projectId);
|
||||
return super.buildTreeAndCountResource(functionalModuleList, true, Translator.get("default.module"));
|
||||
}
|
||||
|
||||
|
||||
public String add(FunctionalCaseModuleCreateRequest request, String userId) {
|
||||
FunctionalCaseModule functionalCaseModule = new FunctionalCaseModule();
|
||||
functionalCaseModule.setId(IDGenerator.nextStr());
|
||||
|
@ -146,7 +144,7 @@ public class FunctionalCaseModuleService extends ModuleTreeService {
|
|||
operationLogService.batchAdd(dtoList);
|
||||
}
|
||||
|
||||
public List<FunctionalCase> deleteModuleByIds(List<String>deleteIds, List<FunctionalCase>functionalCases){
|
||||
public List<FunctionalCase> deleteModuleByIds(List<String> deleteIds, List<FunctionalCase> functionalCases) {
|
||||
if (CollectionUtils.isEmpty(deleteIds)) {
|
||||
return functionalCases;
|
||||
}
|
||||
|
@ -176,7 +174,6 @@ public class FunctionalCaseModuleService extends ModuleTreeService {
|
|||
|
||||
/**
|
||||
* 查找当前项目下模块每个节点对应的资源统计
|
||||
*
|
||||
*/
|
||||
public Map<String, Long> getModuleCountMap(String projectId, List<ModuleCountDTO> moduleCountDTOList) {
|
||||
|
||||
|
@ -212,7 +209,7 @@ public class FunctionalCaseModuleService extends ModuleTreeService {
|
|||
}
|
||||
example.clear();
|
||||
}
|
||||
example.createCriteria().andParentIdEqualTo(functionalCaseModule.getParentId()).andNameEqualTo(functionalCaseModule.getName()).andIdNotEqualTo(functionalCaseModule.getId());
|
||||
example.createCriteria().andParentIdEqualTo(functionalCaseModule.getParentId()).andNameEqualTo(functionalCaseModule.getName()).andIdNotEqualTo(functionalCaseModule.getId()).andProjectIdEqualTo(functionalCaseModule.getProjectId());
|
||||
if (functionalCaseModuleMapper.countByExample(example) > 0) {
|
||||
throw new MSException(Translator.get("node.name.repeat"));
|
||||
}
|
||||
|
@ -269,18 +266,148 @@ public class FunctionalCaseModuleService extends ModuleTreeService {
|
|||
return super.buildTreeAndCountResource(nodeByNodeIds, true, Translator.get("default.module"));
|
||||
}
|
||||
|
||||
public List<BaseTreeNode> getNodeByNodeIds(List<String>moduleIds){
|
||||
public List<BaseTreeNode> getNodeByNodeIds(List<String> moduleIds) {
|
||||
List<String> finalModuleIds = new ArrayList<>(moduleIds);
|
||||
List<BaseTreeNode> totalList = new ArrayList<>();
|
||||
while (CollectionUtils.isNotEmpty(finalModuleIds)) {
|
||||
while (CollectionUtils.isNotEmpty(finalModuleIds)) {
|
||||
List<BaseTreeNode> modules = extFunctionalCaseModuleMapper.selectBaseByIds(finalModuleIds);
|
||||
totalList.addAll(modules);
|
||||
List<String> finalModuleIdList = finalModuleIds;
|
||||
List<String> parentModuleIds = modules.stream().map(BaseTreeNode::getParentId).filter(parentId -> !StringUtils.equalsIgnoreCase(parentId,ModuleConstants.ROOT_NODE_PARENT_ID) && !finalModuleIdList.contains(parentId)).toList();
|
||||
List<String> parentModuleIds = modules.stream().map(BaseTreeNode::getParentId).filter(parentId -> !StringUtils.equalsIgnoreCase(parentId, ModuleConstants.ROOT_NODE_PARENT_ID) && !finalModuleIdList.contains(parentId)).toList();
|
||||
finalModuleIds.clear();
|
||||
finalModuleIds = new ArrayList<>(parentModuleIds);
|
||||
}
|
||||
return totalList.stream().distinct().toList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据模块路径创建模块
|
||||
*
|
||||
* @param modulePath 模块路径
|
||||
* @param projectId 项目ID
|
||||
* @param moduleTree 已存在的模块树
|
||||
* @param userId userId
|
||||
*/
|
||||
public Map<String, String> createCaseModule(List<String> modulePath, String projectId, List<BaseTreeNode> moduleTree, String userId, Map<String, String> pathMap) {
|
||||
modulePath.forEach(path -> {
|
||||
List<String> moduleNames = new ArrayList<>(List.of(path.split("/")));
|
||||
Iterator<String> itemIterator = moduleNames.iterator();
|
||||
AtomicReference<Boolean> hasNode = new AtomicReference<>(false);
|
||||
//当前节点模块名称
|
||||
String currentModuleName;
|
||||
if (moduleNames.size() <= 1) {
|
||||
throw new MSException(Translator.get("test_case_create_module_fail") + ":" + path);
|
||||
} else {
|
||||
itemIterator.next();
|
||||
itemIterator.remove();
|
||||
currentModuleName = itemIterator.next().trim();
|
||||
moduleTree.forEach(module -> {
|
||||
//根节点是否存在
|
||||
if (StringUtils.equals(currentModuleName, module.getName())) {
|
||||
hasNode.set(true);
|
||||
//根节点存在,检查子节点是否存在
|
||||
createModuleByPathIterator(itemIterator, "/" + currentModuleName, module, pathMap, projectId, userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!hasNode.get()) {
|
||||
//根节点不存在,直接创建
|
||||
createModuleByPath(itemIterator, currentModuleName, null, projectId, StringUtils.EMPTY, pathMap, userId);
|
||||
}
|
||||
});
|
||||
return pathMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据模块路径迭代器遍历模块路径
|
||||
*
|
||||
* @param itemIterator 模块路径迭代器
|
||||
* @param currentModulePath 当前节点路径: /模块1/模块2
|
||||
* @param module 当前模块对象
|
||||
* @param pathMap 记录新创建的模块路径和模块ID
|
||||
* @param projectId 项目id
|
||||
* @param userId userId
|
||||
*/
|
||||
private void createModuleByPathIterator(Iterator<String> itemIterator, String currentModulePath, BaseTreeNode module, Map<String, String> pathMap, String projectId, String userId) {
|
||||
List<BaseTreeNode> children = module.getChildren();
|
||||
if (CollectionUtils.isEmpty(children) || !itemIterator.hasNext()) {
|
||||
//没有子节点,根据当前模块目录创建模块节点
|
||||
pathMap.put(currentModulePath, module.getId());
|
||||
if (itemIterator.hasNext()) {
|
||||
createModuleByPath(itemIterator, itemIterator.next().trim(), module, projectId, currentModulePath, pathMap, userId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
String nodeName = itemIterator.next().trim();
|
||||
AtomicReference<Boolean> hasNode = new AtomicReference<>(false);
|
||||
children.forEach(child -> {
|
||||
if (StringUtils.equals(nodeName, child.getName())) {
|
||||
hasNode.set(true);
|
||||
createModuleByPathIterator(itemIterator, currentModulePath + "/" + child.getName(), child, pathMap, projectId, userId);
|
||||
}
|
||||
});
|
||||
|
||||
//若子节点中不包含该目标节点,则在该节点下创建
|
||||
if (!hasNode.get()) {
|
||||
createModuleByPath(itemIterator, nodeName, module, projectId, currentModulePath, pathMap, userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历模块路径,创建模块
|
||||
*
|
||||
* @param itemIterator 模块路径迭代器
|
||||
* @param moduleName 当前模块名称: 模块1
|
||||
* @param parentModule 父模块对象
|
||||
* @param projectId 项目id
|
||||
* @param currentPath 当前模块路径: /模块1
|
||||
* @param pathMap 记录新创建的模块路径和模块ID
|
||||
*/
|
||||
private void createModuleByPath(Iterator<String> itemIterator, String moduleName, BaseTreeNode parentModule, String projectId, String currentPath, Map<String, String> pathMap, String userId) {
|
||||
StringBuilder path = new StringBuilder(currentPath);
|
||||
path.append("/" + moduleName.trim());
|
||||
|
||||
//模块id
|
||||
String pid;
|
||||
if (pathMap.get(path.toString()) != null) {
|
||||
//如果创建过,直接获取模块ID
|
||||
pid = pathMap.get(path.toString());
|
||||
} else {
|
||||
pid = insertNode(moduleName, parentModule == null ? ModuleConstants.ROOT_NODE_PARENT_ID : parentModule.getId(), projectId, userId);
|
||||
pathMap.put(path.toString(), pid);
|
||||
}
|
||||
|
||||
while (itemIterator.hasNext()) {
|
||||
String nextModuleName = itemIterator.next().trim();
|
||||
path.append("/" + nextModuleName);
|
||||
if (pathMap.get(path.toString()) != null) {
|
||||
pid = pathMap.get(path.toString());
|
||||
} else {
|
||||
pid = insertNode(nextModuleName, pid, projectId, userId);
|
||||
pathMap.put(path.toString(), pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建模块
|
||||
*
|
||||
* @param moduleName 模块名称
|
||||
* @param parentId 父模块ID
|
||||
* @param projectId 项目ID
|
||||
* @param userId userId
|
||||
* @return
|
||||
*/
|
||||
private String insertNode(String moduleName, String parentId, String projectId, String userId) {
|
||||
FunctionalCaseModuleCreateRequest request = new FunctionalCaseModuleCreateRequest();
|
||||
request.setProjectId(projectId);
|
||||
request.setName(moduleName);
|
||||
request.setParentId(parentId);
|
||||
return this.add(request, userId);
|
||||
}
|
||||
|
||||
}
|
|
@ -5,8 +5,10 @@ import io.metersphere.bug.mapper.BugRelationCaseMapper;
|
|||
import io.metersphere.functional.constants.CaseEvent;
|
||||
import io.metersphere.functional.constants.CaseFileSourceType;
|
||||
import io.metersphere.functional.constants.FunctionalCaseReviewStatus;
|
||||
import io.metersphere.functional.constants.FunctionalCaseTypeConstants;
|
||||
import io.metersphere.functional.domain.*;
|
||||
import io.metersphere.functional.dto.*;
|
||||
import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
|
||||
import io.metersphere.functional.mapper.*;
|
||||
import io.metersphere.functional.request.*;
|
||||
import io.metersphere.functional.result.CaseManagementResultCode;
|
||||
|
@ -25,6 +27,7 @@ import io.metersphere.sdk.exception.MSException;
|
|||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.domain.CustomFieldOption;
|
||||
import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||
import io.metersphere.system.dto.sdk.TemplateDTO;
|
||||
import io.metersphere.system.dto.sdk.request.PosRequest;
|
||||
|
@ -195,16 +198,6 @@ public class FunctionalCaseService {
|
|||
return functionalCase;
|
||||
}
|
||||
|
||||
private List<CaseCustomFieldDTO> getCustomFields(Map<String, Object> customFields) {
|
||||
List<CaseCustomFieldDTO> list = new ArrayList<>();
|
||||
customFields.keySet().forEach(key -> {
|
||||
CaseCustomFieldDTO caseCustomFieldDTO = new CaseCustomFieldDTO();
|
||||
caseCustomFieldDTO.setFieldId(key);
|
||||
caseCustomFieldDTO.setValue(customFields.get(key).toString());
|
||||
list.add(caseCustomFieldDTO);
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
public Long getNextOrder(String projectId) {
|
||||
Long pos = extFunctionalCaseMapper.getPos(projectId);
|
||||
|
@ -771,4 +764,183 @@ public class FunctionalCaseService {
|
|||
extFunctionalCaseMapper::getLastPos,
|
||||
functionalCaseMapper::updateByPrimaryKeySelective);
|
||||
}
|
||||
|
||||
public String checkNumExist(String num, String projectId) {
|
||||
FunctionalCaseExample example = new FunctionalCaseExample();
|
||||
example.createCriteria().andNumEqualTo(Long.valueOf(num)).andProjectIdEqualTo(projectId).andDeletedEqualTo(false);
|
||||
List<FunctionalCase> functionalCases = functionalCaseMapper.selectByExample(example);
|
||||
if (CollectionUtils.isNotEmpty(functionalCases)) {
|
||||
return functionalCases.get(0).getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导入新建数据
|
||||
*
|
||||
* @param list 导入数据集合
|
||||
* @param request request
|
||||
* @param moduleTree 模块树
|
||||
* @param userId 用户id
|
||||
* @param customFieldsMap 当前默认模板的自定义字段
|
||||
*/
|
||||
public void saveImportData(List<FunctionalCaseExcelData> list, FunctionalCaseImportRequest request, List<BaseTreeNode> moduleTree, String userId, Map<String, TemplateCustomFieldDTO> customFieldsMap, Map<String, String> pathMap) {
|
||||
//默认模板
|
||||
TemplateDTO defaultTemplateDTO = projectTemplateService.getDefaultTemplateDTO(request.getProjectId(), TemplateScene.FUNCTIONAL.name());
|
||||
//模块路径
|
||||
List<String> modulePath = list.stream().map(FunctionalCaseExcelData::getModule).toList();
|
||||
//构建模块树
|
||||
Map<String, String> caseModulePathMap = functionalCaseModuleService.createCaseModule(modulePath, request.getProjectId(), moduleTree, userId, pathMap);
|
||||
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
FunctionalCaseMapper caseMapper = sqlSession.getMapper(FunctionalCaseMapper.class);
|
||||
FunctionalCaseBlobMapper caseBlobMapper = sqlSession.getMapper(FunctionalCaseBlobMapper.class);
|
||||
FunctionalCaseCustomFieldMapper customFieldMapper = sqlSession.getMapper(FunctionalCaseCustomFieldMapper.class);
|
||||
Long nextOrder = getNextOrder(request.getProjectId());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
parseInsertDataToModule(list.get(i), request, userId, caseModulePathMap, defaultTemplateDTO, nextOrder, caseMapper, caseBlobMapper, customFieldMapper, customFieldsMap);
|
||||
}
|
||||
sqlSession.flushStatements();
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
|
||||
private void parseInsertDataToModule(FunctionalCaseExcelData functionalCaseExcelData, FunctionalCaseImportRequest request, String userId, Map<String, String> caseModulePathMap, TemplateDTO defaultTemplateDTO, Long nextOrder,
|
||||
FunctionalCaseMapper caseMapper, FunctionalCaseBlobMapper caseBlobMapper, FunctionalCaseCustomFieldMapper customFieldMapper, Map<String, TemplateCustomFieldDTO> customFieldsMap) {
|
||||
//构建用例
|
||||
FunctionalCase functionalCase = new FunctionalCase();
|
||||
String caseId = IDGenerator.nextStr();
|
||||
functionalCase.setId(caseId);
|
||||
functionalCase.setNum(getNextNum(request.getProjectId()));
|
||||
functionalCase.setModuleId(caseModulePathMap.get(functionalCaseExcelData.getModule()));
|
||||
functionalCase.setProjectId(request.getProjectId());
|
||||
functionalCase.setTemplateId(defaultTemplateDTO.getId());
|
||||
functionalCase.setName(functionalCaseExcelData.getName());
|
||||
functionalCase.setReviewStatus(FunctionalCaseReviewStatus.UN_REVIEWED.name());
|
||||
functionalCase.setTags(handleImportTags(functionalCaseExcelData.getTags()));
|
||||
functionalCase.setCaseEditType(StringUtils.defaultIfBlank(functionalCaseExcelData.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name()));
|
||||
functionalCase.setPos(nextOrder + ServiceUtils.POS_STEP);
|
||||
functionalCase.setVersionId(request.getVersionId());
|
||||
functionalCase.setRefId(caseId);
|
||||
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.UN_EXECUTED.name());
|
||||
functionalCase.setLatest(true);
|
||||
functionalCase.setCreateUser(userId);
|
||||
functionalCase.setCreateTime(System.currentTimeMillis());
|
||||
functionalCase.setUpdateTime(System.currentTimeMillis());
|
||||
caseMapper.insertSelective(functionalCase);
|
||||
|
||||
//用例附属表
|
||||
FunctionalCaseBlob caseBlob = new FunctionalCaseBlob();
|
||||
caseBlob.setId(caseId);
|
||||
caseBlob.setSteps(StringUtils.defaultIfBlank(functionalCaseExcelData.getSteps(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlob.setTextDescription(StringUtils.defaultIfBlank(functionalCaseExcelData.getTextDescription(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlob.setExpectedResult(StringUtils.defaultIfBlank(functionalCaseExcelData.getExpectedResult(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlob.setPrerequisite(StringUtils.defaultIfBlank(functionalCaseExcelData.getPrerequisite(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlob.setDescription(StringUtils.defaultIfBlank(functionalCaseExcelData.getDescription(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlobMapper.insertSelective(caseBlob);
|
||||
|
||||
//自定义字段
|
||||
handleImportCustomField(functionalCaseExcelData, caseId, customFieldMapper, customFieldsMap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理导入标签
|
||||
*
|
||||
* @param tags 标签
|
||||
* @return
|
||||
*/
|
||||
private List<String> handleImportTags(String tags) {
|
||||
List<String> split = List.of(tags.split("[,;]"));
|
||||
return split.stream().map(String::trim).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理导入自定义字段
|
||||
*
|
||||
* @param functionalCaseExcelData 导入数据
|
||||
* @param caseId 用例id
|
||||
* @param customFieldMapper 自定义字段mapper
|
||||
* @param customFieldsMap 当前默认模板的自定义字段
|
||||
*/
|
||||
private void handleImportCustomField(FunctionalCaseExcelData functionalCaseExcelData, String caseId, FunctionalCaseCustomFieldMapper customFieldMapper, Map<String, TemplateCustomFieldDTO> customFieldsMap) {
|
||||
//需要保存的自定义字段
|
||||
Map<String, Object> customData = functionalCaseExcelData.getCustomData();
|
||||
customData.forEach((k, v) -> {
|
||||
if (customFieldsMap.containsKey(k)) {
|
||||
TemplateCustomFieldDTO templateCustomFieldDTO = customFieldsMap.get(k);
|
||||
FunctionalCaseCustomField caseCustomField = new FunctionalCaseCustomField();
|
||||
caseCustomField.setCaseId(caseId);
|
||||
caseCustomField.setFieldId(templateCustomFieldDTO.getFieldId());
|
||||
caseCustomField.setValue(v.toString());
|
||||
customFieldMapper.insertSelective(caseCustomField);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导入更新数据
|
||||
*
|
||||
* @param updateList 更新数据集合
|
||||
* @param request request
|
||||
* @param moduleTree 模块树
|
||||
* @param userId 用户id
|
||||
* @param customFieldsMap 当前默认模板的自定义字段
|
||||
*/
|
||||
public void updateImportData(List<FunctionalCaseExcelData> updateList, FunctionalCaseImportRequest request, List<BaseTreeNode> moduleTree, String userId, Map<String, TemplateCustomFieldDTO> customFieldsMap, Map<String, String> pathMap) {
|
||||
//默认模板
|
||||
TemplateDTO defaultTemplateDTO = projectTemplateService.getDefaultTemplateDTO(request.getProjectId(), TemplateScene.FUNCTIONAL.name());
|
||||
//模块路径
|
||||
List<String> modulePath = updateList.stream().map(FunctionalCaseExcelData::getModule).toList();
|
||||
//构建模块树
|
||||
Map<String, String> caseModulePathMap = functionalCaseModuleService.createCaseModule(modulePath, request.getProjectId(), moduleTree, userId, pathMap);
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
FunctionalCaseMapper caseMapper = sqlSession.getMapper(FunctionalCaseMapper.class);
|
||||
FunctionalCaseBlobMapper caseBlobMapper = sqlSession.getMapper(FunctionalCaseBlobMapper.class);
|
||||
FunctionalCaseCustomFieldMapper customFieldMapper = sqlSession.getMapper(FunctionalCaseCustomFieldMapper.class);
|
||||
for (int i = 0; i < updateList.size(); i++) {
|
||||
parseUpdateDataToModule(updateList.get(i), request, userId, caseModulePathMap, defaultTemplateDTO, caseMapper, caseBlobMapper, customFieldMapper, customFieldsMap);
|
||||
}
|
||||
|
||||
sqlSession.flushStatements();
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
|
||||
private void parseUpdateDataToModule(FunctionalCaseExcelData functionalCaseExcelData, FunctionalCaseImportRequest request, String userId, Map<String, String> caseModulePathMap, TemplateDTO defaultTemplateDTO, FunctionalCaseMapper caseMapper, FunctionalCaseBlobMapper caseBlobMapper, FunctionalCaseCustomFieldMapper customFieldMapper, Map<String, TemplateCustomFieldDTO> customFieldsMap) {
|
||||
//用例表
|
||||
FunctionalCase functionalCase = caseMapper.selectByPrimaryKey(functionalCaseExcelData.getNum());
|
||||
functionalCase.setName(functionalCaseExcelData.getName());
|
||||
functionalCase.setModuleId(caseModulePathMap.get(functionalCaseExcelData.getModule()));
|
||||
functionalCase.setTags(handleImportTags(functionalCaseExcelData.getTags()));
|
||||
functionalCase.setCaseEditType(StringUtils.defaultIfBlank(functionalCaseExcelData.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name()));
|
||||
//模板
|
||||
functionalCase.setTemplateId(defaultTemplateDTO.getId());
|
||||
functionalCase.setVersionId(request.getVersionId());
|
||||
functionalCase.setUpdateUser(userId);
|
||||
functionalCase.setUpdateTime(System.currentTimeMillis());
|
||||
caseMapper.updateByPrimaryKeySelective(functionalCase);
|
||||
|
||||
//用例附属表
|
||||
FunctionalCaseBlob caseBlob = new FunctionalCaseBlob();
|
||||
caseBlob.setId(functionalCase.getId());
|
||||
caseBlob.setSteps(StringUtils.defaultIfBlank(functionalCaseExcelData.getSteps(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlob.setTextDescription(StringUtils.defaultIfBlank(functionalCaseExcelData.getTextDescription(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlob.setExpectedResult(StringUtils.defaultIfBlank(functionalCaseExcelData.getExpectedResult(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlob.setPrerequisite(StringUtils.defaultIfBlank(functionalCaseExcelData.getPrerequisite(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlob.setDescription(StringUtils.defaultIfBlank(functionalCaseExcelData.getDescription(), StringUtils.EMPTY).getBytes(StandardCharsets.UTF_8));
|
||||
caseBlobMapper.updateByPrimaryKeyWithBLOBs(caseBlob);
|
||||
|
||||
//自定义字段
|
||||
handleUpdateCustomField(functionalCaseExcelData, functionalCase.getId(), customFieldMapper, customFieldsMap);
|
||||
|
||||
}
|
||||
|
||||
private void handleUpdateCustomField(FunctionalCaseExcelData functionalCaseExcelData, String caseId, FunctionalCaseCustomFieldMapper customFieldMapper, Map<String, TemplateCustomFieldDTO> customFieldsMap) {
|
||||
FunctionalCaseCustomFieldExample fieldExample = new FunctionalCaseCustomFieldExample();
|
||||
fieldExample.createCriteria().andCaseIdEqualTo(caseId);
|
||||
customFieldMapper.deleteByExample(fieldExample);
|
||||
handleImportCustomField(functionalCaseExcelData, caseId, customFieldMapper, customFieldsMap);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
|||
public static final String FUNCTIONAL_CASE_POS_URL = "/functional/case/edit/pos";
|
||||
public static final String DOWNLOAD_EXCEL_TEMPLATE_URL = "/functional/case/download/excel/template/";
|
||||
public static final String CHECK_EXCEL_URL = "/functional/case/pre-check/excel";
|
||||
public static final String IMPORT_EXCEL_URL = "/functional/case/import/excel";
|
||||
|
||||
@Resource
|
||||
private NotificationMapper notificationMapper;
|
||||
|
@ -132,7 +133,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
|||
paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("files", new LinkedMultiValueMap<>());
|
||||
functionalCaseMvcResult = this.requestMultipartWithOkAndReturn(FUNCTIONAL_CASE_ADD_URL, paramMap);
|
||||
functionalCaseMvcResult = this.requestMultipartWithOkAndReturn(FUNCTIONAL_CASE_ADD_URL, paramMap);
|
||||
// 获取返回值
|
||||
returnData = functionalCaseMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
resultHolder = JSON.parseObject(returnData, ResultHolder.class);
|
||||
|
@ -140,7 +141,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
|||
Assertions.assertNotNull(resultHolder);
|
||||
functionalCase = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalCase.class);
|
||||
FunctionalCaseEditRequest request1 = new FunctionalCaseEditRequest();
|
||||
BeanUtils.copyBean(request1,request);
|
||||
BeanUtils.copyBean(request1, request);
|
||||
request1.setId(functionalCase.getId());
|
||||
request1.setRelateFileMetaIds(new ArrayList<>());
|
||||
paramMap = new LinkedMultiValueMap<>();
|
||||
|
@ -559,7 +560,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
|||
|
||||
|
||||
@Test
|
||||
@Order(20)
|
||||
@Order(18)
|
||||
public void testImportCheckExcel() throws Exception {
|
||||
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/1.xlsx")).getPath();
|
||||
MockMultipartFile file = new MockMultipartFile("file", "11.xlsx", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath));
|
||||
|
@ -569,7 +570,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
|||
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("file", file);
|
||||
MvcResult functionalCaseMvcResult = this.requestMultipartWithOkAndReturn(CHECK_EXCEL_URL,paramMap);
|
||||
MvcResult functionalCaseMvcResult = this.requestMultipartWithOkAndReturn(CHECK_EXCEL_URL, paramMap);
|
||||
|
||||
String functionalCaseImportResponseData = functionalCaseMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
ResultHolder functionalCaseResultHolder = JSON.parseObject(functionalCaseImportResponseData, ResultHolder.class);
|
||||
|
@ -579,7 +580,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
|||
|
||||
paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
this.requestMultipart(CHECK_EXCEL_URL,paramMap);
|
||||
this.requestMultipart(CHECK_EXCEL_URL, paramMap);
|
||||
|
||||
//覆盖异常
|
||||
String filePath2 = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/2.xlsx")).getPath();
|
||||
|
@ -587,6 +588,47 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
|||
paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("file", file2);
|
||||
this.requestMultipart(CHECK_EXCEL_URL,paramMap);
|
||||
this.requestMultipart(CHECK_EXCEL_URL, paramMap);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Order(19)
|
||||
public void testImportExcel() throws Exception {
|
||||
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/3.xlsx")).getPath();
|
||||
MockMultipartFile file = new MockMultipartFile("file", "11.xlsx", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath));
|
||||
FunctionalCaseImportRequest request = new FunctionalCaseImportRequest();
|
||||
request.setCover(true);
|
||||
request.setProjectId("100001100001");
|
||||
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("file", file);
|
||||
MvcResult functionalCaseMvcResult = this.requestMultipartWithOkAndReturn(IMPORT_EXCEL_URL, paramMap);
|
||||
|
||||
String functionalCaseImportResponseData = functionalCaseMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
ResultHolder functionalCaseResultHolder = JSON.parseObject(functionalCaseImportResponseData, ResultHolder.class);
|
||||
FunctionalCaseImportResponse functionalCaseImportResponse = JSON.parseObject(JSON.toJSONString(functionalCaseResultHolder.getData()), FunctionalCaseImportResponse.class);
|
||||
Assertions.assertNotNull(functionalCaseImportResponse);
|
||||
|
||||
|
||||
String filePath1 = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/4.xlsx")).getPath();
|
||||
MockMultipartFile file1 = new MockMultipartFile("file", "14.xlsx", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath1));
|
||||
paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("file", file1);
|
||||
this.requestMultipart(IMPORT_EXCEL_URL, paramMap);
|
||||
|
||||
|
||||
paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
this.requestMultipart(IMPORT_EXCEL_URL, paramMap);
|
||||
|
||||
//覆盖异常
|
||||
String filePath2 = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/2.xlsx")).getPath();
|
||||
MockMultipartFile file2 = new MockMultipartFile("file", "22.xlsx", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath2));
|
||||
paramMap = new LinkedMultiValueMap<>();
|
||||
paramMap.add("request", JSON.toJSONString(request));
|
||||
paramMap.add("file", file2);
|
||||
this.requestMultipart(IMPORT_EXCEL_URL, paramMap);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,3 +72,7 @@ VALUES ('wx_review_id',10006,'测试重新提审', 'test_module_one', 'TEST_MODU
|
|||
INSERT INTO project_version(id, project_id, name, description, status, latest, publish_time, start_time, end_time, create_time, create_user)
|
||||
VALUES ('v2.0.1', 'wx_relationship', 'v1.0', NULL, 'open', b'1', 1698810592000, 1698810592000, 1698810592000, 1698810592000, 'admin');
|
||||
|
||||
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_IMPORT', 10, 'TEST_MODULE_ID_GYQ', '100001100001', '100001', '1223', 'UN_REVIEWED', NULL, 'STEP', 0, 'v3.0.0', 'wx_ref_id', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
|
||||
|
||||
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_IMPORT', 'STEP', '1111', '', '', 'TEST');
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue