feat(测试跟踪): 导入功能用例支持合并单元格

This commit is contained in:
chenjianxing 2022-08-15 10:47:17 +08:00 committed by jianxing
parent 491c5276c5
commit 5a3fb68149
17 changed files with 821 additions and 815 deletions

View File

@ -0,0 +1,28 @@
package io.metersphere.excel.constants;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public enum TestCaseImportFiled {
ID("ID", "ID", "ID"), NAME("用例名称", "用例名稱", "Name"), MODULE("所属模块", "所屬模塊", "Module"),
TAG("标签", "標簽", "Tag"), PREREQUISITE("前置条件", "前置條件", "Prerequisite"), REMARK("备注", "備註", "Remark"),
STEP_DESC("步骤描述", "步驟描述", "Step description"), STEP_RESULT("预期结果", "預期結果", "Step result"), STEP_MODEL("编辑模式", "編輯模式", "Edit Model"),
STATUS("用例状态", "用例狀態", "Case status"), MAINTAINER("责任人", "責任人", "Maintainer"), PRIORITY("用例等级", "用例等級", "Priority");
private Map<Locale, String> filedLangMap;
public Map<Locale, String> getFiledLangMap() {
return this.filedLangMap;
}
TestCaseImportFiled(String zn, String chineseTw, String us) {
this.filedLangMap = new HashMap<>() {{
put(Locale.SIMPLIFIED_CHINESE, zn);
put(Locale.TRADITIONAL_CHINESE, chineseTw);
put(Locale.US, us);
}};
}
}

View File

@ -0,0 +1,38 @@
package io.metersphere.excel.domain;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
@Setter
@Getter
public class ExcelMergeInfo implements Comparable<ExcelMergeInfo> {
/**
* 合并单元格的第一行
*/
private Integer firstRowIndex;
/**
* 合并单元格的最后一行
*/
private Integer lastRowIndex;
/**
* 合并单元格的第一列不考虑同一行合并单元格的情况
*/
private Integer firstColumnIndex;
/**
* 根据 firstRowIndex, firstColumnIndex 重写 compareTo
* 使用 TreeSet Excel 表格顺序查找时可以优化效率
* @param o
* @return
*/
@Override
public int compareTo(@NotNull ExcelMergeInfo o) {
int compare = Integer.compare(this.getFirstRowIndex(), o.getFirstRowIndex());
if (compare == 0) {
return Integer.compare(this.getFirstColumnIndex(), o.getFirstColumnIndex());
}
return compare;
}
}

View File

@ -2,8 +2,11 @@ package io.metersphere.excel.domain;
import com.alibaba.excel.annotation.ExcelIgnore;
import io.metersphere.dto.CustomFieldDao;
import io.metersphere.excel.constants.TestCaseImportFiled;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
@ -47,7 +50,45 @@ public class TestCaseExcelData {
@ExcelIgnore
Map<String, String> customDatas = new LinkedHashMap<>();
@ExcelIgnore
List<String> mergeStepDesc;
@ExcelIgnore
List<String> mergeStepResult;
public List<List<String>> getHead(boolean needNum, List<CustomFieldDao> customFields) {
return new ArrayList<>();
}
public List<List<String>> getHead(boolean needNum, List<CustomFieldDao> customFields, Locale lang) {
List<List<String>> heads = new ArrayList<>();
TestCaseImportFiled[] fields = TestCaseImportFiled.values();
for (TestCaseImportFiled field : fields) {
heads.add(Arrays.asList(field.getFiledLangMap().get(lang)));
}
Iterator<List<String>> iterator = heads.iterator();
while (iterator.hasNext()) {
List<String> head = iterator.next();
if (StringUtils.equals(head.get(0), TestCaseImportFiled.ID.getFiledLangMap().get(lang)) && !needNum) {
iterator.remove();
break;
}
}
if (CollectionUtils.isNotEmpty(customFields)) {
for (CustomFieldDao dto : customFields) {
if (StringUtils.equalsAny(dto.getName(),
TestCaseImportFiled.PRIORITY.getFiledLangMap().get(Locale.SIMPLIFIED_CHINESE),
TestCaseImportFiled.STATUS.getFiledLangMap().get(Locale.SIMPLIFIED_CHINESE),
TestCaseImportFiled.MAINTAINER.getFiledLangMap().get(Locale.SIMPLIFIED_CHINESE).concat("(ID)"))) {
continue;
}
heads.add(new ArrayList<>() {{
add(dto.getName());
}});
}
}
return heads;
}
}

View File

