refactor: 关系图若干优化

This commit is contained in:
chenjianxing 2021-10-21 21:02:11 +08:00 committed by jianxing
parent 18f3b2b082
commit e6a040428f
31 changed files with 291 additions and 71 deletions

View File

@ -23,7 +23,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
JmeterProperties.class JmeterProperties.class
}) })
@EnableScheduling @EnableScheduling
//@PropertySource(value = {"file:c:\\opt\\metersphere\\conf\\metersphere.properties"}, encoding = "UTF-8", ignoreResourceNotFound = true)
@PropertySource(value = {"file:/opt/metersphere/conf/metersphere.properties"}, encoding = "UTF-8", ignoreResourceNotFound = true) @PropertySource(value = {"file:/opt/metersphere/conf/metersphere.properties"}, encoding = "UTF-8", ignoreResourceNotFound = true)
public class Application { public class Application {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -315,6 +315,11 @@ public class ApiDefinitionController {
return apiDefinitionService.getRelationshipApi(id, relationshipType); return apiDefinitionService.getRelationshipApi(id, relationshipType);
} }
@GetMapping("/relationship/count/{id}/")
public int getRelationshipApi(@PathVariable("id") String id) {
return apiDefinitionService.getRelationshipCount(id);
}
@PostMapping("/relationship/relate/{goPage}/{pageSize}") @PostMapping("/relationship/relate/{goPage}/{pageSize}")
public Pager< List<ApiDefinitionResult>> getRelationshipRelateList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) { public Pager< List<ApiDefinitionResult>> getRelationshipRelateList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true); Page<Object> page = PageHelper.startPage(goPage, pageSize, true);

View File

@ -1607,16 +1607,21 @@ public class ApiDefinitionService {
return extApiDefinitionMapper.countQuotedApiByProjectId(projectId); return extApiDefinitionMapper.countQuotedApiByProjectId(projectId);
} }
public int getRelationshipCount(String id) {
return relationshipEdgeService.getRelationshipCount(id, extApiDefinitionMapper::countByIds);
}
public List<RelationshipEdgeDTO> getRelationshipApi(String id, String relationshipType) { public List<RelationshipEdgeDTO> getRelationshipApi(String id, String relationshipType) {
List<RelationshipEdge> relationshipEdges = relationshipEdgeService.getRelationshipEdgeByType(id, relationshipType); List<RelationshipEdge> relationshipEdges = relationshipEdgeService.getRelationshipEdgeByType(id, relationshipType);
List<String> ids = relationshipEdgeService.getRelationIdsByType(relationshipType, relationshipEdges); List<String> ids = relationshipEdgeService.getRelationIdsByType(relationshipType, relationshipEdges);
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {
ApiDefinitionExample example = new ApiDefinitionExample(); ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(ids); example.createCriteria().andIdIn(ids).andStatusNotEqualTo("Trash");
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example); List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
Map<String, ApiDefinition> apiMap = apiDefinitions.stream().collect(Collectors.toMap(ApiDefinition::getId, i -> i)); Map<String, ApiDefinition> apiMap = apiDefinitions.stream().collect(Collectors.toMap(ApiDefinition::getId, i -> i));
List<RelationshipEdgeDTO> results = new ArrayList<>(); List<RelationshipEdgeDTO> results = new ArrayList<>();
buildUserInfo(apiDefinitions);
for (RelationshipEdge relationshipEdge : relationshipEdges) { for (RelationshipEdge relationshipEdge : relationshipEdges) {
RelationshipEdgeDTO relationshipEdgeDTO = new RelationshipEdgeDTO(); RelationshipEdgeDTO relationshipEdgeDTO = new RelationshipEdgeDTO();
BeanUtils.copyBean(relationshipEdgeDTO, relationshipEdge); BeanUtils.copyBean(relationshipEdgeDTO, relationshipEdge);
@ -1627,8 +1632,9 @@ public class ApiDefinitionService {
apiDefinition = apiMap.get(relationshipEdge.getSourceId()); apiDefinition = apiMap.get(relationshipEdge.getSourceId());
} }
relationshipEdgeDTO.setTargetName(apiDefinition.getName()); relationshipEdgeDTO.setTargetName(apiDefinition.getName());
relationshipEdgeDTO.setCreator(apiDefinition.getCreateUser()); relationshipEdgeDTO.setCreator(apiDefinition.getUserId());
relationshipEdgeDTO.setTargetNum(apiDefinition.getNum()); relationshipEdgeDTO.setTargetNum(apiDefinition.getNum());
relationshipEdgeDTO.setStatus(apiDefinition.getStatus());
results.add(relationshipEdgeDTO); results.add(relationshipEdgeDTO);
} }
return results; return results;
@ -1636,6 +1642,21 @@ public class ApiDefinitionService {
return new ArrayList<>(); return new ArrayList<>();
} }
public void buildUserInfo(List<? extends ApiDefinition> apis) {
List<String> userIds = new ArrayList();
userIds.addAll(apis.stream().map(ApiDefinition::getCreateUser).collect(Collectors.toList()));
userIds.addAll(apis.stream().map(ApiDefinition::getDeleteUserId).collect(Collectors.toList()));
userIds.addAll(apis.stream().map(ApiDefinition::getUserId).collect(Collectors.toList()));
if (!org.apache.commons.collections.CollectionUtils.isEmpty(userIds)) {
Map<String, String> userMap = ServiceUtils.getUserNameMap(userIds);
apis.forEach(caseResult -> {
caseResult.setCreateUser(userMap.get(caseResult.getCreateUser()));
caseResult.setDeleteUserId(userMap.get(caseResult.getDeleteUserId()));
caseResult.setUserId(userMap.get(caseResult.getUserId()));
});
}
}
public List<ApiDefinitionResult> getRelationshipRelateList(ApiDefinitionRequest request) { public List<ApiDefinitionResult> getRelationshipRelateList(ApiDefinitionRequest request) {
request = this.initRequest(request, true, true); request = this.initRequest(request, true, true);
List<String> relationshipIds = relationshipEdgeService.getRelationshipIds(request.getId()); List<String> relationshipIds = relationshipEdgeService.getRelationshipIds(request.getId());

View File

@ -76,4 +76,6 @@ public interface ExtApiDefinitionMapper {
long countQuotedApiByProjectId(String projectId); long countQuotedApiByProjectId(String projectId);
List<RelationshipGraphData.Node> getForGraph(@Param("ids") Set<String> ids); List<RelationshipGraphData.Node> getForGraph(@Param("ids") Set<String> ids);
int countByIds(@Param("ids") List<String> ids);
} }

View File

@ -720,5 +720,13 @@
where t3.api_definition_id = #{apiDefinitionId} where t3.api_definition_id = #{apiDefinitionId}
and t3.`status`!='Trash' and t3.`status`!='Trash'
</select> </select>
<select id="countByIds" resultType="java.lang.Integer">
select count(id) from api_definition
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
and api_definition.status != 'Trash';
</select>
</mapper> </mapper>

View File

@ -123,4 +123,6 @@ public interface ExtTestCaseMapper {
List<TestCase> getTestCase(@Param("request") QueryTestCaseRequest request); List<TestCase> getTestCase(@Param("request") QueryTestCaseRequest request);
List<RelationshipGraphData.Node> getTestCaseForGraph(@Param("ids") Set<String> ids); List<RelationshipGraphData.Node> getTestCaseForGraph(@Param("ids") Set<String> ids);
int countByIds(@Param("ids") List<String> ids);
} }

View File

@ -574,6 +574,15 @@
</foreach> </foreach>
and (test_case.status != 'Trash' or test_case.status is NULL); and (test_case.status != 'Trash' or test_case.status is NULL);
</select> </select>
<select id="countByIds" resultType="java.lang.Integer">
select count(id)
from test_case
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
and (test_case.status != 'Trash' or test_case.status is NULL);
</select>
<update id="deleteToGc"> <update id="deleteToGc">
update test_case set original_status=status, update test_case set original_status=status,

View File

@ -1,6 +1,8 @@
package io.metersphere.service; package io.metersphere.service;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.CustomField; import io.metersphere.base.domain.CustomField;
@ -13,6 +15,7 @@ import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*; import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.QueryCustomFieldRequest; import io.metersphere.controller.request.QueryCustomFieldRequest;
import io.metersphere.dto.CustomFieldDao; import io.metersphere.dto.CustomFieldDao;
import io.metersphere.dto.CustomFieldItemDTO;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import io.metersphere.log.utils.ReflexObjectUtil; import io.metersphere.log.utils.ReflexObjectUtil;
import io.metersphere.log.vo.DetailColumn; import io.metersphere.log.vo.DetailColumn;
@ -205,4 +208,13 @@ public class CustomFieldService {
} }
return null; return null;
} }
public static List<CustomFieldItemDTO> getCustomFields(String customFieldsStr) {
if (StringUtils.isNotBlank(customFieldsStr)) {
if (JSONObject.parse(customFieldsStr) instanceof JSONArray) {
return JSONArray.parseArray(customFieldsStr, CustomFieldItemDTO.class);
}
}
return new ArrayList<>();
}
} }

View File

@ -18,6 +18,7 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -97,6 +98,17 @@ public class RelationshipEdgeService {
return relationshipEdgeMapper.selectByExample(example); return relationshipEdgeMapper.selectByExample(example);
} }
public List<RelationshipEdge> getBySourceIdOrTargetId(String id) {
RelationshipEdgeExample example = new RelationshipEdgeExample();
example.createCriteria()
.andSourceIdEqualTo(id);
example.or(
example.createCriteria()
.andTargetIdEqualTo(id)
);
return relationshipEdgeMapper.selectByExample(example);
}
/** /**
* 保存新的边 * 保存新的边
* 校验是否存在环 * 校验是否存在环
@ -128,11 +140,16 @@ public class RelationshipEdgeService {
}); });
} }
// 判断是否有环, 两个方向都搜索一遍 HashSet<String> nodeIds = new HashSet<>();
if (directedCycle(request.getId(), relationshipEdges, new HashSet<>(), true) || nodeIds.addAll(relationshipEdges.stream().map(RelationshipEdge::getSourceId).collect(Collectors.toSet()));
directedCycle(request.getId(), relationshipEdges, new HashSet<>(), false)) { nodeIds.addAll(relationshipEdges.stream().map(RelationshipEdge::getTargetId).collect(Collectors.toSet()));
// 判断是否有环
HashSet<String> visitedSet = new HashSet<>();
nodeIds.forEach(nodeId -> {
if (!visitedSet.contains(nodeId) && directedCycle(nodeId, relationshipEdges, new HashSet<>(), visitedSet)) {
MSException.throwException("关联后存在循环依赖,请检查依赖关系"); MSException.throwException("关联后存在循环依赖,请检查依赖关系");
}; };
});
relationshipEdges.forEach(item -> { relationshipEdges.forEach(item -> {
if (addEdgesIds.contains(item.getSourceId() + item.getTargetId())) { if (addEdgesIds.contains(item.getSourceId() + item.getTargetId())) {
@ -186,11 +203,11 @@ public class RelationshipEdgeService {
* 给定一点深度搜索该连通图中是否存在环 * 给定一点深度搜索该连通图中是否存在环
* @param id * @param id
* @param edges * @param edges
* @param markSet * @param markSet 标记该路径上经过的节点
* @param isForwardDirection * @param visitedSet 标记访问过的节点
* @return * @return
*/ */
public boolean directedCycle(String id, List<RelationshipEdge> edges, Set<String> markSet, Boolean isForwardDirection) { public boolean directedCycle(String id, List<RelationshipEdge> edges, Set<String> markSet, Set<String> visitedSet) {
if (markSet.contains(id)) { if (markSet.contains(id)) {
// 如果已经访问过该节点则说明存在环 // 如果已经访问过该节点则说明存在环
@ -198,23 +215,19 @@ public class RelationshipEdgeService {
} }
markSet.add(id); markSet.add(id);
visitedSet.add(id);
ArrayList<String> nextLevelNodes = new ArrayList(); ArrayList<String> nextLevelNodes = new ArrayList();
for (RelationshipEdge relationshipEdge : edges) { for (RelationshipEdge relationshipEdge : edges) {
if (isForwardDirection) {// 正向则搜索 sourceId 是当前节点的边
if (id.equals(relationshipEdge.getSourceId())) { if (id.equals(relationshipEdge.getSourceId())) {
nextLevelNodes.add(relationshipEdge.getTargetId()); nextLevelNodes.add(relationshipEdge.getTargetId());
} }
} else {
if (id.equals(relationshipEdge.getTargetId())) {
nextLevelNodes.add(relationshipEdge.getSourceId());
}
}
} }
for (String nextNode : nextLevelNodes) { for (String nextNode : nextLevelNodes) {
if (directedCycle(nextNode, edges, markSet, isForwardDirection)) { if (directedCycle(nextNode, edges, markSet, visitedSet)) {
return true; return true;
}; }
} }
// 关键递归完这一条路径要把这个标记去掉否则会误判为有环 // 关键递归完这一条路径要把这个标记去掉否则会误判为有环
@ -225,17 +238,24 @@ public class RelationshipEdgeService {
} }
/** /**
* 给定一个节点获取跟他关联的所有节点的id * 给定一个节点获取直接关联的节点的id
* @param nodeId * @param nodeId
* @return * @return
*/ */
public List<String> getRelationshipIds(String nodeId) { public List<String> getRelationshipIds(String nodeId) {
List<RelationshipEdge> sourceRelationshipEdges = getBySourceId(nodeId); List<RelationshipEdge> sourceRelationshipEdges = getBySourceIdOrTargetId(nodeId);
List<RelationshipEdge> targetRelationshipEdges = getByTargetId(nodeId);
List<String> ids = sourceRelationshipEdges.stream().map(RelationshipEdge::getTargetId).collect(Collectors.toList()); List<String> ids = sourceRelationshipEdges.stream().map(RelationshipEdge::getTargetId).collect(Collectors.toList());
ids.addAll(targetRelationshipEdges.stream().map(RelationshipEdge::getSourceId).collect(Collectors.toList())); ids.addAll(sourceRelationshipEdges.stream().map(RelationshipEdge::getSourceId).collect(Collectors.toList()));
ids.add(nodeId); ids.add(nodeId);
return ids; return ids;
} }
public int getRelationshipCount(String id, Function<List<String>, Integer> countByIdsFunc) {
List<String> ids = getRelationshipIds(id);
ids = ids.stream().filter(i -> !i.equals(id)).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(ids)) {
return countByIdsFunc.apply(ids);
}
return 0;
}
} }

View File

