refactor: 功能案例的导入区分导入新建和导入更新

功能案例的导入区分导入新建和导入更新
This commit is contained in:
song-tianyang 2021-05-25 13:48:16 +08:00 committed by 刘瑞斌
parent 4c6f17201d
commit ab122c6252
23 changed files with 708 additions and 219 deletions

View File

@ -126,7 +126,7 @@
</select>
<select id="getTestCaseNames" resultType="io.metersphere.base.domain.TestCase">
select test_case.id, test_case.name, test_case.priority, test_case.type, test_case.review_status
select test_case.id, test_case.name, test_case.priority, test_case.type, test_case.review_status,test_case.num,test_case.custom_num
from test_case
<where>
<if test="request.combine != null">

View File

@ -4,10 +4,15 @@ import com.alibaba.excel.annotation.ExcelIgnore;
import lombok.Getter;
import lombok.Setter;
import java.util.HashSet;
import java.util.Set;
@Getter
@Setter
public class TestCaseExcelData {
private String id;
@ExcelIgnore
private Integer num;
@ExcelIgnore
@ -32,4 +37,14 @@ public class TestCaseExcelData {
private String stepResult;
@ExcelIgnore
private String stepModel;
public Set<String> getExcludeColumnFiledNames(boolean needNum){
Set<String> excludeColumnFiledNames = new HashSet<>();
if(!needNum){
excludeColumnFiledNames.add("num");
}
excludeColumnFiledNames.add("customNum");
return excludeColumnFiledNames;
}
}

View File

@ -13,12 +13,12 @@ import javax.validation.constraints.Pattern;
@ColumnWidth(15)
public class TestCaseExcelDataCn extends TestCaseExcelData {
@ExcelProperty("ID")
@NotRequired
private Integer num;
// @ExcelProperty("ID")
// @NotRequired
// private Integer num;
@ColumnWidth(50)
@ExcelProperty("自定义ID")
@ExcelProperty("ID")
@NotRequired
private String customNum;

View File

@ -15,4 +15,14 @@ public class TestCaseExcelDataFactory implements ExcelDataFactory {
}
return TestCaseExcelDataCn.class;
}
public TestCaseExcelData getTestCaseExcelDataLocal(){
Locale locale = LocaleContextHolder.getLocale();
if (Locale.US.toString().equalsIgnoreCase(locale.toString())) {
return new TestCaseExcelDataUs();
} else if (Locale.TRADITIONAL_CHINESE.toString().equalsIgnoreCase(locale.toString())) {
return new TestCaseExcelDataTw();
}
return new TestCaseExcelDataCn();
}
}

View File

@ -13,12 +13,12 @@ import javax.validation.constraints.Pattern;
@ColumnWidth(15)
public class TestCaseExcelDataTw extends TestCaseExcelData {
@ExcelProperty("ID")
@NotRequired
private Integer num;
// @ExcelProperty("ID")
// @NotRequired
// private Integer num;
@ColumnWidth(50)
@ExcelProperty("自定義ID")
@ExcelProperty("ID")
@NotRequired
private String customNum;

View File

@ -14,12 +14,12 @@ import javax.validation.constraints.Pattern;
@ColumnWidth(15)
public class TestCaseExcelDataUs extends TestCaseExcelData {
@ExcelProperty("ID")
@NotRequired
private Integer num;
// @ExcelProperty("ID")
// @NotRequired
// private Integer num;
@ColumnWidth(50)
@ExcelProperty("Custom ID")
@ExcelProperty("ID")
@NotRequired
private String customNum;

View File

@ -18,6 +18,10 @@ import org.apache.poi.xssf.usermodel.XSSFRichTextString;
*/
public class FunctionCaseTemplateWriteHandler extends AbstractRowHeightStyleStrategy {
private boolean isNeedId;
public FunctionCaseTemplateWriteHandler(boolean isNeedId){
this.isNeedId = isNeedId;
}
@Override
public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) {
@ -35,44 +39,72 @@ public class FunctionCaseTemplateWriteHandler extends AbstractRowHeightStyleStra
Sheet sheet = writeSheetHolder.getSheet();
Drawing<?> drawingPatriarch = sheet.createDrawingPatriarch();
// 在第一行 第3列创建一个批注
Comment comment1 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 2, 0, (short) 3, 1));
// 输入批注信息
comment1.setString(new XSSFRichTextString(Translator.get("do_not_modify_header_order") + "," + Translator.get("num_needed_modify_testcase") + "," + Translator.get("num_needless_create_testcase")));
if(isNeedId){
// 在第一行 第1列创建一个批注
Comment comment1 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 0, 0, (short) 3, 1));
// 输入批注信息
comment1.setString(new XSSFRichTextString(Translator.get("do_not_modify_header_order")));
// 在第一行 4列创建一个批注
Comment comment2 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 0, (short) 3, 1));
// 输入批注信息
comment2.setString(new XSSFRichTextString(Translator.get("module_created_automatically")));
// 在第一行 3列创建一个批注
Comment comment2 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 2, 0, (short) 3, 1));
// 输入批注信息
comment2.setString(new XSSFRichTextString(Translator.get("module_created_automatically")));
// 在第一行 第5列创建一个批注
Comment comment3 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 4, 0, (short) 3, 1));
// 输入批注信息
comment3.setString(new XSSFRichTextString(Translator.get("options") + "functional、performance、api"));
// // 在第一行 第5列创建一个批注
// Comment comment3 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 4, 0, (short) 3, 1));
// // 输入批注信息
// comment3.setString(new XSSFRichTextString(Translator.get("options") + "functional、performance、api"));
// 在第一行 6列创建一个批注
Comment comment4 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 5, 0, (short) 3, 1));
// 输入批注信息
comment4.setString(new XSSFRichTextString(Translator.get("please_input_workspace_member")));
// 在第一行 4列创建一个批注
Comment comment4 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 0, (short) 3, 1));
// 输入批注信息
comment4.setString(new XSSFRichTextString(Translator.get("please_input_workspace_member")));
// 在第一行 7列创建一个批注
Comment comment5 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 6, 0, (short) 3, 1));
// 输入批注信息
comment5.setString(new XSSFRichTextString(Translator.get("options") + "P0、P1、P2、P3"));
// 在第一行 5列创建一个批注
Comment comment5 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 4, 0, (short) 3, 1));
// 输入批注信息
comment5.setString(new XSSFRichTextString(Translator.get("options") + "P0、P1、P2、P3"));
// 在第一行 第8列创建一个批注
Comment comment6 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 7, 0, (short) 3, 1));
// 输入批注信息
comment6.setString(new XSSFRichTextString(Translator.get("tag_tip_pattern")));
// 在第一行 第6列创建一个批注
Comment comment6 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 5, 0, (short) 3, 1));
// 输入批注信息
comment6.setString(new XSSFRichTextString(Translator.get("tag_tip_pattern")));
// 将批注添加到单元格对象中
sheet.getRow(0).getCell(1).setCellComment(comment1);
sheet.getRow(0).getCell(1).setCellComment(comment2);
sheet.getRow(0).getCell(1).setCellComment(comment4);
sheet.getRow(0).getCell(1).setCellComment(comment5);
sheet.getRow(0).getCell(1).setCellComment(comment6);
}else {
// 在第一行 第2列创建一个批注
Comment comment2 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 1, 0, (short) 3, 1));
// 输入批注信息
comment2.setString(new XSSFRichTextString(Translator.get("do_not_modify_header_order") + ","+Translator.get("module_created_automatically")));
// 在第一行 第3列创建一个批注
Comment comment4 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 2, 0, (short) 3, 1));
// 输入批注信息
comment4.setString(new XSSFRichTextString(Translator.get("please_input_workspace_member")));
// 在第一行 第4列创建一个批注
Comment comment5 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 0, (short) 3, 1));
// 输入批注信息
comment5.setString(new XSSFRichTextString(Translator.get("options") + "P0、P1、P2、P3"));
// 在第一行 第5列创建一个批注
Comment comment6 = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 4, 0, (short) 3, 1));
// 输入批注信息
comment6.setString(new XSSFRichTextString(Translator.get("tag_tip_pattern")));
// 将批注添加到单元格对象中
sheet.getRow(0).getCell(1).setCellComment(comment2);
sheet.getRow(0).getCell(1).setCellComment(comment4);
sheet.getRow(0).getCell(1).setCellComment(comment5);
sheet.getRow(0).getCell(1).setCellComment(comment6);
}
// 将批注添加到单元格对象中
sheet.getRow(0).getCell(1).setCellComment(comment1);
sheet.getRow(0).getCell(1).setCellComment(comment2);
sheet.getRow(0).getCell(1).setCellComment(comment3);
sheet.getRow(0).getCell(1).setCellComment(comment4);
sheet.getRow(0).getCell(1).setCellComment(comment5);
sheet.getRow(0).getCell(1).setCellComment(comment6);
}
}

