feat(2227-permissionQuery): 查询用户角色和权限

This commit is contained in:
zhansihu 2024-04-08 15:20:54 +08:00
parent 599fd9ddaf
commit 695d48560e
22 changed files with 554 additions and 144 deletions

View File

@ -5,6 +5,7 @@ import lombok.Getter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -45,4 +46,10 @@ public enum FeatureResourceType {
public static boolean applyPage(Integer code) {
return PAGE.apply(code) || APP_ENTRY.apply(code);
}
public static List<Integer> navTypes() {
return Arrays.asList(FeatureResourceType.MENU.getCode(),
FeatureResourceType.PAGE.getCode(),
FeatureResourceType.APP_ENTRY.getCode());
}
}

View File

@ -6,6 +6,7 @@ import lombok.Getter;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -44,4 +45,10 @@ public enum RoleTypeEnum {
return isAdmin;
}
public static boolean isAdmin(String roleType) {
return Optional.ofNullable(getRoleType(roleType))
.map(RoleTypeEnum::isAdminRole)
.orElse(false);
}
}

View File

@ -21,4 +21,8 @@ public class WorkspaceOUPair {
private Long ouId;
private Long workspaceId;
private String buildKey() {
return ouId + "-" + workspaceId;
}
}

View File

@ -1,10 +1,15 @@
package cn.axzo.tyr.client.model.req;
import cn.axzo.tyr.client.model.base.WorkspaceOUPair;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* NavTreeReq
*
@ -18,9 +23,12 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class NavTreeReq {
@NotNull(message = "人员ID不能为空")
private Long personId;
/** 登录端 **/
@NotNull(message = "登录端不能为空")
private String terminal;
private Long ouId;
@NotEmpty(message = "单位标识对不能为空")
private List<WorkspaceOUPair> workspaceOUPairs;
}

View File

