feat:关系图谱优化
This commit is contained in:
1 parent
2cf4ef4d10
commit
0149da2823
6 files changed
+354
-48
No files matched your search
+49
@@ -0,0 +1,49 @@
|
||||
package com.bsd.pjcloud.admin.api.dto.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 修改用户关系
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "修改用户关系")
|
||||
public class SysUserRelationUpdateDto {
|
||||
|
||||
@NotNull(message = "关系ID不能为空")
|
||||
@Schema(description = "关系ID")
|
||||
private Long relationId;
|
||||
|
||||
@NotNull(message = "关系主语用户不能为空")
|
||||
@Schema(description = "关系主语用户ID")
|
||||
private Long subjectUserId;
|
||||
|
||||
@NotNull(message = "关系对象用户不能为空")
|
||||
@Schema(description = "关系对象用户ID")
|
||||
private Long objectUserId;
|
||||
|
||||
@NotNull(message = "关系类型不能为空")
|
||||
@Schema(description = "关系类型ID")
|
||||
private Long relationTypeId;
|
||||
|
||||
@Schema(description = "关系生效时间")
|
||||
private LocalDateTime effectiveTime;
|
||||
|
||||
@Schema(description = "关系失效时间")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
@Size(max = 500, message = "关系备注不能超过500个字符")
|
||||
@Schema(description = "关系备注")
|
||||
private String remark;
|
||||
|
||||
@AssertTrue(message = "关系失效时间不能早于生效时间")
|
||||
@Schema(hidden = true)
|
||||
public boolean isRelationTimeValid() {
|
||||
return effectiveTime == null || expireTime == null || !expireTime.isBefore(effectiveTime);
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationCreateUserDto;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationCreateDto;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationUpdateDto;
|
||||
import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationEntity;
|
||||
import com.bsd.pjcloud.admin.api.vo.user.SysUserRelationGraphVo;
|
||||
import com.bsd.pjcloud.admin.service.SysUserRelationService;
|
||||
@@ -35,7 +36,7 @@ public class SysUserRelationController {
|
||||
/**
|
||||
* 查询用户关系图谱
|
||||
*/
|
||||
@Operation(summary = "关系图谱", description = "以指定用户为根节点查询用户关系树")
|
||||
@Operation(summary = "关系图谱", description = "查询指定用户所在的完整关系图谱,并按关系方向生成业务树")
|
||||
@GetMapping("/graph/{userId}")
|
||||
// @PreAuthorize("@pms.hasPermission('dzc_sysUserRelation_view')")
|
||||
public R<SysUserRelationGraphVo> getRelationGraph(@PathVariable Long userId) {
|
||||
@@ -64,4 +65,15 @@ public class SysUserRelationController {
|
||||
return R.ok(sysUserRelationService.createRelation(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改已有用户关系
|
||||
*/
|
||||
@Operation(summary = "修改用户关系", description = "修改关系类型、方向、生效时间及备注")
|
||||
@SysLog("修改用户关系")
|
||||
@PutMapping("/update")
|
||||
// @PreAuthorize("@pms.hasPermission('dzc_sysUserRelation_edit')")
|
||||
public R<Long> updateRelation(@Valid @RequestBody SysUserRelationUpdateDto dto) {
|
||||
return R.ok(sysUserRelationService.updateRelation(dto));
|
||||
}
|
||||
|
||||
}
|
||||
+4
-1
@@ -15,7 +15,10 @@ public interface SysUserRelationTypeMapper extends PjcloudBaseMapper<SysUserRela
|
||||
default Page<SysUserRelationTypeEntity> scopePageList(Page page, SysUserRelationTypeDto dto, DataScope dataScope) {
|
||||
LambdaQueryWrapper<SysUserRelationTypeEntity> wrapper = new LambdaQueryWrapper<SysUserRelationTypeEntity>()
|
||||
.eq(ObjectUtil.isNotEmpty(dto.getRelationTypeId()), SysUserRelationTypeEntity::getRelationTypeId, dto.getRelationTypeId())
|
||||
.like(ObjectUtil.isNotEmpty(dto.getRelationName()), SysUserRelationTypeEntity::getRelationName, dto.getRelationName());
|
||||
.eq(ObjectUtil.isNotEmpty(dto.getCategory()), SysUserRelationTypeEntity::getCategory, dto.getCategory())
|
||||
.like(ObjectUtil.isNotEmpty(dto.getRelationName()), SysUserRelationTypeEntity::getRelationName, dto.getRelationName())
|
||||
.orderByDesc(SysUserRelationTypeEntity::getSortOrder)
|
||||
.orderByDesc(SysUserRelationTypeEntity::getCreateTime);
|
||||
return selectPageByScope(page, wrapper, dataScope);
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -3,6 +3,7 @@ package com.bsd.pjcloud.admin.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationCreateUserDto;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationCreateDto;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationUpdateDto;
|
||||
import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationEntity;
|
||||
import com.bsd.pjcloud.admin.api.vo.user.SysUserRelationGraphVo;
|
||||
|
||||
@@ -11,7 +12,7 @@ public interface SysUserRelationService extends IService<SysUserRelationEntity>
|
||||
/**
|
||||
* 查询用户关系图谱
|
||||
*
|
||||
* @param userId 根用户ID
|
||||
* @param userId 图谱入口用户ID
|
||||
* @return 用户关系图谱
|
||||
*/
|
||||
SysUserRelationGraphVo getRelationGraph(Long userId);
|
||||
@@ -31,4 +32,12 @@ public interface SysUserRelationService extends IService<SysUserRelationEntity>
|
||||
* @return 关系ID
|
||||
*/
|
||||
Long createRelation(SysUserRelationCreateDto dto);
|
||||
|
||||
/**
|
||||
* 修改已有用户关系
|
||||
*
|
||||
* @param dto 关系信息
|
||||
* @return 关系ID
|
||||
*/
|
||||
Long updateRelation(SysUserRelationUpdateDto dto);
|
||||
}
|
||||
+183
-45
@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bsd.pjcloud.admin.api.dto.UserDTO;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationCreateUserDto;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationCreateDto;
|
||||
import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationUpdateDto;
|
||||
import com.bsd.pjcloud.admin.api.entity.SysUser;
|
||||
import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationEntity;
|
||||
import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationTypeEntity;
|
||||
@@ -22,7 +23,11 @@ import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@@ -48,10 +53,15 @@ public class SysUserRelationServiceImpl extends ServiceImpl<SysUserRelationMappe
|
||||
|
||||
private final SysUserRelationTypeService sysUserRelationTypeService;
|
||||
|
||||
/**
|
||||
* 查询入口用户所在的完整关系图谱,并按有向关系选择业务根节点生成树。
|
||||
*
|
||||
* @param userId 图谱入口用户ID
|
||||
* @return 用户关系树
|
||||
*/
|
||||
@Override
|
||||
public SysUserRelationGraphVo getRelationGraph(Long userId) {
|
||||
SysUser rootUser = sysUserService.getById(userId);
|
||||
if (rootUser == null) {
|
||||
if (sysUserService.getById(userId) == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
RelationGraphData graphData = collectRelationGraph(userId);
|
||||
@@ -62,12 +72,14 @@ public class SysUserRelationServiceImpl extends ServiceImpl<SysUserRelationMappe
|
||||
: sysUserRelationTypeService.listByIds(graphData.relationTypeIds).stream()
|
||||
.collect(Collectors.toMap(SysUserRelationTypeEntity::getRelationTypeId,
|
||||
Function.identity(), (first, second) -> first));
|
||||
Long rootUserId = selectRootUserId(userId, graphData, relationTypeMap);
|
||||
List<RelationGraphNode> graphNodes = buildRelationTree(rootUserId, graphData);
|
||||
|
||||
Map<Long, SysUserRelationGraphVo> nodeMap = graphData.nodes.stream()
|
||||
Map<Long, SysUserRelationGraphVo> nodeMap = graphNodes.stream()
|
||||
.map(node -> buildGraphNode(node, userMap, relationTypeMap))
|
||||
.collect(Collectors.toMap(SysUserRelationGraphVo::getNodeId, Function.identity()));
|
||||
SysUserRelationGraphVo rootNode = nodeMap.get(TREE_ROOT_NODE_ID);
|
||||
graphData.nodes.stream()
|
||||
graphNodes.stream()
|
||||
.filter(node -> !Objects.equals(node.nodeId, TREE_ROOT_NODE_ID))
|
||||
.forEach(node -> {
|
||||
SysUserRelationGraphVo parentNode = nodeMap.get(node.parentNodeId);
|
||||
@@ -93,7 +105,7 @@ public class SysUserRelationServiceImpl extends ServiceImpl<SysUserRelationMappe
|
||||
Long objectUserId = dto.getObjectUserId() == null ? newUserId : dto.getObjectUserId();
|
||||
SysUserRelationTypeEntity relationType = validateRelation(
|
||||
subjectUserId, objectUserId, dto.getRelationTypeId());
|
||||
validateRelationNotExists(subjectUserId, objectUserId, relationType);
|
||||
validateRelationNotExists(subjectUserId, objectUserId, relationType, null);
|
||||
|
||||
SysUserRelationEntity relation = new SysUserRelationEntity();
|
||||
relation.setSubjectUserId(subjectUserId);
|
||||
@@ -111,7 +123,7 @@ public class SysUserRelationServiceImpl extends ServiceImpl<SysUserRelationMappe
|
||||
public Long createRelation(SysUserRelationCreateDto dto) {
|
||||
SysUserRelationTypeEntity relationType = validateRelation(
|
||||
dto.getSubjectUserId(), dto.getObjectUserId(), dto.getRelationTypeId());
|
||||
validateRelationNotExists(dto.getSubjectUserId(), dto.getObjectUserId(), relationType);
|
||||
validateRelationNotExists(dto.getSubjectUserId(), dto.getObjectUserId(), relationType, null);
|
||||
|
||||
SysUserRelationEntity relation = new SysUserRelationEntity();
|
||||
BeanUtils.copyProperties(dto, relation);
|
||||
@@ -119,6 +131,29 @@ public class SysUserRelationServiceImpl extends ServiceImpl<SysUserRelationMappe
|
||||
return relation.getRelationId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改已有用户关系,并排除当前关系后校验是否存在重复绑定。
|
||||
*
|
||||
* @param dto 关系修改信息
|
||||
* @return 关系ID
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long updateRelation(SysUserRelationUpdateDto dto) {
|
||||
SysUserRelationEntity relation = getById(dto.getRelationId());
|
||||
if (relation == null) {
|
||||
throw new RuntimeException("用户关系不存在");
|
||||
}
|
||||
SysUserRelationTypeEntity relationType = validateRelation(
|
||||
dto.getSubjectUserId(), dto.getObjectUserId(), dto.getRelationTypeId());
|
||||
validateRelationNotExists(
|
||||
dto.getSubjectUserId(), dto.getObjectUserId(), relationType, dto.getRelationId());
|
||||
|
||||
BeanUtils.copyProperties(dto, relation);
|
||||
updateById(relation);
|
||||
return relation.getRelationId();
|
||||
}
|
||||
|
||||
private void validateCreateUserAndRelation(SysUserRelationCreateUserDto dto) {
|
||||
Long existingUserId = dto.getSubjectUserId() == null ? dto.getObjectUserId() : dto.getSubjectUserId();
|
||||
validateExistingUserAndRelationType(existingUserId, dto.getRelationTypeId());
|
||||
@@ -163,9 +198,10 @@ public class SysUserRelationServiceImpl extends ServiceImpl<SysUserRelationMappe
|
||||
}
|
||||
|
||||
private void validateRelationNotExists(Long subjectUserId, Long objectUserId,
|
||||
SysUserRelationTypeEntity relationType) {
|
||||
SysUserRelationTypeEntity relationType, Long excludeRelationId) {
|
||||
boolean directed = "1".equals(relationType.getDirectedFlag());
|
||||
boolean exists = exists(Wrappers.<SysUserRelationEntity>lambdaQuery()
|
||||
.ne(excludeRelationId != null, SysUserRelationEntity::getRelationId, excludeRelationId)
|
||||
.eq(SysUserRelationEntity::getRelationTypeId, relationType.getRelationTypeId())
|
||||
.and(wrapper -> wrapper
|
||||
.nested(current -> current.eq(SysUserRelationEntity::getSubjectUserId, subjectUserId)
|
||||
@@ -178,60 +214,162 @@ public class SysUserRelationServiceImpl extends ServiceImpl<SysUserRelationMappe
|
||||
}
|
||||
}
|
||||
|
||||
private RelationGraphData collectRelationGraph(Long rootUserId) {
|
||||
/**
|
||||
* 从入口用户开始按无向邻接方式遍历,收集同一连通图中的全部用户和关系。
|
||||
*
|
||||
* @param entryUserId 图谱入口用户ID
|
||||
* @return 完整连通关系数据
|
||||
*/
|
||||
private RelationGraphData collectRelationGraph(Long entryUserId) {
|
||||
RelationGraphData graphData = new RelationGraphData();
|
||||
graphData.userIds.add(rootUserId);
|
||||
graphData.nodes.add(RelationGraphNode.root(rootUserId));
|
||||
graphData.userIds.add(entryUserId);
|
||||
|
||||
Set<Long> expandedUserIds = new HashSet<>();
|
||||
List<RelationNodeContext> currentLevel = List.of(new RelationNodeContext(TREE_ROOT_NODE_ID, rootUserId));
|
||||
Set<Long> currentLevel = Set.of(entryUserId);
|
||||
while (!currentLevel.isEmpty()) {
|
||||
List<RelationNodeContext> expandableNodes = new ArrayList<>();
|
||||
Set<Long> expandableUserIds = new LinkedHashSet<>();
|
||||
for (RelationNodeContext context : currentLevel) {
|
||||
if (expandedUserIds.add(context.userId)) {
|
||||
expandableNodes.add(context);
|
||||
expandableUserIds.add(context.userId);
|
||||
for (Long currentUserId : currentLevel) {
|
||||
if (expandedUserIds.add(currentUserId)) {
|
||||
expandableUserIds.add(currentUserId);
|
||||
}
|
||||
}
|
||||
if (expandableNodes.isEmpty()) {
|
||||
if (expandableUserIds.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Map<Long, List<SysUserRelationEntity>> relationMap = listRelations(expandableUserIds).stream()
|
||||
.flatMap(relation -> Stream.of(relation.getSubjectUserId(), relation.getObjectUserId())
|
||||
.filter(Objects::nonNull)
|
||||
.map(relationUserId -> Map.entry(relationUserId, relation)))
|
||||
.collect(Collectors.groupingBy(Map.Entry::getKey,
|
||||
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
|
||||
List<RelationNodeContext> nextLevel = new ArrayList<>();
|
||||
for (RelationNodeContext context : expandableNodes) {
|
||||
for (SysUserRelationEntity relation : relationMap.getOrDefault(context.userId, List.of())) {
|
||||
boolean parentIsSubject = Objects.equals(context.userId, relation.getSubjectUserId());
|
||||
Long childUserId = parentIsSubject ? relation.getObjectUserId() : relation.getSubjectUserId();
|
||||
if (childUserId == null || Objects.equals(childUserId, context.userId)) {
|
||||
continue;
|
||||
}
|
||||
if (!graphData.relationIds.add(relation.getRelationId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean repeated = !graphData.userIds.add(childUserId);
|
||||
if (relation.getRelationTypeId() != null) {
|
||||
graphData.relationTypeIds.add(relation.getRelationTypeId());
|
||||
}
|
||||
graphData.nodes.add(RelationGraphNode.child(
|
||||
relation.getRelationId(), context.nodeId, context.userId, childUserId,
|
||||
relation, parentIsSubject, repeated));
|
||||
nextLevel.add(new RelationNodeContext(relation.getRelationId(), childUserId));
|
||||
Set<Long> nextLevel = new LinkedHashSet<>();
|
||||
for (SysUserRelationEntity relation : listRelations(expandableUserIds)) {
|
||||
if (!graphData.relationIds.add(relation.getRelationId())) {
|
||||
continue;
|
||||
}
|
||||
graphData.relations.add(relation);
|
||||
if (relation.getRelationTypeId() != null) {
|
||||
graphData.relationTypeIds.add(relation.getRelationTypeId());
|
||||
}
|
||||
Stream.of(relation.getSubjectUserId(), relation.getObjectUserId())
|
||||
.filter(Objects::nonNull)
|
||||
.filter(graphData.userIds::add)
|
||||
.forEach(nextLevel::add);
|
||||
}
|
||||
currentLevel = nextLevel;
|
||||
}
|
||||
return graphData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据有向关系的主语到对象方向选择业务根节点,优先选择可覆盖用户最多的顶层用户。
|
||||
*
|
||||
* @param entryUserId 入口用户ID
|
||||
* @param graphData 完整连通关系数据
|
||||
* @param relationTypeMap 关系类型映射
|
||||
* @return 业务根用户ID
|
||||
*/
|
||||
private Long selectRootUserId(Long entryUserId, RelationGraphData graphData,
|
||||
Map<Long, SysUserRelationTypeEntity> relationTypeMap) {
|
||||
Map<Long, Set<Long>> directedChildren = new HashMap<>();
|
||||
Set<Long> directedChildUserIds = new HashSet<>();
|
||||
for (SysUserRelationEntity relation : graphData.relations) {
|
||||
SysUserRelationTypeEntity relationType = relationTypeMap.get(relation.getRelationTypeId());
|
||||
if (relationType == null || !"1".equals(relationType.getDirectedFlag())) {
|
||||
continue;
|
||||
}
|
||||
directedChildren.computeIfAbsent(relation.getSubjectUserId(), key -> new LinkedHashSet<>())
|
||||
.add(relation.getObjectUserId());
|
||||
directedChildUserIds.add(relation.getObjectUserId());
|
||||
}
|
||||
if (directedChildren.isEmpty()) {
|
||||
return entryUserId;
|
||||
}
|
||||
|
||||
return graphData.userIds.stream()
|
||||
.filter(candidateUserId -> !directedChildUserIds.contains(candidateUserId))
|
||||
.sorted(Comparator
|
||||
.comparingInt((Long candidateUserId) -> countDirectedDescendants(
|
||||
candidateUserId, directedChildren)).reversed()
|
||||
.thenComparing(candidateUserId -> !Objects.equals(candidateUserId, entryUserId))
|
||||
.thenComparing(Comparator.naturalOrder()))
|
||||
.findFirst()
|
||||
.orElse(entryUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计指定用户沿有向关系可覆盖的用户数量,用于选择最合适的业务根节点。
|
||||
*
|
||||
* @param rootUserId 候选根用户ID
|
||||
* @param directedChildren 有向关系的下级用户映射
|
||||
* @return 可覆盖的用户数量,包含候选根用户自身
|
||||
*/
|
||||
private int countDirectedDescendants(Long rootUserId, Map<Long, Set<Long>> directedChildren) {
|
||||
Set<Long> visitedUserIds = new HashSet<>();
|
||||
Deque<Long> pendingUserIds = new ArrayDeque<>();
|
||||
pendingUserIds.add(rootUserId);
|
||||
while (!pendingUserIds.isEmpty()) {
|
||||
Long currentUserId = pendingUserIds.removeFirst();
|
||||
if (!visitedUserIds.add(currentUserId)) {
|
||||
continue;
|
||||
}
|
||||
pendingUserIds.addAll(directedChildren.getOrDefault(currentUserId, Set.of()));
|
||||
}
|
||||
return visitedUserIds.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从业务根节点开始生成树形结构,交叉关系或环中的重复用户只保留引用节点,不再继续展开。
|
||||
*
|
||||
* @param rootUserId 业务根用户ID
|
||||
* @param graphData 完整连通关系数据
|
||||
* @return 树形关系节点列表
|
||||
*/
|
||||
private List<RelationGraphNode> buildRelationTree(Long rootUserId, RelationGraphData graphData) {
|
||||
Map<Long, List<SysUserRelationEntity>> relationMap = graphData.relations.stream()
|
||||
.flatMap(relation -> Stream.of(relation.getSubjectUserId(), relation.getObjectUserId())
|
||||
.filter(Objects::nonNull)
|
||||
.map(relationUserId -> Map.entry(relationUserId, relation)))
|
||||
.collect(Collectors.groupingBy(Map.Entry::getKey,
|
||||
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
relationMap.values().forEach(relations -> relations.sort(
|
||||
Comparator.comparing(SysUserRelationEntity::getRelationId)));
|
||||
|
||||
List<RelationGraphNode> nodes = new ArrayList<>();
|
||||
nodes.add(RelationGraphNode.root(rootUserId));
|
||||
Set<Long> relationIds = new HashSet<>();
|
||||
Set<Long> discoveredUserIds = new HashSet<>();
|
||||
discoveredUserIds.add(rootUserId);
|
||||
List<RelationNodeContext> currentLevel = List.of(
|
||||
new RelationNodeContext(TREE_ROOT_NODE_ID, rootUserId));
|
||||
while (!currentLevel.isEmpty()) {
|
||||
List<RelationNodeContext> nextLevel = new ArrayList<>();
|
||||
for (RelationNodeContext context : currentLevel) {
|
||||
for (SysUserRelationEntity relation : relationMap.getOrDefault(context.userId, List.of())) {
|
||||
if (!relationIds.add(relation.getRelationId())) {
|
||||
continue;
|
||||
}
|
||||
boolean parentIsSubject = Objects.equals(context.userId, relation.getSubjectUserId());
|
||||
Long childUserId = parentIsSubject ? relation.getObjectUserId() : relation.getSubjectUserId();
|
||||
if (childUserId == null || Objects.equals(childUserId, context.userId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean repeated = !discoveredUserIds.add(childUserId);
|
||||
nodes.add(RelationGraphNode.child(
|
||||
relation.getRelationId(), context.nodeId, context.userId, childUserId,
|
||||
relation, parentIsSubject, repeated));
|
||||
if (!repeated) {
|
||||
nextLevel.add(new RelationNodeContext(relation.getRelationId(), childUserId));
|
||||
}
|
||||
}
|
||||
}
|
||||
currentLevel = nextLevel;
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询与指定用户集合任意一端关联的全部有效关系。
|
||||
*
|
||||
* @param userIds 用户ID集合
|
||||
* @return 用户关系列表
|
||||
*/
|
||||
private List<SysUserRelationEntity> listRelations(Set<Long> userIds) {
|
||||
return list(Wrappers.<SysUserRelationEntity>lambdaQuery()
|
||||
.and(wrapper -> wrapper.in(SysUserRelationEntity::getSubjectUserId, userIds)
|
||||
@@ -323,7 +461,7 @@ public class SysUserRelationServiceImpl extends ServiceImpl<SysUserRelationMappe
|
||||
|
||||
private static class RelationGraphData {
|
||||
|
||||
private final List<RelationGraphNode> nodes = new ArrayList<>();
|
||||
private final List<SysUserRelationEntity> relations = new ArrayList<>();
|
||||
|
||||
private final Set<Long> userIds = new LinkedHashSet<>();
|
||||
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package com.bsd.pjcloud.admin.service.impl.user;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.bsd.pjcloud.admin.api.entity.SysUser;
|
||||
import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationEntity;
|
||||
import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationTypeEntity;
|
||||
import com.bsd.pjcloud.admin.api.vo.user.SysUserRelationGraphVo;
|
||||
import com.bsd.pjcloud.admin.service.SysUserRelationTypeService;
|
||||
import com.bsd.pjcloud.admin.service.SysUserService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class SysUserRelationServiceImplTest {
|
||||
|
||||
private static final Long GUARDIAN_USER_ID = 2034824566572941314L;
|
||||
|
||||
private static final Long FIRST_WARD_USER_ID = 2079103470488576001L;
|
||||
|
||||
private static final Long SECOND_WARD_USER_ID = 2079129427022737410L;
|
||||
|
||||
private static final Long RELATION_TYPE_ID = 2079044735024029698L;
|
||||
|
||||
/**
|
||||
* 验证从被监护人进入图谱时,仍以监护人为根节点并展示两个同级被监护人。
|
||||
*/
|
||||
@Test
|
||||
void shouldUseGuardianAsRootWhenGraphIsOpenedFromWard() {
|
||||
SysUserRelationEntity firstRelation = createRelation(
|
||||
2079104982669385730L, FIRST_WARD_USER_ID);
|
||||
SysUserRelationEntity secondRelation = createRelation(
|
||||
2079129636280758273L, SECOND_WARD_USER_ID);
|
||||
|
||||
SysUserService sysUserService = mock(SysUserService.class);
|
||||
SysUserRelationTypeService relationTypeService = mock(SysUserRelationTypeService.class);
|
||||
SysUserRelationServiceImpl service = spy(
|
||||
new SysUserRelationServiceImpl(sysUserService, relationTypeService));
|
||||
|
||||
when(sysUserService.getById(SECOND_WARD_USER_ID)).thenReturn(createUser(SECOND_WARD_USER_ID, "被监护人二"));
|
||||
when(sysUserService.listByIds(any())).thenReturn(List.of(
|
||||
createUser(GUARDIAN_USER_ID, "监护人"),
|
||||
createUser(FIRST_WARD_USER_ID, "被监护人一"),
|
||||
createUser(SECOND_WARD_USER_ID, "被监护人二")));
|
||||
when(relationTypeService.listByIds(any())).thenReturn(List.of(createGuardianRelationType()));
|
||||
doReturn(List.of(secondRelation), List.of(firstRelation, secondRelation), List.of(firstRelation))
|
||||
.when(service).list(org.mockito.ArgumentMatchers.<Wrapper<SysUserRelationEntity>>any());
|
||||
|
||||
SysUserRelationGraphVo graph = service.getRelationGraph(SECOND_WARD_USER_ID);
|
||||
|
||||
assertEquals(GUARDIAN_USER_ID, graph.getUserId());
|
||||
assertEquals(3, graph.getUserCount());
|
||||
assertEquals(2, graph.getRelationCount());
|
||||
assertEquals(Set.of(FIRST_WARD_USER_ID, SECOND_WARD_USER_ID), graph.getChildren().stream()
|
||||
.map(SysUserRelationGraphVo::getUserId)
|
||||
.collect(Collectors.toSet()));
|
||||
assertFalse(graph.getChildren().stream().anyMatch(child -> !child.getChildren().isEmpty()));
|
||||
}
|
||||
|
||||
private SysUserRelationEntity createRelation(Long relationId, Long wardUserId) {
|
||||
SysUserRelationEntity relation = new SysUserRelationEntity();
|
||||
relation.setRelationId(relationId);
|
||||
relation.setSubjectUserId(GUARDIAN_USER_ID);
|
||||
relation.setObjectUserId(wardUserId);
|
||||
relation.setRelationTypeId(RELATION_TYPE_ID);
|
||||
return relation;
|
||||
}
|
||||
|
||||
private SysUser createUser(Long userId, String name) {
|
||||
SysUser user = new SysUser();
|
||||
user.setUserId(userId);
|
||||
user.setName(name);
|
||||
return user;
|
||||
}
|
||||
|
||||
private SysUserRelationTypeEntity createGuardianRelationType() {
|
||||
SysUserRelationTypeEntity relationType = new SysUserRelationTypeEntity();
|
||||
relationType.setRelationTypeId(RELATION_TYPE_ID);
|
||||
relationType.setRelationCode("GUARDIAN_WARD");
|
||||
relationType.setRelationName("监护关系");
|
||||
relationType.setSubjectRoleName("监护人");
|
||||
relationType.setObjectRoleName("被监护对象");
|
||||
relationType.setDirectedFlag("1");
|
||||
return relationType;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user