瀏覽代碼

build protobuf构建word文档

mamingxu 3 周之前
父節點
當前提交
06abcaef41
共有 18 個文件被更改,包括 1185 次插入965 次删除
  1. 2 6
      easier-report-api/src/main/java/com/yaoyicloud/dto/ReportDTO.java
  2. 5 0
      easier-report-biz/pom.xml
  3. 2 2
      easier-report-biz/src/main/java/com/yaoyicloud/config/SessionInterceptor.java
  4. 32 27
      easier-report-biz/src/main/java/com/yaoyicloud/controller/ReportController.java
  5. 72 13
      easier-report-biz/src/main/java/com/yaoyicloud/render/AbstractRender.java
  6. 91 15
      easier-report-biz/src/main/java/com/yaoyicloud/render/AntiBriberyRender.java
  7. 195 195
      easier-report-biz/src/main/java/com/yaoyicloud/render/AttachmentSectionRender.java
  8. 132 132
      easier-report-biz/src/main/java/com/yaoyicloud/render/AuditResultRender.java
  9. 54 45
      easier-report-biz/src/main/java/com/yaoyicloud/render/BasicInfoRender.java
  10. 225 225
      easier-report-biz/src/main/java/com/yaoyicloud/render/FinancialInfoRender.java
  11. 141 141
      easier-report-biz/src/main/java/com/yaoyicloud/render/PublicRecordRender.java
  12. 80 80
      easier-report-biz/src/main/java/com/yaoyicloud/render/ServiceProviderInfoRender.java
  13. 8 3
      easier-report-biz/src/main/java/com/yaoyicloud/service/ReportService.java
  14. 48 33
      easier-report-biz/src/main/java/com/yaoyicloud/service/impl/ReportServiceImpl.java
  15. 4 4
      easier-report-biz/src/main/resources/application.yml
  16. 90 38
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAntiBriberyRender.java
  17. 4 6
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAttachmentSectionRender.java
  18. 二進制
      easier-report-biz/src/test/resources/docx/antiBribery.docx

+ 2 - 6
easier-report-api/src/main/java/com/yaoyicloud/dto/ReportDTO.java

@@ -7,8 +7,6 @@ import lombok.AccessLevel;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-import java.util.Map;
-
 /**
  * 报告DTO
  *
@@ -29,8 +27,7 @@ public class ReportDTO {
 
 
         @NotNull(message = "map数据必填")
-        private Map<String, Object> data;
-
+        private String data;
 
         @NotNull(message = "模版必填")
         private String reportTempFile;
@@ -41,7 +38,6 @@ public class ReportDTO {
         @NotNull(message = "reportType必填")
         private ReportType reportType;
 
-        @NotNull(message = "reportType必填")
-        private  Long relationId;
+
     }
 }

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

@@ -21,6 +21,11 @@
     </properties>
 
     <dependencies>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-expression</artifactId>
+            <version>5.3.18</version>
+        </dependency>
         <!--server-api-->
         <dependency>
             <groupId>javax.servlet</groupId>

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

@@ -12,13 +12,13 @@ import java.util.Map;
 
 public class SessionInterceptor implements HandlerInterceptor {
 
-    private static final Map<String, HttpSession> SESSION_MAP = new HashMap<>();
+    public static final Map<String, HttpSession> SESSION_MAP = new HashMap<>();
 
     @Override
     public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
         @NonNull Object handler)
         throws Exception {
-        String relationId = request.getParameter("relationId");
+        String relationId = request.getHeader("relationId");
         String mysession = request.getHeader("X-Session-Id");
         if (mysession == null) {
             HttpSession session = request.getSession(true);

+ 32 - 27
easier-report-biz/src/main/java/com/yaoyicloud/controller/ReportController.java

@@ -18,6 +18,9 @@ import java.util.Base64;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+
+import static com.yaoyicloud.config.SessionInterceptor.SESSION_MAP;
 
 
 /**
@@ -39,35 +42,37 @@ public class ReportController {
      */
     @EasierLog("创建Plus版本审核报告")
     @PostMapping("/report/create-report")
-    public Map<String, Object>
-    createPlusVersionCheckReport(@Validated @RequestBody ReportDTO.OnCreatePlusVersionReport resource,
-                                 HttpServletRequest request) throws Exception {
+    public Map<String, Object> createPlusVersionCheckReport(
+            @Validated @RequestBody ReportDTO.OnCreatePlusVersionReport resource,
+            HttpServletRequest request
+    ) throws Exception {
+        // 1. 解码文件并生成报告
         byte[] fileBytes = Base64.getDecoder().decode(resource.getReportTempFile());
+        String relationId = request.getHeader("relationId");
+        String reportPath = reportService.createPlusVersionCheckReport(
+                resource.getReportType(),
+                resource.getData(),
+                fileBytes,
+                resource.getReportBastPath(),
+                Long.valueOf(relationId),
+                request
+        );
+        HttpSession session = SESSION_MAP.get(relationId);
+        // 2. 线程安全地更新 Session 中的报告路径列表
+        synchronized (session) {
+            List<String> reportPaths = Optional.ofNullable(
+                    (List<String>) session.getAttribute(relationId)
+            ).orElseGet(ArrayList::new);
 
-
-        synchronized (this) {
-            String reportPath = reportService.createPlusVersionCheckReport(
-                    resource.getReportType(),
-                    resource.getData(),
-                    fileBytes,
-                    resource.getReportBastPath(),
-                    resource.getRelationId()
-            );
-            //把结果保存到session中 一个relationId一个session 这样就把多个结果都保存到一个session中,可以获取路径做拼接
-            //后续 session.removeAttribute清理数据。
-            HttpSession session = request.getSession();
-            String relationId = String.valueOf(resource.getRelationId());
-            @SuppressWarnings("unchecked")
-            List<String> reportPaths = (List<String>) session.getAttribute(relationId);
-
-            if (reportPaths == null) {
-                reportPaths = new ArrayList<>();
-                session.setAttribute(relationId, reportPaths);
-            }
-            Map<String, Object> response = new HashMap<>();
-            response.put("reportResult", reportPath);
-            response.put("sessionId", request.getSession().getId()); // Use the provided session ID
-            return response;
+            reportPaths.add(reportPath);
+            session.setAttribute(relationId, reportPaths);
         }
+
+        // 3. 返回响应
+        Map<String, Object> response = new HashMap<>();
+        response.put("reportResult", reportPath);
+        response.put("sessionId", session.getId());
+        response.put("reportResult", reportPath);
+        return response;
     }
 }

+ 72 - 13
easier-report-biz/src/main/java/com/yaoyicloud/render/AbstractRender.java

@@ -1,14 +1,19 @@
 package com.yaoyicloud.render;
 
 import java.io.ByteArrayInputStream;
+import java.io.File;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
+import cn.hutool.core.util.IdUtil;
 import com.deepoove.poi.XWPFTemplate;
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
+import com.deepoove.poi.policy.RenderPolicy;
+import com.deepoove.poi.template.ElementTemplate;
 
 /**
  * 抽象渲染器
@@ -43,27 +48,36 @@ public abstract class AbstractRender {
     /**
      * Docx 渲染
      *
-     * @param jsonStr 数据
-     * @param addtionalMap 全局数据和动态增加的数据
+
      * @param templateFileContent 模板内容
      * @return 本地文件目录
      * @throws IOException
      */
-    public final String renderDocx(String jsonStr, Map<String, Object> addtionalMap, byte[] templateFileContent,
-        ConfigureBuilder builder, String outputPath) throws IOException {
+    public final String renderDocx(Map<String, Object> dataMap, byte[] templateFileContent,
+        ConfigureBuilder builder, String relationId) throws IOException {
 
         // 注: 报告模板的模板变量按照json序列化的结果命名
-        ObjectMapper objectMapper = new ObjectMapper();
-        Map<String, Object> data = objectMapper.readValue(jsonStr, new TypeReference<Map<String, Object>>() {});
-        data.putAll(addtionalMap);
+        String basicPath = this.getBasicPath();
+        String reportImagePath = this.getReportImagePath();
+        String label = relationId + "_" + IdUtil.fastSimpleUUID();
+        //word导出位置
+        String reportTempWordFile = basicPath + "/" + cwd + "/" + label + ".docx";
+        // 新增:创建文件夹
+        File parentDir = new File(basicPath + "/" + cwd);
+        if (!parentDir.exists()) {
+            boolean created = parentDir.mkdirs();
+            if (!created) {
+                throw new IOException("无法创建文件保存目录");
+            }
+        }
 
         Configure config = builder.build();
         XWPFTemplate template =
-            XWPFTemplate.compile(new ByteArrayInputStream(templateFileContent), config).render(data);
-        template.writeToFile(outputPath);
+            XWPFTemplate.compile(new ByteArrayInputStream(templateFileContent), config).render(dataMap);
+        template.writeToFile(reportTempWordFile);
         template.close();
 
-        this.docxResultPath = outputPath;
+        this.docxResultPath = reportTempWordFile;
         return this.docxResultPath;
     }
 
@@ -106,5 +120,50 @@ public abstract class AbstractRender {
         this.pdfResultPath = cwd + "/1.pdf";
         return this.pdfResultPath;
     }
-
+    protected abstract String getBasicPath() throws IOException;
+    protected abstract String getReportImagePath();
+    protected RenderPolicy indicatorsRenderPolicy() {
+        return new LoopRowTableRenderPolicy() {
+            @Override
+            public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
+                // 处理数据中的null值
+                Object processedData = processData(data);
+                // 调用父类渲染处理后的数据
+                super.render(eleTemplate, processedData, template);
+            }
+
+            private Object processData(Object data) {
+                if (data == null) {
+                    return null;
+                }
+
+                if (data instanceof List) {
+                    // 处理List类型数据
+                    return ((List<?>) data).stream()
+                            .map(this::processItem)
+                            .collect(Collectors.toList());
+                }
+
+                return data;
+            }
+
+            private Object processItem(Object item) {
+                if (item == null) {
+                    return "-";
+                }
+
+                // 如果元素是Map,处理Map中的null值
+                if (item instanceof Map) {
+                    Map<?, ?> map = (Map<?, ?>) item;
+                    return map.entrySet().stream()
+                            .collect(Collectors.toMap(
+                                    Map.Entry::getKey,
+                                    e -> e.getValue() == null ? "-" : e.getValue()
+                            ));
+                }
+
+                return item;
+            }
+        };
+    }
 }

+ 91 - 15
easier-report-biz/src/main/java/com/yaoyicloud/render/AntiBriberyRender.java

@@ -1,45 +1,121 @@
 package com.yaoyicloud.render;
 
 import java.io.IOException;
-import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
-import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
-import com.google.protobuf.util.JsonFormat;
-import com.yaoyicloud.message.FxyProtos.AntiBribery;
+import com.deepoove.poi.policy.RenderPolicy;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yaoyicloud.config.FilerepoProperties;
+import lombok.extern.slf4j.Slf4j;
+
 
 /**
  * AntiBribery渲染器
  *
  */
+@Slf4j
 public final class AntiBriberyRender extends AbstractRender {
+    private final FilerepoProperties filerepoProperties;
 
-    public AntiBriberyRender(String cwd) {
+    // 注入父类所需的 cwd 参数
+    public AntiBriberyRender(String cwd, FilerepoProperties filerepoProperties) {
         super(cwd);
+        this.filerepoProperties = filerepoProperties;
+    }
+
+    @Override
+    protected String getBasicPath() throws IOException {
+        return filerepoProperties.getBasePath();
+    }
+
+    @Override
+    protected String getReportImagePath() {
+        return filerepoProperties.getReportImagePath();
     }
 
     /**
      * Docx 渲染
      *
      * @param info 数据
+     * @param addtionalMap 额外数据
      * @param templateFileContent 模板内容
+     * @param relationId 关联ID
      * @return 本地文件目录
      * @throws IOException
      */
-    public String renderDocx(AntiBribery info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
-        // 不需要定制展示逻辑的时候,使用protobuf的转json方法
-        String jsonStr = JsonFormat.printer().print(info);
+    public String renderDocx(String info, Map<String, Object> addtionalMap, byte[] templateFileContent, String relationId) throws IOException {
+        log.info("开始渲染防贿赂报告,relationId: {}", relationId);
 
-        // 注: 报告模板的模板变量按照json序列化的结果命名
-        // 注: 目前的实现假设:一个session对应一个cwd目录
+        // 配置POI-TL渲染器
         ConfigureBuilder builder = Configure.builder();
-        builder.bind("questionnaireItems", new LoopRowTableRenderPolicy());
-        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
-            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
-        return this.docxResultPath;
+        RenderPolicy indicatorsRenderPolicy = this.indicatorsRenderPolicy();
+        builder.bind("questionnaireItems", indicatorsRenderPolicy);
+
+        // 解析数据
+        ObjectMapper objectMapper = new ObjectMapper();
+        Map<String, Object> data = objectMapper.readValue(info, new TypeReference<Map<String, Object>>() {});
+        if (addtionalMap != null) {
+            data.putAll(addtionalMap);
+        }
+
+        // 填充默认值
+        fillDefaultValues(data);
+
+        try {
+            // 渲染文档
+            String resultPath = this.renderDocx(data, templateFileContent, builder, relationId);
+            log.info("防贿赂报告渲染成功,文件路径: {}", resultPath);
+            return resultPath;
+        } catch (Exception e) {
+            log.error("防贿赂报告渲染失败,relationId: {}", relationId, e);
+            throw new IOException("文档渲染失败", e);
+        }
+    }
+
+    /**
+     * 填充默认值,确保所有必要字段都存在
+     */
+    private void fillDefaultValues(Map<String, Object> data) {
+        // 处理 questionnaireItems 列表
+        if (!data.containsKey("questionnaireItems") || data.get("questionnaireItems") == null) {
+            List<Map<String, Object>> defaultItems = new ArrayList<>();
+            Map<String, Object> defaultItem = new HashMap<>();
+            defaultItem.put("id", 1);
+            defaultItem.put("question", "-");
+            defaultItem.put("answer", "-");
+            defaultItems.add(defaultItem);
+            data.put("questionnaireItems", defaultItems);
+        } else {
+            // 确保列表中的每个项目都有所有字段
+            @SuppressWarnings("unchecked")
+            List<Map<String, Object>> items = (List<Map<String, Object>>) data.get("questionnaireItems");
+            for (Map<String, Object> item : items) {
+                item.putIfAbsent("id", 1);
+                item.putIfAbsent("question", "-");
+                item.putIfAbsent("answer", "-");
+            }
+        }
+
+        // 处理 antiBriberySummary 对象
+        if (!data.containsKey("antiBriberySummary") || data.get("antiBriberySummary") == null) {
+            Map<String, Object> defaultSummary = new HashMap<>();
+            defaultSummary.put("score", "-");
+            defaultSummary.put("riskSummary", "-");
+            defaultSummary.put("suggestion", "-");
+            data.put("antiBriberySummary", defaultSummary);
+        } else {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> summary = (Map<String, Object>) data.get("antiBriberySummary");
+            summary.putIfAbsent("score", "-");
+            summary.putIfAbsent("riskSummary", "-");
+            summary.putIfAbsent("suggestion", "-");
+        }
     }
 
 }

+ 195 - 195
easier-report-biz/src/main/java/com/yaoyicloud/render/AttachmentSectionRender.java

@@ -1,195 +1,195 @@
-package com.yaoyicloud.render;
-
-import java.net.URL;
-
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import javax.imageio.ImageIO;
-
-import org.apache.pdfbox.Loader;
-import org.apache.pdfbox.pdmodel.PDDocument;
-import org.apache.pdfbox.rendering.PDFRenderer;
-import org.apache.poi.xwpf.usermodel.XWPFRun;
-
-import com.deepoove.poi.XWPFTemplate;
-import com.deepoove.poi.config.Configure;
-import com.deepoove.poi.config.ConfigureBuilder;
-import com.deepoove.poi.data.ByteArrayPictureRenderData;
-import com.deepoove.poi.data.HyperlinkTextRenderData;
-import com.deepoove.poi.data.PictureType;
-import com.deepoove.poi.data.style.PictureStyle;
-import com.deepoove.poi.exception.RenderException;
-import com.deepoove.poi.policy.PictureRenderPolicy;
-import com.deepoove.poi.policy.TextRenderPolicy;
-import com.deepoove.poi.template.ElementTemplate;
-import com.deepoove.poi.template.run.RunTemplate;
-import com.deepoove.poi.xwpf.BodyContainer;
-import com.deepoove.poi.xwpf.BodyContainerFactory;
-import com.google.protobuf.Descriptors;
-import com.google.protobuf.util.JsonFormat;
-import com.yaoyicloud.message.FxyProtos.AttachmentSection;
-import cn.hutool.core.lang.Pair;
-import cn.hutool.core.util.StrUtil;
-
-/**
- * AttachmentSection渲染器
- *
- */
-public final class AttachmentSectionRender extends AbstractRender {
-
-    public AttachmentSectionRender(String cwd) {
-        super(cwd);
-    }
-
-    /**
-     * Docx 渲染
-     *
-     * @param info 数据
-     * @param templateFileContent 模板内容
-     * @return 本地文件目录
-     * @throws IOException
-     */
-    public String renderDocx(AttachmentSection info, Map<String, Object> addtionalMap, byte[] templateFileContent)
-        throws IOException {
-
-        // 不需要定制展示逻辑的时候,使用protobuf的转json方法
-        String jsonStr = JsonFormat.printer().print(info);
-
-        // 定制缺省文字,POI-TL模板变量只支持if,不支持else
-        Map<String, Object> newAddtionMap = new HashMap<>();
-        Descriptors.Descriptor descriptor = info.getDescriptorForType();
-        for (Descriptors.FieldDescriptor field : descriptor.getFields()) {
-            if (field.isRepeated()) {
-                List<?> li = (List<?>) info.getField(field);
-                if (li.size() == 0) {
-                    newAddtionMap.put(field.getName() + "AltText", "未上传");
-                }
-            }
-        }
-        newAddtionMap.putAll(addtionalMap);
-
-        // 注: 报告模板的模板变量按照json序列化的结果命名
-        // 注: 目前的实现假设:一个session对应一个cwd目录
-        ConfigureBuilder builder = Configure.builder();
-
-        // 新增特殊处理变量
-        builder.addPlugin('^', new AttachmentRenderPolicy());
-
-        this.docxResultPath = this.renderDocx(jsonStr, newAddtionMap, templateFileContent, builder,
-            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
-        return this.docxResultPath;
-    }
-
-    /**
-     * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
-     */
-    public class AttachmentRenderPolicy extends PictureRenderPolicy {
-
-        public AttachmentRenderPolicy() {}
-
-        @Override
-        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
-            @SuppressWarnings("unchecked")
-            Map<String, Object> mpData = (Map<String, Object>) data;
-            String filename = mpData.getOrDefault("fileName", "").toString().trim();
-            String url = mpData.getOrDefault("fileUri", "").toString();
-            float targetWidth = 456.5f;
-
-            if (StrUtil.isBlank(filename)) {
-                // uri render when no filename
-                TextRenderPolicy.Helper.renderTextRun(((RunTemplate) eleTemplate).getRun(),
-                    new HyperlinkTextRenderData(url, url));
-            } else if (filename.endsWith(".pdf")) {
-                // pdf render, replace data with bytestream
-                PDDocument document = null;
-                try {
-                    document = Loader.loadPDF(new URL(url).openStream().readAllBytes());
-                    PDFRenderer renderer = new PDFRenderer(document);
-
-                    XWPFRun run = ((RunTemplate) eleTemplate).getRun();
-                    BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
-                    // 每页一张图片
-                    for (int i = 0; i < document.getNumberOfPages(); i++) {
-                        // pdf 转 jpeg
-                        BufferedImage image = renderer.renderImageWithDPI(i, 150);
-                        ByteArrayOutputStream stream = new ByteArrayOutputStream();
-                        ImageIO.write(image, "jpg", stream);
-
-                        // 准备docx元素
-                        if (i == 0) {
-                            run.setText("", 0);
-                        } else {
-                            run = bodyContainer.insertNewParagraph(run).createRun();
-                        }
-
-                        // 准备POI-TL数据
-                        ByteArrayPictureRenderData picData =
-                            new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.JPEG);
-                        Pair<Integer, Integer> targetSize =
-                            getTargetSize(image.getWidth(), image.getHeight(), targetWidth);
-                        PictureStyle style = new PictureStyle();
-                        style.setWidth(targetSize.getKey());
-                        style.setHeight(targetSize.getValue());
-                        picData.setPictureStyle(style);
-                        PictureRenderPolicy.Helper.renderPicture(run, picData);
-                    }
-                } catch (Exception e) {
-                    throw new RenderException(
-                        "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
-                } finally {
-                    if (document != null) {
-                        try {
-                            document.close();
-                        } catch (IOException e) {
-                            e.printStackTrace();
-                        }
-                    }
-                }
-            } else {
-                BufferedImage image = null;
-                ByteArrayOutputStream stream = null;
-                try {
-                    image = ImageIO.read(new URL(url));
-                    stream = new ByteArrayOutputStream();
-                    ImageIO.write(image, filename.substring(filename.lastIndexOf('.') + 1, filename.length()), stream);
-                } catch (IOException e) {
-                    throw new RenderException(
-                        "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
-                }
-
-                ByteArrayPictureRenderData picData =
-                    new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.suggestFileType(filename));
-                Pair<Integer, Integer> targetSize = getTargetSize(image.getWidth(), image.getHeight(), targetWidth);
-                PictureStyle style = new PictureStyle();
-                style.setWidth(targetSize.getKey());
-                style.setHeight(targetSize.getValue());
-                picData.setPictureStyle(style);
-                super.render(eleTemplate, picData, template);
-            }
-        }
-
-        /**
-         * 这个方法也应该独立出去共享
-         *
-         * @param originWidth
-         * @param originHeight
-         * @param targetWidth
-         * @return
-         */
-        private static Pair<Integer, Integer> getTargetSize(int originWidth, int originHeight, float targetWidth) {
-            float targetHeight = (originHeight * targetWidth) / originWidth;
-            if (targetHeight > 645) {
-                targetHeight = 645;
-                targetWidth = (originWidth * targetHeight) / originHeight;
-            }
-            return Pair.of(Math.round(targetWidth), Math.round(targetHeight));
-        }
-    }
-}
+//package com.yaoyicloud.render;
+//
+//import java.net.URL;
+//
+//import java.awt.image.BufferedImage;
+//import java.io.ByteArrayOutputStream;
+//import java.io.IOException;
+//import java.nio.file.Paths;
+//import java.util.HashMap;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.UUID;
+//
+//import javax.imageio.ImageIO;
+//
+//import org.apache.pdfbox.Loader;
+//import org.apache.pdfbox.pdmodel.PDDocument;
+//import org.apache.pdfbox.rendering.PDFRenderer;
+//import org.apache.poi.xwpf.usermodel.XWPFRun;
+//
+//import com.deepoove.poi.XWPFTemplate;
+//import com.deepoove.poi.config.Configure;
+//import com.deepoove.poi.config.ConfigureBuilder;
+//import com.deepoove.poi.data.ByteArrayPictureRenderData;
+//import com.deepoove.poi.data.HyperlinkTextRenderData;
+//import com.deepoove.poi.data.PictureType;
+//import com.deepoove.poi.data.style.PictureStyle;
+//import com.deepoove.poi.exception.RenderException;
+//import com.deepoove.poi.policy.PictureRenderPolicy;
+//import com.deepoove.poi.policy.TextRenderPolicy;
+//import com.deepoove.poi.template.ElementTemplate;
+//import com.deepoove.poi.template.run.RunTemplate;
+//import com.deepoove.poi.xwpf.BodyContainer;
+//import com.deepoove.poi.xwpf.BodyContainerFactory;
+//import com.google.protobuf.Descriptors;
+//import com.google.protobuf.util.JsonFormat;
+//import com.yaoyicloud.message.FxyProtos.AttachmentSection;
+//import cn.hutool.core.lang.Pair;
+//import cn.hutool.core.util.StrUtil;
+//
+///**
+// * AttachmentSection渲染器
+// *
+// */
+//public final class AttachmentSectionRender extends AbstractRender {
+//
+//    public AttachmentSectionRender(String cwd) {
+//        super(cwd);
+//    }
+//
+//    /**
+//     * Docx 渲染
+//     *
+//     * @param info 数据
+//     * @param templateFileContent 模板内容
+//     * @return 本地文件目录
+//     * @throws IOException
+//     */
+////    public String renderDocx(AttachmentSection info, Map<String, Object> addtionalMap, byte[] templateFileContent)
+////        throws IOException {
+////
+////        // 不需要定制展示逻辑的时候,使用protobuf的转json方法
+////        String jsonStr = JsonFormat.printer().print(info);
+////
+////        // 定制缺省文字,POI-TL模板变量只支持if,不支持else
+////        Map<String, Object> newAddtionMap = new HashMap<>();
+////        Descriptors.Descriptor descriptor = info.getDescriptorForType();
+////        for (Descriptors.FieldDescriptor field : descriptor.getFields()) {
+////            if (field.isRepeated()) {
+////                List<?> li = (List<?>) info.getField(field);
+////                if (li.size() == 0) {
+////                    newAddtionMap.put(field.getName() + "AltText", "未上传");
+////                }
+////            }
+////        }
+////        newAddtionMap.putAll(addtionalMap);
+////
+////        // 注: 报告模板的模板变量按照json序列化的结果命名
+////        // 注: 目前的实现假设:一个session对应一个cwd目录
+////        ConfigureBuilder builder = Configure.builder();
+////
+////        // 新增特殊处理变量
+////        builder.addPlugin('^', new AttachmentRenderPolicy());
+////
+////        this.docxResultPath = this.renderDocx(jsonStr, newAddtionMap, templateFileContent, builder,
+////            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
+////        return this.docxResultPath;
+////    }
+//
+//    /**
+//     * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
+//     */
+//    public class AttachmentRenderPolicy extends PictureRenderPolicy {
+//
+//        public AttachmentRenderPolicy() {}
+//
+//        @Override
+//        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
+//            @SuppressWarnings("unchecked")
+//            Map<String, Object> mpData = (Map<String, Object>) data;
+//            String filename = mpData.getOrDefault("fileName", "").toString().trim();
+//            String url = mpData.getOrDefault("fileUri", "").toString();
+//            float targetWidth = 456.5f;
+//
+//            if (StrUtil.isBlank(filename)) {
+//                // uri render when no filename
+//                TextRenderPolicy.Helper.renderTextRun(((RunTemplate) eleTemplate).getRun(),
+//                    new HyperlinkTextRenderData(url, url));
+//            } else if (filename.endsWith(".pdf")) {
+//                // pdf render, replace data with bytestream
+//                PDDocument document = null;
+//                try {
+//                    document = Loader.loadPDF(new URL(url).openStream().readAllBytes());
+//                    PDFRenderer renderer = new PDFRenderer(document);
+//
+//                    XWPFRun run = ((RunTemplate) eleTemplate).getRun();
+//                    BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
+//                    // 每页一张图片
+//                    for (int i = 0; i < document.getNumberOfPages(); i++) {
+//                        // pdf 转 jpeg
+//                        BufferedImage image = renderer.renderImageWithDPI(i, 150);
+//                        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+//                        ImageIO.write(image, "jpg", stream);
+//
+//                        // 准备docx元素
+//                        if (i == 0) {
+//                            run.setText("", 0);
+//                        } else {
+//                            run = bodyContainer.insertNewParagraph(run).createRun();
+//                        }
+//
+//                        // 准备POI-TL数据
+//                        ByteArrayPictureRenderData picData =
+//                            new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.JPEG);
+//                        Pair<Integer, Integer> targetSize =
+//                            getTargetSize(image.getWidth(), image.getHeight(), targetWidth);
+//                        PictureStyle style = new PictureStyle();
+//                        style.setWidth(targetSize.getKey());
+//                        style.setHeight(targetSize.getValue());
+//                        picData.setPictureStyle(style);
+//                        PictureRenderPolicy.Helper.renderPicture(run, picData);
+//                    }
+//                } catch (Exception e) {
+//                    throw new RenderException(
+//                        "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
+//                } finally {
+//                    if (document != null) {
+//                        try {
+//                            document.close();
+//                        } catch (IOException e) {
+//                            e.printStackTrace();
+//                        }
+//                    }
+//                }
+//            } else {
+//                BufferedImage image = null;
+//                ByteArrayOutputStream stream = null;
+//                try {
+//                    image = ImageIO.read(new URL(url));
+//                    stream = new ByteArrayOutputStream();
+//                    ImageIO.write(image, filename.substring(filename.lastIndexOf('.') + 1, filename.length()), stream);
+//                } catch (IOException e) {
+//                    throw new RenderException(
+//                        "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
+//                }
+//
+//                ByteArrayPictureRenderData picData =
+//                    new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.suggestFileType(filename));
+//                Pair<Integer, Integer> targetSize = getTargetSize(image.getWidth(), image.getHeight(), targetWidth);
+//                PictureStyle style = new PictureStyle();
+//                style.setWidth(targetSize.getKey());
+//                style.setHeight(targetSize.getValue());
+//                picData.setPictureStyle(style);
+//                super.render(eleTemplate, picData, template);
+//            }
+//        }
+//
+//        /**
+//         * 这个方法也应该独立出去共享
+//         *
+//         * @param originWidth
+//         * @param originHeight
+//         * @param targetWidth
+//         * @return
+//         */
+//        private static Pair<Integer, Integer> getTargetSize(int originWidth, int originHeight, float targetWidth) {
+//            float targetHeight = (originHeight * targetWidth) / originWidth;
+//            if (targetHeight > 645) {
+//                targetHeight = 645;
+//                targetWidth = (originWidth * targetHeight) / originHeight;
+//            }
+//            return Pair.of(Math.round(targetWidth), Math.round(targetHeight));
+//        }
+//    }
+//}

+ 132 - 132
easier-report-biz/src/main/java/com/yaoyicloud/render/AuditResultRender.java

@@ -1,132 +1,132 @@
-package com.yaoyicloud.render;
-
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.Map;
-import java.util.UUID;
-
-import com.deepoove.poi.config.Configure;
-import com.deepoove.poi.config.ConfigureBuilder;
-import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
-import com.yaoyicloud.message.FxyProtos.AuditResult;
-import com.yaoyicloud.message.FxyProtos.CheckItemScore;
-import com.yaoyicloud.message.FxyProtos.CheckSummary;
-
-import cn.hutool.core.util.StrUtil;
-
-/**
- * AuditResult渲染器
- *
- */
-public final class AuditResultRender extends AbstractRender {
-
-    public AuditResultRender(String cwd) {
-        super(cwd);
-    }
-
-    /**
-     * Docx 渲染
-     *
-     * @param info 数据
-     * @param templateFileContent 模板内容
-     * @return 本地文件目录
-     * @throws IOException
-     */
-    public String renderDocx(AuditResult info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
-        ObjectMapper mapper = new ObjectMapper();
-        SimpleModule module = new SimpleModule();
-        module.addSerializer(new AuditResultSerializer(AuditResult.class));
-        module.addSerializer(new CheckItemScoreSerializer(CheckItemScore.class));
-        module.addSerializer(new CheckSummarySerializer(CheckSummary.class));
-        mapper.registerModule(module);
-
-        String jsonStr = mapper.writeValueAsString(info);
-
-        // 注: 报告模板的模板变量按照json序列化的结果命名
-        // 注: 目前的实现假设:一个session对应一个cwd目录
-        ConfigureBuilder builder = Configure.builder();
-        builder.bind("checkItemScores", new LoopRowTableRenderPolicy());
-        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
-            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
-        return this.docxResultPath;
-    }
-
-    /*
-     * 通过序列化和反序列化
-     * 1. 默认显示规则的数据转换
-     * 2. 避免TL处理模板面对复杂数据类型
-     */
-    public class AuditResultSerializer extends StdSerializer<AuditResult> {
-
-        public AuditResultSerializer() {
-            this(null);
-        }
-
-        public AuditResultSerializer(Class<AuditResult> t) {
-            super(t);
-        }
-
-        @Override
-        public void serialize(AuditResult value, JsonGenerator jgen, SerializerProvider provider)
-            throws IOException, JsonProcessingException {
-
-            jgen.writeStartObject();
-            jgen.writeStringField("opinion", StrUtil.isBlank(value.getOpinion()) ? "-" : value.getOpinion());
-            jgen.writeStringField("serviceProviderName", value.getServiceProviderName());
-            // Note: ignore reportVersion
-            jgen.writeStringField("auditDate", value.getAuditDate());
-            jgen.writeObjectField("overallSummary", value.getOverallSummary());
-            jgen.writeObjectField("checkItemScores", value.getCheckItemScoresList());
-            jgen.writeEndObject();
-        }
-    }
-
-    public class CheckItemScoreSerializer extends StdSerializer<CheckItemScore> {
-
-        public CheckItemScoreSerializer() {
-            this(null);
-        }
-
-        public CheckItemScoreSerializer(Class<CheckItemScore> t) {
-            super(t);
-        }
-
-        @Override
-        public void serialize(CheckItemScore value, JsonGenerator jgen, SerializerProvider provider)
-            throws IOException, JsonProcessingException {
-
-            jgen.writeStartObject();
-            jgen.writeStringField("category", StrUtil.isBlank(value.getCategory()) ? "-" : value.getCategory());
-            jgen.writeStringField("itemName", value.getItemName());
-            jgen.writeNumberField("score", value.getScore());
-            jgen.writeEndObject();
-        }
-    }
-
-    public class CheckSummarySerializer extends StdSerializer<CheckSummary> {
-
-        public CheckSummarySerializer() {
-            this(null);
-        }
-
-        public CheckSummarySerializer(Class<CheckSummary> t) {
-            super(t);
-        }
-
-        @Override
-        public void serialize(CheckSummary value, JsonGenerator jgen, SerializerProvider provider)
-            throws IOException, JsonProcessingException {
-            jgen.writeStartObject();
-            jgen.writeStringField("score", value.getScore() < 0 ? "-" : String.valueOf(value.getScore()));
-            jgen.writeStringField("riskSummary", value.getRiskSummary());
-            jgen.writeStringField("suggestion", value.getSuggestion());
-            jgen.writeEndObject();
-        }
-    }
-}
+//package com.yaoyicloud.render;
+//
+//import java.io.IOException;
+//import java.nio.file.Paths;
+//import java.util.Map;
+//import java.util.UUID;
+//
+//import com.deepoove.poi.config.Configure;
+//import com.deepoove.poi.config.ConfigureBuilder;
+//import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
+//import com.fasterxml.jackson.core.JsonGenerator;
+//import com.fasterxml.jackson.core.JsonProcessingException;
+//import com.fasterxml.jackson.databind.ObjectMapper;
+//import com.fasterxml.jackson.databind.SerializerProvider;
+//import com.fasterxml.jackson.databind.module.SimpleModule;
+//import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+//import com.yaoyicloud.message.FxyProtos.AuditResult;
+//import com.yaoyicloud.message.FxyProtos.CheckItemScore;
+//import com.yaoyicloud.message.FxyProtos.CheckSummary;
+//
+//import cn.hutool.core.util.StrUtil;
+//
+///**
+// * AuditResult渲染器
+// *
+// */
+//public final class AuditResultRender extends AbstractRender {
+//
+//    public AuditResultRender(String cwd) {
+//        super(cwd);
+//    }
+//
+//    /**
+//     * Docx 渲染
+//     *
+//     * @param info 数据
+//     * @param templateFileContent 模板内容
+//     * @return 本地文件目录
+//     * @throws IOException
+//     */
+//    public String renderDocx(AuditResult info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
+//        ObjectMapper mapper = new ObjectMapper();
+//        SimpleModule module = new SimpleModule();
+//        module.addSerializer(new AuditResultSerializer(AuditResult.class));
+//        module.addSerializer(new CheckItemScoreSerializer(CheckItemScore.class));
+//        module.addSerializer(new CheckSummarySerializer(CheckSummary.class));
+//        mapper.registerModule(module);
+//
+//        String jsonStr = mapper.writeValueAsString(info);
+//
+//        // 注: 报告模板的模板变量按照json序列化的结果命名
+//        // 注: 目前的实现假设:一个session对应一个cwd目录
+//        ConfigureBuilder builder = Configure.builder();
+//        builder.bind("checkItemScores", new LoopRowTableRenderPolicy());
+//        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
+//            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
+//        return this.docxResultPath;
+//    }
+//
+//    /*
+//     * 通过序列化和反序列化
+//     * 1. 默认显示规则的数据转换
+//     * 2. 避免TL处理模板面对复杂数据类型
+//     */
+//    public class AuditResultSerializer extends StdSerializer<AuditResult> {
+//
+//        public AuditResultSerializer() {
+//            this(null);
+//        }
+//
+//        public AuditResultSerializer(Class<AuditResult> t) {
+//            super(t);
+//        }
+//
+//        @Override
+//        public void serialize(AuditResult value, JsonGenerator jgen, SerializerProvider provider)
+//            throws IOException, JsonProcessingException {
+//
+//            jgen.writeStartObject();
+//            jgen.writeStringField("opinion", StrUtil.isBlank(value.getOpinion()) ? "-" : value.getOpinion());
+//            jgen.writeStringField("serviceProviderName", value.getServiceProviderName());
+//            // Note: ignore reportVersion
+//            jgen.writeStringField("auditDate", value.getAuditDate());
+//            jgen.writeObjectField("overallSummary", value.getOverallSummary());
+//            jgen.writeObjectField("checkItemScores", value.getCheckItemScoresList());
+//            jgen.writeEndObject();
+//        }
+//    }
+//
+//    public class CheckItemScoreSerializer extends StdSerializer<CheckItemScore> {
+//
+//        public CheckItemScoreSerializer() {
+//            this(null);
+//        }
+//
+//        public CheckItemScoreSerializer(Class<CheckItemScore> t) {
+//            super(t);
+//        }
+//
+//        @Override
+//        public void serialize(CheckItemScore value, JsonGenerator jgen, SerializerProvider provider)
+//            throws IOException, JsonProcessingException {
+//
+//            jgen.writeStartObject();
+//            jgen.writeStringField("category", StrUtil.isBlank(value.getCategory()) ? "-" : value.getCategory());
+//            jgen.writeStringField("itemName", value.getItemName());
+//            jgen.writeNumberField("score", value.getScore());
+//            jgen.writeEndObject();
+//        }
+//    }
+//
+//    public class CheckSummarySerializer extends StdSerializer<CheckSummary> {
+//
+//        public CheckSummarySerializer() {
+//            this(null);
+//        }
+//
+//        public CheckSummarySerializer(Class<CheckSummary> t) {
+//            super(t);
+//        }
+//
+//        @Override
+//        public void serialize(CheckSummary value, JsonGenerator jgen, SerializerProvider provider)
+//            throws IOException, JsonProcessingException {
+//            jgen.writeStartObject();
+//            jgen.writeStringField("score", value.getScore() < 0 ? "-" : String.valueOf(value.getScore()));
+//            jgen.writeStringField("riskSummary", value.getRiskSummary());
+//            jgen.writeStringField("suggestion", value.getSuggestion());
+//            jgen.writeEndObject();
+//        }
+//    }
+//}

+ 54 - 45
easier-report-biz/src/main/java/com/yaoyicloud/render/BasicInfoRender.java

@@ -1,45 +1,54 @@
-package com.yaoyicloud.render;
-
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.Map;
-import java.util.UUID;
-
-import com.deepoove.poi.config.Configure;
-import com.deepoove.poi.config.ConfigureBuilder;
-import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
-import com.google.protobuf.util.JsonFormat;
-import com.yaoyicloud.message.FxyProtos.BasicInfo;
-
-/**
- * BasicInfo渲染器
- *
- */
-public final class BasicInfoRender extends AbstractRender {
-
-    public BasicInfoRender(String cwd) {
-        super(cwd);
-    }
-
-    /**
-     * Docx 渲染
-     *
-     * @param info 数据
-     * @param templateFileContent 模板内容
-     * @return 本地文件目录
-     * @throws IOException
-     */
-    public String renderDocx(BasicInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
-        // 不需要定制展示逻辑的时候,使用protobuf的转json方法
-        String jsonStr = JsonFormat.printer().print(info);
-
-        // 注: 报告模板的模板变量按照json序列化的结果命名
-        // 注: 目前的实现假设:一个session对应一个cwd目录
-        ConfigureBuilder builder = Configure.builder();
-        builder.bind("basicInfoChecks", new LoopRowTableRenderPolicy());
-        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
-            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
-        return this.docxResultPath;
-    }
-
-}
+//package com.yaoyicloud.render;
+//
+//import java.io.IOException;
+//import java.nio.file.Paths;
+//import java.util.Map;
+//import java.util.UUID;
+//
+//import com.deepoove.poi.config.Configure;
+//import com.deepoove.poi.config.ConfigureBuilder;
+//import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
+//import com.google.protobuf.util.JsonFormat;
+//import com.yaoyicloud.message.FxyProtos.BasicInfo;
+//
+///**
+// * BasicInfo渲染器
+// *
+// */
+//public final class BasicInfoRender extends AbstractRender {
+//
+//    public BasicInfoRender(String cwd) {
+//        super(cwd);
+//    }
+//
+//    /**
+//     * Docx 渲染
+//     *
+//     * @param info 数据
+//     * @param templateFileContent 模板内容
+//     * @return 本地文件目录
+//     * @throws IOException
+//     */
+//    public String renderDocx(BasicInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
+//        // 不需要定制展示逻辑的时候,使用protobuf的转json方法
+//        String jsonStr = JsonFormat.printer().print(info);
+//
+//        // 注: 报告模板的模板变量按照json序列化的结果命名
+//        // 注: 目前的实现假设:一个session对应一个cwd目录
+//        ConfigureBuilder builder = Configure.builder();
+//        builder.bind("basicInfoChecks", new LoopRowTableRenderPolicy());
+////        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
+////            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
+//        return this.docxResultPath;
+//    }
+//
+//    @Override
+//    protected String getBasicPath() throws IOException {
+//        return "";
+//    }
+//
+//    @Override
+//    protected String getReportImagePath() {
+//        return "";
+//    }
+//}

+ 225 - 225
easier-report-biz/src/main/java/com/yaoyicloud/render/FinancialInfoRender.java

@@ -1,225 +1,225 @@
-package com.yaoyicloud.render;
-
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import org.apache.poi.xwpf.usermodel.XWPFParagraph;
-import org.apache.poi.xwpf.usermodel.XWPFRun;
-import org.apache.poi.xwpf.usermodel.XWPFTable;
-import org.apache.poi.xwpf.usermodel.XWPFTableCell;
-import org.apache.poi.xwpf.usermodel.XWPFTableRow;
-import com.deepoove.poi.XWPFTemplate;
-import com.deepoove.poi.config.Configure;
-import com.deepoove.poi.config.ConfigureBuilder;
-import com.deepoove.poi.exception.RenderException;
-import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
-import com.deepoove.poi.policy.RenderPolicy;
-import com.deepoove.poi.template.ElementTemplate;
-import com.deepoove.poi.template.run.RunTemplate;
-import com.deepoove.poi.util.TableTools;
-import com.google.protobuf.util.JsonFormat;
-import com.yaoyicloud.message.FxyProtos.FinancialInfo;
-import com.yaoyicloud.tools.Util;
-
-/**
- * FinancialInfo渲染器
- *
- */
-public final class FinancialInfoRender extends AbstractRender {
-
-    public FinancialInfoRender(String cwd) {
-        super(cwd);
-    }
-
-    /**
-     * Docx 渲染
-     *
-     * @param info 数据
-     * @param templateFileContent 模板内容
-     * @return 本地文件目录
-     * @throws IOException
-     */
-    public String renderDocx(FinancialInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
-
-        // 不需要定制展示逻辑的时候,使用protobuf的转json方法
-        String jsonStr = JsonFormat.printer().print(info);
-
-        // 注: 报告模板的模板变量按照json序列化的结果命名
-        // 注: 目前的实现假设:一个session对应一个cwd目录
-        ConfigureBuilder builder = Configure.builder();
-        builder.bind("indicators", new LoopRowIncludeStatisticsTableRenderPolicy("values"));
-        builder.bind("financialDataSeq", new LoopColumnStaticTableRenderPolicy("[", "]", false, true, 2));
-        builder.bind("financialCheckDetails", new LoopRowCutAndMergeFirstColTableRenderPolicy());
-        // 注意使用了SpringEL之后,每个模板变量都要设置值,不然会报错
-        builder.useSpringEL();
-
-        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
-            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
-        return this.docxResultPath;
-    }
-
-    /**
-     * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
-     */
-    public class LoopColumnStaticTableRenderPolicy implements RenderPolicy {
-
-        private String prefix;
-        private String suffix;
-        private boolean onSameLine;
-        private boolean reverse;
-        private int valColIndex;
-
-        public LoopColumnStaticTableRenderPolicy() {
-            this(false);
-        }
-
-        public LoopColumnStaticTableRenderPolicy(boolean onSameLine) {
-            this("[", "]", onSameLine);
-        }
-
-        public LoopColumnStaticTableRenderPolicy(String prefix, String suffix) {
-            this(prefix, suffix, false);
-        }
-
-        public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine) {
-            this(prefix, suffix, onSameLine, false);
-        }
-
-        public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse) {
-            this(prefix, suffix, onSameLine, false, 1);
-        }
-
-        public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse,
-            int valRowIndex) {
-            this.prefix = prefix;
-            this.suffix = suffix;
-            this.onSameLine = onSameLine;
-            this.reverse = reverse;
-            this.valColIndex = valRowIndex;
-        }
-
-        @Override
-        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
-            RunTemplate runTemplate = (RunTemplate) eleTemplate;
-            XWPFRun run = runTemplate.getRun();
-            try {
-                if (!TableTools.isInsideTable(run)) {
-                    throw new IllegalStateException(
-                        "The template tag " + runTemplate.getSource() + " must be inside a table");
-                }
-                XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
-                XWPFTable table = tagCell.getTableRow().getTable();
-                run.setText("", 0);
-
-                int templateColIndex = getTemplateColIndex(tagCell);
-                // 模版变量列总是写在左边
-                int minIndex = templateColIndex;
-                int maxIndex = table.getRows().get(valColIndex).getTableCells().size() - 1;
-                int currIndex = reverse ? maxIndex : minIndex;
-                int indexDelta = reverse ? -1 : 1;
-
-                // 目前expression当作数据Map的key来对待,将来可以当作POI-TL变量一致化的处理
-                Map<Integer, String> idx2Expression = new HashMap<>();
-                for (int i = 0; i < table.getRows().size(); i++) {
-                    XWPFTableCell cell = table.getRows().get(i).getCell(templateColIndex);
-                    String text = cell.getText().trim();
-                    if (text.startsWith(prefix) && text.endsWith(suffix)) {
-                        idx2Expression.put(i, text.substring(1, text.length() - 1));
-                        cell.setText("");
-                    }
-                }
-
-                int rowSize = table.getRows().size();
-                @SuppressWarnings("unchecked")
-                List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
-
-                for (Map<String, Object> realData : mpData) {
-                    for (int i = 0; i < rowSize; i++) {
-                        if (!idx2Expression.containsKey(i)) {
-                            continue;
-                        }
-                        XWPFTableRow row = table.getRow(i);
-                        XWPFTableCell valueCell = row.getCell(currIndex);
-                        String valStr = realData.getOrDefault(idx2Expression.get(i), "-").toString();
-                        valueCell.setText(valStr);
-                    }
-                    currIndex += indexDelta;
-                }
-
-            } catch (Exception e) {
-                throw new RenderException("HackLoopTable for " + eleTemplate + "error: " + e.getMessage(), e);
-            }
-        }
-
-        private int getTemplateColIndex(XWPFTableCell tagCell) {
-            return onSameLine ? Util.getColIndexOfFirstRow(tagCell) : (Util.getColIndexOfFirstRow(tagCell) + 1);
-        }
-    }
-
-    public class LoopRowCutAndMergeFirstColTableRenderPolicy extends LoopRowTableRenderPolicy {
-
-        @Override
-        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
-            RunTemplate runTemplate = (RunTemplate) eleTemplate;
-            XWPFRun run = runTemplate.getRun();
-            XWPFTable table = null;
-            try {
-                if (!TableTools.isInsideTable(run)) {
-                    throw new IllegalStateException(
-                        "The template tag " + runTemplate.getSource() + " must be inside a table");
-                }
-                // Reserve the first two rows
-                XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
-                table = tagCell.getTableRow().getTable();
-                for (int i = table.getNumberOfRows() - 1; i > 1; i--) {
-                    table.removeRow(i);
-                }
-            } catch (Exception e) {
-                throw new RenderException(
-                    "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
-            }
-
-            // in case data not sorted by rank
-            @SuppressWarnings("unchecked")
-            List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
-            mpData.sort((a, b) -> Integer.valueOf(a.getOrDefault("rank", "0").toString())
-                - Integer.valueOf(b.getOrDefault("rank", "0").toString()));
-
-            super.render(eleTemplate, data, template);
-
-            try {
-                // merge the first column
-                Util.mergeFirstNColSimple(table, 1, 0);
-            } catch (Exception e) {
-                throw new RenderException(
-                    "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
-            }
-        }
-    }
-
-    public class LoopRowIncludeStatisticsTableRenderPolicy extends LoopRowTableRenderPolicy {
-
-        private String valueTag;
-
-        public LoopRowIncludeStatisticsTableRenderPolicy(String valueTag) {
-            this.valueTag = valueTag;
-        }
-
-        @Override
-        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
-            @SuppressWarnings("unchecked")
-            List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
-            for (Map<String, Object> row : mpData) {
-                @SuppressWarnings("unchecked")
-                List<String> values = (List<String>) row.get(valueTag);
-                row.put("avg", values.stream().mapToLong(Long::valueOf).average().orElse(Double.NaN));
-            }
-
-            super.render(eleTemplate, data, template);
-        }
-    }
-}
+//package com.yaoyicloud.render;
+//
+//import java.io.IOException;
+//import java.nio.file.Paths;
+//import java.util.HashMap;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.UUID;
+//
+//import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+//import org.apache.poi.xwpf.usermodel.XWPFRun;
+//import org.apache.poi.xwpf.usermodel.XWPFTable;
+//import org.apache.poi.xwpf.usermodel.XWPFTableCell;
+//import org.apache.poi.xwpf.usermodel.XWPFTableRow;
+//import com.deepoove.poi.XWPFTemplate;
+//import com.deepoove.poi.config.Configure;
+//import com.deepoove.poi.config.ConfigureBuilder;
+//import com.deepoove.poi.exception.RenderException;
+//import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
+//import com.deepoove.poi.policy.RenderPolicy;
+//import com.deepoove.poi.template.ElementTemplate;
+//import com.deepoove.poi.template.run.RunTemplate;
+//import com.deepoove.poi.util.TableTools;
+//import com.google.protobuf.util.JsonFormat;
+//import com.yaoyicloud.message.FxyProtos.FinancialInfo;
+//import com.yaoyicloud.tools.Util;
+//
+///**
+// * FinancialInfo渲染器
+// *
+// */
+//public final class FinancialInfoRender extends AbstractRender {
+//
+//    public FinancialInfoRender(String cwd) {
+//        super(cwd);
+//    }
+//
+//    /**
+//     * Docx 渲染
+//     *
+//     * @param info 数据
+//     * @param templateFileContent 模板内容
+//     * @return 本地文件目录
+//     * @throws IOException
+//     */
+////    public String renderDocx(FinancialInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
+////
+////        // 不需要定制展示逻辑的时候,使用protobuf的转json方法
+////        String jsonStr = JsonFormat.printer().print(info);
+////
+////        // 注: 报告模板的模板变量按照json序列化的结果命名
+////        // 注: 目前的实现假设:一个session对应一个cwd目录
+////        ConfigureBuilder builder = Configure.builder();
+////        builder.bind("indicators", new LoopRowIncludeStatisticsTableRenderPolicy("values"));
+////        builder.bind("financialDataSeq", new LoopColumnStaticTableRenderPolicy("[", "]", false, true, 2));
+////        builder.bind("financialCheckDetails", new LoopRowCutAndMergeFirstColTableRenderPolicy());
+////        // 注意使用了SpringEL之后,每个模板变量都要设置值,不然会报错
+////        builder.useSpringEL();
+////
+////        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
+////            Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
+////        return this.docxResultPath;
+////    }
+//
+//    /**
+//     * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
+//     */
+//    public class LoopColumnStaticTableRenderPolicy implements RenderPolicy {
+//
+//        private String prefix;
+//        private String suffix;
+//        private boolean onSameLine;
+//        private boolean reverse;
+//        private int valColIndex;
+//
+//        public LoopColumnStaticTableRenderPolicy() {
+//            this(false);
+//        }
+//
+//        public LoopColumnStaticTableRenderPolicy(boolean onSameLine) {
+//            this("[", "]", onSameLine);
+//        }
+//
+//        public LoopColumnStaticTableRenderPolicy(String prefix, String suffix) {
+//            this(prefix, suffix, false);
+//        }
+//
+//        public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine) {
+//            this(prefix, suffix, onSameLine, false);
+//        }
+//
+//        public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse) {
+//            this(prefix, suffix, onSameLine, false, 1);
+//        }
+//
+//        public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse,
+//            int valRowIndex) {
+//            this.prefix = prefix;
+//            this.suffix = suffix;
+//            this.onSameLine = onSameLine;
+//            this.reverse = reverse;
+//            this.valColIndex = valRowIndex;
+//        }
+//
+//        @Override
+//        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
+//            RunTemplate runTemplate = (RunTemplate) eleTemplate;
+//            XWPFRun run = runTemplate.getRun();
+//            try {
+//                if (!TableTools.isInsideTable(run)) {
+//                    throw new IllegalStateException(
+//                        "The template tag " + runTemplate.getSource() + " must be inside a table");
+//                }
+//                XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
+//                XWPFTable table = tagCell.getTableRow().getTable();
+//                run.setText("", 0);
+//
+//                int templateColIndex = getTemplateColIndex(tagCell);
+//                // 模版变量列总是写在左边
+//                int minIndex = templateColIndex;
+//                int maxIndex = table.getRows().get(valColIndex).getTableCells().size() - 1;
+//                int currIndex = reverse ? maxIndex : minIndex;
+//                int indexDelta = reverse ? -1 : 1;
+//
+//                // 目前expression当作数据Map的key来对待,将来可以当作POI-TL变量一致化的处理
+//                Map<Integer, String> idx2Expression = new HashMap<>();
+//                for (int i = 0; i < table.getRows().size(); i++) {
+//                    XWPFTableCell cell = table.getRows().get(i).getCell(templateColIndex);
+//                    String text = cell.getText().trim();
+//                    if (text.startsWith(prefix) && text.endsWith(suffix)) {
+//                        idx2Expression.put(i, text.substring(1, text.length() - 1));
+//                        cell.setText("");
+//                    }
+//                }
+//
+//                int rowSize = table.getRows().size();
+//                @SuppressWarnings("unchecked")
+//                List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
+//
+//                for (Map<String, Object> realData : mpData) {
+//                    for (int i = 0; i < rowSize; i++) {
+//                        if (!idx2Expression.containsKey(i)) {
+//                            continue;
+//                        }
+//                        XWPFTableRow row = table.getRow(i);
+//                        XWPFTableCell valueCell = row.getCell(currIndex);
+//                        String valStr = realData.getOrDefault(idx2Expression.get(i), "-").toString();
+//                        valueCell.setText(valStr);
+//                    }
+//                    currIndex += indexDelta;
+//                }
+//
+//            } catch (Exception e) {
+//                throw new RenderException("HackLoopTable for " + eleTemplate + "error: " + e.getMessage(), e);
+//            }
+//        }
+//
+//        private int getTemplateColIndex(XWPFTableCell tagCell) {
+//            return onSameLine ? Util.getColIndexOfFirstRow(tagCell) : (Util.getColIndexOfFirstRow(tagCell) + 1);
+//        }
+//    }
+//
+//    public class LoopRowCutAndMergeFirstColTableRenderPolicy extends LoopRowTableRenderPolicy {
+//
+//        @Override
+//        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
+//            RunTemplate runTemplate = (RunTemplate) eleTemplate;
+//            XWPFRun run = runTemplate.getRun();
+//            XWPFTable table = null;
+//            try {
+//                if (!TableTools.isInsideTable(run)) {
+//                    throw new IllegalStateException(
+//                        "The template tag " + runTemplate.getSource() + " must be inside a table");
+//                }
+//                // Reserve the first two rows
+//                XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
+//                table = tagCell.getTableRow().getTable();
+//                for (int i = table.getNumberOfRows() - 1; i > 1; i--) {
+//                    table.removeRow(i);
+//                }
+//            } catch (Exception e) {
+//                throw new RenderException(
+//                    "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
+//            }
+//
+//            // in case data not sorted by rank
+//            @SuppressWarnings("unchecked")
+//            List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
+//            mpData.sort((a, b) -> Integer.valueOf(a.getOrDefault("rank", "0").toString())
+//                - Integer.valueOf(b.getOrDefault("rank", "0").toString()));
+//
+//            super.render(eleTemplate, data, template);
+//
+//            try {
+//                // merge the first column
+//                Util.mergeFirstNColSimple(table, 1, 0);
+//            } catch (Exception e) {
+//                throw new RenderException(
+//                    "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
+//            }
+//        }
+//    }
+//
+//    public class LoopRowIncludeStatisticsTableRenderPolicy extends LoopRowTableRenderPolicy {
+//
+//        private String valueTag;
+//
+//        public LoopRowIncludeStatisticsTableRenderPolicy(String valueTag) {
+//            this.valueTag = valueTag;
+//        }
+//
+//        @Override
+//        public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
+//            @SuppressWarnings("unchecked")
+//            List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
+//            for (Map<String, Object> row : mpData) {
+//                @SuppressWarnings("unchecked")
+//                List<String> values = (List<String>) row.get(valueTag);
+//                row.put("avg", values.stream().mapToLong(Long::valueOf).average().orElse(Double.NaN));
+//            }
+//
+//            super.render(eleTemplate, data, template);
+//        }
+//    }
+//}