@ -105,6 +105,11 @@ public class TestCaseController {
return testCaseService.getRelationshipCase(id, relationshipType); return testCaseService.getRelationshipCase(id, relationshipType);
} }
@GetMapping("/relationship/case/count/{id}")
public int getRelationshipCase(@PathVariable("id") String id) {
return testCaseService.getRelationshipCount(id);
}
@GetMapping("recent/{count}") @GetMapping("recent/{count}")
public List<TestCase> recentTestPlans(@PathVariable int count) { public List<TestCase> recentTestPlans(@PathVariable int count) {
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId(); String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();

View File

@ -1,6 +1,5 @@
package io.metersphere.track.issue; package io.metersphere.track.issue;
import com.alibaba.fastjson.JSONArray;
import io.metersphere.base.domain.*; import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.IssuesMapper; import io.metersphere.base.mapper.IssuesMapper;
import io.metersphere.base.mapper.ProjectMapper; import io.metersphere.base.mapper.ProjectMapper;
@ -11,10 +10,7 @@ import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.IntegrationRequest; import io.metersphere.controller.request.IntegrationRequest;
import io.metersphere.dto.CustomFieldItemDTO; import io.metersphere.dto.CustomFieldItemDTO;
import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserDTO;
import io.metersphere.service.IntegrationService; import io.metersphere.service.*;
import io.metersphere.service.ProjectService;
import io.metersphere.service.ResourceService;
import io.metersphere.service.UserService;
import io.metersphere.track.request.testcase.IssuesRequest; import io.metersphere.track.request.testcase.IssuesRequest;
import io.metersphere.track.request.testcase.IssuesUpdateRequest; import io.metersphere.track.request.testcase.IssuesUpdateRequest;
import io.metersphere.track.service.TestCaseIssueService; import io.metersphere.track.service.TestCaseIssueService;
@ -218,13 +214,6 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
} }
} }
protected List<CustomFieldItemDTO> getCustomFields(String customFieldsStr) {
if (StringUtils.isNotBlank(customFieldsStr)) {
return JSONArray.parseArray(customFieldsStr, CustomFieldItemDTO.class);
}
return new ArrayList<>();
}
/** /**
* 将html格式的缺陷描述转成ms平台的格式 * 将html格式的缺陷描述转成ms平台的格式
* *
@ -347,7 +336,7 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
} }
protected void addCustomFields(IssuesUpdateRequest issuesRequest, MultiValueMap<String, Object> paramMap) { protected void addCustomFields(IssuesUpdateRequest issuesRequest, MultiValueMap<String, Object> paramMap) {
List<CustomFieldItemDTO> customFields = getCustomFields(issuesRequest.getCustomFields()); List<CustomFieldItemDTO> customFields = CustomFieldService.getCustomFields(issuesRequest.getCustomFields());
customFields.forEach(item -> { customFields.forEach(item -> {
if (StringUtils.isNotBlank(item.getCustomData())) { if (StringUtils.isNotBlank(item.getCustomData())) {
paramMap.add(item.getCustomData(), item.getValue()); paramMap.add(item.getCustomData(), item.getValue());

View File

@ -11,6 +11,7 @@ import io.metersphere.commons.utils.EncryptUtils;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.CustomFieldItemDTO; import io.metersphere.dto.CustomFieldItemDTO;
import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserDTO;
import io.metersphere.service.CustomFieldService;
import io.metersphere.track.dto.DemandDTO; import io.metersphere.track.dto.DemandDTO;
import io.metersphere.track.issue.client.JiraClientV2; import io.metersphere.track.issue.client.JiraClientV2;
import io.metersphere.track.issue.domain.Jira.JiraAddIssueResponse; import io.metersphere.track.issue.domain.Jira.JiraAddIssueResponse;
@ -109,7 +110,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
} }
public String parseIssueCustomField(String customFieldsStr, JiraIssue jiraIssue) { public String parseIssueCustomField(String customFieldsStr, JiraIssue jiraIssue) {
List<CustomFieldItemDTO> customFields = getCustomFields(customFieldsStr); List<CustomFieldItemDTO> customFields = CustomFieldService.getCustomFields(customFieldsStr);
JSONObject fields = jiraIssue.getFields(); JSONObject fields = jiraIssue.getFields();
customFields.forEach(item -> { customFields.forEach(item -> {
@ -286,7 +287,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
JSONObject addJiraIssueParam = new JSONObject(); JSONObject addJiraIssueParam = new JSONObject();
addJiraIssueParam.put("fields", fields); addJiraIssueParam.put("fields", fields);
List<CustomFieldItemDTO> customFields = getCustomFields(issuesRequest.getCustomFields()); List<CustomFieldItemDTO> customFields = CustomFieldService.getCustomFields(issuesRequest.getCustomFields());
jiraClientV2.setConfig(config); jiraClientV2.setConfig(config);
customFields.forEach(item -> { customFields.forEach(item -> {

View File

@ -15,7 +15,6 @@ import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.ResultHolder; import io.metersphere.controller.ResultHolder;
import io.metersphere.dto.CustomFieldItemDTO;
import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserDTO;
import io.metersphere.service.SystemParameterService; import io.metersphere.service.SystemParameterService;
import io.metersphere.track.dto.DemandDTO; import io.metersphere.track.dto.DemandDTO;

View File

@ -10,6 +10,7 @@ import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest; import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest; import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.api.service.ApiAutomationService; import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.api.service.ApiTestCaseService; import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.*; import io.metersphere.base.domain.*;
@ -1971,8 +1972,9 @@ public class TestCaseService {
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {
TestCaseExample example = new TestCaseExample(); TestCaseExample example = new TestCaseExample();
example.createCriteria().andIdIn(ids); example.createCriteria().andIdIn(ids).andStatusNotEqualTo("Trash");
List<TestCase> testCaseList = testCaseMapper.selectByExample(example); List<TestCaseWithBLOBs> testCaseList = testCaseMapper.selectByExampleWithBLOBs(example);
buildUserInfo(testCaseList);
Map<String, TestCase> caseMap = testCaseList.stream().collect(Collectors.toMap(TestCase::getId, i -> i)); Map<String, TestCase> caseMap = testCaseList.stream().collect(Collectors.toMap(TestCase::getId, i -> i));
List<RelationshipEdgeDTO> results = new ArrayList<>(); List<RelationshipEdgeDTO> results = new ArrayList<>();
for (RelationshipEdge relationshipEdge : relationshipEdges) { for (RelationshipEdge relationshipEdge : relationshipEdges) {
@ -1988,10 +1990,30 @@ public class TestCaseService {
relationshipEdgeDTO.setCreator(testCase.getCreateUser()); relationshipEdgeDTO.setCreator(testCase.getCreateUser());
relationshipEdgeDTO.setTargetNum(testCase.getNum()); relationshipEdgeDTO.setTargetNum(testCase.getNum());
relationshipEdgeDTO.setTargetCustomNum(testCase.getCustomNum()); relationshipEdgeDTO.setTargetCustomNum(testCase.getCustomNum());
relationshipEdgeDTO.setStatus(testCase.getStatus());
results.add(relationshipEdgeDTO); results.add(relationshipEdgeDTO);
} }
return results; return results;
} }
return new ArrayList<>(); return new ArrayList<>();
} }
public void buildUserInfo(List<? extends TestCase> testCases) {
List<String> userIds = new ArrayList();
userIds.addAll(testCases.stream().map(TestCase::getCreateUser).collect(Collectors.toList()));
userIds.addAll(testCases.stream().map(TestCase::getDeleteUserId).collect(Collectors.toList()));
userIds.addAll(testCases.stream().map(TestCase::getMaintainer).collect(Collectors.toList()));
if (!org.apache.commons.collections.CollectionUtils.isEmpty(userIds)) {
Map<String, String> userMap = ServiceUtils.getUserNameMap(userIds);
testCases.forEach(caseResult -> {
caseResult.setCreateUser(userMap.get(caseResult.getCreateUser()));
caseResult.setDeleteUserId(userMap.get(caseResult.getDeleteUserId()));
caseResult.setMaintainer(userMap.get(caseResult.getMaintainer()));
});
}
}
public int getRelationshipCount(String id) {
return relationshipEdgeService.getRelationshipCount(id, extTestCaseMapper::countByIds);
}
} }

@ -1 +1 @@
Subproject commit 6aff490dd3321a760e2a519c0cb6aa6c90850f3f Subproject commit a86e39275aae2a3e2e8aae705da5a4dddbd0089d

View File

@ -9,7 +9,10 @@
<form-rich-text-item class="remark-item" :disabled="readOnly && !hasPermissions" :data="api" prop="remark" label-width="0"/> <form-rich-text-item class="remark-item" :disabled="readOnly && !hasPermissions" :data="api" prop="remark" label-width="0"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('commons.relationship.name')" name="dependencies" class="pane"> <el-tab-pane :label="$t('commons.relationship.name')" name="dependencies" class="pane">
<dependencies-list :read-only="readOnly" :resource-id="api.id" resource-type="API" ref="dependencies"/> <template v-slot:label>
<tab-pane-count :title="$t('commons.relationship.name')" :count="relationshipCount"/>
</template>
<dependencies-list @setCount="setCount" :read-only="readOnly" :resource-id="api.id" resource-type="API" ref="dependencies"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</el-collapse-transition> </el-collapse-transition>
@ -24,13 +27,16 @@ import ApiInfoContainer from "@/business/components/api/definition/components/co
import DependenciesList from "@/business/components/common/components/graph/DependenciesList"; import DependenciesList from "@/business/components/common/components/graph/DependenciesList";
import FormRichTextItem from "@/business/components/track/case/components/FormRichTextItem"; import FormRichTextItem from "@/business/components/track/case/components/FormRichTextItem";
import {hasPermissions} from "@/common/js/utils"; import {hasPermissions} from "@/common/js/utils";
import TabPaneCount from "@/business/components/track/plan/view/comonents/report/detail/component/TabPaneCount";
import {getRelationshipCountApi} from "@/network/api";
export default { export default {
name: "ApiOtherInfo", name: "ApiOtherInfo",
components: {FormRichTextItem, DependenciesList, ApiInfoContainer, MsFormDivider}, components: {TabPaneCount, FormRichTextItem, DependenciesList, ApiInfoContainer, MsFormDivider},
props: ['api','readOnly'], props: ['api','readOnly'],
data() { data() {
return { return {
activeName: 'remark' activeName: 'remark',
relationshipCount: 0
} }
}, },
computed: { computed: {
@ -43,8 +49,18 @@ export default {
if (this.activeName === 'dependencies') { if (this.activeName === 'dependencies') {
this.$refs.dependencies.open(); this.$refs.dependencies.open();
} }
}
}, },
},
mounted() {
getRelationshipCountApi(this.api.id, (data) => {
this.relationshipCount = data;
});
},
methods: {
setCount(count) {
this.relationshipCount = count;
},
}
} }
</script> </script>

View File

@ -26,6 +26,17 @@
min-width="120"> min-width="120">
</ms-table-column> </ms-table-column>
<ms-table-column
prop="status"
min-width="120px"
:label="$t('api_test.definition.api_status')">
<template v-slot:default="scope">
<span class="el-dropdown-link">
<api-status :value="scope.row.status"/>
</span>
</template>
</ms-table-column>
</ms-table> </ms-table>
<api-relationship-relevance <api-relationship-relevance
@ -46,9 +57,12 @@ import RelationshipFunctionalRelevance
import {getRelationshipApi} from "@/network/api"; import {getRelationshipApi} from "@/network/api";
import ApiRelationshipRelevance import ApiRelationshipRelevance
from "@/business/components/api/definition/components/complete/ApiRelationshipRelevance"; from "@/business/components/api/definition/components/complete/ApiRelationshipRelevance";
import ApiStatus from "@/business/components/api/definition/components/list/ApiStatus";
export default { export default {
name: "ApiRelationshipList", name: "ApiRelationshipList",
components: {ApiRelationshipRelevance, RelationshipFunctionalRelevance, MsTableSearchBar, MsTableColumn, MsTable}, components: {
ApiStatus,
ApiRelationshipRelevance, RelationshipFunctionalRelevance, MsTableSearchBar, MsTableColumn, MsTable},
data() { data() {
return { return {
result: {}, result: {},
@ -74,6 +88,7 @@ export default {
getTableData() { getTableData() {
getRelationshipApi(this.apiDefinitionId, this.relationshipType, (data) => { getRelationshipApi(this.apiDefinitionId, this.relationshipType, (data) => {
this.data = data; this.data = data;
this.$emit('setCount', data.length);
}); });
}, },
openRelevance() { openRelevance() {

View File

@ -1,11 +1,23 @@
<template> <template>
<div class="dependencies-container"> <div class="dependencies-container">
<el-main v-xpack>
<i class="el-icon-view" @click="openGraph"></i>
</el-main>
<relationship-list :read-only="readOnly" :title="$t('commons.relationship.pre')" relationship-type="PRE" :resource-id="resourceId" :resource-type="resourceType" ref="preRelationshipList"/> <el-tooltip v-xpack class="item" effect="dark" :content="$t('commons.relationship.graph')" placement="left">
<relationship-list :read-only="readOnly" :title="$t('commons.relationship.post')" relationship-type="POST" :resource-id="resourceId" :resource-type="resourceType" ref="postRelationshipList"/> <font-awesome-icon class="graph-icon" :icon="['fas', 'sitemap']" size="lg" @click="openGraph"/>
</el-tooltip>
<relationship-list
class="pre-list"
:read-only="readOnly" :title="resourceType === 'TEST_CASE' ? $t('commons.relationship.pre_case') : $t('commons.relationship.pre_api')"
relationship-type="PRE" :resource-id="resourceId"
@setCount="setPreCount"
:resource-type="resourceType" ref="preRelationshipList"/>
<relationship-list
class="post-list"
:read-only="readOnly"
:title="resourceType === 'TEST_CASE' ? $t('commons.relationship.post_case') : $t('commons.relationship.post_api')"
relationship-type="POST" :resource-id="resourceId"
@setCount="setPostCount"
:resource-type="resourceType" ref="postRelationshipList"/>
<relationship-graph-drawer v-permission :graph-data="graphData" ref="relationshipGraph"/> <relationship-graph-drawer v-permission :graph-data="graphData" ref="relationshipGraph"/>
@ -26,7 +38,9 @@ export default {
], ],
data() { data() {
return { return {
graphData: {} graphData: {},
preCount: 0,
postCount: 0,
} }
}, },
methods: { methods: {
@ -39,13 +53,26 @@ export default {
this.graphData = data; this.graphData = data;
this.$refs.relationshipGraph.open(); this.$refs.relationshipGraph.open();
}); });
} },
setPreCount(count) {
this.preCount = count;
this.$emit('setCount', this.preCount + this.postCount);
},
setPostCount(count) {
this.postCount = count;
this.$emit('setCount', this.preCount + this.postCount);
},
} }
} }
</script> </script>
<style scoped> <style scoped>
.dependencies-container .el-main { .post-list {
margin-top: 20px; margin-top: 20px;
} }
.graph-icon {
float: right;
margin-right: 20px;
}
</style> </style>

View File

@ -9,6 +9,7 @@
:case-id="resourceId" :case-id="resourceId"
:read-only="readOnly" :read-only="readOnly"
:relationship-type="relationshipType" :relationship-type="relationshipType"
@setCount="setCount"
@deleteRelationship="handleDelete" @deleteRelationship="handleDelete"
ref="testCaseRelationshipList"/> ref="testCaseRelationshipList"/>
@ -17,6 +18,7 @@
:read-only="readOnly" :read-only="readOnly"
:api-definition-id="resourceId" :api-definition-id="resourceId"
:relationship-type="relationshipType" :relationship-type="relationshipType"
@setCount="setCount"
@deleteRelationship="handleDelete" @deleteRelationship="handleDelete"
ref="testCaseRelationshipList"/> ref="testCaseRelationshipList"/>
@ -66,6 +68,9 @@ export default {
this.$success(this.$t('commons.delete_success')); this.$success(this.$t('commons.delete_success'));
}); });
}, },
setCount(count) {
this.$emit('setCount', count);
}
} }
} }
</script> </script>

View File

@ -337,6 +337,7 @@
getTestTemplate() getTestTemplate()
.then((template) => { .then((template) => {
this.testCaseTemplate = template; this.testCaseTemplate = template;
this.$store.commit('setTestCaseTemplate', this.testCaseTemplate);
initAddFuc(); initAddFuc();
}); });
if (this.selectNode && this.selectNode.data && !this.form.id) { if (this.selectNode && this.selectNode.data && !this.form.id) {
@ -461,6 +462,7 @@
getTemplate('field/template/case/get/relate/', this) getTemplate('field/template/case/get/relate/', this)
.then((template) => { .then((template) => {
this.testCaseTemplate = template; this.testCaseTemplate = template;
this.$store.commit('setTestCaseTemplate', this.testCaseTemplate);
initFuc(testCase); initFuc(testCase);
}); });
}, },

View File

@ -40,7 +40,10 @@
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('commons.relationship.name')" name="relationship"> <el-tab-pane :label="$t('commons.relationship.name')" name="relationship">
<dependencies-list :read-only="readOnly" :resource-id="caseId" resource-type="TEST_CASE" ref="relationship"/> <template v-slot:label>
<tab-pane-count :title="$t('commons.relationship.name')" :count="relationshipCount"/>
</template>
<dependencies-list @setCount="setRelationshipCount" :read-only="readOnly" :resource-id="caseId" resource-type="TEST_CASE" ref="relationship"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('test_track.case.attachment')" name="attachment"> <el-tab-pane :label="$t('test_track.case.attachment')" name="attachment">
@ -83,10 +86,13 @@ import TestCaseIssueRelate from "@/business/components/track/case/components/Tes
import FormRichTextItem from "@/business/components/track/case/components/FormRichTextItem"; import FormRichTextItem from "@/business/components/track/case/components/FormRichTextItem";
import TestCaseTestRelate from "@/business/components/track/case/components/TestCaseTestRelate"; import TestCaseTestRelate from "@/business/components/track/case/components/TestCaseTestRelate";
import DependenciesList from "@/business/components/common/components/graph/DependenciesList"; import DependenciesList from "@/business/components/common/components/graph/DependenciesList";
import TabPaneCount from "@/business/components/track/plan/view/comonents/report/detail/component/TabPaneCount";
import {getRelationshipCountCase} from "@/network/testCase";
export default { export default {
name: "TestCaseEditOtherInfo", name: "TestCaseEditOtherInfo",
components: { components: {
TabPaneCount,
DependenciesList, DependenciesList,
TestCaseTestRelate, TestCaseTestRelate,
FormRichTextItem, TestCaseIssueRelate, TestCaseAttachment, MsRichText, TestCaseRichText}, FormRichTextItem, TestCaseIssueRelate, TestCaseAttachment, MsRichText, TestCaseRichText},
@ -99,6 +105,7 @@ export default {
fileList: [], fileList: [],
tableData: [], tableData: [],
demandOptions: [], demandOptions: [],
relationshipCount: 0,
//sysList:this.sysList,// //sysList:this.sysList,//
props: { props: {
multiple: true, multiple: true,
@ -123,12 +130,20 @@ export default {
} else if (this.tabActiveName === 'attachment') { } else if (this.tabActiveName === 'attachment') {
this.getFileMetaData(); this.getFileMetaData();
} }
},
caseId() {
getRelationshipCountCase(this.caseId, (data) => {
this.relationshipCount = data;
});
} }
}, },
methods: { methods: {
updateRemark(text) { updateRemark(text) {
this.form.remark = text; this.form.remark = text;
}, },
setRelationshipCount(count) {
this.relationshipCount = count;
},
reset() { reset() {
this.tabActiveName = "remark"; this.tabActiveName = "remark";
}, },

View File

@ -33,6 +33,15 @@
min-width="120"> min-width="120">
</ms-table-column> </ms-table-column>
<ms-table-column
prop="status"
min-width="100px"
:label="$t('api_test.definition.api_case_status')">
<template slot-scope="{row}">
{{$t(statusMap.get(row.status))}}
</template>
</ms-table-column>
</ms-table> </ms-table>
<relationship-functional-relevance <relationship-functional-relevance
@ -68,6 +77,7 @@ export default {
], ],
condition: {}, condition: {},
options: [], options: [],
statusMap: new Map,
value: '' value: ''
} }
}, },
@ -78,6 +88,14 @@ export default {
}, },
computed: { computed: {
isCustomNum() { isCustomNum() {
let template = this.$store.state.testCaseTemplate;
template.customFields.forEach(item => {
if (item.name === '用例状态') {
for (let i = 0; i < item.options.length; i++) {
this.statusMap.set(item.options[i].value, item.options[i].text);
}
}
});
return this.$store.state.currentProjectIsCustomNum; return this.$store.state.currentProjectIsCustomNum;
}, },
}, },
@ -85,6 +103,7 @@ export default {
getTableData() { getTableData() {
getRelationshipCase(this.caseId, this.relationshipType, (data) => { getRelationshipCase(this.caseId, this.relationshipType, (data) => {
this.data = data; this.data = data;
this.$emit('setCount', data.length);
}); });
}, },
openRelevance() { openRelevance() {

View File

@ -1,7 +1,7 @@
<template> <template>
<span> <span>
<span>{{title}}</span> <span>{{title}}</span>
<span class="el-step__icon is-text ms-api-col ms-header" v-if="count > 0"> <span class="el-step__icon is-text ms-api-col ms-header" :class="{'fix-color': fixColor}" v-if="count > 0">
<span class="el-step__icon-inner">{{count}}</span> <span class="el-step__icon-inner">{{count}}</span>
</span> </span>
</span> </span>
@ -10,7 +10,7 @@
<script> <script>
export default { export default {
name: "TabPaneCount", name: "TabPaneCount",
props: ['title', 'count'] props: ['title', 'count', 'fixColor']
} }
</script> </script>
@ -24,4 +24,14 @@ export default {
/*border-radius: 42%;*/ /*border-radius: 42%;*/
/*margin-left: 4px;*/ /*margin-left: 4px;*/
} }
.fix-color {
background: #783887;
color: white;
/*color: #783887;*/
/*height: 20px;*/
/*border: 0px !important;*/
border-radius: 42%;
margin-left: 4px;
}
</style> </style>

@ -1 +1 @@
Subproject commit 182a87511828000f93a247326b6cedee213e89cb Subproject commit 60d329a5bff2a631c56c4d045c7cea018f65cda9

View File

@ -189,13 +189,14 @@ export default {
generate_test_data: "Generate test data", generate_test_data: "Generate test data",
relationship: { relationship: {
name: 'Dependencies', name: 'Dependencies',
pre: 'Prepositional Object', pre_case: 'Prepositional Case',
post: 'Postposition Object', post_case: 'Postposition Case',
pre_api: 'Prepositional API',
post_api: 'Postposition API',
graph: 'Dependencies Graph', graph: 'Dependencies Graph',
selected: 'Selected Node', selected: 'Selected Node',
direct: 'Direct Link', direct: 'Direct Link',
indirect: 'Indirect Link', indirect: 'Indirect Link',
}, },
project_setting: "Project Setting", project_setting: "Project Setting",
table: { table: {

View File

@ -189,9 +189,11 @@ export default {
run_fail: "执行失败", run_fail: "执行失败",
relationship: { relationship: {
name: '依赖关系', name: '依赖关系',
pre: '前置对象', pre_case: '前置用例',
post: '后置置对象', post_case: '后置用例',
graph: '依赖图', pre_api: '前置接口',
post_api: '后置接口',
graph: '依赖关系图',
selected: '选中节点', selected: '选中节点',
direct: '直接关联', direct: '直接关联',
indirect: '间接关联', indirect: '间接关联',

View File

@ -189,9 +189,11 @@ export default {
run_fail: "執行失敗", run_fail: "執行失敗",
relationship: { relationship: {
name: '依賴關系', name: '依賴關系',
pre: '前置對象', pre_case: '前置用例',
post: '後置置對象', post_case: '後置用例',
graph: '依賴圖', pre_api: '前置接口',
post_api: '後置接口',
graph: '依賴關系圖',
selected: '選中節點', selected: '選中節點',
direct: '直接關聯', direct: '直接關聯',
indirect: '間接關聯', indirect: '間接關聯',

View File

@ -35,3 +35,7 @@ export function editApiTestCaseOrder(request, callback) {
export function getRelationshipApi(id, relationshipType, callback) { export function getRelationshipApi(id, relationshipType, callback) {
return baseGet('/api/definition/relationship/' + id + '/' + relationshipType, callback); return baseGet('/api/definition/relationship/' + id + '/' + relationshipType, callback);
} }
export function getRelationshipCountApi(id, callback) {
return baseGet('/api/definition/relationship/count/' + id + '/', callback);
}

View File

@ -70,3 +70,7 @@ export function getTestCaseNodes(projectId, callback) {
export function getRelationshipCase(id, relationshipType, callback) { export function getRelationshipCase(id, relationshipType, callback) {
return baseGet('/test/case/relationship/case/' + id + '/' + relationshipType, callback); return baseGet('/test/case/relationship/case/' + id + '/' + relationshipType, callback);
} }
export function getRelationshipCountCase(id, callback) {
return baseGet('/test/case/relationship/case/count/' + id + '/', callback);
}

View File

@ -28,7 +28,8 @@ const state = {
pluginFiles: [], pluginFiles: [],
isTestCaseMinderChanged: false, isTestCaseMinderChanged: false,
// 当前项目是否勾选自定义ID // 当前项目是否勾选自定义ID
currentProjectIsCustomNum: false currentProjectIsCustomNum: false,
testCaseTemplate: {},
} }
const store = new Vuex.Store({ const store = new Vuex.Store({

View File

@ -17,6 +17,9 @@ const mutations = {
setTestPlanViewSelectNode: (state, value) => state.testPlanViewSelectNode = value, setTestPlanViewSelectNode: (state, value) => state.testPlanViewSelectNode = value,
setIsTestCaseMinderChanged: (state, value) => state.isTestCaseMinderChanged = value, setIsTestCaseMinderChanged: (state, value) => state.isTestCaseMinderChanged = value,
setCurrentProjectIsCustomNum: (state, value) => state.currentProjectIsCustomNum = value, setCurrentProjectIsCustomNum: (state, value) => state.currentProjectIsCustomNum = value,
setTestCaseTemplate: (state, value) => {
state.testCaseTemplate = value
},
} }
export default mutations; export default mutations;