feat(测试计划): 补充计划报告分享功能

This commit is contained in:
song-cc-rock 2024-05-15 15:50:05 +08:00 committed by Craftsman
parent cd70685204
commit 02a237af85
15 changed files with 299 additions and 17 deletions

View File

@ -312,8 +312,8 @@ public class PermissionConstants {
public static final String TEST_PLAN_READ_ASSOCIATION = "PROJECT_TEST_PLAN:READ+ASSOCIATION";
public static final String TEST_PLAN_REPORT_READ = "PROJECT_TEST_PLAN_REPORT:READ";
public static final String TEST_PLAN_REPORT_READ_ADD = "PROJECT_TEST_PLAN_REPORT:READ+ADD";
public static final String TEST_PLAN_REPORT_READ_UPDATE = "PROJECT_TEST_PLAN_REPORT:READ+UPDATE";
public static final String TEST_PLAN_REPORT_READ_SHARE = "PROJECT_TEST_PLAN_REPORT:READ+SHARE";
public static final String TEST_PLAN_REPORT_READ_DELETE = "PROJECT_TEST_PLAN_REPORT:READ+DELETE";
/*------ end: TEST_PLAN ------*/

View File

@ -1,4 +1,4 @@
package io.metersphere.api.constants;
package io.metersphere.sdk.constants;
/**
* @author: LAN

View File

@ -1,6 +1,5 @@
package io.metersphere.api.service;
import io.metersphere.api.constants.ShareInfoType;
import io.metersphere.api.domain.ApiReport;
import io.metersphere.api.domain.ApiScenarioReport;
import io.metersphere.api.dto.share.ApiReportShareDTO;
@ -11,6 +10,7 @@ import io.metersphere.api.mapper.ApiScenarioReportMapper;
import io.metersphere.project.domain.ProjectApplication;
import io.metersphere.project.domain.ProjectApplicationExample;
import io.metersphere.project.mapper.ProjectApplicationMapper;
import io.metersphere.sdk.constants.ShareInfoType;
import io.metersphere.sdk.domain.ShareInfo;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.mapper.ShareInfoMapper;

View File

@ -1,13 +1,13 @@
package io.metersphere.api.service;
import io.metersphere.api.constants.ApiDefinitionDocType;
import io.metersphere.api.constants.ShareInfoType;
import io.metersphere.api.dto.definition.ApiDefinitionDocDTO;
import io.metersphere.api.dto.definition.ApiDefinitionDocRequest;
import io.metersphere.api.dto.share.ShareInfoDTO;
import io.metersphere.api.mapper.ExtShareInfoMapper;
import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.sdk.constants.ShareInfoType;
import io.metersphere.sdk.domain.ShareInfo;
import io.metersphere.sdk.mapper.ShareInfoMapper;
import io.metersphere.sdk.util.BeanUtils;

View File

@ -1,6 +1,5 @@
package io.metersphere.api.controller;
import io.metersphere.api.constants.ShareInfoType;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.ApiReportBatchRequest;
import io.metersphere.api.dto.definition.ApiReportDTO;
@ -17,10 +16,7 @@ import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.project.domain.ProjectApplication;
import io.metersphere.project.domain.ProjectApplicationExample;
import io.metersphere.project.mapper.ProjectApplicationMapper;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.ApiReportStatus;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.domain.EnvironmentGroup;

View File

@ -1,7 +1,6 @@
package io.metersphere.api.controller;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.constants.ShareInfoType;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.ApiReportBatchRequest;
import io.metersphere.api.dto.definition.ApiReportPageRequest;
@ -21,6 +20,7 @@ import io.metersphere.project.mapper.ProjectApplicationMapper;
import io.metersphere.sdk.constants.ApiReportStatus;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.constants.ShareInfoType;
import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.domain.EnvironmentGroup;

View File

@ -1,7 +1,6 @@
package io.metersphere.api.controller;
import io.metersphere.api.constants.ApiDefinitionDocType;
import io.metersphere.api.constants.ShareInfoType;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionBlob;
@ -17,6 +16,7 @@ import io.metersphere.api.mapper.ExtShareInfoMapper;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.ShareInfoType;
import io.metersphere.sdk.domain.ShareInfo;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.mapper.ShareInfoMapper;

View File

@ -0,0 +1,43 @@
package io.metersphere.plan.controller;
import io.metersphere.plan.dto.TestPlanShareInfo;
import io.metersphere.plan.dto.request.TestPlanReportShareRequest;
import io.metersphere.plan.dto.response.TestPlanShareResponse;
import io.metersphere.plan.service.TestPlanReportShareService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.SessionUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/test-plan/report/share")
@Tag(name = "测试计划-分享")
public class TestPlanReportShareController {
@Resource
private TestPlanReportShareService testPlanReportShareService;
@PostMapping("/gen")
@Operation(summary = "测试计划-报告-分享")
@RequiresPermissions(PermissionConstants.TEST_PLAN_REPORT_READ_SHARE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public TestPlanShareInfo genReportShareInfo(@RequestBody TestPlanReportShareRequest request) {
return testPlanReportShareService.gen(request, SessionUtils.getUserId());
}
@GetMapping("/get/{id}")
@Operation(summary = "接口测试-接口报告-获取分享链接")
public TestPlanShareResponse get(@PathVariable String id) {
return testPlanReportShareService.get(id);
}
@GetMapping("/get-share-time/{id}")
@Operation(summary = "接口测试-接口报告-获取分享链接的有效时间")
public String getShareTime(@PathVariable String id) {
return testPlanReportShareService.getShareTime(id);
}
}

View File

@ -0,0 +1,14 @@
package io.metersphere.plan.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class TestPlanShareInfo {
@Schema(description = "分享ID")
private String id;
@Schema(description = "分享链接")
private String shareUrl;
}

View File

@ -0,0 +1,27 @@
package io.metersphere.plan.dto.request;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Data
public class TestPlanReportShareRequest {
@Schema(description = "分享类型 资源的类型 Single, Batch, API_SHARE_REPORT, TEST_PLAN_SHARE_REPORT")
private String shareType;
@Schema(description = "语言")
private String lang;
@Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{share_info.project_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{share_info.project_id.length_range}", groups = {Created.class, Updated.class})
private String projectId;
@Schema(description = "分享扩展数据 资源的ID" ,requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{share_info.project_id.not_blank}")
private String reportId;
}

View File

@ -0,0 +1,25 @@
package io.metersphere.plan.dto.response;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class TestPlanShareResponse {
@Schema(description = "分享id")
private String id;
@Schema(description = "分享类型 资源的类型 Single, Batch, API_SHARE_REPORT, TEST_PLAN_SHARE_REPORT")
private String shareType;
@Schema(description = "语言")
private String lang;
private String projectId;
@Schema(description = "分享扩展数据 资源ID" ,requiredMode = Schema.RequiredMode.REQUIRED)
private String reportId;
@Schema(description = "分享链接是否被删")
private Boolean deleted;
}

View File

@ -0,0 +1,133 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanReport;
import io.metersphere.plan.dto.TestPlanShareInfo;
import io.metersphere.plan.dto.request.TestPlanReportShareRequest;
import io.metersphere.plan.dto.response.TestPlanShareResponse;
import io.metersphere.plan.mapper.TestPlanReportMapper;
import io.metersphere.project.domain.ProjectApplication;
import io.metersphere.project.domain.ProjectApplicationExample;
import io.metersphere.project.mapper.ProjectApplicationMapper;
import io.metersphere.sdk.constants.ShareInfoType;
import io.metersphere.sdk.domain.ShareInfo;
import io.metersphere.sdk.mapper.ShareInfoMapper;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.user.UserDTO;
import io.metersphere.system.mapper.BaseUserMapper;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class TestPlanReportShareService {
@Resource
private BaseUserMapper baseUserMapper;
@Resource
private ShareInfoMapper shareInfoMapper;
@Resource
private TestPlanReportMapper testPlanReportMapper;
@Resource
private ProjectApplicationMapper projectApplicationMapper;
/**
* 生成计划报告分享信息
* @param shareRequest 分享请求参数
* @param currentUser 当前用户
* @return 计划报告分享信息
*/
public TestPlanShareInfo gen(TestPlanReportShareRequest shareRequest, String currentUser) {
UserDTO userDTO = baseUserMapper.selectById(currentUser);
String lang = userDTO.getLanguage() == null ? LocaleContextHolder.getLocale().toString() : userDTO.getLanguage();
ShareInfo request = new ShareInfo();
BeanUtils.copyBean(request, shareRequest);
request.setLang(lang);
request.setCreateUser(currentUser);
request.setCustomData(shareRequest.getReportId().getBytes());
request.setShareType(ShareInfoType.TEST_PLAN_SHARE_REPORT.name());
ShareInfo shareInfo = createShareInfo(request);
return conversionShareInfoToDTO(shareInfo);
}
/**
* 获取分享信息
* @param id 分享ID
* @return 分享信息
*/
public TestPlanShareResponse get(String id) {
ShareInfo shareInfo = checkResource(id);
TestPlanShareResponse dto = new TestPlanShareResponse();
BeanUtils.copyBean(dto, shareInfo);
dto.setReportId(new String(shareInfo.getCustomData()));
//检查报告ID是否存在
dto.setDeleted(false);
TestPlanReport testPlanReport = testPlanReportMapper.selectByPrimaryKey(dto.getReportId());
if (testPlanReport == null) {
dto.setDeleted(true);
}
return dto;
}
/**
* 获取项目计划报告分享有效期
* @param projectId 项目ID
* @return 有效期
*/
public String getShareTime(String projectId) {
ProjectApplicationExample example = new ProjectApplicationExample();
example.createCriteria().andProjectIdEqualTo(projectId).andTypeEqualTo(ShareInfoType.TEST_PLAN_SHARE_REPORT.name());
List<ProjectApplication> projectApplications = projectApplicationMapper.selectByExample(example);
if (CollectionUtils.isEmpty(projectApplications)) {
return "1D";
} else {
return projectApplications.getFirst().getTypeValue();
}
}
/**
* 校验分享的资源是否存在
* @param id 分享ID
* @return 分享资源信息
*/
private ShareInfo checkResource(String id) {
ShareInfo shareInfo = shareInfoMapper.selectByPrimaryKey(id);
if (shareInfo == null) {
throw new RuntimeException(Translator.get("connection_expired"));
}
return shareInfo;
}
/**
* 创建分享信息
* @param shareInfo 分享的信息
* @return 分享信息
*/
private ShareInfo createShareInfo(ShareInfo shareInfo) {
long createTime = System.currentTimeMillis();
shareInfo.setId(IDGenerator.nextStr());
shareInfo.setCreateTime(createTime);
shareInfo.setUpdateTime(createTime);
shareInfoMapper.insert(shareInfo);
return shareInfo;
}
/**
* 设置分享信息并返回
* @param shareInfo 分享信息
* @return 计划报告分享信息返回
*/
private TestPlanShareInfo conversionShareInfoToDTO(ShareInfo shareInfo) {
TestPlanShareInfo testPlanShareInfo = new TestPlanShareInfo();
if (shareInfo.getCustomData() != null) {
String url = "?shareId=" + shareInfo.getId();
testPlanShareInfo.setId(shareInfo.getId());
testPlanShareInfo.setShareUrl(url);
}
return testPlanShareInfo;
}
}

View File

@ -19,6 +19,7 @@ import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -63,6 +64,7 @@ public class TestPlanBugControllerTests extends BaseTest {
|| StringUtils.contains(bug.getNum(), request.getKeyword()));
// 数据为空
request.setKeyword("oasis-1");
request.setSort(Map.of("b.create_time", "asc"));
this.requestPost(TEST_PLAN_BUG_PAGE, request);
}

View File

@ -1,10 +1,9 @@
package io.metersphere.plan.controller;
import io.metersphere.plan.dto.request.TestPlanReportBatchRequest;
import io.metersphere.plan.dto.request.TestPlanReportDeleteRequest;
import io.metersphere.plan.dto.request.TestPlanReportEditRequest;
import io.metersphere.plan.dto.request.TestPlanReportPageRequest;
import io.metersphere.plan.dto.TestPlanShareInfo;
import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
import io.metersphere.sdk.constants.ShareInfoType;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
@ -19,6 +18,7 @@ import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -32,6 +32,9 @@ public class TestPlanReportControllerTests extends BaseTest {
private static final String RENAME_PLAN_REPORT = "/test-plan/report/rename";
private static final String DELETE_PLAN_REPORT = "/test-plan/report/delete";
private static final String BATCH_DELETE_PLAN_REPORT = "/test-plan/report/batch-delete";
private static final String GEN_AND_SHARE = "/test-plan/report/share/gen";
private static final String GET_SHARE_INFO = "/test-plan/report/share/get";
private static final String GET_SHARE_TIME = "/test-plan/report/share/get-share-time";
@Test
@Order(1)
@ -107,6 +110,39 @@ public class TestPlanReportControllerTests extends BaseTest {
@Test
@Order(5)
void testSharePlanReport() throws Exception {
TestPlanReportShareRequest shareRequest = new TestPlanReportShareRequest();
shareRequest.setReportId("test-plan-report-id-1");
shareRequest.setProjectId("100001100001");
shareRequest.setShareType(ShareInfoType.TEST_PLAN_SHARE_REPORT.name());
this.requestPost(GEN_AND_SHARE, shareRequest);
shareRequest.setLang(Locale.SIMPLIFIED_CHINESE.getLanguage());
MvcResult mvcResult = this.requestPost(GEN_AND_SHARE, shareRequest).andReturn();
String sortData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder sortHolder = JSON.parseObject(sortData, ResultHolder.class);
TestPlanShareInfo shareInfo = JSON.parseObject(JSON.toJSONString(sortHolder.getData()), TestPlanShareInfo.class);
Assertions.assertNotNull(shareInfo);
this.requestGet(GET_SHARE_INFO + "/" + shareInfo.getId());
}
@Test
@Order(6)
void testGetShareInfo() throws Exception {
// 报告被删除
this.requestGet(GET_SHARE_INFO + "/share-1");
// 分享链接未找到
this.requestGet(GET_SHARE_INFO + "/share-2", status().is5xxServerError());
}
@Test
@Order(7)
void testGetShareTime() throws Exception {
this.requestGet(GET_SHARE_TIME + "/100001100001");
this.requestGet(GET_SHARE_TIME + "/100001100002");
}
@Test
@Order(8)
void testDeletePlanReport() throws Exception {
TestPlanReportDeleteRequest request = new TestPlanReportDeleteRequest();
request.setId("test-plan-report-id-1");
@ -115,7 +151,7 @@ public class TestPlanReportControllerTests extends BaseTest {
}
@Test
@Order(6)
@Order(9)
void testBatchDeletePlanReport() throws Exception {
TestPlanReportBatchRequest request = new TestPlanReportBatchRequest();
request.setProjectId("100001100001");

View File

@ -11,4 +11,10 @@ INSERT INTO `test_plan_report`(`id`, `test_plan_id`, `name`, `create_user`, `cre
('test-plan-report-id-1', 'test-plan-id-for991', '测试一下计划报告1', 'admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'MANUAL', 'PENDING', '-', '99.99', 100.00),
('test-plan-report-id-2', 'test-plan-id-for991', '测试一下计划报告1', 'admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'MANUAL', 'PENDING', '-', '99.99', 100.00),
('test-plan-report-id-3', 'test-plan-id-for992', '测试一下计划报告3', 'admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'MANUAL', 'PENDING', '-', '99.99', 100.00),
('test-plan-report-id-4', 'test-plan-id-for992', '测试一下计划报告4', 'admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'MANUAL', 'PENDING', '-', '99.99', 100.00);
('test-plan-report-id-4', 'test-plan-id-for992', '测试一下计划报告4', 'admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'MANUAL', 'PENDING', '-', '99.99', 100.00);
-- 计划报告分享信息
INSERT INTO project_application (`project_id`, `type`, `type_value`) VALUES
('100001100001', 'TEST_PLAN_SHARE_REPORT', '1D');
INSERT INTO `share_info`(`id`, `create_time`, `create_user`, `update_time`, `share_type`, `custom_data`, `lang`, `project_id`) VALUES
('share-1', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'TEST_PLAN_SHARE_REPORT', 0x31303531363635363936353436383137, 'zh_CN', '100001100001');