feat:(hotfix/20240906) 把菜单、角色菜单、产品菜单、角色权限、角色菜单从redis缓存切换到本地内存

This commit is contained in:
lilong 2024-09-11 15:12:28 +08:00
parent efbb4dd26f
commit a033919e17
23 changed files with 1260 additions and 1837 deletions

View File

@ -9,6 +9,7 @@ import cn.axzo.framework.rocketmq.RocketMQEventProducer;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
@ -67,7 +68,7 @@ public class RocketMQEventConfiguration {
@Component
@RocketMQMessageListener(topic = "topic_thrones_${spring.profiles.active}",
consumerGroup = "GID_topic_thrones_${spring.application.name}_${spring.profiles.active}",
consumeMode = ConsumeMode.ORDERLY,
messageModel = MessageModel.BROADCASTING,
nameServer = "${rocketmq.name-server}"
)
public static class ThronesListener extends BaseListener implements RocketMQListener<MessageExt> {
@ -85,7 +86,7 @@ public class RocketMQEventConfiguration {
@Component
@RocketMQMessageListener(topic = "topic_tyr_${spring.profiles.active}",
consumerGroup = "GID_topic_tyr_${spring.application.name}_${spring.profiles.active}",
consumeMode = ConsumeMode.ORDERLY,
messageModel = MessageModel.BROADCASTING,
nameServer = "${rocketmq.name-server}"
)
public static class TyrListener extends BaseListener implements RocketMQListener<MessageExt> {
@ -103,7 +104,7 @@ public class RocketMQEventConfiguration {
@Component
@RocketMQMessageListener(topic = "topic_apisix_plat_${spring.profiles.active}",
consumerGroup = "GID_topic_apisix_plat_${spring.application.name}_${spring.profiles.active}",
consumeMode = ConsumeMode.ORDERLY,
messageModel = MessageModel.BROADCASTING,
nameServer = "${rocketmq.name-server}"
)
public static class ApiSixPlatListener extends BaseListener implements RocketMQListener<MessageExt> {

View File

@ -180,6 +180,8 @@ public class PrivateController {
private RoleSaasFeatureResourceCacheService roleSaasFeatureResourceCacheService;
@Autowired
private SendDingTalkHandler sendDingTalkHandler;
@Autowired
private ProductSaasFeatureResourceCacheService productSaasFeatureResourceCacheService;
/**
* 统一层级的roleGroup按照id升序sort从1递增
@ -363,22 +365,17 @@ public class PrivateController {
});
}
@PostMapping("/api/private/productPermission/add")
public Object addProductPermission(@Validated @RequestBody ProductPermissionCacheService.StoreProductPermissionParam request) {
productPermissionCacheService.store(request);
@PostMapping("/api/private/productPermission/refresh")
public Object addProductPermission(@Validated @RequestBody ProductPermissionCacheService.RefreshProductPermissionCacheParam request) {
productPermissionCacheService.refreshCache(request);
return "ok";
}
@PostMapping("/api/private/productPermission/get")
@PostMapping("/api/private/productPermission/list")
public Object getProductPermission(@Validated @RequestBody ProductPermissionCacheService.ListProductPermissionParam request) {
return productPermissionCacheService.list(request);
}
@PostMapping("/api/private/productPermission/has")
public Object hasProductIds(@Validated @RequestBody ProductPermissionCacheService.HasProductPermissionParam request) {
return productPermissionCacheService.hasProductIds(request);
}
@PostMapping("/api/private/permission/auth")
public Object authPermission(@Validated @RequestBody PermissionCheckReq request) {
return tyrSaasAuthService.authPermission(request);
@ -634,6 +631,12 @@ public class PrivateController {
return featureCodeUtil.resolveFeatureCode(request.getFeatureCodes());
}
/**
* 缓存产品权限
* @param request
* @return
* @throws Exception
*/
@PostMapping("/api/private/productPermission/store")
public Object storeProductPermission(@RequestBody ProductSearchListReq request) throws Exception {
cacheProductPermissionJob.execute(JSON.toJSONString(request));
@ -666,104 +669,109 @@ public class PrivateController {
return "ok";
}
@PostMapping("/api/private/role/insert")
public Object insertRole(@RequestPart("file") MultipartFile file) throws IOException {
List<CreateRoleParam> importExcels = EasyExcel.read(file.getInputStream())
.head(CreateRoleParam.class)
.sheet()
.doReadSync();
importExcels = importExcels.stream()
.filter(e -> Objects.equals(e.getType(), "c"))
.filter(e -> Objects.nonNull(e.getGroupId()))
.collect(Collectors.toList());
Set<Long> roleGroupIds = importExcels.stream()
.map(CreateRoleParam::getGroupId)
.collect(Collectors.toSet());
Map<Long, SaasRoleGroup> saasRoleGroups = saasRoleGroupDao.listByIds(roleGroupIds).stream()
.collect(Collectors.toMap(SaasRoleGroup::getId, Function.identity()));
importExcels.forEach(e -> {
SaasRoleGroup saasRoleGroup = saasRoleGroups.get(e.getGroupId());
if (saasRoleGroup == null) {
log.info("not found saasRoleGroup,{}", e.getGroupId());
return;
}
SaveOrUpdateRoleVO.GroupInfoVO groupInfoVO = new SaveOrUpdateRoleVO.GroupInfoVO();
groupInfoVO.setId(e.getGroupId());
groupInfoVO.setWorkspaceTypeCode(saasRoleGroup.getWorkspaceTypeCode());
SaveOrUpdateRoleVO saveOrUpdateRoleVO = SaveOrUpdateRoleVO.builder()
.name(e.getRoleName())
.roleType("init")
.workspaceId(-1L)
.ownerOuId(-1L)
.operatorId(2007696L)
.operatorName("李龙")
.groupTree(Lists.newArrayList(groupInfoVO))
.permissionGroupName("通用权限")
.permissionGroupType("feature")
.roleCode(e.getRoleCode())
.isDisplay(true)
.enabled(true)
.build();
try {
roleService.saveOrUpdate(saveOrUpdateRoleVO);
} catch (Exception ex) {
log.info("roleName {},ex:", e.getRoleName(), ex);
}
});
return "ok";
}
/**
* 缓存角色权限
* @param request
* @return
* @throws Exception
*/
@PostMapping("/api/private/rolePermission/store")
public Object storeRolePermission(@RequestBody RoleService.PageSaasRoleParam request) throws Exception {
public Object storeRolePermission(@RequestBody RoleService.ListSaasRoleParam request) throws Exception {
cacheRolePermissionJob.execute(JSON.toJSONString(request));
return "ok";
}
/**
* 缓存菜单
* @param request
* @return
* @throws Exception
*/
@PostMapping("/api/private/saasFeature/store")
public Object storeSaasFeature(@RequestBody StoreFeatureParam request) throws Exception {
cacheSaasFeatureJob.execute(JSON.toJSONString(request));
return "ok";
}
/**
* 缓存产品菜单
* @param request
* @return
* @throws Exception
*/
@PostMapping("/api/private/productSaasFeature/store")
public Object storeProductSaasFeature(@RequestBody ProductSearchListReq request) throws Exception {
cacheProductFeatureResourceJob.execute(JSON.toJSONString(request));
return "ok";
}
/**
* 缓存角色菜单
* @param request
* @return
* @throws Exception
*/
@PostMapping("/api/private/roleSaasFeature/store")
public Object storeRoleSaasFeature(@RequestBody RoleService.PageSaasRoleParam request) throws Exception {
cacheRoleFeatureResourceJob.execute(JSON.toJSONString(request));
return "ok";
}
@PostMapping("/api/private/saasFeature/list")
public Object listSaasFeature(@RequestBody SaasFeatureResourceService.ListSaasFeatureResourceCache request) throws Exception {
return saasFeatureResourceService.listCache(request);
}
@PostMapping("/api/private/workspaceProductCached/list")
public Object workspaceProductCached(@RequestBody WorkspaceProductService.ListWorkspaceProductPermissionCacheParam request) {
return workspaceProductService.listWorkspaceProductPermissionCached(request);
}
/**
* 查询角色权限
* @param request
* @return
*/
@PostMapping("/api/private/rolePermissionCache/list")
public Object rolePermissionCache(@RequestBody RolePermissionCacheService.ListRolePermissionParam request) {
return rolePermissionCacheService.list(request);
}
/**
* 查询产品权限
* @param request
* @return
*/
@PostMapping("/api/private/productPermissionCached/list")
public Object productPermissionCached(@RequestBody ProductPermissionCacheService.ListProductPermissionParam request) {
return productPermissionCacheService.list(request);
}
/**
* 查询角色菜单
* @param request
* @return
*/
@PostMapping("/api/private/roleSaasFeatureResourceCache/list")
public Object listRoleSaasFeatureResourceCache(@RequestBody RoleSaasFeatureResourceCacheService.ListRoleSaasFeatureResourceParam request) {
return roleSaasFeatureResourceCacheService.list(request);
}
/**
* 查询产品菜单
* @param request
* @return
*/
@PostMapping("/api/private/productFeatureResourceCached/list")
public Object listProductSaasFeatureResourceCache(@RequestBody ProductSaasFeatureResourceCacheService.ListProductFeatureResourceParam request) {
return productSaasFeatureResourceCacheService.list(request);
}
/**
* 查询菜单树
* @param request
* @return
*/
@PostMapping("/api/private/featureResourceCache/list")
public Object listSaasFeatureResourceCache(@RequestBody SaasFeatureResourceService.ListSaasFeatureResourceCache request) {
return saasFeatureResourceService.listCache(request);
}
@PostMapping("/api/private/saasPageElement/refreshCmpFeatureResourceLinkUrl")
public ApiResult refreshCmpFeatureResourceLinkUrl(@RequestBody RefreshFeatureResourceLinkUrlParam request) {
Long startId = 0L;

View File

@ -2,18 +2,12 @@ package cn.axzo.tyr.server.event.inner;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.tyr.client.model.req.PageProductFeatureRelationReq;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.client.model.product.ProductSearchListReq;
import cn.axzo.tyr.client.model.product.ProductVO;
import cn.axzo.tyr.server.event.payload.PageElementFeatureResourceUpsertPayload;
import cn.axzo.tyr.server.event.payload.ProductPermissionCreatedPayload;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.entity.SaasFeature;
import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation;
import cn.axzo.tyr.server.service.ProductFeatureRelationService;
import cn.axzo.tyr.server.service.ProductPermissionCacheService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import com.google.common.collect.Lists;
import cn.axzo.tyr.server.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
@ -21,18 +15,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.OLD_FEATURE;
/**
* 缓存产品的权限
*/
@ -45,29 +30,8 @@ public class CacheProductPermissionHandler implements InitializingBean {
@Autowired
private ProductPermissionCacheService productPermissionCacheService;
@Autowired
private ProductFeatureRelationService productFeatureRelationService;
@Autowired
private SaasFeatureResourceService saasFeatureResourceService;
@Autowired
private SaasFeatureDao saasFeatureDao;
private ProductService productService;
private void storeProductPermission(List<SaasProductModuleFeatureRelation> productFeatures) {
if (CollectionUtils.isEmpty(productFeatures)) {
return;
}
List<ProductPermissionCacheService.ProductPermission> productPermissions = resolveProductPermissions(productFeatures);
if (CollectionUtils.isEmpty(productPermissions)) {
return;
}
ProductPermissionCacheService.StoreProductPermissionParam storeProductPermissionParam = ProductPermissionCacheService.StoreProductPermissionParam.builder()
.productPermissions(productPermissions)
.build();
productPermissionCacheService.store(storeProductPermissionParam);
}
public void onProductPermissionUpsert(Event event, EventConsumer.Context context) {
log.info("begin cached product permission handler rocketmq event: {}", event);
@ -76,16 +40,11 @@ public class CacheProductPermissionHandler implements InitializingBean {
if (CollectionUtils.isEmpty(payload.getProductModuleIds())) {
return;
}
PageProductFeatureRelationReq pageProductFeatureRelationReq = PageProductFeatureRelationReq.builder()
.productModuleIds(payload.getProductModuleIds())
ProductPermissionCacheService.RefreshProductPermissionCacheParam param = ProductPermissionCacheService.RefreshProductPermissionCacheParam.builder()
.productIds(payload.getProductModuleIds())
.build();
List<SaasProductModuleFeatureRelation> productFeatures = productFeatureRelationService.list(pageProductFeatureRelationReq);
if (CollectionUtils.isEmpty(productFeatures)) {
return;
}
storeProductPermission(productFeatures);
productPermissionCacheService.refreshCache(param);
log.info("end cached product permission handler rocketmq event: {}", event);
}
@ -97,14 +56,17 @@ public class CacheProductPermissionHandler implements InitializingBean {
return;
}
PageProductFeatureRelationReq pageProductFeatureRelationReq = PageProductFeatureRelationReq.builder()
.build();
List<SaasProductModuleFeatureRelation> productFeatures = productFeatureRelationService.list(pageProductFeatureRelationReq);
if (CollectionUtils.isEmpty(productFeatures)) {
Set<Long> productIds = productService.list(new ProductSearchListReq()).getData().stream()
.map(ProductVO::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(productIds)) {
return;
}
storeProductPermission(productFeatures);
ProductPermissionCacheService.RefreshProductPermissionCacheParam param = ProductPermissionCacheService.RefreshProductPermissionCacheParam.builder()
.productIds(productIds)
.build();
productPermissionCacheService.refreshCache(param);
log.info("end cached product permission handler rocketmq event: {}", event);
}
@ -113,216 +75,4 @@ public class CacheProductPermissionHandler implements InitializingBean {
eventConsumer.registerHandler(EventTypeEnum.PRODUCT_PERMISSION_CREATED.getEventCode(), this::onProductPermissionUpsert);
eventConsumer.registerHandler(EventTypeEnum.PAGE_ELEMENT_FEATURE_RESOURCE_UPSERT.getEventCode(), this::onPageElementFeatureResourceUpsert);
}
public List<ProductPermissionCacheService.ProductPermission> resolveProductPermissions(List<SaasProductModuleFeatureRelation> productPermissions) {
if (CollectionUtils.isEmpty(productPermissions)) {
return Collections.emptyList();
}
// 新的菜单树是是把有权限点的父节点也存进去了所以直接解析
Map<Long, SaasFeatureResourceResp> featureResources = listSaasFeatureResource(productPermissions);
Map<Long, SaasFeatureResourceResp> parentFeatureResources = listParentSaasFeatureResource(featureResources);
// 旧的菜单树只存储了权限点信息没有把父节点存进去需要解析父节点进行存储
Map<Long, SaasFeature> saasFeatures = listSaasFeature(productPermissions);
Map<Long, SaasFeature> parentSaasFeatures = listParentSaasFeature(saasFeatures);
return productPermissions.stream()
.collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId))
.entrySet()
.stream()
.map(e -> {
List<SaasProductModuleFeatureRelation> productFeatureRelations = e.getValue();
if (CollectionUtils.isEmpty(productFeatureRelations)) {
return null;
}
List<ProductPermissionCacheService.PermissionDTO> permissions = productFeatureRelations.stream()
.map(relation -> {
if (Objects.equals(relation.getType(), NEW_FEATURE)) {
return resolveFeatureResourcePermission(relation, featureResources, parentFeatureResources);
}
return resolveFeaturePermission(relation, saasFeatures, parentSaasFeatures);
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(permissions)) {
return null;
}
return ProductPermissionCacheService.ProductPermission.builder()
.productId(e.getKey())
.permissions(permissions)
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private List<ProductPermissionCacheService.PermissionDTO> resolveFeaturePermission(SaasProductModuleFeatureRelation relation, Map<Long, SaasFeature> saasFeatures, Map<Long, SaasFeature> parentSaasFeatures) {
SaasFeature saasFeature = saasFeatures.get(relation.getFeatureId());
if (Objects.isNull(saasFeature)) {
return null;
}
List<ProductPermissionCacheService.PermissionDTO> permissionDTOS = Lists.newArrayList(ProductPermissionCacheService.PermissionDTO.builder()
.featureId(saasFeature.getId())
.featureCode(saasFeature.getFeatureCode())
.featureType(saasFeature.getFeatureType())
.terminal(saasFeature.getTerminal())
.cooperateType(relation.getDictCode())
.build());
List<ProductPermissionCacheService.PermissionDTO> parentPermissions = saasFeature.splitPath().stream()
.map(parentSaasFeatures::get)
.filter(Objects::nonNull)
.map(f -> ProductPermissionCacheService.PermissionDTO.builder()
.featureId(f.getId())
.featureCode(f.getFeatureCode())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.cooperateType(relation.getDictCode())
.build())
.collect(Collectors.toList());
permissionDTOS.addAll(parentPermissions);
return permissionDTOS;
}
private List<ProductPermissionCacheService.PermissionDTO> resolveFeatureResourcePermission(SaasProductModuleFeatureRelation relation, Map<Long, SaasFeatureResourceResp> featureResources, Map<Long, SaasFeatureResourceResp> parentFeatureResources) {
SaasFeatureResourceResp featureResource = featureResources.get(relation.getFeatureId());
// 菜单节点是不会关联元素code所以缓存的featureCode使用菜单编码
if (Objects.isNull(featureResource)) {
return null;
}
if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) {
return null;
}
List<ProductPermissionCacheService.PermissionDTO> permissionDTOS = featureResource.getSaasPageElements().stream()
.map(pageElement -> ProductPermissionCacheService.PermissionDTO.builder()
.featureId(featureResource.getId())
.featureCode(pageElement.getCode())
.featureType(featureResource.getFeatureType())
.terminal(featureResource.getTerminal())
.cooperateType(relation.getDictCode())
.itemCode(pageElement.getItemCode())
.version(pageElement.getVersion())
.appType(pageElement.getAppType())
.build())
.collect(Collectors.toList());
List<ProductPermissionCacheService.PermissionDTO> parentPermissions = featureResource.resolvePath().stream()
.map(parentFeatureResources::get)
.filter(Objects::nonNull)
.map(f -> {
if (CollectionUtils.isEmpty(f.getSaasPageElements())) {
return null;
}
return f.getSaasPageElements().stream()
.map(pageElement -> ProductPermissionCacheService.PermissionDTO.builder()
.featureId(f.getId())
.featureCode(pageElement.getCode())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.cooperateType(relation.getDictCode())
.itemCode(pageElement.getItemCode())
.version(pageElement.getVersion())
.appType(pageElement.getAppType())
.build())
.collect(Collectors.toList());
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toList());
permissionDTOS.addAll(parentPermissions);
return permissionDTOS;
}
private Map<Long, SaasFeatureResourceResp> listSaasFeatureResource(List<SaasProductModuleFeatureRelation> productPermissions) {
List<Long> featureIds = productPermissions.stream()
.filter(e -> Objects.equals(e.getType(), NEW_FEATURE))
.map(SaasProductModuleFeatureRelation::getFeatureId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(featureIds)
.needPageElement(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeatureResourceResp> listParentSaasFeatureResource(Map<Long, SaasFeatureResourceResp> productPermissions) {
List<Long> parentIds = productPermissions.values().stream()
.map(SaasFeatureResourceResp::resolvePath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(parentIds)
.needPageElement(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeature> listSaasFeature(List<SaasProductModuleFeatureRelation> productPermissions) {
Set<Long> featureIds = productPermissions.stream()
.filter(e -> Objects.equals(e.getType(), OLD_FEATURE))
.map(SaasProductModuleFeatureRelation::getFeatureId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
return saasFeatureDao.listByIds(featureIds).stream()
.collect(Collectors.toMap(SaasFeature::getId, Function.identity()));
}
private Map<Long, SaasFeature> listParentSaasFeature(Map<Long, SaasFeature> saasFeatures) {
if (CollectionUtils.isEmpty(saasFeatures)) {
return Collections.emptyMap();
}
List<Long> parentIds = saasFeatures.values().stream()
.map(SaasFeature::splitPath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
return saasFeatureDao.listByIds(parentIds).stream()
.collect(Collectors.toMap(SaasFeature::getId, Function.identity()));
}
}

View File

@ -2,20 +2,12 @@ package cn.axzo.tyr.server.event.inner;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.tyr.client.common.enums.FeatureResourceType;
import cn.axzo.tyr.client.common.enums.PageElementFeatureResourceRelationTypeEnum;
import cn.axzo.tyr.client.model.req.PageProductFeatureRelationReq;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.PageElementResp;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.client.model.product.ProductSearchListReq;
import cn.axzo.tyr.client.model.product.ProductVO;
import cn.axzo.tyr.server.event.payload.PageElementFeatureResourceUpsertPayload;
import cn.axzo.tyr.server.event.payload.ProductPermissionCreatedPayload;
import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation;
import cn.axzo.tyr.server.service.ProductFeatureRelationService;
import cn.axzo.tyr.server.service.ProductSaasFeatureResourceCacheService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import cn.axzo.tyr.server.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
@ -23,18 +15,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE;
@Slf4j
@Component
public class CacheProductSaasFeatureResourceHandler implements InitializingBean {
@ -42,36 +25,9 @@ public class CacheProductSaasFeatureResourceHandler implements InitializingBean
@Autowired
private EventConsumer eventConsumer;
@Autowired
private ProductFeatureRelationService productFeatureRelationService;
@Autowired
private ProductSaasFeatureResourceCacheService productSaasFeatureResourceCacheService;
@Autowired
private SaasFeatureResourceService saasFeatureResourceService;
public static final Set<Integer> FEATURE_RESOURCE_TYPES = Sets.newHashSet(FeatureResourceType.MENU.getCode(),
FeatureResourceType.PAGE.getCode(),
FeatureResourceType.MENU_PARTITION_GROUP.getCode(),
FeatureResourceType.GROUP.getCode(),
FeatureResourceType.APP_ENTRY.getCode());
private void storeProductFeatureResource(List<SaasProductModuleFeatureRelation> productFeatures) {
if (CollectionUtils.isEmpty(productFeatures)) {
return;
}
List<ProductSaasFeatureResourceCacheService.ProductFeatureResource> productFeatureResources = resolveProductFeatureResources(productFeatures);
if (CollectionUtils.isEmpty(productFeatureResources)) {
return;
}
ProductSaasFeatureResourceCacheService.StoreProductFeatureResourceParam storeProductFeatureResourceParam = ProductSaasFeatureResourceCacheService.StoreProductFeatureResourceParam.builder()
.productFeatureResources(productFeatureResources)
.build();
productSaasFeatureResourceCacheService.store(storeProductFeatureResourceParam);
}
private ProductService productService;
public void onProductPermissionUpsert(Event event, EventConsumer.Context context) {
log.info("begin cached product featureResource handler rocketmq event: {}", event);
@ -81,13 +37,10 @@ public class CacheProductSaasFeatureResourceHandler implements InitializingBean
return;
}
PageProductFeatureRelationReq pageProductFeatureRelationReq = PageProductFeatureRelationReq.builder()
.productModuleIds(payload.getProductModuleIds())
.type(NEW_FEATURE)
ProductSaasFeatureResourceCacheService.RefreshProductFeatureResourceCacheParam param = ProductSaasFeatureResourceCacheService.RefreshProductFeatureResourceCacheParam.builder()
.productIds(payload.getProductModuleIds())
.build();
List<SaasProductModuleFeatureRelation> productFeatures = productFeatureRelationService.list(pageProductFeatureRelationReq);
storeProductFeatureResource(productFeatures);
productSaasFeatureResourceCacheService.refreshCache(param);
log.info("end cached product featureResource handler rocketmq event: {}", event);
}
@ -99,15 +52,17 @@ public class CacheProductSaasFeatureResourceHandler implements InitializingBean
return;
}
PageProductFeatureRelationReq pageProductFeatureRelationReq = PageProductFeatureRelationReq.builder()
.type(NEW_FEATURE)
.build();
List<SaasProductModuleFeatureRelation> productFeatures = productFeatureRelationService.list(pageProductFeatureRelationReq);
if (CollectionUtils.isEmpty(productFeatures)) {
Set<Long> productIds = productService.list(new ProductSearchListReq()).getData().stream()
.map(ProductVO::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(productIds)) {
return;
}
storeProductFeatureResource(productFeatures);
ProductSaasFeatureResourceCacheService.RefreshProductFeatureResourceCacheParam param = ProductSaasFeatureResourceCacheService.RefreshProductFeatureResourceCacheParam.builder()
.productIds(productIds)
.build();
productSaasFeatureResourceCacheService.refreshCache(param);
log.info("end cached product featureResource handler rocketmq event: {}", event);
}
@ -117,121 +72,4 @@ public class CacheProductSaasFeatureResourceHandler implements InitializingBean
eventConsumer.registerHandler(EventTypeEnum.PAGE_ELEMENT_FEATURE_RESOURCE_UPSERT.getEventCode(), this::onPageElementFeatureResourceUpsert);
}
public List<ProductSaasFeatureResourceCacheService.ProductFeatureResource> resolveProductFeatureResources(List<SaasProductModuleFeatureRelation> productPermissions) {
if (CollectionUtils.isEmpty(productPermissions)) {
return Collections.emptyList();
}
Map<Long, SaasFeatureResourceResp> featureResources = listSaasFeatureResource(productPermissions);
Map<Long, SaasFeatureResourceResp> parentFeatureResources = listParentSaasFeatureResource(featureResources);
return productPermissions.stream()
.collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId))
.entrySet()
.stream()
.map(e -> {
List<SaasProductModuleFeatureRelation> productFeatureRelations = e.getValue();
if (CollectionUtils.isEmpty(productFeatureRelations)) {
return null;
}
List<ProductSaasFeatureResourceCacheService.FeatureResourceDTO> productFeatureResources = productFeatureRelations.stream()
.map(relation -> {
SaasFeatureResourceResp featureResource = featureResources.get(relation.getFeatureId());
if (Objects.isNull(featureResource) || StringUtils.isBlank(featureResource.getUniCode())) {
return null;
}
ProductSaasFeatureResourceCacheService.FeatureResourceDTO featureResourceDTO = from(featureResource, relation);
List<ProductSaasFeatureResourceCacheService.FeatureResourceDTO> featureResourceDTOS = Lists.newArrayList(featureResourceDTO);
List<ProductSaasFeatureResourceCacheService.FeatureResourceDTO> parentPermissions = featureResource.resolvePath().stream()
.map(parentFeatureResources::get)
.filter(Objects::nonNull)
.map(f -> {
if (StringUtils.isBlank(f.getUniCode())) {
return null;
}
return from(featureResource, relation);
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
featureResourceDTOS.addAll(parentPermissions);
return featureResourceDTOS;
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.filter(f -> FEATURE_RESOURCE_TYPES.contains(f.getFeatureType()))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(productFeatureResources)) {
return null;
}
return ProductSaasFeatureResourceCacheService.ProductFeatureResource.builder()
.productId(e.getKey())
.featureResources(productFeatureResources)
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private ProductSaasFeatureResourceCacheService.FeatureResourceDTO from(SaasFeatureResourceResp featureResource,
SaasProductModuleFeatureRelation relation) {
return ProductSaasFeatureResourceCacheService.FeatureResourceDTO.builder()
.featureId(featureResource.getId())
.featureType(featureResource.getFeatureType())
.terminal(featureResource.getTerminal())
.uniCode(featureResource.getUniCode())
.cooperateType(relation.getDictCode())
.build();
}
private Map<Long, SaasFeatureResourceResp> listSaasFeatureResource(List<SaasProductModuleFeatureRelation> productPermissions) {
List<Long> featureIds = productPermissions.stream()
.map(SaasProductModuleFeatureRelation::getFeatureId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(featureIds)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeatureResourceResp> listParentSaasFeatureResource(Map<Long, SaasFeatureResourceResp> productPermissions) {
List<Long> parentIds = productPermissions.values().stream()
.map(SaasFeatureResourceResp::resolvePath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(parentIds)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
}

View File

@ -2,17 +2,11 @@ package cn.axzo.tyr.server.event.inner;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes;
import cn.axzo.tyr.client.model.res.SaasRoleRes;
import cn.axzo.tyr.server.event.payload.PageElementFeatureResourceUpsertPayload;
import cn.axzo.tyr.server.event.payload.RolePermissionCreatedPayload;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.entity.SaasFeature;
import cn.axzo.tyr.server.service.RolePermissionCacheService;
import cn.axzo.tyr.server.service.RoleService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -21,19 +15,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.OLD_FEATURE;
/**
* 缓存角色的权限
*/
@ -47,28 +32,6 @@ public class CacheRolePermissionHandler implements InitializingBean {
private RolePermissionCacheService rolePermissionCacheService;
@Autowired
private RoleService roleService;
@Autowired
private SaasFeatureResourceService saasFeatureResourceService;
@Autowired
private SaasFeatureDao saasFeatureDao;
private void storeRolePermission(List<SaasRoleRes> roles) {
if (CollectionUtils.isEmpty(roles)) {
return;
}
List<RolePermissionCacheService.RolePermission> rolePermissions = resolveRolePermission(roles);
if (CollectionUtils.isEmpty(rolePermissions)) {
return;
}
RolePermissionCacheService.StoreRolePermissionParam storeRolePermissionParam = RolePermissionCacheService.StoreRolePermissionParam.builder()
.rolePermissions(rolePermissions)
.build();
rolePermissionCacheService.store(storeRolePermissionParam);
}
public void onRolePermissionUpsert(Event event, EventConsumer.Context context) {
log.info("begin cached role permission handler rocketmq event: {}", event);
@ -84,11 +47,19 @@ public class CacheRolePermissionHandler implements InitializingBean {
.roleIds(Optional.ofNullable(payload.getRoleIds())
.map(Lists::newArrayList)
.orElse(null))
.needPermissionRelation(true)
.build();
List<SaasRoleRes> roles = roleService.list(listSaasRoleParam);
Set<Long> roleIds = roleService.list(listSaasRoleParam).stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(roleIds)) {
return;
}
RolePermissionCacheService.RefreshRolePermissionCacheParam refreshRolePermissionCacheParam = RolePermissionCacheService.RefreshRolePermissionCacheParam.builder()
.roleIds(roleIds)
.build();
rolePermissionCacheService.refreshCache(refreshRolePermissionCacheParam);
storeRolePermission(roles);
log.info("end cached role permission handler rocketmq event: {}", event);
}
@ -99,12 +70,18 @@ public class CacheRolePermissionHandler implements InitializingBean {
return;
}
RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder()
.needPermissionRelation(true)
.build();
List<SaasRoleRes> roles = roleService.list(listSaasRoleParam);
RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder().build();
Set<Long> roleIds = roleService.list(listSaasRoleParam).stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(roleIds)) {
return;
}
storeRolePermission(roles);
RolePermissionCacheService.RefreshRolePermissionCacheParam refreshRolePermissionCacheParam = RolePermissionCacheService.RefreshRolePermissionCacheParam.builder()
.roleIds(roleIds)
.build();
rolePermissionCacheService.refreshCache(refreshRolePermissionCacheParam);
}
@Override
@ -112,209 +89,4 @@ public class CacheRolePermissionHandler implements InitializingBean {
eventConsumer.registerHandler(EventTypeEnum.ROLE_PERMISSION_CREATED.getEventCode(), this::onRolePermissionUpsert);
eventConsumer.registerHandler(EventTypeEnum.PAGE_ELEMENT_FEATURE_RESOURCE_UPSERT.getEventCode(), this::onPageElementFeatureResourceUpsert);
}
public List<RolePermissionCacheService.RolePermission> resolveRolePermission(List<SaasRoleRes> roles) {
Map<Long, SaasFeatureResourceResp> featureResources = listSaasFeatureResource(roles);
Map<Long, SaasFeatureResourceResp> parentFeatureResources = listParentSaasFeatureResource(featureResources);
Map<Long, SaasFeature> saasFeatures = listSaasFeature(roles);
Map<Long, SaasFeature> parentSaasFeatures = listParentSaasFeature(saasFeatures);
return roles.stream()
.map(e -> {
if (CollectionUtils.isEmpty(e.getPermissionRelations())) {
return null;
}
List<RolePermissionCacheService.PermissionDTO> permissions = e.getPermissionRelations().stream()
.distinct()
.map(permissionRelation -> {
if (Objects.equals(permissionRelation.getType(), NEW_FEATURE)) {
return resolveFeatureResourcePermission(permissionRelation, featureResources, parentFeatureResources);
}
return resolveFeaturePermission(permissionRelation, saasFeatures, parentSaasFeatures);
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(permissions)) {
return null;
}
return RolePermissionCacheService.RolePermission.builder()
.roleId(e.getId())
.permissions(permissions)
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private static List<RolePermissionCacheService.PermissionDTO> resolveFeaturePermission(SaasPermissionRelationRes permissionRelation, Map<Long, SaasFeature> saasFeatures, Map<Long, SaasFeature> parentSaasFeatures) {
SaasFeature saasFeature = saasFeatures.get(permissionRelation.getFeatureId());
if (Objects.isNull(saasFeature)) {
return null;
}
List<RolePermissionCacheService.PermissionDTO> permissionDTOS = Lists.newArrayList(RolePermissionCacheService.PermissionDTO.builder()
.featureId(saasFeature.getId())
.featureCode(saasFeature.getFeatureCode())
.featureType(saasFeature.getFeatureType())
.terminal(saasFeature.getTerminal())
.build());
List<RolePermissionCacheService.PermissionDTO> parentPermissions = saasFeature.splitPath().stream()
.map(parentSaasFeatures::get)
.filter(Objects::nonNull)
.map(f -> RolePermissionCacheService.PermissionDTO.builder()
.featureId(f.getId())
.featureCode(f.getFeatureCode())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.build())
.collect(Collectors.toList());
permissionDTOS.addAll(parentPermissions);
return permissionDTOS;
}
private static List<RolePermissionCacheService.PermissionDTO> resolveFeatureResourcePermission(SaasPermissionRelationRes permissionRelation, Map<Long, SaasFeatureResourceResp> featureResources, Map<Long, SaasFeatureResourceResp> parentFeatureResources) {
SaasFeatureResourceResp featureResource = featureResources.get(permissionRelation.getFeatureId());
// 菜单节点是不会关联元素code所以缓存的featureCode使用菜单编码
if (Objects.isNull(featureResource)) {
return null;
}
if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) {
return null;
}
List<RolePermissionCacheService.PermissionDTO> permissionDTOS = featureResource.getSaasPageElements().stream()
.map(pageElement -> RolePermissionCacheService.PermissionDTO.builder()
.featureId(featureResource.getId())
.featureCode(pageElement.getCode())
.featureType(featureResource.getFeatureType())
.terminal(featureResource.getTerminal())
.build())
.collect(Collectors.toList());
List<RolePermissionCacheService.PermissionDTO> parentPermissions = featureResource.resolvePath().stream()
.map(parentFeatureResources::get)
.filter(Objects::nonNull)
.map(f -> {
if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) {
return null;
}
return featureResource.getSaasPageElements().stream()
.map(pageElement -> RolePermissionCacheService.PermissionDTO.builder()
.featureId(f.getId())
.featureCode(pageElement.getCode())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.build())
.collect(Collectors.toList());
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toList());
permissionDTOS.addAll(parentPermissions);
return permissionDTOS;
}
private Map<Long, SaasFeatureResourceResp> listSaasFeatureResource(List<SaasRoleRes> roles) {
List<Long> featureIds = roles.stream()
.filter(e -> !CollectionUtils.isEmpty(e.getPermissionRelations()))
.map(SaasRoleRes::getPermissionRelations)
.flatMap(Collection::stream)
.filter(e -> Objects.equals(e.getType(), NEW_FEATURE))
.map(SaasPermissionRelationRes::getFeatureId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(featureIds)
.needPageElement(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeatureResourceResp> listParentSaasFeatureResource(Map<Long, SaasFeatureResourceResp> productPermissions) {
List<Long> parentIds = productPermissions.values().stream()
.map(SaasFeatureResourceResp::resolvePath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(parentIds)
.needPageElement(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeature> listSaasFeature(List<SaasRoleRes> roles) {
Set<Long> featureIds = roles.stream()
.filter(e -> !CollectionUtils.isEmpty(e.getPermissionRelations()))
.map(SaasRoleRes::getPermissionRelations)
.flatMap(Collection::stream)
.filter(e -> Objects.equals(e.getType(), OLD_FEATURE))
.map(SaasPermissionRelationRes::getFeatureId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
return saasFeatureDao.listByIds(featureIds)
.stream()
.collect(Collectors.toMap(SaasFeature::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeature> listParentSaasFeature(Map<Long, SaasFeature> saasFeatures) {
if (CollectionUtils.isEmpty(saasFeatures)) {
return Collections.emptyMap();
}
List<Long> parentIds = saasFeatures.values().stream()
.map(SaasFeature::splitPath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
return saasFeatureDao.listByIds(parentIds).stream()
.collect(Collectors.toMap(SaasFeature::getId, Function.identity()));
}
}

View File

@ -2,15 +2,11 @@ package cn.axzo.tyr.server.event.inner;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes;
import cn.axzo.tyr.client.model.res.SaasRoleRes;
import cn.axzo.tyr.server.event.payload.PageElementFeatureResourceUpsertPayload;
import cn.axzo.tyr.server.event.payload.RolePermissionCreatedPayload;
import cn.axzo.tyr.server.service.RoleSaasFeatureResourceCacheService;
import cn.axzo.tyr.server.service.RoleService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -19,18 +15,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.event.inner.CacheProductSaasFeatureResourceHandler.FEATURE_RESOURCE_TYPES;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE;
/**
* 因为菜单树不一定有权限code
* 登录时需要查询菜单树所以把菜单树缓存起来
@ -44,27 +32,8 @@ public class CacheRoleSaasFeatureResourceHandler implements InitializingBean {
@Autowired
private RoleService roleService;
@Autowired
private SaasFeatureResourceService saasFeatureResourceService;
@Autowired
private RoleSaasFeatureResourceCacheService roleSaasFeatureResourceCacheService;
private void storeRoleFeatureResource(List<SaasRoleRes> roles) {
if (CollectionUtils.isEmpty(roles)) {
return;
}
List<RoleSaasFeatureResourceCacheService.RoleFeatureResource> rolePermissions = resolveRoleFeatureResource(roles);
if (CollectionUtils.isEmpty(rolePermissions)) {
return;
}
RoleSaasFeatureResourceCacheService.StoreRoleSaasFeatureResourceParam storeRoleFeatureResourceParam = RoleSaasFeatureResourceCacheService.StoreRoleSaasFeatureResourceParam.builder()
.roleSaasFeatureResources(rolePermissions)
.build();
roleSaasFeatureResourceCacheService.store(storeRoleFeatureResourceParam);
}
public void onRolePermissionUpsert(Event event, EventConsumer.Context context) {
log.info("begin cached role saasFeatureResource handler rocketmq event: {}", event);
RolePermissionCreatedPayload payload = event.normalizedData(RolePermissionCreatedPayload.class);
@ -79,12 +48,18 @@ public class CacheRoleSaasFeatureResourceHandler implements InitializingBean {
.roleIds(Optional.ofNullable(payload.getRoleIds())
.map(Lists::newArrayList)
.orElse(null))
.needPermissionRelation(true)
.type(NEW_FEATURE)
.build();
List<SaasRoleRes> roles = roleService.list(listSaasRoleParam);
Set<Long> roleIds = roleService.list(listSaasRoleParam).stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(roleIds)) {
return;
}
storeRoleFeatureResource(roles);
RoleSaasFeatureResourceCacheService.RefreshRoleFeatureResourceCacheParam param = RoleSaasFeatureResourceCacheService.RefreshRoleFeatureResourceCacheParam.builder()
.roleIds(roleIds)
.build();
roleSaasFeatureResourceCacheService.refreshCache(param);
log.info("end cached role saasFeatureResource handler rocketmq event: {}", event);
}
@ -97,12 +72,19 @@ public class CacheRoleSaasFeatureResourceHandler implements InitializingBean {
}
RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder()
.needPermissionRelation(true)
.type(NEW_FEATURE)
.build();
List<SaasRoleRes> roles = roleService.list(listSaasRoleParam);
storeRoleFeatureResource(roles);
Set<Long> roleIds = roleService.list(listSaasRoleParam).stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(roleIds)) {
return;
}
RoleSaasFeatureResourceCacheService.RefreshRoleFeatureResourceCacheParam param = RoleSaasFeatureResourceCacheService.RefreshRoleFeatureResourceCacheParam.builder()
.roleIds(roleIds)
.build();
roleSaasFeatureResourceCacheService.refreshCache(param);
}
@Override
@ -111,116 +93,4 @@ public class CacheRoleSaasFeatureResourceHandler implements InitializingBean {
eventConsumer.registerHandler(EventTypeEnum.PAGE_ELEMENT_FEATURE_RESOURCE_UPSERT.getEventCode(), this::onPageElementFeatureResourceUpsert);
}
public List<RoleSaasFeatureResourceCacheService.RoleFeatureResource> resolveRoleFeatureResource(List<SaasRoleRes> roles) {
Map<Long, SaasFeatureResourceResp> featureResources = listSaasFeatureResource(roles);
Map<Long, SaasFeatureResourceResp> parentFeatureResources = listParentSaasFeatureResource(featureResources);
return roles.stream()
.map(e -> {
if (CollectionUtils.isEmpty(e.getPermissionRelations())) {
return null;
}
List<RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO> permissions = e.getPermissionRelations().stream()
.distinct()
.map(permissionRelation -> {
SaasFeatureResourceResp featureResource = featureResources.get(permissionRelation.getFeatureId());
if (Objects.isNull(featureResource) || StringUtils.isBlank(featureResource.getUniCode())) {
return null;
}
List<RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO> featureResourceDTOS = Lists.newArrayList(RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO.builder()
.featureId(featureResource.getId())
.featureType(featureResource.getFeatureType())
.terminal(featureResource.getTerminal())
.uniCode(featureResource.getUniCode())
.build());
List<RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO> parentPermissions = featureResource.resolvePath().stream()
.map(parentFeatureResources::get)
.filter(Objects::nonNull)
.map(f -> {
if (StringUtils.isBlank(f.getUniCode())) {
return null;
}
return RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO.builder()
.featureId(f.getId())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.uniCode(f.getUniCode())
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
featureResourceDTOS.addAll(parentPermissions);
return featureResourceDTOS;
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.filter(f -> FEATURE_RESOURCE_TYPES.contains(f.getFeatureType()))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(permissions)) {
return null;
}
return RoleSaasFeatureResourceCacheService.RoleFeatureResource.builder()
.roleId(e.getId())
.saasFeatureResources(permissions)
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private Map<Long, SaasFeatureResourceResp> listSaasFeatureResource(List<SaasRoleRes> roles) {
List<Long> featureIds = roles.stream()
.filter(e -> !CollectionUtils.isEmpty(e.getPermissionRelations()))
.map(SaasRoleRes::getPermissionRelations)
.flatMap(Collection::stream)
.map(SaasPermissionRelationRes::getFeatureId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(featureIds)
.needFeatureCodes(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeatureResourceResp> listParentSaasFeatureResource(Map<Long, SaasFeatureResourceResp> productPermissions) {
List<Long> parentIds = productPermissions.values().stream()
.map(SaasFeatureResourceResp::resolvePath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(parentIds)
.needFeatureCodes(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
}

View File

@ -4,7 +4,7 @@ import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandler;
import cn.axzo.tyr.server.event.payload.SaasFeatureUpsertPayload;
import cn.axzo.tyr.server.job.CacheSaasFeatureJob;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -22,7 +22,7 @@ public class CacheSaasFeatureHandler implements EventHandler, InitializingBean {
@Autowired
private EventConsumer eventConsumer;
@Autowired
private CacheSaasFeatureJob cacheSaasFeatureJob;
private SaasFeatureResourceService saasFeatureResourceService;
@Override
public void onEvent(Event event, EventConsumer.Context context) {
@ -33,7 +33,10 @@ public class CacheSaasFeatureHandler implements EventHandler, InitializingBean {
return;
}
cacheSaasFeatureJob.cacheSaasFeature(Sets.newHashSet(payload.getTerminal()));
SaasFeatureResourceService.RefreshFeatureResourceCacheParam param = SaasFeatureResourceService.RefreshFeatureResourceCacheParam.builder()
.terminals(Sets.newHashSet(payload.getTerminal()))
.build();
saasFeatureResourceService.refreshCache(param);
log.info("end cached saasFeature handler rocketmq event: {}", event);
}

View File

@ -4,7 +4,7 @@ import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandler;
import cn.axzo.tyr.server.event.payload.SaasFeatureResourceUpsertPayload;
import cn.axzo.tyr.server.job.CacheSaasFeatureJob;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.InitializingBean;
@ -21,7 +21,7 @@ public class CacheSaasFeatureResourceHandler implements EventHandler, Initializi
@Autowired
private EventConsumer eventConsumer;
@Autowired
private CacheSaasFeatureJob cacheSaasFeatureJob;
private SaasFeatureResourceService saasFeatureResourceService;
@Override
public void onEvent(Event event, EventConsumer.Context context) {
@ -32,7 +32,10 @@ public class CacheSaasFeatureResourceHandler implements EventHandler, Initializi
return;
}
cacheSaasFeatureJob.cacheSaasFeatureResource(payload.getTerminals());
SaasFeatureResourceService.RefreshFeatureResourceCacheParam param = SaasFeatureResourceService.RefreshFeatureResourceCacheParam.builder()
.terminals(payload.getTerminals())
.build();
saasFeatureResourceService.refreshCache(param);
log.info("end cached saasFeatureResource handler rocketmq event: {}", event);
}

View File

@ -1,10 +1,8 @@
package cn.axzo.tyr.server.job;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.tyr.client.model.product.ProductSearchListReq;
import cn.axzo.tyr.client.model.product.ProductVO;
import cn.axzo.tyr.server.event.inner.CacheProductSaasFeatureResourceHandler;
import cn.axzo.tyr.server.event.payload.ProductPermissionCreatedPayload;
import cn.axzo.tyr.server.service.ProductSaasFeatureResourceCacheService;
import cn.axzo.tyr.server.service.ProductService;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
@ -13,6 +11,7 @@ import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Optional;
import java.util.Set;
@ -23,7 +22,7 @@ import java.util.stream.Collectors;
public class CacheProductFeatureResourceJob extends IJobHandler {
@Autowired
private CacheProductSaasFeatureResourceHandler cacheProductSaasFeatureResourceHandler;
private ProductSaasFeatureResourceCacheService productSaasFeatureResourceCacheService;
@Autowired
private ProductService productService;
@ -41,14 +40,14 @@ public class CacheProductFeatureResourceJob extends IJobHandler {
.map(ProductVO::getId)
.collect(Collectors.toSet());
ProductPermissionCreatedPayload payload = ProductPermissionCreatedPayload.builder()
.productModuleIds(productIds)
.build();
if (CollectionUtils.isEmpty(productIds)) {
return ReturnT.SUCCESS;
}
Event event = Event.builder()
.data(payload)
ProductSaasFeatureResourceCacheService.RefreshProductFeatureResourceCacheParam param = ProductSaasFeatureResourceCacheService.RefreshProductFeatureResourceCacheParam.builder()
.productIds(productIds)
.build();
cacheProductSaasFeatureResourceHandler.onProductPermissionUpsert(event, null);
productSaasFeatureResourceCacheService.refreshCache(param);
return ReturnT.SUCCESS;
}
}

View File

@ -1,10 +1,8 @@
package cn.axzo.tyr.server.job;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.tyr.client.model.product.ProductSearchListReq;
import cn.axzo.tyr.client.model.product.ProductVO;
import cn.axzo.tyr.server.event.inner.CacheProductPermissionHandler;
import cn.axzo.tyr.server.event.payload.ProductPermissionCreatedPayload;
import cn.axzo.tyr.server.service.ProductPermissionCacheService;
import cn.axzo.tyr.server.service.ProductService;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
@ -13,6 +11,7 @@ import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Optional;
import java.util.Set;
@ -23,7 +22,7 @@ import java.util.stream.Collectors;
public class CacheProductPermissionJob extends IJobHandler {
@Autowired
private CacheProductPermissionHandler cacheProductPermissionHandler;
private ProductPermissionCacheService productPermissionCacheService;
@Autowired
private ProductService productService;
@ -40,15 +39,14 @@ public class CacheProductPermissionJob extends IJobHandler {
.stream()
.map(ProductVO::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(productIds)) {
return ReturnT.SUCCESS;
}
ProductPermissionCreatedPayload payload = ProductPermissionCreatedPayload.builder()
.productModuleIds(productIds)
ProductPermissionCacheService.RefreshProductPermissionCacheParam param = ProductPermissionCacheService.RefreshProductPermissionCacheParam.builder()
.productIds(productIds)
.build();
Event event = Event.builder()
.data(payload)
.build();
cacheProductPermissionHandler.onProductPermissionUpsert(event, null);
productPermissionCacheService.refreshCache(param);
return ReturnT.SUCCESS;
}
}

View File

@ -1,15 +1,8 @@
package cn.axzo.tyr.server.job;
import cn.axzo.foundation.page.PageResp;
import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes;
import cn.axzo.tyr.client.model.res.SaasRoleRes;
import cn.axzo.tyr.server.event.inner.CacheRoleSaasFeatureResourceHandler;
import cn.axzo.tyr.server.repository.dao.SaasPgroupRoleRelationDao;
import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation;
import cn.axzo.tyr.server.repository.entity.SaasPgroupRoleRelation;
import cn.axzo.tyr.server.service.RoleSaasFeatureResourceCacheService;
import cn.axzo.tyr.server.service.RoleService;
import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
@ -19,9 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -30,102 +20,32 @@ import java.util.stream.Collectors;
@Component
public class CacheRoleFeatureResourceJob extends IJobHandler {
@Autowired
private CacheRoleSaasFeatureResourceHandler cacheRoleSaasFeatureResourceHandler;
@Autowired
private RoleService roleService;
@Autowired
private SaasPgroupPermissionRelationService saasPgroupPermissionRelationService;
@Autowired
private SaasPgroupRoleRelationDao saasPgroupRoleRelationDao;
@Autowired
private RoleSaasFeatureResourceCacheService roleSaasFeatureResourceCacheService;
private static final Integer DEFAULT_PAGE_SIZE = 2000;
@Override
@XxlJob("CacheRoleFeatureResourceJob")
public ReturnT<String> execute(String s) throws Exception {
log.info("start CacheRoleFeatureResourceJob, s:{}", s);
RoleService.PageSaasRoleParam pageSaasRoleParam = Optional.ofNullable(s)
.map(e -> JSONObject.parseObject(e, RoleService.PageSaasRoleParam.class))
.orElseGet(() -> RoleService.PageSaasRoleParam.builder().build());
RoleService.ListSaasRoleParam listSaasRoleParam = Optional.ofNullable(s)
.map(e -> JSONObject.parseObject(e, RoleService.ListSaasRoleParam.class))
.orElseGet(() -> RoleService.ListSaasRoleParam.builder().build());
// 因为角色权限集是重复使用通过角色找权限集数据量太大直接查询所有权限集的权限比较快
Map<Long, List<SaasPermissionRelationRes>> permissionRelations = listPgroupPermissionRelation();
Integer pageNumber = 1;
while (true) {
pageSaasRoleParam.setPage(pageNumber++);
pageSaasRoleParam.setPageSize(DEFAULT_PAGE_SIZE);
PageResp<SaasRoleRes> page = roleService.page(pageSaasRoleParam);
store(page.getData(), permissionRelations);
if (!page.hasNext()) {
break;
}
Set<Long> roleIds = roleService.list(listSaasRoleParam).stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(roleIds)) {
return ReturnT.SUCCESS;
}
RoleSaasFeatureResourceCacheService.RefreshRoleFeatureResourceCacheParam param = RoleSaasFeatureResourceCacheService.RefreshRoleFeatureResourceCacheParam.builder()
.roleIds(roleIds)
.build();
roleSaasFeatureResourceCacheService.refreshCache(param);
return ReturnT.SUCCESS;
}
private Map<Long, List<SaasPermissionRelationRes>> listPgroupPermissionRelation() {
return saasPgroupPermissionRelationService.list().stream()
.collect(Collectors.groupingBy(SaasPgroupPermissionRelation::getGroupId,
Collectors.mapping(e -> SaasPermissionRelationRes.builder()
.featureId(e.getFeatureId())
.featureType(e.getFeatureType())
.type(e.getType())
.build(), Collectors.toList())));
}
private Map<Long, Set<Long>> listPermissionGroup(List<Long> roleIds) {
return saasPgroupRoleRelationDao.findByRoleIds(roleIds).stream()
.collect(Collectors.groupingBy(SaasPgroupRoleRelation::getRoleId,
Collectors.mapping(SaasPgroupRoleRelation::getGroupId, Collectors.toSet())));
}
private void store(List<SaasRoleRes> roles,
Map<Long, List<SaasPermissionRelationRes>> permissionRelations) {
if (CollectionUtils.isEmpty(roles)) {
return;
}
List<Long> roleIds = roles.stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toList());
Map<Long, Set<Long>> roleGroupMap = listPermissionGroup(roleIds);
roles.forEach(e -> {
Set<Long> groupIds = roleGroupMap.get(e.getId());
if (CollectionUtils.isEmpty(groupIds)) {
return;
}
List<SaasPermissionRelationRes> rolePermissions = groupIds.stream()
.map(permissionRelations::get)
.filter(f -> !CollectionUtils.isEmpty(f))
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
e.setPermissionRelations(rolePermissions);
});
List<RoleSaasFeatureResourceCacheService.RoleFeatureResource> roleFeatureResources = cacheRoleSaasFeatureResourceHandler.resolveRoleFeatureResource(roles);
if (CollectionUtils.isEmpty(roleFeatureResources)) {
return;
}
RoleSaasFeatureResourceCacheService.StoreRoleSaasFeatureResourceParam storeRolePermissionParam = RoleSaasFeatureResourceCacheService.StoreRoleSaasFeatureResourceParam.builder()
.roleSaasFeatureResources(roleFeatureResources)
.build();
roleSaasFeatureResourceCacheService.store(storeRolePermissionParam);
}
}

View File

@ -1,15 +1,8 @@
package cn.axzo.tyr.server.job;
import cn.axzo.foundation.page.PageResp;
import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes;
import cn.axzo.tyr.client.model.res.SaasRoleRes;
import cn.axzo.tyr.server.event.inner.CacheRolePermissionHandler;
import cn.axzo.tyr.server.repository.dao.SaasPgroupRoleRelationDao;
import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation;
import cn.axzo.tyr.server.repository.entity.SaasPgroupRoleRelation;
import cn.axzo.tyr.server.service.RolePermissionCacheService;
import cn.axzo.tyr.server.service.RoleService;
import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
@ -19,9 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -30,102 +20,33 @@ import java.util.stream.Collectors;
@Component
public class CacheRolePermissionJob extends IJobHandler {
@Autowired
private CacheRolePermissionHandler cacheRolePermissionHandler;
@Autowired
private RoleService roleService;
@Autowired
private SaasPgroupPermissionRelationService saasPgroupPermissionRelationService;
@Autowired
private SaasPgroupRoleRelationDao saasPgroupRoleRelationDao;
@Autowired
private RolePermissionCacheService rolePermissionCacheService;
private static final Integer DEFAULT_PAGE_SIZE = 2000;
@Override
@XxlJob("CacheRolePermissionJob")
public ReturnT<String> execute(String s) throws Exception {
log.info("start CacheRolePermissionJob, s:{}", s);
RoleService.PageSaasRoleParam pageSaasRoleParam = Optional.ofNullable(s)
.map(e -> JSONObject.parseObject(e, RoleService.PageSaasRoleParam.class))
.orElseGet(() -> RoleService.PageSaasRoleParam.builder().build());
RoleService.ListSaasRoleParam listSaasRoleParam = Optional.ofNullable(s)
.map(e -> JSONObject.parseObject(e, RoleService.ListSaasRoleParam.class))
.orElseGet(() -> RoleService.ListSaasRoleParam.builder().build());
// 因为角色权限集是重复使用通过角色找权限集数据量太大直接查询所有权限集的权限比较快
Map<Long, List<SaasPermissionRelationRes>> permissionRelations = listPgroupPermissionRelation();
Integer pageNumber = 1;
while (true) {
pageSaasRoleParam.setPage(pageNumber++);
pageSaasRoleParam.setPageSize(DEFAULT_PAGE_SIZE);
PageResp<SaasRoleRes> page = roleService.page(pageSaasRoleParam);
store(page.getData(), permissionRelations);
if (!page.hasNext()) {
break;
}
Set<Long> roleIds = roleService.list(listSaasRoleParam).stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(roleIds)) {
return ReturnT.SUCCESS;
}
RolePermissionCacheService.RefreshRolePermissionCacheParam refreshRolePermissionCacheParam = RolePermissionCacheService.RefreshRolePermissionCacheParam.builder()
.roleIds(roleIds)
.build();
rolePermissionCacheService.refreshCache(refreshRolePermissionCacheParam);
return ReturnT.SUCCESS;
}
private Map<Long, List<SaasPermissionRelationRes>> listPgroupPermissionRelation() {
return saasPgroupPermissionRelationService.list().stream()
.collect(Collectors.groupingBy(SaasPgroupPermissionRelation::getGroupId,
Collectors.mapping(e -> SaasPermissionRelationRes.builder()
.featureId(e.getFeatureId())
.featureType(e.getFeatureType())
.type(e.getType())
.build(), Collectors.toList())));
}
private Map<Long, Set<Long>> listPermissionGroup(List<Long> roleIds) {
return saasPgroupRoleRelationDao.findByRoleIds(roleIds).stream()
.collect(Collectors.groupingBy(SaasPgroupRoleRelation::getRoleId,
Collectors.mapping(SaasPgroupRoleRelation::getGroupId, Collectors.toSet())));
}
private void store(List<SaasRoleRes> roles,
Map<Long, List<SaasPermissionRelationRes>> permissionRelations) {
if (CollectionUtils.isEmpty(roles)) {
return;
}
List<Long> roleIds = roles.stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toList());
Map<Long, Set<Long>> roleGroupMap = listPermissionGroup(roleIds);
roles.forEach(e -> {
Set<Long> groupIds = roleGroupMap.get(e.getId());
if (CollectionUtils.isEmpty(groupIds)) {
return;
}
List<SaasPermissionRelationRes> rolePermissions = groupIds.stream()
.map(permissionRelations::get)
.filter(f -> !CollectionUtils.isEmpty(f))
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
e.setPermissionRelations(rolePermissions);
});
List<RolePermissionCacheService.RolePermission> rolePermissions = cacheRolePermissionHandler.resolveRolePermission(roles);
if (CollectionUtils.isEmpty(rolePermissions)) {
return;
}
RolePermissionCacheService.StoreRolePermissionParam storeRolePermissionParam = RolePermissionCacheService.StoreRolePermissionParam.builder()
.rolePermissions(rolePermissions)
.build();
rolePermissionCacheService.store(storeRolePermissionParam);
}
}

View File

@ -1,13 +1,9 @@
package cn.axzo.tyr.server.job;
import cn.axzo.tyr.client.common.enums.FeatureResourceStatus;
import cn.axzo.tyr.client.model.enums.DelegatedType;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.PageElementResp;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.entity.SaasFeature;
import cn.axzo.tyr.server.repository.entity.SaasFeatureResource;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
@ -22,8 +18,6 @@ import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -37,11 +31,6 @@ public class CacheSaasFeatureJob extends IJobHandler {
@Autowired
private SaasFeatureResourceService saasFeatureResourceService;
/**
* 分组这些菜单节点没有版本号默认为0方便权限过滤
*/
private static final int DEFAULT_VERSION = 0;
@Override
@XxlJob("CacheSaasFeatureJob")
public ReturnT<String> execute(String s) throws Exception {
@ -50,6 +39,7 @@ public class CacheSaasFeatureJob extends IJobHandler {
StoreSaasFeatureParam req = Optional.ofNullable(s)
.map(e -> JSONObject.parseObject(e, StoreSaasFeatureParam.class))
.orElseGet(() -> StoreSaasFeatureParam.builder().build());
cacheSaasFeature(req.getTerminals());
cacheSaasFeatureResource(req.getTerminals());
@ -57,66 +47,42 @@ public class CacheSaasFeatureJob extends IJobHandler {
}
public void cacheSaasFeature(Set<String> terminals) {
Map<String, List<SaasFeatureResourceService.SaasFeatureResourceCache>> saasFeatures = saasFeatureDao.lambdaQuery()
// 没有办法只有所有端除非写sql去重菜单数量不多
Set<String> updateTerminals = saasFeatureDao.lambdaQuery()
.in(CollectionUtils.isNotEmpty(terminals), SaasFeature::getTerminal, terminals)
.list()
.stream()
.collect(Collectors.groupingBy(SaasFeature::getTerminal,
Collectors.mapping(e -> SaasFeatureResourceService.SaasFeatureResourceCache
.builder()
.featureId(e.getId())
.notAuth(DelegatedType.notAuth(e.getDelegatedType()))
.parentIds(e.splitPath())
.build(), Collectors.toList())));
.map(SaasFeature::getTerminal)
.collect(Collectors.toSet());
List<SaasFeatureResourceService.SaasFeatureResourceDTO> saasFeatureResources = saasFeatures.entrySet().stream()
.map(e -> SaasFeatureResourceService.SaasFeatureResourceDTO.builder()
.terminal(e.getKey())
.features(e.getValue())
.build())
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(updateTerminals)) {
return;
}
SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder()
.saasFeatureResources(saasFeatureResources)
SaasFeatureResourceService.RefreshFeatureResourceCacheParam param = SaasFeatureResourceService.RefreshFeatureResourceCacheParam.builder()
.terminals(updateTerminals)
.build();
saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache);
saasFeatureResourceService.refreshCache(param);
}
public void cacheSaasFeatureResource(Set<String> terminals) {
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.needPageElement(true)
.terminals(terminals)
.build();
Map<String, List<SaasFeatureResourceService.SaasFeatureResourceCache>> saasFeatureResources = saasFeatureResourceService.list(pageSaasFeatureResourceReq)
Set<String> updateTerminals = saasFeatureResourceService.list(pageSaasFeatureResourceReq)
.stream()
.collect(Collectors.groupingBy(SaasFeatureResourceResp::getTerminal,
Collectors.mapping(e -> SaasFeatureResourceService.SaasFeatureResourceCache
.builder()
.featureId(e.getId())
.notAuth(SaasFeatureResource.AuthType.isAllRole(e.getAuthType()))
.parentIds(e.resolvePath())
.uniCode(e.getUniCode())
.status(e.getStatus())
.version(Optional.ofNullable(e.getSaasPageElements())
.map(pageElement -> pageElement.stream()
.findFirst()
.map(PageElementResp::getVersion)
.orElse(DEFAULT_VERSION))
.orElse(DEFAULT_VERSION))
.build(), Collectors.toList())));
.map(SaasFeatureResourceResp::getTerminal)
.collect(Collectors.toSet());
List<SaasFeatureResourceService.SaasFeatureResourceDTO> featureResources = saasFeatureResources.entrySet().stream()
.map(e -> SaasFeatureResourceService.SaasFeatureResourceDTO.builder()
.terminal(e.getKey())
.features(e.getValue())
.build())
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(updateTerminals)) {
return;
}
SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder()
.saasFeatureResources(featureResources)
SaasFeatureResourceService.RefreshFeatureResourceCacheParam param = SaasFeatureResourceService.RefreshFeatureResourceCacheParam.builder()
.terminals(updateTerminals)
.build();
saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache);
saasFeatureResourceService.refreshCache(param);
}
@Data

View File

@ -18,18 +18,15 @@ public interface ProductPermissionCacheService {
*/
Map<Long, List<PermissionDTO>> list(ListProductPermissionParam param);
/**
* 存储产品的权限信息
* @param param
*/
void store(StoreProductPermissionParam param);
void refreshCache(RefreshProductPermissionCacheParam param);
/**
* 产品是否有缓存权限
* @param param
* @return
*/
List<Long> hasProductIds(HasProductPermissionParam param);
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class RefreshProductPermissionCacheParam {
private Set<Long> productIds;
}
@Data
@Builder

View File

@ -13,7 +13,15 @@ public interface ProductSaasFeatureResourceCacheService {
Map<Long, List<FeatureResourceDTO>> list(ListProductFeatureResourceParam param);
void store(StoreProductFeatureResourceParam param);
void refreshCache(RefreshProductFeatureResourceCacheParam param);
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class RefreshProductFeatureResourceCacheParam {
private Set<Long> productIds;
}
@Data
@Builder

View File

@ -14,29 +14,14 @@ public interface RolePermissionCacheService {
Map<Long, List<PermissionDTO>> list(ListRolePermissionParam param);
/**
* redisKey:roleId
* redisValue:permission
* @param param
*/
void store(StoreRolePermissionParam param);
List<Long> hasRoleIds(HasRolePermissionParam param);
void refreshCache(RefreshRolePermissionCacheParam param);
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class HasRolePermissionParam {
private List<Long> roleIds;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class StoreRolePermissionParam {
private List<RolePermission> rolePermissions;
class RefreshRolePermissionCacheParam {
private Set<Long> roleIds;
}
@Data

View File

@ -13,8 +13,15 @@ public interface RoleSaasFeatureResourceCacheService {
Map<Long, List<SaasFeatureResourceDTO>> list(ListRoleSaasFeatureResourceParam param);
void store(StoreRoleSaasFeatureResourceParam param);
void refreshCache(RefreshRoleFeatureResourceCacheParam param);
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class RefreshRoleFeatureResourceCacheParam {
private Set<Long> roleIds;
}
@Data
@Builder

View File

@ -64,10 +64,18 @@ public interface SaasFeatureResourceService extends IService<SaasFeatureResource
void deleteFeatureResource(DeleteFeatureResourceReq param);
void storeCache(StoreSaasFeatureResourceCache param);
Map<String, List<SaasFeatureResourceCache>> listCache(ListSaasFeatureResourceCache param);
void refreshCache(RefreshFeatureResourceCacheParam param);
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class RefreshFeatureResourceCacheParam {
private Set<String> terminals;
}
@Data
@Builder
@NoArgsConstructor

View File

@ -1,228 +1,348 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.foundation.exception.Axssert;
import cn.axzo.pokonyan.config.redis.RedisClient;
import cn.axzo.basics.common.exception.ServiceException;
import cn.axzo.tyr.client.model.req.PageProductFeatureRelationReq;
import cn.axzo.tyr.server.event.inner.CacheProductPermissionHandler;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.entity.SaasFeature;
import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation;
import cn.axzo.tyr.server.service.ProductFeatureRelationService;
import cn.axzo.tyr.server.service.ProductPermissionCacheService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.config.exception.BizResultCode.REDIS_PRODUCT_NOT_NULL;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.OLD_FEATURE;
@Slf4j
@Service
@RefreshScope
public class ProductPermissionCacheServiceImpl implements ProductPermissionCacheService {
private static final String PRODUCT_PERMISSION_KEY = "product:permission:%s";
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private ProductFeatureRelationService productFeatureRelationService;
@Autowired
private CacheProductPermissionHandler cacheProductPermissionHandler;
private SaasFeatureResourceService saasFeatureResourceService;
@Autowired
private SaasFeatureDao saasFeatureDao;
/** 产品权限缓存过期时间 **/
@Value("${product.permission.expire.minutes:14}")
private Long expireInMinutes;
private LoadingCache<Long, Optional<List<PermissionDTO>>> productPermissionCache = CacheBuilder.newBuilder()
.expireAfterWrite(14, TimeUnit.MINUTES)
.maximumSize(5000)
.build(new CacheLoader<Long, Optional<List<PermissionDTO>>>() {
@Override
public Optional<List<PermissionDTO>> load(Long productId) {
return listProductPermissions(Lists.newArrayList(productId))
.values()
.stream()
.findFirst();
}
@Override
public Map<Long, Optional<List<PermissionDTO>>> loadAll(Iterable keys) throws Exception {
List<Long> productIds = Lists.newArrayList(keys);
Map<Long, List<PermissionDTO>> productPermissions = listProductPermissions(productIds);
return Maps.toMap(productIds, productId -> Optional.ofNullable(productPermissions.get(productId)));
}
});
@Override
public Map<Long, List<PermissionDTO>> list(ListProductPermissionParam param) {
if (CollectionUtils.isEmpty(param.getProductIds())) {
return Collections.emptyMap();
}
Map<Long, List<PermissionDTO>> permissionCached = listProductPermissionCached(param);
Map<Long, List<PermissionDTO>> productPermissions;
try {
productPermissions = productPermissionCache.getAll(param.getProductIds()).entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().orElseGet(Lists::newArrayList)));
} catch (ExecutionException ex) {
log.error("list product cache permission error:{} error", param.getProductIds(), ex);
// 外面有做降级
throw new ServiceException("查询产品权限缓存异常");
}
fillCacheProductPermissions(param, permissionCached);
if (CollectionUtils.isEmpty(param.getFeatureCodes())) {
return productPermissions;
}
return permissionCached;
}
private Map<Long, List<PermissionDTO>> listProductPermissionCached(ListProductPermissionParam param) {
List<Object> redisValues = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (Long productId : param.getProductIds()) {
String redisKey = getKey(productId);
if (CollectionUtils.isEmpty(param.getFeatureCodes())) {
RedisClient.HashOps.hGetAll(redisKey);
} else {
RedisClient.HashOps.hMultiGet(redisKey, Lists.newArrayList(param.getFeatureCodes()));
}
}
return null;
}
});
return Streams.zip(param.getProductIds().stream(),
redisValues.stream(),
(productId, redisValue) -> {
if (Objects.isNull(redisValue)) {
return null;
}
if (CollectionUtils.isEmpty(param.getFeatureCodes())) {
List<PermissionDTO> permissions = (List<PermissionDTO>) ((Map) redisValue).values().stream()
.flatMap(e -> JSONArray.parseArray(JSONArray.toJSONString(e), PermissionDTO.class).stream())
.collect(Collectors.toList());
return Pair.of(productId, permissions);
}
List<PermissionDTO> permissions = (List<PermissionDTO>) ((List) redisValue).stream()
.filter(Objects::nonNull)
.flatMap(e -> JSONArray.parseArray((String) e, PermissionDTO.class).stream())
.collect(Collectors.toList());
return Pair.of(productId, permissions);
})
.filter(Objects::nonNull)
return productPermissions.entrySet()
.stream()
.map(e -> Pair.of(e.getKey(), e.getValue().stream()
.filter(permission -> param.getFeatureCodes().contains(permission.getFeatureCode()))
.collect(Collectors.toList()))
)
.filter(e -> !CollectionUtils.isEmpty(e.getValue()))
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}
@Override
public void store(StoreProductPermissionParam param) {
Axssert.check(param.getProductPermissions().stream().allMatch(e -> Objects.nonNull(e.getProductId())), REDIS_PRODUCT_NOT_NULL);
redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (ProductPermission productPermission : param.getProductPermissions()) {
String redisKey = getKey(productPermission.getProductId());
Map<String, String> redisValues = productPermission.getPermissions().stream()
.collect(Collectors.groupingBy(PermissionDTO::getFeatureCode))
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> JSONObject.toJSONString(e.getValue())));
// 存在hash中部分key移除为了处理快直接把redisKey删除掉修改不频繁
redisTemplate.delete(redisKey);
RedisClient.HashOps.hPutAll(redisKey, redisValues);
redisTemplate.expire(redisKey, expireInMinutes, TimeUnit.MINUTES);
log.info("succeed to store product permission: redisKey:{} value:{}", redisKey, redisValues);
}
return null;
}
});
}
@Override
public List<Long> hasProductIds(HasProductPermissionParam param) {
List<Object> redisValues = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (Long productId : param.getProductIds()) {
String redisKey = getKey(productId);
operations.hasKey(redisKey);
}
return null;
}
});
return Streams.zip(param.getProductIds().stream(),
redisValues.stream(),
(productId, redisValue) -> Pair.of(productId, redisValue))
.filter(e -> BooleanUtils.isTrue((Boolean) e.getValue()))
.map(Pair::getKey)
.collect(Collectors.toList());
}
/**
* 组装在缓存中没有查询到权限的产品
* @param param
* @param permissionCached
*/
private void fillCacheProductPermissions(ListProductPermissionParam param,
Map<Long, List<PermissionDTO>> permissionCached) {
Sets.SetView<Long> difference = Sets.difference(param.getProductIds(), permissionCached.keySet());
if (difference.isEmpty()) {
public void refreshCache(RefreshProductPermissionCacheParam param) {
if (CollectionUtils.isEmpty(param.getProductIds())) {
return;
}
Map<Long, List<PermissionDTO>> productPermissions = listProductPermissions(Lists.newArrayList(param.getProductIds()));
productPermissionCache.putAll(Maps.toMap(param.getProductIds(), productId -> Optional.ofNullable(productPermissions.get(productId))));
}
private Map<Long, List<PermissionDTO>> listProductPermissions(List<Long> productIds) {
if (CollectionUtils.isEmpty(productIds)) {
return Collections.emptyMap();
}
PageProductFeatureRelationReq pageProductFeatureRelationReq = PageProductFeatureRelationReq.builder()
.productModuleIds(difference)
.productModuleIds(Sets.newHashSet(productIds))
.build();
List<SaasProductModuleFeatureRelation> productPermissions = productFeatureRelationService.list(pageProductFeatureRelationReq);
List<ProductPermission> productPermissionsCache = cacheProductPermissionHandler.resolveProductPermissions(productPermissions);
if (CollectionUtils.isEmpty(productPermissionsCache)) return;
StoreProductPermissionParam storeWorkspaceProductParam = StoreProductPermissionParam.builder()
.productPermissions(productPermissionsCache)
.build();
store(storeWorkspaceProductParam);
Map<Long, List<PermissionDTO>> productPermissionMap = productPermissionsCache.stream()
return resolveProductPermissions(productPermissions).stream()
.collect(Collectors.toMap(ProductPermission::getProductId, ProductPermission::getPermissions));
permissionCached.putAll(productPermissionMap);
}
private String getKey(Object... params) {
return String.format(PRODUCT_PERMISSION_KEY, params);
private List<ProductPermissionCacheService.ProductPermission> resolveProductPermissions(List<SaasProductModuleFeatureRelation> productPermissions) {
if (CollectionUtils.isEmpty(productPermissions)) {
return Collections.emptyList();
}
// 新的菜单树是是把有权限点的父节点也存进去了所以直接解析
Map<Long, SaasFeatureResourceResp> featureResources = listSaasFeatureResource(productPermissions);
Map<Long, SaasFeatureResourceResp> parentFeatureResources = listParentSaasFeatureResource(featureResources);
// 旧的菜单树只存储了权限点信息没有把父节点存进去需要解析父节点进行存储
Map<Long, SaasFeature> saasFeatures = listSaasFeature(productPermissions);
Map<Long, SaasFeature> parentSaasFeatures = listParentSaasFeature(saasFeatures);
return productPermissions.stream()
.collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId))
.entrySet()
.stream()
.map(e -> {
List<SaasProductModuleFeatureRelation> productFeatureRelations = e.getValue();
if (CollectionUtils.isEmpty(productFeatureRelations)) {
return null;
}
List<ProductPermissionCacheService.PermissionDTO> permissions = productFeatureRelations.stream()
.map(relation -> {
if (Objects.equals(relation.getType(), NEW_FEATURE)) {
return resolveFeatureResourcePermission(relation, featureResources, parentFeatureResources);
}
return resolveFeaturePermission(relation, saasFeatures, parentSaasFeatures);
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(permissions)) {
return null;
}
return ProductPermissionCacheService.ProductPermission.builder()
.productId(e.getKey())
.permissions(permissions)
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class PermissionWrapper {
private List<ProductPermissionCacheService.PermissionDTO> resolveFeaturePermission(SaasProductModuleFeatureRelation relation, Map<Long, SaasFeature> saasFeatures, Map<Long, SaasFeature> parentSaasFeatures) {
SaasFeature saasFeature = saasFeatures.get(relation.getFeatureId());
if (Objects.isNull(saasFeature)) {
return null;
}
/**
* 协同关系类型
* 原saas_product_module_feature_relation.dictCode
*/
private String cooperateType;
List<ProductPermissionCacheService.PermissionDTO> permissionDTOS = Lists.newArrayList(ProductPermissionCacheService.PermissionDTO.builder()
.featureId(saasFeature.getId())
.featureCode(saasFeature.getFeatureCode())
.featureType(saasFeature.getFeatureType())
.terminal(saasFeature.getTerminal())
.cooperateType(relation.getDictCode())
.build());
private Long featureId;
List<ProductPermissionCacheService.PermissionDTO> parentPermissions = saasFeature.splitPath().stream()
.map(parentSaasFeatures::get)
.filter(Objects::nonNull)
.map(f -> ProductPermissionCacheService.PermissionDTO.builder()
.featureId(f.getId())
.featureCode(f.getFeatureCode())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.cooperateType(relation.getDictCode())
.build())
.collect(Collectors.toList());
private String featureCode;
permissionDTOS.addAll(parentPermissions);
return permissionDTOS;
}
private String terminal;
private List<ProductPermissionCacheService.PermissionDTO> resolveFeatureResourcePermission(SaasProductModuleFeatureRelation relation, Map<Long, SaasFeatureResourceResp> featureResources, Map<Long, SaasFeatureResourceResp> parentFeatureResources) {
SaasFeatureResourceResp featureResource = featureResources.get(relation.getFeatureId());
// 菜单节点是不会关联元素code所以缓存的featureCode使用菜单编码
if (Objects.isNull(featureResource)) {
return null;
}
private Integer featureType;
if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) {
return null;
}
private Long productId;
List<ProductPermissionCacheService.PermissionDTO> permissionDTOS = featureResource.getSaasPageElements().stream()
.map(pageElement -> ProductPermissionCacheService.PermissionDTO.builder()
.featureId(featureResource.getId())
.featureCode(pageElement.getCode())
.featureType(featureResource.getFeatureType())
.terminal(featureResource.getTerminal())
.cooperateType(relation.getDictCode())
.itemCode(pageElement.getItemCode())
.version(pageElement.getVersion())
.appType(pageElement.getAppType())
.build())
.collect(Collectors.toList());
List<ProductPermissionCacheService.PermissionDTO> parentPermissions = featureResource.resolvePath().stream()
.map(parentFeatureResources::get)
.filter(Objects::nonNull)
.map(f -> {
if (CollectionUtils.isEmpty(f.getSaasPageElements())) {
return null;
}
return f.getSaasPageElements().stream()
.map(pageElement -> ProductPermissionCacheService.PermissionDTO.builder()
.featureId(f.getId())
.featureCode(pageElement.getCode())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.cooperateType(relation.getDictCode())
.itemCode(pageElement.getItemCode())
.version(pageElement.getVersion())
.appType(pageElement.getAppType())
.build())
.collect(Collectors.toList());
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toList());
permissionDTOS.addAll(parentPermissions);
return permissionDTOS;
}
private Map<Long, SaasFeatureResourceResp> listSaasFeatureResource(List<SaasProductModuleFeatureRelation> productPermissions) {
List<Long> featureIds = productPermissions.stream()
.filter(e -> Objects.equals(e.getType(), NEW_FEATURE))
.map(SaasProductModuleFeatureRelation::getFeatureId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(featureIds)
.needPageElement(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeatureResourceResp> listParentSaasFeatureResource(Map<Long, SaasFeatureResourceResp> productPermissions) {
List<Long> parentIds = productPermissions.values().stream()
.map(SaasFeatureResourceResp::resolvePath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(parentIds)
.needPageElement(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeature> listSaasFeature(List<SaasProductModuleFeatureRelation> productPermissions) {
Set<Long> featureIds = productPermissions.stream()
.filter(e -> Objects.equals(e.getType(), OLD_FEATURE))
.map(SaasProductModuleFeatureRelation::getFeatureId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
return saasFeatureDao.listByIds(featureIds).stream()
.collect(Collectors.toMap(SaasFeature::getId, Function.identity()));
}
private Map<Long, SaasFeature> listParentSaasFeature(Map<Long, SaasFeature> saasFeatures) {
if (CollectionUtils.isEmpty(saasFeatures)) {
return Collections.emptyMap();
}
List<Long> parentIds = saasFeatures.values().stream()
.map(SaasFeature::splitPath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
return saasFeatureDao.listByIds(parentIds).stream()
.collect(Collectors.toMap(SaasFeature::getId, Function.identity()));
}
}

View File

@ -1,38 +1,41 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.foundation.exception.Axssert;
import cn.axzo.pokonyan.config.redis.RedisClient;
import cn.axzo.basics.common.exception.ServiceException;
import cn.axzo.tyr.client.common.enums.FeatureResourceType;
import cn.axzo.tyr.client.model.req.PageProductFeatureRelationReq;
import cn.axzo.tyr.server.event.inner.CacheProductSaasFeatureResourceHandler;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation;
import cn.axzo.tyr.server.service.ProductFeatureRelationService;
import cn.axzo.tyr.server.service.ProductSaasFeatureResourceCacheService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.config.exception.BizResultCode.REDIS_PRODUCT_NOT_NULL;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE;
@Slf4j
@ -40,139 +43,213 @@ import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.
@RefreshScope
public class ProductSaasFeatureResourceCacheServiceImpl implements ProductSaasFeatureResourceCacheService {
private static final String PRODUCT_SAAS_FEATURE_RESOURCE_KEY = "product:feature:resource:%s";
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private ProductFeatureRelationService productFeatureRelationService;
@Autowired
private CacheProductSaasFeatureResourceHandler cacheProductSaasFeatureResourceHandler;
private SaasFeatureResourceService saasFeatureResourceService;
@Value("${product.feature.resouce.expire.minutes:14}")
private Long expireInMinutes;
public static final Set<Integer> FEATURE_RESOURCE_TYPES = Sets.newHashSet(FeatureResourceType.MENU.getCode(),
FeatureResourceType.PAGE.getCode(),
FeatureResourceType.MENU_PARTITION_GROUP.getCode(),
FeatureResourceType.GROUP.getCode(),
FeatureResourceType.APP_ENTRY.getCode());
private LoadingCache<Long, Optional<List<FeatureResourceDTO>>> productFeatureResourceCache = CacheBuilder.newBuilder()
.expireAfterWrite(14, TimeUnit.MINUTES)
.maximumSize(5000)
.build(new CacheLoader<Long, Optional<List<FeatureResourceDTO>>>() {
@Override
public Optional<List<FeatureResourceDTO>> load(Long productId) {
return listProductFeatureResource(Lists.newArrayList(productId))
.values()
.stream()
.findFirst();
}
@Override
public Map<Long, Optional<List<FeatureResourceDTO>>> loadAll(Iterable keys) throws Exception {
List<Long> productIds = Lists.newArrayList(keys);
Map<Long, List<FeatureResourceDTO>> productPermissions = listProductFeatureResource(productIds);
return Maps.toMap(productIds, productId -> Optional.ofNullable(productPermissions.get(productId)));
}
});
@Override
public Map<Long, List<FeatureResourceDTO>> list(ListProductFeatureResourceParam param) {
if (CollectionUtils.isEmpty(param.getProductIds())) {
return Collections.emptyMap();
}
Map<Long, List<FeatureResourceDTO>> featureResourceCached = listProductFeatureResourceCached(param);
Map<Long, List<FeatureResourceDTO>> productFeatureResources;
try {
productFeatureResources = productFeatureResourceCache.getAll(param.getProductIds()).entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().orElseGet(Lists::newArrayList)));
} catch (ExecutionException ex) {
log.error("list product cache featureResource error:{} error", param.getProductIds(), ex);
// 外面有做降级
throw new ServiceException("查询产品菜单缓存异常");
}
fillCacheProductFeatureResource(param, featureResourceCached);
if (CollectionUtils.isEmpty(param.getUniCodes())) {
return productFeatureResources;
}
return featureResourceCached;
}
@Override
public void store(StoreProductFeatureResourceParam param) {
Axssert.check(param.getProductFeatureResources().stream().allMatch(e -> Objects.nonNull(e.getProductId())), REDIS_PRODUCT_NOT_NULL);
redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (ProductFeatureResource productFeatureResource : param.getProductFeatureResources()) {
String redisKey = getKey(productFeatureResource.getProductId());
Map<String, String> redisValues = productFeatureResource.getFeatureResources().stream()
.collect(Collectors.groupingBy(FeatureResourceDTO::getUniCode))
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> JSONObject.toJSONString(e.getValue())));
// 存在hash中部分key移除为了处理快直接把redisKey删除掉修改不频繁
redisTemplate.delete(redisKey);
RedisClient.HashOps.hPutAll(redisKey, redisValues);
redisTemplate.expire(redisKey, expireInMinutes, TimeUnit.MINUTES);
log.info("succeed to store product featureResource: redisKey:{} value:{}", redisKey, redisValues);
}
return null;
}
});
}
private Map<Long, List<FeatureResourceDTO>> listProductFeatureResourceCached(ListProductFeatureResourceParam param) {
List<Object> redisValues = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (Long productId : param.getProductIds()) {
String redisKey = getKey(productId);
if (CollectionUtils.isEmpty(param.getUniCodes())) {
RedisClient.HashOps.hGetAll(redisKey);
} else {
RedisClient.HashOps.hMultiGet(redisKey, Lists.newArrayList(param.getUniCodes()));
}
}
return null;
}
});
return Streams.zip(param.getProductIds().stream(),
redisValues.stream(),
(productId, redisValue) -> {
if (Objects.isNull(redisValue)) {
return null;
}
if (CollectionUtils.isEmpty(param.getUniCodes())) {
List<FeatureResourceDTO> featureResources = (List<FeatureResourceDTO>) ((Map) redisValue).values().stream()
.flatMap(e -> JSONArray.parseArray(JSONArray.toJSONString(e), FeatureResourceDTO.class).stream())
.collect(Collectors.toList());
return Pair.of(productId, featureResources);
}
List<FeatureResourceDTO> featureResources = (List<FeatureResourceDTO>) ((List) redisValue).stream()
.filter(Objects::nonNull)
.flatMap(e -> JSONArray.parseArray((String) e, FeatureResourceDTO.class).stream())
.collect(Collectors.toList());
return Pair.of(productId, featureResources);
})
.filter(Objects::nonNull)
return productFeatureResources.entrySet()
.stream()
.map(e -> Pair.of(e.getKey(), e.getValue().stream()
.filter(permission -> param.getUniCodes().contains(permission.getUniCode()))
.collect(Collectors.toList()))
)
.filter(e -> !CollectionUtils.isEmpty(e.getValue()))
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}
private void fillCacheProductFeatureResource(ListProductFeatureResourceParam param,
Map<Long, List<FeatureResourceDTO>> featureResourceCached) {
Sets.SetView<Long> difference = Sets.difference(param.getProductIds(), featureResourceCached.keySet());
if (difference.isEmpty()) {
@Override
public void refreshCache(RefreshProductFeatureResourceCacheParam param) {
if (CollectionUtils.isEmpty(param.getProductIds())) {
return;
}
Map<Long, List<FeatureResourceDTO>> productPermissions = listProductFeatureResource(Lists.newArrayList(param.getProductIds()));
productFeatureResourceCache.putAll(Maps.toMap(param.getProductIds(), productId -> Optional.ofNullable(productPermissions.get(productId))));
}
private Map<Long, List<FeatureResourceDTO>> listProductFeatureResource(List<Long> productIds) {
if (CollectionUtils.isEmpty(productIds)) {
return Collections.emptyMap();
}
PageProductFeatureRelationReq pageProductFeatureRelationReq = PageProductFeatureRelationReq.builder()
.productModuleIds(difference)
.productModuleIds(Sets.newHashSet(productIds))
.type(NEW_FEATURE)
.featureResourceTypes(Lists.newArrayList(FeatureResourceType.MENU.getCode(),
FeatureResourceType.PAGE.getCode(),
FeatureResourceType.MENU_PARTITION_GROUP.getCode(),
FeatureResourceType.GROUP.getCode(),
FeatureResourceType.APP_ENTRY.getCode()))
.build();
List<SaasProductModuleFeatureRelation> productPermissions = productFeatureRelationService.list(pageProductFeatureRelationReq);
List<ProductFeatureResource> productFeatureResources = cacheProductSaasFeatureResourceHandler.resolveProductFeatureResources(productPermissions);
if (CollectionUtils.isEmpty(productFeatureResources)) return;
StoreProductFeatureResourceParam storeProductFeatureResourceParam = StoreProductFeatureResourceParam.builder()
.productFeatureResources(productFeatureResources)
.build();
store(storeProductFeatureResourceParam);
Map<Long, List<FeatureResourceDTO>> productFeatureResourceMap = productFeatureResources.stream()
return resolveProductFeatureResources(productPermissions).stream()
.collect(Collectors.toMap(ProductFeatureResource::getProductId, ProductFeatureResource::getFeatureResources));
featureResourceCached.putAll(productFeatureResourceMap);
}
private String getKey(Object... params) {
return String.format(PRODUCT_SAAS_FEATURE_RESOURCE_KEY, params);
private List<ProductSaasFeatureResourceCacheService.ProductFeatureResource> resolveProductFeatureResources(List<SaasProductModuleFeatureRelation> productPermissions) {
if (CollectionUtils.isEmpty(productPermissions)) {
return Collections.emptyList();
}
Map<Long, SaasFeatureResourceResp> featureResources = listSaasFeatureResource(productPermissions);
Map<Long, SaasFeatureResourceResp> parentFeatureResources = listParentSaasFeatureResource(featureResources);
return productPermissions.stream()
.collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId))
.entrySet()
.stream()
.map(e -> {
List<SaasProductModuleFeatureRelation> productFeatureRelations = e.getValue();
if (CollectionUtils.isEmpty(productFeatureRelations)) {
return null;
}
List<ProductSaasFeatureResourceCacheService.FeatureResourceDTO> productFeatureResources = productFeatureRelations.stream()
.map(relation -> {
SaasFeatureResourceResp featureResource = featureResources.get(relation.getFeatureId());
if (Objects.isNull(featureResource) || StringUtils.isBlank(featureResource.getUniCode())) {
return null;
}
ProductSaasFeatureResourceCacheService.FeatureResourceDTO featureResourceDTO = from(featureResource, relation);
List<ProductSaasFeatureResourceCacheService.FeatureResourceDTO> featureResourceDTOS = Lists.newArrayList(featureResourceDTO);
List<ProductSaasFeatureResourceCacheService.FeatureResourceDTO> parentPermissions = featureResource.resolvePath().stream()
.map(parentFeatureResources::get)
.filter(Objects::nonNull)
.map(f -> {
if (StringUtils.isBlank(f.getUniCode())) {
return null;
}
return from(featureResource, relation);
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
featureResourceDTOS.addAll(parentPermissions);
return featureResourceDTOS;
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.filter(f -> FEATURE_RESOURCE_TYPES.contains(f.getFeatureType()))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(productFeatureResources)) {
return null;
}
return ProductSaasFeatureResourceCacheService.ProductFeatureResource.builder()
.productId(e.getKey())
.featureResources(productFeatureResources)
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private ProductSaasFeatureResourceCacheService.FeatureResourceDTO from(SaasFeatureResourceResp featureResource,
SaasProductModuleFeatureRelation relation) {
return ProductSaasFeatureResourceCacheService.FeatureResourceDTO.builder()
.featureId(featureResource.getId())
.featureType(featureResource.getFeatureType())
.terminal(featureResource.getTerminal())
.uniCode(featureResource.getUniCode())
.cooperateType(relation.getDictCode())
.build();
}
private Map<Long, SaasFeatureResourceResp> listSaasFeatureResource(List<SaasProductModuleFeatureRelation> productPermissions) {
List<Long> featureIds = productPermissions.stream()
.map(SaasProductModuleFeatureRelation::getFeatureId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(featureIds)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeatureResourceResp> listParentSaasFeatureResource(Map<Long, SaasFeatureResourceResp> productPermissions) {
List<Long> parentIds = productPermissions.values().stream()
.map(SaasFeatureResourceResp::resolvePath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(parentIds)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
}

View File

@ -1,207 +1,338 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.foundation.exception.Axssert;
import cn.axzo.pokonyan.config.redis.RedisClient;
import cn.axzo.basics.common.exception.ServiceException;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes;
import cn.axzo.tyr.client.model.res.SaasRoleRes;
import cn.axzo.tyr.server.event.inner.CacheRolePermissionHandler;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.entity.SaasFeature;
import cn.axzo.tyr.server.service.RolePermissionCacheService;
import cn.axzo.tyr.server.service.RoleService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.config.exception.BizResultCode.REDIS_ROLE_NOT_NULL;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.OLD_FEATURE;
@Slf4j
@Service
@RefreshScope
public class RolePermissionCacheServiceImpl implements RolePermissionCacheService {
private static final String ROLE_PERMISSION_KEY = "role:permission:%s";
@Autowired
protected StringRedisTemplate redisTemplate;
@Autowired
private RoleService roleService;
@Autowired
private CacheRolePermissionHandler cacheRolePermissionHandler;
private SaasFeatureResourceService saasFeatureResourceService;
@Autowired
private SaasFeatureDao saasFeatureDao;
/** 角色权限缓存过期时间 **/
@Value("${role.permission.expire.minutes:14}")
private Long expireInMinutes;
private static Long expireInMinutes;
private LoadingCache<Long, Optional<List<PermissionDTO>>> rolePermissionCache = CacheBuilder.newBuilder()
.expireAfterWrite(14, TimeUnit.MINUTES)
.maximumSize(5000)
.build(new CacheLoader<Long, Optional<List<PermissionDTO>>>() {
@Override
public Optional<List<PermissionDTO>> load(Long roleId) {
return listRolePermission(Lists.newArrayList(roleId))
.values()
.stream()
.findFirst();
}
@Override
public Map<Long, Optional<List<PermissionDTO>>> loadAll(Iterable keys) throws Exception {
List<Long> roleIds = Lists.newArrayList(keys);
Map<Long, List<PermissionDTO>> rolePermissions = listRolePermission(roleIds);
return Maps.toMap(roleIds, roleId -> Optional.ofNullable(rolePermissions.get(roleId)));
}
});
@Override
public Map<Long, List<PermissionDTO>> list(ListRolePermissionParam param) {
if (CollectionUtils.isEmpty(param.getRoleIds())) {
return Collections.emptyMap();
}
Map<Long, List<PermissionDTO>> rolePermissions;
try {
rolePermissions = rolePermissionCache.getAll(param.getRoleIds()).entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().orElseGet(Lists::newArrayList)));
} catch (ExecutionException ex) {
log.error("list role cache permission error:{} error", param.getRoleIds(), ex);
// 外面有做降级
throw new ServiceException("查询角色权限缓存异常");
}
Map<Long, List<PermissionDTO>> permissions = listRolePermissionCached(param);
if (CollectionUtils.isEmpty(param.getFeatureCodes())) {
return rolePermissions;
}
fillCacheRolePermissions(param, permissions);
return permissions;
}
private Map<Long, List<PermissionDTO>> listRolePermissionCached(ListRolePermissionParam param) {
List<Object> redisValues = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (Long roleId : param.getRoleIds()) {
String redisKey = getKey(roleId);
if (CollectionUtils.isEmpty(param.getFeatureCodes())) {
RedisClient.HashOps.hGetAll(redisKey);
} else {
RedisClient.HashOps.hMultiGet(redisKey, Lists.newArrayList(param.getFeatureCodes()));
}
}
return null;
}
});
return Streams.zip(param.getRoleIds().stream(),
redisValues.stream(),
(roleId, redisValue) -> {
if (Objects.isNull(redisValue)) {
return null;
}
if (CollectionUtils.isEmpty(param.getFeatureCodes())) {
List<PermissionDTO> permissions = (List<PermissionDTO>) ((Map) redisValue).values().stream()
.flatMap(e -> JSONArray.parseArray(JSONArray.toJSONString(e), PermissionDTO.class).stream())
.collect(Collectors.toList());
return Pair.of(roleId, permissions);
}
List<PermissionDTO> permissions = (List<PermissionDTO>) ((List) redisValue).stream()
.filter(Objects::nonNull)
.flatMap(e -> JSONArray.parseArray((String) e, PermissionDTO.class).stream())
.collect(Collectors.toList());
return Pair.of(roleId, permissions);
})
.filter(Objects::nonNull)
return rolePermissions.entrySet()
.stream()
.map(e -> Pair.of(e.getKey(), e.getValue().stream()
.filter(permission -> param.getFeatureCodes().contains(permission.getFeatureCode()))
.collect(Collectors.toList()))
)
.filter(e -> !CollectionUtils.isEmpty(e.getValue()))
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}
@Override
public void store(StoreRolePermissionParam param) {
Axssert.check(param.getRolePermissions().stream().allMatch(e -> Objects.nonNull(e.getRoleId())), REDIS_ROLE_NOT_NULL);
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
connection.openPipeline();
for (RolePermission rolePermission : param.getRolePermissions()) {
String redisKey = getKey(rolePermission.getRoleId());
Map<String, String> redisValues = rolePermission.getPermissions().stream()
.collect(Collectors.groupingBy(PermissionDTO::getFeatureCode))
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> JSONObject.toJSONString(e.getValue())));
// 存在hash中部分key移除为了处理快直接把redisKey删除掉修改不频繁
redisTemplate.delete(redisKey);
RedisClient.HashOps.hPutAll(redisKey, redisValues);
redisTemplate.expire(redisKey, expireInMinutes, TimeUnit.MINUTES);
log.info("succeed to store role permission: redisKey:{} value:{}", redisKey, redisValues);
}
return null;
});
}
@Override
public List<Long> hasRoleIds(HasRolePermissionParam param) {
List<Object> redisValues = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (Long roleId : param.getRoleIds()) {
String redisKey = getKey(roleId);
operations.hasKey(redisKey);
}
return null;
}
});
return Streams.zip(param.getRoleIds().stream(),
redisValues.stream(),
(roleId, redisValue) -> Pair.of(roleId, redisValue))
.filter(e -> BooleanUtils.isTrue((Boolean) e.getValue()))
.map(Pair::getKey)
.collect(Collectors.toList());
}
/**
* 组装在缓存中没有查询到权限的角色权限
* @param param
* @param permissionCached
*/
private void fillCacheRolePermissions(ListRolePermissionParam param,
Map<Long, List<PermissionDTO>> permissionCached) {
Sets.SetView<Long> difference = Sets.difference(param.getRoleIds(), permissionCached.keySet());
if (difference.isEmpty()) {
public void refreshCache(RefreshRolePermissionCacheParam param) {
if (CollectionUtils.isEmpty(param.getRoleIds())) {
return;
}
Map<Long, List<PermissionDTO>> rolePermissions = listRolePermission(Lists.newArrayList(param.getRoleIds()));
rolePermissionCache.putAll(Maps.toMap(param.getRoleIds(), roleId -> Optional.ofNullable(rolePermissions.get(roleId))));
}
private Map<Long, List<PermissionDTO>> listRolePermission(List<Long> roleIds) {
RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder()
.roleIds(Lists.newArrayList(difference))
.roleIds(roleIds)
.needPermissionRelation(true)
.build();
List<SaasRoleRes> roles = roleService.list(listSaasRoleParam);
if (CollectionUtils.isEmpty(roles)) {
return;
return Collections.emptyMap();
}
List<RolePermission> rolePermissions = cacheRolePermissionHandler.resolveRolePermission(roles);
if (CollectionUtils.isEmpty(rolePermissions)) {
return;
}
StoreRolePermissionParam storeRolePermissionParam = StoreRolePermissionParam.builder()
.rolePermissions(rolePermissions)
.build();
store(storeRolePermissionParam);
Map<Long, List<PermissionDTO>> rolePermissionMap = rolePermissions.stream()
.collect(Collectors.toMap(RolePermission::getRoleId, RolePermission::getPermissions));
permissionCached.putAll(rolePermissionMap);
return resolveRolePermission(roles).stream()
.collect(Collectors.toMap(RolePermissionCacheService.RolePermission::getRoleId,
RolePermissionCacheService.RolePermission::getPermissions));
}
private String getKey(Object... params) {
return String.format(ROLE_PERMISSION_KEY, params);
private List<RolePermissionCacheService.RolePermission> resolveRolePermission(List<SaasRoleRes> roles) {
Map<Long, SaasFeatureResourceResp> featureResources = listSaasFeatureResource(roles);
Map<Long, SaasFeatureResourceResp> parentFeatureResources = listParentSaasFeatureResource(featureResources);
Map<Long, SaasFeature> saasFeatures = listSaasFeature(roles);
Map<Long, SaasFeature> parentSaasFeatures = listParentSaasFeature(saasFeatures);
return roles.stream()
.map(e -> {
if (CollectionUtils.isEmpty(e.getPermissionRelations())) {
return null;
}
List<RolePermissionCacheService.PermissionDTO> permissions = e.getPermissionRelations().stream()
.distinct()
.map(permissionRelation -> {
if (Objects.equals(permissionRelation.getType(), NEW_FEATURE)) {
return resolveFeatureResourcePermission(permissionRelation, featureResources, parentFeatureResources);
}
return resolveFeaturePermission(permissionRelation, saasFeatures, parentSaasFeatures);
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(permissions)) {
return null;
}
return RolePermissionCacheService.RolePermission.builder()
.roleId(e.getId())
.permissions(permissions)
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private static List<RolePermissionCacheService.PermissionDTO> resolveFeaturePermission(SaasPermissionRelationRes permissionRelation, Map<Long, SaasFeature> saasFeatures, Map<Long, SaasFeature> parentSaasFeatures) {
SaasFeature saasFeature = saasFeatures.get(permissionRelation.getFeatureId());
if (Objects.isNull(saasFeature)) {
return null;
}
List<RolePermissionCacheService.PermissionDTO> permissionDTOS = Lists.newArrayList(RolePermissionCacheService.PermissionDTO.builder()
.featureId(saasFeature.getId())
.featureCode(saasFeature.getFeatureCode())
.featureType(saasFeature.getFeatureType())
.terminal(saasFeature.getTerminal())
.build());
List<RolePermissionCacheService.PermissionDTO> parentPermissions = saasFeature.splitPath().stream()
.map(parentSaasFeatures::get)
.filter(Objects::nonNull)
.map(f -> RolePermissionCacheService.PermissionDTO.builder()
.featureId(f.getId())
.featureCode(f.getFeatureCode())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.build())
.collect(Collectors.toList());
permissionDTOS.addAll(parentPermissions);
return permissionDTOS;
}
private static List<RolePermissionCacheService.PermissionDTO> resolveFeatureResourcePermission(SaasPermissionRelationRes permissionRelation, Map<Long, SaasFeatureResourceResp> featureResources, Map<Long, SaasFeatureResourceResp> parentFeatureResources) {
SaasFeatureResourceResp featureResource = featureResources.get(permissionRelation.getFeatureId());
// 菜单节点是不会关联元素code所以缓存的featureCode使用菜单编码
if (Objects.isNull(featureResource)) {
return null;
}
if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) {
return null;
}
List<RolePermissionCacheService.PermissionDTO> permissionDTOS = featureResource.getSaasPageElements().stream()
.map(pageElement -> RolePermissionCacheService.PermissionDTO.builder()
.featureId(featureResource.getId())
.featureCode(pageElement.getCode())
.featureType(featureResource.getFeatureType())
.terminal(featureResource.getTerminal())
.build())
.collect(Collectors.toList());
List<RolePermissionCacheService.PermissionDTO> parentPermissions = featureResource.resolvePath().stream()
.map(parentFeatureResources::get)
.filter(Objects::nonNull)
.map(f -> {
if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) {
return null;
}
return featureResource.getSaasPageElements().stream()
.map(pageElement -> RolePermissionCacheService.PermissionDTO.builder()
.featureId(f.getId())
.featureCode(pageElement.getCode())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.build())
.collect(Collectors.toList());
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toList());
permissionDTOS.addAll(parentPermissions);
return permissionDTOS;
}
private Map<Long, SaasFeatureResourceResp> listSaasFeatureResource(List<SaasRoleRes> roles) {
List<Long> featureIds = roles.stream()
.filter(e -> !CollectionUtils.isEmpty(e.getPermissionRelations()))
.map(SaasRoleRes::getPermissionRelations)
.flatMap(Collection::stream)
.filter(e -> Objects.equals(e.getType(), NEW_FEATURE))
.map(SaasPermissionRelationRes::getFeatureId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(featureIds)
.needPageElement(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeatureResourceResp> listParentSaasFeatureResource(Map<Long, SaasFeatureResourceResp> productPermissions) {
List<Long> parentIds = productPermissions.values().stream()
.map(SaasFeatureResourceResp::resolvePath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(parentIds)
.needPageElement(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeature> listSaasFeature(List<SaasRoleRes> roles) {
Set<Long> featureIds = roles.stream()
.filter(e -> !CollectionUtils.isEmpty(e.getPermissionRelations()))
.map(SaasRoleRes::getPermissionRelations)
.flatMap(Collection::stream)
.filter(e -> Objects.equals(e.getType(), OLD_FEATURE))
.map(SaasPermissionRelationRes::getFeatureId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
return saasFeatureDao.listByIds(featureIds)
.stream()
.collect(Collectors.toMap(SaasFeature::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeature> listParentSaasFeature(Map<Long, SaasFeature> saasFeatures) {
if (CollectionUtils.isEmpty(saasFeatures)) {
return Collections.emptyMap();
}
List<Long> parentIds = saasFeatures.values().stream()
.map(SaasFeature::splitPath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
return saasFeatureDao.listByIds(parentIds).stream()
.collect(Collectors.toMap(SaasFeature::getId, Function.identity()));
}
}

View File

@ -1,153 +1,127 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.foundation.exception.Axssert;
import cn.axzo.pokonyan.config.redis.RedisClient;
import cn.axzo.basics.common.exception.ServiceException;
import cn.axzo.tyr.client.common.enums.FeatureResourceType;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes;
import cn.axzo.tyr.client.model.res.SaasRoleRes;
import cn.axzo.tyr.server.event.inner.CacheRoleSaasFeatureResourceHandler;
import cn.axzo.tyr.server.service.RoleSaasFeatureResourceCacheService;
import cn.axzo.tyr.server.service.RoleService;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.config.exception.BizResultCode.REDIS_ROLE_NOT_NULL;
import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE;
import static cn.axzo.tyr.server.service.impl.ProductSaasFeatureResourceCacheServiceImpl.FEATURE_RESOURCE_TYPES;
@Slf4j
@Service
@RefreshScope
public class RoleSaasFeatureResourceCacheServiceImpl implements RoleSaasFeatureResourceCacheService {
private static final String ROLE_SAAS_FEATURE_RESOURCE_KEY = "role:feature:resource:%s";
@Autowired
protected StringRedisTemplate redisTemplate;
@Autowired
private RoleService roleService;
@Autowired
private CacheRoleSaasFeatureResourceHandler cacheRoleSaasFeatureResourceHandler;
private SaasFeatureResourceService saasFeatureResourceService;
/** 角色菜单缓存过期时间 **/
@Value("${role.feature.resource.expire.minutes:14}")
private Long expireInMinutes;
private LoadingCache<Long, Optional<List<SaasFeatureResourceDTO>>> roleFeatureResourceCache = CacheBuilder.newBuilder()
.expireAfterWrite(14, TimeUnit.MINUTES)
.maximumSize(5000)
.build(new CacheLoader<Long, Optional<List<SaasFeatureResourceDTO>>>() {
@Override
public Optional<List<SaasFeatureResourceDTO>> load(Long roleId) {
return listRoleFeatureResource(Lists.newArrayList(roleId))
.values()
.stream()
.findFirst();
}
@Override
public Map<Long, Optional<List<SaasFeatureResourceDTO>>> loadAll(Iterable keys) throws Exception {
List<Long> roleIds = Lists.newArrayList(keys);
Map<Long, List<SaasFeatureResourceDTO>> roleFeatureResources = listRoleFeatureResource(roleIds);
return Maps.toMap(roleIds, roleId -> Optional.ofNullable(roleFeatureResources.get(roleId)));
}
});
@Override
public Map<Long, List<SaasFeatureResourceDTO>> list(ListRoleSaasFeatureResourceParam param) {
if (CollectionUtils.isEmpty(param.getRoleIds())) {
return Collections.emptyMap();
}
Map<Long, List<SaasFeatureResourceDTO>> featureResources = listRoleSaasFeatureResourceCached(param);
Map<Long, List<SaasFeatureResourceDTO>> roleFeatureResources;
try {
roleFeatureResources = roleFeatureResourceCache.getAll(param.getRoleIds()).entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().orElseGet(Lists::newArrayList)));
} catch (ExecutionException ex) {
log.error("list role cache featureResource error:{} error", param.getRoleIds(), ex);
// 外面有做降级
throw new ServiceException("查询角色菜单缓存异常");
}
fillCacheRoleFeatureResources(param, featureResources);
return featureResources;
}
if (CollectionUtils.isEmpty(param.getUniCodes())) {
return roleFeatureResources;
}
@Override
public void store(StoreRoleSaasFeatureResourceParam param) {
Axssert.check(param.getRoleSaasFeatureResources().stream().allMatch(e -> Objects.nonNull(e.getRoleId())), REDIS_ROLE_NOT_NULL);
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
connection.openPipeline();
for (RoleFeatureResource roleFeatureResource : param.getRoleSaasFeatureResources()) {
String redisKey = getKey(roleFeatureResource.getRoleId());
Map<String, String> redisValues = roleFeatureResource.getSaasFeatureResources().stream()
.collect(Collectors.groupingBy(SaasFeatureResourceDTO::getUniCode))
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> JSONObject.toJSONString(e.getValue())));
// 存在hash中部分key移除为了处理快直接把redisKey删除掉修改不频繁
redisTemplate.delete(redisKey);
RedisClient.HashOps.hPutAll(redisKey, redisValues);
redisTemplate.expire(redisKey, expireInMinutes, TimeUnit.MINUTES);
log.info("succeed to store role featureResource: redisKey:{} value:{}", redisKey, redisValues);
}
return null;
});
}
private Map<Long, List<SaasFeatureResourceDTO>> listRoleSaasFeatureResourceCached(ListRoleSaasFeatureResourceParam param) {
List<Object> redisValues = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (Long roleId : param.getRoleIds()) {
String redisKey = getKey(roleId);
if (CollectionUtils.isEmpty(param.getUniCodes())) {
RedisClient.HashOps.hGetAll(redisKey);
} else {
RedisClient.HashOps.hMultiGet(redisKey, Lists.newArrayList(param.getUniCodes()));
}
}
return null;
}
});
return Streams.zip(param.getRoleIds().stream(),
redisValues.stream(),
(roleId, redisValue) -> {
if (Objects.isNull(redisValue)) {
return null;
}
if (CollectionUtils.isEmpty(param.getUniCodes())) {
List<SaasFeatureResourceDTO> featureResources = (List<SaasFeatureResourceDTO>) ((Map) redisValue).values().stream()
.flatMap(e -> JSONArray.parseArray(JSONArray.toJSONString(e), SaasFeatureResourceDTO.class).stream())
.collect(Collectors.toList());
return Pair.of(roleId, featureResources);
}
List<SaasFeatureResourceDTO> featureResources = (List<SaasFeatureResourceDTO>) ((List) redisValue).stream()
.filter(Objects::nonNull)
.flatMap(e -> JSONArray.parseArray((String) e, SaasFeatureResourceDTO.class).stream())
.collect(Collectors.toList());
return Pair.of(roleId, featureResources);
})
.filter(Objects::nonNull)
return roleFeatureResources.entrySet()
.stream()
.map(e -> Pair.of(e.getKey(), e.getValue().stream()
.filter(permission -> param.getUniCodes().contains(permission.getUniCode()))
.collect(Collectors.toList()))
)
.filter(e -> !CollectionUtils.isEmpty(e.getValue()))
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}
private void fillCacheRoleFeatureResources(ListRoleSaasFeatureResourceParam param,
Map<Long, List<SaasFeatureResourceDTO>> featureResources) {
Sets.SetView<Long> difference = Sets.difference(param.getRoleIds(), featureResources.keySet());
if (difference.isEmpty()) {
@Override
public void refreshCache(RefreshRoleFeatureResourceCacheParam param) {
if (CollectionUtils.isEmpty(param.getRoleIds())) {
return;
}
Map<Long, List<SaasFeatureResourceDTO>> roleFeatureResources = listRoleFeatureResource(Lists.newArrayList(param.getRoleIds()));
roleFeatureResourceCache.putAll(Maps.toMap(param.getRoleIds(), roleId -> Optional.ofNullable(roleFeatureResources.get(roleId))));
}
private Map<Long, List<SaasFeatureResourceDTO>> listRoleFeatureResource(List<Long> roleIds) {
if (CollectionUtils.isEmpty(roleIds)) {
return Collections.emptyMap();
}
RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder()
.roleIds(Lists.newArrayList(difference))
.roleIds(roleIds)
.needPermissionRelation(true)
.type(NEW_FEATURE)
.featureResourceTypes(Lists.newArrayList(FeatureResourceType.MENU,
@ -159,26 +133,122 @@ public class RoleSaasFeatureResourceCacheServiceImpl implements RoleSaasFeatureR
List<SaasRoleRes> roles = roleService.list(listSaasRoleParam);
if (CollectionUtils.isEmpty(roles)) {
return;
return Collections.emptyMap();
}
List<RoleSaasFeatureResourceCacheService.RoleFeatureResource> roleFeatureResources = cacheRoleSaasFeatureResourceHandler.resolveRoleFeatureResource(roles);
if (CollectionUtils.isEmpty(roleFeatureResources)) {
return;
}
StoreRoleSaasFeatureResourceParam storeRolePermissionParam = StoreRoleSaasFeatureResourceParam.builder()
.roleSaasFeatureResources(roleFeatureResources)
.build();
store(storeRolePermissionParam);
Map<Long, List<SaasFeatureResourceDTO>> rolePermissionMap = roleFeatureResources.stream()
return resolveRoleFeatureResource(roles).stream()
.collect(Collectors.toMap(RoleSaasFeatureResourceCacheService.RoleFeatureResource::getRoleId, RoleSaasFeatureResourceCacheService.RoleFeatureResource::getSaasFeatureResources));
featureResources.putAll(rolePermissionMap);
}
private String getKey(Object... params) {
return String.format(ROLE_SAAS_FEATURE_RESOURCE_KEY, params);
private List<RoleSaasFeatureResourceCacheService.RoleFeatureResource> resolveRoleFeatureResource(List<SaasRoleRes> roles) {
Map<Long, SaasFeatureResourceResp> featureResources = listSaasFeatureResource(roles);
Map<Long, SaasFeatureResourceResp> parentFeatureResources = listParentSaasFeatureResource(featureResources);
return roles.stream()
.map(e -> {
if (CollectionUtils.isEmpty(e.getPermissionRelations())) {
return null;
}
List<RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO> permissions = e.getPermissionRelations().stream()
.distinct()
.map(permissionRelation -> {
SaasFeatureResourceResp featureResource = featureResources.get(permissionRelation.getFeatureId());
if (Objects.isNull(featureResource) || StringUtils.isBlank(featureResource.getUniCode())) {
return null;
}
List<RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO> featureResourceDTOS = Lists.newArrayList(RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO.builder()
.featureId(featureResource.getId())
.featureType(featureResource.getFeatureType())
.terminal(featureResource.getTerminal())
.uniCode(featureResource.getUniCode())
.build());
List<RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO> parentPermissions = featureResource.resolvePath().stream()
.map(parentFeatureResources::get)
.filter(Objects::nonNull)
.map(f -> {
if (StringUtils.isBlank(f.getUniCode())) {
return null;
}
return RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO.builder()
.featureId(f.getId())
.featureType(f.getFeatureType())
.terminal(f.getTerminal())
.uniCode(f.getUniCode())
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
featureResourceDTOS.addAll(parentPermissions);
return featureResourceDTOS;
})
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.filter(f -> FEATURE_RESOURCE_TYPES.contains(f.getFeatureType()))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(permissions)) {
return null;
}
return RoleSaasFeatureResourceCacheService.RoleFeatureResource.builder()
.roleId(e.getId())
.saasFeatureResources(permissions)
.build();
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private Map<Long, SaasFeatureResourceResp> listSaasFeatureResource(List<SaasRoleRes> roles) {
List<Long> featureIds = roles.stream()
.filter(e -> !CollectionUtils.isEmpty(e.getPermissionRelations()))
.map(SaasRoleRes::getPermissionRelations)
.flatMap(Collection::stream)
.map(SaasPermissionRelationRes::getFeatureId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(featureIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(featureIds)
.needFeatureCodes(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
private Map<Long, SaasFeatureResourceResp> listParentSaasFeatureResource(Map<Long, SaasFeatureResourceResp> productPermissions) {
List<Long> parentIds = productPermissions.values().stream()
.map(SaasFeatureResourceResp::resolvePath)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(parentIds)) {
return Collections.emptyMap();
}
// 存在pre环境更改了节点的父节点可能导致产品没有父节点的权限这里补齐父节点的权限
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.ids(parentIds)
.needFeatureCodes(true)
.build();
return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.collect(Collectors.toMap(SaasFeatureResourceResp::getId, Function.identity(), (f, s) -> s));
}
}

View File

@ -1,6 +1,7 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.basics.common.exception.ServiceException;
import cn.axzo.basics.common.util.StopWatchUtil;
import cn.axzo.basics.common.util.TreeUtil;
import cn.axzo.foundation.dao.support.converter.PageConverter;
@ -10,7 +11,6 @@ import cn.axzo.foundation.page.PageResp;
import cn.axzo.framework.domain.web.code.BaseCode;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.pokonyan.config.mybatisplus.BaseEntity;
import cn.axzo.pokonyan.config.redis.RedisClient;
import cn.axzo.tyr.client.common.enums.FeatureResourceAuthType;
import cn.axzo.tyr.client.common.enums.FeatureResourceStatus;
import cn.axzo.tyr.client.common.enums.FeatureResourceType;
@ -50,29 +50,24 @@ import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService;
import cn.azxo.framework.common.utils.StringUtils;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -82,7 +77,9 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -117,16 +114,35 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl<SaasFeatureResou
private final MqProducer mqProducer;
private final SaasFeatureDao saasFeatureDao;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/** 菜单树过期时间 **/
@Value("${saas.feature.resource.expire.minutes:14}")
private Long expireInMinutes;
private static final String SAAS_FEATURE_RESOURCE_KEY = "saas:feature:resource:%s";
private static final String TARGET_TYPE = "saasFeatureResourceId";
private LoadingCache<String, Optional<List<SaasFeatureResourceCache>>> featureResourceCache = CacheBuilder.newBuilder()
.expireAfterWrite(14, TimeUnit.MINUTES)
.maximumSize(5000)
.build(new CacheLoader<String, Optional<List<SaasFeatureResourceCache>>>() {
@Override
public Optional<List<SaasFeatureResourceCache>> load(String terminal) {
return listFeatureSources(Lists.newArrayList(terminal))
.values()
.stream()
.findFirst();
}
@Override
public Map<String, Optional<List<SaasFeatureResourceCache>>> loadAll(Iterable keys) throws Exception {
List<String> terminals = Lists.newArrayList(keys);
Map<String, List<SaasFeatureResourceCache>> featureResources = listFeatureSources(terminals);
return Maps.toMap(terminals, terminal -> Optional.ofNullable(featureResources.get(terminal)));
}
});
@Override
public List<SaasFeatureResource> listNavByIds(List<Long> featureIds, List<Integer> featureTypes) {
//按需扩展要查询的字段
@ -791,85 +807,44 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl<SaasFeatureResou
});
}
@Override
public void storeCache(StoreSaasFeatureResourceCache param) {
redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (SaasFeatureResourceDTO saasFeatureResource : param.getSaasFeatureResources()) {
String redisKey = getKey(saasFeatureResource.getTerminal());
String redisValue = JSONObject.toJSONString(saasFeatureResource.getFeatures());
RedisClient.StringOps.setEx(redisKey, redisValue,
expireInMinutes, TimeUnit.MINUTES);
log.info("succeed to store featureResource: redisKey:{} value:{}", redisKey, redisValue);
}
return null;
}
});
}
@Override
public Map<String, List<SaasFeatureResourceCache>> listCache(ListSaasFeatureResourceCache param) {
if (org.springframework.util.CollectionUtils.isEmpty(param.getTerminals())) {
return Collections.emptyMap();
}
Map<String, List<SaasFeatureResourceCache>> featureSourceCached = listFeatureSourceCached(param);
fillCacheFeatureSources(param, featureSourceCached);
return featureSourceCached;
Map<String, List<SaasFeatureResourceCache>> terminalFeatureResources;
try {
terminalFeatureResources = featureResourceCache.getAll(param.getTerminals()).entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().orElseGet(Lists::newArrayList)));
} catch (ExecutionException ex) {
log.error("list terminal cache featureResource error:{} error", param.getTerminals(), ex);
// 外面有做降级
throw new ServiceException("查询端的菜单缓存异常");
}
return terminalFeatureResources;
}
private Map<String, List<SaasFeatureResourceCache>> listFeatureSourceCached(ListSaasFeatureResourceCache param) {
List<Object> redisValues = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (String terminal : param.getTerminals()) {
String redisKey = getKey(terminal);
RedisClient.StringOps.get(redisKey);
}
return null;
}
});
return Streams.zip(param.getTerminals().stream(),
redisValues.stream(),
(terminal, redisValue) -> {
if (Objects.isNull(redisValue)) {
return null;
}
return Pair.of(terminal, JSONArray.parseArray((String) redisValue,
SaasFeatureResourceCache.class));
})
.filter(Objects::nonNull)
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}
private void fillCacheFeatureSources(ListSaasFeatureResourceCache param,
Map<String, List<SaasFeatureResourceCache>> featureSourceCached) {
Sets.SetView<String> difference = Sets.difference(param.getTerminals(), featureSourceCached.keySet());
if (difference.isEmpty()) {
@Override
public void refreshCache(RefreshFeatureResourceCacheParam param) {
if (org.springframework.util.CollectionUtils.isEmpty(param.getTerminals())) {
return;
}
List<SaasFeatureResourceDTO> saasFeatureResources = resolveSaasFeature(difference);
Map<String, List<SaasFeatureResourceCache>> featureResources = listFeatureSources(Lists.newArrayList(param.getTerminals()));
SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder()
.saasFeatureResources(saasFeatureResources)
.build();
storeCache(storeSaasFeatureResourceCache);
featureResourceCache.putAll(Maps.toMap(param.getTerminals(), terminal -> Optional.ofNullable(featureResources.get(terminal))));
}
Map<String, List<SaasFeatureResourceCache>> featureSourceMap = saasFeatureResources.stream()
private Map<String, List<SaasFeatureResourceCache>> listFeatureSources(List<String> terminals) {
if (CollectionUtils.isEmpty(terminals)) {
return Collections.emptyMap();
}
return resolveSaasFeature(Sets.newHashSet(terminals)).stream()
.collect(Collectors.toMap(SaasFeatureResourceDTO::getTerminal, SaasFeatureResourceDTO::getFeatures));
featureSourceCached.putAll(featureSourceMap);
}
private List<SaasFeatureResourceDTO> resolveSaasFeature(Set<String> terminals) {
@ -915,8 +890,4 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl<SaasFeatureResou
allFeatureResources.addAll(saasFeatureResources);
return allFeatureResources;
}
private String getKey(Object... params) {
return String.format(SAAS_FEATURE_RESOURCE_KEY, params);
}
}