Browse Source

attachment section render

dengjia 1 month ago
parent
commit
ecaeea9338
18 changed files with 319 additions and 16 deletions
  1. 5 0
      easier-report-biz/pom.xml
  2. 5 2
      easier-report-biz/src/main/java/com/yaoyicloud/render/AbstractRender.java
  3. 3 2
      easier-report-biz/src/main/java/com/yaoyicloud/render/AntiBriberyRender.java
  4. 195 0
      easier-report-biz/src/main/java/com/yaoyicloud/render/AttachmentSectionRender.java
  5. 3 2
      easier-report-biz/src/main/java/com/yaoyicloud/render/AuditResultRender.java
  6. 3 2
      easier-report-biz/src/main/java/com/yaoyicloud/render/BasicInfoRender.java
  7. 2 2
      easier-report-biz/src/main/java/com/yaoyicloud/render/FinancialInfoRender.java
  8. 3 2
      easier-report-biz/src/main/java/com/yaoyicloud/render/PublicRecordRender.java
  9. 4 3
      easier-report-biz/src/main/java/com/yaoyicloud/render/ServiceProviderInfoRender.java
  10. 36 1
      easier-report-biz/src/main/proto/fxy.proto
  11. 3 0
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAntiBriberyRender.java
  12. 42 0
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAttachmentSectionRender.java
  13. 3 0
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAuditResultRender.java
  14. 3 0
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestBasicInfoRender.java
  15. 3 0
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestFinancialInfoRender.java
  16. 3 0
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestPublicRecordRender.java
  17. 3 0
      easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestServiceProviderInfoRender.java
  18. BIN
      easier-report-biz/src/test/resources/docx/attachments.docx

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

@@ -68,6 +68,11 @@
             <artifactId>flying-saucer-pdf</artifactId>
             <version>9.12.0</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.pdfbox</groupId>
+            <artifactId>pdfbox</artifactId>
+            <version>3.0.5</version>
+        </dependency>
         <!-- iText 7 核心 -->
         <!--        <dependency>-->
         <!--            <groupId>com.itextpdf</groupId>-->

+ 5 - 2
easier-report-biz/src/main/java/com/yaoyicloud/render/AbstractRender.java

@@ -43,16 +43,19 @@ public abstract class AbstractRender {
     /**
      * Docx 渲染
      *
-     * @param info 数据
+     * @param jsonStr 数据
+     * @param addtionalMap 全局数据和动态增加的数据
      * @param templateFileContent 模板内容
      * @return 本地文件目录
      * @throws IOException
      */
-    public final String renderDocx(String jsonStr, byte[] templateFileContent, ConfigureBuilder builder, String outputPath) throws IOException {
+    public final String renderDocx(String jsonStr, Map<String, Object> addtionalMap, byte[] templateFileContent,
+        ConfigureBuilder builder, String outputPath) throws IOException {
 
         // 注: 报告模板的模板变量按照json序列化的结果命名
         ObjectMapper objectMapper = new ObjectMapper();
         Map<String, Object> data = objectMapper.readValue(jsonStr, new TypeReference<Map<String, Object>>() {});
+        data.putAll(addtionalMap);
 
         Configure config = builder.build();
         XWPFTemplate template =

+ 3 - 2
easier-report-biz/src/main/java/com/yaoyicloud/render/AntiBriberyRender.java

@@ -2,6 +2,7 @@ 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;
@@ -28,7 +29,7 @@ public final class AntiBriberyRender extends AbstractRender {
      * @return 本地文件目录
      * @throws IOException
      */
-    public String renderDocx(AntiBribery info, byte[] templateFileContent) throws IOException {
+    public String renderDocx(AntiBribery info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
         // 不需要定制展示逻辑的时候,使用protobuf的转json方法
         String jsonStr = JsonFormat.printer().print(info);
 
@@ -36,7 +37,7 @@ public final class AntiBriberyRender extends AbstractRender {
         // 注: 目前的实现假设:一个session对应一个cwd目录
         ConfigureBuilder builder = Configure.builder();
         builder.bind("questionnaireItems", new LoopRowTableRenderPolicy());
-        this.docxResultPath = this.renderDocx(jsonStr, templateFileContent, builder,
+        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
             Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
         return this.docxResultPath;
     }

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

@@ -0,0 +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));
+        }
+    }
+}

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

@@ -2,6 +2,7 @@ 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;
@@ -37,7 +38,7 @@ public final class AuditResultRender extends AbstractRender {
      * @return 本地文件目录
      * @throws IOException
      */
-    public String renderDocx(AuditResult info, byte[] templateFileContent) 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));
@@ -51,7 +52,7 @@ public final class AuditResultRender extends AbstractRender {
         // 注: 目前的实现假设:一个session对应一个cwd目录
         ConfigureBuilder builder = Configure.builder();
         builder.bind("checkItemScores", new LoopRowTableRenderPolicy());
-        this.docxResultPath = this.renderDocx(jsonStr, templateFileContent, builder,
+        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
             Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
         return this.docxResultPath;
     }

+ 3 - 2
easier-report-biz/src/main/java/com/yaoyicloud/render/BasicInfoRender.java

@@ -2,6 +2,7 @@ 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;
@@ -28,7 +29,7 @@ public final class BasicInfoRender extends AbstractRender {
      * @return 本地文件目录
      * @throws IOException
      */
-    public String renderDocx(BasicInfo info, byte[] templateFileContent) throws IOException {
+    public String renderDocx(BasicInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
         // 不需要定制展示逻辑的时候,使用protobuf的转json方法
         String jsonStr = JsonFormat.printer().print(info);
 
@@ -36,7 +37,7 @@ public final class BasicInfoRender extends AbstractRender {
         // 注: 目前的实现假设:一个session对应一个cwd目录
         ConfigureBuilder builder = Configure.builder();
         builder.bind("basicInfoChecks", new LoopRowTableRenderPolicy());
-        this.docxResultPath = this.renderDocx(jsonStr, templateFileContent, builder,
+        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
             Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
         return this.docxResultPath;
     }

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

@@ -43,7 +43,7 @@ public final class FinancialInfoRender extends AbstractRender {
      * @return 本地文件目录
      * @throws IOException
      */
-    public String renderDocx(FinancialInfo info, byte[] templateFileContent) throws IOException {
+    public String renderDocx(FinancialInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
 
         // 不需要定制展示逻辑的时候,使用protobuf的转json方法
         String jsonStr = JsonFormat.printer().print(info);
@@ -57,7 +57,7 @@ public final class FinancialInfoRender extends AbstractRender {
         // 注意使用了SpringEL之后,每个模板变量都要设置值,不然会报错
         builder.useSpringEL();
 
-        this.docxResultPath = this.renderDocx(jsonStr, templateFileContent, builder,
+        this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
             Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
         return this.docxResultPath;
     }

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

@@ -3,6 +3,7 @@ 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;
 
@@ -38,7 +39,7 @@ public final class PublicRecordRender extends AbstractRender {
      * @return 本地文件目录
      * @throws IOException
      */
-    public String renderDocx(PublicRecord info, byte[] templateFileContent) 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();
@@ -56,7 +57,7 @@ public final class PublicRecordRender extends AbstractRender {
         builder.bind("taxPenalties", new LoopRowTableRenderPolicy());
         builder.bind("severeViolations", new LoopRowTableRenderPolicy());
         this.docxResultPath =
-            this.renderDocx(jsonStr, templateFileContent, builder,
+            this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
                 Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
         return this.docxResultPath;
     }

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

@@ -2,6 +2,7 @@ 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;
@@ -33,7 +34,8 @@ public final class ServiceProviderInfoRender extends AbstractRender {
      * @return 本地文件目录
      * @throws IOException
      */
-    public String renderDocx(ServiceProviderInfo info, byte[] templateFileContent) 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));
@@ -44,7 +46,7 @@ public final class ServiceProviderInfoRender extends AbstractRender {
         // 注: 报告模板的模板变量按照json序列化的结果命名
         // 注: 目前的实现假设:一个session对应一个cwd目录
         this.docxResultPath =
-            this.renderDocx(jsonStr, templateFileContent, Configure.builder(),
+            this.renderDocx(jsonStr, addtionalMap, templateFileContent, Configure.builder(),
                 Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
         return this.docxResultPath;
     }
@@ -72,7 +74,6 @@ public final class ServiceProviderInfoRender extends AbstractRender {
             jgen.writeStringField("name", StrUtil.isBlank(value.getName()) ? "-" : value.getName());
             jgen.writeStringField("type", StrUtil.isBlank(value.getType()) ? "-" : value.getType());
             jgen.writeStringField("reportDate", value.getReportDate());
-            jgen.writeStringField("tenantName", value.getTenantName());
             jgen.writeEndObject();
         }
     }

+ 36 - 1
easier-report-biz/src/main/proto/fxy.proto

@@ -7,7 +7,6 @@ message ServiceProviderInfo {
     optional string name = 1; // ${服务商信息:服务商名称}
     optional string type = 2; // "基金会"
     optional string reportDate = 3; // ${服务商信息:报告日期}
-    optional string tenantName = 4; // ${服务商信息:租户名称}
 }
 
 message CheckItemDetail {
@@ -84,6 +83,7 @@ message Attachment {
     optional string fileUri = 2;
 }
 
+/* 通用基础信息 */
 message BasicInfo {
     optional string entName = 1;
     optional string establishmentDate = 2; // 成立登记日期(格式:yyyy-MM-dd)
@@ -289,3 +289,38 @@ message AntiBribery {
     repeated QuestionnaireItem questionnaireItems = 1; // 反贿赂反腐败诚信保证问卷
     optional CheckSummary antiBriberySummary = 2; // 反贿赂反腐败诚信保证评分及建议
 }
+
+/* 包含所有可能的附件 */
+message AttachmentSection {
+    optional string selfDeclLink = 1;
+    repeated string fillDeclLinks = 2;
+
+    repeated Attachment businessLicenseImages = 3; // 营业执照图片路径
+
+    repeated Attachment bankCertificateImages = 4; // 银行开户证明图片路径
+
+    repeated Attachment isoCertificationImages = 5; // ISO认证证书图片路径
+
+    repeated Attachment otherCertifications = 6; // 其他资质文件路径
+
+    repeated Attachment securityLevelCertifications = 7; // 信息安全等级保护证明路径
+
+    repeated Attachment organizationalStructureImages = 8; // 组织架构图路径
+
+    repeated Attachment financialInfoLinks = 9; // 财务信息在线链接
+
+    repeated Attachment taxDeclarationImages = 10; // 增值税申报表路径
+
+    repeated Attachment taxPaymentCertificateImages = 11; // 完税凭证图片路径
+
+    repeated Attachment socialSecurityDetailsLinks = 12; // 社保缴纳证明路径
+
+    repeated Attachment nsxydjwj = 13; // 纳税信用等级相关支持性文件
+
+    repeated Attachment interestConflicts = 14; // 利益冲突
+
+    repeated Attachment disanfangcangzhaos = 15; // 第三方参照
+
+    repeated Attachment overallGuaranteeStatements = 16; // 总体保证声明
+
+}

+ 3 - 0
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAntiBriberyRender.java

@@ -4,6 +4,8 @@ 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;
@@ -27,6 +29,7 @@ public class TestAntiBriberyRender {
                 .addQuestionnaireItems(
                     QuestionnaireItem.newBuilder().setId(1).setQuestion("question2").setAnswer("answer2"))
                 .build(),
+            Map.of(),
             content);
 
         assertTrue(retPath.length() > 0);

+ 42 - 0
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAttachmentSectionRender.java

@@ -0,0 +1,42 @@
+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.Attachment;
+import com.yaoyicloud.message.FxyProtos.AttachmentSection;
+import com.yaoyicloud.render.AttachmentSectionRender;
+
+public class TestAttachmentSectionRender {
+
+    @Test
+    public void testRenderDocx() throws IOException {
+
+        byte[] content = Files
+            .readAllBytes(Paths.get(getClass().getClassLoader().getResource("docx/attachments.docx").getFile()));
+        AttachmentSectionRender render = new AttachmentSectionRender("../temp/");
+        String retPath = render.renderDocx(
+            AttachmentSection.newBuilder()
+                .setSelfDeclLink("https://abc.com/111")
+                .addBusinessLicenseImages(Attachment.newBuilder().setFileName("").setFileUri("https://bcd.com"))
+                .addBusinessLicenseImages(Attachment.newBuilder().setFileName("yyc_past_logo.70f2a91c.png")
+                    .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"))
+                .build(),
+            Map.of(),
+            content);
+
+        assertTrue(retPath.length() > 0);
+    }
+
+}

+ 3 - 0
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestAuditResultRender.java

@@ -4,6 +4,8 @@ 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.AuditResult;
@@ -27,6 +29,7 @@ public class TestAuditResultRender {
                 .addCheckItemScores(
                     CheckItemScore.newBuilder().setCategory("cate2").setItemName("name2").setScore(2L))
                 .build(),
+            Map.of(),
             content);
 
         assertTrue(retPath.length() > 0);

+ 3 - 0
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestBasicInfoRender.java

@@ -4,6 +4,8 @@ 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.BasicInfo;
@@ -27,6 +29,7 @@ public class TestBasicInfoRender {
                 .addBasicInfoChecks(
                     CheckItemDetail.newBuilder().setName("item2").setScore(2L))
                 .build(),
+            Map.of(),
             content);
 
         assertTrue(retPath.length() > 0);

+ 3 - 0
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestFinancialInfoRender.java

@@ -4,6 +4,8 @@ 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.CheckItemDetail;
@@ -36,6 +38,7 @@ public class TestFinancialInfoRender {
                 .addFinancialCheckDetails(CheckItemDetail.newBuilder().setCategory("cate1").setName("n1")
                     .setResult("r1").setScore(0).setReviewResult("rr1").setReviewScore(0).setRank(0))
                 .build(),
+            Map.of(),
             content);
 
         assertTrue(retPath.length() > 0);

+ 3 - 0
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestPublicRecordRender.java

@@ -4,6 +4,8 @@ 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.CheckSummary;
@@ -25,6 +27,7 @@ public class TestPublicRecordRender {
                 .addDishonestPersons(DishonestPersonsInfo.newBuilder().setCourt("fake cout"))
                 .addDishonestPersons(DishonestPersonsInfo.newBuilder().setAmount("fake amount"))
                 .build(),
+            Map.of(),
             content);
 
         assertTrue(retPath.length() > 0);

+ 3 - 0
easier-report-biz/src/test/java/com/yaoyicloud/render/test/TestServiceProviderInfoRender.java

@@ -4,6 +4,8 @@ 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.ServiceProviderInfo;
@@ -20,6 +22,7 @@ public class TestServiceProviderInfoRender {
         ServiceProviderInfoRender render = new ServiceProviderInfoRender("../temp/");
         String retPath = render.renderDocx(
             ServiceProviderInfo.newBuilder().setName("宇宙公司").setType("服务商").build(),
+            Map.of(),
             content);
 
         assertTrue(retPath.length() > 0);

BIN
easier-report-biz/src/test/resources/docx/attachments.docx