@ -5,23 +5,17 @@ import com.alibaba.excel.annotation.write.style.ColumnWidth;
import io.metersphere.dto.CustomFieldDao;
import io.metersphere.excel.annotation.NotRequired;
import lombok.Data;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@Data
@ColumnWidth(15)
public class TestCaseExcelDataCn extends TestCaseExcelData {
// @ExcelProperty("ID")
// @NotRequired
// private Integer num;
@ColumnWidth(50)
@ExcelProperty("ID")
@NotRequired
@ -45,11 +39,6 @@ public class TestCaseExcelDataCn extends TestCaseExcelData {
@Length(min = 0, max = 1000)
private String tags;
// @NotBlank(message = "{cannot_be_null}")
// @ExcelProperty("测试方式")
// @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
// private String method;
@ColumnWidth(50)
@ExcelProperty("前置条件")
private String prerequisite;
@ -86,63 +75,6 @@ public class TestCaseExcelDataCn extends TestCaseExcelData {
@Override
public List<List<String>> getHead(boolean needNum, List<CustomFieldDao> customFields) {
List<List<String>> returnList = new ArrayList<>();
if (needNum) {
List<String> list = new ArrayList<>();
list.add("ID");
returnList.add(list);
}
List<String> list1 = new ArrayList<>();
list1.add("用例名称");
returnList.add(list1);
List<String> list2 = new ArrayList<>();
list2.add("所属模块");
returnList.add(list2);
List<String> list3 = new ArrayList<>();
list3.add("标签");
returnList.add(list3);
List<String> list4 = new ArrayList<>();
list4.add("前置条件");
returnList.add(list4);
List<String> list5 = new ArrayList<>();
list5.add("备注");
returnList.add(list5);
List<String> list6 = new ArrayList<>();
list6.add("步骤描述");
returnList.add(list6);
List<String> list7 = new ArrayList<>();
list7.add("预期结果");
returnList.add(list7);
List<String> list8 = new ArrayList<>();
list8.add("编辑模式");
returnList.add(list8);
List<String> list9 = new ArrayList<>();
list9.add("用例等级");
returnList.add(list9);
if (CollectionUtils.isNotEmpty(customFields)) {
for (CustomFieldDao dto : customFields) {
if (StringUtils.equals(dto.getName(), "用例等级")) {
continue;
}
List<String> list = new ArrayList<>();
if (StringUtils.equals(dto.getName(), "责任人")) {
list.add("责任人(ID)");
} else {
list.add(dto.getName());
}
returnList.add(list);
}
}
return returnList;
return super.getHead(needNum, customFields, Locale.SIMPLIFIED_CHINESE);
}
}

View File

@ -5,23 +5,17 @@ import com.alibaba.excel.annotation.write.style.ColumnWidth;
import io.metersphere.dto.CustomFieldDao;
import io.metersphere.excel.annotation.NotRequired;
import lombok.Data;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@Data
@ColumnWidth(15)
public class TestCaseExcelDataTw extends TestCaseExcelData {
// @ExcelProperty("ID")
// @NotRequired
// private Integer num;
@ColumnWidth(50)
@ExcelProperty("ID")
@NotRequired
@ -45,11 +39,6 @@ public class TestCaseExcelDataTw extends TestCaseExcelData {
@Length(min = 0, max = 1000)
private String tags;
// @NotBlank(message = "{cannot_be_null}")
// @ExcelProperty("測試方式")
// @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
// private String method;
@ColumnWidth(50)
@ExcelProperty("前置條件")
private String prerequisite;
@ -81,75 +70,11 @@ public class TestCaseExcelDataTw extends TestCaseExcelData {
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
private String priority;
@ExcelProperty("維護人(ID)")
@ExcelProperty("責任人(ID)")
private String maintainer;
@Override
public List<List<String>> getHead(boolean needNum, List<CustomFieldDao> customFields) {
List<List<String>> returnList = new ArrayList<>();
if (needNum) {
List<String> list = new ArrayList<>();
list.add("ID");
returnList.add(list);
}
List<String> list1 = new ArrayList<>();
list1.add("用例名稱");
returnList.add(list1);
List<String> list2 = new ArrayList<>();
list2.add("所屬模塊");
returnList.add(list2);
List<String> list3 = new ArrayList<>();
list3.add("標簽");
returnList.add(list3);
List<String> list4 = new ArrayList<>();
list4.add("前置條件");
returnList.add(list4);
List<String> list5 = new ArrayList<>();
list5.add("備註");
returnList.add(list5);
List<String> list6 = new ArrayList<>();
list6.add("步驟描述");
returnList.add(list6);
List<String> list7 = new ArrayList<>();
list7.add("預期結果");
returnList.add(list7);
List<String> list8 = new ArrayList<>();
list8.add("編輯模式");
returnList.add(list8);
List<String> list9 = new ArrayList<>();
list9.add("用例等級");
returnList.add(list9);
List<String> list10 = new ArrayList<>();
list10.add("用例狀態");
returnList.add(list10);
if (CollectionUtils.isNotEmpty(customFields)) {
for (CustomFieldDao dto : customFields) {
if (StringUtils.equals(dto.getName(), "用例等級")) {
continue;
}
if (StringUtils.equals(dto.getName(), "用例状态")) {
continue;
}
List<String> list = new ArrayList<>();
if (StringUtils.equals(dto.getName(), "责任人")) {
list.add("維護人(ID)");
} else {
list.add(dto.getName());
}
returnList.add(list);
}
}
return returnList;
return super.getHead(needNum, customFields, Locale.TRADITIONAL_CHINESE);
}
}

View File

@ -5,24 +5,18 @@ import com.alibaba.excel.annotation.write.style.ColumnWidth;
import io.metersphere.dto.CustomFieldDao;
import io.metersphere.excel.annotation.NotRequired;
import lombok.Data;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@Data
@ColumnWidth(15)
public class TestCaseExcelDataUs extends TestCaseExcelData {
// @ExcelProperty("ID")
// @NotRequired
// private Integer num;
@ColumnWidth(50)
@ExcelProperty("ID")
@NotRequired
@ -46,11 +40,6 @@ public class TestCaseExcelDataUs extends TestCaseExcelData {
@Length(min = 0, max = 1000)
private String tags;
// @NotBlank(message = "{cannot_be_null}")
// @ExcelProperty("Method")
// @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
// private String method;
@ColumnWidth(50)
@ExcelProperty("Prerequisite")
private String prerequisite;
@ -86,70 +75,6 @@ public class TestCaseExcelDataUs extends TestCaseExcelData {
@Override
public List<List<String>> getHead(boolean needNum, List<CustomFieldDao> customFields) {
List<List<String>> returnList = new ArrayList<>();
if (needNum) {
List<String> list = new ArrayList<>();
list.add("ID");
returnList.add(list);
}
List<String> list1 = new ArrayList<>();
list1.add("Name");
returnList.add(list1);
List<String> list2 = new ArrayList<>();
list2.add("Module");
returnList.add(list2);
List<String> list3 = new ArrayList<>();
list3.add("Tag");
returnList.add(list3);
List<String> list4 = new ArrayList<>();
list4.add("Prerequisite");
returnList.add(list4);
List<String> list5 = new ArrayList<>();
list5.add("Remark");
returnList.add(list5);
List<String> list6 = new ArrayList<>();
list6.add("Step description");
returnList.add(list6);
List<String> list7 = new ArrayList<>();
list7.add("Step result");
returnList.add(list7);
List<String> list8 = new ArrayList<>();
list8.add("Edit Model");
returnList.add(list8);
List<String> list9 = new ArrayList<>();
list9.add("Priority");
returnList.add(list9);
List<String> list10 = new ArrayList<>();
list10.add("Case status");
returnList.add(list10);
if (CollectionUtils.isNotEmpty(customFields)) {
for (CustomFieldDao dto : customFields) {
if (StringUtils.equals(dto.getName(), "用例等级")) {
continue;
}
if (StringUtils.equals(dto.getName(), "用例状态")) {
continue;
}
List<String> list = new ArrayList<>();
if (StringUtils.equals(dto.getName(), "责任人")) {
list.add("Maintainer(ID)");
} else {
list.add(dto.getName());
}
returnList.add(list);
}
}
return returnList;
return super.getHead(needNum, customFields, Locale.US);
}
}

View File

@ -0,0 +1,57 @@
package io.metersphere.excel.handler;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.handler.context.RowWriteHandlerContext;
import io.metersphere.excel.constants.TestCaseImportFiled;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.List;
import java.util.Map;
public class FunctionCaseMergeWriteHandler implements RowWriteHandler {
/**
* 存储需要合并单元格的信息key 是需要合并的第一条的行号值是需要合并多少行
*/
Map<Integer, Integer> rowMergeInfo;
List<List<String>> headList;
int stepDescRowIndex;
int stepResultRowIndex;
public FunctionCaseMergeWriteHandler(Map<Integer, Integer> rowMergeInfo, List<List<String>> headList) {
this.rowMergeInfo = rowMergeInfo;
this.headList = headList;
for (int i = 0; i < headList.size(); i++) {
List<String> list = headList.get(i);
for (String head : list) {
if (TestCaseImportFiled.STEP_DESC.getFiledLangMap().values().contains(head)) {
stepDescRowIndex = i;
} else if (TestCaseImportFiled.STEP_RESULT.getFiledLangMap().values().contains(head)) {
stepResultRowIndex = i;
}
}
}
}
@Override
public void afterRowDispose(RowWriteHandlerContext context) {
if (context.getHead() || context.getRelativeRowIndex() == null) {
return;
}
Integer mergeCount = rowMergeInfo.get(context.getRowIndex());
if (mergeCount == null || mergeCount <= 0) {
return;
}
for (int i = 0; i < headList.size(); i++) {
// 除了描述其他数据合并多行
if (i != stepDescRowIndex && i != stepResultRowIndex) {
CellRangeAddress cellRangeAddress =
new CellRangeAddress(context.getRowIndex(), context.getRowIndex() + mergeCount - 1, i, i);
context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(cellRangeAddress);
}
}
}
}

View File

@ -9,11 +9,16 @@ import io.metersphere.base.domain.TestCase;
import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.CustomFieldDao;
import io.metersphere.dto.CustomFieldOption;
import io.metersphere.excel.annotation.NotRequired;
import io.metersphere.excel.constants.TestCaseImportFiled;
import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.ExcelMergeInfo;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.domain.TestCaseExcelDataFactory;
import io.metersphere.excel.utils.ExcelValidateHelper;
@ -23,11 +28,11 @@ import io.metersphere.track.request.testcase.TestCaseImportRequest;
import io.metersphere.track.service.TestCaseService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -48,7 +53,6 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
private Map<Integer, String> headMap;
private Map<String, String> excelHeadToFieldNameDic = new HashMap<>();
/**
* 每隔2000条存储数据库然后清理list 方便内存回收
*/
@ -71,13 +75,36 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
private TestCaseImportRequest request;
private Set<ExcelMergeInfo> mergeInfoSet;
// 存储当前合并的一条完整数据其中步骤没有合并是多行
private TestCaseExcelData currentMergeData;
private static final String ERROR_MSG_SEPARATOR = ";";
/**
* 标记下当前遍历的行是不是有合并单元格
*/
private Boolean isMergeRow;
/**
* 标记下当前遍历的行是不是合并单元格的最后一行
*/
private Boolean isMergeLastRow;
/**
* 存储合并单元格对应的数据key 为重写了 compareTo ExcelMergeInfo
*/
private HashMap<ExcelMergeInfo, String> mergeCellDataMap = new HashMap<>();
public boolean isUpdated() {
return isUpdated;
}
public TestCaseNoModelDataListener(TestCaseImportRequest request, Class c) {
public TestCaseNoModelDataListener(TestCaseImportRequest request, Class c, Set<ExcelMergeInfo> mergeInfoSet) {
this.mergeInfoSet = mergeInfoSet;
this.excelDataClass = c;
this.testCaseService = (TestCaseService) CommonBeanFactory.getBean("testCaseService");
this.testCaseService = CommonBeanFactory.getBean(TestCaseService.class);
customIds = new HashSet<>();
this.request = request;
@ -89,11 +116,11 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
if (StringUtils.isNotEmpty(name)) {
name = name.trim();
}
if (StringUtils.equalsAny(name, "责任人", "維護人", "Maintainer")) {
if (TestCaseImportFiled.MAINTAINER.getFiledLangMap().values().contains(name)) {
customFieldsMap.put("maintainer", dto);
} else if (StringUtils.equalsAny(name, "用例等级", "用例等級", "Priority")) {
} else if (TestCaseImportFiled.PRIORITY.getFiledLangMap().values().contains(name)) {
customFieldsMap.put("priority", dto);
} else if (StringUtils.equalsAny(name, "用例状态", "用例狀態", "Case status")) {
} else if (TestCaseImportFiled.STATUS.getFiledLangMap().values().contains(name)) {
customFieldsMap.put("status", dto);
} else {
customFieldsMap.put(name, dto);
@ -115,98 +142,249 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
}
public void invoke(Map<Integer, String> data, AnalysisContext analysisContext) {
String errMsg;
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
String updateMsg = "update_testcase";
TestCaseExcelData testCaseExcelData = this.parseDataToModel(data);
handleMergeData(data, rowIndex);
TestCaseExcelData testCaseExcelData;
// 读取名称列如果该列是合并单元格则读取多行数据后合并步骤
if (this.isMergeRow) {
if (currentMergeData == null) {
// 如果是合并单元格的首行
testCaseExcelData = this.parseDataToModel(data);
testCaseExcelData.setMergeStepDesc(new ArrayList<>() {{
add(testCaseExcelData.getStepDesc());
}});
testCaseExcelData.setMergeStepResult(new ArrayList<>() {{
add(testCaseExcelData.getStepResult());
}});
// 记录下数据并返回
this.currentMergeData = testCaseExcelData;
return;
} else {
// 获取存储的数据并添加多个步骤
this.currentMergeData.getMergeStepDesc()
.add(data.get(getStepDescColIndex()));
this.currentMergeData.getMergeStepResult()
.add(data.get(getStepResultColIndex()));
// 是最后一行的合并单元格保存并清空 currentMergeData走之后的逻辑
if (this.isMergeLastRow) {
testCaseExcelData = this.currentMergeData;
this.currentMergeData = null;
} else {
return;
}
}
} else {
testCaseExcelData = this.parseDataToModel(data);
}
StringBuilder errMsg;
try {
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
errMsg = ExcelValidateHelper.validateEntity(testCaseExcelData);
errMsg = new StringBuilder(ExcelValidateHelper.validateEntity(testCaseExcelData));
//自定义校验规则
if (StringUtils.isEmpty(errMsg)) {
errMsg = validate(testCaseExcelData, errMsg);
validate(testCaseExcelData, errMsg);
}
} catch (NoSuchFieldException e) {
errMsg = Translator.get("parse_data_error");
errMsg = new StringBuilder(Translator.get("parse_data_error"));
LogUtil.error(e.getMessage(), e);
}
if (!StringUtils.isEmpty(errMsg)) {
//如果errMsg只有"update testcase"说明用例待更新
if (!errMsg.equals(updateMsg)) {
ExcelErrData excelErrData = new ExcelErrData(testCaseExcelData, rowIndex,
Translator.get("number") + " " + rowIndex + " " + Translator.get("row") + Translator.get("error")
+ "" + errMsg);
errList.add(excelErrData);
}
ExcelErrData excelErrData = new ExcelErrData(testCaseExcelData, rowIndex,
Translator.get("number")
.concat(" ")
.concat(rowIndex.toString()).concat(" ")
.concat(Translator.get("row"))
.concat(Translator.get("error"))
.concat("")
.concat(errMsg.toString()));
errList.add(excelErrData);
} else {
list.add(testCaseExcelData);
if (isCreateModel()) {
list.add(testCaseExcelData);
}
}
if (list.size() > BATCH_COUNT) {
if (list.size() > BATCH_COUNT || updateList.size() > BATCH_COUNT) {
saveData();
list.clear();
updateList.clear();
}
}
public String validate(TestCaseExcelData data, String errMsg) {
Set<String> savedCustomIds = request.getSavedCustomIds();
String importType = request.getImportType();
StringBuilder stringBuilder = new StringBuilder(errMsg);
if (request.isUseCustomId() || StringUtils.equals(importType, FunctionCaseImportEnum.Update.name())) {
if (data.getCustomNum() == null) {
stringBuilder.append(Translator.get("id_required") + ";");
} else {
String customId = data.getCustomNum();
if (StringUtils.isEmpty(customId)) {
stringBuilder.append(Translator.get("id_required") + ";");
} else if (customIds.contains(customId.toLowerCase())) {
stringBuilder.append(Translator.get("id_repeat_in_table") + ";");
} else if (StringUtils.equals(FunctionCaseImportEnum.Create.name(), importType) && savedCustomIds.contains(customId)) {
stringBuilder.append(Translator.get("custom_num_is_exist") + ";");
} else if (StringUtils.equals(FunctionCaseImportEnum.Update.name(), importType) && !savedCustomIds.contains(customId)) {
stringBuilder.append(Translator.get("custom_num_is_not_exist") + ";");
} else {
customIds.add(customId.toLowerCase());
/**
* 处理合并单元格
*
* @param data
* @param rowIndex
*/
private void handleMergeData(Map<Integer, String> data, Integer rowIndex) {
this.isMergeRow = false;
this.isMergeLastRow = false;
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)) {
this.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 == mergeInfo.getLastRowIndex()) {
// 根据名称列是否是合并单元格判断是不是同一条用例
if (getNameColIndex().equals(col)) {
this.isMergeLastRow = true;
}
}
}
} else if (rowIndex > mergeInfo.getLastRowIndex()) {
// TreeSet 按照行号排序了
// 清除掉上一次已经遍历完成的数据提高查询效率
iterator.remove();
}
}
}
});
}
String nodePath = data.getNodePath();
//校验所属模块"
if (nodePath != null) {
String[] nodes = nodePath.split("/");
//校验模块深度
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
stringBuilder.append(Translator.get("test_case_node_level_tip") +
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; ");
public void validate(TestCaseExcelData data, StringBuilder errMsg) {
validateCustomNum(data, errMsg);
validateModule(data, errMsg);
validateCustomField(data, errMsg);
validateIdExist(data, errMsg);
validateDbExist(data, errMsg);
}
private void validateDbExist(TestCaseExcelData data, StringBuilder stringBuilder) {
if (request.getTestCaseNames().contains(data.getName())) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(request.getProjectId());
String steps = getSteps(data);
testCase.setSteps(steps);
testCase.setType(TestCaseConstants.Type.Functional.getValue());
boolean dbExist = testCaseService.exist(testCase);
boolean excelExist = false;
if (dbExist) {
// db exist
stringBuilder.append(
Translator.get("test_case_already_exists")
.concat("")
.concat(data.getName())
.concat(ERROR_MSG_SEPARATOR));
} else {
// @Data 重写了 equals hashCode 方法
excelExist = excelDataList.contains(data);
}
//模块名不能为空
for (int i = 0; i < nodes.length; i++) {
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
stringBuilder.append(Translator.get("module_not_null") + "; ");
break;
if (excelExist) {
// excel exist
stringBuilder.append(
Translator.get("test_case_already_exists_excel")
.concat("")
.concat(data.getName())
.concat(ERROR_MSG_SEPARATOR));
} else {
if (!dbExist) {
excelDataList.add(data);
}
}
//增加字数校验每一层不能超过100字
for (int i = 0; i < nodes.length; i++) {
String nodeStr = nodes[i];
if (StringUtils.isNotEmpty(nodeStr)) {
if (nodeStr.trim().length() > 100) {
stringBuilder.append(Translator.get("module") + Translator.get("test_track.length_less_than") + "100:" + nodeStr);
break;
} else {
request.getTestCaseNames().add(data.getName());
excelDataList.add(data);
}
}
/**
* 校验Excel中是否有ID
* 有的话校验ID是否已在当前项目中存在存在则更新用例
* 不存在则继续校验看是否重复不重复则新建用例
*
* @param data
* @param stringBuilder
*/
@Nullable
private void validateIdExist(TestCaseExcelData data, StringBuilder stringBuilder) {
//当前读取的数据有ID
if (null != data.getCustomNum()) {
if (isUpdateModel()) {
String checkResult = null;
if (request.isUseCustomId()) {
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum(), request.getProjectId());
} else {
int customNumId = -1;
try {
customNumId = Integer.parseInt(data.getCustomNum());
} catch (Exception e) {
LogUtil.error(e);
}
if (customNumId < 0) {
stringBuilder.append(Translator.get("id_not_rightful"))
.append("[")
.append(data.getCustomNum())
.append("]; ");
} else {
checkResult = testCaseService.checkIdExist(customNumId, request.getProjectId());
}
}
//该ID在当前项目中存在
if (null != checkResult) {
//如果前面所经过的校验都没报错
if (StringUtils.isEmpty(stringBuilder)) {
data.setId(checkResult);
//将当前数据存入更新列表
updateList.add(data);
}
} else {
// 该ID在当前数据库中不存在应当继续校验用例是否重复,
// 在下面的校验过程中num的值会被用于判断是否重复所以应当先设置为null
data.setNum(null);
}
}
}
}
private boolean isUpdateModel() {
return StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Update.name());
}
private boolean isCreateModel() {
return StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Create.name());
}
private void validateCustomField(TestCaseExcelData data, StringBuilder stringBuilder) {
//校验自定义必填字段
for (Map.Entry<String, CustomFieldDao> customEntry : customFieldsMap.entrySet()) {
String customName = customEntry.getKey();
CustomFieldDao field = customEntry.getValue();
if (field.getRequired()) {
String value = null;
String value;
if (StringUtils.equals(customName, "status")) {
Map<String, String> valueMap = new HashMap<>() {{
put("Prepare", Translator.get("test_case_status_prepare"));
@ -221,11 +399,14 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
} else if (StringUtils.equals(customName, "maintainer")) {
value = data.getMaintainer();
//校验维护人
if (StringUtils.isBlank(data.getMaintainer())) {
if (StringUtils.isBlank(value)) {
data.setMaintainer(SessionUtils.getUserId());
} else {
if (!request.getUserIds().contains(data.getMaintainer())) {
stringBuilder.append(Translator.get("user_not_exists") + "" + data.getMaintainer() + "; ");
if (!request.getUserIds().contains(value)) {
stringBuilder.append(Translator.get("user_not_exists"))
.append( "")
.append(value)
.append(ERROR_MSG_SEPARATOR);
}
}
continue;
@ -233,93 +414,80 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
value = data.getCustomDatas().get(customName);
}
if (StringUtils.isEmpty(value)) {
stringBuilder.append(field.getName() + " " + Translator.get("required") + "; ");
stringBuilder.append(field.getName())
.append(" ")
.append(Translator.get("required"))
.append(ERROR_MSG_SEPARATOR);
}
}
}
}
/*
校验Excel中是否有ID
有的话校验ID是否已在当前项目中存在存在则更新用例
不存在则继续校验看是否重复不重复则新建用例
*/
if (null != data.getCustomNum()) { //当前读取的数据有ID
if (StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Update.name())) {
String checkResult = null;
if (request.isUseCustomId()) {
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum(), request.getProjectId());
} else {
int customNumId = -1;
try {
customNumId = Integer.parseInt(data.getCustomNum());
} catch (Exception e) {
}
if (customNumId < 0) {
stringBuilder.append(Translator.get("id_not_rightful") + "[" + data.getCustomNum() + "]; ");
} else {
checkResult = testCaseService.checkIdExist(customNumId, request.getProjectId());
}
}
if (null != checkResult) { //该ID在当前项目中存在
//如果前面所经过的校验都没报错
if (StringUtils.isEmpty(stringBuilder)) {
data.setId(checkResult);
updateList.add(data); //将当前数据存入更新列表
stringBuilder.append("update_testcase"); //该信息用于在invoke方法中判断是否该更新用例
}
return stringBuilder.toString();
} else {
/*
该ID在当前数据库中不存在应当继续校验用例是否重复,
在下面的校验过程中num的值会被用于判断是否重复所以应当先设置为null
*/
data.setNum(null);
private void validateModule(TestCaseExcelData data, StringBuilder stringBuilder) {
String nodePath = data.getNodePath();
//校验所属模块"
if (nodePath != null) {
String[] nodes = nodePath.split("/");
//校验模块深度
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
stringBuilder.append(Translator.get("test_case_node_level_tip"))
.append(TestCaseConstants.MAX_NODE_DEPTH)
.append(Translator.get("test_case_node_level"))
.append("; ");
}
//模块名不能为空
for (int i = 0; i < nodes.length; i++) {
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
stringBuilder.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) {
stringBuilder.append(Translator.get("module"))
.append(Translator.get("test_track.length_less_than"))
.append("100:")
.append(nodeStr);
break;
}
}
}
}
}
/*
校验用例
*/
if (request.getTestCaseNames().contains(data.getName())) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(request.getProjectId());
String steps = getSteps(data);
testCase.setSteps(steps);
testCase.setType("functional");
boolean dbExist = testCaseService.exist(testCase);
boolean excelExist = false;
if (dbExist) {
// db exist
stringBuilder.append(Translator.get("test_case_already_exists") + "" + data.getName() + "; ");
private void validateCustomNum(TestCaseExcelData data, StringBuilder stringBuilder) {
if (request.isUseCustomId() || isUpdateModel()) {
if (data.getCustomNum() == null) {
stringBuilder.append(Translator.get("id_required"))
.append(ERROR_MSG_SEPARATOR);
} else {
// @Data 重写了 equals hashCode 方法
excelExist = excelDataList.contains(data);
}
if (excelExist) {
// excel exist
stringBuilder.append(Translator.get("test_case_already_exists_excel") + "" + data.getName() + "; ");
} else {
if (!dbExist) {
excelDataList.add(data);
String customId = data.getCustomNum();
if (StringUtils.isEmpty(customId)) {
stringBuilder.append(Translator.get("id_required"))
.append(ERROR_MSG_SEPARATOR);
} else if (customIds.contains(customId.toLowerCase())) {
stringBuilder.append(Translator.get("id_repeat_in_table"))
.append(ERROR_MSG_SEPARATOR);
} else if (isCreateModel() && request.getSavedCustomIds().contains(customId)) {
stringBuilder.append(Translator.get("custom_num_is_exist"))
.append(ERROR_MSG_SEPARATOR);
} else if (isUpdateModel() && !request.getSavedCustomIds().contains(customId)) {
stringBuilder.append(Translator.get("custom_num_is_not_exist"))
.append(ERROR_MSG_SEPARATOR);
} else {
customIds.add(customId.toLowerCase());
}
}
} else {
request.getTestCaseNames().add(data.getName());
excelDataList.add(data);
}
return stringBuilder.toString();
}
/**
* 根据自定义字段的选项值获取对应的选项id
*
* @param name
* @param text
* @param optionsStr
@ -394,33 +562,22 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
}
/**
* 检验导入功能用例的状态
*
* @param status
* @return
*/
private boolean checkCaseStatus(String status) {
if (StringUtils.equalsAnyIgnoreCase(status, "Underway", "进行中", "進行中")) {
return true;
} else if (StringUtils.equalsAnyIgnoreCase(status, "Prepare", "未开始", "未開始")) {
return true;
} else if (StringUtils.equalsAnyIgnoreCase(status, "Completed", "已完成", "已完成")) {
return true;
}
return false;
}
private TestCaseWithBLOBs convert2TestCase(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
TestCaseWithBLOBs testCase = parseData(data);
testCase.setId(UUID.randomUUID().toString());
testCase.setProjectId(request.getProjectId());
testCase.setCreateTime(System.currentTimeMillis());
testCase.setUpdateTime(System.currentTimeMillis());
if (request.isUseCustomId()) {
testCase.setCustomNum(data.getCustomNum());
}
return testCase;
}
@NotNull
private TestCaseWithBLOBs parseData(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(request.getProjectId());
testCase.setUpdateTime(System.currentTimeMillis());
String nodePath = data.getNodePath();
@ -435,21 +592,10 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
//将标签设置为前端可解析的格式
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
testCase.setType("functional");
String caseStatusValue = "";
if (StringUtils.equalsAnyIgnoreCase(data.getStatus(), "Underway", "进行中", "進行中")) {
caseStatusValue = "Underway";
} else if (StringUtils.equalsAnyIgnoreCase(data.getStatus(), "Prepare", "未开始", "未開始")) {
caseStatusValue = "Prepare";
} else if (StringUtils.equalsAnyIgnoreCase(data.getStatus(), "Completed", "已完成", "已完成")) {
caseStatusValue = "Completed";
}
data.setStatus(caseStatusValue);
testCase.setType(TestCaseConstants.Type.Functional.getValue());
data.setStatus(data.getStatus());
// todo 这里要获取模板的自定义字段再新建关联关系
// String customFieldsJson = this.getCustomFieldsJson(data);
// testCase.setCustomFields(customFieldsJson);
if (StringUtils.isNotBlank(data.getMaintainer())) {
testCase.setMaintainer(data.getMaintainer());
}
@ -472,51 +618,12 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
* @return
*/
private TestCaseWithBLOBs convert2TestCaseForUpdate(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(request.getProjectId());
TestCaseWithBLOBs testCase = parseData(data);
testCase.setUpdateTime(System.currentTimeMillis());
//调整nodePath格式
String nodePath = data.getNodePath();
if (!nodePath.startsWith("/")) {
nodePath = "/" + nodePath;
}
if (nodePath.endsWith("/")) {
nodePath = nodePath.substring(0, nodePath.length() - 1);
}
testCase.setNodePath(nodePath);
String steps = getSteps(data);
testCase.setSteps(steps);
JSONArray customArr = new JSONArray();
String caseStatusValue = "";
if (StringUtils.equalsAnyIgnoreCase(data.getStatus(), "Underway", "进行中", "進行中")) {
caseStatusValue = "Underway";
} else if (StringUtils.equalsAnyIgnoreCase(data.getStatus(), "Prepare", "未开始", "未開始")) {
caseStatusValue = "Prepare";
} else if (StringUtils.equalsAnyIgnoreCase(data.getStatus(), "Completed", "已完成", "已完成")) {
caseStatusValue = "Completed";
}
data.setStatus(caseStatusValue);
// todo
// String customFieldsJson = this.getCustomFieldsJson(data);
// testCase.setCustomFields(customFieldsJson);
if (StringUtils.isNotBlank(data.getMaintainer())) {
testCase.setMaintainer(data.getMaintainer());
}
//将标签设置为前端可解析的格式
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
if (!request.isUseCustomId()) {
testCase.setNum(Integer.parseInt(data.getCustomNum()));
testCase.setCustomNum(null);
}
return testCase;
}
@ -549,9 +656,21 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
public String getSteps(TestCaseExcelData data) {
JSONArray jsonArray = new JSONArray();
// 如果是合并单元格则组合多条单元格的数据
if (CollectionUtils.isNotEmpty(data.getMergeStepDesc())
|| CollectionUtils.isNotEmpty(data.getMergeStepResult())) {
for (int i = 0; i < data.getMergeStepDesc().size(); i++) {
JSONObject step = new JSONObject(true);
step.put("num", i + 1);
step.put("desc", Optional.ofNullable(data.getMergeStepDesc().get(i)).orElse(""));
step.put("result", Optional.ofNullable(data.getMergeStepResult().get(i)).orElse(""));
jsonArray.add(step);
}
return jsonArray.toJSONString();
}
List<String> stepDescList = new ArrayList<>();
List<String> stepResList = new ArrayList<>();
ListUtils<String> listUtils = new ListUtils<>();
Set<Integer> rowNums = new HashSet<>();
if (data.getStepDesc() != null) {
@ -643,6 +762,27 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
return rowInfo;
}
private Integer getNameColIndex() {
return findColIndex("name");
}
private Integer getStepResultColIndex() {
return findColIndex("stepResult");
}
private Integer getStepDescColIndex() {
return findColIndex("stepDesc");
}
private Integer findColIndex(String colName) {
for (Integer key : headMap.keySet()) {
if (StringUtils.equals(headMap.get(key), colName)) {
return key;
}
}
return null;
}
private TestCaseExcelData parseDataToModel(Map<Integer, String> row) {
TestCaseExcelData data = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal();
for (Map.Entry<Integer, String> headEntry : headMap.entrySet()) {
@ -712,64 +852,6 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
}
}
private String getCustomFieldsJson(TestCaseExcelData data) {
JSONArray customArr = new JSONArray();
for (Map.Entry<String, CustomFieldDao> customEntry : customFieldsMap.entrySet()) {
String customName = customEntry.getKey();
CustomFieldDao field = customEntry.getValue();
String value = null;
if (StringUtils.equals(customName, "status")) {
value = data.getStatus();
} else if (StringUtils.equals(customName, "priority")) {
value = data.getPriority();
} else if (StringUtils.equals(customName, "maintainer")) {
value = data.getMaintainer();
} else {
value = data.getCustomDatas().get(customName);
}
if (StringUtils.isEmpty(value)) {
value = "";
}
if (field.getType().equalsIgnoreCase("multipleSelect")) {
value = modifyMultipleSelectPattern(value);
}
JSONObject statusObj = new JSONObject();
statusObj.put("id", UUID.randomUUID().toString());
statusObj.put("name", field.getName());
statusObj.put("value", value);
statusObj.put("customData", null);
statusObj.put("type", field.getType());
customArr.add(statusObj);
}
return customArr.toJSONString();
}
/**
* 调整自定义多选下拉框格式便于前端进行解析
* 例如对于下拉值1下拉值2将调整为:["下拉值1","下拉值2"]
*/
public String modifyMultipleSelectPattern(String values) {
try {
if (StringUtils.isNotBlank(values)) {
JSONArray array = JSONArray.parseArray(values);
return array.toJSONString();
}
return "[]";
} catch (Exception e) {
if (values != null) {
Stream<String> stringStream = Arrays.stream(values.split("[,;]")); //当标签值以中英文的逗号和分号分隔时才能正确解析
List<String> valueList = stringStream.map(multip -> multip = "\"" + multip + "\"")
.collect(Collectors.toList());
String modifiedValues = StringUtils.join(valueList, ",");
modifiedValues = "[" + modifiedValues + "]";
return modifiedValues;
} else {
return "[]";
}
}
}
/**
* @description: 获取注解里ExcelProperty的value
*/
@ -801,13 +883,4 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
public int index;
public String rowInfo;
}
public static boolean isNumericzidai(String str) {
Pattern pattern = Pattern.compile("-?[0-9]+(\\.[0-9]+)?");
Matcher isNum = pattern.matcher(str);
if (!isNum.matches()) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,40 @@
package io.metersphere.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellExtra;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.excel.domain.ExcelMergeInfo;
import java.util.Set;
/**
* 数据预处理读取合并的单元格信息
*/
public class TestCasePretreatmentListener extends AnalysisEventListener {
Set<ExcelMergeInfo> mergeInfoSet;
public TestCasePretreatmentListener(Set<ExcelMergeInfo> mergeInfoSet) {
this.mergeInfoSet = mergeInfoSet;
}
@Override
public void invoke(Object integerStringMap, AnalysisContext analysisContext) {}
@Override
public void extra(CellExtra extra, AnalysisContext context) {
if (extra.getType() == CellExtraTypeEnum.MERGE) {
// 将合并单元格信息保留
ExcelMergeInfo mergeInfo = new ExcelMergeInfo();
BeanUtils.copyBean(mergeInfo, extra);
this.mergeInfoSet.add(mergeInfo);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {}
}

View File

@ -4,17 +4,15 @@ import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.exception.ExcelException;
import org.apache.commons.collections.CollectionUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Optional;
public class EasyExcelExporter {
@ -25,58 +23,74 @@ public class EasyExcelExporter {
}
public void export(HttpServletResponse response, List data, String fileName, String sheetName) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
buildExportResponse(response, fileName);
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setWrapped(true);
try {
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle);
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
EasyExcel.write(response.getOutputStream(), this.clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(data);
} catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("Utf-8 encoding is not supported");
EasyExcel.write(response.getOutputStream(), this.clazz)
.registerWriteHandler(horizontalCellStyleStrategy)
.sheet(sheetName)
.doWrite(data);
} catch (IOException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("IO exception");
LogUtil.error(e);
MSException.throwException(e.getMessage());
}
}
public void exportByCustomWriteHandler(HttpServletResponse response, List<List<String>> headList, List<List<Object>> data, String fileName, String sheetName) {
if(CollectionUtils.isEmpty(headList)){
headList = new ArrayList<>();
}
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
public void exportByCustomWriteHandler(HttpServletResponse response, List<List<String>> headList,
List<List<Object>> data, String fileName, String sheetName) {
buildExportResponse(response, fileName);
try {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
EasyExcel.write(response.getOutputStream()).head(headList).
sheet(sheetName).doWrite(data);
} catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("Utf-8 encoding is not supported");
EasyExcel.write(response.getOutputStream())
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
.sheet(sheetName)
.doWrite(data);
} catch (IOException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("IO exception");
LogUtil.error(e);
MSException.throwException(e.getMessage());
}
}
public void exportByCustomWriteHandler(HttpServletResponse response, List<List<String>> headList, List<List<Object>> data, String fileName, String sheetName, WriteHandler writeHandler) {
if(CollectionUtils.isEmpty(headList)){
headList = new ArrayList<>();
}
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
public void buildExportResponse(HttpServletResponse response, String fileName) {
try {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
EasyExcel.write(response.getOutputStream()).head(headList).registerWriteHandler(writeHandler).
sheet(sheetName).doWrite(data);
} catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("Utf-8 encoding is not supported");
} catch (IOException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("IO exception");
LogUtil.error(e);
MSException.throwException(e.getMessage());
}
}
public void exportByCustomWriteHandler(HttpServletResponse response, List<List<String>> headList, List<List<Object>> data,
String fileName, String sheetName, WriteHandler writeHandler) {
buildExportResponse(response, fileName);
try {
EasyExcel.write(response.getOutputStream())
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
.registerWriteHandler(writeHandler)
.sheet(sheetName)
.doWrite(data);
} catch (IOException e) {
LogUtil.error(e);
MSException.throwException(e.getMessage());
}
}
public void exportByCustomWriteHandler(HttpServletResponse response, List<List<String>> headList, List<List<Object>> data,
String fileName, String sheetName, WriteHandler writeHandler1, WriteHandler writeHandler2) {
buildExportResponse(response, fileName);
try {
EasyExcel.write(response.getOutputStream())
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
.registerWriteHandler(writeHandler1)
.registerWriteHandler(writeHandler2)
.sheet(sheetName)
.doWrite(data);
} catch (IOException e) {
LogUtil.error(e);
MSException.throwException(e.getMessage());
}
}

View File

@ -107,7 +107,7 @@ public class TestCaseController {
public List<TestCaseDTO> listByMethod(@PathVariable String projectId) {
QueryTestCaseRequest request = new QueryTestCaseRequest();
request.setProjectId(projectId);
return testCaseService.listTestCaseMthod(request);
return testCaseService.listTestCaseMethod(request);
}
@GetMapping("/relationship/case/{id}/{relationshipType}")

View File

@ -15,7 +15,6 @@ public class TestCaseDTO extends TestCaseWithBLOBs {
private String maintainerName;
private String apiName;
private String performName;
private String lastResultId;
private String projectName;
private String createName;

View File

@ -1,7 +1,9 @@
package io.metersphere.track.service;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
@ -29,12 +31,11 @@ import io.metersphere.controller.request.ProjectVersionRequest;
import io.metersphere.controller.request.ResetOrderRequest;
import io.metersphere.controller.request.member.QueryMemberRequest;
import io.metersphere.dto.*;
import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.domain.TestCaseExcelDataFactory;
import io.metersphere.excel.domain.*;
import io.metersphere.excel.handler.FunctionCaseMergeWriteHandler;
import io.metersphere.excel.handler.FunctionCaseTemplateWriteHandler;
import io.metersphere.excel.listener.TestCaseNoModelDataListener;
import io.metersphere.excel.listener.TestCasePretreatmentListener;
import io.metersphere.excel.utils.EasyExcelExporter;
import io.metersphere.excel.utils.FunctionCaseImportEnum;
import io.metersphere.i18n.Translator;
@ -187,6 +188,9 @@ public class TestCaseService {
private FileContentMapper fileContentMapper;
@Resource
private AttachmentService attachmentService;
@Resource
@Lazy
private TestCaseTemplateService testCaseTemplateService;
private ThreadLocal<Integer> importCreateNum = new ThreadLocal<>();
private ThreadLocal<Integer> beforeImportCreateNum = new ThreadLocal<>();
@ -814,7 +818,7 @@ public class TestCaseService {
}
}
public List<TestCaseDTO> listTestCaseMthod(QueryTestCaseRequest request) {
public List<TestCaseDTO> listTestCaseMethod(QueryTestCaseRequest request) {
return extTestCaseMapper.listByMethod(request);
}
@ -986,7 +990,6 @@ public class TestCaseService {
testCaseNodeService.createNodes(xmindParser.getNodePaths(), projectId);
}
if (CollectionUtils.isNotEmpty(xmindParser.getTestCase())) {
// Collections.reverse(xmindParser.getTestCase());
this.saveImportData(xmindParser.getTestCase(), request);
names = xmindParser.getTestCase().stream().map(TestCase::getName).collect(Collectors.toList());
ids = xmindParser.getTestCase().stream().map(TestCase::getId).collect(Collectors.toList());
@ -1013,7 +1016,6 @@ public class TestCaseService {
testCaseNodeService.createNodes(nodePathList, projectId);
}
if (CollectionUtils.isNotEmpty(continueCaseList)) {
// Collections.reverse(continueCaseList);
this.saveImportData(continueCaseList, request);
names.addAll(continueCaseList.stream().map(TestCase::getName).collect(Collectors.toList()));
ids.addAll(continueCaseList.stream().map(TestCase::getId).collect(Collectors.toList()));
@ -1037,7 +1039,7 @@ public class TestCaseService {
String projectId = request.getProjectId();
Set<String> userIds;
Project project = projectService.getProjectById(projectId);
boolean useCunstomId = projectService.useCustomNum(project);
boolean useCustomId = projectService.useCustomNum(project);
Set<String> savedIds = new HashSet<>();
Set<String> testCaseNames = new HashSet<>();
@ -1046,7 +1048,7 @@ public class TestCaseService {
List<TestCase> testCases = getTestCaseForImport(projectId);
for (TestCase testCase : testCases) {
if (useCunstomId) {
if (useCustomId) {
savedIds.add(testCase.getCustomNum());
} else {
savedIds.add(String.valueOf(testCase.getNum()));
@ -1077,8 +1079,14 @@ public class TestCaseService {
request.setTestCaseNames(testCaseNames);
request.setCustomFields(customFields);
request.setSavedCustomIds(savedIds);
request.setUseCustomId(useCunstomId);
TestCaseNoModelDataListener easyExcelListener = new TestCaseNoModelDataListener(request, clazz);
request.setUseCustomId(useCustomId);
Set<ExcelMergeInfo> mergeInfoSet = new TreeSet<>();
// 预处理查询合并单元格信息
EasyExcel.read(multipartFile.getInputStream(), null, new TestCasePretreatmentListener(mergeInfoSet))
.extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();
TestCaseNoModelDataListener easyExcelListener = new TestCaseNoModelDataListener(request, clazz, mergeInfoSet);
//读取excel数据
EasyExcelFactory.read(multipartFile.getInputStream(), easyExcelListener).sheet().doRead();
@ -1250,40 +1258,6 @@ public class TestCaseService {
}
}
public void testCaseTemplateExport(String projectId, String importType, HttpServletResponse response) {
try {
TestCaseExcelData testCaseExcelData = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal();
boolean useCustomNum = projectService.useCustomNum(projectId);
boolean importFileNeedNum = false;
if (useCustomNum || StringUtils.equals(importType, FunctionCaseImportEnum.Update.name())) {
//导入更新 or 开启使用自定义ID时导出ID列
importFileNeedNum = true;
}
TestCaseTemplateService testCaseTemplateService = CommonBeanFactory.getBean(TestCaseTemplateService.class);
TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(projectId);
List<CustomFieldDao> customFields = null;
if (testCaseTemplate == null) {
customFields = new ArrayList<>();
} else {
customFields = testCaseTemplate.getCustomFields();
}
List<List<String>> headList = testCaseExcelData.getHead(importFileNeedNum, customFields);
EasyExcelExporter easyExcelExporter = new EasyExcelExporter(testCaseExcelData.getClass());
Map<String, List<String>> caseLevelAndStatusValueMap = testCaseTemplateService.getCaseLevelAndStatusMapByProjectId(projectId);
FunctionCaseTemplateWriteHandler handler = new FunctionCaseTemplateWriteHandler(importFileNeedNum, headList, caseLevelAndStatusValueMap);
easyExcelExporter.exportByCustomWriteHandler(response, headList, generateExportDatas(importFileNeedNum),
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"), handler);
} catch (Exception e) {
LogUtil.error(e);
MSException.throwException(e);
}
}
public void download(String fileName, HttpServletResponse res) throws IOException {
if (StringUtils.isEmpty(fileName)) {
fileName = "xmind.xml";
@ -1326,75 +1300,64 @@ public class TestCaseService {
}
}
private List<List<Object>> generateExportDatas(boolean needCustomId) {
List<List<Object>> list = new ArrayList<>();
StringBuilder path = new StringBuilder("");
List<String> types = TestCaseConstants.Type.getValues();
SessionUser user = SessionUtils.getUser();
for (int i = 1; i <= 5; i++) {
List<Object> rowData = new ArrayList<>();
if (needCustomId) {
rowData.add("");
private List<TestCaseDTO> generateExportData(String projectId) {
List<TestCaseDTO> list = new ArrayList<>();
StringBuilder path = new StringBuilder();
for (int i = 1; i <= 4; i++) {
path.append("/" + Translator.get("module") + i);
TestCaseDTO testCaseDTO = new TestCaseDTO();
testCaseDTO.setCustomNum("");
testCaseDTO.setName(Translator.get("test_case") + i);
testCaseDTO.setNodePath(path.toString());
testCaseDTO.setPriority("P" + i % 4);
testCaseDTO.setRemark(Translator.get("remark_optional"));
testCaseDTO.setPrerequisite(Translator.get("preconditions_optional"));
JSONArray steps = new JSONArray();
for (int j = 1; j <= 2; j++) {
JSONObject stepItem = new JSONObject(true);
stepItem.put("desc", Translator.get("test_case_step_desc") + j);
stepItem.put("result", Translator.get("test_case_step_result") + j);
steps.add(stepItem);
}
rowData.add(Translator.get("test_case") + i);
path.append("/" + Translator.get("module") + i);
rowData.add(path.toString());
rowData.add("");
rowData.add(Translator.get("preconditions_optional"));
rowData.add(Translator.get("remark_optional"));
rowData.add("1. " + Translator.get("step_tip_separate") + "\n2. " + Translator.get("step_tip_order") + "\n3. " + Translator.get("step_tip_optional"));
rowData.add("1. " + Translator.get("result_tip_separate") + "\n2. " + Translator.get("result_tip_order") + "\n3. " + Translator.get("result_tip_optional"));
rowData.add("");
rowData.add("P" + i % 4);
list.add(rowData);
testCaseDTO.setSteps(steps.toJSONString());
testCaseDTO.setProjectId(projectId);
list.add(testCaseDTO);
}
return list;
}
private List<TestCaseExcelData> generateExportTemplate() {
List<TestCaseExcelData> list = new ArrayList<>();
StringBuilder path = new StringBuilder("");
List<String> types = TestCaseConstants.Type.getValues();
SessionUser user = SessionUtils.getUser();
TestCaseExcelDataFactory factory = new TestCaseExcelDataFactory();
for (int i = 1; i <= 5; i++) {
TestCaseExcelData data = factory.getTestCaseExcelDataLocal();
data.setName(Translator.get("test_case") + i);
path.append("/" + Translator.get("module") + i);
data.setNodePath(path.toString());
data.setPriority("P" + i % 4);
String type = types.get(i % 3);
data.setPrerequisite(Translator.get("preconditions_optional"));
data.setStepDesc("1. " + Translator.get("step_tip_separate") +
"\n2. " + Translator.get("step_tip_order") + "\n3. " + Translator.get("step_tip_optional"));
data.setStepResult("1. " + Translator.get("result_tip_separate") + "\n2. " + Translator.get("result_tip_order") + "\n3. " + Translator.get("result_tip_optional"));
data.setMaintainer(user.getId());
data.setRemark(Translator.get("remark_optional"));
list.add(data);
}
list.add(new TestCaseExcelData());
return list;
}
public void testCaseExport(HttpServletResponse response, TestCaseBatchRequest request) {
try {
String projectId = request.getProjectId();
request.getCondition().setStatusIsNot(CommonConstants.TrashStatus);
List<TestCaseDTO> testCaseList = getExportData(request);
testCaseExport(response, projectId, testCaseList, true);
}
TestCaseExcelData testCaseExcelData = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal();
TestCaseTemplateService testCaseTemplateService = CommonBeanFactory.getBean(TestCaseTemplateService.class);
TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(request.getProjectId());
List<CustomFieldDao> customFields = Optional.ofNullable(testCaseTemplate.getCustomFields()).orElse(new ArrayList<>());
public void testCaseExport(HttpServletResponse response, String projectId, List<TestCaseDTO> testCaseList, boolean needIdCol) {
TestCaseExcelData testCaseExcelData = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal();
Map<Integer, Integer> rowMergeInfo = new HashMap<>();
TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(projectId);
List<CustomFieldDao> customFields = Optional.ofNullable(testCaseTemplate.getCustomFields()).orElse(new ArrayList<>());
List<List<String>> headList = testCaseExcelData.getHead(needIdCol, customFields);
FunctionCaseMergeWriteHandler writeHandler = new FunctionCaseMergeWriteHandler(rowMergeInfo, headList);
boolean isUseCustomId = projectService.useCustomNum(projectId);
List<List<String>> headList = testCaseExcelData.getHead(true, customFields);
List<List<Object>> testCaseDataByExcelList = this.generateTestCaseExcel(headList, generateTestCaseExcel(request));
EasyExcelExporter easyExcelExporter = new EasyExcelExporter(testCaseExcelData.getClass());
easyExcelExporter.exportByCustomWriteHandler(response, headList, testCaseDataByExcelList,
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"));
Map<String, List<String>> caseLevelAndStatusValueMap = testCaseTemplateService.getCaseLevelAndStatusMapByProjectId(projectId);
FunctionCaseTemplateWriteHandler handler = new FunctionCaseTemplateWriteHandler(needIdCol, headList, caseLevelAndStatusValueMap);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException(e);
}
List<TestCaseExcelData> excelData = parseCaseData2ExcelData(testCaseList, rowMergeInfo, isUseCustomId);
List<List<Object>> data = parseExcelData2List(headList, excelData);
new EasyExcelExporter(testCaseExcelData.getClass())
.exportByCustomWriteHandler(response, headList, data, Translator.get("test_case_import_template_name"),
Translator.get("test_case_import_template_sheet"), handler, writeHandler);
}
public void testCaseTemplateExport(String projectId, String importType, HttpServletResponse response) {
//导入更新 or 开启使用自定义ID时导出ID列
boolean needIdCol = projectService.useCustomNum(projectId) || StringUtils.equals(importType, FunctionCaseImportEnum.Update.name());
testCaseExport(response, projectId, generateExportData(projectId), needIdCol);
}
@ -1446,8 +1409,8 @@ public class TestCaseService {
return rootMind;
}
private List<List<Object>> generateTestCaseExcel(List<List<String>> headListParams, List<TestCaseExcelData> datas) {
List<List<Object>> returnDatas = new ArrayList<>();
private List<List<Object>> parseExcelData2List(List<List<String>> headListParams, List<TestCaseExcelData> data) {
List<List<Object>> result = new ArrayList<>();
//转化excel头
List<String> headList = new ArrayList<>();
for (List<String> list : headListParams) {
@ -1456,17 +1419,17 @@ public class TestCaseService {
}
}
for (TestCaseExcelData model : datas) {
List<Object> list = new ArrayList<>();
for (TestCaseExcelData model : data) {
List<Object> fields = new ArrayList<>();
Map<String, String> customDataMaps = Optional.ofNullable(model.getCustomDatas()).orElse(new HashMap<>());
for (String head : headList) {
if (StringUtils.equalsAnyIgnoreCase(head, "ID")) {
list.add(model.getCustomNum());
fields.add(model.getCustomNum());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Name", "用例名稱", "用例名称")) {
list.add(model.getName());
fields.add(model.getName());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Module", "所屬模塊", "所属模块")) {
list.add(model.getNodePath());
fields.add(model.getNodePath());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Tag", "標簽", "标签")) {
String tags = "";
try {
@ -1478,32 +1441,32 @@ public class TestCaseService {
}
} catch (Exception e) {
}
list.add(tags);
fields.add(tags);
} else if (StringUtils.equalsAnyIgnoreCase(head, "Prerequisite", "前置條件", "前置条件")) {
list.add(model.getPrerequisite());
fields.add(model.getPrerequisite());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Remark", "備註", "备注")) {
list.add(model.getRemark());
fields.add(model.getRemark());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Step description", "步驟描述", "步骤描述")) {
list.add(model.getStepDesc());
fields.add(model.getStepDesc());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Step result", "預期結果", "预期结果")) {
list.add(model.getStepResult());
fields.add(model.getStepResult());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Edit Model", "編輯模式", "编辑模式")) {
list.add(model.getStepModel());
fields.add(model.getStepModel());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Priority", "用例等級", "用例等级")) {
list.add(model.getPriority());
fields.add(model.getPriority());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Case status", "用例状态", "用例狀態")) {
list.add(model.getStatus());
fields.add(model.getStatus());
} else if (StringUtils.equalsAnyIgnoreCase(head, "Maintainer(ID)", "责任人(ID)", "維護人(ID)")) {
list.add(model.getMaintainer());
fields.add(model.getMaintainer());
} else {
String value = Optional.ofNullable(customDataMaps.get(head)).orElse("");
list.add(value);
fields.add(value);
}
}
returnDatas.add(list);
result.add(fields);
}
return returnDatas;
return result;
}
public List<TestCaseDTO> findByBatchRequest(TestCaseBatchRequest request) {
@ -1526,18 +1489,16 @@ public class TestCaseService {
return testCaseList;
}
private List<TestCaseExcelData> generateTestCaseExcel(TestCaseBatchRequest request) {
request.getCondition().setStatusIsNot("Trash");
List<TestCaseDTO> testCaseList = this.getExportData(request);
boolean isUseCustomId = projectService.useCustomNum(request.getProjectId());
private List<TestCaseExcelData> parseCaseData2ExcelData(List<TestCaseDTO> testCaseList,
Map<Integer, Integer> rowMergeInfo, Boolean isUseCustomId) {
if (CollectionUtils.isEmpty(testCaseList)) {
return new ArrayList<>();
}
List<TestCaseExcelData> list = new ArrayList<>();
StringBuilder step = new StringBuilder();
StringBuilder result = new StringBuilder();
Map<String, Map<String, String>> customSelectValueMap = new HashMap<>();
Map<String, String> customNameMap = new HashMap<>();
TestCaseTemplateService testCaseTemplateService = CommonBeanFactory.getBean(TestCaseTemplateService.class);
TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(request.getProjectId());
TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(testCaseList.get(0).getProjectId());
List<CustomFieldDao> customFieldList;
if (testCaseTemplate == null) {
@ -1545,6 +1506,113 @@ public class TestCaseService {
} else {
customFieldList = testCaseTemplate.getCustomFields();
}
buildExportCustomFieldMap(customSelectValueMap, customNameMap, customFieldList);
for (int rowIndex = 0; rowIndex < testCaseList.size(); rowIndex++) {
TestCaseDTO t = testCaseList.get(rowIndex);
List<String> stepDescList = new ArrayList<>();
List<String> stepResultList = new ArrayList<>();
setExportSystemField(t, customNameMap, customSelectValueMap);
TestCaseExcelData data = new TestCaseExcelData();
BeanUtils.copyBean(data, t);
if (isUseCustomId) {
data.setCustomNum(t.getCustomNum());
} else {
if (t.getNum() == null) {
data.setCustomNum("");
} else {
data.setCustomNum(String.valueOf(t.getNum()));
}
}
buildExportStep(t, stepDescList, stepResultList, data);
buildExportCustomField(customSelectValueMap, customNameMap, t, data);
if (CollectionUtils.isNotEmpty(stepDescList)) {
// 如果有多条步骤则添加多条数据之后合并单元格
for (int i = 0; i < stepDescList.size(); i++) {
TestCaseExcelData excelData;
if (i == 0) {
// 第一行存全量元素
excelData = data;
if (stepDescList.size() > 1) {
// 保存合并单元格的下标和数量
rowMergeInfo.put(list.size() + 1, stepDescList.size());
}
} else {
// 之后的行只存步骤
excelData = new TestCaseExcelData();
}
excelData.setStepDesc(stepDescList.get(i));
excelData.setStepResult(stepResultList.get(i));
list.add(excelData);
}
} else {
list.add(data);
}
}
return list;
}
private void buildExportCustomField(Map<String, Map<String, String>> customSelectValueMap, Map<String, String> customNameMap, TestCaseDTO t, TestCaseExcelData data) {
try {
List<CustomFieldResource> fields = customFieldTestCaseService.getByResourceId(t.getId());
Map<String, String> map = new HashMap<>();
for (int index = 0; index < fields.size(); index++) {
CustomFieldResource field = fields.get(index);
//进行key value对换
String id = field.getFieldId();
if (StringUtils.isNotBlank(field.getValue())) {
String value = JSONObject.parse(field.getValue()).toString();
if (customSelectValueMap.containsKey(id)
&& customSelectValueMap.get(id).containsKey(value)) {
value = customSelectValueMap.get(id).get(value);
}
map.put(customNameMap.get(id), value);
}
}
data.setCustomDatas(map);
} catch (Exception e) {
LogUtil.error(e);
}
}
private void buildExportStep(TestCaseDTO t, List<String> stepDescList, List<String> stepResultList, TestCaseExcelData data) {
if (StringUtils.isBlank(t.getStepModel())) {
data.setStepModel(TestCaseConstants.StepModel.STEP.name());
} else {
data.setStepModel(t.getStepModel());
}
if (StringUtils.equals(data.getStepModel(), TestCaseConstants.StepModel.TEXT.name())) {
data.setStepDesc(t.getStepDescription());
data.setStepResult(t.getExpectedResult());
} else {
String steps = t.getSteps();
JSONArray jsonArray = new JSONArray();
//解决旧版本保存用例导出报错
try {
jsonArray = JSON.parseArray(steps);
} catch (Exception e) {
if (steps.contains("null") && !steps.contains("\"null\"")) {
steps = steps.replace("null", "\"\"");
jsonArray = JSON.parseArray(steps);
}
}
for (int j = 0; j < jsonArray.size(); j++) {
// 将步骤存储起来之后生成多条数据再合并单元格
JSONObject item = jsonArray.getJSONObject(j );
String stepDesc = item.getString("desc");
String stepResult = item.getString("result");
if (StringUtils.isNotBlank(stepDesc) || StringUtils.isNotBlank(stepResult)) {
stepDescList.add(Optional.ofNullable(stepDesc).orElse(""));
stepResultList.add(Optional.ofNullable(stepResult).orElse(""));
}
}
}
}
private void buildExportCustomFieldMap(Map<String, Map<String, String>> customSelectValueMap, Map<String, String> customNameMap, List<CustomFieldDao> customFieldList) {
for (CustomFieldDao dto : customFieldList) {
Map<String, String> map = new HashMap<>();
if (StringUtils.equals("select", dto.getType())) {
@ -1573,119 +1641,9 @@ public class TestCaseService {
customSelectValueMap.put(dto.getId(), map);
customNameMap.put(dto.getId(), dto.getName());
}
testCaseList.forEach(t -> {
setExportSystemField(t, customNameMap, customSelectValueMap);
TestCaseExcelData data = new TestCaseExcelData();
data.setNum(t.getNum());
data.setName(t.getName());
data.setNodePath(t.getNodePath());
data.setPriority(t.getPriority());
if (isUseCustomId) {
data.setCustomNum(t.getCustomNum());
} else {
data.setCustomNum(String.valueOf(t.getNum()));
}
if (StringUtils.isBlank(t.getStepModel())) {
data.setStepModel(TestCaseConstants.StepModel.STEP.name());
} else {
data.setStepModel(t.getStepModel());
}
data.setPrerequisite(t.getPrerequisite());
data.setTags(t.getTags());
if (StringUtils.equals(t.getMethod(), "manual") || StringUtils.isBlank(t.getMethod())) {
if (StringUtils.equals(data.getStepModel(), TestCaseConstants.StepModel.TEXT.name())) {
data.setStepDesc(t.getStepDescription());
data.setStepResult(t.getExpectedResult());
} else {
String steps = t.getSteps();
String setp = "";
setp = steps;
JSONArray jsonArray = null;
//解决旧版本保存用例导出报错
try {
jsonArray = JSON.parseArray(setp);
} catch (Exception e) {
if (steps.contains("null") && !steps.contains("\"null\"")) {
setp = steps.replace("null", "\"\"");
jsonArray = JSON.parseArray(setp);
}
}
if (CollectionUtils.isNotEmpty(jsonArray)) {
for (int j = 0; j < jsonArray.size(); j++) {
int num = j + 1;
String stepItem = jsonArray.getJSONObject(j).getString("desc");
if (StringUtils.isEmpty(stepItem)) {
stepItem = "";
}
//正则去空格回车换行符制表符
stepItem = stepItem.replaceAll("\\s*|\t|\r|\n", "");
step.append(num + "." + stepItem + "\n");
String resultItem = jsonArray.getJSONObject(j).getString("result");
if (StringUtils.isEmpty(resultItem)) {
resultItem = "";
}
result.append(num + "." + resultItem + "\n");
}
}
data.setStepDesc(step.toString());
data.setStepResult(result.toString());
step.setLength(0);
result.setLength(0);
}
data.setRemark(t.getRemark());
} else if ("auto".equals(t.getMethod()) && "api".equals(t.getType())) {
data.setStepDesc("");
data.setStepResult("");
if (t.getTestId() != null && "other".equals(t.getTestId())) {
data.setRemark(t.getOtherTestName());
} else {
data.setRemark("[" + t.getApiName() + "]" + "\n" + t.getRemark());
}
} else if ("auto".equals(t.getMethod()) && "performance".equals(t.getType())) {
data.setStepDesc("");
data.setStepResult("");
if (t.getTestId() != null && "other".equals(t.getTestId())) {
data.setRemark(t.getOtherTestName());
} else {
data.setRemark(t.getPerformName());
}
}
data.setMaintainer(t.getMaintainer());
data.setStatus(t.getStatus());
try {
List<CustomFieldResource> fields = customFieldTestCaseService.getByResourceId(t.getId());
Map<String, String> map = new HashMap<>();
for (int index = 0; index < fields.size(); index++) {
CustomFieldResource field = fields.get(index);
//进行key value对换
String id = field.getFieldId();
if (StringUtils.isNotBlank(field.getValue())) {
String value = JSONObject.parse(field.getValue()).toString();
if (customSelectValueMap.containsKey(id)
&& customSelectValueMap.get(id).containsKey(value)) {
value = customSelectValueMap.get(id).get(value);
}
map.put(customNameMap.get(id), value);
}
}
data.setCustomDatas(map);
} catch (Exception e) {
LogUtil.error(e);
}
list.add(data);
});
return list;
}
public void setExportSystemField(TestCaseDTO testCase, Map<String, String> customNameMap,
public void setExportSystemField(TestCaseDTO testCase, Map<String, String> customNameMap,
Map<String, Map<String, String>> customSelectValueMap) {
String statusKey = null;
for (String k : customNameMap.keySet()) {
@ -2512,7 +2470,7 @@ public class TestCaseService {
relationExample.createCriteria().andRelationTypeEqualTo(AttachmentType.TEST_CASE.type());
List<AttachmentModuleRelation> relations = attachmentModuleRelationMapper.selectByExample(relationExample);
Map<String, List<AttachmentModuleRelation>> relationGroup = relations.stream().collect(Collectors.groupingBy(AttachmentModuleRelation::getRelationId));
for(Map.Entry<String, List<AttachmentModuleRelation>> entry : relationGroup.entrySet()) {
for (Map.Entry<String, List<AttachmentModuleRelation>> entry : relationGroup.entrySet()) {
final String caseId = entry.getKey();
final String uploadPath = FileUtils.ATTACHMENT_DIR + File.separator + AttachmentType.TEST_CASE.type() + File.separator + caseId;
// 获取同一用例关联的文件ID

View File

@ -47,12 +47,6 @@ tester=
read_only_user=
module=
preconditions_optional=
step_tip_separate=
step_tip_order=
step_tip_optional=
result_tip_separate=
result_tip_order=
result_tip_optional=
remark_optional=
do_not_modify_header_order=
module_created_automatically=

View File

@ -146,12 +146,6 @@ num_needed_modify_testcase=ID is needed when modifying testcase
num_needless_create_testcase=ID is needless when creating testcase
tag_tip_pattern=Labels should be separated by semicolons or commas
preconditions_optional=Preconditions optional
step_tip_separate=Each step is separated by a new line
step_tip_order=Mark the serial number before the step
step_tip_optional=Test steps and results optional
result_tip_separate=Each result is separated by a new line
result_tip_order=Mark the serial number before the result
result_tip_optional=Test steps and results optional
remark_optional=Remark optional
do_not_modify_header_order=Do not modify the header order
module_created_automatically=If there is no such module, will be created automatically

View File

@ -146,12 +146,6 @@ num_needed_modify_testcase=修改用例时ID必填
num_needless_create_testcase=创建用例时无需ID
tag_tip_pattern=标签之间以分号或者逗号隔开
preconditions_optional=前置条件选填
step_tip_separate=每个步骤以换行分隔
step_tip_order=步骤前需标序号
step_tip_optional=测试步骤和结果选填
result_tip_separate=每条结果以换行分隔
result_tip_order=结果前需标序号
result_tip_optional=测试步骤和结果选填
remark_optional=备注选填
do_not_modify_header_order=请勿修改表头顺序
module_created_automatically=若无该模块将自动创建

View File

@ -146,12 +146,6 @@ num_needed_modify_testcase=修改用例時ID必填
num_needless_create_testcase=創建用例時無需ID
tag_tip_pattern=標簽之間以分號或者逗號隔開
preconditions_optional=前置條件選填
step_tip_separate=每個步驟以換行分隔
step_tip_order=步驟前需標序號
step_tip_optional=測試步驟和結果選填
result_tip_separate=每條結果以換行分隔
result_tip_order=結果前需標序號
result_tip_optional=測試步驟和結果選填
remark_optional=備註選填
do_not_modify_header_order=請勿修改表頭順序
module_created_automatically=若無該模塊將自動創建