@ -31,7 +31,7 @@ public class PermissionQueryReq {
private String terminal;
private Integer featureType;
private List<Integer> featureTypes;
private List<String> featureCodes;

View File

@ -1,5 +1,7 @@
package cn.axzo.tyr.client.model.res;
import cn.axzo.basics.common.model.IBaseTree;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -18,7 +20,7 @@ import java.util.List;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NavTreeResp {
public class NavTreeResp implements IBaseTree<NavTreeResp, Long> {
//APP侧需要扩展返回必要的数据
@ -30,6 +32,31 @@ public class NavTreeResp {
private String featureName;
/** 资源类型 **/
private Integer featureType;
/** 上级ID **/
private Long parentId;
/** 子级 **/
private List<NavTreeResp> children;
@JsonIgnore
@Override
public Long getNodeCode() {
return id;
}
@JsonIgnore
@Override
public Long getParentNodeCode() {
return parentId;
}
@JsonIgnore
@Override
public List<NavTreeResp> getNodeChildren() {
return children;
}
@Override
public void setNodeChildren(List<NavTreeResp> nodeChildren) {
this.children = nodeChildren;
}
}

View File

@ -2,18 +2,16 @@ package cn.axzo.tyr.server.controller.permission;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.tyr.client.feign.FeatureResourceApi;
import cn.axzo.tyr.client.model.req.ResourceSyncReq;
import cn.axzo.tyr.client.model.req.FeatureResourceTreeSaveReq;
import cn.axzo.tyr.client.model.res.FeatureResourceDTO;
import cn.axzo.tyr.client.model.req.ResourceSyncReq;
import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode;
import cn.axzo.tyr.server.service.FeatureResourceSyncService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.hutool.core.collection.CollectionUtil;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
/**
@ -28,11 +26,13 @@ import java.util.List;
@RequiredArgsConstructor
public class FeatureResourceController implements FeatureResourceApi {
private final FeatureResourceSyncService featureResourceSyncService;
private final SaasFeatureResourceService featureResourceService;
@Override
public ApiResult<List<FeatureResourceTreeNode>> getSyncTreeById(Long id) {
return ApiResult.ok(featureResourceService.getSyncTreeById(id));
return ApiResult.ok(featureResourceSyncService.getSyncTreeById(id));
}
@Override
@ -42,7 +42,7 @@ public class FeatureResourceController implements FeatureResourceApi {
log.warn("no ids to sync from base env");
return ApiResult.ok();
}
featureResourceService.syncFromBase(req);
featureResourceSyncService.syncFromBase(req);
return ApiResult.ok();
}

View File

@ -0,0 +1,25 @@
package cn.axzo.tyr.server.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 权限对象
*
* @version V1.0
* @author: ZhanSiHu
* @date: 2024/4/8 11:09
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PermissionDO {
private Long ouId;
private Long workspaceId;
private List<Long> featureIds;
}

View File

@ -0,0 +1,42 @@
package cn.axzo.tyr.server.model;
import cn.axzo.tyr.client.model.base.WorkspaceOUPair;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Set;
/**
* 权限查询上下文
*
* @version V1.0
* @author: ZhanSiHu
* @date: 2024/4/8 11:04
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PermissionQueryContext {
/** 用户标识 **/
private UserIdentity userIdentity;
/** 权限范围 **/
private List<WorkspaceOUPair> workspaceOUPairs;
/** 登录端 **/
private String terminal;
/** 资源类型 **/
private List<Integer> featureTypes;
/** 预览角色ID **/
private List<Long> previewRoleIds;
/** 资源ID **/
private Set<Long> featureIds;
}

View File

@ -0,0 +1,24 @@
package cn.axzo.tyr.server.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 角色权限关联
*
* @version V1.0
* @author: ZhanSiHu
* @date: 2024/4/8 14:50
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RoleFeatureRelation {
private Long roleId;
private Long featureId;
}

View File

@ -0,0 +1,32 @@
package cn.axzo.tyr.server.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 角色和权限
*
* @version V1.0
* @author: ZhanSiHu
* @date: 2024/4/8 14:16
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RoleWithFeature {
private Long roleId;
private String roleName;
private String roleType;
private Integer productUnitType;
private List<Long> featureIds;
}

View File

@ -0,0 +1,24 @@
package cn.axzo.tyr.server.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户标识对象
*
* @version V1.0
* @author: ZhanSiHu
* @date: 2024/4/8 10:59
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserIdentity {
private Long personId;
private Long identityId;
private Integer identityType;
}

View File

@ -5,6 +5,7 @@ import cn.axzo.pokonyan.config.mybatisplus.BaseEntity;
import cn.axzo.tyr.client.common.enums.RoleTypeEnum;
import cn.axzo.tyr.client.model.enums.IdentityType;
import cn.axzo.tyr.client.model.req.RoleWithUserQueryReq;
import cn.axzo.tyr.server.model.RoleFeatureRelation;
import cn.axzo.tyr.server.repository.entity.SaasRole;
import cn.axzo.tyr.server.repository.entity.SaasRoleWithUser;
import cn.axzo.tyr.server.repository.mapper.SaasRoleMapper;
@ -115,5 +116,9 @@ public class SaasRoleDao extends ServiceImpl<SaasRoleMapper, SaasRole> {
.eq(SaasRole::getRoleType, RoleTypeEnum.INIT.getValue())
.list();
}
public List<RoleFeatureRelation> listFeatureByIds(Set<Long> roleIds, Set<Long> featureIds) {
return this.baseMapper.listFeatureByIds(roleIds, featureIds);
}
}

View File

@ -1,6 +1,7 @@
package cn.axzo.tyr.server.repository.mapper;
import cn.axzo.tyr.client.model.enums.IdentityType;
import cn.axzo.tyr.server.model.RoleFeatureRelation;
import cn.axzo.tyr.server.repository.entity.SaasRole;
import cn.axzo.tyr.server.repository.entity.SaasRoleWithUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@ -27,5 +28,6 @@ public interface SaasRoleMapper extends BaseMapper<SaasRole> {
List<SaasRole> listRoleByFeatures(@Param("featureIds") Set<Long> featureIds);
List<RoleFeatureRelation> listFeatureByIds(@Param("roleIds") Set<Long> roleIds, @Param("featureIds") Set<Long> featureIds);
}

View File

@ -0,0 +1,20 @@
package cn.axzo.tyr.server.service;
import cn.axzo.tyr.client.model.req.ResourceSyncReq;
import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode;
import java.util.List;
/**
* 功能资源同步服务
*
* @version V1.0
* @author: ZhanSiHu
* @date: 2024/4/8 10:09
*/
public interface FeatureResourceSyncService {
List<FeatureResourceTreeNode> getSyncTreeById(Long id);
void syncFromBase(ResourceSyncReq req);
}

View File

@ -12,6 +12,7 @@ import cn.axzo.tyr.client.model.vo.SaasRoleCategoryVO;
import cn.axzo.tyr.client.model.vo.SaasRoleGroupCodeVO;
import cn.axzo.tyr.client.model.vo.SaasRoleVO;
import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO;
import cn.axzo.tyr.server.model.RoleWithFeature;
import cn.axzo.tyr.server.repository.entity.SaasRole;
import cn.axzo.tyr.server.repository.entity.SaasRoleWithUser;
import cn.axzo.tyr.server.service.impl.TyrSaasAuthServiceImpl;
@ -101,4 +102,6 @@ public interface RoleService {
/** 查询超管和管理员角色 **/
List<SaasRole> listAdmins(Long workspaceId, Long ouId);
List<RoleWithFeature> listWithFeatures(Set<Long> roleIds, Set<Long> featureIds);
}

View File

@ -1,8 +1,6 @@
package cn.axzo.tyr.server.service;
import cn.axzo.tyr.client.model.req.ResourceSyncReq;
import cn.axzo.tyr.client.model.res.FeatureResourceDTO;
import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode;
import cn.axzo.tyr.server.repository.entity.SaasFeatureResource;
import java.util.List;
@ -15,7 +13,6 @@ import java.util.List;
*/
public interface SaasFeatureResourceService {
List<FeatureResourceTreeNode> getSyncTreeById(Long id);
void syncFromBase(ResourceSyncReq req);
/** 根据ID查询导航菜单页面信息 - 限制查询字段 **/
List<SaasFeatureResource> listNavByIds(List<Long> featureIds);
}

View File

@ -0,0 +1,151 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.basics.common.util.TreeUtil;
import cn.axzo.tyr.client.common.enums.FeatureResourceType;
import cn.axzo.tyr.client.model.req.ResourceSyncReq;
import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode;
import cn.axzo.tyr.server.inner.feign.BaseFeatureResourceApi;
import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao;
import cn.axzo.tyr.server.repository.entity.SaasFeatureResource;
import cn.axzo.tyr.server.service.FeatureResourceSyncService;
import cn.axzo.tyr.server.util.RpcInternalUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 功能资源同步服务实现
*
* @version V1.0
* @author: ZhanSiHu
* @date: 2024/4/8 10:09
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FeatureResourceSyncServiceImpl implements FeatureResourceSyncService {
private final SaasFeatureResourceDao featureResourceDao;
private final BaseFeatureResourceApi baseFeatureResourceApi;
@Override
public List<FeatureResourceTreeNode> getSyncTreeById(Long id) {
//选中同步的数据
SaasFeatureResource resource = featureResourceDao.lambdaQuery()
.eq(SaasFeatureResource::getId, id)
.one();
if (resource == null) {
return Collections.emptyList();
}
//找到所有上级 - 包含自己
List<SaasFeatureResource> resourceList = featureResourceDao.lambdaQuery()
.in(SaasFeatureResource::getId, resource.splitPath())
.list();
//resourceList.add(resource);
//如果是页面或应用入口-同时返回所有页面组件
if (FeatureResourceType.applyPage(resource.getFeatureType())) {
List<SaasFeatureResource> componentList = featureResourceDao.lambdaQuery()
.eq(SaasFeatureResource::getFeatureType, FeatureResourceType.COMPONENT.getCode())
.apply("FIND_IN_SET(" + id + ", path)")
.list();
resourceList.addAll(componentList);
}
List<FeatureResourceTreeNode> dtoList = BeanMapper.copyList(resourceList, FeatureResourceTreeNode.class);
return TreeUtil.buildTree(dtoList);
}
@Override
public void syncFromBase(ResourceSyncReq req) {
final Map<Long, String> codeCache = new ConcurrentHashMap<>();
for (Long id : req.getIds()) {
//TODO:@Zhan 并发处理同一个parent下同批查询
//获取基准环境配置数据:同步某个ID的数据 需要同步处理它所有上级及下级组件
List<FeatureResourceTreeNode> syncList = RpcInternalUtil.rpcProcessor(() -> baseFeatureResourceApi.getSyncTreeById(id),
"get base sync tree by id", id).getData();
doSyncFromBase(syncList, codeCache, req.getOperatorId());
}
}
private void doSyncFromBase(List<FeatureResourceTreeNode> syncList, Map<Long, String> codeCache, Long operatorId) {
for (FeatureResourceTreeNode treeNode : syncList) {
if (codeCache.containsKey(treeNode.getId())) {
//已处理过
log.info("already sync resource:{}", treeNode.getId());
//递归子节点
if (CollectionUtil.isNotEmpty(treeNode.getChildren())) {
doSyncFromBase(treeNode.getChildren(), codeCache, operatorId);
}
continue;
}
//缓存code
codeCache.put(treeNode.getId(), treeNode.getFeatureCode());
SaasFeatureResource baseResource = BeanMapper.copyBean(treeNode, SaasFeatureResource.class);
//修正数据
String parentCode = codeCache.get(baseResource.getParentId());
fixData(baseResource, parentCode);
SaasFeatureResource resource = featureResourceDao.getByCode(treeNode.getFeatureCode());
if (resource == null) {
//新增
baseResource.setCreateBy(operatorId);
baseResource.setUpdateBy(operatorId);
newResource(baseResource);
} else {
//更新
baseResource.setId(resource.getId());
baseResource.setPath(baseResource.getPath() + "," + resource.getId());
baseResource.setUpdateBy(operatorId);
featureResourceDao.updateById(baseResource);
if (!StrUtil.equals(baseResource.getPath(), resource.getPath())) {
//层级变化
log.info("replace path from old:{} to new:{}", resource.getPath(), baseResource.getPath());
featureResourceDao.replacePath(resource.getPath(), baseResource.getPath());
}
}
//递归子节点
if (CollectionUtil.isNotEmpty(treeNode.getChildren())) {
doSyncFromBase(treeNode.getChildren(), codeCache, operatorId);
}
}
}
/** 修正当前环境的数据 parentId path **/
private void fixData(SaasFeatureResource resource, String parentCode) {
if (StrUtil.isBlank(parentCode)) {
resource.setParentId(0L);
resource.setPath("0");
} else {
//找当前环境的parent
SaasFeatureResource parent = featureResourceDao.getByCode(parentCode);
if (parent == null) {
resource.setParentId(0L);
resource.setPath("0");
} else {
resource.setParentId(parent.getId());
resource.setPath(parent.getPath());
}
}
}
private void newResource(SaasFeatureResource resource) {
featureResourceDao.save(resource);
//path追加自身ID
resource.setPath(resource.getPath() + "," + resource.getId());
featureResourceDao.updateById(resource);
}
}

View File

@ -1,14 +1,35 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.basics.common.util.TreeUtil;
import cn.axzo.tyr.client.common.enums.FeatureResourceType;
import cn.axzo.tyr.client.model.enums.IdentityType;
import cn.axzo.tyr.server.model.PermissionDO;
import cn.axzo.tyr.server.model.PermissionQueryContext;
import cn.axzo.tyr.server.model.RoleWithFeature;
import cn.axzo.tyr.server.model.UserIdentity;
import cn.axzo.tyr.client.model.base.WorkspaceOUPair;
import cn.axzo.tyr.client.model.req.IdentityAuthReq;
import cn.axzo.tyr.client.model.req.NavTreeReq;
import cn.axzo.tyr.client.model.req.PermissionQueryReq;
import cn.axzo.tyr.client.model.res.NavTreeResp;
import cn.axzo.tyr.server.repository.entity.SaasFeatureResource;
import cn.axzo.tyr.server.repository.entity.SaasRoleUserRelation;
import cn.axzo.tyr.server.service.PermissionQueryService;
import cn.axzo.tyr.server.service.RoleService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.axzo.tyr.server.util.KeyUtil;
import cn.hutool.core.collection.CollectionUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 权限查询服务实现
@ -22,11 +43,94 @@ import java.util.List;
@RequiredArgsConstructor
public class PermissionQueryServiceImpl implements PermissionQueryService {
private final SaasFeatureResourceService featureResourceService;
private final RoleUserService roleUserService;
private final RoleService roleService;
@Override
public List<NavTreeResp> getNavTree(NavTreeReq req) {
//TODO:@Zhan
//构造参数
PermissionQueryContext context = BeanMapper.copyBean(req, PermissionQueryContext.class);
//限制只查菜单页面
context.setFeatureTypes(FeatureResourceType.navTypes());
//查询权限
List<PermissionDO> permissions = queryUserPermission(context);
if (CollectionUtil.isEmpty(permissions)) {
return Collections.emptyList();
}
List<Long> featureIds = permissions.stream().map(PermissionDO::getFeatureIds).flatMap(List::stream).collect(Collectors.toList());
//反查资源信息
List<SaasFeatureResource> resourceList = featureResourceService.listNavByIds(featureIds);
//组装导航树
return TreeUtil.buildTree(BeanMapper.copyList(resourceList, NavTreeResp.class));
}
private List<PermissionDO> queryUserPermission(PermissionQueryContext context) {
//查询用户具有的角色
List<SaasRoleUserRelation> userRoleRelations = listRoleUserRelations(context);
if (CollectionUtil.isEmpty(userRoleRelations)) {
log.warn("no user role relation found");
return Collections.emptyList();
}
Set<Long> realWorkspaceId = userRoleRelations.stream().map(SaasRoleUserRelation::getWorkspaceId).collect(Collectors.toSet());
//TODO: 查询产品分配的权限
Set<Long> roleIds = userRoleRelations.stream().map(SaasRoleUserRelation::getRoleId).collect(Collectors.toSet());
//查询角色权限
List<RoleWithFeature> roles = roleService.listWithFeatures(roleIds, context.getFeatureIds());
return Collections.emptyList();
}
private List<SaasRoleUserRelation> listRoleUserRelations(PermissionQueryContext context) {
if (CollectionUtil.isNotEmpty(context.getPreviewRoleIds())) {
//指定了角色 则不需要去查用户角色关系
log.info("mock specify roles relation");
return mockRoleUserRelation(context);
}
//查询人员角色关系
Set<Long> workspaceIds = new HashSet<>();
Set<Long> ouIds = new HashSet<>();
Set<String> owKeys = new HashSet<>();
List<WorkspaceOUPair> workspaceOUPairs = context.getWorkspaceOUPairs();
workspaceOUPairs.forEach(ow -> {
workspaceIds.add(ow.getWorkspaceId());
ouIds.add(ow.getOuId());
owKeys.add(KeyUtil.buildKeyBySeparator(ow.getWorkspaceId(), ow.getOuId()));
});
UserIdentity userIdentity = context.getUserIdentity();
List<SaasRoleUserRelation> relations = roleUserService.queryByWorkspaceIdOrOu(userIdentity.getPersonId(),
userIdentity.getIdentityId(), IdentityType.getIdentityType(userIdentity.getIdentityType()), workspaceIds, ouIds);
if (CollectionUtil.isEmpty(relations)) {
log.warn("no user role relations found");
return relations;
}
//工作台和单位需成对查询 对结果二次过滤
return relations.stream()
.filter(roleUserService -> owKeys.contains(
KeyUtil.buildKeyBySeparator(roleUserService.getWorkspaceId(), roleUserService.getOuId())))
.collect(Collectors.toList());
}
private List<SaasRoleUserRelation> mockRoleUserRelation(PermissionQueryContext context) {
final List<SaasRoleUserRelation> relations = new ArrayList<>();
List<WorkspaceOUPair> workspaceOUPairs = context.getWorkspaceOUPairs();
// mock 看做已有指定的角色
for (WorkspaceOUPair ow : workspaceOUPairs) {
List<SaasRoleUserRelation> mockRelations = context.getPreviewRoleIds().stream().map(id -> {
SaasRoleUserRelation relation = new SaasRoleUserRelation();
relation.setRoleId(id);
relation.setOuId(ow.getOuId());
relation.setWorkspaceId(ow.getWorkspaceId());
relation.setIdentityId(context.getUserIdentity().getIdentityId());
relation.setIdentityType(context.getUserIdentity().getIdentityType());
// 使用角色ID替代不需要在查询一次
relation.setId(id);
return relation;
}).collect(Collectors.toList());
relations.addAll(mockRelations);
}
return relations;
}
}

View File

@ -27,6 +27,8 @@ import cn.axzo.tyr.client.model.vo.SaasRoleGroupCodeVO;
import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO;
import cn.axzo.tyr.client.model.vo.SaasRoleVO;
import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO;
import cn.axzo.tyr.server.model.RoleFeatureRelation;
import cn.axzo.tyr.server.model.RoleWithFeature;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.dao.SaasPermissionGroupDao;
import cn.axzo.tyr.server.repository.dao.SaasPgroupRoleRelationDao;
@ -975,4 +977,30 @@ public class RoleServiceImpl implements RoleService {
.or().eq(SaasRole::getRoleType, RoleTypeEnum.ADMIN.getValue());})
.list();
}
@Override
public List<RoleWithFeature> listWithFeatures(Set<Long> roleIds, Set<Long> featureIds) {
List<RoleWithFeature> result = new ArrayList<>();
List<SaasRole> roles = saasRoleDao.listByIds(roleIds);
if (CollectionUtil.isEmpty(roles)) {
log.warn("no roles found for ids:{}", roles);
return result;
}
List<RoleFeatureRelation> relations = saasRoleDao.listFeatureByIds(roleIds, featureIds);
Map<Long, List<Long>> mapping = relations.stream()
.collect(Collectors.groupingBy(RoleFeatureRelation::getRoleId,
Collectors.mapping(RoleFeatureRelation::getFeatureId, Collectors.toList())));
for (SaasRole role : roles) {
result.add(RoleWithFeature.builder()
.roleId(role.getId())
.roleName(role.getName())
.roleType(role.getRoleType())
.productUnitType(role.getProductUnitType())
.featureIds(mapping.getOrDefault(role.getId(), Collections.emptyList()))
.build());
}
return result;
}
}

