diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/DelegatedType.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/DelegatedType.java index 3dc3bbd2..b5685854 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/DelegatedType.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/DelegatedType.java @@ -5,6 +5,7 @@ import lombok.Getter; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * 权限点授权策略类型 @@ -39,4 +40,8 @@ public enum DelegatedType { public boolean sameCode(Integer code) { return this.code.equals(code); } + + public static boolean notAuth(Integer delegatedType) { + return Objects.equals(NO_NEED.getCode(), delegatedType); + } } 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 7a042be8..94b47359 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 @@ -7,6 +7,7 @@ import cn.axzo.foundation.page.PageResp; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.framework.rocketmq.Event; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.tyr.client.model.product.ProductSearchListReq; import cn.axzo.tyr.client.model.req.CommonDictQueryReq; import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq; import cn.axzo.tyr.client.model.req.PagePgroupPermissionRelationReq; @@ -21,6 +22,9 @@ import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO; import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO; import cn.axzo.tyr.server.event.outer.CacheWorkspaceProductHandler; import cn.axzo.tyr.server.event.payload.ServicePkgProductCreatedPayload; +import cn.axzo.tyr.server.job.CacheProductPermissionJob; +import cn.axzo.tyr.server.job.CacheRolePermissionJob; +import cn.axzo.tyr.server.job.CacheSaasFeatureJob; import cn.axzo.tyr.server.repository.dao.SaasFeatureDao; import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao; import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao; @@ -123,6 +127,12 @@ public class PrivateController { private CacheWorkspaceProductHandler cacheWorkspaceProductHandler; @Autowired private SaasRoleGroupDao saasRoleGroupDao; + @Autowired + private CacheProductPermissionJob cacheProductPermissionJob; + @Autowired + private CacheRolePermissionJob cacheRolePermissionJob; + @Autowired + private CacheSaasFeatureJob cacheSaasFeatureJob; /** * 统一层级的roleGroup按照id升序,sort从1递增 @@ -578,8 +588,8 @@ public class PrivateController { } @PostMapping("/api/private/workspaceProduct/store") - public Object storeWorkspaceProduct(@RequestBody WorkspaceProductService.StoreWorkspaceProductParam request) { - workspaceProductService.storeWorkspaceProduct(request); + public Object storeWorkspaceProduct(@RequestBody ProductSearchListReq request) throws Exception { + cacheProductPermissionJob.execute(JSON.toJSONString(request)); return "ok"; } @@ -651,6 +661,26 @@ public class PrivateController { return "ok"; } + @PostMapping("/api/private/rolePermission/store") + public Object storeRolePermission(@RequestBody RoleService.PageSaasRoleParam request) throws Exception { + cacheRolePermissionJob.execute(JSON.toJSONString(request)); + return "ok"; + } + + @PostMapping("/api/private/saasFeature/store") + public Object storeSaasFeature(@RequestBody StoreFeatureParam request) throws Exception { + cacheSaasFeatureJob.execute(request.getTerminal()); + return "ok"; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class StoreFeatureParam { + private String terminal; + } + @Data @Builder @NoArgsConstructor 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 e62ded89..24e1a6c1 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 @@ -81,7 +81,7 @@ public class CacheRolePermissionHandler implements EventHandler, InitializingBea log.info("end cached role permission handler rocketmq event: {}", event); } - @Override + @Override public void afterPropertiesSet() throws Exception { eventConsumer.registerHandler(EventTypeEnum.ROLE_PERMISSION_CREATED.getEventCode(), this); } 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 new file mode 100644 index 00000000..6980f956 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureHandler.java @@ -0,0 +1,69 @@ +package cn.axzo.tyr.server.event.inner; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.tyr.client.model.enums.DelegatedType; +import cn.axzo.tyr.server.event.payload.SaasFeatureUpsertPayload; +import cn.axzo.tyr.server.repository.dao.SaasFeatureDao; +import cn.axzo.tyr.server.repository.entity.SaasFeature; +import cn.axzo.tyr.server.service.SaasFeatureResourceService; +import cn.axzo.tyr.server.service.SaasFeatureService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 缓存全量权限点,因为鉴权等逻辑,需要查询免授权和权限点是否存在,数据量大,数据库压力大 + */ +@Slf4j +@Component +public class CacheSaasFeatureHandler implements EventHandler, InitializingBean { + + @Autowired + private EventConsumer eventConsumer; + @Autowired + private SaasFeatureDao saasFeatureDao; + @Autowired + private SaasFeatureResourceService saasFeatureResourceService; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + log.info("begin cached saasFeature handler rocketmq event: {}", event); + SaasFeatureUpsertPayload payload = event.normalizedData(SaasFeatureUpsertPayload.class); + + if (StringUtils.isBlank(payload.getTerminal())) { + return; + } + + List saasFeatures = saasFeatureDao.lambdaQuery() + .eq(SaasFeature::getTerminal, payload.getTerminal()) + .list() + .stream() + .map(e -> SaasFeatureResourceService.SaasFeatureResourceCache + .builder() + .featureId(e.getId()) + .notAuth(DelegatedType.notAuth(e.getDelegatedType())) + .build()) + .collect(Collectors.toList()); + + SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder() + .terminal(payload.getTerminal()) + .features(saasFeatures) + .build(); + saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache); + + + log.info("end cached saasFeature handler rocketmq event: {}", event); + } + + @Override + public void afterPropertiesSet() throws Exception { + eventConsumer.registerHandler(EventTypeEnum.SAAS_FEATURE_UPSERT.getEventCode(), this); + } +} 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 new file mode 100644 index 00000000..9bfc9489 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/inner/CacheSaasFeatureResourceHandler.java @@ -0,0 +1,66 @@ +package cn.axzo.tyr.server.event.inner; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq; +import cn.axzo.tyr.server.event.payload.SaasFeatureResourceUpsertPayload; +import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; +import cn.axzo.tyr.server.service.SaasFeatureResourceService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 缓存全量权限点,因为鉴权等逻辑,需要查询免授权和权限点是否存在,数据量大,数据库压力大 + */ +@Slf4j +@Component +public class CacheSaasFeatureResourceHandler implements EventHandler, InitializingBean { + + @Autowired + private EventConsumer eventConsumer; + @Autowired + private SaasFeatureResourceService saasFeatureResourceService; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + log.info("begin cached saasFeatureResource handler rocketmq event: {}", event); + SaasFeatureResourceUpsertPayload payload = event.normalizedData(SaasFeatureResourceUpsertPayload.class); + + if (StringUtils.isBlank(payload.getTerminal())) { + return; + } + + // 直接查询缓存所有节点,因为修改的代码不好改 + + PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder() + .terminal(payload.getTerminal()) + .build(); + + List saasFeatures = saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream() + .map(e -> SaasFeatureResourceService.SaasFeatureResourceCache.builder() + .featureId(e.getId()) + .notAuth(SaasFeatureResource.AuthType.isAllRole(e.getAuthType())) + .build()) + .collect(Collectors.toList()); + + SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder() + .terminal(payload.getTerminal()) + .features(saasFeatures) + .build(); + saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache); + + log.info("end cached saasFeatureResource handler rocketmq event: {}", event); + } + + @Override + public void afterPropertiesSet() throws Exception { + eventConsumer.registerHandler(EventTypeEnum.SAAS_FEATURE_RESOURCE_UPSERT.getEventCode(), this); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureResourceUpsertPayload.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureResourceUpsertPayload.java index 6590c1cc..648fb7d9 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureResourceUpsertPayload.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureResourceUpsertPayload.java @@ -6,7 +6,6 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; -import java.util.Set; @Data @Builder @@ -14,5 +13,5 @@ import java.util.Set; @AllArgsConstructor public class SaasFeatureResourceUpsertPayload implements Serializable { - private Set ids; + private String terminal; } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureUpsetPayload.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureUpsertPayload.java similarity index 67% rename from tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureUpsetPayload.java rename to tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureUpsertPayload.java index 7c6349cc..312a86cd 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureUpsetPayload.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/SaasFeatureUpsertPayload.java @@ -6,13 +6,14 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; -import java.util.Set; @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class SaasFeatureUpsetPayload implements Serializable { +public class SaasFeatureUpsertPayload implements Serializable { - private Set ids; + private Long id; + + private String terminal; } 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 new file mode 100644 index 00000000..299f47f3 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheProductPermissionJob.java @@ -0,0 +1,54 @@ +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.ProductService; +import com.alibaba.fastjson.JSONObject; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +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 java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class CacheProductPermissionJob extends IJobHandler { + + @Autowired + private CacheProductPermissionHandler cacheProductPermissionHandler; + @Autowired + private ProductService productService; + + @Override + @XxlJob("CacheProductPermissionJob") + public ReturnT execute(String s) throws Exception { + + log.info("start CacheProductPermissionJob, s:{}", s); + ProductSearchListReq productSearchListReq = Optional.ofNullable(s) + .map(e -> JSONObject.parseObject(e, ProductSearchListReq.class)) + .orElseGet(ProductSearchListReq::new); + + Set productIds = productService.list(productSearchListReq).getData() + .stream() + .map(ProductVO::getId) + .collect(Collectors.toSet()); + + ProductPermissionCreatedPayload payload = ProductPermissionCreatedPayload.builder() + .productModuleIds(productIds) + .build(); + + Event event = Event.builder() + .data(payload) + .build(); + cacheProductPermissionHandler.onEvent(event, null); + return ReturnT.SUCCESS; + } +} 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 new file mode 100644 index 00000000..5e464316 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheRolePermissionJob.java @@ -0,0 +1,68 @@ +package cn.axzo.tyr.server.job; + +import cn.axzo.foundation.page.PageResp; +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.tyr.client.model.res.SaasRoleRes; +import cn.axzo.tyr.server.event.inner.CacheRolePermissionHandler; +import cn.axzo.tyr.server.event.payload.RolePermissionCreatedPayload; +import cn.axzo.tyr.server.service.RoleService; +import com.alibaba.fastjson.JSONObject; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +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 java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class CacheRolePermissionJob extends IJobHandler { + + @Autowired + private CacheRolePermissionHandler cacheRolePermissionHandler; + @Autowired + private RoleService roleService; + + private static final Integer DEFAULT_PAGE_SIZE = 1000; + + @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()); + + Integer pageNumber = 1; + while (true) { + pageSaasRoleParam.setPage(pageNumber++); + pageSaasRoleParam.setPageSize(DEFAULT_PAGE_SIZE); + + PageResp page = roleService.page(pageSaasRoleParam); + Set roleIds = page.getData().stream() + .map(SaasRoleRes::getId) + .collect(Collectors.toSet()); + + RolePermissionCreatedPayload payload = RolePermissionCreatedPayload.builder() + .roleIds(roleIds) + .build(); + + Event event = Event.builder() + .data(payload) + .build(); + + cacheRolePermissionHandler.onEvent(event, null); + + if (!page.hasNext()) { + break; + } + } + + return ReturnT.SUCCESS; + } +} 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 new file mode 100644 index 00000000..b0d0c165 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CacheSaasFeatureJob.java @@ -0,0 +1,86 @@ +package cn.axzo.tyr.server.job; + +import cn.axzo.tyr.client.model.enums.DelegatedType; +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.SaasFeatureResource; +import cn.axzo.tyr.server.service.SaasFeatureResourceService; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class CacheSaasFeatureJob extends IJobHandler { + + @Autowired + private SaasFeatureDao saasFeatureDao; + @Autowired + private SaasFeatureResourceService saasFeatureResourceService; + + @Override + @XxlJob("CacheSaasFeatureJob") + public ReturnT execute(String s) throws Exception { + + log.info("start CacheSaasFeatureJob, s:{}", s); + + cacheSaasFeature(s); + + cacheSaasFeatureResource(s); + return ReturnT.SUCCESS; + } + + private void cacheSaasFeature(String terminal) { + Map> saasFeatures = saasFeatureDao.lambdaQuery() + .eq(StringUtils.isNotBlank(terminal), SaasFeature::getTerminal, terminal) + .list() + .stream() + .collect(Collectors.groupingBy(SaasFeature::getTerminal, + Collectors.mapping(e -> SaasFeatureResourceService.SaasFeatureResourceCache + .builder() + .featureId(e.getId()) + .notAuth(DelegatedType.notAuth(e.getDelegatedType())) + .build(), Collectors.toList()))); + + saasFeatures.entrySet().forEach(e -> { + SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder() + .terminal(e.getKey()) + .features(e.getValue()) + .build(); + saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache); + }); + } + + private void cacheSaasFeatureResource(String terminal) { + PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder() + .terminal(terminal) + .build(); + + Map> saasFeatureResources = saasFeatureResourceService.list(pageSaasFeatureResourceReq) + .stream() + .collect(Collectors.groupingBy(SaasFeatureResourceResp::getTerminal, + Collectors.mapping(e -> SaasFeatureResourceService.SaasFeatureResourceCache + .builder() + .featureId(e.getId()) + .notAuth(SaasFeatureResource.AuthType.isAllRole(e.getAuthType())) + .build(), Collectors.toList()))); + + saasFeatureResources.entrySet().forEach(e -> { + SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder() + .terminal(e.getKey()) + .features(e.getValue()) + .build(); + saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache); + }); + } +} 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 1e10c7c4..0ca7f997 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 @@ -11,8 +11,13 @@ import cn.axzo.tyr.server.model.ResourcePermission; import cn.axzo.tyr.server.model.ResourcePermissionQueryDTO; import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; import com.baomidou.mybatisplus.extension.service.IService; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -59,4 +64,38 @@ public interface SaasFeatureResourceService extends IService> listCache(ListSaasFeatureResourceCache param); + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class StoreSaasFeatureResourceCache { + + private String terminal; + + private List features; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class ListSaasFeatureResourceCache { + + private Set terminals; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class SaasFeatureResourceCache { + + private Long featureId; + + private boolean notAuth; + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionPointServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionPointServiceImpl.java index 51feb1b6..86c81b8d 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionPointServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionPointServiceImpl.java @@ -1,37 +1,29 @@ package cn.axzo.tyr.server.service.impl; -import static cn.axzo.tyr.client.model.enums.FeatureType.BUTTON; -import static cn.axzo.tyr.client.model.enums.FeatureType.MODULE; -import static cn.axzo.tyr.server.common.constants.PermissionConstant.*; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -import cn.axzo.tyr.client.model.enums.FeatureDataType; -import cn.axzo.tyr.client.model.req.QueryPermissionByIdsReq; -import cn.axzo.tyr.client.model.res.SimplePermissionPointResp; -import cn.hutool.core.date.StopWatch; -import cn.hutool.core.lang.Opt; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; - import cn.axzo.basics.common.BeanMapper; import cn.axzo.basics.common.util.StopWatchUtil; import cn.axzo.basics.common.util.TreeUtil; import cn.axzo.framework.domain.web.code.BaseCode; +import cn.axzo.framework.rocketmq.Event; import cn.axzo.tyr.client.model.dict.request.BasicDictNodeReq; import cn.axzo.tyr.client.model.dict.request.BasicDictQueryReq; import cn.axzo.tyr.client.model.dict.response.BasicDictNodeResp; import cn.axzo.tyr.client.model.dict.response.BasicDictTreeResp; import cn.axzo.tyr.client.model.enums.DictTypeFiledEnum; import cn.axzo.tyr.client.model.enums.DictWorkSpaceTypeEnum; +import cn.axzo.tyr.client.model.enums.FeatureDataType; import cn.axzo.tyr.client.model.enums.FeatureType; -import cn.axzo.tyr.client.model.permission.*; +import cn.axzo.tyr.client.model.permission.PermissionPointDTO; +import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest; +import cn.axzo.tyr.client.model.permission.PermissionPointMoveRequest; +import cn.axzo.tyr.client.model.permission.PermissionPointTreeNode; +import cn.axzo.tyr.client.model.permission.PermissionPointTreeQueryReq; +import cn.axzo.tyr.client.model.permission.PermissionPointVO; +import cn.axzo.tyr.client.model.req.QueryPermissionByIdsReq; +import cn.axzo.tyr.client.model.res.SimplePermissionPointResp; import cn.axzo.tyr.server.common.util.Throws; +import cn.axzo.tyr.server.config.MqProducer; +import cn.axzo.tyr.server.event.payload.SaasFeatureUpsertPayload; import cn.axzo.tyr.server.repository.dao.SaasFeatureDao; import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao; import cn.axzo.tyr.server.repository.dao.SaasProductModuleFeatureRelationDao; @@ -40,10 +32,32 @@ import cn.axzo.tyr.server.service.PermissionPointService; import cn.axzo.tyr.server.service.SaasBasicDictService; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import static cn.axzo.tyr.server.util.RpcInternalUtil.checkAndGetData; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +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.client.model.enums.FeatureType.BUTTON; +import static cn.axzo.tyr.client.model.enums.FeatureType.MODULE; +import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_BIZ_NO_PREFIX; +import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_PATH_DELIMITER; +import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_TOP_BIZ_NO; +import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_TOP_PATH; +import static cn.axzo.tyr.server.event.inner.EventTypeEnum.SAAS_FEATURE_UPSERT; /** * 权限点服务实现 @@ -61,6 +75,9 @@ public class PermissionPointServiceImpl implements PermissionPointService { private final SaasBasicDictService saasBasicDictService; private final SaasPgroupPermissionRelationDao saasPgroupPermissionRelationDao; private final SaasProductModuleFeatureRelationDao saasProductModuleFeatureRelationDao; + private final MqProducer mqProducer; + + private static final String TARGET_TYPE = "saasFeatureId"; @Override public List listTreeNodes(PermissionPointTreeQueryReq request) { @@ -333,6 +350,11 @@ public class PermissionPointServiceImpl implements PermissionPointService { this.saasFeatureDao.updateById(saasFeature); //返回一些要用的数据 dto.setBusinessNo(feature.getBusinessNo()); + + sendMsg(SaasFeatureUpsertPayload.builder() + .id(dto.getId()) + .terminal(feature.getTerminal()) + .build()); return dto; } @@ -367,6 +389,11 @@ public class PermissionPointServiceImpl implements PermissionPointService { dto.setBusinessNo(saasFeature.getBusinessNo()); //调整排序 - 兼容处理老数据,数据规范化 changeSort(saasFeature, saasFeature.getSort()); + + sendMsg(SaasFeatureUpsertPayload.builder() + .id(dto.getId()) + .terminal(saasFeature.getTerminal()) + .build()); return dto; } @@ -426,6 +453,11 @@ public class PermissionPointServiceImpl implements PermissionPointService { //删除关联数据 this.saasPgroupPermissionRelationDao.removeByPermissionPointIds(delIds); this.saasProductModuleFeatureRelationDao.removeByPermissionPointIds(delIds); + + sendMsg(SaasFeatureUpsertPayload.builder() + .id(feature.getId()) + .terminal(feature.getTerminal()) + .build()); return bizNoList; } @@ -643,4 +675,12 @@ public class PermissionPointServiceImpl implements PermissionPointService { .build(); } + private void sendMsg(SaasFeatureUpsertPayload saasFeatureUpsertPayload) { + Event event = Event.builder() + .targetType(TARGET_TYPE) + .eventCode(SAAS_FEATURE_UPSERT.getEventCode()) + .data(saasFeatureUpsertPayload) + .build(); + mqProducer.send(event); + } } 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 bbf6022a..d9bf13de 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 @@ -10,6 +10,7 @@ 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; @@ -44,18 +45,28 @@ 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.collect.Lists; 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.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; @@ -65,6 +76,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static cn.axzo.tyr.server.config.exception.BizResultCode.FEATURE_RESOURCE_NOT_FOUND; @@ -95,6 +107,15 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl redisTemplate; + + /** 菜单树过期时间 **/ + @Value("${saas.feature.resource.expire:180}") + private Long expireInDays; + + private static final String SAAS_FEATURE_RESOURCE_KEY = "saas:feature:resource:%s"; + private static final String TARGET_TYPE = "saasFeatureResourceId"; @Override public List listNavByIds(List featureIds, List featureTypes) { @@ -267,7 +288,7 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl deleteFeatureResource) { @@ -671,4 +701,43 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl> listCache(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 String getKey(Object... params) { + return String.format(SAAS_FEATURE_RESOURCE_KEY, params); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/TyrSaasAuthServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/TyrSaasAuthServiceImpl.java index a7ea8da4..eddcdd13 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/TyrSaasAuthServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/TyrSaasAuthServiceImpl.java @@ -2,6 +2,7 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.apollo.workspace.common.enums.TableIsDeleteEnum; import cn.axzo.basics.common.BeanMapper; +import cn.axzo.framework.domain.ServiceException; import cn.axzo.pokonyan.util.TraceSupplier; import cn.axzo.thrones.client.saas.ServicePkgClient; import cn.axzo.thrones.client.saas.entity.serivicepgkproduct.ServicePkgProduct; @@ -10,7 +11,6 @@ import cn.axzo.tyr.client.common.enums.RoleTypeEnum; import cn.axzo.tyr.client.common.enums.WorkspaceJoinType; import cn.axzo.tyr.client.model.enums.DelegatedType; import cn.axzo.tyr.client.model.enums.IdentityType; -import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest; import cn.axzo.tyr.client.model.permission.PermissionPointTreeNode; import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO; import cn.axzo.tyr.client.model.req.CheckIdentityPermissionReq; @@ -447,8 +447,52 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { private String terminal; } + private Set checkFeatureCodes(ListPermissionUser param) { + // 查询免授权的featureCodes,判断featureCodes里是否有免授权的featureCodes + // 兼容新老版本,需要通过featureCode查询新版本的features,原逻辑是查询当前菜单资源的所有子数据 + List features = permissionPointService.listNodeWithChildrenByCodes(Lists.newArrayList(param.getFeatureCodes()), param.getTerminal()); + + ListSaasFeatureResourceParam listSaasFeatureResourceParam = ListSaasFeatureResourceParam.builder() + .featureCodes(param.getFeatureCodes()) + .terminal(param.getTerminal()) + .build(); + List saasFeatureResources = listSaasFeatureResource(listSaasFeatureResourceParam); + if (CollectionUtil.isEmpty(features) && CollectionUtils.isEmpty(saasFeatureResources)) { + log.warn("no features data found for:{}", param.getFeatureCodes()); + return Collections.emptySet(); + } + + //是否免授权权限点 + Optional freeFeature = features.stream() + .filter(f -> DelegatedType.NO_NEED.sameCode(f.getDelegatedType())) + .findAny(); + if (freeFeature.isPresent()) { + throw new ServiceException("不能查询免授权权限点人员"); + } + + Optional freeFeatureResource = saasFeatureResources.stream() + .filter(e -> SaasFeatureResource.AuthType.isAllRole(e.getAuthType())) + .findFirst(); + if (freeFeatureResource.isPresent()) { + throw new ServiceException("不能查询免授权权限点人员"); + } + + Set allFeatureIds = Sets.newHashSet(); + allFeatureIds.addAll(features.stream() + .map(SaasFeature::getId) + .collect(Collectors.toSet())); + + allFeatureIds.addAll(saasFeatureResources.stream() + .map(SaasFeatureResourceResp::getId) + .collect(Collectors.toSet())); + + return allFeatureIds; + } + private List listPermissionUser(ListPermissionUser param) { - // TODO 查询免授权的featureCodes,判断featureCodes里是否有免授权的featureCodes + + // 查询的featureCodes有可能已经被删除,但是saas_feature_code效率比较低,两个表的id不会重复,所以解析成id去过滤权限 + Set featureIds = checkFeatureCodes(param); WorkspaceProductService.ListWorkspaceProductPermissionCacheParam listWorkspaceProductPermissionCacheParam = WorkspaceProductService.ListWorkspaceProductPermissionCacheParam.builder() .workspaceIds(Sets.newHashSet(param.getWorkspaceId())) @@ -462,6 +506,7 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { .map(WorkspaceProductService.ProductPermission::getPermissions) .filter(e -> !CollectionUtils.isEmpty(e)) .flatMap(Collection::stream) + .filter(e -> featureIds.contains(e.getFeatureId())) .filter(e -> StringUtils.isBlank(param.getTerminal()) || Objects.equals(e.getTerminal(), param.getTerminal())) .collect(Collectors.toList()); @@ -470,7 +515,7 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { return Collections.emptyList(); } - return getWorkspaceUserV2(param, productPermissions); + return getWorkspaceUserV2(param, productPermissions, featureIds); } @Override @@ -520,13 +565,17 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { } private Set resolvePermissionNormalRole(ListPermissionUser req, - List productPermissions) { + List productPermissions, + Set featureIds) { // 因为通过权限id找对应的角色数据量巨大,所以通过找项目的角色,再找有权限的角色比较快 Set allRoleIds = saasRoleUserRelationMapper.listRoleIds(SaasRoleUserRelationMapper.ListRole.builder() .ouId(req.getOuId()) .workspaceId(req.getWorkspaceId()) .build()); + if (CollectionUtils.isEmpty(allRoleIds)) { + return Collections.emptySet(); + } RolePermissionCacheService.ListRolePermissionParam listRolePermissionParam = RolePermissionCacheService.ListRolePermissionParam.builder() .roleIds(allRoleIds) @@ -538,6 +587,7 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { Set normalRoleIds = normalRolePermissionMap.entrySet().stream() .filter(e -> !CollectionUtils.isEmpty(e.getValue())) + .filter(e -> e.getValue().stream().anyMatch(p -> featureIds.contains(p.getFeatureId()))) .filter(e -> StringUtils.isBlank(req.getTerminal()) || e.getValue().stream().anyMatch(p -> Objects.equals(p.getTerminal(), req.getTerminal()))) .map(Map.Entry::getKey) @@ -574,13 +624,14 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { } private List getWorkspaceUserV2(ListPermissionUser req, - List productPermissions) { + List productPermissions, + Set featureIds) { //超管和管理员 List adminRoles = listAdminRole(req); Set adminPermissionRoleIds = resolvePermissionAdminRole(adminRoles, productPermissions); - Set normalPermissionRoleIds = resolvePermissionNormalRole(req, productPermissions); + Set normalPermissionRoleIds = resolvePermissionNormalRole(req, productPermissions, featureIds); Set roleIds = Sets.newHashSet(); roleIds.addAll(adminPermissionRoleIds); @@ -1444,6 +1495,8 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { result.setPermissions(permissions); return result; } + + private IdentityAuthRes.WorkspacePermission buildPermissionsV2(IdentityAuthReq.WorkspaceOuPair workspaceOuPair, List productPermissions, @@ -1455,11 +1508,22 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { .ouId(workspaceOuPair.getOuId()) .build(); + // 根据项目的产品找到对应端的所有权限点,因为可能存在某些权限点被删除或者是免授权,虽然有两颗权限点树,但是id不会重复,新的权限点从100000开始,历史的后续不会再使用 + Set terminals = productPermissions.stream() + .map(ProductPermissionCacheService.PermissionDTO::getTerminal) + .collect(Collectors.toSet()); + List allFeatures = listSaasFeatureCaches(terminals); + + if (CollectionUtils.isEmpty(allFeatures)) { + return workspacePermission; + } + //超管和管理员权限 Set adminPermissionPoints = buildAdminPermissionV2(productPermissions, saasRoles); - Set noAuthPermissionPoints = buildNoAuthPermission(productPermissions); + Set noAuthPermissionPoints = buildNoAuthPermission(productPermissions, allFeatures); Set normalPermissionPoints = buildNormalPermissionV2(productPermissions, saasRoles, rolePermissions); + //组装返回值 //是否超管 boolean superAdmin = saasRoles.stream() @@ -1470,12 +1534,28 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { allPermissionPoints.addAll(adminPermissionPoints); allPermissionPoints.addAll(noAuthPermissionPoints); allPermissionPoints.addAll(normalPermissionPoints); - // TODO 过滤掉已经删除的权限点 + // 过滤掉已经删除的权限点 + Set effectFeatureIds = allFeatures.stream() + .map(SaasFeatureResourceService.SaasFeatureResourceCache::getFeatureId) + .collect(Collectors.toSet()); - workspacePermission.setPermissionPoint(Lists.newArrayList(allPermissionPoints)); + workspacePermission.setPermissionPoint(allPermissionPoints.stream() + .filter(e -> effectFeatureIds.contains(e.getFeatureId())) + .collect(Collectors.toList())); return workspacePermission; } + private List listSaasFeatureCaches(Set terminals) { + SaasFeatureResourceService.ListSaasFeatureResourceCache listSaasFeatureResourceCache = SaasFeatureResourceService.ListSaasFeatureResourceCache.builder() + .terminals(terminals) + .build(); + return saasFeatureResourceService.listCache(listSaasFeatureResourceCache) + .values() + .stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + private Set buildAdminPermissionV2(List productPermissions, List saasRoles) { //超管和管理员角色 @@ -1506,23 +1586,19 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { } - private Set buildNoAuthPermission(List productPermissions) { - // 旧的菜单树免授权的节点很少,所以直接查询,线上只有11个 - Set noAuthSaasFeatureCodes = permissionPointService.queryList(PermissionPointListQueryRequest.builder() - .delegatedType(DelegatedType.NO_NEED.getCode()) - .build()) - .stream() - .map(PermissionPointTreeNode::getCode) + private Set buildNoAuthPermission(List productPermissions, + List allFeatuers) { + Set notAuthFeatureIds = allFeatuers.stream() + .filter(SaasFeatureResourceService.SaasFeatureResourceCache::isNotAuth) + .map(SaasFeatureResourceService.SaasFeatureResourceCache::getFeatureId) .collect(Collectors.toSet()); - // TODO 查询新权限树免授权的权限点 - - if (CollectionUtils.isEmpty(noAuthSaasFeatureCodes)) { + if (CollectionUtils.isEmpty(notAuthFeatureIds)) { return Collections.emptySet(); } return productPermissions.stream() - .filter(productPermission -> noAuthSaasFeatureCodes.contains(productPermission.getFeatureCode())) + .filter(productPermission -> notAuthFeatureIds.contains(productPermission.getFeatureId())) .map(e -> IdentityAuthRes.PermissionPoint.builder() .featureCode(e.getFeatureCode()) .featureId(e.getFeatureId())