mirror of
https://gitee.com/fit2cloud-feizhiyun/MeterSphere.git
synced 2024-12-02 12:09:13 +08:00
feat(测试用例): 用例脑图导出
This commit is contained in:
parent
781d59d649
commit
495f4c9463
@ -1,12 +1,15 @@
|
||||
package io.metersphere.functional.domain;
|
||||
|
||||
import io.metersphere.validation.groups.*;
|
||||
import io.metersphere.validation.groups.Created;
|
||||
import io.metersphere.validation.groups.Updated;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ExportTask implements Serializable {
|
||||
@ -43,6 +46,11 @@ public class ExportTask implements Serializable {
|
||||
@Schema(description = "创建时间")
|
||||
private Long updateTime;
|
||||
|
||||
@Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{export_task.project_id.not_blank}", groups = {Created.class})
|
||||
@Size(min = 1, max = 50, message = "{export_task.project_id.length_range}", groups = {Created.class, Updated.class})
|
||||
private String projectId;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public enum Column {
|
||||
@ -54,7 +62,8 @@ public class ExportTask implements Serializable {
|
||||
createUser("create_user", "createUser", "VARCHAR", false),
|
||||
createTime("create_time", "createTime", "BIGINT", false),
|
||||
updateUser("update_user", "updateUser", "VARCHAR", false),
|
||||
updateTime("update_time", "updateTime", "BIGINT", false);
|
||||
updateTime("update_time", "updateTime", "BIGINT", false),
|
||||
projectId("project_id", "projectId", "VARCHAR", false);
|
||||
|
||||
private static final String BEGINNING_DELIMITER = "`";
|
||||
|
||||
@ -99,7 +108,7 @@ public class ExportTask implements Serializable {
|
||||
return this.getEscapedColumnName() + " ASC";
|
||||
}
|
||||
|
||||
public static Column[] excludes(Column ... excludes) {
|
||||
public static Column[] excludes(Column... excludes) {
|
||||
ArrayList<Column> columns = new ArrayList<>(Arrays.asList(Column.values()));
|
||||
if (excludes != null && excludes.length > 0) {
|
||||
columns.removeAll(new ArrayList<>(Arrays.asList(excludes)));
|
||||
|
@ -713,6 +713,76 @@ public class ExportTaskExample {
|
||||
addCriterion("update_time not between", value1, value2, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdIsNull() {
|
||||
addCriterion("project_id is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdIsNotNull() {
|
||||
addCriterion("project_id is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdEqualTo(String value) {
|
||||
addCriterion("project_id =", value, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdNotEqualTo(String value) {
|
||||
addCriterion("project_id <>", value, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdGreaterThan(String value) {
|
||||
addCriterion("project_id >", value, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("project_id >=", value, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdLessThan(String value) {
|
||||
addCriterion("project_id <", value, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdLessThanOrEqualTo(String value) {
|
||||
addCriterion("project_id <=", value, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdLike(String value) {
|
||||
addCriterion("project_id like", value, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdNotLike(String value) {
|
||||
addCriterion("project_id not like", value, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdIn(List<String> values) {
|
||||
addCriterion("project_id in", values, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdNotIn(List<String> values) {
|
||||
addCriterion("project_id not in", values, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdBetween(String value1, String value2) {
|
||||
addCriterion("project_id between", value1, value2, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andProjectIdNotBetween(String value1, String value2) {
|
||||
addCriterion("project_id not between", value1, value2, "projectId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
@ -11,6 +11,7 @@
|
||||
<result column="create_time" jdbcType="BIGINT" property="createTime" />
|
||||
<result column="update_user" jdbcType="VARCHAR" property="updateUser" />
|
||||
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
|
||||
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
@ -71,7 +72,8 @@
|
||||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, `name`, `type`, fileId, `state`, create_user, create_time, update_user, update_time
|
||||
id, `name`, `type`, fileId, `state`, create_user, create_time, update_user, update_time,
|
||||
project_id
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.functional.domain.ExportTaskExample" resultMap="BaseResultMap">
|
||||
select
|
||||
@ -106,12 +108,12 @@
|
||||
<insert id="insert" parameterType="io.metersphere.functional.domain.ExportTask">
|
||||
insert into export_task (id, `name`, `type`,
|
||||
fileId, `state`, create_user,
|
||||
create_time, update_user, update_time
|
||||
)
|
||||
create_time, update_user, update_time,
|
||||
project_id)
|
||||
values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR},
|
||||
#{fileid,jdbcType=VARCHAR}, #{state,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
|
||||
#{createTime,jdbcType=BIGINT}, #{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT}
|
||||
)
|
||||
#{createTime,jdbcType=BIGINT}, #{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT},
|
||||
#{projectId,jdbcType=VARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.functional.domain.ExportTask">
|
||||
insert into export_task
|
||||
@ -143,6 +145,9 @@
|
||||
<if test="updateTime != null">
|
||||
update_time,
|
||||
</if>
|
||||
<if test="projectId != null">
|
||||
project_id,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
@ -172,6 +177,9 @@
|
||||
<if test="updateTime != null">
|
||||
#{updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="projectId != null">
|
||||
#{projectId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.functional.domain.ExportTaskExample" resultType="java.lang.Long">
|
||||
@ -210,6 +218,9 @@
|
||||
<if test="record.updateTime != null">
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="record.projectId != null">
|
||||
project_id = #{record.projectId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
@ -225,7 +236,8 @@
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR},
|
||||
create_time = #{record.createTime,jdbcType=BIGINT},
|
||||
update_user = #{record.updateUser,jdbcType=VARCHAR},
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT}
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT},
|
||||
project_id = #{record.projectId,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
@ -257,6 +269,9 @@
|
||||
<if test="updateTime != null">
|
||||
update_time = #{updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="projectId != null">
|
||||
project_id = #{projectId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
@ -269,19 +284,20 @@
|
||||
create_user = #{createUser,jdbcType=VARCHAR},
|
||||
create_time = #{createTime,jdbcType=BIGINT},
|
||||
update_user = #{updateUser,jdbcType=VARCHAR},
|
||||
update_time = #{updateTime,jdbcType=BIGINT}
|
||||
update_time = #{updateTime,jdbcType=BIGINT},
|
||||
project_id = #{projectId,jdbcType=VARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<insert id="batchInsert" parameterType="map">
|
||||
insert into export_task
|
||||
(id, `name`, `type`, fileId, `state`, create_user, create_time, update_user, update_time
|
||||
)
|
||||
(id, `name`, `type`, fileId, `state`, create_user, create_time, update_user, update_time,
|
||||
project_id)
|
||||
values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.id,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, #{item.type,jdbcType=VARCHAR},
|
||||
#{item.fileid,jdbcType=VARCHAR}, #{item.state,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR},
|
||||
#{item.createTime,jdbcType=BIGINT}, #{item.updateUser,jdbcType=VARCHAR}, #{item.updateTime,jdbcType=BIGINT}
|
||||
)
|
||||
#{item.createTime,jdbcType=BIGINT}, #{item.updateUser,jdbcType=VARCHAR}, #{item.updateTime,jdbcType=BIGINT},
|
||||
#{item.projectId,jdbcType=VARCHAR})
|
||||
</foreach>
|
||||
</insert>
|
||||
<insert id="batchInsertSelective" parameterType="map">
|
||||
@ -321,6 +337,9 @@
|
||||
<if test="'update_time'.toString() == column.value">
|
||||
#{item.updateTime,jdbcType=BIGINT}
|
||||
</if>
|
||||
<if test="'project_id'.toString() == column.value">
|
||||
#{item.projectId,jdbcType=VARCHAR}
|
||||
</if>
|
||||
</foreach>
|
||||
)
|
||||
</foreach>
|
||||
|
@ -12,6 +12,7 @@ CREATE TABLE export_task(
|
||||
`name` VARCHAR(255) COMMENT '名称' ,
|
||||
`type` VARCHAR(50) NOT NULL COMMENT '资源类型' ,
|
||||
`fileId` VARCHAR(255) COMMENT '文件id' ,
|
||||
`project_id` VARCHAR(255) NOT NULL COMMENT '项目id' ,
|
||||
`state` VARCHAR(50) NOT NULL COMMENT '状态' ,
|
||||
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
|
||||
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
|
||||
@ -24,6 +25,7 @@ CREATE TABLE export_task(
|
||||
|
||||
|
||||
CREATE INDEX idx_create_user ON export_task(`create_user`);
|
||||
CREATE INDEX idx_project_id ON export_task(`project_id`);
|
||||
CREATE INDEX idx_state ON export_task(`state`);
|
||||
CREATE INDEX idx_create_time ON export_task(`create_time`);
|
||||
CREATE INDEX idx_type ON export_task(`type`);
|
||||
|
@ -258,12 +258,12 @@ public class FunctionalCaseController {
|
||||
functionalCaseFileService.export(SessionUtils.getUserId(), request);
|
||||
}
|
||||
|
||||
@GetMapping("/stop/{projectId}")
|
||||
@GetMapping("/stop/{taskId}")
|
||||
@Operation(summary = "用例管理-功能用例-导出-停止导出")
|
||||
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_EXPORT)
|
||||
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||
public void caseStopExport(@PathVariable String projectId) {
|
||||
functionalCaseFileService.stopExport(projectId, SessionUtils.getUserId());
|
||||
public void caseStopExport(@PathVariable String taskId) {
|
||||
functionalCaseFileService.stopExport(taskId, SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
@GetMapping("/download/xmind/template/{projectId}")
|
||||
@ -289,4 +289,12 @@ public class FunctionalCaseController {
|
||||
public ResponseEntity<byte[]> downloadImgById(@PathVariable String projectId, @PathVariable String fileId) {
|
||||
return functionalCaseFileService.downloadFile(projectId, fileId);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/export/xmind")
|
||||
@Operation(summary = "用例管理-功能用例-xmind导出")
|
||||
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_EXPORT)
|
||||
public void caseExportXmind(@Validated @RequestBody FunctionalCaseExportRequest request) {
|
||||
functionalCaseXmindService.exportFunctionalCaseXmind(request, SessionUtils.getUserId());
|
||||
}
|
||||
}
|
||||
|
@ -64,10 +64,8 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serial;
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -104,11 +102,12 @@ public class FunctionalCaseFileService {
|
||||
private FunctionalCaseLogService functionalCaseLogService;
|
||||
@Resource
|
||||
private SystemParameterMapper systemParameterMapper;
|
||||
private static final String EXPORT_FILE_NAME = "case_export";
|
||||
@Resource
|
||||
private ExportTaskManager exportTaskManager;
|
||||
@Resource
|
||||
private ExportTaskMapper exportTaskMapper;
|
||||
private static final String XMIND = ".xmind";
|
||||
private static final String XLSX = ".xlsx";
|
||||
|
||||
/**
|
||||
* 下载excel导入模板
|
||||
@ -333,17 +332,17 @@ public class FunctionalCaseFileService {
|
||||
}
|
||||
}
|
||||
|
||||
public void export(String userId, FunctionalCaseExportRequest request){
|
||||
public void export(String userId, FunctionalCaseExportRequest request) {
|
||||
try {
|
||||
ExportTaskExample exportTaskExample = new ExportTaskExample();
|
||||
exportTaskExample.createCriteria().andTypeEqualTo(ExportConstants.ExportType.CASE.toString()).andStateEqualTo(ExportConstants.ExportState.PREPARED.toString());
|
||||
long preparedCount = exportTaskMapper.countByExample(exportTaskExample);
|
||||
if (preparedCount>0) {
|
||||
if (preparedCount > 0) {
|
||||
throw new MSException(Translator.get("export_case_task_existed"));
|
||||
}
|
||||
exportTaskManager.exportAsyncTask(userId, ExportConstants.ExportType.CASE.toString(), request, t->exportFunctionalCaseZip(request));
|
||||
exportTaskManager.exportAsyncTask(request.getProjectId(), userId, ExportConstants.ExportType.CASE.toString(), request, t -> exportFunctionalCaseZip(request, userId));
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.error("导出失败:"+e);
|
||||
LogUtils.error("导出失败:" + e);
|
||||
throw new MSException(e);
|
||||
}
|
||||
}
|
||||
@ -354,7 +353,7 @@ public class FunctionalCaseFileService {
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public String exportFunctionalCaseZip(FunctionalCaseExportRequest request) {
|
||||
public String exportFunctionalCaseZip(FunctionalCaseExportRequest request, String userId) {
|
||||
File tmpDir = null;
|
||||
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
|
||||
try {
|
||||
@ -375,11 +374,11 @@ public class FunctionalCaseFileService {
|
||||
uploadFileToMinio(singeFile, request.getFileId());
|
||||
}
|
||||
functionalCaseLogService.exportExcelLog(request);
|
||||
List<ExportTask> exportTasks = getExportTasks();
|
||||
List<ExportTask> exportTasks = getExportTasks(request.getProjectId(), userId);
|
||||
String taskId;
|
||||
if (CollectionUtils.isNotEmpty(exportTasks)) {
|
||||
taskId = exportTasks.getFirst().getId();
|
||||
updateExportTask(ExportConstants.ExportState.SUCCESS.toString(), taskId);
|
||||
updateExportTask(ExportConstants.ExportState.SUCCESS.name(), taskId, request.getFileId());
|
||||
} else {
|
||||
taskId = MsgType.CONNECT.name();
|
||||
}
|
||||
@ -387,9 +386,9 @@ public class FunctionalCaseFileService {
|
||||
socketMsgDTO.setReportId(request.getFileId());
|
||||
ExportWebSocketHandler.sendMessageSingle(socketMsgDTO);
|
||||
} catch (Exception e) {
|
||||
List<ExportTask> exportTasks = getExportTasks();
|
||||
List<ExportTask> exportTasks = getExportTasks(request.getProjectId(), userId);
|
||||
if (CollectionUtils.isNotEmpty(exportTasks)) {
|
||||
updateExportTask(ExportConstants.ExportState.SUCCESS.toString(), exportTasks.getFirst().getId());
|
||||
updateExportTask(ExportConstants.ExportState.ERROR.name(), exportTasks.getFirst().getId(), request.getFileId());
|
||||
}
|
||||
LogUtils.error(e);
|
||||
throw new MSException(e);
|
||||
@ -397,23 +396,26 @@ public class FunctionalCaseFileService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<ExportTask> getExportTasks() {
|
||||
public List<ExportTask> getExportTasks(String projectId, String userId) {
|
||||
ExportTaskExample exportTaskExample = new ExportTaskExample();
|
||||
exportTaskExample.createCriteria().andTypeEqualTo(ExportConstants.ExportType.CASE.toString()).andStateEqualTo(ExportConstants.ExportState.PREPARED.toString());
|
||||
exportTaskExample.createCriteria().andTypeEqualTo(ExportConstants.ExportType.CASE.toString()).andStateEqualTo(ExportConstants.ExportState.PREPARED.toString())
|
||||
.andCreateUserEqualTo(userId).andProjectIdEqualTo(projectId);
|
||||
exportTaskExample.setOrderByClause("create_time desc");
|
||||
return exportTaskMapper.selectByExample(exportTaskExample);
|
||||
}
|
||||
|
||||
private void updateExportTask(String state, String taskId) {
|
||||
public void updateExportTask(String state, String taskId, String fileId) {
|
||||
ExportTask exportTask = new ExportTask();
|
||||
exportTask.setState(state);
|
||||
exportTask.setFileid(fileId);
|
||||
exportTask.setId(taskId);
|
||||
exportTaskMapper.updateByPrimaryKey(exportTask);
|
||||
exportTaskMapper.updateByPrimaryKeySelective(exportTask);
|
||||
}
|
||||
|
||||
private void uploadFileToMinio(File file, String fileId) {
|
||||
public void uploadFileToMinio(File file, String fileId) {
|
||||
FileRequest fileRequest = new FileRequest();
|
||||
fileRequest.setFileName(EXPORT_FILE_NAME);
|
||||
fileRequest.setFolder(DefaultRepositoryDir.getExportExcelTempDir() + "/" + fileId);
|
||||
fileRequest.setFileName(fileId);
|
||||
fileRequest.setFolder(DefaultRepositoryDir.getExportExcelTempDir());
|
||||
fileRequest.setStorage(StorageType.MINIO.name());
|
||||
try {
|
||||
FileInputStream inputStream = new FileInputStream(file);
|
||||
@ -787,22 +789,33 @@ public class FunctionalCaseFileService {
|
||||
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||
byte[] bytes;
|
||||
FileRequest fileRequest = new FileRequest();
|
||||
fileRequest.setFileName(EXPORT_FILE_NAME);
|
||||
fileRequest.setFolder(DefaultRepositoryDir.getExportExcelTempDir() + "/" + fileId);
|
||||
fileRequest.setFileName(fileId);
|
||||
fileRequest.setFolder(DefaultRepositoryDir.getExportExcelTempDir());
|
||||
fileRequest.setStorage(StorageType.MINIO.name());
|
||||
try {
|
||||
bytes = fileService.download(fileRequest);
|
||||
} catch (Exception e) {
|
||||
throw new MSException("get file error");
|
||||
}
|
||||
String fileName = "";
|
||||
if (StringUtils.endsWith(fileId, XMIND)) {
|
||||
fileName = "Metersphere_case_" + project.getName() + XMIND;
|
||||
}
|
||||
if (StringUtils.endsWith(fileId, XLSX)) {
|
||||
fileName = "Metersphere_case_" + project.getName() + XLSX;
|
||||
}
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + "Metersphere_case_" + project.getName() + "\"")
|
||||
.body(bytes);
|
||||
try {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()))
|
||||
.body(bytes);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new MSException("Utf-8 encoding is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public void stopExport(String projectId, String userId) {
|
||||
exportTaskManager.sendStopMessage(projectId, userId);
|
||||
public void stopExport(String taskId, String userId) {
|
||||
exportTaskManager.sendStopMessage(taskId, userId);
|
||||
}
|
||||
}
|
||||
|
@ -1372,7 +1372,7 @@ public class FunctionalCaseService {
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
private List<FunctionalCase> getCaseDataByIds(List<String> ids) {
|
||||
public List<FunctionalCase> getCaseDataByIds(List<String> ids) {
|
||||
FunctionalCaseExample example = new FunctionalCaseExample();
|
||||
example.createCriteria().andIdIn(ids);
|
||||
return functionalCaseMapper.selectByExample(example);
|
||||
|
@ -1,21 +1,41 @@
|
||||
package io.metersphere.functional.service;
|
||||
|
||||
import io.metersphere.functional.domain.ExportTask;
|
||||
import io.metersphere.functional.domain.FunctionalCase;
|
||||
import io.metersphere.functional.domain.FunctionalCaseBlob;
|
||||
import io.metersphere.functional.domain.FunctionalCaseCustomField;
|
||||
import io.metersphere.functional.request.FunctionalCaseExportRequest;
|
||||
import io.metersphere.functional.socket.ExportWebSocketHandler;
|
||||
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindDTO;
|
||||
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindData;
|
||||
import io.metersphere.functional.xmind.utils.XmindExportUtil;
|
||||
import io.metersphere.sdk.constants.ModuleConstants;
|
||||
import io.metersphere.sdk.constants.MsgType;
|
||||
import io.metersphere.sdk.dto.SocketMsgDTO;
|
||||
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.constants.ExportConstants;
|
||||
import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||
import io.metersphere.system.manager.ExportTaskManager;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
@ -28,7 +48,17 @@ public class FunctionalCaseXmindService {
|
||||
|
||||
@Resource
|
||||
private FunctionalCaseFileService functionalCaseFileService;
|
||||
|
||||
@Resource
|
||||
private FunctionalCaseService functionalCaseService;
|
||||
@Resource
|
||||
private FunctionalCaseCustomFieldService functionalCaseCustomFieldService;
|
||||
@Resource
|
||||
private FunctionalCaseModuleService functionalCaseModuleService;
|
||||
private static final String EXPORT_CASE_TMP_DIR = "tmp";
|
||||
@Resource
|
||||
private ExportTaskManager exportTaskManager;
|
||||
@Resource
|
||||
private FunctionalCaseLogService functionalCaseLogService;
|
||||
|
||||
public void downloadXmindTemplate(String projectId, HttpServletResponse response) {
|
||||
List<TemplateCustomFieldDTO> customFields = functionalCaseFileService.getCustomFields(projectId);
|
||||
@ -59,5 +89,141 @@ public class FunctionalCaseXmindService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出xmind
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public void exportFunctionalCaseXmind(FunctionalCaseExportRequest request, String userId) {
|
||||
try {
|
||||
exportTaskManager.exportAsyncTask(request.getProjectId(), userId, ExportConstants.ExportType.CASE.toString(), request, t -> exportXmind(request, userId));
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.error("导出失败:" + e);
|
||||
throw new MSException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String exportXmind(FunctionalCaseExportRequest request, String userId) {
|
||||
//获取导出的ids集合
|
||||
List<String> ids = functionalCaseService.doSelectIds(request, request.getProjectId());
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
FunctionalCaseXmindData xmindData = buildXmindData(ids, request);
|
||||
File tmpFile = new File(getClass().getClassLoader().getResource(StringUtils.EMPTY).getPath() +
|
||||
File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr() + ".xmind");
|
||||
List<TemplateCustomFieldDTO> templateCustomFields = functionalCaseFileService.getCustomFields(request.getProjectId());
|
||||
TemplateCustomFieldDTO templateCustomFieldDTO = templateCustomFields.stream().filter(item -> StringUtils.equalsIgnoreCase(item.getFieldName(), Translator.get("custom_field.functional_priority"))).findFirst().get();
|
||||
XmindExportUtil.export(xmindData, request, tmpFile, templateCustomFieldDTO);
|
||||
functionalCaseFileService.uploadFileToMinio(tmpFile, request.getFileId());
|
||||
|
||||
functionalCaseLogService.exportExcelLog(request);
|
||||
List<ExportTask> exportTasks = functionalCaseFileService.getExportTasks(request.getProjectId(), userId);
|
||||
String taskId;
|
||||
if (CollectionUtils.isNotEmpty(exportTasks)) {
|
||||
taskId = exportTasks.getFirst().getId();
|
||||
functionalCaseFileService.updateExportTask(ExportConstants.ExportState.SUCCESS.name(), taskId, request.getFileId());
|
||||
} else {
|
||||
taskId = MsgType.CONNECT.name();
|
||||
}
|
||||
SocketMsgDTO socketMsgDTO = new SocketMsgDTO(request.getFileId(), "", MsgType.CONNECT.name(), taskId);
|
||||
socketMsgDTO.setReportId(request.getFileId());
|
||||
ExportWebSocketHandler.sendMessageSingle(socketMsgDTO);
|
||||
} catch (Exception e) {
|
||||
List<ExportTask> exportTasks = functionalCaseFileService.getExportTasks(request.getProjectId(), userId);
|
||||
if (CollectionUtils.isNotEmpty(exportTasks)) {
|
||||
functionalCaseFileService.updateExportTask(ExportConstants.ExportState.ERROR.name(), exportTasks.getFirst().getId(), request.getFileId());
|
||||
}
|
||||
LogUtils.error(e);
|
||||
throw new MSException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private FunctionalCaseXmindData buildXmindData(List<String> ids, FunctionalCaseExportRequest request) {
|
||||
FunctionalCaseXmindData xmindData = new FunctionalCaseXmindData();
|
||||
xmindData.setModuleId("MODULE");
|
||||
xmindData.setModuleName("MODULE");
|
||||
//基础信息
|
||||
List<FunctionalCase> caseList = functionalCaseService.getCaseDataByIds(ids);
|
||||
//大字段
|
||||
Map<String, FunctionalCaseBlob> functionalCaseBlobMap = functionalCaseService.copyBlobInfo(ids);
|
||||
//自定义字段
|
||||
Map<String, List<FunctionalCaseCustomField>> customFieldMap = functionalCaseCustomFieldService.getCustomFieldMapByCaseIds(ids);
|
||||
|
||||
|
||||
Map<String, List<FunctionalCase>> moduleCaseMap = caseList.stream().collect(Collectors.groupingBy(FunctionalCase::getModuleId));
|
||||
List<BaseTreeNode> tree = functionalCaseModuleService.getTree(request.getProjectId());
|
||||
|
||||
for (Map.Entry<String, List<FunctionalCase>> entry : moduleCaseMap.entrySet()) {
|
||||
String moduleId = entry.getKey();
|
||||
List<FunctionalCase> dataList = entry.getValue();
|
||||
List<FunctionalCaseXmindDTO> dtos = buildXmindDTO(dataList, functionalCaseBlobMap, customFieldMap);
|
||||
if (StringUtils.equals(moduleId, ModuleConstants.DEFAULT_NODE_ID)) {
|
||||
xmindData.setFunctionalCaseList(dtos);
|
||||
} else {
|
||||
LinkedList<BaseTreeNode> returnList = new LinkedList<>();
|
||||
LinkedList<BaseTreeNode> modulePathDataList = getModuleById(moduleId, tree, returnList);
|
||||
xmindData.setItem(modulePathDataList, dtos);
|
||||
System.out.println("modulePathDataList: " + modulePathDataList);
|
||||
}
|
||||
}
|
||||
|
||||
return xmindData;
|
||||
|
||||
}
|
||||
|
||||
private LinkedList<BaseTreeNode> getModuleById(String moduleId, List<BaseTreeNode> tree, LinkedList<BaseTreeNode> returnList) {
|
||||
|
||||
for (BaseTreeNode baseTreeNode : tree) {
|
||||
if (StringUtils.equals(baseTreeNode.getId(), moduleId)) {
|
||||
BaseTreeNode node = new BaseTreeNode();
|
||||
node.setId(baseTreeNode.getId());
|
||||
node.setName(baseTreeNode.getName());
|
||||
returnList.addFirst(node);
|
||||
return returnList;
|
||||
} else {
|
||||
List<BaseTreeNode> children = baseTreeNode.getChildren();
|
||||
if (CollectionUtils.isNotEmpty(children)) {
|
||||
LinkedList<BaseTreeNode> result = getModuleById(moduleId, children, returnList);
|
||||
if (CollectionUtils.isNotEmpty(result)) {
|
||||
BaseTreeNode node = new BaseTreeNode();
|
||||
node.setId(baseTreeNode.getId());
|
||||
node.setName(baseTreeNode.getName());
|
||||
returnList.addFirst(node);
|
||||
return returnList;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
|
||||
private List<FunctionalCaseXmindDTO> buildXmindDTO(List<FunctionalCase> dataList, Map<String, FunctionalCaseBlob> functionalCaseBlobMap, Map<String, List<FunctionalCaseCustomField>> customFieldMap) {
|
||||
List<FunctionalCaseXmindDTO> caseXmindDTOS = new ArrayList<>();
|
||||
dataList.forEach(item -> {
|
||||
FunctionalCaseBlob functionalCaseBlob = functionalCaseBlobMap.get(item.getId());
|
||||
List<FunctionalCaseCustomField> customFields = customFieldMap.get(item.getId());
|
||||
FunctionalCaseXmindDTO dto = new FunctionalCaseXmindDTO();
|
||||
dto.setId(item.getId());
|
||||
dto.setNum(item.getNum().toString());
|
||||
dto.setProjectId(item.getProjectId());
|
||||
dto.setName(item.getName());
|
||||
dto.setTags(item.getTags().toString());
|
||||
dto.setCaseEditType(item.getCaseEditType());
|
||||
dto.setSteps(new String(functionalCaseBlob.getSteps() == null ? new byte[0] : functionalCaseBlob.getSteps(), StandardCharsets.UTF_8));
|
||||
dto.setTextDescription(new String(functionalCaseBlob.getTextDescription() == null ? new byte[0] : functionalCaseBlob.getTextDescription(), StandardCharsets.UTF_8));
|
||||
dto.setExpectedResult(new String(functionalCaseBlob.getExpectedResult() == null ? new byte[0] : functionalCaseBlob.getExpectedResult(), StandardCharsets.UTF_8));
|
||||
dto.setPrerequisite(new String(functionalCaseBlob.getPrerequisite() == null ? new byte[0] : functionalCaseBlob.getPrerequisite(), StandardCharsets.UTF_8));
|
||||
dto.setDescription(new String(functionalCaseBlob.getDescription() == null ? new byte[0] : functionalCaseBlob.getDescription(), StandardCharsets.UTF_8));
|
||||
dto.setCustomFieldDTOList(customFields);
|
||||
caseXmindDTOS.add(dto);
|
||||
});
|
||||
return caseXmindDTOS;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.metersphere.functional.xmind.domain;
|
||||
|
||||
import io.metersphere.functional.dto.FunctionalCaseCustomFieldDTO;
|
||||
import io.metersphere.functional.domain.FunctionalCaseCustomField;
|
||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@ -47,7 +47,7 @@ public class FunctionalCaseXmindDTO {
|
||||
private String description;
|
||||
|
||||
@Schema(description = "自定义字段")
|
||||
private List<FunctionalCaseCustomFieldDTO> customFieldDTOList;
|
||||
private List<FunctionalCaseCustomField> customFieldDTOList;
|
||||
|
||||
@Schema(description = "模板自定义字段")
|
||||
private List<TemplateCustomFieldDTO> templateCustomFieldDTOList;
|
||||
|
@ -1,10 +1,14 @@
|
||||
package io.metersphere.functional.xmind.domain;
|
||||
|
||||
import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -21,4 +25,49 @@ public class FunctionalCaseXmindData implements Serializable {
|
||||
private List<FunctionalCaseXmindData> children = new ArrayList<>();
|
||||
|
||||
|
||||
public void setItem(LinkedList<BaseTreeNode> modulePathDataList, List<FunctionalCaseXmindDTO> dataList) {
|
||||
if (CollectionUtils.isNotEmpty(modulePathDataList) && CollectionUtils.isNotEmpty(dataList)) {
|
||||
if (modulePathDataList.size() == 1) {
|
||||
this.setData(modulePathDataList.getFirst(), dataList);
|
||||
} else {
|
||||
BaseTreeNode caseNode = modulePathDataList.getFirst();
|
||||
if (caseNode != null) {
|
||||
FunctionalCaseXmindData matchedData = null;
|
||||
for (FunctionalCaseXmindData item : children) {
|
||||
if (StringUtils.equals(item.getModuleId(), caseNode.getId())) {
|
||||
matchedData = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(matchedData == null){
|
||||
matchedData = new FunctionalCaseXmindData();
|
||||
matchedData.setModuleId(caseNode.getId());
|
||||
matchedData.setModuleName(caseNode.getName());
|
||||
this.children.add(matchedData);
|
||||
}
|
||||
modulePathDataList.removeFirst();
|
||||
matchedData.setItem(modulePathDataList,dataList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setData(BaseTreeNode caseNode, List<FunctionalCaseXmindDTO> dataList) {
|
||||
if (caseNode != null && CollectionUtils.isNotEmpty(dataList)) {
|
||||
boolean matching = false;
|
||||
for (FunctionalCaseXmindData item : children) {
|
||||
if (StringUtils.equals(item.getModuleId(), caseNode.getId())) {
|
||||
matching = true;
|
||||
item.setFunctionalCaseList(dataList);
|
||||
}
|
||||
}
|
||||
if (!matching) {
|
||||
FunctionalCaseXmindData child = new FunctionalCaseXmindData();
|
||||
child.setModuleId(caseNode.getId());
|
||||
child.setModuleName(caseNode.getName());
|
||||
child.setFunctionalCaseList(dataList);
|
||||
this.children.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package io.metersphere.functional.xmind.utils;
|
||||
|
||||
import io.metersphere.functional.constants.FunctionalCaseTypeConstants;
|
||||
import io.metersphere.functional.domain.FunctionalCaseCustomField;
|
||||
import io.metersphere.functional.excel.domain.FunctionalCaseExportColumns;
|
||||
import io.metersphere.functional.excel.domain.FunctionalCaseHeader;
|
||||
import io.metersphere.functional.request.FunctionalCaseExportRequest;
|
||||
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindDTO;
|
||||
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindData;
|
||||
import io.metersphere.sdk.constants.CustomFieldType;
|
||||
@ -16,12 +20,14 @@ import org.xmind.core.*;
|
||||
import org.xmind.core.style.IStyle;
|
||||
import org.xmind.core.style.IStyleSheet;
|
||||
|
||||
import java.io.File;
|
||||
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;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
@ -37,7 +43,7 @@ public class XmindExportUtil {
|
||||
* @param template
|
||||
*/
|
||||
public static void downloadTemplate(HttpServletResponse response, FunctionalCaseXmindData caseData, boolean template, Map<String, List<String>> customFieldOptionsMap) {
|
||||
IWorkbook workBook = createXmindByCaseData(caseData, template, customFieldOptionsMap);
|
||||
IWorkbook workBook = createXmindByCaseData(caseData, template, customFieldOptionsMap, null, null);
|
||||
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
@ -53,7 +59,7 @@ public class XmindExportUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static IWorkbook createXmindByCaseData(FunctionalCaseXmindData caseData, boolean template, Map<String, List<String>> customFieldOptionsMap) {
|
||||
private static IWorkbook createXmindByCaseData(FunctionalCaseXmindData caseData, boolean template, Map<String, List<String>> customFieldOptionsMap, FunctionalCaseExportRequest request, TemplateCustomFieldDTO priority) {
|
||||
// 创建思维导图的工作空间
|
||||
IWorkbookBuilder workbookBuilder = Core.getWorkbookBuilder();
|
||||
IWorkbook workbook = workbookBuilder.createWorkbook();
|
||||
@ -75,14 +81,18 @@ public class XmindExportUtil {
|
||||
|
||||
if (CollectionUtils.isNotEmpty(caseData.getChildren())) {
|
||||
for (FunctionalCaseXmindData data : caseData.getChildren()) {
|
||||
addItemTopic(rootTopic, workbook, styleMap, data, true, template, customFieldOptionsMap);
|
||||
if (template) {
|
||||
addTemplateTopic(rootTopic, workbook, styleMap, data, true, customFieldOptionsMap);
|
||||
} else {
|
||||
addTopic(rootTopic, workbook, styleMap, data, true, request, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
return workbook;
|
||||
}
|
||||
|
||||
private static void addItemTopic(ITopic parentTpoic, IWorkbook workbook, Map<String, IStyle> styleMap, FunctionalCaseXmindData xmindData,
|
||||
boolean isFirstLevel, boolean template, Map<String, List<String>> customFieldOptionsMap) {
|
||||
private static void addTemplateTopic(ITopic parentTopic, IWorkbook workbook, Map<String, IStyle> styleMap, FunctionalCaseXmindData xmindData,
|
||||
boolean isFirstLevel, Map<String, List<String>> customFieldOptionsMap) {
|
||||
ITopic topic = workbook.createTopic();
|
||||
topic.setTitleText(xmindData.getModuleName());
|
||||
if (isFirstLevel) {
|
||||
@ -94,7 +104,7 @@ public class XmindExportUtil {
|
||||
topic.setStyleId(styleMap.get("subTopicStyle").getId());
|
||||
}
|
||||
}
|
||||
parentTpoic.add(topic);
|
||||
parentTopic.add(topic);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(xmindData.getFunctionalCaseList())) {
|
||||
IStyle style = null;
|
||||
@ -107,21 +117,18 @@ public class XmindExportUtil {
|
||||
if (style != null) {
|
||||
itemTopic.setStyleId(style.getId());
|
||||
}
|
||||
if (template) {
|
||||
// 模板
|
||||
buildTemplateTopic(topic, style, dto, itemTopic, workbook, customFieldOptionsMap);
|
||||
}
|
||||
buildTemplateTopic(topic, style, dto, itemTopic, workbook, customFieldOptionsMap);
|
||||
}
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(xmindData.getChildren())) {
|
||||
for (FunctionalCaseXmindData data : xmindData.getChildren()) {
|
||||
addItemTopic(topic, workbook, styleMap, data, false, template, customFieldOptionsMap);
|
||||
addTemplateTopic(topic, workbook, styleMap, data, false, customFieldOptionsMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildTemplateTopic(ITopic topic, IStyle style, FunctionalCaseXmindDTO dto, ITopic itemTopic, IWorkbook workbook,Map<String, List<String>> customFieldOptionsMap) {
|
||||
private static void buildTemplateTopic(ITopic topic, IStyle style, FunctionalCaseXmindDTO dto, ITopic itemTopic, IWorkbook workbook, Map<String, List<String>> customFieldOptionsMap) {
|
||||
|
||||
//用例名称
|
||||
TemplateCustomFieldDTO priorityDto = dto.getTemplateCustomFieldDTOList().stream().filter(item -> StringUtils.equalsIgnoreCase(item.getFieldName(), Translator.get("custom_field.functional_priority"))).findFirst().get();
|
||||
@ -230,7 +237,7 @@ public class XmindExportUtil {
|
||||
dto.getTemplateCustomFieldDTOList().forEach(item -> {
|
||||
if (!StringUtils.equalsIgnoreCase(item.getFieldName(), Translator.get("custom_field.functional_priority"))) {
|
||||
ITopic customTopic = workbook.createTopic();
|
||||
String fieldComment = getComment(item,customFieldOptionsMap);
|
||||
String fieldComment = getComment(item, customFieldOptionsMap);
|
||||
customTopic.setTitleText(item.getFieldName().concat(": ").concat(fieldComment));
|
||||
if (style != null) {
|
||||
customTopic.setStyleId(style.getId());
|
||||
@ -242,7 +249,7 @@ public class XmindExportUtil {
|
||||
topic.add(itemTopic);
|
||||
}
|
||||
|
||||
private static String getComment(TemplateCustomFieldDTO templateCustomFieldDTO,Map<String, List<String>> customFieldOptionsMap) {
|
||||
private static String getComment(TemplateCustomFieldDTO templateCustomFieldDTO, Map<String, List<String>> customFieldOptionsMap) {
|
||||
String comment = "";
|
||||
if (StringUtils.equalsAnyIgnoreCase(templateCustomFieldDTO.getType(), CustomFieldType.MULTIPLE_MEMBER.name(), CustomFieldType.MEMBER.name())) {
|
||||
if (templateCustomFieldDTO.getRequired()) {
|
||||
@ -356,4 +363,112 @@ public class XmindExportUtil {
|
||||
|
||||
return styleMap;
|
||||
}
|
||||
|
||||
|
||||
public static void export(FunctionalCaseXmindData xmindData, FunctionalCaseExportRequest request, File tmpFile, TemplateCustomFieldDTO priority) {
|
||||
IWorkbook workBook = createXmindByCaseData(xmindData, false, null, request, priority);
|
||||
try {
|
||||
workBook.save(tmpFile.getAbsolutePath());
|
||||
} 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 void addTopic(ITopic parentTopic, IWorkbook workbook, Map<String, IStyle> styleMap, FunctionalCaseXmindData xmindData, boolean isFirstLevel, FunctionalCaseExportRequest request, TemplateCustomFieldDTO priority) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
parentTopic.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());
|
||||
}
|
||||
buildTopic(topic, style, dto, itemTopic, workbook, request, xmindData.getModuleName(), priority);
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(xmindData.getChildren())) {
|
||||
for (FunctionalCaseXmindData data : xmindData.getChildren()) {
|
||||
addTopic(topic, workbook, styleMap, data, false, request, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildTopic(ITopic topic, IStyle style, FunctionalCaseXmindDTO dto, ITopic itemTopic, IWorkbook workbook, FunctionalCaseExportRequest request, String moduleName, TemplateCustomFieldDTO priority) {
|
||||
List<String> systemColumns = request.getSystemFields().stream().map(FunctionalCaseHeader::getId).toList();
|
||||
FunctionalCaseExportColumns columns = new FunctionalCaseExportColumns();
|
||||
|
||||
Map<String, String> customFieldMap = dto.getCustomFieldDTOList().stream().collect(Collectors.toMap(FunctionalCaseCustomField::getFieldId, FunctionalCaseCustomField::getValue));
|
||||
|
||||
//用例名称
|
||||
String casePriority = customFieldMap.get(priority.getFieldId());
|
||||
itemTopic.setTitleText("case-".concat(StringUtils.defaultIfBlank(casePriority,StringUtils.EMPTY)).concat(": ").concat(dto.getName()));
|
||||
//系统字段
|
||||
systemColumns.forEach(item -> {
|
||||
if (columns.getSystemColumns().containsKey(item) && !StringUtils.equalsIgnoreCase(item, "name")) {
|
||||
ITopic preTopic = workbook.createTopic();
|
||||
switch (item) {
|
||||
case "num":
|
||||
preTopic.setTitleText(columns.getSystemColumns().get(item).concat(": ").concat(dto.getNum()));
|
||||
break;
|
||||
case "prerequisite":
|
||||
preTopic.setTitleText(columns.getSystemColumns().get(item).concat(": ").concat(dto.getPrerequisite()));
|
||||
break;
|
||||
case "module":
|
||||
preTopic.setTitleText(columns.getSystemColumns().get(item).concat(": ").concat(moduleName));
|
||||
break;
|
||||
case "text_description":
|
||||
preTopic.setTitleText(columns.getSystemColumns().get(item).concat(": ").concat(dto.getTextDescription()));
|
||||
break;
|
||||
case "expected_result":
|
||||
preTopic.setTitleText(columns.getSystemColumns().get(item).concat(": ").concat(dto.getExpectedResult()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (style != null) {
|
||||
preTopic.setStyleId(style.getId());
|
||||
}
|
||||
itemTopic.add(preTopic, ITopic.ATTACHED);
|
||||
}
|
||||
});
|
||||
|
||||
//自定义字段
|
||||
Map<String, String> customColumnsMap = request.getCustomFields().stream().collect(Collectors.toMap(FunctionalCaseHeader::getId, FunctionalCaseHeader::getName));
|
||||
|
||||
|
||||
customColumnsMap.forEach((k, v) -> {
|
||||
if (customFieldMap.containsKey(k)) {
|
||||
ITopic preTopic = workbook.createTopic();
|
||||
preTopic.setTitleText(v.concat(": ").concat(customFieldMap.get(k)));
|
||||
if (style != null) {
|
||||
preTopic.setStyleId(style.getId());
|
||||
}
|
||||
itemTopic.add(preTopic, ITopic.ATTACHED);
|
||||
}
|
||||
});
|
||||
|
||||
topic.add(itemTopic);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||
public static final String EXPORT_COLUMNS_URL = "/functional/case/export/columns/";
|
||||
public static final String DOWNLOAD_FILE_URL = "/functional/case/download/file/";
|
||||
public static final String STOP_EXPORT_URL = "/functional/case/stop/";
|
||||
public static final String EXPORT_XMIND_URL = "/functional/case/export/xmind";
|
||||
|
||||
@Resource
|
||||
private NotificationMapper notificationMapper;
|
||||
@ -856,8 +857,8 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||
@Order(24)
|
||||
public void downloadFile() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get(DOWNLOAD_FILE_URL + DEFAULT_PROJECT_ID + "/" + "123142342")
|
||||
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||
.header(SessionConstants.CSRF_TOKEN, csrfToken));
|
||||
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||
.header(SessionConstants.CSRF_TOKEN, csrfToken));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -865,4 +866,44 @@ public class FunctionalCaseControllerTests extends BaseTest {
|
||||
public void stopExport() throws Exception {
|
||||
this.requestGetExcel(STOP_EXPORT_URL + DEFAULT_PROJECT_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void exportXmind() 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_XMIND_URL, request);
|
||||
request.setSelectIds(new ArrayList<>());
|
||||
this.requestPost(EXPORT_XMIND_URL, request);
|
||||
request.setSelectIds(List.of("TEST_FUNCTIONAL_CASE_ID_8"));
|
||||
this.requestPost(EXPORT_XMIND_URL, request);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,10 @@ VALUES ('TEST_FUNCTIONAL_CASE_ID_6', 6, 'TEST_MODULE_ID_GYQ', '100001100001', '1
|
||||
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time)
|
||||
VALUES ('TEST_FUNCTIONAL_CASE_ID_7', 7, 'TEST_MODULE_ID_GYQ', '100001100001', '100001', 'copy_long_name_11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111', 'UN_REVIEWED', NULL, 'STEP', 0, 'v3.0.0', 'TEST_REF_ID_2', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
|
||||
|
||||
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time)
|
||||
VALUES ('TEST_FUNCTIONAL_CASE_ID_8', 1, 'root', '100001100001', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
|
||||
|
||||
|
||||
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ID', 'STEP', '1111', '', '', 'TEST');
|
||||
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ID_1', 'STEP', '1111', '', '', '1111');
|
||||
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ID_2', 'STEP', '2222', '', '', '2222');
|
||||
@ -38,6 +42,7 @@ INSERT INTO functional_case_blob(id, steps, text_description, expected_result, p
|
||||
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ID_5', 'STEP', '5555', '', '', '5555');
|
||||
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ID_6', 'STEP', '6666', '', '', '6666');
|
||||
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ID_7', 'STEP', '7777', '', '', '7777');
|
||||
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ID_8', NULL, NUll, NUll, NUll, NUll);
|
||||
|
||||
|
||||
INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('TEST_FUNCTIONAL_CASE_ID', '100548878725546079', '22');
|
||||
|
@ -34,7 +34,8 @@ public class ExportTaskManager {
|
||||
public static final String EXPORT_CONSUME = "export_consume";
|
||||
|
||||
|
||||
public <T> void exportAsyncTask(String userId, String type, T t, Function<Object, Object> selectListFunc) throws InterruptedException {
|
||||
public <T> void exportAsyncTask(String projectId, String userId, String type, T t, Function<Object, Object> selectListFunc) throws InterruptedException {
|
||||
ExportTask exportTask = buildExportTask(projectId, userId, type);
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(1);
|
||||
Future<?> future = executorService.submit(() -> {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
@ -44,12 +45,10 @@ public class ExportTaskManager {
|
||||
}
|
||||
LogUtils.info("Thread has been interrupted.");
|
||||
});
|
||||
Thread.sleep(6000);
|
||||
ExportTask exportTask = buildExportTask(userId, type);
|
||||
map.put(exportTask.getId(), future);
|
||||
}
|
||||
|
||||
private ExportTask buildExportTask(String userId, String type) {
|
||||
private ExportTask buildExportTask(String projectId, String userId, String type) {
|
||||
ExportTask exportTask = new ExportTask();
|
||||
exportTask.setId(IDGenerator.nextStr());
|
||||
exportTask.setType(type);
|
||||
@ -58,6 +57,7 @@ public class ExportTaskManager {
|
||||
exportTask.setState(ExportConstants.ExportState.PREPARED.toString());
|
||||
exportTask.setUpdateUser(userId);
|
||||
exportTask.setUpdateTime(System.currentTimeMillis());
|
||||
exportTask.setProjectId(projectId);
|
||||
exportTaskMapper.insert(exportTask);
|
||||
return exportTask;
|
||||
}
|
||||
@ -71,15 +71,17 @@ public class ExportTaskManager {
|
||||
kafkaTemplate.send(KafkaTopicConstants.EXPORT, JSON.toJSONString(exportTask));
|
||||
}
|
||||
|
||||
@KafkaListener(id=EXPORT_CONSUME, topics = KafkaTopicConstants.EXPORT, groupId = EXPORT_CONSUME + "_" + "${random.uuid}")
|
||||
@KafkaListener(id = EXPORT_CONSUME, topics = KafkaTopicConstants.EXPORT, groupId = EXPORT_CONSUME + "_" + "${random.uuid}")
|
||||
public void stop(ConsumerRecord<?, String> record) {
|
||||
LogUtils.info("Service consume platform_plugin message: " + record.value());
|
||||
ExportTask exportTask = JSON.parseObject(record.value(), ExportTask.class);
|
||||
if (exportTask!=null && StringUtils.isNotBlank(exportTask.getId())) {
|
||||
if (exportTask != null && StringUtils.isNotBlank(exportTask.getId())) {
|
||||
String id = exportTask.getId();
|
||||
map.get(id).cancel(true);
|
||||
map.remove(id);
|
||||
exportTaskMapper.updateByPrimaryKey(exportTask);
|
||||
if (map.containsKey(id)) {
|
||||
map.get(id).cancel(true);
|
||||
map.remove(id);
|
||||
}
|
||||
exportTaskMapper.updateByPrimaryKeySelective(exportTask);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user