+ 141 - 141
easier-report-biz/src/main/java/com/yaoyicloud/render/PublicRecordRender.java

@@ -1,141 +1,141 @@
-package com.yaoyicloud.render;
-
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.stream.Collectors;
-
-import com.deepoove.poi.config.Configure;
-import com.deepoove.poi.config.ConfigureBuilder;
-import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.MessageOrBuilder;
-import com.google.protobuf.util.JsonFormat;
-import com.yaoyicloud.message.FxyProtos.PublicRecord;
-
-/**
- * PublicRecord渲染器
- *
- */
-public final class PublicRecordRender extends AbstractRender {
-
-    public PublicRecordRender(String cwd) {
-        super(cwd);
-    }
-
-    /**
-     * Docx 渲染
-     *
-     * @param info 数据
-     * @param templateFileContent 模板内容
-     * @return 本地文件目录
-     * @throws IOException
-     */
-    public String renderDocx(PublicRecord info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
-        // TODO: let mapper be package static
-        ObjectMapper mapper = new ObjectMapper();
-        SimpleModule module = new SimpleModule();
-        module.addSerializer(new PublicRecordSerializer(PublicRecord.class));
-        mapper.registerModule(module);
-
-        String jsonStr = mapper.writeValueAsString(info);
-
-        // 注: 报告模板的模板变量按照json序列化的结果命名
-        // 注: 目前的实现假设:一个session对应一个cwd目录
-        ConfigureBuilder builder = Configure.builder();
-        builder.bind("dishonestPersons", new LoopRowTableRenderPolicy());
-        builder.bind("businessAbnormals", new LoopRowTableRenderPolicy());
-        builder.bind("penaltyRecords", new LoopRowTableRenderPolicy());
-        builder.bind("taxPenalties", new LoopRowTableRenderPolicy());
-        builder.bind("severeViolations", new LoopRowTableRenderPolicy());
-        this.docxResultPath =
-            this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
-                Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
-        return this.docxResultPath;
-    }
-
-    /*
-     * 通过序列化和反序列化
-     * 1. 进行默认显示规则的数据转换
-     * 2. 避免POI-TL处理模板面对复杂数据类型
-     */
-    public class PublicRecordSerializer extends StdSerializer<PublicRecord> {
-
-        public PublicRecordSerializer() {
-            this(null);
-        }
-
-        public PublicRecordSerializer(Class<PublicRecord> t) {
-            super(t);
-        }
-
-        /**
-         * 为了增加*Count的数值,定制了序列化方法。 TODO: 可能更简单的方法是使用POI-TL的Spring表达式能力
-         */
-        @Override
-        public void serialize(PublicRecord value, JsonGenerator jgen, SerializerProvider provider)
-            throws IOException, JsonProcessingException {
-
-            jgen.writeStartObject();
-
-            jgen.writeArrayFieldStart("dishonestPersons");
-            writeRawArray(
-                value.getDishonestPersonsList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()),
-                jgen);
-            jgen.writeEndArray();
-            jgen.writeNumberField("dishonestPersonsCount", value.getDishonestPersonsCount());
-
-            jgen.writeArrayFieldStart("businessAbnormals");
-            writeRawArray(
-                value.getBusinessAbnormalsList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()),
-                jgen);
-            jgen.writeEndArray();
-            jgen.writeNumberField("businessAbnormalsCount", value.getBusinessAbnormalsCount());
-
-            jgen.writeArrayFieldStart("penaltyRecords");
-            writeRawArray(
-                value.getPenaltyRecordsList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()),
-                jgen);
-            jgen.writeEndArray();
-            jgen.writeNumberField("penaltyRecordsCount", value.getPenaltyRecordsCount());
-
-            jgen.writeArrayFieldStart("taxPenalties");
-            writeRawArray(
-                value.getTaxPenaltiesList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()), jgen);
-            jgen.writeEndArray();
-            jgen.writeNumberField("taxPenaltiesCount", value.getTaxPenaltiesCount());
-
-            jgen.writeArrayFieldStart("severeViolations");
-            writeRawArray(
-                value.getSevereViolationsList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()),
-                jgen);
-            jgen.writeEndArray();
-            jgen.writeNumberField("severeViolationsCount", value.getSevereViolationsCount());
-
-            jgen.writeRaw(", \"publicRecordSummary\": " + JsonFormat.printer().print(value.getPublicRecordSummary()));
-
-            jgen.writeEndObject();
-        }
-
-        private void writeRawArray(List<MessageOrBuilder> values, JsonGenerator jgen)
-            throws IOException, InvalidProtocolBufferException {
-            String comma = null;
-            for (MessageOrBuilder item : values) {
-                if (comma != null) {
-                    jgen.writeRaw(comma);
-                } else {
-                    comma = ",";
-                }
-                jgen.writeRaw(JsonFormat.printer().print(item));
-            }
-        }
-    }
-}
+//package com.yaoyicloud.render;
+//
+//import java.io.IOException;
+//import java.nio.file.Paths;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.UUID;
+//import java.util.stream.Collectors;
+//
+//import com.deepoove.poi.config.Configure;
+//import com.deepoove.poi.config.ConfigureBuilder;
+//import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
+//import com.fasterxml.jackson.core.JsonGenerator;
+//import com.fasterxml.jackson.core.JsonProcessingException;
+//import com.fasterxml.jackson.databind.ObjectMapper;
+//import com.fasterxml.jackson.databind.SerializerProvider;
+//import com.fasterxml.jackson.databind.module.SimpleModule;
+//import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+//import com.google.protobuf.InvalidProtocolBufferException;
+//import com.google.protobuf.MessageOrBuilder;
+//import com.google.protobuf.util.JsonFormat;
+//import com.yaoyicloud.message.FxyProtos.PublicRecord;
+//
+///**
+// * PublicRecord渲染器
+// *
+// */
+//public final class PublicRecordRender extends AbstractRender {
+//
+//    public PublicRecordRender(String cwd) {
+//        super(cwd);
+//    }
+//
+//    /**
+//     * Docx 渲染
+//     *
+//     * @param info 数据
+//     * @param templateFileContent 模板内容
+//     * @return 本地文件目录
+//     * @throws IOException
+//     */
+////    public String renderDocx(PublicRecord info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
+////        // TODO: let mapper be package static
+////        ObjectMapper mapper = new ObjectMapper();
+////        SimpleModule module = new SimpleModule();
+////        module.addSerializer(new PublicRecordSerializer(PublicRecord.class));
+////        mapper.registerModule(module);
+////
+////        String jsonStr = mapper.writeValueAsString(info);
+////
+////        // 注: 报告模板的模板变量按照json序列化的结果命名
+////        // 注: 目前的实现假设:一个session对应一个cwd目录
+////        ConfigureBuilder builder = Configure.builder();
+////        builder.bind("dishonestPersons", new LoopRowTableRenderPolicy());
+////        builder.bind("businessAbnormals", new LoopRowTableRenderPolicy());
+////        builder.bind("penaltyRecords", new LoopRowTableRenderPolicy());
+////        builder.bind("taxPenalties", new LoopRowTableRenderPolicy());
+////        builder.bind("severeViolations", new LoopRowTableRenderPolicy());
+////        this.docxResultPath =
+////            this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
+////                Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
+////        return this.docxResultPath;
+////    }
+//
+//    /*
+//     * 通过序列化和反序列化
+//     * 1. 进行默认显示规则的数据转换
+//     * 2. 避免POI-TL处理模板面对复杂数据类型
+//     */
+//    public class PublicRecordSerializer extends StdSerializer<PublicRecord> {
+//
+//        public PublicRecordSerializer() {
+//            this(null);
+//        }
+//
+//        public PublicRecordSerializer(Class<PublicRecord> t) {
+//            super(t);
+//        }
+//
+//        /**
+//         * 为了增加*Count的数值,定制了序列化方法。 TODO: 可能更简单的方法是使用POI-TL的Spring表达式能力
+//         */
+//        @Override
+//        public void serialize(PublicRecord value, JsonGenerator jgen, SerializerProvider provider)
+//            throws IOException, JsonProcessingException {
+//
+//            jgen.writeStartObject();
+//
+//            jgen.writeArrayFieldStart("dishonestPersons");
+//            writeRawArray(
+//                value.getDishonestPersonsList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()),
+//                jgen);
+//            jgen.writeEndArray();
+//            jgen.writeNumberField("dishonestPersonsCount", value.getDishonestPersonsCount());
+//
+//            jgen.writeArrayFieldStart("businessAbnormals");
+//            writeRawArray(
+//                value.getBusinessAbnormalsList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()),
+//                jgen);
+//            jgen.writeEndArray();
+//            jgen.writeNumberField("businessAbnormalsCount", value.getBusinessAbnormalsCount());
+//
+//            jgen.writeArrayFieldStart("penaltyRecords");
+//            writeRawArray(
+//                value.getPenaltyRecordsList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()),
+//                jgen);
+//            jgen.writeEndArray();
+//            jgen.writeNumberField("penaltyRecordsCount", value.getPenaltyRecordsCount());
+//
+//            jgen.writeArrayFieldStart("taxPenalties");
+//            writeRawArray(
+//                value.getTaxPenaltiesList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()), jgen);
+//            jgen.writeEndArray();
+//            jgen.writeNumberField("taxPenaltiesCount", value.getTaxPenaltiesCount());
+//
+//            jgen.writeArrayFieldStart("severeViolations");
+//            writeRawArray(
+//                value.getSevereViolationsList().stream().map(o -> (MessageOrBuilder) o).collect(Collectors.toList()),
+//                jgen);
+//            jgen.writeEndArray();
+//            jgen.writeNumberField("severeViolationsCount", value.getSevereViolationsCount());
+//
+//            jgen.writeRaw(", \"publicRecordSummary\": " + JsonFormat.printer().print(value.getPublicRecordSummary()));
+//
+//            jgen.writeEndObject();
+//        }
+//
+//        private void writeRawArray(List<MessageOrBuilder> values, JsonGenerator jgen)
+//            throws IOException, InvalidProtocolBufferException {
+//            String comma = null;
+//            for (MessageOrBuilder item : values) {
+//                if (comma != null) {
+//                    jgen.writeRaw(comma);
+//                } else {
+//                    comma = ",";
+//                }
+//                jgen.writeRaw(JsonFormat.printer().print(item));
+//            }
+//        }
+//    }
+//}

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

@@ -1,80 +1,80 @@
-package com.yaoyicloud.render;
-
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.Map;
-import java.util.UUID;
-
-import com.deepoove.poi.config.Configure;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
-import com.yaoyicloud.message.FxyProtos.ServiceProviderInfo;
-
-import cn.hutool.core.util.StrUtil;
-
-/**
- * ServiceProviderInfo渲染器
- *
- */
-public final class ServiceProviderInfoRender extends AbstractRender {
-
-    public ServiceProviderInfoRender(String cwd) {
-        super(cwd);
-    }
-
-    /**
-     * Docx 渲染
-     *
-     * @param info 数据
-     * @param templateFileContent 模板内容
-     * @return 本地文件目录
-     * @throws IOException
-     */
-    public String renderDocx(ServiceProviderInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent)
-        throws IOException {
-        ObjectMapper mapper = new ObjectMapper();
-        SimpleModule module = new SimpleModule();
-        module.addSerializer(new ServiceProviderInfoSerializer(ServiceProviderInfo.class));
-        mapper.registerModule(module);
-
-        String jsonStr = mapper.writeValueAsString(info);
-
-        // 注: 报告模板的模板变量按照json序列化的结果命名
-        // 注: 目前的实现假设:一个session对应一个cwd目录
-        this.docxResultPath =
-            this.renderDocx(jsonStr, addtionalMap, templateFileContent, Configure.builder(),
-                Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
-        return this.docxResultPath;
-    }
-
-    /*
-     * 通过序列化和反序列化
-     * 1. 进行默认显示规则的数据转换
-     * 2. 避免POI-TL处理模板面对复杂数据类型
-     */
-    public class ServiceProviderInfoSerializer extends StdSerializer<ServiceProviderInfo> {
-
-        public ServiceProviderInfoSerializer() {
-            this(null);
-        }
-
-        public ServiceProviderInfoSerializer(Class<ServiceProviderInfo> t) {
-            super(t);
-        }
-
-        @Override
-        public void serialize(ServiceProviderInfo value, JsonGenerator jgen, SerializerProvider provider)
-            throws IOException, JsonProcessingException {
-
-            jgen.writeStartObject();
-            jgen.writeStringField("name", StrUtil.isBlank(value.getName()) ? "-" : value.getName());
-            jgen.writeStringField("type", StrUtil.isBlank(value.getType()) ? "-" : value.getType());
-            jgen.writeStringField("reportDate", value.getReportDate());
-            jgen.writeEndObject();
-        }
-    }
-}
+//package com.yaoyicloud.render;
+//
+//import java.io.IOException;
+//import java.nio.file.Paths;
+//import java.util.Map;
+//import java.util.UUID;
+//
+//import com.deepoove.poi.config.Configure;
+//import com.fasterxml.jackson.core.JsonGenerator;
+//import com.fasterxml.jackson.core.JsonProcessingException;
+//import com.fasterxml.jackson.databind.ObjectMapper;
+//import com.fasterxml.jackson.databind.SerializerProvider;
+//import com.fasterxml.jackson.databind.module.SimpleModule;
+//import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+//import com.yaoyicloud.message.FxyProtos.ServiceProviderInfo;
+//
+//import cn.hutool.core.util.StrUtil;
+//
+///**
+// * ServiceProviderInfo渲染器
+// *
+// */
+//public final class ServiceProviderInfoRender extends AbstractRender {
+//
+//    public ServiceProviderInfoRender(String cwd) {
+//        super(cwd);
+//    }
+//
+//    /**
+//     * Docx 渲染
+//     *
+//     * @param info 数据
+//     * @param templateFileContent 模板内容
+//     * @return 本地文件目录
+//     * @throws IOException
+//     */
+////    public String renderDocx(ServiceProviderInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent)
+////        throws IOException {
+////        ObjectMapper mapper = new ObjectMapper();
+////        SimpleModule module = new SimpleModule();
+////        module.addSerializer(new ServiceProviderInfoSerializer(ServiceProviderInfo.class));
+////        mapper.registerModule(module);
+////
+////        String jsonStr = mapper.writeValueAsString(info);
+////
+////        // 注: 报告模板的模板变量按照json序列化的结果命名
+////        // 注: 目前的实现假设:一个session对应一个cwd目录
+////        this.docxResultPath =
+////            this.renderDocx(jsonStr, addtionalMap, templateFileContent, Configure.builder(),
+////                Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
+////        return this.docxResultPath;
+////    }
+//
+//    /*
+//     * 通过序列化和反序列化
+//     * 1. 进行默认显示规则的数据转换
+//     * 2. 避免POI-TL处理模板面对复杂数据类型
+//     */
+//    public class ServiceProviderInfoSerializer extends StdSerializer<ServiceProviderInfo> {
+//
+//        public ServiceProviderInfoSerializer() {
+//            this(null);
+//        }
+//
+//        public ServiceProviderInfoSerializer(Class<ServiceProviderInfo> t) {
+//            super(t);
+//        }
+//
+//        @Override
+//        public void serialize(ServiceProviderInfo value, JsonGenerator jgen, SerializerProvider provider)
+//            throws IOException, JsonProcessingException {
+//
+//            jgen.writeStartObject();
+//            jgen.writeStringField("name", StrUtil.isBlank(value.getName()) ? "-" : value.getName());
+//            jgen.writeStringField("type", StrUtil.isBlank(value.getType()) ? "-" : value.getType());
+//            jgen.writeStringField("reportDate", value.getReportDate());
+//            jgen.writeEndObject();
+//        }
+//    }
+//}

+ 8 - 3
easier-report-biz/src/main/java/com/yaoyicloud/service/ReportService.java

@@ -1,7 +1,10 @@
 package com.yaoyicloud.service;
 
+
 import com.yaoyicloud.constant.enums.ReportType;
-import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
 
 /**
  * 报告服务
@@ -11,12 +14,14 @@ import java.util.Map;
  */
 public interface ReportService {
 
+    @SuppressWarnings("checkstyle:ParameterNumber")
     String createPlusVersionCheckReport(
             ReportType reportType,
-            Map<String, Object> data,
+            String data,
             byte[] templateBytes,
             String outputBasePath,
-            Long relationId
+            Long relationId,
+            HttpServletRequest request
     ) throws Exception;
 
 }

+ 48 - 33
easier-report-biz/src/main/java/com/yaoyicloud/service/impl/ReportServiceImpl.java

@@ -3,7 +3,11 @@ package com.yaoyicloud.service.impl;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.util.Map;
+
+
+import com.yaoyicloud.config.FilerepoProperties;
 import com.yaoyicloud.constant.enums.ReportType;
+import com.yaoyicloud.render.AntiBriberyRender;
 import com.yaoyicloud.service.ReportService;
 import com.yaoyicloud.template.AcademicAssociationReport;
 import com.yaoyicloud.template.FoundationReport;
@@ -13,6 +17,8 @@ import org.springframework.stereotype.Service;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * 报告服务
  *
@@ -26,12 +32,13 @@ public class ReportServiceImpl implements ReportService {
     private final PlatformCompanyReport platformCompanyReport;
     private final AcademicAssociationReport academicAssociationReport;
     private final FoundationReport foundationReport;
+    private final FilerepoProperties filerepoProperties;
 
 
     @SuppressWarnings("checkstyle:ReturnCount")
     @Override
     public String createPlusVersionCheckReport(
-        ReportType reportType, Map<String, Object> data, byte[] templateBytes, String outputBasePath, Long relationId)
+            ReportType reportType, String data, byte[] templateBytes, String outputBasePath, Long relationId, HttpServletRequest request)
         throws Exception {
 
         // 1. 将字节流模版写入临时文件
@@ -40,38 +47,46 @@ public class ReportServiceImpl implements ReportService {
             fos.write(templateBytes);
         }
         Map<String, Object> processedData = null;
-        switch (reportType) {
-            case PLATFORM_COMPANY:
-                 processedData = platformCompanyReport.processData(data);
-                return platformCompanyReport.exportReport(
-                        processedData,
-                    tempTemplateFile.getAbsolutePath(),
-                    outputBasePath,
-                    relationId);
-            case ACADEMIC_ASSOCIATIONS:
-                processedData = academicAssociationReport.processData(data);
-                return academicAssociationReport.exportReport(
-                        processedData,
-                        tempTemplateFile.getAbsolutePath(),
-                        outputBasePath,
-                        relationId);
-            case FOUNDATION:
-                processedData = foundationReport.processData(data);
-                return foundationReport.exportReport(
-                        processedData,
-                        tempTemplateFile.getAbsolutePath(),
-                        outputBasePath,
-                        relationId);
-            case PROMOTION:
-                processedData = platformCompanyReport.processData(data);
-                return platformCompanyReport.exportReport(
-                        processedData,
-                        tempTemplateFile.getAbsolutePath(),
-                        outputBasePath,
-                        relationId);
-            default:
-                throw new UnsupportedOperationException("Unsupported report type: " + reportType);
-        }
+
+        String sessionId = request.getSession().getId();
+
+
+        // AntiBriberyRender antiBriberyRender = new AntiBriberyRender("../temp/");
+        AntiBriberyRender antiBriberyRender = new AntiBriberyRender(sessionId, filerepoProperties);
+        return antiBriberyRender.renderDocx(data, processedData, templateBytes, String.valueOf(relationId));
+
+//        switch (reportType) {
+//            case PLATFORM_COMPANY:
+//                 processedData = platformCompanyReport.processData(data);
+//                return platformCompanyReport.exportReport(
+//                        processedData,
+//                    tempTemplateFile.getAbsolutePath(),
+//                    outputBasePath,
+//                    relationId);
+//            case ACADEMIC_ASSOCIATIONS:
+//                processedData = academicAssociationReport.processData(data);
+//                return academicAssociationReport.exportReport(
+//                        processedData,
+//                        tempTemplateFile.getAbsolutePath(),
+//                        outputBasePath,
+//                        relationId);
+//            case FOUNDATION:
+//                processedData = foundationReport.processData(data);
+//                return foundationReport.exportReport(
+//                        processedData,
+//                        tempTemplateFile.getAbsolutePath(),
+//                        outputBasePath,
+//                        relationId);
+//            case PROMOTION:
+//                processedData = platformCompanyReport.processData(data);
+//                return platformCompanyReport.exportReport(
+//                        processedData,
+//                        tempTemplateFile.getAbsolutePath(),
+//                        outputBasePath,
+//                        relationId);
+//            default:
+//                throw new UnsupportedOperationException("Unsupported report type: " + reportType);
+//        }
     }
 
 

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

@@ -113,8 +113,8 @@ logging:
     com.yaoyicloud.easier.filerepo.mapper: debug
 
 # 租户表维护
-#easier:
-#  office:
-#    pdf:
-#      rootPath: C:/Users/yyy/dev/yyc3/easier-be
+easier:
+  office:
+    pdf:
+      rootPath: C:/Users/yyy/dev/yyc3/easier-be
 

+ 90 - 38
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAntiBriberyRender.java

@@ -1,38 +1,90 @@
-package com.yaoyicloud.render.test;
-
-import static org.junit.Assert.assertTrue;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Map;
-
-import org.junit.Test;
-
-import com.yaoyicloud.message.FxyProtos.AntiBribery;
-import com.yaoyicloud.message.FxyProtos.CheckSummary;
-import com.yaoyicloud.message.FxyProtos.QuestionnaireItem;
-import com.yaoyicloud.render.AntiBriberyRender;
-
-public class TestAntiBriberyRender {
-
-    @Test
-    public void testRenderDocx() throws IOException {
-
-        byte[] content = Files
-            .readAllBytes(Paths.get(getClass().getClassLoader().getResource("docx/antiBribery.docx").getFile()));
-        AntiBriberyRender render = new AntiBriberyRender("../temp/");
-        String retPath = render.renderDocx(
-            AntiBribery.newBuilder()
-                .setAntiBriberySummary(CheckSummary.newBuilder().setRiskSummary("high risk").setSuggestion("accept it"))
-                .addQuestionnaireItems(
-                    QuestionnaireItem.newBuilder().setId(1).setQuestion("question1").setAnswer("answer1"))
-                .addQuestionnaireItems(
-                    QuestionnaireItem.newBuilder().setId(1).setQuestion("question2").setAnswer("answer2"))
-                .build(),
-            Map.of(),
-            content);
-
-        assertTrue(retPath.length() > 0);
-    }
-
-}
+//package com.yaoyicloud.render.test;
+//
+//import static org.junit.Assert.assertTrue;
+//import java.io.IOException;
+//import java.nio.file.Files;
+//import java.nio.file.Paths;
+//import java.util.Map;
+//
+//import com.google.protobuf.InvalidProtocolBufferException;
+//import com.google.protobuf.util.JsonFormat;
+//import com.yaoyicloud.message.FxyProtos;
+//import org.junit.Test;
+//
+//import com.yaoyicloud.message.FxyProtos.AntiBribery;
+//import com.yaoyicloud.message.FxyProtos.CheckSummary;
+//import com.yaoyicloud.message.FxyProtos.QuestionnaireItem;
+//import com.yaoyicloud.render.AntiBriberyRender;
+//
+//public class
+//
+//
+//
+//
+//
+//
+//TestAntiBriberyRender {
+//
+//    @Test
+//    public void testRenderDocx() throws IOException {
+//
+//        byte[] content = Files
+//            .readAllBytes(Paths.get(getClass().getClassLoader().getResource("docx/antiBribery.docx").getFile()));
+//        AntiBriberyRender render = new AntiBriberyRender("../temp/");
+//        String retPath = render.renderDocx(
+//            AntiBribery.newBuilder()
+//                .setAntiBriberySummary(CheckSummary.newBuilder().setRiskSummary("high risk").setSuggestion("accept it"))
+//                .addQuestionnaireItems(
+//                    QuestionnaireItem.newBuilder().setId(1).setQuestion("question1").setAnswer("answer1"))
+//                .addQuestionnaireItems(
+//                    QuestionnaireItem.newBuilder().setId(1).setQuestion("question2").setAnswer("answer2"))
+//                .build(),
+//            Map.of(),
+//            content);
+//
+//        assertTrue(retPath.length() > 0);
+//    }
+//
+//    @Test
+//    public void testNullValuesInProtobuf() throws InvalidProtocolBufferException {
+//        // 创建 Protobuf 对象构建器
+//        FxyProtos.CheckSummary.Builder builder = FxyProtos.CheckSummary.newBuilder();
+//
+//        // 测试 null 值的处理
+//        Object nullValue = null;
+//
+//        // 方式1:直接设置 null(会触发 NullPointerException,需注释掉此行观察)
+//        // builder.setRiskSummary((String) nullValue); // 取消注释此行会抛出 NPE
+//
+//        // 方式2:使用安全的方式设置 null(将 null 转换为空字符串或默认值)
+//        builder.setRiskSummary(nullValue != null ? (String) nullValue : "")
+//                .setSuggestion(nullValue != null ? (String) nullValue : "")
+//                .setScore(nullValue != null ? (Integer) nullValue : 0);
+//
+//        // 构建 Protobuf 对象
+//        FxyProtos.CheckSummary protoObject = builder.build();
+//
+//        // 转换为 JSON(包括默认值字段)
+//        String json = JsonFormat.printer()
+//                .includingDefaultValueFields()
+//                .print(protoObject);
+//
+//        System.out.println("JSON 格式: " + json);
+//
+//        // 从 JSON 恢复 Protobuf 对象
+//        FxyProtos.CheckSummary.Builder restoredBuilder = FxyProtos.CheckSummary.newBuilder();
+//        JsonFormat.parser()
+//                .ignoringUnknownFields()
+//                .merge(json, restoredBuilder);
+//
+//        FxyProtos.CheckSummary restoredObject = restoredBuilder.build();
+//
+//        System.out.println("恢复的 Protobuf 对象: " + restoredObject);
+//
+//        // 验证字段值
+//        System.out.println("riskSummary 是否为空字符串: " + restoredObject.getRiskSummary().isEmpty());
+//        System.out.println("suggestion 是否为空字符串: " + restoredObject.getSuggestion().isEmpty());
+//        System.out.println("score 是否为 0: " + (restoredObject.getScore() == 0));
+//    }
+//
+//}

+ 4 - 6
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAttachmentSectionRender.java

@@ -2,6 +2,7 @@ package com.yaoyicloud.render.test;
 
 import static org.junit.Assert.assertTrue;
 import java.io.IOException;
+import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.Map;
@@ -15,10 +16,10 @@ import com.yaoyicloud.render.AttachmentSectionRender;
 public class TestAttachmentSectionRender {
 
     @Test
-    public void testRenderDocx() throws IOException {
+    public void testRenderDocx() throws IOException, URISyntaxException {
 
         byte[] content = Files
-            .readAllBytes(Paths.get(getClass().getClassLoader().getResource("docx/attachments.docx").getFile()));
+            .readAllBytes(Paths.get(getClass().getClassLoader().getResource("docx/attachments.docx").toURI()));
         AttachmentSectionRender render = new AttachmentSectionRender("../temp/");
         String retPath = render.renderDocx(
             AttachmentSection.newBuilder()
@@ -28,10 +29,7 @@ public class TestAttachmentSectionRender {
                     .setFileUri("https://pre.yaoeasier.com/assets/yyc_past_logo.70f2a91c.png"))
                 .addBusinessLicenseImages(Attachment.newBuilder().setFileName(".pdf")
                     .setFileUri(
-                        "https://yyc3-1321096020.cos.ap-beijing.myqcloud.com/declaration/20250522/1dfcf990ed6442abbf816d302298667b.pdf"
-                            + "?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250523T052431Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400"
-                            + "&X-Amz-Credential=AKIDRilauFg4fDfE9B6tJoNxTvHSdovqXBfw%2F20250523%2Fap-beijing%2Fs3%2Faws4_request&X-Amz-Signature"
-                            + "=45ca610048b38dd4fa54bdd7b0b926b5e40864fda1b27556da51a8665526a848"))
+                        "https://pre.yaoeasier.com/#/decl?tId=1834769374606344194&declSn=271af310f8194007b6444b13a95cf267"))
                 .build(),
             Map.of(),
             content);

二進制
easier-report-biz/src/test/resources/docx/antiBribery.docx