View File

@ -12,6 +12,7 @@ import io.metersphere.commons.utils.LogUtil;
import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.utils.ExcelValidateHelper;
import io.metersphere.excel.utils.FunctionCaseImportEnum;
import io.metersphere.i18n.Translator;
import io.metersphere.track.service.TestCaseService;
import org.apache.commons.lang3.StringUtils;
@ -32,8 +33,16 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
protected boolean isUpdated = false; //判断是否更新过用例将会传给前端
public boolean isUseCustomId;
public String importType;
Set<String> testCaseNames;
Set<String> customIds;
Set<String> savedCustomIds;
Set<String> userIds;
private List<String> names = new LinkedList<>();
@ -59,18 +68,42 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
return isUpdated;
}
public TestCaseDataIgnoreErrorListener(Class clazz, String projectId, Set<String> testCaseNames, Set<String> userIds) {
public TestCaseDataIgnoreErrorListener(Class clazz, String projectId, Set<String> testCaseNames, Set<String> savedCustomIds, Set<String> userIds,boolean isUseCustomId,String importType) {
this.clazz = clazz;
this.testCaseService = (TestCaseService) CommonBeanFactory.getBean("testCaseService");
this.projectId = projectId;
this.testCaseNames = testCaseNames;
this.userIds = userIds;
this.isUseCustomId = isUseCustomId;
this.importType = importType;
this.customIds = new HashSet<>();
this.savedCustomIds = savedCustomIds;
}
@Override
public String validate(TestCaseExcelData data, String errMsg) {
String nodePath = data.getNodePath();
StringBuilder stringBuilder = new StringBuilder(errMsg);
if(isUseCustomId || StringUtils.equals(this.importType,FunctionCaseImportEnum.Update.name())){
if(data.getCustomNum() == null){
stringBuilder.append(Translator.get("id_required")+";");
}else {
String customId = data.getCustomNum().toString();
if(StringUtils.isEmpty(customId)){
stringBuilder.append(Translator.get("id_required")+";");
}else if(customIds.contains(customId)) {
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);
}
}
}
String nodePath = data.getNodePath();
//校验所属模块"
if (nodePath != null) {
String[] nodes = nodePath.split("/");
@ -107,50 +140,62 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
有的话校验ID是否已在当前项目中存在存在则更新用例
不存在则继续校验看是否重复不重复则新建用例
*/
if (null != data.getNum()) { //当前读取的数据有ID
if (null != testCaseService.checkIdExist(data.getNum(), projectId)) { //该ID在当前项目中存在
//如果前面所经过的校验都没报错
if (StringUtils.isEmpty(stringBuilder)) {
updateList.add(data); //将当前数据存入更新列表
stringBuilder.append("update_testcase"); //该信息用于在invoke方法中判断是否该更新用例
if (null != data.getCustomNum()) { //当前读取的数据有ID
if(StringUtils.equals(this.importType,FunctionCaseImportEnum.Update.name())){
String checkResult = null;
if(isUseCustomId){
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum().toString(), projectId);
}else {
checkResult = testCaseService.checkIdExist(Integer.parseInt(data.getCustomNum()), projectId);
}
return stringBuilder.toString();
} else {
if (null != checkResult) { //该ID在当前项目中存在
//如果前面所经过的校验都没报错
if (StringUtils.isEmpty(stringBuilder)) {
updateList.add(data); //将当前数据存入更新列表
stringBuilder.append("update_testcase"); //该信息用于在invoke方法中判断是否该更新用例
}
return stringBuilder.toString();
} else {
/*
该ID在当前数据库中不存在应当继续校验用例是否重复,
在下面的校验过程中num的值会被用于判断是否重复所以应当先设置为null
*/
data.setNum(null);
if(!StringUtils.equals(this.importType,FunctionCaseImportEnum.Update.name())){
data.setNum(null);
}
}
}
}
/*
校验用例
*/
if (testCaseNames.contains(data.getName())) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(projectId);
String steps = getSteps(data);
testCase.setSteps(steps);
boolean dbExist = testCaseService.exist(testCase);
boolean excelExist = false;
if (dbExist) {
// db exist
stringBuilder.append(Translator.get("test_case_already_exists") + "" + data.getName() + "; ");
} else {
// @Data 重写了 equals hashCode 方法
excelExist = excelDataList.contains(data);
}
if (excelExist) {
// excel exist
stringBuilder.append(Translator.get("test_case_already_exists_excel") + "" + data.getName() + "; ");
} else {
excelDataList.add(data);
}
} else {
// if (testCaseNames.contains(data.getName())) {
// TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
// BeanUtils.copyBean(testCase, data);
// testCase.setProjectId(projectId);
// 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() + "; ");
// } else {
// // @Data 重写了 equals hashCode 方法
// excelExist = excelDataList.contains(data);
// }
// if (excelExist) {
// // excel exist
// stringBuilder.append(Translator.get("test_case_already_exists_excel") + "" + data.getName() + "; ");
// } else {
// excelDataList.add(data);
// }
// } else {
testCaseNames.add(data.getName());
excelDataList.add(data);
}
// }
return stringBuilder.toString();
}
@ -171,7 +216,11 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
List<TestCaseWithBLOBs> result2 = updateList.stream()
.map(item -> this.convert2TestCaseForUpdate(item))
.collect(Collectors.toList());
testCaseService.updateImportDataCarryId(result2, projectId);
if(this.isUseCustomId){
testCaseService.updateImportDataCustomId(result2, projectId);
}else {
testCaseService.updateImportDataCarryId(result2, projectId);
}
this.setNames(result2.stream().map(TestCase::getName).collect(Collectors.toList()));
this.setIds(result2.stream().map(TestCase::getId).collect(Collectors.toList()));
this.isUpdated = true;
@ -188,7 +237,9 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
testCase.setProjectId(this.projectId);
testCase.setCreateTime(System.currentTimeMillis());
testCase.setUpdateTime(System.currentTimeMillis());
testCase.setCustomNum(data.getCustomNum());
if(this.isUseCustomId){
testCase.setCustomNum(data.getCustomNum().toString());
}
String nodePath = data.getNodePath();
if (!nodePath.startsWith("/")) {
@ -202,7 +253,7 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
//将标签设置为前端可解析的格式
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
testCase.setType("functional");
if (StringUtils.isNotBlank(data.getStepModel())
&& StringUtils.equals(data.getStepModel(), TestCaseConstants.StepModel.TEXT.name())) {
testCase.setStepDescription(data.getStepDesc());
@ -243,6 +294,10 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
if(!isUseCustomId){
testCase.setNum(Integer.parseInt(data.getCustomNum()));
testCase.setCustomNum(null);
}
return testCase;
}
@ -276,22 +331,59 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
public String getSteps(TestCaseExcelData data) {
JSONArray jsonArray = new JSONArray();
String[] stepDesc = new String[1];
String[] stepRes = new String[1];
List<String> stepDescList = new ArrayList<>();
List<String> stepResList = new ArrayList<>();
// String[] stepDesc = new String[1];
// String[] stepRes = new String[1];
if (data.getStepDesc() != null) {
stepDesc = data.getStepDesc().split("\r\n|\n");
String[] stepDesc = data.getStepDesc().split("\r\n|\n");
StringBuffer stepBuffer = new StringBuffer();
int stepIndex = 1;
for (String row : stepDesc) {
if(StringUtils.startsWithAny(row,
stepIndex+")","("+stepIndex+")","\"+stepIndex+\"",
stepIndex+".",stepIndex+",",stepIndex+"")){
stepDescList.add(stepBuffer.toString());
stepBuffer = new StringBuffer();
stepIndex++;
stepBuffer.append(row);
}else {
stepBuffer.append(row);
}
}
if(StringUtils.isNotEmpty(stepBuffer.toString())){
stepDescList.add(stepBuffer.toString());
}
} else {
stepDesc[0] = "";
stepDescList.add("");
}
if (data.getStepResult() != null) {
stepRes = data.getStepResult().split("\r\n|\n");
String [] stepRes = data.getStepResult().split("\r\n|\n");
StringBuffer stepBuffer = new StringBuffer();
int stepIndex = 1;
for (String row : stepRes) {
if(StringUtils.startsWithAny(row,
stepIndex+")","("+stepIndex+")","\"+stepIndex+\"",
stepIndex+".",stepIndex+",",stepIndex+"")){
stepResList.add(stepBuffer.toString());
stepBuffer = new StringBuffer();
stepIndex++;
stepBuffer.append(row);
}else {
stepBuffer.append(row);
}
}
if(StringUtils.isNotEmpty(stepBuffer.toString())){
stepResList.add(stepBuffer.toString());
}
} else {
stepRes[0] = "";
stepResList.add("");
}
String pattern = "(^\\d+)(\\.)?";
int index = stepDesc.length > stepRes.length ? stepDesc.length : stepRes.length;
int index = stepDescList.size() > stepResList.size() ? stepDescList.size() : stepResList.size();
for (int i = 0; i < index; i++) {
@ -302,21 +394,21 @@ public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseE
Pattern descPattern = Pattern.compile(pattern);
Pattern resPattern = Pattern.compile(pattern);
if (i < stepDesc.length) {
Matcher descMatcher = descPattern.matcher(stepDesc[i]);
if (i < stepDescList.size()) {
Matcher descMatcher = descPattern.matcher(stepDescList.get(i));
if (descMatcher.find()) {
step.put("desc", descMatcher.replaceAll(""));
} else {
step.put("desc", stepDesc[i]);
step.put("desc", stepDescList.get(i));
}
}
if (i < stepRes.length) {
Matcher resMatcher = resPattern.matcher(stepRes[i]);
if (i < stepResList.size()) {
Matcher resMatcher = resPattern.matcher(stepResList.get(i));
if (resMatcher.find()) {
step.put("result", resMatcher.replaceAll(""));
} else {
step.put("result", stepRes[i]);
step.put("result", stepResList.get(i));
}
}

View File

@ -12,6 +12,7 @@ import io.metersphere.commons.utils.LogUtil;
import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.utils.ExcelValidateHelper;
import io.metersphere.excel.utils.FunctionCaseImportEnum;
import io.metersphere.i18n.Translator;
import io.metersphere.track.service.TestCaseService;
import org.apache.commons.lang3.StringUtils;
@ -32,8 +33,16 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
protected boolean isUpdated = false; //判断是否更新过用例将会传给前端
public boolean isUseCustomId;
public String importType;
Set<String> testCaseNames;
Set<String> customIds;
Set<String> savedCustomIds;
Set<String> userIds;
private List<String> names = new LinkedList<>();
@ -43,19 +52,41 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
return isUpdated;
}
public TestCaseDataListener(Class clazz, String projectId, Set<String> testCaseNames, Set<String> userIds) {
public TestCaseDataListener(Class clazz, String projectId, Set<String> testCaseNames,Set<String> savedCustomIds, Set<String> userIds,boolean isUseCustomId,String importType) {
this.clazz = clazz;
this.testCaseService = (TestCaseService) CommonBeanFactory.getBean("testCaseService");
this.projectId = projectId;
this.testCaseNames = testCaseNames;
this.userIds = userIds;
this.isUseCustomId = isUseCustomId;
this.importType = importType;
customIds = new HashSet<>();
this.savedCustomIds = savedCustomIds;
}
@Override
public String validate(TestCaseExcelData data, String errMsg) {
String nodePath = data.getNodePath();
StringBuilder stringBuilder = new StringBuilder(errMsg);
if(isUseCustomId || StringUtils.equals(this.importType,FunctionCaseImportEnum.Update.name())){
if(data.getCustomNum() == null){
stringBuilder.append(Translator.get("id_required")+";");
}else {
String customId = data.getCustomNum().toString();
if(StringUtils.isEmpty(customId)){
stringBuilder.append(Translator.get("id_required")+";");
}else if(customIds.contains(customId)) {
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);
}
}
}
String nodePath = data.getNodePath();
//校验所属模块"
if (nodePath != null) {
String[] nodes = nodePath.split("/");
@ -83,10 +114,6 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
}
}
// if (StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) {
// stringBuilder.append(Translator.get("functional_method_tip") + "; ");
// }
//校验维护人
if (!userIds.contains(data.getMaintainer())) {
@ -98,57 +125,68 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
有的话校验ID是否已在当前项目中存在存在则更新用例
不存在则继续校验看是否重复不重复则新建用例
*/
if (null != data.getNum()) { //当前读取的数据有ID
if (null != data.getCustomNum()) { //当前读取的数据有ID
if (null != testCaseService.checkIdExist(data.getNum(), projectId)) { //该ID在当前项目中存在
//如果前面所经过的校验都没报错
if (StringUtils.isEmpty(stringBuilder)) {
updateList.add(data); //将当前数据存入更新列表
stringBuilder.append("update_testcase"); //该信息用于在invoke方法中判断是否该更新用例
if(StringUtils.equals(this.importType,FunctionCaseImportEnum.Update.name())){
String checkResult = null;
if(isUseCustomId){
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum().toString(), projectId);
}else {
checkResult = testCaseService.checkIdExist(Integer.parseInt(data.getCustomNum()), projectId);
}
return stringBuilder.toString();
} else {
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);
data.setNum(null);
}
}
}
/*
校验用例
*/
if (testCaseNames.contains(data.getName())) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(projectId);
String steps = getSteps(data);
testCase.setSteps(steps);
boolean dbExist = testCaseService.exist(testCase);
boolean excelExist = false;
if (dbExist) {
// db exist
stringBuilder.append(Translator.get("test_case_already_exists") + "" + data.getName() + "; ");
} else {
// @Data 重写了 equals hashCode 方法
excelExist = excelDataList.contains(data);
}
if (excelExist) {
// excel exist
stringBuilder.append(Translator.get("test_case_already_exists_excel") + "" + data.getName() + "; ");
} else {
excelDataList.add(data);
}
} else {
// if (testCaseNames.contains(data.getName())) {
// TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
// BeanUtils.copyBean(testCase, data);
// testCase.setProjectId(projectId);
// 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() + "; ");
// } else {
// // @Data 重写了 equals hashCode 方法
// excelExist = excelDataList.contains(data);
// }
//
// if (excelExist) {
// // excel exist
// stringBuilder.append(Translator.get("test_case_already_exists_excel") + "" + data.getName() + "; ");
// } else {
// excelDataList.add(data);
// }
//
// } else {
testCaseNames.add(data.getName());
excelDataList.add(data);
}
// }
return stringBuilder.toString();
}
@ -191,7 +229,11 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
List<TestCaseWithBLOBs> result2 = updateList.stream()
.map(item -> this.convert2TestCaseForUpdate(item))
.collect(Collectors.toList());
testCaseService.updateImportDataCarryId(result2, projectId);
if(this.isUseCustomId){
testCaseService.updateImportDataCustomId(result2, projectId);
}else {
testCaseService.updateImportDataCarryId(result2, projectId);
}
this.isUpdated = true;
this.setNames(result2.stream().map(TestCase::getName).collect(Collectors.toList()));
this.setIds(result2.stream().map(TestCase::getId).collect(Collectors.toList()));
@ -208,7 +250,10 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
testCase.setProjectId(this.projectId);
testCase.setCreateTime(System.currentTimeMillis());
testCase.setUpdateTime(System.currentTimeMillis());
testCase.setCustomNum(data.getCustomNum());
if(this.isUseCustomId){
testCase.setCustomNum(data.getCustomNum().toString());
}
String nodePath = data.getNodePath();
if (!nodePath.startsWith("/")) {
@ -222,6 +267,7 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
//将标签设置为前端可解析的格式
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
testCase.setType("functional");
if (StringUtils.isNotBlank(data.getStepModel())
&& StringUtils.equals(data.getStepModel(), TestCaseConstants.StepModel.TEXT.name())) {
@ -263,6 +309,11 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
if(!isUseCustomId){
testCase.setNum(Integer.parseInt(data.getCustomNum()));
testCase.setCustomNum(null);
}
return testCase;
}
@ -296,22 +347,69 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
public String getSteps(TestCaseExcelData data) {
JSONArray jsonArray = new JSONArray();
String[] stepDesc = new String[1];
String[] stepRes = new String[1];
List<String> stepDescList = new ArrayList<>();
List<String> stepResList = new ArrayList<>();
// String[] stepDesc = new String[1];
// String[] stepRes = new String[1];
if (data.getStepDesc() != null) {
stepDesc = data.getStepDesc().split("\r\n|\n");
String[] stepDesc = data.getStepDesc().split("\r\n|\n");
StringBuffer stepBuffer = new StringBuffer();
int stepIndex = 1;
for (String row : stepDesc) {
if(StringUtils.startsWithAny(row,
stepIndex+")","("+stepIndex+")","\"+stepIndex+\"",
stepIndex+".",stepIndex+",",stepIndex+"")){
if(StringUtils.isNotEmpty(stepBuffer.toString())){
stepDescList.add(stepBuffer.toString());
}
stepBuffer = new StringBuffer();
stepIndex++;
stepBuffer.append(row);
}else {
if(StringUtils.isNotEmpty(stepBuffer.toString())){
stepBuffer.append("\r\n");
}
stepBuffer.append(row);
}
}
if(StringUtils.isNotEmpty(stepBuffer.toString())){
stepDescList.add(stepBuffer.toString());
}
} else {
stepDesc[0] = "";
stepDescList.add("");
}
if (data.getStepResult() != null) {
stepRes = data.getStepResult().split("\r\n|\n");
String [] stepRes = data.getStepResult().split("\r\n|\n");
StringBuffer stepBuffer = new StringBuffer();
int stepIndex = 1;
for (String row : stepRes) {
if(StringUtils.startsWithAny(row,
stepIndex+")","("+stepIndex+")","\"+stepIndex+\"",
stepIndex+".",stepIndex+",",stepIndex+"")){
if(StringUtils.isNotEmpty(stepBuffer.toString())){
stepResList.add(stepBuffer.toString());
}
stepBuffer = new StringBuffer();
stepIndex++;
stepBuffer.append(row);
}else {
if(StringUtils.isNotEmpty(stepBuffer.toString())){
stepBuffer.append("\r\n");
}
stepBuffer.append(row);
}
}
if(StringUtils.isNotEmpty(stepBuffer.toString())){
stepResList.add(stepBuffer.toString());
}
} else {
stepRes[0] = "";
stepResList.add("");
}
String pattern = "(^\\d+)(\\.)?";
int index = stepDesc.length > stepRes.length ? stepDesc.length : stepRes.length;
int index = stepDescList.size() > stepResList.size() ? stepDescList.size() : stepResList.size();
for (int i = 0; i < index; i++) {
@ -322,21 +420,21 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
Pattern descPattern = Pattern.compile(pattern);
Pattern resPattern = Pattern.compile(pattern);
if (i < stepDesc.length) {
Matcher descMatcher = descPattern.matcher(stepDesc[i]);
if (i < stepDescList.size()) {
Matcher descMatcher = descPattern.matcher(stepDescList.get(i));
if (descMatcher.find()) {
step.put("desc", descMatcher.replaceAll(""));
} else {
step.put("desc", stepDesc[i]);
step.put("desc", stepDescList.get(i));
}
}
if (i < stepRes.length) {
Matcher resMatcher = resPattern.matcher(stepRes[i]);
if (i < stepResList.size()) {
Matcher resMatcher = resPattern.matcher(stepResList.get(i));
if (resMatcher.find()) {
step.put("result", resMatcher.replaceAll(""));
} else {
step.put("result", stepRes[i]);
step.put("result", stepResList.get(i));
}
}

View File

@ -6,12 +6,14 @@ import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
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.List;
import java.util.Set;
public class EasyExcelExporter {
@ -39,12 +41,19 @@ public class EasyExcelExporter {
}
}
public void exportByCustomWriteHandler(HttpServletResponse response, List data, String fileName, String sheetName, WriteHandler writeHandler) {
public void exportByCustomWriteHandler(HttpServletResponse response, Set<String> excludeColumnFiledNames, List data, String fileName, String sheetName, WriteHandler writeHandler) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
try {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
EasyExcel.write(response.getOutputStream(), this.clazz).registerWriteHandler(writeHandler).sheet(sheetName).doWrite(data);
if(CollectionUtils.isNotEmpty(excludeColumnFiledNames)){
EasyExcel.write(response.getOutputStream(), this.clazz).
registerWriteHandler(writeHandler).
excludeColumnFiledNames(excludeColumnFiledNames).
sheet(sheetName).doWrite(data);
}else {
EasyExcel.write(response.getOutputStream(), this.clazz).registerWriteHandler(writeHandler).sheet(sheetName).doWrite(data);
}
} catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("Utf-8 encoding is not supported");

View File

@ -0,0 +1,9 @@
package io.metersphere.excel.utils;
/**
* @author song.tianyang
* @Date 2021/5/24 1:52 下午
*/
public enum FunctionCaseImportEnum {
Create,Update
}

View File

@ -219,6 +219,21 @@ public class ProjectService {
return projectMapper.selectByPrimaryKey(id);
}
public boolean useCustomNum(String projectId){
Project project = this.getProjectById(projectId);
if (project != null) {
Boolean customNum = project.getCustomNum();
// 未开启自定义ID
if (!customNum) {
return false;
} else {
return true;
}
} else {
return false;
}
}
public List<Project> getByCaseTemplateId(String templateId) {
ProjectExample example = new ProjectExample();
example.createCriteria()

View File

@ -153,31 +153,31 @@ public class TestCaseController {
return testCaseService.deleteTestCase(testCaseId);
}
@PostMapping("/import/{projectId}/{userId}")
@PostMapping("/import/{projectId}/{userId}/{importType}")
@MsAuditLog(module = "track_test_case", type = OperLogConstants.IMPORT, project = "#projectId")
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId, @PathVariable String userId, HttpServletRequest request) {
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId, @PathVariable String userId, @PathVariable String importType, HttpServletRequest request) {
checkPermissionService.checkProjectOwner(projectId);
return testCaseService.testCaseImport(file, projectId, userId, request);
return testCaseService.testCaseImport(file, projectId, userId, importType,request);
}
@PostMapping("/importIgnoreError/{projectId}/{userId}")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_IMPORT)
@MsAuditLog(module = "track_test_case", type = OperLogConstants.IMPORT, project = "#projectId")
public ExcelResponse testCaseImportIgnoreError(MultipartFile file, @PathVariable String projectId, @PathVariable String userId, HttpServletRequest request) {
public ExcelResponse testCaseImportIgnoreError(MultipartFile file, @PathVariable String projectId, @PathVariable String userId, @PathVariable String importType, HttpServletRequest request) {
checkPermissionService.checkProjectOwner(projectId);
return testCaseService.testCaseImportIgnoreError(file, projectId, userId, request);
return testCaseService.testCaseImportIgnoreError(file, projectId, userId,importType, request);
}
@GetMapping("/export/template")
@GetMapping("/export/template/{projectId}/{importType}")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_EXPORT)
public void testCaseTemplateExport(HttpServletResponse response) {
testCaseService.testCaseTemplateExport(response);
public void testCaseTemplateExport(@PathVariable String projectId,@PathVariable String importType,HttpServletResponse response) {
testCaseService.testCaseTemplateExport(projectId,importType,response);
}
@GetMapping("/export/xmindTemplate")
@GetMapping("/export/xmindTemplate/{projectId}/{importType}")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_EXPORT)
public void xmindTemplate(HttpServletResponse response) {
testCaseService.testCaseXmindTemplateExport(response);
public void xmindTemplate(@PathVariable String projectId,@PathVariable String importType,HttpServletResponse response) {
testCaseService.testCaseXmindTemplateExport(projectId,importType,response);
}
@PostMapping("/export/testcase")

View File

@ -24,6 +24,7 @@ import io.metersphere.excel.handler.FunctionCaseTemplateWriteHandler;
import io.metersphere.excel.listener.TestCaseDataIgnoreErrorListener;
import io.metersphere.excel.listener.TestCaseDataListener;
import io.metersphere.excel.utils.EasyExcelExporter;
import io.metersphere.excel.utils.FunctionCaseImportEnum;
import io.metersphere.i18n.Translator;
import io.metersphere.log.utils.ReflexObjectUtil;
import io.metersphere.log.vo.DetailColumn;
@ -252,17 +253,33 @@ public class TestCaseService {
* 根据id和pojectId查询id是否在数据库中存在
* 在数据库中单id的话是可重复的,id与projectId的组合是唯一的
*/
public Integer checkIdExist(Integer id, String projectId) {
public String checkIdExist(Integer id, String projectId) {
TestCaseExample example = new TestCaseExample();
TestCaseExample.Criteria criteria = example.createCriteria();
if (null != id) {
criteria.andNumEqualTo(id);
criteria.andProjectIdEqualTo(projectId);
long count = testCaseMapper.countByExample(example); //查询是否有包含此ID的数据
if (count == 0) { //如果ID不存在
List<TestCase> testCaseList = testCaseMapper.selectByExample(example); //查询是否有包含此ID的数据
if (testCaseList.isEmpty()) { //如果ID不存在
return null;
} else { //有对应ID的数据
return id;
return testCaseList.get(0).getId();
}
}
return null;
}
public String checkCustomIdExist(String id, String projectId) {
TestCaseExample example = new TestCaseExample();
TestCaseExample.Criteria criteria = example.createCriteria();
if (null != id) {
criteria.andCustomNumEqualTo(id);
criteria.andProjectIdEqualTo(projectId);
List<TestCase> testCaseList = testCaseMapper.selectByExample(example); //查询是否有包含此ID的数据
if (testCaseList.isEmpty()) { //如果ID不存在
return null;
} else { //有对应ID的数据
return testCaseList.get(0).getId();
}
}
return null;
@ -399,24 +416,33 @@ public class TestCaseService {
}
public ExcelResponse testCaseImport(MultipartFile multipartFile, String projectId, String userId, HttpServletRequest request) {
public ExcelResponse testCaseImport(MultipartFile multipartFile, String projectId, String userId,String importType, HttpServletRequest request) {
ExcelResponse excelResponse = new ExcelResponse();
boolean isUpdated = false; //判断是否更新了用例
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest();
queryTestCaseRequest.setProjectId(projectId);
boolean useCunstomId = projectService.useCustomNum(projectId);
List<TestCase> testCases = extTestCaseMapper.getTestCaseNames(queryTestCaseRequest);
Set<String> testCaseNames = testCases.stream()
.map(TestCase::getName)
.collect(Collectors.toSet());
Set<String> savedIds = new HashSet<>();
Set<String> testCaseNames = new HashSet<>();
for (TestCase testCase : testCases) {
if(useCunstomId){
savedIds.add(testCase.getCustomNum());
}else {
savedIds.add(String.valueOf(testCase.getNum()));
}
testCaseNames.add(testCase.getName());
}
List<ExcelErrData<TestCaseExcelData>> errList = null;
if (multipartFile == null) {
MSException.throwException(Translator.get("upload_fail"));
}
if (multipartFile.getOriginalFilename().endsWith(".xmind")) {
try {
XmindCaseParser xmindParser = new XmindCaseParser(this, userId, projectId, testCaseNames);
XmindCaseParser xmindParser = new XmindCaseParser(this, userId, projectId, testCaseNames,useCunstomId,importType);
errList = xmindParser.parse(multipartFile);
if (CollectionUtils.isEmpty(xmindParser.getNodePaths())
&& CollectionUtils.isEmpty(xmindParser.getTestCase())
@ -470,7 +496,7 @@ public class TestCaseService {
//根据本地语言环境选择用哪种数据对象进行存放读取的数据
Class clazz = new TestCaseExcelDataFactory().getExcelDataByLocal();
TestCaseDataListener easyExcelListener = new TestCaseDataListener(clazz, projectId, testCaseNames, userIds);
TestCaseDataListener easyExcelListener = new TestCaseDataListener(clazz, projectId, testCaseNames,savedIds, userIds,useCunstomId,importType);
//读取excel数据
EasyExcelFactory.read(multipartFile.getInputStream(), clazz, easyExcelListener).sheet().doRead();
request.setAttribute("ms-req-title", String.join(",", easyExcelListener.getNames()));
@ -534,7 +560,9 @@ public class TestCaseService {
testcase.setUpdateTime(System.currentTimeMillis());
testcase.setNodeId(nodePathMap.get(testcase.getNodePath()));
testcase.setSort(sort.getAndIncrement());
testcase.setNum(num.decrementAndGet());
if(testcase.getNum() == null){
testcase.setNum(num.decrementAndGet());
}
testcase.setReviewStatus(TestCaseReviewStatus.Prepare.name());
mapper.updateByPrimaryKeySelective(testcase);
});
@ -581,23 +609,76 @@ public class TestCaseService {
sqlSession.flushStatements();
}
/**
* 把Excel中带ID的数据更新到数据库
* feat(测试跟踪):通过Excel导入导出时有ID字段可通过Excel导入来更新用例 (#1727)
*
* @param testCases
* @param projectId
*/
public void updateImportDataCustomId(List<TestCaseWithBLOBs> testCases, String projectId) {
Map<String, String> nodePathMap = testCaseNodeService.createNodeByTestCases(testCases, projectId);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class);
public void testCaseTemplateExport(HttpServletResponse response) {
/*
获取用例的网页上所显示id数据库ID映射
*/
List<String> customIds = testCases.stream()
.map(TestCase::getCustomNum)
.collect(Collectors.toList());
TestCaseExample example = new TestCaseExample();
example.createCriteria().andCustomNumIn(customIds)
.andProjectIdEqualTo(projectId);
List<TestCase> testCasesList = testCaseMapper.selectByExample(example);
Map<String, String> customIdMap = testCasesList.stream()
.collect(Collectors.toMap(TestCase::getCustomNum, TestCase::getId));
if (!testCases.isEmpty()) {
AtomicInteger sort = new AtomicInteger();
testCases.forEach(testcase -> {
testcase.setUpdateTime(System.currentTimeMillis());
testcase.setNodeId(nodePathMap.get(testcase.getNodePath()));
testcase.setSort(sort.getAndIncrement());
testcase.setId(customIdMap.get(testcase.getCustomNum()));
mapper.updateByPrimaryKeySelective(testcase);
});
}
sqlSession.flushStatements();
}
public void testCaseTemplateExport(String projectId,String importType,HttpServletResponse response) {
try {
EasyExcelExporter easyExcelExporter = new EasyExcelExporter(new TestCaseExcelDataFactory().getExcelDataByLocal());
FunctionCaseTemplateWriteHandler handler = new FunctionCaseTemplateWriteHandler();
easyExcelExporter.exportByCustomWriteHandler(response, generateExportTemplate(),
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;
}
//不包含ID列
Set<String> excludeColumnFiledNames = testCaseExcelData.getExcludeColumnFiledNames(importFileNeedNum);
EasyExcelExporter easyExcelExporter = new EasyExcelExporter(testCaseExcelData.getClass());
FunctionCaseTemplateWriteHandler handler = new FunctionCaseTemplateWriteHandler(importFileNeedNum);
easyExcelExporter.exportByCustomWriteHandler(response,excludeColumnFiledNames, generateExportTemplate(),
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"), handler);
} catch (Exception e) {
MSException.throwException(e);
}
}
public void download(HttpServletResponse res) throws IOException {
public void download(String fileName,HttpServletResponse res) throws IOException {
if(StringUtils.isEmpty(fileName)){
fileName = "xmind.xml";
}
// 发送给客户端的数据
byte[] buff = new byte[1024];
try (OutputStream outputStream = res.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(TestCaseService.class.getResourceAsStream("/io/metersphere/xmind/template/xmind.xml"));) {
BufferedInputStream bis = new BufferedInputStream(TestCaseService.class.getResourceAsStream("/io/metersphere/xmind/template/"+fileName));) {
int i = bis.read(buff);
while (i != -1) {
outputStream.write(buff, 0, buff.length);
@ -610,12 +691,23 @@ public class TestCaseService {
}
}
public void testCaseXmindTemplateExport(HttpServletResponse response) {
public void testCaseXmindTemplateExport(String projectId,String importType,HttpServletResponse response) {
try {
response.setContentType("application/octet-stream");
response.setCharacterEncoding("utf-8");
boolean isUseCustomId = projectService.useCustomNum(projectId);
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("思维导图用例模版", "UTF-8") + ".xmind");
download(response);
String fileName = null;
if(StringUtils.equals(importType,FunctionCaseImportEnum.Update.name())){
fileName = "xmind_update.xml";
}else{
if(isUseCustomId){
fileName = "xmind_custom_id.xml";
}else {
fileName = "xmind_system_id.xml";
}
}
download(fileName,response);
} catch (Exception ex) {
}
@ -1063,7 +1155,7 @@ public class TestCaseService {
extTestCaseMapper.updateTestCaseCustomNumByProjectId(projectId);
}
public ExcelResponse testCaseImportIgnoreError(MultipartFile multipartFile, String projectId, String userId, HttpServletRequest request) {
public ExcelResponse testCaseImportIgnoreError(MultipartFile multipartFile, String projectId, String userId, String importType,HttpServletRequest request) {
ExcelResponse excelResponse = new ExcelResponse();
boolean isUpdated = false; //判断是否更新了用例
@ -1071,16 +1163,24 @@ public class TestCaseService {
QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest();
queryTestCaseRequest.setProjectId(projectId);
List<TestCase> testCases = extTestCaseMapper.getTestCaseNames(queryTestCaseRequest);
Set<String> testCaseNames = testCases.stream()
.map(TestCase::getName)
.collect(Collectors.toSet());
boolean useCunstomId = projectService.useCustomNum(projectId);
Set<String> savedIds = new HashSet<>();
Set<String> testCaseNames = new HashSet<>();
for (TestCase testCase : testCases) {
if(useCunstomId){
savedIds.add(testCase.getCustomNum());
}else {
savedIds.add(String.valueOf(testCase.getNum()));
}
testCaseNames.add(testCase.getName());
}
List<ExcelErrData<TestCaseExcelData>> errList = null;
if (multipartFile == null) {
MSException.throwException(Translator.get("upload_fail"));
}
if (multipartFile.getOriginalFilename().endsWith(".xmind")) {
try {
XmindCaseParser xmindParser = new XmindCaseParser(this, userId, projectId, testCaseNames);
XmindCaseParser xmindParser = new XmindCaseParser(this, userId, projectId, testCaseNames,useCunstomId,importType);
errList = xmindParser.parse(multipartFile);
if (CollectionUtils.isEmpty(xmindParser.getNodePaths())
&& CollectionUtils.isEmpty(xmindParser.getTestCase())
@ -1138,8 +1238,7 @@ public class TestCaseService {
try {
//根据本地语言环境选择用哪种数据对象进行存放读取的数据
Class clazz = new TestCaseExcelDataFactory().getExcelDataByLocal();
TestCaseDataIgnoreErrorListener easyExcelListener = new TestCaseDataIgnoreErrorListener(clazz, projectId, testCaseNames, userIds);
TestCaseDataIgnoreErrorListener easyExcelListener = new TestCaseDataIgnoreErrorListener(clazz, projectId, testCaseNames,savedIds, userIds,useCunstomId, importType);
//读取excel数据
EasyExcelFactory.read(multipartFile.getInputStream(), clazz, easyExcelListener).sheet().doRead();

View File

@ -9,6 +9,7 @@ import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.utils.FunctionCaseImportEnum;
import io.metersphere.i18n.Translator;
import io.metersphere.track.service.TestCaseService;
import io.metersphere.xmind.parser.XmindParser;
@ -63,7 +64,11 @@ public class XmindCaseParser {
private List<String> errorPath;
public XmindCaseParser(TestCaseService testCaseService, String userId, String projectId, Set<String> testCaseNames) {
private boolean isUseCustomId;
private String importType;
public XmindCaseParser(TestCaseService testCaseService, String userId, String projectId, Set<String> testCaseNames,boolean isUseCustomId,String importType) {
this.testCaseService = testCaseService;
this.maintainer = userId;
this.projectId = projectId;
@ -75,6 +80,8 @@ public class XmindCaseParser {
nodePaths = new ArrayList<>();
continueValidatedCase = new ArrayList<>();
errorPath = new ArrayList<>();
this.isUseCustomId = isUseCustomId;
this.importType = importType;
}
private static final String TC_REGEX = "(?:tc:|tc|tc)";
@ -92,11 +99,19 @@ public class XmindCaseParser {
}
public List<TestCaseWithBLOBs> getTestCase() {
return this.testCases;
if(StringUtils.equals(this.importType,FunctionCaseImportEnum.Create.name())){
return this.testCases;
}else {
return new ArrayList<>();
}
}
public List<TestCaseWithBLOBs> getUpdateTestCase() {
return this.updateTestCases;
if(StringUtils.equals(this.importType,FunctionCaseImportEnum.Update.name())){
return this.updateTestCases;
}else {
return new ArrayList<>();
}
}
public List<String> getNodePaths() {
@ -183,18 +198,19 @@ public class XmindCaseParser {
process.add(Translator.get("functional_method_tip"), nodePath + data.getName());
}
if (testCaseNames.contains(data.getName())) {
TestCaseWithBLOBs bloBs = testCaseService.checkTestCaseExist(data);
if (bloBs != null) {
// process.add(Translator.get("test_case_already_exists_excel"), nodePath + "/" + data.getName());
// 记录需要变更的用例
BeanUtils.copyBean(bloBs, data, "id");
updateTestCases.add(bloBs);
return false;
}
} else {
testCaseNames.add(data.getName());
}
// if (testCaseNames.contains(data.getName())) {
// TestCaseWithBLOBs bloBs = testCaseService.checkTestCaseExist(data);
// if (bloBs != null) {
// // process.add(Translator.get("test_case_already_exists_excel"), nodePath + "/" + data.getName());
// // 记录需要变更的用例
// BeanUtils.copyBean(bloBs, data, "id");
// updateTestCases.add(bloBs);
// return false;
// }
//
// } else {
// testCaseNames.add(data.getName());
// }
// 用例等级和用例性质处理
if (!priorityList.contains(data.getPriority())) {
@ -214,6 +230,45 @@ public class XmindCaseParser {
process.add(Translator.get("test_case_already_exists_excel"), nodePath + "/" + compartData.getName());
}
compartDatas.add(compartData);
//自定义ID判断
if(StringUtils.isEmpty(data.getCustomNum())){
if(StringUtils.equals(this.importType, FunctionCaseImportEnum.Update.name())){
validatePass = false;
process.add(Translator.get("id_required"), nodePath + "/" + compartData.getName());
return false;
}else{
if(isUseCustomId){
validatePass = false;
process.add(Translator.get("custom_num_is_not_exist"), nodePath + "/" + compartData.getName());
return false;
}
}
}
//判断更新
if(StringUtils.equals(this.importType,FunctionCaseImportEnum.Update.name())) {
String checkResult = null;
if (isUseCustomId) {
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum().toString(), projectId);
} else {
checkResult = testCaseService.checkIdExist(Integer.parseInt(data.getCustomNum()), projectId);
}
if (null != checkResult) { //该ID在当前项目中存在
//如果前面所经过的校验都没报错
if(!isUseCustomId){
data.setNum(Integer.parseInt(data.getCustomNum()));
data.setCustomNum(null);
}
data.setId(checkResult);
updateTestCases.add(data);
}else {
process.add(Translator.get("custom_num_is_not_exist"), nodePath + "/" + compartData.getName());
validatePass = false;
}
}
if (validatePass) {
this.continueValidatedCase.add(data);
}
@ -351,7 +406,10 @@ public class XmindCaseParser {
});
}
testCase.setRemark(rc.toString());
testCase.setCustomNum(customId.toString());
if(isUseCustomId || StringUtils.equals(importType,FunctionCaseImportEnum.Update.name())){
testCase.setCustomNum(customId.toString());
}
testCase.setTags(JSON.toJSONString(tags));
testCase.setSteps(this.getSteps(steps));
// 校验合规性

View File

@ -64,5 +64,7 @@ password_format_is_incorrect=
please_input_workspace_member=
test_case_report_template_repeat=
custom_field_already=
id_required=
id_repeat_in_table=
template_already=
expect_name_exists=

View File

@ -156,6 +156,9 @@ test_case_module_already_exists=The module name already exists at the same level
api_test_name_already_exists=Test name already exists
functional_method_tip=Functional test not support auto method
custom_num_is_exist=Use case custom ID already exists
custom_num_is_not_exist=Use case custom ID not exists
id_required=ID required
id_repeat_in_table=ID is repeat in table
#ldap
ldap_url_is_null=LDAP address is empty
ldap_dn_is_null=LDAP binding DN is empty

View File

@ -156,6 +156,9 @@ test_case_module_already_exists=同层级下已存在该模块名称
api_test_name_already_exists=测试名称已经存在
functional_method_tip=功能测试不支持自动方式
custom_num_is_exist=用例自定义ID已存在
custom_num_is_not_exist=用例自定义ID不存在
id_required=ID必填
id_repeat_in_table=表格内ID重复
#ldap
ldap_url_is_null=LDAP地址为空
ldap_dn_is_null=LDAP绑定DN为空

View File

@ -156,6 +156,9 @@ test_case_module_already_exists=同層級下已存在該模塊名稱
api_test_name_already_exists=測試名稱已經存在
functional_method_tip=功能測試不支持自動方式
custom_num_is_exist=用例自定義ID已存在
custom_num_is_not_exist=用例自定義ID不存在
id_required=ID必填
id_repeat_in_table=表格內ID重複
#ldap
ldap_url_is_null=LDAP地址為空
ldap_dn_is_null=LDAP綁定DN為空

View File

@ -4,9 +4,36 @@
<el-tabs v-model="activeName" @tab-click="clickTabs" simple>
<el-tab-pane :label="$t('test_track.case.import.excel_title')" name="excelImport">
<el-row class="import-row" style="margin-left: 34px">
<el-radio v-model="importType" label="Create">导入新建</el-radio>
<el-radio v-model="importType" label="Update">导入更新</el-radio>
</el-row>
<el-row class="import-row">
<div class="el-step__icon is-text" style="background-color: #C9E6F8;border-color: #C9E6F8;margin-right: 10px">
<div class="el-step__icon-inner">1</div>
</div>
<label class="ms-license-label">{{$t('test_track.case.import.import_desc')}}</label>
</el-row>
<el-row class="import-row">
<div style="margin-left: 34px">
<div v-if="importType === 'Create'">
项目设置中测试用例自定义ID 开关开启时ID为必填项
</div>
<div v-else >
导入更新时ID为必填项
</div>
</div>
</el-row>
<el-row class="import-row">
<div class="el-step__icon is-text"
style="background-color: #C9E6F8;border-color: #C9E6F8;margin-right: 10px ">
<div class="el-step__icon-inner">2</div>
</div>
<label class="ms-license-label">{{$t('test_track.case.import.import_file')}}</label>
</el-row>
<el-row>
<el-link type="primary" class="download-template"
<el-link type="primary" class="download-template" style="margin: 20px 34px;"
@click="downloadTemplate"
>{{ $t('test_track.case.import.download_template') }}
</el-link>
@ -56,19 +83,31 @@
</el-tab-pane>
<!-- Xmind 导入 -->
<el-tab-pane :label="$t('test_track.case.import.xmind_title')" name="xmindImport" style="border: 0px">
<el-row class="import-row" style="margin-left: 34px">
<el-radio v-model="importType" label="Create">导入新建</el-radio>
<el-radio v-model="importType" label="Update">导入更新</el-radio>
</el-row>
<el-row class="import-row">
<div class="el-step__icon is-text" style="background-color: #C9E6F8;border-color: #C9E6F8;margin-right: 10px">
<div class="el-step__icon-inner">1</div>
</div>
<label class="ms-license-label">{{$t('test_track.case.import.import_desc')}}</label>
</el-row>
<el-row class="import-row">
<el-card :body-style="{ padding: '0px' }">
<img src="../../../../../assets/xmind.jpg"
class="testcase-import-img">
</el-card>
<el-row class="import-row" style="margin-left: 34px">
<div v-if="importType === 'Create'">
项目设置中测试用例自定义ID 开关开启时ID为必填项
</div>
<div v-else >
导入更新时ID为必填项
</div>
</el-row>
<!-- <el-row class="import-row">-->
<!-- <el-card :body-style="{ padding: '0px' }">-->
<!-- <img src="../../../../../assets/xmind.jpg"-->
<!-- class="testcase-import-img">-->
<!-- </el-card>-->
<!-- </el-row>-->
<el-row class="import-row">
<div class="el-step__icon is-text"
style="background-color: #C9E6F8;border-color: #C9E6F8;margin-right: 10px ">
@ -77,7 +116,7 @@
<label class="ms-license-label">{{$t('test_track.case.import.import_file')}}</label>
</el-row>
<el-row class="import-row">
<el-link type="primary" class="download-template"
<el-link type="primary" class="download-template" style="margin: 0px 34px;"
@click="downloadXmindTemplate"
>{{$t('test_track.case.import.download_template')}}
</el-link>
@ -144,6 +183,7 @@
fileList: [],
lastXmindFile: null,
lastExcelFile: null,
importType:"Create",
errList: [],
xmindErrList: [],
isLoading: false,
@ -227,10 +267,11 @@
}
},
downloadTemplate() {
this.$fileDownload('/test/case/export/template');
this.$fileDownload('/test/case/export/template/'+this.projectId+"/"+this.importType);
},
downloadXmindTemplate() {
axios.get('/test/case/export/xmindTemplate', {responseType: 'blob'})
axios.get('/test/case/export/xmindTemplate/'+this.projectId+"/"+this.importType, {responseType: 'blob'})
.then(response => {
let fileName = window.decodeURI(response.headers['content-disposition'].split('=')[1]);
let link = document.createElement("a");
@ -246,7 +287,7 @@
let user = JSON.parse(localStorage.getItem(TokenKey));
this.result = this.$fileUpload('/test/case/import/' + this.projectId + '/' + user.id, file.file, null, {}, response => {
this.result = this.$fileUpload('/test/case/import/' + this.projectId + '/' + user.id+'/'+this.importType, file.file, null, {}, response => {
let res = response.data;
if (res.success) {
this.$success(this.$t('test_track.case.import.success'));
@ -279,7 +320,7 @@
this.uploadIgnoreError = true;
file = this.lastExcelFile;
}
this.result = this.$fileUpload('/test/case/importIgnoreError/' + this.projectId + '/' + user.id, file, null, {}, response => {
this.result = this.$fileUpload('/test/case/importIgnoreError/' + this.projectId + '/' + user.id+'/'+this.importType, file, null, {}, response => {
let res = response.data;
this.$success(this.$t('test_track.case.import.success'));
this.dialogVisible = false;
@ -305,7 +346,7 @@
this.lastXmindFile = file.file;
let user = JSON.parse(localStorage.getItem(TokenKey));
this.result = this.$fileUpload('/test/case/import/' + this.projectId + '/' + user.id, file.file, null, {}, response => {
this.result = this.$fileUpload('/test/case/import/' + this.projectId + '/' + user.id+'/'+this.importType, file.file, null, {}, response => {
let res = response.data;
if (res.success) {
this.$success(this.$t('test_track.case.import.success'));