feat(测试跟踪): 用例及缺陷附件支持文件管理
This commit is contained in:
parent
d359bbf66f
commit
c0870f06e5
|
@ -27,6 +27,7 @@
|
|||
<xmlbeans.version>5.1.0</xmlbeans.version>
|
||||
<poi.version>5.1.0</poi.version>
|
||||
<jgit.version>6.2.0.202206071550-r</jgit.version>
|
||||
<commons-fileupload.version>1.3</commons-fileupload.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -152,6 +153,17 @@
|
|||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>${commons-fileupload.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
|
|
|
@ -12,5 +12,7 @@ public class AttachmentModuleRelation implements Serializable {
|
|||
|
||||
private String attachmentId;
|
||||
|
||||
private String fileMetadataRefId;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -313,6 +313,76 @@ public class AttachmentModuleRelationExample {
|
|||
addCriterion("attachment_id not between", value1, value2, "attachmentId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdIsNull() {
|
||||
addCriterion("file_metadata_ref_id is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdIsNotNull() {
|
||||
addCriterion("file_metadata_ref_id is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdEqualTo(String value) {
|
||||
addCriterion("file_metadata_ref_id =", value, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdNotEqualTo(String value) {
|
||||
addCriterion("file_metadata_ref_id <>", value, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdGreaterThan(String value) {
|
||||
addCriterion("file_metadata_ref_id >", value, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("file_metadata_ref_id >=", value, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdLessThan(String value) {
|
||||
addCriterion("file_metadata_ref_id <", value, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdLessThanOrEqualTo(String value) {
|
||||
addCriterion("file_metadata_ref_id <=", value, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdLike(String value) {
|
||||
addCriterion("file_metadata_ref_id like", value, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdNotLike(String value) {
|
||||
addCriterion("file_metadata_ref_id not like", value, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdIn(List<String> values) {
|
||||
addCriterion("file_metadata_ref_id in", values, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdNotIn(List<String> values) {
|
||||
addCriterion("file_metadata_ref_id not in", values, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdBetween(String value1, String value2) {
|
||||
addCriterion("file_metadata_ref_id between", value1, value2, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileMetadataRefIdNotBetween(String value1, String value2) {
|
||||
addCriterion("file_metadata_ref_id not between", value1, value2, "fileMetadataRefId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class FileAttachmentMetadata implements Serializable {
|
||||
private String id;
|
||||
|
@ -21,5 +22,9 @@ public class FileAttachmentMetadata implements Serializable {
|
|||
|
||||
private String filePath;
|
||||
|
||||
private Boolean isLocal;
|
||||
|
||||
private Boolean isRelatedDeleted;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
<result column="relation_id" jdbcType="VARCHAR" property="relationId" />
|
||||
<result column="relation_type" jdbcType="VARCHAR" property="relationType" />
|
||||
<result column="attachment_id" jdbcType="VARCHAR" property="attachmentId" />
|
||||
<result column="file_metadata_ref_id" jdbcType="VARCHAR" property="fileMetadataRefId" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
|
@ -65,7 +66,7 @@
|
|||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
relation_id, relation_type, attachment_id
|
||||
relation_id, relation_type, attachment_id, file_metadata_ref_id
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.AttachmentModuleRelationExample" resultMap="BaseResultMap">
|
||||
select
|
||||
|
@ -88,10 +89,10 @@
|
|||
</if>
|
||||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.AttachmentModuleRelation">
|
||||
insert into attachment_module_relation (relation_id, relation_type, attachment_id
|
||||
)
|
||||
values (#{relationId,jdbcType=VARCHAR}, #{relationType,jdbcType=VARCHAR}, #{attachmentId,jdbcType=VARCHAR}
|
||||
)
|
||||
insert into attachment_module_relation (relation_id, relation_type, attachment_id,
|
||||
file_metadata_ref_id)
|
||||
values (#{relationId,jdbcType=VARCHAR}, #{relationType,jdbcType=VARCHAR}, #{attachmentId,jdbcType=VARCHAR},
|
||||
#{fileMetadataRefId,jdbcType=VARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.AttachmentModuleRelation">
|
||||
insert into attachment_module_relation
|
||||
|
@ -105,6 +106,9 @@
|
|||
<if test="attachmentId != null">
|
||||
attachment_id,
|
||||
</if>
|
||||
<if test="fileMetadataRefId != null">
|
||||
file_metadata_ref_id,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="relationId != null">
|
||||
|
@ -116,6 +120,9 @@
|
|||
<if test="attachmentId != null">
|
||||
#{attachmentId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="fileMetadataRefId != null">
|
||||
#{fileMetadataRefId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.AttachmentModuleRelationExample" resultType="java.lang.Long">
|
||||
|
@ -136,6 +143,9 @@
|
|||
<if test="record.attachmentId != null">
|
||||
attachment_id = #{record.attachmentId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.fileMetadataRefId != null">
|
||||
file_metadata_ref_id = #{record.fileMetadataRefId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
|
@ -145,7 +155,8 @@
|
|||
update attachment_module_relation
|
||||
set relation_id = #{record.relationId,jdbcType=VARCHAR},
|
||||
relation_type = #{record.relationType,jdbcType=VARCHAR},
|
||||
attachment_id = #{record.attachmentId,jdbcType=VARCHAR}
|
||||
attachment_id = #{record.attachmentId,jdbcType=VARCHAR},
|
||||
file_metadata_ref_id = #{record.fileMetadataRefId,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<mapper namespace="io.metersphere.base.mapper.ext.ExtAttachmentModuleRelationMapper">
|
||||
<insert id="batchInsert" parameterType="java.util.List">
|
||||
INSERT INTO
|
||||
attachment_module_relation (relation_id, relation_type, attachment_id)
|
||||
attachment_module_relation (relation_id, relation_type, attachment_id, file_metadata_ref_id)
|
||||
VALUES
|
||||
<foreach collection="attachmentModuleRelations" item="relation" separator="," >
|
||||
(#{relation.relationId}, #{relation.relationType}, #{relation.attachmentId})
|
||||
(#{relation.relationId}, #{relation.relationType}, #{relation.attachmentId}, #{relation.fileMetadataRefId})
|
||||
</foreach>
|
||||
</insert>
|
||||
</mapper>
|
|
@ -1,5 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum FileAssociationType {
|
||||
API, CASE, SCENARIO, UI, ENVIRONMENT
|
||||
API, CASE, SCENARIO, UI, ENVIRONMENT, TEST_CASE, ISSUE
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package io.metersphere.metadata.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AttachmentDumpRequest extends DumpFileRequest{
|
||||
private String attachmentId;
|
||||
}
|
|
@ -8,6 +8,7 @@ import io.metersphere.commons.exception.MSException;
|
|||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.performance.request.QueryProjectFileRequest;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import io.metersphere.xmind.utils.FileUtil;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -50,6 +51,12 @@ public class FileService {
|
|||
return FileUtils.fileToByte(attachmentFile);
|
||||
}
|
||||
|
||||
public MultipartFile getAttachmentMultipartFile(String id) {
|
||||
FileAttachmentMetadata fileAttachmentMetadata = fileAttachmentMetadataMapper.selectByPrimaryKey(id);
|
||||
File attachmentFile = new File(fileAttachmentMetadata.getFilePath() + "/" + fileAttachmentMetadata.getName());
|
||||
return FileUtil.fileToMultipartFile(attachmentFile);
|
||||
}
|
||||
|
||||
public FileContent getFileContent(String fileId) {
|
||||
return fileContentMapper.selectByPrimaryKey(fileId);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package io.metersphere.track.controller;
|
||||
|
||||
import io.metersphere.base.domain.FileAttachmentMetadata;
|
||||
import io.metersphere.metadata.service.FileMetadataService;
|
||||
import io.metersphere.metadata.vo.AttachmentDumpRequest;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.track.request.attachment.AttachmentRequest;
|
||||
import io.metersphere.track.request.testplan.FileOperationRequest;
|
||||
|
@ -14,6 +16,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
import javax.annotation.Resource;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -27,15 +30,23 @@ public class AttachmentController {
|
|||
private FileService fileService;
|
||||
@Resource
|
||||
private AttachmentService attachmentService;
|
||||
@Resource
|
||||
private FileMetadataService fileMetadataService;
|
||||
|
||||
@PostMapping(value = "/upload", consumes = {"multipart/form-data"})
|
||||
public void uploadAttachment(@RequestPart("request") AttachmentRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
||||
attachmentService.uploadAttachment(request, file);
|
||||
}
|
||||
|
||||
@GetMapping("/preview/{fileId}")
|
||||
public ResponseEntity<byte[]> previewAttachment(@PathVariable String fileId) {
|
||||
byte[] bytes = fileService.getAttachmentBytes(fileId);
|
||||
@GetMapping("/preview/{fileId}/{isLocal}")
|
||||
public ResponseEntity<byte[]> previewAttachment(@PathVariable String fileId, @PathVariable Boolean isLocal) {
|
||||
byte[] bytes;
|
||||
if (isLocal) {
|
||||
bytes = fileService.getAttachmentBytes(fileId);
|
||||
} else {
|
||||
String refId = attachmentService.getRefIdByAttachmentId(fileId);
|
||||
bytes = fileMetadataService.loadFileAsBytes(refId);
|
||||
}
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileId + "\"")
|
||||
|
@ -44,7 +55,13 @@ public class AttachmentController {
|
|||
|
||||
@PostMapping("/download")
|
||||
public ResponseEntity<byte[]> downloadAttachment(@RequestBody FileOperationRequest fileOperationRequest) {
|
||||
byte[] bytes = fileService.getAttachmentBytes(fileOperationRequest.getId());
|
||||
byte[] bytes;
|
||||
if (fileOperationRequest.getIsLocal()) {
|
||||
bytes = fileService.getAttachmentBytes(fileOperationRequest.getId());
|
||||
} else {
|
||||
String refId = attachmentService.getRefIdByAttachmentId(fileOperationRequest.getId());
|
||||
bytes = fileMetadataService.loadFileAsBytes(refId);
|
||||
}
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileOperationRequest.getName(), StandardCharsets.UTF_8) + "\"")
|
||||
|
@ -61,4 +78,22 @@ public class AttachmentController {
|
|||
public List<FileAttachmentMetadata> listMetadata(@RequestBody AttachmentRequest request) {
|
||||
return attachmentService.listMetadata(request);
|
||||
}
|
||||
|
||||
@PostMapping("/metadata/relate")
|
||||
public void relate(@RequestBody AttachmentRequest request) {
|
||||
attachmentService.relate(request);
|
||||
}
|
||||
|
||||
@PostMapping("/metadata/unrelated")
|
||||
public void unrelated(@RequestBody AttachmentRequest request) {
|
||||
attachmentService.unrelated(request);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/metadata/dump")
|
||||
public void dumpFile(@RequestBody AttachmentDumpRequest request) {
|
||||
List<MultipartFile> files = new ArrayList<>();
|
||||
MultipartFile file = fileService.getAttachmentMultipartFile(request.getAttachmentId());
|
||||
files.add(file);
|
||||
fileMetadataService.dumpFile(request, files);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.metersphere.track.issue;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.base.domain.*;
|
||||
|
|
|
@ -2,6 +2,8 @@ package io.metersphere.track.request.attachment;
|
|||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author songcc
|
||||
|
@ -14,4 +16,6 @@ public class AttachmentRequest {
|
|||
private String belongId;
|
||||
|
||||
private String copyBelongId;
|
||||
|
||||
private List<String> metadataRefIds;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@ public class EditTestCaseRequest extends TestCaseWithBLOBs {
|
|||
private String copyCaseId;
|
||||
// 是否处理附件文件
|
||||
private boolean handleAttachment = true;
|
||||
// 关联文件管理引用ID
|
||||
private List<String> relateFileMetaIds = new ArrayList<>();
|
||||
// 取消关联文件应用ID
|
||||
private List<String> unRelateFileMetaIds = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 创建新版本时 是否连带复制其他信息的配置类
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.metersphere.track.dto.PlatformStatusDTO;
|
|||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
|
@ -53,4 +54,8 @@ public class IssuesUpdateRequest extends IssuesWithBLOBs {
|
|||
* 复制缺陷时原始缺陷ID
|
||||
*/
|
||||
private String copyIssueId;
|
||||
// 关联文件管理引用ID
|
||||
private List<String> relateFileMetaIds = new ArrayList<>();
|
||||
// 取消关联文件应用ID
|
||||
private List<String> unRelateFileMetaIds = new ArrayList<>();
|
||||
}
|
||||
|
|
|
@ -8,4 +8,5 @@ import lombok.Setter;
|
|||
public class FileOperationRequest {
|
||||
private String id;
|
||||
private String name;
|
||||
private Boolean isLocal;
|
||||
}
|
||||
|
|
|
@ -5,24 +5,31 @@ import io.metersphere.base.mapper.*;
|
|||
import io.metersphere.base.mapper.ext.ExtAttachmentModuleRelationMapper;
|
||||
import io.metersphere.commons.constants.AttachmentSyncType;
|
||||
import io.metersphere.commons.constants.AttachmentType;
|
||||
import io.metersphere.commons.constants.FileAssociationType;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.FileUtils;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.metadata.service.FileMetadataService;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.track.issue.IssueFactory;
|
||||
import io.metersphere.track.request.attachment.AttachmentRequest;
|
||||
import io.metersphere.track.request.testcase.IssuesRequest;
|
||||
import io.metersphere.track.request.testcase.IssuesUpdateRequest;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.SqlSessionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
@ -48,6 +55,14 @@ public class AttachmentService {
|
|||
private AttachmentModuleRelationMapper attachmentModuleRelationMapper;
|
||||
@Resource
|
||||
private ExtAttachmentModuleRelationMapper extAttachmentModuleRelationMapper;
|
||||
@Resource
|
||||
private FileMetadataMapper fileMetadataMapper;
|
||||
@Resource
|
||||
private FileAssociationMapper fileAssociationMapper;
|
||||
@Resource
|
||||
private FileMetadataService fileMetadataService;
|
||||
@Resource
|
||||
SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
public void uploadAttachment(AttachmentRequest request, MultipartFile file) {
|
||||
// 附件上传的前置校验
|
||||
|
@ -129,25 +144,162 @@ public class AttachmentService {
|
|||
example.createCriteria().andRelationIdEqualTo(request.getCopyBelongId()).andRelationTypeEqualTo(request.getBelongType());
|
||||
List<AttachmentModuleRelation> attachmentModuleRelations = attachmentModuleRelationMapper.selectByExample(example);
|
||||
if (CollectionUtils.isNotEmpty(attachmentModuleRelations)) {
|
||||
attachmentModuleRelations.forEach(attachmentModuleRelation -> {
|
||||
FileAttachmentMetadata fileAttachmentMetadata = fileService.copyAttachment(attachmentModuleRelation.getAttachmentId(), request.getBelongType(), request.getBelongId());
|
||||
// 本地附件
|
||||
List<String> localAttachments = attachmentModuleRelations.stream()
|
||||
.filter(relation -> StringUtils.isEmpty(relation.getFileMetadataRefId()))
|
||||
.map(AttachmentModuleRelation::getAttachmentId)
|
||||
.filter(StringUtils::isNotEmpty).collect(Collectors.toList());
|
||||
localAttachments.forEach(localAttachmentId -> {
|
||||
FileAttachmentMetadata fileAttachmentMetadata = fileService.copyAttachment(localAttachmentId, request.getBelongType(), request.getBelongId());
|
||||
AttachmentModuleRelation record = new AttachmentModuleRelation();
|
||||
record.setRelationId(request.getBelongId());
|
||||
record.setRelationType(request.getBelongType());
|
||||
record.setAttachmentId(fileAttachmentMetadata.getId());
|
||||
attachmentModuleRelationMapper.insert(record);
|
||||
});
|
||||
// 文件管理关联附件
|
||||
List<AttachmentModuleRelation> refAttachments = attachmentModuleRelations.stream()
|
||||
.filter(relation -> StringUtils.isNotEmpty(relation.getFileMetadataRefId())).collect(Collectors.toList());
|
||||
refAttachments.forEach(refAttachment -> {
|
||||
refAttachment.setRelationId(request.getBelongId());
|
||||
attachmentModuleRelationMapper.insert(refAttachment);
|
||||
// 缺陷类型的附件, 关联时需单独同步第三方平台
|
||||
if (AttachmentType.ISSUE.type().equals(request.getBelongType())) {
|
||||
String metadataRefId = getRefIdByAttachmentId(refAttachment.getAttachmentId());
|
||||
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(metadataRefId);
|
||||
IssuesWithBLOBs issues = issuesMapper.selectByPrimaryKey(request.getBelongId());
|
||||
IssuesUpdateRequest updateRequest = new IssuesUpdateRequest();
|
||||
updateRequest.setPlatformId(issues.getPlatformId());
|
||||
File refFile = downloadMetadataFile(metadataRefId, fileMetadata.getName());
|
||||
IssuesRequest issuesRequest = new IssuesRequest();
|
||||
issuesRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
Objects.requireNonNull(IssueFactory.createPlatform(issues.getPlatform(), issuesRequest))
|
||||
.syncIssuesAttachment(updateRequest, refFile, AttachmentSyncType.UPLOAD);
|
||||
FileUtils.deleteFile(FileUtils.ATTACHMENT_TMP_DIR + File.separator + fileMetadata.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public List<FileAttachmentMetadata> listMetadata(AttachmentRequest request) {
|
||||
List<String> attachmentIds = getAttachmentIdsByParam(request);
|
||||
if (CollectionUtils.isEmpty(attachmentIds)) {
|
||||
return new ArrayList<>();
|
||||
List<FileAttachmentMetadata> attachments = new ArrayList<FileAttachmentMetadata>();
|
||||
AttachmentModuleRelationExample example = new AttachmentModuleRelationExample();
|
||||
example.createCriteria().andRelationIdEqualTo(request.getBelongId()).andRelationTypeEqualTo(request.getBelongType());
|
||||
List<AttachmentModuleRelation> attachmentModuleRelations = attachmentModuleRelationMapper.selectByExample(example);
|
||||
Map<String, String> relationMap = attachmentModuleRelations.stream()
|
||||
.collect(Collectors.toMap(AttachmentModuleRelation::getAttachmentId,
|
||||
relation -> relation.getFileMetadataRefId() == null ? "" : relation.getFileMetadataRefId()));
|
||||
List<String> attachmentIds = attachmentModuleRelations.stream().map(AttachmentModuleRelation::getAttachmentId)
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(attachmentIds)) {
|
||||
FileAttachmentMetadataExample fileExample = new FileAttachmentMetadataExample();
|
||||
fileExample.createCriteria().andIdIn(attachmentIds);
|
||||
List<FileAttachmentMetadata> fileAttachmentMetadata = fileAttachmentMetadataMapper.selectByExample(fileExample);
|
||||
fileAttachmentMetadata.forEach(file -> {
|
||||
String fileRefId = relationMap.get(file.getId());
|
||||
if (StringUtils.isEmpty(fileRefId)) {
|
||||
file.setIsLocal(Boolean.TRUE);
|
||||
file.setIsRelatedDeleted(Boolean.FALSE);
|
||||
} else {
|
||||
file.setIsLocal(Boolean.FALSE);
|
||||
FileAssociation fileAssociation = fileAssociationMapper.selectByPrimaryKey(fileRefId);
|
||||
if (fileAssociation != null) {
|
||||
// 关联文件信息同步
|
||||
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(fileAssociation.getFileMetadataId());
|
||||
file.setIsRelatedDeleted(Boolean.FALSE);
|
||||
file.setName(fileMetadata.getName());
|
||||
file.setSize(fileMetadata.getSize());
|
||||
file.setCreator(fileMetadata.getCreateUser());
|
||||
file.setCreateTime(fileMetadata.getCreateTime());
|
||||
} else {
|
||||
file.setIsRelatedDeleted(Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
});
|
||||
attachments.addAll(fileAttachmentMetadata);
|
||||
}
|
||||
FileAttachmentMetadataExample fileExample = new FileAttachmentMetadataExample();
|
||||
fileExample.createCriteria().andIdIn(attachmentIds);
|
||||
return fileAttachmentMetadataMapper.selectByExample(fileExample);
|
||||
return attachments;
|
||||
}
|
||||
|
||||
public void relate(AttachmentRequest request) {
|
||||
// 批量关联文件管理
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
FileAssociationMapper associationBatchMapper = sqlSession.getMapper(FileAssociationMapper.class);
|
||||
AttachmentModuleRelationMapper attachmentModuleRelationBatchMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class);
|
||||
FileAttachmentMetadataMapper fileAttachmentMetadataBatchMapper = sqlSession.getMapper(FileAttachmentMetadataMapper.class);
|
||||
if (CollectionUtils.isNotEmpty(request.getMetadataRefIds())) {
|
||||
request.getMetadataRefIds().forEach(metadataRefId -> {
|
||||
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(metadataRefId);
|
||||
FileAssociation fileAssociation = new FileAssociation();
|
||||
fileAssociation.setId(UUID.randomUUID().toString());
|
||||
fileAssociation.setFileMetadataId(metadataRefId);
|
||||
fileAssociation.setFileType(fileMetadata.getType());
|
||||
if (AttachmentType.TEST_CASE.type().equals(request.getBelongType())) {
|
||||
fileAssociation.setType(FileAssociationType.TEST_CASE.name());
|
||||
} else {
|
||||
fileAssociation.setType(FileAssociationType.ISSUE.name());
|
||||
}
|
||||
fileAssociation.setProjectId(fileMetadata.getProjectId());
|
||||
fileAssociation.setSourceItemId(metadataRefId);
|
||||
fileAssociation.setSourceId(request.getBelongId());
|
||||
associationBatchMapper.insert(fileAssociation);
|
||||
AttachmentModuleRelation record = new AttachmentModuleRelation();
|
||||
record.setRelationId(request.getBelongId());
|
||||
record.setRelationType(request.getBelongType());
|
||||
record.setFileMetadataRefId(fileAssociation.getId());
|
||||
record.setAttachmentId(UUID.randomUUID().toString());
|
||||
attachmentModuleRelationBatchMapper.insert(record);
|
||||
FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
|
||||
BeanUtils.copyBean(fileAttachmentMetadata, fileMetadata);
|
||||
fileAttachmentMetadata.setId(record.getAttachmentId());
|
||||
fileAttachmentMetadata.setCreator(fileMetadata.getCreateUser());
|
||||
fileAttachmentMetadata.setFilePath(fileMetadata.getPath());
|
||||
fileAttachmentMetadataBatchMapper.insert(fileAttachmentMetadata);
|
||||
// 缺陷类型的附件, 关联时需单独同步第三方平台
|
||||
if (AttachmentType.ISSUE.type().equals(request.getBelongType())) {
|
||||
IssuesWithBLOBs issues = issuesMapper.selectByPrimaryKey(request.getBelongId());
|
||||
IssuesUpdateRequest updateRequest = new IssuesUpdateRequest();
|
||||
updateRequest.setPlatformId(issues.getPlatformId());
|
||||
File refFile = downloadMetadataFile(metadataRefId, fileMetadata.getName());
|
||||
IssuesRequest issuesRequest = new IssuesRequest();
|
||||
issuesRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
Objects.requireNonNull(IssueFactory.createPlatform(issues.getPlatform(), issuesRequest))
|
||||
.syncIssuesAttachment(updateRequest, refFile, AttachmentSyncType.UPLOAD);
|
||||
FileUtils.deleteFile(FileUtils.ATTACHMENT_TMP_DIR + File.separator + fileMetadata.getName());
|
||||
}
|
||||
});
|
||||
sqlSession.flushStatements();
|
||||
if (sqlSession != null && sqlSessionFactory != null) {
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void unrelated(AttachmentRequest request) {
|
||||
// 缺陷类型的附件, 取消关联时同步第三方平台
|
||||
if (AttachmentType.ISSUE.type().equals(request.getBelongType())) {
|
||||
IssuesWithBLOBs issues = issuesMapper.selectByPrimaryKey(request.getBelongId());
|
||||
request.getMetadataRefIds().forEach(metadataRefId -> {
|
||||
FileAttachmentMetadata fileAttachmentMetadata = fileAttachmentMetadataMapper.selectByPrimaryKey(metadataRefId);
|
||||
IssuesUpdateRequest updateRequest = new IssuesUpdateRequest();
|
||||
updateRequest.setPlatformId(issues.getPlatformId());
|
||||
File deleteFile = new File(FileUtils.ATTACHMENT_TMP_DIR + File.separator + fileAttachmentMetadata.getName());
|
||||
IssuesRequest issuesRequest = new IssuesRequest();
|
||||
issuesRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
Objects.requireNonNull(IssueFactory.createPlatform(issues.getPlatform(), issuesRequest))
|
||||
.syncIssuesAttachment(updateRequest, deleteFile, AttachmentSyncType.DELETE);
|
||||
});
|
||||
}
|
||||
AttachmentModuleRelationExample example = new AttachmentModuleRelationExample();
|
||||
example.createCriteria().andRelationIdEqualTo(request.getBelongId())
|
||||
.andRelationTypeEqualTo(request.getBelongType())
|
||||
.andAttachmentIdIn(request.getMetadataRefIds());
|
||||
FileAttachmentMetadataExample exampleAttachment = new FileAttachmentMetadataExample();
|
||||
exampleAttachment.createCriteria().andIdIn(request.getMetadataRefIds());
|
||||
fileAttachmentMetadataMapper.deleteByExample(exampleAttachment);
|
||||
attachmentModuleRelationMapper.deleteByExample(example);
|
||||
}
|
||||
|
||||
public List<String> getAttachmentIdsByParam(AttachmentRequest request) {
|
||||
|
@ -159,6 +311,15 @@ public class AttachmentService {
|
|||
return attachmentIds;
|
||||
}
|
||||
|
||||
public String getRefIdByAttachmentId(String attachmentId) {
|
||||
AttachmentModuleRelationExample example = new AttachmentModuleRelationExample();
|
||||
example.createCriteria().andAttachmentIdEqualTo(attachmentId);
|
||||
List<AttachmentModuleRelation> relations = attachmentModuleRelationMapper.selectByExample(example);
|
||||
String associationId = relations.get(0).getFileMetadataRefId();
|
||||
FileAssociation fileAssociation = fileAssociationMapper.selectByPrimaryKey(associationId);
|
||||
return fileAssociation.getFileMetadataId();
|
||||
}
|
||||
|
||||
public void initAttachment() {
|
||||
List<AttachmentModuleRelation> attachmentModuleRelations = new ArrayList<>();
|
||||
List<IssueFile> issueFiles = issueFileMapper.selectByExample(new IssueFileExample());
|
||||
|
@ -183,4 +344,9 @@ public class AttachmentService {
|
|||
}
|
||||
extAttachmentModuleRelationMapper.batchInsert(attachmentModuleRelations);
|
||||
}
|
||||
|
||||
public File downloadMetadataFile(String fileMetadataRefId, String filename) {
|
||||
byte[] refFileBytes = fileMetadataService.loadFileAsBytes(fileMetadataRefId);
|
||||
return FileUtils.byteToFile(refFileBytes, FileUtils.ATTACHMENT_TMP_DIR, filename);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@ import io.metersphere.track.request.testcase.IssuesUpdateRequest;
|
|||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.SqlSessionUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -44,6 +48,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
|
@ -91,11 +96,15 @@ public class IssuesService {
|
|||
@Resource
|
||||
IssueFileMapper issueFileMapper;
|
||||
@Resource
|
||||
SqlSessionFactory sqlSessionFactory;
|
||||
@Resource
|
||||
private AttachmentService attachmentService;
|
||||
@Resource
|
||||
private CustomFieldService customFieldService;
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private FileMetadataMapper fileMetadataMapper;
|
||||
|
||||
private static final String SYNC_THIRD_PARTY_ISSUES_KEY = "ISSUE:SYNC";
|
||||
|
||||
|
@ -129,9 +138,9 @@ public class IssuesService {
|
|||
attachmentRequest.setBelongType(AttachmentType.ISSUE.type());
|
||||
attachmentService.copyAttachment(attachmentRequest);
|
||||
} else {
|
||||
final String issueId = issues.getId();
|
||||
// 新增, 需保存并同步所有待上传的附件
|
||||
if (CollectionUtils.isNotEmpty(files)) {
|
||||
final String issueId = issues.getId();
|
||||
files.forEach(file -> {
|
||||
AttachmentRequest attachmentRequest = new AttachmentRequest();
|
||||
attachmentRequest.setBelongId(issueId);
|
||||
|
@ -139,6 +148,48 @@ public class IssuesService {
|
|||
attachmentService.uploadAttachment(attachmentRequest, file);
|
||||
});
|
||||
}
|
||||
// 处理待关联的文件附件, 生成关联记录, 并同步至第三方平台
|
||||
if (CollectionUtils.isNotEmpty(issuesRequest.getRelateFileMetaIds())) {
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
FileAssociationMapper associationBatchMapper = sqlSession.getMapper(FileAssociationMapper.class);
|
||||
AttachmentModuleRelationMapper attachmentModuleRelationBatchMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class);
|
||||
FileAttachmentMetadataMapper fileAttachmentMetadataBatchMapper = sqlSession.getMapper(FileAttachmentMetadataMapper.class);
|
||||
issuesRequest.getRelateFileMetaIds().forEach(filemetaId -> {
|
||||
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(filemetaId);
|
||||
FileAssociation fileAssociation = new FileAssociation();
|
||||
fileAssociation.setId(UUID.randomUUID().toString());
|
||||
fileAssociation.setFileMetadataId(filemetaId);
|
||||
fileAssociation.setFileType(fileMetadata.getType());
|
||||
fileAssociation.setType(FileAssociationType.ISSUE.name());
|
||||
fileAssociation.setProjectId(fileMetadata.getProjectId());
|
||||
fileAssociation.setSourceItemId(filemetaId);
|
||||
fileAssociation.setSourceId(issueId);
|
||||
associationBatchMapper.insert(fileAssociation);
|
||||
AttachmentModuleRelation relation = new AttachmentModuleRelation();
|
||||
relation.setRelationId(issueId);
|
||||
relation.setRelationType(AttachmentType.ISSUE.type());
|
||||
relation.setFileMetadataRefId(fileAssociation.getId());
|
||||
relation.setAttachmentId(UUID.randomUUID().toString());
|
||||
attachmentModuleRelationBatchMapper.insert(relation);
|
||||
FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
|
||||
BeanUtils.copyBean(fileAttachmentMetadata, fileMetadata);
|
||||
fileAttachmentMetadata.setId(relation.getAttachmentId());
|
||||
fileAttachmentMetadata.setCreator(fileMetadata.getCreateUser());
|
||||
fileAttachmentMetadata.setFilePath(fileMetadata.getPath());
|
||||
fileAttachmentMetadataBatchMapper.insert(fileAttachmentMetadata);
|
||||
// 下载文件管理文件, 同步到第三方平台
|
||||
File refFile = attachmentService.downloadMetadataFile(filemetaId, fileMetadata.getName());
|
||||
IssuesRequest addIssueRequest = new IssuesRequest();
|
||||
addIssueRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
Objects.requireNonNull(IssueFactory.createPlatform(issuesRequest.getPlatform(), addIssueRequest))
|
||||
.syncIssuesAttachment(issuesRequest, refFile, AttachmentSyncType.UPLOAD);
|
||||
FileUtils.deleteFile(FileUtils.ATTACHMENT_TMP_DIR + File.separator + fileMetadata.getName());
|
||||
});
|
||||
sqlSession.flushStatements();
|
||||
if (sqlSession != null && sqlSessionFactory != null) {
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getIssue(issues.getId());
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import io.metersphere.api.service.ApiTestCaseService;
|
|||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.domain.ext.CustomFieldResource;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtAttachmentModuleRelationMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtProjectVersionMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
||||
|
@ -135,6 +136,8 @@ public class TestCaseService {
|
|||
@Resource
|
||||
AttachmentModuleRelationMapper attachmentModuleRelationMapper;
|
||||
@Resource
|
||||
ExtAttachmentModuleRelationMapper extAttachmentModuleRelationMapper;
|
||||
@Resource
|
||||
private LoadTestMapper loadTestMapper;
|
||||
@Resource
|
||||
private ApiScenarioMapper apiScenarioMapper;
|
||||
|
@ -2101,6 +2104,41 @@ public class TestCaseService {
|
|||
attachmentService.uploadAttachment(attachmentRequest, file);
|
||||
});
|
||||
}
|
||||
// 同步待关联的文件附件, 生成关联记录
|
||||
if (CollectionUtils.isNotEmpty(request.getRelateFileMetaIds())) {
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
FileAssociationMapper associationBatchMapper = sqlSession.getMapper(FileAssociationMapper.class);
|
||||
AttachmentModuleRelationMapper attachmentModuleRelationBatchMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class);
|
||||
FileAttachmentMetadataMapper fileAttachmentMetadataBatchMapper = sqlSession.getMapper(FileAttachmentMetadataMapper.class);
|
||||
request.getRelateFileMetaIds().forEach(filemetaId -> {
|
||||
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(filemetaId);
|
||||
FileAssociation fileAssociation = new FileAssociation();
|
||||
fileAssociation.setId(UUID.randomUUID().toString());
|
||||
fileAssociation.setFileMetadataId(filemetaId);
|
||||
fileAssociation.setFileType(fileMetadata.getType());
|
||||
fileAssociation.setType(FileAssociationType.TEST_CASE.name());
|
||||
fileAssociation.setProjectId(fileMetadata.getProjectId());
|
||||
fileAssociation.setSourceItemId(filemetaId);
|
||||
fileAssociation.setSourceId(testCaseWithBLOBs.getId());
|
||||
associationBatchMapper.insert(fileAssociation);
|
||||
AttachmentModuleRelation record = new AttachmentModuleRelation();
|
||||
record.setRelationId(testCaseWithBLOBs.getId());
|
||||
record.setRelationType(AttachmentType.TEST_CASE.type());
|
||||
record.setFileMetadataRefId(fileAssociation.getId());
|
||||
record.setAttachmentId(UUID.randomUUID().toString());
|
||||
attachmentModuleRelationBatchMapper.insert(record);
|
||||
FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
|
||||
BeanUtils.copyBean(fileAttachmentMetadata, fileMetadata);
|
||||
fileAttachmentMetadata.setId(record.getAttachmentId());
|
||||
fileAttachmentMetadata.setCreator(fileMetadata.getCreateUser());
|
||||
fileAttachmentMetadata.setFilePath(fileMetadata.getPath());
|
||||
fileAttachmentMetadataBatchMapper.insert(fileAttachmentMetadata);
|
||||
});
|
||||
sqlSession.flushStatements();
|
||||
if (sqlSession != null && sqlSessionFactory != null) {
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
return testCaseWithBLOBs;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package io.metersphere.xmind.utils;
|
||||
|
||||
import io.metersphere.commons.utils.FileUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItem;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.commons.CommonsMultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* 工具类
|
||||
|
@ -47,6 +48,23 @@ public class FileUtil {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* File 转 MultipartFile
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
public static MultipartFile fileToMultipartFile(File file) {
|
||||
DiskFileItem item = new DiskFileItem("file", MediaType.MULTIPART_FORM_DATA_VALUE, true,
|
||||
file.getName(), (int)file.length(), file.getParentFile());
|
||||
try {
|
||||
OutputStream os = item.getOutputStream();
|
||||
os.write(FileUtils.fileToByte(file));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new CommonsMultipartFile(item);
|
||||
}
|
||||
|
||||
public static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
|
|
|
@ -151,3 +151,8 @@ ALTER TABLE `file_metadata` ADD INDEX `INDEX_REF_ID`(`ref_id`);
|
|||
ALTER TABLE `ui_scenario` ADD COLUMN scenario_type VARCHAR(100) NOT NULL DEFAULT 'scenario' COMMENT 'Scenario type' AFTER `level`;
|
||||
|
||||
ALTER TABLE `ui_scenario_module` ADD COLUMN scenario_type VARCHAR(100) NOT NULL DEFAULT 'scenario' COMMENT 'Scenario type' AFTER `level`;
|
||||
|
||||
-- 功能用例, 缺陷管理文件管理关联字段
|
||||
ALTER TABLE `attachment_module_relation` MODIFY COLUMN attachment_id VARCHAR(50) NULL;
|
||||
|
||||
ALTER TABLE `attachment_module_relation` ADD COLUMN file_metadata_ref_id VARCHAR(50) DEFAULT NULL COMMENT 'FILE ASSOCIATION ID';
|
||||
|
|
|
@ -2,13 +2,22 @@
|
|||
<div>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col>
|
||||
<el-table class="basic-config" :data="tableData">
|
||||
<el-table class="basic-config" :data="tableData" :row-class-name="handleIsRelated">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('load_test.file_name')">
|
||||
<template v-slot:default="scope">
|
||||
<el-tooltip class="item" effect="dark" :content="scope.row.name" placement="top">
|
||||
<el-progress
|
||||
class="row-delete-name"
|
||||
type="line"
|
||||
v-if="!scope.row.isLocal && scope.row.isRelatedDeleted"
|
||||
:stroke-width="40"
|
||||
:text-inside="true"
|
||||
:format="clearPercentage(scope.row)">
|
||||
</el-progress>
|
||||
<el-progress
|
||||
v-else
|
||||
:color="scope.row.progress >= 100 ? '' : uploadProgressColor"
|
||||
type="line"
|
||||
:format="clearPercentage(scope.row)"
|
||||
|
@ -37,7 +46,7 @@
|
|||
:width="70"
|
||||
:label="$t('commons.status')">
|
||||
<template v-slot:default="scope">
|
||||
<span :class="scope.row.status === 'success' ? 'green' : scope.row.status === 'error' ? 'red' : scope.row.status === 'toUpload' ? 'yellow' : ''">{{ scope.row.status | formatStatus}}</span>
|
||||
<span :class="scope.row.status === 'expired' ? 'lightgrey' : scope.row.status === 'success' ? 'green' : scope.row.status === 'error' ? 'red' : scope.row.status === 'toUpload' || 'toRelate' ? 'yellow' : ''">{{ formatStatus(scope.row.status) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
|
@ -46,19 +55,27 @@
|
|||
:label="$t('group.operator')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:width="140"
|
||||
:width="180"
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<el-button @click="preview(scope.row)" :disabled="!scope.row.id" type="primary"
|
||||
<el-button @click="preview(scope.row)" type="primary" :disabled="!scope.row.id || scope.row.status === 'toRelate' || scope.row.isRelatedDeleted"
|
||||
v-if="scope.row.progress === 100 && isPreview(scope.row)"
|
||||
icon="el-icon-view" size="mini" circle/>
|
||||
<el-button @click="handleDownload(scope.row)" type="primary" :disabled="!scope.row.id"
|
||||
<el-button @click="handleDownload(scope.row)" type="primary" :disabled="!scope.row.id || scope.row.status === 'toRelate' || scope.row.isRelatedDeleted"
|
||||
v-if="scope.row.progress === 100"
|
||||
icon="el-icon-download" size="mini" circle/>
|
||||
<el-button :disabled="isCopy || !scope.row.id"
|
||||
@click="handleUpload(scope.row)" type="primary"
|
||||
v-if="scope.row.progress === 100 && scope.row.isLocal"
|
||||
icon="el-icon-upload" size="mini" circle/>
|
||||
<el-button :disabled="readOnly || !isDelete || isCopy || (!scope.row.id && scope.row.status !== 'toUpload')"
|
||||
@click="handleDelete(scope.row, scope.$index)" type="danger"
|
||||
v-if="scope.row.progress === 100"
|
||||
icon="el-icon-delete" size="mini"
|
||||
v-if="scope.row.progress === 100 && scope.row.isLocal"
|
||||
icon="el-icon-delete" size="mini" circle/>
|
||||
<el-button :disabled="readOnly || !isDelete || isCopy || (!scope.row.id && scope.row.status !== 'toRelate')"
|
||||
@click="handleUnRelate(scope.row, scope.$index)" type="danger"
|
||||
v-if="scope.row.progress === 100 && !scope.row.isLocal"
|
||||
icon="el-icon-unlock" size="mini"
|
||||
circle/>
|
||||
<el-button :disabled="readOnly || !isDelete" @click="handleCancel(scope.row, scope.$index)" type="danger"
|
||||
v-if="scope.row.progress < 100"
|
||||
|
@ -100,10 +117,14 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
uploadProgressColor: '#d4f6d4',
|
||||
uploadSuccessColor: '#FFFFFF'
|
||||
uploadSuccessColor: '#FFFFFF',
|
||||
that: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleIsRelated(row, rowIndex) {
|
||||
return row.row.isRelatedDeleted ? 'delete-row' : '';
|
||||
},
|
||||
clearPercentage(row) {
|
||||
return () => {
|
||||
return row.name;
|
||||
|
@ -120,19 +141,35 @@ export default {
|
|||
this.$fileDownloadPost('/attachment/download', {
|
||||
name: file.name,
|
||||
id: file.id,
|
||||
isLocal: file.isLocal
|
||||
});
|
||||
},
|
||||
handleUpload(file) {
|
||||
this.$emit("handleDump", file);
|
||||
},
|
||||
handleDelete(file, index) {
|
||||
this.$emit("handleDelete", file, index);
|
||||
},
|
||||
handleUnRelate(file, index) {
|
||||
this.$emit("handleUnRelate", file, index);
|
||||
},
|
||||
handleCancel(file, index) {
|
||||
this.$emit("handleCancel", file, index);
|
||||
},
|
||||
},
|
||||
filters: {
|
||||
formatStatus(status) {
|
||||
if (isNaN(status)) {
|
||||
return status === 'success' ? '完成' : status === 'toUpload' ? '待上传' : '失败'
|
||||
switch (status) {
|
||||
case 'success':
|
||||
return this.$t('commons.file_upload_status.success');
|
||||
case 'toUpload':
|
||||
return this.$t('commons.file_upload_status.to_upload');
|
||||
case 'toRelate':
|
||||
return this.$t('commons.file_upload_status.to_relate');
|
||||
case 'expired':
|
||||
return this.$t('commons.file_upload_status.expired');
|
||||
default:
|
||||
return this.$t('commons.file_upload_status.error');
|
||||
}
|
||||
}
|
||||
return Math.floor(status * 100 / 100) + "%";
|
||||
}
|
||||
|
@ -145,6 +182,10 @@ export default {
|
|||
color: black;
|
||||
}
|
||||
|
||||
::v-deep .el-progress.row-delete-name .el-progress-bar__innerText {
|
||||
color: lightgrey!important;
|
||||
}
|
||||
|
||||
::v-deep .el-progress-bar__outer,
|
||||
::v-deep .el-progress-bar__inner {
|
||||
border-radius: inherit ;
|
||||
|
@ -169,4 +210,14 @@ export default {
|
|||
.yellow {
|
||||
color: #E6A23C;
|
||||
}
|
||||
|
||||
.lightgrey {
|
||||
color: lightgrey;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.el-table .delete-row {
|
||||
color: lightgrey;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -742,6 +742,9 @@ export default {
|
|||
if (this.validate(param)) {
|
||||
let option = this.getOption(param);
|
||||
this.result = this.$request(option, (response) => {
|
||||
// 保存用例后刷新附件
|
||||
this.currentTestCaseInfo.isCopy = false;
|
||||
this.$refs.otherInfo.getFileMetaData(response.data.id);
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.path = "/test/case/edit";
|
||||
// this.operationType = "edit"
|
||||
|
@ -767,8 +770,6 @@ export default {
|
|||
if (callback) {
|
||||
callback(this);
|
||||
}
|
||||
// 保存用例后刷新附件
|
||||
|
||||
//更新版本
|
||||
if (hasLicense()) {
|
||||
this.getVersionHistory();
|
||||
|
@ -810,6 +811,12 @@ export default {
|
|||
if (this.selectedOtherInfo) {
|
||||
param.otherInfoConfig = this.selectedOtherInfo;
|
||||
}
|
||||
if (this.$refs.otherInfo.relateFiles.length > 0) {
|
||||
param.relateFileMetaIds = this.$refs.otherInfo.relateFiles;
|
||||
}
|
||||
if (this.$refs.otherInfo.unRelateFiles.length > 0) {
|
||||
param.unRelateFileMetaIds = this.$refs.otherInfo.unRelateFiles;
|
||||
}
|
||||
return param;
|
||||
},
|
||||
parseOldFields(param) {
|
||||
|
|
|
@ -60,23 +60,33 @@
|
|||
|
||||
<el-tab-pane :label="$t('test_track.case.attachment')" name="attachment">
|
||||
<el-row>
|
||||
<el-col :span="22">
|
||||
<el-upload
|
||||
multiple
|
||||
:limit="8"
|
||||
action=""
|
||||
:auto-upload="true"
|
||||
:file-list="fileList"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="handleUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:disabled="readOnly || isCopy">
|
||||
<el-button :disabled="readOnly || isCopy" type="primary" size="mini">{{$t('test_track.case.add_attachment')}}</el-button>
|
||||
<el-col :span="22" style="margin-bottom: 10px;">
|
||||
<div class="upload-default" @click.stop>
|
||||
<el-popover placement="right" trigger="hover">
|
||||
<div>
|
||||
<el-upload
|
||||
multiple
|
||||
:limit="8"
|
||||
action=""
|
||||
:auto-upload="true"
|
||||
:file-list="fileList"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="handleUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:disabled="readOnly || isCopy">
|
||||
<el-button :disabled="readOnly || isCopy" type="text">{{$t('permission.project_file.local_upload')}}</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-button type="text" :disabled="readOnly || isCopy" @click="associationFile">{{ $t('permission.project_file.associated_files') }}</el-button>
|
||||
<i class="el-icon-plus" slot="reference"/>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div :class="readOnly ? 'testplan-local-upload-tip' : 'not-testplan-local-upload-tip'">
|
||||
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
|
@ -86,6 +96,8 @@
|
|||
:is-copy="isCopy"
|
||||
:is-delete="!isTestPlan"
|
||||
@handleDelete="handleDelete"
|
||||
@handleUnRelate="handleUnRelate"
|
||||
@handleDump="handleDump"
|
||||
@handleCancel="handleCancel"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -121,6 +133,8 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<ms-file-metadata-list ref="metadataList" @checkRows="checkRows"/>
|
||||
<ms-file-batch-move ref="module" @setModuleId="setModuleId"/>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
|
@ -137,10 +151,12 @@ import TabPaneCount from "@/business/components/track/plan/view/comonents/report
|
|||
import {getRelationshipCountCase} from "@/network/testCase";
|
||||
import TestCaseComment from "@/business/components/track/case/components/TestCaseComment";
|
||||
import ReviewCommentItem from "@/business/components/track/review/commom/ReviewCommentItem";
|
||||
import {byteToSize, getTypeByFileName, hasLicense} from "@/common/js/utils";
|
||||
import {byteToSize, getCurrentProjectID, getTypeByFileName, getUUID, hasLicense} from "@/common/js/utils";
|
||||
import {TokenKey} from "@/common/js/constants";
|
||||
import axios from "axios";
|
||||
import {validateAndSetLicense} from "@/business/permission";
|
||||
import MsFileMetadataList from "@/business/components/project/menu/file/quote/QuoteFileList";
|
||||
import MsFileBatchMove from "@/business/components/project/menu/file/module/FileBatchMove";
|
||||
|
||||
export default {
|
||||
name: "TestCaseEditOtherInfo",
|
||||
|
@ -150,6 +166,8 @@ export default {
|
|||
TestCaseTestRelate,
|
||||
TestCaseComment,
|
||||
ReviewCommentItem,
|
||||
MsFileMetadataList,
|
||||
MsFileBatchMove,
|
||||
FormRichTextItem, TestCaseIssueRelate, TestCaseAttachment, MsRichText, TestCaseRichText
|
||||
},
|
||||
props: ['form', 'labelWidth', 'caseId', 'readOnly', 'projectId', 'isTestPlan', 'planId', 'versionEnable', 'isCopy', 'copyCaseId',
|
||||
|
@ -174,7 +192,10 @@ export default {
|
|||
},
|
||||
intervalMap: new Map(),
|
||||
cancelFileToken: [],
|
||||
uploadFiles: []
|
||||
uploadFiles: [],
|
||||
relateFiles: [],
|
||||
unRelateFiles: [],
|
||||
dumpFile: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -262,7 +283,8 @@ export default {
|
|||
progress: this.type === 'add' ? 100 : 0,
|
||||
status: this.type === 'add' ? 'toUpload' : 0,
|
||||
creator: user.name,
|
||||
type: getTypeByFileName(file.name)
|
||||
type: getTypeByFileName(file.name),
|
||||
isLocal: true
|
||||
});
|
||||
|
||||
if (this.type === 'add') {
|
||||
|
@ -365,7 +387,8 @@ export default {
|
|||
this.fileList.splice(index, 1);
|
||||
this.tableData.splice(index, 1);
|
||||
if (this.type === 'add') {
|
||||
this.uploadFiles.splice(index, 1);
|
||||
let delIndex = this.uploadFiles.findIndex(uploadFile => uploadFile.name === file.name)
|
||||
this.uploadFiles.splice(delIndex, 1);
|
||||
} else {
|
||||
this.$get('/attachment/delete/testcase/' + file.id , response => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
|
@ -373,6 +396,36 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
handleUnRelate(file, index) {
|
||||
// 取消关联
|
||||
this.$alert(this.$t('load_test.unrelated_file_confirm') + file.name + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let unRelateFileIndex = this.tableData.findIndex(f => f.name === file.name);
|
||||
this.tableData.splice(unRelateFileIndex, 1);
|
||||
if (file.status === 'toRelate') {
|
||||
// 待关联的记录, 直接移除
|
||||
let unRelateId = this.relateFiles.findIndex(f => f === file.id);
|
||||
this.relateFiles.splice(unRelateId, 1);
|
||||
} else {
|
||||
// 已经关联的记录
|
||||
this.unRelateFiles.push(file.id);
|
||||
let data = {'belongType': 'testcase', 'belongId': this.caseId, 'metadataRefIds': this.unRelateFiles};
|
||||
this.$post('/attachment/metadata/unrelated', data, response => {
|
||||
this.$success(this.$t('commons.unrelated_success'));
|
||||
this.getFileMetaData();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDump(file) {
|
||||
this.$refs.module.init();
|
||||
this.dumpFile = file;
|
||||
},
|
||||
handleCancel(file, index) {
|
||||
this.fileList.splice(index, 1);
|
||||
let cancelToken = this.cancelFileToken.filter(f => f.name === file.name)[0];
|
||||
|
@ -382,13 +435,17 @@ export default {
|
|||
cancelFile.status = 'error';
|
||||
},
|
||||
getFileMetaData(id) {
|
||||
if (this.type === 'edit') {
|
||||
this.relateFiles = [];
|
||||
this.unRelateFiles = [];
|
||||
}
|
||||
this.$emit("update:isClickAttachmentTab", true);
|
||||
// 保存用例后传入用例id,刷新文件列表,可以预览和下载
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
let testCaseId;
|
||||
if (this.isCopy) {
|
||||
testCaseId = this.copyCaseId
|
||||
testCaseId = id ? id : this.copyCaseId
|
||||
} else {
|
||||
testCaseId = id ? id : this.caseId;
|
||||
}
|
||||
|
@ -404,12 +461,61 @@ export default {
|
|||
this.tableData = JSON.parse(JSON.stringify(files));
|
||||
this.tableData.map(f => {
|
||||
f.size = byteToSize(f.size);
|
||||
f.status = 'success';
|
||||
f.status = f.isRelatedDeleted ? 'expired' : 'success';
|
||||
f.progress = 100
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
associationFile() {
|
||||
this.$refs.metadataList.open();
|
||||
},
|
||||
checkRows(rows) {
|
||||
let repeatRecord = false;
|
||||
for (let row of rows) {
|
||||
let rowIndex = this.tableData.findIndex(item => item.name === row.name);
|
||||
if (rowIndex >= 0) {
|
||||
this.$error(this.$t('load_test.exist_related_file') + ": " + row.name);
|
||||
repeatRecord = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!repeatRecord) {
|
||||
if (this.type === 'add') {
|
||||
// 新增
|
||||
rows.forEach(row => {
|
||||
this.relateFiles.push(row.id);
|
||||
this.tableData.push({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
size: byteToSize(row.size),
|
||||
updateTime: row.createTime,
|
||||
progress: 100,
|
||||
status: 'toRelate',
|
||||
creator: row.createUser,
|
||||
type: row.type,
|
||||
isLocal: false,
|
||||
});
|
||||
})
|
||||
} else {
|
||||
// 编辑
|
||||
let metadataRefIds = [];
|
||||
rows.forEach(row => metadataRefIds.push(row.id));
|
||||
let data = {'belongType': 'testcase', 'belongId': this.caseId, 'metadataRefIds': metadataRefIds};
|
||||
this.$post('/attachment/metadata/relate', data, response => {
|
||||
this.$success(this.$t('commons.relate_success'));
|
||||
this.getFileMetaData();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
setModuleId(moduleId) {
|
||||
let data = {id: getUUID(), resourceId: getCurrentProjectID(), moduleId: moduleId,
|
||||
projectId: getCurrentProjectID(), fileName: this.dumpFile.name, attachmentId: this.dumpFile.id};
|
||||
this.$post("/attachment/metadata/dump", data, (response) => {
|
||||
this.$success(this.$t("organization.integration.successful_operation"));
|
||||
});
|
||||
},
|
||||
getRelatedTest() {
|
||||
this.$refs.relateTest.initTable();
|
||||
},
|
||||
|
@ -510,4 +616,45 @@ export default {
|
|||
.demandInput {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.el-icon-plus {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.upload-default {
|
||||
background-color: #fbfdff;
|
||||
border: 1px dashed #c0ccda;
|
||||
border-radius: 6px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
width: 40px;
|
||||
height: 30px;
|
||||
line-height: 32px;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.upload-default i {
|
||||
color: #8c939d;
|
||||
}
|
||||
|
||||
.upload-default:hover {
|
||||
border: 1px dashed #783887;
|
||||
}
|
||||
|
||||
.testplan-local-upload-tip {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 25px;
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
.not-testplan-local-upload-tip {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 25px;
|
||||
top: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<el-dialog :visible.sync="dialogVisible" width="80%" :destroy-on-close="true" :before-close="close" :append-to-body="true">
|
||||
<div>
|
||||
<img :src="'/attachment/preview/' + file.id" :alt="$t('test_track.case.img_loading_fail')" style="width: 100%;height: 100%;"
|
||||
<img :src="'/attachment/preview/' + file.id + '/' + file.isLocal" :alt="$t('test_track.case.img_loading_fail')" style="width: 100%;height: 100%;"
|
||||
v-if="file.type === 'JPG' || file.type === 'JPEG' || file.type === 'PNG'">
|
||||
<div v-if="file.type === 'PDF'">
|
||||
<test-case-pdf :file-id="file.id"/>
|
||||
<test-case-pdf :file-id="file.id" :is-local="file.isLocal"/>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
|
|
@ -10,7 +10,8 @@ export default {
|
|||
name: "TestCasePdf",
|
||||
components: {pdf},
|
||||
props: {
|
||||
fileId: String
|
||||
fileId: String,
|
||||
isLocal: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -21,7 +22,7 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
this.loading = true;
|
||||
this.loadingTask = pdf.createLoadingTask("/attachment/preview/" + this.fileId);
|
||||
this.loadingTask = pdf.createLoadingTask("/attachment/preview/" + this.fileId + "/" + this.isLocal);
|
||||
this.loadingTask.promise.then(pdf => {
|
||||
this.numPages = pdf.numPages
|
||||
this.loading = false;
|
||||
|
|
|
@ -102,23 +102,33 @@
|
|||
|
||||
<el-tab-pane :label="$t('test_track.case.attachment')" name="attachment">
|
||||
<el-row>
|
||||
<el-col :span="22">
|
||||
<el-upload
|
||||
multiple
|
||||
:limit="8"
|
||||
action=""
|
||||
:auto-upload="true"
|
||||
:file-list="fileList"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="handleUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:disabled="type === 'copy'">
|
||||
<el-button type="primary" :disabled="type === 'copy'" size="mini">{{$t('test_track.case.add_attachment')}}</el-button>
|
||||
<el-col :span="22" style="margin-bottom: 10px;">
|
||||
<div class="upload-default" @click.stop>
|
||||
<el-popover placement="right" trigger="hover">
|
||||
<div>
|
||||
<el-upload
|
||||
multiple
|
||||
:limit="8"
|
||||
action=""
|
||||
:auto-upload="true"
|
||||
:file-list="fileList"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="handleUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:disabled="readOnly || type === 'copy'">
|
||||
<el-button :disabled="readOnly || type === 'copy'" type="text">{{$t('permission.project_file.local_upload')}}</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-button type="text" :disabled="readOnly || type === 'copy'" @click="associationFile">{{ $t('permission.project_file.associated_files') }}</el-button>
|
||||
<i class="el-icon-plus" slot="reference"/>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="local-upload-tips">
|
||||
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 10px">
|
||||
|
@ -128,7 +138,9 @@
|
|||
:is-delete="isDelete"
|
||||
:is-copy="type === 'copy'"
|
||||
@handleDelete="handleDelete"
|
||||
@handleCancel="handleCancel"/>
|
||||
@handleCancel="handleCancel"
|
||||
@handleUnRelate="handleUnRelate"
|
||||
@handleDump="handleDump"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
@ -170,9 +182,10 @@
|
|||
<issue-comment :issues-id="form.id"
|
||||
@getComments="getComments"
|
||||
ref="issueComment"/>
|
||||
<ms-file-metadata-list ref="metadataList" @checkRows="checkRows"/>
|
||||
<ms-file-batch-move ref="module" @setModuleId="setModuleId"/>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
|
||||
</el-main>
|
||||
</template>
|
||||
|
||||
|
@ -193,7 +206,7 @@ import {
|
|||
getCurrentProjectID,
|
||||
getCurrentUser,
|
||||
getCurrentUserId,
|
||||
getCurrentWorkspaceId, getTypeByFileName, hasLicense,
|
||||
getCurrentWorkspaceId, getTypeByFileName, getUUID, hasLicense,
|
||||
} from "@/common/js/utils";
|
||||
import {enableThirdPartTemplate, getIssuePartTemplateWithProject, getPlatformTransitions} from "@/network/Issue";
|
||||
import CustomFiledFormItem from "@/business/components/common/components/form/CustomFiledFormItem";
|
||||
|
@ -204,6 +217,8 @@ import {TokenKey} from "@/common/js/constants";
|
|||
import {Message} from "element-ui";
|
||||
import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment";
|
||||
import axios from "axios";
|
||||
import MsFileMetadataList from "@/business/components/project/menu/file/quote/QuoteFileList";
|
||||
import MsFileBatchMove from "@/business/components/project/menu/file/module/FileBatchMove";
|
||||
|
||||
const {getIssuesById} = require("@/network/Issue");
|
||||
|
||||
|
@ -222,7 +237,9 @@ export default {
|
|||
MsMarkDownText,
|
||||
IssueComment,
|
||||
ReviewCommentItem,
|
||||
TestCaseAttachment
|
||||
TestCaseAttachment,
|
||||
MsFileMetadataList,
|
||||
MsFileBatchMove,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -308,7 +325,10 @@ export default {
|
|||
readOnly: false,
|
||||
isDelete: true,
|
||||
cancelFileToken: [],
|
||||
uploadFiles: []
|
||||
uploadFiles: [],
|
||||
relateFiles: [],
|
||||
unRelateFiles: [],
|
||||
dumpFile: {},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -362,6 +382,7 @@ export default {
|
|||
}
|
||||
},
|
||||
open(data, type) {
|
||||
this.uploadFiles = [];
|
||||
this.tabActiveName = 'relateTestCase'
|
||||
this.showFollow = false;
|
||||
this.result.loading = true;
|
||||
|
@ -546,8 +567,10 @@ export default {
|
|||
}
|
||||
|
||||
param.withoutTestCaseIssue = this.isMinder;
|
||||
|
||||
param.thirdPartPlatform = this.enableThirdPartTemplate;
|
||||
if (this.relateFiles.length > 0) {
|
||||
param.relateFileMetaIds = this.relateFiles;
|
||||
}
|
||||
return param;
|
||||
},
|
||||
_save() {
|
||||
|
@ -651,7 +674,8 @@ export default {
|
|||
progress: this.type === 'add' || this.isCaseEdit? 100 : 0,
|
||||
status: this.type === 'add' || this.isCaseEdit? 'toUpload' : 0,
|
||||
creator: user.name,
|
||||
type: getTypeByFileName(file.name)
|
||||
type: getTypeByFileName(file.name),
|
||||
isLocal: true
|
||||
});
|
||||
|
||||
if (this.type === 'add' || this.isCaseEdit) {
|
||||
|
@ -753,7 +777,8 @@ export default {
|
|||
this.fileList.splice(index, 1);
|
||||
this.tableData.splice(index, 1);
|
||||
if (this.type === 'add' || this.isCaseEdit) {
|
||||
this.uploadFiles.splice(index, 1);
|
||||
let delIndex = this.uploadFiles.findIndex(uploadFile => uploadFile.name === file.name)
|
||||
this.uploadFiles.splice(delIndex, 1);
|
||||
} else {
|
||||
this.$get('/attachment/delete/issue/' + file.id , response => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
|
@ -761,6 +786,38 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
handleUnRelate(file, index) {
|
||||
// 取消关联
|
||||
this.$alert(this.$t('load_test.unrelated_file_confirm') + file.name + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let unRelateFileIndex = this.tableData.findIndex(f => f.name === file.name);
|
||||
this.tableData.splice(unRelateFileIndex, 1);
|
||||
if (file.status === 'toRelate') {
|
||||
// 待关联的记录, 直接移除
|
||||
let unRelateId = this.relateFiles.findIndex(f => f === file.id);
|
||||
this.relateFiles.splice(unRelateId, 1);
|
||||
} else {
|
||||
// 已经关联的记录
|
||||
this.unRelateFiles.push(file.id);
|
||||
let data = {'belongType': 'issue', 'belongId': this.issueId, 'metadataRefIds': this.unRelateFiles};
|
||||
this.result.loading = true;
|
||||
this.$post('/attachment/metadata/unrelated', data, response => {
|
||||
this.$success(this.$t('commons.unrelated_success'));
|
||||
this.result.loading = false;
|
||||
this.getFileMetaData(this.issueId);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDump(file) {
|
||||
this.$refs.module.init();
|
||||
this.dumpFile = file;
|
||||
},
|
||||
handleCancel(file, index) {
|
||||
this.fileList.splice(index, 1);
|
||||
let cancelToken = this.cancelFileToken.filter(f => f.name === file.name)[0];
|
||||
|
@ -769,7 +826,63 @@ export default {
|
|||
cancelFile.progress = 100;
|
||||
cancelFile.status = 'error';
|
||||
},
|
||||
associationFile() {
|
||||
this.$refs.metadataList.open();
|
||||
},
|
||||
checkRows(rows) {
|
||||
let repeatRecord = false;
|
||||
for (let row of rows) {
|
||||
let rowIndex = this.tableData.findIndex(item => item.name === row.name);
|
||||
if (rowIndex >= 0) {
|
||||
this.$error(this.$t('load_test.exist_related_file') + ": " + row.name);
|
||||
repeatRecord = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!repeatRecord) {
|
||||
if (this.type === 'add') {
|
||||
// 新增
|
||||
rows.forEach(row => {
|
||||
this.relateFiles.push(row.id);
|
||||
this.tableData.push({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
size: byteToSize(row.size),
|
||||
updateTime: row.createTime,
|
||||
progress: 100,
|
||||
status: 'toRelate',
|
||||
creator: row.createUser,
|
||||
type: row.type,
|
||||
isLocal: false,
|
||||
});
|
||||
})
|
||||
} else {
|
||||
// 编辑
|
||||
let metadataRefIds = [];
|
||||
rows.forEach(row => metadataRefIds.push(row.id));
|
||||
let data = {'belongType': 'issue', 'belongId': this.issueId, 'metadataRefIds': metadataRefIds};
|
||||
this.result.loading = true;
|
||||
this.$post('/attachment/metadata/relate', data, response => {
|
||||
this.$success(this.$t('commons.relate_success'));
|
||||
this.result.loading = false;
|
||||
this.getFileMetaData(this.issueId);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
setModuleId(moduleId) {
|
||||
let data = {id: getUUID(), resourceId: getCurrentProjectID(), moduleId: moduleId,
|
||||
projectId: getCurrentProjectID(), fileName: this.dumpFile.name, attachmentId: this.dumpFile.id};
|
||||
this.$post("/attachment/metadata/dump", data, (response) => {
|
||||
this.$success(this.$t("organization.integration.successful_operation"));
|
||||
});
|
||||
},
|
||||
getFileMetaData(id) {
|
||||
if (this.type === 'edit') {
|
||||
this.uploadFiles = [];
|
||||
this.relateFiles = [];
|
||||
this.unRelateFiles = [];
|
||||
}
|
||||
// 保存用例后传入用例id,刷新文件列表,可以预览和下载
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
|
@ -838,4 +951,38 @@ export default {
|
|||
font-size: xx-small;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.el-icon-plus {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.upload-default {
|
||||
background-color: #fbfdff;
|
||||
border: 1px dashed #c0ccda;
|
||||
border-radius: 6px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
width: 40px;
|
||||
height: 30px;
|
||||
line-height: 32px;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.upload-default i {
|
||||
color: #8c939d;
|
||||
}
|
||||
|
||||
.upload-default:hover {
|
||||
border: 1px dashed #783887;
|
||||
}
|
||||
|
||||
.local-upload-tips {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 25px;
|
||||
top: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -72,6 +72,8 @@ export default {
|
|||
warning_module_add: "Tree modules are up to 8 levels deep",
|
||||
send_success: 'Send successfully',
|
||||
delete_success: 'Deleted successfully',
|
||||
relate_success: 'Related successfully',
|
||||
unrelated_success: 'Unrelated successfully',
|
||||
modify_success: 'Modify Success',
|
||||
copy_success: 'Copy Success',
|
||||
delete_cancel: 'Deleted Cancel',
|
||||
|
@ -188,6 +190,13 @@ export default {
|
|||
month: "Month",
|
||||
year: "Year"
|
||||
},
|
||||
file_upload_status: {
|
||||
success: 'Success',
|
||||
to_upload: 'To upload',
|
||||
to_relate: 'To be associated',
|
||||
expired: 'Expired',
|
||||
error: 'Error'
|
||||
},
|
||||
test_unit: 'tests',
|
||||
remove: 'Remove',
|
||||
next_level: "Next level",
|
||||
|
@ -1098,6 +1107,7 @@ export default {
|
|||
related_file_not_found: "No related test file found!",
|
||||
delete_file_when_uploading: 'The current operation may interrupt the file being uploaded!',
|
||||
delete_file_confirm: 'Confirm delete file:',
|
||||
unrelated_file_confirm: 'Confirm unrelated file: ',
|
||||
file_size_out_of_bounds: "File size out of bounds, file name: ",
|
||||
file_size_limit: "The number of files exceeds the limit!",
|
||||
delete_file: "The file already exists, please delete the file with the same name first!",
|
||||
|
@ -1160,6 +1170,7 @@ export default {
|
|||
report_type: 'Report type',
|
||||
upload_jmx: 'Upload JMX',
|
||||
exist_jmx: 'Existed Files',
|
||||
exist_related_file: 'Existed Relate Files',
|
||||
other_resource: 'Resource Files',
|
||||
upload_file: 'Upload Files',
|
||||
load_exist_file: 'Load Project Files',
|
||||
|
|
|
@ -137,7 +137,7 @@ export default {
|
|||
cancel_relevance_project: "Disassociating the project will also cancel the associated test cases under the project",
|
||||
img_loading_fail: "Image failed to load",
|
||||
pdf_loading_fail: "PDF loading failed",
|
||||
upload_tip: "file size limit[0-500MB]",
|
||||
upload_tip: "Local upload file size is limited to [0-500MB]",
|
||||
add_attachment: "Add",
|
||||
attachment: "Attachment",
|
||||
upload_time: "Upload Time",
|
||||
|
|
|
@ -128,7 +128,7 @@ export default {
|
|||
cancel_relevance_project: "取消项目关联会同时取消该项目下已关联的测试用例",
|
||||
img_loading_fail: "图片加载失败",
|
||||
pdf_loading_fail: "PDF加载失败",
|
||||
upload_tip: "文件大小限制[0-500MB]",
|
||||
upload_tip: "本地上传, 文件大小限制[0-500MB]",
|
||||
add_attachment: "添加",
|
||||
attachment: "附件",
|
||||
upload_time: "上传时间",
|
||||
|
|
|
@ -128,7 +128,7 @@ export default {
|
|||
cancel_relevance_project: "取消項目關聯會同時取消該項目下已關聯的測試用例",
|
||||
img_loading_fail: "圖片加載失敗",
|
||||
pdf_loading_fail: "PDF加載失敗",
|
||||
upload_tip: "文件大小限制[0-500MB]",
|
||||
upload_tip: "本地上傳, 文件大小限制[0-500MB]",
|
||||
add_attachment: "添加",
|
||||
attachment: "附件",
|
||||
upload_time: "上傳時間",
|
||||
|
|
|
@ -71,6 +71,8 @@ export default {
|
|||
warning_module_add: "模块树深度最大为8层",
|
||||
send_success: '发送成功',
|
||||
delete_success: '删除成功',
|
||||
relate_success: '关联成功',
|
||||
unrelated_success: '取消关联成功',
|
||||
copy_success: '复制成功',
|
||||
modify_success: '修改成功',
|
||||
delete_cancel: '已取消删除',
|
||||
|
@ -182,6 +184,13 @@ export default {
|
|||
month: "月",
|
||||
year: "年"
|
||||
},
|
||||
file_upload_status: {
|
||||
success: '完成',
|
||||
to_upload: '待上传',
|
||||
to_relate: '待关联',
|
||||
expired: '已失效',
|
||||
error: '失败'
|
||||
},
|
||||
test_unit: '测试',
|
||||
system_parameter_setting: '系统参数设置',
|
||||
connection_successful: '连接成功',
|
||||
|
@ -1106,6 +1115,7 @@ export default {
|
|||
related_file_not_found: "未找到关联的测试文件!",
|
||||
delete_file_when_uploading: '当前操作可能会中断正在上传的文件!',
|
||||
delete_file_confirm: '确认删除文件: ',
|
||||
unrelated_file_confirm: '确认取消关联: ',
|
||||
file_size_limit: "文件个数超出限制!",
|
||||
file_size_out_of_bounds: "文件大小超出范围, 文件名称: ",
|
||||
delete_file: "文件已存在,请先删除同名文件!",
|
||||
|
@ -1171,6 +1181,7 @@ export default {
|
|||
report_type: "报告类型",
|
||||
upload_jmx: '上传 JMX 文件',
|
||||
exist_jmx: '已存在的文件',
|
||||
exist_related_file: '已存在的关联文件',
|
||||
other_resource: '资源文件',
|
||||
upload_file: '上传新文件',
|
||||
load_exist_file: '加载文件',
|
||||
|
|
|
@ -70,6 +70,8 @@ export default {
|
|||
save_success: '保存成功',
|
||||
send_success: '發送成功',
|
||||
delete_success: '刪除成功',
|
||||
relate_success: '關聯成功',
|
||||
unrelated_success: '取消關聯成功',
|
||||
copy_success: '復製成功',
|
||||
warning_module_add: "模塊樹深度最大為8層",
|
||||
modify_success: '修改成功',
|
||||
|
@ -182,6 +184,13 @@ export default {
|
|||
month: "月",
|
||||
year: "年"
|
||||
},
|
||||
file_upload_status: {
|
||||
success: '完成',
|
||||
to_upload: '待上傳',
|
||||
to_relate: '待關聯',
|
||||
expired: '已失效',
|
||||
error: '失敗'
|
||||
},
|
||||
test_unit: '測試',
|
||||
system_parameter_setting: '系統參數設置',
|
||||
connection_successful: '連接成功',
|
||||
|
@ -1102,6 +1111,7 @@ export default {
|
|||
related_file_not_found: "未找到關聯的測試文件!",
|
||||
delete_file_when_uploading: '當前操作可能會中斷正在上傳的文件!',
|
||||
delete_file_confirm: '確認刪除文件: ',
|
||||
unrelated_file_confirm: '確認取消關聯: ',
|
||||
file_size_limit: "文件個數超出限製!",
|
||||
file_size_out_of_bounds: "文件大小超出範圍, 文件名称: ",
|
||||
delete_file: "文件已存在,請先刪除同名文件!",
|
||||
|
@ -1167,6 +1177,7 @@ export default {
|
|||
report_type: "报告类型",
|
||||
upload_jmx: '上傳 JMX 文件',
|
||||
exist_jmx: '已存在的文件',
|
||||
exist_related_file: '已存在的關聯文件',
|
||||
other_resource: '資源文件',
|
||||
upload_file: '上傳新文件',
|
||||
load_exist_file: '加載文件',
|
||||
|
|
Loading…
Reference in New Issue