feat:(REQ-2545) 增加项目的产品缓存,提高鉴权接口响应

This commit is contained in:
lilong 2024-07-11 10:35:37 +08:00
parent 1087c505c8
commit bef5d839e2
15 changed files with 557 additions and 6 deletions

View File

@ -1,6 +1,7 @@
package cn.axzo.tyr.server;
import cn.axzo.pokonyan.config.mybatisplus.EntityMetaObjectHandler;
import cn.axzo.tyr.server.config.RocketMQEventConfiguration;
import cn.axzo.tyr.server.job.CMSRoleJobHandler;
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
@ -20,6 +21,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
@EnableDiscoveryClient
@MapperScan(value = {"cn.axzo.tyr.server.repository.mapper"})
@SpringBootApplication(scanBasePackages = "cn.axzo")
@Import(RocketMQEventConfiguration.class)
public class TyrApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TyrApplication.class, args);

View File

@ -1,10 +1,12 @@
package cn.axzo.tyr.server;
import cn.axzo.tyr.server.config.RocketMQEventConfiguration;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableAsync;
@ -15,6 +17,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
@EnableFeignClients(basePackages = {"cn.axzo"})
@Log4j2
@EnableAsync
@Import(RocketMQEventConfiguration.class)
public class TyrServerDevApplication {
public static void main(String[] args) throws Exception {

View File

@ -1,10 +1,12 @@
package cn.axzo.tyr.server;
import cn.axzo.tyr.server.config.RocketMQEventConfiguration;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableAsync;
@ -16,6 +18,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
@EnableFeignClients(basePackages = {"cn.axzo"})
@Log4j2
@EnableAsync
@Import(RocketMQEventConfiguration.class)
public class TyrServerPreApplication {
public static void main(String[] args) throws Exception {

View File

@ -1,10 +1,12 @@
package cn.axzo.tyr.server;
import cn.axzo.tyr.server.config.RocketMQEventConfiguration;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableAsync;
@ -16,6 +18,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
@EnableFeignClients(basePackages = {"cn.axzo"})
@Log4j2
@EnableAsync
@Import(RocketMQEventConfiguration.class)
public class TyrServerTestApplication {
public static void main(String[] args) throws Exception {

View File

@ -0,0 +1,90 @@
package cn.axzo.tyr.server.config;
import cn.axzo.framework.rocketmq.BaseListener;
import cn.axzo.framework.rocketmq.DefaultEventConsumer;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandlerRepository;
import cn.axzo.framework.rocketmq.EventProducer;
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.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.function.Consumer;
/**
* @Author: liyong.tian
* @Date: 2023/7/25 14:43
* @Description:
*/
@Slf4j
public class RocketMQEventConfiguration {
@Value("${spring.application.name}")
private String appName;
@Value("${topic}")
private String topic;
@Bean
public RocketMQTemplate ser(){
return new RocketMQTemplate();
}
@Bean
EventProducer eventProducer(RocketMQTemplate rocketMQTemplate) {
return new RocketMQEventProducer(rocketMQTemplate,
appName,
appName,
EventProducer.Context.<RocketMQEventProducer.RocketMQMessageMeta>builder()
.meta(RocketMQEventProducer.RocketMQMessageMeta.builder()
.topic(topic)
.build())
.build(),
null
);
}
@Bean
EventConsumer eventConsumer(EventHandlerRepository eventHandlerRepository) {
Consumer<EventConsumer.EventWrapper> callback = (eventWrapper) -> {
if (eventWrapper.isHandled()) {
// 只收集被App真正消费的消息.
//String topic = (String) eventWrapper.getExt().get(EVENT_TOPIC_KEY);
}
};
return new DefaultEventConsumer(appName, eventHandlerRepository, callback);
}
@Slf4j
@Component
@RocketMQMessageListener(topic = "topic_thrones_${spring.profiles.active}",
consumerGroup = "GID_topic_thrones_${spring.application.name}_${spring.profiles.active}",
consumeMode = ConsumeMode.ORDERLY,
nameServer = "${rocketmq.name-server}"
)
public static class DefaultListener extends BaseListener implements RocketMQListener<MessageExt> {
@Autowired
private EventConsumer eventConsumer;
@Override
public void onMessage(MessageExt message) {
super.onEvent(message, eventConsumer);
}
}
@Bean
EventHandlerRepository eventHandlerRepository() {
return new EventHandlerRepository((ex, logText) -> {
log.warn("MQ, handle warning {}", logText, ex);
});
}
}

View File

@ -11,8 +11,9 @@ public enum BizResultCode implements IResultCode {
CANT_DELETE_ROLE_GROUP("100001", "不能删除角色分组,当前角色分组下有子角色分组"),
ROLE_GROUP_NOT_FOUND("100002", "角色分组不存在"),
REDIS_ROLE_NOT_NULL("100003", "角色id不能为空"),
REDIS_PRODUCT_NOT_NULL("100004", "产品id不能为空"),
FEATURE_RESOURCE_NOT_FOUND("100005", "菜单资源不存在");
REDIS_PRODUCT_NOT_NULL("100004", "产品不能为空"),
FEATURE_RESOURCE_NOT_FOUND("100005", "菜单资源不存在"),
WORKSPACE_ID_NOT_NULL("100006", "项目id不能为空");
private String errorCode;
private String errorMessage;

View File

@ -5,6 +5,7 @@ import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum;
import cn.axzo.basics.common.util.TreeUtil;
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.req.CommonDictQueryReq;
import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq;
@ -17,6 +18,8 @@ import cn.axzo.tyr.client.model.res.FeatureResourceDTO;
import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode;
import cn.axzo.tyr.client.model.res.SaasRoleRes;
import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO;
import cn.axzo.tyr.server.event.outer.CacheWorkspaceProductHandler;
import cn.axzo.tyr.server.event.payload.ServicePkgProductCreatedPayload;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao;
import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao;
@ -40,6 +43,7 @@ import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService;
import cn.axzo.tyr.server.service.SaasRoleGroupService;
import cn.axzo.tyr.server.service.TyrSaasAuthService;
import cn.axzo.tyr.server.service.WorkspaceProductService;
import cn.axzo.tyr.server.service.impl.SaasFeatureResourceCacheService;
import cn.axzo.tyr.server.util.FeatureCodeUtil;
import com.alibaba.fastjson.JSON;
@ -112,7 +116,10 @@ public class PrivateController {
private ProductFeatureRelationService productFeatureRelationService;
@Autowired
private FeatureCodeUtil featureCodeUtil;
@Autowired
private WorkspaceProductService workspaceProductService;
@Autowired
private CacheWorkspaceProductHandler cacheWorkspaceProductHandler;
/**
* 统一层级的roleGroup按照id升序sort从1递增
@ -567,6 +574,26 @@ public class PrivateController {
return featureCodeUtil.resolveFeatureCode(request.getFeatureCodes());
}
@PostMapping("/api/private/workspaceProduct/store")
public Object storeWorkspaceProduct(@RequestBody WorkspaceProductService.StoreWorkspaceProductParam request) {
workspaceProductService.storeWorkspaceProduct(request);
return "ok";
}
@PostMapping("/api/private/workspaceProduct/list")
public Object listWorkspaceProduct(@RequestBody WorkspaceProductService.WorkspaceProductParam request) {
return workspaceProductService.listWorkspaceProductCached(request);
}
@PostMapping("/api/private/cacheWorkspaceProductHandler/process")
public Object processCacheWorkspaceProductHandler(@RequestBody ServicePkgProductCreatedPayload request) {
Event event = Event.builder()
.data(request)
.build();
cacheWorkspaceProductHandler.onEvent(event, null);
return "ok";
}
@Data
@Builder
@NoArgsConstructor

View File

@ -63,7 +63,6 @@ public class TyrSaasAuthController implements TyrSaasAuthApi {
}
return ApiResult.ok(tyrSaasAuthService.findIdentityAuthMix(identityAuthReq));
}
@Override
public ApiResult<Boolean> hasPermissionForIdentityV2(CheckIdentityPermissionReq req) {
//单独做下参数校验

View File

@ -0,0 +1,58 @@
package cn.axzo.tyr.server.event.outer;
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.ServicePkgProductCreatedPayload;
import cn.axzo.tyr.server.service.WorkspaceProductService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 缓存项目的产品
*/
@Slf4j
@Component
public class CacheWorkspaceProductHandler implements EventHandler, InitializingBean {
@Autowired
private EventConsumer eventConsumer;
@Autowired
private WorkspaceProductService workspaceProductService;
@Override
public void onEvent(Event event, EventConsumer.Context context) {
log.info("begin cached workspace product handler rocketmq event: {}", event);
ServicePkgProductCreatedPayload payload = event.normalizedData(ServicePkgProductCreatedPayload.class);
if (Objects.isNull(payload) || CollectionUtils.isEmpty(payload.getSaasServicePkgProducts())) {
return;
}
WorkspaceProductService.WorkspaceProductDTO workspaceProductDTO = WorkspaceProductService.WorkspaceProductDTO.builder()
.workspaceId(payload.getSaasServicePackage().getSpaceId())
.productIds(payload.getSaasServicePkgProducts().stream()
.map(ServicePkgProductCreatedPayload.SaasServicePkgProductPo::getProductId)
.collect(Collectors.toSet()))
.build();
WorkspaceProductService.StoreWorkspaceProductParam storeWorkspaceProductParam = WorkspaceProductService.StoreWorkspaceProductParam.builder()
.workspaceProducts(Lists.newArrayList(workspaceProductDTO))
.build();
workspaceProductService.storeWorkspaceProduct(storeWorkspaceProductParam);
log.info("end cached workspace product handler rocketmq event: {}", event);
}
@Override
public void afterPropertiesSet() throws Exception {
eventConsumer.registerHandler(EventTypeEnum.SERVICE_PKG_PRODUCT_CREATED.getEventCode(), this);
}
}

View File

@ -0,0 +1,31 @@
package cn.axzo.tyr.server.event.outer;
import cn.axzo.framework.rocketmq.Event;
import lombok.Getter;
/**
* @Classname EventTypeEnum
* @Date 2021/2/7 6:05 下午
* @Created by lilong
*/
@Getter
public enum EventTypeEnum {
SERVICE_PKG_PRODUCT_CREATED("service-pkg-product", "service-pkg-product-created", "创建服务包的产品"),
;
EventTypeEnum(String model, String name, String desc) {
this.eventCode = Event.EventCode.builder()
.module(model)
.name(name)
.build();
this.model = model;
this.name = name;
this.desc = desc;
}
private String model;
private String name;
private String desc;
private Event.EventCode eventCode;
}

View File

@ -0,0 +1,99 @@
package cn.axzo.tyr.server.event.payload;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ServicePkgProductCreatedPayload implements Serializable {
private SaasServicePackagePo saasServicePackage;
private List<SaasServicePkgProductPo> saasServicePkgProducts;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SaasServicePackagePo {
private Long id;
/**
* 单位ID
*/
private Long ouId;
/**
* 租户id
*/
private Long saasTenantId;
/**
* 服务包类型: 1.企业空间 2.项目空间 3.政企空间6.OMS空间
*/
private Integer type;
/**
* 空间id
*/
private Long spaceId;
/**
* license 名称
*/
private String name;
/**
* 有效期开始
*/
private LocalDateTime effectiveDate;
/**
* 有效斯结束
*/
private LocalDateTime ineffectiveDate;
/**
* license状态 : 1.未生效 2.有效 3.限制使用 4.停用 5.过期
*/
private Integer status;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SaasServicePkgProductPo {
private Long id;
/**
* license id
*/
private Long servicePkgId;
/**
* license相关联的产品
*/
private Long productId;
/**
* 产品功能名称
*/
private String productName;
/**
* 0是基础产品 1非基础产品
*/
private Boolean isBasic;
}
}

View File

@ -33,6 +33,8 @@ public class PermissionCacheKey {
private Boolean disableAll;
public String buildAuthKey() {
// 因为personIdidentityIdidentityType这三个参数用户现在可以随意组合
//
return personId == null ? KeyUtil.buildKeyBySeparator("auth-i", identityId, identityType.getCode(), ouId, workspaceId)
: KeyUtil.buildKeyBySeparator("auth-p", personId, ouId, workspaceId);
}

View File

@ -16,6 +16,19 @@ public interface WorkspaceProductService {
List<WorkspaceProduct> listWorkspaceProduct(WorkspaceProductParam param);
/**
* 从缓存中查询项目的产品
* @param param
* @return
*/
List<WorkspaceProduct> listWorkspaceProductCached(WorkspaceProductParam param);
/**
* 缓存项目的产品
* @param param
*/
void storeWorkspaceProduct(StoreWorkspaceProductParam param);
@Data
@Builder
@NoArgsConstructor
@ -60,5 +73,42 @@ public interface WorkspaceProductService {
*/
private List<SaasProductModuleFeatureRelation> saasProductModuleFeatureRelations;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class StoreWorkspaceProductParam {
private List<WorkspaceProductDTO> workspaceProducts;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class WorkspaceProductDTO {
/**
* 项目id
*/
private Long workspaceId;
/**
* 产品id
*/
private Set<Long> productIds;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class ListWorkspaceProductParam {
/**
* 项目id
*/
private Set<Long> workspaceIds;
}
}

View File

@ -1407,7 +1407,7 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
FeatureIdPair.builder().featureIds(featureIds).type(NEW_FEATURE).build()
))
.build();
Set<SaasProductModuleFeatureRelation> workspaceProductFeatures = workspaceProductService.listWorkspaceProduct(workspaceProductParam).stream()
Set<SaasProductModuleFeatureRelation> workspaceProductFeatures = workspaceProductService.listWorkspaceProductCached(workspaceProductParam).stream()
.map(WorkspaceProductService.WorkspaceProduct::getSaasProductModuleFeatureRelations)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
@ -1468,7 +1468,7 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
FeatureIdPair.builder().featureIds(featureIds).type(OLD_FEATURE).build()
))
.build();
Set<SaasProductModuleFeatureRelation> workspaceProductFeatures = workspaceProductService.listWorkspaceProduct(workspaceProductParam).stream()
Set<SaasProductModuleFeatureRelation> workspaceProductFeatures = workspaceProductService.listWorkspaceProductCached(workspaceProductParam).stream()
.map(WorkspaceProductService.WorkspaceProduct::getSaasProductModuleFeatureRelations)
.filter(Objects::nonNull)
.flatMap(Collection::stream)

View File

@ -1,6 +1,8 @@
package cn.axzo.tyr.server.service.impl;
import cn.axzo.foundation.exception.Axssert;
import cn.axzo.pokonyan.config.mybatisplus.BaseEntity;
import cn.axzo.pokonyan.config.redis.RedisUtil;
import cn.axzo.thrones.client.saas.ServicePkgClient;
import cn.axzo.thrones.client.saas.entity.serivicepgkproduct.ServicePkgProduct;
import cn.axzo.thrones.client.saas.entity.servicepkg.ServicePkgDetailRes;
@ -12,9 +14,20 @@ import cn.axzo.tyr.server.service.WorkspaceProductService;
import cn.axzo.tyr.server.utils.RpcInternalUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
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 java.util.Collection;
@ -23,8 +36,12 @@ 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.REDIS_PRODUCT_NOT_NULL;
import static cn.axzo.tyr.server.config.exception.BizResultCode.WORKSPACE_ID_NOT_NULL;
@Slf4j
@Service
public class WorkspaceProductServiceImpl implements WorkspaceProductService {
@ -35,6 +52,14 @@ public class WorkspaceProductServiceImpl implements WorkspaceProductService {
private ProductModuleDao productModuleDao;
@Autowired
private ProductFeatureRelationService productFeatureRelationService;
@Autowired
protected StringRedisTemplate redisTemplate;
/** 授权缓存过期时间 **/
@Value("${workspace.product.expire:90}")
private Long expireInDays;
private static final String WORKSPACE_PRODUCT_KEY = "workspace:product:%s";
@Override
public List<WorkspaceProduct> listWorkspaceProduct(WorkspaceProductParam param) {
@ -104,4 +129,162 @@ public class WorkspaceProductServiceImpl implements WorkspaceProductService {
.build())
.collect(Collectors.toList());
}
@Override
public List<WorkspaceProduct> listWorkspaceProductCached(WorkspaceProductParam param) {
if (CollectionUtils.isEmpty(param.getWorkspaceIds())) {
return Collections.emptyList();
}
ListWorkspaceProductParam listWorkspaceProductParam = ListWorkspaceProductParam.builder()
.workspaceIds(param.getWorkspaceIds())
.build();
Map<Long, Set<Long>> workspaceProducts = listWorkspaceProduct(listWorkspaceProductParam);
// 存在项目没有在缓存中查询到产品的情况
fillCacheWorkspaseProducts(param, workspaceProducts);
Set<Long> productIds = workspaceProducts.values().stream()
.flatMap(Collection::stream)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(productIds)) {
return Collections.emptyList();
}
// 已被删除产品过滤一层
Set<Long> finalProductIds = productModuleDao.listByIds(productIds)
.stream()
.filter(productModule -> Objects.equals(productModule.getIsDelete(),0L))
.map(BaseEntity::getId)
.collect(Collectors.toSet());
if (CollectionUtil.isEmpty(finalProductIds)) {
log.warn("all product is deleted for workspace :{}", param.getWorkspaceIds());
return Collections.emptyList();
}
ProductFeatureQuery productFeatureQuery = ProductFeatureQuery.builder()
.productIds(finalProductIds)
.featureResourceTypes(param.getFeatureResourceTypes())
.terminal(param.getTerminal())
.type(param.getType())
.featureIdPairs(param.getFeatureIdPairs())
.build();
Map<Long, List<SaasProductModuleFeatureRelation>> saasProductModuleFeatureRelations = productFeatureRelationService.queryOnCondition(productFeatureQuery).stream()
.collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId));
return workspaceProducts.entrySet().stream()
.filter(e -> CollectionUtils.isNotEmpty(e.getValue()))
.map(e -> {
List<SaasProductModuleFeatureRelation> features = e.getValue().stream()
.map(saasProductModuleFeatureRelations::get)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toList());
return WorkspaceProduct.builder()
.workspaceId(e.getKey())
.saasProductModuleFeatureRelations(features)
.build();
})
.collect(Collectors.toList());
}
@Override
public void storeWorkspaceProduct(StoreWorkspaceProductParam param) {
Axssert.checkNotEmpty(param.getWorkspaceProducts(), REDIS_PRODUCT_NOT_NULL);
redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (WorkspaceProductDTO workspaceProduct : param.getWorkspaceProducts()) {
String redisKey = getKey(workspaceProduct.getWorkspaceId());
RedisUtil.StringValueOps.setEx(redisKey, JSON.toJSONString(workspaceProduct.getProductIds()),
expireInDays, TimeUnit.DAYS);
}
return null;
}
});
}
private Map<Long, Set<Long>> listWorkspaceProduct(ListWorkspaceProductParam param) {
Axssert.checkNotEmpty(param.getWorkspaceIds(), WORKSPACE_ID_NOT_NULL);
List<String> redisKeys = param.getWorkspaceIds().stream()
.map(this::getKey)
.collect(Collectors.toList());
List<String> redisValues = redisTemplate.opsForValue().multiGet(redisKeys);
return Streams.zip(param.getWorkspaceIds().stream(),
redisValues.stream(),
(workspaceId, redisValue) -> {
if (StringUtils.isBlank(redisValue)) {
return null;
}
Set<Long> productIds = JSON.parseArray(redisValue.toString())
.stream()
.map(e -> Long.valueOf(e.toString()))
.collect(Collectors.toSet());
return Pair.of(workspaceId, productIds);
})
.filter(Objects::nonNull)
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}
/**
* 组装在缓存中没有查询到产品的项目
* @param param
* @param workspaceProducts
*/
private void fillCacheWorkspaseProducts(WorkspaceProductParam param,
Map<Long, Set<Long>> workspaceProducts) {
Sets.SetView<Long> difference = Sets.difference(param.getWorkspaceIds(), workspaceProducts.keySet());
if (difference.isEmpty()) {
return;
}
List<ServicePkgDetailRes> servicePkgDetailRes = RpcInternalUtil.rpcListProcessor(() -> servicePkgClient.getServicePkgDetailBySpaceId(difference),
"查询项目的产品", difference).getData();
if (CollectionUtil.isEmpty(servicePkgDetailRes)) {
return;
}
List<WorkspaceProductDTO> workspaceProductDTOS = Lists.newArrayList();
servicePkgDetailRes.forEach(e -> {
Set<Long> workpsaceProductIds = e.getProducts().stream()
.map(ServicePkgProduct::getProductId)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(workpsaceProductIds)) {
return;
}
workspaceProducts.put(e.getSpaceId(), workpsaceProductIds);
WorkspaceProductDTO workspaceProductDTO = WorkspaceProductDTO.builder()
.workspaceId(e.getSpaceId())
.productIds(workpsaceProductIds)
.build();
workspaceProductDTOS.add(workspaceProductDTO);
});
if (CollectionUtils.isEmpty(workspaceProductDTOS)) {
return;
}
StoreWorkspaceProductParam storeWorkspaceProductParam = StoreWorkspaceProductParam.builder()
.workspaceProducts(workspaceProductDTOS)
.build();
storeWorkspaceProduct(storeWorkspaceProductParam);
}
private String getKey(Object... params) {
return String.format(WORKSPACE_PRODUCT_KEY, params);
}
}