Commit 0a2155eeeb86595efdbd347c5ce46e7b57082438

Authored by 张楣扬
1 parent 3271c6ee

feat(tenant): 集成Redis缓存优化租户管道配置存储

- 添加spring-boot-starter-data-redis依赖支持Redis功能
- 引入JsonUtils和TypeReference用于JSON序列化和反序列化
- 使用StringRedisTemplate替代ConcurrentHashMap存储租户管道配置
- 实现Redis键值结构tax:agent:tenant:pipeline::{tenantId}进行缓存管理
- 添加数据库兜底查询逻辑确保数据一致性
- 实现缓存加载时的Redis写入和日志记录功能
- 优化getTenantPipeline方法通过Redis缓存提高查询性能
tax-central/pom.xml
@@ -31,6 +31,10 @@ @@ -31,6 +31,10 @@
31 <artifactId>spring-boot-starter-validation</artifactId> 31 <artifactId>spring-boot-starter-validation</artifactId>
32 </dependency> 32 </dependency>
33 <dependency> 33 <dependency>
  34 + <groupId>org.springframework.boot</groupId>
  35 + <artifactId>spring-boot-starter-data-redis</artifactId>
  36 + </dependency>
  37 + <dependency>
34 <groupId>com.jayway.jsonpath</groupId> 38 <groupId>com.jayway.jsonpath</groupId>
35 <artifactId>json-path</artifactId> 39 <artifactId>json-path</artifactId>
36 </dependency> 40 </dependency>
tax-central/src/main/java/com/diligrp/tax/central/context/TenantStorageContext.java
@@ -3,17 +3,19 @@ package com.diligrp.tax.central.context; @@ -3,17 +3,19 @@ package com.diligrp.tax.central.context;
3 import com.diligrp.tax.central.model.PipelineDO; 3 import com.diligrp.tax.central.model.PipelineDO;
4 import com.diligrp.tax.central.service.ITenantService; 4 import com.diligrp.tax.central.service.ITenantService;
5 import com.diligrp.tax.central.type.SystemType; 5 import com.diligrp.tax.central.type.SystemType;
  6 +import com.diligrp.tax.central.utils.JsonUtils;
  7 +import com.fasterxml.jackson.core.type.TypeReference;
6 import jakarta.annotation.Resource; 8 import jakarta.annotation.Resource;
7 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
8 import org.springframework.boot.context.event.ApplicationReadyEvent; 10 import org.springframework.boot.context.event.ApplicationReadyEvent;
9 import org.springframework.context.event.EventListener; 11 import org.springframework.context.event.EventListener;
  12 +import org.springframework.data.redis.core.StringRedisTemplate;
10 import org.springframework.stereotype.Component; 13 import org.springframework.stereotype.Component;
11 14
12 import java.util.List; 15 import java.util.List;
13 import java.util.Map; 16 import java.util.Map;
14 import java.util.Objects; 17 import java.util.Objects;
15 import java.util.Optional; 18 import java.util.Optional;
16 -import java.util.concurrent.ConcurrentHashMap;  
17 import java.util.stream.Collectors; 19 import java.util.stream.Collectors;
18 20
19 /** 21 /**
@@ -25,29 +27,51 @@ import java.util.stream.Collectors; @@ -25,29 +27,51 @@ import java.util.stream.Collectors;
25 @Component 27 @Component
26 public class TenantStorageContext { 28 public class TenantStorageContext {
27 29
  30 + // Redis中租户管道配置的Key前缀:tax:agent:tenant:pipeline::{tenantId}
  31 + private static final String TENANT_PIPELINE_KEY_PREFIX = "tax:agent:tenant:pipeline::";
  32 +
28 @Resource 33 @Resource
29 private ITenantService tenantService; 34 private ITenantService tenantService;
30 35
31 - private static final ConcurrentHashMap<Long, Map<SystemType, Map<String, PipelineDO>>> TENANT_PIPELINE_MAP = new ConcurrentHashMap<>(); 36 + @Resource
  37 + private StringRedisTemplate stringRedisTemplate;
  38 +
  39 + private String getTenantPipelineKey(Long tenantId) {
  40 + return TENANT_PIPELINE_KEY_PREFIX + tenantId;
  41 + }
  42 +
  43 + /**
  44 + * 统一数据库兜底查询,并记录查询日志
  45 + * @param tenantId
  46 + * @param pipelineCode
  47 + * @param reason
  48 + * @return
  49 + */
  50 + private Optional<PipelineDO> queryPipelineFromDb(Long tenantId, String pipelineCode, String reason) {
  51 + Optional<PipelineDO> result = tenantService.findByTenantIdAndPipelineCode(tenantId, pipelineCode);
  52 + log.info("统一数据库兜底查询, tenantId:{}, pipelineCode:{}, found:{}", tenantId, pipelineCode, result.isPresent());
  53 + return result;
  54 + }
32 55
33 public Optional<PipelineDO> getTenantPipeline(Long tenantId, SystemType systemType, String pipelineCode) { 56 public Optional<PipelineDO> getTenantPipeline(Long tenantId, SystemType systemType, String pipelineCode) {
34 - var tpMap = TENANT_PIPELINE_MAP.get(tenantId);  
35 - if (Objects.isNull(tpMap)) {  
36 - return Optional.empty();  
37 - }  
38 - Map<String, PipelineDO> pipelineMap = tpMap.get(systemType);  
39 - if (Objects.isNull(pipelineMap)) {  
40 - return Optional.empty();  
41 - }  
42 - return Optional.ofNullable(pipelineMap.get(pipelineCode)).or(() -> tenantService.findByTenantIdAndPipelineCode(tenantId, pipelineCode)); 57 + return Optional.ofNullable(stringRedisTemplate.opsForValue().get(getTenantPipelineKey(tenantId)))
  58 + .map(json -> JsonUtils.fromJsonString(json, new TypeReference<Map<SystemType, Map<String, PipelineDO>>>() {}))
  59 + .map(tpMap -> Optional.ofNullable(tpMap.get(systemType))
  60 + .flatMap(pipelineMap -> Optional.ofNullable(pipelineMap.get(pipelineCode))
  61 + .or(() -> queryPipelineFromDb(tenantId, pipelineCode, "Redis中systemType或pipelineCode未命中"))))
  62 + .orElseGet(() -> queryPipelineFromDb(tenantId, pipelineCode, "Redis租户Key未命中"));
43 } 63 }
44 64
45 public void loadTenantPipeline() { 65 public void loadTenantPipeline() {
46 List<PipelineDO> taxPipelineDOVOS = tenantService.listAllEnablePipeline(); 66 List<PipelineDO> taxPipelineDOVOS = tenantService.listAllEnablePipeline();
47 Optional.ofNullable(taxPipelineDOVOS).ifPresent(pipelines -> { 67 Optional.ofNullable(taxPipelineDOVOS).ifPresent(pipelines -> {
48 var tenantPipelineMap = pipelines.stream().collect(Collectors.groupingBy(PipelineDO::getTenantId, Collectors.groupingBy(PipelineDO::getSystemTypeEnum, Collectors.toMap(PipelineDO::getCode, e -> e)))); 68 var tenantPipelineMap = pipelines.stream().collect(Collectors.groupingBy(PipelineDO::getTenantId, Collectors.groupingBy(PipelineDO::getSystemTypeEnum, Collectors.toMap(PipelineDO::getCode, e -> e))));
49 - TENANT_PIPELINE_MAP.clear();  
50 - TENANT_PIPELINE_MAP.putAll(tenantPipelineMap); 69 + tenantPipelineMap.forEach((tenantId, pipelineMap) -> {
  70 + String tenantPipelineJson = JsonUtils.toJsonString(pipelineMap);
  71 + String redisKey = getTenantPipelineKey(tenantId);
  72 + stringRedisTemplate.opsForValue().set(redisKey, tenantPipelineJson);
  73 + log.info("redis写入缓存, key:{}, tenantId:{}, systemCount:{}", redisKey, tenantId, pipelineMap.size());
  74 + });
51 }); 75 });
52 } 76 }
53 77