feat: 功能用例导入支持选择版本

This commit is contained in:
AnAngle 2022-01-24 00:06:07 +08:00 committed by jianxing
parent 85fe565b74
commit c01fb7b575
7 changed files with 199 additions and 1330 deletions

View File

@ -1,550 +0,0 @@
package io.metersphere.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.TestCase;
import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.utils.*;
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;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestCaseDataIgnoreErrorListener extends EasyExcelListener<TestCaseExcelData> {
private TestCaseService testCaseService;
private String projectId;
protected List<TestCaseExcelData> updateList = new ArrayList<>(); //存储待更新用例的集合
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<>();
private List<String> ids = new LinkedList<>();
public List<String> getNames() {
return this.names;
}
public List<String> getIds() {
return this.ids;
}
public void setNames(List<String> names) {
this.names = names;
}
public void setIds(List<String> ids) {
this.ids = ids;
}
public boolean isUpdated() {
return isUpdated;
}
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) {
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("/");
//校验模块深度
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") + "; ");
}
//模块名不能为空
for (int i = 0; i < nodes.length; i++) {
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
stringBuilder.append(Translator.get("module_not_null") + "; ");
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") + Translator.get("test_track.length_less_than") + "100:" + nodeStr);
break;
}
}
}
}
//校验维护人
if (StringUtils.isBlank(data.getMaintainer())) {
data.setMaintainer(SessionUtils.getUserId());
} else {
if (!userIds.contains(data.getMaintainer())) {
stringBuilder.append(Translator.get("user_not_exists") + "" + data.getMaintainer() + "; ");
}
}
/*
校验Excel中是否有ID
有的话校验ID是否已在当前项目中存在存在则更新用例
不存在则继续校验看是否重复不重复则新建用例
*/
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);
}
if (null != checkResult) { //该ID在当前项目中存在
//如果前面所经过的校验都没报错
if (StringUtils.isEmpty(stringBuilder)) {
updateList.add(data); //将当前数据存入更新列表
stringBuilder.append("update_testcase"); //该信息用于在invoke方法中判断是否该更新用例
}
return stringBuilder.toString();
} else {
/*
该ID在当前数据库中不存在应当继续校验用例是否重复,
在下面的校验过程中num的值会被用于判断是否重复所以应当先设置为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);
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 {
if(!dbExist){
excelDataList.add(data);
}
}
} else {
testCaseNames.add(data.getName());
excelDataList.add(data);
}
return stringBuilder.toString();
}
@Override
public void saveData() {
if (!(list.size() == 0)) {
Collections.reverse(list); //因为saveImportData里面是先分配最大的ID这个ID应该先发给list中最后的数据所以要reverse
List<TestCaseWithBLOBs> result = list.stream()
.map(item -> this.convert2TestCase(item))
.collect(Collectors.toList());
testCaseService.saveImportData(result, projectId);
this.setNames(result.stream().map(TestCase::getName).collect(Collectors.toList()));
this.setIds(result.stream().map(TestCase::getId).collect(Collectors.toList()));
this.isUpdated = true;
}
if (!(updateList.size() == 0)) {
List<TestCaseWithBLOBs> result2 = updateList.stream()
.map(item -> this.convert2TestCaseForUpdate(item))
.collect(Collectors.toList());
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;
updateList.clear();
}
}
private TestCaseWithBLOBs convert2TestCase(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setId(UUID.randomUUID().toString());
testCase.setProjectId(this.projectId);
testCase.setCreateTime(System.currentTimeMillis());
testCase.setUpdateTime(System.currentTimeMillis());
if(this.isUseCustomId){
testCase.setCustomNum(data.getCustomNum().toString());
}
String nodePath = data.getNodePath();
if (!nodePath.startsWith("/")) {
nodePath = "/" + nodePath;
}
if (nodePath.endsWith("/")) {
nodePath = nodePath.substring(0, nodePath.length() - 1);
}
testCase.setNodePath(nodePath);
JSONArray customArr = new JSONArray();
String caseStatusValue = "";
if(StringUtils.equalsAny(data.getStatus(),"Underway","underway","进行中","進行中")){
caseStatusValue = "Underway";
}else if(StringUtils.equalsAny(data.getStatus(),"Prepare","prepare","未开始","未開始")){
caseStatusValue = "Prepare";
}else if(StringUtils.equalsAny(data.getStatus(),"Completed","completed","已完成","已完成")){
caseStatusValue = "Completed";
}
if(StringUtils.isNotEmpty(caseStatusValue)){
JSONObject statusObj = new JSONObject();
statusObj.put("id",UUID.randomUUID().toString());
statusObj.put("name","用例状态");
statusObj.put("value",caseStatusValue);
statusObj.put("customData",null);
customArr.add(statusObj);
}
if(StringUtils.isNotEmpty(data.getMaintainer())){
JSONObject obj = new JSONObject();
obj.put("id",UUID.randomUUID().toString());
obj.put("name","责任人");
obj.put("value",data.getMaintainer());
obj.put("customData",null);
customArr.add(obj);
}
if(customArr.size()>0){
testCase.setCustomFields(customArr.toString());
}
//将标签设置为前端可解析的格式
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());
testCase.setExpectedResult(data.getStepResult());
} else {
String steps = getSteps(data);
testCase.setSteps(steps);
}
return testCase;
}
/**
* 将Excel中的数据对象转换为用于更新操作的用例数据对象
*
* @param data
* @return
*/
private TestCaseWithBLOBs convert2TestCaseForUpdate(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(this.projectId);
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.equalsAny(data.getStatus(),"Underway","underway","进行中","進行中")){
caseStatusValue = "Underway";
}else if(StringUtils.equalsAny(data.getStatus(),"Prepare","prepare","未开始","未開始")){
caseStatusValue = "Prepare";
}else if(StringUtils.equalsAny(data.getStatus(),"Completed","completed","已完成","已完成")){
caseStatusValue = "Completed";
}
if(StringUtils.isNotEmpty(caseStatusValue)){
JSONObject statusObj = new JSONObject();
statusObj.put("id",UUID.randomUUID().toString());
statusObj.put("name","用例状态");
statusObj.put("value",caseStatusValue);
statusObj.put("customData",null);
customArr.add(statusObj);
}
if(StringUtils.isNotEmpty(data.getMaintainer())){
JSONObject obj = new JSONObject();
obj.put("id",UUID.randomUUID().toString());
obj.put("name","责任人");
obj.put("value",data.getMaintainer());
obj.put("customData",null);
customArr.add(obj);
}
if(customArr.size()>0){
testCase.setCustomFields(customArr.toString());
}
//将标签设置为前端可解析的格式
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
if(!isUseCustomId){
testCase.setNum(Integer.parseInt(data.getCustomNum()));
testCase.setCustomNum(null);
}
return testCase;
}
/**
* 调整tags格式便于前端进行解析
* 例如对于标签1标签2将调整为:["标签1","标签2"]
*/
public String modifyTagPattern(TestCaseExcelData data) {
String tags = data.getTags();
try {
if (StringUtils.isNotBlank(tags)) {
JSONArray.parse(tags);
return tags;
}
return "[]";
} catch (Exception e) {
if (tags != null) {
Stream<String> stringStream = Arrays.stream(tags.split("[,;]")); //当标签值以中英文的逗号和分号分隔时才能正确解析
List<String> tagList = stringStream.map(tag -> tag = "\"" + tag + "\"")
.collect(Collectors.toList());
String modifiedTags = StringUtils.join(tagList, ",");
modifiedTags = "[" + modifiedTags + "]";
return modifiedTags;
} else {
return "[]";
}
}
}
public String getSteps(TestCaseExcelData data) {
JSONArray jsonArray = new JSONArray();
List<String> stepDescList = new ArrayList<>();
List<String> stepResList = new ArrayList<>();
ListUtils<String> listUtils = new ListUtils<String>();
if (data.getStepDesc() != null) {
String[] stepDesc = data.getStepDesc().split("\r\n|\n");
StringBuffer stepBuffer = new StringBuffer();
int lastStepIndex = 1;
for (String row : stepDesc) {
RowInfo rowInfo = this.parseIndexInRow(row);
int rowIndex = rowInfo.index;
String rowMessage = rowInfo.rowInfo;
if(rowIndex > -1){
listUtils.set(stepDescList,lastStepIndex-1,stepBuffer.toString(),"");
stepBuffer = new StringBuffer();
lastStepIndex = rowIndex;
stepBuffer.append(rowMessage);
}else {
stepBuffer.append(row);
}
}
if(StringUtils.isNotEmpty(stepBuffer.toString())){
listUtils.set(stepDescList,lastStepIndex-1,stepBuffer.toString(),"");
}
} else {
stepDescList.add("");
}
if (data.getStepResult() != null) {
String [] stepRes = data.getStepResult().split("\r\n|\n");
StringBuffer stepBuffer = new StringBuffer();
int lastStepIndex = 1;
for (String row : stepRes) {
RowInfo rowInfo = this.parseIndexInRow(row);
int rowIndex = rowInfo.index;
String rowMessage = rowInfo.rowInfo;
if(rowIndex > -1){
listUtils.set(stepResList,lastStepIndex-1,stepBuffer.toString(),"");
stepBuffer = new StringBuffer();
lastStepIndex = rowIndex;
stepBuffer.append(rowMessage);
}else {
stepBuffer.append(row);
}
}
if(StringUtils.isNotEmpty(stepBuffer.toString())){
listUtils.set(stepResList,lastStepIndex-1,stepBuffer.toString(),"");
}
} else {
stepResList.add("");
}
int index = stepDescList.size() > stepResList.size() ? stepDescList.size() : stepResList.size();
for (int i = 0; i < index; i++) {
// 保持插入顺序判断用例是否有相同的steps
JSONObject step = new JSONObject(true);
step.put("num", i + 1);
if (i < stepDescList.size()) {
step.put("desc", stepDescList.get(i));
}
if (i < stepResList.size()) {
step.put("result", stepResList.get(i));
}
jsonArray.add(step);
}
return jsonArray.toJSONString();
}
private RowInfo parseIndexInRow(String row) {
RowInfo rowInfo = new RowInfo();
String parseString = row;
int index = -1;
String rowMessage = row;
String [] indexSplitCharArr = new String[]{")","","]","",".",",","",""};
if(StringUtils.startsWithAny(row,"(","","[","")){
parseString = parseString.substring(1);
}
for (String splitChar : indexSplitCharArr) {
if(StringUtils.contains(parseString,splitChar)){
String[] rowSplit = StringUtils.split(parseString,splitChar);
if(rowSplit.length > 0){
String indexString = rowSplit[0];
if(StringUtils.isNumeric(indexString)){
try {
index = Integer.parseInt(indexString);
rowMessage = StringUtils.substring(parseString,indexString.length()+splitChar.length());
}catch (Exception e){}
if(index > -1){
break;
}else {
rowMessage = row;
}
}
}
}
}
rowInfo.index = index;
if(rowMessage == null){
rowMessage = "";
}
rowInfo.rowInfo = rowMessage;
return rowInfo;
}
@Override
public void invoke(TestCaseExcelData testCaseExcelData, AnalysisContext analysisContext) {
String errMsg;
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
String updateMsg = "update_testcase";
try {
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
errMsg = ExcelValidateHelper.validateEntity(testCaseExcelData);
//自定义校验规则
errMsg = validate(testCaseExcelData, errMsg);
} catch (NoSuchFieldException e) {
errMsg = 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);
}
} else {
list.add(testCaseExcelData);
}
if (errList.isEmpty() && list.size() > BATCH_COUNT) {
saveData();
list.clear();
}
}
class RowInfo{
public int index;
public String rowInfo;
}
}

View File

@ -1,588 +0,0 @@
package io.metersphere.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.TestCase;
import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.utils.*;
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;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
private TestCaseService testCaseService;
private String projectId;
protected List<TestCaseExcelData> updateList = new ArrayList<>(); //存储待更新用例的集合
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<>();
private List<String> ids = new LinkedList<>();
public boolean isUpdated() {
return isUpdated;
}
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) {
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("/");
//校验模块深度
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") + "; ");
}
//模块名不能为空
for (int i = 0; i < nodes.length; i++) {
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
stringBuilder.append(Translator.get("module_not_null") + "; ");
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") + Translator.get("test_track.length_less_than") + "100:" + nodeStr);
break;
}
}
}
}
//校验维护人
if (StringUtils.isBlank(data.getMaintainer())) {
data.setMaintainer(SessionUtils.getUserId());
} else {
if (!userIds.contains(data.getMaintainer())) {
stringBuilder.append(Translator.get("user_not_exists") + "" + data.getMaintainer() + "; ");
}
}
/*
校验Excel中是否有ID
有的话校验ID是否已在当前项目中存在存在则更新用例
不存在则继续校验看是否重复不重复则新建用例
*/
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 {
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, projectId);
}
}
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);
}
}
}
/*
校验用例
*/
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 {
if(!dbExist){
excelDataList.add(data);
}
}
} else {
testCaseNames.add(data.getName());
excelDataList.add(data);
}
return stringBuilder.toString();
}
public List<String> getNames() {
return this.names;
}
public List<String> getIds() {
return this.ids;
}
public void setNames(List<String> names) {
this.names = names;
}
public void setIds(List<String> ids) {
this.ids = ids;
}
@Override
public void saveData() {
//excel中用例都有错误时就返回只要有用例可用于更新或者插入就不返回
if (!errList.isEmpty()) {
return;
}
if (!(list.size() == 0)) {
Collections.reverse(list); //因为saveImportData里面是先分配最大的ID这个ID应该先发给list中最后的数据所以要reverse
List<TestCaseWithBLOBs> result = list.stream()
.map(item -> this.convert2TestCase(item))
.collect(Collectors.toList());
testCaseService.saveImportData(result, projectId);
this.setNames(result.stream().map(TestCase::getName).collect(Collectors.toList()));
this.setIds(result.stream().map(TestCase::getId).collect(Collectors.toList()));
this.isUpdated = true;
}
if (!(updateList.size() == 0)) {
List<TestCaseWithBLOBs> result2 = updateList.stream()
.map(item -> this.convert2TestCaseForUpdate(item))
.collect(Collectors.toList());
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()));
updateList.clear();
}
}
private TestCaseWithBLOBs convert2TestCase(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setId(UUID.randomUUID().toString());
testCase.setProjectId(this.projectId);
testCase.setCreateTime(System.currentTimeMillis());
testCase.setUpdateTime(System.currentTimeMillis());
if(this.isUseCustomId){
testCase.setCustomNum(data.getCustomNum().toString());
}
String nodePath = data.getNodePath();
if (!nodePath.startsWith("/")) {
nodePath = "/" + nodePath;
}
if (nodePath.endsWith("/")) {
nodePath = nodePath.substring(0, nodePath.length() - 1);
}
testCase.setNodePath(nodePath);
//将标签设置为前端可解析的格式
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
testCase.setType("functional");
JSONArray customArr = new JSONArray();
String caseStatusValue = "";
if(StringUtils.equalsAny(data.getStatus(),"Underway","进行中","進行中")){
caseStatusValue = "Underway";
}else if(StringUtils.equalsAny(data.getStatus(),"Prepare","未开始","未開始")){
caseStatusValue = "Prepare";
}else if(StringUtils.equalsAny(data.getStatus(),"Completed","已完成","已完成")){
caseStatusValue = "Completed";
}
if(StringUtils.isNotEmpty(caseStatusValue)){
testCase.setStatus(caseStatusValue);
JSONObject statusObj = new JSONObject();
statusObj.put("id",UUID.randomUUID().toString());
statusObj.put("name","用例状态");
statusObj.put("value",caseStatusValue);
statusObj.put("customData",null);
statusObj.put("type","select");
customArr.add(statusObj);
}
if(StringUtils.isNotEmpty(data.getMaintainer())){
JSONObject obj = new JSONObject();
obj.put("id",UUID.randomUUID().toString());
obj.put("name","责任人");
obj.put("value",data.getMaintainer());
obj.put("customData",null);
obj.put("type","member");
customArr.add(obj);
}
if(StringUtils.isNotEmpty(data. getPriority())){
JSONObject obj = new JSONObject();
obj.put("id",UUID.randomUUID().toString());
obj.put("name","用例等级");
obj.put("value",data.getPriority());
obj.put("customData",null);
obj.put("type","select");
customArr.add(obj);
}
if(customArr.size()>0){
testCase.setCustomFields(customArr.toString());
}
if (StringUtils.isNotBlank(data.getStepModel())
&& StringUtils.equals(data.getStepModel(), TestCaseConstants.StepModel.TEXT.name())) {
testCase.setStepDescription(data.getStepDesc());
testCase.setExpectedResult(data.getStepResult());
} else {
String steps = getSteps(data);
testCase.setSteps(steps);
}
return testCase;
}
/**
* 将Excel中的数据对象转换为用于更新操作的用例数据对象
*
* @param data
* @return
*/
private TestCaseWithBLOBs convert2TestCaseForUpdate(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(this.projectId);
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.equalsAny(data.getStatus(),"Underway","进行中","進行中")){
caseStatusValue = "Underway";
}else if(StringUtils.equalsAny(data.getStatus(),"Prepare","未开始","未開始")){
caseStatusValue = "Prepare";
}else if(StringUtils.equalsAny(data.getStatus(),"Completed","已完成","已完成")){
caseStatusValue = "Completed";
}
if(StringUtils.isNotEmpty(caseStatusValue)){
JSONObject statusObj = new JSONObject();
statusObj.put("id",UUID.randomUUID().toString());
statusObj.put("name","用例状态");
statusObj.put("value",caseStatusValue);
statusObj.put("customData",null);
customArr.add(statusObj);
}
if(StringUtils.isNotEmpty(data.getMaintainer())){
JSONObject obj = new JSONObject();
obj.put("id",UUID.randomUUID().toString());
obj.put("name","责任人");
obj.put("value",data.getMaintainer());
obj.put("customData",null);
customArr.add(obj);
}
if(customArr.size()>0){
testCase.setCustomFields(customArr.toString());
}
//将标签设置为前端可解析的格式
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
if(!isUseCustomId){
testCase.setNum(Integer.parseInt(data.getCustomNum()));
testCase.setCustomNum(null);
}
return testCase;
}
/**
* 调整tags格式便于前端进行解析
* 例如对于标签1标签2将调整为:["标签1","标签2"]
*/
public String modifyTagPattern(TestCaseExcelData data) {
String tags = data.getTags();
try {
if (StringUtils.isNotBlank(tags)) {
JSONArray.parse(tags);
return tags;
}
return "[]";
} catch (Exception e) {
if (tags != null) {
Stream<String> stringStream = Arrays.stream(tags.split("[,;]")); //当标签值以中英文的逗号和分号分隔时才能正确解析
List<String> tagList = stringStream.map(tag -> tag = "\"" + tag + "\"")
.collect(Collectors.toList());
String modifiedTags = StringUtils.join(tagList, ",");
modifiedTags = "[" + modifiedTags + "]";
return modifiedTags;
} else {
return "[]";
}
}
}
public String getSteps(TestCaseExcelData data) {
JSONArray jsonArray = new JSONArray();
List<String> stepDescList = new ArrayList<>();
List<String> stepResList = new ArrayList<>();
ListUtils<String> listUtils = new ListUtils<String>();
if (data.getStepDesc() != null) {
String desc = data.getStepDesc().replaceAll("\\n([1-9]\\.)", "\r\n$1");
String [] stepDesc = desc.split("\r\n");
StringBuffer stepBuffer = new StringBuffer();
int lastStepIndex = 1;
for (String row : stepDesc) {
RowInfo rowInfo = this.parseIndexInRow(row);
int rowIndex = rowInfo.index;
String rowMessage = rowInfo.rowInfo;
if(rowIndex > -1){
listUtils.set(stepDescList,lastStepIndex-1,stepBuffer.toString(),"");
stepBuffer = new StringBuffer();
lastStepIndex = rowIndex;
stepBuffer.append(rowMessage);
}else {
stepBuffer.append(row);
}
}
if(StringUtils.isNotEmpty(stepBuffer.toString())){
listUtils.set(stepDescList,lastStepIndex-1,stepBuffer.toString(),"");
}
} else {
stepDescList.add("");
}
if (data.getStepResult() != null) {
String stepResult = data.getStepResult().replaceAll("\\n([1-9]\\.)", "\r\n$1");
String [] stepRes = stepResult.split("\r\n");
StringBuffer stepBuffer = new StringBuffer();
int lastStepIndex = 1;
for (String row : stepRes) {
RowInfo rowInfo = this.parseIndexInRow(row);
int rowIndex = rowInfo.index;
String rowMessage = rowInfo.rowInfo;
if(rowIndex > -1){
listUtils.set(stepResList,lastStepIndex-1,stepBuffer.toString(),"");
stepBuffer = new StringBuffer();
lastStepIndex = rowIndex;
stepBuffer.append(rowMessage);
}else {
stepBuffer.append(row);
}
}
if(StringUtils.isNotEmpty(stepBuffer.toString())){
listUtils.set(stepResList,lastStepIndex-1,stepBuffer.toString(),"");
}
} else {
stepResList.add("");
}
int index = stepDescList.size() > stepResList.size() ? stepDescList.size() : stepResList.size();
for (int i = 0; i < index; i++) {
// 保持插入顺序判断用例是否有相同的steps
JSONObject step = new JSONObject(true);
step.put("num", i + 1);
if (i < stepDescList.size()) {
step.put("desc", stepDescList.get(i));
}
if (i < stepResList.size()) {
step.put("result", stepResList.get(i));
}
jsonArray.add(step);
}
return jsonArray.toJSONString();
}
private RowInfo parseIndexInRow(String row) {
RowInfo rowInfo = new RowInfo();
String parseString = row;
int index = -1;
String rowMessage = row;
String [] indexSplitCharArr = new String[]{")","","]","",".",",","",""};
if(StringUtils.startsWithAny(row,"(","","[","")){
parseString = parseString.substring(1);
}
for (String splitChar : indexSplitCharArr) {
if(StringUtils.contains(parseString,splitChar)){
String[] rowSplit = StringUtils.split(parseString,splitChar);
if(rowSplit.length > 0){
String indexString = rowSplit[0];
if(StringUtils.isNumeric(indexString)){
try {
index = Integer.parseInt(indexString);
rowMessage = StringUtils.substring(parseString,indexString.length()+splitChar.length());
}catch (Exception e){}
if(index > -1){
break;
}else {
rowMessage = row;
}
}
}
}
}
rowInfo.index = index;
if(rowMessage == null){
rowMessage = "";
}
rowInfo.rowInfo = rowMessage;
return rowInfo;
}
@Override
public void invoke(TestCaseExcelData testCaseExcelData, AnalysisContext analysisContext) {
String errMsg;
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
String updateMsg = "update_testcase";
try {
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
errMsg = ExcelValidateHelper.validateEntity(testCaseExcelData);
//自定义校验规则
errMsg = validate(testCaseExcelData, errMsg);
} catch (NoSuchFieldException e) {
errMsg = 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);
}
} else {
list.add(testCaseExcelData);
}
if (list.size() > BATCH_COUNT) {
saveData();
list.clear();
}
}
class RowInfo{
public int index;
public String rowInfo;
}
}

View File

@ -17,13 +17,13 @@ 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.request.testcase.TestCaseImportRequest;
import io.metersphere.track.service.TestCaseService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -37,8 +37,6 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
private Class excelDataClass;
boolean isIgnoreError = false;
protected List<ExcelErrData<TestCaseExcelData>> errList = new ArrayList<>();
protected List<TestCaseExcelData> excelDataList = new ArrayList<>();
@ -53,46 +51,33 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
private TestCaseService testCaseService;
private String projectId;
protected List<TestCaseExcelData> updateList = new ArrayList<>(); //存储待更新用例的集合
protected List<TestCaseExcelData> list = new ArrayList<>();
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<>();
private List<String> ids = new LinkedList<>();
Map<String,CustomFieldDao> customFieldsMap = new HashMap<>();
private TestCaseImportRequest request;
public boolean isUpdated() {
return isUpdated;
}
public TestCaseNoModelDataListener(boolean isIgnoreError,Class c,List<CustomFieldDao> customFields, String projectId, Set<String> testCaseNames, Set<String> savedCustomIds, Set<String> userIds, boolean isUseCustomId, String importType) {
public TestCaseNoModelDataListener(TestCaseImportRequest request, Class c) {
this.excelDataClass = c;
this.isIgnoreError = isIgnoreError;
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;
this.request = request;
List<CustomFieldDao> customFields = request.getCustomFields();
if(CollectionUtils.isNotEmpty(customFields)){
for (CustomFieldDao dto:customFields) {
String name = dto.getName();
@ -166,8 +151,10 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
}
public String validate(TestCaseExcelData data, String errMsg) {
Set<String> savedCustomIds = request.getSavedCustomIds();
String importType = request.getImportType();
StringBuilder stringBuilder = new StringBuilder(errMsg);
if (isUseCustomId || StringUtils.equals(this.importType, FunctionCaseImportEnum.Update.name())) {
if (request.isUseCustomId() || StringUtils.equals(importType, FunctionCaseImportEnum.Update.name())) {
if (data.getCustomNum() == null) {
stringBuilder.append(Translator.get("id_required") + ";");
} else {
@ -231,7 +218,7 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
if (StringUtils.isBlank(data.getMaintainer())) {
data.setMaintainer(SessionUtils.getUserId());
} else {
if (!userIds.contains(data.getMaintainer())) {
if (!request.getUserIds().contains(data.getMaintainer())) {
stringBuilder.append(Translator.get("user_not_exists") + "" + data.getMaintainer() + "; ");
}
}
@ -252,10 +239,10 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
*/
if (null != data.getCustomNum()) { //当前读取的数据有ID
if (StringUtils.equals(this.importType, FunctionCaseImportEnum.Update.name())) {
if (StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Update.name())) {
String checkResult = null;
if (isUseCustomId) {
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum().toString(), projectId);
if (request.isUseCustomId()) {
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum(), request.getProjectId());
} else {
int customNumId = -1;
try {
@ -265,7 +252,7 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
if (customNumId < 0) {
stringBuilder.append(Translator.get("id_not_rightful") + "[" + data.getCustomNum() + "]; ");
} else {
checkResult = testCaseService.checkIdExist(customNumId, projectId);
checkResult = testCaseService.checkIdExist(customNumId, request.getProjectId());
}
}
if (null != checkResult) { //该ID在当前项目中存在
@ -290,10 +277,10 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
/*
校验用例
*/
if (testCaseNames.contains(data.getName())) {
if (request.getTestCaseNames().contains(data.getName())) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(projectId);
testCase.setProjectId(request.getProjectId());
String steps = getSteps(data);
testCase.setSteps(steps);
testCase.setType("functional");
@ -319,7 +306,7 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
}
} else {
testCaseNames.add(data.getName());
request.getTestCaseNames().add(data.getName());
excelDataList.add(data);
}
return stringBuilder.toString();
@ -344,7 +331,7 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
public void saveData() {
//excel中用例都有错误时就返回只要有用例可用于更新或者插入就不返回
if (!errList.isEmpty() && !isIgnoreError) {
if (!errList.isEmpty() && !request.isIgnore()) {
return;
}
@ -353,7 +340,7 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
List<TestCaseWithBLOBs> result = list.stream()
.map(item -> this.convert2TestCase(item))
.collect(Collectors.toList());
testCaseService.saveImportData(result, projectId);
testCaseService.saveImportData(result, request);
this.names = result.stream().map(TestCase::getName).collect(Collectors.toList());
this.ids = result.stream().map(TestCase::getId).collect(Collectors.toList());
this.isUpdated = true;
@ -363,11 +350,7 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
List<TestCaseWithBLOBs> result2 = updateList.stream()
.map(item -> this.convert2TestCaseForUpdate(item))
.collect(Collectors.toList());
if (this.isUseCustomId) {
testCaseService.updateImportDataCustomId(result2, projectId);
} else {
testCaseService.updateImportDataCarryId(result2, projectId);
}
testCaseService.updateImportData(result2, request);
this.isUpdated = true;
this.names = result2.stream().map(TestCase::getName).collect(Collectors.toList());
this.ids = result2.stream().map(TestCase::getId).collect(Collectors.toList());
@ -381,11 +364,11 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setId(UUID.randomUUID().toString());
testCase.setProjectId(this.projectId);
testCase.setProjectId(request.getProjectId());
testCase.setCreateTime(System.currentTimeMillis());
testCase.setUpdateTime(System.currentTimeMillis());
if (this.isUseCustomId) {
testCase.setCustomNum(data.getCustomNum().toString());
if (request.isUseCustomId()) {
testCase.setCustomNum(data.getCustomNum());
}
String nodePath = data.getNodePath();
@ -440,7 +423,7 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
private TestCaseWithBLOBs convert2TestCaseForUpdate(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(this.projectId);
testCase.setProjectId(request.getProjectId());
testCase.setUpdateTime(System.currentTimeMillis());
//调整nodePath格式
@ -477,7 +460,7 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
String modifiedTags = modifyTagPattern(data);
testCase.setTags(modifiedTags);
if (!isUseCustomId) {
if (!request.isUseCustomId()) {
testCase.setNum(Integer.parseInt(data.getCustomNum()));
testCase.setCustomNum(null);
}

View File

@ -1,14 +1,27 @@
package io.metersphere.track.request.testcase;
import io.metersphere.dto.CustomFieldDao;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Set;
@Getter
@Setter
public class TestCaseImportRequest {
private String projectId;
private String userId;
private String importType;
private String version;
private String versionId;
private boolean ignore;
private Set<String> userIds;
/**
* 已存在用例名称
*/
private Set<String> testCaseNames;
private List<CustomFieldDao> customFields;
private Set<String> savedCustomIds;
private boolean isUseCustomId;
}

View File

@ -298,7 +298,6 @@ public class TestCaseService {
TestCaseWithBLOBs oldTestCase = testCaseMapper.selectByPrimaryKey(testCase.getId());
testCase.setId(UUID.randomUUID().toString());
testCase.setNum(oldTestCase.getNum());
testCase.setVersionId(testCase.getVersionId());
testCase.setCreateTime(System.currentTimeMillis());
testCase.setUpdateTime(System.currentTimeMillis());
testCase.setCreateUser(SessionUtils.getUserId());
@ -708,6 +707,10 @@ public class TestCaseService {
if (multipartFile == null) {
MSException.throwException(Translator.get("upload_fail"));
}
if (StringUtils.isBlank(request.getVersionId()) && StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Create.name())) {
// 创建如果没选版本就创建最新版本更新时没选就更新最近版本的用例
request.setVersionId(extProjectVersionMapper.getDefaultVersion(request.getProjectId()));
}
if (multipartFile.getOriginalFilename().endsWith(".xmind")) {
return testCaseXmindImport(multipartFile, request, httpRequest);
} else {
@ -723,11 +726,11 @@ public class TestCaseService {
private ExcelResponse getImportResponse(List<ExcelErrData<TestCaseExcelData>> errList, boolean isUpdated) {
ExcelResponse excelResponse = new ExcelResponse();
excelResponse.setIsUpdated(isUpdated);
//如果包含错误信息就导出错误信息
if (!errList.isEmpty()) {
excelResponse.setSuccess(false);
excelResponse.setErrList(errList);
excelResponse.setIsUpdated(isUpdated);
} else {
excelResponse.setSuccess(true);
}
@ -746,7 +749,9 @@ public class TestCaseService {
.collect(Collectors.toSet());
try {
XmindCaseParser xmindParser = new XmindCaseParser(this, request.getUserId(), projectId, testCaseNames, useCunstomId, request.getImportType());
request.setTestCaseNames(testCaseNames);
request.setUseCustomId(useCunstomId);
XmindCaseParser xmindParser = new XmindCaseParser(request);
errList = xmindParser.parse(multipartFile);
if (CollectionUtils.isEmpty(xmindParser.getNodePaths())
&& CollectionUtils.isEmpty(xmindParser.getTestCase())
@ -767,12 +772,12 @@ public class TestCaseService {
}
if (CollectionUtils.isNotEmpty(xmindParser.getTestCase())) {
// Collections.reverse(xmindParser.getTestCase());
this.saveImportData(xmindParser.getTestCase(), projectId);
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());
}
if (CollectionUtils.isNotEmpty(xmindParser.getUpdateTestCase())) {
this.updateImportData(xmindParser.getUpdateTestCase(), projectId);
this.updateImportData(xmindParser.getUpdateTestCase(), request);
names.addAll(xmindParser.getUpdateTestCase().stream().map(TestCase::getName).collect(Collectors.toList()));
ids.addAll(xmindParser.getUpdateTestCase().stream().map(TestCase::getId).collect(Collectors.toList()));
}
@ -784,7 +789,7 @@ public class TestCaseService {
if (CollectionUtils.isNotEmpty(continueCaseList) || CollectionUtils.isNotEmpty(xmindParser.getUpdateTestCase())) {
if (CollectionUtils.isNotEmpty(xmindParser.getUpdateTestCase())) {
continueCaseList.removeAll(xmindParser.getUpdateTestCase());
this.updateImportData(xmindParser.getUpdateTestCase(), projectId);
this.updateImportData(xmindParser.getUpdateTestCase(), request);
names = xmindParser.getTestCase().stream().map(TestCase::getName).collect(Collectors.toList());
ids = xmindParser.getTestCase().stream().map(TestCase::getId).collect(Collectors.toList());
}
@ -794,7 +799,7 @@ public class TestCaseService {
}
if (CollectionUtils.isNotEmpty(continueCaseList)) {
// Collections.reverse(continueCaseList);
this.saveImportData(continueCaseList, projectId);
this.saveImportData(continueCaseList, request);
names.addAll(continueCaseList.stream().map(TestCase::getName).collect(Collectors.toList()));
ids.addAll(continueCaseList.stream().map(TestCase::getId).collect(Collectors.toList()));
@ -809,7 +814,7 @@ public class TestCaseService {
LogUtil.error(e);
MSException.throwException(e.getMessage());
}
return getImportResponse(errList, false);
return getImportResponse(errList, true);
}
private ExcelResponse testCaseExcelImport(MultipartFile multipartFile, TestCaseImportRequest request,
@ -866,8 +871,12 @@ public class TestCaseService {
} else {
customFields = testCaseTemplate.getCustomFields();
}
TestCaseNoModelDataListener easyExcelListener = new TestCaseNoModelDataListener(request.isIgnore(), clazz, customFields, projectId, testCaseNames,
savedIds, userIds, useCunstomId, request.getImportType());
request.setUserIds(userIds);
request.setTestCaseNames(testCaseNames);
request.setCustomFields(customFields);
request.setSavedCustomIds(savedIds);
request.setUseCustomId(useCunstomId);
TestCaseNoModelDataListener easyExcelListener = new TestCaseNoModelDataListener(request, clazz);
//读取excel数据
EasyExcelFactory.read(multipartFile.getInputStream(), easyExcelListener).sheet().doRead();
@ -883,7 +892,8 @@ public class TestCaseService {
return getImportResponse(errList, isUpdated);
}
public void saveImportData(List<TestCaseWithBLOBs> testCases, String projectId) {
public void saveImportData(List<TestCaseWithBLOBs> testCases, TestCaseImportRequest request) {
String projectId = request.getProjectId();
Map<String, String> nodePathMap = testCaseNodeService.createNodeByTestCases(testCases, projectId);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
Project project = projectService.getProjectById(projectId);
@ -909,6 +919,9 @@ public class TestCaseService {
testcase.setReviewStatus(TestCaseReviewStatus.Prepare.name());
testcase.setStatus(TestCaseReviewStatus.Prepare.name());
testcase.setOrder(nextOrder);
testcase.setRefId(testcase.getId());
testcase.setVersionId(request.getVersionId());
testcase.setLatest(true);
mapper.insert(testcase);
nextOrder += ServiceUtils.ORDER_STEP;
}
@ -919,34 +932,35 @@ public class TestCaseService {
}
}
public void updateImportData(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);
try {
if (!testCases.isEmpty()) {
AtomicInteger sort = new AtomicInteger();
AtomicInteger num = new AtomicInteger();
num.set(getNextNum(projectId) + testCases.size());
testCases.forEach(testcase -> {
TestCaseWithBLOBs oldCase = testCaseMapper.selectByPrimaryKey(testcase.getId());
String customFieldStr = this.updateCustomField(oldCase.getCustomFields(), testcase.getPriority());
testcase.setUpdateTime(System.currentTimeMillis());
testcase.setNodeId(nodePathMap.get(testcase.getNodePath()));
testcase.setSort(sort.getAndIncrement());
if (testcase.getNum() == null) {
testcase.setNum(num.decrementAndGet());
}
testcase.setReviewStatus(TestCaseReviewStatus.Prepare.name());
testcase.setCustomFields(customFieldStr);
mapper.updateByPrimaryKeySelective(testcase);
});
sqlSession.flushStatements();
}
} finally {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
}
// public void updateImportData(List<TestCaseWithBLOBs> testCases, TestCaseImportRequest request) {
// String projectId = request.getProjectId();
// Map<String, String> nodePathMap = testCaseNodeService.createNodeByTestCases(testCases, projectId);
// SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
// TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class);
// try {
// if (!testCases.isEmpty()) {
// AtomicInteger sort = new AtomicInteger();
// AtomicInteger num = new AtomicInteger();
// num.set(getNextNum(projectId) + testCases.size());
// testCases.forEach(testcase -> {
// TestCaseWithBLOBs oldCase = testCaseMapper.selectByPrimaryKey(testcase.getId());
// String customFieldStr = this.updateCustomField(oldCase.getCustomFields(), testcase.getPriority());
// testcase.setUpdateTime(System.currentTimeMillis());
// testcase.setNodeId(nodePathMap.get(testcase.getNodePath()));
// testcase.setSort(sort.getAndIncrement());
// if (testcase.getNum() == null) {
// testcase.setNum(num.decrementAndGet());
// }
// testcase.setReviewStatus(TestCaseReviewStatus.Prepare.name());
// testcase.setCustomFields(customFieldStr);
// mapper.updateByPrimaryKeySelective(testcase);
// });
// sqlSession.flushStatements();
// }
// } finally {
// SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
// }
// }
private String updateCustomField(String customFields, String priority) {
try {
@ -971,68 +985,55 @@ public class TestCaseService {
* feat(测试跟踪):通过Excel导入导出时有ID字段可通过Excel导入来更新用例 (#1727)
*
* @param testCases
* @param projectId
* @param request
*/
public void updateImportDataCarryId(List<TestCaseWithBLOBs> testCases, String projectId) {
public void updateImportData(List<TestCaseWithBLOBs> testCases, TestCaseImportRequest request) {
String projectId = request.getProjectId();
List<TestCase> insertCases = new ArrayList<>();
Map<String, String> nodePathMap = testCaseNodeService.createNodeByTestCases(testCases, projectId);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class);
/*
获取用例的网页上所显示id数据库ID映射
*/
List<Integer> nums = testCases.stream()
.map(TestCase::getNum)
.collect(Collectors.toList());
TestCaseExample example = new TestCaseExample();
example.createCriteria().andNumIn(nums)
.andProjectIdEqualTo(projectId);
List<TestCase> testCasesList = testCaseMapper.selectByExample(example);
Map<Integer, String> numIdMap = testCasesList.stream()
.collect(Collectors.toMap(TestCase::getNum, TestCase::getId));
TestCaseExample.Criteria criteria = example.createCriteria();
criteria.andProjectIdEqualTo(projectId);
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(numIdMap.get(testcase.getNum()));
mapper.updateByPrimaryKeySelective(testcase);
});
if (request.isUseCustomId()) {
List<String> nums = testCases.stream()
.map(TestCase::getCustomNum)
.collect(Collectors.toList());
criteria.andCustomNumIn(nums);
} else {
List<Integer> nums = testCases.stream()
.map(TestCase::getNum)
.collect(Collectors.toList());
criteria.andNumIn(nums);
}
sqlSession.flushStatements();
if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
// 获取用例的网页上所显示id数据库ID映射
Map<Object, TestCase> customIdMap;
if (StringUtils.isBlank(request.getVersionId())) {
// 没选版本更新最新版本
criteria.andLatestEqualTo(true);
List<TestCase> testCasesList = testCaseMapper.selectByExample(example);
customIdMap = testCasesList.stream()
.collect(Collectors.toMap(i -> request.isUseCustomId() ? i.getCustomNum() : i.getNum(), i -> i, (k1, k2) -> k1));
} else {
List<TestCase> testCasesList = testCaseMapper.selectByExample(example);
customIdMap = testCasesList.stream()
.collect(Collectors.toMap(i -> request.isUseCustomId() ? i.getCustomNum() : i.getNum(),
i -> i, (k1, k2) -> {
// 查找出来多条选取当前版本的用例没有的话随便保存一条以便新建的时候设置对应的ref_id
if (k1.getVersionId().equals(request.getVersionId())) {
return k1;
} else if (k2.getVersionId().equals(request.getVersionId())) {
return k2;
}
return k1;
}));
}
}
/**
* 把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);
/*
获取用例的网页上所显示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, (k1, k2) -> k1));
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class);
try {
if (!testCases.isEmpty()) {
AtomicInteger sort = new AtomicInteger();
@ -1040,11 +1041,43 @@ public class TestCaseService {
testcase.setUpdateTime(System.currentTimeMillis());
testcase.setNodeId(nodePathMap.get(testcase.getNodePath()));
testcase.setSort(sort.getAndIncrement());
testcase.setId(customIdMap.get(testcase.getCustomNum()));
mapper.updateByPrimaryKeySelective(testcase);
TestCase dbCase = request.isUseCustomId() ? customIdMap.get(testcase.getCustomNum()) : customIdMap.get(testcase.getNum());
testcase.setId(dbCase.getId());
testcase.setRefId(dbCase.getId());
if (StringUtils.isNotBlank(request.getVersionId())) {
// 选了版本就更新到对应的版本
if (dbCase.getVersionId().equals(request.getVersionId())) {
mapper.updateByPrimaryKeySelective(testcase);
} else { // 没有对应的版本就新建对应版本用例
testcase.setCreateTime(System.currentTimeMillis());
testcase.setVersionId(request.getVersionId());
testcase.setId(UUID.randomUUID().toString());
testcase.setOrder(dbCase.getOrder());
testcase.setCreateUser(SessionUtils.getUserId());
testcase.setCreateUser(SessionUtils.getUserId());
testcase.setCreateUser(SessionUtils.getUserId());
testcase.setCustomNum(dbCase.getCustomNum());
testcase.setNum(dbCase.getNum());
testcase.setType(dbCase.getType());
if (StringUtils.isBlank(testcase.getStatus())) {
testcase.setStatus(TestCaseReviewStatus.Prepare.name());
}
testcase.setReviewStatus(TestCaseReviewStatus.Prepare.name());
insertCases.add(testcase); // 由于是批处理这里先保存最后再执行
mapper.insert(testcase);
}
} else {
mapper.updateByPrimaryKeySelective(testcase);
}
});
}
sqlSession.flushStatements();
insertCases.forEach(item -> {
checkAndSetLatestVersion(item.getRefId(), request.getVersionId(), request.getProjectId());
});
} catch (Exception e) {
LogUtil.error(e);
} finally {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}

View File

@ -7,10 +7,12 @@ import com.google.common.collect.ImmutableMap;
import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.CommonBeanFactory;
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.request.testcase.TestCaseImportRequest;
import io.metersphere.track.service.TestCaseService;
import io.metersphere.xmind.parser.XmindParser;
import io.metersphere.xmind.parser.pojo.Attached;
@ -32,16 +34,10 @@ import java.util.regex.Pattern;
public class XmindCaseParser {
private TestCaseService testCaseService;
private String maintainer;
private String projectId;
/**
* 过程校验记录
*/
private DetailUtil process;
/**
* 已存在用例名称
*/
private Set<String> testCaseNames;
/**
* 转换后的案例信息
*/
@ -64,15 +60,11 @@ public class XmindCaseParser {
private List<String> errorPath;
private boolean isUseCustomId;
private TestCaseImportRequest request;
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;
this.testCaseNames = testCaseNames;
public XmindCaseParser(TestCaseImportRequest request) {
this.testCaseService = CommonBeanFactory.getBean(TestCaseService.class);
this.request = request;
testCases = new LinkedList<>();
updateTestCases = new LinkedList<>();
compartDatas = new ArrayList<>();
@ -80,8 +72,6 @@ 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)";
@ -94,12 +84,12 @@ public class XmindCaseParser {
compartDatas.clear();
testCases.clear();
updateTestCases.clear();
testCaseNames.clear();
request.getTestCaseNames().clear();
nodePaths.clear();
}
public List<TestCaseWithBLOBs> getTestCase() {
if (StringUtils.equals(this.importType, FunctionCaseImportEnum.Create.name())) {
if (StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Create.name())) {
return this.testCases;
} else {
return new ArrayList<>();
@ -107,7 +97,7 @@ public class XmindCaseParser {
}
public List<TestCaseWithBLOBs> getUpdateTestCase() {
if (StringUtils.equals(this.importType, FunctionCaseImportEnum.Update.name())) {
if (StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Update.name())) {
return this.updateTestCases;
} else {
return new ArrayList<>();
@ -205,20 +195,6 @@ 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 (!priorityList.contains(data.getPriority())) {
validatePass = false;
@ -239,15 +215,17 @@ public class XmindCaseParser {
compartDatas.add(compartData);
String importType = request.getImportType();
String projectId = request.getProjectId();
boolean isUseCustomId = request.isUseCustomId();
//自定义ID判断
if (StringUtils.isEmpty(data.getCustomNum())) {
if (StringUtils.equals(this.importType, FunctionCaseImportEnum.Update.name())) {
validatePass = false;
if (StringUtils.equals(importType, FunctionCaseImportEnum.Update.name())) {
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;
}
@ -255,10 +233,10 @@ public class XmindCaseParser {
}
//判断更新
if (StringUtils.equals(this.importType, FunctionCaseImportEnum.Update.name())) {
String checkResult = null;
if (StringUtils.equals(importType, FunctionCaseImportEnum.Update.name())) {
String checkResult;
if (isUseCustomId) {
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum().toString(), projectId);
checkResult = testCaseService.checkCustomIdExist(data.getCustomNum(), projectId);
} else {
checkResult = testCaseService.checkIdExist(Integer.parseInt(data.getCustomNum()), projectId);
}
@ -361,8 +339,8 @@ public class XmindCaseParser {
*/
private void formatTestCase(String title, String nodePath, List<Attached> attacheds) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
testCase.setProjectId(projectId);
testCase.setMaintainer(maintainer);
testCase.setProjectId(request.getProjectId());
testCase.setMaintainer(request.getUserId());
testCase.setPriority(priorityList.get(0));
testCase.setMethod("manual");
testCase.setType("functional");
@ -416,7 +394,7 @@ public class XmindCaseParser {
});
}
testCase.setRemark(rc.toString());
if (isUseCustomId || StringUtils.equals(importType, FunctionCaseImportEnum.Update.name())) {
if (request.isUseCustomId() || StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Update.name())) {
testCase.setCustomNum(customId.toString());
}

View File

@ -53,6 +53,7 @@
:file-list="fileList">
<template v-slot:trigger>
<el-button size="mini" type="success" plain>{{$t('commons.please_select')}}</el-button>
<version-select style="margin-left: 25px" v-xpack :project-id="projectId" @changeVersion="changeVersion"/>
</template>
<template v-slot:tip>
<div v-if="isExcel" class="el-upload__tip">{{$t('test_track.case.import.upload_limit')}}</div>
@ -61,11 +62,6 @@
</el-upload>
</el-row>
<el-row class="import-row">
<el-button :disabled="!lastFile" size="small" @click="upload(false)">{{$t('test_track.case.import.click_upload')}}</el-button>
<version-select v-xpack :project-id="projectId" @changeVersion="changeVersion"/>
</el-row>
<el-row>
<ul>
<li v-for="errFile in errList" :key="errFile.rowNum">
@ -74,11 +70,15 @@
</ul>
</el-row>
<el-row style="text-align: right" v-if="showContinueBtn">
<div style="margin-right: 20px;margin-bottom: 10px;">
<el-row style="text-align: right" >
<div v-if="showContinueBtn" style="margin-right: 20px;margin-bottom: 10px;">
<el-checkbox :value="true" :disabled="true">{{ $t('test_track.case.import.ignore_error') }}</el-checkbox>
</div>
<el-button type="primary" @click="upload(true)">{{ $t('test_track.case.import.continue_upload') }}
<el-button v-if="showContinueBtn" type="primary" @click="upload(true)">
{{ $t('test_track.case.import.continue_upload') }}
</el-button>
<el-button v-if="!showContinueBtn" type="primary" @click="upload(false)">
{{ $t('test_track.case.import.click_upload') }}
</el-button>
<el-button @click="$emit('close')">{{ $t('commons.cancel') }}</el-button>
</el-row>
@ -104,7 +104,7 @@ export default {
showContinueBtn: false,
uploadIgnoreError: false,
lastFile: null,
version: null
versionId: null
}
},
created() {
@ -177,7 +177,7 @@ export default {
projectId: getCurrentProjectID(),
userId: getCurrentUserId(),
importType: this.importType,
version: this.version,
versionId: this.versionId,
ignore: isIgnore
};
this.result = this.$fileUpload('/test/case/import',
@ -198,7 +198,7 @@ export default {
});
},
changeVersion(data) {
this.version = data;
this.versionId = data;
}
}
}