From a033919e17d32b902ff9ec7471ae73d43442ed46 Mon Sep 17 00:00:00 2001 From: lilong Date: Wed, 11 Sep 2024 15:12:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:(hotfix/20240906)=20=E6=8A=8A=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E3=80=81=E8=A7=92=E8=89=B2=E8=8F=9C=E5=8D=95=E3=80=81?= =?UTF-8?q?=E4=BA=A7=E5=93=81=E8=8F=9C=E5=8D=95=E3=80=81=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E6=9D=83=E9=99=90=E3=80=81=E8=A7=92=E8=89=B2=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E4=BB=8Eredis=E7=BC=93=E5=AD=98=E5=88=87=E6=8D=A2=E5=88=B0?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=86=85=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/RocketMQEventConfiguration.java | 7 +- .../server/controller/PrivateController.java | 146 +++--- .../inner/CacheProductPermissionHandler.java | 284 +----------- ...acheProductSaasFeatureResourceHandler.java | 194 +------- .../inner/CacheRolePermissionHandler.java | 272 +---------- .../CacheRoleSaasFeatureResourceHandler.java | 174 +------ .../event/inner/CacheSaasFeatureHandler.java | 9 +- .../CacheSaasFeatureResourceHandler.java | 9 +- .../job/CacheProductFeatureResourceJob.java | 19 +- .../server/job/CacheProductPermissionJob.java | 20 +- .../job/CacheRoleFeatureResourceJob.java | 106 +---- .../server/job/CacheRolePermissionJob.java | 103 +--- .../tyr/server/job/CacheSaasFeatureJob.java | 74 +-- .../ProductPermissionCacheService.java | 19 +- ...roductSaasFeatureResourceCacheService.java | 10 +- .../service/RolePermissionCacheService.java | 21 +- .../RoleSaasFeatureResourceCacheService.java | 9 +- .../service/SaasFeatureResourceService.java | 12 +- .../ProductPermissionCacheServiceImpl.java | 438 +++++++++++------- ...ctSaasFeatureResourceCacheServiceImpl.java | 311 ++++++++----- .../impl/RolePermissionCacheServiceImpl.java | 425 +++++++++++------ ...leSaasFeatureResourceCacheServiceImpl.java | 298 +++++++----- .../impl/SaasFeatureResourceServiceImpl.java | 137 +++--- 23 files changed, 1260 insertions(+), 1837 deletions(-) diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/RocketMQEventConfiguration.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/RocketMQEventConfiguration.java index 86ff70f1..f418db02 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/config/RocketMQEventConfiguration.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/RocketMQEventConfiguration.java @@ -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 { @@ -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 { @@ -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 { diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/PrivateController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/PrivateController.java index fc1845b8..e0d94537 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/PrivateController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/PrivateController.java @@ -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 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 roleGroupIds = importExcels.stream() - .map(CreateRoleParam::getGroupId) - .collect(Collectors.toSet()); - - Map 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; diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheProductPermissionHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheProductPermissionHandler.java index a39257d2..3c5a48fa 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheProductPermissionHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheProductPermissionHandler.java @@ -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 productFeatures) { - - if (CollectionUtils.isEmpty(productFeatures)) { - return; - } - - List 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 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 productFeatures = productFeatureRelationService.list(pageProductFeatureRelationReq); - - if (CollectionUtils.isEmpty(productFeatures)) { + Set 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 resolveProductPermissions(List productPermissions) { - if (CollectionUtils.isEmpty(productPermissions)) { - return Collections.emptyList(); - } - - // 新的菜单树是是把有权限点的父节点也存进去了,所以直接解析 - Map featureResources = listSaasFeatureResource(productPermissions); - - Map parentFeatureResources = listParentSaasFeatureResource(featureResources); - - // 旧的菜单树只存储了权限点信息,没有把父节点存进去,需要解析父节点进行存储 - Map saasFeatures = listSaasFeature(productPermissions); - - Map parentSaasFeatures = listParentSaasFeature(saasFeatures); - - return productPermissions.stream() - .collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId)) - .entrySet() - .stream() - .map(e -> { - List productFeatureRelations = e.getValue(); - - if (CollectionUtils.isEmpty(productFeatureRelations)) { - return null; - } - - List 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 resolveFeaturePermission(SaasProductModuleFeatureRelation relation, Map saasFeatures, Map parentSaasFeatures) { - SaasFeature saasFeature = saasFeatures.get(relation.getFeatureId()); - if (Objects.isNull(saasFeature)) { - return null; - } - - List permissionDTOS = Lists.newArrayList(ProductPermissionCacheService.PermissionDTO.builder() - .featureId(saasFeature.getId()) - .featureCode(saasFeature.getFeatureCode()) - .featureType(saasFeature.getFeatureType()) - .terminal(saasFeature.getTerminal()) - .cooperateType(relation.getDictCode()) - .build()); - - List 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 resolveFeatureResourcePermission(SaasProductModuleFeatureRelation relation, Map featureResources, Map parentFeatureResources) { - SaasFeatureResourceResp featureResource = featureResources.get(relation.getFeatureId()); - // 菜单节点是不会关联元素code,所以缓存的featureCode使用菜单编码 - if (Objects.isNull(featureResource)) { - return null; - } - - if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) { - return null; - } - - List 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 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 listSaasFeatureResource(List productPermissions) { - - List 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 listParentSaasFeatureResource(Map productPermissions) { - - List 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 listSaasFeature(List productPermissions) { - - Set 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 listParentSaasFeature(Map saasFeatures) { - - if (CollectionUtils.isEmpty(saasFeatures)) { - return Collections.emptyMap(); - } - - List 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())); - } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheProductSaasFeatureResourceHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheProductSaasFeatureResourceHandler.java index 113ca793..56c680e8 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheProductSaasFeatureResourceHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheProductSaasFeatureResourceHandler.java @@ -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 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 productFeatures) { - - if (CollectionUtils.isEmpty(productFeatures)) { - return; - } - - List 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 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 productFeatures = productFeatureRelationService.list(pageProductFeatureRelationReq); - - if (CollectionUtils.isEmpty(productFeatures)) { + Set 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 resolveProductFeatureResources(List productPermissions) { - if (CollectionUtils.isEmpty(productPermissions)) { - return Collections.emptyList(); - } - - Map featureResources = listSaasFeatureResource(productPermissions); - - Map parentFeatureResources = listParentSaasFeatureResource(featureResources); - - return productPermissions.stream() - .collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId)) - .entrySet() - .stream() - .map(e -> { - List productFeatureRelations = e.getValue(); - - if (CollectionUtils.isEmpty(productFeatureRelations)) { - return null; - } - - List 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 featureResourceDTOS = Lists.newArrayList(featureResourceDTO); - - List 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 listSaasFeatureResource(List productPermissions) { - - List 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 listParentSaasFeatureResource(Map productPermissions) { - - List 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)); - } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheRolePermissionHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheRolePermissionHandler.java index b902d524..b0a66cc4 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheRolePermissionHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheRolePermissionHandler.java @@ -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 roles) { - - if (CollectionUtils.isEmpty(roles)) { - return; - } - - List 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 roles = roleService.list(listSaasRoleParam); + Set 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 roles = roleService.list(listSaasRoleParam); + RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder().build(); + Set 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 resolveRolePermission(List roles) { - - - Map featureResources = listSaasFeatureResource(roles); - - Map parentFeatureResources = listParentSaasFeatureResource(featureResources); - - Map saasFeatures = listSaasFeature(roles); - - Map parentSaasFeatures = listParentSaasFeature(saasFeatures); - - - return roles.stream() - .map(e -> { - if (CollectionUtils.isEmpty(e.getPermissionRelations())) { - return null; - } - - List 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 resolveFeaturePermission(SaasPermissionRelationRes permissionRelation, Map saasFeatures, Map parentSaasFeatures) { - SaasFeature saasFeature = saasFeatures.get(permissionRelation.getFeatureId()); - if (Objects.isNull(saasFeature)) { - return null; - } - - List permissionDTOS = Lists.newArrayList(RolePermissionCacheService.PermissionDTO.builder() - .featureId(saasFeature.getId()) - .featureCode(saasFeature.getFeatureCode()) - .featureType(saasFeature.getFeatureType()) - .terminal(saasFeature.getTerminal()) - .build()); - - List 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 resolveFeatureResourcePermission(SaasPermissionRelationRes permissionRelation, Map featureResources, Map parentFeatureResources) { - SaasFeatureResourceResp featureResource = featureResources.get(permissionRelation.getFeatureId()); - // 菜单节点是不会关联元素code,所以缓存的featureCode使用菜单编码 - if (Objects.isNull(featureResource)) { - return null; - } - - if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) { - return null; - } - - List 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 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 listSaasFeatureResource(List roles) { - - List 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 listParentSaasFeatureResource(Map productPermissions) { - - List 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 listSaasFeature(List roles) { - - Set 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 listParentSaasFeature(Map saasFeatures) { - - if (CollectionUtils.isEmpty(saasFeatures)) { - return Collections.emptyMap(); - } - - List 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())); - } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheRoleSaasFeatureResourceHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheRoleSaasFeatureResourceHandler.java index 1c9875ea..93be689b 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheRoleSaasFeatureResourceHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheRoleSaasFeatureResourceHandler.java @@ -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 roles) { - - if (CollectionUtils.isEmpty(roles)) { - return; - } - - List 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 roles = roleService.list(listSaasRoleParam); + Set 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 roles = roleService.list(listSaasRoleParam); - storeRoleFeatureResource(roles); + Set 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 resolveRoleFeatureResource(List roles) { - - Map featureResources = listSaasFeatureResource(roles); - - Map parentFeatureResources = listParentSaasFeatureResource(featureResources); - - return roles.stream() - .map(e -> { - if (CollectionUtils.isEmpty(e.getPermissionRelations())) { - return null; - } - - List permissions = e.getPermissionRelations().stream() - .distinct() - .map(permissionRelation -> { - SaasFeatureResourceResp featureResource = featureResources.get(permissionRelation.getFeatureId()); - if (Objects.isNull(featureResource) || StringUtils.isBlank(featureResource.getUniCode())) { - return null; - } - - List featureResourceDTOS = Lists.newArrayList(RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO.builder() - .featureId(featureResource.getId()) - .featureType(featureResource.getFeatureType()) - .terminal(featureResource.getTerminal()) - .uniCode(featureResource.getUniCode()) - .build()); - List 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 listSaasFeatureResource(List roles) { - - List 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 listParentSaasFeatureResource(Map productPermissions) { - - List 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)); - } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureHandler.java index b5651162..268cb780 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureHandler.java @@ -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); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureResourceHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureResourceHandler.java index 9695a0e0..ea56c5cc 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureResourceHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureResourceHandler.java @@ -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); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductFeatureResourceJob.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductFeatureResourceJob.java index 2a4c602e..0bbc2b63 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductFeatureResourceJob.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductFeatureResourceJob.java @@ -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; } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductPermissionJob.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductPermissionJob.java index b05d63a3..e7913351 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductPermissionJob.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductPermissionJob.java @@ -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; } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRoleFeatureResourceJob.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRoleFeatureResourceJob.java index d1eb092c..725bec72 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRoleFeatureResourceJob.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRoleFeatureResourceJob.java @@ -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 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> permissionRelations = listPgroupPermissionRelation(); - - Integer pageNumber = 1; - while (true) { - pageSaasRoleParam.setPage(pageNumber++); - pageSaasRoleParam.setPageSize(DEFAULT_PAGE_SIZE); - PageResp page = roleService.page(pageSaasRoleParam); - - store(page.getData(), permissionRelations); - - if (!page.hasNext()) { - break; - } + Set 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> 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> listPermissionGroup(List roleIds) { - - return saasPgroupRoleRelationDao.findByRoleIds(roleIds).stream() - .collect(Collectors.groupingBy(SaasPgroupRoleRelation::getRoleId, - Collectors.mapping(SaasPgroupRoleRelation::getGroupId, Collectors.toSet()))); - } - - private void store(List roles, - Map> permissionRelations) { - - if (CollectionUtils.isEmpty(roles)) { - return; - } - - List roleIds = roles.stream() - .map(SaasRoleRes::getId) - .collect(Collectors.toList()); - Map> roleGroupMap = listPermissionGroup(roleIds); - - roles.forEach(e -> { - Set groupIds = roleGroupMap.get(e.getId()); - if (CollectionUtils.isEmpty(groupIds)) { - return; - } - - List rolePermissions = groupIds.stream() - .map(permissionRelations::get) - .filter(f -> !CollectionUtils.isEmpty(f)) - .flatMap(Collection::stream) - .distinct() - .collect(Collectors.toList()); - - e.setPermissionRelations(rolePermissions); - }); - - List roleFeatureResources = cacheRoleSaasFeatureResourceHandler.resolveRoleFeatureResource(roles); - - if (CollectionUtils.isEmpty(roleFeatureResources)) { - return; - } - - RoleSaasFeatureResourceCacheService.StoreRoleSaasFeatureResourceParam storeRolePermissionParam = RoleSaasFeatureResourceCacheService.StoreRoleSaasFeatureResourceParam.builder() - .roleSaasFeatureResources(roleFeatureResources) - .build(); - roleSaasFeatureResourceCacheService.store(storeRolePermissionParam); - } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRolePermissionJob.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRolePermissionJob.java index 3b0d3bea..d97fa08b 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRolePermissionJob.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRolePermissionJob.java @@ -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 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> permissionRelations = listPgroupPermissionRelation(); - - Integer pageNumber = 1; - while (true) { - pageSaasRoleParam.setPage(pageNumber++); - pageSaasRoleParam.setPageSize(DEFAULT_PAGE_SIZE); - PageResp page = roleService.page(pageSaasRoleParam); - - store(page.getData(), permissionRelations); - - if (!page.hasNext()) { - break; - } + Set 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> 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> listPermissionGroup(List roleIds) { - - return saasPgroupRoleRelationDao.findByRoleIds(roleIds).stream() - .collect(Collectors.groupingBy(SaasPgroupRoleRelation::getRoleId, - Collectors.mapping(SaasPgroupRoleRelation::getGroupId, Collectors.toSet()))); - } - - private void store(List roles, - Map> permissionRelations) { - - if (CollectionUtils.isEmpty(roles)) { - return; - } - - List roleIds = roles.stream() - .map(SaasRoleRes::getId) - .collect(Collectors.toList()); - Map> roleGroupMap = listPermissionGroup(roleIds); - - roles.forEach(e -> { - Set groupIds = roleGroupMap.get(e.getId()); - if (CollectionUtils.isEmpty(groupIds)) { - return; - } - - List rolePermissions = groupIds.stream() - .map(permissionRelations::get) - .filter(f -> !CollectionUtils.isEmpty(f)) - .flatMap(Collection::stream) - .distinct() - .collect(Collectors.toList()); - - e.setPermissionRelations(rolePermissions); - }); - - List rolePermissions = cacheRolePermissionHandler.resolveRolePermission(roles); - - if (CollectionUtils.isEmpty(rolePermissions)) { - return; - } - - RolePermissionCacheService.StoreRolePermissionParam storeRolePermissionParam = RolePermissionCacheService.StoreRolePermissionParam.builder() - .rolePermissions(rolePermissions) - .build(); - rolePermissionCacheService.store(storeRolePermissionParam); - } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheSaasFeatureJob.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheSaasFeatureJob.java index b667eb0d..622a1b4c 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheSaasFeatureJob.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheSaasFeatureJob.java @@ -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 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 terminals) { - Map> saasFeatures = saasFeatureDao.lambdaQuery() + // 没有办法只有所有端,除非写sql去重,菜单数量不多 + Set 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 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 terminals) { PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder() - .needPageElement(true) .terminals(terminals) .build(); - Map> saasFeatureResources = saasFeatureResourceService.list(pageSaasFeatureResourceReq) + Set 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 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 diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductPermissionCacheService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductPermissionCacheService.java index d222620d..1e67c38a 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductPermissionCacheService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductPermissionCacheService.java @@ -18,18 +18,15 @@ public interface ProductPermissionCacheService { */ Map> list(ListProductPermissionParam param); - /** - * 存储产品的权限信息 - * @param param - */ - void store(StoreProductPermissionParam param); + void refreshCache(RefreshProductPermissionCacheParam param); - /** - * 产品是否有缓存权限 - * @param param - * @return - */ - List hasProductIds(HasProductPermissionParam param); + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class RefreshProductPermissionCacheParam { + private Set productIds; + } @Data @Builder diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductSaasFeatureResourceCacheService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductSaasFeatureResourceCacheService.java index 71ddb527..e7441513 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductSaasFeatureResourceCacheService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductSaasFeatureResourceCacheService.java @@ -13,7 +13,15 @@ public interface ProductSaasFeatureResourceCacheService { Map> list(ListProductFeatureResourceParam param); - void store(StoreProductFeatureResourceParam param); + void refreshCache(RefreshProductFeatureResourceCacheParam param); + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class RefreshProductFeatureResourceCacheParam { + private Set productIds; + } @Data @Builder diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/RolePermissionCacheService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/RolePermissionCacheService.java index ae332e55..67b62a8d 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/RolePermissionCacheService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/RolePermissionCacheService.java @@ -14,29 +14,14 @@ public interface RolePermissionCacheService { Map> list(ListRolePermissionParam param); - /** - * redisKey:roleId - * redisValue:permission - * @param param - */ - void store(StoreRolePermissionParam param); - - List hasRoleIds(HasRolePermissionParam param); + void refreshCache(RefreshRolePermissionCacheParam param); @Data @Builder @NoArgsConstructor @AllArgsConstructor - class HasRolePermissionParam { - private List roleIds; - } - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - class StoreRolePermissionParam { - private List rolePermissions; + class RefreshRolePermissionCacheParam { + private Set roleIds; } @Data diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/RoleSaasFeatureResourceCacheService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/RoleSaasFeatureResourceCacheService.java index f5a95828..5d70fe28 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/RoleSaasFeatureResourceCacheService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/RoleSaasFeatureResourceCacheService.java @@ -13,8 +13,15 @@ public interface RoleSaasFeatureResourceCacheService { Map> list(ListRoleSaasFeatureResourceParam param); - void store(StoreRoleSaasFeatureResourceParam param); + void refreshCache(RefreshRoleFeatureResourceCacheParam param); + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class RefreshRoleFeatureResourceCacheParam { + private Set roleIds; + } @Data @Builder diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java index 10ec91e5..7aa6fa61 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java @@ -64,10 +64,18 @@ public interface SaasFeatureResourceService extends IService> listCache(ListSaasFeatureResourceCache param); + void refreshCache(RefreshFeatureResourceCacheParam param); + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class RefreshFeatureResourceCacheParam { + private Set terminals; + } + @Data @Builder @NoArgsConstructor diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductPermissionCacheServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductPermissionCacheServiceImpl.java index 6e1415b6..2c48a40b 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductPermissionCacheServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductPermissionCacheServiceImpl.java @@ -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 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>> productPermissionCache = CacheBuilder.newBuilder() + .expireAfterWrite(14, TimeUnit.MINUTES) + .maximumSize(5000) + .build(new CacheLoader>>() { + @Override + public Optional> load(Long productId) { + return listProductPermissions(Lists.newArrayList(productId)) + .values() + .stream() + .findFirst(); + } + + @Override + public Map>> loadAll(Iterable keys) throws Exception { + List productIds = Lists.newArrayList(keys); + + Map> productPermissions = listProductPermissions(productIds); + + return Maps.toMap(productIds, productId -> Optional.ofNullable(productPermissions.get(productId))); + } + }); + @Override public Map> list(ListProductPermissionParam param) { if (CollectionUtils.isEmpty(param.getProductIds())) { return Collections.emptyMap(); } - Map> permissionCached = listProductPermissionCached(param); + Map> 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> listProductPermissionCached(ListProductPermissionParam param) { - - List redisValues = redisTemplate.executePipelined(new SessionCallback() { - @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 permissions = (List) ((Map) redisValue).values().stream() - .flatMap(e -> JSONArray.parseArray(JSONArray.toJSONString(e), PermissionDTO.class).stream()) - .collect(Collectors.toList()); - - return Pair.of(productId, permissions); - } - - List permissions = (List) ((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() { - @Override - public Object execute(RedisOperations operations) throws DataAccessException { - - for (ProductPermission productPermission : param.getProductPermissions()) { - String redisKey = getKey(productPermission.getProductId()); - - Map 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 hasProductIds(HasProductPermissionParam param) { - List redisValues = redisTemplate.executePipelined(new SessionCallback() { - @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> permissionCached) { - - Sets.SetView difference = Sets.difference(param.getProductIds(), permissionCached.keySet()); - if (difference.isEmpty()) { + public void refreshCache(RefreshProductPermissionCacheParam param) { + if (CollectionUtils.isEmpty(param.getProductIds())) { return; } + Map> productPermissions = listProductPermissions(Lists.newArrayList(param.getProductIds())); + + productPermissionCache.putAll(Maps.toMap(param.getProductIds(), productId -> Optional.ofNullable(productPermissions.get(productId)))); + } + + private Map> listProductPermissions(List productIds) { + + if (CollectionUtils.isEmpty(productIds)) { + return Collections.emptyMap(); + } + PageProductFeatureRelationReq pageProductFeatureRelationReq = PageProductFeatureRelationReq.builder() - .productModuleIds(difference) + .productModuleIds(Sets.newHashSet(productIds)) .build(); List productPermissions = productFeatureRelationService.list(pageProductFeatureRelationReq); - List productPermissionsCache = cacheProductPermissionHandler.resolveProductPermissions(productPermissions); - if (CollectionUtils.isEmpty(productPermissionsCache)) return; - - StoreProductPermissionParam storeWorkspaceProductParam = StoreProductPermissionParam.builder() - .productPermissions(productPermissionsCache) - .build(); - store(storeWorkspaceProductParam); - - Map> 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 resolveProductPermissions(List productPermissions) { + if (CollectionUtils.isEmpty(productPermissions)) { + return Collections.emptyList(); + } + + // 新的菜单树是是把有权限点的父节点也存进去了,所以直接解析 + Map featureResources = listSaasFeatureResource(productPermissions); + + Map parentFeatureResources = listParentSaasFeatureResource(featureResources); + + // 旧的菜单树只存储了权限点信息,没有把父节点存进去,需要解析父节点进行存储 + Map saasFeatures = listSaasFeature(productPermissions); + + Map parentSaasFeatures = listParentSaasFeature(saasFeatures); + + return productPermissions.stream() + .collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId)) + .entrySet() + .stream() + .map(e -> { + List productFeatureRelations = e.getValue(); + + if (CollectionUtils.isEmpty(productFeatureRelations)) { + return null; + } + + List 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 resolveFeaturePermission(SaasProductModuleFeatureRelation relation, Map saasFeatures, Map parentSaasFeatures) { + SaasFeature saasFeature = saasFeatures.get(relation.getFeatureId()); + if (Objects.isNull(saasFeature)) { + return null; + } - /** - * 协同关系类型 - * 原saas_product_module_feature_relation.dictCode - */ - private String cooperateType; + List 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 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 resolveFeatureResourcePermission(SaasProductModuleFeatureRelation relation, Map featureResources, Map 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 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 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 listSaasFeatureResource(List productPermissions) { + + List 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 listParentSaasFeatureResource(Map productPermissions) { + + List 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 listSaasFeature(List productPermissions) { + + Set 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 listParentSaasFeature(Map saasFeatures) { + + if (CollectionUtils.isEmpty(saasFeatures)) { + return Collections.emptyMap(); + } + + List 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())); } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductSaasFeatureResourceCacheServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductSaasFeatureResourceCacheServiceImpl.java index 97d7c6cc..09eeb53f 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductSaasFeatureResourceCacheServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductSaasFeatureResourceCacheServiceImpl.java @@ -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 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 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>> productFeatureResourceCache = CacheBuilder.newBuilder() + .expireAfterWrite(14, TimeUnit.MINUTES) + .maximumSize(5000) + .build(new CacheLoader>>() { + @Override + public Optional> load(Long productId) { + return listProductFeatureResource(Lists.newArrayList(productId)) + .values() + .stream() + .findFirst(); + } + + @Override + public Map>> loadAll(Iterable keys) throws Exception { + List productIds = Lists.newArrayList(keys); + + Map> productPermissions = listProductFeatureResource(productIds); + + return Maps.toMap(productIds, productId -> Optional.ofNullable(productPermissions.get(productId))); + } + }); + @Override public Map> list(ListProductFeatureResourceParam param) { if (CollectionUtils.isEmpty(param.getProductIds())) { return Collections.emptyMap(); } - Map> featureResourceCached = listProductFeatureResourceCached(param); + Map> 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() { - @Override - public Object execute(RedisOperations operations) throws DataAccessException { - - for (ProductFeatureResource productFeatureResource : param.getProductFeatureResources()) { - String redisKey = getKey(productFeatureResource.getProductId()); - - Map 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> listProductFeatureResourceCached(ListProductFeatureResourceParam param) { - - List redisValues = redisTemplate.executePipelined(new SessionCallback() { - @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 featureResources = (List) ((Map) redisValue).values().stream() - .flatMap(e -> JSONArray.parseArray(JSONArray.toJSONString(e), FeatureResourceDTO.class).stream()) - .collect(Collectors.toList()); - - return Pair.of(productId, featureResources); - } - - List featureResources = (List) ((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> featureResourceCached) { - - Sets.SetView difference = Sets.difference(param.getProductIds(), featureResourceCached.keySet()); - if (difference.isEmpty()) { + @Override + public void refreshCache(RefreshProductFeatureResourceCacheParam param) { + if (CollectionUtils.isEmpty(param.getProductIds())) { return; } + Map> productPermissions = listProductFeatureResource(Lists.newArrayList(param.getProductIds())); + + productFeatureResourceCache.putAll(Maps.toMap(param.getProductIds(), productId -> Optional.ofNullable(productPermissions.get(productId)))); + } + + private Map> listProductFeatureResource(List 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 productPermissions = productFeatureRelationService.list(pageProductFeatureRelationReq); - List productFeatureResources = cacheProductSaasFeatureResourceHandler.resolveProductFeatureResources(productPermissions); - if (CollectionUtils.isEmpty(productFeatureResources)) return; - - StoreProductFeatureResourceParam storeProductFeatureResourceParam = StoreProductFeatureResourceParam.builder() - .productFeatureResources(productFeatureResources) - .build(); - store(storeProductFeatureResourceParam); - - Map> 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 resolveProductFeatureResources(List productPermissions) { + if (CollectionUtils.isEmpty(productPermissions)) { + return Collections.emptyList(); + } + + Map featureResources = listSaasFeatureResource(productPermissions); + + Map parentFeatureResources = listParentSaasFeatureResource(featureResources); + + return productPermissions.stream() + .collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId)) + .entrySet() + .stream() + .map(e -> { + List productFeatureRelations = e.getValue(); + + if (CollectionUtils.isEmpty(productFeatureRelations)) { + return null; + } + + List 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 featureResourceDTOS = Lists.newArrayList(featureResourceDTO); + + List 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 listSaasFeatureResource(List productPermissions) { + + List 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 listParentSaasFeatureResource(Map productPermissions) { + + List 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)); } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RolePermissionCacheServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RolePermissionCacheServiceImpl.java index 43ac0dbb..14eb0b7a 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RolePermissionCacheServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RolePermissionCacheServiceImpl.java @@ -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>> rolePermissionCache = CacheBuilder.newBuilder() + .expireAfterWrite(14, TimeUnit.MINUTES) + .maximumSize(5000) + .build(new CacheLoader>>() { + @Override + public Optional> load(Long roleId) { + return listRolePermission(Lists.newArrayList(roleId)) + .values() + .stream() + .findFirst(); + } + + @Override + public Map>> loadAll(Iterable keys) throws Exception { + List roleIds = Lists.newArrayList(keys); + + Map> rolePermissions = listRolePermission(roleIds); + + return Maps.toMap(roleIds, roleId -> Optional.ofNullable(rolePermissions.get(roleId))); + } + }); @Override public Map> list(ListRolePermissionParam param) { if (CollectionUtils.isEmpty(param.getRoleIds())) { return Collections.emptyMap(); } + Map> 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> permissions = listRolePermissionCached(param); + if (CollectionUtils.isEmpty(param.getFeatureCodes())) { + return rolePermissions; + } - fillCacheRolePermissions(param, permissions); - return permissions; - } - - - private Map> listRolePermissionCached(ListRolePermissionParam param) { - - List redisValues = redisTemplate.executePipelined(new SessionCallback() { - @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 permissions = (List) ((Map) redisValue).values().stream() - .flatMap(e -> JSONArray.parseArray(JSONArray.toJSONString(e), PermissionDTO.class).stream()) - .collect(Collectors.toList()); - return Pair.of(roleId, permissions); - } - - List permissions = (List) ((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) connection -> { - - connection.openPipeline(); - - for (RolePermission rolePermission : param.getRolePermissions()) { - String redisKey = getKey(rolePermission.getRoleId()); - - Map 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 hasRoleIds(HasRolePermissionParam param) { - List redisValues = redisTemplate.executePipelined(new SessionCallback() { - @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> permissionCached) { - - Sets.SetView difference = Sets.difference(param.getRoleIds(), permissionCached.keySet()); - if (difference.isEmpty()) { + public void refreshCache(RefreshRolePermissionCacheParam param) { + if (CollectionUtils.isEmpty(param.getRoleIds())) { return; } + Map> rolePermissions = listRolePermission(Lists.newArrayList(param.getRoleIds())); + rolePermissionCache.putAll(Maps.toMap(param.getRoleIds(), roleId -> Optional.ofNullable(rolePermissions.get(roleId)))); + } + private Map> listRolePermission(List roleIds) { RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder() - .roleIds(Lists.newArrayList(difference)) + .roleIds(roleIds) .needPermissionRelation(true) .build(); List roles = roleService.list(listSaasRoleParam); if (CollectionUtils.isEmpty(roles)) { - return; + return Collections.emptyMap(); } - List rolePermissions = cacheRolePermissionHandler.resolveRolePermission(roles); - - if (CollectionUtils.isEmpty(rolePermissions)) { - return; - } - - StoreRolePermissionParam storeRolePermissionParam = StoreRolePermissionParam.builder() - .rolePermissions(rolePermissions) - .build(); - store(storeRolePermissionParam); - - Map> 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 resolveRolePermission(List roles) { + + + Map featureResources = listSaasFeatureResource(roles); + + Map parentFeatureResources = listParentSaasFeatureResource(featureResources); + + Map saasFeatures = listSaasFeature(roles); + + Map parentSaasFeatures = listParentSaasFeature(saasFeatures); + + + return roles.stream() + .map(e -> { + if (CollectionUtils.isEmpty(e.getPermissionRelations())) { + return null; + } + + List 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 resolveFeaturePermission(SaasPermissionRelationRes permissionRelation, Map saasFeatures, Map parentSaasFeatures) { + SaasFeature saasFeature = saasFeatures.get(permissionRelation.getFeatureId()); + if (Objects.isNull(saasFeature)) { + return null; + } + + List permissionDTOS = Lists.newArrayList(RolePermissionCacheService.PermissionDTO.builder() + .featureId(saasFeature.getId()) + .featureCode(saasFeature.getFeatureCode()) + .featureType(saasFeature.getFeatureType()) + .terminal(saasFeature.getTerminal()) + .build()); + + List 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 resolveFeatureResourcePermission(SaasPermissionRelationRes permissionRelation, Map featureResources, Map parentFeatureResources) { + SaasFeatureResourceResp featureResource = featureResources.get(permissionRelation.getFeatureId()); + // 菜单节点是不会关联元素code,所以缓存的featureCode使用菜单编码 + if (Objects.isNull(featureResource)) { + return null; + } + + if (CollectionUtils.isEmpty(featureResource.getSaasPageElements())) { + return null; + } + + List 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 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 listSaasFeatureResource(List roles) { + + List 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 listParentSaasFeatureResource(Map productPermissions) { + + List 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 listSaasFeature(List roles) { + + Set 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 listParentSaasFeature(Map saasFeatures) { + + if (CollectionUtils.isEmpty(saasFeatures)) { + return Collections.emptyMap(); + } + + List 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())); } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleSaasFeatureResourceCacheServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleSaasFeatureResourceCacheServiceImpl.java index f63f3e02..5a3e4a7c 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleSaasFeatureResourceCacheServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleSaasFeatureResourceCacheServiceImpl.java @@ -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>> roleFeatureResourceCache = CacheBuilder.newBuilder() + .expireAfterWrite(14, TimeUnit.MINUTES) + .maximumSize(5000) + .build(new CacheLoader>>() { + @Override + public Optional> load(Long roleId) { + return listRoleFeatureResource(Lists.newArrayList(roleId)) + .values() + .stream() + .findFirst(); + } + + @Override + public Map>> loadAll(Iterable keys) throws Exception { + List roleIds = Lists.newArrayList(keys); + + Map> roleFeatureResources = listRoleFeatureResource(roleIds); + + return Maps.toMap(roleIds, roleId -> Optional.ofNullable(roleFeatureResources.get(roleId))); + } + }); + @Override public Map> list(ListRoleSaasFeatureResourceParam param) { if (CollectionUtils.isEmpty(param.getRoleIds())) { return Collections.emptyMap(); } - Map> featureResources = listRoleSaasFeatureResourceCached(param); + Map> 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) connection -> { - - connection.openPipeline(); - - for (RoleFeatureResource roleFeatureResource : param.getRoleSaasFeatureResources()) { - String redisKey = getKey(roleFeatureResource.getRoleId()); - - Map 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> listRoleSaasFeatureResourceCached(ListRoleSaasFeatureResourceParam param) { - - List redisValues = redisTemplate.executePipelined(new SessionCallback() { - @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 featureResources = (List) ((Map) redisValue).values().stream() - .flatMap(e -> JSONArray.parseArray(JSONArray.toJSONString(e), SaasFeatureResourceDTO.class).stream()) - .collect(Collectors.toList()); - return Pair.of(roleId, featureResources); - } - - List featureResources = (List) ((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> featureResources) { - - Sets.SetView difference = Sets.difference(param.getRoleIds(), featureResources.keySet()); - if (difference.isEmpty()) { + @Override + public void refreshCache(RefreshRoleFeatureResourceCacheParam param) { + if (CollectionUtils.isEmpty(param.getRoleIds())) { return; } + Map> roleFeatureResources = listRoleFeatureResource(Lists.newArrayList(param.getRoleIds())); + + roleFeatureResourceCache.putAll(Maps.toMap(param.getRoleIds(), roleId -> Optional.ofNullable(roleFeatureResources.get(roleId)))); + } + + private Map> listRoleFeatureResource(List 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 roles = roleService.list(listSaasRoleParam); if (CollectionUtils.isEmpty(roles)) { - return; + return Collections.emptyMap(); } - List roleFeatureResources = cacheRoleSaasFeatureResourceHandler.resolveRoleFeatureResource(roles); - - if (CollectionUtils.isEmpty(roleFeatureResources)) { - return; - } - - StoreRoleSaasFeatureResourceParam storeRolePermissionParam = StoreRoleSaasFeatureResourceParam.builder() - .roleSaasFeatureResources(roleFeatureResources) - .build(); - store(storeRolePermissionParam); - - Map> 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 resolveRoleFeatureResource(List roles) { + + Map featureResources = listSaasFeatureResource(roles); + + Map parentFeatureResources = listParentSaasFeatureResource(featureResources); + + return roles.stream() + .map(e -> { + if (CollectionUtils.isEmpty(e.getPermissionRelations())) { + return null; + } + + List permissions = e.getPermissionRelations().stream() + .distinct() + .map(permissionRelation -> { + SaasFeatureResourceResp featureResource = featureResources.get(permissionRelation.getFeatureId()); + if (Objects.isNull(featureResource) || StringUtils.isBlank(featureResource.getUniCode())) { + return null; + } + + List featureResourceDTOS = Lists.newArrayList(RoleSaasFeatureResourceCacheService.SaasFeatureResourceDTO.builder() + .featureId(featureResource.getId()) + .featureType(featureResource.getFeatureType()) + .terminal(featureResource.getTerminal()) + .uniCode(featureResource.getUniCode()) + .build()); + List 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 listSaasFeatureResource(List roles) { + + List 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 listParentSaasFeatureResource(Map productPermissions) { + + List 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)); } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java index 5f426559..5592923d 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java @@ -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 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>> featureResourceCache = CacheBuilder.newBuilder() + .expireAfterWrite(14, TimeUnit.MINUTES) + .maximumSize(5000) + .build(new CacheLoader>>() { + @Override + public Optional> load(String terminal) { + return listFeatureSources(Lists.newArrayList(terminal)) + .values() + .stream() + .findFirst(); + } + + @Override + public Map>> loadAll(Iterable keys) throws Exception { + List terminals = Lists.newArrayList(keys); + + Map> featureResources = listFeatureSources(terminals); + + return Maps.toMap(terminals, terminal -> Optional.ofNullable(featureResources.get(terminal))); + } + }); + @Override public List listNavByIds(List featureIds, List featureTypes) { //按需扩展要查询的字段 @@ -791,85 +807,44 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl() { - @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> listCache(ListSaasFeatureResourceCache param) { if (org.springframework.util.CollectionUtils.isEmpty(param.getTerminals())) { return Collections.emptyMap(); } - Map> featureSourceCached = listFeatureSourceCached(param); - - fillCacheFeatureSources(param, featureSourceCached); - - return featureSourceCached; + Map> 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> listFeatureSourceCached(ListSaasFeatureResourceCache param) { - - List redisValues = redisTemplate.executePipelined(new SessionCallback() { - @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> featureSourceCached) { - - Sets.SetView 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 saasFeatureResources = resolveSaasFeature(difference); + Map> 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> featureSourceMap = saasFeatureResources.stream() + private Map> listFeatureSources(List 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 resolveSaasFeature(Set terminals) { @@ -915,8 +890,4 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl