feat(测试用例): 用例xmind模板下载
This commit is contained in:
parent
7e9a68d3f1
commit
f53bac0332
|
@ -34,6 +34,12 @@
|
||||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||||
<artifactId>httpclient5</artifactId>
|
<artifactId>httpclient5</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- xmind export-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.eljah</groupId>
|
||||||
|
<artifactId>xmindjbehaveplugin</artifactId>
|
||||||
|
<version>${xmindjbehaveplugin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- start spring 相关 -->
|
<!-- start spring 相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -256,4 +256,14 @@ case.review.status.re_reviewed=Re reviewed
|
||||||
case.execute.status.pending=Pending
|
case.execute.status.pending=Pending
|
||||||
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
||||||
functional_case_execute_comment_template=[Execute comment:%s %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
|
functional_case_review_comment_template=[Review comment:%s %s(%s)]\n%s\n
|
||||||
|
functional_case_xmind_template=Functional case xmind template
|
||||||
|
download_template_failed=Download template failed
|
||||||
|
functional_case=Functional case
|
||||||
|
xmind_prerequisite=Prerequisite
|
||||||
|
xmind_description=Remark
|
||||||
|
xmind_tags=tags
|
||||||
|
xmind_textDescription=Text description
|
||||||
|
xmind_expectedResult=Expected result
|
||||||
|
xmind_step=Step
|
||||||
|
xmind_stepDescription=Step description
|
|
@ -254,4 +254,14 @@ case.review.status.re_reviewed=重新提审
|
||||||
case.execute.status.pending=未执行
|
case.execute.status.pending=未执行
|
||||||
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
||||||
functional_case_execute_comment_template=【执行评论:%s %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
|
functional_case_review_comment_template=【评审评论:%s %s(%s)】\n%s\n
|
||||||
|
functional_case_xmind_template=思维导图用例模版
|
||||||
|
download_template_failed=下载思维导图模版失败
|
||||||
|
functional_case=功能用例
|
||||||
|
xmind_prerequisite=前置条件
|
||||||
|
xmind_description=备注
|
||||||
|
xmind_tags=标签
|
||||||
|
xmind_textDescription=文本描述
|
||||||
|
xmind_expectedResult=预期结果
|
||||||
|
xmind_step=用例步骤
|
||||||
|
xmind_stepDescription=步骤描述
|
|
@ -256,4 +256,13 @@ case.execute.status.pending=未執行
|
||||||
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
functional_case_comment_template=【评论:%s(%s)】\n%s\n
|
||||||
functional_case_execute_comment_template=【執行評論:%s %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
|
functional_case_review_comment_template=【評審評論:%s %s(%s)】\n%s\n
|
||||||
|
functional_case_xmind_template=思維導圖用例模板
|
||||||
|
download_template_failed=下載思維導圖模板失敗
|
||||||
|
functional_case=功能用例
|
||||||
|
xmind_prerequisite=前置條件
|
||||||
|
xmind_description=備注
|
||||||
|
xmind_tags=標簽
|
||||||
|
xmind_textDescription=文本描述
|
||||||
|
xmind_expectedResult=預期結果
|
||||||
|
xmind_step=用例步驟
|
||||||
|
xmind_stepDescription=步驟描述
|
|
@ -9,10 +9,7 @@ import io.metersphere.functional.dto.FunctionalCasePageDTO;
|
||||||
import io.metersphere.functional.dto.FunctionalCaseVersionDTO;
|
import io.metersphere.functional.dto.FunctionalCaseVersionDTO;
|
||||||
import io.metersphere.functional.dto.response.FunctionalCaseImportResponse;
|
import io.metersphere.functional.dto.response.FunctionalCaseImportResponse;
|
||||||
import io.metersphere.functional.request.*;
|
import io.metersphere.functional.request.*;
|
||||||
import io.metersphere.functional.service.FunctionalCaseFileService;
|
import io.metersphere.functional.service.*;
|
||||||
import io.metersphere.functional.service.FunctionalCaseLogService;
|
|
||||||
import io.metersphere.functional.service.FunctionalCaseNoticeService;
|
|
||||||
import io.metersphere.functional.service.FunctionalCaseService;
|
|
||||||
import io.metersphere.project.dto.CustomFieldOptions;
|
import io.metersphere.project.dto.CustomFieldOptions;
|
||||||
import io.metersphere.project.service.ProjectTemplateService;
|
import io.metersphere.project.service.ProjectTemplateService;
|
||||||
import io.metersphere.sdk.constants.PermissionConstants;
|
import io.metersphere.sdk.constants.PermissionConstants;
|
||||||
|
@ -33,7 +30,6 @@ 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;
|
||||||
|
@ -60,6 +56,8 @@ public class FunctionalCaseController {
|
||||||
private ProjectTemplateService projectTemplateService;
|
private ProjectTemplateService projectTemplateService;
|
||||||
@Resource
|
@Resource
|
||||||
private FunctionalCaseFileService functionalCaseFileService;
|
private FunctionalCaseFileService functionalCaseFileService;
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseXmindService functionalCaseXmindService;
|
||||||
|
|
||||||
//TODO 获取模板列表(多模板功能暂时不做)
|
//TODO 获取模板列表(多模板功能暂时不做)
|
||||||
|
|
||||||
|
@ -257,4 +255,12 @@ public class FunctionalCaseController {
|
||||||
public void testCaseExport(@Validated @RequestBody FunctionalCaseExportRequest request) {
|
public void testCaseExport(@Validated @RequestBody FunctionalCaseExportRequest request) {
|
||||||
functionalCaseFileService.exportFunctionalCaseZip(request);
|
functionalCaseFileService.exportFunctionalCaseZip(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/download/xmind/template/{projectId}")
|
||||||
|
@Operation(summary = "用例管理-功能用例-xmind导入-下载模板")
|
||||||
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
|
||||||
|
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||||
|
public void xmindTemplateExport(@PathVariable String projectId, HttpServletResponse response) {
|
||||||
|
functionalCaseXmindService.downloadXmindTemplate(projectId, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,7 +280,7 @@ public class FunctionalCaseFileService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TemplateCustomFieldDTO> getCustomFields(String projectId) {
|
public List<TemplateCustomFieldDTO> getCustomFields(String projectId) {
|
||||||
TemplateDTO defaultTemplateDTO = projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.FUNCTIONAL.name());
|
TemplateDTO defaultTemplateDTO = projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.FUNCTIONAL.name());
|
||||||
List<TemplateCustomFieldDTO> customFields = Optional.ofNullable(defaultTemplateDTO.getCustomFields()).orElse(new ArrayList<>());
|
List<TemplateCustomFieldDTO> customFields = Optional.ofNullable(defaultTemplateDTO.getCustomFields()).orElse(new ArrayList<>());
|
||||||
return customFields;
|
return customFields;
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package io.metersphere.functional.service;
|
||||||
|
|
||||||
|
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindData;
|
||||||
|
import io.metersphere.functional.xmind.utils.XmindExportUtil;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public class FunctionalCaseXmindService {
|
||||||
|
|
||||||
|
public static final String template = "/template/template.json";
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseFileService functionalCaseFileService;
|
||||||
|
|
||||||
|
|
||||||
|
public void downloadXmindTemplate(String projectId, HttpServletResponse response) {
|
||||||
|
List<TemplateCustomFieldDTO> customFields = functionalCaseFileService.getCustomFields(projectId);
|
||||||
|
try (InputStream stream = FunctionalCaseXmindService.class.getResourceAsStream(template)) {
|
||||||
|
FunctionalCaseXmindData functionalCaseXmindData = JSON.parseObject(stream, FunctionalCaseXmindData.class);
|
||||||
|
setTemplateCustomFields(functionalCaseXmindData.getChildren(), customFields);
|
||||||
|
|
||||||
|
XmindExportUtil.downloadTemplate(response, functionalCaseXmindData, true);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e.getMessage());
|
||||||
|
throw new MSException(Translator.get("download_template_failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTemplateCustomFields(List<FunctionalCaseXmindData> children, List<TemplateCustomFieldDTO> customFields) {
|
||||||
|
if (CollectionUtils.isNotEmpty(children)) {
|
||||||
|
children.forEach(data -> {
|
||||||
|
data.getFunctionalCaseList().forEach(item -> {
|
||||||
|
item.setTemplateCustomFieldDTOList(customFields);
|
||||||
|
});
|
||||||
|
if (CollectionUtils.isNotEmpty(data.getChildren())) {
|
||||||
|
setTemplateCustomFields(data.getChildren(), customFields);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package io.metersphere.functional.xmind.domain;
|
||||||
|
|
||||||
|
import io.metersphere.functional.dto.FunctionalCaseCustomFieldDTO;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class FunctionalCaseXmindDTO {
|
||||||
|
|
||||||
|
@Schema(description = "ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "业务ID")
|
||||||
|
private String num;
|
||||||
|
|
||||||
|
@Schema(description = "项目ID")
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
@Schema(description = "名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "标签(JSON)")
|
||||||
|
private String tags;
|
||||||
|
|
||||||
|
@Schema(description = "编辑模式:步骤模式/文本模式")
|
||||||
|
private String caseEditType;
|
||||||
|
|
||||||
|
@Schema(description = "用例步骤(JSON),step_model 为 Step 时启用")
|
||||||
|
private String steps;
|
||||||
|
|
||||||
|
@Schema(description = "步骤描述,step_model 为 Text 时启用")
|
||||||
|
private String textDescription;
|
||||||
|
|
||||||
|
@Schema(description = "预期结果,step_model 为 Text 时启用")
|
||||||
|
private String expectedResult;
|
||||||
|
|
||||||
|
@Schema(description = "前置条件")
|
||||||
|
private String prerequisite;
|
||||||
|
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "自定义字段")
|
||||||
|
private List<FunctionalCaseCustomFieldDTO> customFieldDTOList;
|
||||||
|
|
||||||
|
@Schema(description = "模板自定义字段")
|
||||||
|
private List<TemplateCustomFieldDTO> templateCustomFieldDTOList;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package io.metersphere.functional.xmind.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class FunctionalCaseXmindData implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private List<FunctionalCaseXmindDTO> functionalCaseList;
|
||||||
|
private String moduleName;
|
||||||
|
private String moduleId;
|
||||||
|
private List<FunctionalCaseXmindData> children = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,334 @@
|
||||||
|
package io.metersphere.functional.xmind.utils;
|
||||||
|
|
||||||
|
import io.metersphere.functional.constants.FunctionalCaseTypeConstants;
|
||||||
|
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindDTO;
|
||||||
|
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindData;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.xmind.core.*;
|
||||||
|
import org.xmind.core.style.IStyle;
|
||||||
|
import org.xmind.core.style.IStyleSheet;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public class XmindExportUtil {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载xmind模板
|
||||||
|
*
|
||||||
|
* @param response
|
||||||
|
* @param caseData
|
||||||
|
* @param template
|
||||||
|
*/
|
||||||
|
public static void downloadTemplate(HttpServletResponse response, FunctionalCaseXmindData caseData, boolean template) {
|
||||||
|
IWorkbook workBook = createXmindByCaseData(caseData, template);
|
||||||
|
|
||||||
|
response.setContentType("application/octet-stream");
|
||||||
|
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||||
|
try {
|
||||||
|
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(Translator.get("functional_case_xmind_template"), StandardCharsets.UTF_8.name()) + ".xmind");
|
||||||
|
workBook.save(response.getOutputStream());
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
LogUtils.error(e.getMessage(), e);
|
||||||
|
throw new MSException("Utf-8 encoding is not supported");
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e.getMessage(), e);
|
||||||
|
throw new MSException("IO exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IWorkbook createXmindByCaseData(FunctionalCaseXmindData caseData, boolean template) {
|
||||||
|
// 创建思维导图的工作空间
|
||||||
|
IWorkbookBuilder workbookBuilder = Core.getWorkbookBuilder();
|
||||||
|
IWorkbook workbook = workbookBuilder.createWorkbook();
|
||||||
|
|
||||||
|
Map<String, IStyle> styleMap = initTheme(workbook);
|
||||||
|
|
||||||
|
// 获得默认sheet
|
||||||
|
ISheet primarySheet = workbook.getPrimarySheet();
|
||||||
|
if (styleMap.containsKey("mapStyle")) {
|
||||||
|
primarySheet.setStyleId(styleMap.get("mapStyle").getId());
|
||||||
|
}
|
||||||
|
// 获得根主题
|
||||||
|
ITopic rootTopic = primarySheet.getRootTopic();
|
||||||
|
if (styleMap.containsKey("centralTopicStyle")) {
|
||||||
|
rootTopic.setStyleId(styleMap.get("centralTopicStyle").getId());
|
||||||
|
}
|
||||||
|
// 设置根主题的标题
|
||||||
|
rootTopic.setTitleText(Translator.get("functional_case"));
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(caseData.getChildren())) {
|
||||||
|
for (FunctionalCaseXmindData data : caseData.getChildren()) {
|
||||||
|
addItemTopic(rootTopic, workbook, styleMap, data, true, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return workbook;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addItemTopic(ITopic parentTpoic, IWorkbook workbook, Map<String, IStyle> styleMap, FunctionalCaseXmindData xmindData, boolean isFirstLevel, boolean template) {
|
||||||
|
ITopic topic = workbook.createTopic();
|
||||||
|
topic.setTitleText(xmindData.getModuleName());
|
||||||
|
if (isFirstLevel) {
|
||||||
|
if (styleMap.containsKey("mainTopicStyle")) {
|
||||||
|
topic.setStyleId(styleMap.get("mainTopicStyle").getId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (styleMap.containsKey("subTopicStyle")) {
|
||||||
|
topic.setStyleId(styleMap.get("subTopicStyle").getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parentTpoic.add(topic);
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(xmindData.getFunctionalCaseList())) {
|
||||||
|
IStyle style = null;
|
||||||
|
if (styleMap.containsKey("subTopicStyle")) {
|
||||||
|
style = styleMap.get("subTopicStyle");
|
||||||
|
}
|
||||||
|
for (FunctionalCaseXmindDTO dto : xmindData.getFunctionalCaseList()) {
|
||||||
|
// 创建小节节点
|
||||||
|
ITopic itemTopic = workbook.createTopic();
|
||||||
|
if (style != null) {
|
||||||
|
itemTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
if (template) {
|
||||||
|
// 模板
|
||||||
|
buildTemplateTopic(topic, style, dto, itemTopic, workbook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(xmindData.getChildren())) {
|
||||||
|
for (FunctionalCaseXmindData data : xmindData.getChildren()) {
|
||||||
|
addItemTopic(topic, workbook, styleMap, data, false, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void buildTemplateTopic(ITopic topic, IStyle style, FunctionalCaseXmindDTO dto, ITopic itemTopic, IWorkbook workbook) {
|
||||||
|
|
||||||
|
//用例名称
|
||||||
|
itemTopic.setTitleText("case-P0: " + dto.getName());
|
||||||
|
|
||||||
|
//前置条件
|
||||||
|
if (StringUtils.isNotBlank(dto.getPrerequisite())) {
|
||||||
|
ITopic preTopic = workbook.createTopic();
|
||||||
|
preTopic.setTitleText(Translator.get("xmind_prerequisite") + ": " + dto.getPrerequisite());
|
||||||
|
if (style != null) {
|
||||||
|
preTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
itemTopic.add(preTopic, ITopic.ATTACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
//备注
|
||||||
|
if (StringUtils.isNotBlank(dto.getDescription())) {
|
||||||
|
ITopic deTopic = workbook.createTopic();
|
||||||
|
deTopic.setTitleText(Translator.get("xmind_description") + ": " + dto.getDescription());
|
||||||
|
if (style != null) {
|
||||||
|
deTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
itemTopic.add(deTopic, ITopic.ATTACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
//标签
|
||||||
|
if (StringUtils.isNotBlank(dto.getTags())) {
|
||||||
|
try {
|
||||||
|
List<String> arr = JSON.parseArray(dto.getTags());
|
||||||
|
String tagStr = StringUtils.EMPTY;
|
||||||
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
|
tagStr = tagStr + arr.get(i) + "|";
|
||||||
|
}
|
||||||
|
if (tagStr.endsWith("|")) {
|
||||||
|
tagStr = tagStr.substring(0, tagStr.length() - 1);
|
||||||
|
}
|
||||||
|
ITopic tagTopic = workbook.createTopic();
|
||||||
|
tagTopic.setTitleText(Translator.get("xmind_tags") + ":" + tagStr);
|
||||||
|
if (style != null) {
|
||||||
|
tagTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
itemTopic.add(tagTopic, ITopic.ATTACHED);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.equalsIgnoreCase(dto.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name())) {
|
||||||
|
//文本描述
|
||||||
|
ITopic textDesTopic = workbook.createTopic();
|
||||||
|
String desc = dto.getTextDescription();
|
||||||
|
textDesTopic.setTitleText(desc == null ? Translator.get("xmind_textDescription") + ": " : Translator.get("xmind_textDescription") + ": " + desc);
|
||||||
|
if (style != null) {
|
||||||
|
textDesTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = dto.getExpectedResult();
|
||||||
|
ITopic resultTopic = workbook.createTopic();
|
||||||
|
resultTopic.setTitleText(result == null ? Translator.get("xmind_expectedResult") + ": " : Translator.get("xmind_expectedResult") + ": " + result);
|
||||||
|
if (style != null) {
|
||||||
|
resultTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
textDesTopic.add(resultTopic, ITopic.ATTACHED);
|
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(desc) || StringUtils.isNotEmpty(result)) {
|
||||||
|
itemTopic.add(textDesTopic, ITopic.ATTACHED);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//步骤描述
|
||||||
|
try {
|
||||||
|
ITopic stepDesTopic = workbook.createTopic();
|
||||||
|
stepDesTopic.setTitleText(Translator.get("xmind_stepDescription"));
|
||||||
|
if (style != null) {
|
||||||
|
stepDesTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
List<Map> arr = JSON.parseArray(dto.getSteps());
|
||||||
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
|
Map<String, String> obj = arr.get(i);
|
||||||
|
if (obj.containsKey("desc")) {
|
||||||
|
ITopic stepTopic = workbook.createTopic();
|
||||||
|
String desc = obj.get("desc");
|
||||||
|
stepTopic.setTitleText(Translator.get("xmind_step") + ": " + desc);
|
||||||
|
if (style != null) {
|
||||||
|
stepTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasResult = false;
|
||||||
|
if (obj.containsKey("result")) {
|
||||||
|
String result = obj.get("result");
|
||||||
|
if (StringUtils.isNotEmpty(result)) {
|
||||||
|
hasResult = true;
|
||||||
|
ITopic resultTopic = workbook.createTopic();
|
||||||
|
resultTopic.setTitleText(Translator.get("xmind_expectedResult") + ": " + result);
|
||||||
|
if (style != null) {
|
||||||
|
resultTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
stepTopic.add(resultTopic, ITopic.ATTACHED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(desc) || hasResult) {
|
||||||
|
stepDesTopic.add(stepTopic, ITopic.ATTACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemTopic.add(stepDesTopic, ITopic.ATTACHED);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//自定义字段
|
||||||
|
dto.getTemplateCustomFieldDTOList().forEach(item -> {
|
||||||
|
if (!StringUtils.equalsIgnoreCase(item.getFieldName(), "functional_priority")) {
|
||||||
|
ITopic customTopic = workbook.createTopic();
|
||||||
|
customTopic.setTitleText(item.getFieldName() + ":");
|
||||||
|
if (style != null) {
|
||||||
|
customTopic.setStyleId(style.getId());
|
||||||
|
}
|
||||||
|
itemTopic.add(customTopic, ITopic.ATTACHED);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
topic.add(itemTopic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Map<String, IStyle> initTheme(IWorkbook workbook) {
|
||||||
|
Map<String, IStyle> styleMap = new HashMap<>();
|
||||||
|
|
||||||
|
IStyleSheet styleSheet = workbook.getStyleSheet();
|
||||||
|
IStyle mapStyle = styleSheet.createStyle(IStyle.MAP);
|
||||||
|
mapStyle.setProperty("line-tapered", "none");
|
||||||
|
mapStyle.setProperty("multi-line-colors", "none");
|
||||||
|
mapStyle.setProperty("svg:fill", "#FFFFFF");
|
||||||
|
mapStyle.setProperty("color-gradient", "none");
|
||||||
|
styleSheet.addStyle(mapStyle, IStyleSheet.NORMAL_STYLES);
|
||||||
|
styleMap.put("mapStyle", mapStyle);
|
||||||
|
|
||||||
|
IStyle centralTopicStyle = styleSheet.createStyle(IStyle.TOPIC);
|
||||||
|
centralTopicStyle.setProperty("line-width", "1pt");
|
||||||
|
centralTopicStyle.setProperty("svg:fill", "#DCE6F2");
|
||||||
|
centralTopicStyle.setProperty("fo:font-family", "Microsoft YaHei");
|
||||||
|
centralTopicStyle.setProperty("border-line-width", "5pt");
|
||||||
|
centralTopicStyle.setProperty("shape-class", "org.xmind.topicShape.roundedRect");
|
||||||
|
centralTopicStyle.setProperty("fo:color", "#376092");
|
||||||
|
centralTopicStyle.setProperty("line-class", "org.xmind.branchConnection.curve");
|
||||||
|
centralTopicStyle.setProperty("border-line-color", "#558ED5");
|
||||||
|
centralTopicStyle.setProperty("line-color", "#558ED5");
|
||||||
|
styleSheet.addStyle(centralTopicStyle, IStyleSheet.NORMAL_STYLES);
|
||||||
|
styleMap.put("centralTopicStyle", centralTopicStyle);
|
||||||
|
|
||||||
|
IStyle mainTopicStyle = styleSheet.createStyle(IStyle.TOPIC);
|
||||||
|
mainTopicStyle.setProperty("line-width", "1pt");
|
||||||
|
mainTopicStyle.setProperty("svg:fill", "#DCE6F2");
|
||||||
|
mainTopicStyle.setProperty("fo:font-family", "Microsoft YaHei");
|
||||||
|
mainTopicStyle.setProperty("border-line-width", "2pt");
|
||||||
|
mainTopicStyle.setProperty("shape-class", "org.xmind.topicShape.roundedRect");
|
||||||
|
mainTopicStyle.setProperty("fo:color", "#17375E");
|
||||||
|
mainTopicStyle.setProperty("line-class", "org.xmind.branchConnection.curve");
|
||||||
|
mainTopicStyle.setProperty("border-line-color", "#558ED5");
|
||||||
|
mainTopicStyle.setProperty("line-color", "#558ED5");
|
||||||
|
styleSheet.addStyle(mainTopicStyle, IStyleSheet.NORMAL_STYLES);
|
||||||
|
styleMap.put("mainTopicStyle", mainTopicStyle);
|
||||||
|
|
||||||
|
IStyle subTopicStyle = styleSheet.createStyle(IStyle.TOPIC);
|
||||||
|
subTopicStyle.setProperty("line-width", "1pt");
|
||||||
|
subTopicStyle.setProperty("fo:font-family", "Microsoft YaHei");
|
||||||
|
subTopicStyle.setProperty("border-line-width", "3pt");
|
||||||
|
subTopicStyle.setProperty("line-class", "org.xmind.branchConnection.curve");
|
||||||
|
subTopicStyle.setProperty("border-line-color", "#558ED5");
|
||||||
|
subTopicStyle.setProperty("line-color", "#558ED5");
|
||||||
|
styleSheet.addStyle(subTopicStyle, IStyleSheet.NORMAL_STYLES);
|
||||||
|
styleMap.put("subTopicStyle", subTopicStyle);
|
||||||
|
|
||||||
|
IStyle floatingTopicStyle = styleSheet.createStyle(IStyle.TOPIC);
|
||||||
|
floatingTopicStyle.setProperty("svg:fill", "#558ED5");
|
||||||
|
floatingTopicStyle.setProperty("fo:font-family", "Microsoft YaHei");
|
||||||
|
floatingTopicStyle.setProperty("border-line-width", "0pt");
|
||||||
|
floatingTopicStyle.setProperty("fo:color", "#FFFFFF");
|
||||||
|
floatingTopicStyle.setProperty("fo:font-weight", "bold");
|
||||||
|
floatingTopicStyle.setProperty("line-color", "#558ED5");
|
||||||
|
styleSheet.addStyle(floatingTopicStyle, IStyleSheet.NORMAL_STYLES);
|
||||||
|
styleMap.put("floatingTopicStyle", floatingTopicStyle);
|
||||||
|
|
||||||
|
IStyle summaryTopic = styleSheet.createStyle(IStyle.TOPIC);
|
||||||
|
summaryTopic.setProperty("fo:font-style", "italic");
|
||||||
|
summaryTopic.setProperty("svg:fill", "#77933C");
|
||||||
|
summaryTopic.setProperty("fo:font-family", "Microsoft YaHei");
|
||||||
|
summaryTopic.setProperty("border-line-width", "0pt");
|
||||||
|
summaryTopic.setProperty("fo:font-size", "10pt");
|
||||||
|
summaryTopic.setProperty("shape-class", "org.xmind.topicShape.roundedRect");
|
||||||
|
summaryTopic.setProperty("fo:color", "#FFFFFF");
|
||||||
|
summaryTopic.setProperty("line-class", "org.xmind.branchConnection.curve");
|
||||||
|
styleSheet.addStyle(summaryTopic, IStyleSheet.NORMAL_STYLES);
|
||||||
|
styleMap.put("summaryTopic", floatingTopicStyle);
|
||||||
|
|
||||||
|
IStyle itemTopic = styleSheet.createStyle(IStyle.TOPIC);
|
||||||
|
itemTopic.setProperty("fo:text-align", "center");
|
||||||
|
itemTopic.setProperty("line-width", "1pt");
|
||||||
|
itemTopic.setProperty("svg:fill", "none");
|
||||||
|
itemTopic.setProperty("fo:font-family", "Microsoft YaHei");
|
||||||
|
itemTopic.setProperty("border-line-width", "2pt");
|
||||||
|
itemTopic.setProperty("shape-class", "org.xmind.topicShape.underline");
|
||||||
|
itemTopic.setProperty("fo:font-size", "14pt");
|
||||||
|
itemTopic.setProperty("fo:color", "#17375E");
|
||||||
|
itemTopic.setProperty("line-class", "org.xmind.branchConnection.curve");
|
||||||
|
itemTopic.setProperty("border-line-color", "#558ED5");
|
||||||
|
itemTopic.setProperty("line-color", "#558ED5");
|
||||||
|
styleSheet.addStyle(itemTopic, IStyleSheet.NORMAL_STYLES);
|
||||||
|
styleMap.put("itemTopic", itemTopic);
|
||||||
|
|
||||||
|
return styleMap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"functionalCaseList": [],
|
||||||
|
"moduleName": "rootNode",
|
||||||
|
"moduleId": "rootNode",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"functionalCaseList": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"num": 1,
|
||||||
|
"projectId": "12314",
|
||||||
|
"name": "导出模板用例A",
|
||||||
|
"tags": "[\"标签789\"]",
|
||||||
|
"caseEditType": "STEP",
|
||||||
|
"steps": "[{\"num\":1,\"desc\":\"123\",\"result\":\"123\"},{\"num\":2,\"desc\":\"SDFS\",\"result\":\"SDFS\"}]",
|
||||||
|
"textDescription": "",
|
||||||
|
"expectedResult": "",
|
||||||
|
"prerequisite": "这是前置",
|
||||||
|
"description": "这是备注",
|
||||||
|
"customFieldDTOList": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"num": 2,
|
||||||
|
"projectId": "12314",
|
||||||
|
"name": "导出模板用例B",
|
||||||
|
"tags": "[\"标签789\"]",
|
||||||
|
"caseEditType": "TEXT",
|
||||||
|
"steps": "",
|
||||||
|
"textDescription": "<p style=\"\">akhfksajhfaskfha</p>",
|
||||||
|
"expectedResult": "<p style=\"\">asdfsdfasfs</p>",
|
||||||
|
"prerequisite": "前置",
|
||||||
|
"description": "这是备注",
|
||||||
|
"customFieldDTOList": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"moduleName": "模块1",
|
||||||
|
"moduleId": "module1",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"functionalCaseList": [
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"num": 3,
|
||||||
|
"projectId": "12314",
|
||||||
|
"name": "导出模板用例C",
|
||||||
|
"tags": "[\"标签789\"]",
|
||||||
|
"caseEditType": "STEP",
|
||||||
|
"steps": "[{\"num\":1,\"desc\":\"123\",\"result\":\"123\"},{\"num\":2,\"desc\":\"SDFS\",\"result\":\"SDFS\"}]",
|
||||||
|
"textDescription": "",
|
||||||
|
"expectedResult": "",
|
||||||
|
"prerequisite": "前置",
|
||||||
|
"description": "这是备注",
|
||||||
|
"customFieldDTOList": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"moduleName": "模块2",
|
||||||
|
"moduleId": "module2",
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -87,6 +87,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
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";
|
public static final String EXPORT_EXCEL_URL = "/functional/case/export/excel";
|
||||||
|
public static final String DOWNLOAD_XMIND_TEMPLATE_URL = "/functional/case/download/xmind/template/";
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private NotificationMapper notificationMapper;
|
private NotificationMapper notificationMapper;
|
||||||
|
@ -834,4 +835,11 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
request.setFileId("123142342");
|
request.setFileId("123142342");
|
||||||
this.requestPost(EXPORT_EXCEL_URL, request);
|
this.requestPost(EXPORT_EXCEL_URL, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(22)
|
||||||
|
public void testDownloadXmindTemplate() throws Exception {
|
||||||
|
this.requestGetExcel(DOWNLOAD_XMIND_TEMPLATE_URL + DEFAULT_PROJECT_ID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue