feat(测试跟踪): 缺陷管理附件同步功能
--story=1008034 --user=宋昌昌 【Bug转需求】[缺陷管理]-github#9580-jira集成,缺陷模版使用 jira 缺陷模版,字段没有同步全 https://www.tapd.cn/55049933/
This commit is contained in:
parent
863b8caf0e
commit
ec1434964c
|
@ -27,8 +27,7 @@ public class FileUtils {
|
|||
public static final String MD_IMAGE_DIR = "/opt/metersphere/data/image/markdown";
|
||||
public static final String UI_IMAGE_DIR = "/opt/metersphere/data/image/ui/screenshots";
|
||||
public static final String ATTACHMENT_DIR = "/opt/metersphere/data/attachment";
|
||||
public static final String TEST_CASE_ATTACHMENT_DIR = "/opt/metersphere/data/attachment/testcase";
|
||||
public static final String ISSUE_ATTACHMENT_DIR = "/opt/metersphere/data/attachment/issue";
|
||||
public static final String ATTACHMENT_TMP_DIR = "/opt/metersphere/data/attachment/tmp";
|
||||
|
||||
|
||||
public static byte[] listBytesToZip(Map<String, byte[]> mapReport) {
|
||||
|
|
|
@ -14,8 +14,7 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -137,6 +136,38 @@ public class FileService {
|
|||
return fileAttachmentMetadata;
|
||||
}
|
||||
|
||||
public FileAttachmentMetadata saveAttachmentByBytes(byte[] bytes, String attachmentType, String belongId, String attachmentName) {
|
||||
String uploadPath = FileUtils.ATTACHMENT_DIR + "/" + attachmentType + "/" + belongId;
|
||||
File parentFile = new File(uploadPath);
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdir();
|
||||
}
|
||||
try (OutputStream os = new FileOutputStream(uploadPath + "/" + attachmentName)){
|
||||
InputStream in = new ByteArrayInputStream(bytes);
|
||||
int len = 0;
|
||||
byte[] buf = new byte[1024];
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
os.write(buf, 0, len);
|
||||
}
|
||||
os.flush();
|
||||
|
||||
final FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
|
||||
fileAttachmentMetadata.setId(UUID.randomUUID().toString());
|
||||
fileAttachmentMetadata.setName(attachmentName);
|
||||
fileAttachmentMetadata.setType(getFileType(attachmentName).name());
|
||||
fileAttachmentMetadata.setSize(Integer.valueOf(bytes.length).longValue());
|
||||
fileAttachmentMetadata.setCreateTime(System.currentTimeMillis());
|
||||
fileAttachmentMetadata.setUpdateTime(System.currentTimeMillis());
|
||||
fileAttachmentMetadata.setCreator(SessionUtils.getUser().getName());
|
||||
fileAttachmentMetadata.setFilePath(uploadPath);
|
||||
fileAttachmentMetadataMapper.insert(fileAttachmentMetadata);
|
||||
return fileAttachmentMetadata;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void deleteAttachment(List<String> ids) {
|
||||
for (String id : ids) {
|
||||
FileAttachmentMetadata fileAttachmentMetadata = fileAttachmentMetadataMapper.selectByPrimaryKey(id);
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
|
|||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.IssueFileMapper;
|
||||
import io.metersphere.base.mapper.IssuesMapper;
|
||||
import io.metersphere.base.mapper.ProjectMapper;
|
||||
import io.metersphere.base.mapper.TestCaseIssuesMapper;
|
||||
|
@ -64,6 +65,9 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
|
|||
protected boolean isThirdPartTemplate;
|
||||
protected CustomFieldIssuesService customFieldIssuesService;
|
||||
protected CustomFieldService customFieldService;
|
||||
protected IssuesService issuesService;
|
||||
protected FileService fileService;
|
||||
protected IssueFileMapper issueFileMapper;
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
|
@ -90,6 +94,9 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
|
|||
this.testCaseIssueService = CommonBeanFactory.getBean(TestCaseIssueService.class);
|
||||
this.customFieldIssuesService = CommonBeanFactory.getBean(CustomFieldIssuesService.class);
|
||||
this.customFieldService = CommonBeanFactory.getBean(CustomFieldService.class);
|
||||
this.issuesService = CommonBeanFactory.getBean(IssuesService.class);
|
||||
this.fileService = CommonBeanFactory.getBean(FileService.class);
|
||||
this.issueFileMapper = CommonBeanFactory.getBean(IssueFileMapper.class);
|
||||
}
|
||||
|
||||
protected String getPlatformConfig(String platform) {
|
||||
|
|
|
@ -11,6 +11,7 @@ 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;
|
||||
|
||||
|
@ -31,13 +32,13 @@ public interface IssuesPlatform {
|
|||
*
|
||||
* @param issuesRequest issueRequest
|
||||
*/
|
||||
IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest);
|
||||
IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files);
|
||||
|
||||
/**
|
||||
* 更新缺陷
|
||||
* @param request
|
||||
*/
|
||||
void updateIssue(IssuesUpdateRequest request);
|
||||
void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files);
|
||||
|
||||
/**
|
||||
* 删除缺陷平台缺陷
|
||||
|
|
|
@ -2,15 +2,15 @@ package io.metersphere.track.issue;
|
|||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.base.domain.IssuesDao;
|
||||
import io.metersphere.base.domain.IssuesWithBLOBs;
|
||||
import io.metersphere.base.domain.Project;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.domain.ext.CustomFieldResource;
|
||||
import io.metersphere.commons.constants.AttachmentType;
|
||||
import io.metersphere.commons.constants.CustomFieldType;
|
||||
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,8 +31,10 @@ 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;
|
||||
|
@ -225,7 +227,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) {
|
||||
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
|
||||
|
||||
setUserConfig();
|
||||
Project project = getProject();
|
||||
|
@ -237,6 +239,20 @@ 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);
|
||||
|
@ -463,7 +479,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateIssue(IssuesUpdateRequest request) {
|
||||
public void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files) {
|
||||
setUserConfig();
|
||||
Project project = getProject();
|
||||
List<File> imageFiles = getImageFiles(request);
|
||||
|
@ -471,18 +487,27 @@ 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());
|
||||
}
|
||||
});
|
||||
Set<String> attachmentNames = new HashSet<>();
|
||||
// 更新附件
|
||||
JiraIssue jiraIssue = jiraClientV2.getIssues(request.getPlatformId());
|
||||
JSONObject fields = jiraIssue.getFields();
|
||||
JSONArray attachments = fields.getJSONArray("attachment");
|
||||
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)) {
|
||||
if (!request.getDescription().contains(filename) && deleteAttachmentNames.contains(filename)) {
|
||||
String fileId = attachment.getString("id");
|
||||
jiraClientV2.deleteAttachment(fileId);
|
||||
}
|
||||
|
@ -496,6 +521,20 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
|||
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 {
|
||||
|
@ -554,6 +593,8 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
|||
List<CustomFieldResource> customFieldResource = customFieldService.getCustomFieldResource(customFields);
|
||||
customFieldMap.put(item.getId(), customFieldResource);
|
||||
issuesMapper.updateByPrimaryKeySelective(item);
|
||||
// 同步第三方平台系统附件字段
|
||||
syncJiraIssueAttachments(item, jiraClientV2.getIssues(item.getPlatformId()));
|
||||
} catch (HttpClientErrorException e) {
|
||||
if (e.getRawStatusCode() == 404) {
|
||||
// 标记成删除
|
||||
|
@ -854,4 +895,23 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
|||
public ResponseEntity proxyForGet(String url, Class responseEntityClazz) {
|
||||
return jiraClientV2.proxyForGet(url, responseEntityClazz);
|
||||
}
|
||||
|
||||
public void syncJiraIssueAttachments(IssuesWithBLOBs issue, JiraIssue jiraIssue) {
|
||||
issuesService.deleteIssueAttachments(issue.getId());
|
||||
JSONArray attachments = jiraIssue.getFields().getJSONArray("attachment");
|
||||
if (CollectionUtils.isEmpty(attachments)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < attachments.size(); i++) {
|
||||
JSONObject attachment = attachments.getJSONObject(i);
|
||||
String id = attachment.getString("id");
|
||||
byte[] content = jiraClientV2.getAttachmentContent(id);
|
||||
String filename = attachment.getString("filename");
|
||||
FileAttachmentMetadata fileAttachmentMetadata = fileService.saveAttachmentByBytes(content, AttachmentType.ISSUE.type(), issue.getId(), filename);
|
||||
IssueFile issueFile = new IssueFile();
|
||||
issueFile.setIssueId(issue.getId());
|
||||
issueFile.setFileId(fileAttachmentMetadata.getId());
|
||||
issueFileMapper.insert(issueFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ 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;
|
||||
|
@ -41,7 +42,7 @@ public class LocalPlatform extends LocalAbstractPlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) {
|
||||
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
|
||||
String issueStatus = "new";
|
||||
if (StringUtils.isNotBlank(issuesRequest.getCustomFields())) {
|
||||
List<CustomFieldItemDTO> customFields = issuesRequest.getRequestFields();
|
||||
|
@ -75,7 +76,7 @@ public class LocalPlatform extends LocalAbstractPlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateIssue(IssuesUpdateRequest request) {
|
||||
public void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files) {
|
||||
handleIssueUpdate(request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ 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;
|
||||
|
@ -88,7 +89,7 @@ public class TapdPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) {
|
||||
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
|
||||
|
||||
MultiValueMap<String, Object> param = buildUpdateParam(issuesRequest);
|
||||
TapdBug bug = tapdClient.addIssue(param);
|
||||
|
@ -109,7 +110,7 @@ public class TapdPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateIssue(IssuesUpdateRequest request) {
|
||||
public void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files) {
|
||||
MultiValueMap<String, Object> param = buildUpdateParam(request);
|
||||
param.add("id", request.getPlatformId());
|
||||
handleIssueUpdate(request);
|
||||
|
|
|
@ -37,6 +37,7 @@ 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;
|
||||
|
@ -197,7 +198,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) {
|
||||
public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest, List<MultipartFile> files) {
|
||||
setUserConfig();
|
||||
|
||||
MultiValueMap<String, Object> param = buildUpdateParam(issuesRequest);
|
||||
|
@ -228,7 +229,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateIssue(IssuesUpdateRequest request) {
|
||||
public void updateIssue(IssuesUpdateRequest request, List<MultipartFile> files) {
|
||||
setUserConfig();
|
||||
MultiValueMap<String, Object> param = buildUpdateParam(request);
|
||||
if (request.getTransitions() != null) {
|
||||
|
|
|
@ -257,6 +257,14 @@ public abstract class JiraAbstractClient extends BaseClient {
|
|||
return (JiraIssueListResponse)getResultForObject(JiraIssueListResponse.class, responseEntity);
|
||||
}
|
||||
|
||||
public byte[] getAttachmentContent(String contentId) {
|
||||
ResponseEntity<byte[]> responseEntity;
|
||||
String url = getBaseUrl() + "/attachment/content/{1}";
|
||||
responseEntity = restTemplate.exchange(url,
|
||||
HttpMethod.GET, getAuthHttpEntity(), byte[].class, contentId);
|
||||
return responseEntity.getBody();
|
||||
}
|
||||
|
||||
public JiraIssueListResponse getProjectIssuesAttachment(int startAt, int maxResults, String projectKey, String issueType) {
|
||||
return getProjectIssues(startAt, maxResults, projectKey, issueType, "attachment");
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ public class IssuesService {
|
|||
List<AbstractIssuePlatform> platformList = getAddPlatforms(issuesRequest);
|
||||
IssuesWithBLOBs issues = null;
|
||||
for (AbstractIssuePlatform platform : platformList) {
|
||||
issues = platform.addIssue(issuesRequest);
|
||||
issues = platform.addIssue(issuesRequest, null);
|
||||
}
|
||||
if (issuesRequest.getIsPlanEdit()) {
|
||||
issuesRequest.getAddResourceIds().forEach(l -> {
|
||||
|
@ -131,7 +131,7 @@ public class IssuesService {
|
|||
issuesRequest.getId();
|
||||
List<AbstractIssuePlatform> platformList = getUpdatePlatforms(issuesRequest);
|
||||
platformList.forEach(platform -> {
|
||||
platform.updateIssue(issuesRequest);
|
||||
platform.updateIssue(issuesRequest, null);
|
||||
});
|
||||
customFieldIssuesService.editFields(issuesRequest.getId(), issuesRequest.getEditFields());
|
||||
customFieldIssuesService.addFields(issuesRequest.getId(), issuesRequest.getAddFields());
|
||||
|
@ -142,7 +142,7 @@ public class IssuesService {
|
|||
List<AbstractIssuePlatform> platformList = getAddPlatforms(issuesRequest);
|
||||
IssuesWithBLOBs issues = null;
|
||||
for (AbstractIssuePlatform platform : platformList) {
|
||||
issues = platform.addIssue(issuesRequest);
|
||||
issues = platform.addIssue(issuesRequest, files);
|
||||
}
|
||||
if (issuesRequest.getIsPlanEdit()) {
|
||||
issuesRequest.getAddResourceIds().forEach(l -> {
|
||||
|
@ -187,7 +187,7 @@ public class IssuesService {
|
|||
issuesRequest.getId();
|
||||
List<AbstractIssuePlatform> platformList = getUpdatePlatforms(issuesRequest);
|
||||
platformList.forEach(platform -> {
|
||||
platform.updateIssue(issuesRequest);
|
||||
platform.updateIssue(issuesRequest, files);
|
||||
});
|
||||
customFieldIssuesService.editFields(issuesRequest.getId(), issuesRequest.getEditFields());
|
||||
customFieldIssuesService.addFields(issuesRequest.getId(), issuesRequest.getAddFields());
|
||||
|
@ -929,4 +929,17 @@ public class IssuesService {
|
|||
|
||||
return platformStatusDTOS;
|
||||
}
|
||||
|
||||
public void deleteIssueAttachments(String issueId) {
|
||||
fileService.deleteAttachment(AttachmentType.ISSUE.type(), issueId);
|
||||
IssueFileExample example = new IssueFileExample();
|
||||
example.createCriteria().andIssueIdEqualTo(issueId);
|
||||
List<IssueFile> issueFiles = issueFileMapper.selectByExample(example);
|
||||
if (issueFiles.size() == 0) {
|
||||
return;
|
||||
}
|
||||
List<String> ids = issueFiles.stream().map(IssueFile::getFileId).collect(Collectors.toList());
|
||||
fileService.deleteFileAttachmentByIds(ids);
|
||||
issueFileMapper.deleteByExample(example);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue