From 5a3fb68149a36e4b506c59abfe112c3a3ba10f14 Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Mon, 15 Aug 2022 10:47:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=8B=E8=AF=95=E8=B7=9F=E8=B8=AA):=20?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E5=8A=9F=E8=83=BD=E7=94=A8=E4=BE=8B=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=90=88=E5=B9=B6=E5=8D=95=E5=85=83=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/constants/TestCaseImportFiled.java | 28 + .../excel/domain/ExcelMergeInfo.java | 38 ++ .../excel/domain/TestCaseExcelData.java | 41 ++ .../excel/domain/TestCaseExcelDataCn.java | 72 +- .../excel/domain/TestCaseExcelDataTw.java | 81 +-- .../excel/domain/TestCaseExcelDataUs.java | 79 +-- .../FunctionCaseMergeWriteHandler.java | 57 ++ .../listener/TestCaseNoModelDataListener.java | 637 ++++++++++-------- .../TestCasePretreatmentListener.java | 40 ++ .../excel/utils/EasyExcelExporter.java | 94 +-- .../track/controller/TestCaseController.java | 2 +- .../io/metersphere/track/dto/TestCaseDTO.java | 1 - .../track/service/TestCaseService.java | 442 ++++++------ .../main/resources/i18n/messages.properties | 6 - .../resources/i18n/messages_en_US.properties | 6 - .../resources/i18n/messages_zh_CN.properties | 6 - .../resources/i18n/messages_zh_TW.properties | 6 - 17 files changed, 821 insertions(+), 815 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/excel/constants/TestCaseImportFiled.java create mode 100644 backend/src/main/java/io/metersphere/excel/domain/ExcelMergeInfo.java create mode 100644 backend/src/main/java/io/metersphere/excel/handler/FunctionCaseMergeWriteHandler.java create mode 100644 backend/src/main/java/io/metersphere/excel/listener/TestCasePretreatmentListener.java diff --git a/backend/src/main/java/io/metersphere/excel/constants/TestCaseImportFiled.java b/backend/src/main/java/io/metersphere/excel/constants/TestCaseImportFiled.java new file mode 100644 index 0000000000..5c7d27bc3e --- /dev/null +++ b/backend/src/main/java/io/metersphere/excel/constants/TestCaseImportFiled.java @@ -0,0 +1,28 @@ +package io.metersphere.excel.constants; + + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public enum TestCaseImportFiled { + + ID("ID", "ID", "ID"), NAME("用例名称", "用例名稱", "Name"), MODULE("所属模块", "所屬模塊", "Module"), + TAG("标签", "標簽", "Tag"), PREREQUISITE("前置条件", "前置條件", "Prerequisite"), REMARK("备注", "備註", "Remark"), + STEP_DESC("步骤描述", "步驟描述", "Step description"), STEP_RESULT("预期结果", "預期結果", "Step result"), STEP_MODEL("编辑模式", "編輯模式", "Edit Model"), + STATUS("用例状态", "用例狀態", "Case status"), MAINTAINER("责任人", "責任人", "Maintainer"), PRIORITY("用例等级", "用例等級", "Priority"); + + private Map filedLangMap; + + public Map getFiledLangMap() { + return this.filedLangMap; + } + + TestCaseImportFiled(String zn, String chineseTw, String us) { + this.filedLangMap = new HashMap<>() {{ + put(Locale.SIMPLIFIED_CHINESE, zn); + put(Locale.TRADITIONAL_CHINESE, chineseTw); + put(Locale.US, us); + }}; + } +} diff --git a/backend/src/main/java/io/metersphere/excel/domain/ExcelMergeInfo.java b/backend/src/main/java/io/metersphere/excel/domain/ExcelMergeInfo.java new file mode 100644 index 0000000000..86270165ed --- /dev/null +++ b/backend/src/main/java/io/metersphere/excel/domain/ExcelMergeInfo.java @@ -0,0 +1,38 @@ +package io.metersphere.excel.domain; + +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; + +@Setter +@Getter +public class ExcelMergeInfo implements Comparable { + + /** + * 合并单元格的第一行 + */ + private Integer firstRowIndex; + /** + * 合并单元格的最后一行 + */ + private Integer lastRowIndex; + /** + * 合并单元格的第一列,不考虑同一行合并单元格的情况 + */ + private Integer firstColumnIndex; + + /** + * 根据 firstRowIndex, firstColumnIndex 重写 compareTo + * 使用 TreeSet 按 Excel 表格顺序查找时,可以优化效率 + * @param o + * @return + */ + @Override + public int compareTo(@NotNull ExcelMergeInfo o) { + int compare = Integer.compare(this.getFirstRowIndex(), o.getFirstRowIndex()); + if (compare == 0) { + return Integer.compare(this.getFirstColumnIndex(), o.getFirstColumnIndex()); + } + return compare; + } +} diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java index e67d9ca299..f15c985532 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java @@ -2,8 +2,11 @@ package io.metersphere.excel.domain; import com.alibaba.excel.annotation.ExcelIgnore; import io.metersphere.dto.CustomFieldDao; +import io.metersphere.excel.constants.TestCaseImportFiled; import lombok.Getter; import lombok.Setter; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import java.util.*; @@ -47,7 +50,45 @@ public class TestCaseExcelData { @ExcelIgnore Map customDatas = new LinkedHashMap<>(); + @ExcelIgnore + List mergeStepDesc; + @ExcelIgnore + List mergeStepResult; + public List> getHead(boolean needNum, List customFields) { return new ArrayList<>(); } + + public List> getHead(boolean needNum, List customFields, Locale lang) { + List> heads = new ArrayList<>(); + TestCaseImportFiled[] fields = TestCaseImportFiled.values(); + for (TestCaseImportFiled field : fields) { + heads.add(Arrays.asList(field.getFiledLangMap().get(lang))); + } + + Iterator> iterator = heads.iterator(); + + while (iterator.hasNext()) { + List head = iterator.next(); + if (StringUtils.equals(head.get(0), TestCaseImportFiled.ID.getFiledLangMap().get(lang)) && !needNum) { + iterator.remove(); + break; + } + } + + if (CollectionUtils.isNotEmpty(customFields)) { + for (CustomFieldDao dto : customFields) { + if (StringUtils.equalsAny(dto.getName(), + TestCaseImportFiled.PRIORITY.getFiledLangMap().get(Locale.SIMPLIFIED_CHINESE), + TestCaseImportFiled.STATUS.getFiledLangMap().get(Locale.SIMPLIFIED_CHINESE), + TestCaseImportFiled.MAINTAINER.getFiledLangMap().get(Locale.SIMPLIFIED_CHINESE).concat("(ID)"))) { + continue; + } + heads.add(new ArrayList<>() {{ + add(dto.getName()); + }}); + } + } + return heads; + } } diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataCn.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataCn.java index 9b86142316..83b34eec04 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataCn.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataCn.java @@ -5,23 +5,17 @@ import com.alibaba.excel.annotation.write.style.ColumnWidth; import io.metersphere.dto.CustomFieldDao; import io.metersphere.excel.annotation.NotRequired; import lombok.Data; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; -import java.util.ArrayList; import java.util.List; +import java.util.Locale; @Data @ColumnWidth(15) public class TestCaseExcelDataCn extends TestCaseExcelData { -// @ExcelProperty("ID") -// @NotRequired -// private Integer num; - @ColumnWidth(50) @ExcelProperty("ID") @NotRequired @@ -45,11 +39,6 @@ public class TestCaseExcelDataCn extends TestCaseExcelData { @Length(min = 0, max = 1000) private String tags; -// @NotBlank(message = "{cannot_be_null}") -// @ExcelProperty("测试方式") -// @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}") -// private String method; - @ColumnWidth(50) @ExcelProperty("前置条件") private String prerequisite; @@ -86,63 +75,6 @@ public class TestCaseExcelDataCn extends TestCaseExcelData { @Override public List> getHead(boolean needNum, List customFields) { - List> returnList = new ArrayList<>(); - if (needNum) { - List list = new ArrayList<>(); - list.add("ID"); - returnList.add(list); - } - - List list1 = new ArrayList<>(); - list1.add("用例名称"); - returnList.add(list1); - - List list2 = new ArrayList<>(); - list2.add("所属模块"); - returnList.add(list2); - - List list3 = new ArrayList<>(); - list3.add("标签"); - returnList.add(list3); - - List list4 = new ArrayList<>(); - list4.add("前置条件"); - returnList.add(list4); - - List list5 = new ArrayList<>(); - list5.add("备注"); - returnList.add(list5); - - List list6 = new ArrayList<>(); - list6.add("步骤描述"); - returnList.add(list6); - - List list7 = new ArrayList<>(); - list7.add("预期结果"); - returnList.add(list7); - - List list8 = new ArrayList<>(); - list8.add("编辑模式"); - returnList.add(list8); - - List list9 = new ArrayList<>(); - list9.add("用例等级"); - returnList.add(list9); - - if (CollectionUtils.isNotEmpty(customFields)) { - for (CustomFieldDao dto : customFields) { - if (StringUtils.equals(dto.getName(), "用例等级")) { - continue; - } - List list = new ArrayList<>(); - if (StringUtils.equals(dto.getName(), "责任人")) { - list.add("责任人(ID)"); - } else { - list.add(dto.getName()); - } - returnList.add(list); - } - } - return returnList; + return super.getHead(needNum, customFields, Locale.SIMPLIFIED_CHINESE); } } diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataTw.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataTw.java index 314e885af5..c7503033f8 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataTw.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataTw.java @@ -5,23 +5,17 @@ import com.alibaba.excel.annotation.write.style.ColumnWidth; import io.metersphere.dto.CustomFieldDao; import io.metersphere.excel.annotation.NotRequired; import lombok.Data; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; -import java.util.ArrayList; import java.util.List; +import java.util.Locale; @Data @ColumnWidth(15) public class TestCaseExcelDataTw extends TestCaseExcelData { -// @ExcelProperty("ID") -// @NotRequired -// private Integer num; - @ColumnWidth(50) @ExcelProperty("ID") @NotRequired @@ -45,11 +39,6 @@ public class TestCaseExcelDataTw extends TestCaseExcelData { @Length(min = 0, max = 1000) private String tags; -// @NotBlank(message = "{cannot_be_null}") -// @ExcelProperty("測試方式") -// @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}") -// private String method; - @ColumnWidth(50) @ExcelProperty("前置條件") private String prerequisite; @@ -81,75 +70,11 @@ public class TestCaseExcelDataTw extends TestCaseExcelData { @Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}") private String priority; - @ExcelProperty("維護人(ID)") + @ExcelProperty("責任人(ID)") private String maintainer; @Override public List> getHead(boolean needNum, List customFields) { - List> returnList = new ArrayList<>(); - if (needNum) { - List list = new ArrayList<>(); - list.add("ID"); - returnList.add(list); - } - - List list1 = new ArrayList<>(); - list1.add("用例名稱"); - returnList.add(list1); - - List list2 = new ArrayList<>(); - list2.add("所屬模塊"); - returnList.add(list2); - - List list3 = new ArrayList<>(); - list3.add("標簽"); - returnList.add(list3); - - List list4 = new ArrayList<>(); - list4.add("前置條件"); - returnList.add(list4); - - List list5 = new ArrayList<>(); - list5.add("備註"); - returnList.add(list5); - - List list6 = new ArrayList<>(); - list6.add("步驟描述"); - returnList.add(list6); - - List list7 = new ArrayList<>(); - list7.add("預期結果"); - returnList.add(list7); - - List list8 = new ArrayList<>(); - list8.add("編輯模式"); - returnList.add(list8); - - List list9 = new ArrayList<>(); - list9.add("用例等級"); - returnList.add(list9); - - List list10 = new ArrayList<>(); - list10.add("用例狀態"); - returnList.add(list10); - - if (CollectionUtils.isNotEmpty(customFields)) { - for (CustomFieldDao dto : customFields) { - if (StringUtils.equals(dto.getName(), "用例等級")) { - continue; - } - if (StringUtils.equals(dto.getName(), "用例状态")) { - continue; - } - List list = new ArrayList<>(); - if (StringUtils.equals(dto.getName(), "责任人")) { - list.add("維護人(ID)"); - } else { - list.add(dto.getName()); - } - returnList.add(list); - } - } - return returnList; + return super.getHead(needNum, customFields, Locale.TRADITIONAL_CHINESE); } } diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java index eabe9bd213..961c58a109 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java @@ -5,24 +5,18 @@ import com.alibaba.excel.annotation.write.style.ColumnWidth; import io.metersphere.dto.CustomFieldDao; import io.metersphere.excel.annotation.NotRequired; import lombok.Data; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; -import java.util.ArrayList; import java.util.List; +import java.util.Locale; @Data @ColumnWidth(15) public class TestCaseExcelDataUs extends TestCaseExcelData { -// @ExcelProperty("ID") -// @NotRequired -// private Integer num; - @ColumnWidth(50) @ExcelProperty("ID") @NotRequired @@ -46,11 +40,6 @@ public class TestCaseExcelDataUs extends TestCaseExcelData { @Length(min = 0, max = 1000) private String tags; -// @NotBlank(message = "{cannot_be_null}") -// @ExcelProperty("Method") -// @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}") -// private String method; - @ColumnWidth(50) @ExcelProperty("Prerequisite") private String prerequisite; @@ -86,70 +75,6 @@ public class TestCaseExcelDataUs extends TestCaseExcelData { @Override public List> getHead(boolean needNum, List customFields) { - List> returnList = new ArrayList<>(); - if (needNum) { - List list = new ArrayList<>(); - list.add("ID"); - returnList.add(list); - } - - List list1 = new ArrayList<>(); - list1.add("Name"); - returnList.add(list1); - - List list2 = new ArrayList<>(); - list2.add("Module"); - returnList.add(list2); - - List list3 = new ArrayList<>(); - list3.add("Tag"); - returnList.add(list3); - - List list4 = new ArrayList<>(); - list4.add("Prerequisite"); - returnList.add(list4); - - List list5 = new ArrayList<>(); - list5.add("Remark"); - returnList.add(list5); - - List list6 = new ArrayList<>(); - list6.add("Step description"); - returnList.add(list6); - - List list7 = new ArrayList<>(); - list7.add("Step result"); - returnList.add(list7); - - List list8 = new ArrayList<>(); - list8.add("Edit Model"); - returnList.add(list8); - - List list9 = new ArrayList<>(); - list9.add("Priority"); - returnList.add(list9); - - List list10 = new ArrayList<>(); - list10.add("Case status"); - returnList.add(list10); - - if (CollectionUtils.isNotEmpty(customFields)) { - for (CustomFieldDao dto : customFields) { - if (StringUtils.equals(dto.getName(), "用例等级")) { - continue; - } - if (StringUtils.equals(dto.getName(), "用例状态")) { - continue; - } - List list = new ArrayList<>(); - if (StringUtils.equals(dto.getName(), "责任人")) { - list.add("Maintainer(ID)"); - } else { - list.add(dto.getName()); - } - returnList.add(list); - } - } - return returnList; + return super.getHead(needNum, customFields, Locale.US); } } diff --git a/backend/src/main/java/io/metersphere/excel/handler/FunctionCaseMergeWriteHandler.java b/backend/src/main/java/io/metersphere/excel/handler/FunctionCaseMergeWriteHandler.java new file mode 100644 index 0000000000..21561c8a8f --- /dev/null +++ b/backend/src/main/java/io/metersphere/excel/handler/FunctionCaseMergeWriteHandler.java @@ -0,0 +1,57 @@ +package io.metersphere.excel.handler; + +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; +import io.metersphere.excel.constants.TestCaseImportFiled; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.util.List; +import java.util.Map; + +public class FunctionCaseMergeWriteHandler implements RowWriteHandler { + + /** + * 存储需要合并单元格的信息,key 是需要合并的第一条的行号,值是需要合并多少行 + */ + Map rowMergeInfo; + List> headList; + int stepDescRowIndex; + int stepResultRowIndex; + + public FunctionCaseMergeWriteHandler(Map rowMergeInfo, List> headList) { + this.rowMergeInfo = rowMergeInfo; + this.headList = headList; + for (int i = 0; i < headList.size(); i++) { + List list = headList.get(i); + for (String head : list) { + if (TestCaseImportFiled.STEP_DESC.getFiledLangMap().values().contains(head)) { + stepDescRowIndex = i; + } else if (TestCaseImportFiled.STEP_RESULT.getFiledLangMap().values().contains(head)) { + stepResultRowIndex = i; + } + } + } + } + + @Override + public void afterRowDispose(RowWriteHandlerContext context) { + if (context.getHead() || context.getRelativeRowIndex() == null) { + return; + } + + Integer mergeCount = rowMergeInfo.get(context.getRowIndex()); + + if (mergeCount == null || mergeCount <= 0) { + return; + } + + for (int i = 0; i < headList.size(); i++) { + // 除了描述其他数据合并多行 + if (i != stepDescRowIndex && i != stepResultRowIndex) { + CellRangeAddress cellRangeAddress = + new CellRangeAddress(context.getRowIndex(), context.getRowIndex() + mergeCount - 1, i, i); + context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(cellRangeAddress); + } + } + } +} diff --git a/backend/src/main/java/io/metersphere/excel/listener/TestCaseNoModelDataListener.java b/backend/src/main/java/io/metersphere/excel/listener/TestCaseNoModelDataListener.java index 20c8efd916..c36d986898 100644 --- a/backend/src/main/java/io/metersphere/excel/listener/TestCaseNoModelDataListener.java +++ b/backend/src/main/java/io/metersphere/excel/listener/TestCaseNoModelDataListener.java @@ -9,11 +9,16 @@ import io.metersphere.base.domain.TestCase; import io.metersphere.base.domain.TestCaseWithBLOBs; import io.metersphere.commons.constants.TestCaseConstants; import io.metersphere.commons.exception.MSException; -import io.metersphere.commons.utils.*; +import io.metersphere.commons.utils.BeanUtils; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.commons.utils.SessionUtils; import io.metersphere.dto.CustomFieldDao; import io.metersphere.dto.CustomFieldOption; import io.metersphere.excel.annotation.NotRequired; +import io.metersphere.excel.constants.TestCaseImportFiled; import io.metersphere.excel.domain.ExcelErrData; +import io.metersphere.excel.domain.ExcelMergeInfo; import io.metersphere.excel.domain.TestCaseExcelData; import io.metersphere.excel.domain.TestCaseExcelDataFactory; import io.metersphere.excel.utils.ExcelValidateHelper; @@ -23,11 +28,11 @@ import io.metersphere.track.request.testcase.TestCaseImportRequest; import io.metersphere.track.service.TestCaseService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -48,7 +53,6 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener headMap; private Map excelHeadToFieldNameDic = new HashMap<>(); - /** * 每隔2000条存储数据库,然后清理list ,方便内存回收 */ @@ -71,13 +75,36 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener mergeInfoSet; + + // 存储当前合并的一条完整数据,其中步骤没有合并是多行 + private TestCaseExcelData currentMergeData; + + private static final String ERROR_MSG_SEPARATOR = ";"; + + /** + * 标记下当前遍历的行是不是有合并单元格 + */ + private Boolean isMergeRow; + + /** + * 标记下当前遍历的行是不是合并单元格的最后一行 + */ + private Boolean isMergeLastRow; + + /** + * 存储合并单元格对应的数据,key 为重写了 compareTo 的 ExcelMergeInfo + */ + private HashMap mergeCellDataMap = new HashMap<>(); + public boolean isUpdated() { return isUpdated; } - public TestCaseNoModelDataListener(TestCaseImportRequest request, Class c) { + public TestCaseNoModelDataListener(TestCaseImportRequest request, Class c, Set mergeInfoSet) { + this.mergeInfoSet = mergeInfoSet; this.excelDataClass = c; - this.testCaseService = (TestCaseService) CommonBeanFactory.getBean("testCaseService"); + this.testCaseService = CommonBeanFactory.getBean(TestCaseService.class); customIds = new HashSet<>(); this.request = request; @@ -89,11 +116,11 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener data, AnalysisContext analysisContext) { - String errMsg; + Integer rowIndex = analysisContext.readRowHolder().getRowIndex(); - String updateMsg = "update_testcase"; - TestCaseExcelData testCaseExcelData = this.parseDataToModel(data); + + handleMergeData(data, rowIndex); + + TestCaseExcelData testCaseExcelData; + // 读取名称列,如果该列是合并单元格,则读取多行数据后合并步骤 + if (this.isMergeRow) { + if (currentMergeData == null) { + // 如果是合并单元格的首行 + testCaseExcelData = this.parseDataToModel(data); + testCaseExcelData.setMergeStepDesc(new ArrayList<>() {{ + add(testCaseExcelData.getStepDesc()); + }}); + testCaseExcelData.setMergeStepResult(new ArrayList<>() {{ + add(testCaseExcelData.getStepResult()); + }}); + // 记录下数据并返回 + this.currentMergeData = testCaseExcelData; + return; + } else { + // 获取存储的数据,并添加多个步骤 + this.currentMergeData.getMergeStepDesc() + .add(data.get(getStepDescColIndex())); + this.currentMergeData.getMergeStepResult() + .add(data.get(getStepResultColIndex())); + // 是最后一行的合并单元格,保存并清空 currentMergeData,走之后的逻辑 + if (this.isMergeLastRow) { + testCaseExcelData = this.currentMergeData; + this.currentMergeData = null; + } else { + return; + } + } + } else { + testCaseExcelData = this.parseDataToModel(data); + } + + StringBuilder errMsg; try { //根据excel数据实体中的javax.validation + 正则表达式来校验excel数据 - errMsg = ExcelValidateHelper.validateEntity(testCaseExcelData); + errMsg = new StringBuilder(ExcelValidateHelper.validateEntity(testCaseExcelData)); //自定义校验规则 if (StringUtils.isEmpty(errMsg)) { - errMsg = validate(testCaseExcelData, errMsg); + validate(testCaseExcelData, errMsg); } } catch (NoSuchFieldException e) { - errMsg = Translator.get("parse_data_error"); + errMsg = new StringBuilder(Translator.get("parse_data_error")); LogUtil.error(e.getMessage(), e); } if (!StringUtils.isEmpty(errMsg)) { - - //如果errMsg只有"update testcase",说明用例待更新 - if (!errMsg.equals(updateMsg)) { - ExcelErrData excelErrData = new ExcelErrData(testCaseExcelData, rowIndex, - Translator.get("number") + " " + rowIndex + " " + Translator.get("row") + Translator.get("error") - + ":" + errMsg); - errList.add(excelErrData); - } + ExcelErrData excelErrData = new ExcelErrData(testCaseExcelData, rowIndex, + Translator.get("number") + .concat(" ") + .concat(rowIndex.toString()).concat(" ") + .concat(Translator.get("row")) + .concat(Translator.get("error")) + .concat(":") + .concat(errMsg.toString())); + errList.add(excelErrData); } else { - list.add(testCaseExcelData); + if (isCreateModel()) { + list.add(testCaseExcelData); + } } - if (list.size() > BATCH_COUNT) { + if (list.size() > BATCH_COUNT || updateList.size() > BATCH_COUNT) { saveData(); list.clear(); + updateList.clear(); } } - public String validate(TestCaseExcelData data, String errMsg) { - Set savedCustomIds = request.getSavedCustomIds(); - String importType = request.getImportType(); - StringBuilder stringBuilder = new StringBuilder(errMsg); - if (request.isUseCustomId() || StringUtils.equals(importType, FunctionCaseImportEnum.Update.name())) { - if (data.getCustomNum() == null) { - stringBuilder.append(Translator.get("id_required") + ";"); - } else { - String customId = data.getCustomNum(); - if (StringUtils.isEmpty(customId)) { - stringBuilder.append(Translator.get("id_required") + ";"); - } else if (customIds.contains(customId.toLowerCase())) { - stringBuilder.append(Translator.get("id_repeat_in_table") + ";"); - } else if (StringUtils.equals(FunctionCaseImportEnum.Create.name(), importType) && savedCustomIds.contains(customId)) { - stringBuilder.append(Translator.get("custom_num_is_exist") + ";"); - } else if (StringUtils.equals(FunctionCaseImportEnum.Update.name(), importType) && !savedCustomIds.contains(customId)) { - stringBuilder.append(Translator.get("custom_num_is_not_exist") + ";"); - } else { - customIds.add(customId.toLowerCase()); + /** + * 处理合并单元格 + * + * @param data + * @param rowIndex + */ + private void handleMergeData(Map data, Integer rowIndex) { + this.isMergeRow = false; + this.isMergeLastRow = false; + data.keySet().forEach(col -> { + Iterator iterator = mergeInfoSet.iterator(); + while (iterator.hasNext()) { + ExcelMergeInfo mergeInfo = iterator.next(); + // 如果单元格的行号在合并单元格的范围之间,并且列号相等,说明该单元格是合并单元格中的一部分 + if (mergeInfo.getFirstRowIndex() <= rowIndex && rowIndex <= mergeInfo.getLastRowIndex() + && col.equals(mergeInfo.getFirstColumnIndex())) { + // 根据名称列是否是合并单元格判断是不是同一条用例 + if (getNameColIndex().equals(col)) { + this.isMergeRow = true; + } + // 如果是合并单元格的第一个cell,则把这个单元格的数据存起来 + if (rowIndex.equals(mergeInfo.getFirstRowIndex())) { + if (StringUtils.isNotBlank(data.get(col))) { + mergeCellDataMap.put(mergeInfo, data.get(col)); + } + } else { + // 非第一个,获取存储的数据填充 + String cellData = mergeCellDataMap.get(mergeInfo); + if (StringUtils.isNotBlank(cellData)) { + data.put(col, cellData); + } + // 如果合并单元格的最后一个单元格,标记下 + if (rowIndex == mergeInfo.getLastRowIndex()) { + // 根据名称列是否是合并单元格判断是不是同一条用例 + if (getNameColIndex().equals(col)) { + this.isMergeLastRow = true; + } + } + } + } else if (rowIndex > mergeInfo.getLastRowIndex()) { + // TreeSet 按照行号排序了 + // 清除掉上一次已经遍历完成的数据,提高查询效率 + iterator.remove(); } } - } + }); + } - String nodePath = data.getNodePath(); - //校验”所属模块" - if (nodePath != null) { - String[] nodes = nodePath.split("/"); - //校验模块深度 - if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) { - stringBuilder.append(Translator.get("test_case_node_level_tip") + - TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; "); + public void validate(TestCaseExcelData data, StringBuilder errMsg) { + + validateCustomNum(data, errMsg); + + validateModule(data, errMsg); + + validateCustomField(data, errMsg); + + validateIdExist(data, errMsg); + + validateDbExist(data, errMsg); + } + + private void validateDbExist(TestCaseExcelData data, StringBuilder stringBuilder) { + if (request.getTestCaseNames().contains(data.getName())) { + TestCaseWithBLOBs testCase = new TestCaseWithBLOBs(); + BeanUtils.copyBean(testCase, data); + testCase.setProjectId(request.getProjectId()); + String steps = getSteps(data); + testCase.setSteps(steps); + testCase.setType(TestCaseConstants.Type.Functional.getValue()); + + boolean dbExist = testCaseService.exist(testCase); + boolean excelExist = false; + + if (dbExist) { + // db exist + stringBuilder.append( + Translator.get("test_case_already_exists") + .concat(":") + .concat(data.getName()) + .concat(ERROR_MSG_SEPARATOR)); + } else { + // @Data 重写了 equals 和 hashCode 方法 + excelExist = excelDataList.contains(data); } - //模块名不能为空 - for (int i = 0; i < nodes.length; i++) { - if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) { - stringBuilder.append(Translator.get("module_not_null") + "; "); - break; + + if (excelExist) { + // excel exist + stringBuilder.append( + Translator.get("test_case_already_exists_excel") + .concat(":") + .concat(data.getName()) + .concat(ERROR_MSG_SEPARATOR)); + } else { + if (!dbExist) { + excelDataList.add(data); } } - //增加字数校验,每一层不能超过100字 - for (int i = 0; i < nodes.length; i++) { - String nodeStr = nodes[i]; - if (StringUtils.isNotEmpty(nodeStr)) { - if (nodeStr.trim().length() > 100) { - stringBuilder.append(Translator.get("module") + Translator.get("test_track.length_less_than") + "100:" + nodeStr); - break; + + } else { + request.getTestCaseNames().add(data.getName()); + excelDataList.add(data); + } + } + + /** + * 校验Excel中是否有ID + * 有的话校验ID是否已在当前项目中存在,存在则更新用例, + * 不存在则继续校验看是否重复,不重复则新建用例 + * + * @param data + * @param stringBuilder + */ + @Nullable + private void validateIdExist(TestCaseExcelData data, StringBuilder stringBuilder) { + + //当前读取的数据有ID + if (null != data.getCustomNum()) { + if (isUpdateModel()) { + String checkResult = null; + if (request.isUseCustomId()) { + checkResult = testCaseService.checkCustomIdExist(data.getCustomNum(), request.getProjectId()); + } else { + int customNumId = -1; + try { + customNumId = Integer.parseInt(data.getCustomNum()); + } catch (Exception e) { + LogUtil.error(e); + } + if (customNumId < 0) { + stringBuilder.append(Translator.get("id_not_rightful")) + .append("[") + .append(data.getCustomNum()) + .append("]; "); + } else { + checkResult = testCaseService.checkIdExist(customNumId, request.getProjectId()); } } + //该ID在当前项目中存在 + if (null != checkResult) { + //如果前面所经过的校验都没报错 + if (StringUtils.isEmpty(stringBuilder)) { + data.setId(checkResult); + //将当前数据存入更新列表 + updateList.add(data); + } + } else { + // 该ID在当前数据库中不存在,应当继续校验用例是否重复, + // 在下面的校验过程中,num的值会被用于判断是否重复,所以应当先设置为null + data.setNum(null); + } } } + } + private boolean isUpdateModel() { + return StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Update.name()); + } + + private boolean isCreateModel() { + return StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Create.name()); + } + + private void validateCustomField(TestCaseExcelData data, StringBuilder stringBuilder) { //校验自定义必填字段 for (Map.Entry customEntry : customFieldsMap.entrySet()) { String customName = customEntry.getKey(); CustomFieldDao field = customEntry.getValue(); if (field.getRequired()) { - String value = null; + String value; if (StringUtils.equals(customName, "status")) { Map valueMap = new HashMap<>() {{ put("Prepare", Translator.get("test_case_status_prepare")); @@ -221,11 +399,14 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener TestCaseConstants.MAX_NODE_DEPTH + 1) { + stringBuilder.append(Translator.get("test_case_node_level_tip")) + .append(TestCaseConstants.MAX_NODE_DEPTH) + .append(Translator.get("test_case_node_level")) + .append("; "); + } + //模块名不能为空 + for (int i = 0; i < nodes.length; i++) { + if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) { + stringBuilder.append(Translator.get("module_not_null")) + .append(ERROR_MSG_SEPARATOR); + break; + } + } + //增加字数校验,每一层不能超过100字 + for (int i = 0; i < nodes.length; i++) { + String nodeStr = nodes[i]; + if (StringUtils.isNotEmpty(nodeStr)) { + if (nodeStr.trim().length() > 100) { + stringBuilder.append(Translator.get("module")) + .append(Translator.get("test_track.length_less_than")) + .append("100:") + .append(nodeStr); + break; + } } } - } + } - /* - 校验用例 - */ - if (request.getTestCaseNames().contains(data.getName())) { - TestCaseWithBLOBs testCase = new TestCaseWithBLOBs(); - BeanUtils.copyBean(testCase, data); - testCase.setProjectId(request.getProjectId()); - String steps = getSteps(data); - testCase.setSteps(steps); - testCase.setType("functional"); - - boolean dbExist = testCaseService.exist(testCase); - boolean excelExist = false; - - if (dbExist) { - // db exist - stringBuilder.append(Translator.get("test_case_already_exists") + ":" + data.getName() + "; "); + private void validateCustomNum(TestCaseExcelData data, StringBuilder stringBuilder) { + if (request.isUseCustomId() || isUpdateModel()) { + if (data.getCustomNum() == null) { + stringBuilder.append(Translator.get("id_required")) + .append(ERROR_MSG_SEPARATOR); } else { - // @Data 重写了 equals 和 hashCode 方法 - excelExist = excelDataList.contains(data); - } - - if (excelExist) { - // excel exist - stringBuilder.append(Translator.get("test_case_already_exists_excel") + ":" + data.getName() + "; "); - } else { - if (!dbExist) { - excelDataList.add(data); + String customId = data.getCustomNum(); + if (StringUtils.isEmpty(customId)) { + stringBuilder.append(Translator.get("id_required")) + .append(ERROR_MSG_SEPARATOR); + } else if (customIds.contains(customId.toLowerCase())) { + stringBuilder.append(Translator.get("id_repeat_in_table")) + .append(ERROR_MSG_SEPARATOR); + } else if (isCreateModel() && request.getSavedCustomIds().contains(customId)) { + stringBuilder.append(Translator.get("custom_num_is_exist")) + .append(ERROR_MSG_SEPARATOR); + } else if (isUpdateModel() && !request.getSavedCustomIds().contains(customId)) { + stringBuilder.append(Translator.get("custom_num_is_not_exist")) + .append(ERROR_MSG_SEPARATOR); + } else { + customIds.add(customId.toLowerCase()); } } - - } else { - request.getTestCaseNames().add(data.getName()); - excelDataList.add(data); } - return stringBuilder.toString(); } /** * 根据自定义字段的选项值获取对应的选项id + * * @param name * @param text * @param optionsStr @@ -394,33 +562,22 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener stepDescList = new ArrayList<>(); List stepResList = new ArrayList<>(); - ListUtils listUtils = new ListUtils<>(); Set rowNums = new HashSet<>(); if (data.getStepDesc() != null) { @@ -643,6 +762,27 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener row) { TestCaseExcelData data = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal(); for (Map.Entry headEntry : headMap.entrySet()) { @@ -712,64 +852,6 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener customEntry : customFieldsMap.entrySet()) { - String customName = customEntry.getKey(); - CustomFieldDao field = customEntry.getValue(); - - String value = null; - if (StringUtils.equals(customName, "status")) { - value = data.getStatus(); - } else if (StringUtils.equals(customName, "priority")) { - value = data.getPriority(); - } else if (StringUtils.equals(customName, "maintainer")) { - value = data.getMaintainer(); - } else { - value = data.getCustomDatas().get(customName); - } - if (StringUtils.isEmpty(value)) { - value = ""; - } - if (field.getType().equalsIgnoreCase("multipleSelect")) { - value = modifyMultipleSelectPattern(value); - } - JSONObject statusObj = new JSONObject(); - statusObj.put("id", UUID.randomUUID().toString()); - statusObj.put("name", field.getName()); - statusObj.put("value", value); - statusObj.put("customData", null); - statusObj.put("type", field.getType()); - customArr.add(statusObj); - } - return customArr.toJSONString(); - } - - /** - * 调整自定义多选下拉框格式,便于前端进行解析。 - * 例如对于:下拉值1,下拉值2。将调整为:["下拉值1","下拉值2"]。 - */ - public String modifyMultipleSelectPattern(String values) { - try { - if (StringUtils.isNotBlank(values)) { - JSONArray array = JSONArray.parseArray(values); - return array.toJSONString(); - } - return "[]"; - } catch (Exception e) { - if (values != null) { - Stream stringStream = Arrays.stream(values.split("[,;,;]")); //当标签值以中英文的逗号和分号分隔时才能正确解析 - List valueList = stringStream.map(multip -> multip = "\"" + multip + "\"") - .collect(Collectors.toList()); - String modifiedValues = StringUtils.join(valueList, ","); - modifiedValues = "[" + modifiedValues + "]"; - return modifiedValues; - } else { - return "[]"; - } - } - } - /** * @description: 获取注解里ExcelProperty的value */ @@ -801,13 +883,4 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener mergeInfoSet; + + public TestCasePretreatmentListener(Set mergeInfoSet) { + this.mergeInfoSet = mergeInfoSet; + } + + @Override + public void invoke(Object integerStringMap, AnalysisContext analysisContext) {} + + @Override + public void extra(CellExtra extra, AnalysisContext context) { + if (extra.getType() == CellExtraTypeEnum.MERGE) { + // 将合并单元格信息保留 + ExcelMergeInfo mergeInfo = new ExcelMergeInfo(); + BeanUtils.copyBean(mergeInfo, extra); + this.mergeInfoSet.add(mergeInfo); + } + } + + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) {} +} diff --git a/backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java b/backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java index 16337005aa..4c35374ac3 100644 --- a/backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java +++ b/backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java @@ -4,17 +4,15 @@ import com.alibaba.excel.EasyExcel; import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; -import io.metersphere.exception.ExcelException; -import org.apache.commons.collections.CollectionUtils; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; -import java.util.Set; +import java.util.Optional; public class EasyExcelExporter { @@ -25,58 +23,74 @@ public class EasyExcelExporter { } public void export(HttpServletResponse response, List data, String fileName, String sheetName) { - response.setContentType("application/vnd.ms-excel"); - response.setCharacterEncoding("utf-8"); + buildExportResponse(response, fileName); WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); contentWriteCellStyle.setWrapped(true); try { HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle); - response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx"); - EasyExcel.write(response.getOutputStream(), this.clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(data); - } catch (UnsupportedEncodingException e) { - LogUtil.error(e.getMessage(), e); - throw new ExcelException("Utf-8 encoding is not supported"); + EasyExcel.write(response.getOutputStream(), this.clazz) + .registerWriteHandler(horizontalCellStyleStrategy) + .sheet(sheetName) + .doWrite(data); } catch (IOException e) { - LogUtil.error(e.getMessage(), e); - throw new ExcelException("IO exception"); + LogUtil.error(e); + MSException.throwException(e.getMessage()); } } - public void exportByCustomWriteHandler(HttpServletResponse response, List> headList, List> data, String fileName, String sheetName) { - if(CollectionUtils.isEmpty(headList)){ - headList = new ArrayList<>(); - } - response.setContentType("application/vnd.ms-excel"); - response.setCharacterEncoding("utf-8"); + public void exportByCustomWriteHandler(HttpServletResponse response, List> headList, + List> data, String fileName, String sheetName) { + buildExportResponse(response, fileName); try { - response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx"); - EasyExcel.write(response.getOutputStream()).head(headList). - sheet(sheetName).doWrite(data); - } catch (UnsupportedEncodingException e) { - LogUtil.error(e.getMessage(), e); - throw new ExcelException("Utf-8 encoding is not supported"); + EasyExcel.write(response.getOutputStream()) + .head(Optional.ofNullable(headList).orElse(new ArrayList<>())) + .sheet(sheetName) + .doWrite(data); } catch (IOException e) { - LogUtil.error(e.getMessage(), e); - throw new ExcelException("IO exception"); + LogUtil.error(e); + MSException.throwException(e.getMessage()); } } - public void exportByCustomWriteHandler(HttpServletResponse response, List> headList, List> data, String fileName, String sheetName, WriteHandler writeHandler) { - if(CollectionUtils.isEmpty(headList)){ - headList = new ArrayList<>(); - } - response.setContentType("application/vnd.ms-excel"); - response.setCharacterEncoding("utf-8"); + public void buildExportResponse(HttpServletResponse response, String fileName) { try { + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding("utf-8"); response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx"); - EasyExcel.write(response.getOutputStream()).head(headList).registerWriteHandler(writeHandler). - sheet(sheetName).doWrite(data); - } catch (UnsupportedEncodingException e) { - LogUtil.error(e.getMessage(), e); - throw new ExcelException("Utf-8 encoding is not supported"); } catch (IOException e) { - LogUtil.error(e.getMessage(), e); - throw new ExcelException("IO exception"); + LogUtil.error(e); + MSException.throwException(e.getMessage()); + } + } + + public void exportByCustomWriteHandler(HttpServletResponse response, List> headList, List> data, + String fileName, String sheetName, WriteHandler writeHandler) { + buildExportResponse(response, fileName); + try { + EasyExcel.write(response.getOutputStream()) + .head(Optional.ofNullable(headList).orElse(new ArrayList<>())) + .registerWriteHandler(writeHandler) + .sheet(sheetName) + .doWrite(data); + } catch (IOException e) { + LogUtil.error(e); + MSException.throwException(e.getMessage()); + } + } + + public void exportByCustomWriteHandler(HttpServletResponse response, List> headList, List> data, + String fileName, String sheetName, WriteHandler writeHandler1, WriteHandler writeHandler2) { + buildExportResponse(response, fileName); + try { + EasyExcel.write(response.getOutputStream()) + .head(Optional.ofNullable(headList).orElse(new ArrayList<>())) + .registerWriteHandler(writeHandler1) + .registerWriteHandler(writeHandler2) + .sheet(sheetName) + .doWrite(data); + } catch (IOException e) { + LogUtil.error(e); + MSException.throwException(e.getMessage()); } } diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java index 6a6819119f..c114a34123 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java @@ -107,7 +107,7 @@ public class TestCaseController { public List listByMethod(@PathVariable String projectId) { QueryTestCaseRequest request = new QueryTestCaseRequest(); request.setProjectId(projectId); - return testCaseService.listTestCaseMthod(request); + return testCaseService.listTestCaseMethod(request); } @GetMapping("/relationship/case/{id}/{relationshipType}") diff --git a/backend/src/main/java/io/metersphere/track/dto/TestCaseDTO.java b/backend/src/main/java/io/metersphere/track/dto/TestCaseDTO.java index 61b41dc1d3..a7a700c1a6 100644 --- a/backend/src/main/java/io/metersphere/track/dto/TestCaseDTO.java +++ b/backend/src/main/java/io/metersphere/track/dto/TestCaseDTO.java @@ -15,7 +15,6 @@ public class TestCaseDTO extends TestCaseWithBLOBs { private String maintainerName; private String apiName; - private String performName; private String lastResultId; private String projectName; private String createName; diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 8211af202e..e513bfa1f2 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -1,7 +1,9 @@ package io.metersphere.track.service; +import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcelFactory; +import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -29,12 +31,11 @@ import io.metersphere.controller.request.ProjectVersionRequest; import io.metersphere.controller.request.ResetOrderRequest; import io.metersphere.controller.request.member.QueryMemberRequest; import io.metersphere.dto.*; -import io.metersphere.excel.domain.ExcelErrData; -import io.metersphere.excel.domain.ExcelResponse; -import io.metersphere.excel.domain.TestCaseExcelData; -import io.metersphere.excel.domain.TestCaseExcelDataFactory; +import io.metersphere.excel.domain.*; +import io.metersphere.excel.handler.FunctionCaseMergeWriteHandler; import io.metersphere.excel.handler.FunctionCaseTemplateWriteHandler; import io.metersphere.excel.listener.TestCaseNoModelDataListener; +import io.metersphere.excel.listener.TestCasePretreatmentListener; import io.metersphere.excel.utils.EasyExcelExporter; import io.metersphere.excel.utils.FunctionCaseImportEnum; import io.metersphere.i18n.Translator; @@ -187,6 +188,9 @@ public class TestCaseService { private FileContentMapper fileContentMapper; @Resource private AttachmentService attachmentService; + @Resource + @Lazy + private TestCaseTemplateService testCaseTemplateService; private ThreadLocal importCreateNum = new ThreadLocal<>(); private ThreadLocal beforeImportCreateNum = new ThreadLocal<>(); @@ -814,7 +818,7 @@ public class TestCaseService { } } - public List listTestCaseMthod(QueryTestCaseRequest request) { + public List listTestCaseMethod(QueryTestCaseRequest request) { return extTestCaseMapper.listByMethod(request); } @@ -986,7 +990,6 @@ public class TestCaseService { testCaseNodeService.createNodes(xmindParser.getNodePaths(), projectId); } if (CollectionUtils.isNotEmpty(xmindParser.getTestCase())) { -// Collections.reverse(xmindParser.getTestCase()); this.saveImportData(xmindParser.getTestCase(), request); names = xmindParser.getTestCase().stream().map(TestCase::getName).collect(Collectors.toList()); ids = xmindParser.getTestCase().stream().map(TestCase::getId).collect(Collectors.toList()); @@ -1013,7 +1016,6 @@ public class TestCaseService { testCaseNodeService.createNodes(nodePathList, projectId); } if (CollectionUtils.isNotEmpty(continueCaseList)) { -// Collections.reverse(continueCaseList); this.saveImportData(continueCaseList, request); names.addAll(continueCaseList.stream().map(TestCase::getName).collect(Collectors.toList())); ids.addAll(continueCaseList.stream().map(TestCase::getId).collect(Collectors.toList())); @@ -1037,7 +1039,7 @@ public class TestCaseService { String projectId = request.getProjectId(); Set userIds; Project project = projectService.getProjectById(projectId); - boolean useCunstomId = projectService.useCustomNum(project); + boolean useCustomId = projectService.useCustomNum(project); Set savedIds = new HashSet<>(); Set testCaseNames = new HashSet<>(); @@ -1046,7 +1048,7 @@ public class TestCaseService { List testCases = getTestCaseForImport(projectId); for (TestCase testCase : testCases) { - if (useCunstomId) { + if (useCustomId) { savedIds.add(testCase.getCustomNum()); } else { savedIds.add(String.valueOf(testCase.getNum())); @@ -1077,8 +1079,14 @@ public class TestCaseService { request.setTestCaseNames(testCaseNames); request.setCustomFields(customFields); request.setSavedCustomIds(savedIds); - request.setUseCustomId(useCunstomId); - TestCaseNoModelDataListener easyExcelListener = new TestCaseNoModelDataListener(request, clazz); + request.setUseCustomId(useCustomId); + Set mergeInfoSet = new TreeSet<>(); + + // 预处理,查询合并单元格信息 + EasyExcel.read(multipartFile.getInputStream(), null, new TestCasePretreatmentListener(mergeInfoSet)) + .extraRead(CellExtraTypeEnum.MERGE).sheet().doRead(); + + TestCaseNoModelDataListener easyExcelListener = new TestCaseNoModelDataListener(request, clazz, mergeInfoSet); //读取excel数据 EasyExcelFactory.read(multipartFile.getInputStream(), easyExcelListener).sheet().doRead(); @@ -1250,40 +1258,6 @@ public class TestCaseService { } } - public void testCaseTemplateExport(String projectId, String importType, HttpServletResponse response) { - try { - TestCaseExcelData testCaseExcelData = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal(); - - - boolean useCustomNum = projectService.useCustomNum(projectId); - boolean importFileNeedNum = false; - if (useCustomNum || StringUtils.equals(importType, FunctionCaseImportEnum.Update.name())) { - //导入更新 or 开启使用自定义ID时,导出ID列 - importFileNeedNum = true; - } - - TestCaseTemplateService testCaseTemplateService = CommonBeanFactory.getBean(TestCaseTemplateService.class); - TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(projectId); - List customFields = null; - if (testCaseTemplate == null) { - customFields = new ArrayList<>(); - } else { - customFields = testCaseTemplate.getCustomFields(); - } - - List> headList = testCaseExcelData.getHead(importFileNeedNum, customFields); - EasyExcelExporter easyExcelExporter = new EasyExcelExporter(testCaseExcelData.getClass()); - Map> caseLevelAndStatusValueMap = testCaseTemplateService.getCaseLevelAndStatusMapByProjectId(projectId); - FunctionCaseTemplateWriteHandler handler = new FunctionCaseTemplateWriteHandler(importFileNeedNum, headList, caseLevelAndStatusValueMap); - easyExcelExporter.exportByCustomWriteHandler(response, headList, generateExportDatas(importFileNeedNum), - Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"), handler); - - } catch (Exception e) { - LogUtil.error(e); - MSException.throwException(e); - } - } - public void download(String fileName, HttpServletResponse res) throws IOException { if (StringUtils.isEmpty(fileName)) { fileName = "xmind.xml"; @@ -1326,75 +1300,64 @@ public class TestCaseService { } } - private List> generateExportDatas(boolean needCustomId) { - List> list = new ArrayList<>(); - StringBuilder path = new StringBuilder(""); - List types = TestCaseConstants.Type.getValues(); - SessionUser user = SessionUtils.getUser(); - for (int i = 1; i <= 5; i++) { - List rowData = new ArrayList<>(); - if (needCustomId) { - rowData.add(""); + private List generateExportData(String projectId) { + List list = new ArrayList<>(); + StringBuilder path = new StringBuilder(); + for (int i = 1; i <= 4; i++) { + path.append("/" + Translator.get("module") + i); + TestCaseDTO testCaseDTO = new TestCaseDTO(); + testCaseDTO.setCustomNum(""); + testCaseDTO.setName(Translator.get("test_case") + i); + testCaseDTO.setNodePath(path.toString()); + testCaseDTO.setPriority("P" + i % 4); + testCaseDTO.setRemark(Translator.get("remark_optional")); + testCaseDTO.setPrerequisite(Translator.get("preconditions_optional")); + JSONArray steps = new JSONArray(); + for (int j = 1; j <= 2; j++) { + JSONObject stepItem = new JSONObject(true); + stepItem.put("desc", Translator.get("test_case_step_desc") + j); + stepItem.put("result", Translator.get("test_case_step_result") + j); + steps.add(stepItem); } - rowData.add(Translator.get("test_case") + i); - path.append("/" + Translator.get("module") + i); - rowData.add(path.toString()); - rowData.add(""); - rowData.add(Translator.get("preconditions_optional")); - rowData.add(Translator.get("remark_optional")); - rowData.add("1. " + Translator.get("step_tip_separate") + "\n2. " + Translator.get("step_tip_order") + "\n3. " + Translator.get("step_tip_optional")); - rowData.add("1. " + Translator.get("result_tip_separate") + "\n2. " + Translator.get("result_tip_order") + "\n3. " + Translator.get("result_tip_optional")); - rowData.add(""); - rowData.add("P" + i % 4); - list.add(rowData); + testCaseDTO.setSteps(steps.toJSONString()); + testCaseDTO.setProjectId(projectId); + list.add(testCaseDTO); } return list; } - private List generateExportTemplate() { - List list = new ArrayList<>(); - StringBuilder path = new StringBuilder(""); - List types = TestCaseConstants.Type.getValues(); - SessionUser user = SessionUtils.getUser(); - TestCaseExcelDataFactory factory = new TestCaseExcelDataFactory(); - for (int i = 1; i <= 5; i++) { - TestCaseExcelData data = factory.getTestCaseExcelDataLocal(); - data.setName(Translator.get("test_case") + i); - path.append("/" + Translator.get("module") + i); - data.setNodePath(path.toString()); - data.setPriority("P" + i % 4); - String type = types.get(i % 3); - data.setPrerequisite(Translator.get("preconditions_optional")); - data.setStepDesc("1. " + Translator.get("step_tip_separate") + - "\n2. " + Translator.get("step_tip_order") + "\n3. " + Translator.get("step_tip_optional")); - data.setStepResult("1. " + Translator.get("result_tip_separate") + "\n2. " + Translator.get("result_tip_order") + "\n3. " + Translator.get("result_tip_optional")); - data.setMaintainer(user.getId()); - data.setRemark(Translator.get("remark_optional")); - list.add(data); - } - list.add(new TestCaseExcelData()); - return list; - } public void testCaseExport(HttpServletResponse response, TestCaseBatchRequest request) { - try { + String projectId = request.getProjectId(); + request.getCondition().setStatusIsNot(CommonConstants.TrashStatus); + List testCaseList = getExportData(request); + testCaseExport(response, projectId, testCaseList, true); + } - TestCaseExcelData testCaseExcelData = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal(); - TestCaseTemplateService testCaseTemplateService = CommonBeanFactory.getBean(TestCaseTemplateService.class); - TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(request.getProjectId()); - List customFields = Optional.ofNullable(testCaseTemplate.getCustomFields()).orElse(new ArrayList<>()); + public void testCaseExport(HttpServletResponse response, String projectId, List testCaseList, boolean needIdCol) { + TestCaseExcelData testCaseExcelData = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal(); + Map rowMergeInfo = new HashMap<>(); + TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(projectId); + List customFields = Optional.ofNullable(testCaseTemplate.getCustomFields()).orElse(new ArrayList<>()); + List> headList = testCaseExcelData.getHead(needIdCol, customFields); + FunctionCaseMergeWriteHandler writeHandler = new FunctionCaseMergeWriteHandler(rowMergeInfo, headList); + boolean isUseCustomId = projectService.useCustomNum(projectId); - List> headList = testCaseExcelData.getHead(true, customFields); - List> testCaseDataByExcelList = this.generateTestCaseExcel(headList, generateTestCaseExcel(request)); - EasyExcelExporter easyExcelExporter = new EasyExcelExporter(testCaseExcelData.getClass()); - easyExcelExporter.exportByCustomWriteHandler(response, headList, testCaseDataByExcelList, - Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet")); + Map> caseLevelAndStatusValueMap = testCaseTemplateService.getCaseLevelAndStatusMapByProjectId(projectId); + FunctionCaseTemplateWriteHandler handler = new FunctionCaseTemplateWriteHandler(needIdCol, headList, caseLevelAndStatusValueMap); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e); - } + List excelData = parseCaseData2ExcelData(testCaseList, rowMergeInfo, isUseCustomId); + List> data = parseExcelData2List(headList, excelData); + new EasyExcelExporter(testCaseExcelData.getClass()) + .exportByCustomWriteHandler(response, headList, data, Translator.get("test_case_import_template_name"), + Translator.get("test_case_import_template_sheet"), handler, writeHandler); + } + + public void testCaseTemplateExport(String projectId, String importType, HttpServletResponse response) { + //导入更新 or 开启使用自定义ID时,导出ID列 + boolean needIdCol = projectService.useCustomNum(projectId) || StringUtils.equals(importType, FunctionCaseImportEnum.Update.name()); + testCaseExport(response, projectId, generateExportData(projectId), needIdCol); } @@ -1446,8 +1409,8 @@ public class TestCaseService { return rootMind; } - private List> generateTestCaseExcel(List> headListParams, List datas) { - List> returnDatas = new ArrayList<>(); + private List> parseExcelData2List(List> headListParams, List data) { + List> result = new ArrayList<>(); //转化excel头 List headList = new ArrayList<>(); for (List list : headListParams) { @@ -1456,17 +1419,17 @@ public class TestCaseService { } } - for (TestCaseExcelData model : datas) { - List list = new ArrayList<>(); + for (TestCaseExcelData model : data) { + List fields = new ArrayList<>(); Map customDataMaps = Optional.ofNullable(model.getCustomDatas()).orElse(new HashMap<>()); for (String head : headList) { if (StringUtils.equalsAnyIgnoreCase(head, "ID")) { - list.add(model.getCustomNum()); + fields.add(model.getCustomNum()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Name", "用例名稱", "用例名称")) { - list.add(model.getName()); + fields.add(model.getName()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Module", "所屬模塊", "所属模块")) { - list.add(model.getNodePath()); + fields.add(model.getNodePath()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Tag", "標簽", "标签")) { String tags = ""; try { @@ -1478,32 +1441,32 @@ public class TestCaseService { } } catch (Exception e) { } - list.add(tags); + fields.add(tags); } else if (StringUtils.equalsAnyIgnoreCase(head, "Prerequisite", "前置條件", "前置条件")) { - list.add(model.getPrerequisite()); + fields.add(model.getPrerequisite()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Remark", "備註", "备注")) { - list.add(model.getRemark()); + fields.add(model.getRemark()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Step description", "步驟描述", "步骤描述")) { - list.add(model.getStepDesc()); + fields.add(model.getStepDesc()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Step result", "預期結果", "预期结果")) { - list.add(model.getStepResult()); + fields.add(model.getStepResult()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Edit Model", "編輯模式", "编辑模式")) { - list.add(model.getStepModel()); + fields.add(model.getStepModel()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Priority", "用例等級", "用例等级")) { - list.add(model.getPriority()); + fields.add(model.getPriority()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Case status", "用例状态", "用例狀態")) { - list.add(model.getStatus()); + fields.add(model.getStatus()); } else if (StringUtils.equalsAnyIgnoreCase(head, "Maintainer(ID)", "责任人(ID)", "維護人(ID)")) { - list.add(model.getMaintainer()); + fields.add(model.getMaintainer()); } else { String value = Optional.ofNullable(customDataMaps.get(head)).orElse(""); - list.add(value); + fields.add(value); } } - returnDatas.add(list); + result.add(fields); } - return returnDatas; + return result; } public List findByBatchRequest(TestCaseBatchRequest request) { @@ -1526,18 +1489,16 @@ public class TestCaseService { return testCaseList; } - private List generateTestCaseExcel(TestCaseBatchRequest request) { - request.getCondition().setStatusIsNot("Trash"); - List testCaseList = this.getExportData(request); - boolean isUseCustomId = projectService.useCustomNum(request.getProjectId()); + private List parseCaseData2ExcelData(List testCaseList, + Map rowMergeInfo, Boolean isUseCustomId) { + if (CollectionUtils.isEmpty(testCaseList)) { + return new ArrayList<>(); + } List list = new ArrayList<>(); - StringBuilder step = new StringBuilder(); - StringBuilder result = new StringBuilder(); - Map> customSelectValueMap = new HashMap<>(); Map customNameMap = new HashMap<>(); TestCaseTemplateService testCaseTemplateService = CommonBeanFactory.getBean(TestCaseTemplateService.class); - TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(request.getProjectId()); + TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(testCaseList.get(0).getProjectId()); List customFieldList; if (testCaseTemplate == null) { @@ -1545,6 +1506,113 @@ public class TestCaseService { } else { customFieldList = testCaseTemplate.getCustomFields(); } + + buildExportCustomFieldMap(customSelectValueMap, customNameMap, customFieldList); + + for (int rowIndex = 0; rowIndex < testCaseList.size(); rowIndex++) { + TestCaseDTO t = testCaseList.get(rowIndex); + List stepDescList = new ArrayList<>(); + List stepResultList = new ArrayList<>(); + + setExportSystemField(t, customNameMap, customSelectValueMap); + TestCaseExcelData data = new TestCaseExcelData(); + BeanUtils.copyBean(data, t); + if (isUseCustomId) { + data.setCustomNum(t.getCustomNum()); + } else { + if (t.getNum() == null) { + data.setCustomNum(""); + } else { + data.setCustomNum(String.valueOf(t.getNum())); + } + } + buildExportStep(t, stepDescList, stepResultList, data); + buildExportCustomField(customSelectValueMap, customNameMap, t, data); + + if (CollectionUtils.isNotEmpty(stepDescList)) { + // 如果有多条步骤则添加多条数据,之后合并单元格 + for (int i = 0; i < stepDescList.size(); i++) { + TestCaseExcelData excelData; + if (i == 0) { + // 第一行存全量元素 + excelData = data; + if (stepDescList.size() > 1) { + // 保存合并单元格的下标和数量 + rowMergeInfo.put(list.size() + 1, stepDescList.size()); + } + } else { + // 之后的行只存步骤 + excelData = new TestCaseExcelData(); + } + excelData.setStepDesc(stepDescList.get(i)); + excelData.setStepResult(stepResultList.get(i)); + list.add(excelData); + } + } else { + list.add(data); + } + } + return list; + } + + private void buildExportCustomField(Map> customSelectValueMap, Map customNameMap, TestCaseDTO t, TestCaseExcelData data) { + try { + List fields = customFieldTestCaseService.getByResourceId(t.getId()); + Map map = new HashMap<>(); + for (int index = 0; index < fields.size(); index++) { + CustomFieldResource field = fields.get(index); + //进行key value对换 + String id = field.getFieldId(); + if (StringUtils.isNotBlank(field.getValue())) { + String value = JSONObject.parse(field.getValue()).toString(); + if (customSelectValueMap.containsKey(id) + && customSelectValueMap.get(id).containsKey(value)) { + value = customSelectValueMap.get(id).get(value); + } + map.put(customNameMap.get(id), value); + } + } + data.setCustomDatas(map); + } catch (Exception e) { + LogUtil.error(e); + } + } + + private void buildExportStep(TestCaseDTO t, List stepDescList, List stepResultList, TestCaseExcelData data) { + if (StringUtils.isBlank(t.getStepModel())) { + data.setStepModel(TestCaseConstants.StepModel.STEP.name()); + } else { + data.setStepModel(t.getStepModel()); + } + if (StringUtils.equals(data.getStepModel(), TestCaseConstants.StepModel.TEXT.name())) { + data.setStepDesc(t.getStepDescription()); + data.setStepResult(t.getExpectedResult()); + } else { + String steps = t.getSteps(); + JSONArray jsonArray = new JSONArray(); + //解决旧版本保存用例导出报错 + try { + jsonArray = JSON.parseArray(steps); + } catch (Exception e) { + if (steps.contains("null") && !steps.contains("\"null\"")) { + steps = steps.replace("null", "\"\""); + jsonArray = JSON.parseArray(steps); + } + } + for (int j = 0; j < jsonArray.size(); j++) { + // 将步骤存储起来,之后生成多条数据,再合并单元格 + JSONObject item = jsonArray.getJSONObject(j ); + String stepDesc = item.getString("desc"); + String stepResult = item.getString("result"); + if (StringUtils.isNotBlank(stepDesc) || StringUtils.isNotBlank(stepResult)) { + stepDescList.add(Optional.ofNullable(stepDesc).orElse("")); + stepResultList.add(Optional.ofNullable(stepResult).orElse("")); + } + } + } + } + + private void buildExportCustomFieldMap(Map> customSelectValueMap, Map customNameMap, List customFieldList) { for (CustomFieldDao dto : customFieldList) { Map map = new HashMap<>(); if (StringUtils.equals("select", dto.getType())) { @@ -1573,119 +1641,9 @@ public class TestCaseService { customSelectValueMap.put(dto.getId(), map); customNameMap.put(dto.getId(), dto.getName()); } - - - testCaseList.forEach(t -> { - setExportSystemField(t, customNameMap, customSelectValueMap); - TestCaseExcelData data = new TestCaseExcelData(); - data.setNum(t.getNum()); - data.setName(t.getName()); - data.setNodePath(t.getNodePath()); - data.setPriority(t.getPriority()); - if (isUseCustomId) { - data.setCustomNum(t.getCustomNum()); - } else { - data.setCustomNum(String.valueOf(t.getNum())); - } - if (StringUtils.isBlank(t.getStepModel())) { - data.setStepModel(TestCaseConstants.StepModel.STEP.name()); - } else { - data.setStepModel(t.getStepModel()); - } - data.setPrerequisite(t.getPrerequisite()); - data.setTags(t.getTags()); - if (StringUtils.equals(t.getMethod(), "manual") || StringUtils.isBlank(t.getMethod())) { - if (StringUtils.equals(data.getStepModel(), TestCaseConstants.StepModel.TEXT.name())) { - data.setStepDesc(t.getStepDescription()); - data.setStepResult(t.getExpectedResult()); - } else { - String steps = t.getSteps(); - String setp = ""; - setp = steps; - JSONArray jsonArray = null; - - //解决旧版本保存用例导出报错 - try { - jsonArray = JSON.parseArray(setp); - } catch (Exception e) { - if (steps.contains("null") && !steps.contains("\"null\"")) { - setp = steps.replace("null", "\"\""); - jsonArray = JSON.parseArray(setp); - } - } - - if (CollectionUtils.isNotEmpty(jsonArray)) { - for (int j = 0; j < jsonArray.size(); j++) { - int num = j + 1; - String stepItem = jsonArray.getJSONObject(j).getString("desc"); - if (StringUtils.isEmpty(stepItem)) { - stepItem = ""; - } - //正则去空格、回车、换行符、制表符 - stepItem = stepItem.replaceAll("\\s*|\t|\r|\n", ""); - step.append(num + "." + stepItem + "\n"); - String resultItem = jsonArray.getJSONObject(j).getString("result"); - if (StringUtils.isEmpty(resultItem)) { - resultItem = ""; - } - result.append(num + "." + resultItem + "\n"); - - } - } - - data.setStepDesc(step.toString()); - data.setStepResult(result.toString()); - step.setLength(0); - result.setLength(0); - } - data.setRemark(t.getRemark()); - - } else if ("auto".equals(t.getMethod()) && "api".equals(t.getType())) { - data.setStepDesc(""); - data.setStepResult(""); - if (t.getTestId() != null && "other".equals(t.getTestId())) { - data.setRemark(t.getOtherTestName()); - } else { - data.setRemark("[" + t.getApiName() + "]" + "\n" + t.getRemark()); - } - - } else if ("auto".equals(t.getMethod()) && "performance".equals(t.getType())) { - data.setStepDesc(""); - data.setStepResult(""); - if (t.getTestId() != null && "other".equals(t.getTestId())) { - data.setRemark(t.getOtherTestName()); - } else { - data.setRemark(t.getPerformName()); - } - } - data.setMaintainer(t.getMaintainer()); - data.setStatus(t.getStatus()); - try { - List fields = customFieldTestCaseService.getByResourceId(t.getId()); - Map map = new HashMap<>(); - for (int index = 0; index < fields.size(); index++) { - CustomFieldResource field = fields.get(index); - //进行key value对换 - String id = field.getFieldId(); - if (StringUtils.isNotBlank(field.getValue())) { - String value = JSONObject.parse(field.getValue()).toString(); - if (customSelectValueMap.containsKey(id) - && customSelectValueMap.get(id).containsKey(value)) { - value = customSelectValueMap.get(id).get(value); - } - map.put(customNameMap.get(id), value); - } - } - data.setCustomDatas(map); - } catch (Exception e) { - LogUtil.error(e); - } - list.add(data); - }); - return list; } - public void setExportSystemField(TestCaseDTO testCase, Map customNameMap, + public void setExportSystemField(TestCaseDTO testCase, Map customNameMap, Map> customSelectValueMap) { String statusKey = null; for (String k : customNameMap.keySet()) { @@ -2512,7 +2470,7 @@ public class TestCaseService { relationExample.createCriteria().andRelationTypeEqualTo(AttachmentType.TEST_CASE.type()); List relations = attachmentModuleRelationMapper.selectByExample(relationExample); Map> relationGroup = relations.stream().collect(Collectors.groupingBy(AttachmentModuleRelation::getRelationId)); - for(Map.Entry> entry : relationGroup.entrySet()) { + for (Map.Entry> entry : relationGroup.entrySet()) { final String caseId = entry.getKey(); final String uploadPath = FileUtils.ATTACHMENT_DIR + File.separator + AttachmentType.TEST_CASE.type() + File.separator + caseId; // 获取同一用例关联的文件ID diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 8e90b4cd06..3e314cb237 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -47,12 +47,6 @@ tester= read_only_user= module= preconditions_optional= -step_tip_separate= -step_tip_order= -step_tip_optional= -result_tip_separate= -result_tip_order= -result_tip_optional= remark_optional= do_not_modify_header_order= module_created_automatically= diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 6c94c125b2..27f9b5cc62 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -146,12 +146,6 @@ num_needed_modify_testcase=ID is needed when modifying testcase num_needless_create_testcase=ID is needless when creating testcase tag_tip_pattern=Labels should be separated by semicolons or commas preconditions_optional=Preconditions optional -step_tip_separate=Each step is separated by a new line -step_tip_order=Mark the serial number before the step -step_tip_optional=Test steps and results optional -result_tip_separate=Each result is separated by a new line -result_tip_order=Mark the serial number before the result -result_tip_optional=Test steps and results optional remark_optional=Remark optional do_not_modify_header_order=Do not modify the header order module_created_automatically=If there is no such module, will be created automatically diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 464b4de49c..f6b3257667 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -146,12 +146,6 @@ num_needed_modify_testcase=修改用例时ID必填 num_needless_create_testcase=创建用例时无需ID tag_tip_pattern=标签之间以分号或者逗号隔开 preconditions_optional=前置条件选填 -step_tip_separate=每个步骤以换行分隔 -step_tip_order=步骤前需标序号 -step_tip_optional=测试步骤和结果选填 -result_tip_separate=每条结果以换行分隔 -result_tip_order=结果前需标序号 -result_tip_optional=测试步骤和结果选填 remark_optional=备注选填 do_not_modify_header_order=请勿修改表头顺序 module_created_automatically=若无该模块将自动创建 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 079e1c7c0a..8c0c90e428 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -146,12 +146,6 @@ num_needed_modify_testcase=修改用例時ID必填 num_needless_create_testcase=創建用例時無需ID tag_tip_pattern=標簽之間以分號或者逗號隔開 preconditions_optional=前置條件選填 -step_tip_separate=每個步驟以換行分隔 -step_tip_order=步驟前需標序號 -step_tip_optional=測試步驟和結果選填 -result_tip_separate=每條結果以換行分隔 -result_tip_order=結果前需標序號 -result_tip_optional=測試步驟和結果選填 remark_optional=備註選填 do_not_modify_header_order=請勿修改表頭順序 module_created_automatically=若無該模塊將自動創建