feat(功能用例): 新增获取三方需求列表

This commit is contained in:
guoyuqi 2024-01-16 11:41:31 +08:00 committed by Yuki Guo
parent a5a7e3012a
commit 72383aa6c3
20 changed files with 200 additions and 144 deletions

View File

@ -2,7 +2,6 @@ package io.metersphere.provider;
import io.metersphere.api.domain.ApiTestCase; import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.dto.TestCaseProviderDTO; import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.request.AssociateCaseModuleProviderRequest;
import io.metersphere.request.AssociateOtherCaseRequest; import io.metersphere.request.AssociateOtherCaseRequest;
import io.metersphere.request.TestCasePageProviderRequest; import io.metersphere.request.TestCasePageProviderRequest;
@ -31,7 +30,7 @@ public interface BaseAssociateApiProvider {
* @param deleted 接口定义是否删除 * @param deleted 接口定义是否删除
* @return 接口模块统计数量 * @return 接口模块统计数量
*/ */
Map<String, Long> moduleCount(String sourceType, String sourceName, String apiCaseColumnName, AssociateCaseModuleProviderRequest request, boolean deleted); Map<String, Long> moduleCount(String sourceType, String sourceName, String apiCaseColumnName, TestCasePageProviderRequest request, boolean deleted);
/** /**
* 根据页面筛选条件获取批量操作的用例 * 根据页面筛选条件获取批量操作的用例

View File

@ -1,37 +0,0 @@
package io.metersphere.request;
import io.metersphere.sdk.constants.ModuleConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
@Data
public class AssociateCaseModuleProviderRequest {
@Schema(description = "关联关系表里主ID eg:功能用例关联接口用例时为功能用例id")
@NotBlank(message = "{api_definition.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition.project_id.length_range}")
private String sourceId;
@Schema(description = "模块ID(根据模块树查询时要把当前节点以及子节点都放在这里。)")
private List<@NotBlank String> moduleIds;
@Schema(description = "协议", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_module.protocol.not_blank}")
@Size(min = 1, max = 20, message = "{api_definition_module.protocol.length_range}")
private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_module.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_module.project_id.length_range}")
private String projectId;
@Schema(description = "用例关键字")
private String keyword;
@Schema(description = "模块关键字")
private String moduleKeyword;
}

View File

@ -4,7 +4,6 @@ import io.metersphere.api.dto.debug.ApiTreeNode;
import io.metersphere.api.dto.definition.ApiModuleRequest; import io.metersphere.api.dto.definition.ApiModuleRequest;
import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.ModuleCountDTO;
import io.metersphere.project.dto.NodeSortQueryParam; import io.metersphere.project.dto.NodeSortQueryParam;
import io.metersphere.request.AssociateCaseModuleProviderRequest;
import io.metersphere.system.dto.sdk.BaseModule; import io.metersphere.system.dto.sdk.BaseModule;
import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.dto.sdk.BaseTreeNode;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -35,9 +34,4 @@ public interface ExtApiDefinitionModuleMapper {
List<BaseTreeNode> selectNodeByIds(@Param("ids") List<String> ids); List<BaseTreeNode> selectNodeByIds(@Param("ids") List<String> ids);
List<BaseTreeNode> selectBaseByIds(@Param("ids") List<String> ids); List<BaseTreeNode> selectBaseByIds(@Param("ids") List<String> ids);
List<ModuleCountDTO> countModuleIdByProviderRequest(@Param("table") String resourceType, @Param("sourceName") String sourceName, @Param("apiCaseColumnName") String apiCaseColumnName, @Param("request") AssociateCaseModuleProviderRequest request, @Param("deleted") boolean deleted);
List<BaseTreeNode> selectIdAndParentIdByProviderRequest(@Param("request") AssociateCaseModuleProviderRequest request);
} }

View File

@ -116,58 +116,6 @@
ORDER BY pos ORDER BY pos
</select> </select>
<select id="countModuleIdByProviderRequest" resultType="io.metersphere.project.dto.ModuleCountDTO">
SELECT ad.module_id AS moduleId, count(ad.id) AS dataCount
FROM api_test_case atc inner join api_definition ad on atc.api_definition_id = ad.id
<include refid="api_case_module_request"/>
and atc.id not in
(
select associate.${apiCaseColumnName} from ${table} associate where associate.${sourceName} = #{request.sourceId}
)
GROUP BY ad.module_id
</select>
<select id="selectIdAndParentIdByProviderRequest" resultType="io.metersphere.system.dto.sdk.BaseTreeNode">
SELECT m.id,
m.parent_id AS parentId
FROM api_definition_module m
<include refid="provider_module_request"/>
ORDER BY pos
</select>
<sql id="api_case_module_request">
<where>
ad.deleted = #{deleted}
<if test="request.projectId != null and request.projectId != ''">
AND ad.project_id = #{request.projectId}
</if>
<if test="request.keyword != null and request.keyword != ''">
AND atc.name like CONCAT('%', #{request.keyword},'%')
</if>
<if test="request.moduleIds != null and request.moduleIds.size() != 0">
AND ad.module_id IN
<foreach collection="request.moduleIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="request.protocol != null and request.protocol != ''">
AND ad.protocol = #{request.protocol}
</if>
AND ad.latest = 1
<!--<if test="request.versionId != null and request.versionId != ''">
and api_definition.version_id = #{request.versionId}
</if>
<if test="request.refId != null and request.refId != ''">
and api_definition.ref_id = #{request.refId}
</if>
<if test="request.versionId == null and request.refId == null and request.id == null">
AND api_definition.latest = 1
</if>-->
</where>
</sql>
<sql id="api_request"> <sql id="api_request">
<where> <where>
<if test="request.projectId != null and request.projectId != ''"> <if test="request.projectId != null and request.projectId != ''">
@ -395,23 +343,4 @@
</where> </where>
</sql> </sql>
<sql id="provider_module_request">
<where>
<if test="request.projectId != null and request.projectId != ''">
AND m.project_id = #{request.projectId}
</if>
<if test="request.moduleKeyword != null and request.moduleKeyword != ''">
AND m.name like CONCAT('%', #{request.moduleKeyword},'%')
</if>
<if test="request.moduleIds != null and request.moduleIds.size() != 0">
AND m.id IN
<foreach collection="request.moduleIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</where>
</sql>
</mapper> </mapper>

View File

@ -7,8 +7,10 @@ import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCasePageRequest; import io.metersphere.api.dto.definition.ApiTestCasePageRequest;
import io.metersphere.api.dto.definition.CasePassDTO; import io.metersphere.api.dto.definition.CasePassDTO;
import io.metersphere.dto.TestCaseProviderDTO; import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.project.dto.ModuleCountDTO;
import io.metersphere.request.AssociateOtherCaseRequest; import io.metersphere.request.AssociateOtherCaseRequest;
import io.metersphere.request.TestCasePageProviderRequest; import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -47,6 +49,10 @@ public interface ExtApiTestCaseMapper {
List<TestCaseProviderDTO> listByProviderRequest(@Param("table") String resourceType, @Param("sourceName") String sourceName, @Param("apiCaseColumnName") String apiCaseColumnName, @Param("request") TestCasePageProviderRequest request, @Param("deleted") boolean deleted); List<TestCaseProviderDTO> listByProviderRequest(@Param("table") String resourceType, @Param("sourceName") String sourceName, @Param("apiCaseColumnName") String apiCaseColumnName, @Param("request") TestCasePageProviderRequest request, @Param("deleted") boolean deleted);
List<ModuleCountDTO> countModuleIdByProviderRequest(@Param("table") String resourceType, @Param("sourceName") String sourceName, @Param("apiCaseColumnName") String apiCaseColumnName, @Param("request") TestCasePageProviderRequest request, @Param("deleted") boolean deleted);
List<BaseTreeNode> selectIdAndParentIdByProjectId(@Param("projectId") String projectId);
List<ApiTestCase> getTestCaseByProvider(@Param("request") AssociateOtherCaseRequest request, @Param("deleted") boolean deleted); List<ApiTestCase> getTestCaseByProvider(@Param("request") AssociateOtherCaseRequest request, @Param("deleted") boolean deleted);

View File

@ -185,6 +185,26 @@
<include refid="queryWhereCondition"/> <include refid="queryWhereCondition"/>
</select> </select>
<select id="countModuleIdByProviderRequest" resultType="io.metersphere.project.dto.ModuleCountDTO">
SELECT a.module_id AS moduleId, count(t1.id) AS dataCount
FROM api_test_case t1 inner join api_definition a on t1.api_definition_id = a.id
WHERE t1.deleted =#{deleted}
and t1.id not in
(
select associate.${apiCaseColumnName} from ${table} associate where associate.${sourceName} = #{request.sourceId}
)
<include refid="queryWhereCondition"/>
GROUP BY a.module_id
</select>
<select id="selectIdAndParentIdByProjectId" resultType="io.metersphere.system.dto.sdk.BaseTreeNode">
SELECT a.id,
a.parent_id AS parentId
FROM api_definition_module a
WHERE a.project_id = #{projectId}
ORDER BY pos
</select>
<select id="getTestCaseByProvider" resultType="io.metersphere.api.domain.ApiTestCase"> <select id="getTestCaseByProvider" resultType="io.metersphere.api.domain.ApiTestCase">
SELECT SELECT
t1.id, t1.version_id t1.id, t1.version_id

View File

@ -3,13 +3,11 @@ package io.metersphere.api.provider;
import io.metersphere.api.domain.ApiTestCase; import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.api.domain.ApiTestCaseExample; import io.metersphere.api.domain.ApiTestCaseExample;
import io.metersphere.api.mapper.ApiTestCaseMapper; import io.metersphere.api.mapper.ApiTestCaseMapper;
import io.metersphere.api.mapper.ExtApiDefinitionModuleMapper;
import io.metersphere.api.mapper.ExtApiTestCaseMapper; import io.metersphere.api.mapper.ExtApiTestCaseMapper;
import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.api.service.definition.ApiDefinitionModuleService;
import io.metersphere.dto.TestCaseProviderDTO; import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.ModuleCountDTO;
import io.metersphere.provider.BaseAssociateApiProvider; import io.metersphere.provider.BaseAssociateApiProvider;
import io.metersphere.request.AssociateCaseModuleProviderRequest;
import io.metersphere.request.AssociateOtherCaseRequest; import io.metersphere.request.AssociateOtherCaseRequest;
import io.metersphere.request.TestCasePageProviderRequest; import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.sdk.util.Translator; import io.metersphere.sdk.util.Translator;
@ -27,9 +25,6 @@ public class AssociateApiProvider implements BaseAssociateApiProvider {
@Resource @Resource
private ExtApiTestCaseMapper extApiTestCaseMapper; private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ExtApiDefinitionModuleMapper extApiDefinitionModuleMapper;
@Resource @Resource
private ApiDefinitionModuleService moduleTreeService; private ApiDefinitionModuleService moduleTreeService;
@ -45,10 +40,10 @@ public class AssociateApiProvider implements BaseAssociateApiProvider {
} }
@Override @Override
public Map<String, Long> moduleCount(String sourceType, String sourceName, String apiCaseColumnName, AssociateCaseModuleProviderRequest request, boolean deleted) { public Map<String, Long> moduleCount(String sourceType, String sourceName, String apiCaseColumnName, TestCasePageProviderRequest request, boolean deleted) {
request.setModuleIds(null); request.setModuleIds(null);
//查找根据moduleIds查找模块下的接口数量 查非delete状态的 //查找根据moduleIds查找模块下的接口数量 查非delete状态的
List<ModuleCountDTO> moduleCountDTOList = extApiDefinitionModuleMapper.countModuleIdByProviderRequest(sourceType, sourceName, apiCaseColumnName, request, deleted); List<ModuleCountDTO> moduleCountDTOList = extApiTestCaseMapper.countModuleIdByProviderRequest(sourceType, sourceName, apiCaseColumnName, request, deleted);
long allCount = getAllCount(moduleCountDTOList); long allCount = getAllCount(moduleCountDTOList);
Map<String, Long> moduleCountMap = getModuleCountMap(request, moduleCountDTOList); Map<String, Long> moduleCountMap = getModuleCountMap(request, moduleCountDTOList);
moduleCountMap.put(DEBUG_MODULE_COUNT_ALL, allCount); moduleCountMap.put(DEBUG_MODULE_COUNT_ALL, allCount);
@ -81,15 +76,15 @@ public class AssociateApiProvider implements BaseAssociateApiProvider {
/** /**
* 查找当前项目下模块每个节点对应的资源统计 * 查找当前项目下模块每个节点对应的资源统计
*/ */
public Map<String, Long> getModuleCountMap(AssociateCaseModuleProviderRequest request, List<ModuleCountDTO> moduleCountDTOList) { public Map<String, Long> getModuleCountMap(TestCasePageProviderRequest request, List<ModuleCountDTO> moduleCountDTOList) {
//构建模块树并计算每个节点下的所有数量包含子节点 //构建模块树并计算每个节点下的所有数量包含子节点
List<BaseTreeNode> treeNodeList = this.getTreeOnlyIdsAndResourceCount(request, moduleCountDTOList); List<BaseTreeNode> treeNodeList = this.getTreeOnlyIdsAndResourceCount(request, moduleCountDTOList);
return moduleTreeService.getIdCountMapByBreadth(treeNodeList); return moduleTreeService.getIdCountMapByBreadth(treeNodeList);
} }
public List<BaseTreeNode> getTreeOnlyIdsAndResourceCount(AssociateCaseModuleProviderRequest request, List<ModuleCountDTO> moduleCountDTOList) { public List<BaseTreeNode> getTreeOnlyIdsAndResourceCount(TestCasePageProviderRequest request, List<ModuleCountDTO> moduleCountDTOList) {
//节点内容只有Id和parentId //节点内容只有Id和parentId
List<BaseTreeNode> fileModuleList = extApiDefinitionModuleMapper.selectIdAndParentIdByProviderRequest(request); List<BaseTreeNode> fileModuleList = extApiTestCaseMapper.selectIdAndParentIdByProjectId(request.getProjectId());
return moduleTreeService.buildTreeAndCountResource(fileModuleList, moduleCountDTOList, true, Translator.get(UNPLANNED_API)); return moduleTreeService.buildTreeAndCountResource(fileModuleList, moduleCountDTOList, true, Translator.get(UNPLANNED_API));
} }

View File

@ -3,9 +3,8 @@ package io.metersphere.api.controller;
import io.metersphere.api.domain.ApiTestCase; import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.api.provider.AssociateApiProvider; import io.metersphere.api.provider.AssociateApiProvider;
import io.metersphere.dto.TestCaseProviderDTO; import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.request.AssociateCaseModuleProviderRequest;
import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.request.AssociateOtherCaseRequest; import io.metersphere.request.AssociateOtherCaseRequest;
import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest; import io.metersphere.system.base.BaseTest;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@ -48,10 +47,15 @@ public class AssociateApiProviderTest extends BaseTest {
@Test @Test
@Order(2) @Order(2)
public void moduleCountSuccess() throws Exception { public void moduleCountSuccess() throws Exception {
AssociateCaseModuleProviderRequest request = new AssociateCaseModuleProviderRequest(); TestCasePageProviderRequest request = new TestCasePageProviderRequest();
request.setSourceType("API");
request.setSourceId("gyq_associate_case_id_1"); request.setSourceId("gyq_associate_case_id_1");
request.setProjectId("project-associate-case-test"); request.setProjectId("project-associate-case-test");
request.setKeyword("测试查询模块用"); request.setCurrent(1);
request.setPageSize(10);
request.setSort(new HashMap<>() {{
put("createTime", "desc");
}});
Map<String, Long> stringLongMap = provider.moduleCount("functional_case_test", "case_id", "source_id", request, false); Map<String, Long> stringLongMap = provider.moduleCount("functional_case_test", "case_id", "source_id", request, false);
String jsonString = JSON.toJSONString(stringLongMap); String jsonString = JSON.toJSONString(stringLongMap);

View File

@ -4,9 +4,12 @@ import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.functional.dto.FunctionalDemandDTO; import io.metersphere.functional.dto.FunctionalDemandDTO;
import io.metersphere.functional.request.FunctionalCaseDemandRequest; import io.metersphere.functional.request.FunctionalCaseDemandRequest;
import io.metersphere.functional.request.FunctionalThirdDemandPageRequest;
import io.metersphere.functional.request.QueryDemandListRequest; import io.metersphere.functional.request.QueryDemandListRequest;
import io.metersphere.functional.service.FunctionalCaseDemandService; import io.metersphere.functional.service.FunctionalCaseDemandService;
import io.metersphere.functional.service.FunctionalCaseLogService; import io.metersphere.functional.service.FunctionalCaseLogService;
import io.metersphere.plugin.platform.dto.reponse.DemandRelatePageResponse;
import io.metersphere.plugin.platform.utils.PluginPager;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.log.annotation.Log; import io.metersphere.system.log.annotation.Log;
import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.constants.OperationLogType;
@ -69,4 +72,11 @@ public class FunctionalCaseDemandController {
public void batchRelevance(@RequestBody @Validated FunctionalCaseDemandRequest request) { public void batchRelevance(@RequestBody @Validated FunctionalCaseDemandRequest request) {
functionalCaseDemandService.batchRelevance(request, SessionUtils.getUserId()); functionalCaseDemandService.batchRelevance(request, SessionUtils.getUserId());
} }
@PostMapping("/third/list/page")
@Operation(summary = "用例管理-功能用例-关联需求-批量关联需求")
@RequiresPermissions(value = {PermissionConstants.FUNCTIONAL_CASE_READ_ADD, PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE, PermissionConstants.FUNCTIONAL_CASE_READ_DELETE}, logical = Logical.OR)
public PluginPager<DemandRelatePageResponse> pageDemand(@RequestBody @Validated FunctionalThirdDemandPageRequest request) {
return functionalCaseDemandService.pageDemand(request);
}
} }

View File

@ -53,12 +53,12 @@ public class FunctionalTestCaseController {
@Operation(summary = "用例管理-功能用例-关联其他用例-统计需要关联用例模块数量") @Operation(summary = "用例管理-功能用例-关联其他用例-统计需要关联用例模块数量")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ) @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
@CheckOwner(resourceId = "#request.projectId", resourceType = "project") @CheckOwner(resourceId = "#request.projectId", resourceType = "project")
public Map<String, Long> moduleCount(@Validated @RequestBody AssociateCaseModuleProviderRequest request) { public Map<String, Long> moduleCount(@Validated @RequestBody TestCasePageProviderRequest request) {
return functionalTestCaseService.moduleCount(request, false); return functionalTestCaseService.moduleCount(request, false);
} }
@PostMapping("/associate/case/module/tree") @PostMapping("/associate/case/module/tree")
@Operation(summary = "用例管理-功能用例-关联其他用例-查找模块") @Operation(summary = "用例管理-功能用例-关联其他用例-获取需要关联的用例模块树")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ) @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
@CheckOwner(resourceId = "#request.projectId", resourceType = "project") @CheckOwner(resourceId = "#request.projectId", resourceType = "project")
public List<BaseTreeNode> getTree(@RequestBody @Validated AssociateCaseModuleRequest request) { public List<BaseTreeNode> getTree(@RequestBody @Validated AssociateCaseModuleRequest request) {

View File

@ -29,6 +29,7 @@ public interface ExtFunctionalCaseModuleMapper {
List<BaseTreeNode> selectApiCaseModuleByRequest(@Param("request") AssociateCaseModuleRequest request); List<BaseTreeNode> selectApiCaseModuleByRequest(@Param("request") AssociateCaseModuleRequest request);
List<BaseTreeNode> selectApiScenarioModuleByRequest(@Param("request") AssociateCaseModuleRequest request);
List<BaseTreeNode> selectIdAndParentIdByProjectIdAndReviewId(@Param("projectId")String projectId, @Param("reviewId")String reviewId); List<BaseTreeNode> selectIdAndParentIdByProjectIdAndReviewId(@Param("projectId")String projectId, @Param("reviewId")String reviewId);
} }

View File

@ -82,6 +82,18 @@
ORDER BY pos ORDER BY pos
</select> </select>
<select id="selectApiScenarioModuleByRequest" resultType="io.metersphere.system.dto.sdk.BaseTreeNode">
SELECT m.id,
m.parent_id AS parentId,
m.name,
m.pos,
m.project_id,
'MODULE' AS type
FROM api_scenario_module m
<include refid="module_request"/>
ORDER BY pos
</select>
<select id="selectIdAndParentIdByProjectIdAndReviewId" resultType="io.metersphere.system.dto.sdk.BaseTreeNode"> <select id="selectIdAndParentIdByProjectIdAndReviewId" resultType="io.metersphere.system.dto.sdk.BaseTreeNode">
SELECT fcm.id, fcm.parent_id AS parentId SELECT fcm.id, fcm.parent_id AS parentId
FROM functional_case_module fcm FROM functional_case_module fcm

View File

@ -18,6 +18,7 @@ public class AssociateCaseModuleRequest extends BaseCondition {
@NotBlank(message = "{api_definition_module.protocol.not_blank}") @NotBlank(message = "{api_definition_module.protocol.not_blank}")
@Size(min = 1, max = 20, message = "{api_definition_module.protocol.length_range}") @Size(min = 1, max = 20, message = "{api_definition_module.protocol.length_range}")
private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP; private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_module.project_id.not_blank}") @NotBlank(message = "{api_definition_module.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_module.project_id.length_range}") @Size(min = 1, max = 50, message = "{api_definition_module.project_id.length_range}")

View File

@ -0,0 +1,33 @@
package io.metersphere.functional.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.Map;
@Data
public class FunctionalThirdDemandPageRequest {
@Schema(description = "ms系统当前的项目id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_third_demand_page_request.project_id.not_blank}")
private String projectId;
@Schema(description = "需求分页查询关键字")
private String query;
@Schema(description = "列表筛选条件")
private Map<String, Object> filter;
@Schema(description = "开始页码", requiredMode = Schema.RequiredMode.REQUIRED)
@Min(value = 1, message = "当前页码必须大于0")
private int startPage;
@Schema(description = "每页条数", requiredMode = Schema.RequiredMode.REQUIRED)
@Min(value = 5, message = "每页显示条数必须不小于5")
@Max(value = 500, message = "每页显示条数不能大于500")
private int pageSize;
}

View File

@ -7,7 +7,13 @@ import io.metersphere.functional.dto.FunctionalDemandDTO;
import io.metersphere.functional.mapper.ExtFunctionalCaseDemandMapper; import io.metersphere.functional.mapper.ExtFunctionalCaseDemandMapper;
import io.metersphere.functional.mapper.FunctionalCaseDemandMapper; import io.metersphere.functional.mapper.FunctionalCaseDemandMapper;
import io.metersphere.functional.request.FunctionalCaseDemandRequest; import io.metersphere.functional.request.FunctionalCaseDemandRequest;
import io.metersphere.functional.request.FunctionalThirdDemandPageRequest;
import io.metersphere.functional.request.QueryDemandListRequest; import io.metersphere.functional.request.QueryDemandListRequest;
import io.metersphere.plugin.platform.dto.reponse.DemandRelatePageResponse;
import io.metersphere.plugin.platform.dto.request.DemandPageRequest;
import io.metersphere.plugin.platform.spi.Platform;
import io.metersphere.plugin.platform.utils.PluginPager;
import io.metersphere.project.service.ProjectApplicationService;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.Translator; import io.metersphere.sdk.util.Translator;
@ -44,6 +50,8 @@ public class FunctionalCaseDemandService {
private SqlSessionFactory sqlSessionFactory; private SqlSessionFactory sqlSessionFactory;
@Resource @Resource
private SystemParameterMapper systemParameterMapper; private SystemParameterMapper systemParameterMapper;
@Resource
private ProjectApplicationService projectApplicationService;
/** /**
* 获取需求列表 * 获取需求列表
@ -206,4 +214,15 @@ public class FunctionalCaseDemandService {
sqlSession.flushStatements(); sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
} }
public PluginPager<DemandRelatePageResponse> pageDemand(FunctionalThirdDemandPageRequest request) {
DemandPageRequest demandPageRequest = new DemandPageRequest();
demandPageRequest.setQuery(request.getQuery());
demandPageRequest.setFilter(request.getFilter());
demandPageRequest.setStartPage(request.getStartPage());
demandPageRequest.setPageSize(request.getPageSize());
demandPageRequest.setProjectConfig(projectApplicationService.getProjectDemandThirdPartConfig(request.getProjectId()));
Platform platform = projectApplicationService.getPlatform(request.getProjectId(), false);
return platform.pageDemand(demandPageRequest);
}
} }

View File

@ -1,7 +1,6 @@
package io.metersphere.functional.service; package io.metersphere.functional.service;
import io.metersphere.api.domain.ApiTestCase; import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.bug.mapper.BugRelationCaseMapper;
import io.metersphere.dto.BugProviderDTO; import io.metersphere.dto.BugProviderDTO;
import io.metersphere.dto.TestCaseProviderDTO; import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.functional.constants.AssociateCaseType; import io.metersphere.functional.constants.AssociateCaseType;
@ -30,6 +29,7 @@ import org.redisson.api.IdGenerator;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -63,8 +63,7 @@ public class FunctionalTestCaseService {
@Resource @Resource
private BaseAssociateBugProvider baseAssociateBugProvider; private BaseAssociateBugProvider baseAssociateBugProvider;
@Resource
private BugRelationCaseMapper bugRelationCaseMapper;
/** /**
* 获取功能用例未关联的接口用例列表 * 获取功能用例未关联的接口用例列表
@ -83,7 +82,7 @@ public class FunctionalTestCaseService {
* @param deleted 接口定义是否删除 * @param deleted 接口定义是否删除
* @return 接口模块统计数量 * @return 接口模块统计数量
*/ */
public Map<String, Long> moduleCount(AssociateCaseModuleProviderRequest request, boolean deleted) { public Map<String, Long> moduleCount(TestCasePageProviderRequest request, boolean deleted) {
return provider.moduleCount("functional_case_test", "case_id", "source_id", request, deleted); return provider.moduleCount("functional_case_test", "case_id", "source_id", request, deleted);
} }
@ -162,7 +161,12 @@ public class FunctionalTestCaseService {
} }
public List<BaseTreeNode> getTree(AssociateCaseModuleRequest request) { public List<BaseTreeNode> getTree(AssociateCaseModuleRequest request) {
List<BaseTreeNode> fileModuleList = extFunctionalCaseModuleMapper.selectApiCaseModuleByRequest(request); List<BaseTreeNode> fileModuleList = new ArrayList<>();
switch (request.getSourceType()) {
case AssociateCaseType.API -> fileModuleList = extFunctionalCaseModuleMapper.selectApiCaseModuleByRequest(request);
case AssociateCaseType.SCENARIO -> fileModuleList = extFunctionalCaseModuleMapper.selectApiScenarioModuleByRequest(request);
default -> new ArrayList<>();
}
return functionalCaseModuleService.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_API)); return functionalCaseModuleService.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_API));
} }

View File

@ -6,22 +6,30 @@ import io.metersphere.functional.dto.DemandDTO;
import io.metersphere.functional.dto.FunctionalDemandDTO; import io.metersphere.functional.dto.FunctionalDemandDTO;
import io.metersphere.functional.mapper.FunctionalCaseDemandMapper; import io.metersphere.functional.mapper.FunctionalCaseDemandMapper;
import io.metersphere.functional.request.FunctionalCaseDemandRequest; import io.metersphere.functional.request.FunctionalCaseDemandRequest;
import io.metersphere.functional.request.FunctionalThirdDemandPageRequest;
import io.metersphere.functional.request.QueryDemandListRequest; import io.metersphere.functional.request.QueryDemandListRequest;
import io.metersphere.plugin.platform.dto.reponse.DemandRelatePageResponse;
import io.metersphere.plugin.platform.utils.PluginPager;
import io.metersphere.sdk.constants.SessionConstants; import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.domain.OperationLog; import io.metersphere.sdk.domain.OperationLog;
import io.metersphere.sdk.domain.OperationLogExample; import io.metersphere.sdk.domain.OperationLogExample;
import io.metersphere.sdk.mapper.OperationLogMapper; import io.metersphere.sdk.mapper.OperationLogMapper;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BasePluginTestService;
import io.metersphere.system.base.BaseTest; import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.domain.SystemParameter; import io.metersphere.system.domain.SystemParameter;
import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.mapper.ServiceIntegrationMapper;
import io.metersphere.system.mapper.SystemParameterMapper; import io.metersphere.system.mapper.SystemParameterMapper;
import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.mockserver.client.MockServerClient;
import org.mockserver.model.Header;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -35,6 +43,8 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ -48,12 +58,25 @@ public class FunctionalCaseDemandControllerTests extends BaseTest {
private OperationLogMapper operationLogMapper; private OperationLogMapper operationLogMapper;
@Resource @Resource
private SystemParameterMapper systemParameterMapper; private SystemParameterMapper systemParameterMapper;
@Resource
private ServiceIntegrationMapper serviceIntegrationMapper;
@Resource
private BasePluginTestService basePluginTestService;
@Resource
private MockServerClient mockServerClient;
@Value("${embedded.mockserver.host}")
private String mockServerHost;
@Value("${embedded.mockserver.port}")
private int mockServerHostPort;
private static final String URL_DEMAND_PAGE = "/functional/case/demand/page"; private static final String URL_DEMAND_PAGE = "/functional/case/demand/page";
private static final String URL_DEMAND_ADD = "/functional/case/demand/add"; private static final String URL_DEMAND_ADD = "/functional/case/demand/add";
private static final String URL_DEMAND_UPDATE = "/functional/case/demand/update"; private static final String URL_DEMAND_UPDATE = "/functional/case/demand/update";
private static final String URL_DEMAND_CANCEL = "/functional/case/demand/cancel/"; private static final String URL_DEMAND_CANCEL = "/functional/case/demand/cancel/";
private static final String URL_DEMAND_BATCH_RELEVANCE = "/functional/case/demand/batch/relevance"; private static final String URL_DEMAND_BATCH_RELEVANCE = "/functional/case/demand/batch/relevance";
private static final String URL_DEMAND_PAGE_DEMAND = "/functional/case/demand/third/list/page";
@Test @Test
@ -435,4 +458,36 @@ public class FunctionalCaseDemandControllerTests extends BaseTest {
Assertions.assertTrue(CollectionUtils.isEmpty(operationLogs)); Assertions.assertTrue(CollectionUtils.isEmpty(operationLogs));
} }
@Test
@Order(13)
public void pageDemandSuccess() throws Exception {
basePluginTestService.addJiraPlugin();
basePluginTestService.addServiceIntegration(DEFAULT_ORGANIZATION_ID);
mockServerClient
.when(
request()
.withMethod("GET")
.withPath("/rest/api/2/search"))
.respond(
response()
.withStatusCode(200)
.withHeaders(
new Header("Content-Type", "application/json; charset=utf-8"),
new Header("Cache-Control", "public, max-age=86400"))
.withBody("{\"id\":\"123456\",\"name\":\"test\"}")
);
FunctionalThirdDemandPageRequest functionalThirdDemandPageRequest = new FunctionalThirdDemandPageRequest();
functionalThirdDemandPageRequest.setProjectId("gyq_project-case-demand-test");
functionalThirdDemandPageRequest.setPageSize(10);
functionalThirdDemandPageRequest.setStartPage(1);
MvcResult mvcResultDemand= this.requestPostWithOkAndReturn(URL_DEMAND_PAGE_DEMAND, functionalThirdDemandPageRequest);
PluginPager<DemandRelatePageResponse> tableData = JSON.parseObject(JSON.toJSONString(
JSON.parseObject(mvcResultDemand.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class).getData()),
PluginPager.class);
System.out.println(JSON.toJSONString(tableData));
}
} }

