feat(测试用例): 功能用例导出excel
This commit is contained in:
parent
f76a8cbd7a
commit
279384539d
|
@ -32,6 +32,7 @@ public class DefaultRepositoryDir {
|
||||||
* 会定时清理
|
* 会定时清理
|
||||||
*/
|
*/
|
||||||
private static final String SYSTEM_TEMP_DIR = SYSTEM_ROOT_DIR + "/temp";
|
private static final String SYSTEM_TEMP_DIR = SYSTEM_ROOT_DIR + "/temp";
|
||||||
|
private static final String EXPORT_EXCEL_TEMP_DIR = SYSTEM_ROOT_DIR + "/export/excel";
|
||||||
|
|
||||||
/*------ end: 系统下资源目录 --------*/
|
/*------ end: 系统下资源目录 --------*/
|
||||||
|
|
||||||
|
@ -158,6 +159,9 @@ public class DefaultRepositoryDir {
|
||||||
return SYSTEM_TEMP_DIR;
|
return SYSTEM_TEMP_DIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getExportExcelTempDir() {
|
||||||
|
return EXPORT_EXCEL_TEMP_DIR;
|
||||||
|
}
|
||||||
public static String getSystemTempCompressDir() {
|
public static String getSystemTempCompressDir() {
|
||||||
return SYSTEM_TEMP_DIR + "/compress";
|
return SYSTEM_TEMP_DIR + "/compress";
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,7 @@ public class CompressUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将多个文件压缩
|
* 将多个文件压缩
|
||||||
|
*
|
||||||
* @param zipFilePath 压缩文件所在路径
|
* @param zipFilePath 压缩文件所在路径
|
||||||
* @param fileList 要压缩的文件
|
* @param fileList 要压缩的文件
|
||||||
* @return
|
* @return
|
||||||
|
@ -113,6 +114,31 @@ public class CompressUtils {
|
||||||
return zipFile;
|
return zipFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将多个文件压缩至指定路径
|
||||||
|
*
|
||||||
|
* @param fileList 待压缩的文件列表
|
||||||
|
* @param zipFilePath 压缩文件路径
|
||||||
|
* @return 返回压缩好的文件
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static File zipFilesToPath(String zipFilePath, List<File> fileList) throws IOException {
|
||||||
|
File zipFile = new File(zipFilePath);
|
||||||
|
try( // 文件输出流
|
||||||
|
FileOutputStream outputStream = getFileStream(zipFile);
|
||||||
|
// 压缩流
|
||||||
|
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)
|
||||||
|
){
|
||||||
|
int size = fileList.size();
|
||||||
|
// 压缩列表中的文件
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
File file = fileList.get(i);
|
||||||
|
zipFile(file, zipOutputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zipFile;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Zip解压
|
* Zip解压
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.metersphere.sdk.util;
|
package io.metersphere.sdk.util;
|
||||||
|
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -15,4 +16,9 @@ public class MsFileUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteDir(String path) throws Exception {
|
||||||
|
File file = new File(path);
|
||||||
|
FileUtils.deleteDirectory(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,3 +247,13 @@ case.minder.all.case=All Case
|
||||||
case.minder.status.success=success
|
case.minder.status.success=success
|
||||||
case.minder.status.error=error
|
case.minder.status.error=error
|
||||||
case.minder.status.blocked=blocked
|
case.minder.status.blocked=blocked
|
||||||
|
|
||||||
|
case.review.status.un_reviewed=Unreviewed
|
||||||
|
case.review.status.under_reviewed=Under review
|
||||||
|
case.review.status.pass=Passed
|
||||||
|
case.review.status.un_pass=Un pass
|
||||||
|
case.review.status.re_reviewed=Re reviewed
|
||||||
|
case.execute.status.pending=Pending
|
||||||
|
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
||||||
|
functional_case_execute_comment_template=[Execute comment:%s %s(%s)]\n%s\n
|
||||||
|
functional_case_review_comment_template=[Review comment:%s %s(%s)]\n%s\n
|
|
@ -246,3 +246,12 @@ case.minder.status.success=成功
|
||||||
case.minder.status.error=失败
|
case.minder.status.error=失败
|
||||||
case.minder.status.blocked=阻塞
|
case.minder.status.blocked=阻塞
|
||||||
|
|
||||||
|
case.review.status.un_reviewed=未评审
|
||||||
|
case.review.status.under_reviewed=评审中
|
||||||
|
case.review.status.pass=已通过
|
||||||
|
case.review.status.un_pass=不通过
|
||||||
|
case.review.status.re_reviewed=重新提审
|
||||||
|
case.execute.status.pending=未执行
|
||||||
|
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
||||||
|
functional_case_execute_comment_template=【执行评论:%s %s(%s)】\n%s\n
|
||||||
|
functional_case_review_comment_template=【评审评论:%s %s(%s)】\n%s\n
|
|
@ -247,3 +247,13 @@ case.minder.status.success=成功
|
||||||
case.minder.status.error=失敗
|
case.minder.status.error=失敗
|
||||||
case.minder.status.blocked=阻塞
|
case.minder.status.blocked=阻塞
|
||||||
|
|
||||||
|
case.review.status.un_reviewed=未評審
|
||||||
|
case.review.status.under_reviewed=評審中
|
||||||
|
case.review.status.pass=已通過
|
||||||
|
case.review.status.un_pass=不通過
|
||||||
|
case.review.status.re_reviewed=重新提審
|
||||||
|
case.execute.status.pending=未執行
|
||||||
|
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
||||||
|
functional_case_execute_comment_template=【執行評論:%s %s(%s)】\n%s\n
|
||||||
|
functional_case_review_comment_template=【評審評論:%s %s(%s)】\n%s\n
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,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.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import org.apache.shiro.authz.annotation.Logical;
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
|
@ -223,7 +224,7 @@ public class FunctionalCaseController {
|
||||||
|
|
||||||
@PostMapping("/pre-check/excel")
|
@PostMapping("/pre-check/excel")
|
||||||
@Operation(summary = "用例管理-功能用例-excel导入检查")
|
@Operation(summary = "用例管理-功能用例-excel导入检查")
|
||||||
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE)
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_IMPORT)
|
||||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||||
public FunctionalCaseImportResponse preCheckExcel(@RequestPart("request") FunctionalCaseImportRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
public FunctionalCaseImportResponse preCheckExcel(@RequestPart("request") FunctionalCaseImportRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
||||||
return functionalCaseFileService.preCheckExcel(request, file);
|
return functionalCaseFileService.preCheckExcel(request, file);
|
||||||
|
@ -232,7 +233,7 @@ public class FunctionalCaseController {
|
||||||
|
|
||||||
@PostMapping("/import/excel")
|
@PostMapping("/import/excel")
|
||||||
@Operation(summary = "用例管理-功能用例-excel导入")
|
@Operation(summary = "用例管理-功能用例-excel导入")
|
||||||
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE)
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_IMPORT)
|
||||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||||
public FunctionalCaseImportResponse importExcel(@RequestPart("request") FunctionalCaseImportRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
public FunctionalCaseImportResponse importExcel(@RequestPart("request") FunctionalCaseImportRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
||||||
SessionUser user = SessionUtils.getUser();
|
SessionUser user = SessionUtils.getUser();
|
||||||
|
@ -248,4 +249,12 @@ public class FunctionalCaseController {
|
||||||
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc");
|
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc");
|
||||||
return PageUtils.setPageInfo(page, functionalCaseService.operationHistoryList(request));
|
return PageUtils.setPageInfo(page, functionalCaseService.operationHistoryList(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/export/excel")
|
||||||
|
@Operation(summary = "用例管理-功能用例-excel导出")
|
||||||
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_EXPORT)
|
||||||
|
public void testCaseExport(@Validated @RequestBody FunctionalCaseExportRequest request) {
|
||||||
|
functionalCaseFileService.exportFunctionalCaseZip(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package io.metersphere.functional.excel.constants;
|
||||||
|
|
||||||
|
public enum FunctionalCaseExportOtherField {
|
||||||
|
|
||||||
|
CREATE_USER("createUser"),
|
||||||
|
CREATE_TIME("createTime"),
|
||||||
|
UPDATE_USER("updateUser"),
|
||||||
|
UPDATE_TIME("updateTime"),
|
||||||
|
REVIEW_STATUS("reviewStatus"),
|
||||||
|
LAST_EXECUTE_RESULT("lastExecuteResult"),
|
||||||
|
CASE_COMMENT("caseComment"),
|
||||||
|
EXECUTE_COMMENT("executeComment"),
|
||||||
|
REVIEW_COMMENT("reviewComment");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
FunctionalCaseExportOtherField(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public enum FunctionalCaseExecuteStatus {
|
||||||
|
|
||||||
|
PENDING("case.execute.status.pending", 1),
|
||||||
|
SUCCESS("case.minder.status.success", 2),
|
||||||
|
BLOCKED("case.minder.status.blocked", 3),
|
||||||
|
ERROR("case.minder.status.error", 4);
|
||||||
|
|
||||||
|
private String i18nKey;
|
||||||
|
private Integer order;
|
||||||
|
|
||||||
|
FunctionalCaseExecuteStatus(String i18nKey, int order) {
|
||||||
|
this.i18nKey = i18nKey;
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getI18nKey() {
|
||||||
|
return i18nKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import io.metersphere.sdk.util.DateUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportCaseCommentConverter implements FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
if (caseCommentMap.containsKey(functionalCase.getId())) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
String template = Translator.get("functional_case_comment_template");
|
||||||
|
List<FunctionalCaseComment> caseComments = caseCommentMap.get(functionalCase.getId());
|
||||||
|
caseComments.forEach(item -> {
|
||||||
|
String updateTime = DateUtils.getTimeString(item.getUpdateTime());
|
||||||
|
String content = item.getContent();
|
||||||
|
result.append(String.format(template, item.getCreateUser(), updateTime, content));
|
||||||
|
});
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能用例导出时解析其他字段对应的列
|
||||||
|
*
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public interface FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap);
|
||||||
|
|
||||||
|
default String getFromMapOfNullable(Map<String, String> map, String key) {
|
||||||
|
if (StringUtils.isNotBlank(key)) {
|
||||||
|
return map.get(key);
|
||||||
|
}
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
default String getFromMapOfNullableWithTranslate(Map<String, String> map, String key) {
|
||||||
|
String value = getFromMapOfNullable(map, key);
|
||||||
|
if (StringUtils.isNotBlank(value)) {
|
||||||
|
return Translator.get(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.excel.constants.FunctionalCaseExportOtherField;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportConverterFactory {
|
||||||
|
|
||||||
|
public static Map<String, FunctionalCaseExportConverter> getConverters(List<String> keys) {
|
||||||
|
Map<String, FunctionalCaseExportConverter> converterMapResult = new HashMap<>();
|
||||||
|
try {
|
||||||
|
HashMap<String, Class<? extends FunctionalCaseExportConverter>> converterMap = getConverterMap();
|
||||||
|
for (String key : keys) {
|
||||||
|
Class<? extends FunctionalCaseExportConverter> clazz = converterMap.get(key);
|
||||||
|
if (clazz != null) {
|
||||||
|
converterMapResult.put(key, clazz.getDeclaredConstructor().newInstance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
return converterMapResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FunctionalCaseExportConverter getConverter(String key) {
|
||||||
|
try {
|
||||||
|
Class<? extends FunctionalCaseExportConverter> clazz = getConverterMap().get(key);
|
||||||
|
if (clazz != null) {
|
||||||
|
return clazz.getDeclaredConstructor().newInstance();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashMap<String, Class<? extends FunctionalCaseExportConverter>> getConverterMap() {
|
||||||
|
return new HashMap<>() {{
|
||||||
|
put(FunctionalCaseExportOtherField.CREATE_USER.getValue(), FunctionalCaseExportCreateUserConverter.class);
|
||||||
|
put(FunctionalCaseExportOtherField.CREATE_TIME.getValue(), FunctionalCaseExportCreateTimeConverter.class);
|
||||||
|
put(FunctionalCaseExportOtherField.UPDATE_USER.getValue(), FunctionalCaseExportUpdateUserConverter.class);
|
||||||
|
put(FunctionalCaseExportOtherField.UPDATE_TIME.getValue(), FunctionalCaseExportUpdateTimeConverter.class);
|
||||||
|
put(FunctionalCaseExportOtherField.REVIEW_STATUS.getValue(), FunctionalCaseExportReviewStatusConverter.class);
|
||||||
|
put(FunctionalCaseExportOtherField.LAST_EXECUTE_RESULT.getValue(), FunctionalCaseExportExecuteStatusConverter.class);
|
||||||
|
put(FunctionalCaseExportOtherField.CASE_COMMENT.getValue(), FunctionalCaseExportCaseCommentConverter.class);
|
||||||
|
put(FunctionalCaseExportOtherField.EXECUTE_COMMENT.getValue(), FunctionalCaseExportExecuteCommentConverter.class);
|
||||||
|
put(FunctionalCaseExportOtherField.REVIEW_COMMENT.getValue(), FunctionalCaseExportReviewCommentConverter.class);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import io.metersphere.sdk.util.DateUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportCreateTimeConverter implements FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
return DateUtils.getTimeString(functionalCase.getCreateTime());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import io.metersphere.project.service.ProjectApplicationService;
|
||||||
|
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||||
|
import io.metersphere.system.domain.User;
|
||||||
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportCreateUserConverter implements FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
public Map<String, String> userMap = new HashMap<>();
|
||||||
|
|
||||||
|
public FunctionalCaseExportCreateUserConverter() {
|
||||||
|
ProjectApplicationService projectApplicationService = CommonBeanFactory.getBean(ProjectApplicationService.class);
|
||||||
|
List<User> memberOption = projectApplicationService.getProjectUserList(SessionUtils.getCurrentProjectId());
|
||||||
|
memberOption.forEach(option -> userMap.put(option.getId(), option.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
return getFromMapOfNullable(userMap, functionalCase.getCreateUser());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import io.metersphere.sdk.util.DateUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportExecuteCommentConverter implements FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
private Map<String, String> executeStatusMap = new HashMap<>();
|
||||||
|
|
||||||
|
public FunctionalCaseExportExecuteCommentConverter() {
|
||||||
|
for (FunctionalCaseExecuteStatus value : FunctionalCaseExecuteStatus.values()) {
|
||||||
|
executeStatusMap.put(value.name(), value.getI18nKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
if (executeCommentMap.containsKey(functionalCase.getId())) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
String template = Translator.get("functional_case_execute_comment_template");
|
||||||
|
List<TestPlanCaseExecuteHistory> executeComment = executeCommentMap.get(functionalCase.getId());
|
||||||
|
executeComment.forEach(item -> {
|
||||||
|
String status = getFromMapOfNullableWithTranslate(executeStatusMap, item.getStatus());
|
||||||
|
String createTime = DateUtils.getTimeString(item.getCreateTime());
|
||||||
|
String content = new String(item.getContent() == null ? new byte[0] : item.getContent(), StandardCharsets.UTF_8);
|
||||||
|
result.append(String.format(template, item.getCreateUser(), status, createTime, content));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportExecuteStatusConverter implements FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
private Map<String, String> executeStatusMap = new HashMap<>();
|
||||||
|
|
||||||
|
public FunctionalCaseExportExecuteStatusConverter() {
|
||||||
|
for (FunctionalCaseExecuteStatus value : FunctionalCaseExecuteStatus.values()) {
|
||||||
|
executeStatusMap.put(value.name(), value.getI18nKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
return getFromMapOfNullableWithTranslate(executeStatusMap, functionalCase.getLastExecuteResult());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import io.metersphere.sdk.util.DateUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportReviewCommentConverter implements FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
private Map<String, String> reviewStatusMap = new HashMap<>();
|
||||||
|
|
||||||
|
public FunctionalCaseExportReviewCommentConverter() {
|
||||||
|
for (FunctionalCaseReviewStatus value : FunctionalCaseReviewStatus.values()) {
|
||||||
|
reviewStatusMap.put(value.name(), value.getI18nKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
if (reviewCommentMap.containsKey(functionalCase.getId())) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
String template = Translator.get("functional_case_review_comment_template");
|
||||||
|
List<CaseReviewHistory> reviewComent = reviewCommentMap.get(functionalCase.getId());
|
||||||
|
reviewComent.forEach(item -> {
|
||||||
|
String status = getFromMapOfNullableWithTranslate(reviewStatusMap, item.getStatus());
|
||||||
|
String createTime = DateUtils.getTimeString(item.getCreateTime());
|
||||||
|
String content = new String(item.getContent() == null ? new byte[0] : item.getContent(), StandardCharsets.UTF_8);
|
||||||
|
result.append(String.format(template, item.getCreateUser(), status, createTime, content));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportReviewStatusConverter implements FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
private Map<String, String> caseReviewStatusMap = new HashMap<>();
|
||||||
|
|
||||||
|
public FunctionalCaseExportReviewStatusConverter() {
|
||||||
|
for (FunctionalCaseReviewStatus value : FunctionalCaseReviewStatus.values()) {
|
||||||
|
caseReviewStatusMap.put(value.name(), value.getI18nKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
return getFromMapOfNullableWithTranslate(caseReviewStatusMap, functionalCase.getReviewStatus());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import io.metersphere.sdk.util.DateUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportUpdateTimeConverter implements FunctionalCaseExportConverter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
return DateUtils.getTimeString(functionalCase.getUpdateTime());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionalCaseExportUpdateUserConverter extends FunctionalCaseExportCreateUserConverter {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parse(FunctionalCase functionalCase, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap) {
|
||||||
|
return getFromMapOfNullable(userMap, functionalCase.getUpdateUser());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package io.metersphere.functional.excel.converter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public enum FunctionalCaseReviewStatus {
|
||||||
|
|
||||||
|
UN_REVIEWED("case.review.status.un_reviewed", 1),
|
||||||
|
UNDER_REVIEWED("case.review.status.under_reviewed", 2),
|
||||||
|
PASS("case.review.status.pass", 3),
|
||||||
|
UN_PASS("case.review.status.un_pass", 4),
|
||||||
|
RE_REVIEWED("case.review.status.re_reviewed", 5);
|
||||||
|
|
||||||
|
private String i18nKey;
|
||||||
|
private Integer order;
|
||||||
|
|
||||||
|
FunctionalCaseReviewStatus(String i18nKey, int order) {
|
||||||
|
this.i18nKey = i18nKey;
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getI18nKey() {
|
||||||
|
return i18nKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package io.metersphere.functional.excel.domain;
|
package io.metersphere.functional.excel.domain;
|
||||||
|
|
||||||
import com.alibaba.excel.annotation.ExcelIgnore;
|
import com.alibaba.excel.annotation.ExcelIgnore;
|
||||||
|
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||||
import io.metersphere.functional.excel.constants.FunctionalCaseImportFiled;
|
import io.metersphere.functional.excel.constants.FunctionalCaseImportFiled;
|
||||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
@ -40,6 +41,9 @@ public class FunctionalCaseExcelData {
|
||||||
@ExcelIgnore
|
@ExcelIgnore
|
||||||
Map<String, String> otherFields;
|
Map<String, String> otherFields;
|
||||||
|
|
||||||
|
@ExcelIgnore
|
||||||
|
private WriteCellData<String> hyperLinkName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 合并文本描述
|
* 合并文本描述
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package io.metersphere.functional.excel.domain;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class FunctionalCaseHeader implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "字段英文名称")
|
||||||
|
private String id;
|
||||||
|
@Schema(description = "字段中文名称")
|
||||||
|
private String name;
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package io.metersphere.functional.excel.handler;
|
||||||
|
|
||||||
|
import com.alibaba.excel.write.handler.RowWriteHandler;
|
||||||
|
import com.alibaba.excel.write.handler.context.RowWriteHandlerContext;
|
||||||
|
import io.metersphere.functional.excel.constants.FunctionalCaseImportFiled;
|
||||||
|
import org.apache.poi.ss.util.CellRangeAddress;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class FunctionCaseMergeWriteHandler implements RowWriteHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储需要合并单元格的信息,key 是需要合并的第一条的行号,值是需要合并多少行
|
||||||
|
*/
|
||||||
|
Map<Integer, Integer> rowMergeInfo;
|
||||||
|
List<List<String>> headList;
|
||||||
|
int textDescriptionRowIndex;
|
||||||
|
int expectedResultRowIndex;
|
||||||
|
|
||||||
|
public FunctionCaseMergeWriteHandler(Map<Integer, Integer> rowMergeInfo, List<List<String>> headList) {
|
||||||
|
this.rowMergeInfo = rowMergeInfo;
|
||||||
|
this.headList = headList;
|
||||||
|
for (int i = 0; i < headList.size(); i++) {
|
||||||
|
List<String> list = headList.get(i);
|
||||||
|
for (String head : list) {
|
||||||
|
if (FunctionalCaseImportFiled.TEXT_DESCRIPTION.containsHead(head)) {
|
||||||
|
textDescriptionRowIndex = i;
|
||||||
|
} else if (FunctionalCaseImportFiled.EXPECTED_RESULT.containsHead(head)) {
|
||||||
|
expectedResultRowIndex = 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 != textDescriptionRowIndex && i != expectedResultRowIndex) {
|
||||||
|
CellRangeAddress cellRangeAddress =
|
||||||
|
new CellRangeAddress(context.getRowIndex(), context.getRowIndex() + mergeCount - 1, i, i);
|
||||||
|
context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(cellRangeAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class FunctionCaseTemplateWriteHandler implements RowWriteHandler {
|
public class FunctionCaseTemplateWriteHandler implements RowWriteHandler {
|
||||||
|
|
||||||
Map<String, List<String>> caseLevelAndStatusValueMap;
|
Map<String, List<String>> customFieldOptionsMap;
|
||||||
|
|
||||||
private Sheet sheet;
|
private Sheet sheet;
|
||||||
private Drawing<?> drawingPatriarch;
|
private Drawing<?> drawingPatriarch;
|
||||||
|
@ -37,9 +37,9 @@ public class FunctionCaseTemplateWriteHandler implements RowWriteHandler {
|
||||||
private Map<String, TemplateCustomFieldDTO> customField;
|
private Map<String, TemplateCustomFieldDTO> customField;
|
||||||
private Map<String, Integer> fieldMap = new HashMap<>();
|
private Map<String, Integer> fieldMap = new HashMap<>();
|
||||||
|
|
||||||
public FunctionCaseTemplateWriteHandler(List<List<String>> headList, Map<String, List<String>> caseLevelAndStatusValueMap, Map<String, TemplateCustomFieldDTO> customFieldMap) {
|
public FunctionCaseTemplateWriteHandler(List<List<String>> headList, Map<String, List<String>> customFieldOptionsMap, Map<String, TemplateCustomFieldDTO> customFieldMap) {
|
||||||
initIndex(headList);
|
initIndex(headList);
|
||||||
this.caseLevelAndStatusValueMap = caseLevelAndStatusValueMap;
|
this.customFieldOptionsMap = customFieldOptionsMap;
|
||||||
this.customField = customFieldMap;
|
this.customField = customFieldMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public class FunctionCaseTemplateWriteHandler implements RowWriteHandler {
|
||||||
//自定义字段
|
//自定义字段
|
||||||
if (customField.containsKey(entry.getKey())) {
|
if (customField.containsKey(entry.getKey())) {
|
||||||
TemplateCustomFieldDTO templateCustomFieldDTO = customField.get(entry.getKey());
|
TemplateCustomFieldDTO templateCustomFieldDTO = customField.get(entry.getKey());
|
||||||
List<String> strings = caseLevelAndStatusValueMap.get(entry.getKey());
|
List<String> strings = customFieldOptionsMap.get(entry.getKey());
|
||||||
if (StringUtils.equalsAnyIgnoreCase(templateCustomFieldDTO.getType(), CustomFieldType.MULTIPLE_MEMBER.name(), CustomFieldType.MEMBER.name())) {
|
if (StringUtils.equalsAnyIgnoreCase(templateCustomFieldDTO.getType(), CustomFieldType.MULTIPLE_MEMBER.name(), CustomFieldType.MEMBER.name())) {
|
||||||
if (templateCustomFieldDTO.getRequired()) {
|
if (templateCustomFieldDTO.getRequired()) {
|
||||||
setComment(fieldMap.get(entry.getKey()), Translator.get("required").concat(",").concat(Translator.get("excel.template.member")));
|
setComment(fieldMap.get(entry.getKey()), Translator.get("required").concat(",").concat(Translator.get("excel.template.member")));
|
||||||
|
@ -104,13 +104,13 @@ public class FunctionCaseTemplateWriteHandler implements RowWriteHandler {
|
||||||
} else {
|
} else {
|
||||||
if (templateCustomFieldDTO.getRequired()) {
|
if (templateCustomFieldDTO.getRequired()) {
|
||||||
if (CollectionUtils.isNotEmpty(strings)) {
|
if (CollectionUtils.isNotEmpty(strings)) {
|
||||||
setComment(fieldMap.get(entry.getKey()), Translator.get("required").concat(":").concat(Translator.get("options")).concat(JSON.toJSONString(caseLevelAndStatusValueMap.get(entry.getKey()))));
|
setComment(fieldMap.get(entry.getKey()), Translator.get("required").concat(":").concat(Translator.get("options")).concat(JSON.toJSONString(customFieldOptionsMap.get(entry.getKey()))));
|
||||||
} else {
|
} else {
|
||||||
setComment(fieldMap.get(entry.getKey()), Translator.get("required"));
|
setComment(fieldMap.get(entry.getKey()), Translator.get("required"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (CollectionUtils.isNotEmpty(strings)) {
|
if (CollectionUtils.isNotEmpty(strings)) {
|
||||||
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required").concat(":").concat(Translator.get("options")).concat(JSON.toJSONString(caseLevelAndStatusValueMap.get(entry.getKey()))));
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required").concat(":").concat(Translator.get("options")).concat(JSON.toJSONString(customFieldOptionsMap.get(entry.getKey()))));
|
||||||
} else {
|
} else {
|
||||||
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required"));
|
setComment(fieldMap.get(entry.getKey()), Translator.get("excel.template.not_required"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,4 +71,8 @@ public abstract class AbstractCustomFieldValidator {
|
||||||
}
|
}
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object parse2Value(String value, TemplateCustomFieldDTO customField) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.stream.Collectors;
|
||||||
public class CustomFieldMemberValidator extends AbstractCustomFieldValidator {
|
public class CustomFieldMemberValidator extends AbstractCustomFieldValidator {
|
||||||
|
|
||||||
protected Map<String, String> userIdMap;
|
protected Map<String, String> userIdMap;
|
||||||
|
protected Map<String, String> userEmailMap;
|
||||||
protected Map<String, String> userNameMap;
|
protected Map<String, String> userNameMap;
|
||||||
|
|
||||||
public CustomFieldMemberValidator() {
|
public CustomFieldMemberValidator() {
|
||||||
|
@ -31,9 +32,12 @@ public class CustomFieldMemberValidator extends AbstractCustomFieldValidator {
|
||||||
.collect(
|
.collect(
|
||||||
Collectors.toMap(user -> user.getId().toLowerCase(), User::getId)
|
Collectors.toMap(user -> user.getId().toLowerCase(), User::getId)
|
||||||
);
|
);
|
||||||
|
userEmailMap = new HashMap<>();
|
||||||
|
memberOption.stream()
|
||||||
|
.forEach(user -> userEmailMap.put(user.getEmail().toLowerCase(), user.getId()));
|
||||||
userNameMap = new HashMap<>();
|
userNameMap = new HashMap<>();
|
||||||
memberOption.stream()
|
memberOption.stream()
|
||||||
.forEach(user -> userNameMap.put(user.getEmail().toLowerCase(), user.getId()));
|
.forEach(user -> userNameMap.put(user.getId(), user.getName().toLowerCase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,7 +47,7 @@ public class CustomFieldMemberValidator extends AbstractCustomFieldValidator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
value = value.toLowerCase();
|
value = value.toLowerCase();
|
||||||
if (userIdMap.containsKey(value) || userNameMap.containsKey(value)) {
|
if (userIdMap.containsKey(value) || userEmailMap.containsKey(value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new CustomFieldValidateException(String.format(Translator.get("custom_field_member_tip"), customField.getFieldName()));
|
throw new CustomFieldValidateException(String.format(Translator.get("custom_field_member_tip"), customField.getFieldName()));
|
||||||
|
@ -55,9 +59,19 @@ public class CustomFieldMemberValidator extends AbstractCustomFieldValidator {
|
||||||
if (userIdMap.containsKey(keyOrValue)) {
|
if (userIdMap.containsKey(keyOrValue)) {
|
||||||
return userIdMap.get(keyOrValue);
|
return userIdMap.get(keyOrValue);
|
||||||
}
|
}
|
||||||
|
if (userEmailMap.containsKey(keyOrValue)) {
|
||||||
|
return userEmailMap.get(keyOrValue);
|
||||||
|
}
|
||||||
|
return keyOrValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object parse2Value(String keyOrValue, TemplateCustomFieldDTO customField) {
|
||||||
|
keyOrValue = keyOrValue.toLowerCase();
|
||||||
if (userNameMap.containsKey(keyOrValue)) {
|
if (userNameMap.containsKey(keyOrValue)) {
|
||||||
return userNameMap.get(keyOrValue);
|
return userNameMap.get(keyOrValue);
|
||||||
}
|
}
|
||||||
return keyOrValue;
|
return keyOrValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class CustomFieldMultipleMemberValidator extends CustomFieldMemberValidat
|
||||||
|
|
||||||
for (String item : parse2Array(customField.getFieldName(), value)) {
|
for (String item : parse2Array(customField.getFieldName(), value)) {
|
||||||
item = item.toLowerCase();
|
item = item.toLowerCase();
|
||||||
if (!userIdMap.containsKey(item) && !userNameMap.containsKey(item)) {
|
if (!userIdMap.containsKey(item) && !userEmailMap.containsKey(item)) {
|
||||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_member_tip"), customField.getFieldName()));
|
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_member_tip"), customField.getFieldName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,27 @@ public class CustomFieldMultipleMemberValidator extends CustomFieldMemberValidat
|
||||||
if (userIdMap.containsKey(item)) {
|
if (userIdMap.containsKey(item)) {
|
||||||
keyOrValues.set(i, userIdMap.get(item));
|
keyOrValues.set(i, userIdMap.get(item));
|
||||||
}
|
}
|
||||||
|
if (userEmailMap.containsKey(item)) {
|
||||||
|
keyOrValues.set(i, userEmailMap.get(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JSON.toJSONString(keyOrValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object parse2Value(String keyOrValuesStr, TemplateCustomFieldDTO customField) {
|
||||||
|
if (StringUtils.isBlank(keyOrValuesStr)) {
|
||||||
|
return JSON.toJSONString(new ArrayList<>());
|
||||||
|
}
|
||||||
|
List<String> keyOrValues = parse2Array(keyOrValuesStr);
|
||||||
|
|
||||||
|
for (int i = 0; i < keyOrValues.size(); i++) {
|
||||||
|
String item = keyOrValues.get(i).toLowerCase();
|
||||||
if (userNameMap.containsKey(item)) {
|
if (userNameMap.containsKey(item)) {
|
||||||
keyOrValues.set(i, userNameMap.get(item));
|
keyOrValues.set(i, userNameMap.get(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return JSON.toJSONString(keyOrValues);
|
return JSON.toJSONString(keyOrValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,12 @@ package io.metersphere.functional.excel.validate;
|
||||||
import io.metersphere.functional.excel.exception.CustomFieldValidateException;
|
import io.metersphere.functional.excel.exception.CustomFieldValidateException;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import io.metersphere.sdk.util.Translator;
|
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.TemplateCustomFieldDTO;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author wx
|
* @author wx
|
||||||
|
@ -48,4 +47,21 @@ public class CustomFieldMultipleSelectValidator extends CustomFieldSelectValidat
|
||||||
}
|
}
|
||||||
return JSON.toJSONString(keyOrValues);
|
return JSON.toJSONString(keyOrValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object parse2Value(String keyOrValuesStr, TemplateCustomFieldDTO customField) {
|
||||||
|
Map<String, String> optionValueMap = customField.getOptions().stream().collect(Collectors.toMap(CustomFieldOption::getFieldId, CustomFieldOption::getValue));
|
||||||
|
if (StringUtils.isBlank(keyOrValuesStr)) {
|
||||||
|
return JSON.toJSONString(new ArrayList<>());
|
||||||
|
}
|
||||||
|
List<String> keyOrValues = parse2Array(keyOrValuesStr);
|
||||||
|
for (int i = 0; i < keyOrValues.size(); i++) {
|
||||||
|
String item = keyOrValues.get(i);
|
||||||
|
if (optionValueMap.containsKey(item)) {
|
||||||
|
keyOrValues.set(i, optionValueMap.get(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JSON.toJSONString(keyOrValues);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,4 +51,14 @@ public class CustomFieldMultipleTextValidator extends AbstractCustomFieldValidat
|
||||||
|
|
||||||
return JSON.toJSONString(keyOrValues);
|
return JSON.toJSONString(keyOrValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object parse2Value(String keyOrValuesStr, TemplateCustomFieldDTO customField) {
|
||||||
|
if (StringUtils.isBlank(keyOrValuesStr)) {
|
||||||
|
return JSON.toJSONString(new ArrayList<>());
|
||||||
|
}
|
||||||
|
List<String> keyOrValues = parse2Array(keyOrValuesStr);
|
||||||
|
|
||||||
|
return JSON.toJSONString(keyOrValues);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,15 @@ public class CustomFieldSelectValidator extends AbstractCustomFieldValidator {
|
||||||
return keyOrValuesStr;
|
return keyOrValuesStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object parse2Value(String keyOrValuesStr, TemplateCustomFieldDTO customField) {
|
||||||
|
Map<String, String> optionValueMap = customField.getOptions().stream().collect(Collectors.toMap(CustomFieldOption::getFieldId, CustomFieldOption::getValue));
|
||||||
|
if (optionValueMap.containsKey(keyOrValuesStr)) {
|
||||||
|
return optionValueMap.get(keyOrValuesStr);
|
||||||
|
}
|
||||||
|
return keyOrValuesStr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取自定义字段的选项值和key
|
* 获取自定义字段的选项值和key
|
||||||
* 存储到缓存中,增强导入时性能
|
* 存储到缓存中,增强导入时性能
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package io.metersphere.functional.mapper;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public interface ExtFunctionalCaseCommentMapper {
|
||||||
|
List<FunctionalCaseComment> getCaseComment(@Param("ids") List<String> ids);
|
||||||
|
|
||||||
|
List<TestPlanCaseExecuteHistory> getExecuteComment(@Param("ids") List<String> ids);
|
||||||
|
|
||||||
|
List<CaseReviewHistory> getReviewComment(@Param("ids") List<String> ids);
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="io.metersphere.functional.mapper.ExtFunctionalCaseCommentMapper">
|
||||||
|
|
||||||
|
<select id="getCaseComment" resultType="io.metersphere.functional.domain.FunctionalCaseComment">
|
||||||
|
SELECT
|
||||||
|
functional_case_comment.case_id,
|
||||||
|
functional_case_comment.content,
|
||||||
|
functional_case_comment.update_time,
|
||||||
|
user.name as createUser
|
||||||
|
FROM
|
||||||
|
functional_case_comment
|
||||||
|
INNER JOIN user ON functional_case_comment.create_user = user.id
|
||||||
|
where functional_case_comment.case_id in
|
||||||
|
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getExecuteComment" resultType="io.metersphere.plan.domain.TestPlanCaseExecuteHistory">
|
||||||
|
SELECT
|
||||||
|
test_plan_case_execute_history.case_id,
|
||||||
|
test_plan_case_execute_history.content,
|
||||||
|
test_plan_case_execute_history.create_time,
|
||||||
|
user.name as createUser
|
||||||
|
FROM
|
||||||
|
test_plan_case_execute_history
|
||||||
|
INNER JOIN user ON test_plan_case_execute_history.create_user = user.id
|
||||||
|
where test_plan_case_execute_history.case_id in
|
||||||
|
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
<select id="getReviewComment" resultType="io.metersphere.functional.domain.CaseReviewHistory">
|
||||||
|
SELECT
|
||||||
|
case_review_history.case_id,
|
||||||
|
case_review_history.content,
|
||||||
|
case_review_history.create_time,
|
||||||
|
user.name as createUser
|
||||||
|
FROM
|
||||||
|
case_review_history
|
||||||
|
INNER JOIN user ON case_review_history.create_user = user.id
|
||||||
|
where case_review_history.case_id in
|
||||||
|
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.metersphere.functional.request;
|
||||||
|
|
||||||
|
import io.metersphere.functional.dto.BaseFunctionalCaseBatchDTO;
|
||||||
|
import io.metersphere.functional.excel.domain.FunctionalCaseHeader;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class FunctionalCaseExportRequest extends BaseFunctionalCaseBatchDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "系统字段", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private List<FunctionalCaseHeader> systemFields = new ArrayList<>();
|
||||||
|
|
||||||
|
@Schema(description = "自定义字段")
|
||||||
|
private List<FunctionalCaseHeader> customFields = new ArrayList<>();
|
||||||
|
|
||||||
|
@Schema(description = "其他字段")
|
||||||
|
private List<FunctionalCaseHeader> otherFields = new ArrayList<>();
|
||||||
|
|
||||||
|
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
@Schema(description = "文件id")
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
}
|
|
@ -3,36 +3,72 @@ package io.metersphere.functional.service;
|
||||||
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.EasyExcel;
|
||||||
import com.alibaba.excel.EasyExcelFactory;
|
import com.alibaba.excel.EasyExcelFactory;
|
||||||
import com.alibaba.excel.enums.CellExtraTypeEnum;
|
import com.alibaba.excel.enums.CellExtraTypeEnum;
|
||||||
|
import com.alibaba.excel.metadata.data.HyperlinkData;
|
||||||
|
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||||
|
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||||
|
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||||||
|
import com.alibaba.excel.write.metadata.style.WriteFont;
|
||||||
|
import io.metersphere.functional.constants.FunctionalCaseTypeConstants;
|
||||||
|
import io.metersphere.functional.domain.*;
|
||||||
import io.metersphere.functional.dto.response.FunctionalCaseImportResponse;
|
import io.metersphere.functional.dto.response.FunctionalCaseImportResponse;
|
||||||
import io.metersphere.functional.excel.constants.FunctionalCaseImportFiled;
|
import io.metersphere.functional.excel.constants.FunctionalCaseImportFiled;
|
||||||
|
import io.metersphere.functional.excel.converter.FunctionalCaseExportConverter;
|
||||||
|
import io.metersphere.functional.excel.converter.FunctionalCaseExportConverterFactory;
|
||||||
import io.metersphere.functional.excel.domain.ExcelMergeInfo;
|
import io.metersphere.functional.excel.domain.ExcelMergeInfo;
|
||||||
import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
|
import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
|
||||||
import io.metersphere.functional.excel.domain.FunctionalCaseExcelDataFactory;
|
import io.metersphere.functional.excel.domain.FunctionalCaseExcelDataFactory;
|
||||||
|
import io.metersphere.functional.excel.domain.FunctionalCaseHeader;
|
||||||
|
import io.metersphere.functional.excel.handler.FunctionCaseMergeWriteHandler;
|
||||||
import io.metersphere.functional.excel.handler.FunctionCaseTemplateWriteHandler;
|
import io.metersphere.functional.excel.handler.FunctionCaseTemplateWriteHandler;
|
||||||
import io.metersphere.functional.excel.listener.FunctionalCaseCheckEventListener;
|
import io.metersphere.functional.excel.listener.FunctionalCaseCheckEventListener;
|
||||||
import io.metersphere.functional.excel.listener.FunctionalCaseImportEventListener;
|
import io.metersphere.functional.excel.listener.FunctionalCaseImportEventListener;
|
||||||
import io.metersphere.functional.excel.listener.FunctionalCasePretreatmentListener;
|
import io.metersphere.functional.excel.listener.FunctionalCasePretreatmentListener;
|
||||||
|
import io.metersphere.functional.excel.validate.AbstractCustomFieldValidator;
|
||||||
|
import io.metersphere.functional.excel.validate.CustomFieldValidatorFactory;
|
||||||
|
import io.metersphere.functional.mapper.ExtFunctionalCaseCommentMapper;
|
||||||
|
import io.metersphere.functional.request.FunctionalCaseExportRequest;
|
||||||
import io.metersphere.functional.request.FunctionalCaseImportRequest;
|
import io.metersphere.functional.request.FunctionalCaseImportRequest;
|
||||||
|
import io.metersphere.functional.socket.ExportWebSocketHandler;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
|
import io.metersphere.project.domain.Project;
|
||||||
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
||||||
|
import io.metersphere.project.mapper.ProjectMapper;
|
||||||
import io.metersphere.project.service.ProjectTemplateService;
|
import io.metersphere.project.service.ProjectTemplateService;
|
||||||
import io.metersphere.sdk.constants.TemplateScene;
|
import io.metersphere.sdk.constants.*;
|
||||||
|
import io.metersphere.sdk.dto.SocketMsgDTO;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.sdk.util.Translator;
|
import io.metersphere.sdk.util.*;
|
||||||
import io.metersphere.system.domain.CustomFieldOption;
|
import io.metersphere.system.domain.CustomFieldOption;
|
||||||
|
import io.metersphere.system.domain.SystemParameter;
|
||||||
|
import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||||
import io.metersphere.system.dto.sdk.SessionUser;
|
import io.metersphere.system.dto.sdk.SessionUser;
|
||||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
import io.metersphere.system.dto.sdk.TemplateDTO;
|
import io.metersphere.system.dto.sdk.TemplateDTO;
|
||||||
import io.metersphere.system.excel.utils.EasyExcelExporter;
|
import io.metersphere.system.excel.utils.EasyExcelExporter;
|
||||||
|
import io.metersphere.system.mapper.SystemParameterMapper;
|
||||||
|
import io.metersphere.system.service.FileService;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import io.metersphere.system.utils.ServiceUtils;
|
import io.metersphere.system.utils.ServiceUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.poi.ss.usermodel.Font;
|
||||||
|
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,7 +86,22 @@ public class FunctionalCaseFileService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private FunctionalCaseService functionalCaseService;
|
private FunctionalCaseService functionalCaseService;
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseModuleService functionalCaseModuleService;
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseCustomFieldService functionalCaseCustomFieldService;
|
||||||
|
private static final String EXPORT_CASE_TMP_DIR = "tmp";
|
||||||
|
private static final int EXPORT_CASE_MAX_COUNT = 2000;
|
||||||
|
@Resource
|
||||||
|
private ExtFunctionalCaseCommentMapper extFunctionalCaseCommentMapper;
|
||||||
|
@Resource
|
||||||
|
private ProjectMapper projectMapper;
|
||||||
|
@Resource
|
||||||
|
private FileService fileService;
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseLogService functionalCaseLogService;
|
||||||
|
@Resource
|
||||||
|
private SystemParameterMapper systemParameterMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载excel导入模板
|
* 下载excel导入模板
|
||||||
|
@ -105,10 +156,17 @@ public class FunctionalCaseFileService {
|
||||||
for (String head : headList) {
|
for (String head : headList) {
|
||||||
boolean isSystemField = false;
|
boolean isSystemField = false;
|
||||||
for (FunctionalCaseImportFiled importFiled : importFields) {
|
for (FunctionalCaseImportFiled importFiled : importFields) {
|
||||||
|
if (StringUtils.equals("name", importFiled.getValue()) && model.getHyperLinkName() != null) {
|
||||||
|
fields.add(model.getHyperLinkName());
|
||||||
|
isSystemField = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (importFiled.containsHead(head)) {
|
if (importFiled.containsHead(head)) {
|
||||||
fields.add(importFiled.parseExcelDataValue(model));
|
fields.add(importFiled.parseExcelDataValue(model));
|
||||||
isSystemField = true;
|
isSystemField = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!isSystemField) {
|
if (!isSystemField) {
|
||||||
Object value = customDataMaps.get(head);
|
Object value = customDataMaps.get(head);
|
||||||
|
@ -267,4 +325,408 @@ public class FunctionalCaseFileService {
|
||||||
throw new MSException(Translator.get("check_import_excel_error"));
|
throw new MSException(Translator.get("check_import_excel_error"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出excel
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
@Async
|
||||||
|
public void exportFunctionalCaseZip(FunctionalCaseExportRequest request) {
|
||||||
|
File tmpDir = null;
|
||||||
|
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
|
||||||
|
try {
|
||||||
|
tmpDir = new File(getClass().getClassLoader().getResource(StringUtils.EMPTY).getPath() +
|
||||||
|
EXPORT_CASE_TMP_DIR + File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr());
|
||||||
|
// 生成tmp随机目录
|
||||||
|
MsFileUtils.deleteDir(tmpDir.getPath());
|
||||||
|
tmpDir.mkdirs();
|
||||||
|
// 生成EXCEL
|
||||||
|
List<File> batchExcels = generateCaseExportExcel(tmpDir.getPath(), request, project);
|
||||||
|
if (batchExcels.size() > 1) {
|
||||||
|
// EXCEL -> ZIP (EXCEL数目大于1)
|
||||||
|
File zipFile = CompressUtils.zipFilesToPath(tmpDir.getPath() + File.separatorChar + "Metersphere_case_" + project.getName() + ".zip", batchExcels);
|
||||||
|
uploadFileToMinio(zipFile, request.getFileId());
|
||||||
|
} else {
|
||||||
|
// EXCEL (EXCEL数目等于1)
|
||||||
|
File singeFile = batchExcels.get(0);
|
||||||
|
uploadFileToMinio(singeFile, request.getFileId());
|
||||||
|
}
|
||||||
|
functionalCaseLogService.exportExcelLog(request);
|
||||||
|
SocketMsgDTO socketMsgDTO = new SocketMsgDTO(request.getFileId(), "", MsgType.CONNECT.name(), MsgType.CONNECT.name());
|
||||||
|
socketMsgDTO.setReportId(request.getFileId());
|
||||||
|
ExportWebSocketHandler.sendMessageSingle(socketMsgDTO);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadFileToMinio(File file, String fileId) {
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFileName(file.getName());
|
||||||
|
fileRequest.setFolder(DefaultRepositoryDir.getExportExcelTempDir() + "/" + fileId);
|
||||||
|
fileRequest.setStorage(StorageType.MINIO.name());
|
||||||
|
try {
|
||||||
|
FileInputStream inputStream = new FileInputStream(file);
|
||||||
|
fileService.upload(inputStream, fileRequest);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MSException("save file error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<File> generateCaseExportExcel(String tmpZipPath, FunctionalCaseExportRequest request, Project project) {
|
||||||
|
List<File> tmpExportExcelList = new ArrayList<>();
|
||||||
|
//excel表头
|
||||||
|
List<List<String>> headList = getFunctionalCaseExportHeads(request);
|
||||||
|
//获取导出的ids集合
|
||||||
|
List<String> ids = functionalCaseService.doSelectIds(request, request.getProjectId());
|
||||||
|
if (CollectionUtils.isEmpty(ids)) {
|
||||||
|
return tmpExportExcelList;
|
||||||
|
}
|
||||||
|
//获取当前项目下默认模板的自定义字段属性
|
||||||
|
List<TemplateCustomFieldDTO> customFields = getCustomFields(request.getProjectId());
|
||||||
|
//默认字段+自定义字段的 options集合
|
||||||
|
Map<String, List<String>> customFieldOptionsMap = getCustomFieldOptionsMap(customFields);
|
||||||
|
Map<String, TemplateCustomFieldDTO> customFieldMap = customFields.stream().collect(Collectors.toMap(TemplateCustomFieldDTO::getFieldName, templateCustomFieldDTO -> templateCustomFieldDTO));
|
||||||
|
|
||||||
|
//获取url
|
||||||
|
SystemParameter parameter = systemParameterMapper.selectByPrimaryKey(ParamConstants.BASE.URL.getValue());
|
||||||
|
|
||||||
|
//获取用例模块map
|
||||||
|
Map<String, String> moduleMap = getModuleMap(request.getProjectId());
|
||||||
|
//2000条,分批导出
|
||||||
|
AtomicInteger count = new AtomicInteger(0);
|
||||||
|
SubListUtils.dealForSubList(ids, EXPORT_CASE_MAX_COUNT, (subIds) -> {
|
||||||
|
count.getAndIncrement();
|
||||||
|
// 生成writeHandler
|
||||||
|
Map<Integer, Integer> rowMergeInfo = new HashMap<>();
|
||||||
|
FunctionCaseMergeWriteHandler writeHandler = new FunctionCaseMergeWriteHandler(rowMergeInfo, headList);
|
||||||
|
//表头备注信息
|
||||||
|
FunctionCaseTemplateWriteHandler handler = new FunctionCaseTemplateWriteHandler(headList, customFieldOptionsMap, customFieldMap);
|
||||||
|
|
||||||
|
//获取导出数据
|
||||||
|
List<FunctionalCaseExcelData> excelData = parseCaseData2ExcelData(subIds, rowMergeInfo, request, customFields, moduleMap, parameter.getParamValue());
|
||||||
|
List<List<Object>> data = parseExcelData2List(headList, excelData);
|
||||||
|
|
||||||
|
File createFile = new File(tmpZipPath + File.separatorChar + "Metersphere_case_" + project.getName() + count.get() + ".xlsx");
|
||||||
|
if (!createFile.exists()) {
|
||||||
|
try {
|
||||||
|
createFile.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MSException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//生成临时EXCEL
|
||||||
|
EasyExcel.write(createFile)
|
||||||
|
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
|
||||||
|
.registerWriteHandler(handler)
|
||||||
|
.registerWriteHandler(writeHandler)
|
||||||
|
.registerWriteHandler(FunctionCaseTemplateWriteHandler.getHorizontalWrapStrategy())
|
||||||
|
.excelType(ExcelTypeEnum.XLSX).sheet(Translator.get("test_case_import_template_sheet")).doWrite(data);
|
||||||
|
tmpExportExcelList.add(createFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
return tmpExportExcelList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<FunctionalCaseExcelData> parseCaseData2ExcelData(List<String> ids, Map<Integer, Integer> rowMergeInfo, FunctionalCaseExportRequest request, List<TemplateCustomFieldDTO> customFields, Map<String, String> moduleMap, String url) {
|
||||||
|
List<FunctionalCaseExcelData> list = new ArrayList<>();
|
||||||
|
//基础信息
|
||||||
|
Map<String, FunctionalCase> functionalCaseMap = functionalCaseService.copyBaseInfo(request.getProjectId(), ids);
|
||||||
|
//大字段
|
||||||
|
Map<String, FunctionalCaseBlob> functionalCaseBlobMap = functionalCaseService.copyBlobInfo(ids);
|
||||||
|
//自定义字段
|
||||||
|
Map<String, List<FunctionalCaseCustomField>> customFieldMap = functionalCaseCustomFieldService.getCustomFieldMapByCaseIds(ids);
|
||||||
|
//用例评论
|
||||||
|
Map<String, List<FunctionalCaseComment>> caseCommentMap = getCaseComment(ids);
|
||||||
|
//执行评论
|
||||||
|
Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap = getExecuteComment(ids);
|
||||||
|
//评审评论
|
||||||
|
Map<String, List<CaseReviewHistory>> reviewCommentMap = getReviewComment(ids);
|
||||||
|
|
||||||
|
ids.forEach(id -> {
|
||||||
|
List<String> textDescriptionList = new ArrayList<>();
|
||||||
|
List<String> expectedResultList = new ArrayList<>();
|
||||||
|
//构建基本参数
|
||||||
|
FunctionalCaseExcelData data = new FunctionalCaseExcelData();
|
||||||
|
FunctionalCase functionalCase = functionalCaseMap.get(id);
|
||||||
|
FunctionalCaseBlob functionalCaseBlob = functionalCaseBlobMap.get(id);
|
||||||
|
//构建基本参数
|
||||||
|
buildBaseField(data, functionalCase, functionalCaseBlob, moduleMap, textDescriptionList, expectedResultList, url);
|
||||||
|
//构建自定义字段
|
||||||
|
buildExportCustomField(customFields, customFieldMap.get(id), data, request);
|
||||||
|
//构建其他字段
|
||||||
|
buildExportOtherField(functionalCase, data, caseCommentMap, executeCommentMap, reviewCommentMap, request);
|
||||||
|
validateExportTextField(data);
|
||||||
|
if (CollectionUtils.isNotEmpty(textDescriptionList)) {
|
||||||
|
// 如果有多条步骤则添加多条数据,之后合并单元格
|
||||||
|
buildExportMergeData(rowMergeInfo, list, textDescriptionList, expectedResultList, data);
|
||||||
|
} else {
|
||||||
|
list.add(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建基本参数
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param functionalCase
|
||||||
|
* @param functionalCaseBlob
|
||||||
|
*/
|
||||||
|
private void buildBaseField(FunctionalCaseExcelData data, FunctionalCase functionalCase, FunctionalCaseBlob functionalCaseBlob, Map<String, String> moduleMap, List<String> textDescriptionList, List<String> expectedResultList, String url) {
|
||||||
|
data.setNum(functionalCase.getNum().toString());
|
||||||
|
data.setModule(moduleMap.get(functionalCase.getModuleId()));
|
||||||
|
data.setTags(functionalCase.getTags().toString());
|
||||||
|
//构建步骤
|
||||||
|
buildExportStep(data, functionalCaseBlob, functionalCase.getCaseEditType(), textDescriptionList, expectedResultList);
|
||||||
|
data.setPrerequisite(new String(functionalCaseBlob.getPrerequisite() == null ? new byte[0] : functionalCaseBlob.getPrerequisite(), StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
// 设置超链接
|
||||||
|
WriteCellData<String> hyperlink = new WriteCellData<>(functionalCase.getName());
|
||||||
|
data.setHyperLinkName(hyperlink);
|
||||||
|
HyperlinkData hyperlinkData = new HyperlinkData();
|
||||||
|
hyperlink.setHyperlinkData(hyperlinkData);
|
||||||
|
|
||||||
|
WriteFont writeFont = new WriteFont();
|
||||||
|
writeFont.setUnderline(Font.U_SINGLE);
|
||||||
|
writeFont.setColor(IndexedColors.BLUE.getIndex());
|
||||||
|
WriteCellStyle writeCellStyle = new WriteCellStyle();
|
||||||
|
writeCellStyle.setWriteFont(writeFont);
|
||||||
|
hyperlink.setWriteCellStyle(writeCellStyle);
|
||||||
|
|
||||||
|
hyperlinkData.setAddress(url + "/functional/case/detail/" + functionalCase.getId());
|
||||||
|
hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并单元格
|
||||||
|
*
|
||||||
|
* @param rowMergeInfo
|
||||||
|
* @param list
|
||||||
|
* @param textDescriptionList
|
||||||
|
* @param expectedResultList
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private void buildExportMergeData(Map<Integer, Integer> rowMergeInfo, List<FunctionalCaseExcelData> list, List<String> textDescriptionList, List<String> expectedResultList, FunctionalCaseExcelData data) {
|
||||||
|
for (int i = 0; i < textDescriptionList.size(); i++) {
|
||||||
|
FunctionalCaseExcelData excelData;
|
||||||
|
if (i == 0) {
|
||||||
|
// 第一行存全量元素
|
||||||
|
excelData = data;
|
||||||
|
if (textDescriptionList.size() > 1) {
|
||||||
|
// 保存合并单元格的下标和数量
|
||||||
|
rowMergeInfo.put(list.size() + 1, textDescriptionList.size());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 之后的行只存步骤
|
||||||
|
excelData = new FunctionalCaseExcelData();
|
||||||
|
}
|
||||||
|
excelData.setTextDescription(textDescriptionList.get(i));
|
||||||
|
excelData.setExpectedResult(expectedResultList.get(i));
|
||||||
|
list.add(excelData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateExportTextField(FunctionalCaseExcelData data) {
|
||||||
|
data.setPrerequisite(validateExportText(data.getPrerequisite()));
|
||||||
|
data.setDescription(validateExportText(data.getDescription()));
|
||||||
|
data.setTextDescription(validateExportText(data.getTextDescription()));
|
||||||
|
data.setExpectedResult(validateExportText(data.getExpectedResult()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建其他字段
|
||||||
|
*
|
||||||
|
* @param functionalCase
|
||||||
|
* @param data
|
||||||
|
* @param caseCommentMap
|
||||||
|
* @param executeCommentMap
|
||||||
|
* @param reviewCommentMap
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
private void buildExportOtherField(FunctionalCase functionalCase, FunctionalCaseExcelData data, Map<String, List<FunctionalCaseComment>> caseCommentMap, Map<String, List<TestPlanCaseExecuteHistory>> executeCommentMap, Map<String, List<CaseReviewHistory>> reviewCommentMap, FunctionalCaseExportRequest request) {
|
||||||
|
if (CollectionUtils.isEmpty(request.getOtherFields())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<FunctionalCaseHeader> otherFields = request.getOtherFields();
|
||||||
|
List<String> keys = otherFields.stream().map(FunctionalCaseHeader::getId).toList();
|
||||||
|
Map<String, FunctionalCaseExportConverter> converterMaps = FunctionalCaseExportConverterFactory.getConverters(keys);
|
||||||
|
HashMap<String, String> other = new HashMap<>();
|
||||||
|
otherFields.forEach(header -> {
|
||||||
|
FunctionalCaseExportConverter converter = converterMaps.get(header.getId());
|
||||||
|
if (converter != null) {
|
||||||
|
other.put(header.getName(), converter.parse(functionalCase, caseCommentMap, executeCommentMap, reviewCommentMap));
|
||||||
|
} else {
|
||||||
|
other.put(header.getName(), StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.setOtherFields(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评审评论
|
||||||
|
*
|
||||||
|
* @param ids
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Map<String, List<CaseReviewHistory>> getReviewComment(List<String> ids) {
|
||||||
|
List<CaseReviewHistory> reviewHistories = extFunctionalCaseCommentMapper.getReviewComment(ids);
|
||||||
|
Map<String, List<CaseReviewHistory>> reviewHistoryMap = reviewHistories.stream().collect(Collectors.groupingBy(CaseReviewHistory::getCaseId));
|
||||||
|
return reviewHistoryMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行评论
|
||||||
|
*
|
||||||
|
* @param ids
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Map<String, List<TestPlanCaseExecuteHistory>> getExecuteComment(List<String> ids) {
|
||||||
|
List<TestPlanCaseExecuteHistory> historyList = extFunctionalCaseCommentMapper.getExecuteComment(ids);
|
||||||
|
Map<String, List<TestPlanCaseExecuteHistory>> commentMap = historyList.stream().collect(Collectors.groupingBy(TestPlanCaseExecuteHistory::getCaseId));
|
||||||
|
return commentMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例评论
|
||||||
|
*
|
||||||
|
* @param ids
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Map<String, List<FunctionalCaseComment>> getCaseComment(List<String> ids) {
|
||||||
|
List<FunctionalCaseComment> functionalCaseComments = extFunctionalCaseCommentMapper.getCaseComment(ids);
|
||||||
|
Map<String, List<FunctionalCaseComment>> commentMap = functionalCaseComments.stream().collect(Collectors.groupingBy(FunctionalCaseComment::getCaseId));
|
||||||
|
return commentMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建自定义字段
|
||||||
|
*
|
||||||
|
* @param templateCustomFields
|
||||||
|
* @param functionalCaseCustomFields
|
||||||
|
* @param data
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
private void buildExportCustomField(List<TemplateCustomFieldDTO> templateCustomFields, List<FunctionalCaseCustomField> functionalCaseCustomFields, FunctionalCaseExcelData data, FunctionalCaseExportRequest request) {
|
||||||
|
if (CollectionUtils.isEmpty(request.getCustomFields())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HashMap<String, AbstractCustomFieldValidator> customFieldValidatorMap = CustomFieldValidatorFactory.getValidatorMap();
|
||||||
|
Map<String, TemplateCustomFieldDTO> customFieldsMap = templateCustomFields.stream().collect(Collectors.toMap(TemplateCustomFieldDTO::getFieldId, i -> i));
|
||||||
|
Map<String, String> caseFieldvalueMap = functionalCaseCustomFields.stream().collect(Collectors.toMap(FunctionalCaseCustomField::getFieldId, FunctionalCaseCustomField::getValue));
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
customFieldsMap.forEach((k, v) -> {
|
||||||
|
if (caseFieldvalueMap.containsKey(k)) {
|
||||||
|
AbstractCustomFieldValidator customFieldValidator = customFieldValidatorMap.get(v.getType());
|
||||||
|
if (customFieldValidator.isKVOption) {
|
||||||
|
// 这里如果填的是选项值,替换成选项ID,保存
|
||||||
|
map.put(v.getFieldName(), customFieldValidator.parse2Value(caseFieldvalueMap.get(k), v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.setCustomData(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String validateExportText(String textValue) {
|
||||||
|
// poi 导出的单个单元格最大字符数量为 32767 ,这里添加校验提示
|
||||||
|
int maxLength = 32767;
|
||||||
|
if (StringUtils.isNotBlank(textValue) && textValue.length() > maxLength) {
|
||||||
|
return String.format(Translator.get("case_export_text_validate_tip"), maxLength);
|
||||||
|
}
|
||||||
|
return textValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建步骤单元格
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param functionalCaseBlob
|
||||||
|
* @param caseEditType
|
||||||
|
* @param textDescriptionList
|
||||||
|
* @param expectedResultList
|
||||||
|
*/
|
||||||
|
private void buildExportStep(FunctionalCaseExcelData data, FunctionalCaseBlob functionalCaseBlob, String caseEditType, List<String> textDescriptionList, List<String> expectedResultList) {
|
||||||
|
if (StringUtils.equals(caseEditType, FunctionalCaseTypeConstants.CaseEditType.TEXT.name())) {
|
||||||
|
data.setTextDescription(new String(functionalCaseBlob.getTextDescription() == null ? new byte[0] : functionalCaseBlob.getTextDescription(), StandardCharsets.UTF_8));
|
||||||
|
data.setExpectedResult(new String(functionalCaseBlob.getExpectedResult() == null ? new byte[0] : functionalCaseBlob.getExpectedResult(), StandardCharsets.UTF_8));
|
||||||
|
} else {
|
||||||
|
String steps = new String(functionalCaseBlob.getSteps() == null ? new byte[0] : functionalCaseBlob.getSteps(), StandardCharsets.UTF_8);
|
||||||
|
List jsonArray = new ArrayList();
|
||||||
|
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++) {
|
||||||
|
// 将步骤存储起来,之后生成多条数据,再合并单元格
|
||||||
|
Map item = (Map) jsonArray.get(j);
|
||||||
|
String textDescription = Optional.ofNullable(item.get("desc")).orElse(StringUtils.EMPTY).toString();
|
||||||
|
String expectedResult = Optional.ofNullable(item.get("result")).orElse(StringUtils.EMPTY).toString();
|
||||||
|
if (StringUtils.isNotBlank(textDescription) || StringUtils.isNotBlank(expectedResult)) {
|
||||||
|
textDescriptionList.add(textDescription);
|
||||||
|
expectedResultList.add(expectedResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取模块map
|
||||||
|
*
|
||||||
|
* @param projectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Map<String, String> getModuleMap(String projectId) {
|
||||||
|
List<BaseTreeNode> moduleTree = functionalCaseModuleService.getTree(projectId);
|
||||||
|
Map<String, String> moduleMap = moduleTree.stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
|
||||||
|
return moduleMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取导出表头
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private List<List<String>> getFunctionalCaseExportHeads(FunctionalCaseExportRequest request) {
|
||||||
|
List<List<String>> headList = new ArrayList<>() {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 5726921174161850104L;
|
||||||
|
|
||||||
|
{
|
||||||
|
addAll(request.getSystemFields()
|
||||||
|
.stream()
|
||||||
|
.map(item -> Arrays.asList(item.getName()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
addAll(request.getCustomFields()
|
||||||
|
.stream()
|
||||||
|
.map(item -> Arrays.asList(item.getName()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
addAll(request.getOtherFields()
|
||||||
|
.stream()
|
||||||
|
.map(item -> Arrays.asList(item.getName()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return headList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,19 +51,12 @@ public class FunctionalCaseLogService {
|
||||||
private FunctionalCaseAttachmentMapper functionalCaseAttachmentMapper;
|
private FunctionalCaseAttachmentMapper functionalCaseAttachmentMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private FileAssociationMapper fileAssociationMapper;
|
private FileAssociationMapper fileAssociationMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CustomFieldMapper customFieldMapper;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private BugRelationCaseMapper bugRelationCaseMapper;
|
private BugRelationCaseMapper bugRelationCaseMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private BugMapper bugMapper;
|
private BugMapper bugMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private ExtFunctionalCaseModuleMapper extFunctionalCaseModuleMapper;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新用例 日志
|
* 更新用例 日志
|
||||||
|
@ -434,4 +427,21 @@ public class FunctionalCaseLogService {
|
||||||
dto.setMethod(HttpMethodConstants.POST.name());
|
dto.setMethod(HttpMethodConstants.POST.name());
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public LogDTO exportExcelLog(FunctionalCaseExportRequest request) {
|
||||||
|
LogDTO dto = new LogDTO(
|
||||||
|
request.getProjectId(),
|
||||||
|
null,
|
||||||
|
request.getFileId(),
|
||||||
|
null,
|
||||||
|
OperationLogType.EXPORT.name(),
|
||||||
|
OperationLogModule.FUNCTIONAL_CASE,
|
||||||
|
"");
|
||||||
|
dto.setHistory(true);
|
||||||
|
dto.setPath("/functional/case/export/excel");
|
||||||
|
dto.setMethod(HttpMethodConstants.POST.name());
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,7 +303,7 @@ public class FunctionalCaseModuleService extends ModuleTreeService {
|
||||||
currentModuleName = itemIterator.next().trim();
|
currentModuleName = itemIterator.next().trim();
|
||||||
moduleTree.forEach(module -> {
|
moduleTree.forEach(module -> {
|
||||||
//根节点是否存在
|
//根节点是否存在
|
||||||
if (StringUtils.equals(currentModuleName, module.getName())) {
|
if (StringUtils.equalsIgnoreCase(currentModuleName, module.getName())) {
|
||||||
hasNode.set(true);
|
hasNode.set(true);
|
||||||
//根节点存在,检查子节点是否存在
|
//根节点存在,检查子节点是否存在
|
||||||
createModuleByPathIterator(itemIterator, "/" + currentModuleName, module, pathMap, projectId, userId);
|
createModuleByPathIterator(itemIterator, "/" + currentModuleName, module, pathMap, projectId, userId);
|
||||||
|
@ -342,7 +342,7 @@ public class FunctionalCaseModuleService extends ModuleTreeService {
|
||||||
String nodeName = itemIterator.next().trim();
|
String nodeName = itemIterator.next().trim();
|
||||||
AtomicReference<Boolean> hasNode = new AtomicReference<>(false);
|
AtomicReference<Boolean> hasNode = new AtomicReference<>(false);
|
||||||
children.forEach(child -> {
|
children.forEach(child -> {
|
||||||
if (StringUtils.equals(nodeName, child.getName())) {
|
if (StringUtils.equalsIgnoreCase(nodeName, child.getName())) {
|
||||||
hasNode.set(true);
|
hasNode.set(true);
|
||||||
createModuleByPathIterator(itemIterator, currentModulePath + "/" + child.getName(), child, pathMap, projectId, userId);
|
createModuleByPathIterator(itemIterator, currentModulePath + "/" + child.getName(), child, pathMap, projectId, userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1158,7 +1158,7 @@ public class FunctionalCaseService {
|
||||||
private void noticeModule(List<FunctionalCaseDTO> noticeList, FunctionalCaseExcelData functionalCaseExcelData, FunctionalCaseImportRequest request, String userId, Map<String, TemplateCustomFieldDTO> customFieldsMap) {
|
private void noticeModule(List<FunctionalCaseDTO> noticeList, FunctionalCaseExcelData functionalCaseExcelData, FunctionalCaseImportRequest request, String userId, Map<String, TemplateCustomFieldDTO> customFieldsMap) {
|
||||||
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
|
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
|
||||||
functionalCaseDTO.setTriggerMode(Translator.get("log.test_plan.functional_case"));
|
functionalCaseDTO.setTriggerMode(Translator.get("log.test_plan.functional_case"));
|
||||||
functionalCaseDTO.setName(functionalCaseExcelData.getName());
|
functionalCaseDTO.setName(functionalCaseExcelData.getName().toString());
|
||||||
functionalCaseDTO.setProjectId(request.getProjectId());
|
functionalCaseDTO.setProjectId(request.getProjectId());
|
||||||
functionalCaseDTO.setCaseEditType(functionalCaseExcelData.getCaseEditType());
|
functionalCaseDTO.setCaseEditType(functionalCaseExcelData.getCaseEditType());
|
||||||
functionalCaseDTO.setCreateUser(userId);
|
functionalCaseDTO.setCreateUser(userId);
|
||||||
|
@ -1187,7 +1187,7 @@ public class FunctionalCaseService {
|
||||||
functionalCase.setModuleId(caseModulePathMap.get(functionalCaseExcelData.getModule()));
|
functionalCase.setModuleId(caseModulePathMap.get(functionalCaseExcelData.getModule()));
|
||||||
functionalCase.setProjectId(request.getProjectId());
|
functionalCase.setProjectId(request.getProjectId());
|
||||||
functionalCase.setTemplateId(defaultTemplateDTO.getId());
|
functionalCase.setTemplateId(defaultTemplateDTO.getId());
|
||||||
functionalCase.setName(functionalCaseExcelData.getName());
|
functionalCase.setName(functionalCaseExcelData.getName().toString());
|
||||||
functionalCase.setReviewStatus(FunctionalCaseReviewStatus.UN_REVIEWED.name());
|
functionalCase.setReviewStatus(FunctionalCaseReviewStatus.UN_REVIEWED.name());
|
||||||
functionalCase.setTags(handleImportTags(functionalCaseExcelData.getTags()));
|
functionalCase.setTags(handleImportTags(functionalCaseExcelData.getTags()));
|
||||||
functionalCase.setCaseEditType(StringUtils.defaultIfBlank(functionalCaseExcelData.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name()));
|
functionalCase.setCaseEditType(StringUtils.defaultIfBlank(functionalCaseExcelData.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name()));
|
||||||
|
@ -1384,7 +1384,7 @@ public class FunctionalCaseService {
|
||||||
//用例表
|
//用例表
|
||||||
FunctionalCase functionalCase = collect.get(functionalCaseExcelData.getNum()).getFirst();
|
FunctionalCase functionalCase = collect.get(functionalCaseExcelData.getNum()).getFirst();
|
||||||
|
|
||||||
functionalCase.setName(functionalCaseExcelData.getName());
|
functionalCase.setName(functionalCaseExcelData.getName().toString());
|
||||||
functionalCase.setModuleId(caseModulePathMap.get(functionalCaseExcelData.getModule()));
|
functionalCase.setModuleId(caseModulePathMap.get(functionalCaseExcelData.getModule()));
|
||||||
functionalCase.setTags(handleImportTags(functionalCaseExcelData.getTags()));
|
functionalCase.setTags(handleImportTags(functionalCaseExcelData.getTags()));
|
||||||
functionalCase.setCaseEditType(StringUtils.defaultIfBlank(functionalCaseExcelData.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name()));
|
functionalCase.setCaseEditType(StringUtils.defaultIfBlank(functionalCaseExcelData.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name()));
|
||||||
|
@ -1417,7 +1417,7 @@ public class FunctionalCaseService {
|
||||||
FunctionalCase functionalCase = collect.get(functionalCaseExcelData.getNum()).getFirst();
|
FunctionalCase functionalCase = collect.get(functionalCaseExcelData.getNum()).getFirst();
|
||||||
if (CollectionUtils.isNotEmpty(projectApplications) && Boolean.valueOf(projectApplications.getFirst().getTypeValue())) {
|
if (CollectionUtils.isNotEmpty(projectApplications) && Boolean.valueOf(projectApplications.getFirst().getTypeValue())) {
|
||||||
FunctionalCaseBlob blob = blobsCollect.get(functionalCaseExcelData.getNum()).getFirst();
|
FunctionalCaseBlob blob = blobsCollect.get(functionalCaseExcelData.getNum()).getFirst();
|
||||||
if (!StringUtils.equals(functionalCase.getName(), functionalCaseExcelData.getName())
|
if (!StringUtils.equals(functionalCase.getName(), functionalCaseExcelData.getName().toString())
|
||||||
|| !StringUtils.equals(new String(blob.getSteps(), StandardCharsets.UTF_8), StringUtils.defaultIfBlank(functionalCaseExcelData.getSteps(), StringUtils.EMPTY))
|
|| !StringUtils.equals(new String(blob.getSteps(), StandardCharsets.UTF_8), StringUtils.defaultIfBlank(functionalCaseExcelData.getSteps(), StringUtils.EMPTY))
|
||||||
|| !StringUtils.equals(new String(blob.getTextDescription(), StandardCharsets.UTF_8), StringUtils.defaultIfBlank(functionalCaseExcelData.getTextDescription(), StringUtils.EMPTY))
|
|| !StringUtils.equals(new String(blob.getTextDescription(), StandardCharsets.UTF_8), StringUtils.defaultIfBlank(functionalCaseExcelData.getTextDescription(), StringUtils.EMPTY))
|
||||||
|| !StringUtils.equals(new String(blob.getExpectedResult(), StandardCharsets.UTF_8), StringUtils.defaultIfBlank(functionalCaseExcelData.getExpectedResult(), StringUtils.EMPTY))) {
|
|| !StringUtils.equals(new String(blob.getExpectedResult(), StandardCharsets.UTF_8), StringUtils.defaultIfBlank(functionalCaseExcelData.getExpectedResult(), StringUtils.EMPTY))) {
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
package io.metersphere.functional.socket;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.MsgType;
|
||||||
|
import io.metersphere.sdk.dto.SocketMsgDTO;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import jakarta.websocket.*;
|
||||||
|
import jakarta.websocket.server.PathParam;
|
||||||
|
import jakarta.websocket.server.ServerEndpoint;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ServerEndpoint("/ws/export/{fileId}")
|
||||||
|
public class ExportWebSocketHandler {
|
||||||
|
|
||||||
|
public static final Map<String, Session> ONLINE_EXPORT_EXCEL_SESSIONS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static void sendMessage(Session session, SocketMsgDTO message) {
|
||||||
|
if (session == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 替换了web容器后 jetty没有设置永久有效的参数,这里暂时设置超时时间为一天
|
||||||
|
session.setMaxIdleTimeout(86400000L);
|
||||||
|
RemoteEndpoint.Async async = session.getAsyncRemote();
|
||||||
|
if (async == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
async.sendText(JSON.toJSONString(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendMessageSingle(SocketMsgDTO dto) {
|
||||||
|
sendMessage(ONLINE_EXPORT_EXCEL_SESSIONS.get(Optional.ofNullable(dto.getReportId())
|
||||||
|
.orElse(StringUtils.EMPTY)), dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接成功响应
|
||||||
|
*/
|
||||||
|
@OnOpen
|
||||||
|
public void openSession(@PathParam("fileId") String fileId, Session session) {
|
||||||
|
ONLINE_EXPORT_EXCEL_SESSIONS.put(fileId, session);
|
||||||
|
RemoteEndpoint.Async async = session.getAsyncRemote();
|
||||||
|
if (async != null) {
|
||||||
|
async.sendText(JSON.toJSONString(new SocketMsgDTO(fileId, "", MsgType.CONNECT.name(), MsgType.CONNECT.name())));
|
||||||
|
session.setMaxIdleTimeout(180000);
|
||||||
|
}
|
||||||
|
LogUtils.info("客户端: [" + fileId + "] : 连接成功!" + ExportWebSocketHandler.ONLINE_EXPORT_EXCEL_SESSIONS.size(), fileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到消息响应
|
||||||
|
*/
|
||||||
|
@OnMessage
|
||||||
|
public void onMessage(@PathParam("fileId") String fileId, String message) {
|
||||||
|
LogUtils.info("服务器收到:[" + fileId + "] : " + message);
|
||||||
|
SocketMsgDTO dto = JSON.parseObject(message, SocketMsgDTO.class);
|
||||||
|
ExportWebSocketHandler.sendMessageSingle(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接关闭响应
|
||||||
|
*/
|
||||||
|
@OnClose
|
||||||
|
public void onClose(@PathParam("fileId") String fileId, Session session) throws IOException {
|
||||||
|
//当前的Session 移除
|
||||||
|
ExportWebSocketHandler.ONLINE_EXPORT_EXCEL_SESSIONS.remove(fileId);
|
||||||
|
LogUtils.info("[" + fileId + "] : 断开连接!" + ExportWebSocketHandler.ONLINE_EXPORT_EXCEL_SESSIONS.size());
|
||||||
|
//并且通知其他人当前用户已经断开连接了
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接异常响应
|
||||||
|
*/
|
||||||
|
@OnError
|
||||||
|
public void onError(Session session, Throwable throwable) throws IOException {
|
||||||
|
LogUtils.error("连接异常响应", throwable);
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每一分钟群发一次心跳检查
|
||||||
|
*/
|
||||||
|
@Scheduled(fixedRate = 60000)
|
||||||
|
public void heartbeatCheck() {
|
||||||
|
ExportWebSocketHandler.sendMessageSingle(
|
||||||
|
new SocketMsgDTO(MsgType.HEARTBEAT.name(), MsgType.HEARTBEAT.name(), MsgType.HEARTBEAT.name(), "heartbeat check")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import io.metersphere.functional.dto.CaseCustomFieldDTO;
|
||||||
import io.metersphere.functional.dto.FunctionalCaseAttachmentDTO;
|
import io.metersphere.functional.dto.FunctionalCaseAttachmentDTO;
|
||||||
import io.metersphere.functional.dto.FunctionalCasePageDTO;
|
import io.metersphere.functional.dto.FunctionalCasePageDTO;
|
||||||
import io.metersphere.functional.dto.response.FunctionalCaseImportResponse;
|
import io.metersphere.functional.dto.response.FunctionalCaseImportResponse;
|
||||||
|
import io.metersphere.functional.excel.domain.FunctionalCaseHeader;
|
||||||
import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper;
|
import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper;
|
||||||
import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper;
|
import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper;
|
||||||
import io.metersphere.functional.request.*;
|
import io.metersphere.functional.request.*;
|
||||||
|
@ -85,6 +86,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
public static final String CHECK_EXCEL_URL = "/functional/case/pre-check/excel";
|
public static final String CHECK_EXCEL_URL = "/functional/case/pre-check/excel";
|
||||||
public static final String IMPORT_EXCEL_URL = "/functional/case/import/excel";
|
public static final String IMPORT_EXCEL_URL = "/functional/case/import/excel";
|
||||||
public static final String OPERATION_HISTORY_URL = "/functional/case/operation-history";
|
public static final String OPERATION_HISTORY_URL = "/functional/case/operation-history";
|
||||||
|
public static final String EXPORT_EXCEL_URL = "/functional/case/export/excel";
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private NotificationMapper notificationMapper;
|
private NotificationMapper notificationMapper;
|
||||||
|
@ -795,4 +797,41 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
Assertions.assertTrue(CollectionUtils.isNotEmpty(operationHistoryDTOS));
|
Assertions.assertTrue(CollectionUtils.isNotEmpty(operationHistoryDTOS));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(2)
|
||||||
|
public void exportExcel() throws Exception {
|
||||||
|
FunctionalCaseExportRequest request = new FunctionalCaseExportRequest();
|
||||||
|
request.setProjectId(DEFAULT_PROJECT_ID);
|
||||||
|
request.setSelectIds(List.of("TEST_FUNCTIONAL_CASE_ID"));
|
||||||
|
List<FunctionalCaseHeader> sysHeaders = new ArrayList<>() {{
|
||||||
|
add(new FunctionalCaseHeader() {{
|
||||||
|
setId("num");
|
||||||
|
setName("ID");
|
||||||
|
}});
|
||||||
|
add(new FunctionalCaseHeader() {{
|
||||||
|
setId("name");
|
||||||
|
setName("用例名称");
|
||||||
|
}});
|
||||||
|
}};
|
||||||
|
request.setSystemFields(sysHeaders);
|
||||||
|
List<FunctionalCaseHeader> customHeaders = new ArrayList<>() {{
|
||||||
|
add(new FunctionalCaseHeader() {{
|
||||||
|
setId("A");
|
||||||
|
setName("测试3");
|
||||||
|
}});
|
||||||
|
}};
|
||||||
|
request.setCustomFields(customHeaders);
|
||||||
|
List<FunctionalCaseHeader> otherHeaders = new ArrayList<>() {{
|
||||||
|
add(new FunctionalCaseHeader() {{
|
||||||
|
setId("createTime");
|
||||||
|
setName("创建时间");
|
||||||
|
}});
|
||||||
|
}};
|
||||||
|
request.setOtherFields(otherHeaders);
|
||||||
|
|
||||||
|
request.setFileId("123142342");
|
||||||
|
this.requestPost(EXPORT_EXCEL_URL, request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public class MinioConfig {
|
||||||
|
|
||||||
// 设置临时目录下文件的过期时间
|
// 设置临时目录下文件的过期时间
|
||||||
setBucketLifecycle(minioClient);
|
setBucketLifecycle(minioClient);
|
||||||
|
setBucketLifecycleByExcel(minioClient);
|
||||||
|
|
||||||
boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET).build());
|
boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET).build());
|
||||||
if (!exist) {
|
if (!exist) {
|
||||||
|
@ -66,4 +67,36 @@ public class MinioConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置生命周期规则-文件的过期时间
|
||||||
|
* 将 system/export/excel/ 下的文件设置为 1 天后过期
|
||||||
|
* 参考 minio 8.5.2 版本的示例代码
|
||||||
|
* https://github.com/minio/minio-java/blob/8.5.2/examples/SetBucketLifecycle.java
|
||||||
|
*/
|
||||||
|
private static void setBucketLifecycleByExcel(MinioClient minioClient) {
|
||||||
|
List<LifecycleRule> rules = new LinkedList<>();
|
||||||
|
rules.add(
|
||||||
|
new LifecycleRule(
|
||||||
|
Status.ENABLED,
|
||||||
|
null,
|
||||||
|
new Expiration((ZonedDateTime) null, 1, null),
|
||||||
|
new RuleFilter("system/export/excel"),
|
||||||
|
"excel-file",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null));
|
||||||
|
LifecycleConfiguration config = new LifecycleConfiguration(rules);
|
||||||
|
try {
|
||||||
|
minioClient.setBucketLifecycle(
|
||||||
|
SetBucketLifecycleArgs.builder()
|
||||||
|
.bucket(BUCKET)
|
||||||
|
.config(config)
|
||||||
|
.build());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue