浏览代码

build 引入redis作为缓存,对接未来的无状态服务

mamingxu 2 天之前
父节点
当前提交
77c165cffc
共有 20 个文件被更改,包括 253 次插入212 次删除
  1. 6 0
      easier-report-biz/pom.xml
  2. 136 48
      easier-report-biz/src/main/java/com/yaoyicloud/config/CommonDataCache.java
  3. 44 32
      easier-report-biz/src/main/java/com/yaoyicloud/config/ReportPathManager.java
  4. 0 16
      easier-report-biz/src/main/java/com/yaoyicloud/config/SessionInterceptor.java
  5. 1 4
      easier-report-biz/src/main/java/com/yaoyicloud/controller/CsoReportController.java
  6. 1 3
      easier-report-biz/src/main/java/com/yaoyicloud/controller/ReportController.java
  7. 4 7
      easier-report-biz/src/main/java/com/yaoyicloud/controller/ReportUpdateController.java
  8. 8 10
      easier-report-biz/src/main/java/com/yaoyicloud/render/ServiceProviderInfoRender.java
  9. 12 20
      easier-report-biz/src/main/java/com/yaoyicloud/render/cso/EntHeaderSectionRender.java
  10. 1 4
      easier-report-biz/src/main/java/com/yaoyicloud/render/platform/AttachmentSectionRender.java
  11. 1 3
      easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/AntiBriberyNewRender.java
  12. 1 3
      easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/BasicInfoNewRender.java
  13. 11 12
      easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/FinancialInfoNewRender.java
  14. 1 3
      easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/PublicRecordNewRender.java
  15. 1 3
      easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/TaxNewRender.java
  16. 3 17
      easier-report-biz/src/main/java/com/yaoyicloud/service/impl/CsoReportServiceImpl.java
  17. 8 15
      easier-report-biz/src/main/java/com/yaoyicloud/service/impl/ReportUpdateServiceImpl.java
  18. 9 1
      easier-report-biz/src/main/java/com/yaoyicloud/tools/DocxUtil.java
  19. 0 11
      easier-report-biz/src/main/java/com/yaoyicloud/tools/PdfSection.java
  20. 5 0
      easier-report-biz/src/main/resources/application.yml

+ 6 - 0
easier-report-biz/pom.xml

@@ -157,6 +157,12 @@
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <version>2.7.18</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 136 - 48
easier-report-biz/src/main/java/com/yaoyicloud/config/CommonDataCache.java

@@ -1,107 +1,195 @@
 package com.yaoyicloud.config;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.collections4.MapUtils;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.Set;
 
 @Component
 public class CommonDataCache {
 
-    // relationId -> 通用数据映射
-    private final ConcurrentMap<String, ConcurrentMap<String, Object>> dataCache
-            = new ConcurrentHashMap<>();
+    // Redis键前缀(区分其他缓存)
+    private static final String KEY_PREFIX = "data:";
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    // Redis Hash操作工具
+    private final HashOperations<String, String, String> hashOps;
+
+    // 构造函数注入RedisTemplate
+    public CommonDataCache(RedisTemplate<String, String> redisTemplate) {
+        this.hashOps = redisTemplate.opsForHash();
+    }
 
     /**
-     *
+     * 生成Redis Hash键(格式:data:{relationId})
      */
-    public void addData(String sessionId, Map<String, Object> data) {
-        if (data != null) {
-            // 使用putIfAbsent避免重复创建空Map
-            dataCache.putIfAbsent(sessionId, new ConcurrentHashMap<>());
-            // 直接替换整个数据Map
-            dataCache.get(sessionId).putAll(data);
-        }
+    private String getRedisKey(String relationId) {
+        return KEY_PREFIX + relationId;
     }
 
     /**
-     * 添加或更新单个通用数据项
+     * 添加或更新单个键值对
      */
-    public void addDataItem(String sessionId, String key, Object value) {
-        dataCache.computeIfAbsent(sessionId, k -> new ConcurrentHashMap<>())
-                .put(key, value);
+    public void addData(String relationId, String key, Object value) {
+        String redisKey = getRedisKey(relationId);
+        try {
+            // 序列化value并写入Hash
+            hashOps.put(redisKey, key, objectMapper.writeValueAsString(value));
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException("序列化数据失败: " + key, e);
+        }
     }
 
     /**
-     * 获取通用数据
+     * 批量添加
+     */
+    public void addDataMap(String relationId, Map<String, Object> dataMap) {
+        if (MapUtils.isEmpty(dataMap)) {
+            return;
+        }
+        String redisKey = getRedisKey(relationId);
+        // 对 map 中的每个 value 进行 JSON 序列化
+        Map<String, String> serializedMap = new HashMap<>();
+        for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
+            try {
+                serializedMap.put(entry.getKey(), objectMapper.writeValueAsString(entry.getValue()));
+            } catch (JsonProcessingException e) {
+                throw new RuntimeException("序列化数据失败: " + entry.getKey(), e);
+            }
+        }
+        // 存入序列化后的 map
+        hashOps.putAll(redisKey, serializedMap);
+    }
+    /**
+     * 获取指定relationId的所有键值对
      */
-    public Map<String, Object> getData(String sessionId) {
-        ConcurrentMap<String, Object> sessionData = dataCache.get(sessionId);
-        return sessionData != null ? Collections.unmodifiableMap(sessionData) : Collections.emptyMap();
+    public Map<String, Object> getData(String relationId) {
+        String redisKey = getRedisKey(relationId);
+        // 获取Hash中所有field和value
+        Map<String, String> hashEntries = hashOps.entries(redisKey);
+        if (hashEntries.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        // 反序列化为Object
+        Map<String, Object> result = new HashMap<>();
+        for (Map.Entry<String, String> entry : hashEntries.entrySet()) {
+            try {
+                result.put(entry.getKey(), objectMapper.readValue(entry.getValue(), Object.class));
+            } catch (Exception e) {
+                throw new RuntimeException("反序列化数据失败: " + entry.getKey(), e);
+            }
+        }
+        return Collections.unmodifiableMap(result); // 返回不可修改的视图
     }
 
     /**
-     * 获取通用数据项
+     * 获取单个键的值(原始Object类型)
      */
-    public Object getDataItem(String sessionId, String key) {
-        ConcurrentMap<String, Object> sessionData = dataCache.get(sessionId);
-        return sessionData != null ? sessionData.get(key) : null;
+    public Object getDataItem(String relationId, String key) {
+        String redisKey = getRedisKey(relationId);
+        String valueStr = hashOps.get(redisKey, key);
+        if (valueStr == null) {
+            return null;
+        }
+        try {
+            return objectMapper.readValue(valueStr, Object.class);
+        } catch (Exception e) {
+            throw new RuntimeException("反序列化数据失败: " + key, e);
+        }
     }
 
     /**
-     * 获取指定类型的通用数据项
+     * 获取指定类型的单个键值
      */
     @SuppressWarnings("unchecked")
-    public <T> T getDataItem(String sessionId, String key, Class<T> type) {
-        Object value = getDataItem(sessionId, key);
-        return type.isInstance(value) ? (T) value : null;
+    public <T> T getDataItem(String relationId, String key, Class<T> type) {
+        Object value = getDataItem(relationId, key);
+        if (value == null) {
+            return null;
+        }
+        // 若类型匹配,直接强转;否则反序列化为指定类型
+        if (type.isInstance(value)) {
+            return (T) value;
+        } else {
+            try {
+                return objectMapper.convertValue(value, type);
+            } catch (Exception e) {
+                throw new RuntimeException("类型转换失败: " + key + " to " + type.getName(), e);
+            }
+        }
     }
 
     /**
-     * 移除指定session的通用数据
+     * 移除指定relationId的所有数据,并返回被移除的数据
      */
-    public Map<String, Object> removeSessionData(String sessionId) {
-        ConcurrentMap<String, Object> removed = dataCache.remove(sessionId);
-        return removed != null ? new HashMap<>(removed) : Collections.emptyMap();
+    public Map<String, Object> removeSessionData(String relationId) {
+        String redisKey = getRedisKey(relationId);
+        // 先获取数据(用于返回),再删除Hash键
+        Map<String, Object> removedData = getData(relationId);
+        hashOps.getOperations().delete(redisKey); // 删除整个Hash
+        return removedData;
     }
 
     /**
-     * 移除指定session中的某个数据项
+     * 移除指定relationId中的单个键值
      */
-    public Object removeDataItem(String sessionId, String key) {
-        ConcurrentMap<String, Object> sessionData = dataCache.get(sessionId);
-        return sessionData != null ? sessionData.remove(key) : null;
+    public Object removeDataItem(String relationId, String key) {
+        String redisKey = getRedisKey(relationId);
+        // 先获取值(用于返回),再删除field
+        Object value = getDataItem(relationId, key);
+        hashOps.delete(redisKey, key); // 删除Hash中的某个field
+        return value;
     }
 
     /**
-     * 检查是否存在指定session的通用数据
+     * 检查relationId是否存在
      */
-    public boolean hasSessionData(String sessionId) {
-        return dataCache.containsKey(sessionId);
+    public boolean hasSessionData(String relationId) {
+        String redisKey = getRedisKey(relationId);
+        // 判断Hash键是否存在,且至少有一个field
+        return hashOps.size(redisKey) > 0;
     }
 
     /**
-     * 检查指定session中是否存在某个数据项
+     * 检查指定relationId中是否存在某个键
      */
-    public boolean hasDataItem(String sessionId, String key) {
-        ConcurrentMap<String, Object> sessionData = dataCache.get(sessionId);
-        return sessionData != null && sessionData.containsKey(key);
+    public boolean hasDataItem(String relationId, String key) {
+        String redisKey = getRedisKey(relationId);
+        return hashOps.hasKey(redisKey, key);
     }
 
     /**
-     * 获取所有sessionId
+     * 获取所有relationId(注意:生产环境慎用KEYS命令)
      */
-    public java.util.Set<String> getAllSessionIds() {
-        return dataCache.keySet();
+    public Set<String> getAllrelationIds() {
+        String pattern = KEY_PREFIX + "*";
+        Set<String> redisKeys = hashOps.getOperations().keys(pattern);
+        if (redisKeys == null || redisKeys.isEmpty()) {
+            return Collections.emptySet();
+        }
+        // 从Redis键中提取relationId(去掉前缀"cache:")
+        Set<String> relationIds = new HashSet<>();
+        for (String key : redisKeys) {
+            relationIds.add(key.substring(KEY_PREFIX.length()));
+        }
+        return relationIds;
     }
 
     /**
      * 清空所有缓存数据
      */
     public void clear() {
-        dataCache.clear();
+        String pattern = KEY_PREFIX + "*";
+        Set<String> redisKeys = hashOps.getOperations().keys(pattern);
+        if (redisKeys != null && !redisKeys.isEmpty()) {
+            hashOps.getOperations().delete(redisKeys);
+        }
     }
 }

+ 44 - 32
easier-report-biz/src/main/java/com/yaoyicloud/config/ReportPathManager.java

@@ -1,70 +1,77 @@
 package com.yaoyicloud.config;
-
+import org.springframework.data.redis.core.ListOperations;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
-import java.util.ArrayList;
+
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * 报告路径管理器
- */
 @Component
 public class ReportPathManager {
 
-    // sessionId -> 报告路径队列(使用ConcurrentLinkedQueue保证有序且线程安全)
-    private final ConcurrentMap<String, ConcurrentLinkedQueue<String>> sessionReportMap = new ConcurrentHashMap<>();
+    private static final String KEY_PREFIX = "reports:";
+
+
+    private final RedisTemplate<String, String> redisTemplate;
+    private final ListOperations<String, String> listOps;
+
+    public ReportPathManager(RedisTemplate<String, String> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+        this.listOps = redisTemplate.opsForList();
+    }
+
+    /**
+     * 生成Redis键:session:{sessionId}:reports
+     */
+    private String getReportKey(String sessionId) {
+        return KEY_PREFIX + sessionId;
+    }
 
     /**
      * 添加单个报告路径(按插入顺序有序)
      */
     public void addReportPath(String sessionId, String reportPath) {
-        // computeIfAbsent 确保原子性初始化
-        sessionReportMap.computeIfAbsent(sessionId, k -> new ConcurrentLinkedQueue<>())
-                .offer(reportPath);
+        String key = getReportKey(sessionId);
+        listOps.rightPush(key, reportPath);
     }
 
     /**
      * 批量添加报告路径(按集合迭代顺序插入,保证整体有序性)
      */
     public void addReportPaths(String sessionId, Collection<String> reportPaths) {
-        ConcurrentLinkedQueue<String> queue = sessionReportMap.computeIfAbsent(sessionId, k -> new ConcurrentLinkedQueue<>());
-        queue.addAll(reportPaths);
+        String key = getReportKey(sessionId);
+        listOps.rightPushAll(key, reportPaths.toArray(new String[0]));
     }
 
     /**
      * 获取指定sessionId下的所有报告路径(按插入顺序返回)
      */
     public List<String> getReportPaths(String sessionId) {
-        ConcurrentLinkedQueue<String> queue = sessionReportMap.get(sessionId);
-        return queue != null ? new ArrayList<>(queue) : Collections.emptyList();
+        String key = getReportKey(sessionId);
+        return listOps.range(key, 0, -1);
     }
 
     /**
      * 检查sessionId是否存在
      */
     public boolean hasSession(String sessionId) {
-        return sessionReportMap.containsKey(sessionId);
+        String key = getReportKey(sessionId);
+        return redisTemplate.hasKey(key);
     }
 
-    /**
-     * 获取所有sessionId
-     */
-    public Set<String> getAllSessions() {
-        return sessionReportMap.keySet();
-    }
 
     /**
-     * 获取所有报告路径的总数
+     * 获取所有报告路径的总数(注意:需遍历所有key,可能影响性能)
      */
     public int getTotalReportCount() {
-        return sessionReportMap.values().stream()
-                .mapToInt(ConcurrentLinkedQueue::size)
+        Set<String> keys = redisTemplate.keys(KEY_PREFIX + "*");
+        if (keys == null || keys.isEmpty()) {
+            return 0;
+        }
+        return keys.stream()
+                .mapToInt(key -> listOps.size(key).intValue())
                 .sum();
     }
 
@@ -72,14 +79,19 @@ public class ReportPathManager {
      * 移除指定sessionId及其所有报告路径
      */
     public List<String> removeSession(String sessionId) {
-        ConcurrentLinkedQueue<String> removedQueue = sessionReportMap.remove(sessionId);
-        return removedQueue != null ? new ArrayList<>(removedQueue) : Collections.emptyList();
+        String key = getReportKey(sessionId);
+        List<String> result = listOps.range(key, 0, -1);
+        redisTemplate.delete(key);
+        return result != null ? result : Collections.emptyList();
     }
 
     /**
-     * 清空所有数据
+     * 清空所有数据(注意:会删除所有以session:开头的键)
      */
     public void clear() {
-        sessionReportMap.clear();
+        Set<String> keys = redisTemplate.keys(KEY_PREFIX + "*");
+        if (keys != null && !keys.isEmpty()) {
+            redisTemplate.delete(keys);
+        }
     }
 }

+ 0 - 16
easier-report-biz/src/main/java/com/yaoyicloud/config/SessionInterceptor.java

@@ -1,9 +1,7 @@
 package com.yaoyicloud.config;
 
 import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
 import org.springframework.web.servlet.HandlerInterceptor;
-import org.springframework.web.servlet.ModelAndView;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
@@ -30,18 +28,4 @@ public class SessionInterceptor implements HandlerInterceptor {
         return true;
     }
 
-    @Override
-    public void postHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
-        @NonNull Object handler,
-        @Nullable ModelAndView modelAndView) throws Exception {
-        // 这里可以添加请求处理后的逻辑,目前示例中没有具体操作
-    }
-
-    @Override
-    public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
-        @NonNull Object handler, @Nullable Exception ex)
-        throws Exception {
-        // 这里可以添加请求完成后的逻辑,目前示例中没有具体操作
-    }
-
 }

+ 1 - 4
easier-report-biz/src/main/java/com/yaoyicloud/controller/CsoReportController.java

@@ -54,16 +54,13 @@ public class CsoReportController {
             );
             String sessionId = SESSION_MAP.get(relationId);
 
-
             // 3. 构建响应
             Map<String, Object> response = new HashMap<>();
             // 根据是否是最后一个模块决定返回哪个路径
             if (plusVersionCheckReport.isLastModule() && plusVersionCheckReport.getMergedReportPath() != null) {
                 response.put("reportResult", plusVersionCheckReport.getMergedReportPath());
                 response.put("isFinalReport", true);
-                reportPathManager.removeSession(sessionId);
-                commonDataCache.removeSessionData(sessionId);
-
+                commonDataCache.removeSessionData(relationId);
             } else {
                 response.put("reportResult", plusVersionCheckReport.getModulePath());
                 response.put("isFinalReport", false);

+ 1 - 3
easier-report-biz/src/main/java/com/yaoyicloud/controller/ReportController.java

@@ -53,8 +53,6 @@ public class ReportController {
                     resource.getReportBastPath(),
                     resource.getRelationId()
             );
-            //把结果保存到session中 一个relationId一个session 这样就把多个结果都保存到一个session中,可以获取路径做拼接
-            //后续 session.removeAttribute清理数据。
             HttpSession session = request.getSession();
             String relationId = String.valueOf(resource.getRelationId());
             @SuppressWarnings("unchecked")
@@ -66,7 +64,7 @@ public class ReportController {
             }
             Map<String, Object> response = new HashMap<>();
             response.put("reportResult", reportPath);
-            response.put("sessionId", request.getSession().getId()); // Use the provided session ID
+            response.put("sessionId", request.getSession().getId());
             return response;
         }
     }

+ 4 - 7
easier-report-biz/src/main/java/com/yaoyicloud/controller/ReportUpdateController.java

@@ -3,7 +3,7 @@ package com.yaoyicloud.controller;
 
 import com.yaoyicloud.annotation.EasierLog;
 import com.yaoyicloud.config.CommonDataCache;
-import com.yaoyicloud.config.ReportPathManager;
+import com.yaoyicloud.config.RelationCounterRedisUtil;
 import com.yaoyicloud.dto.ReportDTO;
 import com.yaoyicloud.entity.ReportGenerationResult;
 import com.yaoyicloud.service.ReportUpdateService;
@@ -36,7 +36,7 @@ import static com.yaoyicloud.config.SessionInterceptor.SESSION_MAP;
 @Slf4j
 public class ReportUpdateController {
     private final ReportUpdateService reportService;
-    private final ReportPathManager reportPathManager;
+    private final RelationCounterRedisUtil relationCounterRedisUtil;
     private final CommonDataCache commonDataCache;
     /**
      * 创建Plus版本审核报告
@@ -59,18 +59,15 @@ public class ReportUpdateController {
                 Long.valueOf(relationId),
                 resource.getModuleType()
         );
-
         String sessionId = SESSION_MAP.get(relationId);
-
-
         // 3. 构建响应
         Map<String, Object> response = new HashMap<>();
         // 根据是否是最后一个模块决定返回哪个路径
         if (plusVersionCheckReport.isLastModule() && plusVersionCheckReport.getMergedReportPath() != null) {
             response.put("reportResult", plusVersionCheckReport.getMergedReportPath());
             response.put("isFinalReport", true);
-            reportPathManager.removeSession(sessionId);
-            commonDataCache.removeSessionData(sessionId);
+            commonDataCache.removeSessionData(relationId);
+            relationCounterRedisUtil.delete(Long.valueOf(relationId));
         } else {
             response.put("reportResult", plusVersionCheckReport.getModulePath());
             response.put("isFinalReport", false);

+ 8 - 10
easier-report-biz/src/main/java/com/yaoyicloud/render/ServiceProviderInfoRender.java

@@ -2,10 +2,8 @@ package com.yaoyicloud.render;
 
 import java.io.IOException;
 import java.time.LocalDate;
+import java.util.HashMap;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
@@ -28,7 +26,8 @@ public final class ServiceProviderInfoRender extends AbstractRender {
     private final FilerepoProperties filerepoProperties;
     private final CommonDataCache commonDataCache;
 
-    public ServiceProviderInfoRender(String cwd, FilerepoProperties filerepoProperties, CommonDataCache commonDataCache) {
+    public ServiceProviderInfoRender(String cwd, FilerepoProperties filerepoProperties,
+        CommonDataCache commonDataCache) {
         super(cwd);
         this.filerepoProperties = filerepoProperties;
         this.commonDataCache = commonDataCache;
@@ -54,7 +53,7 @@ public final class ServiceProviderInfoRender extends AbstractRender {
      */
 
     public String renderDocx(String info, byte[] templateFileContent,
-        String relationId,  AtomicInteger counter) throws IOException {
+        String relationId, Integer counter) throws IOException {
         log.info("开始渲染公共封面模块,relationId: {}", relationId);
 
         // 配置POI-TL渲染器
@@ -78,13 +77,14 @@ public final class ServiceProviderInfoRender extends AbstractRender {
         String tenantName = (String) data.get("tenantName");
         String name = (String) data.get("serviceProviderName");
         String type = (String) data.get("type");
-        String reportDate =  LocalDate.now().toString();
-        ConcurrentMap<String, Object> commonData = new ConcurrentHashMap<>();
+        String reportDate = LocalDate.now().toString();
+        HashMap<String, Object> commonData = new HashMap<>();
         commonData.put("tenantName", tenantName);
         commonData.put("type", type);
         commonData.put("reportDate", reportDate);
         commonData.put("name", name);
-        commonDataCache.addData(relationId, commonData);
+
+        commonDataCache.addDataMap(relationId, commonData);
         data.put("levelInteger", counter);
         data.putAll(commonData);
         fillDefaultValues(data);
@@ -93,8 +93,6 @@ public final class ServiceProviderInfoRender extends AbstractRender {
             String resultPath = this.renderDocx(data, templateFileContent, builder, relationId, "serviceProviderInfo");
             log.info("渲染公共封面成功,文件路径: {}", resultPath);
 
-
-
             return resultPath;
         } catch (Exception e) {
             log.error("渲染公共封面失败,relationId: {}", relationId, e);

+ 12 - 20
easier-report-biz/src/main/java/com/yaoyicloud/render/cso/EntHeaderSectionRender.java

@@ -3,9 +3,6 @@ package com.yaoyicloud.render.cso;
 import java.io.IOException;
 import java.time.LocalDateTime;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
 import cn.hutool.core.date.DateUtil;
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
@@ -69,28 +66,23 @@ public final class EntHeaderSectionRender extends AbstractRender {
 
         ObjectMapper objectMapper = new ObjectMapper();
         Map<String, Object> data = objectMapper.readValue(completeJson, new TypeReference<Map<String, Object>>() {});
-        ConcurrentMap<String, Object> commonData = new ConcurrentHashMap<>();
-
-        commonData.put("reportCreateTime", DateUtil.format(LocalDateTime.now(), "yyyy年MM月dd日 HH:mm:ss"));
-        commonDataCache.addData(relationId, commonData);
+        commonDataCache.addData(relationId, "reportCreateTime", DateUtil.format(LocalDateTime.now(), "yyyy年MM月dd日 HH:mm:ss"));
         Map<String, Object> commonDataCacheData = commonDataCache.getData(relationId);
         if (MapUtils.isNotEmpty(commonDataCacheData)) {
             data.putAll(commonDataCacheData);
         }
-        commonData.put("totalPackageScore", data.get("totalPackageScore"));
-        commonData.put("totalApprovedScore", data.get("totalApprovedScore"));
-        commonData.put("taskUserNames", data.get("taskUserNames"));
-        commonData.put("submitedTaskScore", data.get("submitedTaskScore"));
-        commonData.put("createTime", data.get("createTime"));
-        commonData.put("serviceEndDate", data.get("serviceEndDate"));
-        commonData.put("sensitiveFlag", data.get("sensitiveFlag"));
-        commonData.put("scorePackageName", data.get("scorePackageName"));
-        commonData.put("entrustingCompany", data.get("entrustingCompany"));
-        commonData.put("taskFinishTime", data.get("taskFinishTime"));
-        commonData.put("serviceCompany", data.get("serviceCompany"));
-
+        commonDataCache.addData(relationId, "totalPackageScore", data.get("totalPackageScore"));
+        commonDataCache.addData(relationId, "totalApprovedScore", data.get("totalApprovedScore"));
+        commonDataCache.addData(relationId, "taskUserNames", data.get("taskUserNames"));
+        commonDataCache.addData(relationId, "submitedTaskScore", data.get("submitedTaskScore"));
+        commonDataCache.addData(relationId, "createTime", data.get("createTime"));
+        commonDataCache.addData(relationId, "serviceEndDate", data.get("serviceEndDate"));
+        commonDataCache.addData(relationId, "sensitiveFlag", data.get("sensitiveFlag"));
+        commonDataCache.addData(relationId, "scorePackageName", data.get("scorePackageName"));
+        commonDataCache.addData(relationId, "entrustingCompany", data.get("entrustingCompany"));
+        commonDataCache.addData(relationId, "taskFinishTime", data.get("taskFinishTime"));
+        commonDataCache.addData(relationId, "serviceCompany", data.get("serviceCompany"));
 
-        commonDataCache.addData(relationId, commonData);
         fillDefaultValues(data);
         try {
             // 渲染文档

+ 1 - 4
easier-report-biz/src/main/java/com/yaoyicloud/render/platform/AttachmentSectionRender.java

@@ -5,9 +5,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.yaoyicloud.config.CommonDataCache;
@@ -54,7 +51,7 @@ public final class AttachmentSectionRender extends AbstractRender {
      * @throws IOException
      */
 
-    public String renderDocx(String info, byte[] templateFileContent, String relationId, AtomicInteger counter) throws IOException {
+    public String renderDocx(String info, byte[] templateFileContent, String relationId, Integer counter) throws IOException {
         log.info("开始渲染附件模块,relationId: {}", relationId);
 
         // 配置POI-TL渲染器

+ 1 - 3
easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/AntiBriberyNewRender.java

@@ -2,8 +2,6 @@ package com.yaoyicloud.render.platform.update;
 
 import java.io.IOException;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
 import com.deepoove.poi.policy.RenderPolicy;
@@ -49,7 +47,7 @@ public final class AntiBriberyNewRender extends AbstractNewRender {
      * @return 本地文件目录
      * @throws IOException
      */
-    public String renderDocx(String info, byte[] templateFileContent, String relationId, AtomicInteger counter)
+    public String renderDocx(String info, byte[] templateFileContent, String relationId, Integer counter)
         throws IOException {
         log.info("开始渲染其他风险报告模块,relationId: {}", relationId);
 

+ 1 - 3
easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/BasicInfoNewRender.java

@@ -4,8 +4,6 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
 import com.deepoove.poi.policy.RenderPolicy;
@@ -55,7 +53,7 @@ public final class BasicInfoNewRender extends AbstractNewRender {
      * @throws IOException
      */
     public String renderDocx(String info, byte[] templateFileContent,
-                             String relationId, AtomicInteger counter) throws IOException {
+                             String relationId, Integer counter) throws IOException {
         log.info("开始渲染工商信息报告模块,relationId: {}", relationId);
 
         // 配置POI-TL渲染器

+ 11 - 12
easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/FinancialInfoNewRender.java

@@ -3,7 +3,6 @@ package com.yaoyicloud.render.platform.update;
 import java.io.IOException;
 import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
@@ -18,7 +17,6 @@ import com.yaoyicloud.render.AbstractNewRender;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.MapUtils;
 
-
 /**
  * FinancialInfo渲染器
  *
@@ -27,11 +25,13 @@ import org.apache.commons.collections4.MapUtils;
 public final class FinancialInfoNewRender extends AbstractNewRender {
     private final FilerepoProperties filerepoProperties;
     private final CommonDataCache commonDataCache;
+
     public FinancialInfoNewRender(String cwd, FilerepoProperties filerepoProperties, CommonDataCache commonDataCache) {
         super(cwd);
         this.filerepoProperties = filerepoProperties;
         this.commonDataCache = commonDataCache;
     }
+
     @Override
     protected String getBasicPath() throws IOException {
         return filerepoProperties.getBasePath();
@@ -51,7 +51,8 @@ public final class FinancialInfoNewRender extends AbstractNewRender {
      * @throws IOException
      */
 
-    public String renderDocx(String info, byte[] templateFileContent, String relationId, AtomicInteger counter) throws IOException {
+    public String renderDocx(String info, byte[] templateFileContent, String relationId, Integer counter)
+        throws IOException {
         log.info("开始渲染财务模块,relationId: {}", relationId);
 
         // 配置POI-TL渲染器
@@ -67,25 +68,24 @@ public final class FinancialInfoNewRender extends AbstractNewRender {
 
         FxyProtos.FinancialInfo defaultInstance = FxyProtos.FinancialInfo.getDefaultInstance();
         FxyProtos.FinancialInfo mergedProto = defaultInstance.toBuilder()
-                .mergeFrom(financialInfoBuilder.build())
-                .build();
+            .mergeFrom(financialInfoBuilder.build())
+            .build();
 
         String completeJson = JsonFormat.printer()
-                .includingDefaultValueFields()
-                .print(mergedProto);
+            .includingDefaultValueFields()
+            .print(mergedProto);
         ObjectMapper objectMapper = new ObjectMapper();
         Map<String, Object> data = objectMapper.readValue(completeJson, new TypeReference<Map<String, Object>>() {});
         data.replaceAll((k, v) -> v.equals("") ? "-" : v);
 
-
         fillBasicDefaultValues(data);
         Map<String, Object> commonDataCacheData = commonDataCache.getData(relationId);
         if (MapUtils.isNotEmpty(commonDataCacheData)) {
             data.putAll(commonDataCacheData);
         }
         data.put("levelInteger", counter);
-//        Map<String, Object> financialSummary = (Map<String, Object>) data.get("financialSummary");
-//        data.put("fiancescore", financialSummary.get("score"));
+        // Map<String, Object> financialSummary = (Map<String, Object>) data.get("financialSummary");
+        // data.put("fiancescore", financialSummary.get("score"));
         try {
             // 渲染文档
             String resultPath = this.renderDocx(data, templateFileContent, builder, relationId, "financialInfo");
@@ -96,6 +96,7 @@ public final class FinancialInfoNewRender extends AbstractNewRender {
             throw new IOException("文档渲染失败", e);
         }
     }
+
     /**
      * 填充默认值,确保所有必要字段都存在
      */
@@ -121,6 +122,4 @@ public final class FinancialInfoNewRender extends AbstractNewRender {
 
     }
 
-
-
 }

+ 1 - 3
easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/PublicRecordNewRender.java

@@ -5,7 +5,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
@@ -19,7 +18,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.MapUtils;
 
-
 @Slf4j
 public final class PublicRecordNewRender extends AbstractNewRender {
     private final FilerepoProperties filerepoProperties;
@@ -51,7 +49,7 @@ public final class PublicRecordNewRender extends AbstractNewRender {
      */
 
     public String renderDocx(String info, byte[] templateFileContent,
-                             String relationId, AtomicInteger counter) throws IOException {
+        String relationId, Integer counter) throws IOException {
         log.info("开始渲染司法风险模块,relationId: {}", relationId);
 
         // 配置POI-TL渲染器

+ 1 - 3
easier-report-biz/src/main/java/com/yaoyicloud/render/platform/update/TaxNewRender.java

@@ -5,8 +5,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
 import com.yaoyicloud.config.CommonDataCache;
 import com.yaoyicloud.message.FxyProtos;
 import org.apache.commons.collections4.CollectionUtils;
@@ -54,7 +52,7 @@ public final class TaxNewRender extends AbstractNewRender {
      */
 
     public String renderDocx(String info, byte[] templateFileContent,
-        String relationId, AtomicInteger count) throws IOException {
+        String relationId, Integer count) throws IOException {
         log.info("开始渲染税务风险模块,relationId: {}", relationId);
 
         // 配置POI-TL渲染器

+ 3 - 17
easier-report-biz/src/main/java/com/yaoyicloud/service/impl/CsoReportServiceImpl.java

@@ -7,15 +7,10 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
 import com.yaoyicloud.render.cso.EntAppendixRender;
 import com.yaoyicloud.render.cso.EntPromotionDetailsRender;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.springframework.stereotype.Service;
-
 import com.yaoyicloud.config.CommonDataCache;
 import com.yaoyicloud.config.FilerepoProperties;
 import com.yaoyicloud.config.ReportPathManager;
@@ -34,9 +29,6 @@ import lombok.extern.slf4j.Slf4j;
 @Service
 @RequiredArgsConstructor
 public class CsoReportServiceImpl implements CsoReportService {
-    // 使用ConcurrentHashMap存储relationId与计数器的映射
-    private final ConcurrentMap<Long, AtomicInteger> moduleCounters = new ConcurrentHashMap<>();
-
     private final FilerepoProperties filerepoProperties;
     private final ReportPathManager reportPathManager;
     private final CommonDataCache commonDataCache;
@@ -46,7 +38,7 @@ public class CsoReportServiceImpl implements CsoReportService {
      */
     @SuppressWarnings("checkstyle:ParameterNumber")
     private String processModule(ModuleType moduleType, String data,
-        byte[] templateBytes, String sessionId, Long relationId, AtomicInteger counter) throws Exception {
+        byte[] templateBytes, String sessionId, Long relationId) throws Exception {
         String reportPath;
 
         switch (moduleType) {
@@ -81,23 +73,17 @@ public class CsoReportServiceImpl implements CsoReportService {
             fos.write(templateBytes);
         }
 
-        // 获取或初始化计数器
-        AtomicInteger counter = moduleCounters.computeIfAbsent(
-            relationId,
-            k -> new AtomicInteger(1));
         String sessionId = SESSION_MAP.get(relationId.toString());
-        String reportPath = null;
 
-        reportPath = processModule(moduleType, data, templateBytes, sessionId, relationId, counter);
+        String reportPath = processModule(moduleType, data, templateBytes, sessionId, relationId);
         // 处理指定模块
         reportPathManager.addReportPath(sessionId, reportPath);
-        counter.incrementAndGet();
 
         String mergedReportPath = null;
         boolean isLastModule = reportPath.contains("Appendix");
 
         if (isLastModule) {
-            List<String> reportPaths = reportPathManager.getReportPaths(sessionId);
+            List<String> reportPaths = reportPathManager.removeSession(sessionId);
             XWPFDocument targetDoc;
             try (FileInputStream fis = new FileInputStream(reportPaths.get(0))) {
                 targetDoc = new XWPFDocument(fis);

+ 8 - 15
easier-report-biz/src/main/java/com/yaoyicloud/service/impl/ReportUpdateServiceImpl.java

@@ -6,13 +6,11 @@ import java.io.FileOutputStream;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import cn.hutool.core.util.IdUtil;
 import com.yaoyicloud.config.CommonDataCache;
 import com.yaoyicloud.config.FilerepoProperties;
+import com.yaoyicloud.config.RelationCounterRedisUtil;
 import com.yaoyicloud.config.ReportPathManager;
 import com.yaoyicloud.constant.enums.ModuleType;
 import com.yaoyicloud.entity.ReportGenerationResult;
@@ -51,12 +49,11 @@ import static com.yaoyicloud.config.SessionInterceptor.SESSION_MAP;
 @Service
 @RequiredArgsConstructor
 public class ReportUpdateServiceImpl implements ReportUpdateService {
-    // 使用ConcurrentHashMap存储relationId与计数器的映射
-    private final ConcurrentMap<Long, AtomicInteger> moduleCounters = new ConcurrentHashMap<>();
 
     private final FilerepoProperties filerepoProperties;
     private final ReportPathManager reportPathManager;
     private final CommonDataCache commonDataCache;
+    private final RelationCounterRedisUtil relationCounterRedisUtil;
 
     @SuppressWarnings("checkstyle:ReturnCount")
     @Override
@@ -70,20 +67,17 @@ public class ReportUpdateServiceImpl implements ReportUpdateService {
             fos.write(templateBytes);
         }
 
-        // 获取或初始化计数器
-        AtomicInteger counter = moduleCounters.computeIfAbsent(
-            relationId,
-            k -> new AtomicInteger(1));
+        Integer count = relationCounterRedisUtil.getCount(relationId);
         String sessionId = SESSION_MAP.get(relationId.toString());
-        String reportPath = processModule(moduleType, data, templateBytes, sessionId, relationId, counter);
+        String reportPath = processModule(moduleType, data, templateBytes, sessionId, relationId, count);
         // 处理指定模块
         reportPathManager.addReportPath(sessionId, reportPath);
-        counter.incrementAndGet();
+        relationCounterRedisUtil.increment(relationId, 1);
 
         String mergedReportPath = null;
         boolean isLastModule = reportPath.contains("attachmentSection");
         if (isLastModule) {
-            List<String> reportPaths = reportPathManager.getReportPaths(sessionId);
+            List<String> reportPaths = reportPathManager.removeSession(sessionId);
             XWPFDocument targetDoc;
             try (FileInputStream fis = new FileInputStream(reportPaths.get(0))) {
                 targetDoc = new XWPFDocument(fis);
@@ -98,8 +92,7 @@ public class ReportUpdateServiceImpl implements ReportUpdateService {
             mergedReportPath = filerepoProperties.getBasePath() + "/" + label + ".docx";
             // 调用合并方法
             DocxUtil.mergeDocx(targetDoc, sourceDocs, mergedReportPath, relationId);
-            // 清除缓存
-            moduleCounters.remove(relationId);
+
         }
 
         return new ReportGenerationResult(reportPath, mergedReportPath, isLastModule);
@@ -110,7 +103,7 @@ public class ReportUpdateServiceImpl implements ReportUpdateService {
      */
     @SuppressWarnings("checkstyle:ParameterNumber")
     private String processModule(ModuleType moduleType, String data,
-        byte[] templateBytes, String sessionId, Long relationId, AtomicInteger counter) throws Exception {
+        byte[] templateBytes, String sessionId, Long relationId, Integer counter) throws Exception {
         String reportPath;
 
         switch (moduleType) {

+ 9 - 1
easier-report-biz/src/main/java/com/yaoyicloud/tools/DocxUtil.java

@@ -154,6 +154,9 @@ public final class DocxUtil {
     }
 
     private static void appendSourceContent(XWPFDocument target, XWPFDocument source) throws InvalidFormatException {
+        if (!target.getBodyElements().isEmpty()) {
+            insertPageBreak(target);
+        }
         for (IBodyElement element : source.getBodyElements()) {
             if (element instanceof XWPFParagraph) {
                 appendParagraph(target, (XWPFParagraph) element);
@@ -217,5 +220,10 @@ public final class DocxUtil {
         }
     }
 
-
+    // 新增:插入分页符的方法
+    private static void insertPageBreak(XWPFDocument target) {
+        XWPFParagraph paragraph = target.createParagraph();
+        XWPFRun run = paragraph.createRun();
+        run.addBreak(org.apache.poi.xwpf.usermodel.BreakType.PAGE); // 添加分页符
+    }
 }

+ 0 - 11
easier-report-biz/src/main/java/com/yaoyicloud/tools/PdfSection.java

@@ -1,11 +0,0 @@
-package com.yaoyicloud.tools;
-
-import lombok.Data;
-
-@Data
-public class PdfSection {
-    private String title;      // 标题文本(如 "1. 报告概述")
-    private int pageNumber;    // 页码
-    private int level;         // 标题级别(1=一级标题,2=二级标题等)
-
-}

+ 5 - 0
easier-report-biz/src/main/resources/application.yml

@@ -10,6 +10,11 @@ jasypt:
     iv-generator-classname: org.jasypt.iv.NoIvGenerator
 
 spring:
+  redis:
+    host: 127.0.0.1
+    port: 6379
+    password: 123456
+    database: 10
   servlet:
     multipart:
       max-file-size: 100MB