View File

@ -129,10 +129,12 @@ public class FunctionalTestCaseControllerTests extends BaseTest {
@Test @Test
@Order(3) @Order(3)
public void getModuleCountSuccess() throws Exception { public void getModuleCountSuccess() throws Exception {
AssociateCaseModuleProviderRequest request = new AssociateCaseModuleProviderRequest(); TestCasePageProviderRequest request = new TestCasePageProviderRequest();
request.setSourceType(AssociateCaseType.API);
request.setSourceId("gyq_associate_case_id_1"); request.setSourceId("gyq_associate_case_id_1");
request.setProjectId("project_gyq_associate_test"); request.setProjectId("project_gyq_associate_test");
request.setKeyword("测试查询模块用"); request.setCurrent(1);
request.setPageSize(10);
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_CASE_PAGE_MODULE_COUNT, request); MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_CASE_PAGE_MODULE_COUNT, request);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);

View File

@ -2,7 +2,9 @@
INSERT INTO organization(id, num, name, description, create_time, update_time, create_user, update_user, deleted, delete_user, delete_time) VALUE INSERT INTO organization(id, num, name, description, create_time, update_time, create_user, update_user, deleted, delete_user, delete_time) VALUE
('organization-case-demand-test', null, 'organization-case-demand-test', 'organization-case-demand-test', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'admin', 0, null, null); ('organization-case-demand-test', null, 'organization-case-demand-test', 'organization-case-demand-test', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'admin', 0, null, null);
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUES INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUES
('project-case-demand-test', null, 'organization-case-demand-test', '用例需求项目', '系统默认创建的项目', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000); ('project-case-demand-test', 1001, 'organization-case-demand-test', '用例需求项目', '系统默认创建的项目', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),
('gyq_project-case-demand-test', 5001, '100001', '用例需求项目', '系统默认创建的项目', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
INSERT INTO template (id, name, remark, internal, update_time, create_time, create_user, scope_type, scope_id, enable_third_part, ref_id, scene) INSERT INTO template (id, name, remark, internal, update_time, create_time, create_user, scope_type, scope_id, enable_third_part, ref_id, scene)
@ -29,3 +31,11 @@ INSERT INTO custom_field(id, name, scene, `type`, remark, internal, scope_type,
VALUES('gyq_custom_id_demand2', 'level', 'FUNCTIONAL', 'SELECT', '', 1, 'ORGANIZATION', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'test_template_case_demand_id'); VALUES('gyq_custom_id_demand2', 'level', 'FUNCTIONAL', 'SELECT', '', 1, 'ORGANIZATION', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'test_template_case_demand_id');
INSERT INTO functional_case_module(id, project_id, name, parent_id, pos, create_time, update_time, create_user, update_user) VALUES ('DEMAND_TEST_MODULE_ID', 'project-case-demand-test', '测试需求所属模块', 'NONE', 0, 1669174143999, 1669174143999, 'admin', 'admin'); INSERT INTO functional_case_module(id, project_id, name, parent_id, pos, create_time, update_time, create_user, update_user) VALUES ('DEMAND_TEST_MODULE_ID', 'project-case-demand-test', '测试需求所属模块', 'NONE', 0, 1669174143999, 1669174143999, 'admin', 'admin');
/*INSERT INTO service_integration(`id`, `plugin_id`, `enable`, `configuration`, `organization_id`) VALUES
('gyq_service_integration_id', 'jira', true, 0x504B0304140008080800BC517657000000000000000000000000030000007A6970258DC10EC2201044FF65CF06D2C498D89347B5574FBD6D8158222CD85D6268E3BF4BE3F5CDBC990DD0DAC531430FB348E65EEBE06B41AAA9289480CC1E4991130D07C022F3A366D7DA13B2373B32261592469AF1572FCF883E289362CB735BF8A4C5EE073474C3CB8E59A6F85EEFF12AE676EC4E67F8FE00504B0708384DA4307800000087000000504B01021400140008080800BC517657384DA43078000000870000000300000000000000000000000000000000007A6970504B0506000000000100010031000000A90000000000, '100001');*/
INSERT INTO project_application (project_id, type, type_value) VALUES
('gyq_project-case-demand-test', 'CASE_RELATED_CASE_ENABLE', 'true'),
('gyq_project-case-demand-test', 'CASE_RELATED_DEMAND_PLATFORM_CONFIG', '{"jiraKey":"TES","jiraDemandTypeId":"10007"}'),
('gyq_project-case-demand-test', 'CASE_RELATED_PLATFORM_KEY', 'jira');

View File

@ -15,6 +15,7 @@ import io.metersphere.system.service.ServiceIntegrationService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
@ -60,10 +61,8 @@ public class BasePluginTestService {
return jiraPlugin; return jiraPlugin;
} }
PluginUpdateRequest request = new PluginUpdateRequest(); PluginUpdateRequest request = new PluginUpdateRequest();
File jarFile = new File( FileUtils.copyInputStreamToFile(this.getClass().getClassLoader().getResource("file/metersphere-jira-plugin-3.x.jar").openStream(), new File(FileUtils.getTempDirectoryPath()+"/metersphere-jira-plugin-3.x.jar"));
this.getClass().getClassLoader().getResource("file/metersphere-jira-plugin-3.x.jar") File jarFile = new File(FileUtils.getTempDirectoryPath()+"/metersphere-jira-plugin-3.x.jar");
.getPath()
);
FileInputStream inputStream = new FileInputStream(jarFile); FileInputStream inputStream = new FileInputStream(jarFile);
MockMultipartFile mockMultipartFile = new MockMultipartFile(jarFile.getName(), jarFile.getName(), "jar", inputStream); MockMultipartFile mockMultipartFile = new MockMultipartFile(jarFile.getName(), jarFile.getName(), "jar", inputStream);
request.setName("测试插件"); request.setName("测试插件");