feat(接口测试): 模块树支持过滤功能

--story=1010765 --user=张勇 【UI/接口测试】模块树支持过滤功能-接口 https://www.tapd.cn/55049933/s/1315940
This commit is contained in:
zhangyong 2022-12-15 15:14:51 +08:00 committed by fit2-zhao
parent 35ce73345e
commit 0d9b8a6dd8
16 changed files with 231 additions and 6 deletions

View File

@ -105,6 +105,8 @@ public interface ExtApiDefinitionMapper {
List<ApiDefinition> selectApiBaseInfoByProjectIdAndProtocolAndStatus(@Param("projectId") String projectId, @Param("protocol") String protocol, @Param("versionId") String versionId, @Param("status") String status);
List<ApiDefinition> selectApiBaseInfoByCondition(@Param("projectId") String projectId, @Param("protocol") String protocol, @Param("versionId") String versionId, @Param("status") String status, @Param("request") ApiDefinitionRequest request);
void updateNoModuleApiToDefaultModule(@Param("projectId") String projectId, @Param("protocol") String protocol, @Param("status") String status, @Param("versionId") String versionId, @Param("moduleId") String moduleId);
List<String> selectApiIdInExecutionInfoByProjectIdIsNull();

View File

@ -1238,6 +1238,22 @@
AND latest IS TRUE
</if>
</select>
<select id="selectApiBaseInfoByCondition" resultType="io.metersphere.base.domain.ApiDefinition">
select id, module_id
from api_definition
<include refid="queryWhereCondition"/>
AND project_id = #{projectId}
AND protocol = #{protocol}
AND status = #{status}
<if test="versionId != null">
AND version_id = #{versionId}
</if>
<if test="versionId == null">
AND latest IS TRUE
</if>
</select>
<select id="selectApiIdInExecutionInfoByProjectIdIsNull" resultType="java.lang.String">
SELECT DISTINCT source_id FROM api_execution_info
WHERE project_id IS NULL

View File

@ -102,6 +102,8 @@ public interface ExtApiScenarioMapper {
List<ApiScenario> selectBaseInfoByProjectIdAndStatus(@Param("projectId") String projectId, @Param("status") String status);
List<ApiScenario> selectBaseInfoByCondition(@Param("projectId") String projectId, @Param("status") String status, @Param("request") ApiScenarioRequest request);
List<ApiCountChartResult> countByRequest(ApiCountRequest request);
List<ApiScenarioDTO> relevanceScenarioList(@Param("request") ApiScenarioRequest request);

View File

@ -913,6 +913,15 @@
AND latest IS TRUE
</select>
<select id="selectBaseInfoByCondition" resultType="io.metersphere.base.domain.ApiScenario">
select api_scenario.id, api_scenario.api_scenario_module_id
from api_scenario
<include refid="queryWhereCondition"/>
AND api_scenario.project_id = #{projectId}
AND api_scenario.status = #{status}
AND api_scenario.latest IS TRUE
</select>
<select id="countByRequest" resultType="io.metersphere.api.dto.ApiCountChartResult">
select
<if test="testCaseGroupColumn != null and testCaseGroupColumn != ''">

View File

@ -1,5 +1,6 @@
package io.metersphere.controller.definition;
import io.metersphere.api.dto.definition.ApiDefinitionRequest;
import io.metersphere.api.dto.definition.ApiModuleDTO;
import io.metersphere.api.dto.definition.DragModuleRequest;
import io.metersphere.service.definition.ApiModuleService;
@ -37,6 +38,13 @@ public class ApiModuleController {
return apiModuleService.getNodeTreeByProjectId(projectId, protocol, versionId);
}
@PostMapping("/list/{projectId}/{protocol}")
public List<ApiModuleDTO> searchNodeByProjectId(@PathVariable String projectId, @PathVariable String protocol, @RequestBody ApiDefinitionRequest request) {
String userId = SessionUtils.getUserId();
ApiDefinitionDefaultApiTypeUtil.addUserSelectApiType(userId, protocol);
return apiModuleService.getNodeTreeByCondition(projectId, protocol, null, request);
}
@GetMapping("/trash/list/{projectId}/{protocol}/{versionId}")
public List<ApiModuleDTO> getTrashNodeByProtocolAndProjectId(@PathVariable String projectId, @PathVariable String protocol,
@PathVariable String versionId) {
@ -48,6 +56,11 @@ public class ApiModuleController {
return apiModuleService.getTrashNodeTreeByProtocolAndProjectId(projectId, protocol, null);
}
@PostMapping("/trash/list/{projectId}/{protocol}")
public List<ApiModuleDTO> searchTrashNodeByProtocolAndProjectId(@PathVariable String projectId, @PathVariable String protocol, @RequestBody ApiDefinitionRequest request) {
return apiModuleService.getTrashNodeTreeByProtocolAndProjectId(projectId, protocol, null, request);
}
@GetMapping("/trash-count/{projectId}/{protocol}")
public long trashCount(@PathVariable String projectId, @PathVariable String protocol) {
String userId = SessionUtils.getUserId();

View File

@ -1,6 +1,7 @@
package io.metersphere.controller.scenario;
import io.metersphere.api.dto.automation.ApiScenarioModuleDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.DragApiScenarioModuleRequest;
import io.metersphere.service.scenario.ApiScenarioModuleService;
import io.metersphere.base.domain.ApiScenarioModule;
@ -24,11 +25,21 @@ public class ApiScenarioModuleController {
return apiScenarioModuleService.getNodeTreeByProjectId(projectId);
}
@PostMapping("/list/{projectId}")
public List<ApiScenarioModuleDTO> searchNodeByProjectId(@PathVariable String projectId, @RequestBody ApiScenarioRequest request) {
return apiScenarioModuleService.getNodeTreeByProjectId(projectId, request);
}
@GetMapping("/trash/list/{projectId}")
public List<ApiScenarioModuleDTO> getTrashNodeByProjectId(@PathVariable String projectId) {
return apiScenarioModuleService.getTrashNodeTreeByProjectId(projectId);
}
@PostMapping("/trash/list/{projectId}")
public List<ApiScenarioModuleDTO> searchTrashNodeByProjectId(@PathVariable String projectId, @RequestBody ApiScenarioRequest request) {
return apiScenarioModuleService.getTrashNodeTreeByProjectId(projectId, request);
}
@PostMapping("/add")
@MsAuditLog(module = OperLogModule.API_AUTOMATION, type = OperLogConstants.CREATE, title = "#node.name", content = "#msClass.getLogDetails(#node)", msClass = ApiScenarioModuleService.class)
public String addNode(@RequestBody ApiScenarioModule node) {

View File

@ -1919,6 +1919,11 @@ public class ApiDefinitionService {
return apiList.stream().collect(Collectors.groupingBy(ApiDefinition::getModuleId));
}
public Map<String, List<ApiDefinition>> selectApiBaseInfoGroupByModuleId(String projectId, String protocol, String versionId, String status, ApiDefinitionRequest request) {
List<ApiDefinition> apiList = extApiDefinitionMapper.selectApiBaseInfoByCondition(projectId, protocol, versionId, status, request);
return apiList.stream().collect(Collectors.groupingBy(ApiDefinition::getModuleId));
}
/**
* 将模块删除了的接口模块改为默认模块
*

View File

@ -102,6 +102,19 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
return getNodeTrees(trashModuleList);
}
public List<ApiModuleDTO> getTrashNodeTreeByProtocolAndProjectId(String projectId, String protocol, String versionId, ApiDefinitionRequest request) {
//回收站数据初始化检查是否存在模块被删除的接口则把接口挂再默认节点上
initTrashDataModule(projectId, protocol, versionId);
//通过回收站里的接口模块进行反显
Map<String, List<ApiDefinition>> trashApiMap =
apiDefinitionService.selectApiBaseInfoGroupByModuleId(projectId, protocol, versionId,
ApiTestDataStatus.TRASH.getValue(), request);
//查找回收站里的模块
List<ApiModuleDTO> trashModuleList = this.selectTreeStructModuleById(trashApiMap.keySet());
this.initApiCount(trashModuleList, trashApiMap);
return getNodeTrees(trashModuleList);
}
private void initApiCount(List<ApiModuleDTO> apiModules, Map<String, List<ApiDefinition>> trashApiMap) {
if (CollectionUtils.isNotEmpty(apiModules) && MapUtils.isNotEmpty(trashApiMap)) {
apiModules.forEach(node -> {
@ -192,6 +205,53 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
return getNodeTrees(apiModules);
}
public List<ApiModuleDTO> getNodeTreeByCondition(String projectId, String protocol, String versionId, ApiDefinitionRequest request ) {
// 判断当前项目下是否有默认模块没有添加默认模块
this.getDefaultNode(projectId, protocol);
List<ApiModuleDTO> apiModules = getApiModulesByProjectAndPro(projectId, protocol);
request.setProjectId(projectId);
request.setProtocol(protocol);
List<String> list = new ArrayList<>();
list.add(ApiTestDataStatus.PREPARE.getValue());
list.add(ApiTestDataStatus.UNDERWAY.getValue());
list.add(ApiTestDataStatus.COMPLETED.getValue());
Map<String, List<String>> filters = new LinkedHashMap<>();
filters.put("status", list);
request.setFilters(filters);
//优化 所有统计SQL一次查询出来
List<String> allModuleIdList = new ArrayList<>();
for (ApiModuleDTO node : apiModules) {
List<String> moduleIds = new ArrayList<>();
moduleIds = this.nodeList(apiModules, node.getId(), moduleIds);
moduleIds.add(node.getId());
for (String moduleId : moduleIds) {
if (!allModuleIdList.contains(moduleId)) {
allModuleIdList.add(moduleId);
}
}
}
request.setModuleIds(allModuleIdList);
if (StringUtils.isNotBlank(versionId)) {
request.setVersionId(versionId);
}
List<Map<String, Object>> moduleCountList = extApiDefinitionMapper.moduleCountByCollection(request);
Map<String, Integer> moduleCountMap = this.parseModuleCountList(moduleCountList);
apiModules.forEach(node -> {
List<String> moduleIds = new ArrayList<>();
moduleIds = this.nodeList(apiModules, node.getId(), moduleIds);
moduleIds.add(node.getId());
int countNum = 0;
for (String moduleId : moduleIds) {
if (moduleCountMap.containsKey(moduleId)) {
countNum += moduleCountMap.get(moduleId).intValue();
}
}
node.setCaseNum(countNum);
});
return getNodeTrees(apiModules);
}
private Map<String, Integer> parseModuleCountList(List<Map<String, Object>> moduleCountList) {
Map<String, Integer> returnMap = new HashMap<>();
for (Map<String, Object> map : moduleCountList) {

View File

@ -100,6 +100,47 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
return getNodeTrees(nodes);
}
public List<ApiScenarioModuleDTO> getNodeTreeByProjectId(String projectId, ApiScenarioRequest request) {
// 判断当前项目下是否有默认模块没有添加默认模块
this.getDefaultNode(projectId);
List<ApiScenarioModuleDTO> nodes = extApiScenarioModuleMapper.getNodeTreeByProjectId(projectId);
request.setProjectId(projectId);
List<String> list = new ArrayList<>();
list.add(ApiTestDataStatus.PREPARE.getValue());
list.add(ApiTestDataStatus.UNDERWAY.getValue());
list.add(ApiTestDataStatus.COMPLETED.getValue());
Map<String, List<String>> filters = new LinkedHashMap<>();
filters.put("status", list);
request.setFilters(filters);
List<String> allModuleIdList = new ArrayList<>();
for (ApiScenarioModuleDTO node : nodes) {
List<String> moduleIds = new ArrayList<>();
moduleIds = this.nodeList(nodes, node.getId(), moduleIds);
moduleIds.add(node.getId());
for (String moduleId : moduleIds) {
if (!allModuleIdList.contains(moduleId)) {
allModuleIdList.add(moduleId);
}
}
}
request.setModuleIds(allModuleIdList);
List<Map<String, Object>> moduleCountList = extApiScenarioMapper.listModuleByCollection(request);
Map<String, Integer> moduleCountMap = this.parseModuleCountList(moduleCountList);
nodes.forEach(node -> {
List<String> moduleIds = new ArrayList<>();
moduleIds = this.nodeList(nodes, node.getId(), moduleIds);
moduleIds.add(node.getId());
int countNum = 0;
for (String moduleId : moduleIds) {
if (moduleCountMap.containsKey(moduleId)) {
countNum += moduleCountMap.get(moduleId).intValue();
}
}
node.setCaseNum(countNum);
});
return getNodeTrees(nodes);
}
public List<ApiScenarioModuleDTO> getTrashNodeTreeByProjectId(String projectId) {
//回收站数据初始化被删除了的数据挂在默认模块上
initTrashDataModule(projectId);
@ -112,6 +153,23 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
return getNodeTrees(trashModuleList);
}
public List<ApiScenarioModuleDTO> getTrashNodeTreeByProjectId(String projectId, ApiScenarioRequest request) {
//回收站数据初始化被删除了的数据挂在默认模块上
initTrashDataModule(projectId);
//通过回收站里的接口模块进行反显
if(request.getFilters() != null && request.getFilters().get("status") != null){
List<String> statusList = new ArrayList<>();
statusList.add(ApiTestDataStatus.TRASH.getValue());
request.getFilters().put("status", statusList);
}
Map<String, List<ApiScenario>> trashApiMap = apiAutomationService.selectApiBaseInfoGroupByModuleId(projectId,
ApiTestDataStatus.TRASH.getValue(), request);
//查找回收站里的模块
List<ApiScenarioModuleDTO> trashModuleList = this.selectTreeStructModuleById(trashApiMap.keySet());
this.initApiCount(trashModuleList, trashApiMap);
return getNodeTrees(trashModuleList);
}
private void initApiCount(List<ApiScenarioModuleDTO> moduleDTOList, Map<String, List<ApiScenario>> scenarioMap) {
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(moduleDTOList) && MapUtils.isNotEmpty(scenarioMap)) {
moduleDTOList.forEach(node -> {

View File

@ -2204,6 +2204,11 @@ public class ApiScenarioService {
return apiScenarioList.stream().collect(Collectors.groupingBy(ApiScenario::getApiScenarioModuleId));
}
public Map<String, List<ApiScenario>> selectApiBaseInfoGroupByModuleId(String projectId, String status, ApiScenarioRequest request) {
List<ApiScenario> apiScenarioList = extApiScenarioMapper.selectBaseInfoByCondition(projectId, status, request);
return apiScenarioList.stream().collect(Collectors.groupingBy(ApiScenario::getApiScenarioModuleId));
}
public List<ApiCountChartResult> countByRequest(ApiCountRequest request) {
return extApiScenarioMapper.countByRequest(request);
}

View File

@ -5,6 +5,11 @@ export function getApiModules(projectId, protocol, currentVersion) {
return get(url);
}
export function postApiModules(projectId, protocol, currentVersion, param) {
let url = '/api/module/list/' + projectId + '/' + protocol + (currentVersion ? '/' + currentVersion : '');
return post(url, param);
}
export function getApiModuleByProjectIdAndProtocol(projectId, protocol) {
let url = '/api/module/list/' + projectId + '/' + protocol;
return get(url);
@ -15,6 +20,11 @@ export function getApiModuleByTrash(projectId, protocol, currentVersion) {
return get(url);
}
export function postApiModuleByTrash(projectId, protocol, currentVersion, param) {
let url = '/api/module/trash/list/' + projectId + '/' + protocol + '/' + (currentVersion ? '/' + currentVersion : '');
return post(url, param);
}
export function getUserDefaultApiType() {
let url = '/api/module/default-type';
return get(url);

View File

@ -5,6 +5,11 @@ export function getModuleByProjectId(projectId) {
return get(url);
}
export function postModuleByProjectId(projectId, param) {
let url = '/api/automation/module/list/' + projectId;
return post(url, param);
}
export function getModuleByRelevanceProjectId(relevanceProjectId) {
let url = '/api/automation/module/list/' + relevanceProjectId;
return get(url);
@ -15,6 +20,11 @@ export function getModuleByTrash(projectId) {
return get(url);
}
export function postModuleByTrash(projectId, param) {
let url = '/api/automation/module/trash/list/' + projectId;
return post(url, param);
}
export function editScenarioModule(params) {
return post('/api/automation/module/edit', params);
}

View File

@ -830,6 +830,7 @@ export default {
}
},
search(projectId) {
this.$EventBus.$emit("scenarioConditionBus", this.condition)
if (this.needRefreshModule()) {
this.$emit('refreshTree');
}

View File

@ -55,7 +55,7 @@ import {
getModuleByProjectId,
getModuleByRelevanceProjectId,
getModuleByTrash,
posScenarioModule,
posScenarioModule, postModuleByProjectId, postModuleByTrash,
} from '@/api/scenario-module';
export default {
@ -112,6 +112,7 @@ export default {
filterText: '',
trashEnable: false,
},
param: {},
data: [],
currentModule: undefined,
operators: [
@ -165,6 +166,16 @@ export default {
this.list();
},
},
created(){
this.$EventBus.$on("scenarioConditionBus", (param)=>{
this.param = param;
})
},
beforeDestroy() {
this.$EventBus.$off("scenarioConditionBus", (param)=>{
this.param = param;
})
},
methods: {
handleImport() {
if (this.projectId) {
@ -188,11 +199,11 @@ export default {
this.setData(response);
});
} else if (this.isTrashData) {
this.result = getModuleByTrash(projectId ? projectId : this.projectId).then((response) => {
this.result = postModuleByTrash(projectId ? projectId : this.projectId, this.param).then((response) => {
this.setData(response);
});
} else {
this.result = getModuleByProjectId(projectId ? projectId : this.projectId).then((response) => {
this.result = postModuleByProjectId(projectId ? projectId : this.projectId, this.param).then((response) => {
this.setData(response);
});
}

View File

@ -849,6 +849,7 @@ export default {
this.search();
},
search() {
this.$EventBus.$emit("apiConditionBus", this.condition)
this.changeSelectDataRangeAll();
this.initTable();
},

View File

@ -52,7 +52,7 @@ import {
getApiModuleByTrash,
getApiModules,
getUserDefaultApiType,
posModule,
posModule, postApiModuleByTrash, postApiModules,
} from '@/api/definition-module';
import MsAddBasisApi from '../basis/AddBasisApi';
import SelectMenu from '@/business/commons/SelectMenu';
@ -83,6 +83,7 @@ export default {
},
data: [],
currentModule: {},
param: {},
};
},
props: {
@ -173,6 +174,16 @@ export default {
}
},
},
created(){
this.$EventBus.$on("apiConditionBus", (param)=>{
this.param = param;
})
},
beforeDestroy() {
this.$EventBus.$off("apiConditionBus", (param)=>{
this.param = param;
})
},
methods: {
initProtocol() {
//
@ -220,11 +231,11 @@ export default {
}
);
} else if (this.isTrashData) {
this.result = getApiModuleByTrash(projectId, this.condition.protocol, this.currentVersion).then((response) => {
this.result = postApiModuleByTrash(projectId, this.condition.protocol, this.currentVersion, this.param).then((response) => {
this.setData(response);
});
} else {
this.result = getApiModules(projectId, this.condition.protocol, this.currentVersion).then((response) => {
this.result = postApiModules(projectId, this.condition.protocol, this.currentVersion, this.param).then((response) => {
this.setData(response);
});
}