View File

@ -1,29 +1,13 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.basics.common.util.TreeUtil;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.tyr.client.common.enums.FeatureResourceType;
import cn.axzo.tyr.client.model.req.ResourceSyncReq;
import cn.axzo.tyr.client.model.res.FeatureResourceDTO;
import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode;
import cn.axzo.tyr.server.inner.feign.BaseFeatureResourceApi;
import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao;
import cn.axzo.tyr.server.repository.entity.SaasFeatureResource;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.axzo.tyr.server.util.RpcInternalUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 功能资源服务实现
@ -39,117 +23,15 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic
private final SaasFeatureResourceDao featureResourceDao;
private final BaseFeatureResourceApi baseFeatureResourceApi;
@Override
public List<FeatureResourceTreeNode> getSyncTreeById(Long id) {
//选中同步的数据
SaasFeatureResource resource = featureResourceDao.lambdaQuery()
.eq(SaasFeatureResource::getId, id)
.one();
if (resource == null) {
return Collections.emptyList();
}
//找到所有上级 - 包含自己
List<SaasFeatureResource> resourceList = featureResourceDao.lambdaQuery()
.in(SaasFeatureResource::getId, resource.splitPath())
public List<SaasFeatureResource> listNavByIds(List<Long> featureIds) {
//按需扩展要查询的字段
return featureResourceDao.lambdaQuery()
.select(SaasFeatureResource::getId,
SaasFeatureResource::getFeatureCode,
SaasFeatureResource::getFeatureName,
SaasFeatureResource::getFeatureType)
.in(SaasFeatureResource::getId, featureIds)
.list();
//resourceList.add(resource);
//如果是页面或应用入口-同时返回所有页面组件
if (FeatureResourceType.applyPage(resource.getFeatureType())) {
List<SaasFeatureResource> componentList = featureResourceDao.lambdaQuery()
.eq(SaasFeatureResource::getFeatureType, FeatureResourceType.COMPONENT.getCode())
.apply("FIND_IN_SET(" + id + ", path)")
.list();
resourceList.addAll(componentList);
}
List<FeatureResourceTreeNode> dtoList = BeanMapper.copyList(resourceList, FeatureResourceTreeNode.class);
return TreeUtil.buildTree(dtoList);
}
@Override
public void syncFromBase(ResourceSyncReq req) {
final Map<Long, String> codeCache = new ConcurrentHashMap<>();
for (Long id : req.getIds()) {
//TODO:@Zhan 并发处理同一个parent下同批查询
//获取基准环境配置数据:同步某个ID的数据 需要同步处理它所有上级及下级组件
List<FeatureResourceTreeNode> syncList = RpcInternalUtil.rpcProcessor(() -> baseFeatureResourceApi.getSyncTreeById(id),
"get base sync tree by id", id).getData();
doSyncFromBase(syncList, codeCache, req.getOperatorId());
}
}
private void doSyncFromBase(List<FeatureResourceTreeNode> syncList, Map<Long, String> codeCache, Long operatorId) {
for (FeatureResourceTreeNode treeNode : syncList) {
if (codeCache.containsKey(treeNode.getId())) {
//已处理过
log.info("already sync resource:{}", treeNode.getId());
//递归子节点
if (CollectionUtil.isNotEmpty(treeNode.getChildren())) {
doSyncFromBase(treeNode.getChildren(), codeCache, operatorId);
}
continue;
}
//缓存code
codeCache.put(treeNode.getId(), treeNode.getFeatureCode());
SaasFeatureResource baseResource = BeanMapper.copyBean(treeNode, SaasFeatureResource.class);
//修正数据
String parentCode = codeCache.get(baseResource.getParentId());
fixData(baseResource, parentCode);
SaasFeatureResource resource = featureResourceDao.getByCode(treeNode.getFeatureCode());
if (resource == null) {
//新增
baseResource.setCreateBy(operatorId);
baseResource.setUpdateBy(operatorId);
newResource(baseResource);
} else {
//更新
baseResource.setId(resource.getId());
baseResource.setPath(baseResource.getPath() + "," + resource.getId());
baseResource.setUpdateBy(operatorId);
featureResourceDao.updateById(baseResource);
if (!StrUtil.equals(baseResource.getPath(), resource.getPath())) {
//层级变化
log.info("replace path from old:{} to new:{}", resource.getPath(), baseResource.getPath());
featureResourceDao.replacePath(resource.getPath(), baseResource.getPath());
}
}
//递归子节点
if (CollectionUtil.isNotEmpty(treeNode.getChildren())) {
doSyncFromBase(treeNode.getChildren(), codeCache, operatorId);
}
}
}
/** 修正当前环境的数据 parentId path **/
private void fixData(SaasFeatureResource resource, String parentCode) {
if (StrUtil.isBlank(parentCode)) {
resource.setParentId(0L);
resource.setPath("0");
} else {
//找当前环境的parent
SaasFeatureResource parent = featureResourceDao.getByCode(parentCode);
if (parent == null) {
resource.setParentId(0L);
resource.setPath("0");
} else {
resource.setParentId(parent.getId());
resource.setPath(parent.getPath());
}
}
}
private void newResource(SaasFeatureResource resource) {
featureResourceDao.save(resource);
//path追加自身ID
resource.setPath(resource.getPath() + "," + resource.getId());
featureResourceDao.updateById(resource);
}
}

View File

@ -126,4 +126,22 @@
</foreach>
</select>
<select id="listFeatureByIds" resultType="cn.axzo.tyr.server.model.RoleFeatureRelation">
SELECT rg.role_id AS roleId, pg.feature_id AS featureId
FROM saas_pgroup_role_relation rg, saas_pgroup_permission_relation pg
WHERE rg.group_id = pg.group_id AND rg.is_delete = 0 AND pg.is_delete = 0
<if test="roleIds != null and roleIds.size > 0">
AND rg.role_id IN
<foreach collection="roleIds" index="index" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
<if test="featureIds != null and featureIds.size > 0">
AND pg.feature_id IN
<foreach collection="featureIds" index="index" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
</select>
</mapper>