feat(测试跟踪): 附件功能改造

--story=1006991 --user=宋昌昌 【测试跟踪】功能用例&缺陷增加附件功能支持视频文件(1.20分支同步上) https://www.tapd.cn/55049933/s/1202348
This commit is contained in:
song-cc-rock 2022-07-14 18:34:33 +08:00 committed by jianxing
parent ad35f8d2b5
commit c25795e462
23 changed files with 423 additions and 369 deletions

View File

@ -126,7 +126,7 @@ public class FileService {
final FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
fileAttachmentMetadata.setId(UUID.randomUUID().toString());
fileAttachmentMetadata.setName(file.getOriginalFilename());
fileAttachmentMetadata.setType(getFileType(fileAttachmentMetadata.getName()).name());
fileAttachmentMetadata.setType(getFileTypeWithoutEnum(fileAttachmentMetadata.getName()));
fileAttachmentMetadata.setSize(file.getSize());
fileAttachmentMetadata.setCreateTime(System.currentTimeMillis());
fileAttachmentMetadata.setUpdateTime(System.currentTimeMillis());
@ -154,7 +154,7 @@ public class FileService {
final FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
fileAttachmentMetadata.setId(UUID.randomUUID().toString());
fileAttachmentMetadata.setName(attachmentName);
fileAttachmentMetadata.setType(getFileType(attachmentName).name());
fileAttachmentMetadata.setType(getFileTypeWithoutEnum(attachmentName));
fileAttachmentMetadata.setSize(Integer.valueOf(bytes.length).longValue());
fileAttachmentMetadata.setCreateTime(System.currentTimeMillis());
fileAttachmentMetadata.setUpdateTime(System.currentTimeMillis());
@ -332,6 +332,15 @@ public class FileService {
return FileType.valueOf(type.toUpperCase());
}
private String getFileTypeWithoutEnum(String filename) {
if (StringUtils.isEmpty(filename)) {
return "";
}
int s = filename.lastIndexOf(".") + 1;
String type = filename.substring(s);
return type.toUpperCase();
}
public List<FileMetadata> getFileMetadataByCaseId(String caseId) {
TestCaseFileExample testCaseFileExample = new TestCaseFileExample();
testCaseFileExample.createCriteria().andCaseIdEqualTo(caseId);
@ -377,7 +386,9 @@ public class FileService {
return fileAttachmentMetadataMapper.selectByExample(example);
}
public FileAttachmentMetadata getFileAttachmentMetadataByFileId(String fileId) {
return fileAttachmentMetadataMapper.selectByPrimaryKey(fileId);
}
public void deleteFileById(String fileId) {
deleteFileByIds(Collections.singletonList(fileId));

View File

@ -27,6 +27,7 @@ import io.metersphere.track.request.testcase.AuthUserIssueRequest;
import io.metersphere.track.request.testcase.IssuesRequest;
import io.metersphere.track.request.testcase.IssuesUpdateRequest;
import io.metersphere.track.service.IssuesService;
import io.metersphere.track.service.TestCaseService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@ -64,22 +65,33 @@ public class IssuesController {
return PageUtils.setPageInfo(page, issuesService.relateList(request));
}
@PostMapping(value = "/add", consumes = {"multipart/form-data"})
@PostMapping(value = "/add")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_ISSUE_READ_CREATE)
@MsAuditLog(module = OperLogModule.TRACK_BUG, type = OperLogConstants.CREATE, content = "#msClass.getLogDetails(#issuesRequest)", msClass = IssuesService.class)
@SendNotice(taskType = NoticeConstants.TaskType.DEFECT_TASK, target = "#issuesRequest",
event = NoticeConstants.Event.CREATE, subject = "缺陷通知")
public IssuesWithBLOBs addIssues(@RequestPart(value = "request") IssuesUpdateRequest issuesRequest, @RequestPart(value = "file", required = false) List<MultipartFile> files) {
return issuesService.addIssuesRefactor(issuesRequest, files);
public IssuesWithBLOBs addIssues(@RequestPart(value = "request") IssuesUpdateRequest issuesRequest) {
return issuesService.addIssues(issuesRequest);
}
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
@PostMapping(value = "/update")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_ISSUE_READ_EDIT)
@MsAuditLog(module = OperLogModule.TRACK_BUG, type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#issuesRequest.id)", content = "#msClass.getLogDetails(#issuesRequest.id)", msClass = IssuesService.class)
@SendNotice(taskType = NoticeConstants.TaskType.DEFECT_TASK, target = "#issuesRequest",
event = NoticeConstants.Event.UPDATE, subject = "缺陷通知")
public void updateIssues(@RequestPart(value = "request") IssuesUpdateRequest issuesRequest, @RequestPart(value = "file", required = false) List<MultipartFile> files) {
issuesService.updateIssuesRefactor(issuesRequest, files);
public void updateIssues(@RequestPart(value = "request") IssuesUpdateRequest issuesRequest) {
issuesService.updateIssues(issuesRequest);
}
@PostMapping(value = "/attachment/upload", consumes = {"multipart/form-data"})
@MsAuditLog(module = OperLogModule.TRACK_BUG, type = OperLogConstants.IMPORT, beforeEvent = "#msClass.getLogDetails(#issuesRequest.id)", content = "#msClass.getLogDetails(#request.id)", msClass = TestCaseService.class)
public void uploadAttachment(@RequestPart("request") IssuesUpdateRequest issuesRequest, @RequestPart(value = "file", required = false) MultipartFile file) {
issuesService.uploadAttachment(issuesRequest, file);
}
@GetMapping("/attachment/delete/{fileId}")
public void deleteAttachment(@PathVariable String fileId) {
issuesService.deleteAttachment(fileId);
}
@GetMapping("/file/attachmentMetadata/{issueId}")

View File

@ -385,15 +385,6 @@ public class TestCaseController {
.body(bytes);
}
@PostMapping("/attachment/download")
public ResponseEntity<byte[]> attachmentDownload(@RequestBody FileOperationRequest fileOperationRequest) {
byte[] bytes = fileService.getAttachmentBytes(fileOperationRequest.getId());
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileOperationRequest.getName(), StandardCharsets.UTF_8) + "\"")
.body(bytes);
}
@GetMapping("/file/preview/{fileId}")
public ResponseEntity<byte[]> preview(@PathVariable String fileId) {
byte[] bytes = fileService.loadFileAsBytes(fileId);
@ -403,8 +394,14 @@ public class TestCaseController {
.body(bytes);
}
@PostMapping(value = "/attachment/upload", consumes = {"multipart/form-data"})
@MsAuditLog(module = OperLogModule.TRACK_TEST_CASE, type = OperLogConstants.IMPORT, beforeEvent = "#msClass.getLogDetails(#request.id)", title = "#request.name", content = "#msClass.getLogDetails(#request.id)", msClass = TestCaseService.class)
public void uploadAttachment(@RequestPart("request") EditTestCaseRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
testCaseService.uploadAttachment(request, file);
}
@GetMapping("/attachment/preview/{fileId}")
public ResponseEntity<byte[]> attachmentPreview(@PathVariable String fileId) {
public ResponseEntity<byte[]> previewAttachment(@PathVariable String fileId) {
byte[] bytes = fileService.getAttachmentBytes(fileId);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
@ -412,6 +409,20 @@ public class TestCaseController {
.body(bytes);
}
@PostMapping("/attachment/download")
public ResponseEntity<byte[]> downloadAttachment(@RequestBody FileOperationRequest fileOperationRequest) {
byte[] bytes = fileService.getAttachmentBytes(fileOperationRequest.getId());
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileOperationRequest.getName(), StandardCharsets.UTF_8) + "\"")
.body(bytes);
}
@GetMapping("/attachment/delete/{fileId}")
public void deleteAttachment(@PathVariable String fileId) {
testCaseService.deleteAttachment(fileId);
}
@PostMapping("/save")
@MsAuditLog(module = OperLogModule.TRACK_TEST_CASE, type = OperLogConstants.CREATE, title = "#testCaseWithBLOBs.name", content = "#msClass.getLogDetails(#testCaseWithBLOBs.id)", msClass = TestCaseService.class)
public TestCaseWithBLOBs saveTestCase(@RequestBody EditTestCaseRequest request) {

View File

@ -11,7 +11,6 @@ import io.metersphere.track.request.testcase.EditTestCaseRequest;
import io.metersphere.track.request.testcase.IssuesRequest;
import io.metersphere.track.request.testcase.IssuesUpdateRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@ -32,13 +31,13 @@ public interface IssuesPlatform {
*
* @param issuesRequest issueRequest
*/
IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files);
IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest);
/**
* 更新缺陷
* @param request
*/
void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files);
void updateIssue(IssuesUpdateRequest request);
/**
* 删除缺陷平台缺陷

View File

@ -10,7 +10,6 @@ import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.constants.IssuesStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.FileUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.CustomFieldDao;
import io.metersphere.dto.CustomFieldItemDTO;
@ -31,10 +30,8 @@ import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@ -227,7 +224,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
}
@Override
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) {
setUserConfig();
Project project = getProject();
@ -239,20 +236,6 @@ public class JiraPlatform extends AbstractIssuePlatform {
// 上传附件
imageFiles.forEach(img -> jiraClientV2.uploadAttachment(result.getKey(), img));
if (files != null) {
files.forEach(multipartFile -> {
try {
File file = new File(FileUtils.ATTACHMENT_TMP_DIR + "/" + multipartFile.getOriginalFilename());
org.apache.commons.io.FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);
jiraClientV2.uploadAttachment(result.getKey(), file);
if (file.exists()) {
file.delete();
}
} catch (IOException e) {
LogUtil.error(e);
}
});
}
String status = getStatus(issues.getFields());
issuesRequest.setPlatformStatus(status);
@ -266,6 +249,21 @@ public class JiraPlatform extends AbstractIssuePlatform {
// 用例与第三方缺陷平台中的缺陷关联
handleTestCaseIssues(issuesRequest);
// 如果是复制新增, 同步附件到第三方平台
if (StringUtils.isNotEmpty(issuesRequest.getCopyIssueId())) {
IssueFileExample example = new IssueFileExample();
example.createCriteria().andIssueIdEqualTo(issuesRequest.getCopyIssueId());
List<IssueFile> issueFiles = issueFileMapper.selectByExample(example);
if (issueFiles != null) {
issueFiles.forEach(issueFile -> {
FileAttachmentMetadata fileAttachmentMetadata = fileService.getFileAttachmentMetadataByFileId(issueFile.getFileId());
// 同步第三方平台附件
File file = new File(fileAttachmentMetadata.getFilePath() + "/" + fileAttachmentMetadata.getName());
jiraClientV2.uploadAttachment(result.getKey(), file);
});
}
}
return res;
}
@ -479,7 +477,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
}
@Override
public void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files) {
public void updateIssue(IssuesUpdateRequest request) {
setUserConfig();
Project project = getProject();
List<File> imageFiles = getImageFiles(request);
@ -487,54 +485,40 @@ public class JiraPlatform extends AbstractIssuePlatform {
JSONObject param = buildUpdateParam(request, getIssueType(project.getIssueConfig()), project.getJiraKey());
jiraClientV2.updateIssue(request.getPlatformId(), JSONObject.toJSONString(param));
List<FileMetadata> updatedFiles = request.getUpdatedFileList();
List<String> updatedFileIds = updatedFiles.stream().map(FileMetadata::getId).collect(Collectors.toList());
List<FileAttachmentMetadata> originFiles = fileService.getFileAttachmentMetadataByIssueId(request.getId());
List<String> deleteAttachmentNames = new ArrayList<String>();
originFiles.forEach(originFile -> {
if (!updatedFileIds.contains(originFile.getId())) {
deleteAttachmentNames.add(originFile.getName());
}
});
List<FileAttachmentMetadata> newFiles = fileService.getFileAttachmentMetadataByIssueId(request.getId());
List<String> newFileNames = newFiles.stream().map(FileAttachmentMetadata::getName).collect(Collectors.toList());
Set<String> attachmentNames = new HashSet<>();
// 更新附件
// 同步Jira平台附件
JiraIssue jiraIssue = jiraClientV2.getIssues(request.getPlatformId());
JSONObject fields = jiraIssue.getFields();
JSONArray attachments = fields.getJSONArray("attachment");
if (!attachments.isEmpty() && attachments.size() > 0) {
// 删除旧附件若缺陷描述中不存在且附件上传的删除列表中存在则删除
if (!attachments.isEmpty() && attachments.size() > 0) {
for (int i = 0; i < attachments.size(); i++) {
JSONObject attachment = attachments.getJSONObject(i);
String filename = attachment.getString("filename");
attachmentNames.add(filename);
if (!request.getDescription().contains(filename) && deleteAttachmentNames.contains(filename)) {
if (!request.getDescription().contains(filename) && !newFileNames.contains(filename)) {
String fileId = attachment.getString("id");
jiraClientV2.deleteAttachment(fileId);
}
}
}
//上传新附件
if (CollectionUtils.isNotEmpty(newFiles)) {
newFiles.forEach(file -> {
if (!attachmentNames.contains(file.getName())) {
File newFile = new File(file.getFilePath() + "/" + file.getName());
jiraClientV2.uploadAttachment(request.getPlatformId(), newFile);
}
});
}
imageFiles.forEach(img -> {
if (!attachmentNames.contains(img.getName())) {
// 旧附件没有才上传新附件
jiraClientV2.uploadAttachment(request.getPlatformId(), img);
}
});
if (files != null) {
files.forEach(multipartFile -> {
try {
File file = new File(FileUtils.ATTACHMENT_TMP_DIR + "/" + multipartFile.getOriginalFilename());
org.apache.commons.io.FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);
jiraClientV2.uploadAttachment(request.getPlatformId(), file);
if (file.exists()) {
file.delete();
}
} catch (IOException e) {
LogUtil.error(e);
}
});
}
if (request.getTransitions() != null) {
try {
@ -548,7 +532,6 @@ public class JiraPlatform extends AbstractIssuePlatform {
LogUtil.error(e);
}
}
handleIssueUpdate(request);
}

View File

@ -13,7 +13,6 @@ import io.metersphere.track.request.testcase.IssuesRequest;
import io.metersphere.track.request.testcase.IssuesUpdateRequest;
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.UUID;
@ -42,7 +41,7 @@ public class LocalPlatform extends LocalAbstractPlatform {
}
@Override
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) {
String issueStatus = "new";
if (StringUtils.isNotBlank(issuesRequest.getCustomFields())) {
List<CustomFieldItemDTO> customFields = issuesRequest.getRequestFields();
@ -76,7 +75,7 @@ public class LocalPlatform extends LocalAbstractPlatform {
}
@Override
public void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files) {
public void updateIssue(IssuesUpdateRequest request) {
handleIssueUpdate(request);
}
}

View File

@ -30,7 +30,6 @@ import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import java.util.*;
import java.util.stream.Collectors;
@ -91,7 +90,7 @@ public class TapdPlatform extends AbstractIssuePlatform {
}
@Override
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) {
MultiValueMap<String, Object> param = buildUpdateParam(issuesRequest);
TapdBug bug = tapdClient.addIssue(param);
@ -112,7 +111,7 @@ public class TapdPlatform extends AbstractIssuePlatform {
}
@Override
public void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files) {
public void updateIssue(IssuesUpdateRequest request) {
MultiValueMap<String, Object> param = buildUpdateParam(request);
param.add("id", request.getPlatformId());
handleIssueUpdate(request);

View File

@ -37,7 +37,6 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.net.URLDecoder;
import java.net.URLEncoder;
@ -198,7 +197,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
}
@Override
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) {
setUserConfig();
MultiValueMap<String, Object> param = buildUpdateParam(issuesRequest);
@ -229,7 +228,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
}
@Override
public void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files) {
public void updateIssue(IssuesUpdateRequest request) {
setUserConfig();
MultiValueMap<String, Object> param = buildUpdateParam(request);
if (request.getTransitions() != null) {

View File

@ -6,7 +6,6 @@ import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.*;
import io.metersphere.base.domain.ext.CustomFieldResource;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
import io.metersphere.commons.constants.AttachmentType;
@ -36,7 +35,6 @@ import io.metersphere.track.request.testcase.IssuesRequest;
import io.metersphere.track.request.testcase.IssuesUpdateRequest;
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
@ -108,7 +106,7 @@ public class IssuesService {
List<AbstractIssuePlatform> platformList = getAddPlatforms(issuesRequest);
IssuesWithBLOBs issues = null;
for (AbstractIssuePlatform platform : platformList) {
issues = platform.addIssue(issuesRequest, null);
issues = platform.addIssue(issuesRequest);
}
if (issuesRequest.getIsPlanEdit()) {
issuesRequest.getAddResourceIds().forEach(l -> {
@ -116,39 +114,9 @@ public class IssuesService {
});
}
saveFollows(issuesRequest.getId(), issuesRequest.getFollows());
List<CustomFieldResource> addFields = issuesRequest.getAddFields();
addFields.addAll(issuesRequest.getEditFields());
customFieldIssuesService.addFields(issuesRequest.getId(), addFields);
return issues;
}
public void updateIssues(IssuesUpdateRequest issuesRequest) {
issuesRequest.getId();
List<AbstractIssuePlatform> platformList = getUpdatePlatforms(issuesRequest);
platformList.forEach(platform -> {
platform.updateIssue(issuesRequest, null);
});
customFieldIssuesService.addFields(issuesRequest.getId(), issuesRequest.getAddFields());
customFieldIssuesService.editFields(issuesRequest.getId(), issuesRequest.getEditFields());
customFieldIssuesService.addFields(issuesRequest.getId(), issuesRequest.getAddFields());
// todo 缺陷更新事件
}
public IssuesWithBLOBs addIssuesRefactor(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
List<AbstractIssuePlatform> platformList = getAddPlatforms(issuesRequest);
IssuesWithBLOBs issues = null;
for (AbstractIssuePlatform platform : platformList) {
issues = platform.addIssue(issuesRequest, files);
}
if (issuesRequest.getIsPlanEdit()) {
issuesRequest.getAddResourceIds().forEach(l -> {
testCaseIssueService.updateIssuesCount(l);
});
}
saveFollows(issuesRequest.getId(), issuesRequest.getFollows());
customFieldIssuesService.addFields(issuesRequest.getId(), issuesRequest.getAddFields());
// copy的附件
// copy附件
if (StringUtils.isNotEmpty(issuesRequest.getCopyIssueId())) {
final String addIssueId = issues.getId();
IssueFileExample example = new IssueFileExample();
@ -156,6 +124,7 @@ public class IssuesService {
List<IssueFile> issueFiles = issueFileMapper.selectByExample(example);
if (issueFiles != null) {
issueFiles.forEach(issueFile -> {
// 同步MS附件
FileAttachmentMetadata fileAttachmentMetadata = fileService.copyAttachment(issueFile.getFileId(), AttachmentType.ISSUE.type(), addIssueId);
IssueFile newIssueFile = new IssueFile();
newIssueFile.setIssueId(addIssueId);
@ -164,57 +133,40 @@ public class IssuesService {
});
}
}
// 新的附件
if (files != null) {
files.forEach(file -> {
FileAttachmentMetadata fileAttachmentMetadata = fileService.saveAttachment(file, AttachmentType.ISSUE.type(), issuesRequest.getId());
IssueFile issueFile = new IssueFile();
issueFile.setIssueId(issuesRequest.getId());
issueFile.setFileId(fileAttachmentMetadata.getId());
issueFileMapper.insert(issueFile);
});
}
return issues;
}
public void updateIssuesRefactor(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
issuesRequest.getId();
public void updateIssues(IssuesUpdateRequest issuesRequest) {
List<AbstractIssuePlatform> platformList = getUpdatePlatforms(issuesRequest);
platformList.forEach(platform -> {
platform.updateIssue(issuesRequest, files);
platform.updateIssue(issuesRequest);
});
customFieldIssuesService.editFields(issuesRequest.getId(), issuesRequest.getEditFields());
customFieldIssuesService.addFields(issuesRequest.getId(), issuesRequest.getAddFields());
// todo 缺陷更新事件
// 删除原来的文件
List<FileMetadata> updatedFiles = issuesRequest.getUpdatedFileList();
List<FileAttachmentMetadata> originFiles = fileService.getFileAttachmentMetadataByIssueId(issuesRequest.getId());
List<String> updatedFileIds = updatedFiles.stream().map(FileMetadata::getId).collect(Collectors.toList());
List<String> originFileIds = originFiles.stream().map(FileAttachmentMetadata::getId).collect(Collectors.toList());
// 获得差异的附件ID
List<String> deleteFileIds = ListUtils.subtract(originFileIds, updatedFileIds);
// 删除差异附件记录, 目录下附件文件
fileService.deleteAttachment(deleteFileIds);
fileService.deleteFileAttachmentByIds(deleteFileIds);
//删除用例文件关联记录
if (!CollectionUtils.isEmpty(deleteFileIds)) {
IssueFileExample issueFileExample = new IssueFileExample();
issueFileExample.createCriteria().andFileIdIn(deleteFileIds);
issueFileMapper.deleteByExample(issueFileExample);
}
// 保存新的附件
if (files != null) {
files.forEach(file -> {
FileAttachmentMetadata fileAttachmentMetadata = fileService.saveAttachment(file, AttachmentType.ISSUE.type(), issuesRequest.getId());
public void uploadAttachment(IssuesUpdateRequest request, MultipartFile file) {
IssuesWithBLOBs issuesWithBLOBs = issuesMapper.selectByPrimaryKey(request.getId());
if (issuesWithBLOBs == null) {
MSException.throwException(Translator.get("issues_attachment_upload_not_found") + request.getId());
}
FileAttachmentMetadata fileAttachmentMetadata = fileService.saveAttachment(file, AttachmentType.ISSUE.type(), request.getId());
IssueFile issueFile = new IssueFile();
issueFile.setIssueId(issuesRequest.getId());
issueFile.setIssueId(request.getId());
issueFile.setFileId(fileAttachmentMetadata.getId());
issueFileMapper.insert(issueFile);
});
}
public void deleteAttachment(String id) {
// 删除附件记录, 目录下附件文件
if (StringUtils.isNotEmpty(id)) {
List<String> ids = Arrays.asList(id);
fileService.deleteAttachment(ids);
fileService.deleteFileAttachmentByIds(ids);
//删除缺陷文件关联记录
IssueFileExample issueFileExample = new IssueFileExample();
issueFileExample.createCriteria().andFileIdIn(ids);
issueFileMapper.deleteByExample(issueFileExample);
}
}
@ -417,6 +369,7 @@ public class IssuesService {
AbstractIssuePlatform platform = IssueFactory.createPlatform(issuesWithBLOBs.getPlatform(), issuesRequest);
platform.deleteIssue(id);
// 删除缺陷对应的附件
fileService.deleteAttachment(AttachmentType.ISSUE.type(), id);
IssueFileExample issueFileExample = new IssueFileExample();
issueFileExample.createCriteria().andIssueIdEqualTo(id);
List<IssueFile> issueFiles = issueFileMapper.selectByExample(issueFileExample);
@ -427,7 +380,6 @@ public class IssuesService {
fileAttachmentMetadataMapper.deleteByExample(fileAttachmentMetadataExample);
}
issueFileMapper.deleteByExample(issueFileExample);
fileService.deleteAttachment(AttachmentType.ISSUE.type(), id);
}
public IssuesWithBLOBs get(String id) {

View File

@ -50,6 +50,7 @@ public class TestCaseIssueService {
List<String> caseIds = getCaseIdsByIssuesId(request.getIssuesId());
List<TestCaseDTO> list = testCaseService.getTestCaseByIds(caseIds);
testCaseService.addProjectName(list);
testCaseService.addVersionName(list);
return list;
}

View File

@ -618,9 +618,11 @@ public class TestCaseService {
testCaseFileExample.createCriteria().andCaseIdEqualTo(testCaseId);
List<TestCaseFile> testCaseFiles = testCaseFileMapper.selectByExample(testCaseFileExample);
List<String> fileIds = testCaseFiles.stream().map(TestCaseFile::getFileId).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(fileIds)) {
FileAttachmentMetadataExample fileAttachmentMetadataExample = new FileAttachmentMetadataExample();
fileAttachmentMetadataExample.createCriteria().andIdIn(fileIds);
fileAttachmentMetadataMapper.deleteByExample(fileAttachmentMetadataExample);
}
testCaseFileMapper.deleteByExample(testCaseFileExample);
fileService.deleteAttachment(AttachmentType.TEST_CASE.type(), testCaseId);
return testCaseMapper.deleteByPrimaryKey(testCaseId);
@ -867,6 +869,7 @@ public class TestCaseService {
public List<TestCaseDTO> getTestCaseByNotInIssue(QueryTestCaseRequest request) {
List<TestCaseDTO> list = extTestCaseMapper.getTestCaseByNotInIssue(request);
addProjectName(list);
addVersionName(list);
return list;
}
@ -885,6 +888,24 @@ public class TestCaseService {
});
}
public void addVersionName(List<TestCaseDTO> list) {
List<String> versionIds = list.stream().map(TestCase::getVersionId).collect(Collectors.toList());
List<ProjectVersion> versions = new ArrayList<>();
if (CollectionUtils.isNotEmpty(versionIds)) {
ProjectVersionExample example = new ProjectVersionExample();
example.createCriteria().andIdIn(versionIds);
versions = projectVersionMapper.selectByExample(example);
}
Map<String, String> projectVersionMap = versions.stream()
.collect(Collectors.toMap(ProjectVersion::getId, ProjectVersion::getName));
list.forEach(item -> {
String versionName = projectVersionMap.get(item.getVersionId());
if (StringUtils.isNotBlank(versionName)) {
item.setVersionName(versionName);
}
});
}
public List<TestCaseDTO> getReviewCase(QueryTestCaseRequest request) {
setDefaultOrder(request);
request.getOrders().forEach(order -> {
@ -1964,7 +1985,6 @@ public class TestCaseService {
public TestCase refactorSave(EditTestCaseRequest request, List<MultipartFile> files) {
final TestCaseWithBLOBs testCaseWithBLOBs = addTestCase(request);
// 复制用例时复制对应附件数据
if (StringUtils.isNotEmpty(request.getCopyCaseId())) {
TestCaseFileExample example = new TestCaseFileExample();
@ -1980,18 +2000,6 @@ public class TestCaseService {
});
}
}
// 新的附件
if (files != null) {
files.forEach(file -> {
FileAttachmentMetadata fileAttachmentMetadata = fileService.saveAttachment(file, AttachmentType.TEST_CASE.type(), request.getId());
TestCaseFile testCaseFile = new TestCaseFile();
testCaseFile.setFileId(fileAttachmentMetadata.getId());
testCaseFile.setCaseId(request.getId());
testCaseFileMapper.insert(testCaseFile);
});
}
return testCaseWithBLOBs;
}
@ -2039,39 +2047,34 @@ public class TestCaseService {
if (testCaseWithBLOBs == null) {
MSException.throwException(Translator.get("edit_load_test_not_found") + request.getId());
}
if (BooleanUtils.isTrue(request.isHandleAttachment())) {
// 新选择了一个文件删除原来的文件
List<FileMetadata> updatedFiles = request.getUpdatedFileList();
List<FileAttachmentMetadata> originFiles = fileService.getFileAttachmentMetadataByCaseId(request.getId());
List<String> updatedFileIds = updatedFiles.stream().map(FileMetadata::getId).collect(Collectors.toList());
List<String> originFileIds = originFiles.stream().map(FileAttachmentMetadata::getId).collect(Collectors.toList());
// 获得差异的附件ID
List<String> deleteFileIds = ListUtils.subtract(originFileIds, updatedFileIds);
// 删除差异附件记录, 目录下附件文件
fileService.deleteAttachment(deleteFileIds);
fileService.deleteFileAttachmentByIds(deleteFileIds);
//删除用例文件关联记录
if (!CollectionUtils.isEmpty(deleteFileIds)) {
TestCaseFileExample testCaseFileExample = new TestCaseFileExample();
testCaseFileExample.createCriteria().andFileIdIn(deleteFileIds);
testCaseFileMapper.deleteByExample(testCaseFileExample);
this.setNode(request);
return editTestCase(request);
}
public void uploadAttachment(EditTestCaseRequest request, MultipartFile file) {
TestCaseWithBLOBs testCaseWithBLOBs = testCaseMapper.selectByPrimaryKey(request.getId());
if (testCaseWithBLOBs == null) {
MSException.throwException(Translator.get("test_case_attachment_upload_not_found") + request.getId());
}
// 保存新的附件
if (files != null) {
files.forEach(file -> {
FileAttachmentMetadata fileAttachmentMetadata = fileService.saveAttachment(file, AttachmentType.TEST_CASE.type(), request.getId());
TestCaseFile testCaseFile = new TestCaseFile();
testCaseFile.setFileId(fileAttachmentMetadata.getId());
testCaseFile.setCaseId(request.getId());
testCaseFileMapper.insert(testCaseFile);
});
}
}
this.setNode(request);
return editTestCase(request);
public void deleteAttachment(String id) {
// 删除附件记录, 目录下附件文件
if (StringUtils.isNotEmpty(id)) {
List<String> ids = Arrays.asList(id);
fileService.deleteAttachment(ids);
fileService.deleteFileAttachmentByIds(ids);
//删除用例文件关联记录
TestCaseFileExample testCaseFileExample = new TestCaseFileExample();
testCaseFileExample.createCriteria().andFileIdIn(ids);
testCaseFileMapper.deleteByExample(testCaseFileExample);
}
}
public String editTestCase(EditTestCaseRequest request, List<MultipartFile> files) {

View File

@ -34,6 +34,9 @@ not_authorized=not authorized.
login_fail=Login fail
user_apikey_limit=Can have up to 5 api keys
please_logout_current_user=Please logout current user first
#attachment upload
test_case_attachment_upload_not_found=Unable to upload attachment, no associated test case found
issues_attachment_upload_not_found=Unable to upload attachment, no associated issue found
#load test
edit_load_test_not_found=Cannot edit test, test not found=
run_load_test_not_found=Cannot run test, test not found=

View File

@ -34,6 +34,9 @@ user_expires=用户过期
not_authorized=未经授权
user_apikey_limit=最多能有5个Api key
please_logout_current_user=请先登出当前用户
#attachment upload
test_case_attachment_upload_not_found=无法上传附件,未找到相关联用例:
issues_attachment_upload_not_found=无法上传附件,未找到相关联缺陷:
#load test
edit_load_test_not_found=无法编辑测试,未找到测试:
run_load_test_not_found=无法运行测试,未找到测试:

View File

@ -34,6 +34,9 @@ user_expires=用戶過期
not_authorized=未經授權
user_apikey_limit=最多能有5個Api key
please_logout_current_user=請先登出當前用戶
#attachment upload
test_case_attachment_upload_not_found=無法上傳附件,未找到相關用例:
issues_attachment_upload_not_found=無法上傳附件,未找到相關缺陷:
#load test
edit_load_test_not_found=無法編輯測試,未找到測試:
run_load_test_not_found=無法運行測試,未找到測試:

View File

@ -9,12 +9,12 @@
<template v-slot:default="scope">
<el-tooltip class="item" effect="dark" :content="scope.row.name" placement="top">
<el-progress
:color="scope.row.percentage >= 100 ? '' : uploadProgressColor"
:color="scope.row.progress >= 100 ? '' : uploadProgressColor"
type="line"
:format="clearPercentage(scope.row)"
:stroke-width="40"
:text-inside="true"
:percentage="scope.row.percentage >= 100 ? 100 : scope.row.percentage"
:percentage="scope.row.progress >= 100 ? 100 : scope.row.progress"
></el-progress>
</el-tooltip>
</template>
@ -37,7 +37,7 @@
:width="70"
:label="$t('commons.status')">
<template v-slot:default="scope">
<span :class="scope.row.status === '完成' ? 'green' : scope.row.status === '失败' ? 'red' : ''">{{ scope.row.status | formatProgressPercentage}}</span>
<span :class="scope.row.status === 'success' ? 'green' : scope.row.status === 'error' ? 'red' : ''">{{ scope.row.status | formatStatus}}</span>
</template>
</el-table-column>
<el-table-column
@ -50,17 +50,18 @@
:label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="preview(scope.row)" :disabled="!scope.row.id" type="primary"
v-if="scope.row.percentage === 100 && isPreview(scope.row)"
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"
v-if="scope.row.percentage === 100"
v-if="scope.row.progress === 100"
icon="el-icon-download" size="mini" circle/>
<el-button :disabled="readOnly || !isDelete || isCopy" @click="handleDelete(scope.row, scope.$index)" type="danger"
v-if="scope.row.percentage === 100"
<el-button :disabled="readOnly || !isDelete || isCopy || !scope.row.id"
@click="handleDelete(scope.row, scope.$index)" type="danger"
v-if="scope.row.progress === 100"
icon="el-icon-delete" size="mini"
circle/>
<el-button :disabled="readOnly || !isDelete" @click="handleCancel(scope.row, scope.$index)" type="danger"
v-if="scope.row.percentage < 100"
v-if="scope.row.progress < 100"
icon="el-icon-close" size="mini"
circle/>
</template>
@ -152,11 +153,11 @@ export default {
},
},
filters: {
formatProgressPercentage(percentage) {
if (isNaN(percentage)) {
return percentage
formatStatus(status) {
if (isNaN(status)) {
return status === 'success' ? '完成' : '失败'
}
return Math.floor(percentage * 100 / 100) + "%";
return Math.floor(status * 100 / 100) + "%";
}
}
}

View File

@ -829,24 +829,6 @@ export default {
},
getOption(param) {
let formData = new FormData();
if (this.$refs.otherInfo && this.$refs.otherInfo.uploadList) {
this.$refs.otherInfo.uploadList.forEach(f => {
formData.append("file", f);
});
}
if (this.$refs.otherInfo && this.$refs.otherInfo.fileList) {
if (param.isCopy) {
// copyID
param.fileIds = this.$refs.otherInfo.fileList.map(f => f.id);
}
param.updatedFileList = this.$refs.otherInfo.fileList;
} else {
param.fileIds = [];
param.updatedFileList = [];
}
param.handleAttachment = this.isClickAttachmentTab;
let requestJson = JSON.stringify(param, function (key, value) {
return key === "file" ? undefined : value
});

View File

@ -57,17 +57,19 @@
<el-row>
<el-col :span="22">
<el-upload
accept=".jpg,.jpeg,.png,.xlsx,.doc,.pdf,.docx,.txt,.json,.jmx,.side,.mp4,.mov,.dcm,.zip,.rar"
action="#"
multiple
:limit="8"
action=""
:auto-upload="true"
:file-list="fileList"
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="handleUpload"
:on-exceed="handleExceed"
multiple
:limit="8"
:disabled="readOnly && isTestPlanEdit"
:file-list="fileList">
<el-button :disabled="readOnly && isTestPlanEdit" type="primary" size="mini">{{$t('test_track.case.add_attachment')}}</el-button>
:on-success="handleSuccess"
:on-error="handleError"
:disabled="(readOnly && isTestPlanEdit) || type === 'add' || isCopy">
<el-button :disabled="(readOnly && isTestPlanEdit) || type === 'add' || isCopy" type="primary" size="mini">{{$t('test_track.case.add_attachment')}}</el-button>
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
</el-upload>
</el-col>
@ -130,8 +132,10 @@ 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} from "@/common/js/utils";
import {byteToSize, getTypeByFileName, hasLicense} from "@/common/js/utils";
import {TokenKey} from "@/common/js/constants";
import axios from "axios";
import {validateAndSetLicense} from "@/business/permission";
export default {
name: "TestCaseEditOtherInfo",
@ -151,7 +155,6 @@ export default {
return {
result: {},
tabActiveName: "remark",
uploadList: [],
fileList: [],
tableData: [],
demandOptions: [],
@ -163,7 +166,8 @@ export default {
//lazy: true,
//lazyLoad:this.lazyLoad
},
intervalMap: new Map()
intervalMap: new Map(),
cancelFileToken: [],
};
},
computed: {
@ -227,12 +231,11 @@ export default {
this.tabActiveName = "remark";
},
fileValidator(file) {
/// todo:
return file.size > 0;
return file.size < 500 * 1024 * 1024;
},
beforeUpload(file) {
if (!this.fileValidator(file)) {
/// todo:
this.$error(this.$t('load_test.file_size_out_of_bounds') + file.name);
return false;
}
@ -240,22 +243,84 @@ export default {
this.$error(this.$t('load_test.delete_file') + ', name: ' + file.name);
return false;
}
},
handleUpload(e) {
//
let file = e.file;
let user = JSON.parse(localStorage.getItem(TokenKey));
this.tableData.push({
name: file.name,
size: byteToSize(file.size),
updateTime: new Date().getTime(),
percentage: 0,
progress: 0,
status: 0,
creator: user.name
creator: user.name,
type: getTypeByFileName(file.name)
});
this.handleProcess(file);
return true;
//
this.uploadFile(e, (param) => {
this.showProgress(e.file, param)
})
},
handleUpload(uploadResources) {
this.uploadList.push(uploadResources.file);
async uploadFile(param, progressCallback) {
let file = param.file;
let progress = 0;
let formData = new FormData();
let requestJson = JSON.stringify({"id": this.caseId});
formData.append("file", file);
formData.append('request', new Blob([requestJson], {
type: "application/json"
}));
let CancelToken = axios.CancelToken
let self = this;
axios({
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
method: 'post',
url: '/test/case/attachment/upload',
data: formData,
cancelToken: new CancelToken(function executor(c) {
self.cancelFileToken.push({"name": file.name, "cancelFunc": c});
}),
onUploadProgress: progressEvent => { //
progress = (progressEvent.loaded / progressEvent.total * 100) | 0
progressCallback({ progress, status: progress })
}
}).then(response => { //
progress = 100;
param.onSuccess(response);
progressCallback({progress, status: 'success'});
}).catch(error => { //
progress = 100;
progressCallback({progress, status: 'error'});
})
},
showProgress(file, params) {
const { progress, status } = params
const arr = [...this.tableData].map(item => {
if (item.name === file.name) {
item.progress = progress
item.status = status
}
return item
})
this.tableData = [...arr]
},
handleExceed(files, fileList) {
this.$error(this.$t('load_test.file_size_limit'));
},
handleSuccess(response, file, fileList) {
let readyFiles = fileList.filter(item => item.status === 'ready')
if (readyFiles.length === 0 ) {
this.getFileMetaData();
}
},
handleError(err, file, fileList) {
let readyFiles = fileList.filter(item => item.status === 'ready')
if (readyFiles.length === 0 ) {
this.getFileMetaData();
}
},
handleDownload(file) {
let data = {
@ -297,52 +362,27 @@ export default {
}
});
},
handleCancel(file, index) {
this.fileList.splice(index, 1);
let i = this.uploadList.findIndex(upLoadFile => upLoadFile.name === file.name);
if (i > -1) {
this.uploadList.splice(i, 1);
}
let cancelFile = this.tableData.filter(f => f.name === file.name)[0];
clearInterval(this.intervalMap.get(cancelFile.name));
cancelFile.percentage = 100;
cancelFile.status = this.$t('notice.result.EXECUTE_FAILED');
},
_handleDelete(file, index) {
this.fileList.splice(index, 1);
this.tableData.splice(index, 1);
let i = this.uploadList.findIndex(upLoadFile => upLoadFile.name === file.name);
if (i > -1) {
this.uploadList.splice(i, 1);
}
this.$get('/test/case/attachment/delete/' + file.id, () => {
this.$success(this.$t('commons.delete_success'));
this.getFileMetaData();
});
},
handleExceed() {
this.$error(this.$t('load_test.file_size_limit'));
},
handleProcess(file) {
let currentUploadFile = this.tableData.filter(f => f.name === file.name)[0];
const interval = setInterval(() => {
let randomNum = Math.floor(Math.random() * 10);
if (currentUploadFile.percentage + randomNum > 100) {
clearInterval(interval)
currentUploadFile.percentage = 100;
currentUploadFile.status = this.$t('notice.result.EXECUTE_COMPLETED')
return
}
currentUploadFile.percentage += randomNum;
currentUploadFile.status += randomNum;
}, file.size > 1024 * 1024 ? 200 : 100)
this.intervalMap.set(currentUploadFile.name, interval);
handleCancel(file, index) {
this.fileList.splice(index, 1);
let cancelToken = this.cancelFileToken.filter(f => f.name === file.name)[0];
cancelToken.cancelFunc();
let cancelFile = this.tableData.filter(f => f.name === file.name)[0];
cancelFile.progress = 100;
cancelFile.status = 'error';
},
getFileMetaData(id) {
this.$emit("update:isClickAttachmentTab", true);
// id
if (this.uploadList && this.uploadList.length > 0 && !id) {
return;
}
this.fileList = [];
this.tableData = [];
this.uploadList = [];
let testCaseId;
if (this.isCopy) {
testCaseId = this.copyCaseId
@ -361,8 +401,8 @@ export default {
this.tableData = JSON.parse(JSON.stringify(files));
this.tableData.map(f => {
f.size = byteToSize(f.size);
f.status = this.$t('notice.result.EXECUTE_COMPLETED');
f.percentage = 100
f.status = 'success';
f.progress = 100
});
});
}

View File

@ -107,16 +107,19 @@
<el-row>
<el-col :span="22">
<el-upload
accept=".jpg,.jpeg,.png,.xlsx,.doc,.pdf,.docx,.txt,.json,.jmx,.side,.mp4,.mov,.dcm,.zip,.rar"
action="#"
multiple
:limit="8"
action=""
:auto-upload="true"
:file-list="fileList"
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="handleUpload"
:on-exceed="handleExceed"
multiple
:limit="8"
:file-list="fileList">
<el-button type="primary" size="mini">{{$t('test_track.case.add_attachment')}}</el-button>
:on-success="handleSuccess"
:on-error="handleError"
:disabled="type === 'add' || type === 'copy'">
<el-button type="primary" :disabled="type === 'add' || type === 'copy'" size="mini">{{$t('test_track.case.add_attachment')}}</el-button>
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
</el-upload>
</el-col>
@ -197,7 +200,7 @@ import {
getCurrentProjectID,
getCurrentUser,
getCurrentUserId,
getCurrentWorkspaceId,
getCurrentWorkspaceId, getTypeByFileName, hasLicense,
} from "@/common/js/utils";
import {enableThirdPartTemplate, getIssuePartTemplateWithProject, getPlatformTransitions} from "@/network/Issue";
import CustomFiledFormItem from "@/business/components/common/components/form/CustomFiledFormItem";
@ -207,6 +210,7 @@ import ReviewCommentItem from "@/business/components/track/review/commom/ReviewC
import {TokenKey} from "@/common/js/constants";
import {Message} from "element-ui";
import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment";
import axios from "axios";
const {getIssuesById} = require("@/network/Issue");
@ -306,12 +310,11 @@ export default {
comments: [],
richTextDefaultOpen: 'preview',
tabActiveName: 'relateTestCase',
uploadList: [],
fileList: [],
tableData: [],
readOnly: false,
isDelete: true,
intervalMap: new Map()
cancelFileToken: [],
};
},
props: {
@ -337,6 +340,15 @@ export default {
return enableThirdPartTemplate(this.currentProject);
},
},
watch: {
tabActiveName() {
if (this.tabActiveName === 'attachment') {
if (this.type === 'edit' && this.issueId) {
this.getFileMetaData(this.issueId);
}
}
},
},
methods: {
resetForm() {
this.form = {
@ -356,12 +368,14 @@ export default {
}
},
open(data, type) {
this.tabActiveName = 'relateTestCase'
this.showFollow = false;
this.result.loading = true;
this.type = type;
this.richTextDefaultOpen = this.type === 'edit' ? 'preview' : 'edit';
if (this.$refs.testCaseIssueList) {
this.$refs.testCaseIssueList.clear();
this.$refs.testCaseIssueList.isXpack = hasLicense();
}
this.$nextTick(() => {
getIssuePartTemplateWithProject((template, project) => {
@ -440,7 +454,6 @@ export default {
initEdit(data) {
this.tableData = [];
this.fileList = [];
this.uploadList = [];
if (data) {
Object.assign(this.form, data);
if (!(data.options instanceof Array)) {
@ -564,21 +577,6 @@ export default {
},
getOption(param) {
let formData = new FormData();
if (this.uploadList) {
this.uploadList.forEach(f => {
formData.append("file", f);
});
}
if (this.fileList) {
// copyID TODO
param.updatedFileList = this.fileList;
} else {
param.fileIds = [];
param.updatedFileList = [];
}
let requestJson = JSON.stringify(param, function (key, value) {
return key === "file" ? undefined : value
});
@ -627,12 +625,11 @@ export default {
}
},
fileValidator(file) {
/// todo:
return file.size > 0;
return file.size < 500 * 1024 * 1024;
},
beforeUpload(file) {
if (!this.fileValidator(file)) {
/// todo:
this.$error(this.$t('load_test.file_size_out_of_bounds') + file.name);
return false;
}
@ -640,22 +637,84 @@ export default {
this.$error(this.$t('load_test.delete_file') + ', name: ' + file.name);
return false;
}
},
handleUpload(e) {
//
let file = e.file;
let user = JSON.parse(localStorage.getItem(TokenKey));
this.tableData.push({
name: file.name,
size: byteToSize(file.size),
updateTime: new Date().getTime(),
percentage: 0,
progress: 0,
status: 0,
creator: user.name
creator: user.name,
type: getTypeByFileName(file.name)
});
this.handleProcess(file);
return true;
//
this.uploadFile(e, (param) => {
this.showProgress(e.file, param)
})
},
handleUpload(uploadResources) {
this.uploadList.push(uploadResources.file);
async uploadFile(param, progressCallback) {
let file = param.file;
let progress = 0;
let formData = new FormData();
let requestJson = JSON.stringify({"id": this.issueId});
formData.append("file", file);
formData.append('request', new Blob([requestJson], {
type: "application/json"
}));
let CancelToken = axios.CancelToken
let self = this;
axios({
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
method: 'post',
url: '/issues/attachment/upload',
data: formData,
cancelToken: new CancelToken(function executor(c) {
self.cancelFileToken.push({"name": file.name, "cancelFunc": c});
}),
onUploadProgress: progressEvent => { //
progress = (progressEvent.loaded / progressEvent.total * 100) | 0
progressCallback({ progress, status: progress })
}
}).then(response => { //
progress = 100;
param.onSuccess(response);
progressCallback({progress, status: 'success'});
}).catch(({error}) => { //
progress = 100;
progressCallback({progress, status: 'error'});
})
},
showProgress(file, params) {
const { progress, status } = params
const arr = [...this.tableData].map(item => {
if (item.name === file.name) {
item.progress = progress
item.status = status
}
return item
})
this.tableData = [...arr]
},
handleExceed(files, fileList) {
this.$error(this.$t('load_test.file_size_limit'));
},
handleSuccess(response, file, fileList) {
let readyFiles = fileList.filter(item => item.status === 'success')
if (readyFiles.length === fileList.length ) {
this.getFileMetaData(this.issueId);
}
},
handleError(err, file, fileList) {
let readyFiles = fileList.filter(item => item.status === 'success')
if (readyFiles.length === fileList.length ) {
this.getFileMetaData(this.issueId);
}
},
handleDownload(file) {
let data = {
@ -697,51 +756,26 @@ export default {
}
});
},
handleCancel(file, index) {
this.fileList.splice(index, 1);
let i = this.uploadList.findIndex(upLoadFile => upLoadFile.name === file.name);
if (i > -1) {
this.uploadList.splice(i, 1);
}
let cancelFile = this.tableData.filter(f => f.name === file.name)[0];
clearInterval(this.intervalMap.get(cancelFile.name));
cancelFile.percentage = 100;
cancelFile.status = this.$t('notice.result.EXECUTE_FAILED');
},
_handleDelete(file, index) {
this.fileList.splice(index, 1);
this.tableData.splice(index, 1);
let i = this.uploadList.findIndex(upLoadFile => upLoadFile.name === file.name);
if (i > -1) {
this.uploadList.splice(i, 1);
}
this.$get('/issues/attachment/delete/' + file.id, () => {
this.$success(this.$t('commons.delete_success'));
this.getFileMetaData(this.issueId);
});
},
handleExceed() {
this.$error(this.$t('load_test.file_size_limit'));
},
handleProcess(file) {
let currentUploadFile = this.tableData.filter(f => f.name === file.name)[0];
const interval = setInterval(() => {
let randomNum = Math.floor(Math.random() * 10);
if (currentUploadFile.percentage + randomNum > 100) {
clearInterval(interval)
currentUploadFile.percentage = 100;
currentUploadFile.status = this.$t('notice.result.EXECUTE_COMPLETED')
return
}
currentUploadFile.percentage += randomNum;
currentUploadFile.status += randomNum;
}, file.size > 1024 * 1024 ? 200 : 100)
this.intervalMap.set(currentUploadFile.name, interval);
handleCancel(file, index) {
this.fileList.splice(index, 1);
let cancelToken = this.cancelFileToken.filter(f => f.name === file.name)[0];
cancelToken.cancelFunc();
let cancelFile = this.tableData.filter(f => f.name === file.name)[0];
cancelFile.progress = 100;
cancelFile.status = 'error';
},
getFileMetaData(id) {
// id
if (this.uploadList && this.uploadList.length > 0 && !id) {
return;
}
this.fileList = [];
this.tableData = [];
this.uploadList = [];
if (id) {
this.result = this.$get("issues/file/attachmentMetadata/" + id, response => {
let files = response.data;
@ -753,8 +787,8 @@ export default {
this.tableData = JSON.parse(JSON.stringify(files));
this.tableData.map(f => {
f.size = byteToSize(f.size);
f.status = this.$t('notice.result.EXECUTE_COMPLETED');
f.percentage = 100
f.status = 'success';
f.progress = 100
});
});
}

View File

@ -47,6 +47,10 @@
prop="projectName">
</ms-table-column>
<ms-table-column v-if="isXpack"
:label="$t('commons.version')"
prop="versionName">
</ms-table-column>
</ms-table>
<test-case-relate-list
@ -64,6 +68,7 @@ import MsTableColumn from "@/business/components/common/components/table/MsTable
import PriorityTableItem from "@/business/components/track/common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "@/business/components/track/common/tableItems/planview/TypeTableItem";
import TestCaseRelateList from "@/business/components/track/issue/TestCaseRelateList";
import {hasLicense} from "@/common/js/utils";
export default {
name: "TestCaseIssueList",
components: {TestCaseRelateList, TypeTableItem, PriorityTableItem, MsTableColumn, MsTable},
@ -80,6 +85,7 @@ export default {
exec: this.handleDelete
}
],
isXpack: false,
};
},
props: {

View File

@ -589,3 +589,13 @@ export function byteToSize(bytes) {
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}
export function getTypeByFileName(filename) {
if (filename === '') {
return '';
}
let type = filename.substr(filename.lastIndexOf('.') + 1);
return type.toUpperCase();
}

View File

@ -1034,6 +1034,7 @@ export default {
upload_type: 'Only JMX/CSV/JAR files can be uploaded',
related_file_not_found: "No related test file found!",
delete_file_confirm: 'Confirm delete 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!",
thread_num: 'VUs:',
@ -2148,8 +2149,8 @@ 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: "Only jpg, jpeg, png, docx, doc, pdf, xlsx, txt, json, jmx, side, mp4, mov, dcm, zip, rar files can be uploaded",
add_attachment: "Add Attachment",
upload_tip: "file size limit[0-500MB]",
add_attachment: "Add",
attachment: "Attachment",
upload_time: "Upload Time",
total: "Total Case",

View File

@ -1041,6 +1041,7 @@ export default {
related_file_not_found: "未找到关联的测试文件!",
delete_file_confirm: '确认删除文件: ',
file_size_limit: "文件个数超出限制!",
file_size_out_of_bounds: "文件大小超出范围, 文件名称: ",
delete_file: "文件已存在,请先删除同名文件!",
thread_num: '并发用户数',
input_thread_num: '请输入线程数',
@ -2145,8 +2146,8 @@ export default {
cancel_relevance_project: "取消项目关联会同时取消该项目下已关联的测试用例",
img_loading_fail: "图片加载失败",
pdf_loading_fail: "PDF加载失败",
upload_tip: "只能上传jpg、jpeg、png、docx、doc、pdf、xlsx、txt、json、jmx、side、mp4、mov、dcm、zip、rar文件",
add_attachment: "添加附件",
upload_tip: "文件大小限制[0-500MB]",
add_attachment: "添加",
attachment: "附件",
upload_time: "上传时间",
total: "用例总数",

View File

@ -1038,6 +1038,7 @@ export default {
related_file_not_found: "未找到關聯的測試文件!",
delete_file_confirm: '確認刪除文件: ',
file_size_limit: "文件個數超出限製!",
file_size_out_of_bounds: "文件大小超出範圍, 文件名称: ",
delete_file: "文件已存在,請先刪除同名文件!",
thread_num: '並發用戶數',
input_thread_num: '請輸入線程數',
@ -2140,8 +2141,8 @@ export default {
cancel_relevance_project: "取消項目關聯會同時取消該項目下已關聯的測試用例",
img_loading_fail: "圖片加載失敗",
pdf_loading_fail: "PDF加載失敗",
upload_tip: "只能上傳jpg、jpeg、png、docx、doc、pdf、xlsx、txt、json、jmx、side、mp4、mov、dcm、zip、rar文件",
add_attachment: "添加附件",
upload_tip: "文件大小限制[0-500MB]",
add_attachment: "添加",
attachment: "附件",
upload_time: "上傳時間",
total: "用例總數",