feat(功能用例): 功能用例评论副文本增加文件
This commit is contained in:
parent
d1e36c20b9
commit
c76e081451
|
@ -1,10 +1,8 @@
|
|||
package io.metersphere.functional.domain;
|
||||
|
||||
import io.metersphere.validation.groups.Created;
|
||||
import io.metersphere.validation.groups.Updated;
|
||||
import io.metersphere.validation.groups.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -12,40 +10,43 @@ import lombok.Data;
|
|||
|
||||
@Data
|
||||
public class FunctionalCaseAttachment implements Serializable {
|
||||
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{functional_case_attachment.id.not_blank}", groups = {Updated.class})
|
||||
@Size(min = 1, max = 50, message = "{functional_case_attachment.id.length_range}", groups = {Created.class, Updated.class})
|
||||
private String id;
|
||||
|
||||
@Schema(description = "功能用例ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Schema(description = "功能用例ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{functional_case_attachment.case_id.not_blank}", groups = {Created.class})
|
||||
@Size(min = 1, max = 50, message = "{functional_case_attachment.case_id.length_range}", groups = {Created.class, Updated.class})
|
||||
private String caseId;
|
||||
|
||||
@Schema(description = "文件的ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Schema(description = "文件的ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{functional_case_attachment.file_id.not_blank}", groups = {Created.class})
|
||||
@Size(min = 1, max = 50, message = "{functional_case_attachment.file_id.length_range}", groups = {Created.class, Updated.class})
|
||||
private String fileId;
|
||||
|
||||
@Schema(description = "文件名称", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Schema(description = "文件名称", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{functional_case_attachment.file_name.not_blank}", groups = {Created.class})
|
||||
@Size(min = 1, max = 255, message = "{functional_case_attachment.file_name.length_range}", groups = {Created.class, Updated.class})
|
||||
private String fileName;
|
||||
|
||||
@Schema(description = "文件大小", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{functional_case_attachment.size.not_blank}", groups = {Created.class})
|
||||
@Size(min = 1, max = 19, message = "{functional_case_attachment.size.length_range}", groups = {Created.class, Updated.class})
|
||||
@Schema(description = "文件来源", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{functional_case_attachment.file_source.not_blank}", groups = {Created.class})
|
||||
@Size(min = 1, max = 50, message = "{functional_case_attachment.file_source.length_range}", groups = {Created.class, Updated.class})
|
||||
private String fileSource;
|
||||
|
||||
@Schema(description = "文件大小", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{functional_case_attachment.size.not_blank}", groups = {Created.class})
|
||||
private Long size;
|
||||
|
||||
@Schema(description = "是否本地", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{functional_case_attachment.local.not_blank}", groups = {Created.class})
|
||||
@Size(min = 1, max = 1, message = "{functional_case_attachment.local.length_range}", groups = {Created.class, Updated.class})
|
||||
@Schema(description = "是否本地", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{functional_case_attachment.local.not_blank}", groups = {Created.class})
|
||||
private Boolean local;
|
||||
|
||||
@Schema(description = "创建人")
|
||||
@Schema(description = "创建人")
|
||||
private String createUser;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@Schema(description = "创建时间")
|
||||
private Long createTime;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -55,6 +56,7 @@ public class FunctionalCaseAttachment implements Serializable {
|
|||
caseId("case_id", "caseId", "VARCHAR", false),
|
||||
fileId("file_id", "fileId", "VARCHAR", false),
|
||||
fileName("file_name", "fileName", "VARCHAR", false),
|
||||
fileSource("file_source", "fileSource", "VARCHAR", false),
|
||||
size("size", "size", "BIGINT", true),
|
||||
local("local", "local", "BIT", true),
|
||||
createUser("create_user", "createUser", "VARCHAR", false),
|
||||
|
|
|
@ -384,6 +384,76 @@ public class FunctionalCaseAttachmentExample {
|
|||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceIsNull() {
|
||||
addCriterion("file_source is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceIsNotNull() {
|
||||
addCriterion("file_source is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceEqualTo(String value) {
|
||||
addCriterion("file_source =", value, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceNotEqualTo(String value) {
|
||||
addCriterion("file_source <>", value, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceGreaterThan(String value) {
|
||||
addCriterion("file_source >", value, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("file_source >=", value, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceLessThan(String value) {
|
||||
addCriterion("file_source <", value, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceLessThanOrEqualTo(String value) {
|
||||
addCriterion("file_source <=", value, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceLike(String value) {
|
||||
addCriterion("file_source like", value, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceNotLike(String value) {
|
||||
addCriterion("file_source not like", value, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceIn(List<String> values) {
|
||||
addCriterion("file_source in", values, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceNotIn(List<String> values) {
|
||||
addCriterion("file_source not in", values, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceBetween(String value1, String value2) {
|
||||
addCriterion("file_source between", value1, value2, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileSourceNotBetween(String value1, String value2) {
|
||||
addCriterion("file_source not between", value1, value2, "fileSource");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeIsNull() {
|
||||
addCriterion("`size` is null");
|
||||
return (Criteria) this;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<result column="case_id" jdbcType="VARCHAR" property="caseId" />
|
||||
<result column="file_id" jdbcType="VARCHAR" property="fileId" />
|
||||
<result column="file_name" jdbcType="VARCHAR" property="fileName" />
|
||||
<result column="file_source" jdbcType="VARCHAR" property="fileSource" />
|
||||
<result column="size" jdbcType="BIGINT" property="size" />
|
||||
<result column="local" jdbcType="BIT" property="local" />
|
||||
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
|
||||
|
@ -70,7 +71,7 @@
|
|||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, case_id, file_id, file_name, `size`, `local`, create_user, create_time
|
||||
id, case_id, file_id, file_name, file_source, `size`, `local`, create_user, create_time
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.functional.domain.FunctionalCaseAttachmentExample" resultMap="BaseResultMap">
|
||||
select
|
||||
|
@ -104,11 +105,13 @@
|
|||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.functional.domain.FunctionalCaseAttachment">
|
||||
insert into functional_case_attachment (id, case_id, file_id,
|
||||
file_name, `size`, `local`, create_user,
|
||||
create_time)
|
||||
file_name, file_source, `size`,
|
||||
`local`, create_user, create_time
|
||||
)
|
||||
values (#{id,jdbcType=VARCHAR}, #{caseId,jdbcType=VARCHAR}, #{fileId,jdbcType=VARCHAR},
|
||||
#{fileName,jdbcType=VARCHAR}, #{size,jdbcType=BIGINT}, #{local,jdbcType=BIT}, #{createUser,jdbcType=VARCHAR},
|
||||
#{createTime,jdbcType=BIGINT})
|
||||
#{fileName,jdbcType=VARCHAR}, #{fileSource,jdbcType=VARCHAR}, #{size,jdbcType=BIGINT},
|
||||
#{local,jdbcType=BIT}, #{createUser,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}
|
||||
)
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.functional.domain.FunctionalCaseAttachment">
|
||||
insert into functional_case_attachment
|
||||
|
@ -125,6 +128,9 @@
|
|||
<if test="fileName != null">
|
||||
file_name,
|
||||
</if>
|
||||
<if test="fileSource != null">
|
||||
file_source,
|
||||
</if>
|
||||
<if test="size != null">
|
||||
`size`,
|
||||
</if>
|
||||
|
@ -151,6 +157,9 @@
|
|||
<if test="fileName != null">
|
||||
#{fileName,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="fileSource != null">
|
||||
#{fileSource,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="size != null">
|
||||
#{size,jdbcType=BIGINT},
|
||||
</if>
|
||||
|
@ -186,6 +195,9 @@
|
|||
<if test="record.fileName != null">
|
||||
file_name = #{record.fileName,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.fileSource != null">
|
||||
file_source = #{record.fileSource,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.size != null">
|
||||
`size` = #{record.size,jdbcType=BIGINT},
|
||||
</if>
|
||||
|
@ -209,6 +221,7 @@
|
|||
case_id = #{record.caseId,jdbcType=VARCHAR},
|
||||
file_id = #{record.fileId,jdbcType=VARCHAR},
|
||||
file_name = #{record.fileName,jdbcType=VARCHAR},
|
||||
file_source = #{record.fileSource,jdbcType=VARCHAR},
|
||||
`size` = #{record.size,jdbcType=BIGINT},
|
||||
`local` = #{record.local,jdbcType=BIT},
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR},
|
||||
|
@ -229,6 +242,9 @@
|
|||
<if test="fileName != null">
|
||||
file_name = #{fileName,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="fileSource != null">
|
||||
file_source = #{fileSource,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="size != null">
|
||||
`size` = #{size,jdbcType=BIGINT},
|
||||
</if>
|
||||
|
@ -249,6 +265,7 @@
|
|||
set case_id = #{caseId,jdbcType=VARCHAR},
|
||||
file_id = #{fileId,jdbcType=VARCHAR},
|
||||
file_name = #{fileName,jdbcType=VARCHAR},
|
||||
file_source = #{fileSource,jdbcType=VARCHAR},
|
||||
`size` = #{size,jdbcType=BIGINT},
|
||||
`local` = #{local,jdbcType=BIT},
|
||||
create_user = #{createUser,jdbcType=VARCHAR},
|
||||
|
@ -257,12 +274,14 @@
|
|||
</update>
|
||||
<insert id="batchInsert" parameterType="map">
|
||||
insert into functional_case_attachment
|
||||
(id, case_id, file_id, file_name, `size`, `local`, create_user, create_time)
|
||||
(id, case_id, file_id, file_name, file_source, `size`, `local`, create_user, create_time
|
||||
)
|
||||
values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.id,jdbcType=VARCHAR}, #{item.caseId,jdbcType=VARCHAR}, #{item.fileId,jdbcType=VARCHAR},
|
||||
#{item.fileName,jdbcType=VARCHAR}, #{item.size,jdbcType=BIGINT}, #{item.local,jdbcType=BIT},
|
||||
#{item.createUser,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT})
|
||||
#{item.fileName,jdbcType=VARCHAR}, #{item.fileSource,jdbcType=VARCHAR}, #{item.size,jdbcType=BIGINT},
|
||||
#{item.local,jdbcType=BIT}, #{item.createUser,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
<insert id="batchInsertSelective" parameterType="map">
|
||||
|
@ -287,6 +306,9 @@
|
|||
<if test="'file_name'.toString() == column.value">
|
||||
#{item.fileName,jdbcType=VARCHAR}
|
||||
</if>
|
||||
<if test="'file_source'.toString() == column.value">
|
||||
#{item.fileSource,jdbcType=VARCHAR}
|
||||
</if>
|
||||
<if test="'size'.toString() == column.value">
|
||||
#{item.size,jdbcType=BIGINT}
|
||||
</if>
|
||||
|
|
|
@ -112,6 +112,7 @@ CREATE TABLE IF NOT EXISTS functional_case_attachment
|
|||
`case_id` VARCHAR(50) NOT NULL COMMENT '功能用例ID',
|
||||
`file_id` VARCHAR(50) NOT NULL COMMENT '文件的ID',
|
||||
`file_name` VARCHAR(255) NOT NULL COMMENT '文件名称',
|
||||
`file_source` VARCHAR(50) NOT NULL DEFAULT 'ATTACHMENT' COMMENT '文件来源' ,
|
||||
`size` BIGINT NOT NULL COMMENT '文件大小',
|
||||
`local` BIT(1) NOT NULL COMMENT '是否本地',
|
||||
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人',
|
||||
|
@ -126,6 +127,7 @@ CREATE INDEX idx_case_id ON functional_case_attachment (case_id);
|
|||
CREATE INDEX idx_local ON functional_case_attachment (local);
|
||||
CREATE INDEX idx_file_id ON functional_case_attachment (file_id);
|
||||
CREATE INDEX idx_file_name ON functional_case_attachment (file_name);
|
||||
CREATE INDEX idx_file_source ON functional_case_attachment(file_source);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS functional_case_follower
|
||||
|
|
|
@ -46,6 +46,7 @@ public class DefaultRepositoryDir {
|
|||
private static final String PROJECT_API_CASE_DIR = PROJECT_DIR + "/api-case/%s";
|
||||
private static final String PROJECT_ENV_SSL_DIR = PROJECT_DIR + "/environment/%s";
|
||||
private static final String PROJECT_FUNCTIONAL_CASE_DIR = PROJECT_DIR + "/functional-case/%s";
|
||||
private static final String PROJECT_FUNCTIONAL_CASE_PREVIEW_DIR = PROJECT_DIR + "/functional-case/preview/%s";
|
||||
private static final String PROJECT_FILE_MANAGEMENT_DIR = PROJECT_DIR + "/file-management";
|
||||
private static final String PROJECT_FILE_MANAGEMENT_PREVIEW_DIR = PROJECT_DIR + "/file-management/preview";
|
||||
/**
|
||||
|
@ -84,6 +85,10 @@ public class DefaultRepositoryDir {
|
|||
return String.format(PROJECT_FUNCTIONAL_CASE_DIR, projectId, functionalCaseId);
|
||||
}
|
||||
|
||||
public static String getFunctionalCasePreviewDir(String projectId, String functionalCaseId) {
|
||||
return String.format(PROJECT_FUNCTIONAL_CASE_PREVIEW_DIR, projectId, functionalCaseId);
|
||||
}
|
||||
|
||||
public static String getFileManagementDir(String projectId) {
|
||||
return String.format(PROJECT_FILE_MANAGEMENT_DIR, projectId);
|
||||
}
|
||||
|
|
|
@ -95,4 +95,11 @@ public interface FileRepository {
|
|||
* @throws Exception
|
||||
*/
|
||||
void copyFile(FileCopyRequest request) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取文件大小
|
||||
* @param request
|
||||
* @throws Exception
|
||||
*/
|
||||
long getFileSize(FileRequest request) throws Exception;
|
||||
}
|
||||
|
|
|
@ -83,4 +83,9 @@ public class GitRepository implements FileRepository {
|
|||
public void copyFile(FileCopyRequest request) throws Exception {
|
||||
throw new MSException("Not support copy file");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFileSize(FileRequest request) throws Exception {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,12 @@ public class LocalFileRepository implements FileRepository {
|
|||
throw new MSException("Not support copy file");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFileSize(FileRequest request) throws Exception {
|
||||
File file = new File(getFilePath(request));
|
||||
return file.length();
|
||||
}
|
||||
|
||||
private String getFilePath(FileRequest request) {
|
||||
MsFileUtils.validateFileName(request.getFolder(), request.getFileName());
|
||||
return StringUtils.join(getFileDir(request), "/", request.getFileName());
|
||||
|
|
|
@ -206,4 +206,14 @@ public class MinioRepository implements FileRepository {
|
|||
.object(fileName) // 文件名
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getFileSize(FileRequest request) throws Exception {
|
||||
String fileName = getPath(request);
|
||||
return client.statObject(StatObjectArgs.builder()
|
||||
.bucket(BUCKET) // 存储桶
|
||||
.object(fileName) // 文件名
|
||||
.build()).size();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.functional.constants;
|
||||
|
||||
public enum CaseFileSourceType {
|
||||
ATTACHMENT,//附件
|
||||
PREREQUISITE,//前置条件
|
||||
TEXT_DESCRIPTION,//步骤描述
|
||||
EXPECTED_RESULT,//预期结果
|
||||
DESCRIPTION,//备注
|
||||
CASE_COMMENT,//用例评论
|
||||
REVIEW_COMMENT//评审评论
|
||||
}
|
|
@ -170,4 +170,11 @@ public class FunctionalCaseAttachmentController {
|
|||
}
|
||||
|
||||
|
||||
@PostMapping("/upload/temp/file")
|
||||
@Operation(summary = "用例管理-功能用例-上传副文本里所需的文件资源,并返回文件ID")
|
||||
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_COMMENT)
|
||||
public String upload(@RequestParam("file") MultipartFile file) throws Exception {
|
||||
return functionalCaseAttachmentService.uploadTemp(file);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class FunctionalCaseCommentRequest {
|
||||
|
||||
|
@ -34,4 +36,15 @@ public class FunctionalCaseCommentRequest {
|
|||
@NotBlank(message = "{functional_case_comment.event.not_blank}", groups = {Created.class})
|
||||
private String event;
|
||||
|
||||
@Schema(description = "项目Id")
|
||||
@NotBlank(message = "{project.id.not_blank}")
|
||||
private String projectId;
|
||||
|
||||
/**
|
||||
* 新上传的文件ID
|
||||
* 创建时先按ID创建目录,再把文件放入目录
|
||||
*/
|
||||
@Schema(description = "新上传的文件ID")
|
||||
private List<String> uploadFileIds;
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.functional.service;
|
|||
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.metersphere.functional.constants.CaseFileSourceType;
|
||||
import io.metersphere.functional.domain.FunctionalCaseAttachment;
|
||||
import io.metersphere.functional.domain.FunctionalCaseAttachmentExample;
|
||||
import io.metersphere.functional.dto.FunctionalCaseAttachmentDTO;
|
||||
|
@ -18,19 +19,25 @@ import io.metersphere.project.service.FileService;
|
|||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||
import io.metersphere.sdk.constants.StorageType;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.file.FileCenter;
|
||||
import io.metersphere.sdk.file.FileCopyRequest;
|
||||
import io.metersphere.sdk.file.FileRepository;
|
||||
import io.metersphere.sdk.file.FileRequest;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.sdk.util.FileAssociationSourceUtil;
|
||||
import io.metersphere.sdk.util.*;
|
||||
import io.metersphere.system.log.constants.OperationLogModule;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -57,6 +64,10 @@ public class FunctionalCaseAttachmentService {
|
|||
private static final String UPLOAD_FILE = "/attachment/upload/file";
|
||||
private static final String DELETED_FILE = "/attachment/delete/file";
|
||||
|
||||
|
||||
@Value("50MB")
|
||||
private DataSize maxFileSize;
|
||||
|
||||
/**
|
||||
* 保存本地上传文件和用例关联关系
|
||||
*
|
||||
|
@ -102,6 +113,7 @@ public class FunctionalCaseAttachmentService {
|
|||
caseAttachment.setCaseId(caseId);
|
||||
caseAttachment.setFileId(fileId);
|
||||
caseAttachment.setFileName(fileName);
|
||||
caseAttachment.setFileSource(CaseFileSourceType.ATTACHMENT.toString());
|
||||
caseAttachment.setSize(fileSize);
|
||||
caseAttachment.setLocal(isLocal);
|
||||
caseAttachment.setCreateUser(userId);
|
||||
|
@ -313,4 +325,136 @@ public class FunctionalCaseAttachmentService {
|
|||
this.unAssociation(request.getCaseId(), Arrays.asList(request.getId()), DELETED_FILE, userId, request.getProjectId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String uploadTemp(MultipartFile file) {
|
||||
String fileName = StringUtils.trim(file.getOriginalFilename());
|
||||
if (file.getSize() > maxFileSize.toBytes()) {
|
||||
throw new MSException(Translator.get("file.size.is.too.large"));
|
||||
}
|
||||
if (StringUtils.isBlank(fileName)) {
|
||||
throw new MSException(Translator.get("file.name.cannot.be.empty"));
|
||||
}
|
||||
String fileId = IDGenerator.nextStr();
|
||||
FileRequest fileRequest = new FileRequest();
|
||||
fileRequest.setFileName(file.getOriginalFilename());
|
||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||
fileRequest.setFolder(systemTempDir + "/" + fileId);
|
||||
try {
|
||||
FileCenter.getDefaultRepository()
|
||||
.saveFile(file, fileRequest);
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
throw new MSException(Translator.get("file_upload_fail"));
|
||||
}
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public void uploadMinioFile(String caseId, String projectId, List<String> uploadFileIds, String userId, String fileSource){
|
||||
String functionalCaseDir = DefaultRepositoryDir.getFunctionalCaseDir(projectId, caseId);
|
||||
// 处理本地上传文件
|
||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||
if (CollectionUtils.isNotEmpty(uploadFileIds)) {
|
||||
// 添加文件与功能用例的关联关系
|
||||
Map<String, String> addFileMap = new HashMap<>();
|
||||
List<FunctionalCaseAttachment> functionalCaseAttachments = uploadFileIds.stream().map(fileId -> {
|
||||
FunctionalCaseAttachment functionalCaseAttachment = new FunctionalCaseAttachment();
|
||||
String fileName = getTempFileNameByFileId(fileId);
|
||||
functionalCaseAttachment.setId(IDGenerator.nextStr());
|
||||
functionalCaseAttachment.setCaseId(caseId);
|
||||
functionalCaseAttachment.setFileId(fileId);
|
||||
functionalCaseAttachment.setFileName(fileName);
|
||||
functionalCaseAttachment.setFileSource(fileSource);
|
||||
long fileSize = 0;
|
||||
try {
|
||||
FileCopyRequest fileCopyRequest = new FileCopyRequest();
|
||||
fileCopyRequest.setFolder(systemTempDir + "/" + fileId);
|
||||
fileCopyRequest.setFileName(fileName);
|
||||
fileSize = defaultRepository.getFileSize(fileCopyRequest);
|
||||
} catch (Exception e) {
|
||||
LogUtils.error("读取文件大小失败");
|
||||
}
|
||||
functionalCaseAttachment.setSize(fileSize);
|
||||
functionalCaseAttachment.setLocal(true);
|
||||
functionalCaseAttachment.setCreateUser(userId);
|
||||
functionalCaseAttachment.setCreateTime(System.currentTimeMillis());
|
||||
addFileMap.put(fileId, fileName);
|
||||
return functionalCaseAttachment;
|
||||
}).toList();
|
||||
functionalCaseAttachmentMapper.batchInsert(functionalCaseAttachments);
|
||||
// 上传文件到对象存储
|
||||
uploadFileResource(functionalCaseDir, addFileMap, projectId, caseId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件ID,查询minio中对应目录下的文件名称
|
||||
*/
|
||||
public String getTempFileNameByFileId(String fileId) {
|
||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||
try {
|
||||
FileRequest fileRequest = new FileRequest();
|
||||
fileRequest.setFolder(systemTempDir + "/" + fileId);
|
||||
List<String> folderFileNames = defaultRepository.getFolderFileNames(fileRequest);
|
||||
if (CollectionUtils.isEmpty(folderFileNames)) {
|
||||
return null;
|
||||
}
|
||||
String[] pathSplit = folderFileNames.get(0).split("/");
|
||||
return pathSplit[pathSplit.length - 1];
|
||||
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传用例管理相关的资源文件
|
||||
*
|
||||
* @param folder 用例管理文件路径
|
||||
* @param addFileMap key:fileId value:fileName
|
||||
*/
|
||||
public void uploadFileResource(String folder, Map<String, String> addFileMap, String projectId, String caseId) {
|
||||
if (MapUtils.isEmpty(addFileMap)) {
|
||||
return;
|
||||
}
|
||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
||||
for (String fileId : addFileMap.keySet()) {
|
||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||
try {
|
||||
String fileName = addFileMap.get(fileId);
|
||||
if (StringUtils.isEmpty(fileName)) {
|
||||
continue;
|
||||
}
|
||||
// 按ID建文件夹,避免文件名重复
|
||||
FileCopyRequest fileCopyRequest = new FileCopyRequest();
|
||||
fileCopyRequest.setCopyFolder(systemTempDir + "/" + fileId);
|
||||
fileCopyRequest.setCopyfileName(fileName);
|
||||
fileCopyRequest.setFileName(fileName);
|
||||
fileCopyRequest.setFolder(folder + "/" + fileId);
|
||||
// 将文件从临时目录复制到资源目录
|
||||
defaultRepository.copyFile(fileCopyRequest);
|
||||
|
||||
String fileType = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1);
|
||||
if (TempFileUtils.isImage(fileType)) {
|
||||
//图片文件自动生成预览图
|
||||
byte[] file = defaultRepository.getFile(fileCopyRequest);
|
||||
byte[] previewImg = TempFileUtils.compressPic(file);
|
||||
fileCopyRequest.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(projectId,caseId));
|
||||
fileCopyRequest.setStorage(StorageType.MINIO.toString());
|
||||
fileService.upload(previewImg, fileCopyRequest);
|
||||
}
|
||||
// 删除临时文件
|
||||
fileCopyRequest.setFolder(systemTempDir + "/" + fileId);
|
||||
fileCopyRequest.setFileName(fileName);
|
||||
defaultRepository.delete(fileCopyRequest);
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
throw new MSException(Translator.get("file_upload_fail"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.functional.service;
|
||||
|
||||
|
||||
import io.metersphere.functional.constants.CaseFileSourceType;
|
||||
import io.metersphere.functional.domain.FunctionalCase;
|
||||
import io.metersphere.functional.domain.FunctionalCaseComment;
|
||||
import io.metersphere.functional.domain.FunctionalCaseCommentExample;
|
||||
|
@ -56,6 +57,11 @@ public class FunctionalCaseCommentService {
|
|||
@Resource
|
||||
private BaseUserMapper baseUserMapper;
|
||||
|
||||
@Resource
|
||||
private FunctionalCaseAttachmentService functionalCaseAttachmentService;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 新增评论
|
||||
*
|
||||
|
@ -112,6 +118,8 @@ public class FunctionalCaseCommentService {
|
|||
}
|
||||
functionalCaseCommentMapper.insert(functionalCaseComment);
|
||||
FunctionalCaseDTO functionalCaseDTO = functionalCaseNoticeService.getFunctionalCaseDTO(functionalCaseCommentRequest);
|
||||
//保存文件
|
||||
functionalCaseAttachmentService.uploadMinioFile(functionalCaseCommentRequest.getCaseId(),functionalCaseCommentRequest.getProjectId(),functionalCaseCommentRequest.getUploadFileIds(), userId, CaseFileSourceType.CASE_COMMENT.toString());
|
||||
//发送@ 通知人
|
||||
sendNotice(functionalCaseCommentRequest, userId, functionalCaseDTO);
|
||||
//发送系统设置的评论通知
|
||||
|
@ -135,6 +143,8 @@ public class FunctionalCaseCommentService {
|
|||
sendNotice(functionalCaseCommentRequest, userId, functionalCaseDTOReply);
|
||||
functionalCaseCommentRequest.setEvent(NoticeConstants.Event.AT);
|
||||
FunctionalCaseDTO functionalCaseDTO = functionalCaseNoticeService.getFunctionalCaseDTO(functionalCaseCommentRequest);
|
||||
//保存文件
|
||||
functionalCaseAttachmentService.uploadMinioFile(functionalCaseCommentRequest.getCaseId(),functionalCaseCommentRequest.getProjectId(),functionalCaseCommentRequest.getUploadFileIds(), userId, CaseFileSourceType.CASE_COMMENT.toString());
|
||||
//发通知
|
||||
sendNotice(functionalCaseCommentRequest, userId, functionalCaseDTO);
|
||||
return functionalCaseComment;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package io.metersphere.functional.controller;
|
||||
|
||||
import io.metersphere.functional.dto.FunctionalCaseAttachmentDTO;
|
||||
import io.metersphere.functional.constants.CaseFileSourceType;
|
||||
import io.metersphere.functional.domain.FunctionalCaseAttachment;
|
||||
import io.metersphere.functional.domain.FunctionalCaseAttachmentExample;
|
||||
import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper;
|
||||
import io.metersphere.functional.request.AttachmentTransferRequest;
|
||||
import io.metersphere.functional.request.FunctionalCaseAssociationFileRequest;
|
||||
import io.metersphere.functional.request.FunctionalCaseDeleteFileRequest;
|
||||
|
@ -19,6 +22,7 @@ import io.metersphere.sdk.util.JSON;
|
|||
import io.metersphere.system.base.BaseTest;
|
||||
import io.metersphere.system.controller.handler.ResultHolder;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
@ -28,11 +32,10 @@ import org.springframework.test.context.jdbc.Sql;
|
|||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
@ -52,6 +55,9 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
|
|||
@Resource
|
||||
private FunctionalCaseAttachmentService functionalCaseAttachmentService;
|
||||
|
||||
@Resource
|
||||
private FunctionalCaseAttachmentMapper functionalCaseAttachmentMapper;
|
||||
|
||||
public static final String ATTACHMENT_PAGE_URL = "/attachment/page";
|
||||
public static final String ATTACHMENT_PREVIEW_URL = "/attachment/preview";
|
||||
public static final String ATTACHMENT_DOWNLOAD_URL = "/attachment/download";
|
||||
|
@ -61,6 +67,7 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
|
|||
public static final String UPLOAD_FILE_URL = "/attachment/upload/file";
|
||||
public static final String DELETE_FILE_URL = "/attachment/delete/file";
|
||||
public static final String OPTIONS_URL = "/attachment/options/";
|
||||
public static final String UPLOAD_TEMP = "/attachment/upload/temp/file";
|
||||
|
||||
|
||||
@Test
|
||||
|
@ -210,7 +217,6 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
|
|||
@Order(8)
|
||||
public void testDeleteFile() throws Exception {
|
||||
FunctionalCaseDeleteFileRequest request = new FunctionalCaseDeleteFileRequest();
|
||||
FunctionalCaseAttachmentDTO attachmentDTO = new FunctionalCaseAttachmentDTO();
|
||||
//覆盖率
|
||||
request.setCaseId("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
|
||||
request.setProjectId("WX_TEST_PROJECT_ID");
|
||||
|
@ -218,8 +224,8 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
|
|||
request.setLocal(false);
|
||||
this.requestPost(DELETE_FILE_URL, request);
|
||||
|
||||
attachmentDTO.setId("TEST_ATTACHMENT_FILE_ID");
|
||||
attachmentDTO.setLocal(true);
|
||||
request.setId("TEST_ATTACHMENT_FILE_ID");
|
||||
request.setLocal(true);
|
||||
this.requestPost(DELETE_FILE_URL, request);
|
||||
}
|
||||
|
||||
|
@ -231,4 +237,59 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
|
|||
this.requestGet(OPTIONS_URL + DEFAULT_PROJECT_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(10)
|
||||
public void testUploadTemp() throws Exception {
|
||||
//覆盖controller方法
|
||||
MockMultipartFile file = getMockMultipartFile();
|
||||
String fileId = doUploadTempFile(file);
|
||||
Assertions.assertTrue(StringUtils.isNotBlank(fileId));
|
||||
file = getNoNameMockMultipartFile();
|
||||
doUploadTempFileFalse(file);
|
||||
functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1","WX_TEST_PROJECT_ID", List.of(fileId),"admin", CaseFileSourceType.CASE_COMMENT.toString());
|
||||
FunctionalCaseAttachmentExample functionalCaseAttachmentExample = new FunctionalCaseAttachmentExample();
|
||||
functionalCaseAttachmentExample.createCriteria().andCaseIdEqualTo("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1").andFileIdEqualTo(fileId).andFileSourceEqualTo(CaseFileSourceType.CASE_COMMENT.toString());
|
||||
List<FunctionalCaseAttachment> functionalCaseAttachments = functionalCaseAttachmentMapper.selectByExample(functionalCaseAttachmentExample);
|
||||
Assertions.assertTrue(CollectionUtils.isNotEmpty(functionalCaseAttachments));
|
||||
functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1","WX_TEST_PROJECT_ID",new ArrayList<>(),"admin", CaseFileSourceType.CASE_COMMENT.toString());
|
||||
String functionalCaseDir = DefaultRepositoryDir.getFunctionalCaseDir("WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
|
||||
functionalCaseAttachmentService.uploadFileResource(functionalCaseDir,new HashMap<>(),"WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
|
||||
Map<String, String> objectObjectHashMap = new HashMap<>();
|
||||
objectObjectHashMap.put(fileId,null);
|
||||
functionalCaseAttachmentService.uploadFileResource(functionalCaseDir,objectObjectHashMap,"WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
|
||||
|
||||
}
|
||||
|
||||
private static MockMultipartFile getMockMultipartFile() {
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file",
|
||||
"file_upload.JPG",
|
||||
MediaType.APPLICATION_OCTET_STREAM_VALUE,
|
||||
"Hello, World!".getBytes()
|
||||
);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
private static MockMultipartFile getNoNameMockMultipartFile() {
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file",
|
||||
null,
|
||||
MediaType.APPLICATION_OCTET_STREAM_VALUE,
|
||||
"Hello, World!".getBytes()
|
||||
);
|
||||
return file;
|
||||
}
|
||||
|
||||
private String doUploadTempFile(MockMultipartFile file) throws Exception {
|
||||
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP, file)
|
||||
.getResponse()
|
||||
.getContentAsString(), ResultHolder.class)
|
||||
.getData().toString();
|
||||
}
|
||||
|
||||
private void doUploadTempFileFalse(MockMultipartFile file) throws Exception {
|
||||
this.requestUploadFile(UPLOAD_TEMP, file).andExpect(status().is5xxServerError());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue