feat(功能用例): 功能用例导入excel模板下载
This commit is contained in:
parent
f6fb6d384b
commit
2b695f6e1e
|
@ -18,7 +18,7 @@ public class BugProviderDTO implements Serializable {
|
||||||
@Schema(description = "id")
|
@Schema(description = "id")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Schema(description = "bugId")
|
@Schema(description = "缺陷id")
|
||||||
private String bugId;
|
private String bugId;
|
||||||
|
|
||||||
@Schema(description = "缺陷名称")
|
@Schema(description = "缺陷名称")
|
||||||
|
|
|
@ -513,4 +513,11 @@ swagger_parse_error_with_auth=Swagger parsing failed, please confirm whether the
|
||||||
swagger_parse_error=Swagger parsing failed or file format is incorrect!
|
swagger_parse_error=Swagger parsing failed or file format is incorrect!
|
||||||
#测试计划
|
#测试计划
|
||||||
permission.test_plan.name=Test plan
|
permission.test_plan.name=Test plan
|
||||||
permission.test_plan_module.name=Test plan module
|
permission.test_plan_module.name=Test plan module
|
||||||
|
|
||||||
|
excel.template.id=Not mandatory, add a new use case when the ID is empty
|
||||||
|
excel.template.case_edit_type=Not mandatory, fill in STEP for step description, fill in Text for text description, default to Text if not filled in
|
||||||
|
excel.template.tag=Not mandatory labels should be separated by semicolons or commas
|
||||||
|
excel.template.text_description=Not mandatory, when the editing mode is STEP, the step description will be based on the identifier [1] [2] [3] To determine whether to split a cell into multiple steps, if not, it is a single step
|
||||||
|
excel.template.member=Not mandatory, please fill in the relevant personnel ID or email under this project
|
||||||
|
excel.template.not_required=Not required
|
|
@ -509,4 +509,11 @@ swagger_parse_error_with_auth=Swagger 解析失败,请确认认证信息是否
|
||||||
swagger_parse_error=Swagger 解析失败,请确认文件格式是否正确!
|
swagger_parse_error=Swagger 解析失败,请确认文件格式是否正确!
|
||||||
#测试计划
|
#测试计划
|
||||||
permission.test_plan.name=测试计划
|
permission.test_plan.name=测试计划
|
||||||
permission.test_plan_module.name=测试计划模块
|
permission.test_plan_module.name=测试计划模块
|
||||||
|
|
||||||
|
excel.template.id=非必填,ID为空时新增用例
|
||||||
|
excel.template.case_edit_type=非必填,步骤描述填写STEP,文本描述填写TEXT,未填写默认为TEXT
|
||||||
|
excel.template.tag=非必填,标签之间以分号或者逗号隔开
|
||||||
|
excel.template.text_description=非必填,编辑模式为STEP时,步骤描述会根据标识[1] [2] [3]...来判断是否将单元格拆分为多个步骤,没有则为一个步骤
|
||||||
|
excel.template.member=非必填,请填写该项目下的相关人员ID或邮箱
|
||||||
|
excel.template.not_required=非必填
|
|
@ -509,4 +509,11 @@ swagger_parse_error_with_auth=Swagger 解析失敗,請檢查 Swagger 接口是
|
||||||
swagger_parse_error=Swagger 解析失敗
|
swagger_parse_error=Swagger 解析失敗
|
||||||
#測試計劃
|
#測試計劃
|
||||||
permission.test_plan.name=測試計劃
|
permission.test_plan.name=測試計劃
|
||||||
permission.test_plan_module.name=測試計劃模塊
|
permission.test_plan_module.name=測試計劃模塊
|
||||||
|
|
||||||
|
excel.template.id=非必填,ID為空時新增用例
|
||||||
|
excel.template.case_edit_type=非必填,步驟描述填寫STEP,文本描述填寫TEXT,為填寫默認為TEXT
|
||||||
|
excel.template.tag=非必填,標簽之間以分號或者逗號隔開
|
||||||
|
excel.template.text_description=非必填,編輯模式為STEP時,步驟描述會根據標識[1] [2] [3]...來判斷是否將單元格拆分為多個步驟,沒有則為一個步驟
|
||||||
|
excel.template.member=非必填,請填寫該項目下的相關人員ID或郵箱
|
||||||
|
excel.template.not_required=非必填
|
|
@ -8,6 +8,7 @@ import io.metersphere.functional.dto.FunctionalCaseDetailDTO;
|
||||||
import io.metersphere.functional.dto.FunctionalCasePageDTO;
|
import io.metersphere.functional.dto.FunctionalCasePageDTO;
|
||||||
import io.metersphere.functional.dto.FunctionalCaseVersionDTO;
|
import io.metersphere.functional.dto.FunctionalCaseVersionDTO;
|
||||||
import io.metersphere.functional.request.*;
|
import io.metersphere.functional.request.*;
|
||||||
|
import io.metersphere.functional.service.FunctionalCaseFileService;
|
||||||
import io.metersphere.functional.service.FunctionalCaseLogService;
|
import io.metersphere.functional.service.FunctionalCaseLogService;
|
||||||
import io.metersphere.functional.service.FunctionalCaseNoticeService;
|
import io.metersphere.functional.service.FunctionalCaseNoticeService;
|
||||||
import io.metersphere.functional.service.FunctionalCaseService;
|
import io.metersphere.functional.service.FunctionalCaseService;
|
||||||
|
@ -15,8 +16,8 @@ import io.metersphere.project.dto.CustomFieldOptions;
|
||||||
import io.metersphere.project.service.ProjectTemplateService;
|
import io.metersphere.project.service.ProjectTemplateService;
|
||||||
import io.metersphere.sdk.constants.PermissionConstants;
|
import io.metersphere.sdk.constants.PermissionConstants;
|
||||||
import io.metersphere.sdk.constants.TemplateScene;
|
import io.metersphere.sdk.constants.TemplateScene;
|
||||||
import io.metersphere.system.dto.sdk.request.PosRequest;
|
|
||||||
import io.metersphere.system.dto.sdk.TemplateDTO;
|
import io.metersphere.system.dto.sdk.TemplateDTO;
|
||||||
|
import io.metersphere.system.dto.sdk.request.PosRequest;
|
||||||
import io.metersphere.system.log.annotation.Log;
|
import io.metersphere.system.log.annotation.Log;
|
||||||
import io.metersphere.system.log.constants.OperationLogType;
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
import io.metersphere.system.notice.annotation.SendNotice;
|
import io.metersphere.system.notice.annotation.SendNotice;
|
||||||
|
@ -28,6 +29,7 @@ import io.metersphere.system.utils.SessionUtils;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
@ -50,6 +52,8 @@ public class FunctionalCaseController {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectTemplateService projectTemplateService;
|
private ProjectTemplateService projectTemplateService;
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseFileService functionalCaseFileService;
|
||||||
|
|
||||||
//TODO 获取模板列表(多模板功能暂时不做)
|
//TODO 获取模板列表(多模板功能暂时不做)
|
||||||
|
|
||||||
|
@ -205,4 +209,12 @@ public class FunctionalCaseController {
|
||||||
functionalCaseService.editPos(request);
|
functionalCaseService.editPos(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/download/excel/template/{projectId}")
|
||||||
|
@Operation(summary = "用例管理-功能用例-excel导入-下载模板")
|
||||||
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
|
||||||
|
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||||
|
public void testCaseTemplateExport(@PathVariable String projectId, HttpServletResponse response) {
|
||||||
|
functionalCaseFileService.downloadExcelTemplate(projectId, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class FunctionalCaseRelationshipController {
|
||||||
|
|
||||||
@GetMapping("/get-ids/{caseId}")
|
@GetMapping("/get-ids/{caseId}")
|
||||||
@Operation(summary = "用例管理-功能用例-用例详情-前后置关系-获取已关联用例id集合(关联用例弹窗前调用)")
|
@Operation(summary = "用例管理-功能用例-用例详情-前后置关系-获取已关联用例id集合(关联用例弹窗前调用)")
|
||||||
@CheckOwner(resourceId = "#reviewId", resourceType = "case_review")
|
@CheckOwner(resourceId = "#caseId", resourceType = "functional_case")
|
||||||
public List<String> getCaseIds(@PathVariable String caseId) {
|
public List<String> getCaseIds(@PathVariable String caseId) {
|
||||||
return functionalCaseRelationshipEdgeService.getExcludeIds(caseId);
|
return functionalCaseRelationshipEdgeService.getExcludeIds(caseId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package io.metersphere.functional.excel.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
public @interface NotRequired {
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package io.metersphere.functional.excel.constants;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum FunctionalCaseImportFiled {
|
||||||
|
|
||||||
|
ID("id", "ID", "ID", "ID", FunctionalCaseExcelData::getNum),
|
||||||
|
NAME("name", "用例名称", "用例名稱", "Name", FunctionalCaseExcelData::getName),
|
||||||
|
MODULE("module", "所属模块", "所屬模塊", "Module", FunctionalCaseExcelData::getModule),
|
||||||
|
TAGS("tags", "标签", "標簽", "Tag", FunctionalCaseImportFiled::parseTags),
|
||||||
|
PREREQUISITE("prerequisite", "前置条件", "前置條件", "Prerequisite", FunctionalCaseExcelData::getPrerequisite),
|
||||||
|
TEXT_DESCRIPTION("textDescription", "步骤描述", "步驟描述", "Text description", FunctionalCaseExcelData::getTextDescription),
|
||||||
|
EXPECTED_RESULT("expectedResult", "预期结果", "預期結果", "Expected result", FunctionalCaseExcelData::getExpectedResult),
|
||||||
|
CASE_EDIT_TYPE("caseEditType", "编辑模式", "編輯模式", "Case edit type", FunctionalCaseExcelData::getCaseEditType),
|
||||||
|
DESCRIPTION("description", "备注", "備註", "Description", FunctionalCaseExcelData::getDescription);
|
||||||
|
|
||||||
|
private Map<Locale, String> filedLangMap;
|
||||||
|
private Function<FunctionalCaseExcelData, String> parseFunc;
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
FunctionalCaseImportFiled(String value, String zn, String chineseTw, String us, Function<FunctionalCaseExcelData, String> parseFunc) {
|
||||||
|
this.filedLangMap = new HashMap<Locale, String>();
|
||||||
|
filedLangMap.put(Locale.SIMPLIFIED_CHINESE, zn);
|
||||||
|
filedLangMap.put(Locale.TRADITIONAL_CHINESE, chineseTw);
|
||||||
|
filedLangMap.put(Locale.US, us);
|
||||||
|
this.value = value;
|
||||||
|
this.parseFunc = parseFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Locale, String> getFiledLangMap() {
|
||||||
|
return this.filedLangMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String parseExcelDataValue(FunctionalCaseExcelData excelData) {
|
||||||
|
return parseFunc.apply(excelData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseTags(FunctionalCaseExcelData excelData) {
|
||||||
|
String tags = StringUtils.EMPTY;
|
||||||
|
try {
|
||||||
|
if (excelData.getTags() != null) {
|
||||||
|
List arr = JSON.parseArray(excelData.getTags());
|
||||||
|
if (CollectionUtils.isNotEmpty(arr)) {
|
||||||
|
tags = StringUtils.joinWith(",", arr.toArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsHead(String head) {
|
||||||
|
return filedLangMap.values().contains(head);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package io.metersphere.functional.excel.domain;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelIgnore;
|
||||||
|
import io.metersphere.functional.excel.constants.FunctionalCaseImportFiled;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class FunctionalCaseExcelData {
|
||||||
|
@ExcelIgnore
|
||||||
|
private String id;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String num;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String name;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String module;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String tags;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String prerequisite;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String description;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String textDescription;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String expectedResult;
|
||||||
|
@ExcelIgnore
|
||||||
|
private String caseEditType;
|
||||||
|
|
||||||
|
@ExcelIgnore
|
||||||
|
Map<String, Object> customData = new LinkedHashMap<>();
|
||||||
|
@ExcelIgnore
|
||||||
|
Map<String, String> otherFields;
|
||||||
|
|
||||||
|
|
||||||
|
public List<List<String>> getHead(List<TemplateCustomFieldDTO> customFields) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<List<String>> getHead(List<TemplateCustomFieldDTO> customFields, Locale lang) {
|
||||||
|
List<List<String>> heads = new ArrayList<>();
|
||||||
|
FunctionalCaseImportFiled[] fields = FunctionalCaseImportFiled.values();
|
||||||
|
for (FunctionalCaseImportFiled field : fields) {
|
||||||
|
heads.add(Arrays.asList(field.getFiledLangMap().get(lang)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(customFields)) {
|
||||||
|
for (TemplateCustomFieldDTO dto : customFields) {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add(dto.getFieldName());
|
||||||
|
heads.add(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return heads;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package io.metersphere.functional.excel.domain;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||||
|
import io.metersphere.functional.excel.annotation.NotRequired;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ColumnWidth(15)
|
||||||
|
public class FunctionalCaseExcelDataCn extends FunctionalCaseExcelData {
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("ID")
|
||||||
|
@NotRequired
|
||||||
|
private String num;
|
||||||
|
|
||||||
|
@NotBlank(message = "{cannot_be_null}")
|
||||||
|
@Length(max = 255)
|
||||||
|
@ExcelProperty("用例名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@NotBlank(message = "{cannot_be_null}")
|
||||||
|
@Length(max = 50)
|
||||||
|
@ExcelProperty("所属模块")
|
||||||
|
@ColumnWidth(30)
|
||||||
|
private String moduleId;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("标签")
|
||||||
|
@NotRequired
|
||||||
|
@Length(min = 0, max = 1000)
|
||||||
|
private String tags;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("前置条件")
|
||||||
|
private String prerequisite;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("备注")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("步骤描述")
|
||||||
|
private String textDescription;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("预期结果")
|
||||||
|
private String expectedResult;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("编辑模式")
|
||||||
|
@NotRequired
|
||||||
|
@Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}")
|
||||||
|
private String caseEditType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<List<String>> getHead(List<TemplateCustomFieldDTO> customFields) {
|
||||||
|
return super.getHead(customFields, Locale.SIMPLIFIED_CHINESE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package io.metersphere.functional.excel.domain;
|
||||||
|
|
||||||
|
import io.metersphere.system.excel.domain.ExcelDataFactory;
|
||||||
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExcelDataFactory implements ExcelDataFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class getExcelDataByLocal() {
|
||||||
|
Locale locale = LocaleContextHolder.getLocale();
|
||||||
|
if (Locale.US.toString().equalsIgnoreCase(locale.toString())) {
|
||||||
|
return FunctionalCaseExcelDataUs.class;
|
||||||
|
} else if (Locale.TRADITIONAL_CHINESE.toString().equalsIgnoreCase(locale.toString())) {
|
||||||
|
return FunctionalCaseExcelDataTw.class;
|
||||||
|
}
|
||||||
|
return FunctionalCaseExcelDataCn.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionalCaseExcelData getFunctionalCaseExcelDataLocal() {
|
||||||
|
Locale locale = LocaleContextHolder.getLocale();
|
||||||
|
if (Locale.US.toString().equalsIgnoreCase(locale.toString())) {
|
||||||
|
return new FunctionalCaseExcelDataUs();
|
||||||
|
} else if (Locale.TRADITIONAL_CHINESE.toString().equalsIgnoreCase(locale.toString())) {
|
||||||
|
return new FunctionalCaseExcelDataTw();
|
||||||
|
}
|
||||||
|
return new FunctionalCaseExcelDataCn();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package io.metersphere.functional.excel.domain;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||||
|
import io.metersphere.functional.excel.annotation.NotRequired;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ColumnWidth(15)
|
||||||
|
public class FunctionalCaseExcelDataTw extends FunctionalCaseExcelData {
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("ID")
|
||||||
|
@NotRequired
|
||||||
|
private String num;
|
||||||
|
|
||||||
|
@NotBlank(message = "{cannot_be_null}")
|
||||||
|
@Length(max = 255)
|
||||||
|
@ExcelProperty("用例名稱")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@NotBlank(message = "{cannot_be_null}")
|
||||||
|
@Length(max = 50)
|
||||||
|
@ExcelProperty("所屬模塊")
|
||||||
|
@ColumnWidth(30)
|
||||||
|
private String module;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("標簽")
|
||||||
|
@NotRequired
|
||||||
|
@Length(min = 0, max = 1000)
|
||||||
|
private String tags;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("前置條件")
|
||||||
|
private String prerequisite;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("備註")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("步驟描述")
|
||||||
|
private String textDescription;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("預期結果")
|
||||||
|
private String expectedResult;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("編輯模式")
|
||||||
|
@NotRequired
|
||||||
|
@Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}")
|
||||||
|
private String caseEditType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<List<String>> getHead(List<TemplateCustomFieldDTO> customFields) {
|
||||||
|
return super.getHead(customFields, Locale.TRADITIONAL_CHINESE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package io.metersphere.functional.excel.domain;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||||
|
import io.metersphere.functional.excel.annotation.NotRequired;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ColumnWidth(15)
|
||||||
|
public class FunctionalCaseExcelDataUs extends FunctionalCaseExcelData {
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("ID")
|
||||||
|
@NotRequired
|
||||||
|
private String num;
|
||||||
|
|
||||||
|
@NotBlank(message = "{cannot_be_null}")
|
||||||
|
@Length(max = 255)
|
||||||
|
@ExcelProperty("Name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@NotBlank(message = "{cannot_be_null}")
|
||||||
|
@Length(max = 50)
|
||||||
|
@ExcelProperty("Module")
|
||||||
|
@ColumnWidth(30)
|
||||||
|
private String module;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("Tag")
|
||||||
|
@NotRequired
|
||||||
|
@Length(min = 0, max = 1000)
|
||||||
|
private String tags;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("Prerequisite")
|
||||||
|
private String prerequisite;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("Description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("Text description")
|
||||||
|
private String textDescription;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("Expected result")
|
||||||
|
private String expectedResult;
|
||||||
|
|
||||||
|
@ColumnWidth(50)
|
||||||
|
@ExcelProperty("Case edit type")
|
||||||
|
@NotRequired
|
||||||
|
@Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}")
|
||||||
|
private String caseEditType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<List<String>> getHead(List<TemplateCustomFieldDTO> customFields) {
|
||||||
|
return super.getHead(customFields, Locale.US);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
package io.metersphere.functional.excel.handler;
|
||||||
|
|
||||||
|
import com.alibaba.excel.util.BooleanUtils;
|
||||||
|
import com.alibaba.excel.write.handler.RowWriteHandler;
|
||||||
|
import com.alibaba.excel.write.handler.context.RowWriteHandlerContext;
|
||||||
|
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||||||
|
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
|
||||||
|
import io.metersphere.functional.excel.constants.FunctionalCaseImportFiled;
|
||||||
|
import io.metersphere.sdk.constants.CustomFieldType;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.poi.ss.usermodel.Comment;
|
||||||
|
import org.apache.poi.ss.usermodel.Drawing;
|
||||||
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionCaseTemplateWriteHandler implements RowWriteHandler {
|
||||||
|
|
||||||
|
Map<String, List<String>> caseLevelAndStatusValueMap;
|
||||||
|
|
||||||
|
private Sheet sheet;
|
||||||
|
private Drawing<?> drawingPatriarch;
|
||||||
|
|
||||||
|
private Map<String, TemplateCustomFieldDTO> customField;
|
||||||
|
private Map<String, Integer> fieldMap = new HashMap<>();
|
||||||
|
|
||||||
|
public FunctionCaseTemplateWriteHandler(List<List<String>> headList, Map<String, List<String>> caseLevelAndStatusValueMap, Map<String, TemplateCustomFieldDTO> customFieldMap) {
|
||||||
|
initIndex(headList);
|
||||||
|
this.caseLevelAndStatusValueMap = caseLevelAndStatusValueMap;
|
||||||
|
this.customField = customFieldMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initIndex(List<List<String>> headList) {
|
||||||
|
int index = 0;
|
||||||
|
for (List<String> list : headList) {
|
||||||
|
for (String head : list) {
|
||||||
|
this.fieldMap.put(head, index);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterRowDispose(RowWriteHandlerContext context) {
|
||||||
|
if (BooleanUtils.isTrue(context.getHead())) {
|
||||||
|
sheet = context.getWriteSheetHolder().getSheet();
|
||||||
|
drawingPatriarch = sheet.createDrawingPatriarch();
|
||||||
|
|
||||||
|
Iterator<Map.Entry<String, Integer>> iterator = fieldMap.entrySet().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Map.Entry<String, Integer> entry = iterator.next();
|
||||||
|
//默认字段
|
||||||
|
if (FunctionalCaseImportFiled.ID.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.id"));
|
||||||
|
}
|
||||||
|
if (FunctionalCaseImportFiled.NAME.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("required"));
|
||||||
|
}
|
||||||
|
if (FunctionalCaseImportFiled.MODULE.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("required").concat(",").concat(Translator.get("module_created_automatically")));
|
||||||
|
}
|
||||||
|
if (FunctionalCaseImportFiled.TAGS.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.tag"));
|
||||||
|
}
|
||||||
|
if (FunctionalCaseImportFiled.CASE_EDIT_TYPE.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.case_edit_type"));
|
||||||
|
}
|
||||||
|
if (FunctionalCaseImportFiled.TEXT_DESCRIPTION.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.text_description"));
|
||||||
|
}
|
||||||
|
if (FunctionalCaseImportFiled.PREREQUISITE.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required"));
|
||||||
|
}
|
||||||
|
if (FunctionalCaseImportFiled.EXPECTED_RESULT.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required"));
|
||||||
|
}
|
||||||
|
if (FunctionalCaseImportFiled.DESCRIPTION.containsHead(entry.getKey())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//自定义字段
|
||||||
|
if (customField.containsKey(entry.getKey())) {
|
||||||
|
TemplateCustomFieldDTO templateCustomFieldDTO = customField.get(entry.getKey());
|
||||||
|
List<String> strings = caseLevelAndStatusValueMap.get(entry.getKey());
|
||||||
|
if (StringUtils.equalsAnyIgnoreCase(templateCustomFieldDTO.getType(), CustomFieldType.MULTIPLE_MEMBER.name(), CustomFieldType.MEMBER.name())) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.member"));
|
||||||
|
} else {
|
||||||
|
if (templateCustomFieldDTO.getRequired()) {
|
||||||
|
if (CollectionUtils.isNotEmpty(strings)) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("required").concat(":").concat(Translator.get("options")).concat(JSON.toJSONString(caseLevelAndStatusValueMap.get(entry.getKey()))));
|
||||||
|
} else {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("required"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (CollectionUtils.isNotEmpty(strings)) {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required").concat(":").concat(JSON.toJSONString(caseLevelAndStatusValueMap.get(entry.getKey()))));
|
||||||
|
} else {
|
||||||
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setComment(Integer index, String text) {
|
||||||
|
if (index == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Comment comment = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, index, 0, index + 3, 1));
|
||||||
|
comment.setString(new XSSFRichTextString(text));
|
||||||
|
sheet.getRow(0).getCell(1).setCellComment(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HorizontalCellStyleStrategy getHorizontalWrapStrategy() {
|
||||||
|
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
|
||||||
|
// 设置自动换行
|
||||||
|
contentWriteCellStyle.setWrapped(true);
|
||||||
|
return new HorizontalCellStyleStrategy(null, contentWriteCellStyle);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
package io.metersphere.functional.service;
|
||||||
|
|
||||||
|
import io.metersphere.functional.excel.constants.FunctionalCaseImportFiled;
|
||||||
|
import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
|
||||||
|
import io.metersphere.functional.excel.domain.FunctionalCaseExcelDataFactory;
|
||||||
|
import io.metersphere.functional.excel.handler.FunctionCaseTemplateWriteHandler;
|
||||||
|
import io.metersphere.project.service.ProjectTemplateService;
|
||||||
|
import io.metersphere.sdk.constants.TemplateScene;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import io.metersphere.system.domain.CustomFieldOption;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateDTO;
|
||||||
|
import io.metersphere.system.excel.utils.EasyExcelExporter;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public class FunctionalCaseFileService {
|
||||||
|
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ProjectTemplateService projectTemplateService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载excel导入模板
|
||||||
|
*
|
||||||
|
* @param projectId
|
||||||
|
* @param response
|
||||||
|
*/
|
||||||
|
public void downloadExcelTemplate(String projectId, HttpServletResponse response) {
|
||||||
|
//获取当前项目下默认模板的自定义字段属性
|
||||||
|
TemplateDTO defaultTemplateDTO = projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.FUNCTIONAL.name());
|
||||||
|
List<TemplateCustomFieldDTO> customFields = Optional.ofNullable(defaultTemplateDTO.getCustomFields()).orElse(new ArrayList<>());
|
||||||
|
|
||||||
|
//获取表头字段 当前项目下默认模板的自定义字段 heads:默认表头名称+自定义字段名称
|
||||||
|
List<List<String>> heads = getTemplateHead(projectId, customFields);
|
||||||
|
|
||||||
|
FunctionalCaseExcelData caseExcelData = new FunctionalCaseExcelDataFactory().getFunctionalCaseExcelDataLocal();
|
||||||
|
|
||||||
|
//默认字段+自定义字段的 options集合
|
||||||
|
Map<String, List<String>> customFieldOptionsMap = getCustomFieldOptionsMap(customFields);
|
||||||
|
Map<String, TemplateCustomFieldDTO> customFieldMap = customFields.stream().collect(Collectors.toMap(TemplateCustomFieldDTO::getFieldName, templateCustomFieldDTO -> templateCustomFieldDTO));
|
||||||
|
|
||||||
|
//表头备注信息
|
||||||
|
FunctionCaseTemplateWriteHandler handler = new FunctionCaseTemplateWriteHandler(heads, customFieldOptionsMap, customFieldMap);
|
||||||
|
|
||||||
|
List<FunctionalCaseExcelData> functionalCaseExcelData = generateExportData();
|
||||||
|
List<List<Object>> data = parseExcelData2List(heads, functionalCaseExcelData);
|
||||||
|
|
||||||
|
new EasyExcelExporter(caseExcelData.getClass())
|
||||||
|
.exportByCustomWriteHandler(response, heads, data, Translator.get("test_case_import_template_name"),
|
||||||
|
Translator.get("test_case_import_template_sheet"), handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<List<Object>> parseExcelData2List(List<List<String>> headListParams, List<FunctionalCaseExcelData> data) {
|
||||||
|
List<List<Object>> result = new ArrayList<>();
|
||||||
|
//转化excel头
|
||||||
|
List<String> headList = new ArrayList<>();
|
||||||
|
for (List<String> list : headListParams) {
|
||||||
|
for (String head : list) {
|
||||||
|
headList.add(head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionalCaseImportFiled[] importFields = FunctionalCaseImportFiled.values();
|
||||||
|
|
||||||
|
|
||||||
|
for (FunctionalCaseExcelData model : data) {
|
||||||
|
List<Object> fields = new ArrayList<>();
|
||||||
|
Map<String, Object> customDataMaps = Optional.ofNullable(model.getCustomData())
|
||||||
|
.orElse(new HashMap<>());
|
||||||
|
Map<String, String> otherFieldMaps = Optional.ofNullable(model.getOtherFields())
|
||||||
|
.orElse(new HashMap<>());
|
||||||
|
for (String head : headList) {
|
||||||
|
boolean isSystemField = false;
|
||||||
|
for (FunctionalCaseImportFiled importFiled : importFields) {
|
||||||
|
if (importFiled.containsHead(head)) {
|
||||||
|
fields.add(importFiled.parseExcelDataValue(model));
|
||||||
|
isSystemField = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isSystemField) {
|
||||||
|
Object value = customDataMaps.get(head);
|
||||||
|
if (value == null) {
|
||||||
|
value = otherFieldMaps.get(head);
|
||||||
|
}
|
||||||
|
if (value == null) {
|
||||||
|
value = StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
fields.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<FunctionalCaseExcelData> generateExportData() {
|
||||||
|
List<FunctionalCaseExcelData> list = new ArrayList<>();
|
||||||
|
StringBuilder path = new StringBuilder();
|
||||||
|
for (int i = 1; i <= 4; i++) {
|
||||||
|
path.append("/" + Translator.get("module") + i);
|
||||||
|
FunctionalCaseExcelData testCaseDTO = new FunctionalCaseExcelData();
|
||||||
|
testCaseDTO.setId(StringUtils.EMPTY);
|
||||||
|
testCaseDTO.setName(Translator.get("test_case") + i);
|
||||||
|
testCaseDTO.setModule(path.toString());
|
||||||
|
testCaseDTO.setPrerequisite(Translator.get("test_case_prerequisite"));
|
||||||
|
testCaseDTO.setCaseEditType("STEP");
|
||||||
|
String textDescription = "";
|
||||||
|
String expectedResult = "";
|
||||||
|
for (int j = 1; j < 5; j++) {
|
||||||
|
textDescription = textDescription + "[" + j + "]" + Translator.get("test_case_step_desc") + i + "\n";
|
||||||
|
|
||||||
|
expectedResult = expectedResult + "[" + j + "]" + Translator.get("test_case_step_result") + i + "\n";
|
||||||
|
|
||||||
|
}
|
||||||
|
testCaseDTO.setTextDescription(textDescription);
|
||||||
|
testCaseDTO.setExpectedResult(expectedResult);
|
||||||
|
|
||||||
|
list.add(testCaseDTO);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, List<String>> getCustomFieldOptionsMap(List<TemplateCustomFieldDTO> customFields) {
|
||||||
|
Map<String, List<String>> returnMap = new HashMap<>();
|
||||||
|
customFields.forEach(item -> {
|
||||||
|
List<String> values = getOptionValues(Optional.ofNullable(item.getOptions()).orElse(new ArrayList<>()));
|
||||||
|
returnMap.put(item.getFieldName(), values);
|
||||||
|
});
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getOptionValues(List<CustomFieldOption> options) {
|
||||||
|
List<String> values = new ArrayList<>();
|
||||||
|
options.forEach(item -> {
|
||||||
|
values.add(item.getText());
|
||||||
|
});
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取表头字段
|
||||||
|
*
|
||||||
|
* @param projectId
|
||||||
|
* @param customFields
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private List<List<String>> getTemplateHead(String projectId, List<TemplateCustomFieldDTO> customFields) {
|
||||||
|
List<List<String>> heads = new FunctionalCaseExcelDataFactory().getFunctionalCaseExcelDataLocal().getHead(customFields);
|
||||||
|
return heads;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import io.metersphere.project.domain.NotificationExample;
|
||||||
import io.metersphere.project.mapper.NotificationMapper;
|
import io.metersphere.project.mapper.NotificationMapper;
|
||||||
import io.metersphere.project.service.ProjectTemplateService;
|
import io.metersphere.project.service.ProjectTemplateService;
|
||||||
import io.metersphere.sdk.constants.CustomFieldType;
|
import io.metersphere.sdk.constants.CustomFieldType;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.constants.TemplateScene;
|
import io.metersphere.sdk.constants.TemplateScene;
|
||||||
import io.metersphere.sdk.constants.TemplateScopeType;
|
import io.metersphere.sdk.constants.TemplateScopeType;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
@ -35,11 +36,14 @@ import org.springframework.mock.web.MockMultipartFile;
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
import org.springframework.test.context.jdbc.SqlConfig;
|
import org.springframework.test.context.jdbc.SqlConfig;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
@AutoConfigureMockMvc
|
@AutoConfigureMockMvc
|
||||||
|
@ -60,6 +64,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
public static final String FUNCTIONAL_CASE_VERSION_URL = "/functional/case/version/";
|
public static final String FUNCTIONAL_CASE_VERSION_URL = "/functional/case/version/";
|
||||||
public static final String FUNCTIONAL_CASE_BATCH_EDIT_URL = "/functional/case/batch/edit";
|
public static final String FUNCTIONAL_CASE_BATCH_EDIT_URL = "/functional/case/batch/edit";
|
||||||
public static final String FUNCTIONAL_CASE_POS_URL = "/functional/case/edit/pos";
|
public static final String FUNCTIONAL_CASE_POS_URL = "/functional/case/edit/pos";
|
||||||
|
public static final String DOWNLOAD_EXCEL_TEMPLATE_URL = "/functional/case/download/excel/template/";
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private NotificationMapper notificationMapper;
|
private NotificationMapper notificationMapper;
|
||||||
|
@ -147,7 +152,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
//增加覆盖率
|
//增加覆盖率
|
||||||
TemplateDTO templateDTO = projectTemplateService.getTemplateDTOById("21312", "100001100001", TemplateScene.FUNCTIONAL.name());
|
TemplateDTO templateDTO = projectTemplateService.getTemplateDTOById("21312", "100001100001", TemplateScene.FUNCTIONAL.name());
|
||||||
List<TemplateCustomFieldDTO> customFields = templateDTO.getCustomFields();
|
List<TemplateCustomFieldDTO> customFields = templateDTO.getCustomFields();
|
||||||
customFields.forEach(item ->{
|
customFields.forEach(item -> {
|
||||||
if (Translator.get("custom_field.functional_priority").equals(item.getFieldName())) {
|
if (Translator.get("custom_field.functional_priority").equals(item.getFieldName())) {
|
||||||
FunctionalCaseCustomField functionalCaseCustomField = new FunctionalCaseCustomField();
|
FunctionalCaseCustomField functionalCaseCustomField = new FunctionalCaseCustomField();
|
||||||
functionalCaseCustomField.setCaseId("TEST_FUNCTIONAL_CASE_ID_3");
|
functionalCaseCustomField.setCaseId("TEST_FUNCTIONAL_CASE_ID_3");
|
||||||
|
@ -440,7 +445,6 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(18)
|
@Order(18)
|
||||||
public void testPos() throws Exception {
|
public void testPos() throws Exception {
|
||||||
|
@ -455,4 +459,19 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_POS_URL, posRequest);
|
this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_POS_URL, posRequest);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(19)
|
||||||
|
public void testDownloadExcelTemplate() throws Exception {
|
||||||
|
this.requestGetExcel(DOWNLOAD_EXCEL_TEMPLATE_URL + DEFAULT_PROJECT_ID);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private MvcResult requestGetExcel(String url) throws Exception {
|
||||||
|
return mockMvc.perform(MockMvcRequestBuilders.get(url)
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
|
.andExpect(status().isOk()).andReturn();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.metersphere.system.excel.domain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public interface ExcelDataFactory {
|
||||||
|
Object getExcelDataByLocal();
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package io.metersphere.system.excel.utils;
|
||||||
|
|
||||||
|
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.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.poi.ss.SpreadsheetVersion;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class EasyExcelExporter {
|
||||||
|
|
||||||
|
|
||||||
|
private Class clazz;
|
||||||
|
|
||||||
|
public EasyExcelExporter(Class clazz) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void export(HttpServletResponse response, List data, String fileName, String sheetName) {
|
||||||
|
buildExportResponse(response, fileName);
|
||||||
|
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
|
||||||
|
contentWriteCellStyle.setWrapped(true);
|
||||||
|
try {
|
||||||
|
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle);
|
||||||
|
EasyExcel.write(response.getOutputStream(), this.clazz)
|
||||||
|
.registerWriteHandler(horizontalCellStyleStrategy)
|
||||||
|
.sheet(sheetName)
|
||||||
|
.doWrite(data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exportByCustomWriteHandler(HttpServletResponse response, List<List<String>> headList,
|
||||||
|
List<List<Object>> data, String fileName, String sheetName) {
|
||||||
|
buildExportResponse(response, fileName);
|
||||||
|
try {
|
||||||
|
EasyExcel.write(response.getOutputStream())
|
||||||
|
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
|
||||||
|
.sheet(sheetName)
|
||||||
|
.doWrite(data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildExportResponse(HttpServletResponse response, String fileName) {
|
||||||
|
try {
|
||||||
|
response.setContentType("application/vnd.ms-excel");
|
||||||
|
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||||
|
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()) + ".xlsx");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exportByCustomWriteHandler(HttpServletResponse response, List<List<String>> headList, List<List<Object>> data,
|
||||||
|
String fileName, String sheetName, WriteHandler writeHandler) {
|
||||||
|
buildExportResponse(response, fileName);
|
||||||
|
try {
|
||||||
|
EasyExcel.write(response.getOutputStream())
|
||||||
|
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
|
||||||
|
.registerWriteHandler(writeHandler)
|
||||||
|
.sheet(sheetName)
|
||||||
|
.doWrite(data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exportByCustomWriteHandler(HttpServletResponse response, List<List<String>> headList, List<List<Object>> data,
|
||||||
|
String fileName, String sheetName, WriteHandler writeHandler1, WriteHandler writeHandler2) {
|
||||||
|
buildExportResponse(response, fileName);
|
||||||
|
try {
|
||||||
|
EasyExcel.write(response.getOutputStream())
|
||||||
|
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
|
||||||
|
.registerWriteHandler(writeHandler1)
|
||||||
|
.registerWriteHandler(writeHandler2)
|
||||||
|
.sheet(sheetName)
|
||||||
|
.doWrite(data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetCellMaxTextLength() {
|
||||||
|
SpreadsheetVersion excel2007 = SpreadsheetVersion.EXCEL2007;
|
||||||
|
if (excel2007.getMaxTextLength() < Integer.MAX_VALUE) {
|
||||||
|
Field field;
|
||||||
|
try {
|
||||||
|
field = excel2007.getClass().getDeclaredField("_maxTextLength");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(excel2007, Integer.MAX_VALUE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
pom.xml
1
pom.xml
|
@ -245,6 +245,7 @@
|
||||||
<exclude>io/metersphere/**/dto/**</exclude>
|
<exclude>io/metersphere/**/dto/**</exclude>
|
||||||
<exclude>io/metersphere/**/config/**</exclude>
|
<exclude>io/metersphere/**/config/**</exclude>
|
||||||
<exclude>io/metersphere/**/constants/**</exclude>
|
<exclude>io/metersphere/**/constants/**</exclude>
|
||||||
|
<exclude>io/metersphere/*/excel/**</exclude>
|
||||||
<exclude>io/metersphere/sdk/**</exclude>
|
<exclude>io/metersphere/sdk/**</exclude>
|
||||||
<exclude>io/metersphere/provider/**</exclude>
|
<exclude>io/metersphere/provider/**</exclude>
|
||||||
<exclude>io/metersphere/plugin/**</exclude>
|
<exclude>io/metersphere/plugin/**</exclude>
|
||||||
|
|
Loading…
Reference in New Issue